summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2017-01-15 17:11:55 +0100
committerHarald Welte <laforge@gnumonks.org>2017-01-15 17:11:55 +0100
commitf4d9df5531681d64d72890add7a81942de9c1c22 (patch)
tree16c3af3bf4e6c69914377cf3d268e2c141273450
parent4b9bff0de15379c97b13ecf9040a17c55fd71493 (diff)
parent2f0b0c955b4031a9dd95829322455cf883d40f53 (diff)
Merge commit '2f0b0c955b4031a9dd95829322455cf883d40f53' into lib-update
-rw-r--r--src/shared/libosmocore/.gitignore36
-rw-r--r--src/shared/libosmocore/.gitreview3
-rw-r--r--src/shared/libosmocore/.mailmap12
-rw-r--r--src/shared/libosmocore/Doxyfile.codec.in12
-rw-r--r--src/shared/libosmocore/Doxyfile.core.in10
-rw-r--r--src/shared/libosmocore/Doxyfile.gsm.in12
-rw-r--r--src/shared/libosmocore/Doxyfile.vty.in12
-rw-r--r--src/shared/libosmocore/Makefile.am10
-rw-r--r--src/shared/libosmocore/TODO-RELEASE9
-rw-r--r--src/shared/libosmocore/configure.ac67
-rwxr-xr-xsrc/shared/libosmocore/contrib/fsm-to-dot.py710
-rwxr-xr-xsrc/shared/libosmocore/contrib/jenkins.sh10
-rw-r--r--src/shared/libosmocore/debian/changelog130
-rw-r--r--src/shared/libosmocore/debian/compat2
-rw-r--r--src/shared/libosmocore/debian/control274
-rw-r--r--src/shared/libosmocore/debian/copyright237
-rw-r--r--src/shared/libosmocore/debian/libosmocodec-doc.doc-base7
-rw-r--r--src/shared/libosmocore/debian/libosmocodec-doc.install1
-rw-r--r--src/shared/libosmocore/debian/libosmocodec0.install1
-rw-r--r--src/shared/libosmocore/debian/libosmocore-dbg.dirs1
-rw-r--r--src/shared/libosmocore/debian/libosmocore-dbg.install1
-rw-r--r--src/shared/libosmocore/debian/libosmocore-dev.dirs8
-rw-r--r--src/shared/libosmocore/debian/libosmocore-dev.install8
-rw-r--r--src/shared/libosmocore/debian/libosmocore-doc.doc-base7
-rw-r--r--src/shared/libosmocore/debian/libosmocore-doc.install1
-rw-r--r--src/shared/libosmocore/debian/libosmocore-utils.dirs1
-rw-r--r--src/shared/libosmocore/debian/libosmocore-utils.install2
-rw-r--r--src/shared/libosmocore/debian/libosmocore.install2
-rw-r--r--src/shared/libosmocore/debian/libosmocore8.install1
-rw-r--r--src/shared/libosmocore/debian/libosmoctrl0.install1
-rw-r--r--src/shared/libosmocore/debian/libosmogb4.install1
-rw-r--r--src/shared/libosmocore/debian/libosmogsm-doc.doc-base7
-rw-r--r--src/shared/libosmocore/debian/libosmogsm-doc.install1
-rw-r--r--src/shared/libosmocore/debian/libosmogsm7.install1
-rw-r--r--src/shared/libosmocore/debian/libosmosim0.install1
-rw-r--r--src/shared/libosmocore/debian/libosmovty-doc.doc-base7
-rw-r--r--src/shared/libosmocore/debian/libosmovty-doc.install1
-rw-r--r--src/shared/libosmocore/debian/libosmovty3.install1
-rw-r--r--src/shared/libosmocore/debian/patches/debian-changes-0.1.17-146
-rw-r--r--src/shared/libosmocore/debian/patches/series1
-rwxr-xr-xsrc/shared/libosmocore/debian/rules50
-rw-r--r--src/shared/libosmocore/doc/osmocom-authn-protocol.txt250
-rw-r--r--src/shared/libosmocore/doc/vty/merge_doc.xsl4
-rw-r--r--src/shared/libosmocore/include/Makefile.am55
-rw-r--r--src/shared/libosmocore/include/osmocom/codec/codec.h58
-rw-r--r--src/shared/libosmocore/include/osmocom/core/application.h5
-rw-r--r--src/shared/libosmocore/include/osmocom/core/backtrace.h5
-rw-r--r--src/shared/libosmocore/include/osmocom/core/bitXXgen.h.tpl105
-rw-r--r--src/shared/libosmocore/include/osmocom/core/bitcomp.h41
-rw-r--r--src/shared/libosmocore/include/osmocom/core/bits.h60
-rw-r--r--src/shared/libosmocore/include/osmocom/core/bitvec.h45
-rw-r--r--src/shared/libosmocore/include/osmocom/core/conv.h7
-rw-r--r--src/shared/libosmocore/include/osmocom/core/crc16.h15
-rw-r--r--src/shared/libosmocore/include/osmocom/core/crcXXgen.h.tpl7
-rw-r--r--src/shared/libosmocore/include/osmocom/core/crcgen.h7
-rw-r--r--src/shared/libosmocore/include/osmocom/core/defs.h47
-rw-r--r--src/shared/libosmocore/include/osmocom/core/endian.h49
-rw-r--r--src/shared/libosmocore/include/osmocom/core/fsm.h204
-rw-r--r--src/shared/libosmocore/include/osmocom/core/gsmtap.h143
-rw-r--r--src/shared/libosmocore/include/osmocom/core/gsmtap_util.h9
-rw-r--r--src/shared/libosmocore/include/osmocom/core/linuxlist.h200
-rw-r--r--src/shared/libosmocore/include/osmocom/core/linuxrbtree.h8
-rw-r--r--src/shared/libosmocore/include/osmocom/core/logging.h147
-rw-r--r--src/shared/libosmocore/include/osmocom/core/loggingrb.h37
-rw-r--r--src/shared/libosmocore/include/osmocom/core/macaddr.h4
-rw-r--r--src/shared/libosmocore/include/osmocom/core/msgb.h130
-rw-r--r--src/shared/libosmocore/include/osmocom/core/msgfile.h5
-rw-r--r--src/shared/libosmocore/include/osmocom/core/panic.h5
-rw-r--r--src/shared/libosmocore/include/osmocom/core/plugin.h5
-rw-r--r--src/shared/libosmocore/include/osmocom/core/prim.h14
-rw-r--r--src/shared/libosmocore/include/osmocom/core/rate_ctr.h36
-rw-r--r--src/shared/libosmocore/include/osmocom/core/select.h13
-rw-r--r--src/shared/libosmocore/include/osmocom/core/serial.h7
-rw-r--r--src/shared/libosmocore/include/osmocom/core/signal.h12
-rw-r--r--src/shared/libosmocore/include/osmocom/core/socket.h14
-rw-r--r--src/shared/libosmocore/include/osmocom/core/stat_item.h137
-rw-r--r--src/shared/libosmocore/include/osmocom/core/statistics.h19
-rw-r--r--src/shared/libosmocore/include/osmocom/core/stats.h121
-rw-r--r--src/shared/libosmocore/include/osmocom/core/strrb.h55
-rw-r--r--src/shared/libosmocore/include/osmocom/core/talloc.h196
-rw-r--r--src/shared/libosmocore/include/osmocom/core/timer.h24
-rw-r--r--src/shared/libosmocore/include/osmocom/core/timer_compat.h5
-rw-r--r--src/shared/libosmocore/include/osmocom/core/utils.h45
-rw-r--r--src/shared/libosmocore/include/osmocom/core/write_queue.h5
-rw-r--r--src/shared/libosmocore/include/osmocom/crypt/auth.h7
-rw-r--r--src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h32
-rw-r--r--src/shared/libosmocore/include/osmocom/ctrl/control_cmd.h190
-rw-r--r--src/shared/libosmocore/include/osmocom/ctrl/control_if.h31
-rw-r--r--src/shared/libosmocore/include/osmocom/ctrl/control_vty.h9
-rw-r--r--src/shared/libosmocore/include/osmocom/ctrl/ports.h18
-rw-r--r--src/shared/libosmocore/include/osmocom/gprs/gprs_bssgp.h9
-rw-r--r--src/shared/libosmocore/include/osmocom/gprs/gprs_bssgp_bss.h14
-rw-r--r--src/shared/libosmocore/include/osmocom/gprs/gprs_msgb.h6
-rw-r--r--src/shared/libosmocore/include/osmocom/gprs/gprs_ns.h52
-rw-r--r--src/shared/libosmocore/include/osmocom/gprs/gprs_ns_frgre.h5
-rw-r--r--src/shared/libosmocore/include/osmocom/gprs/gprs_rlc.h27
-rw-r--r--src/shared/libosmocore/include/osmocom/gprs/protocol/gsm_04_60.h208
-rw-r--r--src/shared/libosmocore/include/osmocom/gprs/protocol/gsm_08_16.h5
-rw-r--r--src/shared/libosmocore/include/osmocom/gprs/protocol/gsm_08_18.h5
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/a5.h14
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/abis_nm.h25
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/apn.h19
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/bitvec_gsm.h13
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/comp128.h12
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/comp128v23.h20
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gan.h5
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gea.h17
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm0341.h9
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm0411_smc.h8
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm0411_smr.h9
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm0411_utils.h7
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm0480.h24
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm0502.h5
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm0503.h174
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm0808.h6
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm23003.h80
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm48.h11
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm48_ie.h5
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm_utils.h93
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsup.h149
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/ipa.h67
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/kasumi.h48
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/l1sap.h149
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/lapd_core.h12
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/lapdm.h57
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/meas_rep.h30
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/mncc.h5
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/oap.h72
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/prim.h5
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_03_40.h32
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_03_41.h78
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08.h309
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08_gprs.h431
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_11.h7
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_12.h8
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_80.h5
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_08.h123
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_58.h64
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_09_02.h137
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_12_21.h58
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_23_003.h23
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_44_318.h4
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/ipaccess.h8
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/smpp34_osmocom.h45
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/rsl.h20
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/rxlev_stat.h5
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/sysinfo.h39
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/tlv.h47
-rw-r--r--src/shared/libosmocore/include/osmocom/sim/class_tables.h42
-rw-r--r--src/shared/libosmocore/include/osmocom/sim/sim.h382
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/buffer.h11
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/command.h33
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/logging.h5
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/misc.h23
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/ports.h26
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/stats.h3
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/telnet_interface.h5
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/vector.h9
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/vty.h28
-rw-r--r--src/shared/libosmocore/libosmocore.pc.in4
-rw-r--r--src/shared/libosmocore/libosmoctrl.pc.in11
-rw-r--r--src/shared/libosmocore/libosmogb.pc.in2
-rw-r--r--src/shared/libosmocore/libosmogsm.pc.in2
-rw-r--r--src/shared/libosmocore/libosmosim.pc.in11
-rw-r--r--src/shared/libosmocore/libosmovty.pc.in2
-rw-r--r--src/shared/libosmocore/src/Makefile.am31
-rw-r--r--src/shared/libosmocore/src/application.c15
-rw-r--r--src/shared/libosmocore/src/backtrace.c2
-rw-r--r--src/shared/libosmocore/src/bitcomp.c354
-rw-r--r--src/shared/libosmocore/src/bits.c129
-rw-r--r--src/shared/libosmocore/src/bitvec.c465
-rw-r--r--src/shared/libosmocore/src/codec/Makefile.am8
-rw-r--r--src/shared/libosmocore/src/codec/gsm610.c39
-rw-r--r--src/shared/libosmocore/src/codec/gsm620.c114
-rw-r--r--src/shared/libosmocore/src/codec/gsm690.c106
-rw-r--r--src/shared/libosmocore/src/conv.c2
-rw-r--r--src/shared/libosmocore/src/crc16.c56
-rw-r--r--src/shared/libosmocore/src/crcXXgen.c.tpl6
-rw-r--r--src/shared/libosmocore/src/ctrl/Makefile.am20
-rw-r--r--src/shared/libosmocore/src/ctrl/control_cmd.c550
-rw-r--r--src/shared/libosmocore/src/ctrl/control_if.c703
-rw-r--r--src/shared/libosmocore/src/ctrl/control_vty.c88
-rw-r--r--src/shared/libosmocore/src/fsm.c519
-rw-r--r--src/shared/libosmocore/src/gb/Makefile.am8
-rw-r--r--src/shared/libosmocore/src/gb/common_vty.c8
-rw-r--r--src/shared/libosmocore/src/gb/gprs_bssgp.c260
-rw-r--r--src/shared/libosmocore/src/gb/gprs_bssgp_bss.c35
-rw-r--r--src/shared/libosmocore/src/gb/gprs_bssgp_util.c106
-rw-r--r--src/shared/libosmocore/src/gb/gprs_bssgp_vty.c48
-rw-r--r--src/shared/libosmocore/src/gb/gprs_ns.c808
-rw-r--r--src/shared/libosmocore/src/gb/gprs_ns_frgre.c14
-rw-r--r--src/shared/libosmocore/src/gb/gprs_ns_vty.c56
-rw-r--r--src/shared/libosmocore/src/gb/libosmogb.map7
-rw-r--r--src/shared/libosmocore/src/gsm/Makefile.am37
-rw-r--r--src/shared/libosmocore/src/gsm/a5.c140
-rw-r--r--src/shared/libosmocore/src/gsm/abis_nm.c181
-rw-r--r--src/shared/libosmocore/src/gsm/apn.c113
-rw-r--r--src/shared/libosmocore/src/gsm/auth_comp128v1.c2
-rw-r--r--src/shared/libosmocore/src/gsm/auth_comp128v23.c68
-rw-r--r--src/shared/libosmocore/src/gsm/auth_core.c52
-rw-r--r--src/shared/libosmocore/src/gsm/auth_milenage.c35
-rw-r--r--src/shared/libosmocore/src/gsm/comp128.c7
-rw-r--r--src/shared/libosmocore/src/gsm/comp128v23.c157
-rw-r--r--src/shared/libosmocore/src/gsm/gan.c10
-rw-r--r--src/shared/libosmocore/src/gsm/gea.c60
-rw-r--r--src/shared/libosmocore/src/gsm/gprs_cipher_core.c34
-rw-r--r--src/shared/libosmocore/src/gsm/gprs_gea.c48
-rw-r--r--src/shared/libosmocore/src/gsm/gprs_rlc.c106
-rw-r--r--src/shared/libosmocore/src/gsm/gsm0341.c60
-rw-r--r--src/shared/libosmocore/src/gsm/gsm0411_smc.c107
-rw-r--r--src/shared/libosmocore/src/gsm/gsm0411_smr.c121
-rw-r--r--src/shared/libosmocore/src/gsm/gsm0411_utils.c54
-rw-r--r--src/shared/libosmocore/src/gsm/gsm0480.c178
-rw-r--r--src/shared/libosmocore/src/gsm/gsm0808.c176
-rw-r--r--src/shared/libosmocore/src/gsm/gsm48.c283
-rw-r--r--src/shared/libosmocore/src/gsm/gsm48_ie.c8
-rw-r--r--src/shared/libosmocore/src/gsm/gsm_04_08_gprs.c212
-rw-r--r--src/shared/libosmocore/src/gsm/gsm_utils.c336
-rw-r--r--src/shared/libosmocore/src/gsm/gsup.c509
-rw-r--r--src/shared/libosmocore/src/gsm/ipa.c458
-rw-r--r--src/shared/libosmocore/src/gsm/kasumi.c186
-rw-r--r--src/shared/libosmocore/src/gsm/lapd_core.c136
-rw-r--r--src/shared/libosmocore/src/gsm/lapdm.c169
-rw-r--r--src/shared/libosmocore/src/gsm/libosmogsm.map138
-rw-r--r--src/shared/libosmocore/src/gsm/milenage/aes.h5
-rw-r--r--src/shared/libosmocore/src/gsm/milenage/aes_i.h7
-rw-r--r--src/shared/libosmocore/src/gsm/milenage/aes_wrap.h5
-rw-r--r--src/shared/libosmocore/src/gsm/milenage/milenage.h5
-rw-r--r--src/shared/libosmocore/src/gsm/oap.c184
-rw-r--r--src/shared/libosmocore/src/gsm/rsl.c52
-rw-r--r--src/shared/libosmocore/src/gsm/sysinfo.c74
-rw-r--r--src/shared/libosmocore/src/gsm/tlv_parser.c256
-rw-r--r--src/shared/libosmocore/src/gsmtap_util.c61
-rw-r--r--src/shared/libosmocore/src/logging.c298
-rw-r--r--src/shared/libosmocore/src/logging_gsmtap.c134
-rw-r--r--src/shared/libosmocore/src/loggingrb.c98
-rw-r--r--src/shared/libosmocore/src/macaddr.c139
-rw-r--r--src/shared/libosmocore/src/msgb.c211
-rw-r--r--src/shared/libosmocore/src/msgfile.c3
-rw-r--r--src/shared/libosmocore/src/panic.c26
-rw-r--r--src/shared/libosmocore/src/plugin.c9
-rw-r--r--src/shared/libosmocore/src/prim.c12
-rw-r--r--src/shared/libosmocore/src/rate_ctr.c65
-rw-r--r--src/shared/libosmocore/src/rbtree.c3
-rw-r--r--src/shared/libosmocore/src/select.c104
-rw-r--r--src/shared/libosmocore/src/serial.c30
-rw-r--r--src/shared/libosmocore/src/signal.c1
-rw-r--r--src/shared/libosmocore/src/sim/Makefile.am26
-rw-r--r--src/shared/libosmocore/src/sim/card_fs_isim.c105
-rw-r--r--src/shared/libosmocore/src/sim/card_fs_sim.c477
-rw-r--r--src/shared/libosmocore/src/sim/card_fs_tetra.c267
-rw-r--r--src/shared/libosmocore/src/sim/card_fs_uicc.c208
-rw-r--r--src/shared/libosmocore/src/sim/card_fs_usim.c380
-rw-r--r--src/shared/libosmocore/src/sim/class_tables.c313
-rw-r--r--src/shared/libosmocore/src/sim/core.c339
-rw-r--r--src/shared/libosmocore/src/sim/gsm_int.h17
-rw-r--r--src/shared/libosmocore/src/sim/reader.c274
-rw-r--r--src/shared/libosmocore/src/sim/reader_pcsc.c159
-rw-r--r--src/shared/libosmocore/src/sim/sim_int.h34
-rw-r--r--src/shared/libosmocore/src/socket.c193
-rw-r--r--src/shared/libosmocore/src/stat_item.c268
-rw-r--r--src/shared/libosmocore/src/statistics.c8
-rw-r--r--src/shared/libosmocore/src/stats.c612
-rw-r--r--src/shared/libosmocore/src/stats_statsd.c169
-rw-r--r--src/shared/libosmocore/src/strrb.c171
-rw-r--r--src/shared/libosmocore/src/talloc.c1804
-rw-r--r--src/shared/libosmocore/src/timer.c40
-rw-r--r--src/shared/libosmocore/src/timer_gettimeofday.c58
-rw-r--r--src/shared/libosmocore/src/utils.c166
-rw-r--r--src/shared/libosmocore/src/vty/Makefile.am13
-rw-r--r--src/shared/libosmocore/src/vty/buffer.c8
-rw-r--r--src/shared/libosmocore/src/vty/command.c739
-rw-r--r--src/shared/libosmocore/src/vty/fsm_vty.c177
-rw-r--r--src/shared/libosmocore/src/vty/logging_vty.c191
-rw-r--r--src/shared/libosmocore/src/vty/stats_vty.c601
-rw-r--r--src/shared/libosmocore/src/vty/telnet_interface.c33
-rw-r--r--src/shared/libosmocore/src/vty/utils.c159
-rw-r--r--src/shared/libosmocore/src/vty/vector.c6
-rw-r--r--src/shared/libosmocore/src/vty/vty.c47
-rw-r--r--src/shared/libosmocore/src/write_queue.c36
-rw-r--r--src/shared/libosmocore/tests/Makefile.am114
-rw-r--r--src/shared/libosmocore/tests/a5/a5_test.c71
-rw-r--r--src/shared/libosmocore/tests/a5/a5_test.ok24
-rw-r--r--src/shared/libosmocore/tests/atlocal.in1
-rw-r--r--src/shared/libosmocore/tests/auth/milenage_test.c7
-rw-r--r--src/shared/libosmocore/tests/auth/milenage_test.ok1
-rw-r--r--src/shared/libosmocore/tests/bits/bitcomp_test.c54
-rw-r--r--src/shared/libosmocore/tests/bits/bitcomp_test.ok19
-rw-r--r--src/shared/libosmocore/tests/bits/bitrev_test.c274
-rw-r--r--src/shared/libosmocore/tests/bits/bitrev_test.ok134
-rw-r--r--src/shared/libosmocore/tests/bitvec/bitvec_test.c291
-rw-r--r--src/shared/libosmocore/tests/bitvec/bitvec_test.ok171
-rw-r--r--src/shared/libosmocore/tests/codec/codec_test.c120
-rw-r--r--src/shared/libosmocore/tests/codec/codec_test.ok15
-rw-r--r--src/shared/libosmocore/tests/comp128/comp128_test.c2125
-rw-r--r--src/shared/libosmocore/tests/comp128/comp128_test.ok2050
-rw-r--r--src/shared/libosmocore/tests/conv/conv_test.c307
-rw-r--r--src/shared/libosmocore/tests/fr/fr_test.c79
-rw-r--r--src/shared/libosmocore/tests/fr/fr_test.err (renamed from src/shared/libosmocore/debian/docs)0
-rw-r--r--src/shared/libosmocore/tests/fr/fr_test.ok1
-rw-r--r--src/shared/libosmocore/tests/fsm/fsm_test.c157
-rw-r--r--src/shared/libosmocore/tests/fsm/fsm_test.err11
-rw-r--r--src/shared/libosmocore/tests/fsm/fsm_test.ok0
-rw-r--r--src/shared/libosmocore/tests/gb/bssgp_fc_test.c19
-rw-r--r--src/shared/libosmocore/tests/gb/bssgp_fc_tests.err30
-rw-r--r--src/shared/libosmocore/tests/gb/gprs_bssgp_test.c318
-rw-r--r--src/shared/libosmocore/tests/gb/gprs_bssgp_test.ok21
-rw-r--r--src/shared/libosmocore/tests/gb/gprs_ns_test.c971
-rw-r--r--src/shared/libosmocore/tests/gb/gprs_ns_test.ok1024
-rw-r--r--src/shared/libosmocore/tests/gea/gea_test.c68
-rw-r--r--src/shared/libosmocore/tests/gea/gea_test.ok15
-rw-r--r--src/shared/libosmocore/tests/gprs/gprs_test.c122
-rw-r--r--src/shared/libosmocore/tests/gprs/gprs_test.ok1
-rw-r--r--src/shared/libosmocore/tests/gsm0408/gsm0408_test.c24
-rw-r--r--src/shared/libosmocore/tests/gsm0408/gsm0408_test.ok1
-rw-r--r--src/shared/libosmocore/tests/gsm0808/gsm0808_test.c2
-rw-r--r--src/shared/libosmocore/tests/gsup/gsup_test.c259
-rw-r--r--src/shared/libosmocore/tests/gsup/gsup_test.ok14
-rw-r--r--src/shared/libosmocore/tests/kasumi/kasumi_test.c136
-rw-r--r--src/shared/libosmocore/tests/kasumi/kasumi_test.ok10
-rw-r--r--src/shared/libosmocore/tests/lapd/lapd_test.c541
-rw-r--r--src/shared/libosmocore/tests/lapd/lapd_test.ok81
-rw-r--r--src/shared/libosmocore/tests/logging/logging_test.c55
-rw-r--r--src/shared/libosmocore/tests/logging/logging_test.err12
-rw-r--r--src/shared/libosmocore/tests/loggingrb/logging_test.err3
-rw-r--r--src/shared/libosmocore/tests/loggingrb/logging_test.ok0
-rw-r--r--src/shared/libosmocore/tests/loggingrb/loggingrb_test.c81
-rw-r--r--src/shared/libosmocore/tests/msgb/msgb_test.c294
-rw-r--r--src/shared/libosmocore/tests/msgb/msgb_test.ok35
-rw-r--r--src/shared/libosmocore/tests/msgfile/msgfile_test.c2
-rw-r--r--src/shared/libosmocore/tests/oap/Makefile.am37
-rw-r--r--src/shared/libosmocore/tests/oap/oap_test.c182
-rw-r--r--src/shared/libosmocore/tests/oap/oap_test.ok42
-rw-r--r--src/shared/libosmocore/tests/sim/sim_test.c58
-rw-r--r--src/shared/libosmocore/tests/sim/sim_test.ok6
-rw-r--r--src/shared/libosmocore/tests/sms/sms_test.c215
-rw-r--r--src/shared/libosmocore/tests/sms/sms_test.ok27
-rw-r--r--src/shared/libosmocore/tests/smscb/gsm0341_test.c73
-rw-r--r--src/shared/libosmocore/tests/smscb/smscb_test.c1
-rw-r--r--src/shared/libosmocore/tests/stats/stats_test.c462
-rw-r--r--src/shared/libosmocore/tests/stats/stats_test.ok108
-rw-r--r--src/shared/libosmocore/tests/strrb/strrb_test.c225
-rw-r--r--src/shared/libosmocore/tests/strrb/strrb_test.ok1
-rw-r--r--src/shared/libosmocore/tests/testsuite.at183
-rw-r--r--src/shared/libosmocore/tests/timer/timer_test.c106
-rw-r--r--src/shared/libosmocore/tests/timer/timer_test.ok372
-rw-r--r--src/shared/libosmocore/tests/tlv/tlv_test.c250
-rw-r--r--src/shared/libosmocore/tests/tlv/tlv_test.ok2
-rw-r--r--src/shared/libosmocore/tests/ussd/ussd_test.c91
-rw-r--r--src/shared/libosmocore/tests/ussd/ussd_test.ok33
-rw-r--r--src/shared/libosmocore/tests/utils/utils_test.c108
-rw-r--r--src/shared/libosmocore/tests/utils/utils_test.ok5
-rw-r--r--src/shared/libosmocore/tests/vty/vty_test.c334
-rw-r--r--src/shared/libosmocore/tests/vty/vty_test.ok113
-rw-r--r--src/shared/libosmocore/tests/write_queue/wqueue_test.c81
-rw-r--r--src/shared/libosmocore/tests/write_queue/wqueue_test.ok1
-rw-r--r--src/shared/libosmocore/utils/Makefile.am13
-rw-r--r--src/shared/libosmocore/utils/conv_codes_gsm.py706
-rw-r--r--src/shared/libosmocore/utils/conv_gen.py290
-rw-r--r--src/shared/libosmocore/utils/osmo-arfcn.c51
-rw-r--r--src/shared/libosmocore/utils/osmo-auc-gen.c32
-rw-r--r--src/shared/libosmocore/utils/osmo-sim-test.c420
362 files changed, 36607 insertions, 4679 deletions
diff --git a/src/shared/libosmocore/.gitignore b/src/shared/libosmocore/.gitignore
index ac19b238..fb159c39 100644
--- a/src/shared/libosmocore/.gitignore
+++ b/src/shared/libosmocore/.gitignore
@@ -7,17 +7,20 @@ Makefile.in
*.lo
*.la
*.pc
+*.pyc
aclocal.m4
acinclude.m4
aminclude.am
m4/*.m4
autom4te.cache
+config.cache
config.h*
config.sub
config.log
config.status
config.guess
configure
+compile
depcomp
missing
ltmain.sh
@@ -26,6 +29,9 @@ stamp-h1
libtool
libosmocore-*.tar.*
+# libtool for different platforms like arm-poky-linux-gnueabi-libtool
+*-libtool
+
Doxyfile.core
Doxyfile.gsm
Doxyfile.vty
@@ -54,6 +60,10 @@ tests/testsuite
tests/testsuite.dir/
tests/testsuite.log
+tests/utils/utils_test
+tests/stats/stats_test
+tests/kasumi/kasumi_test
+tests/gea/gea_test
tests/sms/sms_test
tests/timer/timer_test
tests/msgfile/msgfile_test
@@ -61,27 +71,51 @@ tests/ussd/ussd_test
tests/smscb/smscb_test
tests/bits/bitrev_test
tests/a5/a5_test
+tests/comp128/comp128_test
tests/auth/milenage_test
tests/conv/conv_test
tests/lapd/lapd_test
tests/gsm0808/gsm0808_test
tests/gb/bssgp_fc_test
+tests/gb/gprs_ns_test
tests/gsm0408/gsm0408_test
tests/logging/logging_test
+tests/fr/fr_test
+tests/loggingrb/loggingrb_test
+tests/ringbuf/ringbuf_test
+tests/strrb/strrb_test
+tests/vty/vty_test
+tests/codec/codec_test
+tests/gb/gprs_bssgp_test
+tests/smscb/gsm0341_test
+tests/bitvec/bitvec_test
+tests/bits/bitcomp_test
+tests/gprs/gprs_test
+tests/msgb/msgb_test
+tests/sim/sim_test
+tests/gsup/gsup_test
+tests/tlv/tlv_test
+tests/fsm/fsm_test
+tests/write_queue/wqueue_test
+tests/oap/oap_test
utils/osmo-arfcn
utils/osmo-auc-gen
+utils/osmo-sim-test
doc/codec
doc/core
doc/vty/latex
doc/vty/html
+doc/vty/doxygen_sqlite3.db
doc/gsm
doc/html.tar
+doc/*.tag
src/crc*gen.c
+src/gsm/gsm0503_conv.c
include/osmocom/core/crc*gen.h
-
+include/osmocom/core/bit*gen.h
# vi files
*.sw?
diff --git a/src/shared/libosmocore/.gitreview b/src/shared/libosmocore/.gitreview
new file mode 100644
index 00000000..e0ebd90d
--- /dev/null
+++ b/src/shared/libosmocore/.gitreview
@@ -0,0 +1,3 @@
+[gerrit]
+host=gerrit.osmocom.org
+project=libosmocore
diff --git a/src/shared/libosmocore/.mailmap b/src/shared/libosmocore/.mailmap
new file mode 100644
index 00000000..cda40579
--- /dev/null
+++ b/src/shared/libosmocore/.mailmap
@@ -0,0 +1,12 @@
+Harald Welte <laforge@gnumonks.org>
+Harald Welte <laforge@gnumonks.org> <laflocal@hanuman.gnumonks.org>
+Harald Welte <laforge@gnumonks.org> <laflocal@goeller.de.gnumonks.org>
+Holger Hans Peter Freyther <holger@moiji-mobile.com> <zecke@selfish.org>
+Holger Hans Peter Freyther <holger@moiji-mobile.com> <ich@tamarin.(none)>
+Holger Hans Peter Freyther <holgre@moiji-mobile.com> <holger@freyther.de>
+Andreas Eversberg <jolly@eversberg.eu>
+Andreas Eversberg <jolly@eversberg.eu> <Andreas.Eversberg@versatel.de>
+Andreas Eversberg <jolly@eversberg.eu> <root@nuedel.(none)>
+Pablo Neira Ayuso <pablo@soleta.eu> <pablo@gnumonks.org>
+Max Suraev <msuraev@sysmocom.de>
+Tom Tsou <tom.tsou@ettus.com> <tom@tsou.cc>
diff --git a/src/shared/libosmocore/Doxyfile.codec.in b/src/shared/libosmocore/Doxyfile.codec.in
index fcd5122e..84353791 100644
--- a/src/shared/libosmocore/Doxyfile.codec.in
+++ b/src/shared/libosmocore/Doxyfile.codec.in
@@ -610,7 +610,7 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
-INPUT = include/osmocom/codec src/codec
+INPUT = @srcdir@/include/osmocom/codec @srcdir@/src/codec
# 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
@@ -688,7 +688,7 @@ EXAMPLE_RECURSIVE = NO
# directories that contain image that are included in the documentation (see
# the \image command).
-IMAGE_PATH = images/
+# 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
@@ -1141,7 +1141,7 @@ MATHJAX_RELPATH = http://www.mathjax.org/mathjax
# 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 = NO
+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
@@ -1406,7 +1406,7 @@ PERLMOD_MAKEVAR_PREFIX =
# evaluate all C-preprocessor directives found in the sources and include
# files.
-ENABLE_PREPROCESSING = NO
+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
@@ -1485,12 +1485,12 @@ 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 =
+TAGFILES = doc/libosmocore.tag=../../core/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 =
+GENERATE_TAGFILE = doc/libosmocodec.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
diff --git a/src/shared/libosmocore/Doxyfile.core.in b/src/shared/libosmocore/Doxyfile.core.in
index 18eb2265..58d35bed 100644
--- a/src/shared/libosmocore/Doxyfile.core.in
+++ b/src/shared/libosmocore/Doxyfile.core.in
@@ -610,7 +610,7 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
-INPUT = include/osmocom/core src
+INPUT = @srcdir@/include/osmocom/core @srcdir@/src
# 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
@@ -688,7 +688,7 @@ EXAMPLE_RECURSIVE = NO
# directories that contain image that are included in the documentation (see
# the \image command).
-IMAGE_PATH = images/
+# 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
@@ -1141,7 +1141,7 @@ MATHJAX_RELPATH = http://www.mathjax.org/mathjax
# 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 = NO
+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
@@ -1406,7 +1406,7 @@ PERLMOD_MAKEVAR_PREFIX =
# evaluate all C-preprocessor directives found in the sources and include
# files.
-ENABLE_PREPROCESSING = NO
+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
@@ -1490,7 +1490,7 @@ TAGFILES =
# 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 =
+GENERATE_TAGFILE = doc/libosmocore.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
diff --git a/src/shared/libosmocore/Doxyfile.gsm.in b/src/shared/libosmocore/Doxyfile.gsm.in
index ab25b220..ed4ff47d 100644
--- a/src/shared/libosmocore/Doxyfile.gsm.in
+++ b/src/shared/libosmocore/Doxyfile.gsm.in
@@ -610,7 +610,7 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
-INPUT = include/osmocom/gsm include/osmocom/gsm/protocol src/gsm
+INPUT = @srcdir@/include/osmocom/gsm @srcdir@/include/osmocom/gsm/protocol @srcdir@/src/gsm
# 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
@@ -688,7 +688,7 @@ EXAMPLE_RECURSIVE = NO
# directories that contain image that are included in the documentation (see
# the \image command).
-IMAGE_PATH = images/
+# 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
@@ -1141,7 +1141,7 @@ MATHJAX_RELPATH = http://www.mathjax.org/mathjax
# 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 = NO
+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
@@ -1406,7 +1406,7 @@ PERLMOD_MAKEVAR_PREFIX =
# evaluate all C-preprocessor directives found in the sources and include
# files.
-ENABLE_PREPROCESSING = NO
+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
@@ -1485,12 +1485,12 @@ 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 =
+TAGFILES = doc/libosmocore.tag=../../core/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 =
+GENERATE_TAGFILE = doc/libosmogsm.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
diff --git a/src/shared/libosmocore/Doxyfile.vty.in b/src/shared/libosmocore/Doxyfile.vty.in
index 57f19ad8..073ee0cc 100644
--- a/src/shared/libosmocore/Doxyfile.vty.in
+++ b/src/shared/libosmocore/Doxyfile.vty.in
@@ -610,7 +610,7 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
-INPUT = include/osmocom/vty src/vty
+INPUT = @srcdir@/include/osmocom/vty @srcdir@/src/vty
# 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
@@ -688,7 +688,7 @@ EXAMPLE_RECURSIVE = NO
# directories that contain image that are included in the documentation (see
# the \image command).
-IMAGE_PATH = images/
+# 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
@@ -1141,7 +1141,7 @@ MATHJAX_RELPATH = http://www.mathjax.org/mathjax
# 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 = NO
+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
@@ -1406,7 +1406,7 @@ PERLMOD_MAKEVAR_PREFIX =
# evaluate all C-preprocessor directives found in the sources and include
# files.
-ENABLE_PREPROCESSING = NO
+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
@@ -1485,12 +1485,12 @@ 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 =
+TAGFILES = doc/libosmocore.tag=../../core/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 =
+GENERATE_TAGFILE = doc/libosmovty.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
diff --git a/src/shared/libosmocore/Makefile.am b/src/shared/libosmocore/Makefile.am
index c9b7ccd9..9c901ea5 100644
--- a/src/shared/libosmocore/Makefile.am
+++ b/src/shared/libosmocore/Makefile.am
@@ -1,11 +1,11 @@
ACLOCAL_AMFLAGS = -I m4
-INCLUDES = $(all_includes) -I$(top_srcdir)/include
-SUBDIRS = include src src/vty src/codec src/gsm src/gb tests utils
+AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
+SUBDIRS = include src src/vty src/codec src/gsm src/gb src/ctrl src/sim tests utils
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libosmocore.pc libosmocodec.pc libosmovty.pc libosmogsm.pc \
- libosmogb.pc
+ libosmogb.pc libosmoctrl.pc
BUILT_SOURCES = $(top_srcdir)/.version
$(top_srcdir)/.version:
@@ -13,7 +13,7 @@ $(top_srcdir)/.version:
dist-hook:
echo $(VERSION) > $(distdir)/.tarball-version
-EXTRA_DIST = git-version-gen
+EXTRA_DIST = git-version-gen .version doc/osmocom-authn-protocol.txt
if HAVE_DOXYGEN
@@ -50,7 +50,7 @@ install-data-hook:
uninstall-hook:
cd $(DESTDIR)$(htmldir) && rm -rf {core,gsm,vty,codec}
-DX_CLEAN = doc/{core,gsm,vty,codec}/{html,latex}/* doc/html.tar
+DX_CLEAN = doc/{core,gsm,vty,codec}/html/search/* doc/{core,gsm,vty,codec}/{html,latex}/* doc/html.tar doc/{core,gsm,vty,codec}/doxygen_sqlite3.db doc/*.tag
endif
MOSTLYCLEANFILES = $(DX_CLEAN)
diff --git a/src/shared/libosmocore/TODO-RELEASE b/src/shared/libosmocore/TODO-RELEASE
new file mode 100644
index 00000000..fb0bfeab
--- /dev/null
+++ b/src/shared/libosmocore/TODO-RELEASE
@@ -0,0 +1,9 @@
+# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
+# according to https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
+# In short:
+# LIBVERSION=c:r:a
+# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
+# If any interfaces have been added, removed, or changed since the last update: c + 1:0:0.
+# 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
diff --git a/src/shared/libosmocore/configure.ac b/src/shared/libosmocore/configure.ac
index 24ddd0c7..ec03c264 100644
--- a/src/shared/libosmocore/configure.ac
+++ b/src/shared/libosmocore/configure.ac
@@ -2,7 +2,10 @@ AC_INIT([libosmocore],
m4_esyscmd([./git-version-gen .tarball-version]),
[openbsc@lists.osmocom.org])
-AM_INIT_AUTOMAKE([foreign dist-bzip2 no-dist-gzip 1.6])
+dnl *This* is the root dir, even if an install-sh exists in ../ or ../../
+AC_CONFIG_AUX_DIR([.])
+
+AM_INIT_AUTOMAKE([foreign dist-bzip2 no-dist-gzip 1.6 subdir-objects])
AC_CONFIG_TESTDIR(tests)
dnl kernel style compile messages
@@ -13,10 +16,25 @@ AC_PROG_MAKE_SET
AC_PROG_MKDIR_P
AC_PROG_CC
AC_PROG_INSTALL
-LT_INIT([pic-only])
+LT_INIT([pic-only disable-static])
AC_CONFIG_MACRO_DIR([m4])
+dnl check for pkg-config
+dnl * If pkg-config is missing, we get a "syntax error" for PKG_CHECK_MODULES.
+dnl Instead, we want to say that pkg-config and pkg.m4 are missing.
+dnl * The proper way is PKG_PROG_PKG_CONFIG() but unfortunately that does not
+dnl produce an intelligible error message if pkg-config is missing entirely
+dnl ("syntax error near unexpected token `0.20'").
+dnl * To produce a hint that pkg-config is missing, check for the pkg-config
+dnl binary; but AC_PATH_PROG breaks if the distribution provides only
+dnl prefixed (<arch>-pkg-config) versions, so just print a warning.
+AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
+if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
+ AC_MSG_WARN([You need to install pkg-config])
+fi
+PKG_PROG_PKG_CONFIG([0.20])
+
dnl check os: some linker flags not available on osx
case $host in
*-darwin*)
@@ -31,11 +49,14 @@ AC_SUBST(LTLDFLAGS_OSMOGSM)
dnl checks for header files
AC_HEADER_STDC
-AC_CHECK_HEADERS(execinfo.h sys/select.h sys/socket.h syslog.h ctype.h)
+AC_CHECK_HEADERS(execinfo.h sys/select.h sys/socket.h syslog.h ctype.h netinet/tcp.h)
# for src/conv.c
AC_FUNC_ALLOCA
AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
AC_SUBST(LIBRARY_DL)
+# for src/backtrace.c
+AC_CHECK_LIB(execinfo, backtrace, BACKTRACE_LIB=-lexecinfo, BACKTRACE_LIB=)
+AC_SUBST(BACKTRACE_LIB)
AC_PATH_PROG(DOXYGEN,doxygen,false)
AM_CONDITIONAL(HAVE_DOXYGEN, test $DOXYGEN != false)
@@ -77,15 +98,22 @@ AC_DEFUN([CHECK_TM_INCLUDES_TM_GMTOFF], [
CHECK_TM_INCLUDES_TM_GMTOFF
dnl Generate the output
-AM_CONFIG_HEADER(config.h)
+AC_CONFIG_HEADER(config.h)
-AC_ARG_ENABLE(talloc,
- [AS_HELP_STRING(
- [--disable-talloc],
- [Disable building talloc memory allocator]
- )],
- [enable_talloc=$enableval], [enable_talloc="yes"])
-AM_CONDITIONAL(ENABLE_TALLOC, [test x"$enable_talloc" = x"yes"])
+PKG_CHECK_MODULES(TALLOC, [talloc >= 2.0.1])
+
+AC_ARG_ENABLE([pcsc], [AS_HELP_STRING([--disable-pcsc], [Build without PC/SC support])],
+ [
+ ENABLE_PCSC=$enableval
+ ],
+ [
+ ENABLE_PCSC="yes"
+ ])
+AS_IF([test "x$ENABLE_PCSC" = "xyes"], [
+ PKG_CHECK_MODULES(PCSC, libpcsclite)
+])
+AM_CONDITIONAL(ENABLE_PCSC, test "x$ENABLE_PCSC" = "xyes")
+AC_SUBST(ENABLE_PCSC)
AC_ARG_ENABLE(plugin,
[AS_HELP_STRING(
@@ -170,12 +198,22 @@ then
AM_CONDITIONAL(ENABLE_MSGFILE, false)
AM_CONDITIONAL(ENABLE_SERIAL, false)
AM_CONDITIONAL(ENABLE_VTY, false)
- AM_CONDITIONAL(ENABLE_TALLOC, false)
AM_CONDITIONAL(ENABLE_UTILITIES, false)
AM_CONDITIONAL(ENABLE_GB, false)
AC_DEFINE([PANIC_INFLOOP],[1],[Use infinite loop on panic rather than fprintf/abort])
fi
+AC_ARG_ENABLE(sanitize,
+ [AS_HELP_STRING(
+ [--enable-sanitize],
+ [Compile with address sanitizer enabled],
+ )],
+ [sanitize=$enableval], [sanitize="no"])
+if test x"$sanitize" = x"yes"
+then
+ CFLAGS+=" -fsanitize=address -fsanitize=undefined"
+ CPPFLAGS+=" -fsanitize=address -fsanitize=undefined"
+fi
AC_OUTPUT(
libosmocore.pc
@@ -183,13 +221,18 @@ AC_OUTPUT(
libosmovty.pc
libosmogsm.pc
libosmogb.pc
+ libosmoctrl.pc
+ libosmosim.pc
include/Makefile
src/Makefile
src/vty/Makefile
src/codec/Makefile
+ src/sim/Makefile
src/gsm/Makefile
src/gb/Makefile
+ src/ctrl/Makefile
tests/Makefile
+ tests/atlocal
utils/Makefile
Doxyfile.core
Doxyfile.gsm
diff --git a/src/shared/libosmocore/contrib/fsm-to-dot.py b/src/shared/libosmocore/contrib/fsm-to-dot.py
new file mode 100755
index 00000000..3549d1e5
--- /dev/null
+++ b/src/shared/libosmocore/contrib/fsm-to-dot.py
@@ -0,0 +1,710 @@
+#!/usr/bin/env python
+
+__doc__ = '''
+fsm-to-dot: convert FSM definitons to graph images
+
+Usage:
+ ./fsm-to-dot.py ~/openbsc/openbsc/src/libvlr/*.c
+ for f in *.dot ; do dot -Tpng $f > $f.png; done
+ # dot comes from 'apt-get install graphviz'
+
+Looks for osmo_fsm finite state machine definitions and madly parses .c files
+to draw graphs of them. This uses wild regexes that rely on coding style etc..
+No proper C parsing is done here (pycparser sucked, unfortunately).
+'''
+
+import sys, re
+
+def err(msg):
+ sys.stderr.write(msg + '\n')
+
+class listdict(object):
+ def __getattr__(ld, name):
+ if name == 'add':
+ return ld.__getattribute__(name)
+ return ld.__dict__.__getattribute__(name)
+
+ def _have(ld, name):
+ l = ld.__dict__.get(name)
+ if not l:
+ l = []
+ ld.__dict__[name] = l
+ return l
+
+ def add(ld, name, item):
+ l = ld._have(name)
+ l.append(item)
+ return ld
+
+ def add_dict(ld, d):
+ for k,v in d.items():
+ ld.add(k, v)
+
+ def __setitem__(ld, name, val):
+ return ld.__dict__.__setitem__(name, val)
+
+ def __getitem__(ld, name):
+ return ld.__dict__.__getitem__(name)
+
+ def __str__(ld):
+ return ld.__dict__.__str__()
+
+ def __repr__(ld):
+ return ld.__dict__.__repr__()
+
+ def update(ld, other_ld):
+ for name, items in other_ld.items():
+ ld.extend(name, items)
+ return ld
+
+ def extend(ld, name, vals):
+ l = ld._have(name)
+ l.extend(vals)
+ return ld
+
+re_state_start = re.compile(r'\[([A-Z_][A-Z_0-9]*)\]')
+re_event = re.compile(r'S\(([A-Z_][A-Z_0-9]*)\)')
+re_action = re.compile(r'.action *= *([a-z_][a-z_0-9]*)')
+
+def state_starts(line):
+ m = re_state_start.search(line)
+ if m:
+ return m.group(1)
+ return None
+
+def in_event_starts(line):
+ return line.find('in_event_mask') >= 0
+
+def out_state_starts(line):
+ return line.find('out_state_mask') >= 0
+
+def states_or_events(line):
+ return re_event.findall(line)
+
+def parse_action(line):
+ a = re_action.findall(line)
+ if a:
+ return a[0]
+ return None
+
+def _common_prefix(a, b):
+ for l in reversed(range(1,len(a))):
+ aa = a[:l+1]
+ if b.startswith(aa):
+ return aa
+ return ''
+
+def common_prefix(strs):
+ if not strs:
+ return ''
+ p = None
+ for s in strs:
+ if p is None:
+ p = s
+ continue
+ p = _common_prefix(p, s)
+ if not p:
+ return ''
+ return p
+
+KIND_STATE = 'KIND_STATE'
+KIND_FUNC = 'KIND_FUNC'
+KIND_FSM = 'KIND_FSM'
+BOX_SHAPES = {
+ KIND_STATE : None,
+ KIND_FUNC : 'box',
+ KIND_FSM : 'box3d',
+}
+
+class Event:
+ def __init__(event, name):
+ event.name = name
+ event.short_name = name
+
+ def __cmp__(event, other):
+ return cmp(event.name, other.name)
+
+class Edge:
+ def __init__(edge, to_state, event_name=None, style=None, action=None):
+ edge.to_state = to_state
+ edge.style = style
+ edge.events = []
+ edge.actions = []
+ edge.add_event_name(event_name)
+ edge.add_action(action)
+
+ def add_event_name(edge, event_name):
+ if not event_name:
+ return
+ edge.add_event(Event(event_name))
+
+ def add_event(edge, event):
+ if not event:
+ return
+ if event in edge.events:
+ return
+ edge.events.append(event)
+
+ def add_events(edge, events):
+ for event in events:
+ edge.add_event(event)
+
+ def add_action(edge, action):
+ if not action or action in edge.actions:
+ return
+ edge.actions.append(action)
+
+ def add_actions(edge, actions):
+ for action in actions:
+ edge.add_action(action)
+
+ def event_names(edge):
+ return sorted([event.name for event in edge.events])
+
+ def event_labels(edge):
+ return sorted([event.short_name for event in edge.events])
+
+ def action_labels(edge):
+ return sorted([action + '()' for action in edge.actions])
+
+ def has_event_name(edge, event_name):
+ return event_name in edge.event_names()
+
+class State:
+ name = None
+ short_name = None
+ action = None
+ label = None
+ in_event_names = None
+ out_state_names = None
+ out_edges = None
+ kind = None
+
+ def __init__(state):
+ state.in_event_names = []
+ state.out_state_names = []
+ state.out_edges = []
+ state.kind = KIND_STATE
+
+ def add_out_edge(state, edge):
+ for out_edge in state.out_edges:
+ if out_edge.to_state is edge.to_state:
+ if out_edge.style == edge.style:
+ out_edge.add_events(edge.events)
+ out_edge.add_actions(edge.actions)
+ return
+ # sanity
+ if out_edge.to_state.get_label() == edge.to_state.get_label():
+ raise Exception('Two distinct states exist with identical labels.')
+ state.out_edges.append(edge)
+
+ def get_label(state):
+ if state.label:
+ return state.label
+ l = [state.short_name]
+ if state.action:
+ if state.short_name == state.action:
+ l = []
+ l.append(state.action + '()')
+ return r'\n'.join(l)
+
+ def event_names(state):
+ event_names = []
+ for out_edge in state.out_edges:
+ event_names.extend(out_edge.event_names())
+ return event_names
+
+ def shape_str(state):
+ shape = BOX_SHAPES.get(state.kind, None)
+ if not shape:
+ return ''
+ return ',shape=%s' % shape
+
+ def __repr__(state):
+ return 'State(name=%r,short_name=%r,out=%d)' % (state.name, state.short_name, len(state.out_edges))
+
+class Fsm:
+ def __init__(fsm, struct_name, states_struct_name, from_file=None):
+ fsm.states = []
+ fsm.struct_name = struct_name
+ fsm.states_struct_name = states_struct_name
+ fsm.from_file = from_file
+ fsm.action_funcs = set()
+ fsm.event_names = set()
+
+ def parse_states(fsm, src):
+ state = None
+ started = None
+
+ IN_EVENTS = 'events'
+ OUT_STATES = 'states'
+
+ lines = src.splitlines()
+
+ for line in lines:
+ state_name = state_starts(line)
+ if state_name:
+ state = State()
+ fsm.states.append(state)
+ started = None
+ state.name = state_name
+
+ if in_event_starts(line):
+ started = IN_EVENTS
+ if out_state_starts(line):
+ started = OUT_STATES
+
+ if not state or not started:
+ continue
+
+ tokens = states_or_events(line)
+ if started == IN_EVENTS:
+ state.in_event_names.extend(tokens)
+ elif started == OUT_STATES:
+ state.out_state_names.extend(tokens)
+ else:
+ err('ignoring: %r' % tokens)
+
+ a = parse_action(line)
+ if a:
+ state.action = a
+
+
+ for state in fsm.states:
+ if state.action:
+ fsm.action_funcs.add(state.action)
+ if state.in_event_names:
+ fsm.event_names.update(state.in_event_names)
+
+ fsm.make_states_short_names()
+ fsm.ref_out_states()
+
+ def make_states_short_names(fsm):
+ p = common_prefix([s.name for s in fsm.states])
+ for s in fsm.states:
+ s.short_name = s.name[len(p):]
+ return p
+
+ def make_events_short_names(fsm):
+ p = common_prefix(fsm.event_names)
+ for state in fsm.states:
+ for edge in state.out_edges:
+ for event in edge.events:
+ event.short_name = event.name[len(p):]
+
+ def ref_out_states(fsm):
+ for state in fsm.states:
+ for e in [Edge(fsm.find_state_by_name(n, True)) for n in state.out_state_names]:
+ state.add_out_edge(e)
+
+ def find_state_by_name(fsm, name, strict=False):
+ for state in fsm.states:
+ if state.name == name:
+ return state
+ if strict:
+ raise Exception("State not found: %r" % name);
+ return None
+
+ def find_state_by_action(fsm, action):
+ for state in fsm.states:
+ if state.action == action:
+ return state
+ return None
+
+ def add_special_state(fsm, additional_states, name, in_state=None,
+ out_state=None, event_name=None, kind=KIND_FUNC,
+ state_action=None, label=None, edge_action=None):
+ additional_state = None
+ for s in additional_states:
+ if s.short_name == name:
+ additional_state = s
+ break;
+
+ if not additional_state:
+ for s in fsm.states:
+ if s.short_name == name:
+ additional_state = s
+ break;
+
+ if kind == KIND_FUNC and not state_action:
+ state_action = name
+
+ if not additional_state:
+ additional_state = State()
+ additional_state.short_name = name
+ additional_state.action = state_action
+ additional_state.kind = kind
+ additional_state.label = label
+ additional_states.append(additional_state)
+
+ if out_state:
+ additional_state.out_state_names.append(out_state.name)
+ additional_state.add_out_edge(Edge(out_state, event_name, 'dotted',
+ action=edge_action))
+
+ if in_state:
+ in_state.out_state_names.append(additional_state.name)
+ in_state.add_out_edge(Edge(additional_state, event_name, 'dotted',
+ action=edge_action))
+
+
+ def find_event_edges(fsm, c_files):
+ # enrich state transitions between the states with event labels
+ func_to_state_transitions = listdict()
+ for c_file in c_files:
+ func_to_state_transitions.update( c_file.find_state_transitions(fsm.event_names) )
+
+ # edges between explicit states
+ for state in fsm.states:
+ transitions = func_to_state_transitions.get(state.action)
+ if not transitions:
+ continue
+
+ for to_state_name, event_name in transitions:
+ if not event_name:
+ continue
+ for out_edge in state.out_edges:
+ if out_edge.to_state.name == to_state_name:
+ out_edge.add_event_name(event_name)
+
+ additional_states = []
+
+
+ # functions that aren't state actions but still effect state transitions
+ for func_name, transitions in func_to_state_transitions.items():
+ if func_name in fsm.action_funcs:
+ continue
+ for to_state_name, event_name in transitions:
+ to_state = fsm.find_state_by_name(to_state_name)
+ if not to_state:
+ continue
+ fsm.add_special_state(additional_states, func_name, None, to_state, event_name)
+
+
+ event_sources = c_files.find_event_sources(fsm.event_names)
+
+ for state in fsm.states:
+
+ for in_event_name in state.in_event_names:
+ funcs_for_in_event = event_sources.get(in_event_name)
+ if not funcs_for_in_event:
+ continue
+
+ found = False
+ for out_edge in state.out_edges:
+ if out_edge.has_event_name(in_event_name):
+ out_edge.action = r'\n'.join([(f + '()') for f in funcs_for_in_event
+ if f != state.action])
+
+ # if any functions that don't belong to a state trigger events, add
+ # them to the graph as well
+ additional_funcs = [f for f in funcs_for_in_event if f not in fsm.action_funcs]
+ for af in additional_funcs:
+ fsm.add_special_state(additional_states, af, None, state, in_event_name)
+
+ fsm.states.extend(additional_states)
+
+ # do any existing action functions by chance call other action functions?
+ for state in fsm.states:
+ if not state.action:
+ continue
+ callers = c_files.find_callers(state.action)
+ if not callers:
+ continue
+ for other_state in fsm.states:
+ if other_state.action in callers:
+ other_state.add_out_edge(Edge(state, None, 'dotted'))
+
+ def add_fsm_alloc(fsm, c_files):
+
+ allocating_funcs = []
+ for c_file in c_files:
+ allocating_funcs.extend(c_file.fsm_allocators.get(fsm.struct_name, []))
+
+ starting_state = None
+ if fsm.states:
+ # assume the first state starts
+ starting_state = fsm.states[0]
+
+ additional_states = []
+ for func_name in allocating_funcs:
+ fsm.add_special_state(additional_states, func_name, None, starting_state)
+
+ fsm.states.extend(additional_states)
+
+ def add_cross_fsm_links(fsm, fsms, c_files, fsm_meta):
+ for state in fsm.states:
+ if not state.action:
+ continue
+ if state.kind == KIND_FSM:
+ continue
+ callers = c_files.find_callers(state.action)
+
+ if state.kind == KIND_FUNC:
+ callers.append(state.action)
+
+ if not callers:
+ continue
+
+ for caller in callers:
+ for calling_fsm in fsms:
+ if calling_fsm is fsm:
+ continue
+ calling_state = calling_fsm.find_state_by_action(caller)
+ if not calling_state:
+ continue
+ if calling_state.kind == KIND_FSM:
+ continue
+
+ label = None
+ if state.kind == KIND_STATE:
+ label=fsm.struct_name + ': ' + state.short_name
+ edge_action = caller
+ if calling_state.action == edge_action:
+ edge_action = None
+ calling_fsm.add_special_state(calling_fsm.states, fsm.struct_name,
+ calling_state, kind=KIND_FSM, edge_action=edge_action, label=label)
+
+ label = None
+ if calling_state.kind == KIND_STATE:
+ label=calling_fsm.struct_name + ': ' + calling_state.short_name
+ edge_action = caller
+ if state.action == edge_action:
+ edge_action = None
+ fsm.add_special_state(fsm.states, calling_fsm.struct_name, None,
+ state, kind=KIND_FSM, edge_action=edge_action,
+ label=label)
+
+ # meta overview
+ meta_called_fsm = fsm_meta.have_state(fsm.struct_name, KIND_FSM)
+ meta_calling_fsm = fsm_meta.have_state(calling_fsm.struct_name, KIND_FSM)
+ meta_calling_fsm.add_out_edge(Edge(meta_called_fsm))
+
+
+ def have_state(fsm, name, kind=KIND_STATE):
+ state = fsm.find_state_by_name(name)
+ if not state:
+ state = State()
+ state.name = name
+ state.short_name = name
+ state.kind = kind
+ fsm.states.append(state)
+ return state
+
+ def to_dot(fsm):
+ out = ['digraph G {', 'rankdir=LR;']
+
+ for state in fsm.states:
+ out.append('%s [label="%s"%s]' % (state.short_name, state.get_label(),
+ state.shape_str()))
+
+ for state in fsm.states:
+ for out_edge in state.out_edges:
+ attrs = []
+ labels = []
+ if out_edge.events:
+ labels.extend(out_edge.event_labels())
+ if out_edge.actions:
+ labels.extend(out_edge.action_labels())
+ if labels:
+ attrs.append('label="%s"' % (r'\n'.join(labels)))
+ if out_edge.style:
+ attrs.append('style=%s'% out_edge.style)
+ attrs_str = ''
+ if attrs:
+ attrs_str = ' [%s]' % (','.join(attrs))
+ out.append('%s->%s%s' % (state.short_name, out_edge.to_state.short_name, attrs_str))
+
+ out.append('}\n')
+
+ return '\n'.join(out)
+
+ def write_dot_file(fsm):
+ dot_path = '%s.dot' % fsm.struct_name
+ f = open(dot_path, 'w')
+ f.write(fsm.to_dot())
+ f.close()
+ print(dot_path)
+
+
+re_fsm = re.compile(r'struct osmo_fsm ([a-z_][a-z_0-9]*) =')
+re_fsm_states_struct_name = re.compile(r'\bstates = ([a-z_][a-z_0-9]*)\W*,')
+re_fsm_states = re.compile(r'struct osmo_fsm_state ([a-z_][a-z_0-9]*)\[\] =')
+re_func = re.compile(r'(\b[a-z_][a-z_0-9]*\b)\([^)]*\)\W*^{', re.MULTILINE)
+re_state_trigger = re.compile(r'osmo_fsm_inst_state_chg\([^,]+,\W*([A-Z_][A-Z_0-9]*)\W*,', re.M)
+re_fsm_alloc = re.compile(r'osmo_fsm_inst_alloc[_child]*\(\W*&([a-z_][a-z_0-9]*),', re.M)
+re_fsm_event_dispatch = re.compile(r'osmo_fsm_inst_dispatch\(\W*[^,]+,\W*([A-Z_][A-Z_0-9]*)\W*,', re.M)
+
+class CFile():
+ def __init__(c_file, path):
+ c_file.path = path
+ c_file.src = open(path).read()
+ c_file.funcs = {}
+ c_file.fsm_allocators = listdict()
+
+ def extract_block(c_file, brace_open, brace_close, start):
+ pos = 0
+ try:
+ src = c_file.src
+ block_start = src.find(brace_open, start)
+
+ pos = block_start
+ level = 1
+ while level > 0:
+ pos += 1
+ if src[pos] == brace_open:
+ level += 1
+ elif src[pos] == brace_close:
+ level -= 1
+
+ return src[block_start+1:pos]
+ except:
+ print("Error while trying to extract a code block from %r char pos %d" % (c_file.path, pos))
+ print("Block start at char pos %d" % block_start)
+ try:
+ print(src[block_start - 20 : block_start + 20])
+ print('...')
+ print(src[pos - 20 : pos + 20])
+ except:
+ pass
+ return ''
+
+
+ def find_fsms(c_file):
+ fsms = []
+ for m in re_fsm.finditer(c_file.src):
+ struct_name = m.group(1)
+ struct_def = c_file.extract_block('{', '}', m.start())
+ states_struct_name = re_fsm_states_struct_name.findall(struct_def)[0]
+ fsm = Fsm(struct_name, states_struct_name, c_file)
+ fsms.append(fsm)
+ return fsms
+
+ def find_fsm_states(c_file, fsms):
+ for m in re_fsm_states.finditer(c_file.src):
+ states_struct_name = m.group(1)
+ for fsm in fsms:
+ if states_struct_name == fsm.states_struct_name:
+ fsm.parse_states(c_file.extract_block('{', '}', m.start()))
+
+ def parse_functions(c_file):
+ funcs = {}
+ for m in re_func.finditer(c_file.src):
+ name = m.group(1)
+ func_src = c_file.extract_block('{', '}', m.start())
+ funcs[name] = func_src
+ c_file.funcs = funcs
+ c_file.find_fsm_allocators()
+
+ def find_callers(c_file, func_name):
+ func_call = func_name + '('
+ callers = []
+ for func_name, src in c_file.funcs.items():
+ if src.find(func_call) >= 0:
+ callers.append(func_name)
+ return callers
+
+ def find_fsm_allocators(c_file):
+ c_file.fsm_allocators = listdict()
+ for func_name, src in c_file.funcs.items():
+ for m in re_fsm_alloc.finditer(src):
+ fsm_struct_name = m.group(1)
+ c_file.fsm_allocators.add(fsm_struct_name, func_name)
+
+ def find_state_transitions(c_file, event_names):
+ TO_STATE = 'TO_STATE'
+ EVENT = 'EVENT'
+ func_to_state_transitions = listdict()
+
+ for func_name, src in c_file.funcs.items():
+ found_tokens = []
+
+ for m in re_state_trigger.finditer(src):
+ to_state = m.group(1)
+ found_tokens.append((m.start(), TO_STATE, to_state))
+
+ for event in event_names:
+ re_event = re.compile(r'\b(' + event + r')\b')
+ for m in re_event.finditer(src):
+ event = m.group(1)
+ found_tokens.append((m.start(), EVENT, event))
+
+ found_tokens = sorted(found_tokens)
+
+ last_event = None
+ for start, kind, name in found_tokens:
+ if kind == EVENT:
+ last_event = name
+ else:
+ func_to_state_transitions.add(func_name, (name, last_event))
+
+ return func_to_state_transitions
+
+
+ def find_event_sources(c_file, event_names):
+ c_file.event_sources = listdict()
+ for func_name, src in c_file.funcs.items():
+ for m in re_fsm_event_dispatch.finditer(src):
+ event_name = m.group(1)
+ c_file.event_sources.add(event_name, func_name)
+
+class CFiles(list):
+
+ def find_callers(c_files, func_name):
+ callers = []
+ for c_file in c_files:
+ callers.extend(c_file.find_callers(func_name))
+ return callers
+
+ def find_func_to_state_transitions(c_files):
+ func_to_state_transitions = listdict()
+ for c_file in c_files:
+ func_to_state_transitions.update( c_file.find_state_transitions(fsm.event_names) )
+ return func_to_state_transitions
+
+ def find_event_sources(c_files, event_names):
+ event_sources = listdict()
+ for c_file in c_files:
+ for event, sources in c_file.event_sources.items():
+ if event in event_names:
+ event_sources.extend(event, sources)
+ return event_sources
+
+c_files = CFiles()
+paths_seen = set()
+for path in sys.argv[1:]:
+ if path in paths_seen:
+ continue
+ paths_seen.add(path)
+ c_file = CFile(path)
+ c_files.append(c_file)
+
+for c_file in c_files:
+ c_file.parse_functions()
+
+fsms = []
+for c_file in c_files:
+ fsms.extend(c_file.find_fsms())
+
+for c_file in c_files:
+ c_file.find_fsm_states(fsms)
+ c_file.find_event_sources(fsms)
+
+for fsm in fsms:
+ fsm.find_event_edges(c_files)
+ fsm.add_fsm_alloc(c_files)
+
+fsm_meta = Fsm("meta", "meta")
+for fsm in fsms:
+ fsm.add_cross_fsm_links(fsms, c_files, fsm_meta)
+
+for fsm in fsms:
+ fsm.make_events_short_names()
+
+for fsm in fsms:
+ fsm.write_dot_file()
+
+fsm_meta.write_dot_file()
+
+
+# vim: tabstop=2 shiftwidth=2 expandtab
diff --git a/src/shared/libosmocore/contrib/jenkins.sh b/src/shared/libosmocore/contrib/jenkins.sh
new file mode 100755
index 00000000..108a73ab
--- /dev/null
+++ b/src/shared/libosmocore/contrib/jenkins.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+
+set -ex
+
+autoreconf --install --force
+./configure --enable-static --enable-sanitize
+$MAKE $PARALLEL_MAKE check \
+ || cat-testlogs.sh
+$MAKE distcheck \
+ || cat-testlogs.sh
diff --git a/src/shared/libosmocore/debian/changelog b/src/shared/libosmocore/debian/changelog
index c2b221b1..a5ca3d60 100644
--- a/src/shared/libosmocore/debian/changelog
+++ b/src/shared/libosmocore/debian/changelog
@@ -1,3 +1,131 @@
+libosmocore (0.9.6) unstable; urgency=medium
+
+ * doc: osmocom-authn-protocol.txt: fix numbering, mark unused sync msgs
+ * New upstream release of libosmocore.
+
+ -- Max <msuraev@sysmocom.de> Tue, 13 Dec 2016 18:23:56 +0100
+
+libosmocore (0.9.5) unstable; urgency=medium
+
+ * fix: DLGSUP logging category "unusable"
+ * New upstream release of libosmocore.
+ * ph_data_param struct changed / Extend L1SAP PH-DATA with measurement information
+ * size of ph_tch_param struct changed / Extend with RTP Marker
+ * size of struct log_target changed / Extend with GSMTAP
+
+ -- Max <msuraev@sysmocom.de> Tue, 13 Dec 2016 18:22:56 +0100
+
+libosmocore (0.9.4) unstable; urgency=medium
+
+ * New upstream release of libosmocore.
+ * Internal API for GPRS cipher implementors updated to accommodate for arbitrary key lengths
+ * external talloc dependency / internal talloc removal
+ * size of ph_data_param struct changed / Extend L1SAP PH-DATA with presence information
+
+ -- Max <msuraev@sysmocom.de> Tue, 13 Dec 2016 18:21:56 +0100
+
+libosmocore (0.9.3) unstable; urgency=medium
+
+ * Send all stats values after re-configuration.
+ * Fix using an empty prefix.
+ * Fix sending timing values.
+
+ -- Holger Hans Peter Freyther <holger@moiji-mobile.com> Mon, 09 Nov 2015 21:34:37 +0100
+
+libosmocore (0.9.2) unstable; urgency=medium
+
+ * Fix the counter index after the NS reset.
+
+ -- Holger Hans Peter Freyther <holger@moiji-mobile.com> Wed, 04 Nov 2015 14:47:48 +0100
+
+libosmocore (0.9.1) unstable; urgency=medium
+
+ * Fix rate_ctr attribute for GPRS NS counters.
+
+ -- Holger Hans Peter Freyther <holger@moiji-mobile.com> Wed, 04 Nov 2015 10:23:39 +0100
+
+libosmocore (0.9.0) unstable; urgency=medium
+
+ * StatsD support
+ * Disable building of static libraries
+ * GTP Hub and OAP ports/identifiers assigned
+
+ -- Holger Hans Peter Freyther <holger@moiji-mobile.com> Tue, 03 Nov 2015 09:31:41 +0100
+
+libosmocore (0.8.3) unstable; urgency=medium
+
+ * Prepare new release
+
+ -- Holger Hans Peter Freyther <holger@moiji-mobile.com> Sun, 23 Aug 2015 17:38:55 +0200
+
+libosmocore (0.8.2) unstable; urgency=medium
+
+ * Add endian header
+ * GPRS flow control changes
+ * Add APN format routines to libosmocore.
+
+ -- Holger Hans Peter Freyther <holger@moiji-mobile.com> Sat, 01 Aug 2015 20:18:45 +0200
+
+libosmocore (0.8.0) unstable; urgency=medium
+
+ * New upstream release of libosmocore.
+
+ -- Holger Hans Peter Freyther <holger@freyther.de> Sun, 18 Jan 2015 19:04:10 +0100
+
+libosmocore (0.7.0) unstable; urgency=medium
+
+ * New upstream release of libosmocore.
+
+ -- Harald Welte <laforge@gnumonks.org> Thu, 21 Aug 2014 15:52:00 +0200
+
+libosmocore (0.6.6) UNRELEASED; urgency=medium
+
+ * New upstream release of libosmocore.
+
+ -- Holger Hans Peter Freyther <holger@freyther.de> Mon, 31 Mar 2014 15:37:33 +0200
+
+libosmocore (0.6.5) unstable; urgency=medium
+
+ * New upstream release of libosmocore
+
+ -- Holger Hans Peter Freyther <holger@freyther.de> Mon, 20 Jan 2014 10:37:23 +0100
+
+libosmocore (0.6.4+git3) unstable; urgency=low
+
+ * GPRS fix NS connections to a SGSN when configured via VTY
+
+ -- Jacob Erlbeck <jerlbeck@sysmocom.de> Thu, 07 Nov 2013 16:07:20 +0100
+
+libosmocore (0.6.4+git2) unstable; urgency=low
+
+ * GPRS related changes, some GSM encoding/decoding changes
+ * GPRS correctly determine routable NSVC.
+ * Update with GPRS changes
+
+ -- Holger Hans Peter Freyther <holger@freyther.de> Thu, 10 Oct 2013 14:16:37 +0200
+
+libosmocore (0.6.3+git1-1) unstable; urgency=low
+
+ * New upstream release with new primitives, USSD fixes.
+ * Split libosmocore into several package. One lib per package.
+
+ -- Holger Hans Peter Freyther <holger@freyther.de> Fri, 09 Aug 2013 17:47:30 +0200
+
+libosmocore (0.6.2) unstable; urgency=low
+
+ * New upstream release with NS UDP DSCP ABI changes
+
+ -- Holger Hans Peter Freyther <holger@freyther.de> Wed, 26 Jun 2013 08:01:55 +0200
+
+libosmocore (0.5.3+git1-6) unstable; urgency=low
+
+ * Build new package with the new API required for osmo-bts/openbsc
+ * Package the arfcn utility.
+ * Add the SOCK_RAW work-around for glibc.
+ * Re-enable the debug packages thanks to Jan.
+
+ -- Holger Hans Peter Freyther <holger@freyther.de> Fri, 04 Jan 2013 09:54:55 +0100
+
libosmocore (0.5.3+git1-2) unstable; urgency=low
* New upstream version
@@ -30,6 +158,6 @@ libosmocore (0.1.27) natty; urgency=low
libosmocore (0.1.17-1) unstable; urgency=low
- * Initial release (Closes: #nnnn) <nnnn is the bug number of your ITP>
+ * Initial release
-- Harald Welte <laforge@gnumonks.org> Tue, 24 Aug 2010 10:55:04 +0200
diff --git a/src/shared/libosmocore/debian/compat b/src/shared/libosmocore/debian/compat
index 7f8f011e..ec635144 100644
--- a/src/shared/libosmocore/debian/compat
+++ b/src/shared/libosmocore/debian/compat
@@ -1 +1 @@
-7
+9
diff --git a/src/shared/libosmocore/debian/control b/src/shared/libosmocore/debian/control
index cd8398fd..4cdb672e 100644
--- a/src/shared/libosmocore/debian/control
+++ b/src/shared/libosmocore/debian/control
@@ -1,27 +1,275 @@
Source: libosmocore
+Maintainer: Harald Welte <laforge@gnumonks.org>
Section: libs
Priority: optional
-Maintainer: Harald Welte <laforge@gnumonks.org>
-Build-Depends: debhelper (>= 7.0.50~), autotools-dev, autoconf, automake, libtool, dh-autoreconf, libdpkg-perl, git, doxygen
-Standards-Version: 3.8.4
-Homepage: http://bb.osmocom.org/trac/wiki/libosmocore
+Build-Depends: debhelper (>= 9),
+ autotools-dev,
+ autoconf,
+ automake,
+ libtool,
+ dh-autoreconf,
+ libdpkg-perl,
+ git,
+ doxygen,
+ libpcsclite-dev,
+ pkg-config,
+ libtalloc-dev,
+ python (>= 2.7.6)
+Standards-Version: 3.9.8
Vcs-Git: git://git.osmocom.org/libosmocore.git
Vcs-Browser: http://git.osmocom.org/gitweb?p=libosmocore.git;a=summary
+Homepage: https://projects.osmocom.org/projects/libosmocore
Package: libosmocore
Section: libs
Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}
-Description: Open Source MObile COMmunications CORE library
+Multi-Arch: foreign
+Depends: libosmocodec0 (= ${binary:Version}),
+ libosmocore8 (= ${binary:Version}),
+ libosmogb4 (= ${binary:Version}),
+ libosmogsm7 (= ${binary:Version}),
+ libosmovty3 (= ${binary:Version}),
+ libosmoctrl0 (= ${binary:Version}),
+ libosmosim0 (= ${binary:Version}),
+ ${misc:Depends}
+Description: Open Source MObile COMmunications CORE library (metapackage)
+ The libraries provided by this package contain various utility functions.
+ These were originally developed as part of the
+ OpenBSC project but 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.
+
+Package: libosmocodec0
+Section: libs
+Architecture: any
+Multi-Arch: same
+Depends: ${shlibs:Depends},
+ ${misc:Depends}
+Pre-Depends: ${misc:Pre-Depends}
+Description: Osmo codec 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 libosmocodec library in particular contains the implementation of multiple
+ GSM codecs:
+ .
+ - GSM 06.10 - GSM FR Codec
+ - GSM 06.20 - GSM HR Codec
+ - GSM 06.60 - GSM EFR Codec
+ - GSM 06.90 - GSM AMR Codec
+
+Package: libosmocodec-doc
+Architecture: all
+Section: doc
+Depends: ${misc:Depends},
+ libosmocodec0,
+ libjs-jquery
+Description: Documentation for the osmo codec 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 libosmocodec library.
+
+Package: libosmocore8
+Section: libs
+Architecture: any
+Multi-Arch: same
+Depends: ${shlibs:Depends},
+ ${misc:Depends}
+Pre-Depends: ${misc:Pre-Depends}
+Description: 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
+ 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 libosmocore8 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},
+ libosmocore8,
+ libjs-jquery,
+ libosmocodec-doc,
+ libosmogsm-doc,
+ libosmovty-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
+ 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 libosmocore library.
+
+Package: libosmogb4
+Section: libs
+Architecture: any
+Multi-Arch: same
+Depends: ${shlibs:Depends},
+ ${misc:Depends}
+Pre-Depends: ${misc:Pre-Depends}
+Description: Osmo GPRS GB 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 libosmogb library in particular contains a GPRS BSSGP protocol
+ implementation.
+
+Package: libosmogsm7
+Section: libs
+Architecture: any
+Multi-Arch: same
+Depends: ${shlibs:Depends},
+ ${misc:Depends}
+Pre-Depends: ${misc:Pre-Depends}
+Description: Osmo GSM 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 libosmogsm library in particular is a collection of common code used in
+ various GSM related sub-projects inside the Osmocom family of projects. It
+ includes A5/1 and A5/2 ciphers, COMP128v1, a LAPDm implementation, a GSM TLV
+ parser, SMS utility routines as well as protocol definitions for a series of
+ protocols.
+
+Package: libosmogsm-doc
+Architecture: all
+Section: doc
+Depends: ${misc:Depends},
+ libosmogsm7,
+ libjs-jquery
+Description: Documentation for the Osmo GSM 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 libosmogsm library.
+
+Package: libosmovty3
+Section: libs
+Architecture: any
+Multi-Arch: same
+Depends: ${shlibs:Depends},
+ ${misc:Depends}
+Pre-Depends: ${misc:Pre-Depends}
+Description: Osmo VTY 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 libosmovty library in particular implements the interactive command-line
+ on the VTY (Virtual TTY) as well as configuration file parsing.
+
+Package: libosmovty-doc
+Architecture: all
+Section: doc
+Depends: ${misc:Depends},
+ libosmovty3,
+ libjs-jquery
+Description: Documentation for the Osmo VTY 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 libosmovty library.
+
+Package: libosmoctrl0
+Section: libs
+Architecture: any
+Multi-Arch: same
+Depends: ${shlibs:Depends},
+ ${misc:Depends}
+Pre-Depends: ${misc:Pre-Depends}
+Description: Osmo control 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 libosmoctrl library in particular contains an SNMP-like status interface.
+
+Package: libosmosim0
+Section: libs
+Architecture: any
+Multi-Arch: same
+Depends: ${shlibs:Depends},
+ ${misc:Depends}
+Pre-Depends: ${misc:Pre-Depends}
+Description: 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.
+ .
+ The libosmosim library in particular contains routines for SIM card access.
Package: libosmocore-dev
-Section: libdevel
Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}, libosmocore
+Multi-Arch: same
+Section: libdevel
+Depends: libosmocore,
+ libtalloc-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
+ with any of the libosmocore libraries.
+ .
+ Also static libraries are installed with this package.
+
+Package: libosmocore-utils
+Architecture: any
+Section: utils
+Depends: ${shlibs:Depends},
+ libosmocore,
+ ${misc:Depends}
+Multi-Arch: same
+Description: Utilities for gsm
+ 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.
+ .
+ They use the libosmocore library. The libosmocore library contain 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.
-#Package: libosmocore-dbg
-#Section: libdevel
-#Architecture: any
-#Depends: ${shlibs:Depends}, ${misc:Depends}
-#Description: Debug symbols for Open Source MObile COMmunications CORE library
+Package: libosmocore-dbg
+Architecture: any
+Multi-Arch: same
+Section: debug
+Priority: extra
+Depends: libosmocore (= ${binary:Version}),
+ ${misc:Depends}
+Description: Debug symbols for Open Source MObile COMmunications CORE library
+ This proackage contains debug symbols for all the libraries that are part of
+ libosmocore.
+ .
+ The libosmocore library contain 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.
diff --git a/src/shared/libosmocore/debian/copyright b/src/shared/libosmocore/debian/copyright
index c450be58..2991c854 100644
--- a/src/shared/libosmocore/debian/copyright
+++ b/src/shared/libosmocore/debian/copyright
@@ -1,54 +1,211 @@
-This work was packaged for Debian by:
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: libosmocore
+Source: git://git.osmocom.org/libosmocore.git
- Harald Welte <laforge@gnumonks.org> on Tue, 24 Aug 2010 10:55:04 +0200
+Files: *
+Copyright: 2008 Daniel Willmann <daniel@totalueberwachung.de>
+ 2008-2010 Harald Welte <laforge@gnumonks.org>
+ 2008-2010 Holger Hans Peter Freyther <zecke@selfish.org>
+ 2009-2010 Sylvain Munaut <tnt@246tNt.com>
+ 2009-2010 On-Waves
+ 2010 Nico Golde <nico@ngolde.de>
+License: GPL-2+
-It was downloaded from:
+Files: src/talloc.c include/osmocom/core/talloc.h
+Copyright: 2004 Andrew Tridgell
+ 2006 Stefan Metzmacher
+License: LGPL-3+
- git://git.osmocom.org/libosmocore.git
+Files: include/osmocom/core/loggingrb.h
+ include/osmocom/core/strrb.h
+ src/strrb.c
+ src/loggingrb.c
+Copyright: 2012-2013 Katerina Barone-Adesi <kat.obsc@gmail.com>
+License: GPL-2+
-Upstream Author(s):
+Files: include/osmocom/core/linuxrbtree.h
+ src/rbtree.c
+Copyright: 1999 Andrea Arcangeli <andrea@suse.de>
+ 2002 David Woodhouse <dwmw2@infradead.org>
+License: GPL-2+
- Harald Welte <laforge@gnumonks.org>
- Holger Hans Peter Freyther <zecke@selfish.org>
- Sylvain Munaut <tnt@246tNt.com>
- Daniel Willmann <daniel@totalueberwachung.de>
- Golde <nico@ngolde.de>
- For src/talloc.c and include/osmocore/talloc.h:
- Andrew Tridgell
- Stefan Metzmacher
- For src/vty/* and include/osmocom/vty/*
- Kunihiro Ishiguro
+Files: include/osmocom/core/crc16.h
+Copyright: 2005 Ben Gardner <bgardner@wabtec.com>
+License: GPL-2+
-Copyright:
+Files: src/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+
- Copyright (C) 2008-2010 Harald Welte <laforge@gnumonks.org>
- Copyright (C) 2008-2010 Holger Hans Peter Freyther <zecke@selfish.org>
- Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
- Copyright (C) 2009-2010 On-Waves
- Copyright (C) 2008 Daniel Willmann <daniel@totalueberwachung.de>
- Copyright (C) 2010 Nico Golde <nico@ngolde.de>
- For src/talloc.c and include/osmocore/talloc.h:
- Copyright (C) 2004 Andrew Tridgell
- Copyright (C) 2006 Stefan Metzmacher
- For src/vty/* and include/osmocom/vty/*
- Copyright (C) 1998 Kunihiro Ishiguro
+Files: src/gsm/gsm48_ie.c
+ src/gsm/lapd_core.c
+ src/gsm/lapdm.c
+Copyright: 2008,2010-2011 Harald Welte <laforge@gnumonks.org>
+ 2009-2011 Andreas Eversberg <jolly@eversberg.eu>
+License: GPL-2+
-License:
+Files: src/gsm/gsm0411_smc.c
+ src/gsm/gsm0411_smr.c
+ src/gsm/gsm0411_utils.c
+Copyright: 2008 Daniel Willmann <daniel@totalueberwachung.de>
+ 2009 Harald Welte <laforge@gnumonks.org>
+ 2010-2013 Holger Hans Peter Freyther <zecke@selfish.org>
+ 2010 On-Waves
+ 2011 Andreas Eversberg <jolly@eversberg.eu>
+License: GPL-2+
- GNU General Public License, Version 2 or later
+Files: src/gsm/gsm0480.c
+Copyright: 2009 Mike Haben <michael.haben@btinternet.com>
+ 2010 Holger Hans Peter Freyther <zecke@selfish.org>
+License: GPL-2+
-The Debian packaging is:
+Files: src/gsm/milenage/aes-internal.c
+ src/gsm/milenage/aes-encblock.c
+ src/gsm/milenage/aes.h
+ src/gsm/milenage/milenage.c
+ src/gsm/milenage/aes_wrap.h
+ src/gsm/milenage/aes-internal-enc.c
+ src/gsm/milenage/aes_i.h
+Copyright: 2003-2007 Jouni Malinen <j@w1.fi>
+License: GPL-2
+Comment: Most of the file aes-internal.c is public_domain
- Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>
+Files: include/osmocom/core/bitXXgen.h.tpl
+ src/gsm/kasumi.c
+Copyright: 2013-2014 Max <max.suraev@fairwaves.co>
+License: GPL-2+
-# Please chose a license for your packaging work. If the program you package
-# uses a mainstream license, using the same license is the safest choice.
-# Please avoid to pick license terms that are more restrictive than the
-# packaged work, as it may make Debian's contributions unacceptable upstream.
-# If you just want it to be GPL version 3, leave the following lines in.
+Files: src/gsm/auth_comp128v23.c
+Copyright: 2010-2011 Harald Welte <laforge@gnumonks.org>
+ 2013 Kévin Redon <kevredon@mail.tsaitgaist.info>
+License: GPL-2+
-and is licensed under the GPL version 3,
-see "/usr/share/common-licenses/GPL-3".
+Files: src/gsm/comp128v23.c
+Copyright: 2013 Kévin Redon <kevredon@mail.tsaitgaist.info>
+License: GPL-2+
-# Please also look if there are files or directories which have a
-# different copyright/license attached and list them here.
+Files: tests/fr/fr_test.c
+ tests/logging/logging_test.c
+ tests/loggingrb/loggingrb_test.c
+Copyright: 2008,2009,2012 Holger Hans Peter Freyther <zecke@selfish.org>
+ 2012-2013 Katerina Barone-Adesi <kat.obsc@gmail.com>
+License: AGPL-3+
+
+Files: tests/strrb/strrb_test.c
+ tests/vty/vty_test.c
+Copyright: 2012-2013 Katerina Barone-Adesi <kat.obsc@gmail.com>
+ 2013 Jacob Erlbeck <jerlbeck@sysmocom.de>
+License: GPL-3+
+
+Files: src/vty/* include/osmocom/vty/*
+Copyright: 1997,1998 Kunihiro Ishiguro
+License: GPL-2+
+
+Files: include/osmocom/core/stats.h
+ src/stat_item.c
+ src/stats.c
+ src/vty/stats_vty.c
+ tests/stats/stats_test.c
+Copyright: 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ 2009-2014 by Holger Hans Peter Freyther
+ 2015 Sysmocom s.f.m.c. GmbH
+License: GPL-2+
+
+Files: tests/gb/gprs_ns_test.c
+ tests/gb/gprs_bssgp_test.c
+Copyright: 2013-2014 sysmocom s.f.m.c. GmbH
+License: GPL-2+
+Comment: No license is listed in these files, but it is assumed that
+ the project specific license GPL-2+ applies since contributions by
+ sysmocom people to other files in this package are GPL licensed.
+
+Files: debian/*
+Copyright: 2010-2015 Harald Welte <laforge@gnumonks.org>
+ 2014-2015 Ruben Undheim <ruben.undheim@gmail.com>
+License: GPL-3+
+
+
+License: GPL-2+
+ This package 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/>.
+ .
+ On Debian systems, the complete text of the GNU General
+ Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
+
+License: GPL-2
+ This package 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, version 2 of the License.
+ .
+ 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/>.
+ .
+ On Debian systems, the complete text of the GNU General
+ Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
+
+License: GPL-3+
+ This package 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.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ .
+ On Debian systems, the complete text of the GNU General
+ Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
+
+
+License: LGPL-3+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+ .
+ This library 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
+ Lesser General Public License for more details.
+ .
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ .
+ On Debian systems, the complete text of the GNU Lesser General
+ Public License version 3 can be found in "/usr/share/common-licenses/LGPL-3".
+
+
+License: AGPL-3+
+ 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/>.
diff --git a/src/shared/libosmocore/debian/libosmocodec-doc.doc-base b/src/shared/libosmocore/debian/libosmocodec-doc.doc-base
new file mode 100644
index 00000000..8480c73f
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmocodec-doc.doc-base
@@ -0,0 +1,7 @@
+Document: libosmocodec-doc
+Title: Documentation for the libosmocodec library
+Section: Programming
+
+Format: HTML
+Index: /usr/share/doc/libosmocore/codec/html/index.html
+Files: /usr/share/doc/libosmocore/codec/html/*.html
diff --git a/src/shared/libosmocore/debian/libosmocodec-doc.install b/src/shared/libosmocore/debian/libosmocodec-doc.install
new file mode 100644
index 00000000..be0255b8
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmocodec-doc.install
@@ -0,0 +1 @@
+usr/share/doc/libosmocore/codec/
diff --git a/src/shared/libosmocore/debian/libosmocodec0.install b/src/shared/libosmocore/debian/libosmocodec0.install
new file mode 100644
index 00000000..2676133e
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmocodec0.install
@@ -0,0 +1 @@
+usr/lib/*/libosmocodec*.so.*
diff --git a/src/shared/libosmocore/debian/libosmocore-dbg.dirs b/src/shared/libosmocore/debian/libosmocore-dbg.dirs
deleted file mode 100644
index af59b0a9..00000000
--- a/src/shared/libosmocore/debian/libosmocore-dbg.dirs
+++ /dev/null
@@ -1 +0,0 @@
-usr/lib/debug/lib
diff --git a/src/shared/libosmocore/debian/libosmocore-dbg.install b/src/shared/libosmocore/debian/libosmocore-dbg.install
deleted file mode 100644
index 7ce02127..00000000
--- a/src/shared/libosmocore/debian/libosmocore-dbg.install
+++ /dev/null
@@ -1 +0,0 @@
-usr/lib/debug/lib/*
diff --git a/src/shared/libosmocore/debian/libosmocore-dev.dirs b/src/shared/libosmocore/debian/libosmocore-dev.dirs
deleted file mode 100644
index 94090a39..00000000
--- a/src/shared/libosmocore/debian/libosmocore-dev.dirs
+++ /dev/null
@@ -1,8 +0,0 @@
-usr/lib
-usr/include
-usr/include/osmocom
-usr/include/osmocom/codec
-usr/include/osmocom/core
-usr/include/osmocom/crypt
-usr/include/osmocom/gsm
-usr/include/osmocom/vty
diff --git a/src/shared/libosmocore/debian/libosmocore-dev.install b/src/shared/libosmocore/debian/libosmocore-dev.install
index eec0e15e..465f9330 100644
--- a/src/shared/libosmocore/debian/libosmocore-dev.install
+++ b/src/shared/libosmocore/debian/libosmocore-dev.install
@@ -1,5 +1,5 @@
usr/include/*
-usr/lib/lib*.a
-usr/lib/lib*.so
-usr/lib/lib*.la
-usr/lib/pkgconfig/*
+usr/lib/*/lib*.a
+usr/lib/*/lib*.so
+usr/lib/*/lib*.la
+usr/lib/*/pkgconfig/*
diff --git a/src/shared/libosmocore/debian/libosmocore-doc.doc-base b/src/shared/libosmocore/debian/libosmocore-doc.doc-base
new file mode 100644
index 00000000..b13b82d7
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmocore-doc.doc-base
@@ -0,0 +1,7 @@
+Document: libosmocore-doc
+Title: Documentation for the libosmocore library
+Section: Programming
+
+Format: HTML
+Index: /usr/share/doc/libosmocore/core/html/index.html
+Files: /usr/share/doc/libosmocore/core/html/*.html
diff --git a/src/shared/libosmocore/debian/libosmocore-doc.install b/src/shared/libosmocore/debian/libosmocore-doc.install
new file mode 100644
index 00000000..fe4cb267
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmocore-doc.install
@@ -0,0 +1 @@
+usr/share/doc/libosmocore/core/
diff --git a/src/shared/libosmocore/debian/libosmocore-utils.dirs b/src/shared/libosmocore/debian/libosmocore-utils.dirs
new file mode 100644
index 00000000..e7724817
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmocore-utils.dirs
@@ -0,0 +1 @@
+usr/bin
diff --git a/src/shared/libosmocore/debian/libosmocore-utils.install b/src/shared/libosmocore/debian/libosmocore-utils.install
new file mode 100644
index 00000000..9c3b8dce
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmocore-utils.install
@@ -0,0 +1,2 @@
+usr/bin/osmo-arfcn
+usr/bin/osmo-auc-gen
diff --git a/src/shared/libosmocore/debian/libosmocore.install b/src/shared/libosmocore/debian/libosmocore.install
index 93302609..e69de29b 100644
--- a/src/shared/libosmocore/debian/libosmocore.install
+++ b/src/shared/libosmocore/debian/libosmocore.install
@@ -1,2 +0,0 @@
-usr/lib/lib*.so.*
-usr/share/doc/libosmocore/*
diff --git a/src/shared/libosmocore/debian/libosmocore8.install b/src/shared/libosmocore/debian/libosmocore8.install
new file mode 100644
index 00000000..b73331b9
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmocore8.install
@@ -0,0 +1 @@
+usr/lib/*/libosmocore*.so.*
diff --git a/src/shared/libosmocore/debian/libosmoctrl0.install b/src/shared/libosmocore/debian/libosmoctrl0.install
new file mode 100644
index 00000000..56c64fc0
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmoctrl0.install
@@ -0,0 +1 @@
+usr/lib/*/libosmoctrl*.so.*
diff --git a/src/shared/libosmocore/debian/libosmogb4.install b/src/shared/libosmocore/debian/libosmogb4.install
new file mode 100644
index 00000000..4c474255
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmogb4.install
@@ -0,0 +1 @@
+usr/lib/*/libosmogb*.so.*
diff --git a/src/shared/libosmocore/debian/libosmogsm-doc.doc-base b/src/shared/libosmocore/debian/libosmogsm-doc.doc-base
new file mode 100644
index 00000000..6318c1de
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmogsm-doc.doc-base
@@ -0,0 +1,7 @@
+Document: libosmogsm-doc
+Title: Documentation for the libosmogsm library
+Section: Programming
+
+Format: HTML
+Index: /usr/share/doc/libosmocore/gsm/html/index.html
+Files: /usr/share/doc/libosmocore/gsm/html/*.html
diff --git a/src/shared/libosmocore/debian/libosmogsm-doc.install b/src/shared/libosmocore/debian/libosmogsm-doc.install
new file mode 100644
index 00000000..a4300bc9
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmogsm-doc.install
@@ -0,0 +1 @@
+usr/share/doc/libosmocore/gsm/
diff --git a/src/shared/libosmocore/debian/libosmogsm7.install b/src/shared/libosmocore/debian/libosmogsm7.install
new file mode 100644
index 00000000..5e617298
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmogsm7.install
@@ -0,0 +1 @@
+usr/lib/*/libosmogsm*.so.*
diff --git a/src/shared/libosmocore/debian/libosmosim0.install b/src/shared/libosmocore/debian/libosmosim0.install
new file mode 100644
index 00000000..0a780abc
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmosim0.install
@@ -0,0 +1 @@
+usr/lib/*/libosmosim*.so.*
diff --git a/src/shared/libosmocore/debian/libosmovty-doc.doc-base b/src/shared/libosmocore/debian/libosmovty-doc.doc-base
new file mode 100644
index 00000000..dde189c3
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmovty-doc.doc-base
@@ -0,0 +1,7 @@
+Document: libosmovty-doc
+Title: Documentation for the libosmovty library
+Section: Programming
+
+Format: HTML
+Index: /usr/share/doc/libosmocore/vty/html/index.html
+Files: /usr/share/doc/libosmocore/vty/html/*.html
diff --git a/src/shared/libosmocore/debian/libosmovty-doc.install b/src/shared/libosmocore/debian/libosmovty-doc.install
new file mode 100644
index 00000000..634ba41d
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmovty-doc.install
@@ -0,0 +1 @@
+usr/share/doc/libosmocore/vty/
diff --git a/src/shared/libosmocore/debian/libosmovty3.install b/src/shared/libosmocore/debian/libosmovty3.install
new file mode 100644
index 00000000..fbf6a5fa
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmovty3.install
@@ -0,0 +1 @@
+usr/lib/*/libosmovty*.so.*
diff --git a/src/shared/libosmocore/debian/patches/debian-changes-0.1.17-1 b/src/shared/libosmocore/debian/patches/debian-changes-0.1.17-1
deleted file mode 100644
index c0a54bd7..00000000
--- a/src/shared/libosmocore/debian/patches/debian-changes-0.1.17-1
+++ /dev/null
@@ -1,46 +0,0 @@
-Description: Upstream changes introduced in version 0.1.17-1
- This patch has been created by dpkg-source during the package build.
- Here's the last changelog entry, hopefully it gives details on why
- those changes were made:
- .
- libosmocore (0.1.17-1) unstable; urgency=low
- .
- * Initial release (Closes: #nnnn) <nnnn is the bug number of your ITP>
- .
- The person named in the Author field signed this changelog entry.
-Author: Harald Welte <laforge@gnumonks.org>
-
----
-The information above should follow the Patch Tagging Guidelines, please
-checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here
-are templates for supplementary fields that you might want to add:
-
-Origin: <vendor|upstream|other>, <url of original patch>
-Bug: <url in upstream bugtracker>
-Bug-Debian: http://bugs.debian.org/<bugnumber>
-Bug-Ubuntu: https://launchpad.net/bugs/<bugnumber>
-Forwarded: <no|not-needed|url proving that it has been forwarded>
-Reviewed-By: <name and email of someone who approved the patch>
-Last-Update: <YYYY-MM-DD>
-
---- /dev/null
-+++ libosmocore-0.1.17/.version
-@@ -0,0 +1 @@
-+0.1.17
---- /dev/null
-+++ libosmocore-0.1.17/copyright
-@@ -0,0 +1,14 @@
-+Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135
-+Name: libosmocore
-+Maintainer: Harald Welte <laforge@gnumonks.org>
-+Source: git://git.osmocom.org/libosmocore.git
-+
-+Copyright: 2008-2010 Harald Welte <laforge@gnumonks.org>
-+License: GPL-2+
-+
-+Files: src/talloc.c include/osmocore/talloc.h
-+Copyright: 2004 Andrew Tridgell
-+License: LGPL-3+
-+
-+Files: include/osmocore/linuxlist.h
-+License: GPL-2
diff --git a/src/shared/libosmocore/debian/patches/series b/src/shared/libosmocore/debian/patches/series
index 0ca407b1..e69de29b 100644
--- a/src/shared/libosmocore/debian/patches/series
+++ b/src/shared/libosmocore/debian/patches/series
@@ -1 +0,0 @@
-debian-changes-0.1.17-1
diff --git a/src/shared/libosmocore/debian/rules b/src/shared/libosmocore/debian/rules
index f97995d5..e1735102 100755
--- a/src/shared/libosmocore/debian/rules
+++ b/src/shared/libosmocore/debian/rules
@@ -1,10 +1,4 @@
#!/usr/bin/make -f
-# -*- makefile -*-
-# Sample debian/rules that uses debhelper.
-# This file was originally written by Joey Hess and Craig Small.
-# As a special exception, when this file is copied by dh-make into a
-# dh-make output file, you may use that output file without restriction.
-# This special exception was added by Craig Small in version 0.37 of dh-make.
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
@@ -13,13 +7,51 @@ DEBIAN := $(shell dpkg-parsechangelog | grep ^Version: | cut -d' ' -f2)
DEBVERS := $(shell echo '$(DEBIAN)' | cut -d- -f1)
VERSION := $(shell echo '$(DEBVERS)' | sed -e 's/[+-].*//' -e 's/~//g')
+export DEB_BUILD_MAINT_OPTIONS = hardening=+all
+
+export DEB_LDFLAGS_MAINT_STRIP = -Wl,-Bsymbolic-functions
+
%:
- dh --with autoreconf $@ --fail-missing
+ dh $@ --with autoreconf --fail-missing
+
+override_dh_strip:
+ dh_strip --dbg-package=libosmocore-dbg
-#override_dh_strip:
-# dh_strip --dbg-package=libosmocore-dbg
+override_dh_install:
+ sed -i "/dependency_libs/ s/'.*'/''/" `find . -name '*.la'`
+ dh_install
+
+# Print test results in case of a failure
+override_dh_auto_test:
+ dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
override_dh_autoreconf:
echo $(VERSION) > .tarball-version
dh_autoreconf
+override_dh_auto_configure:
+ dh_auto_configure -- --enable-static
+
+override_dh_clean:
+ dh_clean
+ $(RM) .version
+ $(RM) debian/man/osmo-arfcn.1
+ $(RM) debian/man/osmo-auc-gen.1
+ $(RM) include/osmocom/core/bit16gen.h
+ $(RM) include/osmocom/core/bit32gen.h
+ $(RM) include/osmocom/core/bit64gen.h
+ $(RM) include/osmocom/core/crc16gen.h
+ $(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) tests/package.m4
+ $(RM) tests/testsuite
+ $(RM) -r doc/codec/
+ $(RM) -r doc/core/
+ $(RM) -r doc/gsm/
+ $(RM) -r doc/vty/html/
+ $(RM) -r doc/vty/latex/
diff --git a/src/shared/libosmocore/doc/osmocom-authn-protocol.txt b/src/shared/libosmocore/doc/osmocom-authn-protocol.txt
new file mode 100644
index 00000000..6d057bea
--- /dev/null
+++ b/src/shared/libosmocore/doc/osmocom-authn-protocol.txt
@@ -0,0 +1,250 @@
+
+ Osmocom Authentication Protocol (OAP)
+
+1. General
+
+The Osmocom Authentication Protocol employs mutual authentication to register a
+client with a server over an IPA connection. Milenage is used as the
+authentication algorithm, where client and server have a shared secret.
+
+For example, an SGSN, as OAP client, may use its SGSN ID to register with a MAP
+proxy, an OAP server.
+
+1.1. Connection
+
+The protocol expects that a reliable, ordered, packet boundaries preserving
+connection is used (e.g. IPA over TCP).
+
+1.2. Using IPA
+
+By default, the following identifiers should be used:
+ - IPA protocol: 0xee (OSMO)
+ - IPA OSMO protocol extension: 0x06 (OAP)
+
+2. Procedures
+
+Ideal communication sequence:
+
+ Client Server
+ | |
+ | Register (ID) |
+ |----------------------------------->|
+ | |
+ | Challenge (RAND+AUTN) |
+ |<-----------------------------------|
+ | |
+ | Challenge Result (XRES) |
+ |----------------------------------->|
+ | |
+ | Register Result |
+ |<-----------------------------------|
+
+Variation "test setup":
+
+ Client Server
+ | |
+ | Register (ID) |
+ |----------------------------------->|
+ | |
+ | Register Result |
+ |<-----------------------------------|
+
+Variation "invalid sequence nr":
+
+ Client Server
+ | |
+ | Register (ID) |
+ |----------------------------------->|
+ | |
+ | Challenge (RAND+AUTN) |
+ |<-----------------------------------|
+ | |
+ | Sync Request (AUTS) |
+ |----------------------------------->|
+ | |
+ | Challenge (RAND'+AUTN') |
+ |<-----------------------------------|
+ | |
+ | Challenge Result (XRES) |
+ |----------------------------------->|
+ | |
+ | Register Result |
+ |<-----------------------------------|
+
+2.1. Register
+
+The client sends a REGISTER_REQ message containing an identifier number.
+
+2.2. Challenge
+
+The OAP server (optionally) sends back a CHALLENGE_REQ, containing random bytes
+and a milenage authentication token generated from these random bytes, using a
+shared secret, to authenticate itself to the OAP client. The server may omit
+this challenge entirely, based on its configuration, and immediately reply with
+a Register Result response. If the client cannot be registered (e.g. id is
+invalid), the server sends a REGISTER_ERR response.
+
+2.3. Challenge Result
+
+When the client has received a Challenge, it may verify the server's
+authenticity and validity of the sequence number (included in AUTN), and, if
+valid, reply with a CHALLENGE_RES message. This shall contain an XRES
+authentication token generated by milenage from the same random bytes received
+from the server and the same shared secet. If the client decides to cancel the
+registration (e.g. invalid AUTN), it shall not reply to the CHALLENGE_REQ; a
+CHALLENGE_ERR message may be sent, but is not mandatory. For example, the
+client may directly start with a new REGISTER_REQ message.
+
+2.4. Sync Request
+
+When the client has received a Challenge but sees an invalid sequence number
+(embedded in AUTN, according to the milenage algorithm), the client may send a
+SYNC_REQ message containing an AUTS synchronisation token.
+
+2.5. Sync Result
+
+If the server has received a valid Sync Request, it shall answer by directly
+sending another Challenge (see 2.2.). If an invalid Sync Request is received,
+the server shall reply with a REGISTER_ERR message.
+
+2.6. Register Result
+
+The server sends a REGISTER_RES message to indicate that registration has been
+successful. If the server cannot register the client (e.g. invalid challenge
+response), it shall send a REGISTER_ERR message.
+
+3. Message Format
+
+3.1. General
+
+Every message is based on the following message format
+
+ IEI Info Element Type Pres. Format Length
+ Message type 4.2.1 M V 1
+
+The receiver shall be able to receive IEs in any order. Unknown IEs shall be
+ignored.
+
+3.2.1. Register Request
+
+Client -> Server
+
+ IEI Info Element Type Pres. Format Length
+ Message type 4.2.1 M V 1
+ 30 Client ID big endian int (2 oct) M TLV 4
+
+3.2.2. Register Error
+
+Server -> Client
+
+ IEI Info Element Type Pres. Format Length
+ Message type 4.2.1 M V 1
+ 02 Cause GMM cause, M TLV 3
+ 04.08: 10.5.5.14
+
+3.2.3. Register Result
+
+Server -> Client
+
+ IEI Info Element Type Pres. Format Length
+ Message type 4.2.1 M V 1
+
+3.2.4. Challenge
+
+Server -> Client
+
+ IEI Info Element Type Pres. Format Length
+ Message type 4.2.1 M V 1
+ 20 RAND octet string (16) M TLV 18
+ 23 AUTN octet string (16) M TLV 18
+
+3.2.5. Challenge Error
+
+Client -> Server
+
+ IEI Info Element Type Pres. Format Length
+ Message type 4.2.1 M V 1
+ 02 Cause GMM cause, M TLV 3
+ 04.08: 10.5.5.14
+
+3.2.6. Challenge Result
+
+Client -> Server
+
+ IEI Info Element Type Pres. Format Length
+ Message type 4.2.1 M V 1
+ 21 XRES octet string (8) M TLV 10
+
+3.2.7. Sync Request
+
+Client -> Server
+
+ IEI Info Element Type Pres. Format Length
+ Message type 4.2.1 M V 1
+ 20 AUTS octet string (16) M TLV 18
+
+3.2.8. Sync Error
+
+Not used.
+
+3.2.9. Sync Result
+
+Not used.
+
+
+4. Information Elements
+
+4.1. General
+
+[...]
+
+4.2.1. Message Type
+
+ +---------------------------------------------------+
+ | 8 7 6 5 4 3 2 1 |
+ | |
+ | 0 0 0 0 0 1 0 0 - Register Request |
+ | 0 0 0 0 0 1 0 1 - Register Error |
+ | 0 0 0 0 0 1 1 0 - Register Result |
+ | |
+ | 0 0 0 0 1 0 0 0 - Challenge Request |
+ | 0 0 0 0 1 0 0 1 - Challenge Error |
+ | 0 0 0 0 1 0 1 0 - Challenge Result |
+ | |
+ | 0 0 0 0 1 1 0 0 - Sync Request |
+ | 0 0 0 0 1 1 0 1 - Sync Error (not used) |
+ | 0 0 0 0 1 1 1 0 - Sync Result (not used) |
+ | |
+ +---------------------------------------------------+
+
+4.2.2. IE Identifier (informational)
+
+These are the standard values for the IEI.
+
+ +---------------------------------------------------------+
+ | IEI Info Element Type |
+ | |
+ | 0x02 Cause GMM cause, 04.08: 10.5.5.14 |
+ | 0x20 RAND octet string |
+ | 0x23 AUTN octet string |
+ | 0x24 XRES octet string |
+ | 0x25 AUTS octet string |
+ | 0x30 Client ID big endian int (2 octets) |
+ +---------------------------------------------------------+
+
+4.2.3. Client ID
+
+ 8 7 6 5 4 3 2 1
+ +-----------------------------------------------------+
+ | | Client ID IEI | octet 1
+ +-----------------------------------------------------+
+ | Length of Client ID IE contents (2) | octet 2
+ +-----------------------------------------------------+
+ | Client ID number, most significant byte | octet 3
+ +-----------------------------------------------------+
+ | Client ID number, least significant byte | octet 4
+ +-----------------------------------------------------+
+
+The Client ID number shall be interpreted as an unsigned 16bit integer, where 0
+indicates an invalid / unset ID.
+
diff --git a/src/shared/libosmocore/doc/vty/merge_doc.xsl b/src/shared/libosmocore/doc/vty/merge_doc.xsl
index caea1103..adbc1c14 100644
--- a/src/shared/libosmocore/doc/vty/merge_doc.xsl
+++ b/src/shared/libosmocore/doc/vty/merge_doc.xsl
@@ -18,7 +18,9 @@
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
<xsl:for-each select="$info/*">
- <xsl:copy-of select="." />
+ <xsl:if test="not($info/vty:description)">
+ <xsl:copy-of select="." />
+ </xsl:if>
</xsl:for-each>
</xsl:copy>
</xsl:if>
diff --git a/src/shared/libosmocore/include/Makefile.am b/src/shared/libosmocore/include/Makefile.am
index 60b9ea9f..0a300a84 100644
--- a/src/shared/libosmocore/include/Makefile.am
+++ b/src/shared/libosmocore/include/Makefile.am
@@ -2,8 +2,12 @@ nobase_include_HEADERS = \
osmocom/codec/codec.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/conv.h \
osmocom/core/crc16.h \
osmocom/core/crc16gen.h \
@@ -11,72 +15,100 @@ nobase_include_HEADERS = \
osmocom/core/crc64gen.h \
osmocom/core/crc8gen.h \
osmocom/core/crcgen.h \
+ osmocom/core/endian.h \
+ osmocom/core/defs.h \
+ osmocom/core/fsm.h \
osmocom/core/gsmtap.h \
osmocom/core/gsmtap_util.h \
osmocom/core/linuxlist.h \
osmocom/core/linuxrbtree.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/prim.h \
osmocom/core/process.h \
osmocom/core/rate_ctr.h \
+ osmocom/core/stat_item.h \
osmocom/core/select.h \
osmocom/core/signal.h \
osmocom/core/socket.h \
osmocom/core/statistics.h \
+ osmocom/core/strrb.h \
+ osmocom/core/talloc.h \
osmocom/core/timer.h \
osmocom/core/utils.h \
osmocom/core/write_queue.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/gprs_bssgp.h \
osmocom/gprs/gprs_bssgp_bss.h \
osmocom/gprs/gprs_msgb.h \
osmocom/gprs/gprs_ns.h \
osmocom/gprs/gprs_ns_frgre.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/gsm/a5.h \
osmocom/gsm/abis_nm.h \
+ osmocom/gsm/apn.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/gsm/gsm0808.h \
+ osmocom/gsm/gsm23003.h \
osmocom/gsm/gsm48.h \
osmocom/gsm/gsm48_ie.h \
osmocom/gsm/gsm_utils.h \
+ osmocom/gsm/gsup.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/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_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_44_318.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/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_TALLOC
-nobase_include_HEADERS += osmocom/core/talloc.h
-endif
-
if ENABLE_MSGFILE
nobase_include_HEADERS += osmocom/core/msgfile.h
endif
@@ -91,13 +123,22 @@ 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/vty.h \
+ osmocom/vty/ports.h \
+ osmocom/ctrl/control_vty.h
endif
-noinst_HEADERS = osmocom/core/timer_compat.h
+noinst_HEADERS = \
+ osmocom/core/timer_compat.h \
+ osmocom/gsm/kasumi.h osmocom/gsm/gea.h
+
+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 $@)
diff --git a/src/shared/libosmocore/include/osmocom/codec/codec.h b/src/shared/libosmocore/include/osmocom/codec/codec.h
index 81f5d4ba..fb127b54 100644
--- a/src/shared/libosmocore/include/osmocom/codec/codec.h
+++ b/src/shared/libosmocore/include/osmocom/codec/codec.h
@@ -1,7 +1,9 @@
-#ifndef _OSMOCOM_CODEC_H
-#define _OSMOCOM_CODEC_H
+#pragma once
#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/utils.h>
extern const uint16_t gsm610_bitorder[]; /* FR */
extern const uint16_t gsm620_unvoiced_bitorder[]; /* HR unvoiced */
@@ -17,4 +19,54 @@ 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 */
-#endif /* _OSMOCOM_CODEC_H */
+extern const struct value_string osmo_amr_type_names[];
+
+enum osmo_amr_type {
+ AMR_4_75 = 0,
+ AMR_5_15 = 1,
+ AMR_5_90 = 2,
+ AMR_6_70 = 3,
+ AMR_7_40 = 4,
+ AMR_7_95 = 5,
+ AMR_10_2 = 6,
+ AMR_12_2 = 7,
+ AMR_SID = 8,
+ AMR_GSM_EFR_SID = 9,
+ AMR_TDMA_EFR_SID = 10,
+ AMR_PDC_EFR_SID = 11,
+ AMR_NO_DATA = 15,
+};
+
+enum osmo_amr_quality {
+ AMR_BAD = 0,
+ AMR_GOOD = 1
+};
+
+/*! \brief Check if given AMR Frame Type is a speech frame
+ * \param[in] ft AMR Frame Type
+ * \returns true if AMR with given Frame Type contains voice, false otherwise
+ */
+static inline bool osmo_amr_is_speech(enum osmo_amr_type ft)
+{
+ switch (ft) {
+ case AMR_4_75:
+ case AMR_5_15:
+ case AMR_5_90:
+ case AMR_6_70:
+ case AMR_7_40:
+ case AMR_7_95:
+ case AMR_10_2:
+ case AMR_12_2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool osmo_fr_check_sid(uint8_t *rtp_payload, size_t payload_len);
+bool osmo_hr_check_sid(uint8_t *rtp_payload, size_t payload_len);
+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,
+ int8_t *cmi, enum osmo_amr_type *ft,
+ enum osmo_amr_quality *bfi, int8_t *sti);
diff --git a/src/shared/libosmocore/include/osmocom/core/application.h b/src/shared/libosmocore/include/osmocom/core/application.h
index 34571698..ecaeaa8a 100644
--- a/src/shared/libosmocore/include/osmocom/core/application.h
+++ b/src/shared/libosmocore/include/osmocom/core/application.h
@@ -1,5 +1,4 @@
-#ifndef OSMO_APPLICATION_H
-#define OSMO_APPLICATION_H
+#pragma once
/*!
* \file application.h
@@ -19,5 +18,3 @@ void osmo_init_ignore_signals(void);
int osmo_init_logging(const struct log_info *);
int osmo_daemonize(void);
-
-#endif
diff --git a/src/shared/libosmocore/include/osmocom/core/backtrace.h b/src/shared/libosmocore/include/osmocom/core/backtrace.h
index a24290c5..0c9b6ef9 100644
--- a/src/shared/libosmocore/include/osmocom/core/backtrace.h
+++ b/src/shared/libosmocore/include/osmocom/core/backtrace.h
@@ -1,7 +1,4 @@
-#ifndef _OSMO_BACKTRACE_H_
-#define _OSMO_BACKTRACE_H_
+#pragma once
void osmo_generate_backtrace(void);
void osmo_log_backtrace(int subsys, int level);
-
-#endif
diff --git a/src/shared/libosmocore/include/osmocom/core/bitXXgen.h.tpl b/src/shared/libosmocore/include/osmocom/core/bitXXgen.h.tpl
new file mode 100644
index 00000000..7480a8f4
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/bitXXgen.h.tpl
@@ -0,0 +1,105 @@
+/*
+ * bitXXgen.h
+ *
+ * Copyright (C) 2014 Max <max.suraev@fairwaves.co>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+/*! \brief load unaligned n-byte integer (little-endian encoding) into uintXX_t
+ * \param[in] p Buffer where integer is stored
+ * \param[in] n Number of bytes stored in p
+ * \returns XX bit unsigned integer
+ */
+static inline uintXX_t osmo_loadXXle_ext(const void *p, uint8_t n)
+{
+ uint8_t i;
+ uintXX_t r = 0;
+ const uint8_t *q = (uint8_t *)p;
+ for(i = 0; i < n; r |= ((uintXX_t)q[i] << (8 * i)), i++);
+ return r;
+}
+
+/*! \brief load unaligned n-byte integer (big-endian encoding) into uintXX_t
+ * \param[in] p Buffer where integer is stored
+ * \param[in] n Number of bytes stored in p
+ * \returns XX bit unsigned integer
+ */
+static inline uintXX_t osmo_loadXXbe_ext(const void *p, uint8_t n)
+{
+ uint8_t i;
+ uintXX_t r = 0;
+ const uint8_t *q = (uint8_t *)p;
+ for(i = 0; i < n; r |= ((uintXX_t)q[i] << (XX - 8* (1 + i))), i++);
+ return r;
+}
+
+
+/*! \brief store unaligned n-byte integer (little-endian encoding) from uintXX_t
+ * \param[in] x unsigned XX bit integer
+ * \param[out] p Buffer to store integer
+ * \param[in] n Number of bytes to store
+ */
+static inline void osmo_storeXXle_ext(uintXX_t x, void *p, uint8_t n)
+{
+ uint8_t i;
+ uint8_t *q = (uint8_t *)p;
+ for(i = 0; i < n; q[i] = (x >> i * 8) & 0xFF, i++);
+}
+
+/*! \brief store unaligned n-byte integer (big-endian encoding) from uintXX_t
+ * \param[in] x unsigned XX bit integer
+ * \param[out] p Buffer to store integer
+ * \param[in] n Number of bytes to store
+ */
+static inline void osmo_storeXXbe_ext(uintXX_t x, void *p, uint8_t n)
+{
+ uint8_t i;
+ uint8_t *q = (uint8_t *)p;
+ for(i = 0; i < n; q[i] = (x >> ((n - 1 - i) * 8)) & 0xFF, i++);
+}
+
+
+/* Convenience function for most-used cases */
+
+
+/*! \brief load unaligned XX-bit integer (little-endian encoding) */
+static inline uintXX_t osmo_loadXXle(const void *p)
+{
+ return osmo_loadXXle_ext(p, XX / 8);
+}
+
+/*! \brief load unaligned XX-bit integer (big-endian encoding) */
+static inline uintXX_t osmo_loadXXbe(const void *p)
+{
+ return osmo_loadXXbe_ext(p, XX / 8);
+}
+
+
+/*! \brief store unaligned XX-bit integer (little-endian encoding) */
+static inline void osmo_storeXXle(uintXX_t x, void *p)
+{
+ osmo_storeXXle_ext(x, p, XX / 8);
+}
+
+/*! \brief store unaligned XX-bit integer (big-endian encoding) */
+static inline void osmo_storeXXbe(uintXX_t x, void *p)
+{
+ osmo_storeXXbe_ext(x, p, XX / 8);
+}
diff --git a/src/shared/libosmocore/include/osmocom/core/bitcomp.h b/src/shared/libosmocore/include/osmocom/core/bitcomp.h
new file mode 100644
index 00000000..e87c0e10
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/bitcomp.h
@@ -0,0 +1,41 @@
+#pragma once
+
+/* bit compression routines */
+
+/* (C) 2016 sysmocom s.f.m.c. GmbH by Max Suraev <msuraev@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.
+ *
+ * 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
+ * @{
+ */
+
+/*! \file bitcomp.h
+ * \brief Osmocom bit compression routines
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/bitvec.h>
+
+
+int osmo_t4_encode(struct bitvec *bv);
+
+/*! @} */
diff --git a/src/shared/libosmocore/include/osmocom/core/bits.h b/src/shared/libosmocore/include/osmocom/core/bits.h
index 4c685321..5535920e 100644
--- a/src/shared/libosmocore/include/osmocom/core/bits.h
+++ b/src/shared/libosmocore/include/osmocom/core/bits.h
@@ -1,7 +1,11 @@
-#ifndef _OSMO_BITS_H
-#define _OSMO_BITS_H
+#pragma once
#include <stdint.h>
+#include <stddef.h>
+
+#include <osmocom/core/bit16gen.h>
+#include <osmocom/core/bit32gen.h>
+#include <osmocom/core/bit64gen.h>
/*! \defgroup bits soft, unpacked and packed bits
* @{
@@ -9,20 +13,19 @@
/*! \file bits.h
* \brief Osmocom bit level support code
+ *
+ * NOTE on the endianess of pbit_t:
+ * Bits in a pbit_t are ordered MSB first, i.e. 0x80 is the first bit.
+ * Bit i in a pbit_t array is array[i/8] & (1<<(7-i%8))
*/
typedef int8_t sbit_t; /*!< \brief soft bit (-127...127) */
typedef uint8_t ubit_t; /*!< \brief unpacked bit (0 or 1) */
typedef uint8_t pbit_t; /*!< \brief packed bis (8 bits in a byte) */
-/*
- NOTE on the endianess of pbit_t:
- Bits in a pbit_t are ordered MSB first, i.e. 0x80 is the first bit.
- Bit i in a pbit_t array is array[i/8] & (1<<(7-i%8))
-*/
-
/*! \brief determine how many bytes we would need for \a num_bits packed bits
* \param[in] num_bits Number of packed bits
+ * \returns number of bytes needed for \a num_bits packed bits
*/
static inline unsigned int osmo_pbit_bytesize(unsigned int num_bits)
{
@@ -38,6 +41,14 @@ int osmo_ubit2pbit(pbit_t *out, const ubit_t *in, unsigned int num_bits);
int osmo_pbit2ubit(ubit_t *out, const pbit_t *in, unsigned int num_bits);
+void osmo_nibble_shift_right(uint8_t *out, const uint8_t *in,
+ unsigned int num_nibbles);
+void osmo_nibble_shift_left_unal(uint8_t *out, const uint8_t *in,
+ unsigned int num_nibbles);
+
+void osmo_ubit2sbit(sbit_t *out, const ubit_t *in, unsigned int num_bits);
+void osmo_sbit2ubit(ubit_t *out, const sbit_t *in, unsigned int num_bits);
+
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);
@@ -46,6 +57,27 @@ 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);
+#define OSMO_BIN_SPEC "%d%d%d%d%d%d%d%d"
+#define OSMO_BIN_PRINT(byte) \
+ (byte & 0x80 ? 1 : 0), \
+ (byte & 0x40 ? 1 : 0), \
+ (byte & 0x20 ? 1 : 0), \
+ (byte & 0x10 ? 1 : 0), \
+ (byte & 0x08 ? 1 : 0), \
+ (byte & 0x04 ? 1 : 0), \
+ (byte & 0x02 ? 1 : 0), \
+ (byte & 0x01 ? 1 : 0)
+
+#define OSMO_BIT_SPEC "%c%c%c%c%c%c%c%c"
+#define OSMO_BIT_PRINT(byte) \
+ (byte & 0x80 ? '1' : '.'), \
+ (byte & 0x40 ? '1' : '.'), \
+ (byte & 0x20 ? '1' : '.'), \
+ (byte & 0x10 ? '1' : '.'), \
+ (byte & 0x08 ? '1' : '.'), \
+ (byte & 0x04 ? '1' : '.'), \
+ (byte & 0x02 ? '1' : '.'), \
+ (byte & 0x01 ? '1' : '.')
/* BIT REVERSAL */
@@ -73,6 +105,14 @@ uint32_t osmo_revbytebits_8(uint8_t x);
/* \brief reverse the bits of each byte in a given buffer */
void osmo_revbytebits_buf(uint8_t *buf, int len);
-/*! @} */
+/*! \brief left circular shift
+ * \param[in] in The 16 bit unsigned integer to be rotated
+ * \param[in] shift Number of bits to shift \a in to, [0;16] bits
+ * \returns shifted value
+ */
+static inline uint16_t osmo_rol16(uint16_t in, unsigned shift)
+{
+ return (in << shift) | (in >> (16 - shift));
+}
-#endif /* _OSMO_BITS_H */
+/*! @} */
diff --git a/src/shared/libosmocore/include/osmocom/core/bitvec.h b/src/shared/libosmocore/include/osmocom/core/bitvec.h
index 9c000d02..0e17ba7a 100644
--- a/src/shared/libosmocore/include/osmocom/core/bitvec.h
+++ b/src/shared/libosmocore/include/osmocom/core/bitvec.h
@@ -1,9 +1,10 @@
-#ifndef _BITVEC_H
-#define _BITVEC_H
+#pragma once
/* bit vector utility routines */
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2012 Ivan Klyuchnikov
+ * (C) 2015 Sysmocom s.f.m.c. GmbH
*
* All Rights Reserved
*
@@ -29,9 +30,19 @@
/*! \file bitvec.h
* \brief Osmocom bit vector abstraction
+ *
+ * These functions assume a MSB (most significant bit) first layout of the
+ * bits, so that for instance the 5 bit number abcde (a is MSB) can be
+ * embedded into a byte sequence like in xxxxxxab cdexxxxx. The bit count
+ * starts with the MSB, so the bits in a byte are numbered (MSB) 01234567 (LSB).
+ * Note that there are other incompatible encodings, like it is used
+ * for the EGPRS RLC data block headers (there the bits are numbered from LSB
+ * to MSB).
*/
#include <stdint.h>
+#include <talloc.h>
+#include <stdbool.h>
/*! \brief A single GSM bit
*
@@ -46,7 +57,7 @@ enum bit_value {
/*! \brief structure describing a bit vector */
struct bitvec {
- unsigned int cur_bit; /*!< \brief curser to the next unused bit */
+ unsigned int cur_bit; /*!< \brief cursor to the next unused bit */
unsigned int data_len; /*!< \brief length of data array in bytes */
uint8_t *data; /*!< \brief pointer to data array */
};
@@ -59,12 +70,30 @@ int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnum,
enum bit_value bit);
int bitvec_set_bit(struct bitvec *bv, enum bit_value bit);
int bitvec_get_bit_high(struct bitvec *bv);
-int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count);
-int bitvec_set_uint(struct bitvec *bv, unsigned int in, int count);
-int bitvec_get_uint(struct bitvec *bv, int num_bits);
+int bitvec_set_bits(struct bitvec *bv, const enum bit_value *bits, unsigned int count);
+int bitvec_set_uint(struct bitvec *bv, uint32_t in, unsigned int count);
+int bitvec_get_uint(struct bitvec *bv, unsigned int num_bits);
int bitvec_find_bit_pos(const struct bitvec *bv, unsigned int n, enum bit_value val);
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);
+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);
+unsigned int bitvec_unpack(struct bitvec *bv, const uint8_t *buffer);
+uint64_t bitvec_read_field(struct bitvec *bv, unsigned int *read_index, unsigned int len);
+int bitvec_write_field(struct bitvec *bv, unsigned int *write_index, uint64_t val, unsigned int len);
+int bitvec_fill(struct bitvec *bv, unsigned int num_bits, enum bit_value fill);
+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);
+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,
+ unsigned int array_len, bool dry_run,
+ unsigned int num_bits);
/*! @} */
-
-#endif /* _BITVEC_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/conv.h b/src/shared/libosmocore/include/osmocom/core/conv.h
index e5b2a975..e7f6bd6a 100644
--- a/src/shared/libosmocore/include/osmocom/core/conv.h
+++ b/src/shared/libosmocore/include/osmocom/core/conv.h
@@ -25,11 +25,10 @@
*/
/*! \file conv.h
- * \file Osmocom convolutional encoder and decoder
+ * Osmocom convolutional encoder and decoder
*/
-#ifndef __OSMO_CONV_H__
-#define __OSMO_CONV_H__
+#pragma once
#include <stdint.h>
@@ -142,5 +141,3 @@ int osmo_conv_decode(const struct osmo_conv_code *code,
/*! @} */
-
-#endif /* __OSMO_CONV_H__ */
diff --git a/src/shared/libosmocore/include/osmocom/core/crc16.h b/src/shared/libosmocore/include/osmocom/core/crc16.h
index 0e524176..f1564bd2 100644
--- a/src/shared/libosmocore/include/osmocom/core/crc16.h
+++ b/src/shared/libosmocore/include/osmocom/core/crc16.h
@@ -15,8 +15,7 @@
* Version 2. See the file COPYING for more details.
*/
-#ifndef __CRC16_H
-#define __CRC16_H
+#pragma once
#include <stdint.h>
@@ -31,4 +30,14 @@ static inline uint16_t osmo_crc16_byte(uint16_t crc, const uint8_t data)
return (crc >> 8) ^ osmo_crc16_table[(crc ^ data) & 0xff];
}
-#endif /* __CRC16_H */
+
+/* CCITT polynome 0x8408. This corresponds to x^0 + x^5 + x^12 */
+
+extern uint16_t const osmo_crc16_ccitt_table[256];
+
+extern uint16_t osmo_crc16_ccitt(uint16_t crc, const uint8_t *buffer, size_t len);
+
+static inline uint16_t osmo_crc16_ccitt_byte(uint16_t crc, const uint8_t data)
+{
+ return (crc >> 8) ^ osmo_crc16_ccitt_table[(crc ^ data) & 0xff];
+}
diff --git a/src/shared/libosmocore/include/osmocom/core/crcXXgen.h.tpl b/src/shared/libosmocore/include/osmocom/core/crcXXgen.h.tpl
index 89d083ae..164f7201 100644
--- a/src/shared/libosmocore/include/osmocom/core/crcXXgen.h.tpl
+++ b/src/shared/libosmocore/include/osmocom/core/crcXXgen.h.tpl
@@ -20,15 +20,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef __OSMO_CRCXXGEN_H__
-#define __OSMO_CRCXXGEN_H__
+#pragma once
/*! \addtogroup crcgen
* @{
*/
/*! \file crcXXgen.h
- * \file Osmocom generic CRC routines (for max XX bits poly) header
+ * Osmocom generic CRC routines (for max XX bits poly) header
*/
@@ -54,6 +53,4 @@ void osmo_crcXXgen_set_bits(const struct osmo_crcXXgen_code *code,
/*! @} */
-#endif /* __OSMO_CRCXXGEN_H__ */
-
/* vim: set syntax=c: */
diff --git a/src/shared/libosmocore/include/osmocom/core/crcgen.h b/src/shared/libosmocore/include/osmocom/core/crcgen.h
index 8e208a74..b39b55db 100644
--- a/src/shared/libosmocore/include/osmocom/core/crcgen.h
+++ b/src/shared/libosmocore/include/osmocom/core/crcgen.h
@@ -20,15 +20,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef __OSMO_CRCGEN_H__
-#define __OSMO_CRCGEN_H__
+#pragma once
/*! \defgroup crcgen Osmocom generic CRC routines
* @{
*/
/*! \file crcgen.h
- * \file Osmocom generic CRC routines global header
+ * Osmocom generic CRC routines global header
*/
#include <osmocom/core/crc8gen.h>
@@ -37,5 +36,3 @@
#include <osmocom/core/crc64gen.h>
/*! @} */
-
-#endif /* __OSMO_CRCGEN_H__ */
diff --git a/src/shared/libosmocore/include/osmocom/core/defs.h b/src/shared/libosmocore/include/osmocom/core/defs.h
new file mode 100644
index 00000000..aebe9258
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/defs.h
@@ -0,0 +1,47 @@
+#pragma once
+
+/*! \defgroup utils General-purpose utility functions
+ * @{
+ */
+
+/*! \file defs.h
+ * \brief General definitions that are meant to be included from header files.
+ */
+
+/*! \brief Check for gcc and version.
+ *
+ * \note Albeit glibc provides a features.h file that contains a similar
+ * definition (__GNUC_PREREQ), this definition has been copied from there
+ * to have it available with other libraries, too.
+ *
+ * \return != 0 iff gcc is used and it's version is at least maj.min.
+ */
+#if defined __GNUC__ && defined __GNUC_MINOR__
+# define OSMO_GNUC_PREREQ(maj, min) \
+ ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
+#else
+# define OSMO_GNUC_PREREQ(maj, min) 0
+#endif
+
+/*! \brief Set the deprecated attribute with a message.
+ */
+#if defined(__clang__)
+# define _OSMO_HAS_ATTRIBUTE_DEPRECATED __has_attribute(deprecated)
+# define _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE __has_extension(attribute_deprecated_with_message)
+#elif defined(__GNUC__)
+# define _OSMO_HAS_ATTRIBUTE_DEPRECATED 1
+# define _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE OSMO_GNUC_PREREQ(4,5)
+#endif
+
+#if _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE
+# define OSMO_DEPRECATED(text) __attribute__((__deprecated__(text)))
+#elif _OSMO_HAS_ATTRIBUTE_DEPRECATED
+# define OSMO_DEPRECATED(text) __attribute__((__deprecated__))
+#else
+# define OSMO_DEPRECATED(text)
+#endif
+
+#undef _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE
+#undef _OSMO_HAS_ATTRIBUTE_DEPRECATED
+
+/*! @} */
diff --git a/src/shared/libosmocore/include/osmocom/core/endian.h b/src/shared/libosmocore/include/osmocom/core/endian.h
new file mode 100644
index 00000000..ae133c39
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/endian.h
@@ -0,0 +1,49 @@
+#pragma once
+
+/**
+ * GNU and FreeBSD have various ways to express the
+ * endianess but none of them is similiar 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.
+ *
+ * OSMO_IS_LITTLE_ENDIAN
+ * OSMO_IS_BIG_ENDIAN
+ *
+ */
+
+#if defined(__FreeBSD__)
+#include <sys/endian.h>
+ #if BYTE_ORDER == LITTLE_ENDIAN
+ #define OSMO_IS_LITTLE_ENDIAN 1
+ #define OSMO_IS_BIG_ENDIAN 0
+ #elif BYTE_ORDER == BIG_ENDIAN
+ #define OSMO_IS_LITTLE_ENDIAN 0
+ #define OSMO_IS_BIG_ENDIAN 1
+ #else
+ #error "Unknown endian"
+ #endif
+#elif defined(__APPLE__)
+#include <machine/endian.h>
+ #if defined(__DARWIN_LITTLE_ENDIAN)
+ #define OSMO_IS_LITTLE_ENDIAN 1
+ #define OSMO_IS_BIG_ENDIAN 0
+ #elif defined(__DARWIN_BIG_ENDIAN)
+ #define OSMO_IS_LITTLE_ENDIAN 0
+ #define OSMO_IS_BIG_ENDIAN 1
+ #else
+ #error "Unknown endian"
+ #endif
+#else
+#include <endian.h>
+ #if __BYTE_ORDER == __LITTLE_ENDIAN
+ #define OSMO_IS_LITTLE_ENDIAN 1
+ #define OSMO_IS_BIG_ENDIAN 0
+ #elif __BYTE_ORDER == __BIG_ENDIAN
+ #define OSMO_IS_LITTLE_ENDIAN 0
+ #define OSMO_IS_BIG_ENDIAN 1
+ #else
+ #error "Unknown endian"
+ #endif
+#endif
+
diff --git a/src/shared/libosmocore/include/osmocom/core/fsm.h b/src/shared/libosmocore/include/osmocom/core/fsm.h
new file mode 100644
index 00000000..952f82fa
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/fsm.h
@@ -0,0 +1,204 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/utils.h>
+
+/*! \defgroup fsm Finite State Machine abstraction
+ * @{
+ */
+
+/*! \file fsm.h
+ * \brief Finite State Machine
+ */
+
+struct osmo_fsm_inst;
+
+enum osmo_fsm_term_cause {
+ /*! \brief terminate because parent terminated */
+ OSMO_FSM_TERM_PARENT,
+ /*! \brief terminate on explicit user request */
+ OSMO_FSM_TERM_REQUEST,
+ /*! \brief regular termination of process */
+ OSMO_FSM_TERM_REGULAR,
+ /*! \brief erroneous termination of process */
+ OSMO_FSM_TERM_ERROR,
+ /*! \brief termination due to time-out */
+ OSMO_FSM_TERM_TIMEOUT,
+};
+
+extern const struct value_string osmo_fsm_term_cause_names[];
+static inline const char *osmo_fsm_term_cause_name(enum osmo_fsm_term_cause cause)
+{
+ return get_value_string(osmo_fsm_term_cause_names, cause);
+}
+
+
+/*! \brief description of a rule in the FSM */
+struct osmo_fsm_state {
+ /*! \brief bit-mask of permitted input events for this state */
+ uint32_t in_event_mask;
+ /*! \brief bit-mask to which other states this state may transiton */
+ uint32_t out_state_mask;
+ /*! \brief human-readable name of this state */
+ const char *name;
+ /*! \brief function to be called for events arriving in this state */
+ void (*action)(struct osmo_fsm_inst *fi, uint32_t event, void *data);
+ /*! \brief function to be called just after entering the state */
+ void (*onenter)(struct osmo_fsm_inst *fi, uint32_t prev_state);
+ /*! \brief function to be called just before leaving the state */
+ void (*onleave)(struct osmo_fsm_inst *fi, uint32_t next_state);
+};
+
+/*! \brief a description of an osmocom finite state machine */
+struct osmo_fsm {
+ /*! \brief global list */
+ struct llist_head list;
+ /*! \brief list of instances of this FSM */
+ struct llist_head instances;
+ /*! \brief human readable name */
+ const char *name;
+ /*! \brief table of state transition rules */
+ const struct osmo_fsm_state *states;
+ /*! \brief number of entries in \ref states */
+ unsigned int num_states;
+ /*! \brief bit-mask of events permitted in all states */
+ uint32_t allstate_event_mask;
+ /*! \brief function pointer to be called for allstate events */
+ void (*allstate_action)(struct osmo_fsm_inst *fi, uint32_t event, void *data);
+ /*! \brief clean-up function, called during termination */
+ void (*cleanup)(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause);
+ /*! \brief timer call-back for states with time-out.
+ * \returns 1 to request termination, 0 to keep running. */
+ int (*timer_cb)(struct osmo_fsm_inst *fi);
+ /*! \brief logging sub-system for this FSM */
+ int log_subsys;
+ /*! \brief human-readable names of events */
+ const struct value_string *event_names;
+};
+
+/*! \brief a single instanceof an osmocom finite state machine */
+struct osmo_fsm_inst {
+ /*! \brief member in the fsm->instances list */
+ struct llist_head list;
+ /*! \brief back-pointer to the FSM of which we are an instance */
+ struct osmo_fsm *fsm;
+ /*! \brief human readable identifier */
+ const char *id;
+ /*! \brief human readable fully-qualified name */
+ const char *name;
+ /*! \brief some private data of this instance */
+ void *priv;
+ /*! \brief logging level for this FSM */
+ int log_level;
+ /*! \brief current state of the FSM */
+ uint32_t state;
+
+ /*! \brief timer number for states with time-out */
+ int T;
+ /*! \brief timer back-end for states with time-out */
+ struct osmo_timer_list timer;
+
+ /*! \brief support for fsm-based procedures */
+ struct {
+ /*! \brief the parent FSM that has created us */
+ struct osmo_fsm_inst *parent;
+ /*! \brief the event we should send upon termination */
+ uint32_t parent_term_event;
+ /*! \brief a list of children processes */
+ struct llist_head children;
+ /*! \brief \ref llist_head linked to parent->proc.children */
+ struct llist_head child;
+ } proc;
+};
+
+void osmo_fsm_log_addr(bool log_addr);
+
+#define LOGPFSML(fi, level, fmt, args...) \
+ LOGP((fi)->fsm->log_subsys, level, "%s{%s}: " fmt, \
+ osmo_fsm_inst_name(fi), \
+ osmo_fsm_state_name((fi)->fsm, (fi)->state), ## args)
+
+#define LOGPFSM(fi, fmt, args...) \
+ LOGPFSML(fi, (fi)->log_level, fmt, ## args)
+
+#define LOGPFSMLSRC(fi, level, caller_file, caller_line, fmt, args...) \
+ LOGPSRC((fi)->fsm->log_subsys, level, \
+ caller_file, caller_line, \
+ "%s{%s}: " fmt, \
+ osmo_fsm_inst_name(fi), \
+ osmo_fsm_state_name((fi)->fsm, (fi)->state), \
+ ## args)
+
+#define LOGPFSMSRC(fi, caller_file, caller_line, fmt, args...) \
+ LOGPFSMLSRC(fi, (fi)->log_level, \
+ caller_file, caller_line, \
+ fmt, ## args)
+
+int osmo_fsm_register(struct osmo_fsm *fsm);
+void osmo_fsm_unregister(struct osmo_fsm *fsm);
+struct osmo_fsm *osmo_fsm_find_by_name(const char *name);
+struct osmo_fsm_inst *osmo_fsm_inst_alloc(struct osmo_fsm *fsm, void *ctx, void *priv,
+ int log_level, const char *id);
+struct osmo_fsm_inst *osmo_fsm_inst_alloc_child(struct osmo_fsm *fsm,
+ struct osmo_fsm_inst *parent,
+ uint32_t parent_term_event);
+void osmo_fsm_inst_free(struct osmo_fsm_inst *fi);
+
+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);
+
+/*! \brief perform a state change of the given FSM instance
+ *
+ * This is a macro that calls _osmo_fsm_inst_state_chg() with the given
+ * parameters as well as the caller's source file and line number for logging
+ * purposes. See there for documentation.
+ */
+#define osmo_fsm_inst_state_chg(fi, new_state, timeout_secs, T) \
+ _osmo_fsm_inst_state_chg(fi, new_state, timeout_secs, T, \
+ __BASE_FILE__, __LINE__)
+int _osmo_fsm_inst_state_chg(struct osmo_fsm_inst *fi, uint32_t new_state,
+ unsigned long timeout_secs, int T,
+ const char *file, int line);
+
+/*! \brief dispatch an event to an osmocom finite state machine instance
+ *
+ * This is a macro that calls _osmo_fsm_inst_dispatch() with the given
+ * parameters as well as the caller's source file and line number for logging
+ * purposes. See there for documentation.
+ */
+#define osmo_fsm_inst_dispatch(fi, event, data) \
+ _osmo_fsm_inst_dispatch(fi, event, data, __BASE_FILE__, __LINE__)
+int _osmo_fsm_inst_dispatch(struct osmo_fsm_inst *fi, uint32_t event, void *data,
+ const char *file, int line);
+
+/*! \brief Terminate FSM instance with given cause
+ *
+ * This is a macro that calls _osmo_fsm_inst_term() with the given parameters
+ * as well as the caller's source file and line number for logging purposes.
+ * See there for documentation.
+ */
+#define osmo_fsm_inst_term(fi, cause, data) \
+ _osmo_fsm_inst_term(fi, cause, data, __BASE_FILE__, __LINE__)
+void _osmo_fsm_inst_term(struct osmo_fsm_inst *fi,
+ enum osmo_fsm_term_cause cause, void *data,
+ const char *file, int line);
+
+/*! \brief Terminate all child FSM instances of an FSM instance.
+ *
+ * This is a macro that calls _osmo_fsm_inst_term_children() with the given
+ * parameters as well as the caller's source file and line number for logging
+ * purposes. See there for documentation.
+ */
+#define osmo_fsm_inst_term_children(fi, cause, data) \
+ _osmo_fsm_inst_term_children(fi, cause, data, __BASE_FILE__, __LINE__)
+void _osmo_fsm_inst_term_children(struct osmo_fsm_inst *fi,
+ enum osmo_fsm_term_cause cause,
+ void *data,
+ const char *file, int line);
+
+/*! @} */
diff --git a/src/shared/libosmocore/include/osmocom/core/gsmtap.h b/src/shared/libosmocore/include/osmocom/core/gsmtap.h
index 0b647b28..f9d6f0a4 100644
--- a/src/shared/libosmocore/include/osmocom/core/gsmtap.h
+++ b/src/shared/libosmocore/include/osmocom/core/gsmtap.h
@@ -1,5 +1,4 @@
-#ifndef _GSMTAP_H
-#define _GSMTAP_H
+#pragma once
/* gsmtap header, pseudo-header in front of the actua GSM payload */
@@ -43,7 +42,11 @@
#define GSMTAP_TYPE_GMR1_UM 0x0a /* GMR-1 L2 packets */
#define GSMTAP_TYPE_UMTS_RLC_MAC 0x0b
#define GSMTAP_TYPE_UMTS_RRC 0x0c
-
+#define GSMTAP_TYPE_LTE_RRC 0x0d /* LTE interface */
+#define GSMTAP_TYPE_LTE_MAC 0x0e /* LTE MAC interface */
+#define GSMTAP_TYPE_LTE_MAC_FRAMED 0x0f /* LTE MAC with context hdr */
+#define GSMTAP_TYPE_OSMOCORE_LOG 0x10 /* libosmocore logging */
+#define GSMTAP_TYPE_QC_DIAG 0x11 /* Qualcomm DIAG frame */
/* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */
@@ -135,6 +138,15 @@
#define GSMTAP_UMTS_CH_CCCH 0x02
#define GSMTAP_UMTS_CH_DCCH 0x03
+/* sub-types for TYPE_LTE_RRC */
+#define GSMTAP_LTE_CH_BCCH 0x01
+#define GSMTAP_LTE_CH_CCCH 0x02
+#define GSMTAP_LTE_CH_DCCH 0x03
+#define GSMTAP_LTE_CH_MCCH 0x04
+#define GSMTAP_LTE_CH_PCCH 0x05
+#define GSMTAP_LTE_CH_DTCH 0x06
+#define GSMTAP_LTE_CH_MTCH 0x07
+
/* flags for the ARFCN */
#define GSMTAP_ARFCN_F_PCS 0x8000
#define GSMTAP_ARFCN_F_UPLINK 0x4000
@@ -143,24 +155,123 @@
/* IANA-assigned well-known UDP port for GSMTAP messages */
#define GSMTAP_UDP_PORT 4729
+/* UMTS RRC message types */
+enum {
+ GSMTAP_RRC_SUB_DL_DCCH_Message = 0,
+ GSMTAP_RRC_SUB_UL_DCCH_Message,
+ GSMTAP_RRC_SUB_DL_CCCH_Message,
+ GSMTAP_RRC_SUB_UL_CCCH_Message,
+ GSMTAP_RRC_SUB_PCCH_Message,
+ GSMTAP_RRC_SUB_DL_SHCCH_Message,
+ GSMTAP_RRC_SUB_UL_SHCCH_Message,
+ GSMTAP_RRC_SUB_BCCH_FACH_Message,
+ GSMTAP_RRC_SUB_BCCH_BCH_Message,
+ GSMTAP_RRC_SUB_MCCH_Message,
+ GSMTAP_RRC_SUB_MSCH_Message,
+ GSMTAP_RRC_SUB_HandoverToUTRANCommand,
+ GSMTAP_RRC_SUB_InterRATHandoverInfo,
+ GSMTAP_RRC_SUB_SystemInformation_BCH,
+ GSMTAP_RRC_SUB_System_Information_Container,
+ GSMTAP_RRC_SUB_UE_RadioAccessCapabilityInfo,
+ GSMTAP_RRC_SUB_MasterInformationBlock,
+ GSMTAP_RRC_SUB_SysInfoType1,
+ GSMTAP_RRC_SUB_SysInfoType2,
+ GSMTAP_RRC_SUB_SysInfoType3,
+ GSMTAP_RRC_SUB_SysInfoType4,
+ GSMTAP_RRC_SUB_SysInfoType5,
+ GSMTAP_RRC_SUB_SysInfoType5bis,
+ GSMTAP_RRC_SUB_SysInfoType6,
+ GSMTAP_RRC_SUB_SysInfoType7,
+ GSMTAP_RRC_SUB_SysInfoType8,
+ GSMTAP_RRC_SUB_SysInfoType9,
+ GSMTAP_RRC_SUB_SysInfoType10,
+ GSMTAP_RRC_SUB_SysInfoType11,
+ GSMTAP_RRC_SUB_SysInfoType11bis,
+ GSMTAP_RRC_SUB_SysInfoType12,
+ GSMTAP_RRC_SUB_SysInfoType13,
+ GSMTAP_RRC_SUB_SysInfoType13_1,
+ GSMTAP_RRC_SUB_SysInfoType13_2,
+ GSMTAP_RRC_SUB_SysInfoType13_3,
+ GSMTAP_RRC_SUB_SysInfoType13_4,
+ GSMTAP_RRC_SUB_SysInfoType14,
+ GSMTAP_RRC_SUB_SysInfoType15,
+ GSMTAP_RRC_SUB_SysInfoType15bis,
+ GSMTAP_RRC_SUB_SysInfoType15_1,
+ GSMTAP_RRC_SUB_SysInfoType15_1bis,
+ GSMTAP_RRC_SUB_SysInfoType15_2,
+ GSMTAP_RRC_SUB_SysInfoType15_2bis,
+ GSMTAP_RRC_SUB_SysInfoType15_2ter,
+ GSMTAP_RRC_SUB_SysInfoType15_3,
+ GSMTAP_RRC_SUB_SysInfoType15_3bis,
+ GSMTAP_RRC_SUB_SysInfoType15_4,
+ GSMTAP_RRC_SUB_SysInfoType15_5,
+ GSMTAP_RRC_SUB_SysInfoType15_6,
+ GSMTAP_RRC_SUB_SysInfoType15_7,
+ GSMTAP_RRC_SUB_SysInfoType15_8,
+ GSMTAP_RRC_SUB_SysInfoType16,
+ GSMTAP_RRC_SUB_SysInfoType17,
+ GSMTAP_RRC_SUB_SysInfoType18,
+ GSMTAP_RRC_SUB_SysInfoType19,
+ GSMTAP_RRC_SUB_SysInfoType20,
+ GSMTAP_RRC_SUB_SysInfoType21,
+ GSMTAP_RRC_SUB_SysInfoType22,
+ GSMTAP_RRC_SUB_SysInfoTypeSB1,
+ GSMTAP_RRC_SUB_SysInfoTypeSB2,
+ GSMTAP_RRC_SUB_ToTargetRNC_Container,
+ GSMTAP_RRC_SUB_TargetRNC_ToSourceRNC_Container,
+
+ GSMTAP_RRC_SUB_MAX
+};
+
+/* LTE RRC message types */
+enum {
+ GSMTAP_LTE_RRC_SUB_DL_CCCH_Message = 0,
+ GSMTAP_LTE_RRC_SUB_DL_DCCH_Message,
+ GSMTAP_LTE_RRC_SUB_UL_CCCH_Message,
+ GSMTAP_LTE_RRC_SUB_UL_DCCH_Message,
+ GSMTAP_LTE_RRC_SUB_BCCH_BCH_Message,
+ GSMTAP_LTE_RRC_SUB_BCCH_DL_SCH_Message,
+ GSMTAP_LTE_RRC_SUB_PCCH_Message,
+ GSMTAP_LTE_RRC_SUB_MCCH_Message,
+
+ GSMTAP_LTE_RRC_SUB_MAX
+};
+
/* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */
+/*! \brief Structure of the GTMTAP pseudo-header */
struct gsmtap_hdr {
- uint8_t version; /* version, set to 0x01 currently */
- uint8_t hdr_len; /* length in number of 32bit words */
- uint8_t type; /* see GSMTAP_TYPE_* */
- uint8_t timeslot; /* timeslot (0..7 on Um) */
+ uint8_t version; /*!< version, set to 0x01 currently */
+ uint8_t hdr_len; /*!< length in number of 32bit words */
+ uint8_t type; /*!< see GSMTAP_TYPE_* */
+ uint8_t timeslot; /*!< timeslot (0..7 on Um) */
- uint16_t arfcn; /* ARFCN (frequency) */
- int8_t signal_dbm; /* signal level in dBm */
- int8_t snr_db; /* signal/noise ratio in dB */
+ uint16_t arfcn; /*!< ARFCN (frequency) */
+ int8_t signal_dbm; /*!< signal level in dBm */
+ int8_t snr_db; /*!< signal/noise ratio in dB */
- uint32_t frame_number; /* GSM Frame Number (FN) */
+ uint32_t frame_number; /*!< GSM Frame Number (FN) */
- uint8_t sub_type; /* Type of burst/channel, see above */
- uint8_t antenna_nr; /* Antenna Number */
- uint8_t sub_slot; /* sub-slot within timeslot */
- uint8_t res; /* reserved for future use (RFU) */
+ uint8_t sub_type; /*!< Type of burst/channel, see above */
+ uint8_t antenna_nr; /*!< Antenna Number */
+ uint8_t sub_slot; /*!< sub-slot within timeslot */
+ uint8_t res; /*!< reserved for future use (RFU) */
} __attribute__((packed));
-#endif /* _GSMTAP_H */
+/*! \brief Structure of the GTMTAP libosmocore logging header */
+struct gsmtap_osmocore_log_hdr {
+ struct {
+ uint32_t sec;
+ uint32_t usec;
+ } ts;
+ char proc_name[16]; /*!< name of process */
+ uint32_t pid; /*!< process ID */
+ uint8_t level; /*!< logging level */
+ uint8_t _pad[3];
+ /* TODO: color */
+ char subsys[16]; /*!< logging sub-system */
+ struct {
+ char name[32]; /*!< source file name */
+ uint32_t line_nr;/*!< line number */
+ } src_file;
+} __attribute__((packed));
diff --git a/src/shared/libosmocore/include/osmocom/core/gsmtap_util.h b/src/shared/libosmocore/include/osmocom/core/gsmtap_util.h
index 5609381f..2e3d068d 100644
--- a/src/shared/libosmocore/include/osmocom/core/gsmtap_util.h
+++ b/src/shared/libosmocore/include/osmocom/core/gsmtap_util.h
@@ -1,5 +1,4 @@
-#ifndef _GSMTAP_UTIL_H
-#define _GSMTAP_UTIL_H
+#pragma once
#include <stdint.h>
#include <osmocom/core/write_queue.h>
@@ -27,7 +26,9 @@ struct gsmtap_inst {
struct osmo_fd sink_ofd;/*!< \brief file descriptor */
};
-/*! \brief obtain the file descriptor associated with a gsmtap instance */
+/*! \brief 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;
@@ -54,4 +55,4 @@ int gsmtap_send(struct gsmtap_inst *gti, uint16_t arfcn, uint8_t ts,
int8_t signal_dbm, uint8_t snr, const uint8_t *data,
unsigned int len);
-#endif /* _GSMTAP_UTIL_H */
+/*! @} */
diff --git a/src/shared/libosmocore/include/osmocom/core/linuxlist.h b/src/shared/libosmocore/include/osmocom/core/linuxlist.h
index ff2c4915..09f85443 100644
--- a/src/shared/libosmocore/include/osmocom/core/linuxlist.h
+++ b/src/shared/libosmocore/include/osmocom/core/linuxlist.h
@@ -1,5 +1,20 @@
-#ifndef _LINUX_LLIST_H
-#define _LINUX_LLIST_H
+#pragma once
+
+/*! \defgroup linuxlist Simple doubly linked list implementation
+ * @{
+ */
+
+/*!
+ * \file linuxlist.h
+ *
+ * \brief Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole llists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
#include <stddef.h>
@@ -9,20 +24,18 @@
static inline void prefetch(__attribute__((unused)) const void *x) {;}
-/**
- * container_of - cast a member of a structure out to the containing structure
- *
- * @ptr: the pointer to the member.
- * @type: the type of the container struct this is embedded in.
- * @member: the name of the member within the struct.
+/*! \brief cast a member of a structure out to the containing structure
*
+ * \param[in] ptr the pointer to the member.
+ * \param[in] type the type of the container struct this is embedded in.
+ * \param[in] member the name of the member within the struct.
*/
#define container_of(ptr, type, member) ({ \
- const typeof( ((type *)0)->member ) *__mptr = (typeof( ((type *)0)->member ) *)(ptr); \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type, member) );})
-/*
+/*!
* These are non-NULL pointers that will result in page faults
* under normal circumstances, used to verify that nobody uses
* non-initialized llist entries.
@@ -30,31 +43,28 @@ static inline void prefetch(__attribute__((unused)) const void *x) {;}
#define LLIST_POISON1 ((void *) 0x00100100)
#define LLIST_POISON2 ((void *) 0x00200200)
-/*
- * Simple doubly linked llist implementation.
- *
- * Some of the internal functions ("__xxx") are useful when
- * manipulating whole llists rather than single entries, as
- * sometimes we already know the next/prev entries and we can
- * generate better code by using them directly rather than
- * using the generic single-entry routines.
- */
-
+/*! \brief (double) linked list header structure */
struct llist_head {
+ /*! \brief Pointer to next and previous item */
struct llist_head *next, *prev;
};
#define LLIST_HEAD_INIT(name) { &(name), &(name) }
+/*! \brief define a statically-initialized \ref llist_head
+ * \param[in] name Variable name
+ *
+ * This is a helper macro that will define a named variable of type
+ * \ref llist_head and initialize it */
#define LLIST_HEAD(name) \
struct llist_head name = LLIST_HEAD_INIT(name)
+/*! \brief initialize a \ref llist_head to point back to self */
#define INIT_LLIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
-/*
- * Insert a new entry between two known consecutive entries.
+/*! \brief Insert a new entry between two known consecutive entries.
*
* This is only for internal llist manipulation where we know
* the prev/next entries already!
@@ -69,10 +79,9 @@ static inline void __llist_add(struct llist_head *_new,
prev->next = _new;
}
-/**
- * llist_add - add a new entry
- * @new: new entry to be added
- * @head: llist head to add it after
+/*! \brief add a new entry into a linked list (at head)
+ * \param _new New entry to be added
+ * \param head \ref llist_head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
@@ -82,10 +91,9 @@ static inline void llist_add(struct llist_head *_new, struct llist_head *head)
__llist_add(_new, head, head->next);
}
-/**
- * llist_add_tail - add a new entry
- * @new: new entry to be added
- * @head: llist head to add it before
+/*! \brief add a new entry into a linked list (at tail)
+ * \param _new New entry to be added
+ * \param head Head of linked list to whose tail we shall add \a _new
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
@@ -108,9 +116,9 @@ static inline void __llist_del(struct llist_head * prev, struct llist_head * nex
prev->next = next;
}
-/**
- * llist_del - deletes entry from llist.
- * @entry: the element to delete from the llist.
+/*! \brief Delete entry from linked list
+ * \param entry The element to delete from the llist
+ *
* Note: llist_empty on entry does not return true after this, the entry is
* in an undefined state.
*/
@@ -121,9 +129,8 @@ static inline void llist_del(struct llist_head *entry)
entry->prev = (struct llist_head *)LLIST_POISON2;
}
-/**
- * llist_del_init - deletes entry from llist and reinitialize it.
- * @entry: the element to delete from the llist.
+/*! \brief Delete entry from linked list and reinitialize it
+ * \param entry The element to delete from the list
*/
static inline void llist_del_init(struct llist_head *entry)
{
@@ -131,10 +138,9 @@ static inline void llist_del_init(struct llist_head *entry)
INIT_LLIST_HEAD(entry);
}
-/**
- * llist_move - delete from one llist and add as another's head
- * @llist: the entry to move
- * @head: the head that will precede our entry
+/*! \brief Delete from one llist and add as another's head
+ * \param llist The entry to move
+ * \param head The head that will precede our entry
*/
static inline void llist_move(struct llist_head *llist, struct llist_head *head)
{
@@ -142,10 +148,9 @@ static inline void llist_move(struct llist_head *llist, struct llist_head *head)
llist_add(llist, head);
}
-/**
- * llist_move_tail - delete from one llist and add as another's tail
- * @llist: the entry to move
- * @head: the head that will follow our entry
+/*! \brief Delete from one llist and add as another's tail
+ * \param llist The entry to move
+ * \param head The head that will follow our entry
*/
static inline void llist_move_tail(struct llist_head *llist,
struct llist_head *head)
@@ -154,9 +159,9 @@ static inline void llist_move_tail(struct llist_head *llist,
llist_add_tail(llist, head);
}
-/**
- * llist_empty - tests whether a llist is empty
- * @head: the llist to test.
+/*! \brief Test whether a linked list is empty
+ * \param[in] head The llist to test.
+ * \returns 1 if the list is empty, 0 otherwise
*/
static inline int llist_empty(const struct llist_head *head)
{
@@ -177,10 +182,9 @@ static inline void __llist_splice(struct llist_head *llist,
at->prev = last;
}
-/**
- * llist_splice - join two llists
- * @llist: the new llist to add.
- * @head: the place to add it in the first llist.
+/*! \brief Join two llists
+ * \param llist The new linked list to add
+ * \param head The place to add \a llist in the other list
*/
static inline void llist_splice(struct llist_head *llist, struct llist_head *head)
{
@@ -188,10 +192,9 @@ static inline void llist_splice(struct llist_head *llist, struct llist_head *hea
__llist_splice(llist, head);
}
-/**
- * llist_splice_init - join two llists and reinitialise the emptied llist.
- * @llist: the new llist to add.
- * @head: the place to add it in the first llist.
+/*! \brief join two llists and reinitialise the emptied llist.
+ * \param llist The new linked list to add.
+ * \param head The place to add it in the first llist.
*
* The llist at @llist is reinitialised
*/
@@ -204,28 +207,25 @@ static inline void llist_splice_init(struct llist_head *llist,
}
}
-/**
- * llist_entry - get the struct for this entry
- * @ptr: the &struct llist_head pointer.
- * @type: the type of the struct this is embedded in.
- * @member: the name of the llist_struct within the struct.
+/*! \brief Get the struct containing this list entry
+ * \param ptr The \ref llist_head pointer
+ * \param type The type of the struct this is embedded in
+ * \param @member The name of the \ref llist_head within the struct
*/
#define llist_entry(ptr, type, member) \
container_of(ptr, type, member)
-/**
- * llist_for_each - iterate over a llist
- * @pos: the &struct llist_head to use as a loop counter.
- * @head: the head for your llist.
+/*! \brief Iterate over a linked list
+ * \param pos The \ref llist_head to use as a loop counter
+ * \param head The head of the list over which to iterate
*/
#define llist_for_each(pos, head) \
for (pos = (head)->next, prefetch(pos->next); pos != (head); \
pos = pos->next, prefetch(pos->next))
-/**
- * __llist_for_each - iterate over a llist
- * @pos: the &struct llist_head to use as a loop counter.
- * @head: the head for your llist.
+/*! \brief Iterate over a llist (no prefetch)
+ * \param pos The \ref llist_head to use as a loop counter
+ * \param head The head of the list over which to iterate
*
* This variant differs from llist_for_each() in that it's the
* simplest possible llist iteration code, no prefetching is done.
@@ -235,30 +235,27 @@ static inline void llist_splice_init(struct llist_head *llist,
#define __llist_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
-/**
- * llist_for_each_prev - iterate over a llist backwards
- * @pos: the &struct llist_head to use as a loop counter.
- * @head: the head for your llist.
+/*! \brief Iterate over a llist backwards
+ * \param pos The \ref llist_head to use as a loop counter
+ * \param head The head of the list over which to iterate
*/
#define llist_for_each_prev(pos, head) \
for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
pos = pos->prev, prefetch(pos->prev))
-
-/**
- * llist_for_each_safe - iterate over a llist safe against removal of llist entry
- * @pos: the &struct llist_head to use as a loop counter.
- * @n: another &struct llist_head to use as temporary storage
- * @head: the head for your llist.
+
+/*! \brief Iterate over a list; safe against removal of llist entry
+ * \param pos The \ref llist_head to use as a loop counter
+ * \param n Another \ref llist_head to use as temporary storage
+ * \param head The head of the list over which to iterate
*/
#define llist_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
-/**
- * llist_for_each_entry - iterate over llist of given type
- * @pos: the type * to use as a loop counter.
- * @head: the head for your llist.
- * @member: the name of the llist_struct within the struct.
+/*! \brief Iterate over llist of given type
+ * \param pos The 'type *' to use as a loop counter
+ * \param head The head of the list over which to iterate
+ * \param member The name of the \ref llist_head within struct \a pos
*/
#define llist_for_each_entry(pos, head, member) \
for (pos = llist_entry((head)->next, typeof(*pos), member), \
@@ -267,11 +264,10 @@ static inline void llist_splice_init(struct llist_head *llist,
pos = llist_entry(pos->member.next, typeof(*pos), member), \
prefetch(pos->member.next))
-/**
- * llist_for_each_entry_reverse - iterate backwards over llist of given type.
- * @pos: the type * to use as a loop counter.
- * @head: the head for your llist.
- * @member: the name of the llist_struct within the struct.
+/*! \brief Iterate backwards over llist of given type.
+ * \param pos The 'type *' to use as a loop counter
+ * \param head The head of the list over which to iterate
+ * \param member The name of the \ref llist_head within struct \a pos
*/
#define llist_for_each_entry_reverse(pos, head, member) \
for (pos = llist_entry((head)->prev, typeof(*pos), member), \
@@ -280,12 +276,11 @@ static inline void llist_splice_init(struct llist_head *llist,
pos = llist_entry(pos->member.prev, typeof(*pos), member), \
prefetch(pos->member.prev))
-/**
- * llist_for_each_entry_continue - iterate over llist of given type
- * continuing after existing point
- * @pos: the type * to use as a loop counter.
- * @head: the head for your llist.
- * @member: the name of the llist_struct within the struct.
+/*! \brief iterate over llist of given type continuing after existing
+ * point
+ * \param pos The 'type *' to use as a loop counter
+ * \param head The head of the list over which to iterate
+ * \param member The name of the \ref llist_head within struct \a pos
*/
#define llist_for_each_entry_continue(pos, head, member) \
for (pos = llist_entry(pos->member.next, typeof(*pos), member), \
@@ -294,12 +289,12 @@ static inline void llist_splice_init(struct llist_head *llist,
pos = llist_entry(pos->member.next, typeof(*pos), member), \
prefetch(pos->member.next))
-/**
- * llist_for_each_entry_safe - iterate over llist of given type safe against removal of llist entry
- * @pos: the type * to use as a loop counter.
- * @n: another type * to use as temporary storage
- * @head: the head for your llist.
- * @member: the name of the llist_struct within the struct.
+/*! \brief iterate over llist of given type, safe against removal of
+ * non-consecutive(!) llist entries
+ * \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
+ * \param member The name of the \ref llist_head within struct \a pos
*/
#define llist_for_each_entry_safe(pos, n, head, member) \
for (pos = llist_entry((head)->next, typeof(*pos), member), \
@@ -356,5 +351,6 @@ static inline void llist_splice_init(struct llist_head *llist,
for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \
(pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next))
-
-#endif
+/*!
+ * }@
+ */
diff --git a/src/shared/libosmocore/include/osmocom/core/linuxrbtree.h b/src/shared/libosmocore/include/osmocom/core/linuxrbtree.h
index 079f440d..d3f9fd12 100644
--- a/src/shared/libosmocore/include/osmocom/core/linuxrbtree.h
+++ b/src/shared/libosmocore/include/osmocom/core/linuxrbtree.h
@@ -14,7 +14,8 @@
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
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301, USA.
linux/include/linux/rbtree.h
@@ -91,8 +92,7 @@ static inline struct page * rb_insert_page_cache(struct inode * inode,
-----------------------------------------------------------------------
*/
-#ifndef _LINUX_RBTREE_H
-#define _LINUX_RBTREE_H
+#pragma once
#include <stdlib.h>
@@ -156,5 +156,3 @@ static inline void rb_link_node(struct rb_node * node, struct rb_node * parent,
*rb_link = node;
}
-
-#endif /* _LINUX_RBTREE_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/logging.h b/src/shared/libosmocore/include/osmocom/core/logging.h
index 655f7a44..fe9ae93f 100644
--- a/src/shared/libosmocore/include/osmocom/core/logging.h
+++ b/src/shared/libosmocore/include/osmocom/core/logging.h
@@ -1,5 +1,4 @@
-#ifndef _OSMOCORE_LOGGING_H
-#define _OSMOCORE_LOGGING_H
+#pragma once
/*! \defgroup logging Osmocom logging framework
* @{
@@ -10,6 +9,7 @@
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
+#include <stdbool.h>
#include <osmocom/core/linuxlist.h>
/*! \brief Maximum number of logging contexts */
@@ -20,8 +20,23 @@
#define DEBUG
#ifdef DEBUG
-#define DEBUGP(ss, fmt, args...) logp(ss, __FILE__, __LINE__, 0, fmt, ## args)
-#define DEBUGPC(ss, fmt, args...) logp(ss, __FILE__, __LINE__, 1, fmt, ## args)
+/*! \brief Log a debug message through the Osmocom logging framework
+ * \param[in] ss logging subsystem (e.g. \ref DLGLOBAL)
+ * \param[in] fmt format string
+ * \param[in] args variable argument list
+ */
+#define DEBUGP(ss, fmt, args...) \
+ do { \
+ if (log_check_level(ss, LOGL_DEBUG)) \
+ logp(ss, __BASE_FILE__, __LINE__, 0, fmt, ## args); \
+ } while(0)
+
+#define DEBUGPC(ss, fmt, args...) \
+ do { \
+ if (log_check_level(ss, LOGL_DEBUG)) \
+ logp(ss, __BASE_FILE__, __LINE__, 1, fmt, ## args); \
+ } while(0)
+
#else
#define DEBUGP(xss, fmt, args...)
#define DEBUGPC(ss, fmt, args...)
@@ -40,7 +55,7 @@ void logp(int subsys, const char *file, int line, int cont, const char *format,
* \param[in] args variable argument list
*/
#define LOGP(ss, level, fmt, args...) \
- logp2(ss, level, __FILE__, __LINE__, 0, fmt, ##args)
+ LOGPSRC(ss, level, NULL, 0, fmt, ## args)
/*! \brief Continue a log message through the Osmocom logging framework
* \param[in] ss logging subsystem (e.g. \ref DLGLOBAL)
@@ -49,11 +64,36 @@ void logp(int subsys, const char *file, int line, int cont, const char *format,
* \param[in] args variable argument list
*/
#define LOGPC(ss, level, fmt, args...) \
- logp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args)
+ do { \
+ if (log_check_level(ss, level)) \
+ logp2(ss, level, __BASE_FILE__, __LINE__, 1, fmt, ##args); \
+ } while(0)
+
+/*! \brief Log through the Osmocom logging framework with explicit source.
+ * If caller_file is passed as NULL, __BASE_FILE__ and __LINE__ are used
+ * instead of caller_file and caller_line (so that this macro here defines
+ * both cases in the same place, and to catch cases where callers fail to pass
+ * a non-null filename string).
+ * \param[in] ss logging subsystem (e.g. \ref DLGLOBAL)
+ * \param[in] level logging level (e.g. \ref LOGL_NOTICE)
+ * \param[in] caller_file caller's source file string (e.g. __BASE_FILE__)
+ * \param[in] caller_line caller's source line nr (e.g. __LINE__)
+ * \param[in] fmt format string
+ * \param[in] args variable argument list
+ */
+#define LOGPSRC(ss, level, caller_file, caller_line, fmt, args...) \
+ do { \
+ if (log_check_level(ss, level)) {\
+ if (caller_file) \
+ logp2(ss, level, caller_file, caller_line, 0, fmt, ##args); \
+ else \
+ logp2(ss, level, __BASE_FILE__, __LINE__, 0, fmt, ##args); \
+ }\
+ } while(0)
/*! \brief different log levels */
#define LOGL_DEBUG 1 /*!< \brief debugging information */
-#define LOGL_INFO 3
+#define LOGL_INFO 3 /*!< \brief general information */
#define LOGL_NOTICE 5 /*!< \brief abnormal/unexpected condition */
#define LOGL_ERROR 7 /*!< \brief error condition, requires user action */
#define LOGL_FATAL 8 /*!< \brief fatal, program aborted */
@@ -61,18 +101,24 @@ void logp(int subsys, const char *file, int line, int cont, const char *format,
#define LOG_FILTER_ALL 0x0001
/* logging levels defined by the library itself */
-#define DLGLOBAL -1
-#define DLLAPD -2
-#define DLINP -3
-#define DLMUX -4
-#define DLMI -5
-#define DLMIB -6
-#define DLSMS -7
-#define OSMO_NUM_DLIB 7
-
+#define DLGLOBAL -1 /*!< global logging */
+#define DLLAPD -2 /*!< LAPD implementation */
+#define DLINP -3 /*!< (A-bis) Input sub-system */
+#define DLMUX -4 /*!< Osmocom Multiplex (Osmux) */
+#define DLMI -5 /*!< ISDN-layer below input sub-system */
+#define DLMIB -6 /*!< ISDN layer B-channel */
+#define DLSMS -7 /*!< SMS sub-system */
+#define DLCTRL -8 /*!< Control Interface */
+#define DLGTP -9 /*!< GTP (GPRS Tunneling Protocol */
+#define DLSTATS -10 /*!< Statistics */
+#define DLGSUP -11 /*!< Generic Subscriber Update Protocol */
+#define DLOAP -12 /*!< Osmocom Authentication Protocol */
+#define OSMO_NUM_DLIB 12 /*!< Number of logging sub-systems in libraries */
+
+/*! Configuration of singgle log category / sub-system */
struct log_category {
- uint8_t loglevel;
- uint8_t enabled;
+ uint8_t loglevel; /*!< configured log-level */
+ uint8_t enabled; /*!< is logging enabled? */
};
/*! \brief Information regarding one logging category */
@@ -95,6 +141,18 @@ struct log_target;
typedef int log_filter(const struct log_context *ctx,
struct log_target *target);
+struct log_info;
+struct vty;
+struct gsmtap_inst;
+
+typedef void log_print_filters(struct vty *vty,
+ const struct log_info *info,
+ const struct log_target *tgt);
+
+typedef void log_save_filters(struct vty *vty,
+ const struct log_info *info,
+ const struct log_target *tgt);
+
/*! \brief Logging configuration, passed to \ref log_init */
struct log_info {
/* \brief filter callback function */
@@ -106,6 +164,11 @@ struct log_info {
unsigned int num_cat;
/*! \brief total number of user categories (not library) */
unsigned int num_cat_user;
+
+ /*! \brief filter saving function */
+ log_save_filters *save_fn;
+ /*! \brief filter saving function */
+ log_print_filters *print_fn;
};
/*! \brief Type of logging target */
@@ -114,6 +177,8 @@ enum log_target_type {
LOG_TGT_TYPE_SYSLOG, /*!< \brief syslog based logging */
LOG_TGT_TYPE_FILE, /*!< \brief text file logging */
LOG_TGT_TYPE_STDERR, /*!< \brief stderr logging */
+ LOG_TGT_TYPE_STRRB, /*!< \brief osmo_strrb-backed logging */
+ LOG_TGT_TYPE_GSMTAP, /*!< \brief GSMTAP network logging */
};
/*! \brief structure representing a logging target */
@@ -136,6 +201,10 @@ struct log_target {
unsigned int print_timestamp:1;
/*! \brief should log messages be prefixed with a filename? */
unsigned int print_filename:1;
+ /*! \brief should log messages be prefixed with a category name? */
+ unsigned int print_category:1;
+ /*! \brief should log messages be prefixed with an extended timestamp? */
+ unsigned int print_ext_timestamp:1;
/*! \brief the type of this log taget */
enum log_target_type type;
@@ -154,16 +223,42 @@ struct log_target {
struct {
void *vty;
} tgt_vty;
+
+ struct {
+ void *rb;
+ } tgt_rb;
+
+ struct {
+ struct gsmtap_inst *gsmtap_inst;
+ const char *ident;
+ const char *hostname;
+ } tgt_gsmtap;
};
/*! \brief call-back function to be called when the logging framework
- * wants to log somethnig.
- * \param[[in] target logging target
+ * wants to log a fully formatted string
+ * \param[in] target logging target
* \param[in] level log level of currnet message
* \param[in] string the string that is to be written to the log
*/
void (*output) (struct log_target *target, unsigned int level,
const char *string);
+
+ /*! \brief alternative call-back function to which the logging
+ * framework passes the unfortmatted input arguments,
+ * i.e. bypassing the internal string formatter
+ * \param[in] target logging target
+ * \param[in] subsys logging sub-system
+ * \param[in] level logging level
+ * \param[in] file soure code file name
+ * \param[in] line source code file line number
+ * \param[in] cont continuation of previous statement?
+ * \param[in] format format string
+ * \param[in] ap vararg list of printf arguments
+ */
+ void (*raw_output)(struct log_target *target, int subsys,
+ unsigned int level, const char *file, int line,
+ int cont, const char *format, va_list ap);
};
/* use the above macros */
@@ -171,6 +266,8 @@ void logp2(int subsys, unsigned int level, const char *file,
int line, int cont, const char *format, ...)
__attribute__ ((format (printf, 6, 7)));
int log_init(const struct log_info *inf, void *talloc_ctx);
+void log_fini(void);
+int log_check_level(int subsys, unsigned int level);
/* context management */
void log_reset_context(void);
@@ -180,10 +277,13 @@ int log_set_context(uint8_t ctx, void *value);
void log_set_all_filter(struct log_target *target, int);
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);
void log_set_print_filename(struct log_target *target, int);
+void log_set_print_category(struct log_target *target, int);
void log_set_log_level(struct log_target *target, int log_level);
void log_parse_category_mask(struct log_target *target, const char* mask);
+const char* log_category_name(int subsys);
int log_parse_level(const char *lvl);
const char *log_level_str(unsigned int lvl);
int log_parse_category(const char *category);
@@ -197,7 +297,12 @@ struct log_target *log_target_create_stderr(void);
struct log_target *log_target_create_file(const char *fname);
struct log_target *log_target_create_syslog(const char *ident, int option,
int facility);
+struct log_target *log_target_create_gsmtap(const char *host, uint16_t port,
+ const char *ident,
+ bool ofd_wq_mode,
+ bool add_sink);
int log_target_file_reopen(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);
@@ -210,5 +315,3 @@ struct log_target *log_target_find(int type, const char *fname);
extern struct llist_head osmo_log_target_list;
/*! @} */
-
-#endif /* _OSMOCORE_LOGGING_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/loggingrb.h b/src/shared/libosmocore/include/osmocom/core/loggingrb.h
new file mode 100644
index 00000000..dcd7917c
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/loggingrb.h
@@ -0,0 +1,37 @@
+#pragma once
+
+/* (C) 2012-2013 by Katerina Barone-Adesi <kat.obsc@gmail.com>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+/*! \defgroup loggingrb Osmocom ringbuffer-backed logging
+ * @{
+ */
+
+/*! \file loggingrb.h
+ */
+
+struct log_info;
+
+size_t log_target_rb_used_size(struct log_target const *target);
+size_t log_target_rb_avail_size(struct log_target const *target);
+const char *log_target_rb_get(struct log_target const *target, size_t logindex);
+struct log_target *log_target_create_rb(size_t size);
+
+/*! @} */
diff --git a/src/shared/libosmocore/include/osmocom/core/macaddr.h b/src/shared/libosmocore/include/osmocom/core/macaddr.h
new file mode 100644
index 00000000..8de62382
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/macaddr.h
@@ -0,0 +1,4 @@
+#pragma once
+
+int osmo_macaddr_parse(uint8_t *out, const char *in);
+int osmo_get_macaddr(uint8_t *mac_out, const char *dev_name);
diff --git a/src/shared/libosmocore/include/osmocom/core/msgb.h b/src/shared/libosmocore/include/osmocom/core/msgb.h
index a1939ab6..9cb1c24d 100644
--- a/src/shared/libosmocore/include/osmocom/core/msgb.h
+++ b/src/shared/libosmocore/include/osmocom/core/msgb.h
@@ -1,5 +1,4 @@
-#ifndef _MSGB_H
-#define _MSGB_H
+#pragma once
/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
@@ -23,6 +22,8 @@
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/utils.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/core/defs.h>
/*! \defgroup msgb Message buffers
* @{
@@ -73,6 +74,11 @@ extern void msgb_enqueue(struct llist_head *queue, struct msgb *msg);
extern struct msgb *msgb_dequeue(struct llist_head *queue);
extern void msgb_reset(struct msgb *m);
uint16_t msgb_length(const struct msgb *msg);
+extern const char *msgb_hexdump(const struct msgb *msg);
+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);
+static int msgb_test_invariant(const struct msgb *msg) __attribute__((pure));
#ifdef MSGB_DEBUG
#include <osmocom/core/panic.h>
@@ -180,7 +186,7 @@ static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len)
{
unsigned char *tmp = msgb->tail;
if (msgb_tailroom(msgb) < (int) len)
- MSGB_ABORT(msgb, "Not enough tailroom msgb_push (%u < %u)\n",
+ MSGB_ABORT(msgb, "Not enough tailroom msgb_put (%u < %u)\n",
msgb_tailroom(msgb), len);
msgb->tail += len;
msgb->len += len;
@@ -204,8 +210,7 @@ static inline void msgb_put_u8(struct msgb *msgb, uint8_t word)
static inline void msgb_put_u16(struct msgb *msgb, uint16_t word)
{
uint8_t *space = msgb_put(msgb, 2);
- space[0] = word >> 8 & 0xFF;
- space[1] = word & 0xFF;
+ osmo_store16be(word, space);
}
/*! \brief append a uint32 value to the end of the message
@@ -215,10 +220,7 @@ static inline void msgb_put_u16(struct msgb *msgb, uint16_t word)
static inline void msgb_put_u32(struct msgb *msgb, uint32_t word)
{
uint8_t *space = msgb_put(msgb, 4);
- space[0] = word >> 24 & 0xFF;
- space[1] = word >> 16 & 0xFF;
- space[2] = word >> 8 & 0xFF;
- space[3] = word & 0xFF;
+ osmo_store32be(word, space);
}
/*! \brief remove data from end of message
@@ -227,7 +229,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)
{
- unsigned char *tmp = msgb->data - len;
+ unsigned char *tmp = msgb->tail - len;
if (msgb_length(msgb) < len)
MSGB_ABORT(msgb, "msgb too small to get %u (len %u)\n",
len, msgb_length(msgb));
@@ -235,6 +237,7 @@ static inline unsigned char *msgb_get(struct msgb *msgb, unsigned int len)
msgb->len -= len;
return tmp;
}
+
/*! \brief remove uint8 from end of message
* \param[in] msgb message buffer
* \returns 8bit value taken from end of msgb
@@ -244,6 +247,7 @@ static inline uint8_t msgb_get_u8(struct msgb *msgb)
uint8_t *space = msgb_get(msgb, 1);
return space[0];
}
+
/*! \brief remove uint16 from end of message
* \param[in] msgb message buffer
* \returns 16bit value taken from end of msgb
@@ -251,8 +255,9 @@ static inline uint8_t msgb_get_u8(struct msgb *msgb)
static inline uint16_t msgb_get_u16(struct msgb *msgb)
{
uint8_t *space = msgb_get(msgb, 2);
- return space[0] << 8 | space[1];
+ return osmo_load16be(space);
}
+
/*! \brief remove uint32 from end of message
* \param[in] msgb message buffer
* \returns 32bit value taken from end of msgb
@@ -260,7 +265,7 @@ static inline uint16_t msgb_get_u16(struct msgb *msgb)
static inline uint32_t msgb_get_u32(struct msgb *msgb)
{
uint8_t *space = msgb_get(msgb, 4);
- return space[0] << 24 | space[1] << 16 | space[2] << 8 | space[3];
+ return osmo_load32be(space);
}
/*! \brief prepend (push) some data to start of message
@@ -284,6 +289,37 @@ static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int len)
msgb->len += len;
return msgb->data;
}
+
+/*! \brief prepend a uint8 value to the head of the message
+ * \param[in] msgb message buffer
+ * \param[in] word unsigned 8bit byte to be prepended
+ */
+static inline void msgb_push_u8(struct msgb *msg, uint8_t word)
+{
+ uint8_t *space = msgb_push(msg, 1);
+ space[0] = word;
+}
+
+/*! \brief prepend a uint16 value to the head of the message
+ * \param[in] msgb message buffer
+ * \param[in] word unsigned 16bit byte to be prepended
+ */
+static inline void msgb_push_u16(struct msgb *msg, uint16_t word)
+{
+ uint16_t *space = (uint16_t *) msgb_push(msg, 2);
+ osmo_store16be(word, space);
+}
+
+/*! \brief prepend a uint32 value to the head of the message
+ * \param[in] msgb message buffer
+ * \param[in] word unsigned 32bit byte to be prepended
+ */
+static inline void msgb_push_u32(struct msgb *msg, uint32_t word)
+{
+ uint32_t *space = (uint32_t *) msgb_push(msg, 4);
+ osmo_store32be(word, space);
+}
+
/*! \brief remove (pull) a header from the front of the message buffer
* \param[in] msgb message buffer
* \param[in] len number of octets to be pulled
@@ -299,6 +335,21 @@ static inline unsigned char *msgb_pull(struct msgb *msgb, unsigned int len)
return msgb->data += len;
}
+/*! \brief remove (pull) all headers in front of l3h from the message buffer.
+ * \param[in] msgb message buffer with a valid l3h
+ * \returns pointer to new start of msgb (l3h)
+ *
+ * This function moves the \a data pointer of the \ref msgb further back
+ * in the message, thereby shrinking the size of the message.
+ * l1h and l2h will be cleared.
+ */
+static inline unsigned char *msgb_pull_to_l3(struct msgb *msg)
+{
+ unsigned char *ret = msgb_pull(msg, msg->l3h - msg->data);
+ msg->l1h = msg->l2h = NULL;
+ return ret;
+}
+
/*! \brief remove uint8 from front of message
* \param[in] msgb message buffer
* \returns 8bit value taken from end of msgb
@@ -308,6 +359,7 @@ static inline uint8_t msgb_pull_u8(struct msgb *msgb)
uint8_t *space = msgb_pull(msgb, 1) - 1;
return space[0];
}
+
/*! \brief remove uint16 from front of message
* \param[in] msgb message buffer
* \returns 16bit value taken from end of msgb
@@ -315,8 +367,9 @@ static inline uint8_t msgb_pull_u8(struct msgb *msgb)
static inline uint16_t msgb_pull_u16(struct msgb *msgb)
{
uint8_t *space = msgb_pull(msgb, 2) - 2;
- return space[0] << 8 | space[1];
+ return osmo_load16be(space);
}
+
/*! \brief remove uint32 from front of message
* \param[in] msgb message buffer
* \returns 32bit value taken from end of msgb
@@ -324,7 +377,7 @@ static inline uint16_t msgb_pull_u16(struct msgb *msgb)
static inline uint32_t msgb_pull_u32(struct msgb *msgb)
{
uint8_t *space = msgb_pull(msgb, 4) - 4;
- return space[0] << 24 | space[1] << 16 | space[2] << 8 | space[3];
+ return osmo_load32be(space);
}
/*! \brief Increase headroom of empty msgb, reducing the tailroom
@@ -351,6 +404,8 @@ static inline void msgb_reserve(struct msgb *msg, int len)
*/
static inline int msgb_trim(struct msgb *msg, int len)
{
+ if (len < 0)
+ MSGB_ABORT(msg, "Negative length is not allowed\n");
if (len > msg->data_len)
return -1;
@@ -361,7 +416,7 @@ static inline int msgb_trim(struct msgb *msg, int len)
}
/*! \brief Trim the msgb to a given layer3 length
- * \pram[in] msg message buffer
+ * \param[in] msg message buffer
* \param[in] l3len new layer3 length
* \returns 0 in case of success, negative in case of error
*/
@@ -391,11 +446,50 @@ static inline struct msgb *msgb_alloc_headroom(int size, int headroom,
return msg;
}
+/*! \brief Check a message buffer for consistency
+ * \param[in] msg message buffer
+ * \returns 0 (false) if inconsistent, != 0 (true) otherwise
+ */
+static inline int msgb_test_invariant(const struct msgb *msg)
+{
+ const unsigned char *lbound;
+ if (!msg || !msg->data || !msg->tail ||
+ (msg->data + msg->len != msg->tail) ||
+ (msg->data < msg->head) ||
+ (msg->tail > msg->head + msg->data_len))
+ return 0;
+
+ lbound = msg->head;
+
+ if (msg->l1h) {
+ if (msg->l1h < lbound)
+ return 0;
+ lbound = msg->l1h;
+ }
+ if (msg->l2h) {
+ if (msg->l2h < lbound)
+ return 0;
+ lbound = msg->l2h;
+ }
+ if (msg->l3h) {
+ if (msg->l3h < lbound)
+ return 0;
+ lbound = msg->l3h;
+ }
+ if (msg->l4h) {
+ if (msg->l4h < lbound)
+ return 0;
+ lbound = msg->l4h;
+ }
+
+ return lbound <= msg->head + msg->data_len;
+}
+
/* non inline functions to ease binding */
uint8_t *msgb_data(const struct msgb *msg);
-void msgb_set_talloc_ctx(void *ctx);
-/*! @} */
+void *msgb_talloc_ctx_init(void *root_ctx, unsigned int pool_size);
+void msgb_set_talloc_ctx(void *ctx) OSMO_DEPRECATED("Use msgb_talloc_ctx_init() instead");
-#endif /* _MSGB_H */
+/*! @} */
diff --git a/src/shared/libosmocore/include/osmocom/core/msgfile.h b/src/shared/libosmocore/include/osmocom/core/msgfile.h
index c5e67a45..cab97b22 100644
--- a/src/shared/libosmocore/include/osmocom/core/msgfile.h
+++ b/src/shared/libosmocore/include/osmocom/core/msgfile.h
@@ -19,8 +19,7 @@
*
*/
-#ifndef MSG_FILE_H
-#define MSG_FILE_H
+#pragma once
#include <osmocom/core/linuxlist.h>
@@ -45,5 +44,3 @@ struct osmo_config_list {
};
struct osmo_config_list* osmo_config_list_parse(void *ctx, const char *filename);
-
-#endif
diff --git a/src/shared/libosmocore/include/osmocom/core/panic.h b/src/shared/libosmocore/include/osmocom/core/panic.h
index fd5cf208..5d575c4b 100644
--- a/src/shared/libosmocore/include/osmocom/core/panic.h
+++ b/src/shared/libosmocore/include/osmocom/core/panic.h
@@ -1,5 +1,4 @@
-#ifndef OSMOCORE_PANIC_H
-#define OSMOCORE_PANIC_H
+#pragma once
/*! \addtogroup utils
* @{
@@ -16,5 +15,3 @@ extern void osmo_panic(const char *fmt, ...);
extern void osmo_set_panic_handler(osmo_panic_handler_t h);
/*! @} */
-
-#endif /* OSMOCORE_PANIC_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/plugin.h b/src/shared/libosmocore/include/osmocom/core/plugin.h
index 6c0eccc6..aef1dfc9 100644
--- a/src/shared/libosmocore/include/osmocom/core/plugin.h
+++ b/src/shared/libosmocore/include/osmocom/core/plugin.h
@@ -1,6 +1,3 @@
-#ifndef _OSMO_PLUGIN_H
-#define _OSMO_PLUGIN_H
+#pragma once
int osmo_plugin_load_all(const char *directory);
-
-#endif
diff --git a/src/shared/libosmocore/include/osmocom/core/prim.h b/src/shared/libosmocore/include/osmocom/core/prim.h
index b1026fe3..99a71d5d 100644
--- a/src/shared/libosmocore/include/osmocom/core/prim.h
+++ b/src/shared/libosmocore/include/osmocom/core/prim.h
@@ -1,11 +1,10 @@
-#ifndef OSMO_PRIMITIVE_H
-#define OSMO_PRIMITIVE_H
+#pragma once
/*! \defgroup prim Osmocom primitives
* @{
*/
-/*! \file prim.c */
+/*! \file prim.h */
#include <stdint.h>
#include <osmocom/core/msgb.h>
@@ -18,13 +17,16 @@ enum osmo_prim_operation {
PRIM_OP_REQUEST, /*!< \brief request */
PRIM_OP_RESPONSE, /*!< \brief response */
PRIM_OP_INDICATION, /*!< \brief indication */
- PRIM_OP_CONFIRM, /*!< \brief cofirm */
+ PRIM_OP_CONFIRM, /*!< \brief confirm */
};
+extern const struct value_string osmo_prim_op_names[5];
+
#define _SAP_GSM_SHIFT 24
#define _SAP_GSM_BASE (0x01 << _SAP_GSM_SHIFT)
#define _SAP_TETRA_BASE (0x02 << _SAP_GSM_SHIFT)
+#define _SAP_SS7_BASE (0x03 << _SAP_GSM_SHIFT)
/*! \brief primitive header */
struct osmo_prim_hdr {
@@ -37,7 +39,7 @@ struct osmo_prim_hdr {
/*! \brief initialize a primitive header
* \param[in,out] oph primitive header
* \param[in] sap Service Access Point
- * \param[in] primtive Primitive Number
+ * \param[in] primitive Primitive Number
* \param[in] operation Primitive Operation (REQ/RESP/IND/CONF)
* \param[in] msg Message
*/
@@ -55,4 +57,4 @@ osmo_prim_init(struct osmo_prim_hdr *oph, unsigned int sap,
/*! \brief primitive handler callback type */
typedef int (*osmo_prim_cb)(struct osmo_prim_hdr *oph, void *ctx);
-#endif /* OSMO_PRIMITIVE_H */
+/*! @} */
diff --git a/src/shared/libosmocore/include/osmocom/core/rate_ctr.h b/src/shared/libosmocore/include/osmocom/core/rate_ctr.h
index 24577fdf..ebaa7a71 100644
--- a/src/shared/libosmocore/include/osmocom/core/rate_ctr.h
+++ b/src/shared/libosmocore/include/osmocom/core/rate_ctr.h
@@ -1,5 +1,4 @@
-#ifndef _RATE_CTR_H
-#define _RATE_CTR_H
+#pragma once
/*! \defgroup rate_ctr Rate counters
* @{
@@ -31,6 +30,7 @@ struct rate_ctr_per_intv {
/*! \brief data we keep for each actual value */
struct rate_ctr {
uint64_t current; /*!< \brief current value */
+ uint64_t previous; /*!< \brief previous value, used for delta */
/*! \brief per-interval data */
struct rate_ctr_per_intv intv[RATE_CTR_INTV_NUM];
};
@@ -47,6 +47,8 @@ struct rate_ctr_group_desc {
const char *group_name_prefix;
/*! \brief The human-readable description of the group */
const char *group_description;
+ /*! \brief The class to which this group belongs */
+ int class_id;
/*! \brief The number of counters in this group */
const unsigned int num_ctr;
/*! \brief Pointer to array of counter names */
@@ -69,20 +71,46 @@ struct rate_ctr_group *rate_ctr_group_alloc(void *ctx,
const struct rate_ctr_group_desc *desc,
unsigned int idx);
+static inline void rate_ctr_group_upd_idx(struct rate_ctr_group *grp, unsigned int idx)
+{
+ grp->idx = idx;
+}
+
void rate_ctr_group_free(struct rate_ctr_group *grp);
+/*! \brief Increment the counter by \a inc
+ * \param ctr \ref rate_ctr to increment
+ * \param inc quantity to increment \a ctr by */
void rate_ctr_add(struct rate_ctr *ctr, int inc);
-/*! \brief Increment the counter by 1 */
+/*! \brief Increment the counter by 1
+ * \param ctr \ref rate_ctr to increment */
static inline void rate_ctr_inc(struct rate_ctr *ctr)
{
rate_ctr_add(ctr, 1);
}
+/*! \brief Return the counter difference since the last call to this function */
+int64_t rate_ctr_difference(struct rate_ctr *ctr);
+
int rate_ctr_init(void *tall_ctx);
struct rate_ctr_group *rate_ctr_get_group_by_name_idx(const char *name, const unsigned int idx);
const struct rate_ctr *rate_ctr_get_by_name(const struct rate_ctr_group *ctrg, const char *name);
+typedef int (*rate_ctr_handler_t)(
+ struct rate_ctr_group *, struct rate_ctr *,
+ const struct rate_ctr_desc *, void *);
+typedef int (*rate_ctr_group_handler_t)(struct rate_ctr_group *, void *);
+
+
+/*! \brief Iterate over all counters
+ * \param[in] handle_item Call-back function, aborts if rc < 0
+ * \param[in] data Private data handed through to \a handle_counter
+ */
+int rate_ctr_for_each_counter(struct rate_ctr_group *ctrg,
+ rate_ctr_handler_t handle_counter, void *data);
+
+int rate_ctr_for_each_group(rate_ctr_group_handler_t handle_group, void *data);
+
/*! @} */
-#endif /* RATE_CTR_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/select.h b/src/shared/libosmocore/include/osmocom/core/select.h
index efdd716f..2753637a 100644
--- a/src/shared/libosmocore/include/osmocom/core/select.h
+++ b/src/shared/libosmocore/include/osmocom/core/select.h
@@ -1,5 +1,4 @@
-#ifndef _BSC_SELECT_H
-#define _BSC_SELECT_H
+#pragma once
#include <osmocom/core/linuxlist.h>
@@ -40,6 +39,12 @@ int osmo_fd_register(struct osmo_fd *fd);
void osmo_fd_unregister(struct osmo_fd *fd);
int osmo_select_main(int polling);
-/*! @} */
+struct osmo_fd *osmo_fd_get_by_fd(int fd);
+
+/*
+ * foreign event loop integration
+ */
+int osmo_fd_fill_fds(void *readset, void *writeset, void *exceptset);
+int osmo_fd_disp_fds(void *readset, void *writeset, void *exceptset);
-#endif /* _BSC_SELECT_H */
+/*! @} */
diff --git a/src/shared/libosmocore/include/osmocom/core/serial.h b/src/shared/libosmocore/include/osmocom/core/serial.h
index 889bd8a1..e3f7a9ff 100644
--- a/src/shared/libosmocore/include/osmocom/core/serial.h
+++ b/src/shared/libosmocore/include/osmocom/core/serial.h
@@ -25,11 +25,10 @@
*/
/*! \file serial.h
- * \file Osmocom serial port helpers
+ * Osmocom serial port helpers
*/
-#ifndef __OSMO_SERIAL_H__
-#define __OSMO_SERIAL_H__
+#pragma once
#include <termios.h>
@@ -39,5 +38,3 @@ int osmo_serial_set_custom_baudrate(int fd, int baudrate);
int osmo_serial_clear_custom_baudrate(int fd);
/*! @} */
-
-#endif /* __OSMO_SERIAL_H__ */
diff --git a/src/shared/libosmocore/include/osmocom/core/signal.h b/src/shared/libosmocore/include/osmocom/core/signal.h
index b3a5aaee..5ed4e151 100644
--- a/src/shared/libosmocore/include/osmocom/core/signal.h
+++ b/src/shared/libosmocore/include/osmocom/core/signal.h
@@ -1,5 +1,4 @@
-#ifndef OSMO_SIGNAL_H
-#define OSMO_SIGNAL_H
+#pragma once
#include <stdint.h>
@@ -8,9 +7,9 @@
*/
/*! \file signal.h */
-/* subsystem signaling numbers: we split the numberspace for applications and
- * libraries: from 0 to UINT_MAX/2 for applications, from UINT_MAX/2 to
- * UINT_MAX for libraries. */
+/*! subsystem signaling numbers: we split the numberspace for
+ * applications and libraries: from 0 to UINT_MAX/2 for applications,
+ * from UINT_MAX/2 to UINT_MAX for libraries. */
#define OSMO_SIGNAL_SS_APPS 0
#define OSMO_SIGNAL_SS_RESERVED 2147483648u
@@ -19,6 +18,7 @@ enum {
SS_L_GLOBAL = OSMO_SIGNAL_SS_RESERVED,
SS_L_INPUT,
SS_L_NS,
+ SS_L_VTY,
};
/* application-defined signal types. */
@@ -42,5 +42,3 @@ void osmo_signal_unregister_handler(unsigned int subsys, osmo_signal_cbfn *cbfn,
void osmo_signal_dispatch(unsigned int subsys, unsigned int signal, void *signal_data);
/*! @} */
-
-#endif /* OSMO_SIGNAL_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/socket.h b/src/shared/libosmocore/include/osmocom/core/socket.h
index f15a03a9..6ef0912e 100644
--- a/src/shared/libosmocore/include/osmocom/core/socket.h
+++ b/src/shared/libosmocore/include/osmocom/core/socket.h
@@ -1,5 +1,4 @@
-#ifndef _OSMOCORE_SOCKET_H
-#define _OSMOCORE_SOCKET_H
+#pragma once
/*! \defgroup socket Socket convenience functions
* @{
@@ -15,8 +14,11 @@ struct sockaddr;
struct osmo_fd;
/* flags for osmo_sock_init. */
+/*! connect the socket to a remote peer */
#define OSMO_SOCK_F_CONNECT (1 << 0)
+/*! bind the socket to a local address/port */
#define OSMO_SOCK_F_BIND (1 << 1)
+/*! switch socket to non-blocking mode */
#define OSMO_SOCK_F_NONBLOCK (1 << 2)
int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
@@ -30,6 +32,10 @@ int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen);
-/*! @} */
+int osmo_sock_unix_init(uint16_t type, uint8_t proto,
+ const char *socket_path, unsigned int flags);
+
+int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto,
+ const char *socket_path, unsigned int flags);
-#endif /* _OSMOCORE_SOCKET_H */
+/*! @} */
diff --git a/src/shared/libosmocore/include/osmocom/core/stat_item.h b/src/shared/libosmocore/include/osmocom/core/stat_item.h
new file mode 100644
index 00000000..181b029f
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/stat_item.h
@@ -0,0 +1,137 @@
+#pragma once
+
+/*! \defgroup osmo_stat_item Statistics value item
+ * @{
+ */
+
+/*! \file stat_item.h */
+
+#include <stdint.h>
+
+#include <osmocom/core/linuxlist.h>
+
+struct osmo_stat_item_desc;
+
+#define OSMO_STAT_ITEM_NOVALUE_ID 0
+#define OSMO_STAT_ITEM_NO_UNIT NULL
+
+struct osmo_stat_item_value {
+ int32_t id;
+ int32_t value;
+};
+
+/*! \brief data we keep for each actual value */
+struct osmo_stat_item {
+ const struct osmo_stat_item_desc *desc;
+ /*! \brief the index of the freshest value */
+ int32_t last_value_index;
+ /*! \brief offset to the freshest value in the value fifo */
+ int16_t last_offs;
+ /*! \brief value fifo */
+ struct osmo_stat_item_value values[0];
+};
+
+/*! \brief statistics value description */
+struct osmo_stat_item_desc {
+ const char *name; /*!< \brief name of the item */
+ const char *description;/*!< \brief description of the item */
+ const char *unit; /*!< \brief unit of a value */
+ unsigned int num_values;/*!< \brief number of values to store */
+ int32_t default_value;
+};
+
+/*! \brief description of a statistics value group */
+struct osmo_stat_item_group_desc {
+ /*! \brief The prefix to the name of all values in this group */
+ const char *group_name_prefix;
+ /*! \brief The human-readable description of the group */
+ const char *group_description;
+ /*! \brief The class to which this group belongs */
+ int class_id;
+ /*! \brief The number of values in this group */
+ const unsigned int num_items;
+ /*! \brief Pointer to array of value names */
+ const struct osmo_stat_item_desc *item_desc;
+};
+
+/*! \brief One instance of a counter group class */
+struct osmo_stat_item_group {
+ /*! \brief Linked list of all value groups in the system */
+ struct llist_head list;
+ /*! \brief Pointer to the counter group class */
+ const struct osmo_stat_item_group_desc *desc;
+ /*! \brief The index of this value group within its class */
+ unsigned int idx;
+ /*! \brief 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,
+ unsigned int idx);
+
+static inline void osmo_stat_item_group_udp_idx(
+ struct osmo_stat_item_group *grp, unsigned int idx)
+{
+ grp->idx = idx;
+}
+
+void osmo_stat_item_group_free(struct osmo_stat_item_group *statg);
+
+void osmo_stat_item_set(struct osmo_stat_item *item, int32_t value);
+
+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);
+
+const struct osmo_stat_item *osmo_stat_item_get_by_name(
+ const struct osmo_stat_item_group *statg, const char *name);
+
+/*! \brief Retrieve the next value from the osmo_stat_item object.
+ * If a new value has been set, it is returned. The idx is used to decide
+ * which value to return.
+ * On success, *idx is updated to refer to the next unread value. If
+ * values have been missed due to FIFO overflow, *idx 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 idx 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 *idx, int32_t *value);
+
+/*! \brief Get the last (freshest) value */
+static int32_t osmo_stat_item_get_last(const struct osmo_stat_item *item);
+
+/*! \brief Skip all values of the item and update idx accordingly */
+int osmo_stat_item_discard(const struct osmo_stat_item *item, int32_t *idx);
+
+/*! \brief Skip all values of all items and update idx accordingly */
+int osmo_stat_item_discard_all(int32_t *idx);
+
+typedef int (*osmo_stat_item_handler_t)(
+ struct osmo_stat_item_group *, struct osmo_stat_item *, void *);
+
+typedef int (*osmo_stat_item_group_handler_t)(struct osmo_stat_item_group *, void *);
+
+/*! \brief Iteate over all items
+ * \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 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;
+}
+/*! @} */
diff --git a/src/shared/libosmocore/include/osmocom/core/statistics.h b/src/shared/libosmocore/include/osmocom/core/statistics.h
index 04816c16..41716ccf 100644
--- a/src/shared/libosmocore/include/osmocom/core/statistics.h
+++ b/src/shared/libosmocore/include/osmocom/core/statistics.h
@@ -1,5 +1,4 @@
-#ifndef _STATISTICS_H
-#define _STATISTICS_H
+#pragma once
/*! \file statistics.h
* \brief Common routines regarding statistics */
@@ -10,8 +9,15 @@ struct osmo_counter {
const char *name; /*!< \brief human-readable name */
const char *description; /*!< \brief humn-readable description */
unsigned long value; /*!< \brief current value */
+ unsigned long previous; /*!< \brief previous value */
};
+/*! \brief Decrement counter */
+static inline void osmo_counter_dec(struct osmo_counter *ctr)
+{
+ ctr->value--;
+}
+
/*! \brief Increment counter */
static inline void osmo_counter_inc(struct osmo_counter *ctr)
{
@@ -34,12 +40,12 @@ static inline void osmo_counter_reset(struct osmo_counter *ctr)
struct osmo_counter *osmo_counter_alloc(const char *name);
/*! \brief Free the specified counter
- * \param[ctr] Counter
+ * \param[in] ctr Counter
*/
void osmo_counter_free(struct osmo_counter *ctr);
-/*! \brief Iteate over all counters
- * \param[in] handle_counter Call-back function
+/*! \brief Iterate over all counters
+ * \param[in] handle_counter Call-back function, aborts if rc < 0
* \param[in] data Private dtata handed through to \a handle_counter
*/
int osmo_counters_for_each(int (*handle_counter)(struct osmo_counter *, void *), void *data);
@@ -50,4 +56,5 @@ int osmo_counters_for_each(int (*handle_counter)(struct osmo_counter *, void *),
*/
struct osmo_counter *osmo_counter_get_by_name(const char *name);
-#endif /* _STATISTICS_H */
+/*! \brief Return the counter difference since the last call to this function */
+int osmo_counter_difference(struct osmo_counter *ctr);
diff --git a/src/shared/libosmocore/include/osmocom/core/stats.h b/src/shared/libosmocore/include/osmocom/core/stats.h
new file mode 100644
index 00000000..f754e41d
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/stats.h
@@ -0,0 +1,121 @@
+/* (C) 2015 by Sysmocom s.f.m.c. GmbH
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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 <sys/socket.h>
+#include <osmocom/core/linuxlist.h>
+
+#include <stdint.h>
+
+struct msgb;
+struct osmo_stat_item_group;
+struct osmo_stat_item_desc;
+struct rate_ctr_group;
+struct rate_ctr_desc;
+
+enum osmo_stats_class {
+ OSMO_STATS_CLASS_UNKNOWN,
+ OSMO_STATS_CLASS_GLOBAL,
+ OSMO_STATS_CLASS_PEER,
+ OSMO_STATS_CLASS_SUBSCRIBER,
+};
+
+enum osmo_stats_reporter_type {
+ OSMO_STATS_REPORTER_LOG,
+ OSMO_STATS_REPORTER_STATSD,
+};
+
+struct osmo_stats_reporter {
+ enum osmo_stats_reporter_type type;
+ char *name;
+
+ unsigned int have_net_config : 1;
+
+ /* config */
+ int enabled;
+ char *name_prefix;
+ char *dest_addr_str;
+ char *bind_addr_str;
+ int dest_port;
+ int mtu;
+ enum osmo_stats_class max_class;
+
+ /* state */
+ int running;
+ struct sockaddr dest_addr;
+ int dest_addr_len;
+ struct sockaddr bind_addr;
+ int bind_addr_len;
+ int fd;
+ struct msgb *buffer;
+ int agg_enabled;
+ int force_single_flush;
+
+ struct llist_head list;
+ int (*open)(struct osmo_stats_reporter *srep);
+ int (*close)(struct osmo_stats_reporter *srep);
+ int (*send_counter)(struct osmo_stats_reporter *srep,
+ const struct rate_ctr_group *ctrg,
+ const struct rate_ctr_desc *desc,
+ int64_t value, int64_t delta);
+ int (*send_item)(struct osmo_stats_reporter *srep,
+ const struct osmo_stat_item_group *statg,
+ const struct osmo_stat_item_desc *desc,
+ int64_t value);
+};
+
+struct osmo_stats_config {
+ int interval;
+};
+
+extern struct osmo_stats_config *osmo_stats_config;
+
+void osmo_stats_init(void *ctx);
+int osmo_stats_report();
+
+int osmo_stats_set_interval(int interval);
+
+struct osmo_stats_reporter *osmo_stats_reporter_alloc(enum osmo_stats_reporter_type type,
+ const char *name);
+void osmo_stats_reporter_free(struct osmo_stats_reporter *srep);
+
+struct osmo_stats_reporter *osmo_stats_reporter_find(enum osmo_stats_reporter_type type,
+ const char *name);
+
+int osmo_stats_reporter_set_remote_addr(struct osmo_stats_reporter *srep, const char *addr);
+int osmo_stats_reporter_set_remote_port(struct osmo_stats_reporter *srep, int port);
+int osmo_stats_reporter_set_local_addr(struct osmo_stats_reporter *srep, const char *addr);
+int osmo_stats_reporter_set_mtu(struct osmo_stats_reporter *srep, int mtu);
+int osmo_stats_reporter_set_max_class(struct osmo_stats_reporter *srep,
+ enum osmo_stats_class class_id);
+int osmo_stats_reporter_set_name_prefix(struct osmo_stats_reporter *srep, const char *prefix);
+int osmo_stats_reporter_enable(struct osmo_stats_reporter *srep);
+int osmo_stats_reporter_disable(struct osmo_stats_reporter *srep);
+
+/* reporter creation */
+struct osmo_stats_reporter *osmo_stats_reporter_create_log(const char *name);
+struct osmo_stats_reporter *osmo_stats_reporter_create_statsd(const char *name);
+
+/* helper functions for reporter implementations */
+int osmo_stats_reporter_send(struct osmo_stats_reporter *srep, const char *data,
+ int data_len);
+int osmo_stats_reporter_send_buffer(struct osmo_stats_reporter *srep);
+int osmo_stats_reporter_udp_open(struct osmo_stats_reporter *srep);
+int osmo_stats_reporter_udp_close(struct osmo_stats_reporter *srep);
diff --git a/src/shared/libosmocore/include/osmocom/core/strrb.h b/src/shared/libosmocore/include/osmocom/core/strrb.h
new file mode 100644
index 00000000..7507cf41
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/strrb.h
@@ -0,0 +1,55 @@
+#pragma once
+
+/* (C) 2012-2013 by Katerina Barone-Adesi <kat.obsc@gmail.com>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/*! \defgroup osmo_strrb Osmocom ringbuffers for log strings
+ * @{
+ */
+
+/*! \file strrb.h
+ * \brief Osmocom string ringbuffer handling routines
+ */
+
+#include <unistd.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <osmocom/core/talloc.h>
+
+/*! \brief A structure representing an osmocom string ringbuffer */
+
+#define RB_MAX_MESSAGE_SIZE 240
+struct osmo_strrb {
+ uint16_t start; /*!< \brief index of the first slot */
+ uint16_t end; /*!< \brief index of the last slot */
+ uint16_t size; /*!< \brief max number of messages to store */
+ char **buffer; /*!< \brief storage for messages */
+};
+
+struct osmo_strrb *osmo_strrb_create(TALLOC_CTX * 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);
+bool _osmo_strrb_is_bufindex_valid(const struct osmo_strrb *rb,
+ unsigned int offset);
+size_t osmo_strrb_elements(const struct osmo_strrb *rb);
+int osmo_strrb_add(struct osmo_strrb *rb, const char *data);
+
+/*! @} */
diff --git a/src/shared/libosmocore/include/osmocom/core/talloc.h b/src/shared/libosmocore/include/osmocom/core/talloc.h
index f7f7643b..df7ea7fd 100644
--- a/src/shared/libosmocore/include/osmocom/core/talloc.h
+++ b/src/shared/libosmocore/include/osmocom/core/talloc.h
@@ -1,192 +1,4 @@
-#ifndef _TALLOC_H_
-#define _TALLOC_H_
-/*
- Unix SMB/CIFS implementation.
- Samba temporary memory allocation functions
-
- Copyright (C) Andrew Tridgell 2004-2005
- Copyright (C) Stefan Metzmacher 2006
-
- ** NOTE! The following LGPL license applies to the talloc
- ** library. This does NOT imply that all of Samba is released
- ** under the LGPL
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- This library 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-
-#define HAVE_VA_COPY
-
-/* this is only needed for compatibility with the old talloc */
-typedef void TALLOC_CTX;
-
-/*
- this uses a little trick to allow __LINE__ to be stringified
-*/
-#ifndef __location__
-#define __TALLOC_STRING_LINE1__(s) #s
-#define __TALLOC_STRING_LINE2__(s) __TALLOC_STRING_LINE1__(s)
-#define __TALLOC_STRING_LINE3__ __TALLOC_STRING_LINE2__(__LINE__)
-#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__
-#endif
-
-#ifndef TALLOC_DEPRECATED
-#define TALLOC_DEPRECATED 0
-#endif
-
-#ifndef PRINTF_ATTRIBUTE
-#if (__GNUC__ >= 3)
-/** Use gcc attribute to check printf fns. a1 is the 1-based index of
- * the parameter containing the format, and a2 the index of the first
- * argument. Note that some gcc 2.x versions don't handle this
- * properly **/
-#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2)))
-#else
-#define PRINTF_ATTRIBUTE(a1, a2)
-#endif
-#endif
-
-/* try to make talloc_set_destructor() and talloc_steal() type safe,
- if we have a recent gcc */
-#if (__GNUC__ >= 3)
-#define _TALLOC_TYPEOF(ptr) __typeof__(ptr)
-#define talloc_set_destructor(ptr, function) \
- do { \
- int (*_talloc_destructor_fn)(_TALLOC_TYPEOF(ptr)) = (function); \
- _talloc_set_destructor((ptr), (int (*)(void *))_talloc_destructor_fn); \
- } while(0)
-/* this extremely strange macro is to avoid some braindamaged warning
- stupidity in gcc 4.1.x */
-#define talloc_steal(ctx, ptr) ({ _TALLOC_TYPEOF(ptr) __talloc_steal_ret = (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)); __talloc_steal_ret; })
-#else
-#define talloc_set_destructor(ptr, function) \
- _talloc_set_destructor((ptr), (int (*)(void *))(function))
-#define _TALLOC_TYPEOF(ptr) void *
-#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr))
-#endif
-
-#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference((ctx),(ptr))
-#define talloc_move(ctx, ptr) (_TALLOC_TYPEOF(*(ptr)))_talloc_move((ctx),(void *)(ptr))
-
-/* useful macros for creating type checked pointers */
-#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type)
-#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__)
-#define talloc_ptrtype(ctx, ptr) (_TALLOC_TYPEOF(ptr))talloc_size(ctx, sizeof(*(ptr)))
-
-#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__)
-
-#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type)
-#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__)
-
-#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type)
-#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type)
-#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__)
-#define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count)
-#define talloc_array_length(ctx) (talloc_get_size(ctx)/sizeof(*ctx))
-
-#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type)
-#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__)
-
-#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__)
-
-#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type)
-#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type)
-#define talloc_get_type_abort(ptr, type) (type *)_talloc_get_type_abort(ptr, #type, __location__)
-
-#define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type)
-
-#if TALLOC_DEPRECATED
-#define talloc_zero_p(ctx, type) talloc_zero(ctx, type)
-#define talloc_p(ctx, type) talloc(ctx, type)
-#define talloc_array_p(ctx, type, count) talloc_array(ctx, type, count)
-#define talloc_realloc_p(ctx, p, type, count) talloc_realloc(ctx, p, type, count)
-#define talloc_destroy(ctx) talloc_free(ctx)
-#define talloc_append_string(c, s, a) (s?talloc_strdup_append(s,a):talloc_strdup(c, a))
-#endif
-
-#define TALLOC_FREE(ctx) do { talloc_free(ctx); ctx=NULL; } while(0)
-
-/* The following definitions come from talloc.c */
-void *_talloc(const void *context, size_t size);
-void *talloc_pool(const void *context, size_t size);
-void _talloc_set_destructor(const void *ptr, int (*_destructor)(void *));
-int talloc_increase_ref_count(const void *ptr);
-size_t talloc_reference_count(const void *ptr);
-void *_talloc_reference(const void *context, const void *ptr);
-int talloc_unlink(const void *context, void *ptr);
-const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
-void talloc_set_name_const(const void *ptr, const char *name);
-void *talloc_named(const void *context, size_t size,
- const char *fmt, ...) PRINTF_ATTRIBUTE(3,4);
-void *talloc_named_const(const void *context, size_t size, const char *name);
-const char *talloc_get_name(const void *ptr);
-void *talloc_check_name(const void *ptr, const char *name);
-void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location);
-void *talloc_parent(const void *ptr);
-const char *talloc_parent_name(const void *ptr);
-void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2);
-int talloc_free(void *ptr);
-void talloc_free_children(void *ptr);
-void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name);
-void *_talloc_steal(const void *new_ctx, const void *ptr);
-void *_talloc_move(const void *new_ctx, const void *pptr);
-size_t talloc_total_size(const void *ptr);
-size_t talloc_total_blocks(const void *ptr);
-void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
- void (*callback)(const void *ptr,
- int depth, int max_depth,
- int is_ref,
- void *private_data),
- void *private_data);
-void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f);
-void talloc_report_full(const void *ptr, FILE *f);
-void talloc_report(const void *ptr, FILE *f);
-void talloc_enable_null_tracking(void);
-void talloc_disable_null_tracking(void);
-void talloc_enable_leak_report(void);
-void talloc_enable_leak_report_full(void);
-void *_talloc_zero(const void *ctx, size_t size, const char *name);
-void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name);
-void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name);
-void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name);
-void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name);
-void *talloc_realloc_fn(const void *context, void *ptr, size_t size);
-void *talloc_autofree_context(void);
-size_t talloc_get_size(const void *ctx);
-void *talloc_find_parent_byname(const void *ctx, const char *name);
-void talloc_show_parents(const void *context, FILE *file);
-int talloc_is_parent(const void *context, const void *ptr);
-
-char *talloc_strdup(const void *t, const char *p);
-char *talloc_strdup_append(char *s, const char *a);
-char *talloc_strdup_append_buffer(char *s, const char *a);
-
-char *talloc_strndup(const void *t, const char *p, size_t n);
-char *talloc_strndup_append(char *s, const char *a, size_t n);
-char *talloc_strndup_append_buffer(char *s, const char *a, size_t n);
-
-char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
-char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
-char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
-
-char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
-char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
-char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
-
-void talloc_set_abort_fn(void (*abort_fn)(const char *reason));
-
-#endif
+/* Convenience wrapper. libosmocore used to ship its own internal copy of
+ * talloc, before libtalloc became a standard component on most systems */
+#pragma once
+#include <talloc.h>
diff --git a/src/shared/libosmocore/include/osmocom/core/timer.h b/src/shared/libosmocore/include/osmocom/core/timer.h
index d37af806..dbda13f2 100644
--- a/src/shared/libosmocore/include/osmocom/core/timer.h
+++ b/src/shared/libosmocore/include/osmocom/core/timer.h
@@ -26,10 +26,10 @@
* \brief Osmocom timer handling routines
*/
-#ifndef TIMER_H
-#define TIMER_H
+#pragma once
#include <sys/time.h>
+#include <stdbool.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/linuxrbtree.h>
@@ -38,16 +38,16 @@
* Timer management:
* - Create a struct osmo_timer_list
* - Fill out timeout and use add_timer or
- * use schedule_timer to schedule a timer in
+ * use osmo_timer_schedule to schedule a timer in
* x seconds and microseconds from now...
- * - Use del_timer to remove the timer
+ * - Use osmo_timer_del to remove the timer
*
* Internally:
* - We hook into select.c to give a timeval of the
* nearest timer. On already passed timers we give
* it a 0 to immediately fire after the select
- * - update_timers will call the callbacks and remove
- * the timers.
+ * - osmo_timers_update will call the callbacks and
+ * remove the timers.
*
*/
/*! \brief A structure representing a single instance of a timer */
@@ -84,6 +84,14 @@ void osmo_timers_prepare(void);
int osmo_timers_update(void);
int osmo_timers_check(void);
-/*! @} */
+int osmo_gettimeofday(struct timeval *tv, struct timezone *tz);
+
+/**
+ * timer override
+ */
+
+extern bool osmo_gettimeofday_override;
+extern struct timeval osmo_gettimeofday_override_time;
+void osmo_gettimeofday_override_add(time_t secs, suseconds_t usecs);
-#endif
+/*! @} */
diff --git a/src/shared/libosmocore/include/osmocom/core/timer_compat.h b/src/shared/libosmocore/include/osmocom/core/timer_compat.h
index d86c109e..fb2967ba 100644
--- a/src/shared/libosmocore/include/osmocom/core/timer_compat.h
+++ b/src/shared/libosmocore/include/osmocom/core/timer_compat.h
@@ -26,8 +26,7 @@
* \brief Compatibility header with some helpers
*/
-#ifndef TIMER_COMPAT_H
-#define TIMER_COMPAT_H
+#pragma once
/* Convenience macros for operations on timevals.
@@ -75,5 +74,3 @@
/*! @} */
-
-#endif /* TIMER_COMPAT_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/utils.h b/src/shared/libosmocore/include/osmocom/core/utils.h
index 03861d78..41bbc279 100644
--- a/src/shared/libosmocore/include/osmocom/core/utils.h
+++ b/src/shared/libosmocore/include/osmocom/core/utils.h
@@ -1,5 +1,7 @@
-#ifndef OSMOCORE_UTIL_H
-#define OSMOCORE_UTIL_H
+#pragma once
+
+#include <osmocom/core/backtrace.h>
+#include <osmocom/core/talloc.h>
/*! \defgroup utils General-purpose utility functions
* @{
@@ -13,6 +15,10 @@
#define OSMO_MAX(a, b) ((a) >= (b) ? (a) : (b))
/*! \brief Return the minimum of two specified values */
#define OSMO_MIN(a, b) ((a) >= (b) ? (b) : (a))
+/*! \brief Stringify the contents of a macro, e.g. a port number */
+#define OSMO_STRINGIFY(x) #x
+/*! \brief Make a value_string entry from an enum value name */
+#define OSMO_VALUE_STRING(x) { x, OSMO_STRINGIFY(x) }
#include <stdint.h>
@@ -23,6 +29,8 @@ struct value_string {
};
const char *get_value_string(const struct value_string *vs, uint32_t val);
+const char *get_value_string_or_null(const struct value_string *vs,
+ uint32_t val);
int get_string_value(const struct value_string *vs, const char *str);
@@ -37,7 +45,7 @@ char *osmo_hexdump(const unsigned char *buf, int len);
char *osmo_hexdump_nospc(const unsigned char *buf, int len);
char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len) __attribute__((__deprecated__));
-#define osmo_static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
+#define osmo_static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1] __attribute__((__unused__));
void osmo_str2lower(char *out, const char *in);
void osmo_str2upper(char *out, const char *in);
@@ -51,6 +59,33 @@ do { \
rem -= ret; \
} while (0)
-/*! @} */
+/*! Helper macro to terminate when an assertion failes
+ * \param[in] exp Predicate to verify
+ * This function will generate a backtrace and terminate the program if
+ * the predicate evaluates to false (0).
+ */
+#define OSMO_ASSERT(exp) \
+ if (!(exp)) { \
+ fprintf(stderr, "Assert failed %s %s:%d\n", #exp, __BASE_FILE__, __LINE__); \
+ osmo_generate_backtrace(); \
+ abort(); \
+ }
+
+/*! duplicate a string using talloc and release its prior content (if any)
+ * \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] newstr String that will be copieed to newly allocated string */
+static inline void osmo_talloc_replace_string(void *ctx, char **dst, const char *newstr)
+{
+ if (*dst)
+ talloc_free(*dst);
+ *dst = talloc_strdup(ctx, newstr);
+}
-#endif
+int osmo_constant_time_cmp(const uint8_t *exp, const uint8_t *rel, const int count);
+uint64_t osmo_decode_big_endian(const uint8_t *data, size_t data_len);
+uint8_t *osmo_encode_big_endian(uint64_t value, size_t data_len);
+
+size_t osmo_strlcpy(char *dst, const char *src, size_t siz);
+
+/*! @} */
diff --git a/src/shared/libosmocore/include/osmocom/core/write_queue.h b/src/shared/libosmocore/include/osmocom/core/write_queue.h
index 816c0364..4e0fdf3d 100644
--- a/src/shared/libosmocore/include/osmocom/core/write_queue.h
+++ b/src/shared/libosmocore/include/osmocom/core/write_queue.h
@@ -20,8 +20,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
-#ifndef OSMO_WQUEUE_H
-#define OSMO_WQUEUE_H
+#pragma once
/*! \defgroup write_queue Osmocom msgb write queues
* @{
@@ -59,5 +58,3 @@ int osmo_wqueue_enqueue(struct osmo_wqueue *queue, struct msgb *data);
int osmo_wqueue_bfd_cb(struct osmo_fd *fd, unsigned int what);
/*! @} */
-
-#endif
diff --git a/src/shared/libosmocore/include/osmocom/crypt/auth.h b/src/shared/libosmocore/include/osmocom/crypt/auth.h
index 871e7c87..37b8a8ad 100644
--- a/src/shared/libosmocore/include/osmocom/crypt/auth.h
+++ b/src/shared/libosmocore/include/osmocom/crypt/auth.h
@@ -1,5 +1,4 @@
-#ifndef _OSMOCRYPTO_AUTH_H
-#define _OSMOCRYPTO_AUTH_H
+#pragma once
/*! \addtogroup auth
* @{
@@ -92,10 +91,8 @@ int osmo_auth_register(struct osmo_auth_impl *impl);
int osmo_auth_load(const char *path);
int osmo_auth_supported(enum osmo_auth_algo algo);
-
+void osmo_c4(uint8_t *ck, const uint8_t *kc);
const char *osmo_auth_alg_name(enum osmo_auth_algo alg);
enum osmo_auth_algo osmo_auth_alg_parse(const char *name);
-#endif /* _OSMOCRYPTO_AUTH_H */
-
/* @} */
diff --git a/src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h b/src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h
index 30510711..c302f10b 100644
--- a/src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h
+++ b/src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h
@@ -1,23 +1,30 @@
-#ifndef _GPRS_CIPHER_H
-#define _GPRS_CIPHER_H
+#pragma once
#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/utils.h>
+
+#include <stdint.h>
#define GSM0464_CIPH_MAX_BLOCK 1523
+/* 3GPP TS 24.008 § 10.5.5.3 */
enum gprs_ciph_algo {
- GPRS_ALGO_GEA0,
- GPRS_ALGO_GEA1,
- GPRS_ALGO_GEA2,
- GPRS_ALGO_GEA3,
+ GPRS_ALGO_GEA0 = 0,
+ GPRS_ALGO_GEA1 = 1,
+ GPRS_ALGO_GEA2 = 2,
+ GPRS_ALGO_GEA3 = 3,
+ GPRS_ALGO_GEA4 = 4,
_GPRS_ALGO_NUM
};
+/* 3GPP TS 04.64 Table A.1: */
enum gprs_cipher_direction {
- GPRS_CIPH_MS2SGSN,
- GPRS_CIPH_SGSN2MS,
+ GPRS_CIPH_MS2SGSN = 0,
+ GPRS_CIPH_SGSN2MS = 1,
};
+extern const struct value_string gprs_cipher_names[];
+
/* An implementation of a GPRS cipher */
struct gprs_cipher_impl {
struct llist_head list;
@@ -28,7 +35,7 @@ struct gprs_cipher_impl {
/* As specified in 04.64 Annex A. Uses Kc, IV and direction
* to generate the 1523 bytes cipher stream that need to be
* XORed wit the plaintext for encrypt / ciphertext for decrypt */
- int (*run)(uint8_t *out, uint16_t len, uint64_t kc, uint32_t iv,
+ int (*run)(uint8_t *out, uint16_t len, uint8_t *kc, uint32_t iv,
enum gprs_cipher_direction direction);
};
@@ -40,15 +47,16 @@ int gprs_cipher_load(const char *path);
/* function to be called by core code */
int gprs_cipher_run(uint8_t *out, uint16_t len, enum gprs_ciph_algo algo,
- uint64_t kc, uint32_t iv, enum gprs_cipher_direction dir);
+ uint8_t *kc, uint32_t iv, enum gprs_cipher_direction dir);
/* Do we have an implementation for this cipher? */
int gprs_cipher_supported(enum gprs_ciph_algo algo);
+/* Return key length for supported cipher, in bytes */
+unsigned gprs_cipher_key_length(enum gprs_ciph_algo algo);
+
/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */
uint32_t gprs_cipher_gen_input_ui(uint32_t iov_ui, uint8_t sapi, uint32_t lfn, uint32_t oc);
/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */
uint32_t gprs_cipher_gen_input_i(uint32_t iov_i, uint32_t lfn, uint32_t oc);
-
-#endif /* _GPRS_CIPHER_H */
diff --git a/src/shared/libosmocore/include/osmocom/ctrl/control_cmd.h b/src/shared/libosmocore/include/osmocom/ctrl/control_cmd.h
new file mode 100644
index 00000000..8f2eaa25
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/ctrl/control_cmd.h
@@ -0,0 +1,190 @@
+#pragma once
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/write_queue.h>
+#include <osmocom/core/logging.h>
+
+#include <osmocom/vty/vector.h>
+
+#define CTRL_CMD_ERROR -1
+#define CTRL_CMD_HANDLED 0
+#define CTRL_CMD_REPLY 1
+
+struct ctrl_handle;
+
+enum ctrl_node_type {
+ CTRL_NODE_ROOT, /* Root elements */
+ CTRL_NODE_BTS, /* BTS specific (net.btsN.) */
+ CTRL_NODE_TRX, /* TRX specific (net.btsN.trxM.) */
+ CTRL_NODE_TS, /* TS specific (net.btsN.trxM.tsI.) */
+ _LAST_CTRL_NODE
+};
+
+enum ctrl_type {
+ CTRL_TYPE_UNKNOWN,
+ CTRL_TYPE_GET,
+ CTRL_TYPE_SET,
+ CTRL_TYPE_GET_REPLY,
+ CTRL_TYPE_SET_REPLY,
+ CTRL_TYPE_TRAP,
+ CTRL_TYPE_ERROR
+};
+
+struct ctrl_connection {
+ struct llist_head list_entry;
+
+ /* The queue for sending data back */
+ struct osmo_wqueue write_queue;
+
+ /* Buffer for partial input data */
+ struct msgb *pending_msg;
+
+ /* Callback if the connection was closed */
+ void (*closed_cb)(struct ctrl_connection *conn);
+
+ /* Pending commands for this connection */
+ struct llist_head cmds;
+
+ /* Pending deferred commands for this connection */
+ struct llist_head def_cmds;
+};
+
+struct ctrl_cmd {
+ struct ctrl_connection *ccon;
+ enum ctrl_type type;
+ char *id;
+ void *node;
+ char *variable;
+ char *value;
+ char *reply;
+};
+
+struct ctrl_cmd_struct {
+ int nr_commands;
+ char **command;
+};
+
+struct ctrl_cmd_element {
+ const char *name;
+ struct ctrl_cmd_struct strcmd;
+ int (*set)(struct ctrl_cmd *cmd, void *data);
+ int (*get)(struct ctrl_cmd *cmd, void *data);
+ int (*verify)(struct ctrl_cmd *cmd, const char *value, void *data);
+};
+
+struct ctrl_cmd_map {
+ char *cmd;
+ enum ctrl_type type;
+};
+
+/* deferred control command, i.e. responded asynchronously */
+struct ctrl_cmd_def {
+ struct llist_head list; /* ctrl_connection.def_cmds */
+ struct ctrl_cmd *cmd;
+ void *data; /* opaque user data */
+};
+
+struct ctrl_cmd_def *
+ctrl_cmd_def_make(const void *ctx, struct ctrl_cmd *cmd, void *data, unsigned int secs);
+int ctrl_cmd_def_is_zombie(struct ctrl_cmd_def *cd);
+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);
+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_parse(void *ctx, struct msgb *msg);
+struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd);
+struct ctrl_cmd *ctrl_cmd_cpy(void *ctx, struct ctrl_cmd *cmd);
+struct ctrl_cmd *ctrl_cmd_create(void *ctx, enum ctrl_type);
+struct ctrl_cmd *ctrl_cmd_trap(struct ctrl_cmd *cmd);
+
+#define CTRL_CMD_DEFINE_STRUCT(cmdname, cmdstr, verify_name) \
+static struct ctrl_cmd_element cmd_##cmdname = { \
+ .name = cmdstr, \
+ .get = &get_##cmdname, \
+ .set = &set_##cmdname, \
+ .verify = verify_name, \
+}
+
+#define CTRL_HELPER_GET_INT(cmdname, dtype, element) \
+static int get_##cmdname(struct ctrl_cmd *cmd, void *_data) \
+{ \
+ dtype *node = cmd->node; \
+ cmd->reply = talloc_asprintf(cmd, "%i", node->element); \
+ if (!cmd->reply) { \
+ cmd->reply = "OOM"; \
+ return CTRL_CMD_ERROR; \
+ } \
+ return CTRL_CMD_REPLY; \
+}
+#define CTRL_HELPER_SET_INT(cmdname, dtype, element) \
+static int set_##cmdname(struct ctrl_cmd *cmd, void *_data) \
+{ \
+ dtype *node = cmd->node; \
+ int tmp = atoi(cmd->value); \
+ node->element = tmp; \
+ return get_##cmdname(cmd, _data); \
+}
+#define CTRL_HELPER_VERIFY_RANGE(cmdname, min, max) \
+static int verify_##cmdname(struct ctrl_cmd *cmd, const char *value, void *_data) \
+{ \
+ int tmp = atoi(value); \
+ if ((tmp >= min)&&(tmp <= max)) { \
+ return 0; \
+ } \
+ cmd->reply = "Input not within the range"; \
+ return -1; \
+}
+
+#define CTRL_CMD_DEFINE_RANGE(cmdname, cmdstr, dtype, element, min, max) \
+ CTRL_HELPER_GET_INT(cmdname, dtype, element) \
+ CTRL_HELPER_SET_INT(cmdname, dtype, element) \
+ CTRL_HELPER_VERIFY_RANGE(cmdname, min, max) \
+CTRL_CMD_DEFINE_STRUCT(cmdname, cmdstr, verify_##cmdname)
+
+#define CTRL_HELPER_GET_STRING(cmdname, dtype, element) \
+static int get_##cmdname(struct ctrl_cmd *cmd, void *_data) \
+{ \
+ dtype *data = cmd->node; \
+ cmd->reply = talloc_asprintf(cmd, "%s", data->element); \
+ if (!cmd->reply) { \
+ cmd->reply = "OOM"; \
+ return CTRL_CMD_ERROR; \
+ } \
+ return CTRL_CMD_REPLY; \
+}
+#define CTRL_HELPER_SET_STRING(cmdname, dtype, element) \
+static int set_##cmdname(struct ctrl_cmd *cmd, void *_data) \
+{ \
+ dtype *data = cmd->node; \
+ osmo_talloc_replace_string(cmd->node, &data->element, cmd->value); \
+ return get_##cmdname(cmd, _data); \
+}
+#define CTRL_CMD_DEFINE_STRING(cmdname, cmdstr, dtype, element) \
+ CTRL_HELPER_GET_STRING(cmdname, dtype, element) \
+ CTRL_HELPER_SET_STRING(cmdname, dtype, element) \
+CTRL_CMD_DEFINE_STRUCT(cmdname, cmdstr, NULL)
+
+#define CTRL_CMD_DEFINE(cmdname, cmdstr) \
+static int get_##cmdname(struct ctrl_cmd *cmd, void *data); \
+static int set_##cmdname(struct ctrl_cmd *cmd, void *data); \
+static int verify_##cmdname(struct ctrl_cmd *cmd, const char *value, void *data); \
+CTRL_CMD_DEFINE_STRUCT(cmdname, cmdstr, verify_##cmdname)
+
+#define CTRL_CMD_DEFINE_RO(cmdname, cmdstr) \
+static int get_##cmdname(struct ctrl_cmd *cmd, void *data); \
+static int set_##cmdname(struct ctrl_cmd *cmd, void *data) \
+{ \
+ cmd->reply = "Read Only attribute"; \
+ return CTRL_CMD_ERROR; \
+} \
+static int verify_##cmdname(struct ctrl_cmd *cmd, const char *value, void *data) \
+{ \
+ cmd->reply = "Read Only attribute"; \
+ return 1; \
+} \
+CTRL_CMD_DEFINE_STRUCT(cmdname, cmdstr, verify_##cmdname)
+
+struct gsm_network;
diff --git a/src/shared/libosmocore/include/osmocom/ctrl/control_if.h b/src/shared/libosmocore/include/osmocom/ctrl/control_if.h
new file mode 100644
index 00000000..512ae10e
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/ctrl/control_if.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <osmocom/core/write_queue.h>
+#include <osmocom/ctrl/control_cmd.h>
+
+int ctrl_parse_get_num(vector vline, int i, long *num);
+
+typedef int (*ctrl_cmd_lookup)(void *data, vector vline, int *node_type,
+ void **node_data, int *i);
+
+struct ctrl_handle {
+ struct osmo_fd listen_fd;
+ void *data;
+
+ ctrl_cmd_lookup lookup;
+
+ /* List of control connections */
+ struct llist_head ccon_list;
+};
+
+
+int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd);
+int ctrl_cmd_send_trap(struct ctrl_handle *ctrl, const char *name, char *value);
+struct ctrl_handle *ctrl_interface_setup(void *data, uint16_t port,
+ ctrl_cmd_lookup lookup);
+struct ctrl_handle *ctrl_interface_setup_dynip(void *data,
+ const char *bind_addr,
+ uint16_t port,
+ ctrl_cmd_lookup lookup);
+
+int ctrl_cmd_handle(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd, void *data);
diff --git a/src/shared/libosmocore/include/osmocom/ctrl/control_vty.h b/src/shared/libosmocore/include/osmocom/ctrl/control_vty.h
new file mode 100644
index 00000000..d0ef69f4
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/ctrl/control_vty.h
@@ -0,0 +1,9 @@
+#pragma once
+
+/* Add the 'ctrl' section to VTY, containing the 'bind' command. */
+int ctrl_vty_init(void *ctx);
+
+/* Obtain the IP address configured by the 'ctrl'/'bind A.B.C.D' VTY command.
+ * This should be fed to ctrl_interface_setup() once the configuration has been
+ * read. */
+const char *ctrl_vty_get_bind_addr(void);
diff --git a/src/shared/libosmocore/include/osmocom/ctrl/ports.h b/src/shared/libosmocore/include/osmocom/ctrl/ports.h
new file mode 100644
index 00000000..8bd86807
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/ctrl/ports.h
@@ -0,0 +1,18 @@
+#pragma once
+
+/*
+ * TCP port numbers used for CTRL interfaces in osmocom projects. See also the
+ * osmocom wiki as well as the osmo-gsm-manuals, which should all be kept in
+ * sync with this file:
+ * https://osmocom.org/projects/cellular-infrastructure/wiki/PortNumbers
+ * https://git.osmocom.org/osmo-gsm-manuals/tree/common/chapters/port_numbers.adoc
+ */
+
+#define OSMO_CTRL_PORT_BTS 4238
+#define OSMO_CTRL_PORT_NITB_BSC 4249
+#define OSMO_CTRL_PORT_BSC_NAT 4250
+#define OSMO_CTRL_PORT_SGSN 4251
+/* 4252-4254 used by VTY interface */
+#define OSMO_CTRL_PORT_CSCN 4255
+/* 4256 used by VTY interface */
+#define OSMO_CTRL_PORT_GGSN 4257
diff --git a/src/shared/libosmocore/include/osmocom/gprs/gprs_bssgp.h b/src/shared/libosmocore/include/osmocom/gprs/gprs_bssgp.h
index eb4e7219..b70487ce 100644
--- a/src/shared/libosmocore/include/osmocom/gprs/gprs_bssgp.h
+++ b/src/shared/libosmocore/include/osmocom/gprs/gprs_bssgp.h
@@ -1,5 +1,4 @@
-#ifndef _GPRS_BSSGP_H
-#define _GPRS_BSSGP_H
+#pragma once
#include <stdint.h>
#include <osmocom/core/timer.h>
@@ -13,7 +12,9 @@
/* gprs_bssgp_util.c */
extern struct gprs_ns_inst *bssgp_nsi;
struct msgb *bssgp_msgb_alloc(void);
+struct msgb *bssgp_msgb_copy(const struct msgb *msg, const char *name);
const char *bssgp_cause_str(enum gprs_bssgp_cause cause);
+const char *bssgp_pdu_str(enum bssgp_pdu_type pdu);
/* Transmit a simple response such as BLOCK/UNBLOCK/RESET ACK/NACK */
int bssgp_tx_simple_bvci(uint8_t pdu_type, uint16_t nsei,
uint16_t bvci, uint16_t ns_bvci);
@@ -34,6 +35,7 @@ enum bssgp_prim {
PRIM_NM_BVC_RESET,
PRIM_NM_BVC_BLOCK,
PRIM_NM_BVC_UNBLOCK,
+ PRIM_NM_STATUS,
};
struct osmo_bssgp_prim {
@@ -118,6 +120,7 @@ enum bssgp_ctr {
BSSGP_CTR_BYTES_OUT,
BSSGP_CTR_BLOCKED,
BSSGP_CTR_DISCARDED,
+ BSSGP_CTR_STATUS,
};
@@ -207,5 +210,3 @@ int bssgp_vty_init(void);
void bssgp_set_log_ss(int ss);
int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx);
-
-#endif /* _GPRS_BSSGP_H */
diff --git a/src/shared/libosmocore/include/osmocom/gprs/gprs_bssgp_bss.h b/src/shared/libosmocore/include/osmocom/gprs/gprs_bssgp_bss.h
index f34281e3..d79b2100 100644
--- a/src/shared/libosmocore/include/osmocom/gprs/gprs_bssgp_bss.h
+++ b/src/shared/libosmocore/include/osmocom/gprs/gprs_bssgp_bss.h
@@ -1,5 +1,4 @@
-#ifndef _BSSGP_BSS_H
-#define _BSSGP_BSS_H
+#pragma once
#include <osmocom/core/msgb.h>
#include <osmocom/gprs/gprs_bssgp.h>
@@ -11,16 +10,16 @@
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
+ * 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 Affero General Public License for more details.
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU Affero General Public License
+ * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -66,10 +65,9 @@ int bssgp_rx_paging(struct bssgp_paging_info *pinfo,
int bssgp_tx_fc_bvc(struct bssgp_bvc_ctx *bctx, uint8_t tag,
uint32_t bucket_size, uint32_t bucket_leak_rate,
- uint16_t bmax_default_ms, uint32_t r_default_ms,
+ uint32_t bmax_default_ms, uint32_t r_default_ms,
uint8_t *bucket_full_ratio, uint32_t *queue_delay_ms);
int bssgp_tx_fc_ms(struct bssgp_bvc_ctx *bctx, uint32_t tlli, uint8_t tag,
uint32_t ms_bucket_size, uint32_t bucket_leak_rate,
uint8_t *bucket_full_ratio);
-#endif
diff --git a/src/shared/libosmocore/include/osmocom/gprs/gprs_msgb.h b/src/shared/libosmocore/include/osmocom/gprs/gprs_msgb.h
index f4c85547..06f5cca2 100644
--- a/src/shared/libosmocore/include/osmocom/gprs/gprs_msgb.h
+++ b/src/shared/libosmocore/include/osmocom/gprs/gprs_msgb.h
@@ -1,5 +1,4 @@
-#ifndef _LIBGB_MSGB_H
-#define _LIBGB_MSGB_H
+#pragma once
#include <stdint.h>
/* the data structure stored in msgb->cb for libgb apps */
@@ -16,7 +15,7 @@ struct libgb_msgb_cb {
/* Identifier of a MS (inside BTS), equal to 'struct sgsn_mm_ctx' */
uint32_t tlli;
-} __attribute__((packed));
+} __attribute__((packed, may_alias));
#define LIBGB_MSGB_CB(__msgb) ((struct libgb_msgb_cb *)&((__msgb)->cb[0]))
#define msgb_tlli(__x) LIBGB_MSGB_CB(__x)->tlli
#define msgb_nsei(__x) LIBGB_MSGB_CB(__x)->nsei
@@ -34,4 +33,3 @@ struct libgb_msgb_cb {
#include <osmocom/core/logging.h>
int gprs_log_filter_fn(const struct log_context *ctx,
struct log_target *tar);
-#endif
diff --git a/src/shared/libosmocore/include/osmocom/gprs/gprs_ns.h b/src/shared/libosmocore/include/osmocom/gprs/gprs_ns.h
index a7f32b25..130d8c0f 100644
--- a/src/shared/libosmocore/include/osmocom/gprs/gprs_ns.h
+++ b/src/shared/libosmocore/include/osmocom/gprs/gprs_ns.h
@@ -1,5 +1,4 @@
-#ifndef _GPRS_NS_H
-#define _GPRS_NS_H
+#pragma once
#include <stdint.h>
@@ -24,6 +23,10 @@
"Alive Timer (Tns-alive) timeout\n" \
"Alive Timer (Tns-alive) 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
+
enum ns_timeout {
NS_TOUT_TNS_BLOCK,
NS_TOUT_TNS_BLOCK_RETRIES,
@@ -36,6 +39,7 @@ enum ns_timeout {
#define NSE_S_BLOCKED 0x0001
#define NSE_S_ALIVE 0x0002
+#define NSE_S_RESET 0x0004
/*! \brief Osmocom NS link layer types */
enum gprs_ns_ll {
@@ -49,6 +53,15 @@ enum gprs_ns_evt {
GPRS_NS_EVT_UNIT_DATA,
};
+/*! \brief Osmocom NS VC create status */
+enum gprs_ns_cs {
+ GPRS_NS_CS_CREATED, /*!< A NSVC object has been created */
+ GPRS_NS_CS_FOUND, /*!< A NSVC object has been found */
+ GPRS_NS_CS_REJECTED, /*!< Rejected and answered message */
+ GPRS_NS_CS_SKIPPED, /*!< Skipped message */
+ GPRS_NS_CS_ERROR, /*!< Failed to process message */
+};
+
struct gprs_nsvc;
/*! \brief Osmocom GPRS callback function type */
typedef int gprs_ns_cb_t(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
@@ -73,6 +86,7 @@ struct gprs_ns_inst {
struct osmo_fd fd;
uint32_t local_ip;
uint16_t local_port;
+ int dscp;
} nsip;
/*! \brief NS-over-FR-over-GRE-over-IP specific bits */
struct {
@@ -105,12 +119,15 @@ struct gprs_nsvc {
struct osmo_timer_list timer;
enum nsvc_timer_mode timer_mode;
+ struct timeval timer_started;
int alive_retries;
unsigned int remote_end_is_sgsn:1;
unsigned int persistent:1;
+ unsigned int nsvci_is_valid:1;
struct rate_ctr_group *ctrg;
+ struct osmo_stat_item_group *statg;
/*! \brief which link-layer are we based on? */
enum gprs_ns_ll ll;
@@ -128,7 +145,10 @@ struct gprs_nsvc {
/* Create a new NS protocol instance */
struct gprs_ns_inst *gprs_ns_instantiate(gprs_ns_cb_t *cb, void *ctx);
-/* Destroy a NS protocol instance */
+/* Close a NS protocol instance */
+void gprs_ns_close(struct gprs_ns_inst *nsi);
+
+/* Close and Destroy a NS protocol instance */
void gprs_ns_destroy(struct gprs_ns_inst *nsi);
/* Listen for incoming GPRS packets via NS/UDP */
@@ -158,32 +178,40 @@ struct gprs_nsvc *gprs_nsvc_by_nsei(struct gprs_ns_inst *nsi, uint16_t nsei);
struct gprs_nsvc *gprs_nsvc_by_nsvci(struct gprs_ns_inst *nsi, uint16_t nsvci);
/* Initiate a RESET procedure (including timer start, ...)*/
-void gprs_nsvc_reset(struct gprs_nsvc *nsvc, uint8_t cause);
+int gprs_nsvc_reset(struct gprs_nsvc *nsvc, uint8_t cause);
/* Add NS-specific VTY stuff */
int gprs_ns_vty_init(struct gprs_ns_inst *nsi);
-#define NS_ALLOC_SIZE 2048
-#define NS_ALLOC_HEADROOM 20
-static inline struct msgb *gprs_ns_msgb_alloc(void)
-{
- return msgb_alloc_headroom(NS_ALLOC_SIZE, NS_ALLOC_HEADROOM, "GPRS/NS");
-}
+/* Resturn peer info as string (NOTE: the buffer is allocated statically) */
+const char *gprs_ns_ll_str(struct gprs_nsvc *nsvc);
+
+/* Copy the link layer info from other into nsvc */
+void gprs_ns_ll_copy(struct gprs_nsvc *nsvc, struct gprs_nsvc *other);
+
+/* Clear the link layer info (will never match a real link then) */
+void gprs_ns_ll_clear(struct gprs_nsvc *nsvc);
+
+struct msgb *gprs_ns_msgb_alloc(void);
enum signal_ns {
S_NS_RESET,
S_NS_BLOCK,
S_NS_UNBLOCK,
S_NS_ALIVE_EXP, /* Tns-alive expired more than N times */
+ S_NS_REPLACED, /* nsvc object is replaced (sets old_nsvc) */
+ S_NS_MISMATCH, /* got an unexpected IE (sets msg, pdu_type, ie_type) */
};
struct ns_signal_data {
struct gprs_nsvc *nsvc;
+ struct gprs_nsvc *old_nsvc;
uint8_t cause;
+ uint8_t pdu_type;
+ uint8_t ie_type;
+ struct msgb *msg;
};
void gprs_ns_set_log_ss(int ss);
/*! }@ */
-
-#endif
diff --git a/src/shared/libosmocore/include/osmocom/gprs/gprs_ns_frgre.h b/src/shared/libosmocore/include/osmocom/gprs/gprs_ns_frgre.h
index abcd43ff..72ef7d19 100644
--- a/src/shared/libosmocore/include/osmocom/gprs/gprs_ns_frgre.h
+++ b/src/shared/libosmocore/include/osmocom/gprs/gprs_ns_frgre.h
@@ -1,6 +1,3 @@
-#ifndef _GPRS_NS_FRGRE_H
-#define _GPRS_NS_FRGRE_H
+#pragma once
int gprs_ns_frgre_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg);
-
-#endif
diff --git a/src/shared/libosmocore/include/osmocom/gprs/gprs_rlc.h b/src/shared/libosmocore/include/osmocom/gprs/gprs_rlc.h
new file mode 100644
index 00000000..d34d49bf
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gprs/gprs_rlc.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <stdint.h>
+
+/*! \brief 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];
+};
+
+/*! \brief 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,
+};
+
+/*! \brief EGPRS header types (TS 04.60 10.0a.2) */
+enum egprs_hdr_type {
+ EGPRS_HDR_TYPE1,
+ EGPRS_HDR_TYPE2,
+ EGPRS_HDR_TYPE3,
+};
+
+int egprs_get_cps(struct egprs_cps *cps, uint8_t type, uint8_t bits);
diff --git a/src/shared/libosmocore/include/osmocom/gprs/protocol/gsm_04_60.h b/src/shared/libosmocore/include/osmocom/gprs/protocol/gsm_04_60.h
new file mode 100644
index 00000000..42028bd7
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gprs/protocol/gsm_04_60.h
@@ -0,0 +1,208 @@
+#pragma once
+
+/* General Packet Radio Service (GPRS)
+ * Radio Link Control / Medium Access Control (RLC/MAC) protocol
+ * 3GPP TS 04.60 version 8.27.0 Release 1999
+ */
+
+#include <stdint.h>
+
+#if OSMO_IS_LITTLE_ENDIAN == 1
+/* TS 04.60 10.3a.4.1.1 */
+struct gprs_rlc_ul_header_egprs_1 {
+ 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;
+} __attribute__ ((packed));
+
+/* TS 04.60 10.3a.4.2.1 */
+struct gprs_rlc_ul_header_egprs_2 {
+ 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;
+} __attribute__ ((packed));
+
+/* TS 04.60 10.3a.4.3.1 */
+struct gprs_rlc_ul_header_egprs_3 {
+ 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;
+} __attribute__ ((packed));
+
+struct gprs_rlc_dl_header_egprs_1 {
+ 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;
+} __attribute__ ((packed));
+
+struct gprs_rlc_dl_header_egprs_2 {
+ 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;
+} __attribute__ ((packed));
+
+struct gprs_rlc_dl_header_egprs_3 {
+ 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;
+} __attribute__ ((packed));
+#else
+/* TS 04.60 10.3a.4.1.1 */
+struct gprs_rlc_ul_header_egprs_1 {
+ 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;
+} __attribute__ ((packed));
+
+/* TS 04.60 10.3a.4.2.1 */
+struct gprs_rlc_ul_header_egprs_2 {
+ 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;
+} __attribute__ ((packed));
+
+/* TS 04.60 10.3a.4.3.1 */
+struct gprs_rlc_ul_header_egprs_3 {
+ 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;
+} __attribute__ ((packed));
+
+struct gprs_rlc_dl_header_egprs_1 {
+ 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;
+} __attribute__ ((packed));
+
+struct gprs_rlc_dl_header_egprs_2 {
+ 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;
+} __attribute__ ((packed));
+
+struct gprs_rlc_dl_header_egprs_3 {
+ 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;
+} __attribute__ ((packed));
+#endif
diff --git a/src/shared/libosmocore/include/osmocom/gprs/protocol/gsm_08_16.h b/src/shared/libosmocore/include/osmocom/gprs/protocol/gsm_08_16.h
index 4c3eda32..8b2ac564 100644
--- a/src/shared/libosmocore/include/osmocom/gprs/protocol/gsm_08_16.h
+++ b/src/shared/libosmocore/include/osmocom/gprs/protocol/gsm_08_16.h
@@ -1,5 +1,4 @@
-#ifndef _OSMO_08_16_H
-#define _OSMO_08_16_H
+#pragma once
/* 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)
@@ -81,5 +80,3 @@ enum ns_cause {
NS_CAUSE_UNKN_IP_ADDR = 0x13,
NS_CAUSE_UNKN_IP_TEST_FAILED = 0x14,
};
-
-#endif
diff --git a/src/shared/libosmocore/include/osmocom/gprs/protocol/gsm_08_18.h b/src/shared/libosmocore/include/osmocom/gprs/protocol/gsm_08_18.h
index 3a351eaa..529e9889 100644
--- a/src/shared/libosmocore/include/osmocom/gprs/protocol/gsm_08_18.h
+++ b/src/shared/libosmocore/include/osmocom/gprs/protocol/gsm_08_18.h
@@ -1,5 +1,4 @@
-#ifndef _OSMO_08_18_H
-#define _OSMO_08_18_H
+#pragma once
#include <stdint.h>
@@ -140,5 +139,3 @@ enum gprs_bssgp_cause {
BSSGP_CAUSE_PROTO_ERR_UNSPEC = 0x27,
BSSGP_CAUSE_PDU_INCOMP_FEAT = 0x28,
};
-
-#endif
diff --git a/src/shared/libosmocore/include/osmocom/gsm/a5.h b/src/shared/libosmocore/include/osmocom/gsm/a5.h
index 649dbab1..a2278f22 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/a5.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/a5.h
@@ -20,11 +20,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef __OSMO_A5_H__
-#define __OSMO_A5_H__
+#pragma once
#include <stdint.h>
+#include <osmocom/core/defs.h>
#include <osmocom/core/bits.h>
/*! \defgroup a5 GSM A5 ciphering algorithm
@@ -49,15 +49,13 @@ osmo_a5_fn_count(uint32_t fn)
}
/* Notes:
- * - key must be 8 bytes long (or NULL for A5/0)
+ * - key must be 8 or 16 (for a5/4) bytes long (or NULL for A5/0)
* - the dl and ul pointer must be either NULL or 114 bits long
* - fn is the _real_ GSM frame number.
* (converted internally to fn_count)
*/
-void osmo_a5(int n, const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul);
-void osmo_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul);
-void osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul);
+int osmo_a5(int n, const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul);
+void osmo_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul) OSMO_DEPRECATED("Use generic osmo_a5() instead");
+void osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul) OSMO_DEPRECATED("Use generic osmo_a5() instead");
/*! @} */
-
-#endif /* __OSMO_A5_H__ */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/abis_nm.h b/src/shared/libosmocore/include/osmocom/gsm/abis_nm.h
index 320ac3e5..14dbc08c 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/abis_nm.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/abis_nm.h
@@ -1,5 +1,4 @@
-#ifndef _OSMO_GSM_ABIS_NM_H
-#define _OSMO_GSM_ABIS_NM_H
+#pragma once
/*! \defgroup oml A-bis OML
* @{
@@ -11,6 +10,9 @@
/*! \file abis_nm.h */
+extern const char abis_nm_ipa_magic[13];
+extern const char abis_nm_osmo_magic[12];
+
enum abis_nm_msgtype;
enum gsm_phys_chan_config;
@@ -19,6 +21,7 @@ extern const enum abis_nm_msgtype abis_nm_no_ack_nack[3];
extern const enum abis_nm_msgtype abis_nm_sw_load_msgs[9];
extern const enum abis_nm_msgtype abis_nm_nacks[33];
+extern const struct value_string abis_nm_msg_disc_names[];
extern const struct value_string abis_nm_obj_class_names[];
extern const struct value_string abis_nm_adm_state_names[];
@@ -30,11 +33,21 @@ extern const struct tlv_definition abis_nm_att_tlvdef;
const char *abis_nm_opstate_name(uint8_t os);
const char *abis_nm_avail_name(uint8_t avail);
const char *abis_nm_test_name(uint8_t test);
-void abis_nm_debugp_foh(int ss, struct abis_om_fom_hdr *foh);
+extern const struct tlv_definition abis_nm_osmo_att_tlvdef;
+extern const struct tlv_definition abis_nm_att_tlvdef_ipa;
+
+/*! \brief write a human-readable OML header to the debug log
+ * \param[in] ss Logging sub-system
+ * \param[in] foh A-bis OML FOM header
+ */
+#define abis_nm_debugp_foh(ss, foh) \
+ DEBUGP(ss, "OC=%s(%02x) INST=(%02x,%02x,%02x) ", \
+ get_value_string(abis_nm_obj_class_names, (foh)->obj_class), \
+ (foh)->obj_class, (foh)->obj_inst.bts_nr, (foh)->obj_inst.trx_nr, \
+ (foh)->obj_inst.ts_nr)
+
int abis_nm_chcomb4pchan(enum gsm_phys_chan_config pchan);
-enum abis_nm_chan_comb abis_nm_pchan4chcomb(uint8_t chcomb);
+enum gsm_phys_chan_config abis_nm_pchan4chcomb(uint8_t chcomb);
/*! @} */
-
-#endif /* _OSMO_GSM_ABIS_NM_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/apn.h b/src/shared/libosmocore/include/osmocom/gsm/apn.h
new file mode 100644
index 00000000..b4ece3a2
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/apn.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <stdint.h>
+
+/* 23.003 Section 9.1.1, excluding any terminating zero byte */
+#define APN_NI_MAXLEN 63
+
+/* 23.003 Section 9.1, excluding any terminating zero byte */
+#define APN_MAXLEN 100
+
+char *osmo_apn_qualify(unsigned int mcc, unsigned int mnc, const char *ni);
+
+/* Compose a string of the form '<ni>.mnc001.mcc002.gprs\0', returned in a
+ * static buffer. */
+char *osmo_apn_qualify_from_imsi(const char *imsi,
+ const char *ni, int have_3dig_mnc);
+
+int osmo_apn_from_str(uint8_t *apn_enc, size_t max_apn_enc_len, const char *str);
+char * osmo_apn_to_str(char *out_str, const uint8_t *apn_enc, size_t apn_enc_len);
diff --git a/src/shared/libosmocore/include/osmocom/gsm/bitvec_gsm.h b/src/shared/libosmocore/include/osmocom/gsm/bitvec_gsm.h
new file mode 100644
index 00000000..20741efa
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/bitvec_gsm.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <osmocom/core/bitvec.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+
+/*! \defgroup bitvec helpers for GSM
+ * @{
+ */
+/*! \file bitvec_gsm.h */
+
+void bitvec_add_range1024(struct bitvec *bv, const struct gsm48_range_1024 *r);
+
+/*! @} */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/comp128.h b/src/shared/libosmocore/include/osmocom/gsm/comp128.h
index e4587d4f..8ff76b10 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/comp128.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/comp128.h
@@ -4,19 +4,19 @@
* See comp128.c for details
*/
-#ifndef __COMP128_H__
-#define __COMP128_H__
+#pragma once
#include <stdint.h>
+#include <osmocom/core/defs.h>
+
/*
- * Performs the COMP128 algorithm (used as A3/A8)
+ * Performs the COMP128v1 algorithm (used as A3/A8)
* ki : uint8_t [16]
* srand : uint8_t [16]
* sres : uint8_t [4]
* kc : uint8_t [8]
*/
-void comp128(const uint8_t *ki, const uint8_t *srand, uint8_t *sres, uint8_t *kc);
-
-#endif /* __COMP128_H__ */
+void comp128v1(const uint8_t *ki, const uint8_t *srand, uint8_t *sres, uint8_t *kc);
+void comp128(const uint8_t *ki, const uint8_t *srand, uint8_t *sres, uint8_t *kc) OSMO_DEPRECATED("Use generic API from osmocom/crypt/auth.h instead");
diff --git a/src/shared/libosmocore/include/osmocom/gsm/comp128v23.h b/src/shared/libosmocore/include/osmocom/gsm/comp128v23.h
new file mode 100644
index 00000000..31d39335
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/comp128v23.h
@@ -0,0 +1,20 @@
+/*
+ * COMP128v23 header
+ *
+ * See comp128v23.c for details
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+/*
+ * Performs the COMP128 version 2 and 3 algorithm (used as A3/A8)
+ * ki : uint8_t [16]
+ * srand : uint8_t [16]
+ * sres : uint8_t [4]
+ * kc : uint8_t [8]
+ * returns 1 if not version 2 or 3 specified
+ */
+int comp128v2(const uint8_t *ki, const uint8_t *rand, uint8_t *sres, uint8_t *kc);
+int comp128v3(const uint8_t *ki, const uint8_t *rand, uint8_t *sres, uint8_t *kc);
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gan.h b/src/shared/libosmocore/include/osmocom/gsm/gan.h
index ab4c1e4e..6f2b5351 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/gan.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/gan.h
@@ -1,9 +1,6 @@
-#ifndef _OSMO_GSM_GAN_H
-#define _OSMO_GSM_GAN_H
+#pragma once
#include <osmocom/core/utils.h>
extern const struct value_string gan_msgt_vals[];
static const struct value_string gan_pdisc_vals[];
-
-#endif
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gea.h b/src/shared/libosmocore/include/osmocom/gsm/gea.h
new file mode 100644
index 00000000..3051101d
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/gea.h
@@ -0,0 +1,17 @@
+/*
+ * GEA3 header
+ *
+ * See gea.c for details
+ */
+
+#pragma once
+
+#include <osmocom/crypt/gprs_cipher.h>
+
+#include <stdint.h>
+
+int gea3(uint8_t *out, uint16_t len, uint8_t *kc, uint32_t iv,
+ enum gprs_cipher_direction direct);
+
+int gea4(uint8_t *out, uint16_t len, uint8_t *kc, uint32_t iv,
+ enum gprs_cipher_direction direct);
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm0341.h b/src/shared/libosmocore/include/osmocom/gsm/gsm0341.h
new file mode 100644
index 00000000..29ba3e16
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm0341.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <osmocom/gsm/protocol/gsm_03_41.h>
+
+struct gsm341_ms_message *
+gsm0341_build_msg(void *ctx, uint8_t geo_scope, uint8_t msg_code,
+ uint8_t update, uint16_t msg_id, uint8_t dcs,
+ uint8_t page_total, uint8_t page_cur,
+ uint8_t *data, uint8_t len);
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm0411_smc.h b/src/shared/libosmocore/include/osmocom/gsm/gsm0411_smc.h
index 2140db43..89964fa1 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/gsm0411_smc.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm0411_smc.h
@@ -1,5 +1,4 @@
-#ifndef _GSM0411_SMC_H
-#define _GSM0411_SMC_H
+#pragma once
#include <osmocom/core/timer.h>
#include <osmocom/gsm/protocol/gsm_04_11.h>
@@ -24,6 +23,7 @@
#define GSM411_MNSMS_REL_REQ 0x107
struct gsm411_smc_inst {
+ uint64_t id; /* a unique id for the SMS */
int network; /* is this a MO (0) or MT (1) transfer */
int (*mn_recv) (struct gsm411_smc_inst *inst, int msg_type,
struct msgb *msg);
@@ -43,7 +43,7 @@ struct gsm411_smc_inst {
extern const struct value_string gsm411_cp_cause_strs[];
/* init a new instance */
-void gsm411_smc_init(struct gsm411_smc_inst *inst, int network,
+void gsm411_smc_init(struct gsm411_smc_inst *inst, uint64_t id, int network,
int (*mn_recv) (struct gsm411_smc_inst *inst, int msg_type,
struct msgb *msg),
int (*mm_send) (struct gsm411_smc_inst *inst, int msg_type,
@@ -59,5 +59,3 @@ int gsm411_smc_send(struct gsm411_smc_inst *inst, int msg_type,
/* message from lower layer */
int gsm411_smc_recv(struct gsm411_smc_inst *inst, int msg_type,
struct msgb *msg, int cp_msg_type);
-
-#endif /* _GSM0411_SMC_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm0411_smr.h b/src/shared/libosmocore/include/osmocom/gsm/gsm0411_smr.h
index 5ea8584d..bc908a75 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/gsm0411_smr.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm0411_smr.h
@@ -1,5 +1,4 @@
-#ifndef _GSM0411_SMR_H
-#define _GSM0411_SMR_H
+#pragma once
#include <osmocom/gsm/protocol/gsm_04_11.h>
@@ -11,6 +10,7 @@
#define GSM411_SM_RL_REPORT_IND 0x406
struct gsm411_smr_inst {
+ uint64_t id; /* a unique id for the SMS */
int network; /* is this a MO (0) or MT (1) transfer */
int (*rl_recv) (struct gsm411_smr_inst *inst, int msg_type,
struct msgb *msg);
@@ -24,7 +24,7 @@ struct gsm411_smr_inst {
extern const struct value_string gsm411_rp_cause_strs[];
/* init a new instance */
-void gsm411_smr_init(struct gsm411_smr_inst *inst, int network,
+void gsm411_smr_init(struct gsm411_smr_inst *inst, uint64_t id, int network,
int (*rl_recv) (struct gsm411_smr_inst *inst, int msg_type,
struct msgb *msg),
int (*mn_send) (struct gsm411_smr_inst *inst, int msg_type,
@@ -40,6 +40,3 @@ int gsm411_smr_send(struct gsm411_smr_inst *inst, int msg_type,
/* message from lower layer */
int gsm411_smr_recv(struct gsm411_smr_inst *inst, int msg_type,
struct msgb *msg);
-
-#endif /* _GSM0411_SMR_H */
-
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm0411_utils.h b/src/shared/libosmocore/include/osmocom/gsm/gsm0411_utils.h
index d29ca631..70050105 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/gsm0411_utils.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm0411_utils.h
@@ -1,5 +1,6 @@
-#ifndef _GSM0411_UTILS_H
-#define _GSM0411_UTILS_H
+#pragma once
+
+#include <time.h>
/* Turn int into semi-octet representation: 98 => 0x89 */
uint8_t gsm411_bcdify(uint8_t value);
@@ -32,5 +33,3 @@ int gsm411_push_rp_header(struct msgb *msg, uint8_t rp_msg_type,
/* Prefix msg with a 04.08/04.11 CP header */
int gsm411_push_cp_header(struct msgb *msg, uint8_t proto, uint8_t trans,
uint8_t msg_type);
-
-#endif /* _GSM0411_UTILS_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm0480.h b/src/shared/libosmocore/include/osmocom/gsm/gsm0480.h
index d6626d60..6ca23e98 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/gsm0480.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm0480.h
@@ -1,26 +1,42 @@
-#ifndef gsm0480_h
-#define gsm0480_h
+#pragma once
+#include <osmocom/core/defs.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/protocol/gsm_04_80.h>
#define MAX_LEN_USSD_STRING 31
+/* deprecated */
struct ussd_request {
char text[MAX_LEN_USSD_STRING + 1];
uint8_t transaction_id;
uint8_t invoke_id;
};
+/* deprecated */
int gsm0480_decode_ussd_request(const struct gsm48_hdr *hdr, uint16_t len,
- struct ussd_request *request);
+ struct ussd_request *request) OSMO_DEPRECATED("Use gsm0480_decode_ss_request() instead");
+
+struct ss_request {
+ uint8_t opcode;
+ uint8_t ss_code;
+ uint8_t ussd_text[MAX_LEN_USSD_STRING + 1];
+ uint8_t transaction_id;
+ uint8_t invoke_id;
+};
+
+int gsm0480_decode_ss_request(const struct gsm48_hdr *hdr, uint16_t len,
+ struct ss_request *request);
struct msgb *gsm0480_create_ussd_resp(uint8_t invoke_id, uint8_t trans_id, const char *text);
struct msgb *gsm0480_create_unstructuredSS_Notify(int alertPattern, const char *text);
struct msgb *gsm0480_create_notifySS(const char *text);
+struct msgb *gsm0480_create_ussd_notify(int level, const char *text);
+struct msgb *gsm0480_create_ussd_release_complete(void);
int gsm0480_wrap_invoke(struct msgb *msg, int op, int link_id);
int gsm0480_wrap_facility(struct msgb *msg);
-#endif
+struct gsm48_hdr *gsm0480_l3hdr_push(struct msgb *msg, uint8_t proto_discr,
+ uint8_t msg_type);
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm0502.h b/src/shared/libosmocore/include/osmocom/gsm/gsm0502.h
index 46b629e4..6ee5a600 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/gsm0502.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm0502.h
@@ -1,5 +1,4 @@
-#ifndef OSMOCOM_GSM_0502_H
-#define OSMOCOM_GSM_0502_H
+#pragma once
#include <stdint.h>
@@ -34,5 +33,3 @@ 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);
-
-#endif
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm0503.h b/src/shared/libosmocore/include/osmocom/gsm/gsm0503.h
new file mode 100644
index 00000000..de28ad23
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm0503.h
@@ -0,0 +1,174 @@
+/*
+ * gsm0503.h
+ *
+ * Copyright (C) 2016 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 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/conv.h>
+
+/*! \file gsm0503.h
+ * Osmocom convolutional encoder/decoder for xCCH channels, see 3GPP TS 05.03
+ */
+
+/*! \brief structure describing convolutional code xCCH
+ *
+ * Non-recursive code, flushed, not punctured code.
+ */
+extern const struct osmo_conv_code gsm0503_xcch;
+
+/*! \brief structure describing convolutional code RACH
+ */
+extern const struct osmo_conv_code gsm0503_rach;
+
+/*! \brief structure describing convolutional code SCH
+ */
+extern const struct osmo_conv_code gsm0503_sch;
+
+/*! \brief structures describing convolutional codes CS2/3
+ */
+extern const struct osmo_conv_code gsm0503_cs2;
+extern const struct osmo_conv_code gsm0503_cs3;
+
+/*! \brief structure describing convolutional code TCH/FR
+ */
+extern const struct osmo_conv_code gsm0503_tch_fr;
+
+/*! \brief structure describing convolutional code TCH/HR
+ */
+extern const struct osmo_conv_code gsm0503_tch_hr;
+
+/*! \brief structure describing convolutional code TCH/AFS 12.2
+ */
+extern const struct osmo_conv_code gsm0503_tch_afs_12_2;
+
+/*! \brief structure describing convolutional code TCH/AFS 10.2
+ */
+extern const struct osmo_conv_code gsm0503_tch_afs_10_2;
+
+/*! \brief structure describing convolutional code TCH/AFS 7.95
+ */
+extern const struct osmo_conv_code gsm0503_tch_afs_7_95;
+
+/*! \brief structure describing convolutional code TCH/AFS 7.4
+ */
+extern const struct osmo_conv_code gsm0503_tch_afs_7_4;
+
+/*! \brief structure describing convolutional code TCH/AFS 6.7
+ */
+extern const struct osmo_conv_code gsm0503_tch_afs_6_7;
+
+/*! \brief structure describing convolutional code TCH/AFS 5.9
+ */
+extern const struct osmo_conv_code gsm0503_tch_afs_5_9;
+
+/*! \brief structure describing convolutional code TCH/AFS 5.15
+ */
+extern const struct osmo_conv_code gsm0503_tch_afs_5_15;
+
+/*! \brief structure describing convolutional code TCH/AFS 4.75
+ */
+extern const struct osmo_conv_code gsm0503_tch_afs_4_75;
+
+/*! \brief structure describing convolutional code TCH/AHS 7.95
+ */
+extern const struct osmo_conv_code gsm0503_tch_ahs_7_95;
+
+/*! \brief structure describing convolutional code TCH/AHS 7.4
+ */
+extern const struct osmo_conv_code gsm0503_tch_ahs_7_4;
+
+/*! \brief structure describing convolutional code TCH/AHS 6.7
+ */
+extern const struct osmo_conv_code gsm0503_tch_ahs_6_7;
+
+/*! \brief structure describing convolutional code TCH/AHS 5.9
+ */
+extern const struct osmo_conv_code gsm0503_tch_ahs_5_9;
+
+/*! \brief structure describing convolutional code TCH/AHS 5.15
+ */
+extern const struct osmo_conv_code gsm0503_tch_ahs_5_15;
+
+/*! \brief structure describing convolutional code TCH/AHS 4.75
+ */
+extern const struct osmo_conv_code gsm0503_tch_ahs_4_75;
+
+/*! \brief structure describing convolutional code EDGE MCS-1 DL HDR
+ */
+extern const struct osmo_conv_code gsm0503_mcs1_dl_hdr;
+
+/*! \brief structure describing convolutional code EDGE MCS-1 UL HDR
+ */
+extern const struct osmo_conv_code gsm0503_mcs1_ul_hdr;
+
+/*! \brief structure describing convolutional code EDGE MCS-1
+ */
+extern const struct osmo_conv_code gsm0503_mcs1;
+
+/*! \brief structure describing convolutional code EDGE MCS-2
+ */
+extern const struct osmo_conv_code gsm0503_mcs2;
+
+/*! \brief structure describing convolutional code EDGE MCS-3
+ */
+extern const struct osmo_conv_code gsm0503_mcs3;
+
+/*! \brief structure describing convolutional code EDGE MCS-4
+ */
+extern const struct osmo_conv_code gsm0503_mcs4;
+
+/*! \brief structure describing convolutional code EDGE MCS-5 DL HDR
+ */
+extern const struct osmo_conv_code gsm0503_mcs5_dl_hdr;
+
+/*! \brief structure describing convolutional code EDGE MCS-5 UL HDR
+ */
+extern const struct osmo_conv_code gsm0503_mcs5_ul_hdr;
+
+/*! \brief structure describing convolutional code EDGE MCS-5
+ */
+extern const struct osmo_conv_code gsm0503_mcs5;
+
+/*! \brief structure describing convolutional code EDGE MCS-6
+ */
+extern const struct osmo_conv_code gsm0503_mcs6;
+
+/*! \brief structure describing convolutional code EDGE MCS-7 DL HDR
+ */
+extern const struct osmo_conv_code gsm0503_mcs7_dl_hdr;
+
+/*! \brief structure describing convolutional code EDGE MCS-7 UL HDR
+ */
+extern const struct osmo_conv_code gsm0503_mcs7_ul_hdr;
+
+/*! \brief structure describing convolutional code EDGE MCS-7
+ */
+extern const struct osmo_conv_code gsm0503_mcs7;
+
+/*! \brief structure describing convolutional code EDGE MCS-8
+ */
+extern const struct osmo_conv_code gsm0503_mcs8;
+
+/*! \brief structure describing convolutional code EDGE MCS-9
+ */
+extern const struct osmo_conv_code gsm0503_mcs9;
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm0808.h b/src/shared/libosmocore/include/osmocom/gsm/gsm0808.h
index 5380dd9e..a7e102c6 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/gsm0808.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm0808.h
@@ -17,8 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
-#ifndef OSMOCORE_GSM0808_H
-#define OSMOCORE_GSM0808_H
+#pragma once
#include "tlv.h"
@@ -26,6 +25,7 @@ struct msgb;
struct msgb *gsm0808_create_layer3(struct msgb *msg, uint16_t netcode, uint16_t countrycode, int lac, uint16_t ci);
struct msgb *gsm0808_create_reset(void);
+struct msgb *gsm0808_create_reset_ack(void);
struct msgb *gsm0808_create_clear_command(uint8_t reason);
struct msgb *gsm0808_create_clear_complete(void);
struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id);
@@ -46,5 +46,3 @@ const struct tlv_definition *gsm0808_att_tlvdef(void);
const char *gsm0808_bssmap_name(uint8_t msg_type);
const char *gsm0808_bssap_name(uint8_t msg_type);
-
-#endif
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm23003.h b/src/shared/libosmocore/include/osmocom/gsm/gsm23003.h
new file mode 100644
index 00000000..29e646cc
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm23003.h
@@ -0,0 +1,80 @@
+#pragma once
+#include <stdint.h>
+
+/* 23.003 Chapter 12.1 */
+struct osmo_plmn_id {
+ uint16_t mcc;
+ uint16_t mnc;
+};
+
+/* 4.1 */
+struct osmo_location_area_id {
+ struct osmo_plmn_id plmn;
+ uint16_t lac;
+};
+
+/* 4.2 */
+struct osmo_routing_area_id {
+ struct osmo_location_area_id lac;
+ uint8_t rac;
+};
+
+/* 4.3.1 */
+struct osmo_cell_global_id {
+ struct osmo_location_area_id lai;
+ uint16_t cell_identity;
+};
+
+/* 12.5 */
+struct osmo_service_area_id {
+ struct osmo_location_area_id lai;
+ uint16_t sac;
+};
+
+/* 12.6 */
+struct osmo_shared_network_area_id {
+ struct osmo_plmn_id plmn;
+ uint32_t snac;
+};
+
+/* 5.1 */
+enum osmo_gsn_addr_type {
+ GSN_ADDR_TYPE_IPV4 = 0,
+ GSN_ADDR_TYPE_IPV6 = 1,
+};
+
+/* 5.1 */
+struct osmo_gsn_address {
+ enum osmo_gsn_addr_type type;
+ uint8_t length;
+ uint8_t addr[16];
+};
+
+/* 19.4.2.3 */
+struct osmo_tracking_area_id {
+ struct osmo_plmn_id plmn;
+ uint16_t tac;
+};
+
+struct osmo_eutran_cell_global_id {
+ struct osmo_plmn_id plmn;
+ uint32_t eci; /* FIXME */
+};
+
+/* 2.8.1 */
+struct osmo_mme_id {
+ uint16_t group_id;
+ uint8_t code;
+};
+
+/* 2.8.1 */
+struct osmo_gummei {
+ struct osmo_plmn_id plmn;
+ struct osmo_mme_id mme;
+};
+
+/* 2.8.1 */
+struct osmo_guti {
+ struct osmo_gummei gummei;
+ uint32_t mtmsi;
+};
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm48.h b/src/shared/libosmocore/include/osmocom/gsm/gsm48.h
index 1e7498a9..6a52c2d8 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/gsm48.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm48.h
@@ -1,10 +1,12 @@
-#ifndef _OSMOCORE_GSM48_H
-#define _OSMOCORE_GSM48_H
+#pragma once
#include <osmocom/gsm/tlv.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/gsm48_ie.h>
+/* reserved according to GSM 03.03 § 2.4 */
+#define GSM_RESERVED_TMSI 0xFFFFFFFF
+
/* A parsed GPRS routing area */
struct gprs_ra_id {
uint16_t mnc;
@@ -18,6 +20,7 @@ extern const struct tlv_definition gsm48_rr_att_tlvdef;
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 *rr_cause_name(uint8_t cause);
int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *mcc,
@@ -30,6 +33,7 @@ int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi);
/* Convert Mobile Identity (10.5.1.4) to string */
int gsm48_mi_to_string(char *string, const int str_len,
const uint8_t *mi, const int mi_len);
+const char *gsm48_mi_type_name(uint8_t mi);
/* Parse Routeing Area Identifier */
void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf);
@@ -37,4 +41,5 @@ int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid);
int gsm48_number_of_paging_subchannels(struct gsm48_control_channel_descr *chan_desc);
-#endif
+void gsm48_mcc_mnc_to_bcd(uint8_t *bcd_dst, uint16_t mcc, uint16_t mnc);
+void gsm48_mcc_mnc_from_bcd(uint8_t *bcd_src, uint16_t *mcc, uint16_t *mnc);
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm48_ie.h b/src/shared/libosmocore/include/osmocom/gsm/gsm48_ie.h
index 2e576429..a3ae0763 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/gsm48_ie.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm48_ie.h
@@ -1,5 +1,4 @@
-#ifndef _OSMOCORE_GSM48_IE_H
-#define _OSMOCORE_GSM48_IE_H
+#pragma once
#include <stdint.h>
#include <string.h>
@@ -113,5 +112,3 @@ 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);
-
-#endif
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm_utils.h b/src/shared/libosmocore/include/osmocom/gsm/gsm_utils.h
index 6d316727..1ffe5797 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/gsm_utils.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm_utils.h
@@ -22,11 +22,14 @@
*
*/
-#ifndef GSM_UTILS_H
-#define GSM_UTILS_H
+#pragma once
+#include <stddef.h>
#include <stdint.h>
+#include <osmocom/core/defs.h>
+#include <osmocom/core/utils.h>
+
#define ADD_MODULO(sum, delta, modulo) do { \
if ((sum += delta) >= modulo) \
sum -= modulo; \
@@ -56,25 +59,67 @@ enum gsm_band {
const char *gsm_band_name(enum gsm_band band);
enum gsm_band gsm_band_parse(const char *mhz);
-int gsm_7bit_decode(char *decoded, const uint8_t *user_data, uint8_t length);
-int gsm_7bit_decode_hdr(char *decoded, const uint8_t *user_data, uint8_t length, uint8_t ud_hdr_ind);
-int gsm_7bit_encode(uint8_t *result, const char *data);
+/*!
+ * \brief Decode a sequence of GSM 03.38 encoded 7 bit characters.
+ *
+ * \param decoded The destination buffer for the decoded characters.
+ * \param n A maximum of n chars is written (incl. terminating \0).
+ * Requires n >= 1.
+ * \param user_data A pointer to the start of the packed 7bit character
+ * sequence.
+ * \param length The length of the input sequence in septets, for
+ * example pass octet_length*8/7.
+ *
+ * \returns the number of (8 bit) chars written excluding the terminating \0.
+ * This is the same like strlen(decoded).
+ */
+int gsm_7bit_decode_n(char *decoded, size_t n, const uint8_t *user_data, uint8_t length);
+
+/*!
+ * \brief Decode a sequence of 7 bit characters (USSD encoding).
+ *
+ * \see gsm_7bit_encode_n()
+ */
+int gsm_7bit_decode_n_ussd(char *decoded, size_t n, const uint8_t *user_data, uint8_t length);
+
+/*!
+ * \brief Encode a text string into GSM 03.38 encoded 7 bit characters.
+ *
+ * \param result The destination buffer for the packed 7 bit sequence.
+ * \param n A maximum of n octets is written.
+ * \param data A pointer to the start of the \0 terminated 8 bit character
+ * string.
+ * \param octets_written Iff not NULL, *octets_written will be set to the
+ * number of octets written to the result buffer.
+ *
+ * \returns the number of septets that have been created.
+ */
+int gsm_7bit_encode_n(uint8_t *result, size_t n, const char *data, int *octets_written);
+
+/*!
+ * \brief Encode a text string into GSM 03.38 encoded 7 bit characters (USSD encoding).
+ *
+ * \see gsm_7bit_decode_n()
+ */
+int gsm_7bit_encode_n_ussd(uint8_t *result, size_t n, const char *data, int *octets_written);
-int gsm_septets2octets(uint8_t *result, uint8_t *rdata, uint8_t septet_len, uint8_t padding);
+/* the four functions below are helper functions and here for the unit test */
+int gsm_septets2octets(uint8_t *result, const uint8_t *rdata, uint8_t septet_len, uint8_t padding);
int gsm_septet_encode(uint8_t *result, const char *data);
uint8_t gsm_get_octet_len(const uint8_t sept_len);
+int gsm_7bit_decode_n_hdr(char *decoded, size_t n, const uint8_t *user_data, uint8_t length, uint8_t ud_hdr_ind);
unsigned int ms_class_gmsk_dbm(enum gsm_band band, int ms_class);
int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm);
int ms_pwr_dbm(enum gsm_band band, uint8_t lvl);
-/* According to TS 08.05 Chapter 8.1.4 */
+/* According to TS 05.08 Chapter 8.1.4 */
int rxlev2dbm(uint8_t rxlev);
uint8_t dbm2rxlev(int dbm);
/* According to GSM 04.08 Chapter 10.5.1.6 */
-static inline int ms_cm2_a5n_support(uint8_t *cm2, int n) {
+static inline int ms_cm2_a5n_support(uint8_t *cm2, unsigned n) {
switch (n) {
case 0: return 1;
case 1: return (cm2[0] & (1<<3)) ? 0 : 1;
@@ -85,6 +130,18 @@ static inline int ms_cm2_a5n_support(uint8_t *cm2, int n) {
}
}
+/* According to GSM 04.08 Chapter 10.5.1.7 */
+static inline int ms_cm3_a5n_support(uint8_t *cm3, unsigned n) {
+ switch (n) {
+ case 4: return (cm3[0] & (1<<0)) ? 1 : 0;
+ case 5: return (cm3[0] & (1<<1)) ? 1 : 0;
+ case 6: return (cm3[0] & (1<<2)) ? 1 : 0;
+ case 7: return (cm3[0] & (1<<3)) ? 1 : 0;
+ default:
+ return 0;
+ }
+}
+
/* According to GSM 04.08 Chapter 10.5.2.29 */
static inline int rach_max_trans_val2raw(int val) { return (val >> 1) & 3; }
static inline int rach_max_trans_raw2val(int raw) {
@@ -101,6 +158,9 @@ enum gsm_band gsm_arfcn2band(uint16_t arfcn);
/* Convert an ARFCN to the frequency in MHz * 10 */
uint16_t gsm_arfcn2freq10(uint16_t arfcn, int uplink);
+/* Convert a Frequency in MHz * 10 to ARFCN */
+uint16_t gsm_freq102arfcn(uint16_t freq10, int uplink);
+
/* Convert from frame number to GSM time */
void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn);
@@ -114,6 +174,8 @@ enum gprs_tlli_type {
TLLI_RANDOM,
TLLI_AUXILIARY,
TLLI_RESERVED,
+ TLLI_G_RNTI,
+ TLLI_RAND_G_RNTI,
};
/* TS 03.03 Chapter 2.6 */
@@ -132,6 +194,9 @@ enum gsm_phys_chan_config {
GSM_PCHAN_PDCH, /* GPRS PDCH */
GSM_PCHAN_TCH_F_PDCH, /* TCH/F if used, PDCH otherwise */
GSM_PCHAN_UNKNOWN,
+ GSM_PCHAN_CCCH_SDCCH4_CBCH,
+ GSM_PCHAN_SDCCH8_SACCH8C_CBCH,
+ GSM_PCHAN_TCH_F_TCH_H_PDCH,
_GSM_PCHAN_MAX
};
@@ -144,8 +209,18 @@ enum gsm_chan_t {
GSM_LCHAN_UNKNOWN,
GSM_LCHAN_CCCH,
GSM_LCHAN_PDTCH,
+ GSM_LCHAN_CBCH,
_GSM_LCHAN_MAX
};
+extern const struct value_string gsm_chan_t_names[];
+
+/* Deprectated functions */
+/* Limit encoding and decoding to use no more than this amount of buffer bytes */
+#define GSM_7BIT_LEGACY_MAX_BUFFER_SIZE 0x10000
-#endif
+int gsm_7bit_decode(char *decoded, const uint8_t *user_data, uint8_t length) OSMO_DEPRECATED("Use gsm_7bit_decode_n() instead");
+int gsm_7bit_decode_ussd(char *decoded, const uint8_t *user_data, uint8_t length) OSMO_DEPRECATED("Use gsm_7bit_decode_n_ussd() instead");
+int gsm_7bit_encode(uint8_t *result, const char *data) OSMO_DEPRECATED("Use gsm_7bit_encode_n() instead");
+int gsm_7bit_encode_ussd(uint8_t *result, const char *data, int *octets_written) OSMO_DEPRECATED("Use gsm_7bit_encode_n_ussd() instead");
+int gsm_7bit_encode_oct(uint8_t *result, const char *data, int *octets_written) OSMO_DEPRECATED("Use gsm_7bit_encode_n() instead");
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsup.h b/src/shared/libosmocore/include/osmocom/gsm/gsup.h
new file mode 100644
index 00000000..e2912a64
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsup.h
@@ -0,0 +1,149 @@
+/* Osmocom Generic Subscriber Update Protocol message encoder/decoder */
+
+/* (C) 2014 by sysmocom s.f.m.c. GmbH, Author: Jacob Erlbeck
+ * (C) 2016 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include <stdint.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/protocol/gsm_23_003.h>
+#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
+#include <osmocom/crypt/auth.h>
+
+/*! Maximum nubmer of PDP inside \ref osmo_gsup_message */
+#define OSMO_GSUP_MAX_NUM_PDP_INFO 10 /* GSM 09.02 limits this to 50 */
+/*! Maximum number of auth info inside \ref osmo_gsup_message */
+#define OSMO_GSUP_MAX_NUM_AUTH_INFO 5
+/*! Maximum number of octets encoding MSISDN in BCD format */
+#define OSMO_GSUP_MAX_MSISDN_LEN 9
+
+#define OSMO_GSUP_PDP_TYPE_SIZE 2
+
+/*! Information Element Identifiers for GSUP IEs */
+enum osmo_gsup_iei {
+ OSMO_GSUP_IMSI_IE = 0x01,
+ OSMO_GSUP_CAUSE_IE = 0x02,
+ OSMO_GSUP_AUTH_TUPLE_IE = 0x03,
+ OSMO_GSUP_PDP_INFO_COMPL_IE = 0x04,
+ OSMO_GSUP_PDP_INFO_IE = 0x05,
+ OSMO_GSUP_CANCEL_TYPE_IE = 0x06,
+ OSMO_GSUP_FREEZE_PTMSI_IE = 0x07,
+ OSMO_GSUP_MSISDN_IE = 0x08,
+ OSMO_GSUP_HLR_NUMBER_IE = 0x09,
+ OSMO_GSUP_PDP_CONTEXT_ID_IE = 0x10,
+ OSMO_GSUP_PDP_TYPE_IE = 0x11,
+ OSMO_GSUP_ACCESS_POINT_NAME_IE = 0x12,
+ OSMO_GSUP_PDP_QOS_IE = 0x13,
+ OSMO_GSUP_RAND_IE = 0x20,
+ OSMO_GSUP_SRES_IE = 0x21,
+ OSMO_GSUP_KC_IE = 0x22,
+ /* 3G support */
+ OSMO_GSUP_IK_IE = 0x23,
+ OSMO_GSUP_CK_IE = 0x24,
+ OSMO_GSUP_AUTN_IE = 0x25,
+ OSMO_GSUP_AUTS_IE = 0x26,
+ OSMO_GSUP_RES_IE = 0x27,
+ OSMO_GSUP_CN_DOMAIN_IE = 0x28,
+};
+
+/*! GSUP message type */
+enum osmo_gsup_message_type {
+ OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST = 0b00000100,
+ OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR = 0b00000101,
+ OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT = 0b00000110,
+
+ OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST = 0b00001000,
+ OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR = 0b00001001,
+ OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT = 0b00001010,
+
+ OSMO_GSUP_MSGT_AUTH_FAIL_REPORT = 0b00001011,
+
+ OSMO_GSUP_MSGT_PURGE_MS_REQUEST = 0b00001100,
+ OSMO_GSUP_MSGT_PURGE_MS_ERROR = 0b00001101,
+ OSMO_GSUP_MSGT_PURGE_MS_RESULT = 0b00001110,
+
+ OSMO_GSUP_MSGT_INSERT_DATA_REQUEST = 0b00010000,
+ OSMO_GSUP_MSGT_INSERT_DATA_ERROR = 0b00010001,
+ OSMO_GSUP_MSGT_INSERT_DATA_RESULT = 0b00010010,
+
+ OSMO_GSUP_MSGT_DELETE_DATA_REQUEST = 0b00010100,
+ OSMO_GSUP_MSGT_DELETE_DATA_ERROR = 0b00010101,
+ OSMO_GSUP_MSGT_DELETE_DATA_RESULT = 0b00010110,
+
+ OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST = 0b00011100,
+ OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR = 0b00011101,
+ OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT = 0b00011110,
+};
+
+#define OSMO_GSUP_IS_MSGT_REQUEST(msgt) (((msgt) & 0b00000011) == 0b00)
+#define OSMO_GSUP_IS_MSGT_ERROR(msgt) (((msgt) & 0b00000011) == 0b01)
+#define OSMO_GSUP_TO_MSGT_ERROR(msgt) (((msgt) & 0b11111100) | 0b01)
+
+enum osmo_gsup_cancel_type {
+ OSMO_GSUP_CANCEL_TYPE_UPDATE = 1, /* on wire: 0 */
+ OSMO_GSUP_CANCEL_TYPE_WITHDRAW = 2, /* on wire: 1 */
+};
+
+enum osmo_gsup_cn_domain {
+ OSMO_GSUP_CN_DOMAIN_PS = 1,
+ OSMO_GSUP_CN_DOMAIN_CS = 2,
+};
+
+/*! parsed/decoded PDP context information */
+struct osmo_gsup_pdp_info {
+ unsigned int context_id;
+ int have_info;
+ /*! Type of PDP context */
+ uint16_t pdp_type;
+ /*! APN information, still in encoded form. Can be NULL if no
+ * APN information included */
+ const uint8_t *apn_enc;
+ /*! length (in octets) of apn_enc */
+ size_t apn_enc_len;
+ /*! QoS information, still in encoded form. Can be NULL if no
+ * QoS information included */
+ const uint8_t *qos_enc;
+ /*! length (in octets) of qos_enc */
+ size_t qos_enc_len;
+};
+
+/*! parsed/decoded GSUP protocol message */
+struct osmo_gsup_message {
+ enum osmo_gsup_message_type message_type;
+ char imsi[GSM23003_IMSI_MAX_DIGITS+2];
+ enum gsm48_gmm_cause cause;
+ enum osmo_gsup_cancel_type cancel_type;
+ int pdp_info_compl;
+ int freeze_ptmsi;
+ struct osmo_auth_vector auth_vectors[OSMO_GSUP_MAX_NUM_AUTH_INFO];
+ size_t num_auth_vectors;
+ struct osmo_gsup_pdp_info pdp_infos[OSMO_GSUP_MAX_NUM_PDP_INFO];
+ size_t num_pdp_infos;
+ const uint8_t *msisdn_enc;
+ size_t msisdn_enc_len;
+ const uint8_t *hlr_enc;
+ size_t hlr_enc_len;
+ const uint8_t *auts;
+ const uint8_t *rand;
+ enum osmo_gsup_cn_domain cn_domain;
+};
+
+int osmo_gsup_decode(const uint8_t *data, size_t data_len,
+ struct osmo_gsup_message *gsup_msg);
+void osmo_gsup_encode(struct msgb *msg, const struct osmo_gsup_message *gsup_msg);
diff --git a/src/shared/libosmocore/include/osmocom/gsm/ipa.h b/src/shared/libosmocore/include/osmocom/gsm/ipa.h
new file mode 100644
index 00000000..0bb01c59
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/ipa.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/tlv.h>
+
+struct osmo_fd;
+
+/* internal (host-only) data structure */
+struct ipaccess_unit {
+ uint16_t site_id;
+ uint16_t bts_id;
+ uint16_t trx_id;
+ char *unit_name;
+ char *equipvers;
+ char *swversion;
+ uint8_t mac_addr[6];
+ char *location1;
+ char *location2;
+ char *serno;
+};
+
+/* obtain the human-readable name of an IPA CCM ID TAG */
+const char *ipa_ccm_idtag_name(uint8_t tag);
+
+/* parse a buffer of ID tags into a osmocom TLV style representation */
+int ipa_ccm_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len);
+
+/* Is the TAG included in the length field? */
+int ipa_ccm_idtag_parse_off(struct tlv_parsed *dec, unsigned char *buf, int len, const int len_offset);
+
+/* parse an Unit ID in string format into the 'ipaccess_unit' data structure */
+int ipa_parse_unitid(const char *str, struct ipaccess_unit *unit_data);
+
+/* fill a 'struct ipaccess_unit' based on a parsed IDTAG TLV */
+int ipa_ccm_tlv_to_unitdata(struct ipaccess_unit *ud,
+ const struct tlv_parsed *tp);
+
+/* Send an IPA message to the given FD */
+int ipa_send(int fd, const void *msg, size_t msglen);
+
+/* Send an IPA CCM PONG via the given FD */
+int ipa_ccm_send_pong(int fd);
+
+/* Send an IPA CCM ID_ACK via the given FD */
+int ipa_ccm_send_id_ack(int fd);
+
+/* Send an IPA CCM ID_REQ via the given FD */
+int ipa_ccm_send_id_req(int fd);
+
+/* Common handling of IPA CCM, BSC side */
+int ipa_ccm_rcvmsg_base(struct msgb *msg, struct osmo_fd *bfd);
+
+/* Common handling of IPA CCM, BTS side */
+int ipa_ccm_rcvmsg_bts_base(struct msgb *msg, struct osmo_fd *bfd);
+
+/* prepend (push) an ipaccess_head_ext to the msgb */
+void ipa_prepend_header_ext(struct msgb *msg, int proto);
+
+/* prepend (push) an ipaccess_head to the msgb */
+void ipa_prepend_header(struct msgb *msg, int proto);
+
+struct msgb *ipa_msg_alloc(int headroom);
+
+int ipa_msg_recv(int fd, struct msgb **rmsg);
+int ipa_msg_recv_buffered(int fd, struct msgb **rmsg, struct msgb **tmp_msg);
diff --git a/src/shared/libosmocore/include/osmocom/gsm/kasumi.h b/src/shared/libosmocore/include/osmocom/gsm/kasumi.h
new file mode 100644
index 00000000..8ecf65f6
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/kasumi.h
@@ -0,0 +1,48 @@
+/*
+ * KASUMI header
+ *
+ * See kasumi.c for details
+ * The parameters are described in TS 135 202.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+/*! \brief Single iteration of KASUMI cipher
+ * \param[in] P Block, 64 bits to be processed in this round
+ * \param[in] KLi1 Expanded subkeys
+ * \param[in] KLi2 Expanded subkeys
+ * \param[in] KOi1 Expanded subkeys
+ * \param[in] KOi2 Expanded subkeys
+ * \param[in] KOi3 Expanded subkeys
+ * \param[in] KIi1 Expanded subkeys
+ * \param[in] KIi2 Expanded subkeys
+ * \param[in] KIi3 Expanded subkeys
+ * \returns processed block of 64 bits
+ */
+uint64_t _kasumi(uint64_t P, const uint16_t *KLi1, const uint16_t *KLi2, const uint16_t *KOi1, const uint16_t *KOi2, const uint16_t *KOi3, const uint16_t *KIi1, const uint16_t *KIi2, const uint16_t *KIi3);
+
+/*! \brief Implementation of the KGCORE algorithm (used by A5/3, A5/4, GEA3, GEA4 and ECSD)
+ * \param[in] CA
+ * \param[in] cb
+ * \param[in] cc
+ * \param[in] cd
+ * \param[in] ck 8-bytes long key
+ * \param[out] co cl-dependent
+ * \param[in] cl
+ */
+void _kasumi_kgcore(uint8_t CA, uint8_t cb, uint32_t cc, uint8_t cd, const uint8_t *ck, uint8_t *co, uint16_t cl);
+
+/*! \brief Expand key into set of subkeys - see TS 135 202 for details
+ * \param[in] key (128 bits) as array of bytes
+ * \param[out] KLi1 Expanded subkeys
+ * \param[out] KLi2 Expanded subkeys
+ * \param[out] KOi1 Expanded subkeys
+ * \param[out] KOi2 Expanded subkeys
+ * \param[out] KOi3 Expanded subkeys
+ * \param[out] KIi1 Expanded subkeys
+ * \param[out] KIi2 Expanded subkeys
+ * \param[out] KIi3 Expanded subkeys
+ */
+void _kasumi_key_expand(const uint8_t *key, uint16_t *KLi1, uint16_t *KLi2, uint16_t *KOi1, uint16_t *KOi2, uint16_t *KOi3, uint16_t *KIi1, uint16_t *KIi2, uint16_t *KIi3);
diff --git a/src/shared/libosmocore/include/osmocom/gsm/l1sap.h b/src/shared/libosmocore/include/osmocom/gsm/l1sap.h
new file mode 100644
index 00000000..e199efe1
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/l1sap.h
@@ -0,0 +1,149 @@
+#pragma once
+
+#include <osmocom/core/prim.h>
+
+/*! \brief PH-SAP related primitives (L1<->L2 SAP) */
+enum osmo_ph_prim {
+ PRIM_PH_DATA, /*!< \brief PH-DATA */
+ PRIM_PH_RACH, /*!< \brief PH-RANDOM_ACCESS */
+ PRIM_PH_CONN, /*!< \brief PH-CONNECT */
+ PRIM_PH_EMPTY_FRAME, /*!< \brief PH-EMPTY_FRAME */
+ PRIM_PH_RTS, /*!< \brief PH-RTS */
+ PRIM_MPH_INFO, /*!< \brief MPH-INFO */
+ PRIM_TCH, /*!< \brief TCH */
+ PRIM_TCH_RTS, /*!< \brief TCH */
+};
+
+extern const struct value_string osmo_ph_prim_names[];
+
+/*! \brief PH-SAP related primitives (L1<->L2 SAP) */
+enum osmo_mph_info_type {
+ PRIM_INFO_TIME, /*!< \brief Current GSM time */
+ PRIM_INFO_MEAS, /*!< \brief Measurement indication */
+ PRIM_INFO_ACTIVATE, /*!< \brief Activation of channel */
+ PRIM_INFO_DEACTIVATE, /*!< \brief Deactivation of channel */
+ PRIM_INFO_MODIFY, /*!< \brief Mode Modify of channel */
+ PRIM_INFO_ACT_CIPH, /*!< \brief Activation of ciphering */
+ PRIM_INFO_DEACT_CIPH, /*!< \brief Deactivation of ciphering */
+};
+
+/*! \brief PH-DATA presence information */
+enum osmo_ph_pres_info_type {
+ PRES_INFO_INVALID = 0, /*!< \brief Data is invalid */
+ PRES_INFO_HEADER = 1, /*!< \brief Only header is present and valid */
+ PRES_INFO_FIRST = 3, /*!< \brief First half of data + header are valid (2nd half may be present but invalid) */
+ PRES_INFO_SECOND = 5, /*!< \brief Second half of data + header are valid (1st halfmay be present but invalid) */
+ PRES_INFO_BOTH = 7, /*!< \brief Both parts + header are present and valid */
+ PRES_INFO_UNKNOWN
+};
+
+/*! \brief for PH-RANDOM_ACCESS.req */
+struct ph_rach_req_param {
+ uint8_t ra; /*!< \brief Random Access */
+ uint8_t ta; /*!< \brief Timing Advance */
+ uint8_t tx_power; /*!< \brief Transmit Power */
+ uint8_t is_combined_ccch;/*!< \brief Are we using a combined CCCH? */
+ uint16_t offset; /*!< \brief Timing Offset */
+};
+
+/*! \brief for PH_RA_IND burstType inforamtion */
+enum ph_burst_type {
+ GSM_L1_BURST_TYPE_NONE = 0,
+ GSM_L1_BURST_TYPE_ACCESS_0,
+ GSM_L1_BURST_TYPE_ACCESS_1,
+ GSM_L1_BURST_TYPE_ACCESS_2
+};
+
+/*! \brief for PH-RANDOM_ACCESS.ind */
+struct ph_rach_ind_param {
+ uint8_t chan_nr; /*!< \brief Channel Number (Like RSL) */
+ uint16_t ra; /*!< \brief Random Access */
+ uint8_t acc_delay; /*!< \brief Delay in bit periods */
+ uint32_t fn; /*!< \brief GSM Frame Number at time of RA */
+ uint8_t is_11bit; /*!< \brief no.of bits in RACH*/
+ enum ph_burst_type burst_type; /*!< \brief type of burst*/
+};
+
+/*! \brief for PH-[UNIT]DATA.{req,ind} | PH-RTS.ind */
+struct ph_data_param {
+ uint8_t link_id; /*!< \brief Link Identifier (Like RSL) */
+ uint8_t chan_nr; /*!< \brief Channel Number (Like RSL) */
+ uint32_t fn; /*!< \brief GSM Frame Number */
+ int8_t rssi; /*!< \brief RSSI of receivedindication */
+ uint16_t ber10k; /*!< \brief BER in units of 0.01% */
+ int16_t ta_offs_qbits; /* !< \brief Burst TA Offset in quarter bits */
+ int16_t lqual_cb; /* !< \brief Link quality in centiBel */
+ enum osmo_ph_pres_info_type pdch_presence_info; /*!< \brief Info regarding presence/validity of header and data parts */
+};
+
+/*! \brief for TCH.{req,ind} | TCH-RTS.ind */
+struct ph_tch_param {
+ uint8_t chan_nr; /*!< \brief Channel Number (Like RSL) */
+ uint32_t fn; /*!< \brief GSM Frame Number */
+ int8_t rssi; /*!< \brief RSSI of received indication */
+ uint8_t marker; /*!< \brief RTP Marker bit (speech onset indicator) */
+};
+
+/*! \brief for PH-CONN.ind */
+struct ph_conn_ind_param {
+ uint32_t fn; /*!< \brief GSM Frame Number */
+};
+
+/*! \brief for TIME MPH-INFO.ind */
+struct info_time_ind_param {
+ uint32_t fn; /*!< \brief GSM Frame Number */
+};
+
+/*! \brief for MEAS MPH-INFO.ind */
+struct info_meas_ind_param {
+ uint8_t chan_nr; /*!< \brief Channel Number (Like RSL) */
+ uint16_t ber10k; /*!< \brief BER in units of 0.01% */
+ int16_t ta_offs_qbits; /*!< \brief timing advance offset (in qbits) */
+ int16_t c_i_cb; /*!< \brief C/I ratio in 0.1 dB */
+ uint8_t is_sub:1; /*!< \brief flags */
+ uint8_t inv_rssi; /*!< \brief RSSI in dBm * -1 */
+};
+
+/*! \brief for {ACTIVATE,DEACTIVATE,MODIFY} MPH-INFO.req */
+struct info_act_req_param {
+ uint8_t chan_nr; /*!< \brief Channel Number (Like RSL) */
+ uint8_t sacch_only; /*!< \breif Only deactivate SACCH */
+};
+
+/*! \brief for {ACTIVATE,DEACTIVATE} MPH-INFO.cnf */
+struct info_act_cnf_param {
+ uint8_t chan_nr; /*!< \brief Channel Number (Like RSL) */
+ uint8_t cause; /*!< \brief RSL cause in case of nack */
+};
+
+/*! \brief for {ACTIVATE,DEACTIVATE} MPH-INFO.{req,cnf} */
+struct info_ciph_req_param {
+ uint8_t chan_nr; /*!< \brief Channel Number (Like RSL) */
+ uint8_t downlink; /*!< \brief Apply to downlink */
+ uint8_t uplink; /*!< \brief Apply to uplink */
+};
+
+/*! \brief for MPH-INFO.ind */
+struct mph_info_param {
+ enum osmo_mph_info_type type; /*!< \brief Info message type */
+ union {
+ struct info_time_ind_param time_ind;
+ struct info_meas_ind_param meas_ind;
+ struct info_act_req_param act_req;
+ struct info_act_cnf_param act_cnf;
+ struct info_ciph_req_param ciph_req;
+ } u;
+};
+
+/*! \brief primitive header for PH-SAP primitives */
+struct osmo_phsap_prim {
+ struct osmo_prim_hdr oph; /*!< \brief generic primitive header */
+ union {
+ struct ph_data_param data;
+ struct ph_tch_param tch;
+ struct ph_rach_req_param rach_req;
+ struct ph_rach_ind_param rach_ind;
+ struct ph_conn_ind_param conn_ind;
+ struct mph_info_param info;
+ } u; /*!< \brief request-specific data */
+};
diff --git a/src/shared/libosmocore/include/osmocom/gsm/lapd_core.h b/src/shared/libosmocore/include/osmocom/gsm/lapd_core.h
index 0f4e8899..42ef417e 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/lapd_core.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/lapd_core.h
@@ -1,5 +1,4 @@
-#ifndef _OSMOCOM_LAPD_H
-#define _OSMOCOM_LAPD_H
+#pragma once
#include <stdint.h>
@@ -11,9 +10,9 @@
* @{
*/
-/*! \file lapd.h */
-
-/* primitive related sutff */
+/*! \file lapd_core.h
+ * primitive related stuff
+ */
/*! \brief LAPD related primitives (L2<->L3 SAP)*/
enum osmo_dl_prim {
@@ -125,6 +124,7 @@ 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 {
/*! \brief filled-in once we set the lapd_mode above */
struct lapd_cr_ent loc2rem;
@@ -168,4 +168,4 @@ 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);
-#endif /* _OSMOCOM_LAPD_H */
+/*! @} */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/lapdm.h b/src/shared/libosmocore/include/osmocom/gsm/lapdm.h
index 571fd460..84d109d5 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/lapdm.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/lapdm.h
@@ -1,6 +1,6 @@
-#ifndef _OSMOCOM_LAPDM_H
-#define _OSMOCOM_LAPDM_H
+#pragma once
+#include <osmocom/gsm/l1sap.h>
#include <osmocom/gsm/lapd_core.h>
/*! \defgroup lapdm LAPDm implementation according to GSM TS 04.06
@@ -9,55 +9,6 @@
/*! \file lapdm.h */
-/* primitive related sutff */
-
-/*! \brief LAPDm related primitives (L1<->L2 SAP) */
-enum osmo_ph_prim {
- PRIM_PH_DATA, /*!< \brief PH-DATA */
- PRIM_PH_RACH, /*!< \brief PH-RANDOM_ACCESS */
- PRIM_PH_CONN, /*!< \brief PH-CONNECT */
- PRIM_PH_EMPTY_FRAME, /*!< \brief PH-EMPTY_FRAME */
- PRIM_PH_RTS, /*!< \brief PH-RTS */
-};
-
-/*! \brief for PH-RANDOM_ACCESS.req */
-struct ph_rach_req_param {
- uint8_t ra; /*!< \brief Random Access */
- uint8_t ta; /*!< \brief Timing Advance */
- uint8_t tx_power; /*!< \brief Transmit Power */
- uint8_t is_combined_ccch;/*!< \brief Are we using a combined CCCH? */
- uint16_t offset; /*!< \brief Timing Offset */
-};
-
-/*! \brief for PH-RANDOM_ACCESS.ind */
-struct ph_rach_ind_param {
- uint8_t ra; /*!< \brief Random Access */
- uint8_t acc_delay; /*!< \brief Delay in bit periods */
- uint32_t fn; /*!< \brief GSM Frame Number at time of RA */
-};
-
-/*! \brief for PH-[UNIT]DATA.{req,ind} */
-struct ph_data_param {
- uint8_t link_id; /*!< \brief Link Identifier (Like RSL) */
- uint8_t chan_nr; /*!< \brief Channel Number (Like RSL) */
-};
-
-/*! \brief for PH-CONN.ind */
-struct ph_conn_ind_param {
- uint32_t fn; /*!< \brief GSM Frame Number */
-};
-
-/*! \brief primitive header for LAPDm PH-SAP primitives */
-struct osmo_phsap_prim {
- struct osmo_prim_hdr oph; /*!< \brief generic primitive header */
- union {
- struct ph_data_param data;
- struct ph_rach_req_param rach_req;
- struct ph_rach_ind_param rach_ind;
- struct ph_conn_ind_param conn_ind;
- } u; /*!< \brief request-specific data */
-};
-
/*! \brief LAPDm mode/role */
enum lapdm_mode {
LAPDM_MODE_MS, /*!< \brief behave like a MS (mobile phone) */
@@ -129,6 +80,8 @@ struct lapdm_channel {
const char *get_rsl_name(int value);
extern const char *lapdm_state_names[];
+struct lapdm_datalink *lapdm_datalink_for_sapi(struct lapdm_entity *le, uint8_t sapi);
+
/* initialize a LAPDm entity */
void lapdm_entity_init(struct lapdm_entity *le, enum lapdm_mode mode, int t200);
void lapdm_channel_init(struct lapdm_channel *lc, enum lapdm_mode mode);
@@ -158,5 +111,3 @@ void lapdm_channel_set_flags(struct lapdm_channel *lc, unsigned int flags);
int lapdm_phsap_dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp);
/*! @} */
-
-#endif /* _OSMOCOM_LAPDM_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/meas_rep.h b/src/shared/libosmocore/include/osmocom/gsm/meas_rep.h
new file mode 100644
index 00000000..90c981dd
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/meas_rep.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/* RX Level and RX Quality */
+struct gsm_rx_lev_qual {
+ uint8_t rx_lev;
+ uint8_t rx_qual;
+};
+
+/* unidirectional measumrement report */
+struct gsm_meas_rep_unidir {
+ struct gsm_rx_lev_qual full;
+ struct gsm_rx_lev_qual sub;
+};
+
+enum meas_rep_field {
+ MEAS_REP_DL_RXLEV_FULL,
+ MEAS_REP_DL_RXLEV_SUB,
+ MEAS_REP_DL_RXQUAL_FULL,
+ MEAS_REP_DL_RXQUAL_SUB,
+ MEAS_REP_UL_RXLEV_FULL,
+ MEAS_REP_UL_RXLEV_SUB,
+ MEAS_REP_UL_RXQUAL_FULL,
+ MEAS_REP_UL_RXQUAL_SUB,
+};
+
+size_t gsm0858_rsl_ul_meas_enc(struct gsm_meas_rep_unidir *mru, bool dtxd_used,
+ uint8_t *buf);
diff --git a/src/shared/libosmocore/include/osmocom/gsm/mncc.h b/src/shared/libosmocore/include/osmocom/gsm/mncc.h
index a51267e0..171db7ab 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/mncc.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/mncc.h
@@ -1,5 +1,4 @@
-#ifndef _OSMOCORE_MNCC_H
-#define _OSMOCORE_MNCC_H
+#pragma once
#include <osmocom/gsm/protocol/gsm_04_08.h>
@@ -81,5 +80,3 @@ enum {
GSM_MNCC_BCAP_OTHER_ITC = 5,
GSM_MNCC_BCAP_RESERVED = 7,
};
-
-#endif
diff --git a/src/shared/libosmocore/include/osmocom/gsm/oap.h b/src/shared/libosmocore/include/osmocom/gsm/oap.h
new file mode 100644
index 00000000..d973013a
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/oap.h
@@ -0,0 +1,72 @@
+/* Osmocom Authentication Protocol message encoder/decoder */
+
+/* (C) 2015-2016 by sysmocom s.f.m.c. GmbH
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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/>.
+ *
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
+
+/*! \brief Information Element Identifiers for OAP IEs.
+ * They match osmo_gsup_iei (so far). */
+enum osmo_oap_iei {
+ OAP_CAUSE_IE = 0x02,
+ OAP_RAND_IE = 0x20,
+ OAP_AUTN_IE = 0x23,
+ OAP_XRES_IE = 0x24,
+ OAP_AUTS_IE = 0x25,
+ OAP_CLIENT_ID_IE = 0x30,
+};
+
+/*! \brief OAP message types */
+enum osmo_oap_message_type {
+ OAP_MSGT_REGISTER_REQUEST = 0b00000100,
+ OAP_MSGT_REGISTER_ERROR = 0b00000101,
+ OAP_MSGT_REGISTER_RESULT = 0b00000110,
+
+ OAP_MSGT_CHALLENGE_REQUEST = 0b00001000,
+ OAP_MSGT_CHALLENGE_ERROR = 0b00001001,
+ OAP_MSGT_CHALLENGE_RESULT = 0b00001010,
+
+ OAP_MSGT_SYNC_REQUEST = 0b00001100,
+ OAP_MSGT_SYNC_ERROR = 0b00001101,
+ OAP_MSGT_SYNC_RESULT = 0b00001110,
+};
+
+/*! \brief Parsed/decoded OAP protocol message */
+struct osmo_oap_message {
+ enum osmo_oap_message_type message_type;
+ enum gsm48_gmm_cause cause;
+ uint16_t client_id;
+ int rand_present;
+ uint8_t rand[16];
+ int autn_present;
+ uint8_t autn[16];
+ int xres_present;
+ uint8_t xres[8];
+ int auts_present;
+ uint8_t auts[16];
+};
+
+int osmo_oap_decode(struct osmo_oap_message *oap_msg, const uint8_t *data,
+ size_t data_len);
+void osmo_oap_encode(struct msgb *msg, const struct osmo_oap_message *oap_msg);
diff --git a/src/shared/libosmocore/include/osmocom/gsm/prim.h b/src/shared/libosmocore/include/osmocom/gsm/prim.h
index 5beb2007..5f61dc7c 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/prim.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/prim.h
@@ -1,5 +1,4 @@
-#ifndef OSMO_GSM_PRIM_H
-#define OSMO_GSM_PRIM_H
+#pragma once
#include <osmocom/core/prim.h>
@@ -14,5 +13,3 @@ enum osmo_gsm_sap {
SAP_BSSGP_NM,
SAP_BSSGP_PFM,
};
-
-#endif
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_03_40.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_03_40.h
new file mode 100644
index 00000000..32d5c2c2
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_03_40.h
@@ -0,0 +1,32 @@
+#pragma once
+
+/* GSM TS 03.40 */
+
+/**
+ * 9.1.2.5 Type Of Number
+ */
+enum {
+ GSM340_TYPE_UNKNOWN = 0,
+ GSM340_TYPE_INTERNATIONAL = 1,
+ GSM340_TYPE_NATIONAL = 2,
+ GSM340_TYPE_NETWORK = 3,
+ GSM340_TYPE_SUBSCRIBER = 4,
+ GSM340_TYPE_ALPHA_NUMERIC = 5,
+ GSM340_TYPE_ABBREVIATED = 6,
+ GSM340_TYPE_RESERVED = 7,
+};
+
+/**
+ * 9.1.2.5 Type of Numbering plan.
+ * Applies for numbering plans (Unknown, International, National)
+ */
+enum {
+ GSM340_PLAN_UNKNOWN = 0,
+ GSM340_PLAN_ISDN = 1,
+ GSM340_PLAN_DATA = 3,
+ GSM340_PLAN_TELEX = 4,
+ GSM340_PLAN_NATIONAL = 8,
+ GSM340_PLAN_PRIVATE = 9,
+ GSM340_PLAN_ERMES = 10,
+ GSM340_PLAN_RESERVED = 15,
+};
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_03_41.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_03_41.h
index 54365cbc..40051cd4 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_03_41.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_03_41.h
@@ -1,26 +1,53 @@
-#ifndef PROTO_GSM_03_41_H
-#define PROTO_GSM_03_41_H
+#pragma once
#include <stdint.h>
+#include <osmocom/core/endian.h>
+#include <osmocom/gsm/protocol/gsm_04_12.h>
+
+#ifndef OSMO_IS_LITTLE_ENDIAN
+ #define OSMO_IS_LITTLE_ENDIAN 0
+#endif
+
/* GSM TS 03.41 definitions also TS 23.041*/
+#define GSM341_MAX_PAYLOAD (GSM412_MSG_LEN-sizeof(struct gsm341_ms_message))
+#define GSM341_MAX_CHARS (GSM341_MAX_PAYLOAD*8/7)
+#define GSM341_7BIT_PADDING '\r'
+
/* Chapter 9.3.2 */
struct gsm341_ms_message {
struct {
+#if OSMO_IS_LITTLE_ENDIAN == 1
uint8_t code_hi:6;
uint8_t gs:2;
uint8_t update:4;
uint8_t code_lo:4;
+#else
+ uint8_t gs:2;
+ uint8_t code_hi:6;
+ uint8_t code_lo:4;
+ uint8_t update:4;
+#endif
} serial;
uint16_t msg_id;
struct {
+#if OSMO_IS_LITTLE_ENDIAN == 1
uint8_t language:4;
uint8_t group:4;
+#else
+ uint8_t group:4;
+ uint8_t language:4;
+#endif
} dcs;
struct {
+#if OSMO_IS_LITTLE_ENDIAN == 1
uint8_t total:4;
uint8_t current:4;
+#else
+ uint8_t current:4;
+ uint8_t total:4;
+#endif
} page;
uint8_t data[0];
} __attribute__((packed));
@@ -28,12 +55,21 @@ struct gsm341_ms_message {
/* Chapter 9.4.1.3 */
struct gsm341_etws_message {
struct {
+#if OSMO_IS_LITTLE_ENDIAN == 1
uint8_t code_hi:4;
uint8_t popup:1;
uint8_t alert:1;
uint8_t gs:2;
uint8_t update:4;
uint8_t code_lo:4;
+#else
+ uint8_t gs:2;
+ uint8_t alert:1;
+ uint8_t popup:1;
+ uint8_t code_hi:4;
+ uint8_t code_lo:4;
+ uint8_t update:4;
+#endif
} serial;
uint16_t msg_id;
uint16_t warning_type;
@@ -48,4 +84,40 @@ struct gsm341_etws_message {
#define GSM341_GS_LA_WIDE 2
#define GSM341_GS_CELL_WIDE 3
-#endif /* PROTO_GSM_03_41_H */
+/* Section 9.4.1.2.2 */
+#define GSM341_MSGID_EOTD_ASSISTANCE 0x03E8
+#define GSM341_MSGID_DGPS_CORRECTION 0x03E9
+#define GSM341_MSGID_DGPS_EPH_CLOCK_COR 0x03EA
+#define GSM341_MSGID_GPS_ALMANAC_OTHER 0x03EB
+#define GSM341_MSGID_ETWS_EARTHQUAKE 0x1100
+#define GSM341_MSGID_ETWS_TSUNAMI 0x1101
+#define GSM341_MSGID_ETWS_QUAKE_AND_TSUNAMI 0x1102
+#define GSM341_MSGID_ETWS_TEST 0x1103
+#define GSM341_MSGID_ETWS_OTHER 0x1104
+#define GSM341_MSGID_ETWS_CMAS_PRESIDENTIAL 0x1112
+#define GSM341_MSGID_ETWS_CMAS_EXTREME_IMM_OBSERVED 0x1113
+#define GSM341_MSGID_ETWS_CMAS_EXTREME_IMM_LIKELY 0x1114
+#define GSM341_MSGID_ETWS_CMAS_EXTREME_EXP_OBSERVED 0x1115
+#define GSM341_MSGID_ETWS_CMAS_EXTREME_EXP_LIKELY 0x1116
+#define GSM341_MSGID_ETWS_CMAS_SEVERE_IMM_OBSERVED 0x1117
+#define GSM341_MSGID_ETWS_CMAS_SEVERE_IMM_LIKELY 0x1118
+#define GSM341_MSGID_ETWS_CMAS_SEVERE_EXP_OBSERVED 0x1119
+#define GSM341_MSGID_ETWS_CMAS_SEVERE_EXP_LIKELY 0x111A
+#define GSM341_MSGID_ETWS_CMAS_AMBER 0x111B
+#define GSM341_MSGID_ETWS_CMAS_MONTHLY_TEST 0x111C
+#define GSM341_MSGID_ETWS_CMAS_EXERCISE 0x111D
+#define GSM341_MSGID_ETWS_CMAS_OPERATOR_DEFINED 0x111E
+#define GSM341_MSGID_ETWS_CMAS_PRESIDENTIAL_AL 0x111F
+#define GSM341_MSGID_ETWS_CMAS_EXTREME_IMM_OBSERVED_AL 0x1120
+#define GSM341_MSGID_ETWS_CMAS_EXTREME_IMM_LIKELY_AL 0x1121
+#define GSM341_MSGID_ETWS_CMAS_EXTREME_EXP_OBSERVED_AL 0x1122
+#define GSM341_MSGID_ETWS_CMAS_EXTREME_EXP_LIKELY_AL 0x1123
+#define GSM341_MSGID_ETWS_CMAS_SEVERE_IMM_OBSERVED_AL 0x1124
+#define GSM341_MSGID_ETWS_CMAS_SEVERE_IMM_LIKELY_AL 0x1125
+#define GSM341_MSGID_ETWS_CMAS_SEVERE_EXP_OBSERVED_AL 0x1126
+#define GSM341_MSGID_ETWS_CMAS_SEVERE_EXP_LIKELY_AL 0x1127
+#define GSM341_MSGID_ETWS_CMAS_AMBER_AL 0x1128
+#define GSM341_MSGID_ETWS_CMAS_MONTHLY_TEST_AL 0x1129
+#define GSM341_MSGID_ETWS_CMAS_EXERCISE_AL 0x112A
+#define GSM341_MSGID_ETWS_CMAS_OPERATOR_DEFINED_AL 0x112B
+#define GSM341_MSGID_ETWS_EU_INFO_LOCAL_LANGUAGE 0x1900
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08.h
index 172ef678..767aa3d8 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08.h
@@ -1,7 +1,10 @@
-#ifndef PROTO_GSM_04_08_H
-#define PROTO_GSM_04_08_H
+#pragma once
#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/endian.h>
/* GSM TS 04.08 definitions */
struct gsm_lchan;
@@ -40,6 +43,7 @@ struct gsm48_classmark2 {
} __attribute__ ((packed));
/* Chapter 10.5.2.1b.3 */
+#if OSMO_IS_LITTLE_ENDIAN == 1
struct gsm48_range_1024 {
uint8_t w1_hi:2,
f0:1,
@@ -73,8 +77,44 @@ struct gsm48_range_1024 {
uint8_t w16:6,
w15_lo:2;
} __attribute__ ((packed));
+#else
+struct gsm48_range_1024 {
+ uint8_t form_id:5,
+ f0:1,
+ w1_hi:2;
+ uint8_t w1_lo;
+ uint8_t w2_hi;
+ uint8_t w2_lo:1,
+ w3_hi:7;
+ uint8_t w3_lo:2,
+ w4_hi:6;
+ uint8_t w4_lo:2,
+ w5_hi:6;
+ uint8_t w5_lo:2,
+ w6_hi:6;
+ uint8_t w6_lo:2,
+ w7_hi:6;
+ uint8_t w7_lo:2,
+ w8_hi:6;
+ uint8_t w8_lo:1,
+ w9:7;
+ uint8_t w10:7,
+ w11_hi:1;
+ uint8_t w11_lo:6,
+ w12_hi:2;
+ uint8_t w12_lo:5,
+ w13_hi:3;
+ uint8_t w13_lo:4,
+ w14_hi:4;
+ uint8_t w14_lo:3,
+ w15_hi:5;
+ uint8_t w15_lo:2,
+ w16:6;
+} __attribute__ ((packed));
+#endif
/* Chapter 10.5.2.1b.4 */
+#if OSMO_IS_LITTLE_ENDIAN == 1
struct gsm48_range_512 {
uint8_t orig_arfcn_hi:1,
form_id:7;
@@ -108,8 +148,44 @@ struct gsm48_range_512 {
uint8_t w17:5,
w16_lo:3;
} __attribute__ ((packed));
+#else
+struct gsm48_range_512 {
+ uint8_t form_id:7,
+ orig_arfcn_hi:1;
+ uint8_t orig_arfcn_mid;
+ uint8_t orig_arfcn_lo:1,
+ w1_hi:7;
+ uint8_t w1_lo:2,
+ w2_hi:6;
+ uint8_t w2_lo:2,
+ w3_hi:6;
+ uint8_t w3_lo:2,
+ w4_hi:6;
+ uint8_t w4_lo:1,
+ w5:7;
+ uint8_t w6:7,
+ w7_hi:1;
+ uint8_t w7_lo:6,
+ w8_hi:2;
+ uint8_t w8_lo:4,
+ w9_hi:4;
+ uint8_t w9_lo:2,
+ w10:6;
+ uint8_t w11:6,
+ w12_hi:2;
+ uint8_t w12_lo:4,
+ w13_hi:4;
+ uint8_t w13_lo:2,
+ w14:6;
+ uint8_t w15:6,
+ w16_hi:2;
+ uint8_t w16_lo:3,
+ w17:5;
+} __attribute__ ((packed));
+#endif
/* Chapter 10.5.2.1b.5 */
+#if OSMO_IS_LITTLE_ENDIAN == 1
struct gsm48_range_256 {
uint8_t orig_arfcn_hi:1,
form_id:7;
@@ -149,8 +225,50 @@ struct gsm48_range_256 {
w21:4,
w20_lo:3;
} __attribute__ ((packed));
+#else
+struct gsm48_range_256 {
+ uint8_t form_id:7,
+ orig_arfcn_hi:1;
+ uint8_t orig_arfcn_mid;
+ uint8_t orig_arfcn_lo:1,
+ w1_hi:7;
+ uint8_t w1_lo:1,
+ w2:7;
+ uint8_t w3:7,
+ w4_hi:1;
+ uint8_t w4_lo:5,
+ w5_hi:3;
+ uint8_t w5_lo:3,
+ w6_hi:5;
+ uint8_t w6_lo:1,
+ w7:6,
+ w8_hi:1;
+ uint8_t w8_lo:4,
+ w9_hi:4;
+ uint8_t w9_lo:1,
+ w10:5,
+ w11_hi:2;
+ uint8_t w11_lo:3,
+ w12:5;
+ uint8_t w13:5,
+ w14_hi:3;
+ uint8_t w14_lo:2,
+ w15:5,
+ w16_hi:1;
+ uint8_t w16_lo:3,
+ w17:4,
+ w18_hi:1;
+ uint8_t w18_lo:3,
+ w19:4,
+ w20_hi:1;
+ uint8_t w20_lo:3,
+ w21:4,
+ spare:1;
+} __attribute__ ((packed));
+#endif
/* Chapter 10.5.2.1b.6 */
+#if OSMO_IS_LITTLE_ENDIAN == 1
struct gsm48_range_128 {
uint8_t orig_arfcn_hi:1,
form_id:7;
@@ -192,6 +310,49 @@ struct gsm48_range_128 {
w27:3,
w26_lo:1;
} __attribute__ ((packed));
+#else
+struct gsm48_range_128 {
+ uint8_t form_id:7,
+ orig_arfcn_hi:1;
+ uint8_t orig_arfcn_mid;
+ uint8_t orig_arfcn_lo:1,
+ w1:7;
+ uint8_t w2:6,
+ w3_hi:2;
+ uint8_t w3_lo:4,
+ w4_hi:4;
+ uint8_t w4_lo:1,
+ w5:5,
+ w6_hi:2;
+ uint8_t w6_lo:3,
+ w7:5;
+ uint8_t w8:4,
+ w9:4;
+ uint8_t w10:4,
+ w11:4;
+ uint8_t w12:4,
+ w13:4;
+ uint8_t w14:4,
+ w15:4;
+ uint8_t w16:3,
+ w17:3,
+ w18_hi:2;
+ uint8_t w18_lo:1,
+ w19:3,
+ w20:3,
+ w21_hi:1;
+ uint8_t w21_lo:2,
+ w22:3,
+ w23:3;
+ uint8_t w24:3,
+ w25:3,
+ w26_hi:2;
+ uint8_t w26_lo:1,
+ w27:3,
+ w28:3,
+ spare:1;
+} __attribute__ ((packed));
+#endif
/* Chapter 10.5.2.1b.7 */
struct gsm48_var_bit {
@@ -345,9 +506,11 @@ enum gsm48_chan_mode {
GSM48_CMODE_DATA_14k5 = 0x0f,
GSM48_CMODE_DATA_12k0 = 0x03,
GSM48_CMODE_DATA_6k0 = 0x0b,
- GSM48_CMODE_DATA_3k6 = 0x23,
+ GSM48_CMODE_DATA_3k6 = 0x13,
};
+extern const struct value_string gsm48_chan_mode_names[];
+
/* Chapter 9.1.2 */
struct gsm48_ass_cmd {
/* Semantic is from 10.5.2.5a */
@@ -466,11 +629,20 @@ struct gsm48_control_channel_descr {
uint8_t t3212;
} __attribute__ ((packed));
+enum gsm48_dtx_mode {
+ GSM48_DTX_MAY_BE_USED,
+ GSM48_DTX_SHALL_BE_USED,
+ GSM48_DTX_SHALL_NOT_BE_USED
+};
+
+/* Cell Options for SI6, SACCH (10.5.2.3a.2) or SI3, BCCH (Table 10.5.2.3.1),
+ 3GPP TS 44.018 */
struct gsm48_cell_options {
uint8_t radio_link_timeout:4,
dtx:2,
pwrc:1,
- spare:1;
+ /* either DN-IND or top bit of DTX IND */
+ d:1;
} __attribute__ ((packed));
/* Section 9.2.9 CM service request */
@@ -515,6 +687,12 @@ struct gsm48_system_information_type_2ter {
uint8_t rest_octets[0];
} __attribute__ ((packed));
+/* Section 9.1.34a System information Type 2quater */
+struct gsm48_system_information_type_2quater {
+ struct gsm48_system_information_type_header header;
+ uint8_t rest_octets[0];
+} __attribute__ ((packed));
+
/* Section 9.1.35 System information Type 3 */
struct gsm48_system_information_type_3 {
struct gsm48_system_information_type_header header;
@@ -742,15 +920,111 @@ struct gsm48_rr_status {
#define GSM48_PDISC_SM_GPRS 0x0a
#define GSM48_PDISC_NC_SS 0x0b
#define GSM48_PDISC_LOC 0x0c
+#define GSM48_PDISC_EXTEND 0x0e
#define GSM48_PDISC_MASK 0x0f
#define GSM48_PDISC_USSD 0x11
+bool gsm48_hdr_gmm_cipherable(const struct gsm48_hdr *hdr);
+
+static inline uint8_t gsm48_hdr_pdisc(const struct gsm48_hdr *hdr)
+{
+ /*
+ * 3GPP TS 24.007 version 12.0.0 Release 12,
+ * 11.2.3.1.1 Protocol discriminator
+ */
+ uint8_t pdisc = hdr->proto_discr & GSM48_PDISC_MASK;
+ if (pdisc == GSM48_PDISC_EXTEND)
+ return hdr->proto_discr;
+ return pdisc;
+}
+
+static inline uint8_t gsm48_hdr_trans_id(const struct gsm48_hdr *hdr)
+{
+ /*
+ * 3GPP TS 24.007 version 12.0.0 Release 12,
+ * 11.2.3.1.3 Transaction identifier
+ */
+ return (hdr->proto_discr & 0xf0) >> 4;
+}
+
+#define GSM48_TA_INVALID 220
+
+/*! \brief Check if TA is valid according to 3GPP TS 44.018 § 10.5.2.40
+ * \param[in] ta Timing Advance value
+ * \returns true if ta is valid, false otherwise
+ * Note: Rules for GSM400 band are ignored as it's not implemented in practice.
+ */
+static inline bool gsm48_ta_is_valid(uint8_t ta)
+{
+ return (ta < 64);
+}
+
+static inline uint8_t gsm48_hdr_trans_id_flip_ti(const struct gsm48_hdr *hdr)
+{
+ return gsm48_hdr_trans_id(hdr) ^ 0x08;
+}
+
+static inline uint8_t gsm48_hdr_trans_id_no_ti(const struct gsm48_hdr *hdr)
+{
+ return gsm48_hdr_trans_id(hdr) & 0x07;
+}
+
+static inline uint8_t gsm48_hdr_msg_type_r98(const struct gsm48_hdr *hdr)
+{
+ /*
+ * 3GPP TS 24.007 version 12.0.0 Release 12,
+ * 11.2.3.2.1 Message type octet (when accessing Release 98 and older
+ * networks only)
+ */
+ switch (gsm48_hdr_pdisc(hdr)) {
+ case GSM48_PDISC_MM:
+ case GSM48_PDISC_CC:
+ case GSM48_PDISC_NC_SS:
+ case GSM48_PDISC_GROUP_CC:
+ case GSM48_PDISC_BCAST_CC:
+ case GSM48_PDISC_LOC:
+ return hdr->msg_type & 0xbf;
+ default:
+ return hdr->msg_type;
+ }
+}
+
+static inline uint8_t gsm48_hdr_msg_type_r99(const struct gsm48_hdr *hdr)
+{
+ /*
+ * 3GPP TS 24.007 version 12.0.0 Release 12,
+ * 11.2.3.2.2 Message type octet (when accessing Release 99 and newer
+ * networks)
+ */
+ switch (gsm48_hdr_pdisc(hdr)) {
+ case GSM48_PDISC_MM:
+ case GSM48_PDISC_CC:
+ return hdr->msg_type & 0x3f;
+ case GSM48_PDISC_NC_SS:
+ case GSM48_PDISC_GROUP_CC:
+ case GSM48_PDISC_BCAST_CC:
+ case GSM48_PDISC_LOC:
+ return hdr->msg_type & 0xbf;
+ default:
+ return hdr->msg_type;
+ }
+}
+
+void gsm48_set_dtx(struct gsm48_cell_options *op, enum gsm48_dtx_mode full,
+ enum gsm48_dtx_mode half, bool is_bcch);
+
+#define gsm48_hdr_msg_type gsm48_hdr_msg_type_r99
+
/* Section 10.4 */
#define GSM48_MT_RR_INIT_REQ 0x3c
#define GSM48_MT_RR_ADD_ASS 0x3b
#define GSM48_MT_RR_IMM_ASS 0x3f
#define GSM48_MT_RR_IMM_ASS_EXT 0x39
#define GSM48_MT_RR_IMM_ASS_REJ 0x3a
+#define GSM48_MT_RR_DTM_ASS_FAIL 0x48
+#define GSM48_MT_RR_DTM_REJECT 0x49
+#define GSM48_MT_RR_DTM_REQUEST 0x4A
+#define GSM48_MT_RR_PACKET_ASS 0x4B
#define GSM48_MT_RR_CIPH_M_CMD 0x35
#define GSM48_MT_RR_CIPH_M_COMPL 0x32
@@ -766,6 +1040,8 @@ struct gsm48_rr_status {
#define GSM48_MT_RR_HANDO_COMPL 0x2c
#define GSM48_MT_RR_HANDO_FAIL 0x28
#define GSM48_MT_RR_HANDO_INFO 0x2d
+#define GSM48_MT_RR_HANDO_INFO 0x2d
+#define GSM48_MT_RR_DTM_ASS_CMD 0x4c
#define GSM48_MT_RR_CELL_CHG_ORDER 0x08
#define GSM48_MT_RR_PDCH_ASS_CMD 0x23
@@ -779,8 +1055,13 @@ struct gsm48_rr_status {
#define GSM48_MT_RR_PAG_REQ_3 0x24
#define GSM48_MT_RR_PAG_RESP 0x27
#define GSM48_MT_RR_NOTIF_NCH 0x20
-#define GSM48_MT_RR_NOTIF_FACCH 0x25
+#define GSM48_MT_RR_NOTIF_FACCH 0x25 /* (Reserved) */
#define GSM48_MT_RR_NOTIF_RESP 0x26
+#define GSM48_MT_RR_PACKET_NOTIF 0x4e
+#define GSM48_MT_RR_UTRAN_CLSM_CHG 0x60
+#define GSM48_MT_RR_CDMA2K_CLSM_CHG 0x62
+#define GSM48_MT_RR_IS_TO_UTRAN_HANDO 0x63
+#define GSM48_MT_RR_IS_TO_CDMA2K_HANDO 0x64
#define GSM48_MT_RR_SYSINFO_8 0x18
#define GSM48_MT_RR_SYSINFO_1 0x19
@@ -793,6 +1074,7 @@ struct gsm48_rr_status {
#define GSM48_MT_RR_SYSINFO_2bis 0x02
#define GSM48_MT_RR_SYSINFO_2ter 0x03
+#define GSM48_MT_RR_SYSINFO_2quater 0x07
#define GSM48_MT_RR_SYSINFO_5bis 0x05
#define GSM48_MT_RR_SYSINFO_5ter 0x06
#define GSM48_MT_RR_SYSINFO_9 0x04
@@ -801,6 +1083,10 @@ struct gsm48_rr_status {
#define GSM48_MT_RR_SYSINFO_16 0x3d
#define GSM48_MT_RR_SYSINFO_17 0x3e
+#define GSM48_MT_RR_SYSINFO_18 0x40
+#define GSM48_MT_RR_SYSINFO_19 0x41
+#define GSM48_MT_RR_SYSINFO_20 0x42
+
#define GSM48_MT_RR_CHAN_MODE_MODIF 0x10
#define GSM48_MT_RR_STATUS 0x12
#define GSM48_MT_RR_CHAN_MODE_MODIF_ACK 0x17
@@ -811,8 +1097,9 @@ struct gsm48_rr_status {
#define GSM48_MT_RR_EXT_MEAS_REP 0x36
#define GSM48_MT_RR_EXT_MEAS_REP_ORD 0x37
#define GSM48_MT_RR_GPRS_SUSP_REQ 0x34
+#define GSM48_MT_RR_DTM_INFO 0x4d
-#define GSM48_MT_RR_VGCS_UPL_GRANT 0x08
+#define GSM48_MT_RR_VGCS_UPL_GRANT 0x09
#define GSM48_MT_RR_UPLINK_RELEASE 0x0e
#define GSM48_MT_RR_UPLINK_FREE 0x0c
#define GSM48_MT_RR_UPLINK_BUSY 0x2a
@@ -925,6 +1212,7 @@ struct gsm48_rr_status {
#define GSM48_IE_UTC 0x46 /* 10.5.3.8 */
#define GSM48_IE_NET_TIME_TZ 0x47 /* 10.5.3.9 */
#define GSM48_IE_LSA_IDENT 0x48 /* 10.5.3.11 */
+#define GSM48_IE_NET_DST 0x49 /* 10.5.3.12 [24.008] */
#define GSM48_IE_BEARER_CAP 0x04 /* 10.5.4.5 */
#define GSM48_IE_CAUSE 0x08 /* 10.5.4.11 */
@@ -1013,6 +1301,7 @@ struct gsm48_rr_status {
/* Additional MM elements */
#define GSM48_IE_LOCATION_AREA 0x13
+#define GSM48_IE_AUTN 0x20
#define GSM48_IE_PRIORITY_LEV 0x80
#define GSM48_IE_FOLLOW_ON_PROC 0xa1
#define GSM48_IE_CTS_PERMISSION 0xa2
@@ -1052,9 +1341,9 @@ enum gsm48_rr_cause {
GSM48_RR_CAUSE_ABNORMAL_TIMER = 0x03,
GSM48_RR_CAUSE_ABNORMAL_NOACT = 0x04,
GSM48_RR_CAUSE_PREMPTIVE_REL = 0x05,
- GSM48_RR_CAUSE_HNDOVER_IMP = 0x06,
- GSM48_RR_CAUSE_CHAN_MODE_UNACCT = 0x07,
- GSM48_RR_CAUSE_FREQ_NOT_IMPL = 0x08,
+ GSM48_RR_CAUSE_HNDOVER_IMP = 0x08,
+ GSM48_RR_CAUSE_CHAN_MODE_UNACCT = 0x09,
+ GSM48_RR_CAUSE_FREQ_NOT_IMPL = 0x0a,
GSM48_RR_CAUSE_CALL_CLEARED = 0x41,
GSM48_RR_CAUSE_SEMANT_INCORR = 0x5f,
GSM48_RR_CAUSE_INVALID_MAND_INF = 0x60,
@@ -1335,5 +1624,3 @@ struct gsm48_ra_id {
#define GSM_MACBLOCK_LEN 23
#define GSM_MACBLOCK_PADDING 0x2b
-
-#endif /* PROTO_GSM_04_08_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08_gprs.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08_gprs.h
new file mode 100644
index 00000000..cda1e48b
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08_gprs.h
@@ -0,0 +1,431 @@
+#ifndef _GSM48_GPRS_H
+#define _GSM48_GPRS_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/crypt/gprs_cipher.h>
+
+/* Table 10.4 / 10.4a, GPRS Mobility Management (GMM) */
+#define GSM48_MT_GMM_ATTACH_REQ 0x01
+#define GSM48_MT_GMM_ATTACH_ACK 0x02
+#define GSM48_MT_GMM_ATTACH_COMPL 0x03
+#define GSM48_MT_GMM_ATTACH_REJ 0x04
+#define GSM48_MT_GMM_DETACH_REQ 0x05
+#define GSM48_MT_GMM_DETACH_ACK 0x06
+
+#define GSM48_MT_GMM_RA_UPD_REQ 0x08
+#define GSM48_MT_GMM_RA_UPD_ACK 0x09
+#define GSM48_MT_GMM_RA_UPD_COMPL 0x0a
+#define GSM48_MT_GMM_RA_UPD_REJ 0x0b
+
+#define GSM48_MT_GMM_PTMSI_REALL_CMD 0x10
+#define GSM48_MT_GMM_PTMSI_REALL_COMPL 0x11
+#define GSM48_MT_GMM_AUTH_CIPH_REQ 0x12
+#define GSM48_MT_GMM_AUTH_CIPH_RESP 0x13
+#define GSM48_MT_GMM_AUTH_CIPH_REJ 0x14
+#define GSM48_MT_GMM_AUTH_CIPH_FAIL 0x1C
+#define GSM48_MT_GMM_ID_REQ 0x15
+#define GSM48_MT_GMM_ID_RESP 0x16
+#define GSM48_MT_GMM_STATUS 0x20
+#define GSM48_MT_GMM_INFO 0x21
+
+/* Table 10.4a, GPRS Session Management (GSM) */
+#define GSM48_MT_GSM_ACT_PDP_REQ 0x41
+#define GSM48_MT_GSM_ACT_PDP_ACK 0x42
+#define GSM48_MT_GSM_ACT_PDP_REJ 0x43
+#define GSM48_MT_GSM_REQ_PDP_ACT 0x44
+#define GSM48_MT_GSM_REQ_PDP_ACT_REJ 0x45
+#define GSM48_MT_GSM_DEACT_PDP_REQ 0x46
+#define GSM48_MT_GSM_DEACT_PDP_ACK 0x47
+#define GSM48_MT_GSM_ACT_AA_PDP_REQ 0x50
+#define GSM48_MT_GSM_ACT_AA_PDP_ACK 0x51
+#define GSM48_MT_GSM_ACT_AA_PDP_REJ 0x52
+#define GSM48_MT_GSM_DEACT_AA_PDP_REQ 0x53
+#define GSM48_MT_GSM_DEACT_AA_PDP_ACK 0x54
+#define GSM48_MT_GSM_STATUS 0x55
+
+/* Chapter 10.5.5.2 / Table 10.5.135 */
+#define GPRS_ATT_T_ATTACH 1
+#define GPRS_ATT_T_ATT_WHILE_IMSI 2
+#define GPRS_ATT_T_COMBINED 3
+
+extern const struct value_string *gprs_att_t_strs;
+extern const struct value_string gprs_msgt_gmm_names[];
+
+/* Chapter 10.5.5.5 / Table 10.5.138 */
+#define GPRS_DET_T_MO_GPRS 1
+#define GPRS_DET_T_MO_IMSI 2
+#define GPRS_DET_T_MO_COMBINED 3
+/* Network to MS direction */
+#define GPRS_DET_T_MT_REATT_REQ 1
+#define GPRS_DET_T_MT_REATT_NOTREQ 2
+#define GPRS_DET_T_MT_IMSI 3
+
+extern const struct value_string *gprs_det_t_mo_strs;
+extern const struct value_string *gprs_det_t_mt_strs;
+
+/* Chapter 10.5.5.18 / Table 105.150 */
+#define GPRS_UPD_T_RA 0
+#define GPRS_UPD_T_RA_LA 1
+#define GPRS_UPD_T_RA_LA_IMSI_ATT 2
+#define GPRS_UPD_T_PERIODIC 3
+
+extern const struct value_string *gprs_upd_t_strs;
+
+/* Table 10.4 in 3GPP TS 24.008 (successor to 04.08) */
+#define GSM48_MT_GMM_SERVICE_REQ 0x0c
+#define GSM48_MT_GMM_SERVICE_ACK 0x0d
+#define GSM48_MT_GMM_SERVICE_REJ 0x0e
+
+enum gsm48_gprs_ie_mm {
+ GSM48_IE_GMM_CIPH_CKSN = 0x08, /* 10.5.1.2 */
+ 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 */
+ GSM48_IE_GMM_AUTH_RAND = 0x21, /* 10.5.3.1 */
+ GSM48_IE_GMM_AUTH_SRES = 0x22, /* 10.5.3.2 */
+ GSM48_IE_GMM_IMEISV = 0x23, /* 10.5.1.4 */
+ GSM48_IE_GMM_CAUSE = 0x25, /* 10.5.5.14 */
+ 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_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 */
+};
+
+enum gsm48_gprs_ie_sm {
+ GSM48_IE_GSM_APN = 0x28, /* 10.5.6.1 */
+ GSM48_IE_GSM_PROTO_CONF_OPT = 0x27, /* 10.5.6.3 */
+ GSM48_IE_GSM_PDP_ADDR = 0x2b, /* 10.5.6.4 */
+ GSM48_IE_GSM_AA_TMR = 0x29, /* 10.5.7.3 */
+ GSM48_IE_GSM_NAME_FULL = 0x43, /* 10.5.3.5a */
+ GSM48_IE_GSM_NAME_SHORT = 0x45, /* 10.5.3.5a */
+ GSM48_IE_GSM_TIMEZONE = 0x46, /* 10.5.3.8 */
+ GSM48_IE_GSM_UTC_AND_TZ = 0x47, /* 10.5.3.9 */
+ GSM48_IE_GSM_LSA_ID = 0x48, /* 10.5.3.11 */
+
+ /* Fake IEs that are not present on the Layer3 air interface,
+ * but which we use to simplify internal APIs */
+ OSMO_IE_GSM_REQ_QOS = 0xfd,
+ OSMO_IE_GSM_REQ_PDP_ADDR = 0xfe,
+ OSMO_IE_GSM_SUB_QOS = 0xff,
+};
+
+/* Chapter 9.4.15 / Table 9.4.15 */
+struct gsm48_ra_upd_ack {
+ uint8_t force_stby:4, /* 10.5.5.7 */
+ upd_result:4; /* 10.5.5.17 */
+ uint8_t ra_upd_timer; /* 10.5.7.3 */
+ struct gsm48_ra_id ra_id; /* 10.5.5.15 */
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Chapter 10.5.7.3 */
+enum gsm48_gprs_tmr_unit {
+ GPRS_TMR_2SECONDS = 0 << 5,
+ GPRS_TMR_MINUTE = 1 << 5,
+ GPRS_TMR_6MINUTE = 2 << 5,
+ GPRS_TMR_DEACTIVATED = 7 << 5,
+};
+
+#define GPRS_TMR_UNIT_MASK (7 << 5)
+#define GPRS_TMR_FACT_MASK ((1 << 5)-1)
+
+/* Chapter 9.4.2 / Table 9.4.2 */
+struct gsm48_attach_ack {
+ uint8_t att_result:4, /* 10.5.5.7 */
+ force_stby:4; /* 10.5.5.1 */
+ uint8_t ra_upd_timer; /* 10.5.7.3 */
+ uint8_t radio_prio; /* 10.5.7.2 */
+ struct gsm48_ra_id ra_id; /* 10.5.5.15 */
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Chapter 9.4.9 / Table 9.4.9 */
+struct gsm48_auth_ciph_req {
+ uint8_t ciph_alg:4, /* 10.5.5.3 */
+ imeisv_req:4; /* 10.5.5.10 */
+ uint8_t force_stby:4, /* 10.5.5.7 */
+ ac_ref_nr:4; /* 10.5.5.19 */
+ uint8_t data[0];
+} __attribute__((packed));
+/* optional: TV RAND, TV CKSN */
+
+struct gsm48_auth_ciph_resp {
+ uint8_t ac_ref_nr:4,
+ spare:4;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Chapter 9.5.1 / Table 9.5.1 */
+struct gsm48_act_pdp_ctx_req {
+ uint8_t req_nsapi;
+ uint8_t req_llc_sapi;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Chapter 10.5.5.14 / Table 10.5.147 */
+enum gsm48_gmm_cause {
+ GMM_CAUSE_IMSI_UNKNOWN = 0x02,
+ GMM_CAUSE_ILLEGAL_MS = 0x03,
+ GMM_CAUSE_IMEI_NOT_ACCEPTED = 0x05,
+ GMM_CAUSE_ILLEGAL_ME = 0x06,
+ GMM_CAUSE_GPRS_NOTALLOWED = 0x07,
+ GMM_CAUSE_GPRS_OTHER_NOTALLOWED = 0x08,
+ GMM_CAUSE_MS_ID_NOT_DERIVED = 0x09,
+ GMM_CAUSE_IMPL_DETACHED = 0x0a,
+ GMM_CAUSE_PLMN_NOTALLOWED = 0x0b,
+ GMM_CAUSE_LA_NOTALLOWED = 0x0c,
+ GMM_CAUSE_ROAMING_NOTALLOWED = 0x0d,
+ GMM_CAUSE_NO_GPRS_PLMN = 0x0e,
+ GMM_CAUSE_NO_SUIT_CELL_IN_LA = 0x0f,
+ GMM_CAUSE_MSC_TEMP_NOTREACH = 0x10,
+ GMM_CAUSE_NET_FAIL = 0x11,
+ GMM_CAUSE_MAC_FAIL = 0x14,
+ GMM_CAUSE_SYNC_FAIL = 0x15,
+ GMM_CAUSE_CONGESTION = 0x16,
+ GMM_CAUSE_GSM_AUTH_UNACCEPT = 0x17,
+ GMM_CAUSE_NOT_AUTH_FOR_CSG = 0x19,
+ GMM_CAUSE_SMS_VIA_GPRS_IN_RA = 0x1c,
+ GMM_CAUSE_NO_PDP_ACTIVATED = 0x28,
+ GMM_CAUSE_SEM_INCORR_MSG = 0x5f,
+ GMM_CAUSE_INV_MAND_INFO = 0x60,
+ GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL = 0x61,
+ GMM_CAUSE_MSGT_INCOMP_P_STATE = 0x62,
+ GMM_CAUSE_IE_NOTEXIST_NOTIMPL = 0x63,
+ GMM_CAUSE_COND_IE_ERR = 0x64,
+ GMM_CAUSE_MSG_INCOMP_P_STATE = 0x65,
+ GMM_CAUSE_PROTO_ERR_UNSPEC = 0x6f,
+};
+
+extern const struct value_string *gsm48_gmm_cause_names;
+
+/* Chapter 10.4.6.6 / Table 10.5.157 */
+enum gsm48_gsm_cause {
+ GSM_CAUSE_INSUFF_RSRC = 0x1a,
+ GSM_CAUSE_MISSING_APN = 0x1b,
+ GSM_CAUSE_UNKNOWN_PDP = 0x1c,
+ GSM_CAUSE_AUTH_FAILED = 0x1d,
+ GSM_CAUSE_ACT_REJ_GGSN = 0x1e,
+ GSM_CAUSE_ACT_REJ_UNSPEC = 0x1f,
+ GSM_CAUSE_SERV_OPT_NOTSUPP = 0x20,
+ GSM_CAUSE_REQ_SERV_OPT_NOTSUB = 0x21,
+ GSM_CAUSE_SERV_OPT_TEMP_OOO = 0x22,
+ GSM_CAUSE_NSAPI_IN_USE = 0x23,
+ GSM_CAUSE_DEACT_REGULAR = 0x24,
+ GSM_CAUSE_QOS_NOT_ACCEPTED = 0x25,
+ GSM_CAUSE_NET_FAIL = 0x26,
+ GSM_CAUSE_REACT_RQD = 0x27,
+ GSM_CAUSE_FEATURE_NOTSUPP = 0x28,
+ GSM_CAUSE_INVALID_TRANS_ID = 0x51,
+ GSM_CAUSE_SEM_INCORR_MSG = 0x5f,
+ GSM_CAUSE_INV_MAND_INFO = 0x60,
+ GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL = 0x61,
+ GSM_CAUSE_MSGT_INCOMP_P_STATE = 0x62,
+ GSM_CAUSE_IE_NOTEXIST_NOTIMPL = 0x63,
+ GSM_CAUSE_COND_IE_ERR = 0x64,
+ GSM_CAUSE_MSG_INCOMP_P_STATE = 0x65,
+ GSM_CAUSE_PROTO_ERR_UNSPEC = 0x6f,
+};
+
+extern const struct value_string *gsm48_gsm_cause_names;
+
+/* Section 6.1.2.2: Session management states on the network side */
+enum gsm48_pdp_state {
+ PDP_S_INACTIVE,
+ PDP_S_ACTIVE_PENDING,
+ PDP_S_ACTIVE,
+ PDP_S_INACTIVE_PENDING,
+ PDP_S_MODIFY_PENDING,
+};
+
+/* Table 10.5.155/3GPP TS 24.008 */
+enum gsm48_pdp_type_org {
+ PDP_TYPE_ORG_ETSI = 0x00,
+ PDP_TYPE_ORG_IETF = 0x01,
+};
+enum gsm48_pdp_type_nr {
+ PDP_TYPE_N_ETSI_RESERVED = 0x00,
+ PDP_TYPE_N_ETSI_PPP = 0x01,
+ PDP_TYPE_N_IETF_IPv4 = 0x21,
+ PDP_TYPE_N_IETF_IPv6 = 0x57,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_reliab_class {
+ GSM48_QOS_RC_LLC_ACK_RLC_ACK_DATA_PROT = 2,
+ GSM48_QOS_RC_LLC_UN_RLC_ACK_DATA_PROT = 3,
+ GSM48_QOS_RC_LLC_UN_RLC_UN_PROT_DATA = 4,
+ GSM48_QOS_RC_LLC_UN_RLC_UN_DATA_UN = 5,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_preced_class {
+ GSM48_QOS_PC_HIGH = 1,
+ GSM48_QOS_PC_NORMAL = 2,
+ GSM48_QOS_PC_LOW = 3,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_peak_tput {
+ GSM48_QOS_PEAK_TPUT_1000bps = 1,
+ GSM48_QOS_PEAK_TPUT_2000bps = 2,
+ GSM48_QOS_PEAK_TPUT_4000bps = 3,
+ GSM48_QOS_PEAK_TPUT_8000bps = 4,
+ GSM48_QOS_PEAK_TPUT_16000bps = 5,
+ GSM48_QOS_PEAK_TPUT_32000bps = 6,
+ GSM48_QOS_PEAK_TPUT_64000bps = 7,
+ GSM48_QOS_PEAK_TPUT_128000bps = 8,
+ GSM48_QOS_PEAK_TPUT_256000bps = 9,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_mean_tput {
+ GSM48_QOS_MEAN_TPUT_100bph = 1,
+ GSM48_QOS_MEAN_TPUT_200bph = 2,
+ GSM48_QOS_MEAN_TPUT_500bph = 3,
+ GSM48_QOS_MEAN_TPUT_1000bph = 4,
+ GSM48_QOS_MEAN_TPUT_2000bph = 5,
+ GSM48_QOS_MEAN_TPUT_5000bph = 6,
+ GSM48_QOS_MEAN_TPUT_10000bph = 7,
+ GSM48_QOS_MEAN_TPUT_20000bph = 8,
+ GSM48_QOS_MEAN_TPUT_50000bph = 9,
+ GSM48_QOS_MEAN_TPUT_100kbph = 10,
+ GSM48_QOS_MEAN_TPUT_200kbph = 11,
+ GSM48_QOS_MEAN_TPUT_500kbph = 0xc,
+ GSM48_QOS_MEAN_TPUT_1Mbph = 0xd,
+ GSM48_QOS_MEAN_TPUT_2Mbph = 0xe,
+ GSM48_QOS_MEAN_TPUT_5Mbph = 0xf,
+ GSM48_QOS_MEAN_TPUT_10Mbph = 0x10,
+ GSM48_QOS_MEAN_TPUT_20Mbph = 0x11,
+ GSM48_QOS_MEAN_TPUT_50Mbph = 0x12,
+ GSM48_QOS_MEAN_TPUT_BEST_EFFORT = 0x1f,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_err_sdu {
+ GSM48_QOS_ERRSDU_NODETECT = 1,
+ GSM48_QOS_ERRSDU_YES = 2,
+ GSM48_QOS_ERRSDU_NO = 3,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_deliv_order {
+ GSM48_QOS_DO_ORDERED = 1,
+ GSM48_QOS_DO_UNORDERED = 2,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_traf_class {
+ GSM48_QOS_TC_CONVERSATIONAL = 1,
+ GSM48_QOS_TC_STREAMING = 2,
+ GSM48_QOS_TC_INTERACTIVE = 3,
+ GSM48_QOS_TC_BACKGROUND = 4,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_max_sdu_size {
+ /* values below in 10 octet granularity */
+ GSM48_QOS_MAXSDU_1502 = 0x97,
+ GSM48_QOS_MAXSDU_1510 = 0x98,
+ GSM48_QOS_MAXSDU_1520 = 0x99,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_max_bitrate {
+ GSM48_QOS_MBRATE_1k = 0x01,
+ GSM48_QOS_MBRATE_63k = 0x3f,
+ GSM48_QOS_MBRATE_64k = 0x40,
+ GSM48_QOS_MBRATE_568k = 0x7f,
+ GSM48_QOS_MBRATE_576k = 0x80,
+ GSM48_QOS_MBRATE_8640k = 0xfe,
+ GSM48_QOS_MBRATE_0k = 0xff,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_resid_ber {
+ GSM48_QOS_RBER_5e_2 = 0x01,
+ GSM48_QOS_RBER_1e_2 = 0x02,
+ GSM48_QOS_RBER_5e_3 = 0x03,
+ GSM48_QOS_RBER_4e_3 = 0x04,
+ GSM48_QOS_RBER_1e_3 = 0x05,
+ GSM48_QOS_RBER_1e_4 = 0x06,
+ GSM48_QOS_RBER_1e_5 = 0x07,
+ GSM48_QOS_RBER_1e_6 = 0x08,
+ GSM48_QOS_RBER_6e_8 = 0x09,
+};
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+enum gsm48_qos_sdu_err {
+ GSM48_QOS_SERR_1e_2 = 0x01,
+ GSM48_QOS_SERR_7e_2 = 0x02,
+ GSM48_QOS_SERR_1e_3 = 0x03,
+ GSM48_QOS_SERR_1e_4 = 0x04,
+ GSM48_QOS_SERR_1e_5 = 0x05,
+ GSM48_QOS_SERR_1e_6 = 0x06,
+ GSM48_QOS_SERR_1e_1 = 0x07,
+};
+
+/* 3GPP 24.008 / Chapter 10.5.5.20 / Table 10.5.153a */
+enum gsm48_gmm_service_type {
+ GPRS_SERVICE_T_SIGNALLING = 0x00,
+ GPRS_SERVICE_T_DATA = 0x01,
+ GPRS_SERVICE_T_PAGING_RESP = 0x02,
+ GPRS_SERVICE_T_MBMS_MC_SERV = 0x03,
+ GPRS_SERVICE_T_MBMS_BC_SERV = 0x04,
+};
+
+extern const struct value_string *gprs_service_t_strs;
+
+bool gprs_ms_net_cap_gea_supported(const uint8_t *ms_net_cap, uint8_t cap_len,
+ enum gprs_ciph_algo gea);
+
+/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
+struct gsm48_qos {
+ /* octet 3 */
+ uint8_t reliab_class:3;
+ uint8_t delay_class:3;
+ uint8_t spare:2;
+ /* octet 4 */
+ uint8_t preced_class:3;
+ uint8_t spare2:1;
+ uint8_t peak_tput:4;
+ /* octet 5 */
+ uint8_t mean_tput:5;
+ uint8_t spare3:3;
+ /* octet 6 */
+ uint8_t deliv_err_sdu:3;
+ uint8_t deliv_order:2;
+ uint8_t traf_class:3;
+ /* octet 7 */
+ uint8_t max_sdu_size;
+ /* octet 8 */
+ uint8_t max_bitrate_up;
+ /* octet 9 */
+ uint8_t max_bitrate_down;
+ /* octet 10 */
+ uint8_t sdu_err_ratio:4;
+ uint8_t resid_ber:4;
+ /* octet 11 */
+ uint8_t handling_prio:2;
+ uint8_t xfer_delay:6;
+ /* octet 12 */
+ uint8_t guar_bitrate_up;
+ /* octet 13 */
+ uint8_t guar_bitrate_down;
+ /* octet 14 */
+ uint8_t src_stats_desc:4;
+ uint8_t sig_ind:1;
+ uint8_t spare5:3;
+ /* octet 15 */
+ uint8_t max_bitrate_down_ext;
+ /* octet 16 */
+ uint8_t guar_bitrate_down_ext;
+};
+
+
+#endif /* _GSM48_GPRS_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_11.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_11.h
index f37152fe..651adcaa 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_11.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_11.h
@@ -1,5 +1,4 @@
-#ifndef PROTO_GSM_04_11_H
-#define PROTO_GSM_04_11_H
+#pragma once
#include <stdint.h>
@@ -122,7 +121,7 @@ enum sms_alphabet {
#define GSM340_SMS_COMMAND_MS2SC 0x02
#define GSM340_SMS_SUBMIT_MS2SC 0x01
#define GSM340_SMS_SUBMIT_REP_SC2MS 0x01
-#define GSM340_SMS_RESSERVED 0x03
+#define GSM340_SMS_RESERVED 0x03
/* GSM 03.40 / Chapter 9.2.3.2: TP-More-Messages-to-Send */
#define GSM340_TP_MMS_MORE 0
@@ -186,5 +185,3 @@ enum sms_alphabet {
#define GSM338_DCS_1111_CLASS1_ME 1
#define GSM338_DCS_1111_CLASS2_SIM 2
#define GSM338_DCS_1111_CLASS3_TE 3 /* See TS 07.05 */
-
-#endif /* PROTO_GSM_04_11_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_12.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_12.h
index 9b1538a5..30d6e4fa 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_12.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_12.h
@@ -1,10 +1,12 @@
-#ifndef PROTO_GSM_04_12_H
-#define PROTO_GSM_04_12_H
+#pragma once
#include <stdint.h>
/* GSM TS 04.12 definitions for Short Message Service Cell Broadcast */
+#define GSM412_MSG_LEN 88 /* TS 04.12 Section 3.1 */
+#define GSM412_BLOCK_LEN 22 /* TS 04.12 Section 3.1 */
+
#define GSM412_SEQ_FST_BLOCK 0x0
#define GSM412_SEQ_SND_BLOCK 0x1
#define GSM412_SEQ_TRD_BLOCK 0x2
@@ -27,5 +29,3 @@ struct gsm412_sched_msg {
uint8_t cbsms_msg_map[6];
uint8_t data[0];
} __attribute__((packed));
-
-#endif
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_80.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_80.h
index fa5c9451..2aebb46e 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_80.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_80.h
@@ -1,5 +1,4 @@
-#ifndef PROTO_GSM_04_80_H
-#define PROTO_GSM_04_80_H
+#pragma once
/* GSM TS 04.80 definitions (Supplementary Services Specification, Formats and Coding) */
@@ -122,5 +121,3 @@
#define ASN1_PRINTABLE_STRING_TAG 0x13
#define ASN1_IA5_STRING_TAG 0x16
#define ASN1_UNICODE_STRING_TAG 0x1E
-
-#endif /* PROTO_GSM_04_80_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_08.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_08.h
index 6b8f9359..6fb4e9e8 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_08.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_08.h
@@ -1,7 +1,6 @@
-/* From GSM08.08 */
+/* From GSM08.08 / 3GPP TS 48.008 version 11.7.0 Release 11 */
-#ifndef GSM_0808_H
-#define GSM_0808_H
+#pragma once
#include <stdlib.h>
@@ -48,6 +47,7 @@ enum BSS_MAP_MSG_TYPE {
BSS_MAP_MSG_ASSIGMENT_RQST = 1,
BSS_MAP_MSG_ASSIGMENT_COMPLETE = 2,
BSS_MAP_MSG_ASSIGMENT_FAILURE = 3,
+ BSS_MAP_MSG_CHAN_MOD_RQST = 8,
/* HANDOVER MESSAGES */
BSS_MAP_MSG_HANDOVER_RQST = 16,
@@ -62,6 +62,10 @@ enum BSS_MAP_MSG_TYPE {
BSS_MAP_MSG_HANDOVER_CANDIDATE_RESPONSE = 25,
BSS_MAP_MSG_HANDOVER_REQUIRED_REJECT = 26,
BSS_MAP_MSG_HANDOVER_DETECT = 27,
+ BSS_MAP_MSG_INT_HANDOVER_REQUIRED = 0x70,
+ BSS_MAP_MSG_INT_HANDOVER_REQUIRED_REJ = 0x71,
+ BSS_MAP_MSG_INT_HANDOVER_CMD = 0x72,
+ BSS_MAP_MSG_INT_HANDOVER_ENQUIRY = 0x73,
/* RELEASE MESSAGES */
BSS_MAP_MSG_CLEAR_CMD = 32,
@@ -81,6 +85,8 @@ enum BSS_MAP_MSG_TYPE {
BSS_MAP_MSG_PERFORM_LOCATION_RESPONSE = 45,
BSS_MAP_MSG_PERFORM_LOCATION_ABORT = 46,
BSS_MAP_MSG_COMMON_ID = 47,
+ BSS_MAP_MSG_REROUTE_CMD = 0x78,
+ BSS_MAP_MSG_REROUTE_COMPLETE = 0x79,
/* GENERAL MESSAGES */
BSS_MAP_MSG_RESET = 48,
@@ -92,6 +98,8 @@ enum BSS_MAP_MSG_TYPE {
BSS_MAP_MSG_MSC_INVOKE_TRACE = 54,
BSS_MAP_MSG_BSS_INVOKE_TRACE = 55,
BSS_MAP_MSG_CONNECTIONLESS_INFORMATION = 58,
+ BSS_MAP_MSG_RESET_IP_RSRC = 0x3d,
+ BSS_MAP_MSG_RESET_IP_RSRC_ACK = 0x3e,
/* TERRESTRIAL RESOURCE MESSAGES */
BSS_MAP_MSG_BLOCK = 64,
@@ -134,6 +142,14 @@ enum BSS_MAP_MSG_TYPE {
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_NOTIFICATION_DATA = 0x62,
+ BSS_MAP_MSG_UPLINK_APP_DATA = 0x63,
+
+ /* LOCAL SWITCHING */
+ BSS_MAP_MSG_LCLS_CONNECT_CTRL = 0x74,
+ BSS_MAP_MSG_LCLS_CONNECT_CTRL_ACK = 0x75,
+ BSS_MAP_MSG_LCLS_NOTIFICATION = 0x76,
};
enum GSM0808_IE_CODING {
@@ -218,8 +234,63 @@ enum GSM0808_IE_CODING {
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,
+ GSM0808_IE_GERAN_BSC_CONTAINER = 0x54,
+ GSM0808_IE_NEW_BSS_TO_OLD_BSS_INFO = 0x61,
+ GSM0800_IE_INTER_SYSTEM_INFO = 0x63,
+ GSM0808_IE_SNA_ACCESS_INFO = 0x64,
+ GSM0808_IE_VSTK_RAND_INFO = 0x65,
+ GSM0808_IE_VSTK_INFO = 0x66,
+ GSM0808_IE_PAGING_INFO = 0x67,
+ GSM0808_IE_IMEI = 0x68,
+ GSM0808_IE_VELOCITY_ESTIMATE = 0x55,
+ GSM0808_IE_VGCS_FEATURE_FLAGS = 0x69,
+ GSM0808_IE_TALKER_PRIORITY = 0x6a,
+ GSM0808_IE_EMERGENCY_SET_INDICATION = 0x6b,
+ GSM0808_IE_TALKER_IDENTITY = 0x6c,
+ GSM0808_IE_CELL_ID_LIST_SEGMENT = 0x6d,
+ GSM0808_IE_SMS_TO_VGCS = 0x6e,
+ GSM0808_IE_VGCS_TALKER_MODE = 0x6f,
+ GSM0808_IE_VGCS_VBS_CELL_STATUS = 0x70,
+ GSM0808_IE_CELL_ID_LIST_SEG_EST_CELLS = 0x71,
+ GSM0808_IE_CELL_ID_LIST_SEG_CELLS_TBE = 0x72,
+ GSM0808_IE_CELL_ID_LIST_SEG_REL_CELLS = 0x73,
+ GSM0808_IE_CELL_ID_LIST_SEG_NE_CELLS = 0x74,
+ GSM0808_IE_GANSS_ASSISTANCE_DATA = 0x75,
+ GSM0808_IE_GANSS_POSITIONING_DATA = 0x76,
+ GSM0808_IE_GANSS_LOCATION_TYPE = 0x77,
+ GSM0808_IE_APP_DATA = 0x78,
+ GSM0808_IE_DATA_IDENTITY = 0x79,
+ GSM0808_IE_APP_DATA_INFO = 0x7a,
+ GSM0808_IE_MSISDN = 0x7b,
+ GSM0808_IE_AOIP_TRASP_ADDR = 0x7c,
+ GSM0808_IE_SPEECH_CODEC_LIST = 0x7d,
+ GSM0808_IE_SPEECH_CODEC = 0x7e,
+ GSM0808_IE_CALL_ID = 0x7f,
+ GSM0808_IE_CALL_ID_LIST = 0x80,
+ GSM0808_IE_A_IF_SEL_FOR_RESET = 0x81,
+ GSM0808_IE_KC_128 = 0x83,
+ GSM0808_IE_CSG_IDENTIFIER = 0x84,
+ GSM0808_IE_REDIR_ATTEMPT_FLAG = 0x85,
+ GSM0808_IE_REROUTE_REJ_CAUSE = 0x86,
+ GSM0808_IE_SEND_SEQ_NUM = 0x87,
+ GSM0808_IE_REROUTE_COMPL_OUTCOME = 0x88,
+ GSM0808_IE_GLOBAL_CALL_REF = 0x89,
+ GSM0808_IE_LCLS_CONFIG = 0x8a,
+ GSM0808_IE_LCLS_CONN_STATUS_CTRL = 0x8b,
+ GSM0808_IE_LCLS_CORR_NOT_NEEDED = 0x8c,
+ GSM0808_IE_LCLS_BSS_STATUS = 0x8d,
+ GSM0808_IE_LCLS_BREAK_REQ = 0x8e,
+ GSM0808_IE_CSFB_INDICATION = 0x8f,
+ GSM0808_IE_CS_TO_PS_SRVCC = 0x90,
+ GSM0808_IE_SRC_ENB_TO_TGT_ENB_TRANSP = 0x91,
+ GSM0808_IE_CS_TO_PS_SRVCC_IND = 0x92,
+ GSM0808_IE_CN_TO_MS_TRANSP_INFO = 0x93,
+ GSM0808_IE_SELECTED_PLMN_ID = 0x94,
+ GSM0808_IE_LAST_USED_EUTRAN_PLMN_ID = 0x95,
};
+/* GSM 08.08 3.2.2.5 Cause */
enum gsm0808_cause {
GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE = 0,
GSM0808_CAUSE_RADIO_INTERFACE_FAILURE = 1,
@@ -237,6 +308,14 @@ enum gsm0808_cause {
GSM0808_CAUSE_DIRECTED_RETRY = 13,
GSM0808_CAUSE_JOINED_GROUP_CALL_CHANNEL = 14,
GSM0808_CAUSE_TRAFFIC = 15,
+ GSM0808_CAUSE_REDUCE_LOAD_IN_SERVING_CELL = 0x10,
+ GSM0808_CAUSE_TRAFFIC_LOAD_IN_TGT_HIGHER_THAN_IN_SRC_CELL = 0x11,
+ GSM0808_CAUSE_RELOCATION_TRIGGERED = 0x12,
+ GSM0808_CAUSE_REQUSTED_OPT_NOT_AUTHORISED = 0x14,
+ GSM0808_CAUSE_ALT_CHAN_CONFIG_REQUESTED = 0x15,
+ GSM0808_CAUSE_RESP_TO_INT_HO_ENQ_MSG = 0x16,
+ GSM0808_CAUSE_INT_HO_ENQUIRY_REJECT = 0x17,
+ GSM0808_CAUSE_REDUNDANCY_LEVEL_NOT_ADEQUATE = 0x18,
GSM0808_CAUSE_EQUIPMENT_FAILURE = 32,
GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE = 33,
GSM0808_CAUSE_RQSTED_TERRESTRIAL_RESOURCE_UNAVAILABLE = 34,
@@ -247,19 +326,35 @@ enum gsm0808_cause {
GSM0808_CAUSE_INVALID_CELL = 39,
GSM0808_CAUSE_TRAFFIC_LOAD = 40,
GSM0808_CAUSE_PREEMPTION = 41,
+ GSM0808_CAUSE_DTM_HO_SGSN_FAILURE = 0x2a,
+ GSM0808_CAUSE_DTM_HO_PS_ALLOC_FAILURE = 0x2b,
GSM0808_CAUSE_RQSTED_TRANSCODING_RATE_ADAPTION_UNAVAILABLE = 48,
GSM0808_CAUSE_CIRCUIT_POOL_MISMATCH = 49,
GSM0808_CAUSE_SWITCH_CIRCUIT_POOL = 50,
GSM0808_CAUSE_RQSTED_SPEECH_VERSION_UNAVAILABLE = 51,
GSM0808_CAUSE_LSA_NOT_ALLOWED = 52,
+ GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_UNAVAIL = 0x35,
+ GSM0808_CAUSE_REQ_A_IF_TYPE_UNAVAIL = 0x36,
+ GSM0808_CAUSE_INVALID_CSG_CELL = 0x37,
+ GSM0808_CAUSE_REQ_REDUND_LEVEL_NOT_AVAIL = 0x3f,
GSM0808_CAUSE_CIPHERING_ALGORITHM_NOT_SUPPORTED = 64,
+ GSM0808_CAUSE_GERAN_IU_MODE_FAILURE = 0x41,
+ GSM0808_CAUSE_INC_RELOC_NOT_SUPP_DT_PUESBINE_FEATURE = 0x42,
+ GSM0808_CAUSE_ACCESS_RESTRICTED_DUE_TO_SHARED_NETWORKS = 0x43,
+ GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP = 0x44,
+ GSM0808_CAUSE_REQ_A_IF_TYPE_NOT_SUPP = 0x45,
+ GSM0808_CAUSE_REQ_REDUND_LVL_NOT_SUPP = 0x46,
GSM0808_CAUSE_TERRESTRIAL_CIRCUIT_ALREADY_ALLOCATED = 80,
GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS = 81,
GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING = 82,
GSM0808_CAUSE_INCORRECT_VALUE = 83,
GSM0808_CAUSE_UNKNOWN_MESSAGE_TYPE = 84,
GSM0808_CAUSE_UNKNOWN_INFORMATION_ELEMENT = 85,
+ GSM0808_CAUSE_DTM_HO_INVALID_PS_IND = 0x56,
+ GSM0808_CAUSE_CALL_ID_ALREADY_ALLOC = 0x57,
GSM0808_CAUSE_PROTOCOL_ERROR_BETWEEN_BSS_AND_MSC = 96,
+ GSM0808_CAUSE_VGCS_VBS_CALL_NON_EXISTANT = 0x61,
+ GSM0808_CAUSE_DTM_HO_TIMER_EXPIRY = 0x62,
};
/* GSM 08.08 3.2.2.11 Channel Type */
@@ -269,6 +364,7 @@ enum gsm0808_chan_indicator {
GSM0808_CHAN_SIGN = 3,
};
+/* GSM 08.08 3.2.2.11 Channel Type */
enum gsm0808_chan_rate_type_data {
GSM0808_DATA_FULL_BM = 0x8,
GSM0808_DATA_HALF_LM = 0x9,
@@ -280,6 +376,7 @@ enum gsm0808_chan_rate_type_data {
GSM0808_DATA_MULTI_MASK_NO_CHANGE = 0x30,
};
+/* GSM 08.08 3.2.2.11 Channel Type */
enum gsm0808_chan_rate_type_speech {
GSM0808_SPEECH_FULL_BM = 0x8,
GSM0808_SPEECH_HALF_LM = 0x9,
@@ -291,6 +388,7 @@ enum gsm0808_chan_rate_type_speech {
GSM0808_SPEECH_PERM_NO_CHANGE = 0x1f,
};
+/* GSM 08.08 3.2.2.11 Channel Type */
enum gsm0808_permitted_speech {
GSM0808_PERM_FR1 = 0x01,
GSM0808_PERM_FR2 = 0x11,
@@ -300,4 +398,21 @@ enum gsm0808_permitted_speech {
GSM0808_PERM_HR3 = GSM0808_PERM_FR3 | 0x4,
};
-#endif
+/* GSM 08.08 3.2.2.44 Chosen Encryption Algorithm */
+enum gsm0808_chosen_enc_alg {
+ GSM0808_ALG_ID_A5_0 = 0x01,
+ GSM0808_ALG_ID_A5_1 = 0x02,
+ GSM0808_ALG_ID_A5_2 = 0x03,
+ GSM0808_ALG_ID_A5_3 = 0x04,
+ GSM0808_ALG_ID_A5_4 = 0x05,
+ GSM0808_ALG_ID_A5_5 = 0x06,
+ GSM0808_ALG_ID_A5_6 = 0x07,
+ GSM0808_ALG_ID_A5_7 = 0x08,
+};
+
+/* GSM 08.08 3.2.2.85 Paging Information */
+enum gsm0808_paging_info {
+ GSM0808_PAGINF_FOR_MT_CALL = 0x00,
+ GSM0808_PAGINF_FOR_SMS = 0x01,
+ GSM0808_PAGINF_FOR_USSD = 0x02,
+};
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_58.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_58.h
index 57a8f687..a7a2ccfd 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_58.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_58.h
@@ -1,5 +1,4 @@
-#ifndef PROTO_GSM_08_58_H
-#define PROTO_GSM_08_58_H
+#pragma once
/* GSM Radio Signalling Link messages on the A-bis interface
* 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */
@@ -83,7 +82,7 @@ struct abis_rsl_cchan_hdr {
/* \brief Check if given RSL message discriminator is transparent */
#define ABIS_RSL_MDISC_IS_TRANSP(x) (x & 0x01)
-/* \brief RSL Message Tyoe (Chapter 9.1) */
+/* \brief RSL Message Type (Chapter 9.1) */
enum abis_rsl_msgtype {
/* Radio Link Layer Management */
RSL_MT_DATA_REQ = 0x01,
@@ -296,8 +295,31 @@ enum abis_rsl_ie {
RSL_IE_IPAC_RTP_MPLEX_ID= 0xfe,
};
+/* Ericsson specific IEs, clash with above partially, so they're not
+ * part of the enum */
+#define RSL_IE_ERIC_INST_NR 0x48
+#define RSL_IE_ERIC_PGSL_TIMERS 0x49
+#define RSL_IE_ERIC_REPEAT_DL_FACCH 0x4a
+#define RSL_IE_ERIC_POWER_INFO 0xf0
+#define RSL_IE_ERIC_MOBILE_ID 0xf1
+#define RSL_IE_ERIC_BCCH_MAPPING 0xf2
+#define RSL_IE_ERIC_PACKET_PAG_IND 0xf3
+#define RSL_IE_ERIC_CNTR_CTRL 0xf4
+#define RSL_IE_ERIC_CNTR_CTRL_ACK 0xf5
+#define RSL_IE_ERIC_CNTR_REPORT 0xf6
+#define RSL_IE_ERIC_ICP_CONN 0xf7
+#define RSL_IE_ERIC_EMR_SUPPORT 0xf8
+#define RSL_IE_ERIC_EGPRS_REQ_REF 0xf9
+#define RSL_IE_ERIC_VGCS_REL 0xfa
+#define RSL_IE_ERIC_REP_PER_NCH 0xfb
+#define RSL_IE_ERIC_NY2 0xfc
+#define RSL_IE_ERIC_T3115 0xfd
+#define RSL_IE_ERIC_ACTIVATE_FLAG 0xfe
+#define RSL_IE_ERIC_FULL_NCH_INFO 0xff
+
/* Chapter 9.3.1 */
#define RSL_CHAN_NR_MASK 0xf8
+#define RSL_CHAN_NR_1 0x08 /*< bit to add for 2nd,... lchan */
#define RSL_CHAN_Bm_ACCHs 0x08
#define RSL_CHAN_Lm_ACCHs 0x10 /* .. 0x18 */
#define RSL_CHAN_SDCCH4_ACCH 0x20 /* .. 0x38 */
@@ -305,6 +327,7 @@ enum abis_rsl_ie {
#define RSL_CHAN_BCCH 0x80
#define RSL_CHAN_RACH 0x88
#define RSL_CHAN_PCH_AGCH 0x90
+#define RSL_CHAN_OSMO_PDCH 0xc0 /*< non-standard, for dyn TS */
/* Chapter 9.3.3 */
#define RSL_ACT_TYPE_INITIAL 0x00
@@ -315,6 +338,7 @@ enum abis_rsl_ie {
#define RSL_ACT_INTER_SYNC 0x03
#define RSL_ACT_SECOND_ADD 0x04
#define RSL_ACT_SECOND_MULTI 0x05
+#define RSL_ACT_OSMO_PDCH 0x0f /*< non-standard, for dyn TS */
/*! \brief RSL Channel Mode IF (Chapter 9.3.6) */
struct rsl_ie_chan_mode {
@@ -459,6 +483,7 @@ struct rsl_ie_chan_ident {
#define RSL_EXT_MEAS_ORDER 0x47
#define RSL_MEAS_INFO 0x48
#define RSL_SYSTEM_INFO_13 0x28
+#define RSL_ERIC_SYSTEM_INFO_13 0x0C
#define RSL_SYSTEM_INFO_2quater 0x29
#define RSL_SYSTEM_INFO_9 0x2a
#define RSL_SYSTEM_INFO_18 0x2b
@@ -572,6 +597,35 @@ enum rsl_mrpci_phase {
RSL_MRPCI_PHASE_2PLUS = 3,
};
-/*! @} */
+/* 9.3.20 Release Mode */
+enum rsl_rel_mode {
+ RSL_REL_NORMAL = 0,
+ RSL_REL_LOCAL_END = 1,
+};
-#endif /* PROTO_GSM_08_58_H */
+/*! \brief ip.access specific embedded information elements */
+enum rsl_ipac_embedded_ie {
+ RSL_IPAC_EIE_RXLEV = 0x00,
+ RSL_IPAC_EIE_RXQUAL = 0x01,
+ RSL_IPAC_EIE_FREQ_ERR = 0x02,
+ RSL_IPAC_EIE_TIMING_ERR = 0x03,
+ RSL_IPAC_EIE_MEAS_AVG_CFG = 0x04,
+ RSL_IPAC_EIE_BS_PWR_CTL = 0x05,
+ RSL_IPAC_EIE_MS_PWR_CTL = 0x06,
+ RSL_IPAC_EIE_HANDO_THRESH = 0x07,
+ RSL_IPAC_EIE_NCELL_DEFAULTS = 0x08,
+ RSL_IPAC_EIE_NCELL_LIST = 0x09,
+ RSL_IPAC_EIE_PC_THRESH_COMP = 0x0a,
+ RSL_IPAC_EIE_HO_THRESH_COMP = 0x0b,
+ RSL_IPAC_EIE_HO_CAUSE = 0x0c,
+ RSL_IPAC_EIE_HO_CANDIDATES = 0x0d,
+ RSL_IPAC_EIE_NCELL_BA_CHG_LIST = 0x0e,
+ RSL_IPAC_EIE_NUM_OF_MS = 0x10,
+ RSL_IPAC_EIE_HO_CAND_EXT = 0x11,
+ RSL_IPAC_EIE_NCELL_DEF_EXT = 0x12,
+ RSL_IPAC_EIE_NCELL_LIST_EXT = 0x13,
+ RSL_IPAC_EIE_MASTER_KEY = 0x14,
+ RSL_IPAC_EIE_MASTER_SALT = 0x15,
+};
+
+/*! @} */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_09_02.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_09_02.h
new file mode 100644
index 00000000..3f68b457
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_09_02.h
@@ -0,0 +1,137 @@
+#ifndef PROTO_GSM_09_02_H
+#define PROTO_GSM_09_02_H
+
+/* GSM TS 09.02 definitions (MAP) */
+
+/* Section 17.7.4 */
+/* SS-Status */
+#define GSM0902_SS_STATUS_Q_BIT 0x08
+#define GSM0902_SS_STATUS_P_BIT 0x04
+#define GSM0902_SS_STATUS_R_BIT 0x02
+#define GSM0902_SS_STATUS_A_BIT 0x01
+
+/* SS-Data */
+#define GSM0902_SS_DATA_SS_STATUS_TAG 0x84
+#define GSM0902_SS_DATA_NBR_USER 0x85
+
+/* SS-Info */
+#define GSM0902_SS_INFO_FORW_INFO_TAG 0xA0
+#define GSM0902_SS_INFO_CALL_BARR_INFO_TAG 0xA1
+#define GSM0902_SS_INFO_SS_DATA_TAG 0xA3
+
+/* InterrogateSS-Res */
+#define GSM0902_SS_INTERR_SS_RES_SS_STATUS_TAG 0x80
+#define GSM0902_SS_INTERR_SS_RES_BSG_LIST_TAG 0x81
+#define GSM0902_SS_INTERR_SS_RES_FORW_FEAT_LIST_TAG 0x82
+#define GSM0902_SS_INTERR_SS_RES_GEN_SERV_INFO_TAG 0x83
+
+/* Section 17.7.5 */
+/* Supplementary service codes */
+#define GSM0902_SS_CODE_ALL_SS 0x00
+#define GSM0902_SS_CODE_ALL_LINE_IDENTIFICATION_SS 0x10
+#define GSM0902_SS_CODE_CLIP 0x11
+#define GSM0902_SS_CODE_CLIR 0x12
+#define GSM0902_SS_CODE_COLP 0x13
+#define GSM0902_SS_CODE_COLR 0x14
+#define GSM0902_SS_CODE_MCI 0x15
+#define GSM0902_SS_CODE_ALL_NAME_IDENTIFICATION_SS 0x18
+#define GSM0902_SS_CODE_CNAP 0x19
+#define GSM0902_SS_CODE_ALL_FORWARDING_SS 0x20
+#define GSM0902_SS_CODE_CFU 0x21
+#define GSM0902_SS_CODE_ALL_COND_FORWARDING_SS 0x28
+#define GSM0902_SS_CODE_CFB 0x29
+#define GSM0902_SS_CODE_CFNRY 0x2A
+#define GSM0902_SS_CODE_CFNRC 0x2B
+#define GSM0902_SS_CODE_CD 0x24
+#define GSM0902_SS_CODE_ALL_CALL_OFFERING_SS 0x30
+#define GSM0902_SS_CODE_ECT 0x31
+#define GSM0902_SS_CODE_MAH 0x32
+#define GSM0902_SS_CODE_ALL_CALL_COMPLETION_SS 0x40
+#define GSM0902_SS_CODE_CW 0x41
+#define GSM0902_SS_CODE_HOLD 0x42
+#define GSM0902_SS_CODE_CCBS_A 0x43
+#define GSM0902_SS_CODE_CCBS_B 0x44
+#define GSM0902_SS_CODE_MC 0x45
+#define GSM0902_SS_CODE_ALL_MULTI_PARTY_SS 0x50
+#define GSM0902_SS_CODE_MULTI_PTY 0x51
+#define GSM0902_SS_CODE_ALL_COMMUNITY_OF_INTEREST_SS 0x60
+#define GSM0902_SS_CODE_CUG 0x61
+#define GSM0902_SS_CODE_ALL_CHARGING_SS 0x70
+#define GSM0902_SS_CODE_AOCI 0x71
+#define GSM0902_SS_CODE_AOCC 0x72
+#define GSM0902_SS_CODE_ALL_ADDITIONAL_INFO_TRANSFER_SS 0x80
+#define GSM0902_SS_CODE_UUS1 0x81
+#define GSM0902_SS_CODE_UUS2 0x82
+#define GSM0902_SS_CODE_UUS3 0x83
+#define GSM0902_SS_CODE_ALL_BARRING_SS 0x90
+#define GSM0902_SS_CODE_BARRING_OF_OUTGOING_CALLS 0x91
+#define GSM0902_SS_CODE_BAOC 0x92
+#define GSM0902_SS_CODE_BOIC 0x93
+#define GSM0902_SS_CODE_BOIC_EX_HC 0x94
+#define GSM0902_SS_CODE_BARRING_OF_INCOMING_CALLS 0x99
+#define GSM0902_SS_CODE_BAIC 0x9A
+#define GSM0902_SS_CODE_BIC_ROAM 0x9B
+#define GSM0902_SS_CODE_ALL_PLMN_SPECIFIC_SS 0xF0
+#define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_1 0xF1
+#define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_2 0xF2
+#define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_3 0xF3
+#define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_4 0xF4
+#define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_5 0xF5
+#define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_6 0xF6
+#define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_7 0xF7
+#define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_8 0xF8
+#define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_9 0xF9
+#define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_A 0xFA
+#define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_B 0xFB
+#define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_C 0xFC
+#define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_D 0xFD
+#define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_E 0xFE
+#define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_F 0xFF
+#define GSM0902_SS_CODE_ALL_CALL_PRIORITY_SS 0xA0
+#define GSM0902_SS_CODE_EMLPP 0xA1
+#define GSM0902_SS_CODE_ALL_LCSPRIVACY_EXCEPTION 0xB0
+#define GSM0902_SS_CODE_UNIVERSAL 0xB1
+#define GSM0902_SS_CODE_CALL_SESSION_RELATED 0xB2
+#define GSM0902_SS_CODE_CALL_SESSION_UNRELATED 0xB3
+#define GSM0902_SS_CODE_PLMNOPERATOR 0xB4
+#define GSM0902_SS_CODE_SERVICE_TYPE 0xB5
+#define GSM0902_SS_CODE_ALL_MOLR_SS 0xC0
+#define GSM0902_SS_CODE_BASIC_SELF_LOCATION 0xC1
+#define GSM0902_SS_CODE_AUTONOMOUS_SELF_LOCATION 0xC2
+#define GSM0902_SS_CODE_TRANSFER_TO_THIRD_PARTY 0xC3
+
+/* Section 17.7.9 */
+/* Teleservice codes */
+#define GSM0902_TS_CODE_ALL_TELESERVICES 0x00
+#define GSM0902_TS_CODE_ALL_SPEECH_TRANSMISSION_SERVICES 0x10
+#define GSM0902_TS_CODE_TELEPHONY 0x11
+#define GSM0902_TS_CODE_EMERGENCY_CALLS 0x12
+#define GSM0902_TS_CODE_ALL_SHORT_MESSAGE_SERVICES 0x20
+#define GSM0902_TS_CODE_SHORT_MESSAGE_MT_PP 0x21
+#define GSM0902_TS_CODE_SHORT_MESSAGE_MO_PP 0x22
+#define GSM0902_TS_CODE_ALL_FACSIMILE_TRANSMISSION_SERVICES 0x60
+#define GSM0902_TS_CODE_FACSIMILE_GROUP3AND_ALTER_SPEECH 0x61
+#define GSM0902_TS_CODE_AUTOMATIC_FACSIMILE_GROUP3 0x62
+#define GSM0902_TS_CODE_FACSIMILE_GROUP4 0x63
+#define GSM0902_TS_CODE_ALL_DATA_TELESERVICES 0x70
+#define GSM0902_TS_CODE_ALL_TELESERVICES_EXEPT_SMS 0x80
+#define GSM0902_TS_CODE_ALL_VOICE_GROUP_CALL_SERVICES 0x90
+#define GSM0902_TS_CODE_VOICE_GROUP_CALL 0x91
+#define GSM0902_TS_CODE_VOICE_BROADCAST_CALL 0x92
+#define GSM0902_TS_CODE_ALL_PLMN_SPECIFIC_TS 0xD0
+#define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_1 0xD1
+#define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_2 0xD2
+#define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_3 0xD3
+#define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_4 0xD4
+#define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_5 0xD5
+#define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_6 0xD6
+#define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_7 0xD7
+#define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_8 0xD8
+#define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_9 0xD9
+#define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_A 0xDA
+#define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_B 0xDB
+#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/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_12_21.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_12_21.h
index 694df938..5daab42b 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_12_21.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_12_21.h
@@ -1,5 +1,4 @@
-#ifndef PROTO_GSM_12_21_H
-#define PROTO_GSM_12_21_H
+#pragma once
/* GSM Network Management messages on the A-bis interface
* 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */
@@ -46,6 +45,9 @@ struct abis_om_hdr {
uint8_t data[0];
} __attribute__ ((packed));
+#define ABIS_NM_MSG_SIZE 1024
+#define ABIS_NM_MSG_HEADROOM 128
+
/*! \brief Message Discriminator for Formatted Object Messages */
#define ABIS_OM_MDISC_FOM 0x80
/*! \brief Message Discriminator for Man Machine Interface */
@@ -257,6 +259,26 @@ enum abis_nm_msgtype_ipacc {
NM_MT_IPACC_SET_ATTR_NACK,
};
+/*! \brief OML Probable Cause (Section 9.4.43) Manufacturer specific values */
+enum abis_mm_event_causes {
+ /* Critical causes */
+ OSMO_EVT_CRIT_SW_FATAL = 0x0000,
+ OSMO_EVT_CRIT_PROC_STOP = 0x0002,
+ OSMO_EVT_CRIT_RTP_TOUT = 0x032c,
+ OSMO_EVT_CRIT_BOOT_FAIL = 0x0401,
+ /* Major causes */
+ OSMO_EVT_MAJ_UKWN_MSG = 0x0002,
+ OSMO_EVT_MAJ_RSL_FAIL = 0x0309,
+ OSMO_EVT_MAJ_UNSUP_ATTR = 0x0318,
+ OSMO_EVT_MAJ_NET_CONGEST = 0x032b,
+ /* Minor causes */
+ OSMO_EVT_MIN_PAG_TAB_FULL = 0x0401,
+ /* Warning causes */
+ OSMO_EVT_WARN_SW_WARN = 0x0001,
+};
+
+extern const struct value_string abis_mm_event_cause_names[];
+
enum abis_nm_bs11_cell_alloc {
NM_BS11_CANR_GSM = 0x00,
NM_BS11_CANR_DCS1800 = 0x01,
@@ -486,6 +508,10 @@ enum abis_nm_attr {
NM_ATT_BS11_ANT_TYPE = 0xf4,
NM_ATT_BS11_PLL_MODE = 0xfc,
NM_ATT_BS11_PASSWORD = 0xfd,
+
+ /* osmocom (osmo-bts) specific attributes, used in combination
+ * with the "org.osmocom" manufacturer identification */
+ NM_ATT_OSMO_REDUCEPOWER = 0xfe, /* TLV_TYPE_TV */
};
#define NM_ATT_BS11_FILE_DATA NM_ATT_EVENT_TYPE
@@ -536,6 +562,8 @@ enum abis_nm_chan_comb {
NM_CHANC_IPAC_PDCH = 0x0d, /* PDTCH/F + PACCH/F + PTCCH/F */
NM_CHANC_IPAC_TCHFull_PDCH = 0x80,
NM_CHANC_IPAC_TCHFull_TCHHalf = 0x81,
+ /* osmocom */
+ NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH = 0x90,
};
/*! \brief Event Type (Section 9.4.16) */
@@ -564,6 +592,8 @@ enum abis_nm_pcause_type {
NM_PCAUSE_T_MANUF = 0x03,
};
+extern const struct value_string abis_nm_pcause_type_names[];
+
/*! \brief NACK causes (Section 9.4.36) */
enum abis_nm_nack_cause {
/* General Nack Causes */
@@ -610,6 +640,19 @@ struct abis_nm_channel {
uint8_t subslot; /*!< \brief E1 sub-slot */
} __attribute__ ((packed));
+/*! \brief 3GPP TS 12.21 9.4.53 T200 index */
+enum abis_nm_t200_idx {
+ T200_SDCCH = 0,
+ T200_FACCH_F = 1,
+ T200_FACCH_H = 2,
+ T200_SACCH_TCH_SAPI0 = 3,
+ T200_SACCH_SDCCH = 4,
+ T200_SDCCH_SAPI3 = 5,
+ T200_SACCH_TCH_SAPI3 = 6
+};
+
+extern const uint8_t abis_nm_t200_ms[];
+
/*! \brief Siemens BS-11 specific objects in the SienemsHW (0xA5) object class */
enum abis_bs11_objtype {
BS11_OBJ_ALCO = 0x01,
@@ -743,6 +786,13 @@ enum ipac_bcch_info_type {
IPAC_BINF_CELL_ALLOC = (1 << 2),
};
+struct msgb *abis_nm_fail_evt_rep(enum abis_nm_event_type t,
+ enum abis_nm_severity s,
+ enum abis_nm_pcause_type ct,
+ uint16_t cause_value, const char *fmt, ...);
+struct msgb *abis_nm_fail_evt_vrep(enum abis_nm_event_type t,
+ enum abis_nm_severity s,
+ enum abis_nm_pcause_type ct,
+ uint16_t cause_value, const char *fmt,
+ va_list ap);
/*! @} */
-
-#endif /* PROTO_GSM_12_21_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_23_003.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_23_003.h
new file mode 100644
index 00000000..a2109b60
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_23_003.h
@@ -0,0 +1,23 @@
+#pragma once
+
+/* Chapter 2.2 */
+#define GSM23003_IMSI_MAX_DIGITS 15
+/* Chapter 2.4 */
+#define GSM23003_TMSI_NUM_BYTES 4
+/* Chapter 2.5 */
+#define GSM23003_LMSI_NUM_BYTES 4
+/* Chapter 2.6 */
+#define GSM23003_TLLI_NUM_BYTES 4
+/* Chapter 2.7 */
+#define GSM23003_PTMSI_SIG_NUM_BYTES 3
+/* Chapter 2.8 */
+#define GSM23003_MME_CODE_NUM_BYTES 1
+#define GSM23003_MME_GROUP_NUM_BYTES 2
+#define GSM23003_MTMSI_NUM_BYTES 4
+/* Chapter 6.2.1 */
+#define GSM23003_IMEI_TAC_NUM_DIGITS 8
+#define GSM23003_IMEI_SNR_NUM_DIGITS 6
+#define GSM23003_IMEI_NUM_DIGITS (GSM23003_IMEI_TAC_NUM_DIGITS + \
+ GSM23003_IMEI_SNR_NUM_DIGITS + 1)
+#define GSM23003_IMEISV_NUM_DIGITS (GSM23003_IMEI_TAC_NUM_DIGITS + \
+ GSM23003_IMEI_SNR_NUM_DIGITS + 2)
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_44_318.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_44_318.h
index cd5ec05a..5d7f8787 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_44_318.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_44_318.h
@@ -1,5 +1,4 @@
-#ifndef PROTO_GSM_44_318_H
-#define PROTO_GSM_44_318_H
+#pragma once
#include <stdint.h>
@@ -197,4 +196,3 @@ struct gan_cch_desc_ie {
#endif
uint8_t access_class[2];
} __attribute__((packed));
-#endif /* PROTO_GSM_44_318_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/ipaccess.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/ipaccess.h
index 5d98a21f..ba6cb3be 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/protocol/ipaccess.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/ipaccess.h
@@ -1,5 +1,4 @@
-#ifndef _OSMO_PROTO_IPACCESS_H
-#define _OSMO_PROTO_IPACCESS_H
+#pragma once
#include <stdint.h>
@@ -34,6 +33,9 @@ enum ipaccess_proto_ext {
IPAC_PROTO_EXT_MGCP = 0x01,
IPAC_PROTO_EXT_LAC = 0x02,
IPAC_PROTO_EXT_SMSC = 0x03,
+ IPAC_PROTO_EXT_ORC = 0x04, /* OML Router Control */
+ IPAC_PROTO_EXT_GSUP = 0x05, /* GSUP GPRS extension */
+ IPAC_PROTO_EXT_OAP = 0x06, /* Osmocom Authn Protocol */
};
enum ipaccess_msgtype {
@@ -90,5 +92,3 @@ struct sdp_header_entry {
uint32_t addr2;
uint32_t start;
} __attribute__((packed));
-
-#endif /* _OSMO_PROTO_IPACCESS_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/smpp34_osmocom.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/smpp34_osmocom.h
new file mode 100644
index 00000000..cff6adba
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/smpp34_osmocom.h
@@ -0,0 +1,45 @@
+#pragma once
+/* Osmocom SMPP extensions */
+
+/* Osmocom specific new TLV definitions */
+
+/* ARFCN in 16-bit encoding, highest bit: PCS(1) / DCS(0) */
+#define TLVID_osmo_arfcn 0x2300
+/* Timing advance as uint8_t */
+#define TLVID_osmo_ta 0x2301
+/* Receive signal level (uplink) as int16_t in dBm */
+#define TLVID_osmo_rxlev_ul 0x2302
+/* Receive signal quality (uplink) as uint8_t */
+#define TLVID_osmo_rxqual_ul 0x2303
+/* Receive signal level (downlink) as int16_t in dBm */
+#define TLVID_osmo_rxlev_dl 0x2304
+/* Receive signal quality (downlink) as uint8_t */
+#define TLVID_osmo_rxqual_dl 0x2305
+/* IMEI of the subscriber, if known */
+#define TLVID_osmo_imei 0x2306
+/* MS Layer 1 Transmit Power */
+#define TLVID_osmo_ms_l1_txpwr 0x2307
+/* BTS Layer 1 Transmit Power */
+#define TLVID_osmo_bts_l1_txpwr 0x2308
+
+
+/* DELIVER_SM can contain the following optional Osmocom TLVs:
+ * TLVID_osmo_arfcn
+ * TLVID_osmo_ta
+ * TLVID_osmo_rxlev_ul
+ * TLVID_osmo_rxqual_ul
+ * TLVID_osmo_rxlev_dl
+ * TLVID_osmo_rxqual_dl
+ * TLVID_osmo_imei
+ */
+
+/* SUBMIT_SM_RESP (transaction mode) can contain the following optional
+ * Osmocom TLVs:
+ * TLVID_osmo_arfcn
+ * TLVID_osmo_ta
+ * TLVID_osmo_rxlev_ul
+ * TLVID_osmo_rxqual_ul
+ * TLVID_osmo_rxlev_dl
+ * TLVID_osmo_rxqual_dl
+ * TLVID_osmo_imei
+ */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/rsl.h b/src/shared/libosmocore/include/osmocom/gsm/rsl.h
index b8e4157a..5da61808 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/rsl.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/rsl.h
@@ -1,5 +1,4 @@
-#ifndef _OSMOCORE_RSL_H
-#define _OSMOCORE_RSL_H
+#pragma once
#include <stdint.h>
#include <osmocom/core/utils.h>
@@ -22,6 +21,12 @@ extern const struct tlv_definition rsl_att_tlvdef;
#define rsl_tlv_parse(dec, buf, len) \
tlv_parse(dec, &rsl_att_tlvdef, buf, len, 0, 0)
+extern const struct tlv_definition rsl_ipac_eie_tlvdef;
+
+/*! \brief Parse RSL IPAC EIE TLV structure using \ref tlv_parse */
+#define rsl_ipac_eie_tlv_parse(dec, buf, len) \
+ tlv_parse(dec, &rsl_ipac_eie_tlvdef, buf, len, 0, 0)
+
/* encode channel number as per Section 9.3.1 */
uint8_t rsl_enc_chan_nr(uint8_t type, uint8_t subch, uint8_t timeslot);
/* decode channel number as per Section 9.3.1 */
@@ -34,6 +39,7 @@ const char *rsl_err_name(uint8_t err);
const char *rsl_rlm_cause_name(uint8_t err);
const char *rsl_msg_name(uint8_t err);
const char *rsl_ipac_msg_name(uint8_t msg_type);
+const char *rsl_or_ipac_msg_name(uint8_t msg_type);
/* Section 3.3.2.3 TS 05.02. I think this looks like a table */
int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf);
@@ -50,6 +56,12 @@ void rsl_rll_push_l3(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr,
struct msgb *rsl_rll_simple(uint8_t msg_type, uint8_t chan_nr,
uint8_t link_id, int transparent);
-/*! @} */
+extern const struct value_string rsl_act_type_names[];
-#endif /* _OSMOCORE_RSL_H */
+/*! \brief Return a human readable name for GSM 08.58 RSL_ACT_* constants. */
+static inline const char *rsl_act_type_name(uint8_t act_type)
+{
+ return get_value_string(rsl_act_type_names, act_type);
+}
+
+/*! @} */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/rxlev_stat.h b/src/shared/libosmocore/include/osmocom/gsm/rxlev_stat.h
index 415509dc..7183b173 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/rxlev_stat.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/rxlev_stat.h
@@ -1,5 +1,4 @@
-#ifndef _OSMOCORE_RXLEV_STATS_H
-#define _OSMOCORE_RXLEV_STATS_H
+#pragma once
#define NUM_RXLEVS 32
#define NUM_ARFCNS 1024
@@ -18,5 +17,3 @@ int16_t rxlev_stat_get_next(const struct rxlev_stats *st, uint8_t rxlev, int16_t
void rxlev_stat_reset(struct rxlev_stats *st);
void rxlev_stat_dump(const struct rxlev_stats *st);
-
-#endif /* _OSMOCORE_RXLEV_STATS_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/sysinfo.h b/src/shared/libosmocore/include/osmocom/gsm/sysinfo.h
index 06feb1de..c7972f48 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/sysinfo.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/sysinfo.h
@@ -1,9 +1,12 @@
-#ifndef _OSMO_GSM_SYSINFO_H
-#define _OSMO_GSM_SYSINFO_H
+#pragma once
+#include <stdbool.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
+#define OSMO_EARFCN_INVALID 666
+#define OSMO_EARFCN_MEAS_INVALID 0xff
+
enum osmo_sysinfo_type {
SYSINFO_TYPE_NONE,
SYSINFO_TYPE_1,
@@ -33,11 +36,37 @@ enum osmo_sysinfo_type {
_MAX_SYSINFO_TYPE
};
+struct osmo_earfcn_si2q {
+ /* EARFCN (16 bits) array */
+ uint16_t *arfcn;
+ /* Measurement Bandwidth (3 bits), might be absent
+ (OSMO_EARFCN_MEAS_INVALID is stored in this case) */
+ uint8_t *meas_bw;
+ /* length of arfcn and meas_bw arrays (got to be the same) */
+ size_t length;
+ /* THRESH_E-UTRAN_high (5 bits) */
+ uint8_t thresh_hi;
+ /* THRESH_E-UTRAN_low (5 bits) */
+ uint8_t thresh_lo;
+ /* E-UTRAN_PRIORITY (3 bits) */
+ uint8_t prio;
+ /* E-UTRAN_QRXLEVMIN */
+ uint8_t qrxlm;
+ /* indicates whether thresh_lo value is valid
+ thresh_hi is mandatory and hence always considered valid */
+ bool thresh_lo_valid;
+ /* indicates whether prio value is valid */
+ bool prio_valid;
+ /* indicates whether qrxlm value is valid */
+ bool qrxlm_valid;
+};
+
typedef uint8_t sysinfo_buf_t[GSM_MACBLOCK_LEN];
extern const struct value_string osmo_sitype_strs[_MAX_SYSINFO_TYPE];
-
+int osmo_earfcn_add(struct osmo_earfcn_si2q *e, uint16_t arfcn, uint8_t meas_bw);
+int osmo_earfcn_del(struct osmo_earfcn_si2q *e, uint16_t arfcn);
+size_t osmo_earfcn_bit_size(const struct osmo_earfcn_si2q *e);
+void osmo_earfcn_init(struct osmo_earfcn_si2q *e);
uint8_t osmo_sitype2rsl(enum osmo_sysinfo_type si_type);
enum osmo_sysinfo_type osmo_rsl2sitype(uint8_t rsl_si);
-
-#endif /* _OSMO_GSM_SYSINFO_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/tlv.h b/src/shared/libosmocore/include/osmocom/gsm/tlv.h
index 9c0319d9..701fe688 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/tlv.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/tlv.h
@@ -1,5 +1,4 @@
-#ifndef _TLV_H
-#define _TLV_H
+#pragma once
#include <stdint.h>
#include <string.h>
@@ -403,13 +402,51 @@ int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,
const uint8_t *buf, int buf_len);
int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
const uint8_t *buf, int buf_len, uint8_t lv_tag, uint8_t lv_tag2);
-/* take a master (src) tlvdev and fill up all empty slots in 'dst' */
+/* take a master (src) tlv def and fill up all empty slots in 'dst' */
void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src);
#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
-/*! @} */
+#define TLVP_PRES_LEN(tp, tag, min_len) \
+ (TLVP_PRESENT(tp, tag) && TLVP_LEN(tp, tag) >= min_len)
+
+/*! \brief Align given TLV element with 16 bit value to an even address
+ * \param[in] tp pointer to \ref tlv_parsed
+ * \param[in] pos element to return
+ * \returns aligned 16 bit value
+ */
+static inline uint16_t tlvp_val16_unal(const struct tlv_parsed *tp, int pos)
+{
+ uint16_t res;
+ memcpy(&res, TLVP_VAL(tp, pos), sizeof(res));
+ return res;
+}
+
+/*! \brief Align given TLV element with 32 bit value to an address that is a multiple of 4
+ * \param[in] tp pointer to \ref tlv_parsed
+ * \param[in] pos element to return
+ * \returns aligned 32 bit value
+ */
+static inline uint32_t tlvp_val32_unal(const struct tlv_parsed *tp, int pos)
+{
+ uint32_t res;
+ memcpy(&res, TLVP_VAL(tp, pos), sizeof(res));
+ return res;
+}
+
+struct tlv_parsed *osmo_tlvp_copy(const struct tlv_parsed *tp_orig, void *ctx);
+int osmo_tlvp_merge(struct tlv_parsed *dst, const struct tlv_parsed *src);
+int osmo_shift_v_fixed(uint8_t **data, size_t *data_len,
+ size_t len, uint8_t **value);
+int osmo_match_shift_tv_fixed(uint8_t **data, size_t *data_len,
+ uint8_t tag, size_t len, uint8_t **value);
+int osmo_shift_tlv(uint8_t **data, size_t *data_len,
+ uint8_t *tag, uint8_t **value, size_t *value_len);
+int osmo_match_shift_tlv(uint8_t **data, size_t *data_len,
+ uint8_t tag, uint8_t **value, size_t *value_len);
+int osmo_shift_lv(uint8_t **data, size_t *data_len,
+ uint8_t **value, size_t *value_len);
-#endif /* _TLV_H */
+/*! @} */
diff --git a/src/shared/libosmocore/include/osmocom/sim/class_tables.h b/src/shared/libosmocore/include/osmocom/sim/class_tables.h
new file mode 100644
index 00000000..ad89d949
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/sim/class_tables.h
@@ -0,0 +1,42 @@
+#pragma once
+
+/* simtrace - tables determining APDU case for card emulation
+ *
+ * (C) 2016 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, or
+ * any later version as published by the Free Software Foundation.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdint.h>
+
+struct osim_cla_ins_case {
+ uint8_t cla;
+ uint8_t cla_mask;
+ int (*helper)(const struct osim_cla_ins_case *cic, const uint8_t *hdr);
+ const uint8_t *ins_tbl;
+};
+
+struct osim_cla_ins_card_profile {
+ const char *name;
+ const char *description;
+ const struct osim_cla_ins_case *cic_arr;
+ unsigned int cic_arr_size;
+};
+
+extern const struct osim_cla_ins_card_profile osim_iso7816_cic_profile;
+extern const struct osim_cla_ins_card_profile osim_uicc_cic_profile;
+extern const struct osim_cla_ins_card_profile osim_uicc_sim_cic_profile;
+
+int osim_determine_apdu_case(const struct osim_cla_ins_card_profile *prof,
+ const uint8_t *hdr);
diff --git a/src/shared/libosmocore/include/osmocom/sim/sim.h b/src/shared/libosmocore/include/osmocom/sim/sim.h
new file mode 100644
index 00000000..ba6fb707
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/sim/sim.h
@@ -0,0 +1,382 @@
+#ifndef _OSMOCOM_SIM_H
+#define _OSMOCOM_SIM_H
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/linuxlist.h>
+
+#define APDU_HDR_LEN 5
+
+/*!
+ * \file sim.h
+ * \brief Routines for helping with SIM (ISO/IEC 7816-4 more generally) communication.
+ */
+
+/*! \brief command-response pairs cases
+ *
+ * Enumeration used to identify the APDU structure based on command-response pair case , as specified in ISO/IEC 7816-3:2006(E) §12.1.
+ */
+enum osim_apdu_case {
+ APDU_CASE_1, /*!< command header, no command data field, no response data field */
+ APDU_CASE_2S, /*!< command header, no command data field, response data field (short) */
+ APDU_CASE_2E, /*!< command header, no command data field, response data field (extended) */
+ APDU_CASE_3S, /*!< command header, command data field (short), no response data field */
+ APDU_CASE_3E, /*!< command header, command data field (extended), no response data field */
+ APDU_CASE_4S, /*!< command header, command data field (short), response data field (short) */
+ APDU_CASE_4E /*!< command header, command data field (extended), response data field (extended) */
+};
+
+/*! \brief APDU/TPDU command header
+ *
+ * This structure encode an APDU/TPDU command header, as specified in ISO/IEC 7816-3:2006(E) §12.2 and §12.3.
+ * The APDU (application layer) can be encoded as different TPDUs (transport layer), depending on the transport protocol used.
+ * The TPDU encoding by T=1 of the APDU command header is identical to the APDU.
+ * The TPDU encoding by T=0 of the APDU command header adds a Parameter 3 field, generally used instead of Lc/Le.
+ *
+ * @todo have different structures for APDU, TPDU by T=0, and TPDU by T=1.
+ */
+struct osim_apdu_cmd_hdr {
+ uint8_t cla; /*!< CLASS byte */
+ uint8_t ins; /*!< INSTRUCTION byte */
+ uint8_t p1; /*!< Parameter 1 byte */
+ uint8_t p2; /*!< Parameter 2 byte */
+ uint8_t p3; /*!< Parameter 3 byte, used for TPDU by T=0 */
+} __attribute__ ((packed));
+
+#define msgb_apdu_dr(__x)
+
+/*! \brief APDU command body
+ *
+ * This structure encode a command body, as specified in ISO/IEC 7816-3:2006(E) §12.1.
+ * The data and response contents should be provided along with this structure.
+ */
+struct osim_msgb_cb {
+ enum osim_apdu_case apduc; /*!< command-response pair case, defining the encoding of Lc and Le */
+ uint16_t lc; /*!< number of bytes in the command data field Nc, which will encoded in 0, 1 or 3 bytes into Lc, depending on the case */
+ uint16_t le; /*!< maximum number of bytes expected in the response data field, which will encoded in 0, 1, 2 or 3 bytes into Le, depending on the case */
+ uint16_t sw; /*!< status word, composed of SW1 and SW2 bytes */
+} __attribute__((__may_alias__));
+#define OSIM_MSGB_CB(__msgb) ((struct osim_msgb_cb *)&((__msgb)->cb[0]))
+/*! \brief status word from msgb->cb */
+#define msgb_apdu_case(__x) OSIM_MSGB_CB(__x)->apduc
+#define msgb_apdu_lc(__x) OSIM_MSGB_CB(__x)->lc
+#define msgb_apdu_le(__x) OSIM_MSGB_CB(__x)->le
+#define msgb_apdu_sw(__x) OSIM_MSGB_CB(__x)->sw
+/*! \brief pointer to the command header of the APDU */
+#define msgb_apdu_h(__x) ((struct osim_apdu_cmd_hdr *)(__x)->l2h)
+
+#define msgb_apdu_dc(__x) ((__x)->l2h + sizeof(struct osim_apdu_cmd_hdr))
+#define msgb_apdu_de(__x) ((__x)->l2h + sizeof(struct osim_apdu_cmd_hdr) + msgb_apdu_lc(__x))
+
+/* FILES */
+
+struct osim_file;
+struct osim_file_desc;
+struct osim_decoded_data;
+
+/*! \brief Operations for a given File */
+struct osim_file_ops {
+ /*! Parse binary file data into osim_decoded_data */
+ int (*parse)(struct osim_decoded_data *dd,
+ const struct osim_file_desc *desc,
+ int len, uint8_t *data);
+ /*! Encode osim_decoded_data into binary file */
+ struct msgb * (*encode)(const struct osim_file_desc *desc,
+ const struct osim_decoded_data *decoded);
+};
+
+enum osim_element_type {
+ ELEM_T_NONE,
+ ELEM_T_BOOL, /*!< a boolean flag */
+ ELEM_T_UINT8, /*!< unsigned integer */
+ ELEM_T_UINT16, /*!< unsigned integer */
+ ELEM_T_UINT32, /*!< unsigned integer */
+ ELEM_T_STRING, /*!< generic string */
+ ELEM_T_BCD, /*!< BCD encoded digits */
+ ELEM_T_BYTES, /*!< BCD encoded digits */
+ ELEM_T_GROUP, /*!< group container, has siblings */
+};
+
+enum osim_element_repr {
+ ELEM_REPR_NONE,
+ ELEM_REPR_DEC,
+ ELEM_REPR_HEX,
+};
+
+/*! \brief A single decoded element inside a file */
+struct osim_decoded_element {
+ struct llist_head list;
+
+ enum osim_element_type type;
+ enum osim_element_repr representation;
+ const char *name;
+
+ unsigned int length;
+ union {
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ uint8_t *buf;
+ /*! A list of sibling decoded_items */
+ struct llist_head siblings;
+ } u;
+};
+
+/*! Decoded data for a single file, consisting of all decoded elements */
+struct osim_decoded_data {
+ /*! file to which we belong */
+ const struct osim_file *file;
+ /*! list of 'struct decoded_element' */
+ struct llist_head decoded_elements;
+};
+
+
+enum osim_file_type {
+ TYPE_NONE,
+ TYPE_DF, /*!< Dedicated File */
+ TYPE_ADF, /*!< Application Dedicated File */
+ TYPE_EF, /*!< Entry File */
+ TYPE_EF_INT, /*!< Internal Entry File */
+};
+
+enum osim_ef_type {
+ EF_TYPE_TRANSP, /*!< Transparent EF */
+ EF_TYPE_RECORD_FIXED, /*!< Fixed-Size Record EF */
+ EF_TYPE_RECORD_CYCLIC, /*!< Cyclic Record EF */
+ EF_TYPE_KEY, /*!< Key file as used in TETRA */
+};
+
+#define F_OPTIONAL 0x0001
+
+#define SFI_NONE 0xFF
+
+struct osim_file_desc {
+ struct llist_head list; /*!< local element in list */
+ struct llist_head child_list; /*!< list of children EF in DF */
+ struct osim_file_desc *parent; /*!< parent DF */
+
+ enum osim_file_type type; /*!< Type of the file (EF, DF, ...) */
+ enum osim_ef_type ef_type; /*!< Type of the EF, if type == TYPE_EF */
+
+ uint16_t fid; /*!< File Identifier */
+ uint8_t sfid; /*!< Short File IDentifier */
+ const uint8_t *df_name;
+ uint8_t df_name_len;
+
+ const char *short_name; /*!< Short Name (like EF.ICCID) */
+ const char *long_name; /*!< Long / description */
+ unsigned int flags;
+
+ struct osim_file_ops ops; /*!< Operations (parse/encode */
+
+ struct {
+ size_t min; /*!< Minimum size of the file
+ (transparent) or record in
+ cyclic / linear file */
+ size_t rec; /*!< Recommended size */
+ } size;
+};
+
+/*! \brief A single instance of a file: Descriptor and contents */
+struct osim_file {
+ /*! Descriptor for the file */
+ const struct osim_file_desc *desc;
+
+ /*! Encoded file contents */
+ struct msgb *encoded_data;
+ /*! Parsed/Decoded file contents */
+ struct osim_decoded_data *decoded_data;
+};
+
+/*! Convenience macros for defining EF */
+#define EF(pfid, sfi, pns, pflags, pnl, ptype, smin, srec, pdec, penc) \
+ { \
+ .fid = pfid, \
+ .sfid = sfi, \
+ .type = TYPE_EF, \
+ .ef_type = ptype, \
+ .short_name = pns, \
+ .long_name = pnl, \
+ .flags = pflags, \
+ .ops = { .encode = penc, .parse = pdec }, \
+ .size = { .min = smin, .rec = srec}, \
+ }
+
+
+/*! Convenience macros for defining EF */
+#define EF_TRANSP(fid, sfi, ns, flags, smin, srec, nl, dec, enc) \
+ EF(fid, sfi, ns, flags, nl, EF_TYPE_TRANSP, \
+ smin, srec, dec, enc)
+/*! Convenience macros for defining EF */
+#define EF_TRANSP_N(fid, sfi, ns, flags, smin, srec, nl) \
+ EF_TRANSP(fid, sfi, ns, flags, smin, srec, \
+ nl, &default_decode, NULL)
+
+/*! Convenience macros for defining EF */
+#define EF_CYCLIC(fid, sfi, ns, flags, smin, srec, nl, dec, enc) \
+ EF(fid, sfi, ns, flags, nl, EF_TYPE_RECORD_CYCLIC, \
+ smin, srec, dec, enc)
+/*! Convenience macros for defining EF */
+#define EF_CYCLIC_N(fid, sfi, ns, flags, smin, srec, nl) \
+ EF_CYCLIC(fid, sfi, ns, flags, smin, srec, nl, \
+ &default_decode, NULL)
+
+/*! Convenience macros for defining EF */
+#define EF_LIN_FIX(fid, sfi, ns, flags, smin, srec, nl, dec, enc) \
+ EF(fid, sfi, ns, flags, nl, EF_TYPE_RECORD_FIXED, \
+ smin, srec, dec, enc)
+/*! Convenience macros for defining EF */
+#define EF_LIN_FIX_N(fid, sfi, ns, flags, smin, srec, nl) \
+ EF_LIN_FIX(fid, sfi, ns, flags, smin, srec, nl, \
+ &default_decode, NULL)
+
+/*! Convenience macros for defining EF */
+#define EF_KEY(fid, sfi, ns, flags, smin, srec, nl, dec, enc) \
+ EF(fid, sfi, ns, flags, nl, EF_TYPE_KEY, \
+ smin, srec, dec, enc)
+/*! Convenience macros for defining EF */
+#define EF_KEY_N(fid, sfi, ns, flags, smin, srec, nl) \
+ EF_KEY(fid, sfi, ns, flags, smin, srec, nl, \
+ &default_decode, NULL)
+
+
+struct osim_file_desc *
+osim_file_desc_find_name(struct osim_file_desc *parent, const char *name);
+
+struct osim_file_desc *
+osim_file_desc_find_fid(struct osim_file_desc *parent, uint16_t fid);
+
+struct osim_file_desc *
+osim_file_desc_find_sfid(struct osim_file_desc *parent, uint8_t sfid);
+
+/* STATUS WORDS */
+
+enum osim_card_sw_type {
+ SW_TYPE_NONE,
+ SW_TYPE_STR,
+};
+
+enum osim_card_sw_class {
+ SW_CLS_NONE,
+ SW_CLS_OK,
+ SW_CLS_POSTP,
+ SW_CLS_WARN,
+ SW_CLS_ERROR,
+};
+
+/*! A card status word (SW) */
+struct osim_card_sw {
+ /*! status word code (2 bytes) */
+ uint16_t code;
+ /*! status word mask (2 bytes), to match range/prefix of SW */
+ uint16_t mask;
+ enum osim_card_sw_type type;
+ enum osim_card_sw_class class;
+ union {
+ /*! Human-readable meaning of SW */
+ const char *str;
+ } u;
+};
+
+#define OSIM_CARD_SW_LAST (const struct osim_card_sw) { \
+ .code = 0, .mask = 0, .type = SW_TYPE_NONE, \
+ .class = SW_CLS_NONE, .u.str = NULL \
+}
+
+/*! \brief A card profile (e.g. SIM card */
+struct osim_card_profile {
+ const char *name;
+ /*! Descriptor for the MF (root directory */
+ struct osim_file_desc *mf;
+ /*! Array of pointers to status words */
+ const struct osim_card_sw **sws;
+};
+
+const struct osim_card_sw *osim_find_sw(const struct osim_card_profile *cp,
+ uint16_t sw);
+enum osim_card_sw_class osim_sw_class(const struct osim_card_profile *cp,
+ uint16_t sw_in);
+
+struct osim_card_hdl;
+char *osim_print_sw(const struct osim_card_hdl *ch, uint16_t sw_in);
+
+extern const struct tlv_definition ts102221_fcp_tlv_def;
+extern const struct value_string ts102221_fcp_vals[14];
+
+/* 11.1.1.3 */
+enum ts102221_fcp_tag {
+ UICC_FCP_T_FCP = 0x62,
+ UICC_FCP_T_FILE_SIZE = 0x80,
+ UICC_FCP_T_TOT_F_SIZE = 0x81,
+ UICC_FCP_T_FILE_DESC = 0x82,
+ UICC_FCP_T_FILE_ID = 0x83,
+ UICC_FCP_T_DF_NAME = 0x84,
+ UICC_FCP_T_SFID = 0x88,
+ UICC_FCP_T_LIFEC_STS = 0x8A,
+ UICC_FCP_T_SEC_ATTR_REFEXP= 0x8B,
+ UICC_FCP_T_SEC_ATTR_COMP= 0x8C,
+ UICC_FCP_T_PROPRIETARY = 0xA5,
+ UICC_FCP_T_SEC_ATTR_EXP = 0xAB,
+ UICC_FCP_T_PIN_STS_DO = 0xC6,
+};
+
+struct msgb *osim_new_apdumsg(uint8_t cla, uint8_t ins, uint8_t p1,
+ uint8_t p2, uint16_t lc, uint16_t le);
+
+/* CARD READERS */
+
+enum osim_proto {
+ OSIM_PROTO_T0 = 0,
+ OSIM_PROTO_T1 = 1,
+};
+
+enum osim_reader_driver {
+ OSIM_READER_DRV_PCSC = 0,
+ OSIM_READER_DRV_OPENCT = 1,
+ OSIM_READER_DRV_SERIAL = 2,
+};
+
+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 (*transceive)(struct osim_reader_hdl *rh, struct msgb *msg);
+};
+
+struct osim_reader_hdl {
+ /*! \brief member in global list of readers */
+ struct llist_head list;
+ const struct osim_reader_ops *ops;
+ uint32_t proto_supported;
+ void *priv;
+ /*! \brief current card, if any */
+ struct osim_card_hdl *card;
+};
+
+struct osim_card_hdl {
+ /*! \brief member in global list of cards */
+ struct llist_head list;
+ /*! \brief reader through which card is accessed */
+ struct osim_reader_hdl *reader;
+ /*! \brief card profile */
+ struct osim_card_profile *prof;
+ /*! \brief card protocol */
+ enum osim_proto proto;
+
+ /*! \brief list of channels for this card */
+ struct llist_head channels;
+};
+
+struct osim_chan_hdl {
+ /*! \brief linked to card->channels */
+ struct llist_head list;
+ /*! \brief card to which this channel belongs */
+ struct osim_card_hdl *card;
+ const struct osim_file_desc *cwd;
+};
+
+/* reader.c */
+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 */
diff --git a/src/shared/libosmocore/include/osmocom/vty/buffer.h b/src/shared/libosmocore/include/osmocom/vty/buffer.h
index c9467a91..56c28f04 100644
--- a/src/shared/libosmocore/include/osmocom/vty/buffer.h
+++ b/src/shared/libosmocore/include/osmocom/vty/buffer.h
@@ -16,12 +16,11 @@
*
* 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., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
*/
-#ifndef _ZEBRA_BUFFER_H
-#define _ZEBRA_BUFFER_H
+#pragma once
#include <sys/types.h>
@@ -40,7 +39,7 @@ void buffer_free(struct buffer *);
/* Add the given data to the end of the buffer. */
extern void buffer_put(struct buffer *, const void *, size_t);
/* Add a single character to the end of the buffer. */
-extern void buffer_putc(struct buffer *, u_char);
+extern void buffer_putc(struct buffer *, unsigned char);
/* Add a NUL-terminated string to the end of the buffer. */
extern void buffer_putstr(struct buffer *, const char *);
@@ -98,5 +97,3 @@ extern buffer_status_t buffer_flush_all(struct buffer *, int fd);
*/
extern buffer_status_t buffer_flush_window(struct buffer *, int fd, int width,
int height, int erase, int no_more);
-
-#endif /* _ZEBRA_BUFFER_H */
diff --git a/src/shared/libosmocore/include/osmocom/vty/command.h b/src/shared/libosmocore/include/osmocom/vty/command.h
index 8fbb4824..135ef2fe 100644
--- a/src/shared/libosmocore/include/osmocom/vty/command.h
+++ b/src/shared/libosmocore/include/osmocom/vty/command.h
@@ -16,12 +16,11 @@
*
* 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., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
*/
-#ifndef _ZEBRA_COMMAND_H
-#define _ZEBRA_COMMAND_H
+#pragma once
#include <stdio.h>
#include <sys/types.h>
@@ -76,6 +75,7 @@ enum node_type {
SERVICE_NODE, /*!< \brief Service node. */
DEBUG_NODE, /*!< \brief Debug node. */
CFG_LOG_NODE, /*!< \brief Configure the logging */
+ CFG_STATS_NODE, /*!< \brief Configure the statistics */
VTY_NODE, /*!< \brief Vty node. */
@@ -83,6 +83,15 @@ enum node_type {
L_IPA_NODE, /*!< \brief IPA proxying commands in libosmo-abis. */
L_NS_NODE, /*!< \brief NS node in libosmo-gb. */
L_BSSGP_NODE, /*!< \brief BSSGP node in libosmo-gb. */
+ L_CTRL_NODE, /*!< \brief Control interface node. */
+
+ /*
+ * When adding new nodes to the libosmocore project, these nodes can be
+ * used to avoid ABI changes for unrelated projects.
+ */
+ RESERVED1_NODE, /*!< \brief Reserved for later extensions */
+ RESERVED2_NODE, /*!< \brief Reserved for later extensions */
+ RESERVED3_NODE, /*!< \brief Reserved for later extensions */
_LAST_OSMOVTY_NODE
};
@@ -93,7 +102,7 @@ enum node_type {
* configuration function pointer . */
struct cmd_node {
/*! \brief Node index */
- enum node_type node;
+ int node;
/*! \brief Prompt character at vty interface. */
const char *prompt;
@@ -123,7 +132,7 @@ struct cmd_element {
unsigned int cmdsize; /*!< \brief Command index count. */
char *config; /*!< \brief Configuration string */
vector subconfig; /*!< \brief Sub configuration string */
- u_char attr; /*!< \brief Command attributes */
+ unsigned char attr; /*!< \brief Command attributes */
};
/*! \brief Command description structure. */
@@ -335,11 +344,16 @@ struct desc {
/* Prototypes. */
void install_node(struct cmd_node *, int (*)(struct vty *));
-void install_default(enum node_type);
-void install_element(enum node_type, struct cmd_element *);
+void install_default(int node_type);
+void install_element(int node_type, struct cmd_element *);
void install_element_ve(struct cmd_element *cmd);
void sort_node(void);
+/* This is similar to install_default() but it also creates
+ * 'exit' and 'end' commands.
+ */
+void vty_install_default(int node_type);
+
/* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated
string with a space between each element (allocated using
XMALLOC(MTYPE_TMP)). Returns NULL if shift >= argc. */
@@ -365,10 +379,11 @@ extern struct cmd_element config_end_cmd;
char *host_config_file();
void host_config_set(const char *);
+char *osmo_asciidoc_escape(const char *inp);
+
/* This is called from main when a daemon is invoked with -v or --version. */
void print_version(int print_copyright);
extern void *tall_vty_cmd_ctx;
/*! @} */
-#endif /* _ZEBRA_COMMAND_H */
diff --git a/src/shared/libosmocore/include/osmocom/vty/logging.h b/src/shared/libosmocore/include/osmocom/vty/logging.h
index e0011bf9..9e4b98f9 100644
--- a/src/shared/libosmocore/include/osmocom/vty/logging.h
+++ b/src/shared/libosmocore/include/osmocom/vty/logging.h
@@ -1,5 +1,4 @@
-#ifndef _VTY_LOGGING_H
-#define _VTY_LOGGING_H
+#pragma once
#define LOGGING_STR "Configure log message to this terminal\n"
#define FILTER_STR "Filter log messages\n"
@@ -8,5 +7,3 @@ struct log_info;
void logging_vty_add_cmds(const struct log_info *cat);
struct vty;
struct log_target *osmo_log_vty2tgt(struct vty *vty);
-
-#endif /* _VTY_LOGGING_H */
diff --git a/src/shared/libosmocore/include/osmocom/vty/misc.h b/src/shared/libosmocore/include/osmocom/vty/misc.h
index db4f4a77..b3fb644d 100644
--- a/src/shared/libosmocore/include/osmocom/vty/misc.h
+++ b/src/shared/libosmocore/include/osmocom/vty/misc.h
@@ -1,8 +1,8 @@
-#ifndef OSMO_VTY_MISC_H
-#define OSMO_VTY_MISC_H
+#pragma once
#include <osmocom/vty/vty.h>
#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/stat_item.h>
#include <osmocom/core/utils.h>
#define VTY_DO_LOWER 1
@@ -11,9 +11,22 @@ char *vty_cmd_string_from_valstr(void *ctx, const struct value_string *vals,
const char *end, int do_lower);
void vty_out_rate_ctr_group(struct vty *vty, const char *prefix,
- struct rate_ctr_group *ctrg);
+ struct rate_ctr_group *ctrg);
+
+void vty_out_stat_item_group(struct vty *vty, const char *prefix,
+ struct osmo_stat_item_group *statg);
+
+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);
+
+
+struct osmo_fsm;
+struct osmo_fsm_inst;
+void vty_out_fsm(struct vty *vty, struct osmo_fsm *fsm);
+void vty_out_fsm_inst(struct vty *vty, struct osmo_fsm_inst *fsmi);
+void osmo_fsm_vty_add_cmds(void);
+
int osmo_vty_write_config_file(const char *filename);
int osmo_vty_save_config_file(void);
-
-#endif
diff --git a/src/shared/libosmocore/include/osmocom/vty/ports.h b/src/shared/libosmocore/include/osmocom/vty/ports.h
new file mode 100644
index 00000000..c5e396b4
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/vty/ports.h
@@ -0,0 +1,26 @@
+#pragma once
+
+/*
+ * TCP port numbers used for VTY interfaces in osmocom projects. See also the
+ * osmocom wiki as well as the osmo-gsm-manuals, which should all be kept in
+ * sync with this file:
+ * https://osmocom.org/projects/cellular-infrastructure/wiki/PortNumbers
+ * https://git.osmocom.org/osmo-gsm-manuals/tree/common/chapters/port_numbers.adoc
+ */
+
+/* 4238 used by osmo-bts control interface */
+#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_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_CSCN 4254
+/* 4255 used by control interface */
+#define OSMO_VTY_PORT_MNCC_SIP 4256
+/* 4257 used by control interface */
diff --git a/src/shared/libosmocore/include/osmocom/vty/stats.h b/src/shared/libosmocore/include/osmocom/vty/stats.h
new file mode 100644
index 00000000..3851b4df
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/vty/stats.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void osmo_stats_vty_add_cmds();
diff --git a/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h b/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h
index 3c222014..e939ec71 100644
--- a/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h
+++ b/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h
@@ -18,8 +18,7 @@
*
*/
-#ifndef TELNET_INTERFACE_H
-#define TELNET_INTERFACE_H
+#pragma once
#include <osmocom/core/logging.h>
#include <osmocom/core/select.h>
@@ -52,5 +51,3 @@ int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port);
void telnet_exit(void);
/*! @} */
-
-#endif /* TELNET_INTERFACE_H */
diff --git a/src/shared/libosmocore/include/osmocom/vty/vector.h b/src/shared/libosmocore/include/osmocom/vty/vector.h
index 22a184d6..c00804de 100644
--- a/src/shared/libosmocore/include/osmocom/vty/vector.h
+++ b/src/shared/libosmocore/include/osmocom/vty/vector.h
@@ -16,12 +16,11 @@
*
* 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., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
*/
-#ifndef _ZEBRA_VECTOR_H
-#define _ZEBRA_VECTOR_H
+#pragma once
/* struct for vector */
struct _vector {
@@ -60,5 +59,3 @@ void *vector_lookup(vector, unsigned int);
void *vector_lookup_ensure(vector, unsigned int);
extern void *tall_vty_vec_ctx;
-
-#endif /* _ZEBRA_VECTOR_H */
diff --git a/src/shared/libosmocore/include/osmocom/vty/vty.h b/src/shared/libosmocore/include/osmocom/vty/vty.h
index e656abf6..43cb0cfe 100644
--- a/src/shared/libosmocore/include/osmocom/vty/vty.h
+++ b/src/shared/libosmocore/include/osmocom/vty/vty.h
@@ -1,5 +1,4 @@
-#ifndef _VTY_H
-#define _VTY_H
+#pragma once
#include <stdio.h>
#include <stdarg.h>
@@ -157,9 +156,11 @@ struct vty_app_info {
/*! \brief \ref talloc context */
void *tall_ctx;
/*! \brief call-back for returning to parent n ode */
- enum node_type (*go_parent_cb)(struct vty *vty);
+ int (*go_parent_cb)(struct vty *vty);
/*! \brief call-back to determine if node is config node */
int (*is_config_node)(struct vty *vty, int node);
+ /*! \brief Check if the config is consistent before write */
+ int (*config_is_consistent)(struct vty *vty);
};
/* Prototypes. */
@@ -183,13 +184,28 @@ int vty_shell_serv (struct vty *);
void vty_hello (struct vty *);
void *vty_current_index(struct vty *);
int vty_current_node(struct vty *vty);
-enum node_type vty_go_parent(struct vty *vty);
+int vty_go_parent(struct vty *vty);
+
+/* Return IP address passed to the 'line vty'/'bind' command, or "127.0.0.1" */
+const char *vty_get_bind_addr(void);
extern void *tall_vty_ctx;
extern struct cmd_element cfg_description_cmd;
extern struct cmd_element cfg_no_description_cmd;
-/*! @} */
-#endif
+/**
+ * signal handling
+ */
+enum signal_vty {
+ S_VTY_EVENT,
+};
+
+struct vty_signal_data {
+ enum event event;
+ int sock;
+ struct vty *vty;
+};
+
+/*! @} */
diff --git a/src/shared/libosmocore/libosmocore.pc.in b/src/shared/libosmocore/libosmocore.pc.in
index 7c298693..d355659b 100644
--- a/src/shared/libosmocore/libosmocore.pc.in
+++ b/src/shared/libosmocore/libosmocore.pc.in
@@ -6,6 +6,6 @@ includedir=@includedir@
Name: Osmocom Core Library
Description: C Utility Library
Version: @VERSION@
-Libs: -L${libdir} -losmocore
-Cflags: -I${includedir}/
+Libs: -L${libdir} @TALLOC_LIBS@ -losmocore
+Cflags: -I${includedir}/ @TALLOC_CFLAGS@
diff --git a/src/shared/libosmocore/libosmoctrl.pc.in b/src/shared/libosmocore/libosmoctrl.pc.in
new file mode 100644
index 00000000..4676b31d
--- /dev/null
+++ b/src/shared/libosmocore/libosmoctrl.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Osmocom Control Interface Library
+Description: C Utility Library
+Version: @VERSION@
+Libs: -L${libdir} @TALLOC_LIBS@ -losmoctrl -losmogsm -losmocore
+Cflags: -I${includedir}/
+
diff --git a/src/shared/libosmocore/libosmogb.pc.in b/src/shared/libosmocore/libosmogb.pc.in
index 33cacaf6..a163cc42 100644
--- a/src/shared/libosmocore/libosmogb.pc.in
+++ b/src/shared/libosmocore/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} -losmogb -losmovty
+Libs: -L${libdir} @TALLOC_LIBS@ -losmogb -losmovty -losmocore
Cflags: -I${includedir}/ -fno-strict-aliasing
diff --git a/src/shared/libosmocore/libosmogsm.pc.in b/src/shared/libosmocore/libosmogsm.pc.in
index 753bb3a1..0160be8d 100644
--- a/src/shared/libosmocore/libosmogsm.pc.in
+++ b/src/shared/libosmocore/libosmogsm.pc.in
@@ -6,6 +6,6 @@ includedir=@includedir@
Name: Osmocom GSM Core Library
Description: GSM Core Library
Version: @VERSION@
-Libs: -L${libdir} -losmogsm
+Libs: -L${libdir} @TALLOC_LIBS@ -losmogsm -losmocore
Cflags: -I${includedir}/
diff --git a/src/shared/libosmocore/libosmosim.pc.in b/src/shared/libosmocore/libosmosim.pc.in
new file mode 100644
index 00000000..83777c31
--- /dev/null
+++ b/src/shared/libosmocore/libosmosim.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Osmocom SIM card related utilities Library
+Description: C Utility Library
+Version: @VERSION@
+Libs: -L${libdir} @TALLOC_LIBS@ -losmosim -losmocore
+Cflags: -I${includedir}/
+
diff --git a/src/shared/libosmocore/libosmovty.pc.in b/src/shared/libosmocore/libosmovty.pc.in
index 2cc0b5f8..6204e455 100644
--- a/src/shared/libosmocore/libosmovty.pc.in
+++ b/src/shared/libosmocore/libosmovty.pc.in
@@ -6,6 +6,6 @@ includedir=@includedir@
Name: Osmocom VTY Interface Library
Description: C Utility Library
Version: @VERSION@
-Libs: -L${libdir} -losmovty
+Libs: -L${libdir} @TALLOC_LIBS@ -losmovty -losmocore
Cflags: -I${includedir}/
diff --git a/src/shared/libosmocore/src/Makefile.am b/src/shared/libosmocore/src/Makefile.am
index d719df7c..0cf2665c 100644
--- a/src/shared/libosmocore/src/Makefile.am
+++ b/src/shared/libosmocore/src/Makefile.am
@@ -1,33 +1,30 @@
# 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=4:0:0
+# 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=8:0:0
-INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include
-AM_CFLAGS = -Wall
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
+AM_CFLAGS = -Wall $(TALLOC_CFLAGS)
lib_LTLIBRARIES = libosmocore.la
-libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c bits.c \
- bitvec.c statistics.c \
+libosmocore_la_LIBADD = $(BACKTRACE_LIB) $(TALLOC_LIBS)
+libosmocore_la_SOURCES = timer.c timer_gettimeofday.c select.c signal.c msgb.c bits.c \
+ bitvec.c bitcomp.c statistics.c fsm.c \
write_queue.c utils.c socket.c \
- logging.c logging_syslog.c rate_ctr.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 \
- crc8gen.c crc16gen.c crc32gen.c crc64gen.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
BUILT_SOURCES = crc8gen.c crc16gen.c crc32gen.c crc64gen.c
if ENABLE_PLUGIN
libosmocore_la_SOURCES += plugin.c
-libosmocore_la_LDFLAGS = -version-info $(LIBVERSION) $(LIBRARY_DL)
+libosmocore_la_LDFLAGS = -version-info $(LIBVERSION) $(TALLOC_LIBS) $(LIBRARY_DL) -no-undefined
else
-libosmocore_la_LDFLAGS = -version-info $(LIBVERSION)
-endif
-
-if ENABLE_TALLOC
-libosmocore_la_SOURCES += talloc.c
-else
-libosmocore_la_LIBADD = -ltalloc
+libosmocore_la_LDFLAGS = -version-info $(LIBVERSION) $(TALLOC_LIBS) -no-undefined
endif
if ENABLE_MSGFILE
diff --git a/src/shared/libosmocore/src/application.c b/src/shared/libosmocore/src/application.c
index e0d989e5..8a325c8a 100644
--- a/src/shared/libosmocore/src/application.c
+++ b/src/shared/libosmocore/src/application.c
@@ -40,7 +40,7 @@
* a multi-threaded context, you have to add your own locking.
*
* \section sec_copyright Copyright and License
- * Copyright © 2008-2011 - Harald Welte, Holger Freyther and contributors\n
+ * Copyright © 2008-2016 - Harald Welte, Holger Freyther and contributors\n
* All rights reserved. \n\n
* The source code of libosmocore is licensed under the terms of the GNU
* General Public License as published by the Free Software Foundation;
@@ -72,14 +72,25 @@
struct log_target *osmo_stderr_target;
+static void sighup_hdlr(int signal)
+{
+ log_targets_reopen();
+}
+
/*! \brief Ignore \ref SIGPIPE, \ref SIGALRM, \ref SIGHUP and \ref SIGIO */
void osmo_init_ignore_signals(void)
{
/* Signals that by default would terminate */
+#ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN);
+#endif
signal(SIGALRM, SIG_IGN);
- signal(SIGHUP, SIG_IGN);
+#ifdef SIGHUP
+ signal(SIGHUP, &sighup_hdlr);
+#endif
+#ifdef SIGIO
signal(SIGIO, SIG_IGN);
+#endif
}
/*! \brief Initialize the osmocom logging framework
diff --git a/src/shared/libosmocore/src/backtrace.c b/src/shared/libosmocore/src/backtrace.c
index 5b93becb..6cd798c5 100644
--- a/src/shared/libosmocore/src/backtrace.c
+++ b/src/shared/libosmocore/src/backtrace.c
@@ -70,6 +70,8 @@ void osmo_generate_backtrace(void)
}
/*! \brief Generate and log a call back-trace
+ * \param[in] subsys Logging sub-system
+ * \param[in] level Logging level
*
* This function will generate a function call back-trace of the
* current process and log it to the specified subsystem and
diff --git a/src/shared/libosmocore/src/bitcomp.c b/src/shared/libosmocore/src/bitcomp.c
new file mode 100644
index 00000000..9c01246b
--- /dev/null
+++ b/src/shared/libosmocore/src/bitcomp.c
@@ -0,0 +1,354 @@
+/* bit compression routines */
+
+/* (C) 2016 sysmocom s.f.m.c. GmbH by Max Suraev <msuraev@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.
+ *
+ * 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
+ * @{
+ */
+
+/*! \file bitcomp.c
+ * \brief Osmocom bit compression routines
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+
+#include <osmocom/core/bitvec.h>
+#include <osmocom/core/bitcomp.h>
+
+/*
+ * Terminating codes for uninterrupted sequences of 0 and 1 up to 64 bit length
+ * according to TS 44.060 9.1.10
+ */
+static const unsigned t4_term[2][64] = {
+ {
+ 0b0000110111,
+ 0b10,
+ 0b11,
+ 0b010,
+ 0b011,
+ 0b0011,
+ 0b0010,
+ 0b00011,
+ 0b000101,
+ 0b000100,
+ 0b0000100,
+ 0b0000101,
+ 0b0000111,
+ 0b00000100,
+ 0b00000111,
+ 0b000011000,
+ 0b0000010111,
+ 0b0000011000,
+ 0b0000001000,
+ 0b00001100111,
+ 0b00001101000,
+ 0b00001101100,
+ 0b00000110111,
+ 0b00000101000,
+ 0b00000010111,
+ 0b00000011000,
+ 0b000011001010,
+ 0b000011001011,
+ 0b000011001100,
+ 0b000011001101,
+ 0b000001101000,
+ 0b000001101001,
+ 0b000001101010,
+ 0b000001101011,
+ 0b000011010010,
+ 0b000011010011,
+ 0b000011010100,
+ 0b000011010101,
+ 0b000011010110,
+ 0b000011010111,
+ 0b000001101100,
+ 0b000001101101,
+ 0b000011011010,
+ 0b000011011011,
+ 0b000001010100,
+ 0b000001010101,
+ 0b000001010110,
+ 0b000001010111,
+ 0b000001100100,
+ 0b000001100101,
+ 0b000001010010,
+ 0b000001010011,
+ 0b000000100100,
+ 0b000000110111,
+ 0b000000111000,
+ 0b000000100111,
+ 0b000000101000,
+ 0b000001011000,
+ 0b000001011001,
+ 0b000000101011,
+ 0b000000101100,
+ 0b000001011010,
+ 0b000001100110,
+ 0b000001100111
+ },
+ {
+ 0b00110101,
+ 0b000111,
+ 0b0111,
+ 0b1000,
+ 0b1011,
+ 0b1100,
+ 0b1110,
+ 0b1111,
+ 0b10011,
+ 0b10100,
+ 0b00111,
+ 0b01000,
+ 0b001000,
+ 0b000011,
+ 0b110100,
+ 0b110101,
+ 0b101010,
+ 0b101011,
+ 0b0100111,
+ 0b0001100,
+ 0b0001000,
+ 0b0010111,
+ 0b0000011,
+ 0b0000100,
+ 0b0101000,
+ 0b0101011,
+ 0b0010011,
+ 0b0100100,
+ 0b0011000,
+ 0b00000010,
+ 0b00000011,
+ 0b00011010,
+ 0b00011011,
+ 0b00010010,
+ 0b00010011,
+ 0b00010100,
+ 0b00010101,
+ 0b00010110,
+ 0b00010111,
+ 0b00101000,
+ 0b00101001,
+ 0b00101010,
+ 0b00101011,
+ 0b00101100,
+ 0b00101101,
+ 0b00000100,
+ 0b00000101,
+ 0b00001010,
+ 0b00001011,
+ 0b01010010,
+ 0b01010011,
+ 0b01010100,
+ 0b01010101,
+ 0b00100100,
+ 0b00100101,
+ 0b01011000,
+ 0b01011001,
+ 0b01011010,
+ 0b01011011,
+ 0b01001010,
+ 0b01001011,
+ 0b00110010,
+ 0b00110011,
+ 0b00110100
+ }
+};
+
+static const unsigned t4_term_length[2][64] = {
+ {10, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8, 8, 9, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
+ {8, 6, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}
+};
+
+static const unsigned t4_make_up_length[2][15] = {
+ {10, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13},
+ {5, 5, 6, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9}
+};
+
+static const unsigned t4_make_up[2][15] = {
+ {
+ 0b0000001111,
+ 0b000011001000,
+ 0b000011001001,
+ 0b000001011011,
+ 0b000000110011,
+ 0b000000110100,
+ 0b000000110101,
+ 0b0000001101100,
+ 0b0000001101101,
+ 0b0000001001010,
+ 0b0000001001011,
+ 0b0000001001100,
+ 0b0000001001101,
+ 0b0000001110010,
+ 0b0000001110011
+ },
+ {
+ 0b11011,
+ 0b10010,
+ 0b010111,
+ 0b0110111,
+ 0b00110110,
+ 0b00110111,
+ 0b01100100,
+ 0b01100101,
+ 0b01101000,
+ 0b01100111,
+ 0b011001100,
+ 0b011001101,
+ 0b011010010,
+ 0b011010011,
+ 0b011010100
+ }
+};
+
+/*! \brief Make-up codes for a given length
+ *
+ * \return Return proper make-up code word for an uninterrupted
+ * sequence of b bits of length len according to modified ITU-T T.4
+ * from TS 44.060 Table 9.1.10.2 */
+static inline int t4_rle(struct bitvec *bv, unsigned len, bool b)
+{
+ if (len >= 960) {
+ bitvec_set_uint(bv, t4_make_up[b][14], t4_make_up_length[b][14]);
+ return bitvec_set_uint(bv, t4_term[b][len - 960], t4_term_length[b][len - 960]);
+ }
+
+ if (len >= 896) {
+ bitvec_set_uint(bv, t4_make_up[b][13], t4_make_up_length[b][13]);
+ return bitvec_set_uint(bv, t4_term[b][len - 896], t4_term_length[b][len - 896]);
+ }
+
+ if (len >= 832) {
+ bitvec_set_uint(bv, t4_make_up[b][12], t4_make_up_length[b][12]);
+ return bitvec_set_uint(bv, t4_term[b][len - 832], t4_term_length[b][len - 832]);
+ }
+
+ if (len >= 768) {
+ bitvec_set_uint(bv, t4_make_up[b][11], t4_make_up_length[b][11]);
+ return bitvec_set_uint(bv, t4_term[b][len - 768], t4_term_length[b][len - 768]);
+ }
+
+ if (len >= 704) {
+ bitvec_set_uint(bv, t4_make_up[b][10], t4_make_up_length[b][10]);
+ return bitvec_set_uint(bv, t4_term[b][len - 704], t4_term_length[b][len - 704]);
+ }
+
+ if (len >= 640) {
+ bitvec_set_uint(bv, t4_make_up[b][9], t4_make_up_length[b][9]);
+ return bitvec_set_uint(bv, t4_term[b][len - 640], t4_term_length[b][len - 640]);
+ }
+
+ if (len >= 576) {
+ bitvec_set_uint(bv, t4_make_up[b][8], t4_make_up_length[b][8]);
+ return bitvec_set_uint(bv, t4_term[b][len - 576], t4_term_length[b][len - 576]);
+ }
+
+ if (len >= 512) {
+ bitvec_set_uint(bv, t4_make_up[b][7], t4_make_up_length[b][7]);
+ return bitvec_set_uint(bv, t4_term[b][len - 512], t4_term_length[b][len - 512]);
+ }
+
+ if (len >= 448) {
+ bitvec_set_uint(bv, t4_make_up[b][6], t4_make_up_length[b][6]);
+ return bitvec_set_uint(bv, t4_term[b][len - 448], t4_term_length[b][len - 448]);
+ }
+
+ if (len >= 384) {
+ bitvec_set_uint(bv, t4_make_up[b][5], t4_make_up_length[b][5]);
+ return bitvec_set_uint(bv, t4_term[b][len - 384], t4_term_length[b][len - 384]);
+ }
+
+ if (len >= 320) {
+ bitvec_set_uint(bv, t4_make_up[b][4], t4_make_up_length[b][4]);
+ return bitvec_set_uint(bv, t4_term[b][len - 320], t4_term_length[b][len - 320]);
+ }
+
+ if (len >= 256) {
+ bitvec_set_uint(bv, t4_make_up[b][3], t4_make_up_length[b][3]);
+ return bitvec_set_uint(bv, t4_term[b][len - 256], t4_term_length[b][len - 256]);
+ }
+
+ if (len >= 192) {
+ bitvec_set_uint(bv, t4_make_up[b][2], t4_make_up_length[b][2]);
+ return bitvec_set_uint(bv, t4_term[b][len - 192], t4_term_length[b][len - 192]);
+ }
+
+ if (len >= 128) {
+ bitvec_set_uint(bv, t4_make_up[b][1], t4_make_up_length[b][1]);
+ return bitvec_set_uint(bv, t4_term[b][len - 128], t4_term_length[b][len - 128]);
+ }
+
+ if (len >= 64) {
+ bitvec_set_uint(bv, t4_make_up[b][0], t4_make_up_length[b][0]);
+ return bitvec_set_uint(bv, t4_term[b][len - 64], t4_term_length[b][len - 64]);
+ }
+
+ return bitvec_set_uint(bv, t4_term[b][len], t4_term_length[b][len]);
+}
+
+/*! \brief encode bit vector in-place using T4 encoding
+ * Assumes MSB first encoding.
+ * \param[in] bv bit vector to be encoded
+ * \return color code (if the encoding started with 0 or 1) or -1 on
+ * failure (encoded is bigger than original)
+ */
+int osmo_t4_encode(struct bitvec *bv)
+{
+ unsigned rl0 = bitvec_rl(bv, false), rl1 = bitvec_rl(bv, true);
+ int r = (rl0 > rl1) ? 0 : 1;
+ uint8_t orig[bv->data_len], tmp[bv->data_len * 2]; /* FIXME: better estimate max possible encoding overhead */
+ struct bitvec comp, vec;
+ comp.data = tmp;
+ comp.data_len = bv->data_len * 2;
+ bitvec_zero(&comp);
+ vec.data = orig;
+ vec.data_len = bv->data_len;
+ bitvec_zero(&vec);
+ memcpy(vec.data, bv->data, bv->data_len);
+ vec.cur_bit = bv->cur_bit;
+
+ while (vec.cur_bit > 0) {
+ if (rl0 > rl1) {
+ bitvec_shiftl(&vec, rl0);
+ t4_rle(&comp, rl0, false);
+ } else {
+ bitvec_shiftl(&vec, rl1);
+ t4_rle(&comp, rl1, true);
+ }
+ /*
+ TODO: implement backtracking for optimal encoding
+ printf(" -> [%d/%d]", comp.cur_bit + vec.cur_bit, bv->cur_bit);
+ */
+ rl0 = bitvec_rl(&vec, false);
+ rl1 = bitvec_rl(&vec, true);
+ }
+ if (comp.cur_bit < bv->cur_bit) {
+ memcpy(bv->data, tmp, bv->data_len);
+ bv->cur_bit = comp.cur_bit;
+ return r;
+ }
+ return -1;
+}
+
+
diff --git a/src/shared/libosmocore/src/bits.c b/src/shared/libosmocore/src/bits.c
index 4c67bddb..0c77b279 100644
--- a/src/shared/libosmocore/src/bits.c
+++ b/src/shared/libosmocore/src/bits.c
@@ -1,3 +1,24 @@
+/*
+ * (C) 2011 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2011 by Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
#include <stdint.h>
@@ -40,10 +61,83 @@ int osmo_ubit2pbit(pbit_t *out, const ubit_t *in, unsigned int num_bits)
return outptr - out;
}
+/*! \brief Shift unaligned input to octet-aligned output
+ * \param[out] out output buffer, unaligned
+ * \param[in] in input buffer, octet-aligned
+ * \param[in] num_nibbles number of nibbles
+ */
+void osmo_nibble_shift_right(uint8_t *out, const uint8_t *in,
+ unsigned int num_nibbles)
+{
+ unsigned int i, num_whole_bytes = num_nibbles / 2;
+ if (!num_whole_bytes)
+ return;
+
+ /* first byte: upper nibble empty, lower nibble from src */
+ out[0] = (in[0] >> 4);
+
+ /* bytes 1.. */
+ for (i = 1; i < num_whole_bytes; i++)
+ out[i] = ((in[i - 1] & 0xF) << 4) | (in[i] >> 4);
+
+ /* shift the last nibble, in case there's an odd count */
+ i = num_whole_bytes;
+ if (num_nibbles & 1)
+ out[i] = ((in[i - 1] & 0xF) << 4) | (in[i] >> 4);
+ else
+ out[i] = (in[i - 1] & 0xF) << 4;
+}
+
+/*! \brief Shift unaligned input to octet-aligned output
+ * \param[out] out output buffer, octet-aligned
+ * \param[in] in input buffer, unaligned
+ * \param[in] num_nibbles number of nibbles
+ */
+void osmo_nibble_shift_left_unal(uint8_t *out, const uint8_t *in,
+ unsigned int num_nibbles)
+{
+ unsigned int i, num_whole_bytes = num_nibbles / 2;
+ if (!num_whole_bytes)
+ return;
+
+ for (i = 0; i < num_whole_bytes; i++)
+ out[i] = ((in[i] & 0xF) << 4) | (in[i + 1] >> 4);
+
+ /* shift the last nibble, in case there's an odd count */
+ i = num_whole_bytes;
+ if (num_nibbles & 1)
+ out[i] = (in[i] & 0xF) << 4;
+}
+
+/*! \brief convert unpacked bits to soft bits
+ * \param[out] out output buffer of soft bits
+ * \param[in] in input buffer of unpacked bits
+ * \param[in] num_bits number of bits
+ */
+void osmo_ubit2sbit(sbit_t *out, const ubit_t *in, unsigned int num_bits)
+{
+ unsigned int i;
+ for (i = 0; i < num_bits; i++)
+ out[i] = in[i] ? -127 : 127;
+}
+
+/*! \brief convert soft bits to unpacked bits
+ * \param[out] out output buffer of unpacked bits
+ * \param[in] in input buffer of soft bits
+ * \param[in] num_bits number of bits
+ */
+void osmo_sbit2ubit(ubit_t *out, const sbit_t *in, unsigned int num_bits)
+{
+ unsigned int i;
+ for (i = 0; i < num_bits; i++)
+ out[i] = in[i] < 0;
+}
+
/*! \brief convert packed bits to unpacked bits, return length in bytes
* \param[out] out output buffer of unpacked bits
* \param[in] in input buffer of packed bits
* \param[in] num_bits number of bits
+ * \return number of bytes used in \ref out
*/
int osmo_pbit2ubit(ubit_t *out, const pbit_t *in, unsigned int num_bits)
{
@@ -128,7 +222,16 @@ int osmo_pbit2ubit_ext(ubit_t *out, unsigned int out_ofs,
return out_ofs + num_bits;
}
-/* generalized bit reversal function, Chapter 7 "Hackers Delight" */
+/*! \brief generalized bit reversal function
+ * \param[in] x the 32bit value to be reversed
+ * \param[in] k the type of reversal requested
+ * \returns the reversed 32bit dword
+ *
+ * This function reverses the bit order within a 32bit word. Depending
+ * on "k", it either reverses all bits in a 32bit dword, or the bytes in
+ * the dword, or the bits in each byte of a dword, or simply swaps the
+ * two 16bit words in a dword. See Chapter 7 "Hackers Delight"
+ */
uint32_t osmo_bit_reversal(uint32_t x, enum osmo_br_mode k)
{
if (k & 1) x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1;
@@ -140,7 +243,12 @@ uint32_t osmo_bit_reversal(uint32_t x, enum osmo_br_mode k)
return x;
}
-/* generalized bit reversal function, Chapter 7 "Hackers Delight" */
+/*! \brief reverse the bit-order in each byte of a dword
+ * \param[in] x 32bit input value
+ * \returns 32bit value where bits of each byte have been reversed
+ *
+ * See Chapter 7 "Hackers Delight"
+ */
uint32_t osmo_revbytebits_32(uint32_t x)
{
x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1;
@@ -150,6 +258,12 @@ uint32_t osmo_revbytebits_32(uint32_t x)
return x;
}
+/*! \brief reverse the bit order in a byte
+ * \param[in] x 8bit input value
+ * \returns 8bit value where bits order has been reversed
+ *
+ * See Chapter 7 "Hackers Delight"
+ */
uint32_t osmo_revbytebits_8(uint8_t x)
{
x = (x & 0x55) << 1 | (x & 0xAA) >> 1;
@@ -159,6 +273,12 @@ uint32_t osmo_revbytebits_8(uint8_t x)
return x;
}
+/*! \brief reverse bit-order of each byte in a buffer
+ * \param[in] buf buffer containing bytes to be bit-reversed
+ * \param[in] len length of buffer in bytes
+ *
+ * This function reverses the bits in each byte of the buffer
+ */
void osmo_revbytebits_buf(uint8_t *buf, int len)
{
unsigned int i;
@@ -173,9 +293,8 @@ void osmo_revbytebits_buf(uint8_t *buf, int len)
return;
}
- for (i = unaligned_cnt; i < len; i += 4) {
- uint32_t *cur = (uint32_t *) (buf + i);
- *cur = osmo_revbytebits_32(*cur);
+ for (i = unaligned_cnt; i + 3 < len; i += 4) {
+ osmo_store32be(osmo_revbytebits_32(osmo_load32be(buf + i)), buf + i);
len_remain -= 4;
}
diff --git a/src/shared/libosmocore/src/bitvec.c b/src/shared/libosmocore/src/bitvec.c
index 714c11b7..c895cffa 100644
--- a/src/shared/libosmocore/src/bitvec.c
+++ b/src/shared/libosmocore/src/bitvec.c
@@ -1,6 +1,8 @@
/* bit vector utility routines */
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2012 Ivan Klyuchnikov
+ * (C) 2015 by Sysmocom s.f.m.c. GmbH
*
* All Rights Reserved
*
@@ -30,7 +32,11 @@
#include <errno.h>
#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <osmocom/core/bits.h>
#include <osmocom/core/bitvec.h>
#define BITNUM_FROM_COMP(byte, bit) ((byte*8)+bit)
@@ -69,7 +75,7 @@ static uint8_t bitval2mask(enum bit_value bit, uint8_t bitnum)
/*! \brief check if the bit is 0 or 1 for a given position inside a bitvec
* \param[in] bv the bit vector on which to check
* \param[in] bitnr the bit number inside the bit vector to check
- * \returns
+ * \return value of the requested bit
*/
enum bit_value bitvec_get_bit_pos(const struct bitvec *bv, unsigned int bitnr)
{
@@ -91,6 +97,7 @@ enum bit_value bitvec_get_bit_pos(const struct bitvec *bv, unsigned int bitnr)
/*! \brief check if the bit is L or H for a given position inside a bitvec
* \param[in] bv the bit vector on which to check
* \param[in] bitnr the bit number inside the bit vector to check
+ * \return value of the requested bit
*/
enum bit_value bitvec_get_bit_pos_high(const struct bitvec *bv,
unsigned int bitnr)
@@ -132,10 +139,11 @@ unsigned int bitvec_get_nth_set_bit(const struct bitvec *bv, unsigned int n)
/*! \brief set a bit at given position in a bit vector
* \param[in] bv bit vector on which to operate
- * \param[in] bitnum number of bit to be set
+ * \param[in] bitnr number of bit to be set
* \param[in] bit value to which the bit is to be set
+ * \returns 0 on success, negative value on error
*/
-int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnr,
+inline int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnr,
enum bit_value bit)
{
unsigned int bytenum = bytenum_from_bitnum(bitnr);
@@ -159,8 +167,9 @@ int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnr,
/*! \brief set the next bit inside a bitvec
* \param[in] bv bit vector to be used
* \param[in] bit value of the bit to be set
+ * \returns 0 on success, negative value on error
*/
-int bitvec_set_bit(struct bitvec *bv, enum bit_value bit)
+inline int bitvec_set_bit(struct bitvec *bv, enum bit_value bit)
{
int rc;
@@ -171,7 +180,8 @@ int bitvec_set_bit(struct bitvec *bv, enum bit_value bit)
return rc;
}
-/*! \brief get the next bit (low/high) inside a bitvec */
+/*! \brief get the next bit (low/high) inside a bitvec
+ * \return value of th next bit in the vector */
int bitvec_get_bit_high(struct bitvec *bv)
{
int rc;
@@ -187,8 +197,8 @@ int bitvec_get_bit_high(struct bitvec *bv)
* \param[in] bv bit vector
* \param[in] bits array of \ref bit_value
* \param[in] count number of bits to set
- */
-int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count)
+ * \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;
@@ -201,14 +211,15 @@ int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count)
return 0;
}
-/*! \brief set multiple bits (based on numeric value) at current pos */
-int bitvec_set_uint(struct bitvec *bv, unsigned int ui, int num_bits)
+/*! \brief set multiple bits (based on numeric value) at current pos
+ * \return 0 in case of success; negative in case of error */
+int bitvec_set_uint(struct bitvec *bv, unsigned int ui, unsigned int num_bits)
{
- int i, rc;
-
+ int rc;
+ unsigned i;
for (i = 0; i < num_bits; i++) {
int bit = 0;
- if (ui & (1 << (num_bits - i - 1)))
+ if (ui & (1u << (num_bits - i - 1)))
bit = 1;
rc = bitvec_set_bit(bv, bit);
if (rc)
@@ -218,8 +229,22 @@ int bitvec_set_uint(struct bitvec *bv, unsigned int ui, int num_bits)
return 0;
}
-/*! \brief get multiple bits (based on numeric value) from current pos */
-int bitvec_get_uint(struct bitvec *bv, int num_bits)
+/*! \brief get multiple bits (num_bits) from beginning of vector (MSB side)
+ * \return 16bit signed integer retrieved from bit vector */
+int16_t bitvec_get_int16_msb(const struct bitvec *bv, unsigned int num_bits)
+{
+ if (num_bits > 15 || bv->cur_bit < num_bits)
+ return -EINVAL;
+
+ if (num_bits < 9)
+ return bv->data[0] >> (8 - num_bits);
+
+ return osmo_load16be(bv->data) >> (16 - num_bits);
+}
+
+/*! \brief get multiple bits (based on numeric value) from current pos
+ * \return integer value retrieved from bit vector */
+int bitvec_get_uint(struct bitvec *bv, unsigned int num_bits)
{
int i;
unsigned int ui = 0;
@@ -236,18 +261,32 @@ int bitvec_get_uint(struct bitvec *bv, int num_bits)
return ui;
}
-/*! \brief pad all remaining bits up to num_bits */
-int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit)
+/*! \brief fill num_bits with \fill starting from the current position
+ * \return 0 on success; negative otherwise (out of vector boundary)
+ */
+int bitvec_fill(struct bitvec *bv, unsigned int num_bits, enum bit_value fill)
{
- unsigned int i;
-
- for (i = bv->cur_bit; i <= up_to_bit; i++)
- bitvec_set_bit(bv, L);
+ unsigned i, stop = bv->cur_bit + num_bits;
+ for (i = bv->cur_bit; i < stop; i++)
+ if (bitvec_set_bit(bv, fill) < 0)
+ return -EINVAL;
return 0;
}
-/*! \brief find first bit set in bit vector */
+/*! \brief pad all remaining bits up to num_bits
+ * \return 0 on success; negative otherwise */
+int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit)
+{
+ int n = up_to_bit - bv->cur_bit + 1;
+ if (n < 1)
+ return 0;
+
+ return bitvec_fill(bv, n, L);
+}
+
+/*! \brief find first bit set in bit vector
+ * \return 0 on success; negative otherwise */
int bitvec_find_bit_pos(const struct bitvec *bv, unsigned int n,
enum bit_value val)
{
@@ -261,4 +300,388 @@ int bitvec_find_bit_pos(const struct bitvec *bv, unsigned int n,
return -1;
}
+/*! \brief get multiple bytes from current pos
+ * Assumes MSB first encoding.
+ * \param[in] bv bit vector
+ * \param[in] bytes array
+ * \param[in] count number of bytes to copy
+ * \return 0 on success; negative otherwise
+ */
+int bitvec_get_bytes(struct bitvec *bv, uint8_t *bytes, unsigned int count)
+{
+ int byte_offs = bytenum_from_bitnum(bv->cur_bit);
+ int bit_offs = bv->cur_bit % 8;
+ uint8_t c, last_c;
+ int i;
+ uint8_t *src;
+
+ if (byte_offs + count + (bit_offs ? 1 : 0) > bv->data_len)
+ return -EINVAL;
+
+ if (bit_offs == 0) {
+ memcpy(bytes, bv->data + byte_offs, count);
+ } else {
+ src = bv->data + byte_offs;
+ last_c = *(src++);
+ for (i = count; i > 0; i--) {
+ c = *(src++);
+ *(bytes++) =
+ (last_c << bit_offs) |
+ (c >> (8 - bit_offs));
+ last_c = c;
+ }
+ }
+
+ bv->cur_bit += count * 8;
+ return 0;
+}
+
+/*! \brief set multiple bytes at current pos
+ * Assumes MSB first encoding.
+ * \param[in] bv bit vector
+ * \param[in] bytes array
+ * \param[in] count number of bytes to copy
+ * \return 0 on success; negative otherwise
+ */
+int bitvec_set_bytes(struct bitvec *bv, const uint8_t *bytes, unsigned int count)
+{
+ int byte_offs = bytenum_from_bitnum(bv->cur_bit);
+ int bit_offs = bv->cur_bit % 8;
+ uint8_t c, last_c;
+ int i;
+ uint8_t *dst;
+
+ if (byte_offs + count + (bit_offs ? 1 : 0) > bv->data_len)
+ return -EINVAL;
+
+ if (bit_offs == 0) {
+ memcpy(bv->data + byte_offs, bytes, count);
+ } else if (count > 0) {
+ dst = bv->data + byte_offs;
+ /* Get lower bits of first dst byte */
+ last_c = *dst >> (8 - bit_offs);
+ for (i = count; i > 0; i--) {
+ c = *(bytes++);
+ *(dst++) =
+ (last_c << (8 - bit_offs)) |
+ (c >> bit_offs);
+ last_c = c;
+ }
+ /* Overwrite lower bits of N+1 dst byte */
+ *dst = (*dst & ((1 << (8 - bit_offs)) - 1)) |
+ (last_c << (8 - bit_offs));
+ }
+
+ bv->cur_bit += count * 8;
+ return 0;
+}
+
+/*! \brief Allocate a bit vector
+ * \param[in] size Number of bits 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 *bv = talloc_zero(ctx, struct bitvec);
+ if (!bv)
+ return NULL;
+
+ bv->data = talloc_zero_array(bv, uint8_t, size);
+ if (!(bv->data)) {
+ talloc_free(bv);
+ return NULL;
+ }
+
+ bv->data_len = size;
+ bv->cur_bit = 0;
+ return bv;
+}
+
+/*! \brief Free a bit vector (release its memory)
+ * \param[in] bit vector to free */
+void bitvec_free(struct bitvec *bv)
+{
+ talloc_free(bv->data);
+ talloc_free(bv);
+}
+
+/*! \brief Export a bit vector to a buffer
+ * \param[in] bitvec (unpacked bits)
+ * \param[out] buffer for the unpacked bits
+ * \return number of bytes (= bits) copied */
+unsigned int bitvec_pack(const struct bitvec *bv, uint8_t *buffer)
+{
+ unsigned int i = 0;
+ for (i = 0; i < bv->data_len; i++)
+ buffer[i] = bv->data[i];
+
+ return i;
+}
+
+/*! \brief Copy buffer of unpacked bits into bit vector
+ * \param[in] buffer unpacked input bits
+ * \param[out] bv unpacked bit vector
+ * \return number of bytes (= bits) copied */
+unsigned int bitvec_unpack(struct bitvec *bv, const uint8_t *buffer)
+{
+ unsigned int i = 0;
+ for (i = 0; i < bv->data_len; i++)
+ bv->data[i] = buffer[i];
+
+ return i;
+}
+
+/*! \brief read hexadecimap string into a bit vector
+ * \param[in] src string containing hex digits
+ * \param[out] bv unpacked bit vector
+ * \return 0 in case of success; 1 in case of error
+ */
+int bitvec_unhex(struct bitvec *bv, const char *src)
+{
+ unsigned i;
+ unsigned val;
+ unsigned write_index = 0;
+ unsigned digits = bv->data_len * 2;
+
+ for (i = 0; i < digits; i++) {
+ if (sscanf(src + i, "%1x", &val) < 1) {
+ return 1;
+ }
+ bitvec_write_field(bv, &write_index, val, 4);
+ }
+ return 0;
+}
+
+/*! \brief read part of the vector
+ * \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
+ */
+uint64_t bitvec_read_field(struct bitvec *bv, unsigned int *read_index, unsigned int len)
+{
+ unsigned int i;
+ uint64_t ui = 0;
+ bv->cur_bit = *read_index;
+
+ 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)
+ ui |= ((uint64_t)1 << (len - i - 1));
+ bv->cur_bit++;
+ }
+ *read_index += len;
+ return ui;
+}
+
+/*! \brief write into the vector
+ * \param[in] bv The boolean vector to work on
+ * \param[in,out] write_index Where writing supposed to start in the vector
+ * \param[in] len How many bits to write
+ * \returns next write index or negative value on error
+ */
+int bitvec_write_field(struct bitvec *bv, unsigned int *write_index, uint64_t val, unsigned int len)
+{
+ unsigned int i;
+ int rc;
+ bv->cur_bit = *write_index;
+ for (i = 0; i < len; i++) {
+ int bit = 0;
+ if (val & ((uint64_t)1 << (len - i - 1)))
+ bit = 1;
+ rc = bitvec_set_bit(bv, bit);
+ if (rc)
+ return rc;
+ }
+ *write_index += len;
+ return 0;
+}
+
+/*! \brief convert enum to corresponding character
+ * \param v input value (bit)
+ * \return single character, either 0, 1, L or H */
+char bit_value_to_char(enum bit_value v)
+{
+ switch (v) {
+ case ZERO: return '0';
+ case ONE: return '1';
+ case L: return 'L';
+ case H: return 'H';
+ default: abort();
+ }
+}
+
+/*! \brief prints bit vector to provided string
+ * It's caller's responsibility to ensure that we won't shoot him in the foot:
+ * the provided buffer should be at lest cur_bit + 1 bytes long
+ */
+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++) {
+ if (0 == i % 8)
+ *cur++ = ' ';
+ *cur++ = bit_value_to_char(bitvec_get_bit_pos(bv, i));
+ pos++;
+ }
+ *cur = 0;
+}
+
+/* we assume that x have at least 1 non-b bit */
+static inline unsigned leading_bits(uint8_t x, bool b)
+{
+ if (b) {
+ if (x < 0x80) return 0;
+ if (x < 0xC0) return 1;
+ if (x < 0xE0) return 2;
+ if (x < 0xF0) return 3;
+ if (x < 0xF8) return 4;
+ if (x < 0xFC) return 5;
+ if (x < 0xFE) return 6;
+ } else {
+ if (x > 0x7F) return 0;
+ if (x > 0x3F) return 1;
+ if (x > 0x1F) return 2;
+ if (x > 0xF) return 3;
+ if (x > 7) return 4;
+ if (x > 3) return 5;
+ if (x > 1) return 6;
+ }
+ return 7;
+}
+/*! \brief force bit vector to all 0 and current bit to the beginnig of the vector */
+void bitvec_zero(struct bitvec *bv)
+{
+ bv->cur_bit = 0;
+ memset(bv->data, 0, bv->data_len);
+}
+
+/*! \brief Return number (bits) of uninterrupted bit run in vector starting from the MSB
+ * \param[in] bv The boolean vector to work on
+ * \param[in] b The boolean, sequence of which is looked at from the vector start
+ * \returns Number of consecutive bits of \p b in \p bv
+ */
+unsigned bitvec_rl(const struct bitvec *bv, bool b)
+{
+ unsigned i;
+ for (i = 0; i < (bv->cur_bit % 8 ? bv->cur_bit / 8 + 1 : bv->cur_bit / 8); i++) {
+ if ( (b ? 0xFF : 0) != bv->data[i])
+ return i * 8 + leading_bits(bv->data[i], b);
+ }
+
+ return bv->cur_bit;
+}
+
+/*! \brief Return number (bits) of uninterrupted bit run in vector
+ * starting from the current bit
+ * \param[in] bv The boolean vector to work on
+ * \param[in] b The boolean, sequence of 1's or 0's to be checked
+ * \param[in] max_bits Total Number of Uncmopresed bits
+ * \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 i = 0;
+ unsigned j = 8;
+ int temp_res = 0;
+ int count = 0;
+ unsigned readIndex = bv->cur_bit;
+ unsigned remaining_bits = max_bits % 8;
+ unsigned remaining_bytes = max_bits / 8;
+ unsigned byte_mask = 0xFF;
+
+ if (readIndex % 8) {
+ for (j -= (readIndex % 8) ; j > 0 ; j--) {
+ if (readIndex < max_bits && bitvec_read_field(bv, &readIndex, 1) == b)
+ temp_res++;
+ else {
+ bv->cur_bit--;
+ return temp_res;
+ }
+ }
+ }
+ for (i = (readIndex / 8);
+ i < (remaining_bits ? remaining_bytes + 1 : remaining_bytes);
+ i++, count++) {
+ if ((b ? byte_mask : 0) != bv->data[i]) {
+ bv->cur_bit = (count * 8 +
+ leading_bits(bv->data[i], b) + readIndex);
+ return count * 8 +
+ leading_bits(bv->data[i], b) + temp_res;
+ }
+ }
+ bv->cur_bit = (temp_res + (count * 8)) + readIndex;
+ if (bv->cur_bit > max_bits)
+ bv->cur_bit = max_bits;
+ return (bv->cur_bit - readIndex + temp_res);
+}
+
+/*! \brief Shifts bitvec to the left, n MSB bits lost */
+void bitvec_shiftl(struct bitvec *bv, unsigned n)
+{
+ if (0 == n)
+ return;
+ if (n >= bv->cur_bit) {
+ bitvec_zero(bv);
+ return;
+ }
+
+ memmove(bv->data, bv->data + n / 8, bv->data_len - n / 8);
+
+ uint8_t tmp[2];
+ unsigned i;
+ for (i = 0; i < bv->data_len - 2; i++) {
+ uint16_t t = osmo_load16be(bv->data + i);
+ osmo_store16be(t << (n % 8), &tmp);
+ bv->data[i] = tmp[0];
+ }
+
+ bv->data[bv->data_len - 1] <<= (n % 8);
+ bv->cur_bit -= n;
+}
+
+/*! \brief Add given array to bitvec
+ * \param[in,out] bv bit vector to work with
+ * \param[in] array elements to be added
+ * \param[in] array_len length of array
+ * \param[in] dry_run indicates whether to return number of bits required
+ * instead of adding anything to bv for real
+ * \param[in] num_bits number of bits to consider in each element of array
+ * \returns number of bits necessary to add array elements if dry_run is true,
+ * 0 otherwise (only in this case bv is actually changed)
+ *
+ * N. B: no length checks are performed on bv - it's caller's job to ensure
+ * enough space is available - for example by calling with dry_run = true first.
+ *
+ * Useful for common pattern in CSN.1 spec which looks like:
+ * { 1 < XXX : bit (num_bits) > } ** 0
+ * which means repeat any times (between 0 and infinity),
+ * start each repetition with 1, mark end of repetitions with 0 bit
+ * see app. note in 3GPP TS 24.007 § B.2.1 Rule A2
+ */
+unsigned int bitvec_add_array(struct bitvec *bv, const uint32_t *array,
+ unsigned int array_len, bool dry_run,
+ unsigned int num_bits)
+{
+ unsigned i, bits = 1; /* account for stop bit */
+ for (i = 0; i < array_len; i++) {
+ if (dry_run) {
+ bits += (1 + num_bits);
+ } else {
+ bitvec_set_bit(bv, 1);
+ bitvec_set_uint(bv, array[i], num_bits);
+ }
+ }
+
+ if (dry_run)
+ return bits;
+
+ bitvec_set_bit(bv, 0); /* stop bit - end of the sequence */
+ return 0;
+}
+
/*! @} */
diff --git a/src/shared/libosmocore/src/codec/Makefile.am b/src/shared/libosmocore/src/codec/Makefile.am
index 665768c6..f61d2fe8 100644
--- a/src/shared/libosmocore/src/codec/Makefile.am
+++ b/src/shared/libosmocore/src/codec/Makefile.am
@@ -1,11 +1,13 @@
# 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
+# 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
-INCLUDES = $(all_includes) -I$(top_srcdir)/include
+AM_CPPFLAGS = -I$(top_srcdir)/include $(TALLOC_CFLAGS)
AM_CFLAGS = -Wall
lib_LTLIBRARIES = libosmocodec.la
libosmocodec_la_SOURCES = gsm610.c gsm620.c gsm660.c gsm690.c
-libosmocodec_la_LDFLAGS = -version-info $(LIBVERSION)
+libosmocodec_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined
+libosmocodec_la_LIBADD = $(top_builddir)/src/libosmocore.la
diff --git a/src/shared/libosmocore/src/codec/gsm610.c b/src/shared/libosmocore/src/codec/gsm610.c
index 35f6011d..47faea21 100644
--- a/src/shared/libosmocore/src/codec/gsm610.c
+++ b/src/shared/libosmocore/src/codec/gsm610.c
@@ -22,6 +22,10 @@
*/
#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/bitvec.h>
+#include <osmocom/core/utils.h>
/* GSM FR - subjective importance bit ordering */
/* This array encodes GSM 05.03 Table 2.
@@ -292,3 +296,38 @@ const uint16_t gsm610_bitorder[260] = {
11, /* LARc1:0 */
29, /* LARc5:0 */
};
+
+/*! \brief 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
+ * \param[in] payload_len Length of payload
+ * \returns true if code word is found, false otherwise
+ */
+bool osmo_fr_check_sid(uint8_t *rtp_payload, size_t payload_len)
+{
+ struct bitvec bv;
+ uint16_t i, z_bits[] = { 59, 60, 62, 63, 65, 66, 68, 69, 71, 72, 74, 75,
+ 77, 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 93,
+ 95, 96, 115, 116, 118, 119, 121, 122, 124, 125,
+ 127, 128, 130, 131, 133, 134, 136, 137, 139,
+ 140, 142, 143, 145, 146, 148, 149, 151, 152,
+ 171, 172, 174, 175, 177, 178, 180, 181, 183,
+ 184, 186, 187, 189, 190, 192, 193, 195, 196,
+ 198, 199, 201, 202, 204, 205, 207, 208, 227,
+ 228, 230, 231, 233, 234, 236, 237, 239, 242,
+ 245, 248, 251, 254, 257, 260, 263 };
+
+ /* signature does not match Full Rate SID */
+ if ((rtp_payload[0] >> 4) != 0xD)
+ return false;
+
+ bv.data = 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)
+ return false;
+
+ return true;
+}
diff --git a/src/shared/libosmocore/src/codec/gsm620.c b/src/shared/libosmocore/src/codec/gsm620.c
index fa570e4f..6f1a95bb 100644
--- a/src/shared/libosmocore/src/codec/gsm620.c
+++ b/src/shared/libosmocore/src/codec/gsm620.c
@@ -22,6 +22,10 @@
*/
#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/bitvec.h>
+#include <osmocom/core/utils.h>
/* GSM HR unvoiced (mode=0) frames - subjective importance bit ordering */
/* This array encode mapping between GSM 05.03 Table 3a (bits
@@ -35,36 +39,36 @@ const uint16_t gsm620_unvoiced_bitorder[112] = {
90, /* GSP 0-3:2 */
109, /* GSP 0-4:2 */
15, /* LPC 1:0 */
- 23, /* LPC 2:1 */
- 22, /* LPC 2:2 */
- 21, /* LPC 2:3 */
- 20, /* LPC 2:4 */
19, /* LPC 2:5 */
- 31, /* LPC 3:1 */
- 30, /* LPC 3:2 */
- 29, /* LPC 3:3 */
- 28, /* LPC 3:4 */
- 27, /* LPC 3:5 */
+ 20, /* LPC 2:4 */
+ 21, /* LPC 2:3 */
+ 22, /* LPC 2:2 */
+ 23, /* LPC 2:1 */
26, /* LPC 3:6 */
+ 27, /* LPC 3:5 */
+ 28, /* LPC 3:4 */
+ 29, /* LPC 3:3 */
+ 30, /* LPC 3:2 */
+ 31, /* LPC 3:1 */
61, /* Code 1-2:0 */
- 68, /* Code 2-2:0 */
- 67, /* Code 2-2:1 */
- 66, /* Code 2-2:2 */
- 65, /* Code 2-2:3 */
- 64, /* Code 2-2:4 */
- 63, /* Code 2-2:5 */
62, /* Code 2-2:6 */
- 80, /* Code 1-3:0 */
- 79, /* Code 1-3:1 */
- 78, /* Code 1-3:2 */
- 77, /* Code 1-3:3 */
- 76, /* Code 1-3:4 */
- 75, /* Code 1-3:5 */
+ 63, /* Code 2-2:5 */
+ 64, /* Code 2-2:4 */
+ 65, /* Code 2-2:3 */
+ 66, /* Code 2-2:2 */
+ 67, /* Code 2-2:1 */
+ 68, /* Code 2-2:0 */
74, /* Code 1-3:6 */
- 84, /* Code 2-3:3 */
- 83, /* Code 2-3:4 */
- 82, /* Code 2-3:5 */
+ 75, /* Code 1-3:5 */
+ 76, /* Code 1-3:4 */
+ 77, /* Code 1-3:3 */
+ 78, /* Code 1-3:2 */
+ 79, /* Code 1-3:1 */
+ 80, /* Code 1-3:0 */
81, /* Code 2-3:6 */
+ 82, /* Code 2-3:5 */
+ 83, /* Code 2-3:4 */
+ 84, /* Code 2-3:3 */
32, /* LPC 3:0 */
4, /* R0:0 */
33, /* INT-LPC:0 */
@@ -106,9 +110,9 @@ const uint16_t gsm620_unvoiced_bitorder[112] = {
89, /* GSP 0-3:3 */
70, /* GSP 0-2:3 */
51, /* GSP 0-1:3 */
- 18, /* LPC 2:6 */
- 17, /* LPC 2:7 */
16, /* LPC 2:8 */
+ 17, /* LPC 2:7 */
+ 18, /* LPC 2:6 */
107, /* GSP 0-4:4 */
88, /* GSP 0-3:4 */
69, /* GSP 0-2:4 */
@@ -149,9 +153,9 @@ const uint16_t gsm620_unvoiced_bitorder[112] = {
const uint16_t gsm620_voiced_bitorder[112] = {
13, /* LPC 1:2 */
14, /* LPC 1:1 */
- 20, /* LPC 2:4 */
- 19, /* LPC 2:5 */
18, /* LPC 2:6 */
+ 19, /* LPC 2:5 */
+ 20, /* LPC 2:4 */
53, /* GSP 0-1:4 */
71, /* GSP 0-2:4 */
89, /* GSP 0-3:4 */
@@ -164,22 +168,22 @@ const uint16_t gsm620_voiced_bitorder[112] = {
73, /* GSP 0-2:2 */
91, /* GSP 0-3:2 */
109, /* GSP 0-4:2 */
- 52, /* Code 1:0 */
- 51, /* Code 1:1 */
- 50, /* Code 1:2 */
- 49, /* Code 1:3 */
- 48, /* Code 1:4 */
- 47, /* Code 1:5 */
- 46, /* Code 1:6 */
- 45, /* Code 1:7 */
44, /* Code 1:8 */
- 65, /* Code 2:5 */
- 64, /* Code 2:6 */
- 63, /* Code 2:7 */
+ 45, /* Code 1:7 */
+ 46, /* Code 1:6 */
+ 47, /* Code 1:5 */
+ 48, /* Code 1:4 */
+ 49, /* Code 1:3 */
+ 50, /* Code 1:2 */
+ 51, /* Code 1:1 */
+ 52, /* Code 1:0 */
62, /* Code 2:8 */
- 70, /* Code 2:0 */
- 69, /* Code 2:1 */
+ 63, /* Code 2:7 */
+ 64, /* Code 2:6 */
+ 65, /* Code 2:5 */
68, /* Code 2:2 */
+ 69, /* Code 2:1 */
+ 70, /* Code 2:0 */
80, /* Code 3:8 */
66, /* Code 2:4 */
67, /* Code 2:3 */
@@ -260,3 +264,31 @@ const uint16_t gsm620_voiced_bitorder[112] = {
82, /* Code 3:6 */
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;
+}
+
+/*! \brief 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
+ * \param[in] payload_len Length of payload
+ * \returns true if code word is found, false otherwise
+ */
+bool osmo_hr_check_sid(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 = rtp_payload;
+ bv.data_len = payload_len;
+ bv.cur_bit = 33;
+
+ /* 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]))
+ return false;
+
+ return true;
+}
diff --git a/src/shared/libosmocore/src/codec/gsm690.c b/src/shared/libosmocore/src/codec/gsm690.c
index fdf3302f..b273f0fe 100644
--- a/src/shared/libosmocore/src/codec/gsm690.c
+++ b/src/shared/libosmocore/src/codec/gsm690.c
@@ -22,7 +22,12 @@
*/
#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/codec/codec.h>
/*
* These table map between the raw encoder parameter output and
* the format used before channel coding. Both in GSM and in various
@@ -208,3 +213,104 @@ const uint16_t gsm690_4_75_bitorder[95] = {
88, 90, 91, 34, 55, 68, 89, 37, 58, 71,
92, 31, 52, 65, 86,
};
+
+static const uint8_t amr_len_by_ft[16] = {
+ 12, 13, 15, 17, 19, 20, 26, 31, 7, 0, 0, 0, 0, 0, 0, 0
+};
+
+const struct value_string osmo_amr_type_names[] = {
+ { AMR_4_75, "AMR 4,75 kbits/s" },
+ { AMR_5_15, "AMR 5,15 kbit/s" },
+ { AMR_5_90, "AMR 5,90 kbit/s" },
+ { AMR_6_70, "AMR 6,70 kbit/s (PDC-EFR)" },
+ { AMR_7_40, "AMR 7,40 kbit/s (TDMA-EFR)" },
+ { AMR_7_95, "AMR 7,95 kbit/s" },
+ { AMR_10_2, "AMR 10,2 kbit/s" },
+ { AMR_12_2, "AMR 12,2 kbit/s (GSM-EFR)" },
+ { AMR_SID, "AMR SID" },
+ { AMR_GSM_EFR_SID, "GSM-EFR SID" },
+ { AMR_TDMA_EFR_SID, "TDMA-EFR SID" },
+ { AMR_PDC_EFR_SID, "PDC-EFR SID" },
+ { AMR_NO_DATA, "No Data/NA" },
+ { 0, NULL },
+};
+
+/*! \brief Decode various AMR parameters from RTP payload (RFC 4867) acording to
+ * 3GPP TS 26.101
+ * \param[in] rtppayload Payload from RTP packet
+ * \param[in] payload_len length of rtppayload
+ * \param[out] cmr AMR Codec Mode Request, not filled if NULL
+ * \param[out] cmi AMR Codec Mode Indicator, -1 if not applicable for this type,
+ * not filled if NULL
+ * \param[out] ft AMR Frame Type, not filled if NULL
+ * \param[out] bfi AMR Bad Frame Indicator, not filled if NULL
+ * \param[out] sti AMR SID Type Indicator, -1 if not applicable for this type,
+ * not filled if NULL
+ * \returns length of AMR data or negative value on error
+ */
+int osmo_amr_rtp_dec(const uint8_t *rtppayload, int payload_len, uint8_t *cmr,
+ int8_t *cmi, enum osmo_amr_type *ft,
+ enum osmo_amr_quality *bfi, int8_t *sti)
+{
+ if (payload_len < 2 || !rtppayload)
+ return -EINVAL;
+
+ /* RFC 4867 § 4.4.2 ToC - compound payloads are not supported: F = 0 */
+ uint8_t type = (rtppayload[1] >> 3) & 0xf;
+
+ /* compound payloads are not supported */
+ if (rtppayload[1] >> 7)
+ return -ENOTSUP;
+
+ if (payload_len < amr_len_by_ft[type])
+ return -ENOTSUP;
+
+ if (ft)
+ *ft = type;
+
+ if (cmr)
+ *cmr = rtppayload[0] >> 4;
+
+ if (bfi)
+ *bfi = (rtppayload[1] >> 2) & 1;
+
+ /* Table 6 in 3GPP TS 26.101 */
+ if (cmi)
+ *cmi = (type == AMR_SID) ? ((rtppayload[6] >> 1) & 7) : -1;
+
+ if (sti)
+ *sti = (type == AMR_SID) ? (rtppayload[6] & 0x10) : -1;
+
+ return 2 + amr_len_by_ft[type];
+}
+
+/*! \brief Encode various AMR parameters from RTP payload (RFC 4867)
+ * \param[out] payload Payload for RTP packet, contains speech data (if any)
+ * except for have 2 first bytes where header will be built
+ * \param[in] cmr AMR codec Mode Request
+ * \param[in] ft AMR Frame Type
+ * \param[in] bfi AMR Bad Frame Indicator
+ * \returns length of AMR data (header + ToC + speech data) or negative value
+ * on error
+ *
+ * Note: only octet-aligned mode is supported so the header occupies 2 full
+ * bytes. Optional interleaving header is not supported.
+ */
+int osmo_amr_rtp_enc(uint8_t *payload, uint8_t cmr, enum osmo_amr_type ft,
+ enum osmo_amr_quality bfi)
+{
+ if (cmr > 15)
+ return -EINVAL;
+
+ if (ft > 15)
+ return -ENOTSUP;
+
+ /* RFC 4867 § 4.3.1 payload header */
+ payload[0] = cmr << 4;
+
+ /* RFC 4867 § 4.4.2 ToC - compound payloads are not supported: F = 0 */
+ payload[1] = (((uint8_t)ft) << 3) | (((uint8_t)bfi) << 2);
+
+ /* speech data */
+ return 2 + amr_len_by_ft[ft];
+}
diff --git a/src/shared/libosmocore/src/conv.c b/src/shared/libosmocore/src/conv.c
index ebc3eda7..f13deef7 100644
--- a/src/shared/libosmocore/src/conv.c
+++ b/src/shared/libosmocore/src/conv.c
@@ -27,7 +27,7 @@
*/
/*! \file conv.c
- * \file Osmocom convolutional encoder and decoder
+ * Osmocom convolutional encoder and decoder
*/
#include "config.h"
#ifdef HAVE_ALLOCA_H
diff --git a/src/shared/libosmocore/src/crc16.c b/src/shared/libosmocore/src/crc16.c
index 2741cf53..accb0b45 100644
--- a/src/shared/libosmocore/src/crc16.c
+++ b/src/shared/libosmocore/src/crc16.c
@@ -10,7 +10,7 @@
#include <osmocom/core/crc16.h>
-/** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */
+/*! \brief CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */
uint16_t const osmo_crc16_table[256] = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
@@ -46,13 +46,11 @@ uint16_t const osmo_crc16_table[256] = {
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
};
-/**
- * crc16 - compute the CRC-16 for the data buffer
- * @crc: previous CRC value
- * @buffer: data pointer
- * @len: number of bytes in the buffer
- *
- * Returns the updated CRC value.
+/*! \brief compute the CRC-16 for the data buffer
+ * \param crc[in] previous CRC value
+ * \param buffer[in] data pointer
+ * \param len[in] number of bytes in input \ref buffer
+ * \return updated CRC value
*/
uint16_t osmo_crc16(uint16_t crc, uint8_t const *buffer, size_t len)
{
@@ -60,3 +58,45 @@ uint16_t osmo_crc16(uint16_t crc, uint8_t const *buffer, size_t len)
crc = osmo_crc16_byte(crc, *buffer++);
return crc;
}
+
+uint16_t const osmo_crc16_ccitt_table[256] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+uint16_t osmo_crc16_ccitt(uint16_t crc, uint8_t const *buffer, size_t len)
+{
+ while (len--)
+ crc = osmo_crc16_ccitt_byte(crc, *buffer++);
+ return crc;
+}
diff --git a/src/shared/libosmocore/src/crcXXgen.c.tpl b/src/shared/libosmocore/src/crcXXgen.c.tpl
index 80bf1e2a..7e45c111 100644
--- a/src/shared/libosmocore/src/crcXXgen.c.tpl
+++ b/src/shared/libosmocore/src/crcXXgen.c.tpl
@@ -27,7 +27,7 @@
*/
/*! \file crcXXgen.c
- * \file Osmocom generic CRC routines (for max XX bits poly)
+ * Osmocom generic CRC routines (for max XX bits poly)
*/
#include <stdint.h>
@@ -53,13 +53,13 @@ osmo_crcXXgen_compute_bits(const struct osmo_crcXXgen_code *code,
for (i=0; i<len; i++) {
uintXX_t bit = in[i] & 1;
crc ^= (bit << n);
- if (crc & (1 << n)) {
+ if (crc & ((uintXX_t)1 << n)) {
crc <<= 1;
crc ^= poly;
} else {
crc <<= 1;
}
- crc &= (1ULL << code->bits) - 1;
+ crc &= ((uintXX_t)1 << code->bits) - 1;
}
crc ^= code->remainder;
diff --git a/src/shared/libosmocore/src/ctrl/Makefile.am b/src/shared/libosmocore/src/ctrl/Makefile.am
new file mode 100644
index 00000000..8bf34429
--- /dev/null
+++ b/src/shared/libosmocore/src/ctrl/Makefile.am
@@ -0,0 +1,20 @@
+# 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
+
+AM_CFLAGS = -Wall $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include $(TALLOC_CFLAGS)
+
+lib_LTLIBRARIES = libosmoctrl.la
+
+libosmoctrl_la_SOURCES = control_cmd.c control_if.c
+
+libosmoctrl_la_LDFLAGS = $(LTLDFLAGS_OSMOCTRL) $(TALLOC_LIBS) -version-info $(LIBVERSION) -no-undefined
+libosmoctrl_la_LIBADD = \
+ $(top_builddir)/src/libosmocore.la \
+ $(top_builddir)/src/gsm/libosmogsm.la \
+ $(top_builddir)/src/vty/libosmovty.la
+
+if ENABLE_VTY
+libosmoctrl_la_SOURCES += control_vty.c
+endif
diff --git a/src/shared/libosmocore/src/ctrl/control_cmd.c b/src/shared/libosmocore/src/ctrl/control_cmd.c
new file mode 100644
index 00000000..2cf66cf3
--- /dev/null
+++ b/src/shared/libosmocore/src/ctrl/control_cmd.c
@@ -0,0 +1,550 @@
+/* SNMP-like status interface
+ *
+ * (C) 2010-2011 by Daniel Willmann <daniel@totalueberwachung.de>
+ * (C) 2010-2011 by On-Waves
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <osmocom/ctrl/control_cmd.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/vector.h>
+
+extern vector ctrl_node_vec;
+
+static struct ctrl_cmd_map ccm[] = {
+ {"GET", CTRL_TYPE_GET},
+ {"SET", CTRL_TYPE_SET},
+ {"GET_REPLY", CTRL_TYPE_GET_REPLY},
+ {"SET_REPLY", CTRL_TYPE_SET_REPLY},
+ {"TRAP", CTRL_TYPE_TRAP},
+ {"ERROR", CTRL_TYPE_ERROR},
+ {NULL}
+};
+
+static int ctrl_cmd_str2type(char *s)
+{
+ int i;
+ for (i=0; ccm[i].cmd != NULL; i++) {
+ if (strcasecmp(s, ccm[i].cmd) == 0)
+ return ccm[i].type;
+ }
+ return CTRL_TYPE_UNKNOWN;
+}
+
+static char *ctrl_cmd_type2str(int type)
+{
+ int i;
+ for (i=0; ccm[i].cmd != NULL; i++) {
+ if (ccm[i].type == type)
+ return ccm[i].cmd;
+ }
+ return NULL;
+}
+
+/* Functions from libosmocom */
+extern vector cmd_make_descvec(const char *string, const char *descstr);
+
+/* Get the ctrl_cmd_element that matches this command */
+static struct ctrl_cmd_element *ctrl_cmd_get_element_match(vector vline, vector node)
+{
+ int index, j;
+ const char *desc;
+ struct ctrl_cmd_element *cmd_el;
+ struct ctrl_cmd_struct *cmd_desc;
+ char *str;
+
+ for (index = 0; index < vector_active(node); index++) {
+ if ((cmd_el = vector_slot(node, index))) {
+ cmd_desc = &cmd_el->strcmd;
+ if (cmd_desc->nr_commands > vector_active(vline))
+ continue;
+ for (j =0; j < vector_active(vline) && j < cmd_desc->nr_commands; j++) {
+ str = vector_slot(vline, j);
+ desc = cmd_desc->command[j];
+ if (desc[0] == '*')
+ return cmd_el; /* Partial match */
+ if (strcmp(desc, str) != 0)
+ break;
+ }
+ /* We went through all the elements and all matched */
+ if (j == cmd_desc->nr_commands)
+ return cmd_el;
+ }
+ }
+
+ return NULL;
+}
+
+int ctrl_cmd_exec(vector vline, struct ctrl_cmd *command, vector node, void *data)
+{
+ int ret = CTRL_CMD_ERROR;
+ struct ctrl_cmd_element *cmd_el;
+
+ if ((command->type != CTRL_TYPE_GET) && (command->type != CTRL_TYPE_SET)) {
+ command->reply = "Trying to execute something not GET or SET";
+ goto out;
+ }
+ if ((command->type == CTRL_TYPE_SET) && (!command->value)) {
+ command->reply = "SET without a value";
+ goto out;
+ }
+
+ if (!vline)
+ goto out;
+
+ cmd_el = ctrl_cmd_get_element_match(vline, node);
+
+ if (!cmd_el) {
+ command->reply = "Command not found";
+ goto out;
+ }
+
+ if (command->type == CTRL_TYPE_SET) {
+ if (!cmd_el->set) {
+ command->reply = "SET not implemented";
+ goto out;
+ }
+ if (cmd_el->verify) {
+ if ((ret = cmd_el->verify(command, command->value, data))) {
+ ret = CTRL_CMD_ERROR;
+ /* If verify() set an appropriate error message, don't change it. */
+ if (!command->reply)
+ command->reply = "Value failed verification.";
+ goto out;
+ }
+ }
+ ret = cmd_el->set(command, data);
+ goto out;
+ } else if (command->type == CTRL_TYPE_GET) {
+ if (!cmd_el->get) {
+ command->reply = "GET not implemented";
+ goto out;
+ }
+ ret = cmd_el->get(command, data);
+ goto out;
+ }
+out:
+ if (ret == CTRL_CMD_REPLY) {
+ if (command->type == CTRL_TYPE_SET) {
+ command->type = CTRL_TYPE_SET_REPLY;
+ } else if (command->type == CTRL_TYPE_GET) {
+ command->type = CTRL_TYPE_GET_REPLY;
+ }
+ } else if (ret == CTRL_CMD_ERROR) {
+ command->type = CTRL_TYPE_ERROR;
+ }
+ return ret;
+}
+
+static void add_word(struct ctrl_cmd_struct *cmd,
+ const char *start, const char *end)
+{
+ if (!cmd->command) {
+ cmd->command = talloc_zero_array(tall_vty_vec_ctx,
+ char*, 1);
+ cmd->nr_commands = 0;
+ } else {
+ cmd->command = talloc_realloc(tall_vty_vec_ctx,
+ cmd->command, char*,
+ cmd->nr_commands + 1);
+ }
+
+ cmd->command[cmd->nr_commands++] = talloc_strndup(cmd->command,
+ start, end - start);
+}
+
+static void create_cmd_struct(struct ctrl_cmd_struct *cmd, const char *name)
+{
+ const char *cur, *word;
+
+ for (cur = name, word = NULL; cur[0] != '\0'; ++cur) {
+ /* warn about optionals */
+ if (cur[0] == '(' || cur[0] == ')' || cur[0] == '|') {
+ LOGP(DLCTRL, LOGL_ERROR,
+ "Optionals are not supported in '%s'\n", name);
+ goto failure;
+ }
+
+ if (isspace(cur[0])) {
+ if (word) {
+ add_word(cmd, word, cur);
+ word = NULL;
+ }
+ continue;
+ }
+
+ if (!word)
+ word = cur;
+ }
+
+ if (word)
+ add_word(cmd, word, cur);
+
+ return;
+failure:
+ cmd->nr_commands = 0;
+ talloc_free(cmd->command);
+}
+
+int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd)
+{
+ vector cmds_vec;
+
+ cmds_vec = vector_lookup_ensure(ctrl_node_vec, node);
+
+ if (!cmds_vec) {
+ cmds_vec = vector_init(5);
+ if (!cmds_vec) {
+ LOGP(DLCTRL, LOGL_ERROR, "vector_init failed.\n");
+ return -ENOMEM;
+ }
+ vector_set_index(ctrl_node_vec, node, cmds_vec);
+ }
+
+ vector_set(cmds_vec, cmd);
+
+ create_cmd_struct(&cmd->strcmd, cmd->name);
+ return 0;
+}
+
+struct ctrl_cmd *ctrl_cmd_create(void *ctx, enum ctrl_type type)
+{
+ struct ctrl_cmd *cmd;
+
+ cmd = talloc_zero(ctx, struct ctrl_cmd);
+ if (!cmd)
+ return NULL;
+
+ cmd->type = type;
+ return cmd;
+}
+
+struct ctrl_cmd *ctrl_cmd_cpy(void *ctx, struct ctrl_cmd *cmd)
+{
+ struct ctrl_cmd *cmd2;
+
+ cmd2 = talloc_zero(ctx, struct ctrl_cmd);
+ if (!cmd2)
+ return NULL;
+
+ cmd2->type = cmd->type;
+ if (cmd->id) {
+ cmd2->id = talloc_strdup(cmd2, cmd->id);
+ if (!cmd2->id)
+ goto err;
+ }
+ if (cmd->variable) {
+ cmd2->variable = talloc_strdup(cmd2, cmd->variable);
+ if (!cmd2->variable)
+ goto err;
+ }
+ if (cmd->value) {
+ cmd2->value = talloc_strdup(cmd2, cmd->value);
+ if (!cmd2->value)
+ goto err;
+ }
+ if (cmd->reply) {
+ cmd2->reply = talloc_strdup(cmd2, cmd->reply);
+ if (!cmd2->reply)
+ goto err;
+ }
+
+ return cmd2;
+err:
+ talloc_free(cmd2);
+ return NULL;
+}
+
+struct ctrl_cmd *ctrl_cmd_parse(void *ctx, struct msgb *msg)
+{
+ char *str, *tmp, *saveptr = NULL;
+ char *var, *val;
+ struct ctrl_cmd *cmd;
+
+ cmd = talloc_zero(ctx, struct ctrl_cmd);
+ if (!cmd) {
+ LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate.\n");
+ return NULL;
+ }
+
+ /* Make sure input is NULL terminated */
+ msgb_put_u8(msg, 0);
+ str = (char *) msg->l2h;
+
+ OSMO_ASSERT(str);
+ tmp = strtok_r(str, " ", &saveptr);
+ if (!tmp) {
+ cmd->type = CTRL_TYPE_ERROR;
+ cmd->id = "err";
+ cmd->reply = "Request malformed";
+ goto err;
+ }
+
+ cmd->type = ctrl_cmd_str2type(tmp);
+ if (cmd->type == CTRL_TYPE_UNKNOWN) {
+ cmd->type = CTRL_TYPE_ERROR;
+ cmd->id = "err";
+ cmd->reply = "Request type unknown";
+ goto err;
+ }
+
+ tmp = strtok_r(NULL, " ", &saveptr);
+
+ if (!tmp) {
+ cmd->type = CTRL_TYPE_ERROR;
+ cmd->id = "err";
+ cmd->reply = "Missing ID";
+ goto err;
+ }
+ cmd->id = talloc_strdup(cmd, tmp);
+ if (!cmd->id)
+ goto oom;
+
+ switch (cmd->type) {
+ case CTRL_TYPE_GET:
+ var = strtok_r(NULL, " ", &saveptr);
+ if (!var) {
+ cmd->type = CTRL_TYPE_ERROR;
+ cmd->reply = "GET incomplete";
+ LOGP(DLCTRL, LOGL_NOTICE, "GET Command incomplete\n");
+ goto err;
+ }
+ cmd->variable = talloc_strdup(cmd, var);
+ LOGP(DLCTRL, LOGL_DEBUG, "Command: GET %s\n", cmd->variable);
+ break;
+ case CTRL_TYPE_SET:
+ var = strtok_r(NULL, " ", &saveptr);
+ val = strtok_r(NULL, "\n", &saveptr);
+ if (!var || !val) {
+ cmd->type = CTRL_TYPE_ERROR;
+ cmd->reply = "SET incomplete";
+ LOGP(DLCTRL, LOGL_NOTICE, "SET Command incomplete\n");
+ goto err;
+ }
+ cmd->variable = talloc_strdup(cmd, var);
+ cmd->value = talloc_strdup(cmd, val);
+ if (!cmd->variable || !cmd->value)
+ goto oom;
+ LOGP(DLCTRL, LOGL_DEBUG, "Command: SET %s = %s\n", cmd->variable, cmd->value);
+ break;
+ case CTRL_TYPE_GET_REPLY:
+ case CTRL_TYPE_SET_REPLY:
+ case CTRL_TYPE_TRAP:
+ var = strtok_r(NULL, " ", &saveptr);
+ val = strtok_r(NULL, " ", &saveptr);
+ if (!var || !val) {
+ cmd->type = CTRL_TYPE_ERROR;
+ cmd->reply = "Trap/Reply incomplete";
+ LOGP(DLCTRL, LOGL_NOTICE, "Trap/Reply incomplete\n");
+ goto err;
+ }
+ cmd->variable = talloc_strdup(cmd, var);
+ cmd->reply = talloc_strdup(cmd, val);
+ if (!cmd->variable || !cmd->reply)
+ goto oom;
+ LOGP(DLCTRL, LOGL_DEBUG, "Command: TRAP/REPLY %s: %s\n", cmd->variable, cmd->reply);
+ break;
+ case CTRL_TYPE_ERROR:
+ var = strtok_r(NULL, "\0", &saveptr);
+ if (!var) {
+ cmd->reply = "";
+ goto err;
+ }
+ cmd->reply = talloc_strdup(cmd, var);
+ if (!cmd->reply)
+ goto oom;
+ LOGP(DLCTRL, LOGL_DEBUG, "Command: ERROR %s\n", cmd->reply);
+ break;
+ case CTRL_TYPE_UNKNOWN:
+ default:
+ cmd->type = CTRL_TYPE_ERROR;
+ cmd->reply = "Unknown type";
+ goto err;
+ }
+
+ return cmd;
+oom:
+ cmd->type = CTRL_TYPE_ERROR;
+ cmd->id = "err";
+ cmd->reply = "OOM";
+err:
+ talloc_free(cmd);
+ return NULL;
+}
+
+struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd)
+{
+ struct msgb *msg;
+ char *type, *tmp;
+
+ if (!cmd->id)
+ return NULL;
+
+ msg = msgb_alloc_headroom(4096, 128, "ctrl command make");
+ if (!msg)
+ return NULL;
+
+ type = ctrl_cmd_type2str(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);
+ 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);
+ 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);
+ 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);
+ break;
+ default:
+ LOGP(DLCTRL, LOGL_NOTICE, "Unknown command type %i\n", cmd->type);
+ goto err;
+ break;
+ }
+
+ return msg;
+
+err:
+ msgb_free(msg);
+ return NULL;
+}
+
+struct ctrl_cmd_def *
+ctrl_cmd_def_make(const void *ctx, struct ctrl_cmd *cmd, void *data, unsigned int secs)
+{
+ struct ctrl_cmd_def *cd;
+
+ if (!cmd->ccon)
+ return NULL;
+
+ cd = talloc_zero(ctx, struct ctrl_cmd_def);
+
+ cd->cmd = cmd;
+ cd->data = data;
+
+ /* add to per-connection list of deferred commands */
+ llist_add(&cd->list, &cmd->ccon->def_cmds);
+
+ return cd;
+}
+
+int ctrl_cmd_def_is_zombie(struct ctrl_cmd_def *cd)
+{
+ /* luckily we're still alive */
+ if (cd->cmd)
+ return 0;
+
+ /* if we are a zombie, make sure we really die */
+ llist_del(&cd->list);
+ talloc_free(cd);
+
+ return 1;
+}
+
+int ctrl_cmd_def_send(struct ctrl_cmd_def *cd)
+{
+ struct ctrl_cmd *cmd = cd->cmd;
+
+ int rc;
+
+ /* Deferred commands can only be responses to GET/SET or ERROR, but
+ * never TRAP or anything else */
+ switch (cmd->type) {
+ case CTRL_TYPE_GET:
+ cmd->type = CTRL_TYPE_GET_REPLY;
+ break;
+ case CTRL_TYPE_SET:
+ cmd->type = CTRL_TYPE_SET_REPLY;
+ break;
+ default:
+ cmd->type = CTRL_TYPE_ERROR;
+ }
+
+ rc = ctrl_cmd_send(&cmd->ccon->write_queue, cmd);
+
+ talloc_free(cmd);
+ llist_del(&cd->list);
+ talloc_free(cd);
+
+ return rc;
+}
diff --git a/src/shared/libosmocore/src/ctrl/control_if.c b/src/shared/libosmocore/src/ctrl/control_if.c
new file mode 100644
index 00000000..df394867
--- /dev/null
+++ b/src/shared/libosmocore/src/ctrl/control_if.c
@@ -0,0 +1,703 @@
+/* SNMP-like status interface
+ *
+ * (C) 2010-2011 by Daniel Willmann <daniel@totalueberwachung.de>
+ * (C) 2010-2011 by On-Waves
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+
+#include <netinet/in.h>
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <osmocom/ctrl/control_cmd.h>
+#include <osmocom/ctrl/control_if.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/statistics.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/socket.h>
+
+#include <osmocom/gsm/protocol/ipaccess.h>
+#include <osmocom/gsm/ipa.h>
+
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/vector.h>
+
+vector ctrl_node_vec;
+
+int ctrl_parse_get_num(vector vline, int i, long *num)
+{
+ char *token, *tmp;
+
+ 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)
+ return 0;
+
+ return 1;
+}
+
+/* Send command to all */
+int ctrl_cmd_send_to_all(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd)
+{
+ struct ctrl_connection *ccon;
+ int ret = 0;
+
+ llist_for_each_entry(ccon, &ctrl->ccon_list, list_entry) {
+ if (ccon == cmd->ccon)
+ continue;
+ if (ctrl_cmd_send(&ccon->write_queue, cmd))
+ ret++;
+ }
+ return ret;
+}
+
+int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd)
+{
+ int ret;
+ struct msgb *msg;
+
+ msg = ctrl_cmd_make(cmd);
+ if (!msg) {
+ LOGP(DLCTRL, LOGL_ERROR, "Could not generate msg\n");
+ return -1;
+ }
+
+ ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL);
+ ipa_prepend_header(msg, IPAC_PROTO_OSMO);
+
+ ret = osmo_wqueue_enqueue(queue, msg);
+ if (ret != 0) {
+ LOGP(DLCTRL, LOGL_ERROR, "Failed to enqueue the command.\n");
+ msgb_free(msg);
+ }
+ return ret;
+}
+
+/*! \brief Send TRAP over given Control Interface
+ * \param[in] ctrl Control Interface over which TRAP will be sent
+ * \param[in] name Name of the TRAP variable
+ * \param[in] value Value of the TRAP variable
+ * \return Negative value on error, result of ctrl_cmd_send_to_all() otherwise
+ */
+int ctrl_cmd_send_trap(struct ctrl_handle *ctrl, const char *name, char *value)
+{
+ int r;
+ struct ctrl_cmd *cmd = ctrl_cmd_create(NULL, CTRL_TYPE_TRAP);
+ if (!cmd)
+ return -ENOMEM;
+
+ cmd->id = "0"; /* It's a TRAP! */
+ cmd->variable = name;
+ cmd->reply = value;
+ r = ctrl_cmd_send_to_all(ctrl, cmd);
+ talloc_free(cmd);
+ return r;
+}
+
+struct ctrl_cmd *ctrl_cmd_trap(struct ctrl_cmd *cmd)
+{
+ struct ctrl_cmd *trap;
+
+ trap = ctrl_cmd_cpy(cmd, cmd);
+ if (!trap)
+ return NULL;
+
+ trap->ccon = cmd->ccon;
+ trap->type = CTRL_TYPE_TRAP;
+ return trap;
+}
+
+static void control_close_conn(struct ctrl_connection *ccon)
+{
+ struct ctrl_cmd_def *cd, *cd2;
+
+ osmo_wqueue_clear(&ccon->write_queue);
+ close(ccon->write_queue.bfd.fd);
+ osmo_fd_unregister(&ccon->write_queue.bfd);
+ llist_del(&ccon->list_entry);
+ if (ccon->closed_cb)
+ ccon->closed_cb(ccon);
+ msgb_free(ccon->pending_msg);
+
+ /* clean up deferred commands */
+ llist_for_each_entry_safe(cd, cd2, &ccon->def_cmds, list) {
+ /* delete from list of def_cmds for this ccon */
+ llist_del(&cd->list);
+ /* not strictly needed as this is a slave to the ccon which we
+ * are about to free anyway */
+ talloc_free(cd->cmd);
+ /* set the CMD to null, this is the indication to the user that
+ * the connection for this command has gone */
+ cd->cmd = NULL;
+ }
+
+ talloc_free(ccon);
+}
+
+int ctrl_cmd_handle(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd,
+ void *data)
+{
+ char *request;
+ int i, j, ret, node;
+
+ vector vline, cmdvec, cmds_vec;
+
+ ret = CTRL_CMD_ERROR;
+ cmd->reply = NULL;
+ node = CTRL_NODE_ROOT;
+ cmd->node = data;
+
+ request = talloc_strdup(cmd, cmd->variable);
+ if (!request)
+ goto err;
+
+ for (i=0;i<strlen(request);i++) {
+ if (request[i] == '.')
+ request[i] = ' ';
+ }
+
+ vline = cmd_make_strvec(request);
+ talloc_free(request);
+ if (!vline) {
+ cmd->reply = "cmd_make_strvec failed.";
+ goto err;
+ }
+
+ for (i=0;i<vector_active(vline);i++) {
+ int rc;
+
+ if (ctrl->lookup)
+ rc = ctrl->lookup(data, vline, &node, &cmd->node, &i);
+ else
+ rc = 0;
+
+ if (rc == 1) {
+ /* do nothing */
+ } else if (rc == -ENODEV)
+ goto err_missing;
+ else if (rc == -ERANGE)
+ goto err_index;
+ else {
+ /* If we're here the rest must be the command */
+ cmdvec = vector_init(vector_active(vline)-i);
+ for (j=i; j<vector_active(vline); j++) {
+ vector_set(cmdvec, vector_slot(vline, j));
+ }
+
+ /* Get the command vector of the right node */
+ cmds_vec = vector_lookup(ctrl_node_vec, node);
+
+ if (!cmds_vec) {
+ cmd->reply = "Command not found.";
+ vector_free(cmdvec);
+ break;
+ }
+
+ ret = ctrl_cmd_exec(cmdvec, cmd, cmds_vec, data);
+
+ vector_free(cmdvec);
+ break;
+ }
+
+ if (i+1 == vector_active(vline))
+ cmd->reply = "Command not present.";
+ }
+
+ cmd_free_strvec(vline);
+
+err:
+ if (!cmd->reply) {
+ if (ret == CTRL_CMD_ERROR) {
+ cmd->reply = "An error has occured.";
+ LOGP(DLCTRL, LOGL_NOTICE,
+ "%s: cmd->reply has not been set (ERROR).\n",
+ cmd->variable);
+ } else if (ret == CTRL_CMD_REPLY) {
+ LOGP(DLCTRL, LOGL_NOTICE,
+ "%s: cmd->reply has not been set (type = %d).\n",
+ cmd->variable, cmd->type);
+ cmd->reply = "";
+ } else {
+ cmd->reply = "Command has been handled.";
+ }
+ }
+
+ if (ret == CTRL_CMD_ERROR)
+ cmd->type = CTRL_TYPE_ERROR;
+ return ret;
+
+err_missing:
+ cmd_free_strvec(vline);
+ cmd->type = CTRL_TYPE_ERROR;
+ cmd->reply = "Error while resolving object";
+ return ret;
+err_index:
+ cmd_free_strvec(vline);
+ cmd->type = CTRL_TYPE_ERROR;
+ cmd->reply = "Error while parsing the index.";
+ return ret;
+}
+
+
+static int handle_control_read(struct osmo_fd * bfd)
+{
+ int ret = -1;
+ struct osmo_wqueue *queue;
+ struct ctrl_connection *ccon;
+ struct ipaccess_head *iph;
+ struct ipaccess_head_ext *iph_ext;
+ struct msgb *msg = NULL;
+ struct ctrl_cmd *cmd;
+ struct ctrl_handle *ctrl = bfd->data;
+
+ queue = container_of(bfd, struct osmo_wqueue, bfd);
+ ccon = container_of(queue, struct ctrl_connection, write_queue);
+
+ ret = ipa_msg_recv_buffered(bfd->fd, &msg, &ccon->pending_msg);
+ if (ret <= 0) {
+ if (ret == -EAGAIN)
+ return 0;
+ if (ret == 0)
+ LOGP(DLCTRL, LOGL_INFO, "The control connection was closed\n");
+ else
+ LOGP(DLCTRL, LOGL_ERROR, "Failed to parse ip access message: %d\n", ret);
+
+ goto err;
+ }
+
+ if (msg->len < sizeof(*iph) + sizeof(*iph_ext)) {
+ LOGP(DLCTRL, LOGL_ERROR, "The message is too short.\n");
+ goto err;
+ }
+
+ iph = (struct ipaccess_head *) msg->data;
+ if (iph->proto != IPAC_PROTO_OSMO) {
+ LOGP(DLCTRL, LOGL_ERROR, "Protocol mismatch. We got 0x%x\n", iph->proto);
+ goto err;
+ }
+
+ iph_ext = (struct ipaccess_head_ext *) iph->data;
+ if (iph_ext->proto != IPAC_PROTO_EXT_CTRL) {
+ LOGP(DLCTRL, LOGL_ERROR, "Extended protocol mismatch. We got 0x%x\n", iph_ext->proto);
+ goto err;
+ }
+
+ msg->l2h = iph_ext->data;
+
+ cmd = ctrl_cmd_parse(ccon, msg);
+
+ if (cmd) {
+ cmd->ccon = ccon;
+ if (ctrl_cmd_handle(ctrl, cmd, ctrl->data) != CTRL_CMD_HANDLED) {
+ ctrl_cmd_send(queue, cmd);
+ talloc_free(cmd);
+ }
+ } else {
+ cmd = talloc_zero(ccon, struct ctrl_cmd);
+ if (!cmd)
+ goto err;
+ LOGP(DLCTRL, LOGL_ERROR, "Command parser error.\n");
+ cmd->type = CTRL_TYPE_ERROR;
+ cmd->id = "err";
+ cmd->reply = "Command parser error.";
+ ctrl_cmd_send(queue, cmd);
+ talloc_free(cmd);
+ }
+
+ msgb_free(msg);
+ return 0;
+
+err:
+ control_close_conn(ccon);
+ msgb_free(msg);
+ return ret;
+}
+
+static int control_write_cb(struct osmo_fd *bfd, struct msgb *msg)
+{
+ int rc;
+
+ rc = write(bfd->fd, msg->data, msg->len);
+ if (rc != msg->len)
+ LOGP(DLCTRL, LOGL_ERROR, "Failed to write message to the control connection.\n");
+
+ return rc;
+}
+
+static struct ctrl_connection *ctrl_connection_alloc(void *ctx)
+{
+ struct ctrl_connection *ccon = talloc_zero(ctx, struct ctrl_connection);
+ if (!ccon)
+ return NULL;
+
+ osmo_wqueue_init(&ccon->write_queue, 100);
+ /* Error handling here? */
+
+ INIT_LLIST_HEAD(&ccon->cmds);
+ INIT_LLIST_HEAD(&ccon->def_cmds);
+
+ return ccon;
+}
+
+static int listen_fd_cb(struct osmo_fd *listen_bfd, unsigned int what)
+{
+ int ret, fd, on;
+ struct ctrl_handle *ctrl;
+ struct ctrl_connection *ccon;
+ struct sockaddr_in sa;
+ socklen_t sa_len = sizeof(sa);
+
+
+ if (!(what & BSC_FD_READ))
+ return 0;
+
+ fd = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
+ if (fd < 0) {
+ perror("accept");
+ return fd;
+ }
+ LOGP(DLCTRL, LOGL_INFO, "accept()ed new control connection from %s\n",
+ inet_ntoa(sa.sin_addr));
+
+#ifdef TCP_NODELAY
+ on = 1;
+ ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
+ if (ret != 0) {
+ LOGP(DLCTRL, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno));
+ close(fd);
+ return ret;
+ }
+#endif
+ ccon = ctrl_connection_alloc(listen_bfd->data);
+ if (!ccon) {
+ LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate.\n");
+ close(fd);
+ return -1;
+ }
+
+ ctrl = listen_bfd->data;
+ ccon->write_queue.bfd.data = ctrl;
+ ccon->write_queue.bfd.fd = fd;
+ ccon->write_queue.bfd.when = BSC_FD_READ;
+ ccon->write_queue.read_cb = handle_control_read;
+ ccon->write_queue.write_cb = control_write_cb;
+
+ ret = osmo_fd_register(&ccon->write_queue.bfd);
+ if (ret < 0) {
+ LOGP(DLCTRL, LOGL_ERROR, "Could not register FD.\n");
+ close(ccon->write_queue.bfd.fd);
+ talloc_free(ccon);
+ }
+
+ llist_add(&ccon->list_entry, &ctrl->ccon_list);
+
+ return ret;
+}
+
+static uint64_t get_rate_ctr_value(const struct rate_ctr *ctr, int intv)
+{
+ if (intv >= RATE_CTR_INTV_NUM)
+ return 0;
+
+ /* Absolute value */
+ if (intv == -1) {
+ return ctr->current;
+ } else {
+ return ctr->intv[intv].rate;
+ }
+}
+
+static char *get_all_rate_ctr_in_group(void *ctx, const struct rate_ctr_group *ctrg, int intv)
+{
+ int i;
+ char *counters = talloc_strdup(ctx, "");
+ if (!counters)
+ return NULL;
+
+ for (i=0;i<ctrg->desc->num_ctr;i++) {
+ counters = talloc_asprintf_append(counters, "\n%s.%u.%s %"PRIu64,
+ ctrg->desc->group_name_prefix, ctrg->idx,
+ ctrg->desc->ctr_desc[i].name,
+ get_rate_ctr_value(&ctrg->ctr[i], intv));
+ if (!counters)
+ return NULL;
+ }
+ return counters;
+}
+
+static int get_rate_ctr_group_idx(const struct rate_ctr_group *ctrg, int intv, struct ctrl_cmd *cmd)
+{
+ char *counters;
+
+ counters = get_all_rate_ctr_in_group(cmd, ctrg, intv);
+ if (!counters)
+ goto oom;
+
+ cmd->reply = talloc_asprintf(cmd, "All counters in %s.%u%s",
+ ctrg->desc->group_name_prefix, ctrg->idx, counters);
+ talloc_free(counters);
+ if (!cmd->reply)
+ goto oom;
+
+ return CTRL_CMD_REPLY;
+oom:
+ cmd->reply = "OOM.";
+ return CTRL_CMD_ERROR;
+}
+
+/* rate_ctr */
+CTRL_CMD_DEFINE(rate_ctr, "rate_ctr *");
+static int get_rate_ctr(struct ctrl_cmd *cmd, void *data)
+{
+ int intv;
+ unsigned int idx;
+ char *ctr_group, *ctr_idx, *tmp, *dup, *saveptr, *interval;
+ struct rate_ctr_group *ctrg;
+ const struct rate_ctr *ctr;
+
+ dup = talloc_strdup(cmd, cmd->variable);
+ if (!dup)
+ goto oom;
+
+ /* Skip over possible prefixes (net.) */
+ tmp = strstr(dup, "rate_ctr");
+ if (!tmp) {
+ talloc_free(dup);
+ cmd->reply = "rate_ctr not a token in rate_ctr command!";
+ goto err;
+ }
+
+ strtok_r(tmp, ".", &saveptr);
+ interval = strtok_r(NULL, ".", &saveptr);
+ if (!interval) {
+ talloc_free(dup);
+ cmd->reply = "Missing interval.";
+ goto err;
+ }
+
+ if (!strcmp(interval, "abs")) {
+ intv = -1;
+ } else if (!strcmp(interval, "per_sec")) {
+ intv = RATE_CTR_INTV_SEC;
+ } else if (!strcmp(interval, "per_min")) {
+ intv = RATE_CTR_INTV_MIN;
+ } else if (!strcmp(interval, "per_hour")) {
+ intv = RATE_CTR_INTV_HOUR;
+ } else if (!strcmp(interval, "per_day")) {
+ intv = RATE_CTR_INTV_DAY;
+ } else {
+ talloc_free(dup);
+ cmd->reply = "Wrong interval.";
+ goto err;
+ }
+
+ ctr_group = strtok_r(NULL, ".", &saveptr);
+ ctr_idx = strtok_r(NULL, ".", &saveptr);
+ if (!ctr_group || !ctr_idx) {
+ talloc_free(dup);
+ cmd->reply = "Counter group must be of name.index form e. g. "
+ "e1inp.0";
+ goto err;
+ }
+
+ idx = atoi(ctr_idx);
+
+ ctrg = rate_ctr_get_group_by_name_idx(ctr_group, idx);
+ if (!ctrg) {
+ talloc_free(dup);
+ cmd->reply = "Counter group with given name and index not found";
+ goto err;
+ }
+
+ if (!strlen(saveptr)) {
+ talloc_free(dup);
+ return get_rate_ctr_group_idx(ctrg, intv, cmd);
+ }
+
+ ctr = rate_ctr_get_by_name(ctrg, saveptr);
+ if (!ctr) {
+ cmd->reply = "Counter name not found.";
+ talloc_free(dup);
+ goto err;
+ }
+
+ talloc_free(dup);
+
+ cmd->reply = talloc_asprintf(cmd, "%"PRIu64, get_rate_ctr_value(ctr, intv));
+ if (!cmd->reply)
+ goto oom;
+
+ return CTRL_CMD_REPLY;
+oom:
+ cmd->reply = "OOM";
+err:
+ return CTRL_CMD_ERROR;
+}
+
+static int set_rate_ctr(struct ctrl_cmd *cmd, void *data)
+{
+ cmd->reply = "Can't set rate counter.";
+
+ return CTRL_CMD_ERROR;
+}
+
+static int verify_rate_ctr(struct ctrl_cmd *cmd, const char *value, void *data)
+{
+ return 0;
+}
+
+/* counter */
+CTRL_CMD_DEFINE(counter, "counter *");
+static int get_counter(struct ctrl_cmd *cmd, void *data)
+{
+ char *ctr_name, *tmp, *dup, *saveptr;
+ struct osmo_counter *counter;
+
+ cmd->reply = "OOM";
+ dup = talloc_strdup(cmd, cmd->variable);
+ if (!dup)
+ goto err;
+
+
+ tmp = strstr(dup, "counter");
+ if (!tmp) {
+ talloc_free(dup);
+ goto err;
+ }
+
+ strtok_r(tmp, ".", &saveptr);
+ ctr_name = strtok_r(NULL, "\0", &saveptr);
+
+ if (!ctr_name)
+ goto err;
+
+ counter = osmo_counter_get_by_name(ctr_name);
+ if (!counter) {
+ cmd->reply = "Counter name not found.";
+ talloc_free(dup);
+ goto err;
+ }
+
+ talloc_free(dup);
+
+ cmd->reply = talloc_asprintf(cmd, "%lu", counter->value);
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ goto err;
+ }
+
+ return CTRL_CMD_REPLY;
+err:
+ return CTRL_CMD_ERROR;
+}
+
+static int set_counter(struct ctrl_cmd *cmd, void *data)
+{
+
+ cmd->reply = "Can't set counter.";
+
+ return CTRL_CMD_ERROR;
+}
+
+static int verify_counter(struct ctrl_cmd *cmd, const char *value, void *data)
+{
+ return 0;
+}
+
+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);
+}
+
+struct ctrl_handle *ctrl_interface_setup_dynip(void *data,
+ const char *bind_addr,
+ uint16_t port,
+ ctrl_cmd_lookup lookup)
+{
+ int ret;
+ struct ctrl_handle *ctrl;
+
+ ctrl = talloc_zero(data, struct ctrl_handle);
+ if (!ctrl)
+ return NULL;
+
+ INIT_LLIST_HEAD(&ctrl->ccon_list);
+
+ ctrl->data = data;
+ ctrl->lookup = lookup;
+
+ ctrl_node_vec = vector_init(5);
+ if (!ctrl_node_vec)
+ goto err;
+
+ /* Listen for control connections */
+ ctrl->listen_fd.cb = listen_fd_cb;
+ ctrl->listen_fd.data = ctrl;
+ ret = osmo_sock_init_ofd(&ctrl->listen_fd, AF_INET, SOCK_STREAM, IPPROTO_TCP,
+ bind_addr, port, OSMO_SOCK_F_BIND);
+ if (ret < 0)
+ goto err_vec;
+
+ ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_rate_ctr);
+ if (ret)
+ goto err_vec;
+ ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_counter);
+ if (ret)
+ goto err_vec;
+
+ LOGP(DLCTRL, LOGL_NOTICE, "CTRL at %s %u\n", bind_addr, port);
+ return ctrl;
+err_vec:
+ vector_free(ctrl_node_vec);
+ ctrl_node_vec = NULL;
+err:
+ LOGP(DLCTRL, LOGL_ERROR, "Cannot bind CTRL at %s %u\n",
+ bind_addr, port);
+ talloc_free(ctrl);
+ return NULL;
+}
diff --git a/src/shared/libosmocore/src/ctrl/control_vty.c b/src/shared/libosmocore/src/ctrl/control_vty.c
new file mode 100644
index 00000000..7acb9732
--- /dev/null
+++ b/src/shared/libosmocore/src/ctrl/control_vty.c
@@ -0,0 +1,88 @@
+/* VTY configuration for Control interface
+ *
+ * (C) 2016 by sysmocom s.m.f.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.
+ *
+ * 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 <talloc.h>
+#include <osmocom/ctrl/control_vty.h>
+#include <osmocom/vty/command.h>
+
+static void *ctrl_vty_ctx = NULL;
+static const char *ctrl_vty_bind_addr = NULL;
+
+DEFUN(cfg_ctrl_bind_addr,
+ cfg_ctrl_bind_addr_cmd,
+ "bind A.B.C.D",
+ "Set bind address to listen for Control connections\n"
+ "Local IP address (default 127.0.0.1)\n")
+{
+ talloc_free((char*)ctrl_vty_bind_addr);
+ ctrl_vty_bind_addr = NULL;
+ ctrl_vty_bind_addr = talloc_strdup(ctrl_vty_ctx, argv[0]);
+ return CMD_SUCCESS;
+}
+
+const char *ctrl_vty_get_bind_addr(void)
+{
+ if (!ctrl_vty_bind_addr)
+ return "127.0.0.1";
+ return ctrl_vty_bind_addr;
+}
+
+static struct cmd_node ctrl_node = {
+ L_CTRL_NODE,
+ "%s(config-ctrl)# ",
+ 1,
+};
+
+DEFUN(cfg_ctrl,
+ cfg_ctrl_cmd,
+ "ctrl", "Configure the Control Interface")
+{
+ vty->index = NULL;
+ vty->node = L_CTRL_NODE;
+
+ return CMD_SUCCESS;
+}
+
+static int config_write_ctrl(struct vty *vty)
+{
+ /* So far there's only one element. Omit the entire section if the bind
+ * element is omitted. */
+ if (!ctrl_vty_bind_addr)
+ return CMD_SUCCESS;
+
+ vty_out(vty, "ctrl%s", VTY_NEWLINE);
+ vty_out(vty, " bind %s%s", ctrl_vty_bind_addr, VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+int ctrl_vty_init(void *ctx)
+{
+ ctrl_vty_ctx = ctx;
+ install_element(CONFIG_NODE, &cfg_ctrl_cmd);
+ install_node(&ctrl_node, config_write_ctrl);
+
+ install_element(L_CTRL_NODE, &cfg_ctrl_bind_addr_cmd);
+ return 0;
+}
+
diff --git a/src/shared/libosmocore/src/fsm.c b/src/shared/libosmocore/src/fsm.c
new file mode 100644
index 00000000..0e2c9be4
--- /dev/null
+++ b/src/shared/libosmocore/src/fsm.c
@@ -0,0 +1,519 @@
+/* Osmocom generic Finite State Machine implementation
+ *
+ * (C) 2016 by Harald Welte <laforge@gnumonks.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.
+ *
+ * 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 <stdbool.h>
+#include <string.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+
+/*! \addtogroup fsm
+ * @{
+ */
+
+/*! \file fsm.c
+ * \brief Finite State Machine abstraction
+ *
+ * This is a generic C-language abstraction for implementing finite
+ * state machines within the Osmocom framework. It is intended to
+ * replace existing hand-coded or even only implicitly existing FSMs
+ * all over the existing code base.
+ *
+ * An libosmocore FSM is described by its \ref osmo_fsm description,
+ * which in turn refers to an array of \ref osmo_fsm_state descriptor,
+ * each describing a single state in the FSM.
+ *
+ * The general idea is that all actions performed within one state are
+ * located at one position in the code (the state's action function),
+ * as opposed to the 'message-centric' view of e.g. the existing
+ * state machines of the LAPD(m) coe, where there is one message for
+ * eahc possible event (primitive), and the function then needs to
+ * concern itself on how to handle that event over all possible states.
+ *
+ * For each state, there is a bit-mask of permitted input events for
+ * this state, as well as a bit-mask of permitted new output states to
+ * which the state can change. Furthermore, there is a function
+ * pointer implementing the actual handling of the input events
+ * occurring whilst in thta state.
+ *
+ * Furthermore, each state offers a function pointer that can be
+ * executed just before leaving a state, and another one just after
+ * entering a state.
+ *
+ * When transitioning into a new state, an optional timer number and
+ * time-out can be passed along. The timer is started just after
+ * entering the new state, and will call the \ref osmo_fsm timer_cb
+ * function once it expires. This is intended to be used in telecom
+ * state machines where a given timer (identified by a certain number)
+ * is started to terminate the fsm or terminate the fsm once expected
+ * events are not happening before timeout expiration.
+ *
+ * As there can often be many concurrent FSMs of one given class, we
+ * introduce the concept of \ref osmo_fsm_inst, i.e. an FSM instance.
+ * The instance keeps the actual state, while the \ref osmo_fsm
+ * descriptor contains the static/const descriptor of the FSM's states
+ * and possible transitions.
+ *
+ * osmo_fsm are integrated with the libosmocore logging system. The
+ * logging sub-system is determined by the FSM descriptor, as we assume
+ * one FSM (let's say one related to a location update procedure) is
+ * inevitably always tied to a sub-system. The logging level however
+ * is configurable for each FSM instance, to ensure that e.g. DEBUG
+ * logging can be used for the LU procedure of one subscriber, while
+ * NOTICE level is used for all other subscribers.
+ *
+ * In order to attach private state to the \ref osmo_fsm_inst, it
+ * offers an opaque priv pointer.
+ *
+ */
+
+LLIST_HEAD(osmo_g_fsms);
+static bool fsm_log_addr = true;
+
+/*! \brief specify if FSM instance addresses should be logged or not
+ *
+ * By default, the FSM name includes the pointer address of the \ref
+ * osmo_fsm_inst. This behavior can be disabled (and re-enabled)
+ * using this function.
+ *
+ * \param[in] log_addr Indicate if FSM instance address shall be logged
+ */
+void osmo_fsm_log_addr(bool log_addr)
+{
+ fsm_log_addr = log_addr;
+}
+
+struct osmo_fsm *osmo_fsm_find_by_name(const char *name)
+{
+ struct osmo_fsm *fsm;
+ llist_for_each_entry(fsm, &osmo_g_fsms, list) {
+ if (!strcmp(name, fsm->name))
+ return fsm;
+ }
+ return NULL;
+}
+
+/*! \brief register a FSM with the core
+ *
+ * A FSM descriptor needs to be registered with the core before any
+ * instances can be created for it.
+ *
+ * \param[in] fsm Descriptor of Finite State Machine to be registered
+ * \returns 0 on success; negative on error
+ */
+int osmo_fsm_register(struct osmo_fsm *fsm)
+{
+ if (osmo_fsm_find_by_name(fsm->name))
+ return -EEXIST;
+ llist_add_tail(&fsm->list, &osmo_g_fsms);
+ INIT_LLIST_HEAD(&fsm->instances);
+
+ return 0;
+}
+
+/*! \brief unregister a FSM from the core
+ *
+ * Once the FSM descriptor is unregistered, active instances can still
+ * use it, but no new instances may be created for it.
+ *
+ * \param[in] fsm Descriptor of Finite State Machine to be removed
+ */
+void osmo_fsm_unregister(struct osmo_fsm *fsm)
+{
+ llist_del(&fsm->list);
+}
+
+/* small wrapper function around timer expiration (for logging) */
+static void fsm_tmr_cb(void *data)
+{
+ struct osmo_fsm_inst *fi = data;
+ struct osmo_fsm *fsm = fi->fsm;
+ uint32_t T = fi->T;
+
+ LOGPFSM(fi, "Timeout of T%u\n", fi->T);
+
+ if (fsm->timer_cb) {
+ int rc = fsm->timer_cb(fi);
+ if (rc != 1)
+ return;
+ LOGPFSM(fi, "timer_cb requested termination\n");
+ } else
+ LOGPFSM(fi, "No timer_cb, automatic termination\n");
+
+ /* if timer_cb returns 1 or there is no timer_cb */
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_TIMEOUT, &T);
+}
+
+/*! \brief allocate a new instance of a specified FSM
+ * \param[in] fsm Descriptor of the FSM
+ * \param[in] ctx talloc context from which to allocate memory
+ * \param[in] priv private data reference store in fsm instance
+ * \param[in] log_level The log level for events of this FSM
+ * \returns newly-allocated, initialized and registered FSM instance
+ */
+struct osmo_fsm_inst *osmo_fsm_inst_alloc(struct osmo_fsm *fsm, void *ctx, void *priv,
+ int log_level, const char *id)
+{
+ struct osmo_fsm_inst *fi = talloc_zero(ctx, struct osmo_fsm_inst);
+
+ fi->fsm = fsm;
+ fi->priv = priv;
+ fi->log_level = log_level;
+ fi->timer.data = fi;
+ fi->timer.cb = fsm_tmr_cb;
+ if (id)
+ fi->id = talloc_strdup(fi, id);
+
+ if (!fsm_log_addr) {
+ if (id)
+ fi->name = talloc_asprintf(fi, "%s(%s)", fsm->name, id);
+ else
+ fi->name = talloc_asprintf(fi, "%s", fsm->name);
+ } else {
+ if (id)
+ fi->name = talloc_asprintf(fi, "%s(%s)[%p]", fsm->name,
+ id, fi);
+ else
+ fi->name = talloc_asprintf(fi, "%s[%p]", fsm->name, fi);
+ }
+
+ INIT_LLIST_HEAD(&fi->proc.children);
+ INIT_LLIST_HEAD(&fi->proc.child);
+ llist_add(&fi->list, &fsm->instances);
+
+ LOGPFSM(fi, "Allocated\n");
+
+ return fi;
+}
+
+/*! \brief allocate a new instance of a specified FSM as child of
+ * other FSM instance
+ *
+ * This is like \ref osmo_fsm_inst_alloc but using the parent FSM as
+ * talloc context, and inheriting the log level of the parent.
+ *
+ * \param[in] fsm Descriptor of the to-be-allocated FSM
+ * \param[in] parent Parent FSM instance
+ * \param[in] parent_term_event Event to be sent to parent when terminating
+ * \returns newly-allocated, initialized and registered FSM instance
+ */
+struct osmo_fsm_inst *osmo_fsm_inst_alloc_child(struct osmo_fsm *fsm,
+ struct osmo_fsm_inst *parent,
+ uint32_t parent_term_event)
+{
+ struct osmo_fsm_inst *fi;
+
+ fi = osmo_fsm_inst_alloc(fsm, parent, NULL, parent->log_level,
+ parent->id);
+ if (!fi) {
+ /* indicate immediate termination to caller */
+ osmo_fsm_inst_dispatch(parent, parent_term_event, NULL);
+ return NULL;
+ }
+
+ LOGPFSM(fi, "is child of %s\n", osmo_fsm_inst_name(parent));
+
+ fi->proc.parent = parent;
+ fi->proc.parent_term_event = parent_term_event;
+ llist_add(&fi->proc.child, &parent->proc.children);
+
+ return fi;
+}
+
+/*! \brief delete a given instance of a FSM
+ * \param[in] fsm The FSM to be un-registered and deleted
+ */
+void osmo_fsm_inst_free(struct osmo_fsm_inst *fi)
+{
+ LOGPFSM(fi, "Deallocated\n");
+ osmo_timer_del(&fi->timer);
+ llist_del(&fi->list);
+ talloc_free(fi);
+}
+
+/*! \brief get human-readable name of FSM event
+ * \param[in] fsm FSM descriptor of event
+ * \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)
+{
+ static char buf[32];
+ if (!fsm->event_names) {
+ snprintf(buf, sizeof(buf), "%u", event);
+ return buf;
+ } else
+ return get_value_string(fsm->event_names, event);
+}
+
+/*! \brief get human-readable name of FSM instance
+ * \param[in] fi FSM instance
+ * \returns string rendering of the FSM identity
+ */
+const char *osmo_fsm_inst_name(struct osmo_fsm_inst *fi)
+{
+ if (!fi)
+ return "NULL";
+
+ if (fi->name)
+ return fi->name;
+ else
+ return fi->fsm->name;
+}
+
+/*! \brief get human-readable name of FSM instance
+ * \param[in] fsm FSM descriptor
+ * \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)
+{
+ static char buf[32];
+ if (state >= fsm->num_states) {
+ snprintf(buf, sizeof(buf), "unknown %u", state);
+ return buf;
+ } else
+ return fsm->states[state].name;
+}
+
+/*! \brief perform a state change of the given FSM instance
+ *
+ * Best invoke via the osmo_fsm_inst_state_chg() macro which logs the source
+ * file where the state change was effected. Alternatively, you may pass \a
+ * file as NULL to use the normal file/line indication instead.
+ *
+ * All changes to the FSM instance state must be made via this
+ * function. It verifies that the existing state actually permits a
+ * transiiton to new_state.
+ *
+ * timeout_secs and T are optional parameters, and only have any effect
+ * if timeout_secs is not 0. If the timeout function is used, then the
+ * new_state is entered, and the FSM instances timer is set to expire
+ * in timeout_secs functions. At that time, the FSM's timer_cb
+ * function will be called for handling of the timeout by the user.
+ *
+ * \param[in] fi FSM instance whose state is to change
+ * \param[in] new_state The new state into which we should change
+ * \param[in] timeout_secs Timeout in seconds (if !=0)
+ * \param[in] T Timer number (if \ref timeout_secs != 0)
+ * \param[in] file Calling source file (from osmo_fsm_inst_state_chg macro)
+ * \param[in] line Calling source line (from osmo_fsm_inst_state_chg macro)
+ * \returns 0 on success; negative on error
+ */
+int _osmo_fsm_inst_state_chg(struct osmo_fsm_inst *fi, uint32_t new_state,
+ unsigned long timeout_secs, int T,
+ const char *file, int line)
+{
+ struct osmo_fsm *fsm = fi->fsm;
+ uint32_t old_state = fi->state;
+ const struct osmo_fsm_state *st = &fsm->states[fi->state];
+
+ /* validate if new_state is a valid state */
+ if (!(st->out_state_mask & (1 << new_state))) {
+ LOGPFSMLSRC(fi, LOGL_ERROR, file, line,
+ "transition to state %s not permitted!\n",
+ osmo_fsm_state_name(fsm, new_state));
+ return -EPERM;
+ }
+
+ /* delete the old timer */
+ osmo_timer_del(&fi->timer);
+
+ if (st->onleave)
+ st->onleave(fi, new_state);
+
+ LOGPFSMSRC(fi, file, line, "state_chg to %s\n",
+ osmo_fsm_state_name(fsm, new_state));
+ fi->state = new_state;
+ st = &fsm->states[new_state];
+
+ if (timeout_secs) {
+ fi->T = T;
+ osmo_timer_schedule(&fi->timer, timeout_secs, 0);
+ }
+
+ /* Call 'onenter' last, user might terminate FSM from there */
+ if (st->onenter)
+ st->onenter(fi, old_state);
+
+ return 0;
+}
+
+/*! \brief dispatch an event to an osmocom finite state machine instance
+ *
+ * Best invoke via the osmo_fsm_inst_dispatch() macro which logs the source
+ * file where the event was effected. Alternatively, you may pass \a file as
+ * NULL to use the normal file/line indication instead.
+ *
+ * Any incoming events to \ref osmo_fsm instances must be dispatched to
+ * them via this function. It verifies, whether the event is permitted
+ * based on the current state of the FSM. If not, -1 is returned.
+ *
+ * \param[in] fi FSM instance
+ * \param[in] event Event to send to FSM instance
+ * \param[in] data Data to pass along with the event
+ * \param[in] file Calling source file (from osmo_fsm_inst_dispatch macro)
+ * \param[in] line Calling source line (from osmo_fsm_inst_dispatch macro)
+ * \returns 0 in case of success; negative on error
+ */
+int _osmo_fsm_inst_dispatch(struct osmo_fsm_inst *fi, uint32_t event, void *data,
+ const char *file, int line)
+{
+ struct osmo_fsm *fsm;
+ const struct osmo_fsm_state *fs;
+
+ if (!fi) {
+ LOGPSRC(DLGLOBAL, LOGL_ERROR, file, line,
+ "Trying to dispatch event %u to non-existent"
+ " FSM instance!\n", event);
+ osmo_log_backtrace(DLGLOBAL, LOGL_ERROR);
+ return -ENODEV;
+ }
+
+ fsm = fi->fsm;
+ OSMO_ASSERT(fi->state < fsm->num_states);
+ fs = &fi->fsm->states[fi->state];
+
+ LOGPFSMSRC(fi, file, line,
+ "Received Event %s\n", osmo_fsm_event_name(fsm, event));
+
+ if (((1 << event) & fsm->allstate_event_mask) && fsm->allstate_action) {
+ fsm->allstate_action(fi, event, data);
+ return 0;
+ }
+
+ if (!((1 << event) & fs->in_event_mask)) {
+ LOGPFSMLSRC(fi, LOGL_ERROR, file, line,
+ "Event %s not permitted\n",
+ osmo_fsm_event_name(fsm, event));
+ return -1;
+ }
+ fs->action(fi, event, data);
+
+ return 0;
+}
+
+/*! \brief Terminate FSM instance with given cause
+ *
+ * This safely terminates the given FSM instance by first iterating
+ * over all children and sending them a termination event. Next, it
+ * calls the FSM descriptors cleanup function (if any), followed by
+ * releasing any memory associated with the FSM instance.
+ *
+ * Finally, the parent FSM instance (if any) is notified using the
+ * parent termination event configured at time of FSM instance start.
+ *
+ * \param[in] fi FSM instance to be terminated
+ * \param[in] cause Cause / reason for termination
+ * \param[in] data Opaque event data to be passed with the parent term event
+ * \param[in] file Calling source file (from osmo_fsm_inst_term macro)
+ * \param[in] line Calling source line (from osmo_fsm_inst_term macro)
+ */
+void _osmo_fsm_inst_term(struct osmo_fsm_inst *fi,
+ enum osmo_fsm_term_cause cause, void *data,
+ const char *file, int line)
+{
+ struct osmo_fsm_inst *parent;
+ uint32_t parent_term_event = fi->proc.parent_term_event;
+
+ LOGPFSMSRC(fi, file, line, "Terminating (cause = %s)\n",
+ osmo_fsm_term_cause_name(cause));
+
+ _osmo_fsm_inst_term_children(fi, OSMO_FSM_TERM_PARENT, NULL,
+ file, line);
+
+ /* delete ourselves from the parent */
+ parent = fi->proc.parent;
+ if (parent)
+ LOGPFSMSRC(fi, file, line, "Removing from parent %s\n",
+ osmo_fsm_inst_name(parent));
+ llist_del(&fi->proc.child);
+
+ /* call destructor / clean-up function */
+ if (fi->fsm->cleanup)
+ fi->fsm->cleanup(fi, cause);
+
+ LOGPFSMSRC(fi, file, line, "Freeing instance\n");
+ /* Fetch parent again in case it has changed. */
+ parent = fi->proc.parent;
+ osmo_fsm_inst_free(fi);
+
+ /* indicate our termination to the parent */
+ if (parent && cause != OSMO_FSM_TERM_PARENT)
+ _osmo_fsm_inst_dispatch(parent, parent_term_event, data,
+ file, line);
+}
+
+/*! \brief Terminate all child FSM instances of an FSM instance.
+ *
+ * Iterate over all children and send them a termination event, with the given
+ * cause. Pass OSMO_FSM_TERM_PARENT to avoid dispatching events from the
+ * terminated child FSMs.
+ *
+ * \param[in] fi FSM instance that should be cleared of child FSMs
+ * \param[in] cause Cause / reason for termination (OSMO_FSM_TERM_PARENT)
+ * \param[in] data Opaque event data to be passed with the parent term events
+ * \param[in] file Calling source file (from osmo_fsm_inst_term_children macro)
+ * \param[in] line Calling source line (from osmo_fsm_inst_term_children macro)
+ */
+void _osmo_fsm_inst_term_children(struct osmo_fsm_inst *fi,
+ enum osmo_fsm_term_cause cause,
+ void *data,
+ const char *file, int line)
+{
+ struct osmo_fsm_inst *first_child, *last_seen_first_child;
+
+ /* iterate over all children, starting from the beginning every time:
+ * terminating an FSM may emit events that cause other FSMs to also
+ * terminate and remove themselves from this list. */
+ last_seen_first_child = NULL;
+ while (!llist_empty(&fi->proc.children)) {
+ first_child = llist_entry(fi->proc.children.next,
+ typeof(*first_child),
+ proc.child);
+
+ /* paranoia: do not loop forever */
+ if (first_child == last_seen_first_child) {
+ LOGPFSMLSRC(fi, LOGL_ERROR, file, line,
+ "Internal error while terminating child"
+ " FSMs: a child FSM is stuck\n");
+ break;
+ }
+ last_seen_first_child = first_child;
+
+ /* terminate child */
+ _osmo_fsm_inst_term(first_child, cause, data,
+ file, line);
+ }
+}
+
+const struct value_string osmo_fsm_term_cause_names[] = {
+ OSMO_VALUE_STRING(OSMO_FSM_TERM_PARENT),
+ OSMO_VALUE_STRING(OSMO_FSM_TERM_REQUEST),
+ OSMO_VALUE_STRING(OSMO_FSM_TERM_REGULAR),
+ OSMO_VALUE_STRING(OSMO_FSM_TERM_ERROR),
+ OSMO_VALUE_STRING(OSMO_FSM_TERM_TIMEOUT),
+ { 0, NULL }
+};
+
+/*! @} */
diff --git a/src/shared/libosmocore/src/gb/Makefile.am b/src/shared/libosmocore/src/gb/Makefile.am
index 04d21085..68aed138 100644
--- a/src/shared/libosmocore/src/gb/Makefile.am
+++ b/src/shared/libosmocore/src/gb/Makefile.am
@@ -1,9 +1,9 @@
# 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=2:0:0
+LIBVERSION=4:0:0
-INCLUDES = $(all_includes) -I$(top_srcdir)/include
-AM_CFLAGS = -Wall ${GCC_FVISIBILITY_HIDDEN} -fno-strict-aliasing
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
+AM_CFLAGS = -Wall ${GCC_FVISIBILITY_HIDDEN} -fno-strict-aliasing $(TALLOC_CFLAGS)
# FIXME: this should eventually go into a milenage/Makefile.am
noinst_HEADERS = common_vty.h
@@ -11,7 +11,7 @@ noinst_HEADERS = common_vty.h
if ENABLE_GB
lib_LTLIBRARIES = libosmogb.la
-libosmogb_la_LDFLAGS = $(LTLDFLAGS_OSMOGB) -version-info $(LIBVERSION)
+libosmogb_la_LDFLAGS = $(LTLDFLAGS_OSMOGB) -version-info $(LIBVERSION) $(TALLOC_LIBS)
libosmogb_la_LIBADD = \
$(top_builddir)/src/libosmocore.la \
$(top_builddir)/src/vty/libosmovty.la \
diff --git a/src/shared/libosmocore/src/gb/common_vty.c b/src/shared/libosmocore/src/gb/common_vty.c
index 0bd0b6c3..5b20fcf6 100644
--- a/src/shared/libosmocore/src/gb/common_vty.c
+++ b/src/shared/libosmocore/src/gb/common_vty.c
@@ -4,16 +4,16 @@
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
+ * 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 Affero General Public License for more details.
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU Affero General Public License
+ * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
diff --git a/src/shared/libosmocore/src/gb/gprs_bssgp.c b/src/shared/libosmocore/src/gb/gprs_bssgp.c
index e41c7efb..1ee942f2 100644
--- a/src/shared/libosmocore/src/gb/gprs_bssgp.c
+++ b/src/shared/libosmocore/src/gb/gprs_bssgp.c
@@ -5,16 +5,16 @@
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
+ * 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 Affero General Public License for more details.
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU Affero General Public License
+ * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* TODO:
@@ -31,6 +31,7 @@
#include <osmocom/gsm/tlv.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/stats.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/gprs/gprs_ns.h>
@@ -46,6 +47,7 @@ static const struct rate_ctr_desc bssgp_ctr_description[] = {
{ "bytes.out", "Bytes at BSSGP Level (Out)" },
{ "blocked", "BVC Blocking count" },
{ "discarded", "BVC LLC Discarded count" },
+ { "status", "BVC Status count" },
};
static const struct rate_ctr_group_desc bssgp_ctrg_desc = {
@@ -53,6 +55,7 @@ static const struct rate_ctr_group_desc bssgp_ctrg_desc = {
.group_description = "BSSGP Peer Statistics",
.num_ctr = ARRAY_SIZE(bssgp_ctr_description),
.ctr_desc = bssgp_ctr_description,
+ .class_id = OSMO_STATS_CLASS_PEER,
};
LLIST_HEAD(bssgp_bvc_ctxts);
@@ -398,32 +401,32 @@ static int bssgp_rx_ul_ud(struct msgb *msg, struct tlv_parsed *tp,
return bssgp_prim_cb(&gbp.oph, NULL);
}
-static int bssgp_rx_suspend(struct msgb *msg, struct tlv_parsed *tp,
- struct bssgp_bvc_ctx *ctx)
+static int bssgp_rx_suspend(struct msgb *msg, struct tlv_parsed *tp)
{
struct osmo_bssgp_prim gbp;
struct gprs_ra_id raid;
uint32_t tlli;
+ uint16_t ns_bvci = msgb_bvci(msg);
int rc;
if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
!TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) {
LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx SUSPEND "
- "missing mandatory IE\n", ctx->bvci);
+ "missing mandatory IE\n", ns_bvci);
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
}
- tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
+ tlli = ntohl(tlvp_val32_unal(tp, BSSGP_IE_TLLI));
DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx SUSPEND\n",
- ctx->bvci, tlli);
+ ns_bvci, tlli);
gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
/* Inform GMM about the SUSPEND request */
memset(&gbp, 0, sizeof(gbp));
gbp.nsei = msgb_nsei(msg);
- gbp.bvci = ctx->bvci;
+ gbp.bvci = ns_bvci;
gbp.tlli = tlli;
gbp.ra_id = &raid;
osmo_prim_init(&gbp.oph, SAP_BSSGP_GMM, PRIM_BSSGP_GMM_SUSPEND,
@@ -438,34 +441,34 @@ static int bssgp_rx_suspend(struct msgb *msg, struct tlv_parsed *tp,
return 0;
}
-static int bssgp_rx_resume(struct msgb *msg, struct tlv_parsed *tp,
- struct bssgp_bvc_ctx *ctx)
+static int bssgp_rx_resume(struct msgb *msg, struct tlv_parsed *tp)
{
struct osmo_bssgp_prim gbp;
struct gprs_ra_id raid;
uint32_t tlli;
uint8_t suspend_ref;
+ uint16_t ns_bvci = msgb_bvci(msg);
int rc;
if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
!TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA) ||
!TLVP_PRESENT(tp, BSSGP_IE_SUSPEND_REF_NR)) {
LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESUME "
- "missing mandatory IE\n", ctx->bvci);
+ "missing mandatory IE\n", ns_bvci);
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
}
- tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
+ tlli = ntohl(tlvp_val32_unal(tp, BSSGP_IE_TLLI));
suspend_ref = *TLVP_VAL(tp, BSSGP_IE_SUSPEND_REF_NR);
- DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx RESUME\n", ctx->bvci, tlli);
+ DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx RESUME\n", ns_bvci, tlli);
gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
/* Inform GMM about the RESUME request */
memset(&gbp, 0, sizeof(gbp));
gbp.nsei = msgb_nsei(msg);
- gbp.bvci = ctx->bvci;
+ gbp.bvci = ns_bvci;
gbp.tlli = tlli;
gbp.ra_id = &raid;
gbp.u.resume.suspend_ref = suspend_ref;
@@ -516,6 +519,46 @@ static int bssgp_rx_llc_disc(struct msgb *msg, struct tlv_parsed *tp,
return bssgp_prim_cb(&nmp.oph, NULL);
}
+int bssgp_rx_status(struct msgb *msg, struct tlv_parsed *tp,
+ uint16_t bvci, struct bssgp_bvc_ctx *bctx)
+{
+ struct osmo_bssgp_prim nmp;
+ enum gprs_bssgp_cause cause;
+
+ if (!TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) {
+ LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx STATUS "
+ "missing mandatory IE\n", bvci);
+ cause = BSSGP_CAUSE_PROTO_ERR_UNSPEC;
+ } else {
+ cause = *TLVP_VAL(tp, BSSGP_IE_CAUSE);
+ }
+
+ LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u Rx BVC STATUS, cause=%s\n",
+ bvci, bssgp_cause_str(cause));
+
+ if (cause == BSSGP_CAUSE_BVCI_BLOCKED || cause == BSSGP_CAUSE_UNKNOWN_BVCI) {
+ if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI))
+ LOGP(DBSSGP, LOGL_ERROR,
+ "BSSGP BVCI=%u Rx STATUS cause=%s "
+ "missing conditional BVCI IE\n",
+ bvci, bssgp_cause_str(cause));
+ }
+
+ if (bctx)
+ rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_STATUS]);
+
+ /* send NM_STATUS to NM */
+ memset(&nmp, 0, sizeof(nmp));
+ nmp.nsei = msgb_nsei(msg);
+ nmp.bvci = bvci;
+ nmp.tp = tp;
+ osmo_prim_init(&nmp.oph, SAP_BSSGP_NM, PRIM_NM_STATUS,
+ PRIM_OP_INDICATION, msg);
+
+ return bssgp_prim_cb(&nmp.oph, NULL);
+}
+
+
/* One element (msgb) in a BSSGP Flow Control queue */
struct bssgp_fc_queue_element {
/* linked list of queue elements */
@@ -560,7 +603,7 @@ static void fc_timer_cb(void *data)
fc->queue_depth--;
/* record the time we transmitted this PDU */
- gettimeofday(&time_now, NULL);
+ osmo_gettimeofday(&time_now, NULL);
fc->time_last_pdu = time_now;
/* call the output callback for this FC instance */
@@ -587,19 +630,23 @@ static int fc_queue_timer_cfg(struct bssgp_flow_control *fc)
if (llist_empty(&fc->queue))
return 0;
- fcqe = llist_entry(&fc->queue.next, struct bssgp_fc_queue_element,
+ fcqe = llist_entry(fc->queue.next, struct bssgp_fc_queue_element,
list);
- /* Calculate the point in time at which we will have leaked
- * a sufficient number of bytes from the bucket to transmit
- * the first PDU in the queue */
- msecs = (fcqe->llc_pdu_len * 1000) / fc->bucket_leak_rate;
- /* FIXME: add that time to fc->time_last_pdu and subtract it from
- * current time */
-
- fc->timer.data = fc;
- fc->timer.cb = &fc_timer_cb;
- osmo_timer_schedule(&fc->timer, msecs / 1000, (msecs % 1000) * 1000);
+ if (fc->bucket_leak_rate != 0) {
+ /* Calculate the point in time at which we will have leaked
+ * a sufficient number of bytes from the bucket to transmit
+ * the first PDU in the queue */
+ msecs = (fcqe->llc_pdu_len * 1000) / fc->bucket_leak_rate;
+ /* FIXME: add that time to fc->time_last_pdu and subtract it from
+ * current time */
+ fc->timer.data = fc;
+ fc->timer.cb = &fc_timer_cb;
+ osmo_timer_schedule(&fc->timer, msecs / 1000, (msecs % 1000) * 1000);
+ } else {
+ /* If the PCU is telling us to not send any more data at all,
+ * there's no point starting a timer. */
+ }
return 0;
}
@@ -641,7 +688,7 @@ static int bssgp_fc_needs_queueing(struct bssgp_flow_control *fc, uint32_t pdu_l
/* compute number of centi-seconds that have elapsed since transmitting
* the last PDU (Tc - Tp) */
- gettimeofday(&time_now, NULL);
+ osmo_gettimeofday(&time_now, NULL);
timersub(&time_now, &fc->time_last_pdu, &time_diff);
csecs_elapsed = time_diff.tv_sec*100 + time_diff.tv_usec/10000;
@@ -692,6 +739,7 @@ int bssgp_fc_in(struct bssgp_flow_control *fc, struct msgb *msg,
LOGP(DBSSGP, LOGL_NOTICE, "Single PDU (size=%u) is larger "
"than maximum bucket size (%u)!\n", llc_pdu_len,
fc->bucket_size_max);
+ msgb_free(msg);
return -EIO;
}
@@ -699,7 +747,7 @@ int bssgp_fc_in(struct bssgp_flow_control *fc, struct msgb *msg,
return fc_enqueue(fc, msg, llc_pdu_len, priv);
} else {
/* record the time we transmitted this PDU */
- gettimeofday(&time_now, NULL);
+ osmo_gettimeofday(&time_now, NULL);
fc->time_last_pdu = time_now;
return fc->out_cb(priv, msg, llc_pdu_len, NULL);
}
@@ -718,7 +766,7 @@ void bssgp_fc_init(struct bssgp_flow_control *fc,
fc->bucket_leak_rate = bucket_leak_rate;
fc->max_queue_depth = max_queue_depth;
INIT_LLIST_HEAD(&fc->queue);
- gettimeofday(&fc->time_last_pdu, NULL);
+ osmo_gettimeofday(&fc->time_last_pdu, NULL);
}
/* Initialize the Flow Control parameters for a new MS according to
@@ -742,6 +790,8 @@ int bssgp_fc_ms_init(struct bssgp_flow_control *fc_ms, uint16_t bvci,
static int bssgp_rx_fc_bvc(struct msgb *msg, struct tlv_parsed *tp,
struct bssgp_bvc_ctx *bctx)
{
+ uint32_t old_leak_rate = bctx->fc->bucket_leak_rate;
+ uint32_t old_r_def_ms = bctx->r_default_ms;
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control BVC\n",
bctx->bvci);
@@ -769,6 +819,23 @@ static int bssgp_rx_fc_bvc(struct msgb *msg, struct tlv_parsed *tp,
bctx->r_default_ms = 100 *
ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_R_DEFAULT_MS)) / 8;
+ if (old_leak_rate != 0 && bctx->fc->bucket_leak_rate == 0)
+ LOGP(DBSSGP, LOGL_NOTICE, "BSS instructs us to bucket leak "
+ "rate of 0, stopping all DL GPRS!\n");
+ else if (old_leak_rate == 0 && bctx->fc->bucket_leak_rate != 0)
+ LOGP(DBSSGP, LOGL_NOTICE, "BSS instructs us to bucket leak "
+ "rate of != 0, restarting all DL GPRS!\n");
+
+ if (old_r_def_ms != 0 && bctx->r_default_ms == 0)
+ LOGP(DBSSGP, LOGL_NOTICE, "BSS instructs us to MS default "
+ "bucket leak rate of 0, stopping DL GPRS!\n");
+ else if (old_r_def_ms == 0 && bctx->r_default_ms != 0)
+ LOGP(DBSSGP, LOGL_NOTICE, "BSS instructs us to MS default "
+ "bucket leak rate != 0, restarting DL GPRS!\n");
+
+ /* reconfigure the timer for flow control based on new values */
+ fc_queue_timer_cfg(bctx->fc);
+
/* Send FLOW_CONTROL_BVC_ACK */
return bssgp_tx_fc_bvc_ack(msgb_nsei(msg), *TLVP_VAL(tp, BSSGP_IE_TAG),
msgb_bvci(msg));
@@ -783,10 +850,12 @@ static int bssgp_rx_ptp(struct msgb *msg, struct tlv_parsed *tp,
uint8_t pdu_type = bgph->pdu_type;
int rc = 0;
+ OSMO_ASSERT(pdu_type != BSSGP_PDUT_STATUS);
+
/* If traffic is received on a BVC that is marked as blocked, the
* received PDU shall not be accepted and a STATUS PDU (Cause value:
* BVC Blocked) shall be sent to the peer entity on the signalling BVC */
- if (bctx->state & BVC_S_BLOCKED && pdu_type != BSSGP_PDUT_STATUS) {
+ if (bctx->state & BVC_S_BLOCKED) {
uint16_t bvci = msgb_bvci(msg);
return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &bvci, msg);
}
@@ -820,15 +889,15 @@ static int bssgp_rx_ptp(struct msgb *msg, struct tlv_parsed *tp,
/* FIXME: Send FLOW_CONTROL_MS_ACK */
break;
case BSSGP_PDUT_STATUS:
- /* Some exception has occurred */
- /* FIXME: send NM_STATUS.ind to NM */
+ /* This is already handled in bssgp_rcvmsg() */
+ break;
case BSSGP_PDUT_DOWNLOAD_BSS_PFC:
case BSSGP_PDUT_CREATE_BSS_PFC_ACK:
case BSSGP_PDUT_CREATE_BSS_PFC_NACK:
case BSSGP_PDUT_MODIFY_BSS_PFC:
case BSSGP_PDUT_DELETE_BSS_PFC_ACK:
- DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x not [yet] "
- "implemented\n", bctx->bvci, pdu_type);
+ DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type %s not [yet] "
+ "implemented\n", bctx->bvci, bssgp_pdu_str(pdu_type));
rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
break;
/* those only exist in the SGSN -> BSS direction */
@@ -838,14 +907,14 @@ static int bssgp_rx_ptp(struct msgb *msg, struct tlv_parsed *tp,
case BSSGP_PDUT_RA_CAPA_UPDATE_ACK:
case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
case BSSGP_PDUT_FLOW_CONTROL_MS_ACK:
- DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x only exists "
- "in DL\n", bctx->bvci, pdu_type);
+ DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type %s only exists in DL\n",
+ bctx->bvci, bssgp_pdu_str(pdu_type));
bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
rc = -EINVAL;
break;
default:
- DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x unknown\n",
- bctx->bvci, pdu_type);
+ DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type %s unknown\n",
+ bctx->bvci, bssgp_pdu_str(pdu_type));
rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
break;
}
@@ -862,23 +931,29 @@ static int bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp,
uint8_t pdu_type = bgph->pdu_type;
int rc = 0;
uint16_t ns_bvci = msgb_bvci(msg);
+ uint16_t bvci = bctx ? bctx->bvci : ns_bvci;
switch (bgph->pdu_type) {
case BSSGP_PDUT_SUSPEND:
/* MS wants to suspend */
- rc = bssgp_rx_suspend(msg, tp, bctx);
+ rc = bssgp_rx_suspend(msg, tp);
break;
case BSSGP_PDUT_RESUME:
/* MS wants to resume */
- rc = bssgp_rx_resume(msg, tp, bctx);
+ rc = bssgp_rx_resume(msg, tp);
break;
case BSSGP_PDUT_FLUSH_LL_ACK:
/* BSS informs us it has performed LL FLUSH */
- DEBUGP(DBSSGP, "BSSGP Rx BVCI=%u FLUSH LL ACK\n", bctx->bvci);
+ DEBUGP(DBSSGP, "BSSGP Rx BVCI=%u FLUSH LL ACK\n", bvci);
/* FIXME: send NM_FLUSH_LL.res to NM */
break;
case BSSGP_PDUT_LLC_DISCARD:
/* BSS informs that some LLC PDU's have been discarded */
+ if (!bctx) {
+ LOGP(DBSSGP, LOGL_ERROR,
+ "BSSGP Rx LLC-DISCARD missing mandatory BVCI\n");
+ goto err_mand_ie;
+ }
rc = bssgp_rx_llc_disc(msg, tp, bctx);
break;
case BSSGP_PDUT_BVC_BLOCK:
@@ -911,9 +986,7 @@ static int bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp,
rc = bssgp_rx_bvc_reset(msg, tp, ns_bvci);
break;
case BSSGP_PDUT_STATUS:
- /* Some exception has occurred */
- DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC STATUS\n", bctx->bvci);
- /* FIXME: send NM_STATUS.ind to NM */
+ /* This is already handled in bssgp_rcvmsg() */
break;
/* those only exist in the SGSN -> BSS direction */
case BSSGP_PDUT_PAGING_PS:
@@ -926,14 +999,14 @@ static int bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp,
case BSSGP_PDUT_BVC_BLOCK_ACK:
case BSSGP_PDUT_BVC_UNBLOCK_ACK:
case BSSGP_PDUT_SGSN_INVOKE_TRACE:
- DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x only exists "
- "in DL\n", bctx->bvci, pdu_type);
+ DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type %s only exists in DL\n",
+ bvci, bssgp_pdu_str(pdu_type));
bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
rc = -EINVAL;
break;
default:
- DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x unknown\n",
- bctx->bvci, pdu_type);
+ DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type %s unknown\n",
+ bvci, bssgp_pdu_str(pdu_type));
rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
break;
}
@@ -953,6 +1026,7 @@ int bssgp_rcvmsg(struct msgb *msg)
struct bssgp_bvc_ctx *bctx;
uint8_t pdu_type = bgph->pdu_type;
uint16_t ns_bvci = msgb_bvci(msg);
+ uint16_t bvci = ns_bvci;
int data_len;
int rc = 0;
@@ -968,15 +1042,11 @@ int bssgp_rcvmsg(struct msgb *msg)
rc = bssgp_tlv_parse(&tp, budh->data, data_len);
}
+ if (bvci == BVCI_SIGNALLING && TLVP_PRESENT(&tp, BSSGP_IE_BVCI))
+ bvci = ntohs(*(uint16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI));
+
/* look-up or create the BTS context for this BVC */
- bctx = btsctx_by_bvci_nsei(ns_bvci, msgb_nsei(msg));
- /* Only a RESET PDU can create a new BVC context */
- if (!bctx && pdu_type != BSSGP_PDUT_BVC_RESET) {
- LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU "
- "type %u for unknown BVCI\n", msgb_nsei(msg), ns_bvci,
- pdu_type);
- return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg);
- }
+ bctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg));
if (bctx) {
log_set_context(GPRS_CTX_BVC, bctx);
@@ -985,12 +1055,33 @@ int bssgp_rcvmsg(struct msgb *msg)
msgb_bssgp_len(msg));
}
+ /* Always handle STATUS PDUs, even if they contain an invalid BVCI or
+ * are otherwise unexpected */
+ if (pdu_type == BSSGP_PDUT_STATUS)
+ /* Some exception has occurred */
+ return bssgp_rx_status(msg, &tp, bvci, bctx);
+
+ /* Only a RESET PDU can create a new BVC context, otherwise it must be
+ * registered if a BVCI is given. */
+ if (!bctx && bvci != BVCI_SIGNALLING &&
+ pdu_type != BSSGP_PDUT_BVC_RESET) {
+ LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU "
+ "type %s for unknown BVCI\n", msgb_nsei(msg), bvci,
+ bssgp_pdu_str(pdu_type));
+ return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg);
+ }
+
if (ns_bvci == BVCI_SIGNALLING)
rc = bssgp_rx_sign(msg, &tp, bctx);
else if (ns_bvci == BVCI_PTM)
rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
- else
+ else if (bctx)
rc = bssgp_rx_ptp(msg, &tp, bctx);
+ else
+ LOGP(DBSSGP, LOGL_NOTICE,
+ "NSEI=%u/BVCI=%u Cannot handle PDU type %s for "
+ "unknown BVCI, NS BVCI %u\n",
+ msgb_nsei(msg), bvci, bssgp_pdu_str(pdu_type), ns_bvci);
return rc;
}
@@ -1008,10 +1099,13 @@ int bssgp_tx_dl_ud(struct msgb *msg, uint16_t pdu_lifetime,
uint16_t _pdu_lifetime = htons(pdu_lifetime); /* centi-seconds */
uint16_t drx_params;
+ OSMO_ASSERT(dup != NULL);
+
/* Identifiers from UP: TLLI, BVCI, NSEI (all in msgb->cb) */
if (bvci <= BVCI_PTM ) {
LOGP(DBSSGP, LOGL_ERROR, "Cannot send DL-UD to BVCI %u\n",
bvci);
+ msgb_free(msg);
return -EINVAL;
}
@@ -1019,6 +1113,7 @@ int bssgp_tx_dl_ud(struct msgb *msg, uint16_t pdu_lifetime,
if (!bctx) {
LOGP(DBSSGP, LOGL_ERROR, "Cannot send DL-UD to unknown BVCI %u\n",
bvci);
+ msgb_free(msg);
return -ENODEV;
}
@@ -1038,35 +1133,32 @@ int bssgp_tx_dl_ud(struct msgb *msg, uint16_t pdu_lifetime,
/* FIXME: optional elements: Alignment, UTRAN CCO, LSA, PFI */
- if (dup) {
- /* Old TLLI to help BSS map from old->new */
- if (dup->tlli) {
- uint32_t tlli = htonl(*dup->tlli);
- msgb_tvlv_push(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &tlli);
- }
-
- /* IMSI */
- if (dup->imsi && strlen(dup->imsi)) {
- uint8_t mi[10];
- int imsi_len = gsm48_generate_mid_from_imsi(mi, dup->imsi);
- if (imsi_len > 2)
- msgb_tvlv_push(msg, BSSGP_IE_IMSI,
- imsi_len-2, mi+2);
- }
+ /* Old TLLI to help BSS map from old->new */
+ if (dup->tlli) {
+ uint32_t tlli = htonl(*dup->tlli);
+ msgb_tvlv_push(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &tlli);
+ }
- /* DRX parameters */
- drx_params = htons(dup->drx_parms);
- msgb_tvlv_push(msg, BSSGP_IE_DRX_PARAMS, 2,
- (uint8_t *) &drx_params);
+ /* IMSI */
+ if (dup->imsi && strlen(dup->imsi)) {
+ uint8_t mi[10];
+ int imsi_len = gsm48_generate_mid_from_imsi(mi, dup->imsi);
+ if (imsi_len > 2)
+ msgb_tvlv_push(msg, BSSGP_IE_IMSI,
+ imsi_len-2, mi+2);
+ }
- /* FIXME: Priority */
+ /* DRX parameters */
+ drx_params = htons(dup->drx_parms);
+ msgb_tvlv_push(msg, BSSGP_IE_DRX_PARAMS, 2,
+ (uint8_t *) &drx_params);
- /* MS Radio Access Capability */
- if (dup->ms_ra_cap.len)
- msgb_tvlv_push(msg, BSSGP_IE_MS_RADIO_ACCESS_CAP,
- dup->ms_ra_cap.len, dup->ms_ra_cap.v);
+ /* FIXME: Priority */
- }
+ /* MS Radio Access Capability */
+ if (dup->ms_ra_cap.len)
+ msgb_tvlv_push(msg, BSSGP_IE_MS_RADIO_ACCESS_CAP,
+ dup->ms_ra_cap.len, dup->ms_ra_cap.v);
/* prepend the pdu lifetime */
msgb_tvlv_push(msg, BSSGP_IE_PDU_LIFETIME, 2, (uint8_t *)&_pdu_lifetime);
diff --git a/src/shared/libosmocore/src/gb/gprs_bssgp_bss.c b/src/shared/libosmocore/src/gb/gprs_bssgp_bss.c
index 7d4c07ce..73c13509 100644
--- a/src/shared/libosmocore/src/gb/gprs_bssgp_bss.c
+++ b/src/shared/libosmocore/src/gb/gprs_bssgp_bss.c
@@ -5,16 +5,16 @@
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
+ * 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 Affero General Public License for more details.
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU Affero General Public License
+ * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -34,6 +34,8 @@
#include "common_vty.h"
+#define GSM_IMSI_LENGTH 17
+
uint8_t *bssgp_msgb_tlli_put(struct msgb *msg, uint32_t tlli)
{
uint32_t _tlli = htonl(tlli);
@@ -133,7 +135,7 @@ static struct msgb *common_tx_radio_status(struct bssgp_bvc_ctx *bctx)
static int common_tx_radio_status2(struct msgb *msg, uint8_t cause)
{
msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause);
- LOGPC(DBSSGP, LOGL_NOTICE, "CAUSE=%u\n", cause);
+ LOGPC(DBSSGP, LOGL_NOTICE, "CAUSE=%s\n", bssgp_cause_str(cause));
return gprs_ns_sendmsg(bssgp_nsi, msg);
}
@@ -245,7 +247,7 @@ int bssgp_tx_bvc_block(struct bssgp_bvc_ctx *bctx, uint8_t cause)
uint16_t _bvci = htons(bctx->bvci);
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx BVC-BLOCK "
- "CAUSE=%u\n", bctx->bvci, cause);
+ "CAUSE=%s\n", bctx->bvci, bssgp_cause_str(cause));
msgb_nsei(msg) = bctx->nsei;
msgb_bvci(msg) = 0; /* Signalling */
@@ -285,7 +287,7 @@ int bssgp_tx_bvc_reset(struct bssgp_bvc_ctx *bctx, uint16_t bvci, uint8_t cause)
uint16_t _bvci = htons(bvci);
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx BVC-RESET "
- "CAUSE=%u\n", bvci, cause);
+ "CAUSE=%s\n", bvci, bssgp_cause_str(cause));
msgb_nsei(msg) = bctx->nsei;
msgb_bvci(msg) = 0; /* Signalling */
@@ -315,7 +317,7 @@ int bssgp_tx_bvc_reset(struct bssgp_bvc_ctx *bctx, uint16_t bvci, uint8_t cause)
*/
int bssgp_tx_fc_bvc(struct bssgp_bvc_ctx *bctx, uint8_t tag,
uint32_t bucket_size, uint32_t bucket_leak_rate,
- uint16_t bmax_default_ms, uint32_t r_default_ms,
+ uint32_t bmax_default_ms, uint32_t r_default_ms,
uint8_t *bucket_full_ratio, uint32_t *queue_delay_ms)
{
struct msgb *msg;
@@ -325,19 +327,19 @@ int bssgp_tx_fc_bvc(struct bssgp_bvc_ctx *bctx, uint8_t tag,
if ((bucket_size / 100) > 0xffff)
return -EINVAL;
- e_bucket_size = bucket_size / 100;
+ e_bucket_size = htons(bucket_size / 100);
if ((bucket_leak_rate * 8 / 100) > 0xffff)
return -EINVAL;
- e_leak_rate = (bucket_leak_rate * 8) / 100;
+ e_leak_rate = htons((bucket_leak_rate * 8) / 100);
if ((bmax_default_ms / 100) > 0xffff)
return -EINVAL;
- e_bmax_default_ms = bmax_default_ms / 100;
+ e_bmax_default_ms = htons(bmax_default_ms / 100);
if ((r_default_ms * 8 / 100) > 0xffff)
return -EINVAL;
- e_r_default_ms = (r_default_ms * 8) / 100;
+ e_r_default_ms = htons((r_default_ms * 8) / 100);
if (queue_delay_ms) {
if ((*queue_delay_ms / 10) > 60000)
@@ -345,7 +347,7 @@ int bssgp_tx_fc_bvc(struct bssgp_bvc_ctx *bctx, uint8_t tag,
else if (*queue_delay_ms == 0xFFFFFFFF)
e_queue_delay = 0xFFFF;
else
- e_queue_delay = *queue_delay_ms / 10;
+ e_queue_delay = htons(*queue_delay_ms / 10);
}
msg = bssgp_msgb_alloc();
@@ -498,8 +500,8 @@ int bssgp_rx_paging(struct bssgp_paging_info *pinfo,
if (!TLVP_PRESENT(&tp, BSSGP_IE_IMSI))
goto err_mand_ie;
if (!pinfo->imsi)
- pinfo->imsi = talloc_zero_size(pinfo, 16);
- gsm48_mi_to_string(pinfo->imsi, sizeof(pinfo->imsi),
+ pinfo->imsi = talloc_zero_size(pinfo, GSM_IMSI_LENGTH);
+ gsm48_mi_to_string(pinfo->imsi, GSM_IMSI_LENGTH,
TLVP_VAL(&tp, BSSGP_IE_IMSI),
TLVP_LEN(&tp, BSSGP_IE_IMSI));
@@ -540,11 +542,12 @@ int bssgp_rx_paging(struct bssgp_paging_info *pinfo,
/* Optional (P-)TMSI */
if (TLVP_PRESENT(&tp, BSSGP_IE_TMSI) &&
- TLVP_LEN(&tp, BSSGP_IE_TMSI) >= 4)
+ TLVP_LEN(&tp, BSSGP_IE_TMSI) >= 4) {
if (!pinfo->ptmsi)
pinfo->ptmsi = talloc_zero_size(pinfo, sizeof(uint32_t));
*(pinfo->ptmsi) = ntohl(*(uint32_t *)
TLVP_VAL(&tp, BSSGP_IE_TMSI));
+ }
return 0;
diff --git a/src/shared/libosmocore/src/gb/gprs_bssgp_util.c b/src/shared/libosmocore/src/gb/gprs_bssgp_util.c
index ae4647ef..6fff3621 100644
--- a/src/shared/libosmocore/src/gb/gprs_bssgp_util.c
+++ b/src/shared/libosmocore/src/gb/gprs_bssgp_util.c
@@ -5,16 +5,16 @@
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
+ * 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 Affero General Public License for more details.
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU Affero General Public License
+ * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -62,15 +62,97 @@ static const struct value_string bssgp_cause_strings[] = {
{ 0, NULL },
};
+static const struct value_string bssgp_pdu_strings[] = {
+ { BSSGP_PDUT_DL_UNITDATA, "DL-UNITDATA" },
+ { BSSGP_PDUT_UL_UNITDATA, "UL-UNITDATA" },
+ { BSSGP_PDUT_RA_CAPABILITY, "RA-CAPABILITY" },
+ { BSSGP_PDUT_PTM_UNITDATA, "PTM-UNITDATA" },
+ { BSSGP_PDUT_PAGING_PS, "PAGING-PS" },
+ { BSSGP_PDUT_PAGING_CS, "PAGING-CS" },
+ { BSSGP_PDUT_RA_CAPA_UDPATE, "RA-CAPABILITY-UPDATE" },
+ { BSSGP_PDUT_RA_CAPA_UPDATE_ACK, "RA-CAPABILITY-UPDATE-ACK" },
+ { BSSGP_PDUT_RADIO_STATUS, "RADIO-STATUS" },
+ { BSSGP_PDUT_SUSPEND, "SUSPEND" },
+ { BSSGP_PDUT_SUSPEND_ACK, "SUSPEND-ACK" },
+ { BSSGP_PDUT_SUSPEND_NACK, "SUSPEND-NACK" },
+ { BSSGP_PDUT_RESUME, "RESUME" },
+ { BSSGP_PDUT_RESUME_ACK, "RESUME-ACK" },
+ { BSSGP_PDUT_RESUME_NACK, "RESUME-NACK" },
+ { BSSGP_PDUT_BVC_BLOCK, "BVC-BLOCK" },
+ { BSSGP_PDUT_BVC_BLOCK_ACK, "BVC-BLOCK-ACK" },
+ { BSSGP_PDUT_BVC_RESET, "BVC-RESET" },
+ { BSSGP_PDUT_BVC_RESET_ACK, "BVC-RESET-ACK" },
+ { BSSGP_PDUT_BVC_UNBLOCK, "BVC-UNBLOCK" },
+ { BSSGP_PDUT_BVC_UNBLOCK_ACK, "BVC-UNBLOCK-ACK" },
+ { BSSGP_PDUT_FLOW_CONTROL_BVC, "FLOW-CONTROL-BVC" },
+ { BSSGP_PDUT_FLOW_CONTROL_BVC_ACK, "FLOW-CONTROL-BVC-ACK" },
+ { BSSGP_PDUT_FLOW_CONTROL_MS, "FLOW-CONTROL-MS" },
+ { BSSGP_PDUT_FLOW_CONTROL_MS_ACK, "FLOW-CONTROL-MS-ACK" },
+ { BSSGP_PDUT_FLUSH_LL, "FLUSH-LL" },
+ { BSSGP_PDUT_FLUSH_LL_ACK, "FLUSH-LL-ACK" },
+ { BSSGP_PDUT_LLC_DISCARD, "LLC DISCARDED" },
+ { BSSGP_PDUT_SGSN_INVOKE_TRACE, "SGSN-INVOKE-TRACE" },
+ { BSSGP_PDUT_STATUS, "STATUS" },
+ { BSSGP_PDUT_DOWNLOAD_BSS_PFC, "DOWNLOAD-BSS-PFC" },
+ { BSSGP_PDUT_CREATE_BSS_PFC, "CREATE-BSS-PFC" },
+ { BSSGP_PDUT_CREATE_BSS_PFC_ACK, "CREATE-BSS-PFC-ACK" },
+ { BSSGP_PDUT_CREATE_BSS_PFC_NACK, "CREATE-BSS-PFC-NACK" },
+ { BSSGP_PDUT_MODIFY_BSS_PFC, "MODIFY-BSS-PFC" },
+ { BSSGP_PDUT_MODIFY_BSS_PFC_ACK, "MODIFY-BSS-PFC-ACK" },
+ { BSSGP_PDUT_DELETE_BSS_PFC, "DELETE-BSS-PFC" },
+ { BSSGP_PDUT_DELETE_BSS_PFC_ACK, "DELETE-BSS-PFC-ACK" },
+ { 0, NULL },
+};
+
const char *bssgp_cause_str(enum gprs_bssgp_cause cause)
{
return get_value_string(bssgp_cause_strings, cause);
}
+const char *bssgp_pdu_str(enum bssgp_pdu_type pdu)
+{
+ return get_value_string(bssgp_pdu_strings, pdu);
+}
struct msgb *bssgp_msgb_alloc(void)
{
- return msgb_alloc_headroom(4096, 128, "BSSGP");
+ struct msgb *msg = msgb_alloc_headroom(4096, 128, "BSSGP");
+
+ /* TODO: Add handling of msg == NULL to this function and to all callers */
+ OSMO_ASSERT(msg != NULL);
+
+ msgb_bssgph(msg) = msg->data;
+ return msg;
+}
+
+struct msgb *bssgp_msgb_copy(const struct msgb *msg, const char *name)
+{
+ struct libgb_msgb_cb *old_cb, *new_cb;
+ struct msgb *new_msg;
+
+ new_msg = msgb_copy(msg, name);
+ if (!new_msg)
+ return NULL;
+
+ /* copy GB specific data */
+ old_cb = LIBGB_MSGB_CB(msg);
+ new_cb = LIBGB_MSGB_CB(new_msg);
+
+ if (old_cb->bssgph)
+ new_cb->bssgph = new_msg->_data + (old_cb->bssgph - msg->_data);
+ if (old_cb->llch)
+ new_cb->llch = new_msg->_data + (old_cb->llch - msg->_data);
+
+ /* bssgp_cell_id is a pointer into the old msgb, so we need to make
+ * it a pointer into the new msgb */
+ if (old_cb->bssgp_cell_id)
+ new_cb->bssgp_cell_id = new_msg->_data +
+ (old_cb->bssgp_cell_id - msg->_data);
+ new_cb->nsei = old_cb->nsei;
+ new_cb->bvci = old_cb->bvci;
+ new_cb->tlli = old_cb->tlli;
+
+ return new_msg;
}
/* Transmit a simple response such as BLOCK/UNBLOCK/RESET ACK/NACK */
@@ -99,6 +181,20 @@ int bssgp_tx_status(uint8_t cause, uint16_t *bvci, struct msgb *orig_msg)
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
+ /* GSM 08.18, 10.4.14.1: The BVCI must be included if (and only if) the
+ cause is either "BVCI blocked" or "BVCI unknown" */
+ if (cause == BSSGP_CAUSE_UNKNOWN_BVCI || cause == BSSGP_CAUSE_BVCI_BLOCKED) {
+ if (bvci == NULL)
+ LOGP(DBSSGP, LOGL_ERROR, "BSSGP Tx STATUS, cause=%s: "
+ "missing conditional BVCI\n",
+ bssgp_cause_str(cause));
+ } else {
+ if (bvci != NULL)
+ LOGP(DBSSGP, LOGL_ERROR, "BSSGP Tx STATUS, cause=%s: "
+ "unexpected conditional BVCI\n",
+ bssgp_cause_str(cause));
+ }
+
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u Tx STATUS, cause=%s\n",
bvci ? *bvci : 0, bssgp_cause_str(cause));
msgb_nsei(msg) = msgb_nsei(orig_msg);
diff --git a/src/shared/libosmocore/src/gb/gprs_bssgp_vty.c b/src/shared/libosmocore/src/gb/gprs_bssgp_vty.c
index d8e1d32f..90e50011 100644
--- a/src/shared/libosmocore/src/gb/gprs_bssgp_vty.c
+++ b/src/shared/libosmocore/src/gb/gprs_bssgp_vty.c
@@ -5,16 +5,16 @@
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
+ * 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 Affero General Public License for more details.
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU Affero General Public License
+ * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -33,6 +33,7 @@
#include <osmocom/core/rate_ctr.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/gprs/gprs_bssgp.h>
+#include <osmocom/gprs/gprs_bssgp_bss.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
@@ -42,12 +43,6 @@
#include "common_vty.h"
-/* FIXME: this should go to some common file as it is copied
- * in vty_interface.c of the BSC */
-static const struct value_string gprs_bssgp_timer_strs[] = {
- { 0, NULL }
-};
-
static void log_set_bvc_filter(struct log_target *target,
struct bssgp_bvc_ctx *bctx)
{
@@ -97,9 +92,10 @@ static void dump_bvc(struct vty *vty, struct bssgp_bvc_ctx *bvc, int stats)
if (fc)
vty_out(vty, "FC-BVC(bucket_max: %uoct, leak_rate: "
"%uoct/s, cur_tokens: %uoct, max_q_d: %u, "
- "cur_q_d: %u)\n", fc->bucket_size_max,
+ "cur_q_d: %u)%s", fc->bucket_size_max,
fc->bucket_leak_rate, fc->bucket_counter,
- fc->max_queue_depth, fc->queue_depth);
+ fc->max_queue_depth, fc->queue_depth,
+ VTY_NEWLINE);
}
}
@@ -112,6 +108,30 @@ static void dump_bssgp(struct vty *vty, int stats)
}
}
+DEFUN(bvc_reset, bvc_reset_cmd,
+ "bssgp bvc nsei <0-65535> bvci <0-65535> reset",
+ "Initiate BVC RESET procedure for a given NSEI and BVCI\n"
+ "Filter based on BSSGP Virtual Connection\n"
+ "NSEI of the BVC to be filtered\n"
+ "Network Service Entity Identifier (NSEI)\n"
+ "BVCI of the BVC to be filtered\n"
+ "BSSGP Virtual Connection Identifier (BVCI)\n"
+ "Perform reset procedure\n")
+{
+ int r;
+ uint16_t nsei = atoi(argv[0]), bvci = atoi(argv[1]);
+ struct bssgp_bvc_ctx *bvc = btsctx_by_bvci_nsei(bvci, nsei);
+ if (!bvc) {
+ vty_out(vty, "No BVC for NSEI %d BVCI %d%s", nsei, bvci,
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ r = bssgp_tx_bvc_reset(bvc, bvci, BSSGP_CAUSE_OML_INTERV);
+ vty_out(vty, "Sent BVC RESET for NSEI %d BVCI %d: %d%s", nsei, bvci, r,
+ VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
#define BSSGP_STR "Show information about the BSSGP protocol\n"
DEFUN(show_bssgp, show_bssgp_cmd, "show bssgp",
@@ -135,7 +155,7 @@ DEFUN(show_bvc, show_bvc_cmd, "show bssgp nsei <0-65535> [stats]",
"The NSEI\n" "Include Statistics\n")
{
struct bssgp_bvc_ctx *bvc;
- uint16_t nsei = atoi(argv[1]);
+ uint16_t nsei = atoi(argv[0]);
int show_stats = 0;
if (argc >= 2)
@@ -184,6 +204,7 @@ int bssgp_vty_init(void)
install_element_ve(&show_bssgp_stats_cmd);
install_element_ve(&show_bvc_cmd);
install_element_ve(&logging_fltr_bvc_cmd);
+ install_element_ve(&bvc_reset_cmd);
install_element(CFG_LOG_NODE, &logging_fltr_bvc_cmd);
@@ -192,7 +213,6 @@ int bssgp_vty_init(void)
install_default(L_BSSGP_NODE);
install_element(L_BSSGP_NODE, &libgb_exit_cmd);
install_element(L_BSSGP_NODE, &libgb_end_cmd);
- //install_element(L_BSSGP_NODE, &cfg_bssgp_timer_cmd);
return 0;
}
diff --git a/src/shared/libosmocore/src/gb/gprs_ns.c b/src/shared/libosmocore/src/gb/gprs_ns.c
index cdcf36e1..18845d4b 100644
--- a/src/shared/libosmocore/src/gb/gprs_ns.c
+++ b/src/shared/libosmocore/src/gb/gprs_ns.c
@@ -6,16 +6,16 @@
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
+ * 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 Affero General Public License for more details.
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU Affero General Public License
+ * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -75,6 +75,8 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/stat_item.h>
+#include <osmocom/core/stats.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/signal.h>
#include <osmocom/gprs/gprs_ns.h>
@@ -100,15 +102,27 @@ enum ns_ctr {
NS_CTR_BYTES_OUT,
NS_CTR_BLOCKED,
NS_CTR_DEAD,
+ NS_CTR_REPLACED,
+ NS_CTR_NSEI_CHG,
+ NS_CTR_INV_VCI,
+ NS_CTR_INV_NSEI,
+ NS_CTR_LOST_ALIVE,
+ NS_CTR_LOST_RESET,
};
static const struct rate_ctr_desc nsvc_ctr_description[] = {
- { "packets.in", "Packets at NS Level ( In)" },
- { "packets.out","Packets at NS Level (Out)" },
- { "bytes.in", "Bytes at NS Level ( In)" },
- { "bytes.out", "Bytes at NS Level (Out)" },
- { "blocked", "NS-VC Block count " },
- { "dead", "NS-VC gone dead count " },
+ { "packets.in", "Packets at NS Level ( In)" },
+ { "packets.out","Packets at NS Level (Out)" },
+ { "bytes.in", "Bytes at NS Level ( In)" },
+ { "bytes.out", "Bytes at NS Level (Out)" },
+ { "blocked", "NS-VC Block count " },
+ { "dead", "NS-VC gone dead count " },
+ { "replaced", "NS-VC replaced other count" },
+ { "nsei-chg", "NS-VC changed NSEI count " },
+ { "inv-nsvci", "NS-VCI was invalid count " },
+ { "inv-nsei", "NSEI was invalid count " },
+ { "lost.alive", "ALIVE ACK missing count " },
+ { "lost.reset", "RESET ACK missing count " },
};
static const struct rate_ctr_group_desc nsvc_ctrg_desc = {
@@ -116,8 +130,42 @@ static const struct rate_ctr_group_desc nsvc_ctrg_desc = {
.group_description = "NSVC Peer Statistics",
.num_ctr = ARRAY_SIZE(nsvc_ctr_description),
.ctr_desc = nsvc_ctr_description,
+ .class_id = OSMO_STATS_CLASS_PEER,
};
+enum ns_stat {
+ NS_STAT_ALIVE_DELAY,
+};
+
+static const struct osmo_stat_item_desc nsvc_stat_description[] = {
+ { "alive.delay", "ALIVE reponse time ", "ms", 16, 0 },
+};
+
+static const struct osmo_stat_item_group_desc nsvc_statg_desc = {
+ .group_name_prefix = "ns.nsvc",
+ .group_description = "NSVC Peer Statistics",
+ .num_items = ARRAY_SIZE(nsvc_stat_description),
+ .item_desc = nsvc_stat_description,
+ .class_id = OSMO_STATS_CLASS_PEER,
+};
+
+#define CHECK_TX_RC(rc, nsvc) \
+ if (rc < 0) \
+ LOGP(DNS, LOGL_ERROR, "TX failed (%d) to peer %s\n", \
+ rc, gprs_ns_ll_str(nsvc));
+
+struct msgb *gprs_ns_msgb_alloc(void)
+{
+ struct msgb *msg = msgb_alloc_headroom(NS_ALLOC_SIZE, NS_ALLOC_HEADROOM,
+ "GPRS/NS");
+ if (!msg) {
+ LOGP(DNS, LOGL_ERROR, "Failed to allocate NS message of size %d\n",
+ NS_ALLOC_SIZE);
+ }
+ return msg;
+}
+
+
/*! \brief Lookup struct gprs_nsvc based on NSVCI
* \param[in] nsi NS instance in which to search
* \param[in] nsvci NSVCI to be searched
@@ -136,7 +184,7 @@ struct gprs_nsvc *gprs_nsvc_by_nsvci(struct gprs_ns_inst *nsi, uint16_t nsvci)
/*! \brief Lookup struct gprs_nsvc based on NSEI
* \param[in] nsi NS instance in which to search
* \param[in] nsei NSEI to be searched
- * \returns gprs_nsvc of respective NSEI
+ * \returns first gprs_nsvc of respective NSEI
*/
struct gprs_nsvc *gprs_nsvc_by_nsei(struct gprs_ns_inst *nsi, uint16_t nsei)
{
@@ -148,6 +196,20 @@ struct gprs_nsvc *gprs_nsvc_by_nsei(struct gprs_ns_inst *nsi, uint16_t nsei)
return NULL;
}
+static struct gprs_nsvc *gprs_active_nsvc_by_nsei(struct gprs_ns_inst *nsi,
+ uint16_t nsei)
+{
+ struct gprs_nsvc *nsvc;
+ llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
+ if (nsvc->nsei == nsei) {
+ if (!(nsvc->state & NSE_S_BLOCKED) &&
+ nsvc->state & NSE_S_ALIVE)
+ return nsvc;
+ }
+ }
+ return NULL;
+}
+
/* Lookup struct gprs_nsvc based on remote peer socket addr */
static struct gprs_nsvc *nsvc_by_rem_addr(struct gprs_ns_inst *nsi,
struct sockaddr_in *sin)
@@ -172,12 +234,14 @@ struct gprs_nsvc *gprs_nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci)
nsvc = talloc_zero(nsi, struct gprs_nsvc);
nsvc->nsvci = nsvci;
+ nsvc->nsvci_is_valid = 1;
/* before RESET procedure: BLOCKED and DEAD */
nsvc->state = NSE_S_BLOCKED;
nsvc->nsi = nsi;
nsvc->timer.cb = gprs_ns_timer_cb;
nsvc->timer.data = nsvc;
nsvc->ctrg = rate_ctr_group_alloc(nsvc, &nsvc_ctrg_desc, nsvci);
+ nsvc->statg = osmo_stat_item_group_alloc(nsvc, &nsvc_statg_desc, nsvci);
llist_add(&nsvc->list, &nsi->gprs_nsvcs);
@@ -192,13 +256,15 @@ void gprs_nsvc_delete(struct gprs_nsvc *nsvc)
if (osmo_timer_pending(&nsvc->timer))
osmo_timer_del(&nsvc->timer);
llist_del(&nsvc->list);
+ rate_ctr_group_free(nsvc->ctrg);
+ osmo_stat_item_group_free(nsvc->statg);
talloc_free(nsvc);
}
static void ns_osmo_signal_dispatch(struct gprs_nsvc *nsvc, unsigned int signal,
uint8_t cause)
{
- struct ns_signal_data nssd;
+ struct ns_signal_data nssd = {0};
nssd.nsvc = nsvc;
nssd.cause = cause;
@@ -206,6 +272,30 @@ static void ns_osmo_signal_dispatch(struct gprs_nsvc *nsvc, unsigned int signal,
osmo_signal_dispatch(SS_L_NS, signal, &nssd);
}
+static void ns_osmo_signal_dispatch_mismatch(struct gprs_nsvc *nsvc,
+ struct msgb *msg,
+ uint8_t pdu_type, uint8_t ie_type)
+{
+ struct ns_signal_data nssd = {0};
+
+ nssd.nsvc = nsvc;
+ nssd.pdu_type = pdu_type;
+ nssd.ie_type = ie_type;
+ nssd.msg = msg;
+
+ osmo_signal_dispatch(SS_L_NS, S_NS_MISMATCH, &nssd);
+}
+
+static void ns_osmo_signal_dispatch_replaced(struct gprs_nsvc *nsvc, struct gprs_nsvc *old_nsvc)
+{
+ struct ns_signal_data nssd = {0};
+
+ nssd.nsvc = nsvc;
+ nssd.old_nsvc = old_nsvc;
+
+ osmo_signal_dispatch(SS_L_NS, S_NS_REPLACED, &nssd);
+}
+
/* Section 10.3.2, Table 13 */
static const struct value_string ns_cause_str[] = {
{ NS_CAUSE_TRANSIT_FAIL, "Transit network failure" },
@@ -244,9 +334,17 @@ static int gprs_ns_tx(struct gprs_nsvc *nsvc, struct msgb *msg)
switch (nsvc->ll) {
case GPRS_NS_LL_UDP:
ret = nsip_sendmsg(nsvc, msg);
+ if (ret < 0)
+ LOGP(DNS, LOGL_INFO,
+ "failed to send NS message via UDP: %s\n",
+ strerror(-ret));
break;
case GPRS_NS_LL_FR_GRE:
ret = gprs_ns_frgre_sendmsg(nsvc, msg);
+ if (ret < 0)
+ LOGP(DNS, LOGL_INFO,
+ "failed to send NS message via FR/GRE: %s\n",
+ strerror(-ret));
break;
default:
LOGP(DNS, LOGL_ERROR, "unsupported NS linklayer %u\n", nsvc->ll);
@@ -458,10 +556,20 @@ static void nsvc_start_timer(struct gprs_nsvc *nsvc, enum nsvc_timer_mode mode)
if (osmo_timer_pending(&nsvc->timer))
osmo_timer_del(&nsvc->timer);
+ osmo_gettimeofday(&nsvc->timer_started, NULL);
nsvc->timer_mode = mode;
osmo_timer_schedule(&nsvc->timer, seconds, 0);
}
+static int nsvc_timer_elapsed_ms(struct gprs_nsvc *nsvc)
+{
+ struct timeval now, elapsed;
+ osmo_gettimeofday(&now, NULL);
+ timersub(&now, &nsvc->timer_started, &elapsed);
+
+ return 1000 * elapsed.tv_sec + elapsed.tv_usec / 1000;
+}
+
static void gprs_ns_timer_cb(void *data)
{
struct gprs_nsvc *nsvc = data;
@@ -476,6 +584,7 @@ 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]);
nsvc->alive_retries++;
if (nsvc->alive_retries >
nsvc->nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]) {
@@ -505,6 +614,13 @@ 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]);
+ if (!(nsvc->state & NSE_S_RESET))
+ LOGP(DNS, LOGL_NOTICE,
+ "NSEI=%u Reset timed out but RESET flag is not set\n",
+ nsvc->nsei);
+ /* Mark NS-VC locally as blocked and dead */
+ nsvc->state = NSE_S_BLOCKED | NSE_S_RESET;
/* Chapter 7.3: Re-send the RESET */
gprs_ns_tx_reset(nsvc, NS_CAUSE_OM_INTERVENTION);
/* Re-start Tns-reset timer */
@@ -559,27 +675,24 @@ int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg)
struct gprs_ns_hdr *nsh;
uint16_t bvci = msgb_bvci(msg);
- nsvc = gprs_nsvc_by_nsei(nsi, msgb_nsei(msg));
+ nsvc = gprs_active_nsvc_by_nsei(nsi, msgb_nsei(msg));
if (!nsvc) {
- LOGP(DNS, LOGL_ERROR, "Unable to resolve NSEI %u "
- "to NS-VC!\n", msgb_nsei(msg));
- msgb_free(msg);
- return -EINVAL;
- }
- log_set_context(GPRS_CTX_NSVC, nsvc);
+ int rc;
+ if (gprs_nsvc_by_nsei(nsi, msgb_nsei(msg))) {
+ LOGP(DNS, LOGL_ERROR,
+ "All NS-VCs for NSEI %u are either dead or blocked!\n",
+ msgb_nsei(msg));
+ rc = -EBUSY;
+ } else {
+ LOGP(DNS, LOGL_ERROR, "Unable to resolve NSEI %u "
+ "to NS-VC!\n", msgb_nsei(msg));
+ rc = -EINVAL;
+ }
- if (!(nsvc->state & NSE_S_ALIVE)) {
- LOGP(DNS, LOGL_ERROR, "NSEI=%u is not alive, cannot send\n",
- nsvc->nsei);
msgb_free(msg);
- return -EBUSY;
- }
- if (nsvc->state & NSE_S_BLOCKED) {
- LOGP(DNS, LOGL_ERROR, "NSEI=%u is blocked, cannot send\n",
- nsvc->nsei);
- msgb_free(msg);
- return -EBUSY;
+ return rc;
}
+ log_set_context(GPRS_CTX_NSVC, nsvc);
msg->l2h = msgb_push(msg, sizeof(*nsh) + 3);
nsh = (struct gprs_ns_hdr *) msg->l2h;
@@ -646,20 +759,61 @@ static int gprs_ns_rx_status(struct gprs_nsvc *nsvc, struct msgb *msg)
return 0;
}
+/* Replace a nsvc object with another based on NSVCI.
+ * This function replaces looks for a NSVC with the given NSVCI and replaces it
+ * if possible and necessary. If replaced, the former value of *nsvc is
+ * returned in *old_nsvc.
+ * \return != 0 if *nsvc points to a matching NSVC.
+ */
+static int gprs_nsvc_replace_if_found(uint16_t nsvci,
+ struct gprs_nsvc **nsvc,
+ struct gprs_nsvc **old_nsvc)
+{
+ struct gprs_nsvc *matching_nsvc;
+
+ if ((*nsvc)->nsvci == nsvci) {
+ *old_nsvc = NULL;
+ return 1;
+ }
+
+ matching_nsvc = gprs_nsvc_by_nsvci((*nsvc)->nsi, nsvci);
+
+ if (!matching_nsvc)
+ return 0;
+
+ /* The NS-VCI is already used by this NS-VC */
+
+ char *old_peer;
+
+ /* Exchange the NS-VC objects */
+ *old_nsvc = *nsvc;
+ *nsvc = matching_nsvc;
+
+ /* Do logging */
+ old_peer = talloc_strdup(*old_nsvc, gprs_ns_ll_str(*old_nsvc));
+ LOGP(DNS, LOGL_INFO, "NS-VC changed link (NSVCI=%u) from %s to %s\n",
+ nsvci, old_peer, gprs_ns_ll_str(*nsvc));
+
+ talloc_free(old_peer);
+
+ return 1;
+}
+
/* Section 7.3 */
-static int gprs_ns_rx_reset(struct gprs_nsvc *nsvc, struct msgb *msg)
+static int gprs_ns_rx_reset(struct gprs_nsvc **nsvc, struct msgb *msg)
{
struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h;
struct tlv_parsed tp;
- uint8_t *cause;
- uint16_t *nsvci, *nsei;
+ uint8_t cause;
+ uint16_t nsvci, nsei;
+ struct gprs_nsvc *orig_nsvc = NULL;
int rc;
rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data,
msgb_l2len(msg) - sizeof(*nsh), 0, 0);
if (rc < 0) {
LOGP(DNS, LOGL_ERROR, "NSEI=%u Rx NS RESET "
- "Error during TLV Parse\n", nsvc->nsei);
+ "Error during TLV Parse\n", (*nsvc)->nsei);
return rc;
}
@@ -667,32 +821,217 @@ static int gprs_ns_rx_reset(struct gprs_nsvc *nsvc, struct msgb *msg)
!TLVP_PRESENT(&tp, NS_IE_VCI) ||
!TLVP_PRESENT(&tp, NS_IE_NSEI)) {
LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n");
- gprs_ns_tx_status(nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, msg);
+ gprs_ns_tx_status(*nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, msg);
return -EINVAL;
}
- cause = (uint8_t *) TLVP_VAL(&tp, NS_IE_CAUSE);
- nsvci = (uint16_t *) TLVP_VAL(&tp, NS_IE_VCI);
- nsei = (uint16_t *) TLVP_VAL(&tp, NS_IE_NSEI);
+ cause = *(uint8_t *) TLVP_VAL(&tp, NS_IE_CAUSE);
+ nsvci = ntohs(*(uint16_t *) TLVP_VAL(&tp, NS_IE_VCI));
+ nsei = ntohs(*(uint16_t *) TLVP_VAL(&tp, NS_IE_NSEI));
+
+ LOGP(DNS, LOGL_INFO, "NSVCI=%u%s Rx NS RESET (NSEI=%u, NSVCI=%u, cause=%s)\n",
+ (*nsvc)->nsvci, (*nsvc)->nsvci_is_valid ? "" : "(invalid)",
+ nsei, nsvci, gprs_ns_cause_str(cause));
+
+ if ((*nsvc)->nsvci_is_valid && (*nsvc)->nsvci != nsvci) {
+ if ((*nsvc)->persistent || (*nsvc)->remote_end_is_sgsn) {
+ /* The incoming RESET doesn't match the NSVCI. Send an
+ * appropriate RESET_ACK and ignore the RESET.
+ * See 3GPP TS 08.16, 7.3.1, 2nd paragraph.
+ */
+ ns_osmo_signal_dispatch_mismatch(*nsvc, msg,
+ NS_PDUT_RESET,
+ NS_IE_VCI);
+ rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_INV_VCI]);
+ gprs_ns_tx_reset_ack(*nsvc);
+ return 0;
+ }
- LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS RESET (NSVCI=%u, cause=%s)\n",
- nsvc->nsvci, nsvc->nsei, gprs_ns_cause_str(*cause));
+ /* NS-VCI has changed */
+ if (!gprs_nsvc_replace_if_found(nsvci, nsvc, &orig_nsvc)) {
+ LOGP(DNS, LOGL_INFO, "Creating NS-VC %d replacing %d "
+ "at %s\n",
+ nsvci, (*nsvc)->nsvci,
+ gprs_ns_ll_str(*nsvc));
+ orig_nsvc = *nsvc;
+ *nsvc = gprs_nsvc_create((*nsvc)->nsi, nsvci);
+ (*nsvc)->nsei = nsei;
+ }
+ }
- /* Mark NS-VC as blocked and alive */
- nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE;
+ if ((*nsvc)->nsvci_is_valid && (*nsvc)->nsei != nsei) {
+ if ((*nsvc)->persistent || (*nsvc)->remote_end_is_sgsn) {
+ /* The incoming RESET doesn't match the NSEI. Send an
+ * appropriate RESET_ACK and ignore the RESET.
+ * See 3GPP TS 08.16, 7.3.1, 3rd paragraph.
+ */
+ ns_osmo_signal_dispatch_mismatch(*nsvc, msg,
+ NS_PDUT_RESET,
+ NS_IE_NSEI);
+ rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_INV_NSEI]);
+ rc = gprs_ns_tx_reset_ack(*nsvc);
+ CHECK_TX_RC(rc, *nsvc);
+ return 0;
+ }
- nsvc->nsei = ntohs(*nsei);
- nsvc->nsvci = ntohs(*nsvci);
+ /* NSEI has changed */
+ rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_NSEI_CHG]);
+ (*nsvc)->nsei = nsei;
+ }
- /* start the test procedure */
- gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE);
- nsvc_start_timer(nsvc, NSVC_TIMER_TNS_TEST);
+ /* Mark NS-VC as blocked and alive */
+ (*nsvc)->state = NSE_S_BLOCKED | NSE_S_ALIVE;
+
+ if (orig_nsvc) {
+ rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_REPLACED]);
+ ns_osmo_signal_dispatch_replaced(*nsvc, orig_nsvc);
+
+ /* Update the ll info fields */
+ gprs_ns_ll_copy(*nsvc, orig_nsvc);
+ gprs_ns_ll_clear(orig_nsvc);
+ } else {
+ (*nsvc)->nsei = nsei;
+ (*nsvc)->nsvci = nsvci;
+ (*nsvc)->nsvci_is_valid = 1;
+ rate_ctr_group_upd_idx((*nsvc)->ctrg, nsvci);
+ osmo_stat_item_group_udp_idx((*nsvc)->statg, nsvci);
+ }
/* inform interested parties about the fact that this NSVC
* has received RESET */
- ns_osmo_signal_dispatch(nsvc, S_NS_RESET, *cause);
+ ns_osmo_signal_dispatch(*nsvc, S_NS_RESET, cause);
+
+ rc = gprs_ns_tx_reset_ack(*nsvc);
+
+ /* start the test procedure */
+ gprs_ns_tx_simple((*nsvc), NS_PDUT_ALIVE);
+ nsvc_start_timer((*nsvc), NSVC_TIMER_TNS_TEST);
+
+ return rc;
+}
+
+static int gprs_ns_rx_reset_ack(struct gprs_nsvc **nsvc, struct msgb *msg)
+{
+ struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h;
+ struct tlv_parsed tp;
+ uint16_t nsvci, nsei;
+ struct gprs_nsvc *orig_nsvc = NULL;
+ int rc;
+
+ rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data,
+ msgb_l2len(msg) - sizeof(*nsh), 0, 0);
+ if (rc < 0) {
+ LOGP(DNS, LOGL_ERROR, "NSEI=%u Rx NS RESET ACK "
+ "Error during TLV Parse\n", (*nsvc)->nsei);
+ return rc;
+ }
+
+ if (!TLVP_PRESENT(&tp, NS_IE_VCI) ||
+ !TLVP_PRESENT(&tp, NS_IE_NSEI)) {
+ LOGP(DNS, LOGL_ERROR, "NS RESET ACK Missing mandatory IE\n");
+ rc = gprs_ns_tx_status(*nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, msg);
+ CHECK_TX_RC(rc, *nsvc);
+ return -EINVAL;
+ }
+
+ nsvci = ntohs(tlvp_val16_unal(&tp, NS_IE_VCI));
+ nsei = ntohs(tlvp_val16_unal(&tp, NS_IE_NSEI));
+
+ LOGP(DNS, LOGL_INFO, "NSVCI=%u%s Rx NS RESET ACK (NSEI=%u, NSVCI=%u)\n",
+ (*nsvc)->nsvci, (*nsvc)->nsvci_is_valid ? "" : "(invalid)",
+ nsei, nsvci);
+
+ if (!((*nsvc)->state & NSE_S_RESET)) {
+ /* Not waiting for a RESET_ACK on this NS-VC, ignore it.
+ * See 3GPP TS 08.16, 7.3.1, 5th paragraph.
+ */
+ LOGP(DNS, LOGL_ERROR,
+ "NS RESET ACK Discarding unexpected message for "
+ "NS-VCI %d from SGSN NSEI=%d\n",
+ nsvci, nsei);
+ return 0;
+ }
- return gprs_ns_tx_reset_ack(nsvc);
+ if (!(*nsvc)->nsvci_is_valid) {
+ LOGP(DNS, LOGL_NOTICE,
+ "NS RESET ACK Uninitialised NS-VC (%u) for "
+ "NS-VCI %d, NSEI=%d from %s\n",
+ (*nsvc)->nsvci, nsvci, nsei, gprs_ns_ll_str(*nsvc));
+ return -EINVAL;
+ }
+
+ if ((*nsvc)->nsvci != nsvci) {
+ /* NS-VCI has changed */
+
+ /* if !0, use another NSVC object that matches the NSVCI */
+ int use_other_nsvc;
+
+ /* Only do this with BSS peers */
+ use_other_nsvc = !(*nsvc)->remote_end_is_sgsn &&
+ !(*nsvc)->persistent;
+
+ if (use_other_nsvc)
+ /* Update *nsvc to point to the right NSVC object */
+ use_other_nsvc = gprs_nsvc_replace_if_found(nsvci, nsvc,
+ &orig_nsvc);
+
+ if (!use_other_nsvc) {
+ /* The incoming RESET_ACK doesn't match the NSVCI.
+ * See 3GPP TS 08.16, 7.3.1, 4th paragraph.
+ */
+ ns_osmo_signal_dispatch_mismatch(*nsvc, msg,
+ NS_PDUT_RESET_ACK,
+ NS_IE_VCI);
+ rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_INV_VCI]);
+ LOGP(DNS, LOGL_ERROR,
+ "NS RESET ACK Unknown NS-VCI %d (%s NSEI=%d) "
+ "from %s\n",
+ nsvci,
+ (*nsvc)->remote_end_is_sgsn ? "SGSN" : "BSS",
+ nsei, gprs_ns_ll_str(*nsvc));
+ return -EINVAL;
+ }
+
+ /* Notify others */
+ rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_REPLACED]);
+ ns_osmo_signal_dispatch_replaced(*nsvc, orig_nsvc);
+
+ /* Update the ll info fields */
+ gprs_ns_ll_copy(*nsvc, orig_nsvc);
+ gprs_ns_ll_clear(orig_nsvc);
+ } else if ((*nsvc)->nsei != nsei) {
+ if ((*nsvc)->persistent || (*nsvc)->remote_end_is_sgsn) {
+ /* The incoming RESET_ACK doesn't match the NSEI.
+ * See 3GPP TS 08.16, 7.3.1, 4th paragraph.
+ */
+ ns_osmo_signal_dispatch_mismatch(*nsvc, msg,
+ NS_PDUT_RESET_ACK,
+ NS_IE_NSEI);
+ rate_ctr_inc(&(*nsvc)->ctrg->ctr[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));
+ return -EINVAL;
+ }
+
+ /* NSEI has changed */
+ rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_NSEI_CHG]);
+ (*nsvc)->nsei = nsei;
+ }
+
+ /* Mark NS-VC as blocked and alive */
+ (*nsvc)->state = NSE_S_BLOCKED | NSE_S_ALIVE;
+ (*nsvc)->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE;
+ rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_BLOCKED]);
+ if ((*nsvc)->persistent || (*nsvc)->remote_end_is_sgsn) {
+ /* stop RESET timer */
+ osmo_timer_del(&(*nsvc)->timer);
+ }
+ /* Initiate TEST proc.: Send ALIVE and start timer */
+ rc = gprs_ns_tx_simple(*nsvc, NS_PDUT_ALIVE);
+ nsvc_start_timer(*nsvc, NSVC_TIMER_TNS_TEST);
+
+ return rc;
}
static int gprs_ns_rx_block(struct gprs_nsvc *nsvc, struct msgb *msg)
@@ -730,6 +1069,13 @@ static int gprs_ns_rx_block(struct gprs_nsvc *nsvc, struct msgb *msg)
return gprs_ns_tx_simple(nsvc, NS_PDUT_BLOCK_ACK);
}
+int gprs_ns_vc_create(struct gprs_ns_inst *nsi, struct msgb *msg,
+ struct gprs_nsvc *fallback_nsvc,
+ struct gprs_nsvc **new_nsvc);
+
+int gprs_ns_process_msg(struct gprs_ns_inst *nsi, struct msgb *msg,
+ struct gprs_nsvc **nsvc);
+
/*! \brief Receive incoming NS message from underlying transport layer
* \param nsi NS instance to which the data belongs
* \param[in] msg message buffer containing newly-received data
@@ -743,78 +1089,221 @@ static int gprs_ns_rx_block(struct gprs_nsvc *nsvc, struct msgb *msg)
int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg,
struct sockaddr_in *saddr, enum gprs_ns_ll ll)
{
- struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h;
struct gprs_nsvc *nsvc;
int rc = 0;
/* look up the NSVC based on source address */
nsvc = nsvc_by_rem_addr(nsi, saddr);
+
if (!nsvc) {
- struct tlv_parsed tp;
- uint16_t nsei;
- if (nsh->pdu_type == NS_PDUT_STATUS) {
- LOGP(DNS, LOGL_INFO, "Ignoring NS STATUS from %s:%u "
- "for non-existing NS-VC\n",
- inet_ntoa(saddr->sin_addr), ntohs(saddr->sin_port));
- return 0;
- }
- /* Only the RESET procedure creates a new NSVC */
- if (nsh->pdu_type != NS_PDUT_RESET) {
- /* Since we have no NSVC, we have to use a fake */
- nsvc = nsi->unknown_nsvc;
- log_set_context(GPRS_CTX_NSVC, nsvc);
- LOGP(DNS, LOGL_INFO, "Rejecting NS PDU type 0x%0x "
- "from %s:%u for non-existing NS-VC\n",
- nsh->pdu_type, inet_ntoa(saddr->sin_addr),
- ntohs(saddr->sin_port));
- nsvc->nsvci = nsvc->nsei = 0xfffe;
- nsvc->ip.bts_addr = *saddr;
- nsvc->state = NSE_S_ALIVE;
- nsvc->ll = ll;
-#if 0
- return gprs_ns_tx_reset(nsvc, NS_CAUSE_PDU_INCOMP_PSTATE);
-#else
- return gprs_ns_tx_status(nsvc,
- NS_CAUSE_PDU_INCOMP_PSTATE, 0,
- msg);
-#endif
- }
- rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data,
- msgb_l2len(msg) - sizeof(*nsh), 0, 0);
+ struct gprs_nsvc *fallback_nsvc;
+
+ fallback_nsvc = nsi->unknown_nsvc;
+ log_set_context(GPRS_CTX_NSVC, fallback_nsvc);
+ fallback_nsvc->ip.bts_addr = *saddr;
+ fallback_nsvc->ll = ll;
+
+ rc = gprs_ns_vc_create(nsi, msg, fallback_nsvc, &nsvc);
+
+ if (rc < 0)
+ return rc;
+
+ rc = 0;
+ }
+
+ if (nsvc)
+ rc = gprs_ns_process_msg(nsi, msg, &nsvc);
+
+ return rc;
+}
+
+const char *gprs_ns_ll_str(struct gprs_nsvc *nsvc)
+{
+ static char buf[80];
+ snprintf(buf, sizeof(buf), "%s:%u",
+ inet_ntoa(nsvc->ip.bts_addr.sin_addr),
+ ntohs(nsvc->ip.bts_addr.sin_port));
+ buf[sizeof(buf) - 1] = '\0';
+
+ return buf;
+}
+
+void gprs_ns_ll_copy(struct gprs_nsvc *nsvc, struct gprs_nsvc *other)
+{
+ nsvc->ll = other->ll;
+
+ switch (nsvc->ll) {
+ case GPRS_NS_LL_UDP:
+ nsvc->ip = other->ip;
+ break;
+ case GPRS_NS_LL_FR_GRE:
+ nsvc->frgre = other->frgre;
+ break;
+ default:
+ break;
+ }
+}
+
+void gprs_ns_ll_clear(struct gprs_nsvc *nsvc)
+{
+ switch (nsvc->ll) {
+ case GPRS_NS_LL_UDP:
+ nsvc->ip.bts_addr.sin_addr.s_addr = INADDR_ANY;
+ nsvc->ip.bts_addr.sin_port = 0;
+ break;
+ case GPRS_NS_LL_FR_GRE:
+ nsvc->frgre.bts_addr.sin_addr.s_addr = INADDR_ANY;
+ nsvc->frgre.bts_addr.sin_port = 0;
+ break;
+ default:
+ break;
+ }
+}
+
+/*! \brief Create/get NS-VC independently from underlying transport layer
+ * \param nsi NS instance to which the data belongs
+ * \param[in] msg message buffer containing newly-received data
+ * \param[in] fallback_nsvc is used to send error messages back to the peer
+ * and to initialise the ll info of a created NS-VC object
+ * \param[out] new_nsvc contains a pointer to a NS-VC object if one has
+ * been created or found
+ * \returns < 0 in case of error, GPRS_NS_CS_SKIPPED if a message has been
+ * skipped, GPRS_NS_CS_REJECTED if a message has been rejected and
+ * answered accordingly, GPRS_NS_CS_CREATED if a new NS-VC object
+ * has been created and registered, and GPRS_NS_CS_FOUND if an
+ * existing NS-VC object has been found with the same NSEI.
+ *
+ * This contains the initial NS automaton state (NS-VC not yet attached).
+ */
+int gprs_ns_vc_create(struct gprs_ns_inst *nsi, struct msgb *msg,
+ struct gprs_nsvc *fallback_nsvc,
+ struct gprs_nsvc **new_nsvc)
+{
+ struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *)msg->l2h;
+ struct gprs_nsvc *existing_nsvc;
+
+ struct tlv_parsed tp;
+ uint16_t nsvci;
+ uint16_t nsei;
+
+ int rc;
+
+ if (nsh->pdu_type == NS_PDUT_STATUS) {
+ /* Do not respond, see 3GPP TS 08.16, 7.5.1 */
+ LOGP(DNS, LOGL_INFO, "Ignoring NS STATUS from %s "
+ "for non-existing NS-VC\n",
+ gprs_ns_ll_str(fallback_nsvc));
+ return GPRS_NS_CS_SKIPPED;
+ }
+
+ if (nsh->pdu_type == NS_PDUT_ALIVE_ACK) {
+ /* Ignore this, see 3GPP TS 08.16, 7.4.1 */
+ LOGP(DNS, LOGL_INFO, "Ignoring NS ALIVE ACK from %s "
+ "for non-existing NS-VC\n",
+ gprs_ns_ll_str(fallback_nsvc));
+ return GPRS_NS_CS_SKIPPED;
+ }
+
+ if (nsh->pdu_type == NS_PDUT_RESET_ACK) {
+ /* Ignore this, see 3GPP TS 08.16, 7.3.1 */
+ LOGP(DNS, LOGL_INFO, "Ignoring NS RESET ACK from %s "
+ "for non-existing NS-VC\n",
+ gprs_ns_ll_str(fallback_nsvc));
+ return GPRS_NS_CS_SKIPPED;
+ }
+
+ /* Only the RESET procedure creates a new NSVC */
+ if (nsh->pdu_type != NS_PDUT_RESET) {
+ /* Since we have no NSVC, we have to use a fake */
+ log_set_context(GPRS_CTX_NSVC, fallback_nsvc);
+ LOGP(DNS, LOGL_INFO, "Rejecting NS PDU type 0x%0x "
+ "from %s for non-existing NS-VC\n",
+ nsh->pdu_type, gprs_ns_ll_str(fallback_nsvc));
+ fallback_nsvc->nsvci = fallback_nsvc->nsei = 0xfffe;
+ fallback_nsvc->nsvci_is_valid = 0;
+ fallback_nsvc->state = NSE_S_ALIVE;
+
+ rc = gprs_ns_tx_status(fallback_nsvc,
+ NS_CAUSE_PDU_INCOMP_PSTATE, 0, msg);
if (rc < 0) {
- LOGP(DNS, LOGL_ERROR, "Rx NS RESET Error %d during "
- "TLV Parse\n", rc);
+ LOGP(DNS, LOGL_ERROR, "TX failed (%d) to peer %s\n",
+ rc, gprs_ns_ll_str(fallback_nsvc));
return rc;
}
- if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) ||
- !TLVP_PRESENT(&tp, NS_IE_VCI) ||
- !TLVP_PRESENT(&tp, NS_IE_NSEI)) {
- LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n");
- gprs_ns_tx_status(nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0,
- msg);
- return -EINVAL;
- }
- nsei = ntohs(*(uint16_t *)TLVP_VAL(&tp, NS_IE_NSEI));
- /* Check if we already know this NSEI, the remote end might
- * simply have changed addresses, or it is a SGSN */
- nsvc = gprs_nsvc_by_nsei(nsi, nsei);
- if (!nsvc) {
- nsvc = gprs_nsvc_create(nsi, 0xffff);
- nsvc->ll = ll;
- log_set_context(GPRS_CTX_NSVC, nsvc);
- LOGP(DNS, LOGL_INFO, "Creating NS-VC for BSS at %s:%u\n",
- inet_ntoa(saddr->sin_addr), ntohs(saddr->sin_port));
- }
- /* Update the remote peer IP address/port */
- nsvc->ip.bts_addr = *saddr;
- } else
- msgb_nsei(msg) = nsvc->nsei;
+ return GPRS_NS_CS_REJECTED;
+ }
- log_set_context(GPRS_CTX_NSVC, nsvc);
+ rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data,
+ msgb_l2len(msg) - sizeof(*nsh), 0, 0);
+ if (rc < 0) {
+ LOGP(DNS, LOGL_ERROR, "Rx NS RESET Error %d during "
+ "TLV Parse\n", rc);
+ return rc;
+ }
+ if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) ||
+ !TLVP_PRESENT(&tp, NS_IE_VCI) || !TLVP_PRESENT(&tp, NS_IE_NSEI)) {
+ LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n");
+ rc = gprs_ns_tx_status(fallback_nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0,
+ msg);
+ CHECK_TX_RC(rc, fallback_nsvc);
+ return -EINVAL;
+ }
+ nsvci = ntohs(*(uint16_t *) TLVP_VAL(&tp, NS_IE_VCI));
+ nsei = ntohs(*(uint16_t *) TLVP_VAL(&tp, NS_IE_NSEI));
+ /* Check if we already know this NSVCI, the remote end might
+ * simply have changed addresses, or it is a SGSN */
+ existing_nsvc = gprs_nsvc_by_nsvci(nsi, nsvci);
+ if (!existing_nsvc) {
+ *new_nsvc = gprs_nsvc_create(nsi, 0xffff);
+ (*new_nsvc)->nsvci_is_valid = 0;
+ log_set_context(GPRS_CTX_NSVC, *new_nsvc);
+ gprs_ns_ll_copy(*new_nsvc, fallback_nsvc);
+ LOGP(DNS, LOGL_INFO, "Creating NS-VC for BSS at %s\n",
+ gprs_ns_ll_str(fallback_nsvc));
+
+ return GPRS_NS_CS_CREATED;
+ }
+
+ /* Check NSEI */
+ if (existing_nsvc->nsei != nsei) {
+ LOGP(DNS, LOGL_NOTICE,
+ "NS-VC changed NSEI (NSVCI=%u) from %u to %u\n",
+ nsvci, existing_nsvc->nsei, nsei);
+
+ /* Override old NSEI */
+ existing_nsvc->nsei = nsei;
+
+ /* Do statistics */
+ rate_ctr_inc(&existing_nsvc->ctrg->ctr[NS_CTR_NSEI_CHG]);
+ }
+
+ *new_nsvc = existing_nsvc;
+ gprs_ns_ll_copy(*new_nsvc, fallback_nsvc);
+ return GPRS_NS_CS_FOUND;
+}
+
+/*! \brief Process NS message independently from underlying transport layer
+ * \param nsi NS instance to which the data belongs
+ * \param[in] msg message buffer containing newly-received data
+ * \param[inout] nsvc refers to the virtual connection, may be modified when
+ * processing a NS_RESET
+ * \returns 0 in case of success, < 0 in case of error
+ *
+ * This contains the main NS automaton.
+ */
+int gprs_ns_process_msg(struct gprs_ns_inst *nsi, struct msgb *msg,
+ struct gprs_nsvc **nsvc)
+{
+ struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h;
+ int rc = 0;
+
+ msgb_nsei(msg) = (*nsvc)->nsei;
+
+ log_set_context(GPRS_CTX_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(&(*nsvc)->ctrg->ctr[NS_CTR_PKTS_IN]);
+ rate_ctr_add(&(*nsvc)->ctrg->ctr[NS_CTR_BYTES_IN], msgb_l2len(msg));
switch (nsh->pdu_type) {
case NS_PDUT_ALIVE:
@@ -822,69 +1311,61 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg,
* NS-ALIVE out of the blue, we might have been re-started
* and should send a NS-RESET to make sure everything recovers
* fine. */
- if (nsvc->state == NSE_S_BLOCKED)
- rc = gprs_ns_tx_reset(nsvc, NS_CAUSE_PDU_INCOMP_PSTATE);
- else
- rc = gprs_ns_tx_alive_ack(nsvc);
+ if ((*nsvc)->state == NSE_S_BLOCKED)
+ rc = gprs_nsvc_reset((*nsvc), NS_CAUSE_PDU_INCOMP_PSTATE);
+ else if (!((*nsvc)->state & NSE_S_RESET))
+ rc = gprs_ns_tx_alive_ack(*nsvc);
break;
case NS_PDUT_ALIVE_ACK:
+ if ((*nsvc)->timer_mode == NSVC_TIMER_TNS_ALIVE)
+ osmo_stat_item_set((*nsvc)->statg->items[NS_STAT_ALIVE_DELAY],
+ nsvc_timer_elapsed_ms(*nsvc));
/* stop Tns-alive and start Tns-test */
- nsvc_start_timer(nsvc, NSVC_TIMER_TNS_TEST);
- if (nsvc->remote_end_is_sgsn) {
+ nsvc_start_timer(*nsvc, NSVC_TIMER_TNS_TEST);
+ if ((*nsvc)->remote_end_is_sgsn) {
/* FIXME: this should be one level higher */
- if (nsvc->state & NSE_S_BLOCKED)
- rc = gprs_ns_tx_unblock(nsvc);
+ if ((*nsvc)->state & NSE_S_BLOCKED)
+ rc = gprs_ns_tx_unblock(*nsvc);
}
break;
case NS_PDUT_UNITDATA:
/* actual user data */
- rc = gprs_ns_rx_unitdata(nsvc, msg);
+ rc = gprs_ns_rx_unitdata(*nsvc, msg);
break;
case NS_PDUT_STATUS:
- rc = gprs_ns_rx_status(nsvc, msg);
+ rc = gprs_ns_rx_status(*nsvc, msg);
break;
case NS_PDUT_RESET:
rc = gprs_ns_rx_reset(nsvc, msg);
break;
case NS_PDUT_RESET_ACK:
- LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS RESET ACK\n", nsvc->nsei);
- /* mark NS-VC as blocked + active */
- nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE;
- nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE;
- rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]);
- if (nsvc->persistent || nsvc->remote_end_is_sgsn) {
- /* stop RESET timer */
- osmo_timer_del(&nsvc->timer);
- }
- /* Initiate TEST proc.: Send ALIVE and start timer */
- rc = gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE);
- nsvc_start_timer(nsvc, NSVC_TIMER_TNS_TEST);
+ rc = gprs_ns_rx_reset_ack(nsvc, msg);
break;
case NS_PDUT_UNBLOCK:
/* Section 7.2: unblocking procedure */
- LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS UNBLOCK\n", nsvc->nsei);
- nsvc->state &= ~NSE_S_BLOCKED;
- ns_osmo_signal_dispatch(nsvc, S_NS_UNBLOCK, 0);
- rc = gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK);
+ LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS UNBLOCK\n", (*nsvc)->nsei);
+ (*nsvc)->state &= ~NSE_S_BLOCKED;
+ ns_osmo_signal_dispatch(*nsvc, S_NS_UNBLOCK, 0);
+ rc = gprs_ns_tx_simple(*nsvc, NS_PDUT_UNBLOCK_ACK);
break;
case NS_PDUT_UNBLOCK_ACK:
- LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS UNBLOCK ACK\n", nsvc->nsei);
+ LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS UNBLOCK ACK\n", (*nsvc)->nsei);
/* mark NS-VC as unblocked + active */
- nsvc->state = NSE_S_ALIVE;
- nsvc->remote_state = NSE_S_ALIVE;
- ns_osmo_signal_dispatch(nsvc, S_NS_UNBLOCK, 0);
+ (*nsvc)->state = NSE_S_ALIVE;
+ (*nsvc)->remote_state = NSE_S_ALIVE;
+ ns_osmo_signal_dispatch(*nsvc, S_NS_UNBLOCK, 0);
break;
case NS_PDUT_BLOCK:
- rc = gprs_ns_rx_block(nsvc, msg);
+ rc = gprs_ns_rx_block(*nsvc, msg);
break;
case NS_PDUT_BLOCK_ACK:
- LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS BLOCK ACK\n", nsvc->nsei);
+ LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS BLOCK ACK\n", (*nsvc)->nsei);
/* mark remote NS-VC as blocked + active */
- nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE;
+ (*nsvc)->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE;
break;
default:
LOGP(DNS, LOGL_NOTICE, "NSEI=%u Rx Unknown NS PDU type 0x%02x\n",
- nsvc->nsei, nsh->pdu_type);
+ (*nsvc)->nsei, nsh->pdu_type);
rc = -EINVAL;
break;
}
@@ -912,21 +1393,19 @@ struct gprs_ns_inst *gprs_ns_instantiate(gprs_ns_cb_t *cb, void *ctx)
/* Create the dummy NSVC that we use for sending
* messages to non-existant/unknown NS-VC's */
nsi->unknown_nsvc = gprs_nsvc_create(nsi, 0xfffe);
+ nsi->unknown_nsvc->nsvci_is_valid = 0;
llist_del(&nsi->unknown_nsvc->list);
+ INIT_LLIST_HEAD(&nsi->unknown_nsvc->list);
return nsi;
}
-/*! \brief Destroy an entire NS instance
- * \param nsi gprs_ns_inst that is to be destroyed
- *
- * This function releases all resources associated with the
- * NS-instance.
- */
-void gprs_ns_destroy(struct gprs_ns_inst *nsi)
+void gprs_ns_close(struct gprs_ns_inst *nsi)
{
struct gprs_nsvc *nsvc, *nsvc2;
+ gprs_nsvc_delete(nsi->unknown_nsvc);
+
/* delete all NSVCs and clear their timers */
llist_for_each_entry_safe(nsvc, nsvc2, &nsi->gprs_nsvcs, list)
gprs_nsvc_delete(nsvc);
@@ -935,8 +1414,19 @@ void gprs_ns_destroy(struct gprs_ns_inst *nsi)
if (nsi->nsip.fd.data) {
close(nsi->nsip.fd.fd);
osmo_fd_unregister(&nsi->nsip.fd);
+ nsi->nsip.fd.data = NULL;
}
+}
+/*! \brief Destroy an entire NS instance
+ * \param nsi gprs_ns_inst that is to be destroyed
+ *
+ * This function releases all resources associated with the
+ * NS-instance.
+ */
+void gprs_ns_destroy(struct gprs_ns_inst *nsi)
+{
+ gprs_ns_close(nsi);
/* free the NSI */
talloc_free(nsi);
}
@@ -1051,6 +1541,13 @@ int gprs_ns_nsip_listen(struct gprs_ns_inst *nsi)
if (ret < 0)
return ret;
+ ret = setsockopt(nsi->nsip.fd.fd, IPPROTO_IP, IP_TOS,
+ &nsi->nsip.dscp, sizeof(nsi->nsip.dscp));
+ if (ret < 0)
+ LOGP(DNS, LOGL_ERROR,
+ "Failed to set the DSCP to %d with ret(%d) errno(%d)\n",
+ nsi->nsip.dscp, ret, errno);
+
return ret;
}
@@ -1063,20 +1560,26 @@ int gprs_ns_nsip_listen(struct gprs_ns_inst *nsi)
* will not only send a NS-RESET, but also set the state to BLOCKED and
* start the Tns-reset timer.
*/
-void gprs_nsvc_reset(struct gprs_nsvc *nsvc, uint8_t cause)
+int gprs_nsvc_reset(struct gprs_nsvc *nsvc, uint8_t cause)
{
+ int rc;
+
LOGP(DNS, LOGL_INFO, "NSEI=%u RESET procedure based on API request\n",
nsvc->nsei);
/* Mark NS-VC locally as blocked and dead */
- nsvc->state = NSE_S_BLOCKED;
+ nsvc->state = NSE_S_BLOCKED | NSE_S_RESET;
+
/* Send NS-RESET PDU */
- if (gprs_ns_tx_reset(nsvc, cause) < 0) {
+ rc = gprs_ns_tx_reset(nsvc, cause);
+ if (rc < 0) {
LOGP(DNS, LOGL_ERROR, "NSEI=%u, error resetting NS-VC\n",
nsvc->nsei);
}
/* Start Tns-reset */
nsvc_start_timer(nsvc, NSVC_TIMER_TNS_RESET);
+
+ return rc;
}
/*! \brief Establish a NS connection (from the BSS) to the SGSN
@@ -1100,7 +1603,6 @@ struct gprs_nsvc *gprs_ns_nsip_connect(struct gprs_ns_inst *nsi,
nsvc = gprs_nsvc_create(nsi, nsvci);
nsvc->ip.bts_addr = *dest;
nsvc->nsei = nsei;
- nsvc->nsvci = nsvci;
nsvc->remote_end_is_sgsn = 1;
gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION);
diff --git a/src/shared/libosmocore/src/gb/gprs_ns_frgre.c b/src/shared/libosmocore/src/gb/gprs_ns_frgre.c
index e557c7e8..68225417 100644
--- a/src/shared/libosmocore/src/gb/gprs_ns_frgre.c
+++ b/src/shared/libosmocore/src/gb/gprs_ns_frgre.c
@@ -8,16 +8,16 @@
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
+ * 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 Affero General Public License for more details.
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU Affero General Public License
+ * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -43,12 +43,16 @@
#define GRE_PTYPE_IPv4 0x0800
#define GRE_PTYPE_KAR 0x0000 /* keepalive response */
+#ifndef IPPROTO_GRE
+# define IPPROTO_GRE 47
+#endif
+
struct gre_hdr {
uint16_t flags;
uint16_t ptype;
} __attribute__ ((packed));
-#if defined(__FreeBSD__) || defined(__APPLE__)
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__CYGWIN__)
/**
* On BSD the IPv4 struct is called struct ip and instead of iXX
* the members are called ip_XX. One could change this code to use
diff --git a/src/shared/libosmocore/src/gb/gprs_ns_vty.c b/src/shared/libosmocore/src/gb/gprs_ns_vty.c
index fac431cb..ee305ba5 100644
--- a/src/shared/libosmocore/src/gb/gprs_ns_vty.c
+++ b/src/shared/libosmocore/src/gb/gprs_ns_vty.c
@@ -5,16 +5,16 @@
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
+ * 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 Affero General Public License for more details.
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU Affero General Public License
+ * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -71,7 +71,7 @@ static void log_set_nsvc_filter(struct log_target *target,
static struct cmd_node ns_node = {
L_NS_NODE,
- "%s(ns)#",
+ "%s(config-ns)# ",
1,
};
@@ -131,6 +131,9 @@ static int config_write_ns(struct vty *vty)
if (vty_nsi->nsip.local_port)
vty_out(vty, " encapsulation udp local-port %u%s",
vty_nsi->nsip.local_port, VTY_NEWLINE);
+ if (vty_nsi->nsip.dscp)
+ vty_out(vty, " encapsulation udp dscp %d%s",
+ vty_nsi->nsip.dscp, VTY_NEWLINE);
vty_out(vty, " encapsulation framerelay-gre enabled %u%s",
vty_nsi->frgre.enabled ? 1 : 0, VTY_NEWLINE);
@@ -164,8 +167,10 @@ static void dump_nse(struct vty *vty, struct gprs_nsvc *nsvc, int stats)
inet_ntoa(nsvc->ip.bts_addr.sin_addr),
ntohs(nsvc->ip.bts_addr.sin_port));
vty_out(vty, "%s", VTY_NEWLINE);
- if (stats)
+ if (stats) {
vty_out_rate_ctr_group(vty, " ", nsvc->ctrg);
+ vty_out_stat_item_group(vty, " ", nsvc->statg);
+ }
}
static void dump_ns(struct vty *vty, struct gprs_ns_inst *nsi, int stats)
@@ -454,6 +459,16 @@ DEFUN(cfg_nsip_local_port, cfg_nsip_local_port_cmd,
return CMD_SUCCESS;
}
+DEFUN(cfg_nsip_dscp, cfg_nsip_dscp_cmd,
+ "encapsulation udp dscp <0-255>",
+ ENCAPS_STR "NS over UDP Encapsulation\n"
+ "Set DSCP/TOS on the UDP socket\n" "DSCP Value\n")
+{
+ int dscp = atoi(argv[0]);
+ vty_nsi->nsip.dscp = dscp;
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_frgre_local_ip, cfg_frgre_local_ip_cmd,
"encapsulation framerelay-gre local-ip A.B.C.D",
ENCAPS_STR "NS over Frame Relay over GRE Encapsulation\n"
@@ -486,21 +501,31 @@ DEFUN(cfg_frgre_enable, cfg_frgre_enable_cmd,
}
DEFUN(nsvc_nsei, nsvc_nsei_cmd,
- "nsvc nsei <0-65535> (block|unblock|reset)",
+ "nsvc (nsei|nsvci) <0-65535> (block|unblock|reset)",
"Perform an operation on a NSVC\n"
"NSEI to identify NS-VC Identifier (NS-VCI)\n"
+ "NS-VC Identifier (NS-VCI)\n"
"The NSEI\n"
"Initiate BLOCK procedure\n"
"Initiate UNBLOCK procedure\n"
"Initiate RESET procedure\n")
{
- uint16_t nsvci = atoi(argv[0]);
- const char *operation = argv[1];
+ const char *id_type = argv[0];
+ uint16_t id = atoi(argv[1]);
+ const char *operation = argv[2];
struct gprs_nsvc *nsvc;
- nsvc = gprs_nsvc_by_nsei(vty_nsi, nsvci);
+ if (!strcmp(id_type, "nsei"))
+ nsvc = gprs_nsvc_by_nsei(vty_nsi, id);
+ else if (!strcmp(id_type, "nsvci"))
+ nsvc = gprs_nsvc_by_nsvci(vty_nsi, id);
+ else {
+ vty_out(vty, "%%No such id_type '%s'%s", id_type, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
if (!nsvc) {
- vty_out(vty, "No such NSVCI (%u)%s", nsvci, VTY_NEWLINE);
+ vty_out(vty, "No such %s (%u)%s", id_type, id, VTY_NEWLINE);
return CMD_WARNING;
}
@@ -548,8 +573,16 @@ DEFUN(logging_fltr_nsvc,
int gprs_ns_vty_init(struct gprs_ns_inst *nsi)
{
+ static bool vty_elements_installed = false;
+
vty_nsi = nsi;
+ /* Regression test code may call this function repeatedly, so make sure
+ * that VTY elements are not duplicated, which would assert. */
+ if (vty_elements_installed)
+ return 0;
+ vty_elements_installed = true;
+
install_element_ve(&show_ns_cmd);
install_element_ve(&show_ns_stats_cmd);
install_element_ve(&show_nse_cmd);
@@ -572,6 +605,7 @@ int gprs_ns_vty_init(struct gprs_ns_inst *nsi)
install_element(L_NS_NODE, &cfg_ns_timer_cmd);
install_element(L_NS_NODE, &cfg_nsip_local_ip_cmd);
install_element(L_NS_NODE, &cfg_nsip_local_port_cmd);
+ install_element(L_NS_NODE, &cfg_nsip_dscp_cmd);
install_element(L_NS_NODE, &cfg_frgre_enable_cmd);
install_element(L_NS_NODE, &cfg_frgre_local_ip_cmd);
diff --git a/src/shared/libosmocore/src/gb/libosmogb.map b/src/shared/libosmocore/src/gb/libosmogb.map
index d65819b4..9aec2805 100644
--- a/src/shared/libosmocore/src/gb/libosmogb.map
+++ b/src/shared/libosmocore/src/gb/libosmogb.map
@@ -2,10 +2,12 @@ LIBOSMOGB_1.0 {
global:
bssgp_cause_str;
bssgp_create_cell_id;
+bssgp_pdu_str;
bssgp_fc_in;
bssgp_fc_init;
bssgp_fc_ms_init;
bssgp_msgb_alloc;
+bssgp_msgb_copy;
bssgp_msgb_tlli_put;
bssgp_parse_cell_id;
bssgp_tx_bvc_block;
@@ -38,6 +40,7 @@ bssgp_nsi;
gprs_ns_cause_str;
gprs_ns_destroy;
+gprs_ns_close;
gprs_ns_frgre_listen;
gprs_ns_frgre_sendmsg;
gprs_ns_instantiate;
@@ -53,6 +56,10 @@ gprs_ns_tx_reset;
gprs_ns_tx_status;
gprs_ns_tx_unblock;
gprs_ns_vty_init;
+gprs_ns_ll_str;
+gprs_ns_ll_copy;
+gprs_ns_ll_clear;
+gprs_ns_msgb_alloc;
gprs_nsvc_create;
gprs_nsvc_delete;
diff --git a/src/shared/libosmocore/src/gsm/Makefile.am b/src/shared/libosmocore/src/gsm/Makefile.am
index 0544e0a1..4ec441fd 100644
--- a/src/shared/libosmocore/src/gsm/Makefile.am
+++ b/src/shared/libosmocore/src/gsm/Makefile.am
@@ -1,8 +1,9 @@
# 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=3:0:0
+# 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=7:0:1
-INCLUDES = $(all_includes) -I$(top_srcdir)/include
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include $(TALLOC_CFLAGS)
AM_CFLAGS = -Wall ${GCC_FVISIBILITY_HIDDEN}
# FIXME: this should eventually go into a milenage/Makefile.am
@@ -10,18 +11,30 @@ 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_LTLIBRARIES = libgsmint.la
lib_LTLIBRARIES = libosmogsm.la
-libosmogsm_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c gsm_utils.c \
- rsl.c gsm48.c gsm48_ie.c gsm0808.c sysinfo.c \
- gprs_cipher_core.c gsm0480.c abis_nm.c gsm0502.c \
+libgsmint_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c comp128v23.c \
+ gsm_utils.c rsl.c gsm48.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 \
- lapd_core.c lapdm.c \
- auth_core.c auth_comp128v1.c auth_milenage.c \
- milenage/aes-encblock.c milenage/aes-internal.c \
- milenage/aes-internal-enc.c milenage/milenage.c gan.c
+ lapd_core.c lapdm.c kasumi.c gsm_04_08_gprs.c \
+ auth_core.c auth_comp128v1.c auth_comp128v23.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 gprs_gea.c gsm0503_conv.c oap.c
+libgsmint_la_LDFLAGS = -no-undefined
+libgsmint_la_LIBADD = $(top_builddir)/src/libosmocore.la
-libosmogsm_la_LDFLAGS = $(LTLDFLAGS_OSMOGSM) -version-info $(LIBVERSION)
-libosmogsm_la_LIBADD = $(top_builddir)/src/libosmocore.la
+libosmogsm_la_SOURCES =
+libosmogsm_la_LDFLAGS = $(LTLDFLAGS_OSMOGSM) -version-info $(LIBVERSION) -no-undefined $(TALLOC_LIBS)
+libosmogsm_la_LIBADD = libgsmint.la
EXTRA_DIST = libosmogsm.map
+
+# Convolutional codes generation
+gsm0503_conv.c:
+ $(AM_V_GEN)python2 $(top_srcdir)/utils/conv_gen.py
+
+CLEANFILES = gsm0503_conv.c
diff --git a/src/shared/libosmocore/src/gsm/a5.c b/src/shared/libosmocore/src/gsm/a5.c
index 356060ab..dbba0f20 100644
--- a/src/shared/libosmocore/src/gsm/a5.c
+++ b/src/shared/libosmocore/src/gsm/a5.c
@@ -34,46 +34,72 @@
* \brief Osmocom GSM A5 ciphering algorithm implementation
*/
+#include <errno.h>
#include <string.h>
+#include <stdbool.h>
#include <osmocom/gsm/a5.h>
+#include <osmocom/gsm/kasumi.h>
+#include <osmocom/crypt/auth.h>
-/*! \brief Main method to generate a A5/x cipher stream
- * \param[in] n Which A5/x method to use
- * \param[in] key 8 byte array for the key (as received from the SIM)
+/* Somme OS (like Nuttx) don't have ENOTSUP */
+#ifndef ENOTSUP
+#define ENOTSUP EINVAL
+#endif
+
+/* ------------------------------------------------------------------------ */
+/* A5/3&4 */
+/* ------------------------------------------------------------------------ */
+
+/*! \brief Generate a GSM A5/4 cipher stream
+ * \param[in] key 16 byte array for the key (as received from the SIM)
* \param[in] fn Frame number
* \param[out] dl Pointer to array of ubits to return Downlink cipher stream
* \param[out] ul Pointer to array of ubits to return Uplink cipher stream
+ * \param[in] fn_correct true if fn is a real GSM frame number and thus requires internal conversion
*
- * Currently A5/[0-2] are supported.
- * Either (or both) of dl/ul can be NULL if not needed.
+ * Either (or both) of dl/ul should be NULL if not needed.
+ *
+ * Implementation based on specifications from 3GPP TS 55.216, 3GPP TR 55.919 and ETSI TS 135 202
+ * with slight simplifications (CE hardcoded to 0).
*/
void
-osmo_a5(int n, const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
+_a5_4(const uint8_t *ck, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct)
{
- switch (n)
- {
- case 0:
- if (dl)
- memset(dl, 0x00, 114);
- if (ul)
- memset(ul, 0x00, 114);
- break;
-
- case 1:
- osmo_a5_1(key, fn, dl, ul);
- break;
-
- case 2:
- osmo_a5_2(key, fn, dl, ul);
- break;
-
- default:
- /* a5/[3..7] not supported here/yet */
- break;
- }
+ uint8_t i, gamma[32], uplink[15];
+ uint32_t fn_count = (fn_correct) ? osmo_a5_fn_count(fn) : fn;
+
+ if (ul) {
+ _kasumi_kgcore(0xF, 0, fn_count, 0, ck, gamma, 228);
+ for(i = 0; i < 15; i++) uplink[i] = (gamma[i + 14] << 2) + (gamma[i + 15] >> 6);
+ osmo_pbit2ubit(ul, uplink, 114);
+ }
+ if (dl) {
+ _kasumi_kgcore(0xF, 0, fn_count, 0, ck, gamma, 114);
+ osmo_pbit2ubit(dl, gamma, 114);
+ }
}
+/*! \brief Generate a GSM A5/3 cipher stream
+ * \param[in] key 8 byte array for the key (as received from the SIM)
+ * \param[in] fn Frame number
+ * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
+ * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
+ * \param[in] fn_correct true if fn is a real GSM frame number and thus requires internal conversion
+ *
+ * Either (or both) of dl/ul should be NULL if not needed.
+ *
+ * Implementation based on specifications from 3GPP TS 55.216, 3GPP TR 55.919 and ETSI TS 135 202
+ * with slight simplifications (CE hardcoded to 0).
+ */
+void
+_a5_3(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct)
+{
+ uint8_t ck[16];
+ osmo_c4(ck, key);
+ /* internal function require 128 bit key so we expand by concatenating supplied 64 bit key */
+ _a5_4(ck, fn, dl, ul, fn_correct);
+}
/* ------------------------------------------------------------------------ */
/* A5/1&2 common stuff */
@@ -187,7 +213,7 @@ _a5_1_get_output(uint32_t r[])
* Either (or both) of dl/ul can be NULL if not needed.
*/
void
-osmo_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
+_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
{
uint32_t r[3] = {0, 0, 0};
uint32_t fn_count;
@@ -240,6 +266,10 @@ osmo_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
}
}
+void osmo_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
+{
+ osmo_a5(1, key, fn, dl, ul);
+}
/* ------------------------------------------------------------------------ */
/* A5/2 */
@@ -304,7 +334,7 @@ _a5_2_get_output(uint32_t r[])
* Either (or both) of dl/ul can be NULL if not needed.
*/
void
-osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
+_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
{
uint32_t r[4] = {0, 0, 0, 0};
uint32_t fn_count;
@@ -364,4 +394,56 @@ osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
}
}
+void osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
+{
+ osmo_a5(2, key, fn, dl, ul);
+}
+
+/*! \brief Main method to generate a A5/x cipher stream
+ * \param[in] n Which A5/x method to use
+ * \param[in] key 8 or 16 (for a5/4) byte array for the key (as received from the SIM)
+ * \param[in] fn Frame number
+ * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
+ * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
+ * \returns 0 for success, -ENOTSUP for invalid cipher selection.
+ *
+ * Currently A5/[0-4] are supported.
+ * Either (or both) of dl/ul can be NULL if not needed.
+ */
+int
+osmo_a5(int n, const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
+{
+ switch (n)
+ {
+ case 0:
+ if (dl)
+ memset(dl, 0x00, 114);
+ if (ul)
+ memset(ul, 0x00, 114);
+ break;
+
+ case 1:
+ _a5_1(key, fn, dl, ul);
+ break;
+
+ case 2:
+ _a5_2(key, fn, dl, ul);
+ break;
+
+ case 3:
+ _a5_3(key, fn, dl, ul, true);
+ break;
+
+ case 4:
+ _a5_4(key, fn, dl, ul, true);
+ break;
+
+ default:
+ /* a5/[5..7] not supported here/yet */
+ return -ENOTSUP;
+ }
+
+ return 0;
+}
+
/*! @} */
diff --git a/src/shared/libosmocore/src/gsm/abis_nm.c b/src/shared/libosmocore/src/gsm/abis_nm.c
index f6d4003e..73e3c7e5 100644
--- a/src/shared/libosmocore/src/gsm/abis_nm.c
+++ b/src/shared/libosmocore/src/gsm/abis_nm.c
@@ -36,6 +36,9 @@
#include <osmocom/gsm/protocol/gsm_12_21.h>
#include <osmocom/gsm/abis_nm.h>
+const char abis_nm_ipa_magic[13] = "com.ipaccess";
+const char abis_nm_osmo_magic[12] = "org.osmocom";
+
/*! \brief unidirectional messages from BTS to BSC */
const enum abis_nm_msgtype abis_nm_reports[4] = {
NM_MT_SW_ACTIVATED_REP,
@@ -146,6 +149,28 @@ const char *abis_nm_nack_name(uint8_t nack)
return get_value_string(nack_names, nack);
}
+/* Section 9.4.43: Manufacturer specific values */
+const struct value_string abis_mm_event_cause_names[] = {
+ { OSMO_EVT_CRIT_SW_FATAL, "Fatal software error" },
+ { OSMO_EVT_CRIT_PROC_STOP, "Process stopped" },
+ { OSMO_EVT_CRIT_RTP_TOUT, "RTP error" },
+ { OSMO_EVT_CRIT_BOOT_FAIL, "Boot failure" },
+ { OSMO_EVT_MAJ_UKWN_MSG, "Unknown message" },
+ { OSMO_EVT_MAJ_RSL_FAIL, "RSL failure" },
+ { OSMO_EVT_MAJ_UNSUP_ATTR, "Unsupported attribute" },
+ { OSMO_EVT_MAJ_NET_CONGEST, "Network congestion" },
+ { OSMO_EVT_MIN_PAG_TAB_FULL, "Paging table full" },
+ { OSMO_EVT_WARN_SW_WARN, "Software warning" },
+ { 0, NULL }
+};
+
+const struct value_string abis_nm_pcause_type_names[] = {
+ { NM_PCAUSE_T_X721, "ISO/CCITT values (X.721)"},
+ { NM_PCAUSE_T_GSM, "GSM specific values"},
+ { NM_PCAUSE_T_MANUF, "Manufacturer specific values"},
+ { 0, NULL }
+};
+
/* Chapter 9.4.36 */
static const struct value_string nack_cause_names[] = {
/* General Nack Causes */
@@ -224,6 +249,17 @@ const char *abis_nm_severity_name(uint8_t cause)
return get_value_string(severity_names, cause);
}
+/*! \brief 3GPP TS 12.21 9.4.53 T200 values (in msec) */
+const uint8_t abis_nm_t200_ms[] = {
+ [T200_SDCCH] = 5,
+ [T200_FACCH_F] = 5,
+ [T200_FACCH_H] = 5,
+ [T200_SACCH_TCH_SAPI0] = 10,
+ [T200_SACCH_SDCCH] = 10,
+ [T200_SDCCH_SAPI3] = 5,
+ [T200_SACCH_TCH_SAPI3] = 10
+};
+
/*! \brief Attributes that the BSC can set, not only get, according to Section 9.4 */
const enum abis_nm_attr abis_nm_att_settable[] = {
NM_ATT_ADD_INFO,
@@ -253,6 +289,55 @@ const enum abis_nm_attr abis_nm_att_settable[] = {
NM_ATT_MEAS_TYPE,
};
+/*! \brief GSM A-bis OML IPA TLV parser definition */
+const struct tlv_definition abis_nm_att_tlvdef_ipa = {
+ .def = {
+ /* ip.access specifics */
+ [NM_ATT_IPACC_DST_IP] = { TLV_TYPE_FIXED, 4 },
+ [NM_ATT_IPACC_DST_IP_PORT] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_IPACC_STREAM_ID] = { TLV_TYPE_TV, },
+ [NM_ATT_IPACC_SEC_OML_CFG] = { TLV_TYPE_FIXED, 6 },
+ [NM_ATT_IPACC_IP_IF_CFG] = { TLV_TYPE_FIXED, 8 },
+ [NM_ATT_IPACC_IP_GW_CFG] = { TLV_TYPE_FIXED, 12 },
+ [NM_ATT_IPACC_IN_SERV_TIME] = { TLV_TYPE_FIXED, 4 },
+ [NM_ATT_IPACC_LOCATION] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_PAGING_CFG] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_IPACC_UNIT_ID] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_UNIT_NAME] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_SNMP_CFG] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_PRIM_OML_CFG_LIST] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_NV_FLAGS] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_IPACC_PRIM_OML_FB_TOUT] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_CUR_SW_CFG] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_TIMING_BUS] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_CGI] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_RAC] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_OBJ_VERSION] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_GPRS_PAGING_CFG]= { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_NSEI] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_BVCI] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_NSVCI] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_NS_CFG] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_BSSGP_CFG] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_NS_LINK_CFG] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_RLC_CFG] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_ALM_THRESH_LIST]= { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_MONIT_VAL_LIST] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_TIB_CONTROL] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_SUPP_FEATURES] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_CODING_SCHEMES] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_RLC_CFG_2] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_HEARTB_TOUT] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_UPTIME] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_RLC_CFG_3] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_SSL_CFG] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_SEC_POSSIBLE] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_IML_SSL_STATE] = { TLV_TYPE_TL16V },
+ [NM_ATT_IPACC_REVOC_DATE] = { TLV_TYPE_TL16V },
+ },
+};
+
/*! \brief GSM A-bis OML TLV parser definition */
const struct tlv_definition abis_nm_att_tlvdef = {
.def = {
@@ -323,6 +408,22 @@ const struct tlv_definition abis_nm_att_tlvdef = {
},
};
+/*! \brief org.osmocom GSM A-bis OML TLV parser definition */
+const struct tlv_definition abis_nm_osmo_att_tlvdef = {
+ .def = {
+ [NM_ATT_OSMO_REDUCEPOWER] = { TLV_TYPE_TV },
+ },
+};
+
+/*! \brief Human-readable strings for A-bis OML Object Class */
+const struct value_string abis_nm_msg_disc_names[] = {
+ { ABIS_OM_MDISC_FOM, "FOM" },
+ { ABIS_OM_MDISC_MMI, "MMI" },
+ { ABIS_OM_MDISC_TRAU, "TRAU" },
+ { ABIS_OM_MDISC_MANUF, "MANUF" },
+ { 0, NULL }
+};
+
/*! \brief Human-readable strings for A-bis OML Object Class */
const struct value_string abis_nm_obj_class_names[] = {
{ NM_OC_SITE_MANAGER, "SITE-MANAGER" },
@@ -380,7 +481,7 @@ const char *abis_nm_avail_name(uint8_t avail)
return get_value_string(avail_names, avail);
}
-static struct value_string test_names[] = {
+static const struct value_string test_names[] = {
/* FIXME: standard test names */
{ NM_IPACC_TESTNO_CHAN_USAGE, "Channel Usage" },
{ NM_IPACC_TESTNO_BCCH_CHAN_USAGE, "BCCH Channel Usage" },
@@ -407,18 +508,6 @@ const struct value_string abis_nm_adm_state_names[] = {
{ 0, NULL }
};
-/*! \brief write a human-readable OML header to the debug log
- * \param[in] ss Logging sub-system
- * \param[in] foh A-bis OML FOM header
- */
-void abis_nm_debugp_foh(int ss, struct abis_om_fom_hdr *foh)
-{
- DEBUGP(ss, "OC=%s(%02x) INST=(%02x,%02x,%02x) ",
- get_value_string(abis_nm_obj_class_names, foh->obj_class),
- foh->obj_class, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr,
- foh->obj_inst.ts_nr);
-}
-
static const enum abis_nm_chan_comb chcomb4pchan[] = {
[GSM_PCHAN_NONE] = 0xff,
[GSM_PCHAN_CCCH] = NM_CHANC_mainBCCH,
@@ -429,9 +518,63 @@ static const enum abis_nm_chan_comb chcomb4pchan[] = {
[GSM_PCHAN_PDCH] = NM_CHANC_IPAC_PDCH,
[GSM_PCHAN_TCH_F_PDCH] = NM_CHANC_IPAC_TCHFull_PDCH,
[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,
/* FIXME: bounds check */
};
+/*! \brief 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,
+ enum abis_nm_pcause_type ct,
+ uint16_t cause_value, const char *fmt, ...)
+{
+ va_list ap;
+ struct msgb *nmsg;
+
+ va_start(ap, fmt);
+ nmsg = abis_nm_fail_evt_vrep(t, s, ct, cause_value, fmt, ap);
+ va_end(ap);
+
+ return nmsg;
+}
+
+/*! \brief Pack 3GPP TS 12.21 § 8.8.2 Failure Event Report into msgb */
+struct msgb *abis_nm_fail_evt_vrep(enum abis_nm_event_type t,
+ enum abis_nm_severity s,
+ enum abis_nm_pcause_type ct,
+ uint16_t cause_value, const char *fmt,
+ va_list ap)
+{
+ uint8_t cause[3];
+ int len;
+ char add_text[ABIS_NM_MSG_HEADROOM];
+ struct msgb *nmsg = msgb_alloc_headroom(ABIS_NM_MSG_SIZE,
+ ABIS_NM_MSG_HEADROOM,
+ "OML FAIL EV. REP.");
+ if (!nmsg)
+ return NULL;
+
+ msgb_tv_put(nmsg, NM_ATT_EVENT_TYPE, t);
+ msgb_tv_put(nmsg, NM_ATT_SEVERITY, s);
+
+ cause[0] = ct;
+ osmo_store16be(cause_value, cause + 1);
+
+ msgb_tv_fixed_put(nmsg, NM_ATT_PROB_CAUSE, 3, cause);
+
+ len = vsnprintf(add_text, ABIS_NM_MSG_HEADROOM, fmt, ap);
+ if (len < 0) {
+ msgb_free(nmsg);
+ return NULL;
+ }
+ if (len)
+ msgb_tl16v_put(nmsg, NM_ATT_ADD_TEXT, len, add_text);
+
+ return nmsg;
+}
+
/*! \brief Obtain OML Channel Combination for phnsical channel config */
int abis_nm_chcomb4pchan(enum gsm_phys_chan_config pchan)
{
@@ -442,7 +585,7 @@ int abis_nm_chcomb4pchan(enum gsm_phys_chan_config pchan)
}
/*! \brief Obtain physical channel config for OML Channel Combination */
-enum abis_nm_chan_comb abis_nm_pchan4chcomb(uint8_t chcomb)
+enum gsm_phys_chan_config abis_nm_pchan4chcomb(uint8_t chcomb)
{
int i;
for (i = 0; i < ARRAY_SIZE(chcomb4pchan); i++) {
@@ -452,4 +595,14 @@ enum abis_nm_chan_comb abis_nm_pchan4chcomb(uint8_t chcomb)
return GSM_PCHAN_NONE;
}
+/* this is just for compatibility reasons, it is now a macro */
+#undef abis_nm_debugp_foh
+void abis_nm_debugp_foh(int ss, struct abis_om_fom_hdr *foh)
+{
+ DEBUGP(ss, "OC=%s(%02x) INST=(%02x,%02x,%02x) ",
+ get_value_string(abis_nm_obj_class_names, foh->obj_class),
+ foh->obj_class, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr,
+ foh->obj_inst.ts_nr);
+}
+
/*! @} */
diff --git a/src/shared/libosmocore/src/gsm/apn.c b/src/shared/libosmocore/src/gsm/apn.c
new file mode 100644
index 00000000..ccf36b99
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/apn.c
@@ -0,0 +1,113 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <osmocom/gsm/apn.h>
+
+#define APN_OI_GPRS_FMT "mnc%03u.mcc%03u.gprs"
+#define APN_GPRS_FMT "%s.mnc%03u.mcc%03u.gprs"
+
+static char apn_strbuf[APN_MAXLEN+1];
+
+char *osmo_apn_qualify(unsigned int mcc, unsigned int mnc, const char *ni)
+{
+ snprintf(apn_strbuf, sizeof(apn_strbuf)-1, APN_GPRS_FMT,
+ ni, mnc, mcc);
+ apn_strbuf[sizeof(apn_strbuf)-1] = '\0';
+
+ return apn_strbuf;
+}
+
+char *osmo_apn_qualify_from_imsi(const char *imsi,
+ const char *ni, int have_3dig_mnc)
+{
+ char cbuf[3+1], nbuf[3+1];
+
+ strncpy(cbuf, imsi, 3);
+ cbuf[3] = '\0';
+
+ if (have_3dig_mnc) {
+ strncpy(nbuf, imsi+3, 3);
+ nbuf[3] = '\0';
+ } else {
+ strncpy(nbuf, imsi+3, 2);
+ nbuf[2] = '\0';
+ }
+ return osmo_apn_qualify(atoi(cbuf), atoi(nbuf), ni);
+}
+
+/**
+ * Convert an encoded APN into a dot-separated string.
+ *
+ * \param out_str the destination buffer (size must be >= max(app_enc_len,1))
+ * \param apn_enc the encoded APN
+ * \param apn_enc_len the length of the encoded APN
+ *
+ * \returns out_str on success and NULL otherwise
+ */
+char * osmo_apn_to_str(char *out_str, const uint8_t *apn_enc, size_t apn_enc_len)
+{
+ char *str = out_str;
+ size_t rest_chars = apn_enc_len;
+
+ while (rest_chars > 0 && apn_enc[0]) {
+ size_t label_size = apn_enc[0];
+ if (label_size + 1 > rest_chars)
+ return NULL;
+
+ memmove(str, apn_enc + 1, label_size);
+ str += label_size;
+ rest_chars -= label_size + 1;
+ apn_enc += label_size + 1;
+
+ if (rest_chars)
+ *(str++) = '.';
+ }
+ str[0] = '\0';
+
+ return out_str;
+}
+
+/**
+ * Convert a dot-separated string into an encoded APN.
+ *
+ * \param apn_enc the encoded APN
+ * \param max_apn_enc_len the size of the apn_enc buffer
+ * \param str the source string
+ *
+ * \returns out_str on success and NULL otherwise
+ */
+int osmo_apn_from_str(uint8_t *apn_enc, size_t max_apn_enc_len, const char *str)
+{
+ uint8_t *last_len_field;
+ int len;
+
+ /* Can we even write the length field to the output? */
+ if (max_apn_enc_len == 0)
+ return -1;
+
+ /* Remember where we need to put the length once we know it */
+ last_len_field = apn_enc;
+ len = 1;
+ apn_enc += 1;
+
+ while (str[0]) {
+ if (len >= max_apn_enc_len)
+ return -1;
+
+ if (str[0] == '.') {
+ *last_len_field = (apn_enc - last_len_field) - 1;
+ last_len_field = apn_enc;
+ } else {
+ *apn_enc = str[0];
+ }
+ apn_enc += 1;
+ str += 1;
+ len += 1;
+ }
+
+ *last_len_field = (apn_enc - last_len_field) - 1;
+
+ return len;
+}
diff --git a/src/shared/libosmocore/src/gsm/auth_comp128v1.c b/src/shared/libosmocore/src/gsm/auth_comp128v1.c
index 41aef71c..c40027ee 100644
--- a/src/shared/libosmocore/src/gsm/auth_comp128v1.c
+++ b/src/shared/libosmocore/src/gsm/auth_comp128v1.c
@@ -28,7 +28,7 @@ static int c128v1_gen_vec(struct osmo_auth_vector *vec,
struct osmo_sub_auth_data *aud,
const uint8_t *_rand)
{
- comp128(aud->u.gsm.ki, _rand, vec->sres, vec->kc);
+ comp128v1(aud->u.gsm.ki, _rand, vec->sres, vec->kc);
vec->auth_types = OSMO_AUTH_TYPE_GSM;
return 0;
diff --git a/src/shared/libosmocore/src/gsm/auth_comp128v23.c b/src/shared/libosmocore/src/gsm/auth_comp128v23.c
new file mode 100644
index 00000000..168f8865
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/auth_comp128v23.c
@@ -0,0 +1,68 @@
+/* registers COMP128 version 2 and 3 A3/A8 algorithms for the
+ * GSM/GPRS/3G authentication core infrastructure
+ *
+ */
+
+/* (C) 2010-2011 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2013 by Kévin Redon <kevredon@mail.tsaitgaist.info>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <osmocom/crypt/auth.h>
+#include <osmocom/gsm/comp128v23.h>
+
+static int c128v2_gen_vec(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data *aud,
+ const uint8_t *_rand)
+{
+ comp128v2(aud->u.gsm.ki, _rand, vec->sres, vec->kc);
+ vec->auth_types = OSMO_AUTH_TYPE_GSM;
+
+ return 0;
+}
+
+static struct osmo_auth_impl c128v2_alg = {
+ .algo = OSMO_AUTH_ALG_COMP128v2,
+ .name = "COMP128v2 (libosmogsm built-in)",
+ .priority = 1000,
+ .gen_vec = &c128v2_gen_vec,
+};
+
+static int c128v3_gen_vec(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data *aud,
+ const uint8_t *_rand)
+{
+ comp128v3(aud->u.gsm.ki, _rand, vec->sres, vec->kc);
+ vec->auth_types = OSMO_AUTH_TYPE_GSM;
+
+ return 0;
+}
+
+static struct osmo_auth_impl c128v3_alg = {
+ .algo = OSMO_AUTH_ALG_COMP128v3,
+ .name = "COMP128v3 (libosmogsm built-in)",
+ .priority = 1000,
+ .gen_vec = &c128v3_gen_vec,
+};
+
+static __attribute__((constructor)) void on_dso_load_c128(void)
+{
+ osmo_auth_register(&c128v2_alg);
+ osmo_auth_register(&c128v3_alg);
+}
diff --git a/src/shared/libosmocore/src/gsm/auth_core.c b/src/shared/libosmocore/src/gsm/auth_core.c
index 5cf8dfcf..3aac0694 100644
--- a/src/shared/libosmocore/src/gsm/auth_core.c
+++ b/src/shared/libosmocore/src/gsm/auth_core.c
@@ -43,6 +43,7 @@ static struct osmo_auth_impl *selected_auths[_OSMO_AUTH_ALG_NUM];
/*! \brief Register an authentication algorithm implementation with the core
* \param[in] impl Structure describing implementation and it's callbacks
+ * \returns 0 on success, or a negative error code on failure
*
* This function is called by an authentication implementation plugin to
* register itself with the authentication core.
@@ -64,6 +65,7 @@ int osmo_auth_register(struct osmo_auth_impl *impl)
/*! \brief Load all available authentication plugins from the given path
* \param[in] path Path name of the directory containing the plugins
+ * \returns number of plugins loaded in case of success, negative in case of error
*
* This function will load all plugins contained in the specified path.
*/
@@ -75,6 +77,7 @@ int osmo_auth_load(const char *path)
/*! \brief Determine if a given authentication algorithm is supported
* \param[in] algo Algorithm which should be checked
+ * \returns 1 if algo is supported, 0 if not, negative error on failure
*
* This function is used by an application to determine at runtime if a
* given authentication algorithm is supported or not.
@@ -90,10 +93,54 @@ int osmo_auth_supported(enum osmo_auth_algo algo)
return 0;
}
+/* C5 function to derive UMTS IK from GSM Kc */
+static inline void c5_function(uint8_t *ik, const uint8_t *kc)
+{
+ unsigned int i;
+
+ for (i = 0; i < 4; i++)
+ ik[i] = kc[i] ^ kc[i+4];
+ memcpy(ik+4, kc, 8);
+ for (i = 12; i < 16; i++)
+ ik[i] = ik[i-12];
+}
+
+/* C4 function to derive UMTS CK from GSM Kc */
+void osmo_c4(uint8_t *ck, const uint8_t *kc)
+{
+ memcpy(ck, kc, 8);
+ memcpy(ck+8, kc, 8);
+}
+
+/*! \brief Generate 3G CK + IK from 2G authentication vector
+ * \param vec Authentication Vector to be modified
+ * \returns 1 if the vector was changed, 0 otherwise
+ *
+ * This function performs the C5 and C4 functions to derive the UMTS key
+ * material from the GSM key material in the supplied vector, _if_ the input
+ * vector doesn't yet have UMTS authentication capability.
+ */
+int osmo_auth_3g_from_2g(struct osmo_auth_vector *vec)
+{
+ if ((vec->auth_types & OSMO_AUTH_TYPE_GSM) &&
+ !(vec->auth_types & OSMO_AUTH_TYPE_UMTS)) {
+ c5_function(vec->ik, vec->kc);
+ osmo_c4(vec->ck, vec->kc);
+ /* We cannot actually set OSMO_AUTH_TYPE_UMTS as we have no
+ * AUTN and no RES, and thus can only perform GSM
+ * authentication with this tuple.
+ */
+ return 1;
+ }
+
+ return 0;
+}
+
/*! \brief Generate authentication vector
* \param[out] vec Generated authentication vector
* \param[in] aud Subscriber-specific key material
- * \param[in] rand Random challenge to be used
+ * \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
@@ -125,7 +172,8 @@ int osmo_auth_gen_vec(struct osmo_auth_vector *vec,
* \param[in] aud Subscriber-specific key material
* \param[in] rand_auts RAND value sent by the SIM/MS
* \param[in] auts AUTS value sent by the SIM/MS
- * \param[in] rand Random challenge to be used to generate vector
+ * \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
diff --git a/src/shared/libosmocore/src/gsm/auth_milenage.c b/src/shared/libosmocore/src/gsm/auth_milenage.c
index 5b2787dd..1635ac6b 100644
--- a/src/shared/libosmocore/src/gsm/auth_milenage.c
+++ b/src/shared/libosmocore/src/gsm/auth_milenage.c
@@ -21,39 +21,10 @@
*/
#include <osmocom/crypt/auth.h>
+#include <osmocom/core/bits.h>
#include "milenage/common.h"
#include "milenage/milenage.h"
-static void sqn_u64_to_48bit(uint8_t *sqn, const uint64_t sqn64)
-{
- sqn[5] = (sqn64 >> 0) & 0xff;
- sqn[4] = (sqn64 >> 8) & 0xff;
- sqn[3] = (sqn64 >> 16) & 0xff;
- sqn[2] = (sqn64 >> 24) & 0xff;
- sqn[1] = (sqn64 >> 32) & 0xff;
- sqn[0] = (sqn64 >> 40) & 0xff;
-}
-
-static uint64_t sqn_48bit_to_u64(const uint8_t *sqn)
-{
- uint64_t sqn64;
-
- sqn64 = sqn[0];
- sqn64 <<= 8;
- sqn64 |= sqn[1];
- sqn64 <<= 8;
- sqn64 |= sqn[2];
- sqn64 <<= 8;
- sqn64 |= sqn[3];
- sqn64 <<= 8;
- sqn64 |= sqn[4];
- sqn64 <<= 8;
- sqn64 |= sqn[5];
-
- return sqn64;
-}
-
-
static int milenage_gen_vec(struct osmo_auth_vector *vec,
struct osmo_sub_auth_data *aud,
const uint8_t *_rand)
@@ -62,7 +33,7 @@ static int milenage_gen_vec(struct osmo_auth_vector *vec,
uint8_t sqn[6];
int rc;
- sqn_u64_to_48bit(sqn, aud->u.umts.sqn);
+ osmo_store64be_ext(aud->u.umts.sqn, sqn, 6);
milenage_generate(aud->u.umts.opc, aud->u.umts.amf, aud->u.umts.k,
sqn, _rand,
vec->autn, vec->ik, vec->ck, vec->res, &res_len);
@@ -101,7 +72,7 @@ static int milenage_gen_vec_auts(struct osmo_auth_vector *vec,
if (rc < 0)
return rc;
- aud->u.umts.sqn = sqn_48bit_to_u64(sqn_out) + 1;
+ aud->u.umts.sqn = 1 + (osmo_load64be_ext(sqn_out, 6) >> 16);
return milenage_gen_vec(vec, aud, _rand);
}
diff --git a/src/shared/libosmocore/src/gsm/comp128.c b/src/shared/libosmocore/src/gsm/comp128.c
index b7a23820..abb49179 100644
--- a/src/shared/libosmocore/src/gsm/comp128.c
+++ b/src/shared/libosmocore/src/gsm/comp128.c
@@ -185,7 +185,7 @@ _comp128_permutation(uint8_t *x, uint8_t *bits)
}
void
-comp128(const uint8_t *ki, const uint8_t *rand, uint8_t *sres, uint8_t *kc)
+comp128v1(const uint8_t *ki, const uint8_t *rand, uint8_t *sres, uint8_t *kc)
{
int i;
uint8_t x[32], bits[128];
@@ -228,3 +228,8 @@ comp128(const uint8_t *ki, const uint8_t *rand, uint8_t *sres, uint8_t *kc)
kc[7] = 0;
}
+void
+comp128(const uint8_t *ki, const uint8_t *rand, uint8_t *sres, uint8_t *kc)
+{
+ comp128v1(ki, rand, sres, kc);
+}
diff --git a/src/shared/libosmocore/src/gsm/comp128v23.c b/src/shared/libosmocore/src/gsm/comp128v23.c
new file mode 100644
index 00000000..e21b718e
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/comp128v23.c
@@ -0,0 +1,157 @@
+/* COMP128 version 2 and 3 implementation
+ *
+ * This code is a C conversion of the original code from
+ * http://www.hackingprojects.net/
+ *
+ */
+
+/* (C) 2013 by Kévin Redon <kevredon@mail.tsaitgaist.info>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+static const uint8_t table0[256] = {
+ 197, 235, 60, 151, 98, 96, 3, 100, 248, 118, 42, 117, 172, 211, 181, 203, 61,
+ 126, 156, 87, 149, 224, 55, 132, 186, 63, 238, 255, 85, 83, 152, 33, 160,
+ 184, 210, 219, 159, 11, 180, 194, 130, 212, 147, 5, 215, 92, 27, 46, 113,
+ 187, 52, 25, 185, 79, 221, 48, 70, 31, 101, 15, 195, 201, 50, 222, 137,
+ 233, 229, 106, 122, 183, 178, 177, 144, 207, 234, 182, 37, 254, 227, 231, 54,
+ 209, 133, 65, 202, 69, 237, 220, 189, 146, 120, 68, 21, 125, 38, 30, 2,
+ 155, 53, 196, 174, 176, 51, 246, 167, 76, 110, 20, 82, 121, 103, 112, 56,
+ 173, 49, 217, 252, 0, 114, 228, 123, 12, 93, 161, 253, 232, 240, 175, 67,
+ 128, 22, 158, 89, 18, 77, 109, 190, 17, 62, 4, 153, 163, 59, 145, 138,
+ 7, 74, 205, 10, 162, 80, 45, 104, 111, 150, 214, 154, 28, 191, 169, 213,
+ 88, 193, 198, 200, 245, 39, 164, 124, 84, 78, 1, 188, 170, 23, 86, 226,
+ 141, 32, 6, 131, 127, 199, 40, 135, 16, 57, 71, 91, 225, 168, 242, 206,
+ 97, 166, 44, 14, 90, 236, 239, 230, 244, 223, 108, 102, 119, 148, 251, 29,
+ 216, 8, 9, 249, 208, 24, 105, 94, 34, 64, 95, 115, 72, 134, 204, 43,
+ 247, 243, 218, 47, 58, 73, 107, 241, 179, 116, 66, 36, 143, 81, 250, 139,
+ 19, 13, 142, 140, 129, 192, 99, 171, 157, 136, 41, 75, 35, 165, 26
+}, table1[256] = {
+ 170, 42, 95, 141, 109, 30, 71, 89, 26, 147, 231, 205, 239, 212, 124, 129, 216,
+ 79, 15, 185, 153, 14, 251, 162, 0, 241, 172, 197, 43, 10, 194, 235, 6,
+ 20, 72, 45, 143, 104, 161, 119, 41, 136, 38, 189, 135, 25, 93, 18, 224,
+ 171, 252, 195, 63, 19, 58, 165, 23, 55, 133, 254, 214, 144, 220, 178, 156,
+ 52, 110, 225, 97, 183, 140, 39, 53, 88, 219, 167, 16, 198, 62, 222, 76,
+ 139, 175, 94, 51, 134, 115, 22, 67, 1, 249, 217, 3, 5, 232, 138, 31,
+ 56, 116, 163, 70, 128, 234, 132, 229, 184, 244, 13, 34, 73, 233, 154, 179,
+ 131, 215, 236, 142, 223, 27, 57, 246, 108, 211, 8, 253, 85, 66, 245, 193,
+ 78, 190, 4, 17, 7, 150, 127, 152, 213, 37, 186, 2, 243, 46, 169, 68,
+ 101, 60, 174, 208, 158, 176, 69, 238, 191, 90, 83, 166, 125, 77, 59, 21,
+ 92, 49, 151, 168, 99, 9, 50, 146, 113, 117, 228, 65, 230, 40, 82, 54,
+ 237, 227, 102, 28, 36, 107, 24, 44, 126, 206, 201, 61, 114, 164, 207, 181,
+ 29, 91, 64, 221, 255, 48, 155, 192, 111, 180, 210, 182, 247, 203, 148, 209,
+ 98, 173, 11, 75, 123, 250, 118, 32, 47, 240, 202, 74, 177, 100, 80, 196,
+ 33, 248, 86, 157, 137, 120, 130, 84, 204, 122, 81, 242, 188, 200, 149, 226,
+ 218, 160, 187, 106, 35, 87, 105, 96, 145, 199, 159, 12, 121, 103, 112
+};
+
+
+static void
+_comp128v23_internal(uint8_t *output, const uint8_t *kxor, const uint8_t *rand)
+{
+ uint8_t temp[16];
+ uint8_t km_rm[32];
+ uint8_t i,j,k,z;
+
+ memset(temp, 0, sizeof(temp));
+ memcpy(km_rm, rand, 16);
+ memcpy(km_rm + 16, kxor, 16);
+
+ for (i=0; i<5; i++) {
+ for (z=0; z<16; z++) {
+ temp[z] = table0[table1[km_rm[16+z]]^km_rm[z]];
+ }
+ j=0;
+ while ((1<<i)>j) {
+ k = 0;
+ while ((1<<(4-i))>k) {
+ km_rm[((2*k+1)<<i)+j] = table0[table1[temp[(k<<i)+j]]^(km_rm[(k<<i)+16+j])];
+ km_rm[(k<<(i+1))+j] = temp[(k<<i)+j];
+ k++;
+ }
+ j++;
+ }
+ }
+
+ memset(output,0,16);
+
+ for (i=0; i<16; i++) {
+ for (j=0; j<8; j++) {
+ output[i] ^= (((km_rm[(19*(j+8*i)+19)%256/8]>>(3*j+3)%8)&1)<< j);
+ }
+ }
+}
+
+int
+comp128v3(const uint8_t *ki, const uint8_t *rand, uint8_t *sres, uint8_t *kc)
+{
+ uint8_t k_mix[16];
+ uint8_t rand_mix[16];
+ uint8_t katyvasz[16];
+ uint8_t output[16];
+ uint8_t i;
+
+ memset(k_mix, 0, sizeof(k_mix));
+ memset(rand_mix, 0, sizeof(rand_mix));
+ memset(katyvasz, 0, sizeof(katyvasz));
+ memset(output, 0, sizeof(output));
+
+ for (i=0; i<8; i++) {
+ k_mix[i] = ki[15 - i];
+ k_mix[15 - i] = ki[i];
+ }
+
+ for (i=0; i<8; i++) {
+ rand_mix[i] = rand[15 - i];
+ rand_mix[15 - i] = rand[i];
+ }
+
+ for (i=0; i<16; i++) {
+ katyvasz[i] = k_mix[i]^rand_mix[i];
+ }
+
+ for (i=0; i<8; i++) {
+ _comp128v23_internal(rand_mix,katyvasz,rand_mix);
+ }
+
+ for (i=0; i<16; i++) {
+ output[i] = rand_mix[15-i];
+ }
+
+ memmove(output + 4, output + 8, 8); /* ignore bytes 4..7 */
+
+ /* the algorithm uses 16 bytes until this point, but only 12 bytes are effective
+ * also 12 bytes coming out from the SIM card */
+ memcpy(sres, output, 4);
+ memcpy(kc, output + 4, 8);
+
+ return 0;
+}
+
+int
+comp128v2(const uint8_t *ki, const uint8_t *rand, uint8_t *sres, uint8_t *kc)
+{
+ int r = comp128v3(ki, rand, sres, kc);
+ kc[7] = 0; /* 10 last bits of Kc forced to 0 */
+ kc[6] &= 0xfc;
+ return r;
+}
diff --git a/src/shared/libosmocore/src/gsm/gan.c b/src/shared/libosmocore/src/gsm/gan.c
index e041936e..0bcaa175 100644
--- a/src/shared/libosmocore/src/gsm/gan.c
+++ b/src/shared/libosmocore/src/gsm/gan.c
@@ -2,16 +2,16 @@
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
+ * 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 Affero General Public License for more details.
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU Affero General Public License
+ * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -68,7 +68,7 @@ const struct value_string gan_msgt_vals[] = {
{ 0, NULL }
};
-static const struct value_string gan_pdisc_vals[] = {
+const struct value_string gan_pdisc_vals[] = {
{ GA_PDISC_RC, "RC" },
{ GA_PDISC_CSR, "CSR" },
{ GA_PDISC_PSR, "PSR" },
diff --git a/src/shared/libosmocore/src/gsm/gea.c b/src/shared/libosmocore/src/gsm/gea.c
new file mode 100644
index 00000000..b8f6768a
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/gea.c
@@ -0,0 +1,60 @@
+/*
+ * gea.c
+ *
+ * Implementation of GEA3 and GEA4
+ *
+ * Copyright (C) 2016 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.
+ *
+ * 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>
+#include <osmocom/crypt/gprs_cipher.h>
+#include <osmocom/crypt/auth.h>
+#include <osmocom/gsm/kasumi.h>
+
+#include <stdint.h>
+#include <string.h>
+
+/*! \brief Performs the GEA4 algorithm as in 3GPP TS 55.226 V9.0.0
+ * \param[in,out] out Buffer for gamma for encrypted/decrypted
+ * \param[in] len Length of out, in bytes
+ * \param[in] kc Buffer with the ciphering key
+ * \param[in] iv Init vector
+ * \param[in] direct Direction: 0 (MS -> SGSN) or 1 (SGSN -> MS)
+ */
+int gea4(uint8_t *out, uint16_t len, uint8_t *kc, uint32_t iv,
+ enum gprs_cipher_direction direction)
+{
+ _kasumi_kgcore(0xFF, 0, iv, direction, kc, out, len * 8);
+ return 0;
+}
+
+/*! \brief Performs the GEA3 algorithm as in 3GPP TS 55.216 V6.2.0
+ * \param[in,out] out Buffer for gamma for encrypted/decrypted
+ * \param[in] len Length of out, in bytes
+ * \param[in] kc Buffer with the ciphering key
+ * \param[in] iv Init vector
+ * \param[in] direct Direction: 0 (MS -> SGSN) or 1 (SGSN -> MS)
+ */
+int gea3(uint8_t *out, uint16_t len, uint8_t *kc, uint32_t iv,
+ enum gprs_cipher_direction direction)
+{
+ uint8_t ck[gprs_cipher_key_length(GPRS_ALGO_GEA4)];
+ osmo_c4(ck, kc);
+ return gea4(out, len, ck, iv, direction);
+}
diff --git a/src/shared/libosmocore/src/gsm/gprs_cipher_core.c b/src/shared/libosmocore/src/gsm/gprs_cipher_core.c
index b9a22a10..da6e0a83 100644
--- a/src/shared/libosmocore/src/gsm/gprs_cipher_core.c
+++ b/src/shared/libosmocore/src/gsm/gprs_cipher_core.c
@@ -33,6 +33,15 @@ static LLIST_HEAD(gprs_ciphers);
static struct gprs_cipher_impl *selected_ciphers[_GPRS_ALGO_NUM];
+const struct value_string gprs_cipher_names[] = {
+ { GPRS_ALGO_GEA0, "GEA0" },
+ { GPRS_ALGO_GEA1, "GEA1" },
+ { GPRS_ALGO_GEA2, "GEA2" },
+ { GPRS_ALGO_GEA3, "GEA3" },
+ { GPRS_ALGO_GEA4, "GEA4" },
+ { 0, NULL },
+};
+
/* register a cipher with the core */
int gprs_cipher_register(struct gprs_cipher_impl *ciph)
{
@@ -53,12 +62,14 @@ int gprs_cipher_register(struct gprs_cipher_impl *ciph)
int gprs_cipher_load(const char *path)
{
/* load all plugins available from path */
- return osmo_plugin_load_all(path);
+ if (path)
+ return osmo_plugin_load_all(path);
+ return 0;
}
/* function to be called by core code */
int gprs_cipher_run(uint8_t *out, uint16_t len, enum gprs_ciph_algo algo,
- uint64_t kc, uint32_t iv, enum gprs_cipher_direction dir)
+ uint8_t *kc, uint32_t iv, enum gprs_cipher_direction dir)
{
if (algo >= ARRAY_SIZE(selected_ciphers))
return -ERANGE;
@@ -73,6 +84,23 @@ int gprs_cipher_run(uint8_t *out, uint16_t len, enum gprs_ciph_algo algo,
return selected_ciphers[algo]->run(out, len, kc, iv, dir);
}
+/*! \brief Obtain key lenght for given GPRS cipher
+ * \param[in] algo Enum representive GPRS cipher
+ * \returns unsigned integer key length for supported algorithms,
+ * for GEA0 and unknown ciphers will return 0
+ */
+unsigned gprs_cipher_key_length(enum gprs_ciph_algo algo)
+{
+ switch (algo) {
+ case GPRS_ALGO_GEA0: return 0;
+ case GPRS_ALGO_GEA1:
+ case GPRS_ALGO_GEA2:
+ case GPRS_ALGO_GEA3: return 8;
+ case GPRS_ALGO_GEA4: return 16;
+ default: return 0;
+ }
+}
+
int gprs_cipher_supported(enum gprs_ciph_algo algo)
{
if (algo >= ARRAY_SIZE(selected_ciphers))
@@ -87,7 +115,7 @@ int gprs_cipher_supported(enum gprs_ciph_algo algo)
/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */
uint32_t gprs_cipher_gen_input_ui(uint32_t iov_ui, uint8_t sapi, uint32_t lfn, uint32_t oc)
{
- uint32_t sx = ((1<<27) * sapi) + (1<<31);
+ uint32_t sx = ((1<<27) * sapi) + ((uint32_t ) 1<<31);
return (iov_ui ^ sx) + lfn + oc;
}
diff --git a/src/shared/libosmocore/src/gsm/gprs_gea.c b/src/shared/libosmocore/src/gsm/gprs_gea.c
new file mode 100644
index 00000000..8ff16484
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/gprs_gea.c
@@ -0,0 +1,48 @@
+/*
+ * gprs_gea.c
+ *
+ * GEA 3 & 4 plugin
+ *
+ * Copyright (C) 2016 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.
+ *
+ * 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>
+#include <osmocom/gsm/gea.h>
+
+#include <stdint.h>
+
+static struct gprs_cipher_impl gea3_impl = {
+ .algo = GPRS_ALGO_GEA3,
+ .name = "GEA3 (libosmogsm built-in)",
+ .priority = 100,
+ .run = &gea3,
+};
+
+static struct gprs_cipher_impl gea4_impl = {
+ .algo = GPRS_ALGO_GEA4,
+ .name = "GEA4 (libosmogsm built-in)",
+ .priority = 100,
+ .run = &gea4,
+};
+
+static __attribute__((constructor)) void on_dso_load_gea(void)
+{
+ gprs_cipher_register(&gea3_impl);
+ gprs_cipher_register(&gea4_impl);
+}
diff --git a/src/shared/libosmocore/src/gsm/gprs_rlc.c b/src/shared/libosmocore/src/gsm/gprs_rlc.c
new file mode 100644
index 00000000..d6ccbbe4
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/gprs_rlc.c
@@ -0,0 +1,106 @@
+#include <errno.h>
+#include <string.h>
+
+#include <osmocom/gprs/gprs_rlc.h>
+#include <osmocom/gprs/protocol/gsm_04_60.h>
+
+#define EGPRS_CPS_TYPE1_TBL_SZ 29
+#define EGPRS_CPS_TYPE2_TBL_SZ 8
+#define EGPRS_CPS_TYPE3_TBL_SZ 16
+
+/* 3GPP TS 44.060 10.4.8a.1.1 "Header type 1" */
+static const struct egprs_cps egprs_cps_table_type1[EGPRS_CPS_TYPE1_TBL_SZ] = {
+ { .bits = 0, .mcs = 9, .p = { EGPRS_CPS_P1, EGPRS_CPS_P1 } },
+ { .bits = 1, .mcs = 9, .p = { EGPRS_CPS_P1, EGPRS_CPS_P2 } },
+ { .bits = 2, .mcs = 9, .p = { EGPRS_CPS_P1, EGPRS_CPS_P3 } },
+ { .bits = 3, .mcs = 0, .p = { EGPRS_CPS_NONE, EGPRS_CPS_NONE } },
+ { .bits = 4, .mcs = 9, .p = { EGPRS_CPS_P2, EGPRS_CPS_P1 } },
+ { .bits = 5, .mcs = 9, .p = { EGPRS_CPS_P2, EGPRS_CPS_P2 } },
+ { .bits = 6, .mcs = 9, .p = { EGPRS_CPS_P2, EGPRS_CPS_P3 } },
+ { .bits = 7, .mcs = 0, .p = { EGPRS_CPS_NONE, EGPRS_CPS_NONE } },
+ { .bits = 8, .mcs = 9, .p = { EGPRS_CPS_P3, EGPRS_CPS_P1 } },
+ { .bits = 9, .mcs = 9, .p = { EGPRS_CPS_P3, EGPRS_CPS_P2 } },
+ { .bits = 10, .mcs = 9, .p = { EGPRS_CPS_P3, EGPRS_CPS_P3 } },
+ { .bits = 11, .mcs = 8, .p = { EGPRS_CPS_P1, EGPRS_CPS_P1 } },
+ { .bits = 12, .mcs = 8, .p = { EGPRS_CPS_P1, EGPRS_CPS_P2 } },
+ { .bits = 13, .mcs = 8, .p = { EGPRS_CPS_P1, EGPRS_CPS_P3 } },
+ { .bits = 14, .mcs = 8, .p = { EGPRS_CPS_P2, EGPRS_CPS_P1 } },
+ { .bits = 15, .mcs = 8, .p = { EGPRS_CPS_P2, EGPRS_CPS_P2 } },
+ { .bits = 16, .mcs = 8, .p = { EGPRS_CPS_P2, EGPRS_CPS_P3 } },
+ { .bits = 17, .mcs = 8, .p = { EGPRS_CPS_P3, EGPRS_CPS_P1 } },
+ { .bits = 18, .mcs = 8, .p = { EGPRS_CPS_P3, EGPRS_CPS_P2 } },
+ { .bits = 19, .mcs = 8, .p = { EGPRS_CPS_P3, EGPRS_CPS_P3 } },
+ { .bits = 20, .mcs = 7, .p = { EGPRS_CPS_P1, EGPRS_CPS_P1 } },
+ { .bits = 21, .mcs = 7, .p = { EGPRS_CPS_P1, EGPRS_CPS_P2 } },
+ { .bits = 22, .mcs = 7, .p = { EGPRS_CPS_P1, EGPRS_CPS_P3 } },
+ { .bits = 23, .mcs = 7, .p = { EGPRS_CPS_P2, EGPRS_CPS_P1 } },
+ { .bits = 24, .mcs = 7, .p = { EGPRS_CPS_P2, EGPRS_CPS_P2 } },
+ { .bits = 25, .mcs = 7, .p = { EGPRS_CPS_P2, EGPRS_CPS_P3 } },
+ { .bits = 26, .mcs = 7, .p = { EGPRS_CPS_P3, EGPRS_CPS_P1 } },
+ { .bits = 27, .mcs = 7, .p = { EGPRS_CPS_P3, EGPRS_CPS_P2 } },
+ { .bits = 28, .mcs = 7, .p = { EGPRS_CPS_P3, EGPRS_CPS_P3 } },
+};
+
+/*
+ * 3GPP TS 44.060 10.4.8a.2.1
+ * "Header type 2 in EGPRS TBF or uplink EGPRS2-A TBF"
+ */
+static const struct egprs_cps egprs_cps_table_type2[EGPRS_CPS_TYPE2_TBL_SZ] = {
+ { .bits = 0, .mcs = 6, .p = { EGPRS_CPS_P1, EGPRS_CPS_NONE } },
+ { .bits = 1, .mcs = 6, .p = { EGPRS_CPS_P2, EGPRS_CPS_NONE } },
+ { .bits = 2, .mcs = 6, .p = { EGPRS_CPS_P1, EGPRS_CPS_NONE } },
+ { .bits = 3, .mcs = 6, .p = { EGPRS_CPS_P2, EGPRS_CPS_NONE } },
+ { .bits = 4, .mcs = 5, .p = { EGPRS_CPS_P1, EGPRS_CPS_NONE } },
+ { .bits = 5, .mcs = 5, .p = { EGPRS_CPS_P2, EGPRS_CPS_NONE } },
+ { .bits = 6, .mcs = 6, .p = { EGPRS_CPS_P1, EGPRS_CPS_NONE } },
+ { .bits = 7, .mcs = 6, .p = { EGPRS_CPS_P2, EGPRS_CPS_NONE } },
+};
+
+/* 3GPP TS 44.060 10.4.8a.3 "Header type 3" */
+static const struct egprs_cps egprs_cps_table_type3[EGPRS_CPS_TYPE3_TBL_SZ] = {
+ { .bits = 0, .mcs = 4, .p = { EGPRS_CPS_P1, EGPRS_CPS_NONE } },
+ { .bits = 1, .mcs = 4, .p = { EGPRS_CPS_P2, EGPRS_CPS_NONE } },
+ { .bits = 2, .mcs = 4, .p = { EGPRS_CPS_P3, EGPRS_CPS_NONE } },
+ { .bits = 3, .mcs = 3, .p = { EGPRS_CPS_P1, EGPRS_CPS_NONE } },
+ { .bits = 4, .mcs = 3, .p = { EGPRS_CPS_P2, EGPRS_CPS_NONE } },
+ { .bits = 5, .mcs = 3, .p = { EGPRS_CPS_P3, EGPRS_CPS_NONE } },
+ { .bits = 6, .mcs = 3, .p = { EGPRS_CPS_P1, EGPRS_CPS_NONE } },
+ { .bits = 7, .mcs = 3, .p = { EGPRS_CPS_P2, EGPRS_CPS_NONE } },
+ { .bits = 8, .mcs = 3, .p = { EGPRS_CPS_P3, EGPRS_CPS_NONE } },
+ { .bits = 9, .mcs = 2, .p = { EGPRS_CPS_P1, EGPRS_CPS_NONE } },
+ { .bits = 10, .mcs = 2, .p = { EGPRS_CPS_P2, EGPRS_CPS_NONE } },
+ { .bits = 11, .mcs = 1, .p = { EGPRS_CPS_P1, EGPRS_CPS_NONE } },
+ { .bits = 12, .mcs = 1, .p = { EGPRS_CPS_P2, EGPRS_CPS_NONE } },
+ { .bits = 13, .mcs = 2, .p = { EGPRS_CPS_P1, EGPRS_CPS_NONE } },
+ { .bits = 14, .mcs = 2, .p = { EGPRS_CPS_P2, EGPRS_CPS_NONE } },
+ { .bits = 15, .mcs = 0, .p = { EGPRS_CPS_NONE, EGPRS_CPS_NONE } },
+};
+
+int egprs_get_cps(struct egprs_cps *cps, uint8_t type, uint8_t bits)
+{
+ const struct egprs_cps *table_cps;
+
+ switch (type) {
+ case EGPRS_HDR_TYPE1:
+ if (bits >= EGPRS_CPS_TYPE1_TBL_SZ)
+ return -EINVAL;
+ table_cps = &egprs_cps_table_type1[bits];
+ break;
+ case EGPRS_HDR_TYPE2:
+ if (bits >= EGPRS_CPS_TYPE2_TBL_SZ)
+ return -EINVAL;
+ table_cps = &egprs_cps_table_type2[bits];
+ break;
+ case EGPRS_HDR_TYPE3:
+ if (bits >= EGPRS_CPS_TYPE3_TBL_SZ)
+ return -EINVAL;
+ table_cps = &egprs_cps_table_type3[bits];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ memcpy(cps, table_cps, sizeof *cps);
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/src/gsm/gsm0341.c b/src/shared/libosmocore/src/gsm/gsm0341.c
new file mode 100644
index 00000000..cc773824
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/gsm0341.c
@@ -0,0 +1,60 @@
+/*
+ * (C) 2014 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <arpa/inet.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/gsm/protocol/gsm_03_41.h>
+
+struct gsm341_ms_message *
+gsm0341_build_msg(void *ctx, uint8_t geo_scope, uint8_t msg_code,
+ uint8_t update, uint16_t msg_id, uint8_t dcs,
+ uint8_t page_total, uint8_t page_cur,
+ uint8_t *data, uint8_t len)
+{
+ struct gsm341_ms_message *cbmsg;
+
+ msg_id = htons(msg_id);
+
+ if (len > 88)
+ return NULL;
+
+ cbmsg = talloc_zero_size(ctx, sizeof(*cbmsg)+len);
+ if (!cbmsg)
+ return NULL;
+
+ cbmsg->serial.code_hi = (msg_code >> 4) & 0xF;
+ cbmsg->serial.gs = geo_scope;
+ cbmsg->serial.update = update;
+ cbmsg->serial.code_lo = msg_code & 0xF;
+ cbmsg->msg_id = msg_id;
+ cbmsg->dcs.group = dcs >> 4;
+ cbmsg->dcs.language = dcs & 0xF;
+ cbmsg->page.total = page_total;
+ cbmsg->page.current = page_cur;
+ memcpy(cbmsg->data, data, len);
+
+ return cbmsg;
+}
diff --git a/src/shared/libosmocore/src/gsm/gsm0411_smc.c b/src/shared/libosmocore/src/gsm/gsm0411_smc.c
index 4152ef1c..c44423d4 100644
--- a/src/shared/libosmocore/src/gsm/gsm0411_smc.c
+++ b/src/shared/libosmocore/src/gsm/gsm0411_smc.c
@@ -11,16 +11,16 @@
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
+ * 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 Affero General Public License for more details.
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU Affero General Public License
+ * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -44,12 +44,13 @@
* MMSMS-REL-REQ. It is allowed to destroy this process while processing
* this message.
*
- * There is expeption, if MMSMS-REL-IND is received from lower layer, the
+ * There is an exception, if MMSMS-REL-IND is received from lower layer, the
* process returns to IDLE without sending MMSMS-REL-REQ.
*
*/
#include <string.h>
+#include <inttypes.h>
#include <errno.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/logging.h>
@@ -63,14 +64,17 @@ static void cp_timer_expired(void *data);
#define MAX_SMS_RETRY 2
+#define SMC_LOG_STR "SMC(%" PRIu64 ") "
+
/* init a new instance */
-void gsm411_smc_init(struct gsm411_smc_inst *inst, int network,
+void gsm411_smc_init(struct gsm411_smc_inst *inst, uint64_t id, int network,
int (*mn_recv) (struct gsm411_smc_inst *inst, int msg_type,
struct msgb *msg),
int (*mm_send) (struct gsm411_smc_inst *inst, int msg_type,
struct msgb *msg, int cp_msg_type))
{
memset(inst, 0, sizeof(*inst));
+ inst->id = id;
inst->network = network;
inst->cp_max_retr = MAX_SMS_RETRY;
inst->cp_tc1 = GSM411_TMR_TC1A_SEC / (inst->cp_max_retr + 1);
@@ -78,19 +82,23 @@ void gsm411_smc_init(struct gsm411_smc_inst *inst, int network,
inst->mn_recv = mn_recv;
inst->mm_send = mm_send;
- LOGP(DLSMS, LOGL_INFO, "New SMC instance created\n");
+ LOGP(DLSMS, LOGL_INFO,
+ SMC_LOG_STR "instance created for %s\n",
+ inst->id, inst->network ? "network" : "mobile");
}
/* clear instance */
void gsm411_smc_clear(struct gsm411_smc_inst *inst)
{
- LOGP(DLSMS, LOGL_INFO, "Clear SMC instance\n");
+ LOGP(DLSMS, LOGL_INFO,
+ SMC_LOG_STR "clearing instance\n", inst->id);
osmo_timer_del(&inst->cp_timer);
/* free stored msg */
if (inst->cp_msg) {
- LOGP(DLSMS, LOGL_INFO, "Dropping pending message\n");
+ LOGP(DLSMS, LOGL_INFO,
+ SMC_LOG_STR "dropping pending message\n", inst->id);
msgb_free(inst->cp_msg);
inst->cp_msg = NULL;
}
@@ -120,7 +128,8 @@ const struct value_string gsm411_cp_cause_strs[] = {
static void new_cp_state(struct gsm411_smc_inst *inst,
enum gsm411_cp_state state)
{
- LOGP(DLSMS, LOGL_INFO, "New CP state %s -> %s\n",
+ LOGP(DLSMS, LOGL_INFO,
+ SMC_LOG_STR "new CP state %s -> %s\n", inst->id,
smc_state_names[inst->cp_state], smc_state_names[state]);
inst->cp_state = state;
}
@@ -130,7 +139,8 @@ static int gsm411_tx_cp_error(struct gsm411_smc_inst *inst, uint8_t cause)
struct msgb *nmsg = gsm411_msgb_alloc();
uint8_t *causep;
- LOGP(DLSMS, LOGL_NOTICE, "TX CP-ERROR, cause %d (%s)\n", cause,
+ LOGP(DLSMS, LOGL_NOTICE,
+ SMC_LOG_STR "TX CP-ERROR, cause %d (%s)\n", inst->id, cause,
get_value_string(gsm411_cp_cause_strs, cause));
causep = msgb_put(nmsg, 1);
@@ -146,8 +156,10 @@ static int gsm411_mnsms_est_req(struct gsm411_smc_inst *inst, struct msgb *msg)
struct msgb *nmsg;
if (inst->cp_msg) {
- LOGP(DLSMS, LOGL_FATAL, "EST REQ, but we already have an "
- "cp_msg. This should never happen, please fix!\n");
+ LOGP(DLSMS, LOGL_FATAL,
+ SMC_LOG_STR "EST REQ, but we already have an "
+ "cp_msg. This should never happen, please fix!\n",
+ inst->id);
msgb_free(inst->cp_msg);
}
@@ -164,7 +176,8 @@ static int gsm411_mmsms_send_msg(struct gsm411_smc_inst *inst)
{
struct msgb *nmsg;
- LOGP(DLSMS, LOGL_INFO, "Send CP data\n");
+ LOGP(DLSMS, LOGL_INFO,
+ SMC_LOG_STR "send CP data\n", inst->id);
/* reset retry counter */
if (inst->cp_state != GSM411_CPS_WAIT_CP_ACK)
inst->cp_retx = 0;
@@ -187,8 +200,10 @@ static int gsm411_mmsms_send_msg(struct gsm411_smc_inst *inst)
static int gsm411_mmsms_est_cnf(struct gsm411_smc_inst *inst, struct msgb *msg)
{
if (!inst->cp_msg) {
- LOGP(DLSMS, LOGL_FATAL, "EST CNF, but we have no cp_msg. This "
- "should never happen, please fix!\n");
+ LOGP(DLSMS, LOGL_FATAL,
+ SMC_LOG_STR "EST CNF, but we have no cp_msg. This "
+ "should never happen, please fix!\n",
+ inst->id);
return -EINVAL;
}
@@ -203,7 +218,9 @@ static void cp_timer_expired(void *data)
if (inst->cp_retx == inst->cp_max_retr) {
- LOGP(DLSMS, LOGL_INFO, "TC1* timeout, no more retries.\n");
+ LOGP(DLSMS, LOGL_INFO,
+ SMC_LOG_STR "TC1* timeout, no more retries.\n",
+ inst->id);
/* 5.3.2.1: enter idle state */
new_cp_state(inst, GSM411_CPS_IDLE);
/* indicate error */
@@ -221,7 +238,8 @@ static void cp_timer_expired(void *data)
return;
}
- LOGP(DLSMS, LOGL_INFO, "TC1* timeout, retrying...\n");
+ LOGP(DLSMS, LOGL_INFO,
+ SMC_LOG_STR "TC1* timeout, retrying...\n", inst->id);
inst->cp_retx++;
gsm411_mmsms_est_cnf(inst, NULL);
}
@@ -234,7 +252,8 @@ static int gsm411_mmsms_cp_ack(struct gsm411_smc_inst *inst, struct msgb *msg)
inst->cp_msg = NULL;
}
- LOGP(DLSMS, LOGL_INFO, "Received CP-ACK\n");
+ LOGP(DLSMS, LOGL_INFO,
+ SMC_LOG_STR "received CP-ACK\n", inst->id);
/* 5.3.2.1 enter MM Connection established */
new_cp_state(inst, GSM411_CPS_MM_ESTABLISHED);
/* 5.3.2.1: Reset Timer TC1* */
@@ -244,7 +263,9 @@ static int gsm411_mmsms_cp_ack(struct gsm411_smc_inst *inst, struct msgb *msg)
if (inst->cp_rel) {
struct msgb *nmsg;
- LOGP(DLSMS, LOGL_INFO, "We have pending release.\n");
+ LOGP(DLSMS, LOGL_INFO,
+ SMC_LOG_STR "we have pending release.\n",
+ inst->id);
new_cp_state(inst, GSM411_CPS_IDLE);
/* release MM connection */
nmsg = gsm411_msgb_alloc();
@@ -259,7 +280,8 @@ static int gsm411_mmsms_cp_data(struct gsm411_smc_inst *inst, struct msgb *msg)
struct msgb *nmsg;
int mt = GSM411_MNSMS_DATA_IND;
- LOGP(DLSMS, LOGL_INFO, "Received CP-DATA\n");
+ LOGP(DLSMS, LOGL_INFO,
+ SMC_LOG_STR "received CP-DATA\n", inst->id);
/* 5.3.1 enter MM Connection established (if idle) */
if (inst->cp_state == GSM411_CPS_IDLE) {
new_cp_state(inst, GSM411_CPS_MM_ESTABLISHED);
@@ -280,8 +302,10 @@ static int gsm411_mmsms_cp_data(struct gsm411_smc_inst *inst, struct msgb *msg)
static int gsm411_mnsms_data_req(struct gsm411_smc_inst *inst, struct msgb *msg)
{
if (inst->cp_msg) {
- LOGP(DLSMS, LOGL_FATAL, "DATA REQ, but we already have an "
- "cp_msg. This should never happen, please fix!\n");
+ LOGP(DLSMS, LOGL_FATAL,
+ SMC_LOG_STR "DATA REQ, but we already have an "
+ "cp_msg. This should never happen, please fix!\n",
+ inst->id);
msgb_free(inst->cp_msg);
}
@@ -304,8 +328,8 @@ static int gsm411_mnsms_rel_req(struct gsm411_smc_inst *inst, struct msgb *msg)
/* store release, until established or released */
if (inst->cp_state != GSM411_CPS_MM_ESTABLISHED) {
LOGP(DLSMS, LOGL_NOTICE,
- "Cannot release yet current state: %s\n",
- smc_state_names[inst->cp_state]);
+ SMC_LOG_STR "cannot release yet current state: %s\n",
+ inst->id, smc_state_names[inst->cp_state]);
inst->cp_rel = 1;
return 0;
}
@@ -332,7 +356,8 @@ static int gsm411_mmsms_cp_error(struct gsm411_smc_inst *inst, struct msgb *msg)
inst->cp_msg = NULL;
}
- LOGP(DLSMS, LOGL_INFO, "Received CP-ERROR\n");
+ LOGP(DLSMS, LOGL_INFO,
+ SMC_LOG_STR "received CP-ERROR\n", inst->id);
/* 5.3.4 enter idle */
new_cp_state(inst, GSM411_CPS_IDLE);
/* indicate error */
@@ -352,7 +377,8 @@ static int gsm411_mmsms_rel_ind(struct gsm411_smc_inst *inst, struct msgb *msg)
inst->cp_msg = NULL;
}
- LOGP(DLSMS, LOGL_INFO, "MM layer is released\n");
+ LOGP(DLSMS, LOGL_INFO,
+ SMC_LOG_STR "MM layer is released\n", inst->id);
/* 5.3.4 enter idle */
new_cp_state(inst, GSM411_CPS_IDLE);
/* indicate error */
@@ -385,7 +411,7 @@ static int gsm411_mnsms_abort_req(struct gsm411_smc_inst *inst,
}
/* statefull handling for MNSMS SAP messages */
-static struct smcdownstate {
+static const struct smcdownstate {
uint32_t states;
int type;
const char *name;
@@ -429,13 +455,15 @@ int gsm411_smc_send(struct gsm411_smc_inst *inst, int msg_type,
break;
}
if (i == SMCDOWNSLLEN) {
- LOGP(DLSMS, LOGL_NOTICE, "Message %u unhandled at this state "
- "%s.\n", msg_type, smc_state_names[inst->cp_state]);
+ LOGP(DLSMS, LOGL_NOTICE,
+ SMC_LOG_STR "message %u unhandled at this state %s.\n",
+ inst->id, msg_type, smc_state_names[inst->cp_state]);
msgb_free(msg);
return 0;
}
- LOGP(DLSMS, LOGL_INFO, "Message %s received in state %s\n",
+ LOGP(DLSMS, LOGL_INFO,
+ SMC_LOG_STR "message %s received in state %s\n", inst->id,
smcdownstatelist[i].name, smc_state_names[inst->cp_state]);
rc = smcdownstatelist[i].rout(inst, msg);
@@ -444,7 +472,7 @@ int gsm411_smc_send(struct gsm411_smc_inst *inst, int msg_type,
}
/* statefull handling for MMSMS SAP messages */
-static struct smcdatastate {
+static const struct smcdatastate {
uint32_t states;
int type, cp_type;
const char *name;
@@ -496,7 +524,7 @@ int gsm411_smc_recv(struct gsm411_smc_inst *inst, int msg_type,
/* find function for current state and message */
for (i = 0; i < SMCDATASLLEN; i++) {
- /* state must machtch, MM message must match
+ /* state must match, MM message must match
* CP msg must match only in case of MMSMS_DATA_IND
*/
if ((msg_type == smcdatastatelist[i].type)
@@ -506,15 +534,17 @@ int gsm411_smc_recv(struct gsm411_smc_inst *inst, int msg_type,
break;
}
if (i == SMCDATASLLEN) {
- LOGP(DLSMS, LOGL_NOTICE, "Message 0x%x/%u unhandled at this "
- "state %s.\n", msg_type, cp_msg_type,
+ LOGP(DLSMS, LOGL_NOTICE,
+ SMC_LOG_STR "message 0x%x/%u unhandled at this "
+ "state %s.\n", inst->id, msg_type, cp_msg_type,
smc_state_names[inst->cp_state]);
if (msg_type == GSM411_MMSMS_EST_IND
|| msg_type == GSM411_MMSMS_DATA_IND) {
struct msgb *nmsg;
- LOGP(DLSMS, LOGL_NOTICE, "RX Unimplemented CP "
- "msg_type: 0x%02x\n", msg_type);
+ LOGP(DLSMS, LOGL_NOTICE,
+ SMC_LOG_STR "RX Unimplemented CP "
+ "msg_type: 0x%02x\n", inst->id, msg_type);
/* 5.3.4 enter idle */
new_cp_state(inst, GSM411_CPS_IDLE);
/* indicate error */
@@ -532,7 +562,8 @@ int gsm411_smc_recv(struct gsm411_smc_inst *inst, int msg_type,
return 0;
}
- LOGP(DLSMS, LOGL_INFO, "Message %s received in state %s\n",
+ LOGP(DLSMS, LOGL_INFO,
+ SMC_LOG_STR "message %s received in state %s\n", inst->id,
smcdatastatelist[i].name, smc_state_names[inst->cp_state]);
rc = smcdatastatelist[i].rout(inst, msg);
diff --git a/src/shared/libosmocore/src/gsm/gsm0411_smr.c b/src/shared/libosmocore/src/gsm/gsm0411_smr.c
index 7dd8f723..a1ee9804 100644
--- a/src/shared/libosmocore/src/gsm/gsm0411_smr.c
+++ b/src/shared/libosmocore/src/gsm/gsm0411_smr.c
@@ -11,16 +11,16 @@
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
+ * 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 Affero General Public License for more details.
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU Affero General Public License
+ * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -42,13 +42,14 @@
*
* Sending Abort/Release (MNSMS-ABORT-REQ or MNSMS-REL-REQ) may cause the
* lower layer to become IDLE. Then it is allowed to destroy this instance,
- * so sending this this MUST be the last thing that is done.
+ * so sending this MUST be the last thing that is done.
*
*/
#include <string.h>
#include <errno.h>
+#include <inttypes.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/timer.h>
@@ -59,16 +60,19 @@
#include <osmocom/gsm/gsm0411_smr.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
+#define SMR_LOG_STR "SMR(%" PRIu64 ") "
+
static void rp_timer_expired(void *data);
/* init a new instance */
-void gsm411_smr_init(struct gsm411_smr_inst *inst, int network,
+void gsm411_smr_init(struct gsm411_smr_inst *inst, uint64_t id, int network,
int (*rl_recv) (struct gsm411_smr_inst *inst, int msg_type,
struct msgb *msg),
int (*mn_send) (struct gsm411_smr_inst *inst, int msg_type,
struct msgb *msg))
{
memset(inst, 0, sizeof(*inst));
+ inst->id = id;
inst->network = network;
inst->rp_state = GSM411_RPS_IDLE;
inst->rl_recv = rl_recv;
@@ -76,21 +80,24 @@ void gsm411_smr_init(struct gsm411_smr_inst *inst, int network,
inst->rp_timer.data = inst;
inst->rp_timer.cb = rp_timer_expired;
- LOGP(DLSMS, LOGL_INFO, "New SMR instance created\n");
+ LOGP(DLSMS, LOGL_INFO,
+ SMR_LOG_STR "instance created for %s.\n",
+ inst->id, inst->network ? "network" : "mobile");
}
/* clear instance */
void gsm411_smr_clear(struct gsm411_smr_inst *inst)
{
- LOGP(DLSMS, LOGL_INFO, "Clear SMR instance\n");
+ LOGP(DLSMS, LOGL_INFO,
+ SMR_LOG_STR "clearing SMR instance\n", inst->id);
osmo_timer_del(&inst->rp_timer);
}
-const char *smr_state_names[] = {
+static const char *smr_state_names[] = {
"IDLE",
"WAIT_FOR_RP_ACK",
- "illegal state 2"
+ "illegal state 2",
"WAIT_TO_TX_RP_ACK",
"WAIT_FOR_RETRANS_T",
};
@@ -127,7 +134,8 @@ const struct value_string gsm411_rp_cause_strs[] = {
static void new_rp_state(struct gsm411_smr_inst *inst,
enum gsm411_rp_state state)
{
- LOGP(DLSMS, LOGL_INFO, "New RP state %s -> %s\n",
+ LOGP(DLSMS, LOGL_INFO,
+ SMR_LOG_STR "new RP state %s -> %s\n", inst->id,
smr_state_names[inst->rp_state], smr_state_names[state]);
inst->rp_state = state;
@@ -148,7 +156,7 @@ static int gsm411_rp_sendmsg(struct gsm411_smr_inst *inst, struct msgb *msg,
rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp));
rp->len = len + 2;
rp->msg_type = rp_msg_type;
- rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */
+ rp->msg_ref = rp_msg_ref;
return inst->mn_send(inst, mnsms_msg_type, msg);
}
@@ -160,8 +168,9 @@ static int gsm411_send_rp_error(struct gsm411_smr_inst *inst,
msgb_tv_put(msg, 1, cause);
- LOGP(DLSMS, LOGL_NOTICE, "TX: SMS RP ERROR, cause %d (%s)\n", cause,
- get_value_string(gsm411_rp_cause_strs, cause));
+ LOGP(DLSMS, LOGL_NOTICE,
+ SMR_LOG_STR "TX: SMS RP ERROR, cause %d (%s)\n", inst->id,
+ cause, get_value_string(gsm411_rp_cause_strs, cause));
return gsm411_rp_sendmsg(inst, msg,
(inst->network) ? GSM411_MT_RP_ERROR_MT : GSM411_MT_RP_ERROR_MO,
@@ -172,7 +181,8 @@ static int gsm411_send_release(struct gsm411_smr_inst *inst)
{
struct msgb *msg = gsm411_msgb_alloc();
- LOGP(DLSMS, LOGL_DEBUG, "TX: MNSMS-REL-REQ\n");
+ LOGP(DLSMS, LOGL_DEBUG,
+ SMR_LOG_STR "TX: MNSMS-REL-REQ\n", inst->id);
return inst->mn_send(inst, GSM411_MNSMS_REL_REQ, msg);
}
@@ -181,8 +191,10 @@ static int gsm411_send_abort(struct gsm411_smr_inst *inst)
{
struct msgb *msg = gsm411_msgb_alloc();
+ LOGP(DLSMS, LOGL_DEBUG,
+ SMR_LOG_STR "TX: MNSMS-ABORT-REQ\n", inst->id);
+
msgb_tv_put(msg, 1, 111); //FIXME: better idea ? */
- LOGP(DLSMS, LOGL_DEBUG, "TX: MNSMS-ABORT-REQ\n");
return inst->mn_send(inst, GSM411_MNSMS_ABORT_REQ, msg);
}
@@ -191,14 +203,16 @@ static int gsm411_send_report(struct gsm411_smr_inst *inst)
{
struct msgb *msg = gsm411_msgb_alloc();
- LOGP(DLSMS, LOGL_DEBUG, "Sending empty SM_RL_REPORT_IND\n");
+ LOGP(DLSMS, LOGL_DEBUG,
+ SMR_LOG_STR "Sending empty SM_RL_REPORT_IND\n", inst->id);
return inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg);
}
static int gsm411_rl_data_req(struct gsm411_smr_inst *inst, struct msgb *msg)
{
- LOGP(DLSMS, LOGL_DEBUG, "TX SMS RP-DATA\n");
+ LOGP(DLSMS, LOGL_DEBUG,
+ SMR_LOG_STR "TX SMS RP-DATA\n", inst->id);
/* start TR1N and enter 'wait for RP-ACK state' */
osmo_timer_schedule(&inst->rp_timer, GSM411_TMR_TR1M);
new_rp_state(inst, GSM411_RPS_WAIT_FOR_RP_ACK);
@@ -208,7 +222,8 @@ static int gsm411_rl_data_req(struct gsm411_smr_inst *inst, struct msgb *msg)
static int gsm411_rl_report_req(struct gsm411_smr_inst *inst, struct msgb *msg)
{
- LOGP(DLSMS, LOGL_DEBUG, "TX SMS REPORT\n");
+ LOGP(DLSMS, LOGL_DEBUG,
+ SMR_LOG_STR "TX SMS REPORT\n", inst->id);
new_rp_state(inst, GSM411_RPS_IDLE);
inst->mn_send(inst, GSM411_MNSMS_DATA_REQ, msg);
@@ -225,7 +240,9 @@ static int gsm411_mnsms_est_ind(struct gsm411_smr_inst *inst, struct msgb *msg)
/* check direction */
if (inst->network == (msg_type & 1)) {
- LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type);
+ LOGP(DLSMS, LOGL_NOTICE,
+ SMR_LOG_STR "Invalid RP type 0x%02x\n",
+ inst->id, msg_type);
gsm411_send_rp_error(inst, rp_data->msg_ref,
GSM411_RP_CAUSE_MSG_INCOMP_STATE);
new_rp_state(inst, GSM411_RPS_IDLE);
@@ -236,21 +253,25 @@ static int gsm411_mnsms_est_ind(struct gsm411_smr_inst *inst, struct msgb *msg)
switch (msg_type) {
case GSM411_MT_RP_DATA_MT:
case GSM411_MT_RP_DATA_MO:
- LOGP(DLSMS, LOGL_DEBUG, "RX SMS RP-DATA\n");
+ LOGP(DLSMS, LOGL_DEBUG,
+ SMR_LOG_STR "RX SMS RP-DATA\n", inst->id);
/* start TR2N and enter 'wait to send RP-ACK state' */
osmo_timer_schedule(&inst->rp_timer, GSM411_TMR_TR2M);
new_rp_state(inst, GSM411_RPS_WAIT_TO_TX_RP_ACK);
rc = inst->rl_recv(inst, GSM411_SM_RL_DATA_IND, msg);
break;
case GSM411_MT_RP_SMMA_MO:
- LOGP(DLSMS, LOGL_DEBUG, "RX SMS RP-SMMA\n");
+ LOGP(DLSMS, LOGL_DEBUG,
+ SMR_LOG_STR "RX SMS RP-SMMA\n", inst->id);
/* start TR2N and enter 'wait to send RP-ACK state' */
osmo_timer_schedule(&inst->rp_timer, GSM411_TMR_TR2M);
new_rp_state(inst, GSM411_RPS_WAIT_TO_TX_RP_ACK);
rc = inst->rl_recv(inst, GSM411_SM_RL_DATA_IND, msg);
break;
default:
- LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type);
+ LOGP(DLSMS, LOGL_NOTICE,
+ SMR_LOG_STR "invalid RP type 0x%02x\n",
+ inst->id, msg_type);
gsm411_send_rp_error(inst, rp_data->msg_ref,
GSM411_RP_CAUSE_MSGTYPE_NOTEXIST);
new_rp_state(inst, GSM411_RPS_IDLE);
@@ -271,7 +292,9 @@ static int gsm411_mnsms_data_ind_tx(struct gsm411_smr_inst *inst,
/* check direction */
if (inst->network == (msg_type & 1)) {
- LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type);
+ LOGP(DLSMS, LOGL_NOTICE,
+ SMR_LOG_STR "invalid RP type 0x%02x\n",
+ inst->id, msg_type);
gsm411_send_rp_error(inst, rp_data->msg_ref,
GSM411_RP_CAUSE_MSG_INCOMP_STATE);
new_rp_state(inst, GSM411_RPS_IDLE);
@@ -282,20 +305,24 @@ static int gsm411_mnsms_data_ind_tx(struct gsm411_smr_inst *inst,
switch (msg_type) {
case GSM411_MT_RP_ACK_MO:
case GSM411_MT_RP_ACK_MT:
- LOGP(DLSMS, LOGL_DEBUG, "RX SMS RP-ACK\n");
+ LOGP(DLSMS, LOGL_DEBUG,
+ SMR_LOG_STR "RX SMS RP-ACK\n", inst->id);
new_rp_state(inst, GSM411_RPS_IDLE);
inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg);
gsm411_send_release(inst);
return 0;
case GSM411_MT_RP_ERROR_MO:
case GSM411_MT_RP_ERROR_MT:
- LOGP(DLSMS, LOGL_DEBUG, "RX SMS RP-ERROR\n");
+ LOGP(DLSMS, LOGL_DEBUG,
+ SMR_LOG_STR "RX SMS RP-ERROR\n", inst->id);
new_rp_state(inst, GSM411_RPS_IDLE);
inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg);
gsm411_send_release(inst);
return 0;
default:
- LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type);
+ LOGP(DLSMS, LOGL_NOTICE,
+ SMR_LOG_STR "Invalid RP type 0x%02x\n",
+ inst->id, msg_type);
gsm411_send_rp_error(inst, rp_data->msg_ref,
GSM411_RP_CAUSE_MSGTYPE_NOTEXIST);
new_rp_state(inst, GSM411_RPS_IDLE);
@@ -309,7 +336,8 @@ static int gsm411_mnsms_data_ind_tx(struct gsm411_smr_inst *inst,
static int gsm411_mnsms_error_ind_tx(struct gsm411_smr_inst *inst,
struct msgb *msg)
{
- LOGP(DLSMS, LOGL_DEBUG, "RX SMS MNSMS-ERROR-IND\n");
+ LOGP(DLSMS, LOGL_DEBUG,
+ SMR_LOG_STR "TX SMS MNSMS-ERROR-IND\n", inst->id);
new_rp_state(inst, GSM411_RPS_IDLE);
inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg);
gsm411_send_release(inst);
@@ -319,7 +347,8 @@ static int gsm411_mnsms_error_ind_tx(struct gsm411_smr_inst *inst,
static int gsm411_mnsms_error_ind_rx(struct gsm411_smr_inst *inst,
struct msgb *msg)
{
- LOGP(DLSMS, LOGL_DEBUG, "RX SMS MNSMS-ERROR-IND\n");
+ LOGP(DLSMS, LOGL_DEBUG,
+ SMR_LOG_STR "RX SMS MNSMS-ERROR-IND\n", inst->id);
new_rp_state(inst, GSM411_RPS_IDLE);
return inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg);
}
@@ -328,17 +357,19 @@ static int gsm411_mnsms_error_ind_rx(struct gsm411_smr_inst *inst,
static void rp_timer_expired(void *data)
{
struct gsm411_smr_inst *inst = data;
+ const char *str;
+
+ str = inst->rp_state == GSM411_RPS_WAIT_TO_TX_RP_ACK
+ ? "TR2N" : "TR1N";
- if (inst->rp_state == GSM411_RPS_WAIT_TO_TX_RP_ACK)
- LOGP(DLSMS, LOGL_DEBUG, "TR2N\n");
- else
- LOGP(DLSMS, LOGL_DEBUG, "TR1N\n");
+ LOGP(DLSMS, LOGL_DEBUG,
+ SMR_LOG_STR "%s expired\n", inst->id, str);
gsm411_send_report(inst);
gsm411_send_abort(inst);
}
/* statefull handling for SM-RL SAP messages */
-static struct smrdownstate {
+static const struct smrdownstate {
uint32_t states;
int type;
const char *name;
@@ -372,13 +403,16 @@ int gsm411_smr_send(struct gsm411_smr_inst *inst, int msg_type,
break;
}
if (i == SMRDOWNSLLEN) {
- LOGP(DLSMS, LOGL_NOTICE, "Message %u unhandled at this state "
- "%s.\n", msg_type, smr_state_names[inst->rp_state]);
+ LOGP(DLSMS, LOGL_NOTICE,
+ SMR_LOG_STR "message %u unhandled at this state "
+ "%s.\n", inst->id, msg_type,
+ smr_state_names[inst->rp_state]);
msgb_free(msg);
return 0;
}
- LOGP(DLSMS, LOGL_INFO, "Message %s received in state %s\n",
+ LOGP(DLSMS, LOGL_INFO,
+ SMR_LOG_STR "message %s received in state %s\n", inst->id,
smrdownstatelist[i].name, smr_state_names[inst->rp_state]);
rc = smrdownstatelist[i].rout(inst, msg);
@@ -387,7 +421,7 @@ int gsm411_smr_send(struct gsm411_smr_inst *inst, int msg_type,
}
/* statefull handling for MMSMS SAP messages */
-static struct smrdatastate {
+static const struct smrdatastate {
uint32_t states;
int type;
const char *name;
@@ -429,7 +463,7 @@ int gsm411_smr_recv(struct gsm411_smr_inst *inst, int msg_type,
/* find function for current state and message */
for (i = 0; i < SMRDATASLLEN; i++) {
- /* state must machtch, MM message must match
+ /* state must match, MM message must match
* CP msg must match only in case of MMSMS_DATA_IND
*/
if ((msg_type == smrdatastatelist[i].type)
@@ -437,12 +471,15 @@ int gsm411_smr_recv(struct gsm411_smr_inst *inst, int msg_type,
break;
}
if (i == SMRDATASLLEN) {
- LOGP(DLSMS, LOGL_NOTICE, "Message %u unhandled at this state "
- "%s.\n", msg_type, smr_state_names[inst->rp_state]);
+ LOGP(DLSMS, LOGL_NOTICE,
+ SMR_LOG_STR "message %u unhandled at this state "
+ "%s.\n", inst->id, msg_type,
+ smr_state_names[inst->rp_state]);
return 0;
}
- LOGP(DLSMS, LOGL_INFO, "Message %s received in state %s\n",
+ LOGP(DLSMS, LOGL_INFO,
+ SMR_LOG_STR "message %s received in state %s\n", inst->id,
smrdatastatelist[i].name, smr_state_names[inst->rp_state]);
rc = smrdatastatelist[i].rout(inst, msg);
diff --git a/src/shared/libosmocore/src/gsm/gsm0411_utils.c b/src/shared/libosmocore/src/gsm/gsm0411_utils.c
index fe69bf41..b84c9f25 100644
--- a/src/shared/libosmocore/src/gsm/gsm0411_utils.c
+++ b/src/shared/libosmocore/src/gsm/gsm0411_utils.c
@@ -4,23 +4,23 @@
/* (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
* (C) 2009 by Harald Welte <laforge@gnumonks.org>
- * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2010-2013 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
+ * 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 Affero General Public License for more details.
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU Affero General Public License
+ * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -33,6 +33,9 @@
#include <osmocom/core/logging.h>
#include <osmocom/gsm/gsm48.h>
+#include <osmocom/gsm/gsm0480.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/protocol/gsm_03_40.h>
#include <osmocom/gsm/protocol/gsm_04_11.h>
#define GSM411_ALLOC_SIZE 1024
@@ -136,13 +139,13 @@ static unsigned long gsm340_vp_relative(uint8_t *sms_vp)
vp = *(sms_vp);
if (vp <= 143)
- minutes = vp + 1 * 5;
+ minutes = (vp + 1) * 5;
else if (vp <= 167)
minutes = 12*60 + (vp-143) * 30;
else if (vp <= 196)
- minutes = vp-166 * 60 * 24;
+ minutes = (vp-166) * 60 * 24;
else
- minutes = vp-192 * 60 * 24 * 7;
+ minutes = (vp-192) * 60 * 24 * 7;
return minutes;
}
@@ -269,16 +272,26 @@ int gsm340_gen_oa(uint8_t *oa, unsigned int oa_len, uint8_t type,
{
int len_in_bytes;
- /* prevent buffer overflows */
- if (strlen(number) > 20)
- number = "";
-
oa[1] = 0x80 | (type << 4) | plan;
- len_in_bytes = gsm48_encode_bcd_number(oa, oa_len, 1, number);
-
- /* GSM 03.40 tells us the length is in 'useful semi-octets' */
- oa[0] = strlen(number) & 0xff;
+ if (type == GSM340_TYPE_ALPHA_NUMERIC) {
+ /*
+ * TODO/FIXME: what is the 'useful semi-octets' excluding any
+ * semi octet containing only fill bits.
+ * The current code picks the number of bytes written by the
+ * 7bit encoding routines and multiplies it by two.
+ */
+ gsm_7bit_encode_n(&oa[2], oa_len - 2, number, &len_in_bytes);
+ oa[0] = len_in_bytes * 2;
+ len_in_bytes += 2;
+ } else {
+ /* prevent buffer overflows */
+ if (strlen(number) > 20)
+ number = "";
+ len_in_bytes = gsm48_encode_bcd_number(oa, oa_len, 1, number);
+ /* GSM 03.40 tells us the length is in 'useful semi-octets' */
+ oa[0] = strlen(number) & 0xff;
+ }
return len_in_bytes;
}
@@ -303,12 +316,7 @@ int gsm411_push_rp_header(struct msgb *msg, uint8_t rp_msg_type,
int gsm411_push_cp_header(struct msgb *msg, uint8_t proto, uint8_t trans,
uint8_t msg_type)
{
- struct gsm48_hdr *gh;
-
- gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
- /* Outgoing needs the highest bit set */
- gh->proto_discr = proto | (trans << 4);
- gh->msg_type = msg_type;
-
+ /* Outgoing proto_discr needs the highest bit set */
+ gsm0480_l3hdr_push(msg, proto | (trans << 4), msg_type);
return 0;
}
diff --git a/src/shared/libosmocore/src/gsm/gsm0480.c b/src/shared/libosmocore/src/gsm/gsm0480.c
index b9b3ed97..3c23f6fc 100644
--- a/src/shared/libosmocore/src/gsm/gsm0480.c
+++ b/src/shared/libosmocore/src/gsm/gsm0480.c
@@ -105,7 +105,7 @@ struct msgb *gsm0480_create_unstructuredSS_Notify(int alertPattern, const char *
msgb_put_u8(msg, ASN1_OCTET_STRING_TAG);
ussd_len_ptr = msgb_put(msg, 1);
data = msgb_put(msg, 0);
- len = gsm_7bit_encode(data, text);
+ gsm_7bit_encode_n_ussd(data, msgb_tailroom(msg), text, &len);
msgb_put(msg, len);
ussd_len_ptr[0] = len;
/* USSD-String } */
@@ -172,7 +172,7 @@ struct msgb *gsm0480_create_notifySS(const char *text)
msgb_put_u8(msg, 0x82);
tmp_len = msgb_put(msg, 1);
data = msgb_put(msg, 0);
- len = gsm_7bit_encode(data, text);
+ gsm_7bit_encode_n_ussd(data, msgb_tailroom(msg), text, &len);
tmp_len[0] = len;
msgb_put(msg, len);
@@ -192,31 +192,49 @@ struct msgb *gsm0480_create_notifySS(const char *text)
}
/* Forward declarations */
-static int parse_ussd(const struct gsm48_hdr *hdr,
- uint16_t len, struct ussd_request *req);
-static int parse_ussd_info_elements(const uint8_t *ussd_ie, uint16_t len,
- struct ussd_request *req);
+static int parse_ss(const struct gsm48_hdr *hdr,
+ uint16_t len, struct ss_request *req);
+static int parse_ss_info_elements(const uint8_t *ussd_ie, uint16_t len,
+ struct ss_request *req);
static int parse_facility_ie(const uint8_t *facility_ie, uint16_t length,
- struct ussd_request *req);
+ struct ss_request *req);
static int parse_ss_invoke(const uint8_t *invoke_data, uint16_t length,
- struct ussd_request *req);
+ struct ss_request *req);
static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length,
- struct ussd_request *req);
+ struct ss_request *req);
+static int parse_ss_for_bs_req(const uint8_t *ss_req_data,
+ uint16_t length,
+ struct ss_request *req);
/* Decode a mobile-originated USSD-request message */
int gsm0480_decode_ussd_request(const struct gsm48_hdr *hdr, uint16_t len,
struct ussd_request *req)
{
+ struct ss_request ss;
int rc = 0;
+ memset(&ss, 0, sizeof(ss));
+
if (len < sizeof(*hdr) + 2) {
LOGP(0, LOGL_DEBUG, "USSD Request is too short.\n");
return 0;
}
- if ((hdr->proto_discr & 0x0f) == GSM48_PDISC_NC_SS) {
+ if (gsm48_hdr_pdisc(hdr) == GSM48_PDISC_NC_SS) {
req->transaction_id = hdr->proto_discr & 0x70;
- rc = parse_ussd(hdr, len, req);
+
+ ss.transaction_id = req->transaction_id;
+ rc = parse_ss(hdr, len, &ss);
+
+ /* convert from ss_request to legacy ussd_request */
+ req->transaction_id = ss.transaction_id;
+ req->invoke_id = ss.invoke_id;
+ if (ss.ussd_text[0] == 0xFF)
+ req->text[0] = '\0';
+ else {
+ memcpy(req->text, ss.ussd_text, sizeof(req->text));
+ req->text[sizeof(req->text)-1] = '\0';
+ }
}
if (!rc)
@@ -225,20 +243,42 @@ int gsm0480_decode_ussd_request(const struct gsm48_hdr *hdr, uint16_t len,
return rc;
}
-static int parse_ussd(const struct gsm48_hdr *hdr, uint16_t len, struct ussd_request *req)
+/* Decode a mobile-originated SS request message */
+int gsm0480_decode_ss_request(const struct gsm48_hdr *hdr, uint16_t len,
+ struct ss_request *req)
+{
+ int rc = 0;
+
+ if (len < sizeof(*hdr) + 2) {
+ LOGP(0, LOGL_DEBUG, "SS Request is too short.\n");
+ return 0;
+ }
+
+ if (gsm48_hdr_pdisc(hdr) == GSM48_PDISC_NC_SS) {
+ req->transaction_id = hdr->proto_discr & 0x70;
+ rc = parse_ss(hdr, len, req);
+ }
+
+ if (!rc)
+ LOGP(0, LOGL_DEBUG, "Error occurred while parsing received SS!\n");
+
+ return rc;
+}
+
+static int parse_ss(const struct gsm48_hdr *hdr, uint16_t len, struct ss_request *req)
{
int rc = 1;
- uint8_t msg_type = hdr->msg_type & 0xBF; /* message-type - section 3.4 */
+ uint8_t msg_type = hdr->msg_type & 0x3F; /* message-type - section 3.4 */
switch (msg_type) {
case GSM0480_MTYPE_RELEASE_COMPLETE:
- LOGP(0, LOGL_DEBUG, "USS Release Complete\n");
+ LOGP(0, LOGL_DEBUG, "SS Release Complete\n");
/* could also parse out the optional Cause/Facility data */
- req->text[0] = 0xFF;
+ req->ussd_text[0] = 0xFF;
break;
case GSM0480_MTYPE_REGISTER:
case GSM0480_MTYPE_FACILITY:
- rc &= parse_ussd_info_elements(&hdr->data[0], len - sizeof(*hdr), req);
+ rc &= parse_ss_info_elements(&hdr->data[0], len - sizeof(*hdr), req);
break;
default:
LOGP(0, LOGL_DEBUG, "Unknown GSM 04.80 message-type field 0x%02x\n",
@@ -250,16 +290,16 @@ static int parse_ussd(const struct gsm48_hdr *hdr, uint16_t len, struct ussd_req
return rc;
}
-static int parse_ussd_info_elements(const uint8_t *ussd_ie, uint16_t len,
- struct ussd_request *req)
+static int parse_ss_info_elements(const uint8_t *ss_ie, uint16_t len,
+ struct ss_request *req)
{
int rc = -1;
/* Information Element Identifier - table 3.2 & GSM 04.08 section 10.5 */
uint8_t iei;
uint8_t iei_length;
- iei = ussd_ie[0];
- iei_length = ussd_ie[1];
+ iei = ss_ie[0];
+ iei_length = ss_ie[1];
/* If the data does not fit, report an error */
if (len - 2 < iei_length)
@@ -269,7 +309,7 @@ static int parse_ussd_info_elements(const uint8_t *ussd_ie, uint16_t len,
case GSM48_IE_CAUSE:
break;
case GSM0480_IE_FACILITY:
- rc = parse_facility_ie(ussd_ie+2, iei_length, req);
+ rc = parse_facility_ie(ss_ie + 2, iei_length, req);
break;
case GSM0480_IE_SS_VERSION:
break;
@@ -284,7 +324,7 @@ static int parse_ussd_info_elements(const uint8_t *ussd_ie, uint16_t len,
}
static int parse_facility_ie(const uint8_t *facility_ie, uint16_t length,
- struct ussd_request *req)
+ struct ss_request *req)
{
int rc = 1;
uint8_t offset = 0;
@@ -303,8 +343,8 @@ static int parse_facility_ie(const uint8_t *facility_ie, uint16_t length,
switch (component_type) {
case GSM0480_CTYPE_INVOKE:
rc &= parse_ss_invoke(facility_ie+2,
- component_length,
- req);
+ component_length,
+ req);
break;
case GSM0480_CTYPE_RETURN_RESULT:
break;
@@ -326,7 +366,7 @@ static int parse_facility_ie(const uint8_t *facility_ie, uint16_t length,
/* Parse an Invoke component - see table 3.3 */
static int parse_ss_invoke(const uint8_t *invoke_data, uint16_t length,
- struct ussd_request *req)
+ struct ss_request *req)
{
int rc = 1;
uint8_t offset;
@@ -337,7 +377,7 @@ static int parse_ss_invoke(const uint8_t *invoke_data, uint16_t length,
/* mandatory part */
if (invoke_data[0] != GSM0480_COMPIDTAG_INVOKE_ID) {
LOGP(0, LOGL_DEBUG, "Unexpected GSM 04.80 Component-ID tag "
- "0x%02x (expecting Invoke ID tag)\n", invoke_data[0]);
+ "0x%02x (expecting Invoke ID tag)\n", invoke_data[0]);
}
offset = invoke_data[1] + 2;
@@ -356,12 +396,20 @@ static int parse_ss_invoke(const uint8_t *invoke_data, uint16_t length,
if (offset + 2 > length)
return 0;
uint8_t operation_code = invoke_data[offset+2];
+ req->opcode = operation_code;
switch (operation_code) {
case GSM0480_OP_CODE_PROCESS_USS_REQ:
rc = parse_process_uss_req(invoke_data + offset + 3,
length - offset - 3,
req);
break;
+ case GSM0480_OP_CODE_ACTIVATE_SS:
+ case GSM0480_OP_CODE_DEACTIVATE_SS:
+ case GSM0480_OP_CODE_INTERROGATE_SS:
+ rc = parse_ss_for_bs_req(invoke_data + offset + 3,
+ length - offset - 3,
+ req);
+ break;
default:
LOGP(0, LOGL_DEBUG, "GSM 04.80 operation code 0x%02x "
"is not yet handled\n", operation_code);
@@ -380,7 +428,7 @@ static int parse_ss_invoke(const uint8_t *invoke_data, uint16_t length,
/* Parse the parameters of a Process UnstructuredSS Request */
static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length,
- struct ussd_request *req)
+ struct ss_request *req)
{
int rc = 0;
int num_chars;
@@ -401,8 +449,9 @@ static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length,
/* Prevent a mobile-originated buffer-overrun! */
if (num_chars > MAX_LEN_USSD_STRING)
num_chars = MAX_LEN_USSD_STRING;
- gsm_7bit_decode(req->text,
- &(uss_req_data[7]), num_chars);
+ gsm_7bit_decode_n_ussd((char *)req->ussd_text,
+ sizeof(req->ussd_text),
+ &(uss_req_data[7]), num_chars);
rc = 1;
}
}
@@ -410,10 +459,33 @@ static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length,
return rc;
}
+/* Parse the parameters of a Interrogate/Activate/DeactivateSS Request */
+static int parse_ss_for_bs_req(const uint8_t *ss_req_data,
+ uint16_t length,
+ struct ss_request *req)
+{
+ int rc = 0;
+
+
+ /* we need at least that much */
+ if (length < 5)
+ return 0;
+
+
+ if (ss_req_data[0] == GSM_0480_SEQUENCE_TAG) {
+ if ((ss_req_data[2] == ASN1_OCTET_STRING_TAG) &&
+ ss_req_data[3] == 1) {
+ req->ss_code = ss_req_data[4];
+
+ rc = 1;
+ }
+ }
+ return rc;
+}
+
struct msgb *gsm0480_create_ussd_resp(uint8_t invoke_id, uint8_t trans_id, const char *text)
{
struct msgb *msg;
- struct gsm48_hdr *gh;
uint8_t *ptr8;
int response_len;
@@ -423,7 +495,7 @@ struct msgb *gsm0480_create_ussd_resp(uint8_t invoke_id, uint8_t trans_id, const
/* First put the payload text into the message */
ptr8 = msgb_put(msg, 0);
- response_len = gsm_7bit_encode(ptr8, text);
+ gsm_7bit_encode_n_ussd(ptr8, msgb_tailroom(msg), text, &response_len);
msgb_put(msg, response_len);
/* Then wrap it as an Octet String */
@@ -452,10 +524,48 @@ struct msgb *gsm0480_create_ussd_resp(uint8_t invoke_id, uint8_t trans_id, const
msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
/* And finally pre-pend the L3 header */
+ gsm0480_l3hdr_push(msg,
+ GSM48_PDISC_NC_SS | trans_id
+ | (1<<7) /* TI direction = 1 */,
+ GSM0480_MTYPE_RELEASE_COMPLETE);
+ return msg;
+}
+
+struct gsm48_hdr *gsm0480_l3hdr_push(struct msgb *msg, uint8_t proto_discr,
+ uint8_t msg_type)
+{
+ struct gsm48_hdr *gh;
gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
- gh->proto_discr = GSM48_PDISC_NC_SS | trans_id
- | (1<<7); /* TI direction = 1 */
- gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
+ gh->proto_discr = proto_discr;
+ gh->msg_type = msg_type;
+ return gh;
+}
+
+struct msgb *gsm0480_create_ussd_notify(int level, const char *text)
+{
+ struct msgb *msg;
+
+ msg = gsm0480_create_unstructuredSS_Notify(level, text);
+ if (!msg)
+ return NULL;
+
+ gsm0480_wrap_invoke(msg, GSM0480_OP_CODE_USS_NOTIFY, 0);
+ gsm0480_wrap_facility(msg);
+
+ gsm0480_l3hdr_push(msg, GSM48_PDISC_NC_SS, GSM0480_MTYPE_REGISTER);
+ return msg;
+}
+
+struct msgb *gsm0480_create_ussd_release_complete(void)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(1024, 128, "GSM 04.80 USSD REL COMPL");
+ if (!msg)
+ return NULL;
+ /* FIXME: should this set trans_id and TI direction flag? */
+ gsm0480_l3hdr_push(msg, GSM48_PDISC_NC_SS,
+ GSM0480_MTYPE_RELEASE_COMPLETE);
return msg;
}
diff --git a/src/shared/libosmocore/src/gsm/gsm0808.c b/src/shared/libosmocore/src/gsm/gsm0808.c
index 30098278..4035f465 100644
--- a/src/shared/libosmocore/src/gsm/gsm0808.c
+++ b/src/shared/libosmocore/src/gsm/gsm0808.c
@@ -76,6 +76,19 @@ struct msgb *gsm0808_create_reset(void)
return msg;
}
+struct msgb *gsm0808_create_reset_ack(void)
+{
+ struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+ "bssmap: reset ack");
+ if (!msg)
+ return NULL;
+
+ msgb_v_put(msg, BSS_MAP_MSG_RESET_ACKNOWLEDGE);
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
struct msgb *gsm0808_create_clear_complete(void)
{
struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
@@ -282,31 +295,135 @@ struct msgb *gsm0808_create_dtap(struct msgb *msg_l3, uint8_t link_id)
return msg;
}
+/* As per 3GPP TS 48.008 version 11.7.0 Release 11 */
static const struct tlv_definition bss_att_tlvdef = {
.def = {
+ [GSM0808_IE_CIRCUIT_IDENTITY_CODE] = { TLV_TYPE_FIXED, 2 },
+ [GSM0808_IE_CONNECTION_RELEASE_RQSTED] = { TLV_TYPE_TV },
+ [GSM0808_IE_RESOURCE_AVAILABLE] = { TLV_TYPE_FIXED, 21 },
+ [GSM0808_IE_CAUSE] = { TLV_TYPE_TLV },
[GSM0808_IE_IMSI] = { TLV_TYPE_TLV },
[GSM0808_IE_TMSI] = { TLV_TYPE_TLV },
- [GSM0808_IE_CELL_IDENTIFIER_LIST] = { TLV_TYPE_TLV },
- [GSM0808_IE_CHANNEL_NEEDED] = { TLV_TYPE_TV },
- [GSM0808_IE_EMLPP_PRIORITY] = { TLV_TYPE_TV },
+ [GSM0808_IE_NUMBER_OF_MSS] = { TLV_TYPE_TV },
+ [GSM0808_IE_LAYER_3_HEADER_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_IE_ENCRYPTION_INFORMATION] = { TLV_TYPE_TLV },
[GSM0808_IE_CHANNEL_TYPE] = { TLV_TYPE_TLV },
+ [GSM0808_IE_PERIODICITY] = { TLV_TYPE_TV },
+ [GSM0808_IE_EXTENDED_RESOURCE_INDICATOR]= { TLV_TYPE_TV },
+ [GSM0808_IE_TOTAL_RESOURCE_ACCESSIBLE] = { TLV_TYPE_FIXED, 4 },
+ [GSM0808_IE_LSA_IDENTIFIER] = { TLV_TYPE_TLV },
+ [GSM0808_IE_LSA_IDENTIFIER_LIST] = { TLV_TYPE_TLV },
+ [GSM0808_IE_LSA_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_IE_CELL_IDENTIFIER] = { TLV_TYPE_TLV },
[GSM0808_IE_PRIORITY] = { TLV_TYPE_TLV },
- [GSM0808_IE_CIRCUIT_IDENTITY_CODE] = { TLV_TYPE_FIXED, 2 },
- [GSM0808_IE_DOWNLINK_DTX_FLAG] = { TLV_TYPE_TV },
- [GSM0808_IE_INTERFERENCE_BAND_TO_USE] = { TLV_TYPE_TV },
[GSM0808_IE_CLASSMARK_INFORMATION_T2] = { TLV_TYPE_TLV },
- [GSM0808_IE_GROUP_CALL_REFERENCE] = { TLV_TYPE_TLV },
+ [GSM0808_IE_CLASSMARK_INFORMATION_T3] = { TLV_TYPE_TLV },
+ [GSM0808_IE_INTERFERENCE_BAND_TO_USE] = { TLV_TYPE_TV },
+ [GSM0808_IE_RR_CAUSE] = { TLV_TYPE_TV },
+ [GSM0808_IE_LAYER_3_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_IE_DLCI] = { TLV_TYPE_TV },
+ [GSM0808_IE_DOWNLINK_DTX_FLAG] = { TLV_TYPE_TV },
+ [GSM0808_IE_CELL_IDENTIFIER_LIST] = { TLV_TYPE_TLV },
+ [GSM0808_IE_CELL_ID_LIST_SEGMENT] = { TLV_TYPE_TLV },
+ [GSM0808_IE_CELL_ID_LIST_SEG_EST_CELLS] = { TLV_TYPE_TLV },
+ [GSM0808_IE_CELL_ID_LIST_SEG_CELLS_TBE] = { TLV_TYPE_TLV },
+ [GSM0808_IE_CELL_ID_LIST_SEG_REL_CELLS] = { TLV_TYPE_TLV },
+ [GSM0808_IE_CELL_ID_LIST_SEG_NE_CELLS] = { TLV_TYPE_TLV },
+ [GSM0808_IE_RESPONSE_RQST] = { TLV_TYPE_T },
+ [GSM0808_IE_RESOURCE_INDICATION_METHOD] = { TLV_TYPE_TV },
+ [GSM0808_IE_CLASSMARK_INFORMATION_TYPE_1] = { TLV_TYPE_TV },
+ [GSM0808_IE_CIRCUIT_IDENTITY_CODE_LIST] = { TLV_TYPE_TLV },
+ [GSM0808_IE_DIAGNOSTIC] = { TLV_TYPE_TLV },
+ [GSM0808_IE_CHOSEN_CHANNEL] = { TLV_TYPE_TV },
+ [GSM0808_IE_CIPHER_RESPONSE_MODE] = { TLV_TYPE_TV },
+ [GSM0808_IE_LAYER_3_MESSAGE_CONTENTS] = { TLV_TYPE_TLV },
+ [GSM0808_IE_CHANNEL_NEEDED] = { TLV_TYPE_TV },
+ [GSM0808_IE_TRACE_TYPE] = { TLV_TYPE_TV },
+ [GSM0808_IE_TRIGGERID] = { TLV_TYPE_TLV },
+ [GSM0808_IE_TRACE_REFERENCE] = { TLV_TYPE_TV },
+ [GSM0808_IE_TRANSACTIONID] = { TLV_TYPE_TLV },
+ [GSM0808_IE_MOBILE_IDENTITY] = { TLV_TYPE_TLV },
+ [GSM0808_IE_OMCID] = { TLV_TYPE_TLV },
+ [GSM0808_IE_FORWARD_INDICATOR] = { TLV_TYPE_TV },
+ [GSM0808_IE_CHOSEN_ENCR_ALG] = { TLV_TYPE_TV },
+ [GSM0808_IE_CIRCUIT_POOL] = { TLV_TYPE_TV },
+ [GSM0808_IE_CIRCUIT_POOL_LIST] = { TLV_TYPE_TLV },
+ [GSM0808_IE_TIME_INDICATION] = { TLV_TYPE_TV },
+ [GSM0808_IE_RESOURCE_SITUATION] = { TLV_TYPE_TLV },
+ [GSM0808_IE_CURRENT_CHANNEL_TYPE_1] = { TLV_TYPE_TV },
+ [GSM0808_IE_QUEUEING_INDICATOR] = { TLV_TYPE_TV },
+ [GSM0808_IE_SPEECH_VERSION] = { TLV_TYPE_TV },
+ [GSM0808_IE_ASSIGNMENT_REQUIREMENT] = { TLV_TYPE_TV },
[GSM0808_IE_TALKER_FLAG] = { TLV_TYPE_T },
+ [GSM0808_IE_GROUP_CALL_REFERENCE] = { TLV_TYPE_TLV },
+ [GSM0808_IE_EMLPP_PRIORITY] = { TLV_TYPE_TV },
[GSM0808_IE_CONFIG_EVO_INDI] = { TLV_TYPE_TV },
+ [GSM0808_IE_OLD_BSS_TO_NEW_BSS_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_IE_LCS_QOS] = { TLV_TYPE_TLV },
[GSM0808_IE_LSA_ACCESS_CTRL_SUPPR] = { TLV_TYPE_TV },
+ [GSM0808_IE_LCS_PRIORITY] = { TLV_TYPE_TLV },
+ [GSM0808_IE_LOCATION_TYPE] = { TLV_TYPE_TLV },
+ [GSM0808_IE_LOCATION_ESTIMATE] = { TLV_TYPE_TLV },
+ [GSM0808_IE_POSITIONING_DATA] = { TLV_TYPE_TLV },
+ [GSM0808_IE_LCS_CAUSE] = { 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 },
+ [GSM0808_IE_DECIPHERING_KEYS] = { TLV_TYPE_TLV },
+ [GSM0808_IE_RETURN_ERROR_RQST] = { TLV_TYPE_TLV },
+ [GSM0808_IE_RETURN_ERROR_CAUSE] = { TLV_TYPE_TLV },
+ [GSM0808_IE_SEGMENTATION] = { TLV_TYPE_TLV },
[GSM0808_IE_SERVICE_HANDOVER] = { TLV_TYPE_TLV },
- [GSM0808_IE_ENCRYPTION_INFORMATION] = { TLV_TYPE_TLV },
- [GSM0808_IE_CIPHER_RESPONSE_MODE] = { TLV_TYPE_TV },
- [GSM0808_IE_CELL_IDENTIFIER] = { TLV_TYPE_TLV },
- [GSM0808_IE_CHOSEN_CHANNEL] = { TLV_TYPE_TV },
- [GSM0808_IE_LAYER_3_INFORMATION] = { TLV_TYPE_TLV },
- [GSM0808_IE_SPEECH_VERSION] = { TLV_TYPE_TV },
- [GSM0808_IE_CHOSEN_ENCR_ALG] = { TLV_TYPE_TV },
+ [GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_UMTS] = { TLV_TYPE_TLV },
+ [GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_CDMA2000] = { TLV_TYPE_TLV },
+ [GSM0808_IE_GERAN_CLASSMARK] = { TLV_TYPE_TLV },
+ [GSM0808_IE_GERAN_BSC_CONTAINER] = { TLV_TYPE_TLV },
+ [GSM0808_IE_NEW_BSS_TO_OLD_BSS_INFO] = { TLV_TYPE_TLV },
+ [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_PAGING_INFO] = { TLV_TYPE_TV },
+ [GSM0808_IE_IMEI] = { TLV_TYPE_TLV },
+ [GSM0808_IE_VELOCITY_ESTIMATE] = { TLV_TYPE_TLV },
+ [GSM0808_IE_VGCS_FEATURE_FLAGS] = { TLV_TYPE_TLV },
+ [GSM0808_IE_TALKER_PRIORITY] = { TLV_TYPE_TV },
+ [GSM0808_IE_EMERGENCY_SET_INDICATION] = { TLV_TYPE_T },
+ [GSM0808_IE_TALKER_IDENTITY] = { TLV_TYPE_TLV },
+ [GSM0808_IE_SMS_TO_VGCS] = { TLV_TYPE_TLV },
+ [GSM0808_IE_VGCS_TALKER_MODE] = { TLV_TYPE_TLV },
+ [GSM0808_IE_VGCS_VBS_CELL_STATUS] = { TLV_TYPE_TLV },
+ [GSM0808_IE_GANSS_ASSISTANCE_DATA] = { TLV_TYPE_TLV },
+ [GSM0808_IE_GANSS_POSITIONING_DATA] = { TLV_TYPE_TLV },
+ [GSM0808_IE_GANSS_LOCATION_TYPE] = { TLV_TYPE_TLV },
+ [GSM0808_IE_APP_DATA] = { TLV_TYPE_TLV },
+ [GSM0808_IE_DATA_IDENTITY] = { TLV_TYPE_TLV },
+ [GSM0808_IE_APP_DATA_INFO] = { TLV_TYPE_TLV },
+ [GSM0808_IE_MSISDN] = { TLV_TYPE_TLV },
+ [GSM0808_IE_AOIP_TRASP_ADDR] = { TLV_TYPE_TLV },
+ [GSM0808_IE_SPEECH_CODEC_LIST] = { TLV_TYPE_TLV },
+ [GSM0808_IE_SPEECH_CODEC] = { TLV_TYPE_TLV },
+ [GSM0808_IE_CALL_ID] = { TLV_TYPE_FIXED, 4 },
+ [GSM0808_IE_CALL_ID_LIST] = { TLV_TYPE_TLV },
+ [GSM0808_IE_A_IF_SEL_FOR_RESET] = { TLV_TYPE_TV },
+ [GSM0808_IE_KC_128] = { TLV_TYPE_FIXED, 16 },
+ [GSM0808_IE_CSG_IDENTIFIER] = { TLV_TYPE_TLV },
+ [GSM0808_IE_REDIR_ATTEMPT_FLAG] = { TLV_TYPE_T },
+ [GSM0808_IE_REROUTE_REJ_CAUSE] = { TLV_TYPE_TV },
+ [GSM0808_IE_SEND_SEQ_NUM] = { TLV_TYPE_TV },
+ [GSM0808_IE_REROUTE_COMPL_OUTCOME] = { TLV_TYPE_TV },
+ [GSM0808_IE_GLOBAL_CALL_REF] = { TLV_TYPE_TLV },
+ [GSM0808_IE_LCLS_CONFIG] = { TLV_TYPE_TV },
+ [GSM0808_IE_LCLS_CONN_STATUS_CTRL] = { TLV_TYPE_TV },
+ [GSM0808_IE_LCLS_CORR_NOT_NEEDED] = { TLV_TYPE_TV },
+ [GSM0808_IE_LCLS_BSS_STATUS] = { TLV_TYPE_TV },
+ [GSM0808_IE_LCLS_BREAK_REQ] = { TLV_TYPE_TV },
+ [GSM0808_IE_CSFB_INDICATION] = { TLV_TYPE_T },
+ [GSM0808_IE_CS_TO_PS_SRVCC] = { TLV_TYPE_T },
+ [GSM0808_IE_SRC_ENB_TO_TGT_ENB_TRANSP] = { TLV_TYPE_TLV },
+ [GSM0808_IE_CS_TO_PS_SRVCC_IND] = { TLV_TYPE_T },
+ [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 },
},
};
@@ -319,6 +436,7 @@ static const struct value_string gsm0808_msgt_names[] = {
{ BSS_MAP_MSG_ASSIGMENT_RQST, "ASSIGNMENT REQ" },
{ BSS_MAP_MSG_ASSIGMENT_COMPLETE, "ASSIGNMENT COMPL" },
{ BSS_MAP_MSG_ASSIGMENT_FAILURE, "ASSIGNMENT FAIL" },
+ { BSS_MAP_MSG_CHAN_MOD_RQST, "CHANNEL MODIFY REQUEST" },
{ BSS_MAP_MSG_HANDOVER_RQST, "HANDOVER REQ" },
{ BSS_MAP_MSG_HANDOVER_REQUIRED, "HANDOVER REQUIRED" },
@@ -332,6 +450,10 @@ static const struct value_string gsm0808_msgt_names[] = {
{ BSS_MAP_MSG_HANDOVER_CANDIDATE_RESPONSE, "HANDOVER CAND RESP" },
{ BSS_MAP_MSG_HANDOVER_REQUIRED_REJECT, "HANDOVER REQ REJ" },
{ BSS_MAP_MSG_HANDOVER_DETECT, "HANDOVER DETECT" },
+ { BSS_MAP_MSG_INT_HANDOVER_REQUIRED, "INT HANDOVER REQ" },
+ { BSS_MAP_MSG_INT_HANDOVER_REQUIRED_REJ,"INT HANDOVER REQ REJ" },
+ { BSS_MAP_MSG_INT_HANDOVER_CMD, "INT HANDOVER CMD" },
+ { BSS_MAP_MSG_INT_HANDOVER_ENQUIRY, "INT HANDOVER ENQ" },
{ BSS_MAP_MSG_CLEAR_CMD, "CLEAR COMMAND" },
{ BSS_MAP_MSG_CLEAR_COMPLETE, "CLEAR COMPLETE" },
@@ -347,6 +469,8 @@ static const struct value_string gsm0808_msgt_names[] = {
{ BSS_MAP_MSG_PERFORM_LOCATION_RESPONSE, "PERFORM LOC RESP" },
{ BSS_MAP_MSG_PERFORM_LOCATION_ABORT, "PERFORM LOC ABORT" },
{ BSS_MAP_MSG_COMMON_ID, "COMMON ID" },
+ { BSS_MAP_MSG_REROUTE_CMD, "REROUTE COMMAND" },
+ { BSS_MAP_MSG_REROUTE_COMPLETE, "REROUTE COMPLETE" },
{ BSS_MAP_MSG_RESET, "RESET" },
{ BSS_MAP_MSG_RESET_ACKNOWLEDGE, "RESET ACK" },
@@ -356,6 +480,8 @@ static const struct value_string gsm0808_msgt_names[] = {
{ BSS_MAP_MSG_MSC_INVOKE_TRACE, "MSC INVOKE TRACE" },
{ BSS_MAP_MSG_BSS_INVOKE_TRACE, "BSS INVOKE TRACE" },
{ BSS_MAP_MSG_CONNECTIONLESS_INFORMATION, "CONNLESS INFO" },
+ { BSS_MAP_MSG_RESET_IP_RSRC, "RESET IP RESOURCE" },
+ { BSS_MAP_MSG_RESET_IP_RSRC_ACK, "RESET IP RESOURCE ACK" },
{ BSS_MAP_MSG_BLOCK, "BLOCK" },
{ BSS_MAP_MSG_BLOCKING_ACKNOWLEDGE, "BLOCK ACK" },
@@ -381,7 +507,27 @@ static const struct value_string gsm0808_msgt_names[] = {
{ BSS_MAP_MSG_CIPHER_MODE_REJECT, "CIPHER MODE REJECT" },
{ BSS_MAP_MSG_LOAD_INDICATION, "LOAD IND" },
- /* FIXME: VGCS/VBS */
+ { BSS_MAP_MSG_VGCS_VBS_SETUP, "VGCS/VBS SETUP" },
+ { BSS_MAP_MSG_VGCS_VBS_SETUP_ACK, "VGCS/VBS SETUP ACK" },
+ { BSS_MAP_MSG_VGCS_VBS_SETUP_REFUSE, "VGCS/VBS SETUP REFUSE" },
+ { 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_QUEUING_INDICATION, "VGCS/VBS QUEUING IND" },
+ { 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" },
+ { BSS_MAP_MSG_UPLINK_RELEASE_INDICATION,"UPLINK REL IND" },
+ { BSS_MAP_MSG_UPLINK_REJECT_CMD, "UPLINK REJ CMD" },
+ { 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_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_NOTIFICATION, "LCLS-NOTIFICATION" },
{ 0, NULL }
};
diff --git a/src/shared/libosmocore/src/gsm/gsm48.c b/src/shared/libosmocore/src/gsm/gsm48.c
index ea05d450..b626f82b 100644
--- a/src/shared/libosmocore/src/gsm/gsm48.c
+++ b/src/shared/libosmocore/src/gsm/gsm48.c
@@ -25,16 +25,17 @@
#include <stdint.h>
#include <stdio.h>
#include <string.h>
-
+#include <stdbool.h>
#include <arpa/inet.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/gsm/gsm0502.h>
-
+#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/protocol/gsm_08_58.h>
+#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
const struct tlv_definition gsm48_att_tlvdef = {
.def = {
@@ -131,6 +132,7 @@ const struct tlv_definition gsm48_mm_att_tlvdef = {
[GSM48_IE_UTC] = { TLV_TYPE_TV },
[GSM48_IE_NET_TIME_TZ] = { TLV_TYPE_FIXED, 7 },
[GSM48_IE_LSA_IDENT] = { TLV_TYPE_TLV },
+ [GSM48_IE_NET_DST] = { TLV_TYPE_TLV },
[GSM48_IE_LOCATION_AREA] = { TLV_TYPE_FIXED, 5 },
[GSM48_IE_PRIORITY_LEV] = { TLV_TYPE_SINGLE_TV },
@@ -160,6 +162,11 @@ static const struct value_string rr_cause_names[] = {
{ 0, NULL },
};
+const char *rr_cause_name(uint8_t cause)
+{
+ return get_value_string(rr_cause_names, cause);
+}
+
/* FIXME: convert to value_string */
static const char *cc_state_names[32] = {
"NULL",
@@ -248,9 +255,150 @@ const char *gsm48_cc_msg_name(uint8_t msgtype)
return get_value_string(cc_msg_names, msgtype);
}
-const char *rr_cause_name(uint8_t cause)
+
+static const struct value_string rr_msg_names[] = {
+ /* Channel establishment messages */
+ { GSM48_MT_RR_INIT_REQ, "RR INITIALISATION REQUEST" },
+ { GSM48_MT_RR_ADD_ASS, "ADDITIONAL ASSIGNMENT" },
+ { GSM48_MT_RR_IMM_ASS, "IMMEDIATE ASSIGNMENT" },
+ { GSM48_MT_RR_IMM_ASS_EXT, "MMEDIATE ASSIGNMENT EXTENDED" },
+ { GSM48_MT_RR_IMM_ASS_REJ, "IMMEDIATE ASSIGNMENT REJECT" },
+ { GSM48_MT_RR_DTM_ASS_FAIL, "DTM ASSIGNMENT FAILURE" },
+ { GSM48_MT_RR_DTM_REJECT, "DTM REJECT" },
+ { GSM48_MT_RR_DTM_REQUEST, "DTM REQUEST" },
+ { GSM48_MT_RR_PACKET_ASS, "PACKET ASSIGNMENT" },
+
+ /* Ciphering messages */
+ { GSM48_MT_RR_CIPH_M_CMD, "CIPHERING MODE COMMAND" },
+ { GSM48_MT_RR_CIPH_M_COMPL, "CIPHERING MODE COMPLETE" },
+
+ /* Configuration change messages */
+ { GSM48_MT_RR_CFG_CHG_CMD, "CONFIGURATION CHANGE COMMAND" },
+ { GSM48_MT_RR_CFG_CHG_ACK, "CONFIGURATION CHANGE ACK" },
+ { GSM48_MT_RR_CFG_CHG_REJ, "CONFIGURATION CHANGE REJECT" },
+
+ /* Handover messages */
+ { GSM48_MT_RR_ASS_CMD, "ASSIGNMENT COMMAND" },
+ { GSM48_MT_RR_ASS_COMPL, "ASSIGNMENT COMPLETE" },
+ { GSM48_MT_RR_ASS_FAIL, "ASSIGNMENT FAILURE" },
+ { GSM48_MT_RR_HANDO_CMD, "HANDOVER COMMAND" },
+ { GSM48_MT_RR_HANDO_COMPL, "HANDOVER COMPLETE" },
+ { GSM48_MT_RR_HANDO_FAIL, "HANDOVER FAILURE" },
+ { GSM48_MT_RR_HANDO_INFO, "PHYSICAL INFORMATION" },
+ { GSM48_MT_RR_DTM_ASS_CMD, "DTM ASSIGNMENT COMMAND" },
+
+ { GSM48_MT_RR_CELL_CHG_ORDER, "RR-CELL CHANGE ORDER" },
+ { GSM48_MT_RR_PDCH_ASS_CMD, "PDCH ASSIGNMENT COMMAND" },
+
+ /* Channel release messages */
+ { GSM48_MT_RR_CHAN_REL, "CHANNEL RELEASE" },
+ { GSM48_MT_RR_PART_REL, "PARTIAL RELEASE" },
+ { GSM48_MT_RR_PART_REL_COMP, "PARTIAL RELEASE COMPLETE" },
+
+ /* Paging and Notification messages */
+ { GSM48_MT_RR_PAG_REQ_1, "PAGING REQUEST TYPE 1" },
+ { GSM48_MT_RR_PAG_REQ_2, "PAGING REQUEST TYPE 2" },
+ { GSM48_MT_RR_PAG_REQ_3, "PAGING REQUEST TYPE 3" },
+ { GSM48_MT_RR_PAG_RESP, "PAGING RESPONSE" },
+ { GSM48_MT_RR_NOTIF_NCH, "NOTIFICATION/NCH" },
+ { GSM48_MT_RR_NOTIF_FACCH, "(Reserved)" },
+ { GSM48_MT_RR_NOTIF_RESP, "NOTIFICATION/RESPONSE" },
+ { GSM48_MT_RR_PACKET_NOTIF, "PACKET NOTIFICATION" },
+ /* 3G Specific messages */
+ { GSM48_MT_RR_UTRAN_CLSM_CHG, "UTRAN Classmark Change" },
+ { GSM48_MT_RR_CDMA2K_CLSM_CHG, "cdma 2000 Classmark Change" },
+ { GSM48_MT_RR_IS_TO_UTRAN_HANDO, "Inter System to UTRAN Handover Command" },
+ { GSM48_MT_RR_IS_TO_CDMA2K_HANDO, "Inter System to cdma2000 Handover Command" },
+
+ /* System information messages */
+ { GSM48_MT_RR_SYSINFO_8, "SYSTEM INFORMATION TYPE 8" },
+ { GSM48_MT_RR_SYSINFO_1, "SYSTEM INFORMATION TYPE 1" },
+ { GSM48_MT_RR_SYSINFO_2, "SYSTEM INFORMATION TYPE 2" },
+ { GSM48_MT_RR_SYSINFO_3, "SYSTEM INFORMATION TYPE 3" },
+ { GSM48_MT_RR_SYSINFO_4, "SYSTEM INFORMATION TYPE 4" },
+ { GSM48_MT_RR_SYSINFO_5, "SYSTEM INFORMATION TYPE 5" },
+ { GSM48_MT_RR_SYSINFO_6, "SYSTEM INFORMATION TYPE 6" },
+ { GSM48_MT_RR_SYSINFO_7, "SYSTEM INFORMATION TYPE 7" },
+ { GSM48_MT_RR_SYSINFO_2bis, "SYSTEM INFORMATION TYPE 2bis" },
+ { GSM48_MT_RR_SYSINFO_2ter, "SYSTEM INFORMATION TYPE 2ter" },
+ { GSM48_MT_RR_SYSINFO_2quater, "SYSTEM INFORMATION TYPE 2quater" },
+ { GSM48_MT_RR_SYSINFO_5bis, "SYSTEM INFORMATION TYPE 5bis" },
+ { GSM48_MT_RR_SYSINFO_5ter, "SYSTEM INFORMATION TYPE 5ter" },
+ { GSM48_MT_RR_SYSINFO_9, "SYSTEM INFORMATION TYPE 9" },
+ { GSM48_MT_RR_SYSINFO_13, "SYSTEM INFORMATION TYPE 13" },
+ { GSM48_MT_RR_SYSINFO_16, "SYSTEM INFORMATION TYPE 16" },
+ { GSM48_MT_RR_SYSINFO_17, "SYSTEM INFORMATION TYPE 17" },
+ { GSM48_MT_RR_SYSINFO_18, "SYSTEM INFORMATION TYPE 18" },
+ { GSM48_MT_RR_SYSINFO_19, "SYSTEM INFORMATION TYPE 19" },
+ { GSM48_MT_RR_SYSINFO_20, "SYSTEM INFORMATION TYPE 20" },
+
+ /* Miscellaneous messages */
+ { GSM48_MT_RR_CHAN_MODE_MODIF, "CHANNEL MODE MODIFY" },
+ { GSM48_MT_RR_STATUS, "RR STATUS" },
+ { GSM48_MT_RR_CHAN_MODE_MODIF_ACK, "CHANNEL MODE MODIFY ACKNOWLEDGE" },
+ { GSM48_MT_RR_FREQ_REDEF, "FREQUENCY REDEFINITION" },
+ { GSM48_MT_RR_MEAS_REP, "MEASUREMENT REPORT" },
+ { GSM48_MT_RR_CLSM_CHG, "CLASSMARK CHANGE" },
+ { GSM48_MT_RR_CLSM_ENQ, "CLASSMARK ENQUIRY" },
+ { GSM48_MT_RR_EXT_MEAS_REP, "EXTENDED MEASUREMENT REPORT" },
+ { GSM48_MT_RR_EXT_MEAS_REP_ORD, "EXTENDED MEASUREMENT ORDER" },
+ { GSM48_MT_RR_GPRS_SUSP_REQ, "GPRS SUSPENSION REQUEST" },
+ { GSM48_MT_RR_DTM_INFO, "DTM INFORMATION" },
+
+ /* VGCS uplink control messages */
+ { GSM48_MT_RR_VGCS_UPL_GRANT, "VGCS UPLINK GRANT" },
+ { GSM48_MT_RR_UPLINK_RELEASE, "UPLINK RELEASE" },
+ { GSM48_MT_RR_UPLINK_FREE, "0c" },
+ { GSM48_MT_RR_UPLINK_BUSY, "UPLINK BUSY" },
+ { GSM48_MT_RR_TALKER_IND, "TALKER INDICATION" },
+
+ /* Application messages */
+ { GSM48_MT_RR_APP_INFO, "Application Information" },
+ { 0, NULL }
+};
+
+const char *gsm48_rr_msg_name(uint8_t msgtype)
{
- return get_value_string(rr_cause_names, cause);
+ return get_value_string(rr_msg_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_DATA_14k5, "DATA_14k5" },
+ { GSM48_CMODE_DATA_12k0, "DATA_12k0" },
+ { GSM48_CMODE_DATA_6k0, "DATA_6k0" },
+ { GSM48_CMODE_DATA_3k6, "DATA_3k6" },
+ { 0, NULL },
+};
+
+const struct value_string gsm_chan_t_names[] = {
+ { GSM_LCHAN_NONE, "NONE" },
+ { GSM_LCHAN_SDCCH, "SDCCH" },
+ { GSM_LCHAN_TCH_F, "TCH_F" },
+ { GSM_LCHAN_TCH_H, "TCH_H" },
+ { GSM_LCHAN_UNKNOWN, "UNKNOWN" },
+ { GSM_LCHAN_CCCH, "CCCH" },
+ { GSM_LCHAN_PDTCH, "PDTCH" },
+ { GSM_LCHAN_CBCH, "CBCH" },
+ { 0, NULL },
+};
+
+static const struct value_string mi_type_names[] = {
+ { GSM_MI_TYPE_NONE, "NONE" },
+ { GSM_MI_TYPE_IMSI, "IMSI" },
+ { GSM_MI_TYPE_IMEI, "IMEI" },
+ { GSM_MI_TYPE_IMEISV, "IMEI-SV" },
+ { GSM_MI_TYPE_TMSI, "TMSI" },
+ { 0, NULL }
+};
+
+const char *gsm48_mi_type_name(uint8_t mi)
+{
+ return get_value_string(mi_type_names, mi);
}
static void to_bcd(uint8_t *bcd, uint16_t val)
@@ -263,57 +411,138 @@ static void to_bcd(uint8_t *bcd, uint16_t val)
val = val / 10;
}
-void gsm48_generate_lai(struct gsm48_loc_area_id *lai48, uint16_t mcc,
- uint16_t mnc, uint16_t lac)
+/*! \brief Checks is particular message is cipherable in A/Gb mode according to
+ * 3GPP TS 24.008 § 4.7.1.2
+ * \param[in] hdr Message header
+ * \return true if message can be encrypted, false otherwise
+ */
+bool gsm48_hdr_gmm_cipherable(const struct gsm48_hdr *hdr)
+{
+ switch(hdr->msg_type) {
+ case GSM48_MT_GMM_ATTACH_REQ:
+ case GSM48_MT_GMM_ATTACH_REJ:
+ case GSM48_MT_GMM_AUTH_CIPH_REQ:
+ case GSM48_MT_GMM_AUTH_CIPH_RESP:
+ case GSM48_MT_GMM_AUTH_CIPH_REJ:
+ case GSM48_MT_GMM_AUTH_CIPH_FAIL:
+ case GSM48_MT_GMM_ID_REQ:
+ case GSM48_MT_GMM_ID_RESP:
+ case GSM48_MT_GMM_RA_UPD_REQ:
+ case GSM48_MT_GMM_RA_UPD_REJ:
+ return false;
+ default:
+ return true;
+ }
+}
+
+/* Convert given mcc and mnc to BCD and write to *bcd_dst, which must be an
+ * allocated buffer of (at least) 3 bytes length. */
+void gsm48_mcc_mnc_to_bcd(uint8_t *bcd_dst, uint16_t mcc, uint16_t mnc)
{
uint8_t bcd[3];
to_bcd(bcd, mcc);
- lai48->digits[0] = bcd[0] | (bcd[1] << 4);
- lai48->digits[1] = bcd[2];
+ bcd_dst[0] = bcd[0] | (bcd[1] << 4);
+ bcd_dst[1] = bcd[2];
to_bcd(bcd, mnc);
/* FIXME: do we need three-digit MNC? See Table 10.5.3 */
if (mnc > 99) {
- lai48->digits[1] |= bcd[2] << 4;
- lai48->digits[2] = bcd[0] | (bcd[1] << 4);
+ bcd_dst[1] |= bcd[2] << 4;
+ bcd_dst[2] = bcd[0] | (bcd[1] << 4);
} else {
- lai48->digits[1] |= 0xf << 4;
- lai48->digits[2] = bcd[1] | (bcd[2] << 4);
+ bcd_dst[1] |= 0xf << 4;
+ bcd_dst[2] = bcd[1] | (bcd[2] << 4);
}
+}
+/* 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. */
+void gsm48_mcc_mnc_from_bcd(uint8_t *bcd_src, uint16_t *mcc, uint16_t *mnc)
+{
+ *mcc = (bcd_src[0] & 0x0f) * 100
+ + (bcd_src[0] >> 4) * 10
+ + (bcd_src[1] & 0x0f);
+
+ if ((bcd_src[1] & 0xf0) == 0xf0) {
+ *mnc = (bcd_src[2] & 0x0f) * 10
+ + (bcd_src[2] >> 4);
+ } else {
+ *mnc = (bcd_src[2] & 0x0f) * 100
+ + (bcd_src[2] >> 4) * 10
+ + (bcd_src[1] >> 4);
+ }
+}
+
+void gsm48_generate_lai(struct gsm48_loc_area_id *lai48, uint16_t mcc,
+ uint16_t mnc, uint16_t lac)
+{
+ gsm48_mcc_mnc_to_bcd(&lai48->digits[0], mcc, mnc);
lai48->lac = htons(lac);
}
-/* Attention: this function retunrs true integers, not hex! */
+/* Attention: this function returns true integers, not hex! */
int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *mcc,
uint16_t *mnc, uint16_t *lac)
{
- *mcc = (lai->digits[0] & 0x0f) * 100
- + (lai->digits[0] >> 4) * 10
- + (lai->digits[1] & 0x0f);
+ gsm48_mcc_mnc_from_bcd(&lai->digits[0], mcc, mnc);
+ *lac = ntohs(lai->lac);
+ return 0;
+}
- if ((lai->digits[1] & 0xf0) == 0xf0) {
- *mnc = (lai->digits[2] & 0x0f) * 10
- + (lai->digits[2] >> 4);
+/*! \brief Set DTX mode in Cell Options IE (3GPP TS 44.018)
+ * \param[in] op Cell Options structure in which DTX parameters will be set
+ * \param[in] full Mode for full-rate channels
+ * \param[in] half Mode for half-rate channels
+ * \param[in] is_bcch Indicates if we should use 10.5.2.3.1 instead of
+ * 10.5.2.3a.2
+ *
+ * There is no space for separate DTX settings for Full and Half rate channels
+ * in BCCH - in this case full setting is used for both and half parameter is
+ * ignored.
+ */
+void gsm48_set_dtx(struct gsm48_cell_options *op, enum gsm48_dtx_mode full,
+ enum gsm48_dtx_mode half, bool is_bcch)
+{
+ if (is_bcch) {
+ switch (full) {
+ case GSM48_DTX_MAY_BE_USED:
+ op->dtx = 0;
+ return;
+ case GSM48_DTX_SHALL_BE_USED:
+ op->dtx = 1;
+ return;
+ case GSM48_DTX_SHALL_NOT_BE_USED:
+ op->dtx = 2;
+ return;
+ }
} else {
- *mnc = (lai->digits[2] & 0x0f) * 100
- + (lai->digits[2] >> 4) * 10
- + (lai->digits[1] >> 4);
+ switch (full) {
+ case GSM48_DTX_MAY_BE_USED:
+ op->dtx = (half == GSM48_DTX_SHALL_BE_USED) ? 3 : 0;
+ op->d = (half == GSM48_DTX_SHALL_NOT_BE_USED) ? 0 : 1;
+ return;
+ case GSM48_DTX_SHALL_BE_USED:
+ op->dtx = (half == GSM48_DTX_MAY_BE_USED) ? 3 : 1;
+ op->d = (half == GSM48_DTX_SHALL_BE_USED) ? 1 : 0;
+ return;
+ case GSM48_DTX_SHALL_NOT_BE_USED:
+ op->dtx = 2;
+ op->d = (half == GSM48_DTX_SHALL_BE_USED) ? 1 : 0;
+ return;
+ }
}
- *lac = ntohs(lai->lac);
-
- return 0;
}
int gsm48_generate_mid_from_tmsi(uint8_t *buf, uint32_t tmsi)
{
- uint32_t *tptr = (uint32_t *) &buf[3];
+ uint32_t tmsi_be = htonl(tmsi);
buf[0] = GSM48_IE_MOBILE_ID;
buf[1] = GSM48_TMSI_LEN;
buf[2] = 0xf0 | GSM_MI_TYPE_TMSI;
- *tptr = htonl(tmsi);
+ memcpy(&buf[3], &tmsi_be, sizeof(tmsi_be));
return 7;
}
diff --git a/src/shared/libosmocore/src/gsm/gsm48_ie.c b/src/shared/libosmocore/src/gsm/gsm48_ie.c
index 78619b97..2cc0645d 100644
--- a/src/shared/libosmocore/src/gsm/gsm48_ie.c
+++ b/src/shared/libosmocore/src/gsm/gsm48_ie.c
@@ -985,17 +985,17 @@ int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd,
if (len >= 12)
w[13] = r->w13;
if (len >= 13)
- w[14] = r->w15;
+ w[14] = (r->w14_hi << 2) | r->w14_lo;
if (len >= 13)
- w[15] = (r->w14_hi << 2) | r->w14_lo;
+ w[15] = r->w15;
if (len >= 14)
w[16] = (r->w16_hi << 3) | r->w16_lo;
if (len >= 14)
w[17] = r->w17;
if (len >= 15)
- w[18] = r->w19;
+ w[18] = (r->w18_hi << 3) | r->w18_lo;
if (len >= 15)
- w[19] = (r->w18_hi << 3) | r->w18_lo;
+ w[19] = r->w19;
if (len >= 16)
w[20] = (r->w20_hi << 3) | r->w20_lo;
if (len >= 16)
diff --git a/src/shared/libosmocore/src/gsm/gsm_04_08_gprs.c b/src/shared/libosmocore/src/gsm/gsm_04_08_gprs.c
new file mode 100644
index 00000000..6f0baba9
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/gsm_04_08_gprs.c
@@ -0,0 +1,212 @@
+/* (C) 2009-2016 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by On-Waves
+ * (C) 2014-2015 by Sysmocom s.f.m.c. GmbH
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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 <osmocom/gsm/protocol/gsm_04_08_gprs.h>
+#include <osmocom/crypt/gprs_cipher.h>
+#include <osmocom/core/utils.h>
+
+#include <stdbool.h>
+
+/* Protocol related stuff, should go into libosmocore */
+
+/* 10.5.5.14 GPRS MM Cause / Table 10.5.147 */
+const struct value_string gsm48_gmm_cause_names_[] = {
+ { GMM_CAUSE_IMSI_UNKNOWN, "IMSI unknown in HLR" },
+ { GMM_CAUSE_ILLEGAL_MS, "Illegal MS" },
+ { GMM_CAUSE_IMEI_NOT_ACCEPTED, "IMEI not accepted" },
+ { GMM_CAUSE_ILLEGAL_ME, "Illegal ME" },
+ { GMM_CAUSE_GPRS_NOTALLOWED, "GPRS services not allowed" },
+ { GMM_CAUSE_GPRS_OTHER_NOTALLOWED,
+ "GPRS services and non-GPRS services not allowed" },
+ { GMM_CAUSE_MS_ID_NOT_DERIVED,
+ "MS identity cannot be derived by the network" },
+ { GMM_CAUSE_IMPL_DETACHED, "Implicitly detached" },
+ { GMM_CAUSE_PLMN_NOTALLOWED, "PLMN not allowed" },
+ { GMM_CAUSE_LA_NOTALLOWED, "Location Area not allowed" },
+ { GMM_CAUSE_ROAMING_NOTALLOWED,
+ "Roaming not allowed in this location area" },
+ { GMM_CAUSE_NO_GPRS_PLMN,
+ "GPRS services not allowed in this PLMN" },
+ { GMM_CAUSE_NO_SUIT_CELL_IN_LA, "No suitable cell in LA" },
+ { GMM_CAUSE_MSC_TEMP_NOTREACH, "MSC temporarily not reachable" },
+ { GMM_CAUSE_NET_FAIL, "Network failure" },
+ { GMM_CAUSE_MAC_FAIL, "MAC failure" },
+ { GMM_CAUSE_SYNC_FAIL, "SYNC failure" },
+ { GMM_CAUSE_CONGESTION, "Congestion" },
+ { GMM_CAUSE_GSM_AUTH_UNACCEPT, "GSM authentication unacceptable" },
+ { GMM_CAUSE_NOT_AUTH_FOR_CSG, "Not authorized for this CSG" },
+ { GMM_CAUSE_SMS_VIA_GPRS_IN_RA, "SMS provided via GPRS in this RA" },
+ { GMM_CAUSE_NO_PDP_ACTIVATED, "No PDP context activated" },
+ { GMM_CAUSE_SEM_INCORR_MSG, "Semantically incorrect message" },
+ { GMM_CAUSE_INV_MAND_INFO, "Invalid mandatory information" },
+ { GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL,
+ "Message type non-existant or not implemented" },
+ { GMM_CAUSE_MSGT_INCOMP_P_STATE,
+ "Message type not compatible with protocol state" },
+ { GMM_CAUSE_IE_NOTEXIST_NOTIMPL,
+ "Information element non-existent or not implemented" },
+ { GMM_CAUSE_COND_IE_ERR, "Conditional IE error" },
+ { GMM_CAUSE_MSG_INCOMP_P_STATE,
+ "Message not compatible with protocol state " },
+ { GMM_CAUSE_PROTO_ERR_UNSPEC, "Protocol error, unspecified" },
+ { 0, NULL }
+};
+
+const struct value_string *gsm48_gmm_cause_names = gsm48_gmm_cause_names_;
+
+/* 10.5.6.6 SM Cause / Table 10.5.157 */
+const struct value_string gsm48_gsm_cause_names_[] = {
+ { GSM_CAUSE_INSUFF_RSRC, "Insufficient resources" },
+ { GSM_CAUSE_MISSING_APN, "Missing or unknown APN" },
+ { GSM_CAUSE_UNKNOWN_PDP, "Unknown PDP address or PDP type" },
+ { GSM_CAUSE_AUTH_FAILED, "User Authentication failed" },
+ { GSM_CAUSE_ACT_REJ_GGSN, "Activation rejected by GGSN" },
+ { GSM_CAUSE_ACT_REJ_UNSPEC, "Activation rejected, unspecified" },
+ { GSM_CAUSE_SERV_OPT_NOTSUPP, "Service option not supported" },
+ { GSM_CAUSE_REQ_SERV_OPT_NOTSUB,
+ "Requested service option not subscribed" },
+ { GSM_CAUSE_SERV_OPT_TEMP_OOO,
+ "Service option temporarily out of order" },
+ { GSM_CAUSE_NSAPI_IN_USE, "NSAPI already used" },
+ { GSM_CAUSE_DEACT_REGULAR, "Regular deactivation" },
+ { GSM_CAUSE_QOS_NOT_ACCEPTED, "QoS not accepted" },
+ { GSM_CAUSE_NET_FAIL, "Network Failure" },
+ { GSM_CAUSE_REACT_RQD, "Reactivation required" },
+ { GSM_CAUSE_FEATURE_NOTSUPP, "Feature not supported " },
+ { GSM_CAUSE_INVALID_TRANS_ID, "Invalid transaction identifier" },
+ { GSM_CAUSE_SEM_INCORR_MSG, "Semantically incorrect message" },
+ { GSM_CAUSE_INV_MAND_INFO, "Invalid mandatory information" },
+ { GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL,
+ "Message type non-existant or not implemented" },
+ { GSM_CAUSE_MSGT_INCOMP_P_STATE,
+ "Message type not compatible with protocol state" },
+ { GSM_CAUSE_IE_NOTEXIST_NOTIMPL,
+ "Information element non-existent or not implemented" },
+ { GSM_CAUSE_COND_IE_ERR, "Conditional IE error" },
+ { GSM_CAUSE_MSG_INCOMP_P_STATE,
+ "Message not compatible with protocol state " },
+ { GSM_CAUSE_PROTO_ERR_UNSPEC, "Protocol error, unspecified" },
+ { 0, NULL }
+};
+
+const struct value_string *gsm48_gsm_cause_names = gsm48_gsm_cause_names_;
+
+/*! \brief Check if MS supports particular version of GEA by inspecting
+ * MS network capability IE specified in 3GPP TS 24.008
+ * \param[in] ms_net_cap Buffer with raw MS network capability IE value,
+ * 3 - 10 bytes
+ * \param[in] cap_len Length of ms_net_cap, in bytes
+ * \param[in] gea Version of GEA to check
+ * \returns true if given version is supported by MS, false otherwise
+ */
+bool gprs_ms_net_cap_gea_supported(const uint8_t *ms_net_cap, uint8_t cap_len,
+ enum gprs_ciph_algo gea)
+{
+ switch (gea) {
+ case GPRS_ALGO_GEA0:
+ return true;
+ case GPRS_ALGO_GEA1: /* 1st bit is GEA1: */
+ return 0x80 & ms_net_cap[0];
+ case GPRS_ALGO_GEA2: /* extended GEA bits start from 2nd bit */
+ return 0x40 & ms_net_cap[1]; /* of the next byte */
+ case GPRS_ALGO_GEA3:
+ return 0x20 & ms_net_cap[1];
+ case GPRS_ALGO_GEA4:
+ return 0x10 & ms_net_cap[1];
+ default:
+ return false;
+ }
+}
+
+const struct value_string gprs_msgt_gmm_names[] = {
+ { GSM48_MT_GMM_ATTACH_REQ, "ATTACH REQUEST" },
+ { GSM48_MT_GMM_ATTACH_ACK, "ATTACH ACK" },
+ { GSM48_MT_GMM_ATTACH_COMPL, "ATTACH COMPLETE" },
+ { GSM48_MT_GMM_ATTACH_REJ, "ATTACH REJECT" },
+ { GSM48_MT_GMM_DETACH_REQ, "DETACH REQUEST" },
+ { GSM48_MT_GMM_DETACH_ACK, "DETACH ACK" },
+ { GSM48_MT_GMM_RA_UPD_REQ, "RA UPDATE REQUEST" },
+ { GSM48_MT_GMM_RA_UPD_ACK, "RA UPDATE ACK" },
+ { GSM48_MT_GMM_RA_UPD_COMPL, "RA UPDATE COMPLETE" },
+ { GSM48_MT_GMM_RA_UPD_REJ, "RA UPDATE REJECT" },
+ { GSM48_MT_GMM_PTMSI_REALL_CMD, "PTMSI REALLOC CMD" },
+ { GSM48_MT_GMM_PTMSI_REALL_COMPL, "PTMSI REALLOC COMPLETE" },
+ { GSM48_MT_GMM_AUTH_CIPH_REQ, "AUTH & CIPHER REQUEST" },
+ { GSM48_MT_GMM_AUTH_CIPH_RESP, "AUTH & CIPHER RESPONSE" },
+ { GSM48_MT_GMM_AUTH_CIPH_REJ, "AUTH & CIPHER REJECT" },
+ { GSM48_MT_GMM_AUTH_CIPH_FAIL, "AUTH & CIPHER FAILURE" },
+ { GSM48_MT_GMM_ID_REQ, "IDENTITY REQUEST" },
+ { GSM48_MT_GMM_ID_RESP, "IDENTITY RESPONSE" },
+ { GSM48_MT_GMM_STATUS, "STATUS" },
+ { GSM48_MT_GMM_INFO, "INFO" },
+ { 0, NULL }
+};
+
+/* 10.5.5.2 */
+const struct value_string gprs_att_t_strs_[] = {
+ { GPRS_ATT_T_ATTACH, "GPRS attach" },
+ { GPRS_ATT_T_ATT_WHILE_IMSI, "GPRS attach while IMSI attached" },
+ { GPRS_ATT_T_COMBINED, "Combined GPRS/IMSI attach" },
+ { 0, NULL }
+};
+
+const struct value_string *gprs_att_t_strs = gprs_att_t_strs_;
+
+const struct value_string gprs_upd_t_strs_[] = {
+ { GPRS_UPD_T_RA, "RA updating" },
+ { GPRS_UPD_T_RA_LA, "combined RA/LA updating" },
+ { GPRS_UPD_T_RA_LA_IMSI_ATT, "combined RA/LA updating + IMSI attach" },
+ { GPRS_UPD_T_PERIODIC, "periodic updating" },
+ { 0, NULL }
+};
+
+const struct value_string *gprs_upd_t_strs = gprs_upd_t_strs_;
+
+/* 10.5.5.5 */
+const struct value_string gprs_det_t_mo_strs_[] = {
+ { GPRS_DET_T_MO_GPRS, "GPRS detach" },
+ { GPRS_DET_T_MO_IMSI, "IMSI detach" },
+ { GPRS_DET_T_MO_COMBINED, "Combined GPRS/IMSI detach" },
+ { 0, NULL }
+};
+
+const struct value_string *gprs_det_t_mo_strs = gprs_det_t_mo_strs_;
+
+const struct value_string gprs_det_t_mt_strs_[] = {
+ { GPRS_DET_T_MT_REATT_REQ, "re-attach required" },
+ { GPRS_DET_T_MT_REATT_NOTREQ, "re-attach not required" },
+ { GPRS_DET_T_MT_IMSI, "IMSI detach (after VLR failure)" },
+ { 0, NULL }
+};
+
+const struct value_string *gprs_det_t_mt_strs = gprs_det_t_mt_strs_;
+
+const struct value_string gprs_service_t_strs_[] = {
+ { GPRS_SERVICE_T_SIGNALLING, "signalling" },
+ { GPRS_SERVICE_T_DATA, "data" },
+ { GPRS_SERVICE_T_PAGING_RESP, "paging response" },
+ { GPRS_SERVICE_T_MBMS_MC_SERV, "MBMS multicast service" },
+ { GPRS_SERVICE_T_MBMS_BC_SERV, "MBMS broadcast service" },
+ { 0, NULL }
+};
+
+const struct value_string *gprs_service_t_strs = gprs_service_t_strs_;
diff --git a/src/shared/libosmocore/src/gsm/gsm_utils.c b/src/shared/libosmocore/src/gsm/gsm_utils.c
index 8b1fae08..7365ab7c 100644
--- a/src/shared/libosmocore/src/gsm/gsm_utils.c
+++ b/src/shared/libosmocore/src/gsm/gsm_utils.c
@@ -1,6 +1,6 @@
/*
* (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
- * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009,2013 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2010-2012 by Nico Golde <nico@ngolde.de>
*
@@ -28,7 +28,7 @@
* This library is a collection of common code used in various
* GSM related sub-projects inside the Osmocom family of projects. It
* includes A5/1 and A5/2 ciphers, COMP128v1, a LAPDm implementation,
- * a GSM TLV parser, SMS utility routines as well as
+ * a GSM TLV parser, SMS utility routines as well as
* protocol definitions for a series of protocols:
* * Um L2 (04.06)
* * Um L3 (04.08)
@@ -64,11 +64,15 @@
//#include <openbsc/gsm_data.h>
#include <osmocom/core/utils.h>
+#include <osmocom/core/bitvec.h>
#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/meas_rep.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
+#include <stdbool.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
@@ -80,8 +84,12 @@
* left out as they can't be handled with a char and
* since most phones don't display or write these
* characters this would only needlessly make the code
- * more complex
-*/
+ * more complex.
+ *
+ * Note that this table contains the latin1->7bit mapping _and_ has
+ * been merged with the reverse mapping (7bit->latin1) for the
+ * extended characters at offset 0x7f.
+ */
static unsigned char gsm_7bit_alphabet[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0a, 0xff, 0xff, 0x0d, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
@@ -123,12 +131,15 @@ uint8_t gsm_get_octet_len(const uint8_t sept_len){
}
/* GSM 03.38 6.2.1 Character unpacking */
-int gsm_7bit_decode_hdr(char *text, const uint8_t *user_data, uint8_t septet_l, uint8_t ud_hdr_ind)
+int gsm_7bit_decode_n_hdr(char *text, size_t n, const uint8_t *user_data, uint8_t septet_l, uint8_t ud_hdr_ind)
{
- int i = 0;
- int shift = 0;
- uint8_t c;
- uint8_t next_is_ext = 0;
+ unsigned shift = 0;
+ uint8_t c7, c8, next_is_ext = 0, lu, ru;
+ const uint8_t maxlen = gsm_get_octet_len(septet_l);
+ const char *text_buf_begin = text;
+ const char *text_buf_end = text + n;
+
+ OSMO_ASSERT (n > 0);
/* skip the user data header */
if (ud_hdr_ind) {
@@ -139,37 +150,59 @@ int gsm_7bit_decode_hdr(char *text, const uint8_t *user_data, uint8_t septet_l,
septet_l = septet_l - shift;
}
- for (i = 0; i < septet_l; i++) {
- c =
- ((user_data[((i + shift) * 7 + 7) >> 3] <<
- (7 - (((i + shift) * 7 + 7) & 7))) |
- (user_data[((i + shift) * 7) >> 3] >>
- (((i + shift) * 7) & 7))) & 0x7f;
+ unsigned i, l, r;
+ for (i = 0; i < septet_l && text != text_buf_end - 1; i++) {
+
+ l = ((i + shift) * 7 + 7) >> 3;
+ r = ((i + shift) * 7) >> 3;
+
+ /* the left side index is always >= right side index
+ sometimes it even gets beyond array boundary
+ check for that explicitly and force 0 instead
+ */
+ if (l >= maxlen)
+ lu = 0;
+ else
+ lu = user_data[l] << (7 - (((i + shift) * 7 + 7) & 7));
+
+ ru = user_data[r] >> (((i + shift) * 7) & 7);
+
+ c7 = (lu | ru) & 0x7f;
- /* this is an extension character */
if (next_is_ext) {
+ /* this is an extension character */
next_is_ext = 0;
- *(text++) = gsm_7bit_alphabet[0x7f + c];
- continue;
- }
-
- if (c == 0x1b && i + 1 < septet_l) {
+ c8 = gsm_7bit_alphabet[0x7f + c7];
+ } else if (c7 == 0x1b && i + 1 < septet_l) {
next_is_ext = 1;
+ continue;
} else {
- *(text++) = gsm_septet_lookup(c);
+ c8 = gsm_septet_lookup(c7);
}
+
+ *(text++) = c8;
}
- if (ud_hdr_ind)
- i += shift;
*text = '\0';
- return i;
+ return text - text_buf_begin;
}
-int gsm_7bit_decode(char *text, const uint8_t *user_data, uint8_t septet_l)
+int gsm_7bit_decode_n(char *text, size_t n, const uint8_t *user_data, uint8_t septet_l)
{
- return gsm_7bit_decode_hdr(text, user_data, septet_l, 0);
+ return gsm_7bit_decode_n_hdr(text, n, user_data, septet_l, 0);
+}
+
+int gsm_7bit_decode_n_ussd(char *text, size_t n, const uint8_t *user_data, uint8_t length)
+{
+ int nchars;
+
+ nchars = gsm_7bit_decode_n_hdr(text, n, user_data, length, 0);
+ /* remove last <CR>, if it fits up to the end of last octet */
+ if (nchars && (user_data[gsm_get_octet_len(length) - 1] >> 1) == '\r')
+ text[--nchars] = '\0';
+
+ return nchars;
}
/* GSM 03.38 6.2.1 Prepare character packing */
@@ -202,7 +235,8 @@ int gsm_septet_encode(uint8_t *result, const char *data)
}
/* 7bit to octet packing */
-int gsm_septets2octets(uint8_t *result, uint8_t *rdata, uint8_t septet_len, uint8_t padding){
+int gsm_septets2octets(uint8_t *result, const uint8_t *rdata, uint8_t septet_len, uint8_t padding)
+{
int i = 0, z = 0;
uint8_t cb, nb;
int shift = 0;
@@ -247,14 +281,28 @@ int gsm_septets2octets(uint8_t *result, uint8_t *rdata, uint8_t septet_len, uint
}
/* GSM 03.38 6.2.1 Character packing */
-int gsm_7bit_encode(uint8_t *result, const char *data)
+int gsm_7bit_encode_n(uint8_t *result, size_t n, const char *data, int *octets)
{
int y = 0;
+ int o;
+ 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));
y = gsm_septet_encode(rdata, data);
- gsm_septets2octets(result, rdata, y, 0);
+
+ if (y > max_septets) {
+ /*
+ * Limit the number of septets to avoid the generation
+ * of more than n octets.
+ */
+ y = max_septets;
+ }
+
+ o = gsm_septets2octets(result, rdata, y, 0);
+
+ if (octets)
+ *octets = o;
free(rdata);
@@ -271,6 +319,42 @@ int gsm_7bit_encode(uint8_t *result, const char *data)
return y;
}
+int gsm_7bit_encode_n_ussd(uint8_t *result, size_t n, const char *data, int *octets)
+{
+ int y;
+
+ y = gsm_7bit_encode_n(result, n, data, octets);
+ /* if last octet contains only one bit, add <CR> */
+ if (((y * 7) & 7) == 1)
+ result[(*octets) - 1] |= ('\r' << 1);
+ /* if last character is <CR> and completely fills last octet, add
+ * another <CR>. */
+ if (y && ((y * 7) & 7) == 0 && (result[(*octets) - 1] >> 1) == '\r' && *octets < n - 1) {
+ result[(*octets)++] = '\r';
+ y++;
+ }
+
+ return y;
+}
+
+/*! \brief Build the RSL uplink measurement IE (3GPP TS 08.58 § 9.3.25)
+ * \param[in] mru Unidirectional measurement report structure
+ * \param[in] dtxd_used Indicates if DTXd was used during measurement report
+ * period
+ * \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,
+ uint8_t *buf)
+{
+ buf[0] = dtxd_used ? (1 << 6) : 0;
+ buf[0] |= (mru->full.rx_lev & 0x3f);
+ buf[1] = (mru->sub.rx_lev & 0x3f);
+ buf[2] = ((mru->full.rx_qual & 7) << 3) | (mru->sub.rx_qual & 7);
+
+ return 3;
+}
+
/* convert power class to dBm according to GSM TS 05.05 */
unsigned int ms_class_gmsk_dbm(enum gsm_band band, int class)
{
@@ -335,7 +419,7 @@ int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm)
case GSM_BAND_1800:
if (dbm >= 36)
return 29;
- else if (dbm >= 34)
+ else if (dbm >= 34)
return 30;
else if (dbm >= 32)
return 31;
@@ -400,7 +484,7 @@ int ms_pwr_dbm(enum gsm_band band, uint8_t lvl)
return -EINVAL;
}
-/* According to TS 08.05 Chapter 8.1.4 */
+/* According to TS 05.08 Chapter 8.1.4 */
int rxlev2dbm(uint8_t rxlev)
{
if (rxlev > 63)
@@ -409,7 +493,7 @@ int rxlev2dbm(uint8_t rxlev)
return -110 + rxlev;
}
-/* According to TS 08.05 Chapter 8.1.4 */
+/* According to TS 05.08 Chapter 8.1.4 */
uint8_t dbm2rxlev(int dbm)
{
int rxlev = dbm + 110;
@@ -503,59 +587,79 @@ enum gsm_band gsm_arfcn2band(uint16_t arfcn)
return GSM_BAND_1800;
}
+struct gsm_freq_range {
+ uint16_t arfcn_first;
+ uint16_t arfcn_last;
+ uint16_t freq_ul_first;
+ uint16_t freq_dl_offset;
+ uint16_t flags;
+};
+
+static struct gsm_freq_range gsm_ranges[] = {
+ { 512, 810, 18502, 800, ARFCN_PCS }, /* PCS 1900 */
+ { 0, 124, 8900, 450, 0 }, /* P-GSM + E-GSM ARFCN 0 */
+ { 955, 1023, 8762, 450, 0 }, /* E-GSM + R-GSM */
+ { 128, 251, 8242, 450, 0 }, /* GSM 850 */
+ { 512, 885, 17102, 950, 0 }, /* DCS 1800 */
+ { 259, 293, 4506, 100, 0 }, /* GSM 450 */
+ { 306, 340, 4790, 100, 0 }, /* GSM 480 */
+ { 350, 425, 8060, 450, 0 }, /* GSM 810 */
+ { 438, 511, 7472, 300, 0 }, /* GSM 750 */
+ { /* Guard */ }
+};
+
/* Convert an ARFCN to the frequency in MHz * 10 */
uint16_t gsm_arfcn2freq10(uint16_t arfcn, int uplink)
{
- uint16_t freq10_ul;
- uint16_t freq10_dl;
- int is_pcs = arfcn & ARFCN_PCS;
+ struct gsm_freq_range *r;
+ uint16_t flags = arfcn & ARFCN_FLAG_MASK;
+ uint16_t freq10_ul = 0xffff;
+ uint16_t freq10_dl = 0xffff;
arfcn &= ~ARFCN_FLAG_MASK;
- if (is_pcs) {
- /* DCS 1900 */
- arfcn &= ~ARFCN_PCS;
- freq10_ul = 18502 + 2 * (arfcn-512);
- freq10_dl = freq10_ul + 800;
- } else if (arfcn <= 124) {
- /* Primary GSM + ARFCN 0 of E-GSM */
- freq10_ul = 8900 + 2 * arfcn;
- freq10_dl = freq10_ul + 450;
- } else if (arfcn >= 955 && arfcn <= 1023) {
- /* E-GSM and R-GSM */
- freq10_ul = 8900 + 2 * (arfcn - 1024);
- freq10_dl = freq10_ul + 450;
- } else if (arfcn >= 128 && arfcn <= 251) {
- /* GSM 850 */
- freq10_ul = 8242 + 2 * (arfcn - 128);
- freq10_dl = freq10_ul + 450;
- } else if (arfcn >= 512 && arfcn <= 885) {
- /* DCS 1800 */
- freq10_ul = 17102 + 2 * (arfcn - 512);
- freq10_dl = freq10_ul + 950;
- } else if (arfcn >= 259 && arfcn <= 293) {
- /* GSM 450 */
- freq10_ul = 4506 + 2 * (arfcn - 259);
- freq10_dl = freq10_ul + 100;
- } else if (arfcn >= 306 && arfcn <= 340) {
- /* GSM 480 */
- freq10_ul = 4790 + 2 * (arfcn - 306);
- freq10_dl = freq10_ul + 100;
- } else if (arfcn >= 350 && arfcn <= 425) {
- /* GSM 810 */
- freq10_ul = 8060 + 2 * (arfcn - 350);
- freq10_dl = freq10_ul + 450;
- } else if (arfcn >= 438 && arfcn <= 511) {
- /* GSM 750 */
- freq10_ul = 7472 + 2 * (arfcn - 438);
- freq10_dl = freq10_ul + 300;
- } else
- return 0xffff;
+ for (r=gsm_ranges; r->freq_ul_first>0; r++) {
+ if ((flags == r->flags) &&
+ (arfcn >= r->arfcn_first) &&
+ (arfcn <= r->arfcn_last))
+ {
+ freq10_ul = r->freq_ul_first + 2 * (arfcn - r->arfcn_first);
+ freq10_dl = freq10_ul + r->freq_dl_offset;
+ break;
+ }
+ }
+
+ return uplink ? freq10_ul : freq10_dl;
+}
+
+/* Convert a Frequency in MHz * 10 to ARFCN */
+uint16_t gsm_freq102arfcn(uint16_t freq10, int uplink)
+{
+ struct gsm_freq_range *r;
+ uint16_t freq10_lo, freq10_hi;
+ uint16_t arfcn = 0xffff;
+
+ for (r=gsm_ranges; r->freq_ul_first>0; r++) {
+ /* Generate frequency limits */
+ freq10_lo = r->freq_ul_first;
+ freq10_hi = freq10_lo + 2 * (r->arfcn_last - r->arfcn_first);
+ if (!uplink) {
+ freq10_lo += r->freq_dl_offset;
+ freq10_hi += r->freq_dl_offset;
+ }
+
+ /* Check if this fits */
+ if (freq10 >= freq10_lo && freq10 <= freq10_hi) {
+ arfcn = r->arfcn_first + ((freq10 - freq10_lo) >> 1);
+ arfcn |= r->flags;
+ break;
+ }
+ }
if (uplink)
- return freq10_ul;
- else
- return freq10_dl;
+ arfcn |= ARFCN_UPLINK;
+
+ return arfcn;
}
void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn)
@@ -573,7 +677,41 @@ uint32_t gsm_gsmtime2fn(struct gsm_time *time)
return (51 * ((time->t3 - time->t2 + 26) % 26) + time->t3 + (26 * 51 * time->t1));
}
-/* TS 03.03 Chapter 2.6 */
+/*! \brief append range1024 encoded data to bit vector */
+void bitvec_add_range1024(struct bitvec *bv, const struct gsm48_range_1024 *r)
+{
+ bitvec_set_uint(bv, r->w1_hi, 2);
+ bitvec_set_uint(bv, r->w1_lo, 8);
+ bitvec_set_uint(bv, r->w2_hi, 8);
+ bitvec_set_uint(bv, r->w2_lo, 1);
+ bitvec_set_uint(bv, r->w3_hi, 7);
+ bitvec_set_uint(bv, r->w3_lo, 2);
+ bitvec_set_uint(bv, r->w4_hi, 6);
+ bitvec_set_uint(bv, r->w4_lo, 2);
+ bitvec_set_uint(bv, r->w5_hi, 6);
+ bitvec_set_uint(bv, r->w5_lo, 2);
+ bitvec_set_uint(bv, r->w6_hi, 6);
+ bitvec_set_uint(bv, r->w6_lo, 2);
+ bitvec_set_uint(bv, r->w7_hi, 6);
+ bitvec_set_uint(bv, r->w7_lo, 2);
+ bitvec_set_uint(bv, r->w8_hi, 6);
+ bitvec_set_uint(bv, r->w8_lo, 1);
+ bitvec_set_uint(bv, r->w9, 7);
+ bitvec_set_uint(bv, r->w10, 7);
+ bitvec_set_uint(bv, r->w11_hi, 1);
+ bitvec_set_uint(bv, r->w11_lo, 6);
+ bitvec_set_uint(bv, r->w12_hi, 2);
+ bitvec_set_uint(bv, r->w12_lo, 5);
+ bitvec_set_uint(bv, r->w13_hi, 3);
+ bitvec_set_uint(bv, r->w13_lo, 4);
+ bitvec_set_uint(bv, r->w14_hi, 4);
+ bitvec_set_uint(bv, r->w14_lo, 3);
+ bitvec_set_uint(bv, r->w15_hi, 5);
+ bitvec_set_uint(bv, r->w15_lo, 2);
+ bitvec_set_uint(bv, r->w16, 6);
+}
+
+/* TS 23.003 Chapter 2.6 */
int gprs_tlli_type(uint32_t tlli)
{
if ((tlli & 0xc0000000) == 0xc0000000)
@@ -584,6 +722,10 @@ int gprs_tlli_type(uint32_t tlli)
return TLLI_RANDOM;
else if ((tlli & 0xf8000000) == 0x70000000)
return TLLI_AUXILIARY;
+ else if ((tlli & 0xf0000000) == 0x00000000)
+ return TLLI_G_RNTI;
+ else if ((tlli & 0xf0000000) == 0x10000000)
+ return TLLI_RAND_G_RNTI;
return TLLI_RESERVED;
}
@@ -604,3 +746,39 @@ uint32_t gprs_tmsi2tlli(uint32_t p_tmsi, enum gprs_tlli_type type)
}
return tlli;
}
+
+/* Wrappers for deprecated functions: */
+
+int gsm_7bit_decode(char *text, const uint8_t *user_data, uint8_t septet_l)
+{
+ gsm_7bit_decode_n(text, GSM_7BIT_LEGACY_MAX_BUFFER_SIZE,
+ user_data, septet_l);
+
+ /* Mimic the original behaviour. */
+ return septet_l;
+}
+
+int gsm_7bit_decode_ussd(char *text, const uint8_t *user_data, uint8_t length)
+{
+ return gsm_7bit_decode_n_ussd(text, GSM_7BIT_LEGACY_MAX_BUFFER_SIZE,
+ user_data, length);
+}
+
+int gsm_7bit_encode(uint8_t *result, const char *data)
+{
+ int out;
+ return gsm_7bit_encode_n(result, GSM_7BIT_LEGACY_MAX_BUFFER_SIZE,
+ data, &out);
+}
+
+int gsm_7bit_encode_ussd(uint8_t *result, const char *data, int *octets)
+{
+ return gsm_7bit_encode_n_ussd(result, GSM_7BIT_LEGACY_MAX_BUFFER_SIZE,
+ data, octets);
+}
+
+int gsm_7bit_encode_oct(uint8_t *result, const char *data, int *octets)
+{
+ return gsm_7bit_encode_n(result, GSM_7BIT_LEGACY_MAX_BUFFER_SIZE,
+ data, octets);
+}
diff --git a/src/shared/libosmocore/src/gsm/gsup.c b/src/shared/libosmocore/src/gsm/gsup.c
new file mode 100644
index 00000000..22f57ab7
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/gsup.c
@@ -0,0 +1,509 @@
+/* Osmocom Generic Subscriber Update Protocol message encoder/decoder */
+
+/*
+ * (C) 2014 by sysmocom s.f.m.c. GmbH
+ * (C) 2015 by Holger Hans Peter Freyther
+ * (C) 2016 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * Author: Jacob Erlbeck
+ *
+ * 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 <osmocom/gsm/tlv.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/gsm/gsm48_ie.h>
+#include <osmocom/gsm/gsup.h>
+
+#include <stdint.h>
+
+static int decode_pdp_info(uint8_t *data, size_t data_len,
+ struct osmo_gsup_pdp_info *pdp_info)
+{
+ int rc;
+ uint8_t tag;
+ uint8_t *value;
+ size_t value_len;
+
+ /* specific parts */
+ while (data_len > 0) {
+ enum osmo_gsup_iei iei;
+
+ rc = osmo_shift_tlv(&data, &data_len, &tag, &value, &value_len);
+ if (rc < 0)
+ return -GMM_CAUSE_PROTO_ERR_UNSPEC;
+
+ iei = tag;
+
+ switch (iei) {
+ case OSMO_GSUP_PDP_CONTEXT_ID_IE:
+ 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;
+ break;
+
+ case OSMO_GSUP_ACCESS_POINT_NAME_IE:
+ pdp_info->apn_enc = value;
+ pdp_info->apn_enc_len = value_len;
+ break;
+
+ case OSMO_GSUP_PDP_QOS_IE:
+ pdp_info->qos_enc = value;
+ pdp_info->qos_enc_len = value_len;
+ break;
+
+ default:
+ LOGP(DLGSUP, LOGL_ERROR,
+ "GSUP IE type %d not expected in PDP info\n", iei);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+static int decode_auth_info(uint8_t *data, size_t data_len,
+ struct osmo_auth_vector *auth_vector)
+{
+ int rc;
+ uint8_t tag;
+ uint8_t *value;
+ size_t value_len;
+ enum osmo_gsup_iei iei;
+ uint8_t presence = 0;
+
+ /* specific parts */
+ while (data_len > 0) {
+ rc = osmo_shift_tlv(&data, &data_len, &tag, &value, &value_len);
+ if (rc < 0)
+ return -GMM_CAUSE_PROTO_ERR_UNSPEC;
+
+ iei = tag;
+
+ switch (iei) {
+ case OSMO_GSUP_RAND_IE:
+ if (value_len != sizeof(auth_vector->rand))
+ goto parse_error;
+
+ memcpy(auth_vector->rand, value, value_len);
+ presence |= (1 << 0);
+ break;
+
+ case OSMO_GSUP_SRES_IE:
+ if (value_len != sizeof(auth_vector->sres))
+ goto parse_error;
+
+ memcpy(auth_vector->sres, value, value_len);
+ presence |= (1 << 1);
+ break;
+
+ case OSMO_GSUP_KC_IE:
+ if (value_len != sizeof(auth_vector->kc))
+ goto parse_error;
+
+ memcpy(auth_vector->kc, value, value_len);
+ presence |= (1 << 2);
+ break;
+
+ case OSMO_GSUP_IK_IE:
+ if (value_len != sizeof(auth_vector->ik))
+ goto parse_error;
+ memcpy(auth_vector->ik, value, value_len);
+ presence |= (1 << 4);
+ break;
+
+ case OSMO_GSUP_CK_IE:
+ if (value_len != sizeof(auth_vector->ck))
+ goto parse_error;
+ memcpy(auth_vector->ck, value, value_len);
+ presence |= (1 << 5);
+ break;
+
+ case OSMO_GSUP_AUTN_IE:
+ if (value_len != sizeof(auth_vector->autn))
+ goto parse_error;
+ memcpy(auth_vector->autn, value, value_len);
+ presence |= (1 << 6);
+ break;
+ case OSMO_GSUP_RES_IE:
+ if (value_len > sizeof(auth_vector->res))
+ goto parse_error;
+ memcpy(auth_vector->res, value, value_len);
+ auth_vector->res_len = value_len;
+ presence |= (1 << 7);
+ break;
+
+ default:
+ LOGP(DLGSUP, LOGL_ERROR,
+ "GSUP IE type %d not expected in PDP info\n", iei);
+ continue;
+ }
+ }
+
+ if (presence & 0x07)
+ auth_vector->auth_types |= OSMO_AUTH_TYPE_GSM;
+ if (presence & 0xf0)
+ auth_vector->auth_types |= OSMO_AUTH_TYPE_UMTS;
+
+ return 0;
+
+parse_error:
+ LOGP(DLGSUP, LOGL_ERROR,
+ "GSUP IE type %d, length %zu invalid in PDP info\n", iei, value_len);
+
+ return -1;
+}
+
+/*! Decode (parse) a GSUP message
+ * \param[in] const_data input data to be parsed
+ * \param[in] data_len length of input (\a const_data)
+ * \param[out] gsup_msg callee-allocated output data structure
+ * \returns 0 on success; negative otherwise
+ */
+int osmo_gsup_decode(const uint8_t *const_data, size_t data_len,
+ struct osmo_gsup_message *gsup_msg)
+{
+ int rc;
+ uint8_t tag;
+ /* the shift/match functions expect non-const pointers, but we'll
+ * either copy the data or cast pointers back to const before returning
+ * them
+ */
+ uint8_t *data = (uint8_t *)const_data;
+ uint8_t *value;
+ size_t value_len;
+ static const struct osmo_gsup_pdp_info empty_pdp_info = {0};
+ static const struct osmo_auth_vector empty_auth_info = {{0}};
+ static const struct osmo_gsup_message empty_gsup_message = {0};
+
+ *gsup_msg = empty_gsup_message;
+
+ /* generic part */
+ rc = osmo_shift_v_fixed(&data, &data_len, 1, &value);
+ if (rc < 0)
+ return -GMM_CAUSE_INV_MAND_INFO;
+
+ gsup_msg->message_type = osmo_decode_big_endian(value, 1);
+
+ rc = osmo_match_shift_tlv(&data, &data_len, OSMO_GSUP_IMSI_IE,
+ &value, &value_len);
+
+ if (rc <= 0)
+ return -GMM_CAUSE_INV_MAND_INFO;
+
+ if (value_len * 2 + 1 > sizeof(gsup_msg->imsi))
+ return -GMM_CAUSE_INV_MAND_INFO;
+
+ /* Note that gsm48_decode_bcd_number expects the number of encoded IMSI
+ * octets in the first octet. By coincidence (the TLV encoding) the byte
+ * before the value part already contains this length so we can use it
+ * here.
+ */
+ OSMO_ASSERT(value[-1] == value_len);
+ gsm48_decode_bcd_number(gsup_msg->imsi, sizeof(gsup_msg->imsi),
+ value - 1, 0);
+
+ /* specific parts */
+ while (data_len > 0) {
+ enum osmo_gsup_iei iei;
+ struct osmo_gsup_pdp_info pdp_info;
+ struct osmo_auth_vector auth_info;
+
+ rc = osmo_shift_tlv(&data, &data_len, &tag, &value, &value_len);
+ if (rc < 0)
+ return -GMM_CAUSE_PROTO_ERR_UNSPEC;
+
+ iei = tag;
+
+ switch (iei) {
+ case OSMO_GSUP_IMSI_IE:
+ case OSMO_GSUP_PDP_TYPE_IE:
+ case OSMO_GSUP_ACCESS_POINT_NAME_IE:
+ case OSMO_GSUP_SRES_IE:
+ case OSMO_GSUP_KC_IE:
+ LOGP(DLGSUP, LOGL_NOTICE,
+ "GSUP IE type %d not expected (ignored)\n", iei);
+ continue;
+
+ case OSMO_GSUP_CAUSE_IE:
+ gsup_msg->cause = osmo_decode_big_endian(value, value_len);
+ break;
+
+ case OSMO_GSUP_CANCEL_TYPE_IE:
+ gsup_msg->cancel_type =
+ osmo_decode_big_endian(value, value_len) + 1;
+ break;
+
+ case OSMO_GSUP_PDP_INFO_COMPL_IE:
+ gsup_msg->pdp_info_compl = 1;
+ break;
+
+ case OSMO_GSUP_FREEZE_PTMSI_IE:
+ gsup_msg->freeze_ptmsi = 1;
+ break;
+
+ case OSMO_GSUP_PDP_CONTEXT_ID_IE:
+ /* When these IE appear in the top-level part of the
+ * message, they are used by Delete Subscr Info to delete
+ * single entries. We don't have an extra list for
+ * these but use the PDP info list instead */
+
+ /* fall through */
+
+ case OSMO_GSUP_PDP_INFO_IE:
+ if (gsup_msg->num_pdp_infos >= OSMO_GSUP_MAX_NUM_PDP_INFO) {
+ LOGP(DLGSUP, LOGL_ERROR,
+ "GSUP IE type %d (PDP_INFO) max exceeded\n",
+ iei);
+ return -GMM_CAUSE_COND_IE_ERR;
+ }
+
+ pdp_info = empty_pdp_info;
+
+ if (iei == OSMO_GSUP_PDP_INFO_IE) {
+ rc = decode_pdp_info(value, value_len, &pdp_info);
+ if (rc < 0)
+ return rc;
+ pdp_info.have_info = 1;
+ } else {
+ pdp_info.context_id =
+ osmo_decode_big_endian(value, value_len);
+ }
+
+ gsup_msg->pdp_infos[gsup_msg->num_pdp_infos++] =
+ pdp_info;
+ break;
+
+ case OSMO_GSUP_AUTH_TUPLE_IE:
+ if (gsup_msg->num_auth_vectors >= OSMO_GSUP_MAX_NUM_AUTH_INFO) {
+ LOGP(DLGSUP, LOGL_ERROR,
+ "GSUP IE type %d (AUTH_INFO) max exceeded\n",
+ iei);
+ return -GMM_CAUSE_INV_MAND_INFO;
+ }
+
+ auth_info = empty_auth_info;
+
+ rc = decode_auth_info(value, value_len, &auth_info);
+ if (rc < 0)
+ return rc;
+
+ gsup_msg->auth_vectors[gsup_msg->num_auth_vectors++] =
+ auth_info;
+ break;
+
+ case OSMO_GSUP_AUTS_IE:
+ if (value_len != 16) {
+ LOGP(DLGSUP, LOGL_ERROR,
+ "AUTS length != 16 received\n");
+ return -GMM_CAUSE_COND_IE_ERR;
+ }
+ gsup_msg->auts = value;
+ break;
+
+ case OSMO_GSUP_RAND_IE:
+ if (value_len != 16) {
+ LOGP(DLGSUP, LOGL_ERROR,
+ "RAND length != 16 received\n");
+ return -GMM_CAUSE_COND_IE_ERR;
+ }
+ gsup_msg->rand = value;
+ break;
+
+ case OSMO_GSUP_MSISDN_IE:
+ gsup_msg->msisdn_enc = value;
+ gsup_msg->msisdn_enc_len = value_len;
+ break;
+
+ case OSMO_GSUP_HLR_NUMBER_IE:
+ gsup_msg->hlr_enc = value;
+ gsup_msg->hlr_enc_len = value_len;
+ break;
+
+ case OSMO_GSUP_CN_DOMAIN_IE:
+ gsup_msg->cn_domain = *value;
+ break;
+
+ default:
+ LOGP(DLGSUP, LOGL_NOTICE,
+ "GSUP IE type %d unknown\n", iei);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+static void encode_pdp_info(struct msgb *msg, enum osmo_gsup_iei iei,
+ const struct osmo_gsup_pdp_info *pdp_info)
+{
+ uint8_t *len_field;
+ size_t old_len;
+ uint8_t u8;
+
+ len_field = msgb_tlv_put(msg, iei, 0, NULL) - 1;
+ old_len = msgb_length(msg);
+
+ 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->apn_enc) {
+ msgb_tlv_put(msg, OSMO_GSUP_ACCESS_POINT_NAME_IE,
+ pdp_info->apn_enc_len, pdp_info->apn_enc);
+ }
+
+ if (pdp_info->qos_enc) {
+ msgb_tlv_put(msg, OSMO_GSUP_PDP_QOS_IE,
+ pdp_info->qos_enc_len, pdp_info->qos_enc);
+ }
+
+ /* Update length field */
+ *len_field = msgb_length(msg) - old_len;
+}
+
+static void encode_auth_info(struct msgb *msg, enum osmo_gsup_iei iei,
+ const struct osmo_auth_vector *auth_vector)
+{
+ uint8_t *len_field;
+ size_t old_len;
+
+ len_field = msgb_tlv_put(msg, iei, 0, NULL) - 1;
+ old_len = msgb_length(msg);
+
+ if (auth_vector->auth_types & OSMO_AUTH_TYPE_GSM) {
+ msgb_tlv_put(msg, OSMO_GSUP_RAND_IE,
+ sizeof(auth_vector->rand), auth_vector->rand);
+
+ msgb_tlv_put(msg, OSMO_GSUP_SRES_IE,
+ sizeof(auth_vector->sres), auth_vector->sres);
+
+ msgb_tlv_put(msg, OSMO_GSUP_KC_IE,
+ sizeof(auth_vector->kc), auth_vector->kc);
+ }
+
+ if (auth_vector->auth_types & OSMO_AUTH_TYPE_UMTS) {
+ msgb_tlv_put(msg, OSMO_GSUP_IK_IE,
+ sizeof(auth_vector->ik), auth_vector->ik);
+
+ msgb_tlv_put(msg, OSMO_GSUP_CK_IE,
+ sizeof(auth_vector->ck), auth_vector->ck);
+
+ msgb_tlv_put(msg, OSMO_GSUP_AUTN_IE,
+ sizeof(auth_vector->autn), auth_vector->autn);
+
+ msgb_tlv_put(msg, OSMO_GSUP_RES_IE,
+ auth_vector->res_len, auth_vector->res);
+ }
+
+ /* Update length field */
+ *len_field = msgb_length(msg) - old_len;
+}
+
+/*! Encode a GSUP message
+ * \param[out] msg message buffer to which encoded message is written
+ * \param[in] gsup_msg \ref osmo_gsup_message data to be encoded
+ */
+void osmo_gsup_encode(struct msgb *msg, const struct osmo_gsup_message *gsup_msg)
+{
+ uint8_t u8;
+ int idx;
+ uint8_t bcd_buf[GSM48_MI_SIZE] = {0};
+ size_t bcd_len;
+
+ /* generic part */
+ OSMO_ASSERT(gsup_msg->message_type);
+ msgb_v_put(msg, gsup_msg->message_type);
+
+ bcd_len = gsm48_encode_bcd_number(bcd_buf, sizeof(bcd_buf), 0,
+ gsup_msg->imsi);
+
+ OSMO_ASSERT(bcd_len > 1);
+ OSMO_ASSERT(bcd_len <= sizeof(bcd_buf));
+
+ /* Note that gsm48_encode_bcd_number puts the length into the first
+ * octet. Since msgb_tlv_put will add this length byte, we'll have to
+ * skip it */
+ msgb_tlv_put(msg, OSMO_GSUP_IMSI_IE, bcd_len - 1, &bcd_buf[1]);
+
+ /* specific parts */
+ if (gsup_msg->msisdn_enc)
+ msgb_tlv_put(msg, OSMO_GSUP_MSISDN_IE,
+ gsup_msg->msisdn_enc_len, gsup_msg->msisdn_enc);
+ if (gsup_msg->hlr_enc)
+ msgb_tlv_put(msg, OSMO_GSUP_HLR_NUMBER_IE,
+ gsup_msg->hlr_enc_len, gsup_msg->hlr_enc);
+
+ if ((u8 = gsup_msg->cause))
+ msgb_tlv_put(msg, OSMO_GSUP_CAUSE_IE, sizeof(u8), &u8);
+
+ if ((u8 = gsup_msg->cancel_type)) {
+ u8 -= 1;
+ msgb_tlv_put(msg, OSMO_GSUP_CANCEL_TYPE_IE, sizeof(u8), &u8);
+ }
+
+ if (gsup_msg->pdp_info_compl)
+ msgb_tlv_put(msg, OSMO_GSUP_PDP_INFO_COMPL_IE, 0, &u8);
+
+ if (gsup_msg->freeze_ptmsi)
+ msgb_tlv_put(msg, OSMO_GSUP_FREEZE_PTMSI_IE, 0, &u8);
+
+ for (idx = 0; idx < gsup_msg->num_pdp_infos; idx++) {
+ const struct osmo_gsup_pdp_info *pdp_info;
+
+ pdp_info = &gsup_msg->pdp_infos[idx];
+
+ if (pdp_info->context_id == 0)
+ continue;
+
+ if (pdp_info->have_info) {
+ encode_pdp_info(msg, OSMO_GSUP_PDP_INFO_IE, pdp_info);
+ } else {
+ u8 = pdp_info->context_id;
+ msgb_tlv_put(msg, OSMO_GSUP_PDP_CONTEXT_ID_IE,
+ sizeof(u8), &u8);
+ }
+ }
+
+ for (idx = 0; idx < gsup_msg->num_auth_vectors; idx++) {
+ const struct osmo_auth_vector *auth_vector;
+
+ auth_vector = &gsup_msg->auth_vectors[idx];
+
+ encode_auth_info(msg, OSMO_GSUP_AUTH_TUPLE_IE, auth_vector);
+ }
+
+ if (gsup_msg->auts)
+ msgb_tlv_put(msg, OSMO_GSUP_AUTS_IE, 16, gsup_msg->auts);
+
+ if (gsup_msg->rand)
+ msgb_tlv_put(msg, OSMO_GSUP_RAND_IE, 16, gsup_msg->rand);
+
+ if (gsup_msg->cn_domain) {
+ uint8_t dn = gsup_msg->cn_domain;
+ msgb_tlv_put(msg, OSMO_GSUP_CN_DOMAIN_IE, 1, &dn);
+ }
+}
diff --git a/src/shared/libosmocore/src/gsm/ipa.c b/src/shared/libosmocore/src/gsm/ipa.c
new file mode 100644
index 00000000..f44c3284
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/ipa.c
@@ -0,0 +1,458 @@
+/* OpenBSC Abis input driver for ip.access */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by Holger Hans Peter Freyther
+ * (C) 2010 by On-Waves
+ *
+ * All Rights Reserved
+ *
+ * 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 <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/macaddr.h>
+#include <osmocom/core/select.h>
+
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/protocol/ipaccess.h>
+#include <osmocom/gsm/ipa.h>
+
+#define IPA_ALLOC_SIZE 1200
+
+/*
+ * Common propietary IPA messages:
+ * - PONG: in reply to PING.
+ * - ID_REQUEST: first messages once OML has been established.
+ * - ID_ACK: in reply to ID_ACK.
+ */
+static const uint8_t ipa_pong_msg[] = {
+ 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG
+};
+
+static const uint8_t ipa_id_ack_msg[] = {
+ 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK
+};
+
+static const uint8_t ipa_id_req_msg[] = {
+ 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET,
+ 0x01, IPAC_IDTAG_UNIT,
+ 0x01, IPAC_IDTAG_MACADDR,
+ 0x01, IPAC_IDTAG_LOCATION1,
+ 0x01, IPAC_IDTAG_LOCATION2,
+ 0x01, IPAC_IDTAG_EQUIPVERS,
+ 0x01, IPAC_IDTAG_SWVERSION,
+ 0x01, IPAC_IDTAG_UNITNAME,
+ 0x01, IPAC_IDTAG_SERNR,
+};
+
+
+static const char *idtag_names[] = {
+ [IPAC_IDTAG_SERNR] = "Serial_Number",
+ [IPAC_IDTAG_UNITNAME] = "Unit_Name",
+ [IPAC_IDTAG_LOCATION1] = "Location_1",
+ [IPAC_IDTAG_LOCATION2] = "Location_2",
+ [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version",
+ [IPAC_IDTAG_SWVERSION] = "Software_Version",
+ [IPAC_IDTAG_IPADDR] = "IP_Address",
+ [IPAC_IDTAG_MACADDR] = "MAC_Address",
+ [IPAC_IDTAG_UNIT] = "Unit_ID",
+};
+
+const char *ipa_ccm_idtag_name(uint8_t tag)
+{
+ if (tag >= ARRAY_SIZE(idtag_names))
+ return "unknown";
+
+ return idtag_names[tag];
+}
+
+int ipa_ccm_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len)
+{
+ return ipa_ccm_idtag_parse_off(dec, buf, len, 0);
+}
+
+int ipa_ccm_idtag_parse_off(struct tlv_parsed *dec, unsigned char *buf, int len, const int len_offset)
+{
+ uint8_t t_len;
+ uint8_t t_tag;
+ uint8_t *cur = buf;
+
+ memset(dec, 0, sizeof(*dec));
+
+ while (len >= 2) {
+ len -= 2;
+ t_len = *cur++;
+ t_tag = *cur++;
+
+ if (t_len < len_offset) {
+ LOGP(DLMI, LOGL_ERROR, "minimal offset not included: %d < %d\n", t_len, len_offset);
+ return -EINVAL;
+ }
+
+ if (t_len > len + 1) {
+ LOGP(DLMI, LOGL_ERROR, "The tag does not fit: %d > %d\n", t_len, len + 1);
+ return -EINVAL;
+ }
+
+ DEBUGPC(DLMI, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
+
+ dec->lv[t_tag].len = t_len - len_offset;
+ dec->lv[t_tag].val = cur;
+
+ cur += t_len - len_offset;
+ len -= t_len - len_offset;
+ }
+ return 0;
+}
+
+int ipa_parse_unitid(const char *str, struct ipaccess_unit *unit_data)
+{
+ unsigned long ul;
+ char *endptr;
+ const char *nptr;
+
+ nptr = str;
+ ul = strtoul(nptr, &endptr, 10);
+ if (endptr <= nptr)
+ return -EINVAL;
+ unit_data->site_id = ul & 0xffff;
+
+ if (*endptr++ != '/')
+ return -EINVAL;
+
+ nptr = endptr;
+ ul = strtoul(nptr, &endptr, 10);
+ if (endptr <= nptr)
+ return -EINVAL;
+ unit_data->bts_id = ul & 0xffff;
+
+ if (*endptr++ != '/')
+ return -EINVAL;
+
+ nptr = endptr;
+ ul = strtoul(nptr, &endptr, 10);
+ if (endptr <= nptr)
+ return -EINVAL;
+ unit_data->trx_id = ul & 0xffff;
+
+ return 0;
+}
+
+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));
+
+ if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNITNAME, 1))
+ ud->unit_name = talloc_strdup(ud, (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));
+
+ if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION2, 1))
+ ud->location2 = talloc_strdup(ud, (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));
+
+ if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SWVERSION, 1))
+ ud->swversion = talloc_strdup(ud, (char *)
+ TLVP_VAL(tp, IPAC_IDTAG_SWVERSION));
+
+ if (TLVP_PRES_LEN(tp, IPAC_IDTAG_MACADDR, 17)) {
+ rc = osmo_macaddr_parse(ud->mac_addr, (char *)
+ TLVP_VAL(tp, IPAC_IDTAG_MACADDR));
+ if (rc < 0)
+ goto out;
+ }
+
+ if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNIT, 1))
+ rc = ipa_parse_unitid((char *)
+ TLVP_VAL(tp, IPAC_IDTAG_UNIT), ud);
+
+out:
+ return rc;
+}
+
+int ipa_send(int fd, const void *msg, size_t msglen)
+{
+ int ret;
+
+ ret = write(fd, msg, msglen);
+ if (ret < 0)
+ return -errno;
+ if (ret < msglen) {
+ LOGP(DLINP, LOGL_ERROR, "ipa_send: short write\n");
+ return -EIO;
+ }
+ return ret;
+}
+
+int ipa_ccm_send_pong(int fd)
+{
+ return ipa_send(fd, ipa_pong_msg, sizeof(ipa_pong_msg));
+}
+
+int ipa_ccm_send_id_ack(int fd)
+{
+ return ipa_send(fd, ipa_id_ack_msg, sizeof(ipa_id_ack_msg));
+}
+
+int ipa_ccm_send_id_req(int fd)
+{
+ return ipa_send(fd, ipa_id_req_msg, sizeof(ipa_id_req_msg));
+}
+
+/* base handling of the ip.access protocol */
+int ipa_ccm_rcvmsg_base(struct msgb *msg, struct osmo_fd *bfd)
+{
+ uint8_t msg_type = *(msg->l2h);
+ int ret;
+
+ switch (msg_type) {
+ case IPAC_MSGT_PING:
+ ret = ipa_ccm_send_pong(bfd->fd);
+ if (ret < 0) {
+ LOGP(DLINP, LOGL_ERROR, "Cannot send PING "
+ "message. Reason: %s\n", strerror(errno));
+ break;
+ }
+ ret = 1;
+ break;
+ case IPAC_MSGT_PONG:
+ DEBUGP(DLMI, "PONG!\n");
+ ret = 1;
+ break;
+ case IPAC_MSGT_ID_ACK:
+ DEBUGP(DLMI, "ID_ACK? -> ACK!\n");
+ ret = ipa_ccm_send_id_ack(bfd->fd);
+ if (ret < 0) {
+ LOGP(DLINP, LOGL_ERROR, "Cannot send ID_ACK "
+ "message. Reason: %s\n", strerror(errno));
+ break;
+ }
+ ret = 1;
+ break;
+ default:
+ /* This is not an IPA PING, PONG or ID_ACK message */
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+/* base handling of the ip.access protocol */
+int ipa_ccm_rcvmsg_bts_base(struct msgb *msg, struct osmo_fd *bfd)
+{
+ uint8_t msg_type = *(msg->l2h);
+ int ret = 0;
+
+ switch (msg_type) {
+ case IPAC_MSGT_PING:
+ ret = ipa_ccm_send_pong(bfd->fd);
+ if (ret < 0) {
+ LOGP(DLINP, LOGL_ERROR, "Cannot send PONG "
+ "message. Reason: %s\n", strerror(errno));
+ }
+ break;
+ case IPAC_MSGT_PONG:
+ DEBUGP(DLMI, "PONG!\n");
+ break;
+ case IPAC_MSGT_ID_ACK:
+ DEBUGP(DLMI, "ID_ACK\n");
+ break;
+ }
+ return ret;
+}
+
+
+void ipa_prepend_header_ext(struct msgb *msg, int proto)
+{
+ struct ipaccess_head_ext *hh_ext;
+
+ /* prepend the osmo ip.access header extension */
+ hh_ext = (struct ipaccess_head_ext *) msgb_push(msg, sizeof(*hh_ext));
+ hh_ext->proto = proto;
+}
+
+void ipa_prepend_header(struct msgb *msg, int proto)
+{
+ struct ipaccess_head *hh;
+
+ /* prepend the ip.access header */
+ hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh));
+ hh->len = htons(msg->len - sizeof(*hh));
+ hh->proto = proto;
+}
+
+int ipa_msg_recv(int fd, struct msgb **rmsg)
+{
+ int rc = ipa_msg_recv_buffered(fd, rmsg, NULL);
+ if (rc < 0) {
+ errno = -rc;
+ rc = -1;
+ }
+ return rc;
+}
+
+int ipa_msg_recv_buffered(int fd, struct msgb **rmsg, struct msgb **tmp_msg)
+{
+ struct msgb *msg = tmp_msg ? *tmp_msg : NULL;
+ struct ipaccess_head *hh;
+ int len, ret;
+ int needed;
+
+ if (msg == NULL) {
+ msg = ipa_msg_alloc(0);
+ if (msg == NULL) {
+ ret = -ENOMEM;
+ goto discard_msg;
+ }
+ msg->l1h = msg->tail;
+ }
+
+ if (msg->l2h == NULL) {
+ /* first read our 3-byte header */
+ needed = sizeof(*hh) - msg->len;
+ ret = recv(fd, msg->tail, needed, 0);
+ if (ret == 0)
+ goto discard_msg;
+
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ ret = 0;
+ else {
+ ret = -errno;
+ goto discard_msg;
+ }
+ }
+
+ msgb_put(msg, ret);
+
+ if (ret < needed) {
+ if (msg->len == 0) {
+ ret = -EAGAIN;
+ goto discard_msg;
+ }
+
+ LOGP(DLINP, LOGL_INFO,
+ "Received part of IPA message header (%d/%zu)\n",
+ msg->len, sizeof(*hh));
+ if (!tmp_msg) {
+ ret = -EIO;
+ goto discard_msg;
+ }
+ *tmp_msg = msg;
+ return -EAGAIN;
+ }
+
+ msg->l2h = msg->tail;
+ }
+
+ hh = (struct ipaccess_head *) msg->data;
+
+ /* then read the length as specified in header */
+ len = ntohs(hh->len);
+
+ if (len < 0 || IPA_ALLOC_SIZE < len + sizeof(*hh)) {
+ LOGP(DLINP, LOGL_ERROR, "bad message length of %d bytes, "
+ "received %d bytes\n", len, msg->len);
+ ret = -EIO;
+ goto discard_msg;
+ }
+
+ needed = len - msgb_l2len(msg);
+
+ if (needed > 0) {
+ ret = recv(fd, msg->tail, needed, 0);
+
+ if (ret == 0)
+ goto discard_msg;
+
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ ret = 0;
+ else {
+ ret = -errno;
+ goto discard_msg;
+ }
+ }
+
+ msgb_put(msg, ret);
+
+ if (ret < needed) {
+ LOGP(DLINP, LOGL_INFO,
+ "Received part of IPA message L2 data (%d/%d)\n",
+ msgb_l2len(msg), len);
+ if (!tmp_msg) {
+ ret = -EIO;
+ goto discard_msg;
+ }
+ *tmp_msg = msg;
+ return -EAGAIN;
+ }
+ }
+
+ ret = msgb_l2len(msg);
+
+ if (ret == 0) {
+ LOGP(DLINP, LOGL_INFO,
+ "Discarding IPA message without payload\n");
+ ret = -EAGAIN;
+ goto discard_msg;
+ }
+
+ if (tmp_msg)
+ *tmp_msg = NULL;
+ *rmsg = msg;
+ return ret;
+
+discard_msg:
+ if (tmp_msg)
+ *tmp_msg = NULL;
+ msgb_free(msg);
+ return ret;
+}
+
+struct msgb *ipa_msg_alloc(int headroom)
+{
+ struct msgb *nmsg;
+
+ headroom += sizeof(struct ipaccess_head);
+
+ nmsg = msgb_alloc_headroom(1200 + headroom, headroom, "Abis/IP");
+ if (!nmsg)
+ return NULL;
+ return nmsg;
+}
diff --git a/src/shared/libosmocore/src/gsm/kasumi.c b/src/shared/libosmocore/src/gsm/kasumi.c
new file mode 100644
index 00000000..d5d78a74
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/kasumi.c
@@ -0,0 +1,186 @@
+/* Kasumi cipher and KGcore functions */
+
+/* (C) 2013 by Max <Max.Suraev@fairwaves.ru>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/gsm/kasumi.h>
+
+/* See TS 135 202 for constants and full Kasumi spec. */
+inline static uint16_t kasumi_FI(uint16_t I, uint16_t skey)
+{
+ static const uint16_t S7[] = {
+ 54, 50, 62, 56, 22, 34, 94, 96, 38, 6, 63, 93, 2, 18, 123, 33,
+ 55, 113, 39, 114, 21, 67, 65, 12, 47, 73, 46, 27, 25, 111, 124, 81,
+ 53, 9, 121, 79, 52, 60, 58, 48, 101, 127, 40, 120, 104, 70, 71, 43,
+ 20, 122, 72, 61, 23, 109, 13, 100, 77, 1, 16, 7, 82, 10, 105, 98,
+ 117, 116, 76, 11, 89, 106, 0,125,118, 99, 86, 69, 30, 57, 126, 87,
+ 112, 51, 17, 5, 95, 14, 90, 84, 91, 8, 35,103, 32, 97, 28, 66,
+ 102, 31, 26, 45, 75, 4, 85, 92, 37, 74, 80, 49, 68, 29, 115, 44,
+ 64, 107, 108, 24, 110, 83, 36, 78, 42, 19, 15, 41, 88, 119, 59, 3
+ };
+ static const uint16_t S9[] = {
+ 167, 239, 161, 379, 391, 334, 9, 338, 38, 226, 48, 358, 452, 385, 90, 397,
+ 183, 253, 147, 331, 415, 340, 51, 362, 306, 500, 262, 82, 216, 159, 356, 177,
+ 175, 241, 489, 37, 206, 17, 0, 333, 44, 254, 378, 58, 143, 220, 81, 400,
+ 95, 3, 315, 245, 54, 235, 218, 405, 472, 264, 172, 494, 371, 290, 399, 76,
+ 165, 197, 395, 121, 257, 480, 423, 212, 240, 28, 462, 176, 406, 507, 288, 223,
+ 501, 407, 249, 265, 89, 186, 221, 428,164, 74, 440, 196, 458, 421, 350, 163,
+ 232, 158, 134, 354, 13, 250, 491, 142,191, 69, 193, 425, 152, 227, 366, 135,
+ 344, 300, 276, 242, 437, 320, 113, 278, 11, 243, 87, 317, 36, 93, 496, 27,
+ 487, 446, 482, 41, 68, 156, 457, 131, 326, 403, 339, 20, 39, 115, 442, 124,
+ 475, 384, 508, 53, 112, 170, 479, 151, 126, 169, 73, 268, 279, 321, 168, 364,
+ 363, 292, 46, 499, 393, 327, 324, 24, 456, 267, 157, 460, 488, 426, 309, 229,
+ 439, 506, 208, 271, 349, 401, 434, 236, 16, 209, 359, 52, 56, 120, 199, 277,
+ 465, 416, 252, 287, 246, 6, 83, 305, 420, 345, 153,502, 65, 61, 244, 282,
+ 173, 222, 418, 67, 386, 368, 261, 101, 476, 291, 195,430, 49, 79, 166, 330,
+ 280, 383, 373, 128, 382, 408, 155, 495, 367, 388, 274, 107, 459, 417, 62, 454,
+ 132, 225, 203, 316, 234, 14, 301, 91, 503, 286, 424, 211, 347, 307, 140, 374,
+ 35, 103, 125, 427, 19, 214, 453, 146, 498, 314, 444, 230, 256, 329, 198, 285,
+ 50, 116, 78, 410, 10, 205, 510, 171, 231, 45, 139, 467, 29, 86, 505, 32,
+ 72, 26, 342, 150, 313, 490, 431, 238, 411, 325, 149, 473, 40, 119, 174, 355,
+ 185, 233, 389, 71, 448, 273, 372, 55, 110, 178, 322, 12, 469, 392, 369, 190,
+ 1, 109, 375, 137, 181, 88, 75, 308, 260, 484, 98, 272, 370, 275, 412, 111,
+ 336, 318, 4, 504, 492, 259, 304, 77, 337, 435, 21, 357, 303, 332, 483, 18,
+ 47, 85, 25, 497, 474, 289, 100, 269, 296, 478, 270, 106, 31, 104, 433, 84,
+ 414, 486, 394, 96, 99, 154, 511, 148, 413, 361, 409, 255, 162, 215, 302, 201,
+ 266, 351, 343, 144, 441, 365, 108, 298, 251, 34, 182, 509, 138, 210, 335, 133,
+ 311, 352, 328, 141, 396, 346, 123, 319, 450, 281, 429, 228, 443, 481, 92, 404,
+ 485, 422, 248, 297, 23, 213, 130, 466, 22, 217, 283, 70, 294, 360, 419, 127,
+ 312, 377, 7, 468, 194, 2, 117, 295, 463, 258, 224, 447, 247, 187, 80, 398,
+ 284, 353, 105, 390, 299, 471, 470, 184, 57, 200, 348, 63, 204, 188, 33, 451,
+ 97, 30, 310, 219, 94, 160, 129, 493, 64, 179, 263, 102, 189, 207, 114, 402,
+ 438, 477, 387, 122, 192, 42, 381, 5, 145, 118, 180, 449, 293, 323, 136, 380,
+ 43, 66, 60, 455, 341, 445, 202, 432, 8, 237, 15, 376, 436, 464, 59, 461
+ };
+ uint16_t L, R;
+
+ /* Split 16 bit input into two unequal halves: 9 and 7 bits, same for subkey */
+ L = I >> 7; /* take 9 bits */
+ R = I & 0x7F; /* take 7 bits */
+
+ L = S9[L] ^ R;
+ R = S7[R] ^ (L & 0x7F);
+
+ L ^= (skey & 0x1FF);
+ R ^= (skey >> 9);
+
+ L = S9[L] ^ R;
+ R = S7[R] ^ (L & 0x7F);
+
+ return (R << 9) + L;
+}
+
+inline static uint32_t kasumi_FO(uint32_t I, const uint16_t *KOi1, const uint16_t *KOi2, const uint16_t *KOi3, const uint16_t *KIi1, const uint16_t *KIi2, const uint16_t *KIi3, unsigned i)
+{
+ uint16_t L = I >> 16, R = I; /* Split 32 bit input into Left and Right parts */
+
+ L ^= KOi1[i];
+ L = kasumi_FI(L, KIi1[i]);
+ L ^= R;
+
+ R ^= KOi2[i];
+ R = kasumi_FI(R, KIi2[i]);
+ R ^= L;
+
+ L ^= KOi3[i];
+ L = kasumi_FI(L, KIi3[i]);
+ L ^= R;
+
+ return (((uint32_t)R) << 16) + L;
+}
+
+inline static uint32_t kasumi_FL(uint32_t I, const uint16_t *KLi1, const uint16_t *KLi2, unsigned i)
+{
+ uint16_t L = I >> 16, R = I, tmp; /* Split 32 bit input into Left and Right parts */
+
+ tmp = L & KLi1[i];
+ R ^= osmo_rol16(tmp, 1);
+
+ tmp = R | KLi2[i];
+ L ^= osmo_rol16(tmp, 1);
+
+ return (((uint32_t)L) << 16) + R;
+}
+
+uint64_t _kasumi(uint64_t P, const uint16_t *KLi1, const uint16_t *KLi2, const uint16_t *KOi1, const uint16_t *KOi2, const uint16_t *KOi3, const uint16_t *KIi1, const uint16_t *KIi2, const uint16_t *KIi3)
+{
+ uint32_t i, L = P >> 32, R = P; /* Split 64 bit input into Left and Right parts */
+
+ for (i = 0; i < 8; i++) {
+ R ^= kasumi_FO(kasumi_FL(L, KLi1, KLi2, i), KOi1, KOi2, KOi3, KIi1, KIi2, KIi3, i); /* odd round */
+ i++;
+ L ^= kasumi_FL(kasumi_FO(R, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3, i), KLi1, KLi2, i); /* even round */
+ }
+ return (((uint64_t)L) << 32) + R; /* Concatenate Left and Right 32 bits into 64 bit ciphertext */
+}
+
+void _kasumi_key_expand(const uint8_t *key, uint16_t *KLi1, uint16_t *KLi2, uint16_t *KOi1, uint16_t *KOi2, uint16_t *KOi3, uint16_t *KIi1, uint16_t *KIi2, uint16_t *KIi3)
+{
+ uint16_t i, C[] = { 0x0123, 0x4567, 0x89AB, 0xCDEF, 0xFEDC, 0xBA98, 0x7654, 0x3210 };
+
+ /* Work with 16 bit subkeys and create prime subkeys */
+ for (i = 0; i < 8; i++)
+ C[i] ^= osmo_load16be(key + i * 2);
+ /* C[] now stores K-prime[] */
+
+ /* Create round-specific subkeys */
+ for (i = 0; i < 8; i++) {
+ KLi1[i] = osmo_rol16(osmo_load16be(key + i * 2), 1);
+ KLi2[i] = C[(i + 2) & 0x7];
+
+ KOi1[i] = osmo_rol16(osmo_load16be(key + ((2 * (i + 1)) & 0xE)), 5);
+ KOi2[i] = osmo_rol16(osmo_load16be(key + ((2 * (i + 5)) & 0xE)), 8);
+ KOi3[i] = osmo_rol16(osmo_load16be(key + ((2 * (i + 6)) & 0xE)), 13);
+
+ KIi1[i] = C[(i + 4) & 0x7];
+ KIi2[i] = C[(i + 3) & 0x7];
+ KIi3[i] = C[(i + 7) & 0x7];
+ }
+}
+
+void _kasumi_kgcore(uint8_t CA, uint8_t cb, uint32_t cc, uint8_t cd, const uint8_t *ck, uint8_t *co, uint16_t cl)
+{
+ uint16_t KLi1[8], KLi2[8], KOi1[8], KOi2[8], KOi3[8], KIi1[8], KIi2[8], KIi3[8], i;
+ uint64_t A = ((uint64_t)cc) << 32, BLK = 0, _ca = ((uint64_t)CA << 16) ;
+ A |= _ca;
+ _ca = (uint64_t)((cb << 3) | (cd << 2)) << 24;
+ A |= _ca;
+ /* Register loading complete: see TR 55.919 8.2 and TS 55.216 3.2 */
+
+ uint8_t ck_km[16];
+ for (i = 0; i < 16; i++)
+ ck_km[i] = ck[i] ^ 0x55;
+ /* Modified key established */
+
+ /* preliminary round with modified key */
+ _kasumi_key_expand(ck_km, KLi1, KLi2, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3);
+ A = _kasumi(A, KLi1, KLi2, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3);
+
+ /* Run Kasumi in OFB to obtain enough data for gamma. */
+ _kasumi_key_expand(ck, KLi1, KLi2, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3);
+
+ /* i is a block counter */
+ for (i = 0; i < cl / 64 + 1; i++) {
+ BLK = _kasumi(A ^ i ^ BLK, KLi1, KLi2, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3);
+ osmo_store64be(BLK, co + (i * 8));
+ }
+}
diff --git a/src/shared/libosmocore/src/gsm/lapd_core.c b/src/shared/libosmocore/src/gsm/lapd_core.c
index 96099edb..bf5c388a 100644
--- a/src/shared/libosmocore/src/gsm/lapd_core.c
+++ b/src/shared/libosmocore/src/gsm/lapd_core.c
@@ -25,7 +25,7 @@
* @{
*/
-/*! \file lapd.c */
+/*! \file lapd_core.c */
/*!
* Notes on Buffering: rcv_buffer, tx_queue, tx_hist, send_buffer, send_queue
@@ -82,6 +82,7 @@
#include <osmocom/core/utils.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gsm/lapd_core.h>
+#include <osmocom/gsm/rsl.h>
/* TS 04.06 Table 4 / Section 3.8.1 */
#define LAPD_U_SABM 0x7
@@ -150,16 +151,17 @@ static void lapd_dl_flush_send(struct lapd_datalink *dl)
msgb_free(msg);
/* Clear send-buffer */
- if (dl->send_buffer) {
- msgb_free(dl->send_buffer);
- dl->send_buffer = NULL;
- }
+ msgb_free(dl->send_buffer);
+ dl->send_buffer = NULL;
}
static void lapd_dl_flush_hist(struct lapd_datalink *dl)
{
unsigned int i;
+ if (!dl->range_hist || !dl->tx_hist)
+ return;
+
for (i = 0; i < dl->range_hist; i++) {
if (dl->tx_hist[i].msg) {
msgb_free(dl->tx_hist[i].msg);
@@ -232,10 +234,8 @@ static void lapd_dl_newstate(struct lapd_datalink *dl, uint32_t state)
/* stop T203 on leaving MF EST state, if running */
lapd_stop_t203(dl);
/* remove content res. (network side) on leaving MF EST state */
- if (dl->cont_res) {
- msgb_free(dl->cont_res);
- dl->cont_res = NULL;
- }
+ msgb_free(dl->cont_res);
+ dl->cont_res = NULL;
}
/* start T203 on entering MF EST state, if enabled */
@@ -295,8 +295,8 @@ void lapd_dl_init(struct lapd_datalink *dl, uint8_t k, uint8_t v_range,
if (!tall_lapd_ctx)
tall_lapd_ctx = talloc_named_const(NULL, 1, "lapd context");
- dl->tx_hist = (struct lapd_history *) talloc_zero_array(tall_lapd_ctx,
- struct log_info, dl->range_hist);
+ dl->tx_hist = talloc_zero_array(tall_lapd_ctx,
+ struct lapd_history, dl->range_hist);
}
/* reset to IDLE state */
@@ -311,10 +311,8 @@ void lapd_dl_reset(struct lapd_datalink *dl)
lapd_dl_flush_tx(dl);
lapd_dl_flush_send(dl);
/* Discard partly received L3 message */
- if (dl->rcv_buffer) {
- msgb_free(dl->rcv_buffer);
- dl->rcv_buffer = NULL;
- }
+ msgb_free(dl->rcv_buffer);
+ dl->rcv_buffer = NULL;
/* stop Timers */
lapd_stop_t200(dl);
lapd_stop_t203(dl);
@@ -327,6 +325,7 @@ void lapd_dl_exit(struct lapd_datalink *dl)
lapd_dl_reset(dl);
/* free history buffer list */
talloc_free(dl->tx_hist);
+ dl->tx_hist = NULL;
}
/*! \brief Set the \ref lapdm_mode of a LAPDm entity */
@@ -379,8 +378,8 @@ static int mdl_error(uint8_t cause, struct lapd_msg_ctx *lctx)
struct lapd_datalink *dl = lctx->dl;
struct osmo_dlsap_prim dp;
- LOGP(DLLAPD, LOGL_NOTICE, "sending MDL-ERROR-IND cause %d\n",
- cause);
+ LOGP(DLLAPD, LOGL_NOTICE, "sending MDL-ERROR-IND cause %s\n",
+ rsl_rlm_cause_name(cause));
osmo_prim_init(&dp.oph, 0, PRIM_MDL_ERROR, PRIM_OP_INDICATION, NULL);
dp.u.error_ind.cause = cause;
return dl->send_dlsap(&dp, lctx);
@@ -796,7 +795,7 @@ static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx)
dl->seq_err_cond = 0;
/* G.2.2 Wrong value of the C/R bit */
if (lctx->cr == dl->cr.rem2loc.resp) {
- LOGP(DLLAPD, LOGL_NOTICE, "SABM response error\n");
+ LOGP(DLLAPD, LOGL_ERROR, "SABM response error\n");
msgb_free(msg);
mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
return -EINVAL;
@@ -806,7 +805,7 @@ static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx)
* set, AN MDL-ERROR-INDICATION is sent to MM.
*/
if (lctx->more || length > lctx->n201) {
- LOGP(DLLAPD, LOGL_NOTICE, "SABM too large error\n");
+ LOGP(DLLAPD, LOGL_ERROR, "SABM too large error\n");
msgb_free(msg);
mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
return -EIO;
@@ -820,18 +819,34 @@ static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx)
"frame established state\n");
/* If link is lost on the remote side, we start over
* and send DL-ESTABLISH indication again. */
- if (dl->v_send != dl->v_recv) {
- LOGP(DLLAPD, LOGL_INFO, "Remote reestablish\n");
- mdl_error(MDL_CAUSE_SABM_MF, lctx);
+ /* Additionally, continue in case of content resoltion
+ * (GSM network). This happens, if the mobile has not
+ * yet received UA or another mobile (collision) tries
+ * to establish connection. The mobile must receive
+ * UA again. */
+ /* 5.4.2.1 */
+ if (!length) {
+ /* If no content resolution, this is a
+ * re-establishment. */
+ LOGP(DLLAPD, LOGL_INFO,
+ "Remote reestablish\n");
break;
}
+ if (!dl->cont_res) {
+ LOGP(DLLAPD, LOGL_INFO, "SABM command not "
+ "allowed in state %s\n",
+ lapd_state_names[dl->state]);
+ mdl_error(MDL_CAUSE_SABM_MF, lctx);
+ msgb_free(msg);
+ return 0;
+ }
/* Ignore SABM if content differs from first SABM. */
- if (dl->mode == LAPD_MODE_NETWORK && length
- && dl->cont_res) {
+ if (dl->mode == LAPD_MODE_NETWORK && length) {
#ifdef TEST_CONTENT_RESOLUTION_NETWORK
dl->cont_res->data[0] ^= 0x01;
#endif
- if (memcmp(dl->cont_res, msg->data, length)) {
+ if (memcmp(dl->cont_res->data, msg->data,
+ length)) {
LOGP(DLLAPD, LOGL_INFO, "Another SABM "
"with diffrent content - "
"ignoring!\n");
@@ -857,7 +872,8 @@ static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx)
/* check for contention resoultion */
if (dl->tx_hist[0].msg && dl->tx_hist[0].msg->len) {
LOGP(DLLAPD, LOGL_NOTICE, "SABM not allowed "
- "during contention resolution\n");
+ "during contention resolution (state %s)\n",
+ lapd_state_names[dl->state]);
mdl_error(MDL_CAUSE_SABM_INFO_NOTALL, lctx);
}
lapd_send_ua(lctx, length, msg->l3h);
@@ -892,6 +908,7 @@ static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx)
msgb_free(msg);
} else {
/* 5.4.1.4 Contention resolution establishment */
+ msgb_trim(msg, length);
rc = send_dl_l3(prim, op, lctx, msg);
}
break;
@@ -900,7 +917,7 @@ static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx)
lapd_state_names[dl->state]);
/* G.2.2 Wrong value of the C/R bit */
if (lctx->cr == dl->cr.rem2loc.cmd) {
- LOGP(DLLAPD, LOGL_NOTICE, "DM command error\n");
+ LOGP(DLLAPD, LOGL_ERROR, "DM command error\n");
msgb_free(msg);
mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
return -EINVAL;
@@ -981,7 +998,7 @@ static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx)
LOGP(DLLAPD, LOGL_INFO, "UI received\n");
/* G.2.2 Wrong value of the C/R bit */
if (lctx->cr == dl->cr.rem2loc.resp) {
- LOGP(DLLAPD, LOGL_NOTICE, "UI indicates response "
+ LOGP(DLLAPD, LOGL_ERROR, "UI indicates response "
"error\n");
msgb_free(msg);
mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
@@ -992,7 +1009,7 @@ static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx)
* set, AN MDL-ERROR-INDICATION is sent to MM.
*/
if (length > lctx->n201 || lctx->more) {
- LOGP(DLLAPD, LOGL_NOTICE, "UI too large error "
+ LOGP(DLLAPD, LOGL_ERROR, "UI too large error "
"(%d > N201(%d) or M=%d)\n", length,
lctx->n201, lctx->more);
msgb_free(msg);
@@ -1009,6 +1026,7 @@ static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx)
msgb_free(msg);
return 0;
}
+ msgb_trim(msg, length);
rc = send_dl_l3(PRIM_DL_UNIT_DATA, PRIM_OP_INDICATION, lctx,
msg);
break;
@@ -1025,7 +1043,7 @@ static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx)
dl->seq_err_cond = 0;
/* G.2.2 Wrong value of the C/R bit */
if (lctx->cr == dl->cr.rem2loc.resp) {
- LOGP(DLLAPD, LOGL_NOTICE, "DISC response error\n");
+ LOGP(DLLAPD, LOGL_ERROR, "DISC response error\n");
msgb_free(msg);
mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
return -EINVAL;
@@ -1036,7 +1054,7 @@ static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx)
* primitive with cause "U frame with incorrect
* parameters" is sent to the mobile management entity.
*/
- LOGP(DLLAPD, LOGL_NOTICE,
+ LOGP(DLLAPD, LOGL_ERROR,
"U frame iwth incorrect parameters ");
msgb_free(msg);
mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
@@ -1088,7 +1106,7 @@ static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx)
lapd_state_names[dl->state]);
/* G.2.2 Wrong value of the C/R bit */
if (lctx->cr == dl->cr.rem2loc.cmd) {
- LOGP(DLLAPD, LOGL_NOTICE, "UA indicates command "
+ LOGP(DLLAPD, LOGL_ERROR, "UA indicates command "
"error\n");
msgb_free(msg);
mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
@@ -1099,7 +1117,7 @@ static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx)
* set, AN MDL-ERROR-INDICATION is sent to MM.
*/
if (lctx->more || length > lctx->n201) {
- LOGP(DLLAPD, LOGL_NOTICE, "UA too large error\n");
+ LOGP(DLLAPD, LOGL_ERROR, "UA too large error\n");
msgb_free(msg);
mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
return -EIO;
@@ -1206,7 +1224,7 @@ static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
* with the M bit set to "1", an MDL-ERROR-INDICATION
* primitive with cause "S frame with incorrect
* parameters" is sent to the mobile management entity. */
- LOGP(DLLAPD, LOGL_NOTICE,
+ LOGP(DLLAPD, LOGL_ERROR,
"S frame with incorrect parameters\n");
msgb_free(msg);
mdl_error(MDL_CAUSE_SFRM_INC_PARAM, lctx);
@@ -1425,7 +1443,7 @@ static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
break;
default:
/* G.3.1 */
- LOGP(DLLAPD, LOGL_NOTICE, "Supervisory frame not allowed.\n");
+ LOGP(DLLAPD, LOGL_ERROR, "Supervisory frame not allowed.\n");
msgb_free(msg);
mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
return -EINVAL;
@@ -1443,12 +1461,12 @@ static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
int length = lctx->length;
int rc;
- LOGP(DLLAPD, LOGL_INFO, "I received in state %s\n",
- lapd_state_names[dl->state]);
+ LOGP(DLLAPD, LOGL_INFO, "I received in state %s on SAPI(%u)\n",
+ lapd_state_names[dl->state], lctx->sapi);
/* G.2.2 Wrong value of the C/R bit */
if (lctx->cr == dl->cr.rem2loc.resp) {
- LOGP(DLLAPD, LOGL_NOTICE, "I frame response not allowed\n");
+ LOGP(DLLAPD, LOGL_ERROR, "I frame response not allowed\n");
msgb_free(msg);
mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
return -EINVAL;
@@ -1459,7 +1477,7 @@ static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
* to a numerical value L>N201 or L=0, an MDL-ERROR-INDICATION
* primitive with cause "I frame with incorrect length"
* is sent to the mobile management entity. */
- LOGP(DLLAPD, LOGL_NOTICE, "I frame length not allowed\n");
+ LOGP(DLLAPD, LOGL_ERROR, "I frame length not allowed\n");
msgb_free(msg);
mdl_error(MDL_CAUSE_IFRM_INC_LEN, lctx);
return -EIO;
@@ -1470,7 +1488,7 @@ static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
* cause "I frame with incorrect use of M bit" is sent to the
* mobile management entity. */
if (lctx->more && length < lctx->n201) {
- LOGP(DLLAPD, LOGL_NOTICE, "I frame with M bit too short\n");
+ LOGP(DLLAPD, LOGL_ERROR, "I frame with M bit too short\n");
msgb_free(msg);
mdl_error(MDL_CAUSE_IFRM_INC_MBITS, lctx);
return -EIO;
@@ -1538,8 +1556,7 @@ static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
if (!lctx->more && !dl->rcv_buffer) {
LOGP(DLLAPD, LOGL_INFO, "message in single I frame\n");
/* send a DATA INDICATION to L3 */
- msg->len = length;
- msg->tail = msg->data + length;
+ msgb_trim(msg, length);
rc = send_dl_l3(PRIM_DL_DATA, PRIM_OP_INDICATION, lctx,
msg);
} else {
@@ -1596,6 +1613,12 @@ static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
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) {
LOGP(DLLAPD, LOGL_INFO, "we are not busy and "
"have no pending data, send RR\n");
@@ -1686,10 +1709,8 @@ static int lapd_est_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
memcpy(&dl->lctx, lctx, sizeof(dl->lctx));
/* Discard partly received L3 message */
- if (dl->rcv_buffer) {
- msgb_free(dl->rcv_buffer);
- dl->rcv_buffer = NULL;
- }
+ msgb_free(dl->rcv_buffer);
+ dl->rcv_buffer = NULL;
/* assemble message */
memcpy(&nctx, &dl->lctx, sizeof(nctx));
@@ -1925,7 +1946,7 @@ static int lapd_susp_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
return send_dl_simple(PRIM_DL_SUSP, PRIM_OP_CONFIRM, &dl->lctx);
}
-/* requesst resume or reconnect of link */
+/* request, resume or reconnect of link */
static int lapd_res_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
{
struct lapd_datalink *dl = lctx->dl;
@@ -1942,20 +1963,22 @@ static int lapd_res_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
memcpy(&dl->lctx, lctx, sizeof(dl->lctx));
/* Replace message in the send-buffer (reconnect) */
- if (dl->send_buffer)
- msgb_free(dl->send_buffer);
+ msgb_free(dl->send_buffer);
+ dl->send_buffer = NULL;
+
dl->send_out = 0;
- if (msg && msg->len)
+ if (msg->len) {
/* Write data into the send buffer, to be sent first */
dl->send_buffer = msg;
- else
+ } else {
+ msgb_free(msg);
+ msg = NULL;
dl->send_buffer = NULL;
+ }
/* Discard partly received L3 message */
- if (dl->rcv_buffer) {
- msgb_free(dl->rcv_buffer);
- dl->rcv_buffer = NULL;
- }
+ msgb_free(dl->rcv_buffer);
+ dl->rcv_buffer = NULL;
/* Create new msgb (old one is now free) */
msg = lapd_msgb_alloc(0, "LAPD SABM");
@@ -2070,7 +2093,7 @@ static int lapd_rel_req_idle(struct osmo_dlsap_prim *dp,
}
/* statefull handling for DL SAP messages from L3 */
-static struct l2downstate {
+static const struct l2downstate {
uint32_t states;
int prim, op;
const char *name;
@@ -2167,3 +2190,4 @@ int lapd_recv_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
return rc;
}
+/*! @} */
diff --git a/src/shared/libosmocore/src/gsm/lapdm.c b/src/shared/libosmocore/src/gsm/lapdm.c
index 1c08113e..fa7769b2 100644
--- a/src/shared/libosmocore/src/gsm/lapdm.c
+++ b/src/shared/libosmocore/src/gsm/lapdm.c
@@ -111,9 +111,22 @@ enum lapdm_format {
LAPDm_FMT_B4,
};
+const struct value_string osmo_ph_prim_names[] = {
+ { PRIM_PH_DATA, "PH-DATA" },
+ { PRIM_PH_RACH, "PH-RANDOM_ACCESS" },
+ { PRIM_PH_CONN, "PH-CONNECT" },
+ { PRIM_PH_EMPTY_FRAME, "PH-EMPTY_FRAME" },
+ { PRIM_PH_RTS, "PH-RTS" },
+ { PRIM_MPH_INFO, "MPH-INFO" },
+ { PRIM_TCH, "TCH" },
+ { PRIM_TCH_RTS, "TCH-RTS" },
+ { 0, NULL }
+};
+
static int lapdm_send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg);
static int send_rslms_dlsap(struct osmo_dlsap_prim *dp,
struct lapd_msg_ctx *lctx);
+static int update_pending_frames(struct lapd_msg_ctx *lctx);
static void lapdm_dl_init(struct lapdm_datalink *dl,
struct lapdm_entity *entity, int t200)
@@ -124,6 +137,7 @@ static void lapdm_dl_init(struct lapdm_datalink *dl,
dl->dl.reestablish = 0; /* GSM uses no reestablish */
dl->dl.send_ph_data_req = lapdm_send_ph_data_req;
dl->dl.send_dlsap = send_rslms_dlsap;
+ dl->dl.update_pending_frames = update_pending_frames;
dl->dl.n200_est_rel = N200_EST_REL;
dl->dl.n200 = N200;
dl->dl.t203_sec = 0; dl->dl.t203_usec = 0;
@@ -181,7 +195,7 @@ void lapdm_channel_exit(struct lapdm_channel *lc)
lapdm_entity_exit(&lc->lapdm_dcch);
}
-static struct lapdm_datalink *datalink_for_sapi(struct lapdm_entity *le, uint8_t sapi)
+struct lapdm_datalink *lapdm_datalink_for_sapi(struct lapdm_entity *le, uint8_t sapi)
{
switch (sapi) {
case LAPDm_SAPI_NORMAL:
@@ -193,14 +207,6 @@ static struct lapdm_datalink *datalink_for_sapi(struct lapdm_entity *le, uint8_t
}
}
-/* remove the L2 header from a MSGB */
-static inline unsigned char *msgb_pull_l2h(struct msgb *msg)
-{
- unsigned char *ret = msgb_pull(msg, msg->l3h - msg->l2h);
- msg->l2h = NULL;
- return ret;
-}
-
/* Append padding (if required) */
static void lapdm_pad_msgb(struct msgb *msg, uint8_t n201)
{
@@ -407,6 +413,11 @@ static int send_rslms_dlsap(struct osmo_dlsap_prim *dp,
switch (OSMO_PRIM_HDR(&dp->oph)) {
case OSMO_PRIM(PRIM_DL_EST, PRIM_OP_INDICATION):
+ if (dp->oph.msg && dp->oph.msg->len == 0) {
+ /* omit L3 info by freeing message */
+ msgb_free(dp->oph.msg);
+ dp->oph.msg = NULL;
+ }
rll_msg = RSL_MT_EST_IND;
break;
case OSMO_PRIM(PRIM_DL_EST, PRIM_OP_CONFIRM):
@@ -491,6 +502,25 @@ static int lapdm_send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg)
23);
}
+static int update_pending_frames(struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+ struct msgb *msg;
+ int rc = -1;
+
+ llist_for_each_entry(msg, &dl->tx_queue, list) {
+ if (LAPDm_CTRL_is_I(msg->l2h[1])) {
+ msg->l2h[1] = LAPDm_CTRL_I(dl->v_recv, LAPDm_CTRL_I_Ns(msg->l2h[1]),
+ LAPDm_CTRL_PF_BIT(msg->l2h[1]));
+ rc = 0;
+ } else if (LAPDm_CTRL_is_S(msg->l2h[1])) {
+ LOGP(DLLAPD, LOGL_ERROR, "Supervisory frame in queue, this shouldn't happen\n");
+ }
+ }
+
+ return rc;
+}
+
/* 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)
@@ -546,7 +576,7 @@ static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le,
}
}
- mctx.dl = datalink_for_sapi(le, sapi);
+ mctx.dl = lapdm_datalink_for_sapi(le, sapi);
/* G.2.1 No action on frames containing an unallocated SAPI. */
if (!mctx.dl) {
LOGP(DLLAPD, LOGL_NOTICE, "Received frame for unsupported "
@@ -606,7 +636,7 @@ static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le,
lctx.length = n201;
lctx.more = 0;
msg->l3h = msg->l2h + 2;
- msgb_pull_l2h(msg);
+ msgb_pull_to_l3(msg);
} else {
/* length field */
if (!(msg->l2h[2] & LAPDm_EL)) {
@@ -624,7 +654,7 @@ static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le,
lctx.length = msg->l2h[2] >> 2;
lctx.more = !!(msg->l2h[2] & LAPDm_MORE);
msg->l3h = msg->l2h + 3;
- msgb_pull_l2h(msg);
+ msgb_pull_to_l3(msg);
}
/* store context for messages from lapd */
memcpy(&mctx.dl->mctx, &mctx, sizeof(mctx.dl->mctx));
@@ -639,7 +669,7 @@ static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le,
/* directly pass up to layer3 */
LOGP(DLLAPD, LOGL_INFO, "fmt=Bbis UI\n");
msg->l3h = msg->l2h;
- msgb_pull_l2h(msg);
+ msgb_pull_to_l3(msg);
rc = send_rslms_rll_l3(RSL_MT_UNIT_DATA_IND, &mctx, msg);
break;
default:
@@ -657,6 +687,9 @@ static int l2_ph_rach_ind(struct lapdm_entity *le, uint8_t ra, uint32_t fn, uint
struct gsm_time gt;
struct msgb *msg = msgb_alloc_headroom(512, 64, "RSL CHAN RQD");
+ if (!msg)
+ return -ENOMEM;
+
msg->l2h = msgb_push(msg, sizeof(*ch));
ch = (struct abis_rsl_cchan_hdr *)msg->l2h;
rsl_init_cchan_hdr(ch, RSL_MT_CHAN_RQD);
@@ -687,7 +720,8 @@ 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);
- return -ENODEV;
+ rc = -ENODEV;
+ goto free;
}
switch (oph->primitive) {
@@ -695,7 +729,8 @@ int lapdm_phsap_up(struct osmo_prim_hdr *oph, struct lapdm_entity *le)
if (oph->operation != PRIM_OP_INDICATION) {
LOGP(DLLAPD, LOGL_ERROR, "PH_DATA is not INDICATION %u\n",
oph->operation);
- return -ENODEV;
+ rc = -ENODEV;
+ goto free;
}
rc = l2_ph_data_ind(oph->msg, le, pp->u.data.chan_nr,
pp->u.data.link_id);
@@ -704,7 +739,8 @@ int lapdm_phsap_up(struct osmo_prim_hdr *oph, struct lapdm_entity *le)
if (oph->operation != PRIM_OP_INDICATION) {
LOGP(DLLAPD, LOGL_ERROR, "PH_RTS is not INDICATION %u\n",
oph->operation);
- return -ENODEV;
+ rc = -ENODEV;
+ goto free;
}
rc = l2_ph_data_conf(oph->msg, le);
break;
@@ -718,12 +754,22 @@ int lapdm_phsap_up(struct osmo_prim_hdr *oph, struct lapdm_entity *le)
rc = l2_ph_chan_conf(oph->msg, le, pp->u.rach_ind.fn);
break;
default:
- return -EIO;
+ rc = -EIO;
+ goto free;
}
break;
+ default:
+ LOGP(DLLAPD, LOGL_ERROR, "Unknown primitive %u\n",
+ oph->primitive);
+ rc = -EINVAL;
+ goto free;
}
return rc;
+
+free:
+ msgb_free(oph->msg);
+ return rc;
}
@@ -789,9 +835,8 @@ static int rslms_rx_rll_est_req(struct msgb *msg, struct lapdm_datalink *dl)
}
/* Remove RLL header from msgb and set length to L3-info */
- msgb_pull_l2h(msg);
- msg->len = length;
- msg->tail = msg->l3h + length;
+ msgb_pull_to_l3(msg);
+ msgb_trim(msg, length);
/* prepare prim */
osmo_prim_init(&dp.oph, 0, PRIM_DL_EST, PRIM_OP_REQUEST, msg);
@@ -804,10 +849,10 @@ static int rslms_rx_rll_est_req(struct msgb *msg, struct lapdm_datalink *dl)
static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl)
{
struct lapdm_entity *le = dl->entity;
- int ui_bts = (le->mode == LAPDM_MODE_BTS);
struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
uint8_t chan_nr = rllh->chan_nr;
uint8_t link_id = rllh->link_id;
+ int ui_bts = (le->mode == LAPDM_MODE_BTS && (link_id & 0x40));
uint8_t sapi = link_id & 7;
struct tlv_parsed tv;
int length;
@@ -831,9 +876,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 if the layer3 message length exceeds N201 */
- if (length + 4 + !ui_bts > 23) {
+ if (length + ((link_id & 0x40) ? 4 : 2) + !ui_bts > 23) {
LOGP(DLLAPD, LOGL_ERROR, "frame too large: %d > N201(%d) "
- "(discarding)\n", length, 18 + ui_bts);
+ "(discarding)\n", length,
+ ((link_id & 0x40) ? 18 : 20) + ui_bts);
msgb_free(msg);
return -EIO;
}
@@ -842,18 +888,20 @@ static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl)
le->tx_power, le->ta);
/* Remove RLL header from msgb and set length to L3-info */
- msgb_pull_l2h(msg);
- msg->len = length;
- msg->tail = msg->l3h + length;
+ msgb_pull_to_l3(msg);
+ msgb_trim(msg, length);
/* Push L1 + LAPDm header on msgb */
- msg->l2h = msgb_push(msg, 4 + !ui_bts);
- msg->l2h[0] = le->tx_power;
- msg->l2h[1] = le->ta;
- msg->l2h[2] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, dl->dl.cr.loc2rem.cmd);
- msg->l2h[3] = LAPDm_CTRL_U(LAPDm_U_UI, 0);
+ msg->l2h = 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[4] = LAPDm_LEN(length);
+ msg->l2h[2] = LAPDm_LEN(length);
+ if (link_id & 0x40) {
+ msg->l2h = msgb_push(msg, 2);
+ msg->l2h[0] = le->tx_power;
+ msg->l2h[1] = le->ta;
+ }
/* Tramsmit */
return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, 23);
@@ -878,9 +926,8 @@ static int rslms_rx_rll_data_req(struct msgb *msg, struct lapdm_datalink *dl)
length = TLVP_LEN(&tv, RSL_IE_L3_INFO);
/* Remove RLL header from msgb and set length to L3-info */
- msgb_pull_l2h(msg);
- msg->len = length;
- msg->tail = msg->l3h + length;
+ msgb_pull_to_l3(msg);
+ msgb_trim(msg, length);
/* prepare prim */
osmo_prim_init(&dp.oph, 0, PRIM_DL_DATA, PRIM_OP_REQUEST, msg);
@@ -935,9 +982,8 @@ static int rslms_rx_rll_res_req(struct msgb *msg, struct lapdm_datalink *dl)
length = TLVP_LEN(&tv, RSL_IE_L3_INFO);
/* Remove RLL header from msgb and set length to L3-info */
- msgb_pull_l2h(msg);
- msg->len = length;
- msg->tail = msg->l3h + length;
+ msgb_pull_to_l3(msg);
+ msgb_trim(msg, length);
/* prepare prim */
osmo_prim_init(&dp.oph, 0, (msg_type == RSL_MT_RES_REQ) ? PRIM_DL_RES
@@ -959,12 +1005,11 @@ static int rslms_rx_rll_rel_req(struct msgb *msg, struct lapdm_datalink *dl)
mode = rllh->data[1] & 1;
/* Pull rllh */
- msgb_pull_l2h(msg);
+ msgb_pull_to_l3(msg);
/* 04.06 3.8.3: No information field is permitted with the DISC
* command. */
- msg->len = 0;
- msg->tail = msg->l3h = msg->data;
+ msgb_trim(msg, 0);
/* prepare prim */
osmo_prim_init(&dp.oph, 0, PRIM_DL_REL, PRIM_OP_REQUEST, msg);
@@ -1023,7 +1068,7 @@ static int l2_ph_chan_conf(struct msgb *msg, struct lapdm_entity *le, uint32_t f
gsm_fn2gsmtime(&tm, frame_nr);
- msgb_pull_l2h(msg);
+ msgb_pull_to_l3(msg);
msg->l2h = msgb_push(msg, sizeof(*ch) + sizeof(*ref));
ch = (struct abis_rsl_cchan_hdr *)msg->l2h;
rsl_init_cchan_hdr(ch, RSL_MT_CHAN_CONF);
@@ -1059,18 +1104,50 @@ static int rslms_rx_rll(struct msgb *msg, struct lapdm_channel *lc)
else
le = &lc->lapdm_dcch;
- /* G.2.1 No action schall be taken on frames containing an unallocated
+ /* 4.1.1.5 / 4.1.1.6 / 4.1.1.7 all only exist on MS side, not
+ * BTS side */
+ if (le->mode == LAPDM_MODE_BTS) {
+ switch (msg_type) {
+ case RSL_MT_SUSP_REQ:
+ case RSL_MT_RES_REQ:
+ case RSL_MT_RECON_REQ:
+ LOGP(DLLAPD, LOGL_NOTICE, "(%p) RLL Message '%s' unsupported in BTS side LAPDm\n",
+ lc->name, rsl_msg_name(msg_type));
+ msgb_free(msg);
+ return -EINVAL;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* G.2.1 No action shall be taken on frames containing an unallocated
* SAPI.
*/
- dl = datalink_for_sapi(le, sapi);
+ dl = lapdm_datalink_for_sapi(le, sapi);
if (!dl) {
LOGP(DLLAPD, LOGL_ERROR, "No instance for SAPI %d!\n", sapi);
msgb_free(msg);
return -EINVAL;
}
- LOGP(DLLAPD, LOGL_INFO, "(%p) RLL Message '%s' received. (sapi %d)\n",
- lc->name, rsl_msg_name(msg_type), sapi);
+ switch (msg_type) {
+ case RSL_MT_DATA_REQ:
+ case RSL_MT_SUSP_REQ:
+ case RSL_MT_REL_REQ:
+ /* This is triggered in abnormal error conditions where
+ * set_lapdm_context() was not called for the channel earlier. */
+ if (!dl->dl.lctx.dl) {
+ LOGP(DLLAPD, LOGL_NOTICE, "(%p) RLL Message '%s' received without LAPDm context. (sapi %d)\n",
+ lc->name, rsl_msg_name(msg_type), sapi);
+ msgb_free(msg);
+ return -EINVAL;
+ }
+ break;
+ default:
+ LOGP(DLLAPD, LOGL_INFO, "(%p) RLL Message '%s' received. (sapi %d)\n",
+ lc->name, rsl_msg_name(msg_type), sapi);
+ }
switch (msg_type) {
case RSL_MT_UNIT_DATA_REQ:
diff --git a/src/shared/libosmocore/src/gsm/libosmogsm.map b/src/shared/libosmocore/src/gsm/libosmogsm.map
index 33738881..3d1413dd 100644
--- a/src/shared/libosmocore/src/gsm/libosmogsm.map
+++ b/src/shared/libosmocore/src/gsm/libosmogsm.map
@@ -4,45 +4,116 @@ global:
abis_nm_adm_state_names;
abis_nm_att_settable;
abis_nm_avail_name;
+abis_nm_fail_evt_rep;
abis_nm_chcomb4pchan;
abis_nm_debugp_foh;
abis_nm_event_type_name;
abis_nm_nack_cause_name;
abis_nm_nack_name;
abis_nm_att_tlvdef;
+abis_nm_att_tlvdef_ipa;
+abis_nm_osmo_att_tlvdef;
+abis_nm_msg_disc_names;
abis_nm_obj_class_names;
abis_nm_opstate_name;
abis_nm_nacks;
+abis_nm_t200_ms;
abis_nm_no_ack_nack;
abis_nm_pchan4chcomb;
abis_nm_reports;
abis_nm_severity_name;
abis_nm_sw_load_msgs;
abis_nm_test_name;
+abis_nm_osmo_magic;
+abis_nm_ipa_magic;
+abis_nm_event_cause_names;
+abis_nm_pcause_type_names;
osmo_sitype_strs;
-
+osmo_c4;
+bitvec_add_range1024;
comp128;
dbm2rxlev;
+osmo_earfcn_add;
+osmo_earfcn_del;
+osmo_earfcn_bit_size;
+osmo_earfcn_init;
+
gprs_cipher_gen_input_i;
gprs_cipher_gen_input_ui;
gprs_cipher_load;
gprs_cipher_register;
gprs_cipher_run;
+gprs_cipher_names;
gprs_cipher_supported;
+gprs_cipher_key_length;
gprs_tlli_type;
gprs_tmsi2tlli;
+gprs_ms_net_cap_gea_supported;
+gprs_msgt_gmm_names;
+
+egprs_get_cps;
+
+gsm48_gmm_cause_names;
+gsm48_gsm_cause_names;
+gprs_att_t_strs;
+gprs_upd_t_strs;
+gprs_det_t_mo_strs;
+gprs_det_t_mt_strs;
+gprs_service_t_strs;
+
+gsm0341_build_msg;
gsm0480_create_notifySS;
gsm0480_create_unstructuredSS_Notify;
gsm0480_create_ussd_resp;
+gsm0480_create_ussd_notify;
+gsm0480_create_ussd_release_complete;
gsm0480_decode_ussd_request;
+gsm0480_decode_ss_request;
gsm0480_wrap_facility;
gsm0480_wrap_invoke;
gsm0502_calc_paging_group;
+gsm0503_xcch;
+gsm0503_rach;
+gsm0503_sch;
+gsm0503_cs2;
+gsm0503_cs3;
+gsm0503_tch_fr;
+gsm0503_tch_hr;
+gsm0503_tch_afs_12_2;
+gsm0503_tch_afs_10_2;
+gsm0503_tch_afs_7_95;
+gsm0503_tch_afs_7_4;
+gsm0503_tch_afs_6_7;
+gsm0503_tch_afs_5_9;
+gsm0503_tch_afs_5_15;
+gsm0503_tch_afs_4_75;
+gsm0503_tch_ahs_7_95;
+gsm0503_tch_ahs_7_4;
+gsm0503_tch_ahs_6_7;
+gsm0503_tch_ahs_5_9;
+gsm0503_tch_ahs_5_15;
+gsm0503_tch_ahs_4_75;
+gsm0503_mcs1_dl_hdr;
+gsm0503_mcs1_ul_hdr;
+gsm0503_mcs1;
+gsm0503_mcs2;
+gsm0503_mcs3;
+gsm0503_mcs4;
+gsm0503_mcs5_dl_hdr;
+gsm0503_mcs5_ul_hdr;
+gsm0503_mcs5;
+gsm0503_mcs6;
+gsm0503_mcs7_dl_hdr;
+gsm0503_mcs7_ul_hdr;
+gsm0503_mcs7;
+gsm0503_mcs8;
+gsm0503_mcs9;
+
gsm0808_att_tlvdef;
gsm0808_bssap_name;
gsm0808_bssmap_name;
@@ -60,6 +131,8 @@ gsm0808_create_reset;
gsm0808_create_sapi_reject;
gsm0808_prepend_dtap_header;
+gsm0858_rsl_ul_meas_enc;
+
gsm338_get_sms_alphabet;
gsm340_gen_oa;
@@ -85,8 +158,10 @@ gsm411_rp_cause_strs;
gsm48_att_tlvdef;
gsm48_cc_msg_name;
+gsm48_rr_msg_name;
gsm48_cc_state_name;
gsm48_construct_ra;
+gsm48_hdr_gmm_cipherable;
gsm48_decode_bcd_number;
gsm48_decode_bearer_cap;
gsm48_decode_called;
@@ -130,13 +205,29 @@ gsm48_mm_att_tlvdef;
gsm48_number_of_paging_subchannels;
gsm48_parse_ra;
gsm48_rr_att_tlvdef;
+gsm48_set_dtx;
+gsm48_dtx_mode;
+gsm48_mi_type_name;
+gsm48_mcc_mnc_to_bcd;
+gsm48_mcc_mnc_from_bcd;
+gsm48_chan_mode_names;
+gsm_chan_t_names;
gsm_7bit_decode;
-gsm_7bit_decode_hdr;
+gsm_7bit_decode_ussd;
gsm_7bit_encode;
+gsm_7bit_encode_ussd;
+gsm_7bit_encode_oct;
+
+gsm_7bit_decode_n;
+gsm_7bit_decode_n_ussd;
+gsm_7bit_decode_n_hdr;
+gsm_7bit_encode_n;
+gsm_7bit_encode_n_ussd;
gsm_arfcn2band;
gsm_arfcn2freq10;
+gsm_freq102arfcn;
gsm_band_name;
gsm_band_parse;
gsm_fn2gsmtime;
@@ -163,6 +254,7 @@ lapdm_channel_set_flags;
lapdm_channel_set_l1;
lapdm_channel_set_l3;
lapdm_channel_set_mode;
+lapdm_datalink_for_sapi;
lapdm_entity_exit;
lapdm_entity_init;
lapdm_entity_reset;
@@ -172,6 +264,8 @@ lapdm_phsap_dequeue_prim;
lapdm_phsap_up;
lapdm_rslms_recvmsg;
+osmo_ph_prim_names;
+
milenage_auts;
milenage_check;
milenage_f1;
@@ -191,6 +285,7 @@ osmo_auth_alg_name;
osmo_auth_alg_parse;
osmo_auth_gen_vec;
osmo_auth_gen_vec_auts;
+osmo_auth_3g_from_2g;
osmo_auth_load;
osmo_auth_register;
osmo_auth_supported;
@@ -201,6 +296,7 @@ osmo_sitype2rsl;
rr_cause_name;
rsl_att_tlvdef;
+rsl_ipac_eie_tlvdef;
rsl_ccch_conf_to_bs_cc_chans;
rsl_ccch_conf_to_bs_ccch_sdcch_comb;
rsl_chan_nr_str;
@@ -211,10 +307,12 @@ rsl_init_cchan_hdr;
rsl_init_rll_hdr;
rsl_ipac_msg_name;
rsl_msg_name;
+rsl_or_ipac_msg_name;
rsl_rll_push_hdr;
rsl_rll_push_l3;
rsl_rll_simple;
rsl_rlm_cause_name;
+rsl_act_type_names;
rxlev2dbm;
rxlev_stat_dump;
@@ -229,8 +327,44 @@ tlv_parse_one;
tvlv_att_def;
vtvlv_gan_att_def;
+osmo_tlvp_copy;
+osmo_tlvp_merge;
+osmo_shift_v_fixed;
+osmo_match_shift_tv_fixed;
+osmo_shift_tlv;
+osmo_match_shift_tlv;
+osmo_shift_lv;
+
gan_msgt_vals;
gan_pdisc_vals;
+ipa_ccm_rcvmsg_base;
+ipa_ccm_rcvmsg_bts_base;
+ipa_ccm_send_id_ack;
+ipa_ccm_send_id_req;
+ipa_ccm_send_pong;
+ipa_ccm_tlv_to_unitdata;
+ipa_ccm_idtag_name;
+ipa_ccm_idtag_parse;
+ipa_ccm_idtag_parse_off;
+ipa_msg_alloc;
+ipa_msg_recv;
+ipa_msg_recv_buffered;
+ipa_parse_unitid;
+ipa_prepend_header;
+ipa_prepend_header_ext;
+ipa_send;
+
+osmo_apn_qualify;
+osmo_apn_qualify_from_imsi;
+osmo_apn_to_str;
+osmo_apn_from_str;
+
+osmo_gsup_encode;
+osmo_gsup_decode;
+
+osmo_oap_encode;
+osmo_oap_decode;
+
local: *;
};
diff --git a/src/shared/libosmocore/src/gsm/milenage/aes.h b/src/shared/libosmocore/src/gsm/milenage/aes.h
index ba384a9d..7e97f618 100644
--- a/src/shared/libosmocore/src/gsm/milenage/aes.h
+++ b/src/shared/libosmocore/src/gsm/milenage/aes.h
@@ -12,8 +12,7 @@
* See README and COPYING for more details.
*/
-#ifndef AES_H
-#define AES_H
+#pragma once
#define AES_BLOCK_SIZE 16
@@ -23,5 +22,3 @@ void aes_encrypt_deinit(void *ctx);
void * aes_decrypt_init(const u8 *key, size_t len);
void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
void aes_decrypt_deinit(void *ctx);
-
-#endif /* AES_H */
diff --git a/src/shared/libosmocore/src/gsm/milenage/aes_i.h b/src/shared/libosmocore/src/gsm/milenage/aes_i.h
index 6b40bc78..5d89abce 100644
--- a/src/shared/libosmocore/src/gsm/milenage/aes_i.h
+++ b/src/shared/libosmocore/src/gsm/milenage/aes_i.h
@@ -12,8 +12,7 @@
* See README and COPYING for more details.
*/
-#ifndef AES_I_H
-#define AES_I_H
+#pragma once
#include "aes.h"
@@ -67,7 +66,7 @@ extern const u8 rcons[10];
#else /* AES_SMALL_TABLES */
-#define RCON(i) (rcons[(i)] << 24)
+#define RCON(i) ((u32)rcons[(i)] << 24)
static inline u32 rotr(u32 val, int bits)
{
@@ -118,5 +117,3 @@ static inline u32 rotr(u32 val, int bits)
#define AES_PRIV_SIZE (4 * 44)
void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]);
-
-#endif /* AES_I_H */
diff --git a/src/shared/libosmocore/src/gsm/milenage/aes_wrap.h b/src/shared/libosmocore/src/gsm/milenage/aes_wrap.h
index 4b1c7b08..afa1451e 100644
--- a/src/shared/libosmocore/src/gsm/milenage/aes_wrap.h
+++ b/src/shared/libosmocore/src/gsm/milenage/aes_wrap.h
@@ -19,8 +19,7 @@
* See README and COPYING for more details.
*/
-#ifndef AES_WRAP_H
-#define AES_WRAP_H
+#pragma once
int __must_check aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher);
int __must_check aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain);
@@ -44,5 +43,3 @@ int __must_check aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data,
size_t data_len);
int __must_check aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data,
size_t data_len);
-
-#endif /* AES_WRAP_H */
diff --git a/src/shared/libosmocore/src/gsm/milenage/milenage.h b/src/shared/libosmocore/src/gsm/milenage/milenage.h
index a91e946a..6fb779c6 100644
--- a/src/shared/libosmocore/src/gsm/milenage/milenage.h
+++ b/src/shared/libosmocore/src/gsm/milenage/milenage.h
@@ -12,8 +12,7 @@
* See README and COPYING for more details.
*/
-#ifndef MILENAGE_H
-#define MILENAGE_H
+#pragma once
void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k,
const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik,
@@ -31,5 +30,3 @@ int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand,
u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar);
int milenage_opc_gen(u8 *opc, const u8 *k, const u8 *op);
-
-#endif /* MILENAGE_H */
diff --git a/src/shared/libosmocore/src/gsm/oap.c b/src/shared/libosmocore/src/gsm/oap.c
new file mode 100644
index 00000000..1494a6a6
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/oap.c
@@ -0,0 +1,184 @@
+/* Osmocom Authentication Protocol message encoder/decoder */
+
+/* (C) 2015-2016 by sysmocom s.f.m.c. GmbH
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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 <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/oap.h>
+
+#include <stdint.h>
+
+/*! \brief Decode OAP message data.
+ * \param[out] oap_msg Parsed data is written to this instance.
+ * \param[in] data Pointer to the data buffer containing the OAP message.
+ * \param[in] data_len Length of the OAP message data.
+ * \returns 0 on success, a negative cause value on failure.
+ */
+int osmo_oap_decode(struct osmo_oap_message *oap_msg,
+ const uint8_t *const_data, size_t data_len)
+{
+ int rc;
+ uint8_t tag;
+ /* the shift/match functions expect non-const pointers, but we'll
+ * either copy the data or cast pointers back to const before returning
+ * them
+ */
+ uint8_t *data = (uint8_t *)const_data;
+ uint8_t *value;
+ size_t value_len;
+
+ memset(oap_msg, 0, sizeof(*oap_msg));
+
+ /* message type */
+ rc = osmo_shift_v_fixed(&data, &data_len, 1, &value);
+ if (rc < 0)
+ return -GMM_CAUSE_INV_MAND_INFO;
+ oap_msg->message_type = osmo_decode_big_endian(value, 1);
+
+ /* specific parts */
+ while (data_len > 0) {
+ enum osmo_oap_iei iei;
+
+ rc = osmo_shift_tlv(&data, &data_len, &tag, &value, &value_len);
+ if (rc < 0)
+ return -GMM_CAUSE_PROTO_ERR_UNSPEC;
+
+ iei = tag;
+
+ switch (iei) {
+ case OAP_CLIENT_ID_IE:
+ if (value_len != 2) {
+ LOGP(DLOAP, LOGL_NOTICE,
+ "OAP IE type client ID (%d) should be 2 octets, but has %d\n",
+ (int)iei, (int)value_len);
+ return -GMM_CAUSE_PROTO_ERR_UNSPEC;
+ }
+
+ oap_msg->client_id = osmo_decode_big_endian(value, value_len);
+
+ if (oap_msg->client_id == 0) {
+ LOGP(DLOAP, LOGL_NOTICE,
+ "OAP IE type client ID (%d): client ID must be nonzero.\n",
+ (int)iei);
+ return -GMM_CAUSE_PROTO_ERR_UNSPEC;
+ }
+ break;
+
+ case OAP_AUTN_IE:
+ if (value_len != sizeof(oap_msg->autn)) {
+ LOGP(DLOAP, LOGL_NOTICE,
+ "OAP IE type AUTN (%d) should be %d octets, but has %d\n",
+ (int)iei, (int)sizeof(oap_msg->autn), (int)value_len);
+ return -GMM_CAUSE_PROTO_ERR_UNSPEC;
+ }
+ memcpy(oap_msg->autn, value, value_len);
+ oap_msg->autn_present = value_len ? 1 : 0;
+ break;
+
+ case OAP_RAND_IE:
+ if (value_len != sizeof(oap_msg->rand)) {
+ LOGP(DLOAP, LOGL_NOTICE,
+ "OAP IE type RAND (%d) should be %d octets, but has %d\n",
+ (int)iei, (int)sizeof(oap_msg->rand), (int)value_len);
+ return -GMM_CAUSE_PROTO_ERR_UNSPEC;
+ }
+ memcpy(oap_msg->rand, value, value_len);
+ oap_msg->rand_present = value_len ? 1 : 0;
+ break;
+
+ case OAP_XRES_IE:
+ if (value_len != sizeof(oap_msg->xres)) {
+ LOGP(DLOAP, LOGL_NOTICE,
+ "OAP IE type XRES (%d) should be %d octets, but has %d\n",
+ (int)iei, (int)sizeof(oap_msg->xres), (int)value_len);
+ return -GMM_CAUSE_PROTO_ERR_UNSPEC;
+ }
+ memcpy(oap_msg->xres, value, value_len);
+ oap_msg->xres_present = value_len ? 1 : 0;
+ break;
+
+ case OAP_AUTS_IE:
+ if (value_len != sizeof(oap_msg->auts)) {
+ LOGP(DLOAP, LOGL_NOTICE,
+ "OAP IE type AUTS (%d) should be %d octets, but has %d\n",
+ (int)iei, (int)sizeof(oap_msg->auts), (int)value_len);
+ return -GMM_CAUSE_PROTO_ERR_UNSPEC;
+ }
+ memcpy(oap_msg->auts, value, value_len);
+ oap_msg->auts_present = value_len ? 1 : 0;
+ break;
+
+ case OAP_CAUSE_IE:
+ if (value_len > 1) {
+ LOGP(DLOAP, LOGL_ERROR,
+ "OAP cause may not exceed one octet, is %d", (int)value_len);
+ return -GMM_CAUSE_PROTO_ERR_UNSPEC;
+ }
+ oap_msg->cause = *value;
+ break;
+
+ default:
+ LOGP(DLOAP, LOGL_NOTICE,
+ "OAP IE type %d unknown\n", iei);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/*! \brief Compose OAP message data.
+ * \param[out] msg OAP message data is appended to this message buffer.
+ * \param[in] oap_msg Elements to encode in the message data.
+ */
+void osmo_oap_encode(struct msgb *msg, const struct osmo_oap_message *oap_msg)
+{
+ uint8_t u8;
+
+ /* generic part */
+ OSMO_ASSERT(oap_msg->message_type);
+ msgb_v_put(msg, (uint8_t)oap_msg->message_type);
+
+ /* specific parts */
+ if ((u8 = oap_msg->cause))
+ msgb_tlv_put(msg, OAP_CAUSE_IE, sizeof(u8), &u8);
+
+ if (oap_msg->client_id > 0)
+ msgb_tlv_put(msg, OAP_CLIENT_ID_IE, sizeof(oap_msg->client_id),
+ osmo_encode_big_endian(oap_msg->client_id,
+ sizeof(oap_msg->client_id)));
+
+ if (oap_msg->rand_present)
+ msgb_tlv_put(msg, OAP_RAND_IE, sizeof(oap_msg->rand), oap_msg->rand);
+
+ if (oap_msg->autn_present)
+ msgb_tlv_put(msg, OAP_AUTN_IE, sizeof(oap_msg->autn), oap_msg->autn);
+
+ if (oap_msg->auts_present)
+ msgb_tlv_put(msg, OAP_AUTS_IE, sizeof(oap_msg->auts), oap_msg->auts);
+
+ if (oap_msg->xres_present)
+ msgb_tlv_put(msg, OAP_XRES_IE, sizeof(oap_msg->xres), oap_msg->xres);
+
+ msg->l2h = msg->data;
+}
diff --git a/src/shared/libosmocore/src/gsm/rsl.c b/src/shared/libosmocore/src/gsm/rsl.c
index 5693b4f0..910e8481 100644
--- a/src/shared/libosmocore/src/gsm/rsl.c
+++ b/src/shared/libosmocore/src/gsm/rsl.c
@@ -382,6 +382,17 @@ const char *rsl_ipac_msg_name(uint8_t msg_type)
return get_value_string(rsl_ipac_msgt_names, msg_type);
}
+/*! \brief Get human-readable name of standard or ip.access RSL msg type.
+ * If msg_type is a standard RSL message type, return its human-readable name.
+ * Otherwise return rsl_ipac_msg_name(msg_type). */
+const char *rsl_or_ipac_msg_name(uint8_t msg_type)
+{
+ const char *str = get_value_string_or_null(rsl_msgt_names, msg_type);
+ if (str)
+ return str;
+ return rsl_ipac_msg_name(msg_type);
+}
+
static const struct value_string rsl_rlm_cause_strs[] = {
{ RLL_CAUSE_T200_EXPIRED, "Timer T200 expired (N200+1) times" },
{ RLL_CAUSE_REEST_REQ, "Re-establishment request" },
@@ -394,7 +405,7 @@ static const struct value_string rsl_rlm_cause_strs[] = {
{ RLL_CAUSE_SFRM_INC_PARAM, "S-Frame with incorrect parameters" },
{ RLL_CAUSE_IFRM_INC_MBITS, "I-Frame with incorrect use of M bit" },
{ RLL_CAUSE_IFRM_INC_LEN, "I-Frame with incorrect length" },
- { RLL_CAUSE_FRM_UNIMPL, "Fraeme not implemented" },
+ { RLL_CAUSE_FRM_UNIMPL, "Frame not implemented" },
{ RLL_CAUSE_SABM_MF, "SABM command, multiple frame established state" },
{ RLL_CAUSE_SABM_INFO_NOTALL, "SABM frame with information not allowed in this state" },
{ 0, NULL },
@@ -504,4 +515,43 @@ struct msgb *rsl_rll_simple(uint8_t msg_type, uint8_t chan_nr,
return msg;
}
+const struct tlv_definition rsl_ipac_eie_tlvdef = {
+ .def = {
+ [RSL_IPAC_EIE_RXLEV] = { TLV_TYPE_TV },
+ [RSL_IPAC_EIE_RXQUAL] = { TLV_TYPE_TV },
+ [RSL_IPAC_EIE_FREQ_ERR] = { TLV_TYPE_FIXED, 2 },
+ [RSL_IPAC_EIE_TIMING_ERR] = { TLV_TYPE_TV },
+ [RSL_IPAC_EIE_MEAS_AVG_CFG] = { TLV_TYPE_TLV },
+ [RSL_IPAC_EIE_BS_PWR_CTL] = { TLV_TYPE_FIXED, 3 },
+ [RSL_IPAC_EIE_MS_PWR_CTL] = { TLV_TYPE_FIXED, 3 },
+ [RSL_IPAC_EIE_HANDO_THRESH] = { TLV_TYPE_FIXED, 6 },
+ [RSL_IPAC_EIE_NCELL_DEFAULTS] = { TLV_TYPE_FIXED, 3 },
+ [RSL_IPAC_EIE_NCELL_LIST] = { TLV_TYPE_TLV },
+ [RSL_IPAC_EIE_PC_THRESH_COMP] = { TLV_TYPE_FIXED, 10 },
+ [RSL_IPAC_EIE_HO_THRESH_COMP] = { TLV_TYPE_FIXED, 10 },
+ [RSL_IPAC_EIE_HO_CAUSE] = { TLV_TYPE_TLV },
+ [RSL_IPAC_EIE_HO_CANDIDATES] = { TLV_TYPE_TLV },
+ [RSL_IPAC_EIE_NCELL_BA_CHG_LIST]= { TLV_TYPE_TLV },
+ [RSL_IPAC_EIE_NUM_OF_MS] = { TLV_TYPE_TV },
+ [RSL_IPAC_EIE_HO_CAND_EXT] = { TLV_TYPE_TLV },
+ [RSL_IPAC_EIE_NCELL_DEF_EXT] = { TLV_TYPE_TLV },
+ [RSL_IPAC_EIE_NCELL_LIST_EXT] = { TLV_TYPE_TLV },
+ [RSL_IPAC_EIE_MASTER_KEY] = { TLV_TYPE_TLV },
+ [RSL_IPAC_EIE_MASTER_SALT] = { TLV_TYPE_TLV },
+ },
+};
+
+const struct value_string rsl_act_type_names[] = {
+ { RSL_ACT_TYPE_INITIAL, "INITIAL" },
+ { RSL_ACT_TYPE_REACT, "REACT" },
+ { RSL_ACT_INTRA_IMM_ASS, "INTRA_IMM_ASS" },
+ { RSL_ACT_INTRA_NORM_ASS, "INTRA_NORM_ASS" },
+ { RSL_ACT_INTER_ASYNC, "INTER_ASYNC" },
+ { RSL_ACT_INTER_SYNC, "INTER_SYNC" },
+ { RSL_ACT_SECOND_ADD, "SECOND_ADD" },
+ { RSL_ACT_SECOND_MULTI, "SECOND_MULTI" },
+ { RSL_ACT_OSMO_PDCH, "OSMO_PDCH" },
+ { 0, NULL }
+};
+
/*! @} */
diff --git a/src/shared/libosmocore/src/gsm/sysinfo.c b/src/shared/libosmocore/src/gsm/sysinfo.c
index 1408f6bf..3ec54448 100644
--- a/src/shared/libosmocore/src/gsm/sysinfo.c
+++ b/src/shared/libosmocore/src/gsm/sysinfo.c
@@ -125,6 +125,80 @@ const struct value_string osmo_sitype_strs[_MAX_SYSINFO_TYPE] = {
{ 0, NULL }
};
+/*! \brief Add pair of arfcn and measurement bandwith value to earfcn struct
+ * \param[in,out] e earfcn struct
+ * \param[in] arfcn EARFCN value, 16 bits
+ * \param[in] meas_bw measurement bandwith value
+ * \returns 0 on success, error otherwise
+ */
+int osmo_earfcn_add(struct osmo_earfcn_si2q *e, uint16_t arfcn, uint8_t meas_bw)
+{
+ size_t i;
+ for (i = 0; i < e->length; i++) {
+ if (OSMO_EARFCN_INVALID == e->arfcn[i]) {
+ e->arfcn[i] = arfcn;
+ e->meas_bw[i] = meas_bw;
+ return 0;
+ }
+ }
+ return -ENOMEM;
+}
+
+/*! \brief Return number of bits necessary to represent earfcn struct as
+ * Repeated E-UTRAN Neighbour Cells IE from 3GPP TS 44.018 Table 10.5.2.33b.1
+ * \param[in,out] e earfcn struct
+ * \returns number of bits
+ */
+size_t osmo_earfcn_bit_size(const struct osmo_earfcn_si2q *e)
+{
+ /* 1 stop bit + 5 bits for THRESH_E-UTRAN_high */
+ size_t i, bits = 6;
+ for (i = 0; i < e->length; i++) {
+ if (e->arfcn[i] != OSMO_EARFCN_INVALID) {
+ bits += 17;
+ if (OSMO_EARFCN_MEAS_INVALID == e->meas_bw[i])
+ bits++;
+ else
+ bits += 4;
+ }
+ }
+ bits += (e->prio_valid) ? 4 : 1;
+ bits += (e->thresh_lo_valid) ? 6 : 1;
+ bits += (e->qrxlm_valid) ? 6 : 1;
+ return bits;
+}
+
+/*! \brief Delete arfcn (and corresponding measurement bandwith) from earfcn
+ * struct
+ * \param[in,out] e earfcn struct
+ * \param[in] arfcn EARFCN value, 16 bits
+ * \returns 0 on success, error otherwise
+ */
+int osmo_earfcn_del(struct osmo_earfcn_si2q *e, uint16_t arfcn)
+{
+ size_t i;
+ for (i = 0; i < e->length; i++) {
+ if (arfcn == e->arfcn[i]) {
+ e->arfcn[i] = OSMO_EARFCN_INVALID;
+ e->meas_bw[i] = OSMO_EARFCN_MEAS_INVALID;
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+/*! \brief Initialize earfcn struct
+ * \param[in,out] e earfcn struct
+ */
+void osmo_earfcn_init(struct osmo_earfcn_si2q *e)
+{
+ size_t i;
+ for (i = 0; i < e->length; i++) {
+ e->arfcn[i] = OSMO_EARFCN_INVALID;
+ e->meas_bw[i] = OSMO_EARFCN_MEAS_INVALID;
+ }
+}
+
uint8_t osmo_sitype2rsl(enum osmo_sysinfo_type si_type)
{
return sitype2rsl[si_type];
diff --git a/src/shared/libosmocore/src/gsm/tlv_parser.c b/src/shared/libosmocore/src/gsm/tlv_parser.c
index d18a6bfd..4cc43f67 100644
--- a/src/shared/libosmocore/src/gsm/tlv_parser.c
+++ b/src/shared/libosmocore/src/gsm/tlv_parser.c
@@ -1,12 +1,32 @@
+/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
#include <stdio.h>
#include <stdint.h>
+#include <errno.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/tlv.h>
/*! \addtogroup tlv
* @{
*/
-/*! \file tlv.c */
+/*! \file tlv_parser.c */
struct tlv_definition tvlv_att_def;
struct tlv_definition vtvlv_gan_att_def;
@@ -24,6 +44,65 @@ int tlv_dump(struct tlv_parsed *dec)
return 0;
}
+/*! \brief Copy \ref tlv_parsed using given talloc context
+ * \param[in] tp_orig Parsed TLV structure
+ * \param[in] ctx Talloc context for allocations
+ * \returns NULL on errors, \ref tlv_parsed pointer otherwise
+ */
+struct tlv_parsed *osmo_tlvp_copy(const struct tlv_parsed *tp_orig, void *ctx)
+{
+ struct tlv_parsed *tp_out;
+ size_t i, len;
+
+ tp_out = talloc_zero(ctx, struct tlv_parsed);
+ if (!tp_out)
+ return NULL;
+
+ /* if the original is NULL, return empty tlvp */
+ if (!tp_orig)
+ return tp_out;
+
+ for (i = 0; i < ARRAY_SIZE(tp_orig->lv); i++) {
+ len = tp_orig->lv[i].len;
+ tp_out->lv[i].len = len;
+ if (len && tp_out->lv[i].val) {
+ tp_out->lv[i].val = talloc_zero_size(tp_out, len);
+ if (!tp_out->lv[i].val) {
+ talloc_free(tp_out);
+ return NULL;
+ }
+ memcpy((uint8_t *)tp_out->lv[i].val, tp_orig->lv[i].val,
+ len);
+ }
+ }
+
+ return tp_out;
+}
+
+/*! \brief Merge all \ref tlv_parsed attributes of 'src' into 'dst'
+ * \param[in] dst Parsed TLV structure to merge into
+ * \param[in] src Parsed TLV structure to merge from
+ * \returns 0 on success, negative on error
+ */
+int osmo_tlvp_merge(struct tlv_parsed *dst, const struct tlv_parsed *src)
+{
+ size_t i, len;
+ for (i = 0; i < ARRAY_SIZE(dst->lv); i++) {
+ len = src->lv[i].len;
+ if (len == 0 || src->lv[i].val == NULL)
+ continue;
+ if (dst->lv[i].val) {
+ talloc_free((uint8_t *) dst->lv[i].val);
+ dst->lv[i].len = 0;
+ }
+ dst->lv[i].val = talloc_zero_size(dst, len);
+ if (!dst->lv[i].val)
+ return -ENOMEM;
+ memcpy((uint8_t *) dst->lv[i].val, src->lv[i].val, len);
+ }
+ return 0;
+}
+
/*! \brief Parse a single TLV encoded IE
* \param[out] o_tag the tag of the IE that was found
* \param[out] o_len length of the IE that was found
@@ -51,7 +130,7 @@ int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,
return 1;
}
- /* FIXME: use tables for knwon IEI */
+ /* FIXME: use tables for known IEI */
switch (def->def[tag].type) {
case TLV_TYPE_T:
/* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
@@ -124,7 +203,7 @@ tlv: /* GSM TS 04.07 11.2.4: Type 4 TLV */
return len;
}
-/*! \brief Parse an entire buffer of TLV encoded Information Eleemnts
+/*! \brief Parse an entire buffer of TLV encoded Information Elements
* \param[out] dec caller-allocated pointer to \ref tlv_parsed
* \param[in] def structure defining the valid TLV tags / configurations
* \param[in] buf the input data buffer to be parsed
@@ -206,4 +285,175 @@ static __attribute__((constructor)) void on_dso_load_tlv(void)
vtvlv_gan_att_def.def[i].type = TLV_TYPE_vTvLV_GAN;
}
+/*! Advance the data pointer, subtract length and assign value pointer
+ * \param data pointer to the pointer to data
+ * \param data_len pointer to size_t containing \arg data length
+ * \param[in] len the length that we expect the fixed IE to hav
+ * \param[out] value pointer to pointer of value part of IE
+ * \returns length of IE value; negative in case of error
+ */
+int osmo_shift_v_fixed(uint8_t **data, size_t *data_len,
+ size_t len, uint8_t **value)
+{
+ if (len > *data_len)
+ goto fail;
+
+ if (value)
+ *value = *data;
+
+ *data += len;
+ *data_len -= len;
+
+ return len;
+
+fail:
+ *data += *data_len;
+ *data_len = 0;
+ return -1;
+}
+
+/*! Match tag, check length and assign value pointer
+ * \param data pointer to the pointer to data
+ * \param data_len pointer to size_t containing \arg data length
+ * \param[in] tag the tag (IEI) that we expect at \arg data
+ * \param[in] len the length that we expect the fixed IE to have
+ * \param[out] value pointer to pointer of value part of IE
+ * \returns length of IE value; negative in case of error
+ */
+int osmo_match_shift_tv_fixed(uint8_t **data, size_t *data_len,
+ uint8_t tag, size_t len,
+ uint8_t **value)
+{
+ size_t ie_len;
+
+ if (*data_len == 0)
+ goto fail;
+
+ if ((*data)[0] != tag)
+ return 0;
+
+ if (len > *data_len - 1)
+ goto fail;
+
+ if (value)
+ *value = *data + 1;
+
+ ie_len = len + 1;
+ *data += ie_len;
+ *data_len -= ie_len;
+
+ return ie_len;
+
+fail:
+ *data += *data_len;
+ *data_len = 0;
+ return -1;
+}
+
+/*! Verify TLV header and advance data / subtract length
+ * \param data pointer to the pointer to data
+ * \param data_len pointer to size_t containing \arg data length
+ * \param[in] expected_tag the tag (IEI) that we expect at \arg data
+ * \param[out] value pointer to pointer of value part of IE
+ * \param[out] value_len pointer to length of \arg value
+ * \returns length of IE value; negative in case of error
+ */
+int osmo_match_shift_tlv(uint8_t **data, size_t *data_len,
+ uint8_t expected_tag, uint8_t **value,
+ size_t *value_len)
+{
+ int rc;
+ uint8_t tag;
+ uint8_t *old_data = *data;
+ size_t old_data_len = *data_len;
+
+ rc = osmo_shift_tlv(data, data_len, &tag, value, value_len);
+
+ if (rc > 0 && tag != expected_tag) {
+ *data = old_data;
+ *data_len = old_data_len;
+ return 0;
+ }
+
+ return rc;
+}
+
+/*! Extract TLV and advance data pointer + subtract length
+ * \param data pointer to the pointer to data
+ * \param data_len pointer to size_t containing \arg data lengt
+ * \param[out] tag extract the tag (IEI) at start of \arg data
+ * \param[out] value extracted pointer to value part of TLV
+ * \param[out] value_len extracted length of \arg value
+ * \returns number of bytes subtracted
+ */
+int osmo_shift_tlv(uint8_t **data, size_t *data_len,
+ uint8_t *tag, uint8_t **value, size_t *value_len)
+{
+ size_t len;
+ size_t ie_len;
+
+ if (*data_len < 2)
+ goto fail;
+
+ len = (*data)[1];
+ if (len > *data_len - 2)
+ goto fail;
+
+ if (tag)
+ *tag = (*data)[0];
+ if (value)
+ *value = *data + 2;
+ if (value_len)
+ *value_len = len;
+
+ ie_len = len + 2;
+
+ *data += ie_len;
+ *data_len -= ie_len;
+
+ return ie_len;
+
+fail:
+ *data += *data_len;
+ *data_len = 0;
+ return -1;
+}
+
+/*! Extract LV and advance data pointer + subtract length
+ * \param data pointer to the pointer to data
+ * \param data_len pointer to size_t containing \arg data lengt
+ * \param[out] value extracted pointer to value part of TLV
+ * \param[out] value_len extracted length of \arg value
+ * \returns number of bytes subtracted
+ */
+int osmo_shift_lv(uint8_t **data, size_t *data_len,
+ uint8_t **value, size_t *value_len)
+{
+ size_t len;
+ size_t ie_len;
+
+ if (*data_len < 1)
+ goto fail;
+
+ len = (*data)[0];
+ if (len > *data_len - 1)
+ goto fail;
+
+ if (value)
+ *value = *data + 1;
+ if (value_len)
+ *value_len = len;
+
+ ie_len = len + 1;
+ *data += ie_len;
+ *data_len -= ie_len;
+
+ return ie_len;
+
+fail:
+ *data += *data_len;
+ *data_len = 0;
+ return -1;
+}
+
/*! @} */
diff --git a/src/shared/libosmocore/src/gsmtap_util.c b/src/shared/libosmocore/src/gsmtap_util.c
index ce722da9..ab4a28eb 100644
--- a/src/shared/libosmocore/src/gsmtap_util.c
+++ b/src/shared/libosmocore/src/gsmtap_util.c
@@ -49,7 +49,7 @@
/*! \brief convert RSL channel number to GSMTAP channel type
- * \param[in] rsl_cantype RSL channel type
+ * \param[in] rsl_chantype RSL channel type
* \param[in] link_id RSL link identifier
* \returns GSMTAP channel type
*/
@@ -99,6 +99,7 @@ uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t link_id)
* \param[in] snr Signal/Noise Ratio (SNR)
* \param[in] data Pointer to data buffer
* \param[in] len Length of \ref data
+ * \return dynamically allocated message buffer containing data
*
* This function will allocate a new msgb and fill it with a GSMTAP
* header containing the information
@@ -145,6 +146,7 @@ struct msgb *gsmtap_makemsg_ex(uint8_t type, uint16_t arfcn, uint8_t ts, uint8_t
* \param[in] snr Signal/Noise Ratio (SNR)
* \param[in] data Pointer to data buffer
* \param[in] len Length of \ref data
+ * \return message buffer or NULL in case of error
*
* This function will allocate a new msgb and fill it with a GSMTAP
* header containing the information
@@ -165,6 +167,7 @@ struct msgb *gsmtap_makemsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type,
/*! \brief 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
* return resulting fd. If \a host is NULL, the destination address
@@ -182,7 +185,18 @@ int gsmtap_source_init_fd(const char *host, uint16_t port)
OSMO_SOCK_F_CONNECT);
}
-/*! \brief Add a local sink to an existing GSMTAP source and return fd */
+/*! \brief 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
+ *
+ * In case the GSMTAP socket is connected to a local destination
+ * IP/port, this function creates a corresponding receiving socket
+ * bound to that destination IP + port.
+ *
+ * In case the gsmtap socket is not connected to a local IP/port, or
+ * creation of the receiving socket fails, a negative error code is
+ * returned.
+ */
int gsmtap_source_add_sink_fd(int gsmtap_fd)
{
struct sockaddr_storage ss;
@@ -205,7 +219,8 @@ int gsmtap_source_add_sink_fd(int gsmtap_fd)
/*! \brief Send a \ref msgb through a GSMTAP source
* \param[in] gti GSMTAP instance
- * \param[in] msgb message buffer
+ * \param[in] msg message buffer
+ * \return 0 in case of success; negative in case of error
*/
int gsmtap_sendmsg(struct gsmtap_inst *gti, struct msgb *msg)
{
@@ -271,11 +286,9 @@ static int gsmtap_wq_w_cb(struct osmo_fd *ofd, struct msgb *msg)
rc = write(ofd->fd, msg->data, msg->len);
if (rc < 0) {
- perror("writing msgb to gsmtap fd");
return rc;
}
if (rc != msg->len) {
- perror("short write to gsmtap fd");
return -EIO;
}
@@ -293,7 +306,6 @@ static int gsmtap_sink_fd_cb(struct osmo_fd *fd, unsigned int flags)
rc = read(fd->fd, buf, sizeof(buf));
if (rc < 0) {
- perror("reading from gsmtap sink fd");
return rc;
}
/* simply discard any data arriving on the socket */
@@ -301,10 +313,24 @@ static int gsmtap_sink_fd_cb(struct osmo_fd *fd, unsigned int flags)
return 0;
}
-/*! \brief Add a local sink to an existing GSMTAP source instance */
+/*! \brief 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
+ *
+ * In case the GSMTAP socket is connected to a local destination
+ * IP/port, this function creates a corresponding receiving socket
+ * bound to that destination IP + port.
+ *
+ * In case the gsmtap socket is not connected to a local IP/port, or
+ * creation of the receiving socket fails, a negative error code is
+ * returned.
+ *
+ * The file descriptor of the receiving socket is automatically added
+ * to the libosmocore select() handling.
+ */
int gsmtap_source_add_sink(struct gsmtap_inst *gti)
{
- int fd;
+ int fd, rc;
fd = gsmtap_source_add_sink_fd(gsmtap_inst_fd(gti));
if (fd < 0)
@@ -318,7 +344,11 @@ int gsmtap_source_add_sink(struct gsmtap_inst *gti)
sink_ofd->when = BSC_FD_READ;
sink_ofd->cb = gsmtap_sink_fd_cb;
- osmo_fd_register(sink_ofd);
+ rc = osmo_fd_register(sink_ofd);
+ if (rc < 0) {
+ close(fd);
+ return rc;
+ }
}
return fd;
@@ -328,7 +358,8 @@ int gsmtap_source_add_sink(struct gsmtap_inst *gti)
/*! \brief Open GSMTAP source socket, connect and register osmo_fd
* \param[in] host host name or IP address in string format
* \param[in] port UDP port number in host byte order
- * \param[in] osmo_wq_mode Register \ref osmo_wqueue (1) or not (0)
+ * \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 host/port,
* allocate 'struct gsmtap_inst' and optionally osmo_fd/osmo_wqueue
@@ -338,7 +369,7 @@ struct gsmtap_inst *gsmtap_source_init(const char *host, uint16_t port,
int ofd_wq_mode)
{
struct gsmtap_inst *gti;
- int fd;
+ int fd, rc;
fd = gsmtap_source_init_fd(host, port);
if (fd < 0)
@@ -353,10 +384,16 @@ struct gsmtap_inst *gsmtap_source_init(const char *host, uint16_t port,
osmo_wqueue_init(&gti->wq, 64);
gti->wq.write_cb = &gsmtap_wq_w_cb;
- osmo_fd_register(&gti->wq.bfd);
+ rc = osmo_fd_register(&gti->wq.bfd);
+ if (rc < 0) {
+ close(fd);
+ return NULL;
+ }
}
return gti;
}
#endif /* HAVE_SYS_SOCKET_H */
+
+/*! @} */
diff --git a/src/shared/libosmocore/src/logging.c b/src/shared/libosmocore/src/logging.c
index f58265f7..b0bca549 100644
--- a/src/shared/libosmocore/src/logging.c
+++ b/src/shared/libosmocore/src/logging.c
@@ -38,11 +38,13 @@
#include <strings.h>
#endif
#include <time.h>
+#include <sys/time.h>
#include <errno.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
+#include <osmocom/core/timer.h>
#include <osmocom/vty/logging.h> /* for LOGGING_STR. */
@@ -106,14 +108,40 @@ static const struct log_info_cat internal_cat[OSMO_NUM_DLIB] = {
.enabled = 1, .loglevel = LOGL_NOTICE,
.color = "\033[1;38m",
},
+ [INT2IDX(DLCTRL)] = {
+ .name = "DLCTRL",
+ .description = "Control Interface",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [INT2IDX(DLGTP)] = {
+ .name = "DLGTP",
+ .description = "GPRS GTP library",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [INT2IDX(DLSTATS)] = {
+ .name = "DLSTATS",
+ .description = "Statistics messages and logging",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [INT2IDX(DLGSUP)] = {
+ .name = "DLGSUP",
+ .description = "Generic Subscriber Update Protocol",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [INT2IDX(DLOAP)] = {
+ .name = "DLOAP",
+ .description = "Osmocom Authentication Protocol",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
};
+/*! \brief descriptive string for each log level */
/* You have to keep this in sync with the structure loglevel_strs. */
const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = {
- "Log simply everything",
+ "Don't use. It doesn't log anything",
"Log debug messages and higher levels",
"Log informational messages and higher levels",
- "Log noticable messages and higher levels",
+ "Log noticeable messages and higher levels",
"Log error messages and higher levels",
"Log only fatal messages",
NULL,
@@ -125,13 +153,19 @@ static int subsys_lib2index(int subsys)
return (subsys * -1) + (osmo_log_info->num_cat_user-1);
}
-/*! \brief Parse a human-readable log level into a numeric value */
+/*! \brief Parse a human-readable log level into a numeric value
+ * \param lvl[in] zero-terminated string containing log level name
+ * \returns numeric log level
+ */
int log_parse_level(const char *lvl)
{
return get_string_value(loglevel_strs, lvl);
}
-/*! \brief convert a numeric log level into human-readable string */
+/*! \brief convert a numeric log level into human-readable string
+ * \param lvl[in] numeric log level
+ * \returns zero-terminated string (log level name)
+ */
const char *log_level_str(unsigned int lvl)
{
return get_value_string(loglevel_strs, lvl);
@@ -173,6 +207,7 @@ void log_parse_category_mask(struct log_target* target, const char *_mask)
target->categories[i].enabled = 0;
category_token = strtok(mask, ":");
+ OSMO_ASSERT(category_token);
do {
for (i = 0; i < osmo_log_info->num_cat; ++i) {
size_t length, cat_length;
@@ -215,6 +250,14 @@ static const char* color(int subsys)
return NULL;
}
+const char* log_category_name(int subsys)
+{
+ if (subsys < osmo_log_info->num_cat)
+ return osmo_log_info->cat[subsys].name;
+
+ return NULL;
+}
+
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)
@@ -226,14 +269,26 @@ static void _output(struct log_target *target, unsigned int subsys,
if (target->use_color) {
const char *c = color(subsys);
if (c) {
- ret = snprintf(buf + offset, rem, "%s", color(subsys));
+ ret = snprintf(buf + offset, rem, "%s", c);
if (ret < 0)
goto err;
OSMO_SNPRINTF_RET(ret, rem, offset, len);
}
}
if (!cont) {
- if (target->print_timestamp) {
+ if (target->print_ext_timestamp) {
+ struct tm tm;
+ 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);
+ } else if (target->print_timestamp) {
char *timestr;
time_t tm;
tm = time(NULL);
@@ -244,6 +299,12 @@ static void _output(struct log_target *target, unsigned int subsys,
goto err;
OSMO_SNPRINTF_RET(ret, rem, offset, len);
}
+ if (target->print_category) {
+ ret = snprintf(buf + offset, rem, "%s ", log_category_name(subsys));
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ }
if (target->print_filename) {
ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ",
subsys, file, line);
@@ -267,57 +328,101 @@ err:
target->output(target, level, buf);
}
-/*! \brief vararg version of logging function */
-void osmo_vlogp(int subsys, int level, const char *file, int line,
- int cont, const char *format, va_list ap)
+/* Catch internal logging category indexes as well as out-of-bounds indexes.
+ * For internal categories, the ID is negative starting with -1; and internal
+ * logging categories are added behind the user categories. For out-of-bounds
+ * indexes, return the index of DLGLOBAL. The returned category index is
+ * guaranteed to exist in osmo_log_info, otherwise the program would abort,
+ * which should never happen unless even the DLGLOBAL category is missing. */
+static inline int map_subsys(int subsys)
{
- struct log_target *tar;
+ /* Note: comparing signed and unsigned integers */
+
+ if (subsys > 0 && ((unsigned int)subsys) >= osmo_log_info->num_cat_user)
+ subsys = DLGLOBAL;
if (subsys < 0)
subsys = subsys_lib2index(subsys);
- if (subsys > osmo_log_info->num_cat)
- subsys = DLGLOBAL;
+ if (subsys < 0 || subsys >= osmo_log_info->num_cat)
+ subsys = subsys_lib2index(DLGLOBAL);
- llist_for_each_entry(tar, &osmo_log_target_list, entry) {
- struct log_category *category;
- int output = 0;
- va_list bp;
+ OSMO_ASSERT(!(subsys < 0 || subsys >= osmo_log_info->num_cat));
- category = &tar->categories[subsys];
- /* subsystem is not supposed to be logged */
- if (!category->enabled)
- continue;
+ return subsys;
+}
- /* Check the global log level */
- if (tar->loglevel != 0 && level < tar->loglevel)
- continue;
+static inline int check_log_to_target(struct log_target *tar, int subsys, int level)
+{
+ struct log_category *category;
- /* Check the category log level */
- if (tar->loglevel == 0 && category->loglevel != 0 &&
- level < category->loglevel)
- continue;
+ category = &tar->categories[subsys];
+
+ /* subsystem is not supposed to be logged */
+ if (!category->enabled)
+ return 0;
+
+ /* Check the global log level */
+ if (tar->loglevel != 0 && level < tar->loglevel)
+ return 0;
+
+ /* Check the category log level */
+ if (tar->loglevel == 0 && category->loglevel != 0 &&
+ level < category->loglevel)
+ return 0;
+
+ /* Apply filters here... if that becomes messy we will
+ * need to put filters in a list and each filter will
+ * say stop, continue, output */
+ if ((tar->filter_map & LOG_FILTER_ALL) != 0)
+ return 1;
+
+ if (osmo_log_info->filter_fn)
+ return osmo_log_info->filter_fn(&log_context, tar);
+
+ /* TODO: Check the filter/selector too? */
+ return 1;
+}
+
+/*! \brief vararg version of logging function
+ * \param[in] subsys Logging sub-system
+ * \param[in] level Log level
+ * \param[in] file name of source code file
+ * \param[in] cont continuation (1) or new line (0)
+ * \param[in] format format string
+ * \param[in] ap vararg-list containing format string arguments
+ */
+void osmo_vlogp(int subsys, int level, const char *file, int line,
+ int cont, const char *format, va_list ap)
+{
+ struct log_target *tar;
- /* Apply filters here... if that becomes messy we will
- * need to put filters in a list and each filter will
- * say stop, continue, output */
- if ((tar->filter_map & LOG_FILTER_ALL) != 0)
- output = 1;
- else if (osmo_log_info->filter_fn)
- output = osmo_log_info->filter_fn(&log_context,
- tar);
- if (!output)
+ subsys = map_subsys(subsys);
+
+ llist_for_each_entry(tar, &osmo_log_target_list, entry) {
+ va_list bp;
+
+ if (!check_log_to_target(tar, subsys, level))
continue;
/* According to the manpage, vsnprintf leaves the value of ap
* in undefined state. Since _output uses vsnprintf and it may
* be called several times, we have to pass a copy of ap. */
va_copy(bp, ap);
- _output(tar, subsys, level, file, line, cont, format, bp);
+ if (tar->raw_output)
+ tar->raw_output(tar, subsys, level, file, line, cont, format, bp);
+ else
+ _output(tar, subsys, level, file, line, cont, format, bp);
va_end(bp);
}
}
+/*! \brief logging function used by DEBUGP() macro
+ * \param[in] subsys Logging sub-system
+ * \param[in] file name of source code file
+ * \param[in] cont continuation (1) or new line (0)
+ * \param[in] format format string
+ */
void logp(int subsys, const char *file, int line, int cont,
const char *format, ...)
{
@@ -328,6 +433,13 @@ void logp(int subsys, const char *file, int line, int cont,
va_end(ap);
}
+/*! \brief logging function used by LOGP() macro
+ * \param[in] subsys Logging sub-system
+ * \param[in] level Log level
+ * \param[in] file name of source code file
+ * \param[in] cont continuation (1) or new line (0)
+ * \param[in] format format string
+ */
void logp2(int subsys, unsigned int level, const char *file, int line, int cont, const char *format, ...)
{
va_list ap;
@@ -362,6 +474,7 @@ void log_reset_context(void)
/*! \brief Set the logging context
* \param[in] ctx_nr logging context number
* \param[in] value value to which the context is to be set
+ * \returns 0 in case of success; negative otherwise
*
* A logging context is something like the subscriber identity to which
* the currently processed message relates, or the BTS through which it
@@ -413,6 +526,19 @@ void log_set_print_timestamp(struct log_target *target, int print_timestamp)
target->print_timestamp = print_timestamp;
}
+/*! \brief Enable or disable printing of extended timestamps while logging
+ * \param[in] target Log target to be affected
+ * \param[in] print_timestamp Enable (1) or disable (0) timestamps
+ *
+ * When both timestamp and extended timestamp is enabled then only
+ * the extended timestamp will be used. The format of the timestamp
+ * is YYYYMMDDhhmmssnnn.
+ */
+void log_set_print_extended_timestamp(struct log_target *target, int print_timestamp)
+{
+ target->print_ext_timestamp = print_timestamp;
+}
+
/*! \brief Enable or disable printing of the filename while logging
* \param[in] target Log target to be affected
* \param[in] print_filename Enable (1) or disable (0) filenames
@@ -422,6 +548,17 @@ void log_set_print_filename(struct log_target *target, int print_filename)
target->print_filename = print_filename;
}
+/*! \brief Enable or disable printing of the category name
+ * \param[in] target Log target to be affected
+ * \param[in] print_catname Enable (1) or disable (0) filenames
+ *
+ * Print the category/subsys name in front of every log message.
+ */
+void log_set_print_category(struct log_target *target, int print_category)
+{
+ target->print_category = print_category;
+}
+
/*! \brief Set the global log level for a given log target
* \param[in] target Log target to be affected
* \param[in] log_level New global log level
@@ -431,11 +568,18 @@ void log_set_log_level(struct log_target *target, int log_level)
target->loglevel = log_level;
}
+/*! \brief Set a category filter on a given log target
+ * \param[in] target Log target to be affected
+ * \param[in] category Log category to be affected
+ * \param[in] enable whether to enable or disable the filter
+ * \param[in] level Log level of the filter
+ */
void log_set_category_filter(struct log_target *target, int category,
int enable, int level)
{
- if (category >= osmo_log_info->num_cat)
+ if (!target)
return;
+ category = map_subsys(category);
target->categories[category].enabled = !!enable;
target->categories[category].loglevel = level;
}
@@ -447,7 +591,12 @@ static void _file_output(struct log_target *target, unsigned int level,
fflush(target->tgt_file.out);
}
-/*! \brief Create a new log target skeleton */
+/*! \brief Create a new log target skeleton
+ * \returns dynamically-allocated log target
+ * This funcition allocates a \ref log_target and initializes it
+ * with some default values. The newly created target is not
+ * registered yet.
+ */
struct log_target *log_target_create(void)
{
struct log_target *target;
@@ -484,7 +633,8 @@ struct log_target *log_target_create(void)
return target;
}
-/*! \brief Create the STDERR log target */
+/*! \brief Create the STDERR log target
+ * \returns dynamically-allocated \ref log_target for STDERR */
struct log_target *log_target_create_stderr(void)
{
/* since C89/C99 says stderr is a macro, we can safely do this! */
@@ -549,7 +699,8 @@ struct log_target *log_target_find(int type, const char *fname)
return NULL;
}
-/*! \brief Unregister, close and delete a log target */
+/*! \brief Unregister, close and delete a log target
+ * \param target[in] log target to unregister, close and delete */
void log_target_destroy(struct log_target *target)
{
@@ -571,7 +722,9 @@ void log_target_destroy(struct log_target *target)
talloc_free(target);
}
-/*! \brief close and re-open a log file (for log file rotation) */
+/*! \brief close and re-open a log file (for log file rotation)
+ * \param[in] target log target to re-open
+ * \returns 0 in case of success; negative otherwise */
int log_target_file_reopen(struct log_target *target)
{
fclose(target->tgt_file.out);
@@ -585,8 +738,30 @@ int log_target_file_reopen(struct log_target *target)
return 0;
}
+/*! \brief close and re-open all log files (for log file rotation)
+ * \returns 0 in case of success; negative otherwise */
+int log_targets_reopen(void)
+{
+ struct log_target *tar;
+ int rc = 0;
+
+ llist_for_each_entry(tar, &osmo_log_target_list, entry) {
+ switch (tar->type) {
+ case LOG_TGT_TYPE_FILE:
+ if (log_target_file_reopen(tar) < 0)
+ rc = -1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return rc;
+}
+
/*! \brief Generates the logging command string for VTY
* \param[in] unused_info Deprecated parameter, no longer used!
+ * \returns vty command string for use by VTY command node
*/
const char *log_vty_command_string(const struct log_info *unused_info)
{
@@ -664,6 +839,7 @@ err:
/*! \brief Generates the logging command description for VTY
* \param[in] unused_info Deprecated parameter, no longer used!
+ * \returns logging command description for use by VTY command node
*/
const char *log_vty_command_description(const struct log_info *unused_info)
{
@@ -739,6 +915,7 @@ int log_init(const struct log_info *inf, void *ctx)
if (!osmo_log_info)
return -ENOMEM;
+ osmo_log_info->filter_fn = inf->filter_fn;
osmo_log_info->num_cat_user = inf->num_cat;
/* total number = number of user cat + library cat */
osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat);
@@ -769,4 +946,41 @@ int log_init(const struct log_info *inf, void *ctx)
return 0;
}
+/* \brief De-initialize the Osmocom logging core
+ * This function destroys all targets and releases associated memory */
+void log_fini(void)
+{
+ struct log_target *tar, *tar2;
+
+ llist_for_each_entry_safe(tar, tar2, &osmo_log_target_list, entry)
+ log_target_destroy(tar);
+
+ talloc_free(osmo_log_info);
+ osmo_log_info = NULL;
+ talloc_free(tall_log_ctx);
+ tall_log_ctx = NULL;
+}
+
+/*! \brief Check whether a log entry will be generated.
+ * \returns != 0 if a log entry might get generated by at least one target */
+int log_check_level(int subsys, unsigned int level)
+{
+ struct log_target *tar;
+
+ subsys = map_subsys(subsys);
+
+ /* TODO: The following could/should be cached (update on config) */
+
+ llist_for_each_entry(tar, &osmo_log_target_list, entry) {
+ if (!check_log_to_target(tar, subsys, level))
+ continue;
+
+ /* This might get logged (ignoring filters) */
+ return 1;
+ }
+
+ /* We are sure, that this will not be logged. */
+ return 0;
+}
+
/*! @} */
diff --git a/src/shared/libosmocore/src/logging_gsmtap.c b/src/shared/libosmocore/src/logging_gsmtap.c
new file mode 100644
index 00000000..85f8c28b
--- /dev/null
+++ b/src/shared/libosmocore/src/logging_gsmtap.c
@@ -0,0 +1,134 @@
+/* GSMTAP network logging support code */
+
+/* (C) 2016 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/*! \addtogroup logging
+ * @{
+ */
+
+/*! \file logging_gsmtap.c */
+
+#include "../config.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <arpa/inet.h>
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/gsmtap.h>
+#include <osmocom/core/gsmtap_util.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/timer.h>
+
+#define GSMTAP_LOG_MAX_SIZE 4096
+
+static void _gsmtap_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;
+ struct gsmtap_hdr *gh;
+ struct gsmtap_osmocore_log_hdr *golh;
+ const char *subsys_name = log_category_name(subsys);
+ struct timeval tv;
+ int rc;
+
+ /* get timestamp ASAP */
+ osmo_gettimeofday(&tv, NULL);
+
+ msg = msgb_alloc(sizeof(*gh)+sizeof(*golh)+GSMTAP_LOG_MAX_SIZE,
+ "GSMTAP logging");
+
+ /* GSMTAP header */
+ gh = (struct gsmtap_hdr *) msgb_put(msg, sizeof(*gh));
+ memset(gh, 0, sizeof(*gh));
+ gh->version = GSMTAP_VERSION;
+ gh->hdr_len = sizeof(*gh)/4;
+ gh->type = GSMTAP_TYPE_OSMOCORE_LOG;
+
+ /* Logging header */
+ golh = (struct gsmtap_osmocore_log_hdr *) msgb_put(msg, sizeof(*golh));
+ osmo_strlcpy(golh->proc_name, target->tgt_gsmtap.ident,
+ sizeof(golh->proc_name));
+ if (subsys_name)
+ osmo_strlcpy(golh->subsys, subsys_name+1, sizeof(golh->subsys));
+ else
+ golh->subsys[0] = '\0';
+ osmo_strlcpy(golh->src_file.name, file, sizeof(golh->src_file.name));
+ golh->src_file.line_nr = htonl(line);
+ golh->level = level;
+ /* we always store the timestamp in the message, irrespective
+ * of hat prrint_[ext_]timestamp say */
+ golh->ts.sec = htonl(tv.tv_sec);
+ golh->ts.usec = htonl(tv.tv_usec);
+
+ rc = vsnprintf((char *) msg->tail, msgb_tailroom(msg), format, ap);
+ if (rc < 0)
+ return;
+ msgb_put(msg, rc);
+
+ gsmtap_sendmsg(target->tgt_gsmtap.gsmtap_inst, msg);
+}
+
+/*! \brief Create a new logging target for GSMTAP logging
+ * \param[in] ident string identifier
+ * \returns Log target in case of success, NULL in case of error
+ */
+struct log_target *log_target_create_gsmtap(const char *host, uint16_t port,
+ const char *ident,
+ bool ofd_wq_mode,
+ bool add_sink)
+{
+ struct log_target *target;
+ struct gsmtap_inst *gti;
+
+ target = log_target_create();
+ if (!target)
+ return NULL;
+
+ gti = gsmtap_source_init(host, port, ofd_wq_mode);
+ if (!gti) {
+ log_target_destroy(target);
+ return NULL;
+ }
+
+ if (add_sink)
+ gsmtap_source_add_sink(gti);
+
+ target->tgt_gsmtap.gsmtap_inst = gti;
+ target->tgt_gsmtap.ident = talloc_strdup(target, ident);
+ target->tgt_gsmtap.hostname = talloc_strdup(target, host);
+
+ target->type = LOG_TGT_TYPE_GSMTAP;
+ target->raw_output = _gsmtap_raw_output;
+
+ return target;
+}
+
+/* @} */
diff --git a/src/shared/libosmocore/src/loggingrb.c b/src/shared/libosmocore/src/loggingrb.c
new file mode 100644
index 00000000..bd093b70
--- /dev/null
+++ b/src/shared/libosmocore/src/loggingrb.c
@@ -0,0 +1,98 @@
+/* Ringbuffer-backed logging support code */
+
+/* (C) 2012-2013 by Katerina Barone-Adesi
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/*! \addtogroup logging
+ * @{
+ */
+
+/*! \file loggingrb.c */
+
+#include <osmocom/core/strrb.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/loggingrb.h>
+
+static void _rb_output(struct log_target *target,
+ unsigned int level, const char *log)
+{
+ osmo_strrb_add(target->tgt_rb.rb, log);
+}
+
+/*! \brief Return the number of log strings in the osmo_strrb-backed target.
+ * \param[in] target The target to search.
+ *
+ * \return The number of log strings in the osmo_strrb-backed target.
+ */
+size_t log_target_rb_used_size(struct log_target const *target)
+{
+ return osmo_strrb_elements(target->tgt_rb.rb);
+}
+
+/*! \brief Return the capacity of the osmo_strrb-backed target.
+ * \param[in] target The target to search.
+ *
+ * Note that this is the capacity (aka max number of messages).
+ * It is not the number of unused message slots.
+ * \return The number of log strings in the osmo_strrb-backed target.
+ */
+size_t log_target_rb_avail_size(struct log_target const *target)
+{
+ struct osmo_strrb *rb = target->tgt_rb.rb;
+ return rb->size - 1;
+}
+
+/*! \brief Return the nth log entry in a target.
+ * \param[in] target The target to search.
+ * \param[in] logindex The index of the log entry/error message.
+ *
+ * \return A pointer to the nth message, or NULL if logindex is invalid.
+ */
+const char *log_target_rb_get(struct log_target const *target, size_t logindex)
+{
+ return osmo_strrb_get_nth(target->tgt_rb.rb, logindex);
+}
+
+/*! \brief Create a new logging target for ringbuffer-backed logging.
+ * \param[in] size The capacity (number of messages) of the logging target.
+ * \returns A log target in case of success, NULL in case of error.
+ */
+struct log_target *log_target_create_rb(size_t size)
+{
+ struct log_target *target;
+ struct osmo_strrb *rb;
+
+ target = log_target_create();
+ if (!target)
+ return NULL;
+
+ rb = osmo_strrb_create(target, size + 1);
+ if (!rb) {
+ log_target_destroy(target);
+ return NULL;
+ }
+
+ target->tgt_rb.rb = rb;
+ target->type = LOG_TGT_TYPE_STRRB;
+ target->output = _rb_output;
+
+ return target;
+}
+
+/* @} */
diff --git a/src/shared/libosmocore/src/macaddr.c b/src/shared/libosmocore/src/macaddr.c
new file mode 100644
index 00000000..f83e0546
--- /dev/null
+++ b/src/shared/libosmocore/src/macaddr.c
@@ -0,0 +1,139 @@
+/*
+ * (C) 2013-2014 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2014 by Holger Hans Peter Freyther
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/*! \addtogroup utils
+ * @{
+ */
+
+/*! \file loggingrb.c */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/*! \brief Parse a MAC address from human-readable notation
+ * This function parses an ethernet MAC address in the commonly-used
+ * hex/colon notation (00:00:00:00:00:00) and generates the binary
+ * representation from it.
+ * \param[out] out pointer to caller-allocated buffer of 6 bytes
+ * \param[in] in pointer to input data as string with hex/colon notation
+ */
+int osmo_macaddr_parse(uint8_t *out, const char *in)
+{
+ /* 00:00:00:00:00:00 */
+ char tmp[18];
+ char *tok;
+ unsigned int i = 0;
+
+ if (strlen(in) < 17)
+ return -1;
+
+ strncpy(tmp, in, sizeof(tmp)-1);
+ tmp[sizeof(tmp)-1] = '\0';
+
+ for (tok = strtok(tmp, ":"); tok && (i < 6); tok = strtok(NULL, ":")) {
+ unsigned long ul = strtoul(tok, NULL, 16);
+ out[i++] = ul & 0xff;
+ }
+
+ return 0;
+}
+
+#if defined(__FreeBSD__) || defined(__APPLE__)
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <ifaddrs.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+
+/*! \brief Obtain the MAC address of a given network device
+ * \param[out] mac_out pointer to caller-allocated buffer of 6 bytes
+ * \param[in] dev_name string name of the network device
+ * \returns 0 in case of success; negative otherwise
+ */
+int osmo_get_macaddr(uint8_t *mac_out, const char *dev_name)
+{
+ int rc = -1;
+ struct ifaddrs *ifa, *ifaddr;
+
+ if (getifaddrs(&ifaddr) != 0)
+ return -1;
+
+ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+ struct sockaddr_dl *sdl;
+
+ sdl = (struct sockaddr_dl *) ifa->ifa_addr;
+ if (!sdl)
+ continue;
+ if (sdl->sdl_family != AF_LINK)
+ continue;
+ if (sdl->sdl_type != IFT_ETHER)
+ continue;
+ if (strcmp(ifa->ifa_name, dev_name) != 0)
+ continue;
+
+ memcpy(mac_out, LLADDR(sdl), 6);
+ rc = 0;
+ break;
+ }
+
+ freeifaddrs(ifaddr);
+ return 0;
+}
+
+#else
+
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+/*! \brief Obtain the MAC address of a given network device
+ * \param[out] mac_out pointer to caller-allocated buffer of 6 bytes
+ * \param[in] dev_name string name of the network device
+ * \returns 0 in case of success; negative otherwise
+ */
+int osmo_get_macaddr(uint8_t *mac_out, const char *dev_name)
+{
+ int fd, rc;
+ struct ifreq ifr;
+
+ fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (fd < 0)
+ return fd;
+
+ memset(&ifr, 0, sizeof(ifr));
+ memcpy(&ifr.ifr_name, dev_name, sizeof(ifr.ifr_name));
+ rc = ioctl(fd, SIOCGIFHWADDR, &ifr);
+ close(fd);
+
+ if (rc < 0)
+ return rc;
+
+ memcpy(mac_out, ifr.ifr_hwaddr.sa_data, 6);
+
+ return 0;
+}
+#endif
+
+/*! @} */
diff --git a/src/shared/libosmocore/src/msgb.c b/src/shared/libosmocore/src/msgb.c
index c8564dbb..a27100c6 100644
--- a/src/shared/libosmocore/src/msgb.c
+++ b/src/shared/libosmocore/src/msgb.c
@@ -28,17 +28,19 @@
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
+#include <inttypes.h>
#include <osmocom/core/msgb.h>
//#include <openbsc/gsm_data.h>
#include <osmocom/core/talloc.h>
//#include <openbsc/debug.h>
-void *tall_msgb_ctx;
+void *tall_msgb_ctx = NULL;
/*! \brief Allocate a new message buffer
* \param[in] size Length in octets, including headroom
* \param[in] name Human-readable name to be associated with msgb
+ * \returns dynamically-allocated \ref msgb
*
* This function allocates a 'struct msgb' as well as the underlying
* memory buffer for the actual message data (size specified by \a size)
@@ -74,7 +76,7 @@ void msgb_free(struct msgb *m)
/*! \brief Enqueue message buffer to tail of a queue
* \param[in] queue linked list header of queue
- * \param[in] msgb message buffer to be added to the queue
+ * \param[in] msg message buffer to be added to the queue
*
* The function will append the specified message buffer \a msg to the
* queue implemented by \ref llist_head \a queue
@@ -89,7 +91,7 @@ void msgb_enqueue(struct llist_head *queue, struct msgb *msg)
* \returns message buffer (if any) or NULL if queue empty
*
* The function will remove the first message buffer from the queue
- * implemented by 'ref llist_head \a queue.
+ * implemented by \ref llist_head \a queue.
*/
struct msgb *msgb_dequeue(struct llist_head *queue)
{
@@ -99,13 +101,16 @@ struct msgb *msgb_dequeue(struct llist_head *queue)
return NULL;
lh = queue->next;
- llist_del(lh);
-
- return llist_entry(lh, struct msgb, list);
+
+ if (lh) {
+ llist_del(lh);
+ return llist_entry(lh, struct msgb, list);
+ } else
+ return NULL;
}
/*! \brief Re-set all message buffer pointers
- * \param[in] m message buffer that is to be resetted
+ * \param[in] msg message buffer that is to be resetted
*
* This will re-set the various internal pointers into the underlying
* message buffer, i.e. remvoe all headroom and treat the msgb as
@@ -146,6 +151,7 @@ uint16_t msgb_length(const struct msgb *msg)
}
/*! \brief Set the talloc context for \ref msgb_alloc
+ * Deprecated, use msgb_talloc_ctx_init() instead.
* \param[in] ctx talloc context to be used as root for msgb allocations
*/
void msgb_set_talloc_ctx(void *ctx)
@@ -153,4 +159,195 @@ void msgb_set_talloc_ctx(void *ctx)
tall_msgb_ctx = ctx;
}
+/*! \brief Initialize a msgb talloc context for \ref msgb_alloc.
+ * Create a talloc context called "msgb". If \a pool_size is 0, create a named
+ * const as msgb talloc context. If \a pool_size is nonzero, create a talloc
+ * pool, possibly for faster msgb allocations (see talloc_pool()).
+ * \param[in] root_ctx talloc context used as parent for the new "msgb" ctx.
+ * \param[in] pool_size if nonzero, create a talloc pool of this size.
+ * \returns the new msgb talloc context, e.g. for reporting
+ */
+void *msgb_talloc_ctx_init(void *root_ctx, unsigned int pool_size)
+{
+ if (!pool_size)
+ tall_msgb_ctx = talloc_size(root_ctx, 0);
+ else
+ tall_msgb_ctx = talloc_pool(root_ctx, pool_size);
+ talloc_set_name_const(tall_msgb_ctx, "msgb");
+ return tall_msgb_ctx;
+}
+
+/*! \brief 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] msg The old msgb object
+ * \param[in] name Human-readable name to be associated with msgb
+ */
+struct msgb *msgb_copy(const struct msgb *msg, const char *name)
+{
+ struct msgb *new_msg;
+
+ new_msg = msgb_alloc(msg->data_len, name);
+ if (!new_msg)
+ return NULL;
+
+ /* copy data */
+ memcpy(new_msg->_data, msg->_data, new_msg->data_len);
+
+ /* copy header */
+ new_msg->len = msg->len;
+ new_msg->data += msg->data - msg->_data;
+ new_msg->head += msg->head - msg->_data;
+ new_msg->tail += msg->tail - msg->_data;
+
+ if (msg->l1h)
+ new_msg->l1h = new_msg->_data + (msg->l1h - msg->_data);
+ if (msg->l2h)
+ new_msg->l2h = new_msg->_data + (msg->l2h - msg->_data);
+ if (msg->l3h)
+ new_msg->l3h = new_msg->_data + (msg->l3h - msg->_data);
+ if (msg->l4h)
+ new_msg->l4h = new_msg->_data + (msg->l4h - msg->_data);
+
+ return new_msg;
+}
+
+/*! \brief Resize an area within an msgb
+ *
+ * This resizes a sub area of the msgb data and adjusts the pointers (incl
+ * l1h-l4h) accordingly. The cb part is not updated. If the area is extended,
+ * the contents of the extension is undefined. The complete sub area must be a
+ * part of [data,tail].
+ *
+ * \param[inout] msg The msgb object
+ * \param[in] area A pointer to the sub-area
+ * \param[in] old_size The old size of the sub-area
+ * \param[in] new_size The new size of the sub-area
+ * \returns 0 on success, -1 if there is not enough space to extend the area
+ */
+int msgb_resize_area(struct msgb *msg, uint8_t *area,
+ int old_size, int new_size)
+{
+ int rc;
+ uint8_t *post_start = area + old_size;
+ int pre_len = area - msg->data;
+ int post_len = msg->len - old_size - pre_len;
+ int delta_size = new_size - old_size;
+
+ if (old_size < 0 || new_size < 0)
+ MSGB_ABORT(msg, "Negative sizes are not allowed\n");
+ if (area < msg->data || post_start > msg->tail)
+ MSGB_ABORT(msg, "Sub area is not fully contained in the msg data\n");
+
+ if (delta_size == 0)
+ return 0;
+
+ if (delta_size > 0) {
+ rc = msgb_trim(msg, msg->len + delta_size);
+ if (rc < 0)
+ return rc;
+ }
+
+ memmove(area + new_size, area + old_size, post_len);
+
+ if (msg->l1h >= post_start)
+ msg->l1h += delta_size;
+ if (msg->l2h >= post_start)
+ msg->l2h += delta_size;
+ if (msg->l3h >= post_start)
+ msg->l3h += delta_size;
+ if (msg->l4h >= post_start)
+ msg->l4h += delta_size;
+
+ if (delta_size < 0)
+ msgb_trim(msg, msg->len + delta_size);
+
+ return 0;
+}
+
+
+/*! \brief Return a (static) buffer containing a hexdump of the msg
+ * \param[in] msg message buffer
+ * \returns a pointer to a static char array
+ */
+const char *msgb_hexdump(const struct msgb *msg)
+{
+ static char buf[4100];
+ int buf_offs = 0;
+ int nchars;
+ const unsigned char *start = msg->data;
+ const unsigned char *lxhs[4];
+ int i;
+
+ lxhs[0] = msg->l1h;
+ lxhs[1] = msg->l2h;
+ lxhs[2] = msg->l3h;
+ lxhs[3] = msg->l4h;
+
+ for (i = 0; i < ARRAY_SIZE(lxhs); i++) {
+ if (!lxhs[i])
+ continue;
+
+ if (lxhs[i] < msg->head)
+ continue;
+ if (lxhs[i] > msg->head + msg->data_len)
+ continue;
+ if (lxhs[i] > msg->tail)
+ continue;
+ if (lxhs[i] < msg->data || lxhs[i] > msg->tail) {
+ nchars = snprintf(buf + buf_offs, sizeof(buf) - buf_offs,
+ "(L%d=data%+" PRIdPTR ") ",
+ i+1, lxhs[i] - msg->data);
+ buf_offs += nchars;
+ continue;
+ }
+ if (lxhs[i] < start) {
+ nchars = snprintf(buf + buf_offs, sizeof(buf) - buf_offs,
+ "(L%d%+" PRIdPTR ") ", i+1,
+ start - lxhs[i]);
+ buf_offs += nchars;
+ continue;
+ }
+ nchars = snprintf(buf + buf_offs, sizeof(buf) - buf_offs,
+ "%s[L%d]> ",
+ osmo_hexdump(start, lxhs[i] - start),
+ i+1);
+ if (nchars < 0 || nchars + buf_offs >= sizeof(buf))
+ return "ERROR";
+
+ buf_offs += nchars;
+ start = lxhs[i];
+ }
+ nchars = snprintf(buf + buf_offs, sizeof(buf) - buf_offs,
+ "%s", osmo_hexdump(start, msg->tail - start));
+ if (nchars < 0 || nchars + buf_offs >= sizeof(buf))
+ return "ERROR";
+
+ buf_offs += nchars;
+
+ for (i = 0; i < ARRAY_SIZE(lxhs); i++) {
+ if (!lxhs[i])
+ continue;
+
+ if (lxhs[i] < msg->head || lxhs[i] > msg->head + msg->data_len) {
+ nchars = snprintf(buf + buf_offs, sizeof(buf) - buf_offs,
+ "(L%d out of range) ", i+1);
+ } else if (lxhs[i] <= msg->data + msg->data_len &&
+ lxhs[i] > msg->tail) {
+ nchars = snprintf(buf + buf_offs, sizeof(buf) - buf_offs,
+ "(L%d=tail%+" PRIdPTR ") ",
+ i+1, lxhs[i] - msg->tail);
+ } else
+ continue;
+
+ if (nchars < 0 || nchars + buf_offs >= sizeof(buf))
+ return "ERROR";
+ buf_offs += nchars;
+ }
+
+ return buf;
+}
+
/*! @} */
diff --git a/src/shared/libosmocore/src/msgfile.c b/src/shared/libosmocore/src/msgfile.c
index d2b180d7..bf36bf36 100644
--- a/src/shared/libosmocore/src/msgfile.c
+++ b/src/shared/libosmocore/src/msgfile.c
@@ -21,6 +21,8 @@
*
*/
+#define _WITH_GETLINE
+
#include <osmocom/core/msgfile.h>
#include <osmocom/core/talloc.h>
@@ -28,6 +30,7 @@
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
+#include <stdio.h>
static struct osmo_config_entry *
alloc_entry(struct osmo_config_list *entries,
diff --git a/src/shared/libosmocore/src/panic.c b/src/shared/libosmocore/src/panic.c
index be644ff1..0ce50db2 100644
--- a/src/shared/libosmocore/src/panic.c
+++ b/src/shared/libosmocore/src/panic.c
@@ -24,7 +24,9 @@
* @{
*/
-/*! \file panic.c */
+/*! \file panic.c
+ * \brief Routines for panic handling
+ */
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/core/panic.h>
@@ -58,7 +60,19 @@ static void osmo_panic_default(const char *fmt, va_list args)
#endif
-/*! \brief Terminate the current program with a panic */
+/*! \brief Terminate the current program with a panic
+ *
+ * You can call this function in case some severely unexpected situation
+ * is detected and the program is supposed to terminate in a way that
+ * reports the fact that it terminates.
+ *
+ * The application can register a panic handler function using \ref
+ * osmo_set_panic_handler. If it doesn't, a default panic handler
+ * function is called automatically.
+ *
+ * The default function on most systems will generate a backtrace and
+ * then abort() the process.
+ */
void osmo_panic(const char *fmt, ...)
{
va_list args;
@@ -74,9 +88,15 @@ void osmo_panic(const char *fmt, ...)
}
-/*! \brief Set the panic handler */
+/*! \brief Set the panic handler
+ * \param[in] h New panic handler function
+ *
+ * This changes the panic handling function from the currently active
+ * function to a new call-back function supplied by the caller.
+ */
void osmo_set_panic_handler(osmo_panic_handler_t h)
{
osmo_panic_handler = h;
}
+/*! @} */
diff --git a/src/shared/libosmocore/src/plugin.c b/src/shared/libosmocore/src/plugin.c
index 998bca35..52887acf 100644
--- a/src/shared/libosmocore/src/plugin.c
+++ b/src/shared/libosmocore/src/plugin.c
@@ -20,6 +20,11 @@
*
*/
+/*! \file plugin.c
+ * \brief Routines for loading and managing shared library plug-ins.
+ */
+
+
#include "../config.h"
#if HAVE_DLFCN_H
@@ -32,6 +37,10 @@
#include <osmocom/core/plugin.h>
+/*! \brief Load all plugins available in given directory
+ * \param[in] directory full path name of directory containing plug-ins
+ * \returns number of plugins loaded in case of success, negative in case of error
+ */
int osmo_plugin_load_all(const char *directory)
{
unsigned int num = 0;
diff --git a/src/shared/libosmocore/src/prim.c b/src/shared/libosmocore/src/prim.c
new file mode 100644
index 00000000..3f41c416
--- /dev/null
+++ b/src/shared/libosmocore/src/prim.c
@@ -0,0 +1,12 @@
+#include <osmocom/core/utils.h>
+#include <osmocom/core/prim.h>
+
+/*! \brief human-readable string mapping for
+ * \ref osmo_prim_operation */
+const struct value_string osmo_prim_op_names[5] = {
+ { PRIM_OP_REQUEST, "request" },
+ { PRIM_OP_RESPONSE, "response" },
+ { PRIM_OP_INDICATION, "indication" },
+ { PRIM_OP_CONFIRM, "confirm" },
+ { 0, NULL }
+};
diff --git a/src/shared/libosmocore/src/rate_ctr.c b/src/shared/libosmocore/src/rate_ctr.c
index 8a232e86..f995f3fd 100644
--- a/src/shared/libosmocore/src/rate_ctr.c
+++ b/src/shared/libosmocore/src/rate_ctr.c
@@ -83,6 +83,18 @@ void rate_ctr_add(struct rate_ctr *ctr, int inc)
ctr->current += inc;
}
+/*! \brief Return the counter difference since the last call to this function */
+int64_t rate_ctr_difference(struct rate_ctr *ctr)
+{
+ int64_t result = ctr->current - ctr->previous;
+ ctr->previous = ctr->current;
+
+ return result;
+}
+
+/* TODO: support update intervals > 1s */
+/* TODO: implement this as a special stats reporter */
+
static void interval_expired(struct rate_ctr *ctr, enum rate_ctr_intv intv)
{
/* calculate rate over last interval */
@@ -141,7 +153,10 @@ int rate_ctr_init(void *tall_ctx)
return 0;
}
-/*! \brief Search for counter group based on group name and index */
+/*! \brief Search for counter group based on group name and index
+ * \param[in] name Name of the counter group you're looking for
+ * \param[in] idx Index inside the counter group
+ * \returns \ref rate_ctr_group or NULL in case of error */
struct rate_ctr_group *rate_ctr_get_group_by_name_idx(const char *name, const unsigned int idx)
{
struct rate_ctr_group *ctrg;
@@ -158,7 +173,11 @@ struct rate_ctr_group *rate_ctr_get_group_by_name_idx(const char *name, const un
return NULL;
}
-/*! \brief Search for counter group based on group name */
+/*! \brief Search for counter based on group + name
+ * \param[in] ctrg pointer to \ref rate_ctr_group
+ * \param[in] name name of counter inside group
+ * \returns \ref rate_ctr or NULL in caes of error
+ */
const struct rate_ctr *rate_ctr_get_by_name(const struct rate_ctr_group *ctrg, const char *name)
{
int i;
@@ -177,4 +196,46 @@ const struct rate_ctr *rate_ctr_get_by_name(const struct rate_ctr_group *ctrg, c
return NULL;
}
+/*! \brief Iterate over each counter in group and call function
+ * \param[in] counter group over whose counter to iterate
+ * \param[in] handle_counter function pointer
+ * \param[in] data Data to hand transparently to \ref handle_counter
+ * \returns 0 on success; negative otherwise
+ */
+int rate_ctr_for_each_counter(struct rate_ctr_group *ctrg,
+ rate_ctr_handler_t handle_counter, void *data)
+{
+ int rc = 0;
+ int i;
+
+ for (i = 0; i < ctrg->desc->num_ctr; i++) {
+ struct rate_ctr *ctr = &ctrg->ctr[i];
+ rc = handle_counter(ctrg,
+ ctr, &ctrg->desc->ctr_desc[i], data);
+ if (rc < 0)
+ return rc;
+ }
+
+ return rc;
+}
+
+/*! \brief Iterate over all counter groups
+ * \param[in] handle_group function pointer of callback function
+ * \param[in] data Data to hand transparently to \ref handle_group
+ * \returns 0 on success; negative otherwise
+ */
+int rate_ctr_for_each_group(rate_ctr_group_handler_t handle_group, void *data)
+{
+ struct rate_ctr_group *statg;
+ int rc = 0;
+
+ llist_for_each_entry(statg, &rate_ctr_groups, list) {
+ rc = handle_group(statg, data);
+ if (rc < 0)
+ return rc;
+ }
+
+ return rc;
+}
+
/*! @} */
diff --git a/src/shared/libosmocore/src/rbtree.c b/src/shared/libosmocore/src/rbtree.c
index 4e7c0f3a..f0ebb8c5 100644
--- a/src/shared/libosmocore/src/rbtree.c
+++ b/src/shared/libosmocore/src/rbtree.c
@@ -15,7 +15,8 @@
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
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301, USA
linux/lib/rbtree.c
*/
diff --git a/src/shared/libosmocore/src/select.c b/src/shared/libosmocore/src/select.c
index 6b73377a..da273680 100644
--- a/src/shared/libosmocore/src/select.c
+++ b/src/shared/libosmocore/src/select.c
@@ -16,11 +16,14 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
*/
#include <fcntl.h>
#include <stdio.h>
+#include <string.h>
+#include <sys/select.h>
#include <osmocom/core/select.h>
#include <osmocom/core/linuxlist.h>
@@ -44,6 +47,7 @@ static int unregistered_count;
/*! \brief 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
*/
int osmo_fd_register(struct osmo_fd *fd)
{
@@ -95,78 +99,110 @@ void osmo_fd_unregister(struct osmo_fd *fd)
llist_del(&fd->list);
}
-/*! \brief select main loop integration
- * \param[in] polling should we pollonly (1) or block on select (0)
- */
-int osmo_select_main(int polling)
+inline int osmo_fd_fill_fds(void *_rset, void *_wset, void *_eset)
{
- struct osmo_fd *ufd, *tmp;
- fd_set readset, writeset, exceptset;
- int work = 0, rc;
- struct timeval no_time = {0, 0};
+ fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
+ struct osmo_fd *ufd;
- FD_ZERO(&readset);
- FD_ZERO(&writeset);
- FD_ZERO(&exceptset);
-
- /* prepare read and write fdsets */
llist_for_each_entry(ufd, &osmo_fds, list) {
if (ufd->when & BSC_FD_READ)
- FD_SET(ufd->fd, &readset);
+ FD_SET(ufd->fd, readset);
if (ufd->when & BSC_FD_WRITE)
- FD_SET(ufd->fd, &writeset);
+ FD_SET(ufd->fd, writeset);
if (ufd->when & BSC_FD_EXCEPT)
- FD_SET(ufd->fd, &exceptset);
+ FD_SET(ufd->fd, exceptset);
}
- osmo_timers_check();
-
- if (!polling)
- osmo_timers_prepare();
- rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : osmo_timers_nearest());
- if (rc < 0)
- return 0;
+ return maxfd;
+}
- /* fire timers */
- osmo_timers_update();
+inline int osmo_fd_disp_fds(void *_rset, void *_wset, void *_eset)
+{
+ struct osmo_fd *ufd, *tmp;
+ int work = 0;
+ fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
- /* call registered callback functions */
restart:
unregistered_count = 0;
llist_for_each_entry_safe(ufd, tmp, &osmo_fds, list) {
int flags = 0;
- if (FD_ISSET(ufd->fd, &readset)) {
+ if (FD_ISSET(ufd->fd, readset)) {
flags |= BSC_FD_READ;
- FD_CLR(ufd->fd, &readset);
+ FD_CLR(ufd->fd, readset);
}
- if (FD_ISSET(ufd->fd, &writeset)) {
+ if (FD_ISSET(ufd->fd, writeset)) {
flags |= BSC_FD_WRITE;
- FD_CLR(ufd->fd, &writeset);
+ FD_CLR(ufd->fd, writeset);
}
- if (FD_ISSET(ufd->fd, &exceptset)) {
+ if (FD_ISSET(ufd->fd, exceptset)) {
flags |= BSC_FD_EXCEPT;
- FD_CLR(ufd->fd, &exceptset);
+ FD_CLR(ufd->fd, exceptset);
}
if (flags) {
work = 1;
ufd->cb(ufd, flags);
}
- /* ugly, ugly hack. If more than one filedescriptors were
+ /* ugly, ugly hack. If more than one filedescriptor was
* unregistered, they might have been consecutive and
* llist_for_each_entry_safe() is no longer safe */
/* this seems to happen with the last element of the list as well */
if (unregistered_count >= 1)
goto restart;
}
+
return work;
}
+/*! \brief select main loop integration
+ * \param[in] polling should we pollonly (1) or block on select (0)
+ * \returns 0 if no fd handled; 1 if fd handled; negative in case of error
+ */
+int osmo_select_main(int polling)
+{
+ fd_set readset, writeset, exceptset;
+ int rc;
+ struct timeval no_time = {0, 0};
+
+ FD_ZERO(&readset);
+ FD_ZERO(&writeset);
+ FD_ZERO(&exceptset);
+
+ /* prepare read and write fdsets */
+ osmo_fd_fill_fds(&readset, &writeset, &exceptset);
+
+ if (!polling)
+ osmo_timers_prepare();
+ rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : osmo_timers_nearest());
+ if (rc < 0)
+ return 0;
+
+ /* fire timers */
+ osmo_timers_update();
+
+ /* call registered callback functions */
+ return osmo_fd_disp_fds(&readset, &writeset, &exceptset);
+}
+
+/*! \brief find an osmo_fd based on the integer fd
+ * \param[in] fd file descriptor to use as search key
+ * \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;
+}
+
/*! @} */
#endif /* _HAVE_SYS_SELECT_H */
diff --git a/src/shared/libosmocore/src/serial.c b/src/shared/libosmocore/src/serial.c
index a025ae91..05bdc86e 100644
--- a/src/shared/libosmocore/src/serial.c
+++ b/src/shared/libosmocore/src/serial.c
@@ -27,7 +27,7 @@
*/
/*! \file serial.c
- * \file Osmocom serial port helpers
+ * Osmocom serial port helpers
*/
#include <errno.h>
@@ -59,16 +59,32 @@
int
osmo_serial_init(const char *dev, speed_t baudrate)
{
- int rc, fd=0, v24;
+ int rc, fd=-1, v24, flags;
struct termios tio;
- /* Open device */
- fd = open(dev, O_RDWR | O_NOCTTY);
+ /* Use nonblock as the device might block otherwise */
+ fd = open(dev, O_RDWR | O_NOCTTY | O_SYNC | O_NONBLOCK);
if (fd < 0) {
dbg_perror("open");
return -errno;
}
+ /* now put it into blcoking mode */
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags < 0) {
+ dbg_perror("fcntl get flags");
+ rc = -errno;
+ goto error;
+ }
+
+ flags &= ~O_NONBLOCK;
+ rc = fcntl(fd, F_SETFL, flags);
+ if (rc != 0) {
+ dbg_perror("fcntl set flags");
+ rc = -errno;
+ goto error;
+ }
+
/* Configure serial interface */
rc = tcgetattr(fd, &tio);
if (rc < 0) {
@@ -99,14 +115,14 @@ osmo_serial_init(const char *dev, speed_t baudrate)
rc = ioctl(fd, TIOCMBIS, &v24);
if (rc < 0) {
dbg_perror("ioctl(TIOCMBIS)");
- rc = -errno;
- goto error;
+ /* some serial porst don't support this, so let's not
+ * return an error here */
}
return fd;
error:
- if (fd)
+ if (fd >= 0)
close(fd);
return rc;
}
diff --git a/src/shared/libosmocore/src/signal.c b/src/shared/libosmocore/src/signal.c
index 63843849..84025915 100644
--- a/src/shared/libosmocore/src/signal.c
+++ b/src/shared/libosmocore/src/signal.c
@@ -46,6 +46,7 @@ struct signal_handler {
* \param[in] subsys Subsystem number
* \param[in] cbfn Callback function
* \param[in] data Data passed through to callback
+ * \returns 0 on success; negative in case of error
*/
int osmo_signal_register_handler(unsigned int subsys,
osmo_signal_cbfn *cbfn, void *data)
diff --git a/src/shared/libosmocore/src/sim/Makefile.am b/src/shared/libosmocore/src/sim/Makefile.am
new file mode 100644
index 00000000..4d21f2ad
--- /dev/null
+++ b/src/shared/libosmocore/src/sim/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=0:0:0
+
+AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include
+AM_CFLAGS = -fPIC -Wall $(PCSC_CFLAGS) $(TALLOC_CFLAGS)
+AM_LDFLAGS = $(COVERAGE_LDFLAGS) $(TALLOC_LIBS)
+
+if ENABLE_PCSC
+# FIXME: only build the PC/SC dependent part conditional, but always build other parts
+
+noinst_HEADERS = sim_int.h gsm_int.h
+
+lib_LTLIBRARIES = libosmosim.la
+
+libosmosim_la_SOURCES = core.c reader.c reader_pcsc.c class_tables.c \
+ card_fs_sim.c card_fs_usim.c card_fs_uicc.c \
+ card_fs_isim.c card_fs_tetra.c
+libosmosim_la_LDFLAGS = -version-info $(LIBVERSION)
+libosmosim_la_LIBADD = \
+ $(top_builddir)/src/libosmocore.la \
+ $(top_builddir)/src/gsm/libosmogsm.la \
+ $(PCSC_LIBS)
+
+endif
diff --git a/src/shared/libosmocore/src/sim/card_fs_isim.c b/src/shared/libosmocore/src/sim/card_fs_isim.c
new file mode 100644
index 00000000..339e8627
--- /dev/null
+++ b/src/shared/libosmocore/src/sim/card_fs_isim.c
@@ -0,0 +1,105 @@
+/* 3GPP ISIM specific structures / routines */
+/*
+ * (C) 2014 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+#include <errno.h>
+#include <string.h>
+
+#include <osmocom/sim/sim.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/gsm/gsm48.h>
+
+#include "sim_int.h"
+#include "gsm_int.h"
+
+/* TS 31.103 Version 11.2.0 Release 11 / Chapoter 7.1.3 */
+const struct osim_card_sw ts31_103_sw[] = {
+ {
+ 0x9862, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Security management - Authentication error, incorrect MAC",
+ },
+ OSIM_CARD_SW_LAST
+};
+
+static const struct osim_card_sw *isim_card_sws[] = {
+ ts31_103_sw,
+ ts102221_uicc_sw,
+ NULL
+};
+
+/* TS 31.103 Version 11.2.0 Release 11 / Chapoter 4.2 */
+static const struct osim_file_desc isim_ef_in_adf_isim[] = {
+ EF_TRANSP_N(0x6F02, 0x02, "EF.IMPI", 0, 1, 256,
+ "IMS private user identity"),
+ EF_TRANSP_N(0x6F03, 0x05, "EF.DOMAIN", 0, 1, 256,
+ "Home Network Domain Name"),
+ EF_LIN_FIX_N(0x6F04, 0x04, "EF.IMPU", 0, 1, 256,
+ "IMS public user identity"),
+ EF_TRANSP_N(0x6FAD, 0x03, "EF.AD", 0, 4, 16,
+ "Administrative Data"),
+ EF_LIN_FIX_N(0x6F06, 0x06, "EF.ARR", 0, 1, 256,
+ "Access Rule TLV data objects"),
+ EF_TRANSP_N(0x6F07, 0x07, "EF.IST", F_OPTIONAL, 1, 16,
+ "ISIM Service Table"),
+ EF_LIN_FIX_N(0x6F09, SFI_NONE, "EF.P-CSCF", F_OPTIONAL, 1, 256,
+ "P-CSCF Address"),
+ EF_TRANSP_N(0x6FD5, SFI_NONE, "EF.GBABP", F_OPTIONAL, 1, 35,
+ "GBA Bootstrapping parameters"),
+ EF_LIN_FIX_N(0x6FD7, SFI_NONE, "EF.GBANL", F_OPTIONAL, 1, 256,
+ "NAF Key Identifier TLV Objects"),
+ EF_LIN_FIX_N(0x6FDD, SFI_NONE, "EF.NAFKCA", F_OPTIONAL, 1, 256,
+ "NAF Key Centre Address"),
+ EF_LIN_FIX_N(0x6F3C, SFI_NONE, "EF.SMS", F_OPTIONAL, 176, 176,
+ "Short messages"),
+ EF_TRANSP_N(0x6F43, SFI_NONE, "EF.SMSS", F_OPTIONAL, 2, 4,
+ "SMS status"),
+ EF_LIN_FIX_N(0x6F47, SFI_NONE, "EF.SMSR", F_OPTIONAL, 30, 30,
+ "Short message status reports"),
+ EF_LIN_FIX_N(0x6F42, SFI_NONE, "EF.SMSP", F_OPTIONAL, 29, 64,
+ "Short message service parameters"),
+ EF_LIN_FIX_N(0x6FE7, SFI_NONE, "EF.UICCIARI", F_OPTIONAL, 1, 256,
+ "UICC IARI"),
+};
+
+/* Annex E - TS 101 220 */
+static const uint8_t adf_isim_aid[] = { 0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x04 };
+
+struct osim_card_profile *osim_cprof_isim(void *ctx)
+{
+ struct osim_card_profile *cprof;
+ struct osim_file_desc *mf;
+
+ cprof = talloc_zero(ctx, struct osim_card_profile);
+ cprof->name = "3GPP ISIM";
+ cprof->sws = isim_card_sws;
+
+ mf = alloc_df(cprof, 0x3f00, "MF");
+
+ cprof->mf = mf;
+
+ /* ADF.USIM with its EF siblings */
+ add_adf_with_ef(mf, adf_isim_aid, sizeof(adf_isim_aid),
+ "ADF.ISIM", isim_ef_in_adf_isim,
+ ARRAY_SIZE(isim_ef_in_adf_isim));
+
+ return cprof;
+}
diff --git a/src/shared/libosmocore/src/sim/card_fs_sim.c b/src/shared/libosmocore/src/sim/card_fs_sim.c
new file mode 100644
index 00000000..432c945b
--- /dev/null
+++ b/src/shared/libosmocore/src/sim/card_fs_sim.c
@@ -0,0 +1,477 @@
+/* classic SIM card specific structures/routines */
+/*
+ * (C) 2012-2014 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include <osmocom/sim/sim.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/gsm/gsm48.h>
+
+#include "sim_int.h"
+
+/* 3GPP TS 51.011 / Chapter 9.4 */
+static const struct osim_card_sw ts11_11_sw[] = {
+ {
+ 0x9000, 0xffff, SW_TYPE_STR, SW_CLS_OK,
+ .u.str = "Normal ending of the command",
+ }, {
+ 0x9100, 0xff00, SW_TYPE_STR, SW_CLS_OK,
+ .u.str = "Normal ending of the command - proactive command from SIM pending",
+ }, {
+ 0x9e00, 0xff00, SW_TYPE_STR, SW_CLS_OK,
+ .u.str = "Normal ending of the command - response data for SIM data download",
+ }, {
+ 0x9f00, 0xff00, SW_TYPE_STR, SW_CLS_OK,
+ .u.str = "Normal ending of the command - response data available",
+ }, {
+ 0x9300, 0xffff, SW_TYPE_STR, SW_CLS_POSTP,
+ .u.str = "SIM Application Toolkit is busy, command cannot be executed at present",
+ }, {
+ 0x9200, 0xfff0, SW_TYPE_STR, SW_CLS_WARN,
+ .u.str = "Memory management - Command successful but after using an internal updat retry X times",
+ }, {
+ 0x9240, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Memory management - Memory problem",
+ }, {
+ 0x9400, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Referencing management - no EF selected",
+ }, {
+ 0x9402, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Referencing management - out of range (invalid address)",
+ }, {
+ 0x9404, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Referencing management - file ID not found / pattern not found",
+ }, {
+ 0x9408, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Referencing management - file is inconsistent with the command",
+ }, {
+ 0x9802, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Security management - no CHV initialized",
+ }, {
+ 0x9804, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Security management - access condition not fulfilled",
+ }, {
+ 0x9808, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Security management - in contradiction with CHV status",
+ }, {
+ 0x9810, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Security management - in contradiction with invalidation status",
+ }, {
+ 0x9840, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Security management - unsuccessful CHV verification, no attempt left",
+ }, {
+ 0x9850, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Security management - increase cannot be performed, max value reached",
+ }, {
+ 0x6700, 0xff00, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Application independent - incorrect parameter P3",
+ }, {
+ 0x6b00, 0xff00, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Application independent - incorrect parameter P1 or P2",
+ }, {
+ 0x6d00, 0xff00, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Application independent - unknown instruction code",
+ }, {
+ 0x6e00, 0xff00, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Application independent - wrong instruction class",
+ }, {
+ 0x6f00, 0xff00, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Application independent - technical problem with no diagnostic given",
+ },
+ OSIM_CARD_SW_LAST
+};
+
+static const struct osim_card_sw *sim_card_sws[] = {
+ ts11_11_sw,
+ NULL
+};
+
+static int iccid_decode(struct osim_decoded_data *dd,
+ const struct osim_file_desc *desc,
+ int len, uint8_t *data)
+{
+ struct osim_decoded_element *elem;
+
+ elem = element_alloc(dd, "ICCID", ELEM_T_BCD, ELEM_REPR_DEC);
+ elem->length = len;
+ elem->u.buf = talloc_memdup(elem, data, len);
+
+ return 0;
+}
+
+static int elp_decode(struct osim_decoded_data *dd,
+ const struct osim_file_desc *desc,
+ int len, uint8_t *data)
+{
+ int i, num_lp = len / 2;
+
+ for (i = 0; i < num_lp; i++) {
+ uint8_t *cur = data + i*2;
+ struct osim_decoded_element *elem;
+ elem = element_alloc(dd, "Language Code", ELEM_T_STRING, ELEM_REPR_NONE);
+ elem->u.buf = (uint8_t *) talloc_strndup(elem, (const char *) cur, 2);
+ }
+
+ return 0;
+}
+
+/* 10.3.1 */
+int gsm_lp_decode(struct osim_decoded_data *dd,
+ const struct osim_file_desc *desc,
+ int len, uint8_t *data)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ struct osim_decoded_element *elem;
+ elem = element_alloc(dd, "Language Code", ELEM_T_UINT8, ELEM_REPR_DEC);
+ elem->u.u8 = data[i];
+ }
+
+ return 0;
+}
+
+/* 10.3.2 */
+int gsm_imsi_decode(struct osim_decoded_data *dd,
+ const struct osim_file_desc *desc,
+ int len, uint8_t *data)
+{
+ struct osim_decoded_element *elem;
+
+ if (len < 2)
+ return -EINVAL;
+
+ elem = element_alloc(dd, "IMSI", ELEM_T_BCD, ELEM_REPR_DEC);
+ elem->length = data[0];
+ elem->u.buf = talloc_memdup(elem, data+1, len-1);
+
+ return 0;
+}
+
+/* 10.3.3 */
+static int gsm_kc_decode(struct osim_decoded_data *dd,
+ const struct osim_file_desc *desc,
+ int len, uint8_t *data)
+{
+ struct osim_decoded_element *kc, *cksn;
+
+ if (len < 9)
+ return -EINVAL;
+
+ kc = element_alloc(dd, "Kc", ELEM_T_BYTES, ELEM_REPR_HEX);
+ kc->u.buf = talloc_memdup(kc, data, 8);
+ cksn = element_alloc(dd, "CKSN", ELEM_T_UINT8, ELEM_REPR_DEC);
+ cksn->u.u8 = data[8];
+
+ return 0;
+}
+
+/* 10.3.4 */
+static int gsm_plmnsel_decode(struct osim_decoded_data *dd,
+ const struct osim_file_desc *desc,
+ int len, uint8_t *data)
+{
+ int i, n_plmn = len / 3;
+
+ if (n_plmn < 1)
+ return -EINVAL;
+
+ for (i = 0; i < n_plmn; i++) {
+ uint8_t *cur = data + 3*i;
+ struct osim_decoded_element *elem, *mcc, *mnc;
+ uint8_t ra_buf[6];
+ struct gprs_ra_id ra_id;
+
+ memset(ra_buf, 0, sizeof(ra_buf));
+ memcpy(ra_buf, cur, 3);
+ gsm48_parse_ra(&ra_id, ra_buf);
+
+ elem = element_alloc(dd, "PLMN", ELEM_T_GROUP, ELEM_REPR_NONE);
+
+ mcc = element_alloc_sub(elem, "MCC", ELEM_T_UINT16, ELEM_REPR_DEC);
+ mcc->u.u16 = ra_id.mcc;
+
+ mnc = element_alloc_sub(elem, "MNC", ELEM_T_UINT16, ELEM_REPR_DEC);
+ mnc->u.u16 = ra_id.mnc;
+ }
+
+ return 0;
+}
+
+/* 10.3.5 */
+int gsm_hpplmn_decode(struct osim_decoded_data *dd,
+ const struct osim_file_desc *desc,
+ int len, uint8_t *data)
+{
+ struct osim_decoded_element *elem;
+
+ elem = element_alloc(dd, "Time interval", ELEM_T_UINT8, ELEM_REPR_DEC);
+ elem->u.u8 = *data;
+
+ return 0;
+}
+
+/* Chapter 10.1. Contents of the EFs at the MF level */
+static const struct osim_file_desc sim_ef_in_mf[] = {
+ EF_TRANSP(0x2FE2, SFI_NONE, "EF.ICCID", 0, 10, 10,
+ "ICC Identification", &iccid_decode, NULL),
+ EF_TRANSP(0x2F05, SFI_NONE, "EF.PL", F_OPTIONAL, 2, 20,
+ "Preferred language", &elp_decode, NULL),
+};
+
+/* Chapter 10.3.x Contents of files at the GSM application level */
+static const struct osim_file_desc sim_ef_in_gsm[] = {
+ EF_TRANSP(0x6F05, SFI_NONE, "EF.LP", 0, 1, 16,
+ "Language preference", &gsm_lp_decode, NULL),
+ EF_TRANSP(0x6F07, SFI_NONE, "EF.IMSI", 0, 9, 9,
+ "IMSI", &gsm_imsi_decode, NULL),
+ EF_TRANSP(0x6F20, SFI_NONE, "EF.Kc", 0, 9, 9,
+ "Ciphering key Kc", &gsm_kc_decode, NULL),
+ EF_TRANSP(0x6F30, SFI_NONE, "EF.PLMNsel", F_OPTIONAL, 24, 72,
+ "PLMN selector", &gsm_plmnsel_decode, NULL),
+ EF_TRANSP(0x6F31, SFI_NONE, "EF.HPPLMN", 0, 1, 1,
+ "Higher Priority PLMN search period", &gsm_hpplmn_decode, NULL),
+ EF_TRANSP_N(0x6F37, SFI_NONE, "EF.ACMmax", F_OPTIONAL, 3, 3,
+ "ACM maximum value"),
+ EF_TRANSP_N(0x6F38, SFI_NONE, "EF.SST", 0, 2, 16,
+ "SIM service table"),
+ EF_CYCLIC_N(0x6F39, SFI_NONE, "EF.ACM", F_OPTIONAL, 3, 3,
+ "Accumulated call meter"),
+ EF_TRANSP_N(0x6F3E, SFI_NONE, "EF.GID1", F_OPTIONAL, 1, 8,
+ "Group Identifier Level 1"),
+ EF_TRANSP_N(0x6F3F, SFI_NONE, "EF.GID2", F_OPTIONAL, 1, 8,
+ "Group Identifier Level 2"),
+ EF_TRANSP_N(0x6F46, SFI_NONE, "EF.SPN", F_OPTIONAL, 17, 17,
+ "Service Provider Name"),
+ EF_TRANSP_N(0x6F41, SFI_NONE, "EF.PUCT", F_OPTIONAL, 5, 5,
+ "Price per unit and currency table"),
+ EF_TRANSP_N(0x6F45, SFI_NONE, "EF.CBMI", F_OPTIONAL, 2, 32,
+ "Cell broadcast massage identifier selection"),
+ EF_TRANSP_N(0x6F74, SFI_NONE, "EF.BCCH", 0, 16, 16,
+ "Broadcast control channels"),
+ EF_TRANSP_N(0x6F78, SFI_NONE, "EF.ACC", 0, 2, 2,
+ "Access control class"),
+ EF_TRANSP_N(0x6F7B, SFI_NONE, "EF.FPLMN", 0, 12, 12,
+ "Forbidden PLMNs"),
+ EF_TRANSP_N(0x6F7E, SFI_NONE, "EF.LOCI", 0, 11, 11,
+ "Location information"),
+ EF_TRANSP_N(0x6FAD, SFI_NONE, "EF.AD", 0, 3, 8,
+ "Administrative data"),
+ EF_TRANSP_N(0x6FAE, SFI_NONE, "EF.Phase", 0, 1, 1,
+ "Phase identification"),
+ EF_TRANSP_N(0x6FB1, SFI_NONE, "EF.VGCS", F_OPTIONAL, 4, 80,
+ "Voice Group Call Service"),
+ EF_TRANSP_N(0x6FB2, SFI_NONE, "EF.VGCSS", F_OPTIONAL, 7, 7,
+ "Voice Group Call Service Status"),
+ EF_TRANSP_N(0x6FB3, SFI_NONE, "EF.VBS", F_OPTIONAL, 4, 80,
+ "Voice Broadcast Service"),
+ EF_TRANSP_N(0x6FB4, SFI_NONE, "EF.VBSS", F_OPTIONAL, 7, 7,
+ "Voice Broadcast Service Status"),
+ EF_TRANSP_N(0x6FB5, SFI_NONE, "EF.eMLPP", F_OPTIONAL, 2, 2,
+ "enhanced Mult Level Pre-emption and Priority"),
+ EF_TRANSP_N(0x6FB6, SFI_NONE, "EF.AAeM", F_OPTIONAL, 1, 1,
+ "Automatic Answer for eMLPP Service"),
+ EF_TRANSP_N(0x6F48, SFI_NONE, "EF.CBMID", F_OPTIONAL, 2, 32,
+ "Cell Broadcast Message Identifier for Data Download"),
+ EF_TRANSP_N(0x6FB7, SFI_NONE, "EF.ECC", F_OPTIONAL, 3, 15,
+ "Emergency Call Code"),
+ EF_TRANSP_N(0x6F50, SFI_NONE, "EF.CBMIR", F_OPTIONAL, 4, 64,
+ "Cell broadcast message identifier range selection"),
+ EF_TRANSP_N(0x6F2C, SFI_NONE, "EF.DCK", F_OPTIONAL, 16, 16,
+ "De-personalization Control Keys"),
+ EF_TRANSP_N(0x6F32, SFI_NONE, "EF.CNL", F_OPTIONAL, 6, 60,
+ "Co-operative Network List"),
+ EF_LIN_FIX_N(0x6F51, SFI_NONE, "EF.NIA", F_OPTIONAL, 1, 17,
+ "Network's Indication of Alerting"),
+ EF_TRANSP_N(0x6F52, SFI_NONE, "EF.KcGPRS", F_OPTIONAL, 9, 9,
+ "GPRS Ciphering key KcGPRS"),
+ EF_TRANSP_N(0x6F53, SFI_NONE, "EF.LOCIGPRS", F_OPTIONAL, 14, 14,
+ "GPRS location information"),
+ EF_TRANSP_N(0x6F54, SFI_NONE, "EF.SUME", F_OPTIONAL, 1, 64,
+ "SetUpMenu Elements"),
+ EF_TRANSP_N(0x6F60, SFI_NONE, "EF.PLMNwAcT", F_OPTIONAL, 40, 80,
+ "User controlled PLMN Selector with Access Technology"),
+ EF_TRANSP_N(0x6F61, SFI_NONE, "EF.OPLMNwAcT", F_OPTIONAL, 40, 80,
+ "Operator controlled PLMN Selector with Access Technology"),
+ EF_TRANSP_N(0x6F62, SFI_NONE, "EF.HPLMNwAcT", F_OPTIONAL, 5, 20,
+ "HPLMN Selector with Access Technology"),
+ EF_TRANSP_N(0x6F63, SFI_NONE, "EF.CPBCCH", F_OPTIONAL, 2, 20,
+ "CPBCCH Information"),
+ EF_TRANSP_N(0x6F64, SFI_NONE, "EF.InvScan", F_OPTIONAL, 1, 1,
+ "Investigation Scan"),
+ EF_LIN_FIX_N(0x6FC5, SFI_NONE, "EF.PNN", F_OPTIONAL, 3, 20,
+ "PLMN Network Name"),
+ EF_LIN_FIX_N(0x6FC6, SFI_NONE, "EF.OPL", F_OPTIONAL, 8, 8,
+ "PLMN Operator PLMN List"),
+ EF_LIN_FIX_N(0x6FC7, SFI_NONE, "EF.MBDN", F_OPTIONAL, 14, 30,
+ "Mailbox Dialling Number"),
+ EF_LIN_FIX_N(0x6FC9, SFI_NONE, "EF.MBI", F_OPTIONAL, 4, 4,
+ "Maibox Identifier"),
+ EF_LIN_FIX_N(0x6FCA, SFI_NONE, "EF.MWIS", F_OPTIONAL, 5, 5,
+ "Message Waiting Indication Status"),
+ EF_LIN_FIX_N(0x6FCB, SFI_NONE, "EF.CFIS", F_OPTIONAL, 16, 16,
+ "Call Forwarding Indication Status"),
+ EF_LIN_FIX_N(0x6FC8, SFI_NONE, "EF.EXT6", F_OPTIONAL, 13, 13,
+ "Extension6 (MBDN)"),
+ EF_LIN_FIX_N(0x6FCC, SFI_NONE, "EF.EXT7", F_OPTIONAL, 13, 13,
+ "Extension7 (CFIS)"),
+ EF_TRANSP_N(0x6FCD, SFI_NONE, "EF.SPDI", F_OPTIONAL, 1, 32,
+ "Extension7 (CFIS)"),
+ EF_LIN_FIX_N(0x6FCE, SFI_NONE, "EF.MMSN", F_OPTIONAL, 4, 32,
+ "MMS Notification"),
+ EF_LIN_FIX_N(0x6FCF, SFI_NONE, "EF.EXT8", F_OPTIONAL, 2, 18,
+ "Extension8 (MMSN)"),
+ EF_TRANSP_N(0x6FD0, SFI_NONE, "EF.MMSICP", F_OPTIONAL, 1, 64,
+ "MMS Issuer Connectivity Parameters"),
+ EF_LIN_FIX_N(0x6FD1, SFI_NONE, "EF.MMSUP", F_OPTIONAL, 1, 64,
+ "MMS User Preferences"),
+ EF_TRANSP_N(0x6FD2, SFI_NONE, "EF.MMSUCP", F_OPTIONAL, 1, 64,
+ "MMS User Connectivity Parameters"),
+};
+
+/* 10.4.1 Contents of the files at the SoLSA level */
+static const struct osim_file_desc sim_ef_in_solsa[] = {
+ EF_TRANSP_N(0x4F30, SFI_NONE, "EF.SAI", F_OPTIONAL, 1, 32,
+ "SoLSA Access Indicator"),
+ EF_LIN_FIX_N(0x4F31, SFI_NONE, "EF.SLL", F_OPTIONAL, 1, 32,
+ "SoLSA LSA List"),
+ /* LSA Descriptor files */
+};
+
+/* 10.4.2 Contents of files at the MExE level */
+static const struct osim_file_desc sim_ef_in_mexe[] = {
+ EF_TRANSP_N(0x4F40, SFI_NONE, "EF.MExE-ST", F_OPTIONAL, 1, 8,
+ "MExE Service table"),
+ EF_LIN_FIX_N(0x4F41, SFI_NONE, "EF.ORPK", F_OPTIONAL, 11, 32,
+ "Operator Root Public Key"),
+ EF_LIN_FIX_N(0x4F42, SFI_NONE, "EF.ARPK", F_OPTIONAL, 11, 32,
+ "Administrator Root Public Key"),
+ EF_LIN_FIX_N(0x4F43, SFI_NONE, "EF.TRPK", F_OPTIONAL, 11, 32,
+ "Third Party Root Public Key"),
+};
+
+/* 10.5 Contents of files at the telecom level */
+static const struct osim_file_desc sim_ef_in_telecom[] = {
+ EF_LIN_FIX_N(0x6F3A, SFI_NONE, "EF.ADN", F_OPTIONAL, 14, 30,
+ "Abbreviated dialling numbers"),
+ EF_LIN_FIX_N(0x6F3B, SFI_NONE, "EF.FDN", F_OPTIONAL, 14, 30,
+ "Fixed dialling numbers"),
+ EF_LIN_FIX_N(0x6F3C, SFI_NONE, "EF.SMS", F_OPTIONAL, 176, 176,
+ "Short messages"),
+ EF_LIN_FIX_N(0x6F3D, SFI_NONE, "EF.CCP", F_OPTIONAL, 14, 14,
+ "Capability configuration parameters"),
+ EF_LIN_FIX_N(0x6F4F, SFI_NONE, "EF.ECCP", F_OPTIONAL, 15, 15,
+ "Extended Capability configuration parameters"),
+ EF_LIN_FIX_N(0x6F40, SFI_NONE, "EF.MSISDN", F_OPTIONAL, 14, 30,
+ "MSISDN"),
+ EF_LIN_FIX_N(0x6F42, SFI_NONE, "EF.SMSP", F_OPTIONAL, 28, 44,
+ "Short message service parameters"),
+ EF_TRANSP_N(0x6F43, SFI_NONE, "EF.SMSS", F_OPTIONAL, 2, 3,
+ "SMS Status"),
+ EF_CYCLIC_N(0x6F44, SFI_NONE, "EF.LND", F_OPTIONAL, 14, 30,
+ "Last number dialled"),
+ EF_LIN_FIX_N(0x6F49, SFI_NONE, "EF.SDN", F_OPTIONAL, 14, 30,
+ "Service Dialling Numbers"),
+ EF_LIN_FIX_N(0x6F4A, SFI_NONE, "EF.EXT1", F_OPTIONAL, 13, 13,
+ "Extension 1 (ADN/SSC, MSISDN, LND)"),
+ EF_LIN_FIX_N(0x6F4B, SFI_NONE, "EF.EXT2", F_OPTIONAL, 13, 13,
+ "Extension 2 (FDN/SSC)"),
+ EF_LIN_FIX_N(0x6F4C, SFI_NONE, "EF.EXT3", F_OPTIONAL, 13, 13,
+ "Extension 3 (SDN)"),
+ EF_LIN_FIX_N(0x6F4D, SFI_NONE, "EF.BDN", F_OPTIONAL, 15, 31,
+ "Barred dialling numbers"),
+ EF_LIN_FIX_N(0x6F4E, SFI_NONE, "EF.EXT4", F_OPTIONAL, 13, 13,
+ "Extension 4 (BDN/SSC)"),
+ EF_LIN_FIX_N(0x6F47, SFI_NONE, "EF.SMSR", F_OPTIONAL, 30, 30,
+ "Short message status reports"),
+ EF_LIN_FIX_N(0x6F58, SFI_NONE, "EF.CMI", F_OPTIONAL, 1, 17,
+ "Comparison Method Information"),
+};
+
+/* 10.6.1 Contents of files at the telecom graphics level */
+const struct osim_file_desc sim_ef_in_graphics[] = {
+ EF_LIN_FIX_N(0x4F20, SFI_NONE, "EF.IMG", F_OPTIONAL, 11, 38,
+ "Image"),
+};
+
+int osim_int_cprof_add_gsm(struct osim_file_desc *mf)
+{
+ struct osim_file_desc *gsm;
+
+ gsm = add_df_with_ef(mf, 0x7F20, "DF.GSM", sim_ef_in_gsm,
+ ARRAY_SIZE(sim_ef_in_gsm));
+ /* Chapter 10.2: DFs at the GSM Application Level */
+ add_df_with_ef(gsm, 0x5F30, "DF.IRIDIUM", NULL, 0);
+ add_df_with_ef(gsm, 0x5F31, "DF.GLOBALSTAR", NULL, 0);
+ add_df_with_ef(gsm, 0x5F32, "DF.ICO", NULL, 0);
+ add_df_with_ef(gsm, 0x5F33, "DF.ACeS", NULL, 0);
+ add_df_with_ef(gsm, 0x5F3C, "DF.MExE", sim_ef_in_mexe,
+ ARRAY_SIZE(sim_ef_in_mexe));
+ add_df_with_ef(gsm, 0x5F40, "DF.EIA/TIA-533", NULL, 0);
+ add_df_with_ef(gsm, 0x5F60, "DF.CTS", NULL, 0);
+ add_df_with_ef(gsm, 0x5F70, "DF.SoLSA", sim_ef_in_solsa,
+ ARRAY_SIZE(sim_ef_in_solsa));
+
+ return 0;
+}
+
+int osim_int_cprof_add_telecom(struct osim_file_desc *mf)
+{
+ struct osim_file_desc *tc;
+
+ tc = add_df_with_ef(mf, 0x7F10, "DF.TELECOM", sim_ef_in_telecom,
+ ARRAY_SIZE(sim_ef_in_telecom));
+ add_df_with_ef(tc, 0x5F50, "DF.GRAPHICS", sim_ef_in_graphics,
+ ARRAY_SIZE(sim_ef_in_graphics));
+ add_df_with_ef(mf, 0x7F22, "DF.IS-41", NULL, 0);
+ add_df_with_ef(mf, 0x7F23, "DF.FP-CTS", NULL, 0); /* TS 11.19 */
+
+ return 0;
+}
+
+struct osim_card_profile *osim_cprof_sim(void *ctx)
+{
+ struct osim_card_profile *cprof;
+ struct osim_file_desc *mf;
+ int rc;
+
+ cprof = talloc_zero(ctx, struct osim_card_profile);
+ cprof->name = "GSM SIM";
+ cprof->sws = sim_card_sws;
+
+ mf = alloc_df(cprof, 0x3f00, "MF");
+
+ cprof->mf = mf;
+
+ /* According to Figure 8 */
+ add_filedesc(mf, sim_ef_in_mf, ARRAY_SIZE(sim_ef_in_mf));
+
+ rc = osim_int_cprof_add_gsm(mf);
+ rc |= osim_int_cprof_add_telecom(mf);
+ if (rc != 0) {
+ talloc_free(cprof);
+ return NULL;
+ }
+
+ return cprof;
+}
diff --git a/src/shared/libosmocore/src/sim/card_fs_tetra.c b/src/shared/libosmocore/src/sim/card_fs_tetra.c
new file mode 100644
index 00000000..657e55f7
--- /dev/null
+++ b/src/shared/libosmocore/src/sim/card_fs_tetra.c
@@ -0,0 +1,267 @@
+/* TETRA SIM card specific structures/routines */
+/*
+ * (C) 2014 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include <osmocom/sim/sim.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/gsm/gsm48.h>
+
+#include "sim_int.h"
+#include "gsm_int.h"
+
+/* EN 300 812 V2.1.1 (2001-12) 9.4 */
+static const struct osim_card_sw tsim_sw[] = {
+ {
+ 0x9000, 0xffff, SW_TYPE_STR, SW_CLS_OK,
+ .u.str = "Normal ending of the command",
+ }, {
+ 0x9f00, 0xff00, SW_TYPE_STR, SW_CLS_OK,
+ .u.str = "Normal ending of the command - response data available",
+ }, {
+ 0x9300, 0xffff, SW_TYPE_STR, SW_CLS_POSTP,
+ .u.str = "SIM Application Toolkit is busy, command cannot be executed at present",
+ }, {
+ 0x9200, 0xfff0, SW_TYPE_STR, SW_CLS_WARN,
+ .u.str = "Memory management - Command successful but after using an internal updat retry X times",
+ }, {
+ 0x9240, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Memory management - Memory problem",
+ }, {
+ 0x9400, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Referencing management - no EF selected",
+ }, {
+ 0x9402, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Referencing management - out of range (invalid address)",
+ }, {
+ 0x9404, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Referencing management - file ID not found / pattern not found",
+ }, {
+ 0x9408, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Referencing management - file is inconsistent with the command",
+ }, {
+ 0x9802, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Security management - no CHV initialized",
+ }, {
+ 0x9804, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Security management - access condition not fulfilled",
+ }, {
+ 0x9808, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Security management - in contradiction with CHV status",
+ }, {
+ 0x9810, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Security management - in contradiction with invalidation status",
+ }, {
+ 0x9840, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Security management - unsuccessful CHV verification, no attempt left",
+ }, {
+ 0x9860, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Security management - manipulation flag set",
+ }, {
+ 0x9870, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Security management - SwMI authentication unsuccessful",
+ }, {
+ 0x6700, 0xff00, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Application independent - incorrect parameter P3",
+ }, {
+ 0x6b00, 0xff00, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Application independent - incorrect parameter P1 or P2",
+ }, {
+ 0x6d00, 0xff00, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Application independent - unknown instruction code",
+ }, {
+ 0x6e00, 0xff00, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Application independent - wrong instruction class",
+ }, {
+ 0x6f00, 0xff00, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Application independent - technical problem with no diagnostic given",
+ },
+ OSIM_CARD_SW_LAST
+};
+
+static const struct osim_card_sw *tsim_card_sws[] = {
+ tsim_sw,
+ NULL
+};
+
+/* Chapter 10.2.x */
+static const struct osim_file_desc sim_ef_in_mf[] = {
+ EF_TRANSP_N(0x2FE2, SFI_NONE, "EF.ICCID", 0, 10, 10,
+ "ICC Identification"),
+ EF_TRANSP_N(0x2F00, SFI_NONE, "EF.DIR", F_OPTIONAL, 8, 54,
+ "Application directory"),
+ EF_TRANSP_N(0x2F05, SFI_NONE, "EF.LP", 0, 2, 32,
+ "Language preference"),
+};
+
+////////////////////////////
+
+/* Chapter 10.3.x */
+static const struct osim_file_desc sim_ef_in_tetra[] = {
+ EF_TRANSP_N(0x6F01, SFI_NONE, "EF.SST", 0, 4, 8,
+ "SIM service table"),
+ EF_TRANSP_N(0x6F02, SFI_NONE, "EF.ITSI", 0, 6, 6,
+ "ITSI"),
+ EF_TRANSP_N(0x6F03, SFI_NONE, "EF.ITSIDIS", 0, 1, 1,
+ "ITSI Disable"),
+ EF_TRANSP_N(0x6F05, SFI_NONE, "EF.SCT", 0, 4, 4,
+ "Subscriber Class Table"),
+ EF_TRANSP_N(0x6F06, SFI_NONE, "EF.PHASE", 0, 1, 1,
+ "Phase identification"),
+ EF_KEY_N(0x6F07, SFI_NONE, "EF.CCK", F_OPTIONAL, 12, 12,
+ "Common Cipher Key"),
+ EF_TRANSP_N(0x6F08, SFI_NONE, "EF.CCKLOC", F_OPTIONAL, 31, 31,
+ "CCK location areas"),
+ EF_KEY_N(0x6F09, SFI_NONE, "EF.SCK", F_OPTIONAL, 12, 12,
+ "Static Cipher Keys"),
+ /* X+4 for each record, suggested 1 to 10 */
+ EF_LIN_FIX_N(0x6F0A, SFI_NONE, "EF.GSSIS", 0, 5, 21,
+ "Static Cipher Keys"),
+ /* 2 for each record, one for each recod in GSSIS */
+ EF_LIN_FIX_N(0x6F0B, SFI_NONE, "EF.GRDS", 0, 2, 2,
+ "Group related data for static GSSIS"),
+ EF_LIN_FIX_N(0x6F0C, SFI_NONE, "EF.GSSID", 0, 5, 21,
+ "Dynamic GSSIs"),
+ /* 2 for each record, one for each recod in GSSID */
+ EF_LIN_FIX_N(0x6F0D, SFI_NONE, "EF.GRDD", 0, 2, 2,
+ "Dynamic GSSIs"),
+ EF_LIN_FIX_N(0x6F0E, SFI_NONE, "EF.GCK", F_OPTIONAL, 12, 12,
+ "Group Cipher Keys"),
+ EF_KEY_N(0x6F0F, SFI_NONE, "EF.MGCK", F_OPTIONAL, 12, 12,
+ "Modified Group Cipher Keys"),
+ EF_TRANSP_N(0x6F10, SFI_NONE, "EF.GINFO", 0, 9, 9,
+ "User's group information"),
+ EF_TRANSP_N(0x6F11, SFI_NONE, "EF.SEC", 0, 1, 1,
+ "Security settings"),
+ EF_CYCLIC_N(0x6F12, SFI_NONE, "EF.FORBID", 0, 3, 3,
+ "Security settings"),
+ EF_CYCLIC_N(0x6F13, SFI_NONE, "EF.PREF", F_OPTIONAL, 3, 3,
+ "Preferred networks"),
+ EF_TRANSP_N(0x6F14, SFI_NONE, "EF.SPN", F_OPTIONAL, 17, 17,
+ "Service Provider Name"),
+ EF_TRANSP_N(0x6F15, SFI_NONE, "EF.LOCI", F_OPTIONAL, 7, 7,
+ "Location Information"),
+ EF_TRANSP_N(0x6F16, SFI_NONE, "EF.DNWRK", 0, 3, 3,
+ "Broadcast network information"),
+ EF_LIN_FIX_N(0x6F17, SFI_NONE, "EF.NWT", 0, 5, 5,
+ "Network table"),
+ EF_LIN_FIX_N(0x6F18, SFI_NONE, "EF.GWT", F_OPTIONAL, 13, 13,
+ "Gateway table"),
+ EF_LIN_FIX_N(0x6F19, SFI_NONE, "EF.CMT", F_OPTIONAL, 5, 20,
+ "Call Modifier Table"),
+ EF_LIN_FIX_N(0x6F1A, SFI_NONE, "EF.ADNGWT", F_OPTIONAL, 13, 28,
+ "Abbreviated Dialling Number with Gateways"),
+ EF_LIN_FIX_N(0x6F1C, SFI_NONE, "EF.ADNTETRA", F_OPTIONAL, 9, 23,
+ "Abbreviated dialling numbers for TETRA network"),
+ EF_LIN_FIX_N(0x6F1D, SFI_NONE, "EF.EXTA", F_OPTIONAL, 20, 20,
+ "Extension A"),
+ EF_LIN_FIX_N(0x6F1E, SFI_NONE, "EF.FDNGWT", F_OPTIONAL, 13, 28,
+ "Fixed dialling numbers with Gateways"),
+ EF_LIN_FIX_N(0x6F1F, SFI_NONE, "EF.GWTEXT2", F_OPTIONAL, 13, 13,
+ "Gateway Extension2"),
+ EF_LIN_FIX_N(0x6F20, SFI_NONE, "EF.FDNTETRA", F_OPTIONAL, 9, 25,
+ "Fixed dialling numbers for TETRA network"),
+ EF_LIN_FIX_N(0x6F21, SFI_NONE, "EF.EXTB", F_OPTIONAL, 20, 20,
+ "Extension B"),
+ EF_LIN_FIX_N(0x6F22, SFI_NONE, "EF.LNDGWT", F_OPTIONAL, 13, 28,
+ "Last number dialled with Gateways"),
+ EF_CYCLIC_N(0x6F23, SFI_NONE, "EF.LNDTETRA", F_OPTIONAL, 9, 23,
+ "Last numbers dialled for TETRA network"),
+ EF_LIN_FIX_N(0x6F24, SFI_NONE, "EF.SDNGWT", F_OPTIONAL, 13, 28,
+ "Service Dialling Numbers with gateway"),
+ EF_LIN_FIX_N(0x6F25, SFI_NONE, "EF.GWTEXT3", F_OPTIONAL, 13, 13,
+ "Gateway Extension3"),
+ EF_LIN_FIX_N(0x6F26, SFI_NONE, "EF.SDNTETRA", F_OPTIONAL, 8, 22,
+ "Service Dialling Nubers for TETRA network"),
+ EF_LIN_FIX_N(0x6F27, SFI_NONE, "EF.STXT", F_OPTIONAL, 5, 18,
+ "Status message texts"),
+ EF_LIN_FIX_N(0x6F28, SFI_NONE, "EF.MSGTXT", F_OPTIONAL, 5, 18,
+ "SDS-1 message texts"),
+ EF_LIN_FIX_N(0x6F29, SFI_NONE, "EF.SDS123", F_OPTIONAL, 46, 46,
+ "Status and SDS type 1, 2 and 3 message storage"),
+ EF_LIN_FIX_N(0x6F2A, SFI_NONE, "EF.SDS4", F_OPTIONAL, 255, 255,
+ "Status and SDS type 4 message storage"),
+ EF_LIN_FIX_N(0x6F2B, SFI_NONE, "EF.MSGEXT", F_OPTIONAL, 16, 16,
+ "Message Extension"),
+ EF_LIN_FIX_N(0x6F2C, SFI_NONE, "EF.EADDR", 0, 17, 17,
+ "Emergency addresses"),
+ EF_TRANSP_N(0x6F2D, SFI_NONE, "EF.EINFO", 0, 2, 2,
+ "Emergency call information"),
+ EF_LIN_FIX_N(0x6F2E, SFI_NONE, "EF.DMOCh", F_OPTIONAL, 4, 4,
+ "DMO channel information"),
+ EF_TRANSP_N(0x6F2F, SFI_NONE, "EF.MSCh", F_OPTIONAL, 1, 16,
+ "MS allocation of DMO channels"),
+ EF_TRANSP_N(0x6F30, SFI_NONE, "EF.KH", F_OPTIONAL, 6, 6,
+ "List of Key Holders"),
+ EF_LIN_FIX_N(0x6F31, SFI_NONE, "EF.REPGATE", F_OPTIONAL, 2, 2,
+ "DMO repeater and gateway list"),
+ EF_TRANSP_N(0x6F32, SFI_NONE, "EF.AD", F_OPTIONAL, 2, 2,
+ "Administrative Data"),
+ EF_TRANSP_N(0x6F33, SFI_NONE, "EF.PREF_LA", F_OPTIONAL, 2, 2,
+ "Preferred location areas"),
+ EF_CYCLIC_N(0x6F34, SFI_NONE, "EF.LNDComp", F_OPTIONAL, 3, 3,
+ "Composite LND file"),
+ EF_TRANSP_N(0x6F35, SFI_NONE, "EF.DFLTSTSTGGT", F_OPTIONAL, 16, 16,
+ "Status Default Target"),
+ EF_TRANSP_N(0x6F36, SFI_NONE, "EF.SDSMEM_STATUS", F_OPTIONAL, 7, 7,
+ "SDS Memory Status"),
+ EF_TRANSP_N(0x6F37, SFI_NONE, "EF.WELCOME", F_OPTIONAL, 32, 32,
+ "Welcome Message"),
+ EF_LIN_FIX_N(0x6F38, SFI_NONE, "EF.SDSR", F_OPTIONAL, 2, 2,
+ "SDS delivery report"),
+ EF_LIN_FIX_N(0x6F39, SFI_NONE, "EF.SDSP", F_OPTIONAL, 20, 35,
+ "SDS parameters"),
+ EF_TRANSP_N(0x6F46, SFI_NONE, "EF.DIALSC", 0, 5, 5,
+ "Dialling schemes for TETRA network"),
+ EF_TRANSP_N(0x6F3E, SFI_NONE, "EF.APN", F_OPTIONAL, 65, 65,
+ "APN table"),
+ EF_LIN_FIX_N(0x6FC0, SFI_NONE, "EF.PNI", F_OPTIONAL, 14, 14,
+ "Private Number Information"),
+};
+
+struct osim_card_profile *osim_cprof_tsim(void *ctx)
+{
+ struct osim_card_profile *cprof;
+ struct osim_file_desc *mf;
+ int rc;
+
+ cprof = talloc_zero(ctx, struct osim_card_profile);
+ cprof->name = "TETRA SIM";
+ cprof->sws = tsim_card_sws;
+
+ mf = alloc_df(cprof, 0x3f00, "MF");
+
+ cprof->mf = mf;
+
+ add_filedesc(mf, sim_ef_in_mf, ARRAY_SIZE(sim_ef_in_mf));
+ add_df_with_ef(mf, 0x7F20, "DF.TETRA", sim_ef_in_tetra,
+ ARRAY_SIZE(sim_ef_in_tetra));
+
+ rc = osim_int_cprof_add_telecom(mf);
+ if (rc != 0) {
+ talloc_free(cprof);
+ return NULL;
+ }
+
+ return cprof;
+}
diff --git a/src/shared/libosmocore/src/sim/card_fs_uicc.c b/src/shared/libosmocore/src/sim/card_fs_uicc.c
new file mode 100644
index 00000000..70737b6f
--- /dev/null
+++ b/src/shared/libosmocore/src/sim/card_fs_uicc.c
@@ -0,0 +1,208 @@
+/* ETSI UICC specific structures / routines */
+/*
+ * (C) 2012 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+#include <osmocom/sim/sim.h>
+#include <osmocom/gsm/tlv.h>
+
+/* TS 102 221 V10.0.0 / 10.2.1 */
+const struct osim_card_sw ts102221_uicc_sw[] = {
+ {
+ 0x9000, 0xffff, SW_TYPE_STR, SW_CLS_OK,
+ .u.str = "Normal ending of the command",
+ }, {
+ 0x9100, 0xff00, SW_TYPE_STR, SW_CLS_OK,
+ .u.str = "Normal ending of the command, extra info proactive",
+ }, {
+ 0x9200, 0xff00, SW_TYPE_STR, SW_CLS_OK,
+ .u.str = "Normal ending of the command, extra info regarding transfer session",
+ }, {
+ 0x9300, 0xff00, SW_TYPE_STR, SW_CLS_POSTP,
+ .u.str = "SIM Application Toolkit is busy, command cannot be executed at present",
+ }, {
+ 0x6200, 0xffff, SW_TYPE_STR, SW_CLS_WARN,
+ .u.str = "No information given, state of non volatile memory unchanged",
+ }, {
+ 0x6281, 0xffff, SW_TYPE_STR, SW_CLS_WARN,
+ .u.str = "Part of returned data may be corrupted",
+ }, {
+ 0x6282, 0xffff, SW_TYPE_STR, SW_CLS_WARN,
+ .u.str = "End of file/record reached before reading Le bytes",
+ }, {
+ 0x6283, 0xffff, SW_TYPE_STR, SW_CLS_WARN,
+ .u.str = "Selected file invalidated",
+ }, {
+ 0x6285, 0xffff, SW_TYPE_STR, SW_CLS_WARN,
+ .u.str = "Selected file in termination state",
+ }, {
+ 0x62f1, 0xffff, SW_TYPE_STR, SW_CLS_WARN,
+ .u.str = "More data available",
+ }, {
+ 0x62f2, 0xffff, SW_TYPE_STR, SW_CLS_WARN,
+ .u.str = "More data available and proactive command pending",
+ }, {
+ 0x62f3, 0xffff, SW_TYPE_STR, SW_CLS_WARN,
+ .u.str = "Response data available",
+ }, {
+ 0x63f1, 0xffff, SW_TYPE_STR, SW_CLS_WARN,
+ .u.str = "More data expected",
+ }, {
+ 0x63c0, 0xfff0, SW_TYPE_STR, SW_CLS_WARN,
+ .u.str = "Verification falied, X retries remaining",
+ }, {
+ 0x6400, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Execution - No information given, state of non-volatile memory unchanged",
+ }, {
+ 0x6500, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Execution - No information given, state of non-volatile memory changed",
+ }, {
+ 0x6581, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Execution - Memory problem",
+ }, {
+ 0x6700, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Checking - Wrong length",
+ }, {
+ 0x6700, 0xff00, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Checking - Command dependent error",
+ }, {
+ 0x6b00, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Checking - Wrong parameter(s) P1-P2",
+ }, {
+ 0x6d00, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Checking - Instruction code not supported or valid",
+ }, {
+ 0x6e00, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Checking - Class not supported",
+ }, {
+ 0x6f00, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Checking - Technical problem, no precise diagnostics",
+ }, {
+ 0x6f00, 0xff00, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Checking - Command dependent error",
+ }, {
+ 0x6800, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Function in CLA not supported - No information given",
+ }, {
+ 0x6881, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Function in CLA not supported - Logical channel not supported",
+ }, {
+ 0x6882, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Function in CLA not supportied - Secure messaging not supported",
+ }, {
+ 0x6900, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Command not allowed - No information given",
+ }, {
+ 0x6981, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Command not allowed - Command incompatible with file structure",
+ }, {
+ 0x6982, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Command not allowed - Security status not satisfied",
+ }, {
+ 0x6983, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Command not allowed - Authentication/PIN method blocked",
+ }, {
+ 0x6984, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Command not allowed - Referenced data invalidated",
+ }, {
+ 0x6985, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Command not allowed - Conditions of use not satisfied",
+ }, {
+ 0x6986, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Command not allowed - Noe EF selected",
+ }, {
+ 0x6989, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Command not allowed - secure channel - security not satisfied",
+ }, {
+ 0x6a80, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Wrong parameters - Incorrect parameters in the data field",
+ }, {
+ 0x6a81, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Wrong parameters - Function not supported",
+ }, {
+ 0x6a82, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Wrong parameters - File not found",
+ }, {
+ 0x6a83, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Wrong parameters - Record not found",
+ }, {
+ 0x6a84, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Wrong parameters - Not enough memory space",
+ }, {
+ 0x6a86, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Wrong parameters - Incorrect parameters P1 to P2",
+ }, {
+ 0x6a87, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Wrong parameters - Lc inconsistent with P1 ot P2",
+ }, {
+ 0x6a88, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Wrong parameters - Referenced data not found",
+ }, {
+ 0x9850, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Application error - INCREASE cannot be performed, max value reached",
+ }, {
+ 0x9862, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Application error - Authentication error, application specific",
+ }, {
+ 0x9863, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Application error - Security session or association expired",
+ },
+ OSIM_CARD_SW_LAST
+};
+
+const struct value_string ts102221_fcp_vals[14] = {
+ { UICC_FCP_T_FCP, "File control parameters" },
+ { UICC_FCP_T_FILE_SIZE, "File size" },
+ { UICC_FCP_T_TOT_F_SIZE, "Total size of files" },
+ { UICC_FCP_T_FILE_DESC, "File descriptor" },
+ { UICC_FCP_T_FILE_ID, "File identifier" },
+ { UICC_FCP_T_DF_NAME, "DF name" },
+ { UICC_FCP_T_SFID, "Short file identifier" },
+ { UICC_FCP_T_LIFEC_STS, "Lifecycle status integer" },
+ { UICC_FCP_T_SEC_ATTR_REFEXP, "Security attributes (Referenced/Expanded)" },
+ { UICC_FCP_T_SEC_ATTR_COMP, "Security attributes (Compact)" },
+ { UICC_FCP_T_PROPRIETARY, "Proprietary" },
+ { UICC_FCP_T_SEC_ATTR_EXP, "Security attributes (Expanded)" },
+ { UICC_FCP_T_PIN_STS_DO, "PIN Status DO" },
+ { 0, NULL }
+};
+
+/* FIXME: Ber-TLV ?? */
+const struct tlv_definition ts102221_fcp_tlv_def = {
+ .def = {
+ [UICC_FCP_T_FCP] = { TLV_TYPE_TLV },
+ [UICC_FCP_T_FILE_SIZE] = { TLV_TYPE_TLV },
+ [UICC_FCP_T_TOT_F_SIZE] = { TLV_TYPE_TLV },
+ [UICC_FCP_T_FILE_DESC] = { TLV_TYPE_TLV },
+ [UICC_FCP_T_FILE_ID] = { TLV_TYPE_TLV },
+ [UICC_FCP_T_DF_NAME] = { TLV_TYPE_TLV },
+ [UICC_FCP_T_SFID] = { TLV_TYPE_TLV },
+ [UICC_FCP_T_LIFEC_STS] = { TLV_TYPE_TLV },
+ [UICC_FCP_T_SEC_ATTR_REFEXP] = { TLV_TYPE_TLV },
+ [UICC_FCP_T_SEC_ATTR_COMP] = { TLV_TYPE_TLV },
+ [UICC_FCP_T_PROPRIETARY] = { TLV_TYPE_TLV },
+ [UICC_FCP_T_SEC_ATTR_EXP] = { TLV_TYPE_TLV },
+ [UICC_FCP_T_PIN_STS_DO] = { TLV_TYPE_TLV },
+ },
+};
+
+/* Annex E - TS 101 220 */
+static const uint8_t adf_uicc_aid[] = { 0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x01 };
diff --git a/src/shared/libosmocore/src/sim/card_fs_usim.c b/src/shared/libosmocore/src/sim/card_fs_usim.c
new file mode 100644
index 00000000..22c193f8
--- /dev/null
+++ b/src/shared/libosmocore/src/sim/card_fs_usim.c
@@ -0,0 +1,380 @@
+/* 3GPP USIM specific structures / routines */
+/*
+ * (C) 2012-2014 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+#include <errno.h>
+#include <string.h>
+
+#include <osmocom/sim/sim.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/gsm/gsm48.h>
+
+#include "sim_int.h"
+#include "gsm_int.h"
+
+/* TS 31.102 Version 7.7.0 / Chapter 7.3 */
+const struct osim_card_sw ts31_102_sw[] = {
+ {
+ 0x9862, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Security management - Authentication error, incorrect MAC",
+ }, {
+ 0x9864, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Security management - Authentication error, security context not supported",
+ }, {
+ 0x9865, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Security management - Key freshness error",
+ },
+ OSIM_CARD_SW_LAST
+};
+
+static const struct osim_card_sw *usim_card_sws[] = {
+ ts31_102_sw,
+ ts102221_uicc_sw,
+ NULL
+};
+
+/* TS 102 221 Chapter 13.1 */
+static const struct osim_file_desc uicc_ef_in_mf[] = {
+ EF_LIN_FIX_N(0x2f00, SFI_NONE, "EF.DIR", 0, 1, 32,
+ "Application directory"),
+ EF_TRANSP_N(0x2FE2, SFI_NONE, "EF.ICCID", 0, 10, 10,
+ "ICC Identification"),
+ EF_TRANSP_N(0x2F05, SFI_NONE, "EF.PL", 0, 2, 20,
+ "Preferred Languages"),
+ EF_LIN_FIX_N(0x2F06, SFI_NONE, "EF.ARR", F_OPTIONAL, 1, 256,
+ "Access Rule Reference"),
+};
+
+/* 31.102 Chapter 4.4.3 */
+static const struct osim_file_desc usim_ef_in_df_gsm_access[] = {
+ EF_TRANSP_N(0x4f20, 0x01, "EF.Kc", 0, 9, 9,
+ "Ciphering Key Kc"),
+ EF_TRANSP_N(0x4f52, 0x02, "EF.KcGPRS", F_OPTIONAL, 9, 9,
+ "GPRS Ciphering key KcGPRS"),
+ EF_TRANSP_N(0x4f63, SFI_NONE, "EF.CPBCCH", F_OPTIONAL, 2, 20,
+ "CPBCCH Information"),
+ EF_TRANSP_N(0x4f64, SFI_NONE, "EF.invSCAN", F_OPTIONAL, 1, 1,
+ "Investigation Scan"),
+};
+
+/* 31.102 Chapter 4.2 */
+static const struct osim_file_desc usim_ef_in_adf_usim[] = {
+ EF_TRANSP(0x6F05, 0x02, "EF.LI", 0, 2, 16,
+ "Language Indication", &gsm_lp_decode, NULL),
+ EF_TRANSP(0x6F07, 0x07, "EF.IMSI", 0, 9, 9,
+ "IMSI", &gsm_imsi_decode, NULL),
+ EF_TRANSP_N(0x6F08, 0x08, "EF.Keys", 0, 33, 33,
+ "Ciphering and Integrity Keys"),
+ EF_TRANSP_N(0x6F09, 0x09, "EF.KeysPS", 0, 33, 33,
+ "Ciphering and Integrity Keys for Packet Switched domain"),
+ EF_TRANSP_N(0x6F60, 0x0A, "EF.PLMNwAcT", F_OPTIONAL, 40, 80,
+ "User controlled PLMN Selector with Access Technology"),
+ EF_TRANSP(0x6F31, 0x12, "EF.HPPLMN", 0, 1, 1,
+ "Higher Priority PLMN search period", &gsm_hpplmn_decode, NULL),
+ EF_TRANSP_N(0x6F37, SFI_NONE, "EF.ACMmax", F_OPTIONAL, 3, 3,
+ "ACM maximum value"),
+ EF_TRANSP_N(0x6F38, 0x04, "EF.UST", 0, 1, 16,
+ "USIM Service Table"),
+ EF_CYCLIC_N(0x6F39, SFI_NONE, "EF.ACM", F_OPTIONAL, 3, 3,
+ "Accumulated call meter"),
+ EF_TRANSP_N(0x6F3E, SFI_NONE, "EF.GID1", F_OPTIONAL, 1, 4,
+ "Group Identifier Level 1"),
+ EF_TRANSP_N(0x6F3F, SFI_NONE, "EF.GID2", F_OPTIONAL, 1, 4,
+ "Group Identifier Level 2"),
+ EF_TRANSP_N(0x6F46, SFI_NONE, "EF.SPN", F_OPTIONAL, 17, 17,
+ "Service Provider Name"),
+ EF_TRANSP_N(0x6F41, SFI_NONE, "EF.PUCT", F_OPTIONAL, 5, 5,
+ "Price per unit and currency table"),
+ EF_TRANSP_N(0x6F45, SFI_NONE, "EF.CBMI", F_OPTIONAL, 2, 32,
+ "Cell broadcast massage identifier selection"),
+ EF_TRANSP_N(0x6F78, 0x06, "EF.ACC", 0, 2, 2,
+ "Access control class"),
+ EF_TRANSP_N(0x6F7B, 0x0D, "EF.FPLMN", 0, 12, 36,
+ "Forbidden PLMNs"),
+ EF_TRANSP_N(0x6F7E, 0x0B, "EF.LOCI", 0, 11, 11,
+ "Location information"),
+ EF_TRANSP_N(0x6FAD, 0x03, "EF.AD", 0, 5, 16,
+ "Administrative data"),
+ EF_TRANSP_N(0x6F48, 0x0E, "EF.CBMID", F_OPTIONAL, 2, 32,
+ "Cell Broadcast Message Identifier for Data Download"),
+ EF_TRANSP_N(0x6FB7, 0x01, "EF.ECC", F_OPTIONAL, 5, 32,
+ "Emergency Call Code"),
+ EF_TRANSP_N(0x6F50, SFI_NONE, "EF.CBMIR", F_OPTIONAL, 4, 32,
+ "Cell broadcast message identifier range selection"),
+ EF_TRANSP_N(0x6F73, 0x0C, "EF.PSLOCI", 0, 14, 14,
+ "Pacet Switched location information"),
+ EF_LIN_FIX_N(0x6F3B, SFI_NONE, "EF.FDN", F_OPTIONAL, 14, 32,
+ "Fixed dialling numbers"),
+ EF_LIN_FIX_N(0x6F3C, SFI_NONE, "EF.SMS", F_OPTIONAL, 176, 176,
+ "Short messages"),
+ EF_LIN_FIX_N(0x6F40, SFI_NONE, "EF.MSISDN", F_OPTIONAL, 14, 32,
+ "MSISDN"),
+ EF_LIN_FIX_N(0x6F42, SFI_NONE, "EF.SMSP", F_OPTIONAL, 28, 64,
+ "Short message service parameters"),
+ EF_TRANSP_N(0x6F43, SFI_NONE, "EF.SMSS", F_OPTIONAL, 2, 8,
+ "SMS Status"),
+ EF_LIN_FIX_N(0x6F49, SFI_NONE, "EF.SDN", F_OPTIONAL, 14, 32,
+ "Service Dialling Numbers"),
+ EF_LIN_FIX_N(0x6F4B, SFI_NONE, "EF.EXT2", F_OPTIONAL, 13, 13,
+ "Extension 2"),
+ EF_LIN_FIX_N(0x6F4C, SFI_NONE, "EF.EXT3", F_OPTIONAL, 13, 13,
+ "Extension 3"),
+ EF_LIN_FIX_N(0x6F47, SFI_NONE, "EF.SMSR", F_OPTIONAL, 30, 30,
+ "Short message status reports"),
+ EF_CYCLIC_N(0x6F80, 0x14, "EF.ICI", F_OPTIONAL, 28, 64,
+ "Incoming Calling Information"),
+ EF_CYCLIC_N(0x6F81, 0x15, "EF.OCI", F_OPTIONAL, 27, 64,
+ "Outgoing Calling Information"),
+ EF_CYCLIC_N(0x6F82, SFI_NONE, "EF.ICT", F_OPTIONAL, 3, 3,
+ "Incoming Call Timer"),
+ EF_CYCLIC_N(0x6F83, SFI_NONE, "EF.OCT", F_OPTIONAL, 3, 3,
+ "Outgoing Call Timer"),
+ EF_LIN_FIX_N(0x6F4E, SFI_NONE, "EF.EXT5", F_OPTIONAL, 13, 13,
+ "Extension 5"),
+ EF_LIN_FIX_N(0x6F4F, 0x16, "EF.CCP2", F_OPTIONAL, 15, 32,
+ "Capability Configuration Parameters 2"),
+ EF_TRANSP_N(0x6FB5, SFI_NONE, "EF.eMLPP", F_OPTIONAL, 2, 2,
+ "enhanced Multi Level Precedence and Pre-emption"),
+ EF_TRANSP_N(0x6FB6, SFI_NONE, "EF.AAeM", F_OPTIONAL, 1, 1,
+ "Automatic Answer for eMLPP Service"),
+ EF_TRANSP_N(0x6FC3, SFI_NONE, "EF.Hiddenkey", F_OPTIONAL, 4, 4,
+ "Key for hidden phone book entries"),
+ EF_LIN_FIX_N(0x6F4D, SFI_NONE, "EF.BDN", F_OPTIONAL, 15, 32,
+ "Barred Dialling Numbers"),
+ EF_LIN_FIX_N(0x6F4E, SFI_NONE, "EF.EXT4", F_OPTIONAL, 13, 13,
+ "Extension 4"),
+ EF_LIN_FIX_N(0x6F58, SFI_NONE, "EF.CMI", F_OPTIONAL, 2, 16,
+ "Comparison Method Information"),
+ EF_TRANSP_N(0x6F56, 0x05, "EF.EST", F_OPTIONAL, 1, 8,
+ "Enhanced Services Table"),
+ EF_TRANSP_N(0x6F57, SFI_NONE, "EF.ACL", F_OPTIONAL, 2, 256,
+ "Access Point Name Control List"),
+ EF_TRANSP_N(0x6F2C, SFI_NONE, "EF.DCK", F_OPTIONAL, 16, 16,
+ "Depersonalisation Control Keys"),
+ EF_TRANSP_N(0x6F32, SFI_NONE, "EF.CNL", F_OPTIONAL, 6, 46,
+ "Co-operative Network List"),
+ EF_TRANSP_N(0x6F5B, 0x0F, "EF.START-HFN", 0, 6, 6,
+ "Initialisation values for Hyperframe number"),
+ EF_TRANSP_N(0x6F5C, 0x10, "EF.THRESHOLD", 0, 3, 3,
+ "Maximum value of START"),
+ EF_TRANSP_N(0x6F61, 0x11, "EF.OPLMNwAcT", F_OPTIONAL, 40, 80,
+ "Operator controlled PLMN Selector with Access Technology"),
+ EF_TRANSP_N(0x6F62, 0x13, "EF.HPLMNwAcT", F_OPTIONAL, 5, 30,
+ "HPLMN Selector with Access Technology"),
+ EF_LIN_FIX_N(0x6F06, 0x17, "EF.ARR", 0, 1, 256,
+ "Access Rule Reference"),
+ EF_TRANSP_N(0x6FC4, SFI_NONE, "EF.NETPAR", 0, 46, 256,
+ "Network Parameters"),
+ EF_LIN_FIX_N(0x6FC5, 0x19, "EF.PNN", F_OPTIONAL, 3, 128,
+ "PLMN Network Name"),
+ EF_LIN_FIX_N(0x6FC6, 0x1A, "EF.OPL", F_OPTIONAL, 8, 32,
+ "Operator PLMN List"),
+ EF_LIN_FIX_N(0x6FC7, SFI_NONE, "EF.MBDN", F_OPTIONAL, 14, 32,
+ "Mailbox Dialling Numbers"),
+ EF_LIN_FIX_N(0x6FC8, SFI_NONE, "EF.EXT6", F_OPTIONAL, 13, 13,
+ "Extension 6"),
+ EF_LIN_FIX_N(0x6FC9, SFI_NONE, "EF.MBI", F_OPTIONAL, 4, 5,
+ "Mailbox Identifier"),
+ EF_LIN_FIX_N(0x6FCA, SFI_NONE, "EF.MWIS", F_OPTIONAL, 5, 6,
+ "Message Waiting Indication Status"),
+ EF_LIN_FIX_N(0x6FCB, SFI_NONE, "EF.CFIS", F_OPTIONAL, 16, 16,
+ "Call Forwarding Indication Status"),
+ EF_LIN_FIX_N(0x6FCC, SFI_NONE, "EF.EXT7", F_OPTIONAL, 13, 13,
+ "Extension 7"),
+ EF_TRANSP_N(0x6FCD, 0x1B, "EF.SPDI", F_OPTIONAL, 1, 64,
+ "Service Provider Display Information"),
+ EF_LIN_FIX_N(0x6FCE, SFI_NONE, "EF.MMSN", F_OPTIONAL, 4, 32,
+ "MMS Notification"),
+ EF_LIN_FIX_N(0x6FCF, SFI_NONE, "EF.EXT8", F_OPTIONAL, 3, 16,
+ "Extension 8"),
+ EF_TRANSP_N(0x6FD0, SFI_NONE, "EF.MMSICP", F_OPTIONAL, 3, 256,
+ "MMS Issuer Connectivity Parameters"),
+ EF_LIN_FIX_N(0x6FD1, SFI_NONE, "EF.MMSUP", F_OPTIONAL, 1, 64,
+ "MMS User Preferences"),
+ EF_TRANSP_N(0x6FD2, SFI_NONE, "EF.MMSUCP", F_OPTIONAL, 1, 64,
+ "MMS User Connectivity Parameters"),
+ EF_LIN_FIX_N(0x6FD3, SFI_NONE, "EF.NIA", F_OPTIONAL, 2, 64,
+ "Network's Indication of Alerting"),
+ EF_TRANSP_N(0x6FB1, SFI_NONE, "EF.VGCS", F_OPTIONAL, 4, 80,
+ "Voice Group Call Service"),
+ EF_TRANSP_N(0x6FB2, SFI_NONE, "EF.VGCSS", F_OPTIONAL, 7, 7,
+ "Voice Group Call Service Status"),
+ EF_TRANSP_N(0x6FB3, SFI_NONE, "EF.VBS", F_OPTIONAL, 4, 80,
+ "Voice Broadcast Service"),
+ EF_TRANSP_N(0x6FB4, SFI_NONE, "EF.VBSS", F_OPTIONAL, 7, 7,
+ "Voice Broadcast Service Status"),
+ EF_TRANSP_N(0x6FD4, SFI_NONE, "EF.VGCSCA", F_OPTIONAL, 2, 40,
+ "Voice Group Call Service Ciphering Algorithm"),
+ EF_TRANSP_N(0x6FD5, SFI_NONE, "EF.VBSCA", F_OPTIONAL, 2, 40,
+ "Voice Broadcast Service Ciphering Algorithm"),
+ EF_TRANSP_N(0x6FD6, SFI_NONE, "EF.GBABP", F_OPTIONAL, 4, 128,
+ "GBA Bootstrapping parameters"),
+ EF_LIN_FIX_N(0x6FD7, SFI_NONE, "EF.MSK", F_OPTIONAL, 20, 84,
+ "MBMS Serviec Key List"),
+ EF_LIN_FIX_N(0x6FD8, SFI_NONE, "EF.MUK", F_OPTIONAL, 1, 128,
+ "MBMS User Key"),
+ EF_LIN_FIX_N(0x6FDA, SFI_NONE, "EF.GBANL", F_OPTIONAL, 1, 128,
+ "GBA NAF List"),
+ EF_TRANSP_N(0x6FD9, 0x1D, "EF.EHPLMN", F_OPTIONAL, 3, 30,
+ "Equivalent HPLMN"),
+ EF_TRANSP_N(0x6FDB, SFI_NONE, "EF.EHPLMNPI", F_OPTIONAL, 1, 1,
+ "Equivalent HPLMN Presentation Indication"),
+ EF_TRANSP_N(0x6FDC, SFI_NONE, "EF.LRPLMNSI", F_OPTIONAL, 1, 1,
+ "Last RPLMN Selection Indication"),
+ EF_LIN_FIX_N(0x6FDD, SFI_NONE, "EF.NAFKCA", F_OPTIONAL, 1, 128,
+ "NAF Key Centre Address"),
+ EF_TRANSP_N(0x6FDE, SFI_NONE, "EF.SPNI", F_OPTIONAL, 1, 128,
+ "Service Provider Name Icon"),
+ EF_LIN_FIX_N(0x6FDF, SFI_NONE, "EF.PNNI", F_OPTIONAL, 1, 128,
+ "PLMN Network Name Icon"),
+ EF_LIN_FIX_N(0x6FE2, SFI_NONE, "EF.NCP-IP", F_OPTIONAL, 2, 256,
+ "Network Connectivity Parameters for USIM IP Connections"),
+ EF_TRANSP_N(0x6FE3, 0x1E, "EF.EPSLOCI", F_OPTIONAL, 18, 18,
+ "EPS location information"),
+ EF_LIN_FIX_N(0x6FE4, 0x18, "EF.EPSNSC", F_OPTIONAL, 54, 128,
+ "EPS NAS Security Context"),
+ EF_TRANSP_N(0x6FE6, SFI_NONE, "EF.UFC", F_OPTIONAL, 1, 8,
+ "USAT Facility Control"),
+ EF_TRANSP_N(0x6FE8, SFI_NONE, "EF.NASCONFIG", F_OPTIONAL, 1, 128,
+ "Non Access Stratum Configuration"),
+ EF_LIN_FIX_N(0x6FE7, SFI_NONE, "EF.UICCIARI", F_OPTIONAL, 1, 32,
+ "UICC IARI"),
+ EF_TRANSP_N(0x6FEC, SFI_NONE, "EF.PWS", F_OPTIONAL, 1, 32,
+ "Public Warning System"),
+};
+
+
+
+/* 31.102 Chapter 4.4.1 */
+static const struct osim_file_desc usim_ef_in_solsa[] = {
+ EF_TRANSP_N(0x4F30, SFI_NONE, "EF.SAI", F_OPTIONAL, 2, 32,
+ "SoLSA Access Indicator"),
+ EF_LIN_FIX_N(0x4F31, SFI_NONE, "EF.SLL", F_OPTIONAL, 11, 32,
+ "SoLSA LSA List"),
+ /* LSA descriptor files 4Fxx, hard to represent here */
+};
+
+/* 31.102 Chapter 4.4.4 */
+static const struct osim_file_desc usim_ef_in_df_mexe[] = {
+ EF_TRANSP_N(0x4F40, SFI_NONE, "EF.MexE-ST", F_OPTIONAL, 1, 4,
+ "MexE Service table"),
+ EF_LIN_FIX_N(0x4F41, SFI_NONE, "EF.ORPK", F_OPTIONAL, 10, 16,
+ "Operator Root Public Key"),
+ EF_LIN_FIX_N(0x4F42, SFI_NONE, "EF.ARPK", F_OPTIONAL, 10, 16,
+ "Administrator Root Public Key"),
+ EF_LIN_FIX_N(0x4F43, SFI_NONE, "EF.TPRPK", F_OPTIONAL, 10, 16,
+ "Third Party Root Public Key"),
+ /* TKCDF files 4Fxx, hard to represent here */
+};
+
+/* 31.102 Chapter 4.4.5 */
+static const struct osim_file_desc usim_ef_in_df_wlan[] = {
+ EF_TRANSP_N(0x4F41, 0x01, "EF.Pseudo", F_OPTIONAL, 2, 16,
+ "Pseudonym"),
+ EF_TRANSP_N(0x4F42, 0x02, "EF.UPLMNWLAN", F_OPTIONAL, 30, 60,
+ "User controlled PLMN selector for I-WLAN Access"),
+ EF_TRANSP_N(0x4F43, 0x03, "EF.OPLMNWLAN", F_OPTIONAL, 30, 60,
+ "Operator controlled PLMN selector for I-WLAN Access"),
+ EF_LIN_FIX_N(0x4F44, 0x04, "EF.UWSIDL", F_OPTIONAL, 1, 16,
+ "User controlled WLAN Specific Identifier List"),
+ EF_LIN_FIX_N(0x4F45, 0x05, "EF.OWSIDL", F_OPTIONAL, 1, 16,
+ "Operator controlled WLAN Specific Identifier List"),
+ EF_TRANSP_N(0x4F46, 0x06, "EF.WRI", F_OPTIONAL, 1, 64,
+ "WLAN Reauthentication Identity"),
+ EF_LIN_FIX_N(0x4F47, 0x07, "EF.HWSIDL", F_OPTIONAL, 1, 16,
+ "Home I-WLAN Specific Identifier List"),
+ EF_TRANSP_N(0x4F48, 0x08, "EF.WEPLMNPI", F_OPTIONAL, 1, 1,
+ "I-WLAN Equivalent HPLMN Presentation Indication"),
+ EF_TRANSP_N(0x4F49, 0x09, "EF.WHPI", F_OPTIONAL, 1, 1,
+ "I-WLAN HPLMN Priority Indiation"),
+ EF_TRANSP_N(0x4F4A, 0x0a, "EF.WLRPLMN", F_OPTIONAL, 3, 3,
+ "I-WLAN Last Registered PLMN"),
+};
+
+/* 31.102 Chapter 4.4.6 */
+static const struct osim_file_desc usim_ef_in_df_hnb[] = {
+ EF_LIN_FIX_N(0x4F81, 0x01, "EF.ACSGL", F_OPTIONAL, 1, 128,
+ "Allowed CSG Lists"),
+ EF_LIN_FIX_N(0x4F82, 0x02, "EF.CSGT", F_OPTIONAL, 1, 64,
+ "CSG Type"),
+ EF_LIN_FIX_N(0x4F83, 0x03, "EF.HNBN", F_OPTIONAL, 1, 64,
+ "Home NodeB Name"),
+ EF_LIN_FIX_N(0x4F84, 0x04, "EF.OCSGL", F_OPTIONAL, 1, 64,
+ "Operator CSG List"),
+ EF_LIN_FIX_N(0x4F85, 0x05, "EF.OCSGT", F_OPTIONAL, 1, 64,
+ "Operator CSG Type"),
+ EF_LIN_FIX_N(0x4F86, 0x06, "EF.OHNBN", F_OPTIONAL, 1, 64,
+ "Oprator Home NodeB Name"),
+};
+
+/* Annex E - TS 101 220 */
+static const uint8_t adf_usim_aid[] = { 0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x02 };
+
+struct osim_card_profile *osim_cprof_usim(void *ctx)
+{
+ struct osim_card_profile *cprof;
+ struct osim_file_desc *mf, *uadf;
+ int rc;
+
+ cprof = talloc_zero(ctx, struct osim_card_profile);
+ cprof->name = "3GPP USIM";
+ cprof->sws = usim_card_sws;
+
+ mf = alloc_df(cprof, 0x3f00, "MF");
+
+ cprof->mf = mf;
+
+ /* Core UICC Files */
+ add_filedesc(mf, uicc_ef_in_mf, ARRAY_SIZE(uicc_ef_in_mf));
+
+ /* ADF.USIM with its EF siblings */
+ uadf = add_adf_with_ef(mf, adf_usim_aid, sizeof(adf_usim_aid),
+ "ADF.USIM", usim_ef_in_adf_usim,
+ ARRAY_SIZE(usim_ef_in_adf_usim));
+
+ /* DFs under ADF.USIM */
+ add_df_with_ef(uadf, 0x5F3A, "DF.PHONEBOOK", NULL, 0);
+ add_df_with_ef(uadf, 0x5F3B, "DF.GSM-ACCESS", usim_ef_in_df_gsm_access,
+ ARRAY_SIZE(usim_ef_in_df_gsm_access));
+ add_df_with_ef(uadf, 0x5F3C, "DF.MexE", usim_ef_in_df_mexe,
+ ARRAY_SIZE(usim_ef_in_df_mexe));
+ add_df_with_ef(uadf, 0x5F40, "DF.WLAN", usim_ef_in_df_wlan,
+ ARRAY_SIZE(usim_ef_in_df_wlan));
+ /* Home-NodeB (femtocell) */
+ add_df_with_ef(uadf, 0x5F50, "DF.HNB", usim_ef_in_df_hnb,
+ ARRAY_SIZE(usim_ef_in_df_hnb));
+ /* Support of Localised Service Areas */
+ add_df_with_ef(uadf, 0x5F70, "DF.SoLSA", usim_ef_in_solsa,
+ ARRAY_SIZE(usim_ef_in_solsa));
+ /* OMA BCAST Smart Card Profile */
+ add_df_with_ef(uadf, 0x5F80, "DF.BCAST", NULL, 0);
+
+ /* DF.GSM and DF.TELECOM hierarchy as sub-directory of MF */
+ rc = osim_int_cprof_add_gsm(mf);
+ rc |= osim_int_cprof_add_telecom(mf);
+ if (rc != 0) {
+ talloc_free(cprof);
+ return NULL;
+ }
+
+ return cprof;
+}
diff --git a/src/shared/libosmocore/src/sim/class_tables.c b/src/shared/libosmocore/src/sim/class_tables.c
new file mode 100644
index 00000000..c3e18d82
--- /dev/null
+++ b/src/shared/libosmocore/src/sim/class_tables.c
@@ -0,0 +1,313 @@
+/* simtrace - tables determining APDU case for card emulation
+ *
+ * (C) 2016 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, or
+ * any later version as published by the Free Software Foundation.
+ *
+ * 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., 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[] = {
+ [0xB0] = 2, /* READ BIN */
+ [0xD0] = 3, /* WRITE BIN */
+ [0xD6] = 3, /* UPDATE BIN */
+ [0x0E] = 3, /* ERASE BIN */
+ [0xB2] = 2, /* READ REC */
+ [0xD2] = 3, /* WRITE REC */
+ [0xE2] = 3, /* APPEND REC */
+ [0xDC] = 3, /* UPDATE REC */
+ [0xCA] = 2, /* GET DATA */
+ [0xDA] = 3, /* PUT DATA */
+ [0xA4] = 4, /* SELECT FILE */
+ [0x20] = 3, /* VERIFY */
+ [0x88] = 4, /* INT AUTH */
+ [0x82] = 3, /* EXT AUTH */
+ [0x84] = 2, /* GET CHALLENGE */
+ [0x70] = 2, /* MANAGE CHANNEL */
+};
+
+static const struct osim_cla_ins_case iso7816_4_ins_case[] = {
+ {
+ .cla = 0x00,
+ .cla_mask = 0xF0,
+ .ins_tbl = iso7816_ins_tbl,
+ }, {
+ .cla = 0x80, /* 0x80/0x90 */
+ .cla_mask = 0xE0,
+ .ins_tbl = iso7816_ins_tbl,
+ }, {
+ .cla = 0xB0,
+ .cla_mask = 0xF0,
+ .ins_tbl = iso7816_ins_tbl,
+ }, {
+ .cla = 0xC0,
+ .cla_mask = 0xF0,
+ .ins_tbl = iso7816_ins_tbl,
+ },
+};
+
+const struct osim_cla_ins_card_profile osim_iso7816_cic_profile = {
+ .name = "ISO 7816-4",
+ .description = "ISO 7816-4",
+ .cic_arr = iso7816_4_ins_case,
+ .cic_arr_size = ARRAY_SIZE(iso7816_4_ins_case),
+};
+
+static const uint8_t gsm1111_ins_tbl[256] = {
+ [0xA4] = 4, /* SELECT FILE */
+ [0xF2] = 2, /* STATUS */
+ [0xB0] = 2, /* READ BINARY */
+ [0xD6] = 3, /* UPDATE BINARY */
+ [0xB2] = 2, /* READ RECORD */
+ [0xDC] = 3, /* UPDATE RECORD */
+ [0xA2] = 4, /* SEEK */
+ [0x32] = 4, /* INCREASE */
+ [0x20] = 3, /* VERIFY CHV */
+ [0x24] = 3, /* CHANGE CHV */
+ [0x26] = 3, /* DISABLE CHV */
+ [0x28] = 3, /* ENABLE CHV */
+ [0x2C] = 3, /* UNBLOCK CHV */
+ [0x04] = 1, /* INVALIDATE */
+ [0x44] = 1, /* REHABILITATE */
+ [0x88] = 4, /* RUN GSM ALGO */
+ [0xFA] = 1, /* SLEEP */
+ [0xC0] = 2, /* GET RESPONSE */
+ [0x10] = 3, /* TERMINAL PROFILE */
+ [0xC2] = 4, /* ENVELOPE */
+ [0x12] = 2, /* FETCH */
+ [0x14] = 3, /* TERMINAL RESPONSE */
+};
+
+/* According to Table 9 / Section 9.2 of TS 11.11 */
+static const struct osim_cla_ins_case gsm1111_ins_case[] = {
+ {
+ .cla = 0xA0,
+ .cla_mask = 0xFF,
+ .ins_tbl = gsm1111_ins_tbl,
+ },
+};
+
+const struct osim_cla_ins_card_profile osim_gsm1111_cic_profile = {
+ .name = "GSM SIM",
+ .description = "GSM/3GPP TS 11.11",
+ .cic_arr = gsm1111_ins_case,
+ .cic_arr_size = ARRAY_SIZE(gsm1111_ins_case),
+};
+
+/* ETSI TS 102 221, Table 10.5, CLA = 0x0x, 0x4x or 0x6x */
+static const uint8_t uicc_ins_tbl_046[256] = {
+ [0xA4] = 4, /* SELET FILE */
+ [0xB0] = 2, /* READ BINARY */
+ [0xD6] = 3, /* UPDATE BINARY */
+ [0xB2] = 2, /* READ RECORD */
+ [0xDC] = 3, /* UPDATE RECORD */
+ [0xA2] = 4, /* SEEK */
+ [0x20] = 3, /* VERIFY PIN */
+ [0x24] = 3, /* CHANGE PIN */
+ [0x26] = 3, /* DISABLE PIN */
+ [0x28] = 3, /* ENABLE PIN */
+ [0x2C] = 3, /* UNBLOCK PIN */
+ [0x04] = 1, /* DEACTIVATE FILE */
+ [0x44] = 1, /* ACTIVATE FILE */
+ [0x88] = 4, /* AUTHENTICATE */
+ [0x89] = 4, /* AUTHENTICATE */
+ [0x84] = 2, /* GET CHALLENGE */
+ [0x70] = 2, /* MANAGE CHANNEL */
+ [0x73] = 0x80, /* MANAGE SECURE CHANNEL */
+ [0x75] = 0x80, /* TRANSACT DATA */
+ [0xC0] = 2, /* GET RESPONSE */
+};
+
+static int uicc046_cla_ins_helper(const struct osim_cla_ins_case *cic,
+ const uint8_t *hdr)
+{
+ uint8_t ins = hdr[1];
+ uint8_t p1 = hdr[2];
+ uint8_t p2 = hdr[3];
+ uint8_t p2_cmd;
+
+ switch (ins) {
+ case 0x73: /* MANAGE SECURE CHANNEL */
+ if (p1 == 0x00) /* Retrieve UICC Endpoints */
+ return 2;
+ switch (p1 & 0x07) {
+ case 1: /* Establish SA - Master SA */
+ case 2: /* Establish SA - Conn. SA */
+ case 3: /* Start secure channel SA */
+ p2_cmd = p2 >> 5;
+ if (p2 == 0x80 || p2_cmd == 0) {
+ /* command data */
+ return 3;
+ }
+ if (p2_cmd == 5 || p2_cmd == 1) {
+ /* response data */
+ return 2;
+ }
+ return 0;
+ break;
+ case 4: /* Terminate secure chan SA */
+ return 3;
+ break;
+ }
+ break;
+ case 0x75: /* TRANSACT DATA */
+ if (p1 & 0x04)
+ return 3;
+ else
+ return 2;
+ break;
+ }
+
+ 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 */
+ [0x32] = 4, /* INCREASE */
+ [0xCB] = 4, /* RETRIEVE DATA */
+ [0xDB] = 3, /* SET DATA */
+ [0xAA] = 3, /* TERMINAL CAPABILITY */
+};
+
+/* ETSI TS 102 221, Table 10.5, CLA = 0x80 */
+static const uint8_t uicc_ins_tbl_80[256] = {
+ [0x10] = 3, /* TERMINAL PROFILE */
+ [0xC2] = 4, /* ENVELOPE */
+ [0x12] = 2, /* FETCH */
+ [0x14] = 3, /* TERMINAL RESPONSE */
+};
+
+static const struct osim_cla_ins_case uicc_ins_case[] = {
+ {
+ .cla = 0x80,
+ .cla_mask = 0xFF,
+ .ins_tbl = uicc_ins_tbl_80,
+ }, {
+ .cla = 0x00,
+ .cla_mask = 0xF0,
+ .helper = uicc046_cla_ins_helper,
+ .ins_tbl = uicc_ins_tbl_046,
+ }, {
+ .cla = 0x40,
+ .cla_mask = 0xF0,
+ .helper = uicc046_cla_ins_helper,
+ .ins_tbl = uicc_ins_tbl_046,
+ }, {
+ .cla = 0x60,
+ .cla_mask = 0xF0,
+ .helper = uicc046_cla_ins_helper,
+ .ins_tbl = uicc_ins_tbl_046,
+ }, {
+ .cla = 0x80,
+ .cla_mask = 0xF0,
+ .ins_tbl = uicc_ins_tbl_8ce,
+ }, {
+ .cla = 0xC0,
+ .cla_mask = 0xF0,
+ .ins_tbl = uicc_ins_tbl_8ce,
+ }, {
+ .cla = 0xE0,
+ .cla_mask = 0xF0,
+ .ins_tbl = uicc_ins_tbl_8ce,
+ },
+};
+
+const struct osim_cla_ins_card_profile osim_uicc_cic_profile = {
+ .name = "UICC",
+ .description = "TS 102 221 / 3GPP TS 31.102",
+ .cic_arr = uicc_ins_case,
+ .cic_arr_size = ARRAY_SIZE(uicc_ins_case),
+};
+
+
+static const struct osim_cla_ins_case uicc_sim_ins_case[] = {
+ {
+ .cla = 0xA0,
+ .cla_mask = 0xFF,
+ .ins_tbl = gsm1111_ins_tbl,
+ }, {
+ .cla = 0x80,
+ .cla_mask = 0xFF,
+ .ins_tbl = uicc_ins_tbl_80,
+ }, {
+ .cla = 0x00,
+ .cla_mask = 0xF0,
+ .helper = uicc046_cla_ins_helper,
+ .ins_tbl = uicc_ins_tbl_046,
+ }, {
+ .cla = 0x40,
+ .cla_mask = 0xF0,
+ .helper = uicc046_cla_ins_helper,
+ .ins_tbl = uicc_ins_tbl_046,
+ }, {
+ .cla = 0x60,
+ .cla_mask = 0xF0,
+ .helper = uicc046_cla_ins_helper,
+ .ins_tbl = uicc_ins_tbl_046,
+ }, {
+ .cla = 0x80,
+ .cla_mask = 0xF0,
+ .ins_tbl = uicc_ins_tbl_8ce,
+ }, {
+ .cla = 0xC0,
+ .cla_mask = 0xF0,
+ .ins_tbl = uicc_ins_tbl_8ce,
+ }, {
+ .cla = 0xE0,
+ .cla_mask = 0xF0,
+ .ins_tbl = uicc_ins_tbl_8ce,
+ },
+};
+
+const struct osim_cla_ins_card_profile osim_uicc_sim_cic_profile = {
+ .name = "UICC+SIM",
+ .description = "TS 102 221 / 3GPP TS 31.102 + GSM TS 11.11",
+ .cic_arr = uicc_sim_ins_case,
+ .cic_arr_size = ARRAY_SIZE(uicc_sim_ins_case),
+};
+
+/* 3GPP TS 31.102 */
+const uint8_t usim_ins_case[256] = {
+ [0x88] = 4, /* AUTHENTICATE */
+};
+
+int osim_determine_apdu_case(const struct osim_cla_ins_card_profile *prof,
+ const uint8_t *hdr)
+{
+ uint8_t cla = hdr[0];
+ uint8_t ins = hdr[1];
+ int i;
+ int rc;
+
+ for (i = 0; i < prof->cic_arr_size; i++) {
+ const struct osim_cla_ins_case *cic = &prof->cic_arr[i];
+ if ((cla & cic->cla_mask) != cic->cla)
+ continue;
+ rc = cic->ins_tbl[ins];
+ switch (rc) {
+ case 0x80:
+ return cic->helper(cic, hdr);
+ case 0x00:
+ /* continue with fruther cic, rather than abort
+ * now */
+ continue;
+ default:
+ return rc;
+ }
+ }
+ return 0;
+}
diff --git a/src/shared/libosmocore/src/sim/core.c b/src/shared/libosmocore/src/sim/core.c
new file mode 100644
index 00000000..2f129c9e
--- /dev/null
+++ b/src/shared/libosmocore/src/sim/core.c
@@ -0,0 +1,339 @@
+/* Core routines for SIM/UICC/USIM access */
+/*
+ * (C) 2012 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/sim/sim.h>
+
+struct osim_decoded_data *osim_file_decode(struct osim_file *file,
+ int len, uint8_t *data)
+{
+ struct osim_decoded_data *dd;
+
+ if (!file->desc->ops.parse)
+ return NULL;
+
+ dd = talloc_zero(file, struct osim_decoded_data);
+ if (!dd)
+ return NULL;
+ dd->file = file;
+
+ if (file->desc->ops.parse(dd, file->desc, len, data) < 0) {
+ talloc_free(dd);
+ return NULL;
+ } else
+ return dd;
+}
+
+struct msgb *osim_file_encode(const struct osim_file_desc *desc,
+ const struct osim_decoded_data *data)
+{
+ if (!desc->ops.encode)
+ return NULL;
+
+ return desc->ops.encode(desc, data);
+}
+
+static struct osim_decoded_element *
+__element_alloc(void *ctx, const char *name, enum osim_element_type type,
+ enum osim_element_repr repr)
+{
+ struct osim_decoded_element *elem;
+
+ elem = talloc_zero(ctx, struct osim_decoded_element);
+ if (!elem)
+ return NULL;
+ elem->name = name;
+ elem->type = type;
+ elem->representation = repr;
+
+ if (elem->type == ELEM_T_GROUP)
+ INIT_LLIST_HEAD(&elem->u.siblings);
+
+ return elem;
+}
+
+
+struct osim_decoded_element *
+element_alloc(struct osim_decoded_data *dd, const char *name,
+ enum osim_element_type type, enum osim_element_repr repr)
+{
+ struct osim_decoded_element *elem;
+
+ elem = __element_alloc(dd, name, type, repr);
+ if (!elem)
+ return NULL;
+
+ llist_add_tail(&elem->list, &dd->decoded_elements);
+
+ return elem;
+}
+
+struct osim_decoded_element *
+element_alloc_sub(struct osim_decoded_element *ee, const char *name,
+ enum osim_element_type type, enum osim_element_repr repr)
+{
+ struct osim_decoded_element *elem;
+
+ elem = __element_alloc(ee, name, type, repr);
+ if (!elem)
+ return NULL;
+
+ llist_add(&elem->list, &ee->u.siblings);
+
+ return elem;
+}
+
+
+void add_filedesc(struct osim_file_desc *root, const struct osim_file_desc *in, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++) {
+ struct osim_file_desc *ofd = talloc_memdup(root, &in[i], sizeof(*in));
+ llist_add_tail(&ofd->list, &root->child_list);
+ }
+}
+
+struct osim_file_desc *alloc_df(void *ctx, uint16_t fid, const char *name)
+{
+ struct osim_file_desc *mf;
+
+ mf = talloc_zero(ctx, struct osim_file_desc);
+ if (!mf)
+ return NULL;
+ mf->type = TYPE_DF;
+ mf->fid = fid;
+ mf->short_name = name;
+ INIT_LLIST_HEAD(&mf->child_list);
+
+ return mf;
+}
+
+struct osim_file_desc *
+add_df_with_ef(struct osim_file_desc *parent,
+ uint16_t fid, const char *name,
+ const struct osim_file_desc *in, int num)
+{
+ struct osim_file_desc *df;
+
+ df = alloc_df(parent, fid, name);
+ if (!df)
+ return NULL;
+ df->parent = parent;
+ llist_add_tail(&df->list, &parent->child_list);
+ add_filedesc(df, in, num);
+
+ return df;
+}
+
+struct osim_file_desc *
+add_adf_with_ef(struct osim_file_desc *parent,
+ const uint8_t *adf_name, uint8_t adf_name_len,
+ const char *name, const struct osim_file_desc *in,
+ int num)
+{
+ struct osim_file_desc *df;
+
+ df = alloc_df(parent, 0xffff, name);
+ if (!df)
+ return NULL;
+ df->type = TYPE_ADF;
+ df->df_name = adf_name;
+ df->df_name_len = adf_name_len;
+ df->parent = parent;
+ llist_add_tail(&df->list, &parent->child_list);
+ add_filedesc(df, in, num);
+
+ return df;
+}
+
+struct osim_file_desc *
+osim_file_desc_find_name(struct osim_file_desc *parent, const char *name)
+{
+ struct osim_file_desc *ofd;
+ llist_for_each_entry(ofd, &parent->child_list, list) {
+ if (!strcmp(ofd->short_name, name)) {
+ return ofd;
+ }
+ }
+ return NULL;
+}
+
+struct osim_file_desc *
+osim_file_desc_find_fid(struct osim_file_desc *parent, uint16_t fid)
+{
+ struct osim_file_desc *ofd;
+ llist_for_each_entry(ofd, &parent->child_list, list) {
+ if (ofd->fid == fid) {
+ return ofd;
+ }
+ }
+ return NULL;
+}
+
+struct osim_file_desc *
+osim_file_desc_find_sfid(struct osim_file_desc *parent, uint8_t sfid)
+{
+ struct osim_file_desc *ofd;
+ llist_for_each_entry(ofd, &parent->child_list, list) {
+ if (ofd->sfid == SFI_NONE)
+ continue;
+ if (ofd->sfid == sfid) {
+ return ofd;
+ }
+ }
+ return NULL;
+}
+
+
+/*! \brief Generate an APDU message and initialize APDU command header
+ * \param[in] cla CLASS byte
+ * \param[in] ins INSTRUCTION byte
+ * \param[in] p1 Parameter 1 byte
+ * \param[in] p2 Parameter 2 byte
+ * \param[in] lc number of bytes in the command data field Nc, which will encoded in 0, 1 or 3 bytes into Lc
+ * \param[in] le maximum number of bytes expected in the response data field, which will encoded in 0, 1, 2 or 3 bytes into Le
+ * \returns an APDU message generated using provided APDU parameters
+ *
+ * This function generates an APDU message, as defined in ISO/IEC 7816-4:2005(E) §5.1.
+ * The APDU command header, command and response fields lengths are initialized using the parameters.
+ * The APDU case is determined by the command and response fields lengths.
+ */
+struct msgb *osim_new_apdumsg(uint8_t cla, uint8_t ins, uint8_t p1,
+ uint8_t p2, uint16_t lc, uint16_t le)
+{
+ struct osim_apdu_cmd_hdr *ch;
+ struct msgb *msg = msgb_alloc(lc+le+sizeof(*ch)+2, "APDU");
+ if (!msg)
+ return NULL;
+
+ ch = (struct osim_apdu_cmd_hdr *) msgb_put(msg, sizeof(*ch));
+ msg->l2h = (uint8_t *) ch;
+
+ ch->cla = cla;
+ ch->ins = ins;
+ ch->p1 = p1;
+ ch->p2 = p2;
+
+ msgb_apdu_lc(msg) = lc;
+ msgb_apdu_le(msg) = le;
+
+ if (lc == 0 && le == 0)
+ msgb_apdu_case(msg) = APDU_CASE_1;
+ else if (lc == 0 && le >= 1) {
+ if (le <= 256)
+ msgb_apdu_case(msg) = APDU_CASE_2S;
+ else
+ msgb_apdu_case(msg) = APDU_CASE_2E;
+ } else if (le == 0 && lc >= 1) {
+ if (lc <= 255)
+ msgb_apdu_case(msg) = APDU_CASE_3S;
+ else
+ msgb_apdu_case(msg) = APDU_CASE_3E;
+ } else if (lc >= 1 && le >= 1) {
+ if (lc <= 255 && le <= 256)
+ msgb_apdu_case(msg) = APDU_CASE_4S;
+ else
+ msgb_apdu_case(msg) = APDU_CASE_4E;
+ }
+
+ return msg;
+}
+
+/* FIXME: do we want to mark this as __thread? */
+static char sw_print_buf[256];
+
+char *osim_print_sw(const struct osim_card_hdl *ch, uint16_t sw_in)
+{
+ const struct osim_card_sw *csw;
+
+ if (!ch || !ch->prof)
+ goto ret_def;
+
+ csw = osim_find_sw(ch->prof, sw_in);
+ if (!csw)
+ goto ret_def;
+
+ switch (csw->type) {
+ case SW_TYPE_STR:
+ snprintf(sw_print_buf, sizeof(sw_print_buf),
+ "%04x (%s)", sw_in, csw->u.str);
+ break;
+ default:
+ goto ret_def;
+ }
+
+ sw_print_buf[sizeof(sw_print_buf)-1] = '\0';
+
+ return sw_print_buf;
+
+ret_def:
+ snprintf(sw_print_buf, sizeof(sw_print_buf),
+ "%04x (Unknown)", sw_in);
+ sw_print_buf[sizeof(sw_print_buf)-1] = '\0';
+
+ return sw_print_buf;
+}
+
+
+const struct osim_card_sw *osim_find_sw(const struct osim_card_profile *cp,
+ uint16_t sw_in)
+{
+ const struct osim_card_sw **sw_lists = cp->sws;
+ const struct osim_card_sw *sw_list, *sw;
+
+ for (sw_list = *sw_lists++; sw_list != NULL; sw = sw_list = *sw_lists++) {
+ for (sw = sw_list; sw->code != 0 && sw->mask != 0; sw++) {
+ if ((sw_in & sw->mask) == sw->code)
+ return sw;
+ }
+ }
+ return NULL;
+}
+
+enum osim_card_sw_class osim_sw_class(const struct osim_card_profile *cp,
+ uint16_t sw_in)
+{
+ const struct osim_card_sw *csw = osim_find_sw(cp, sw_in);
+
+ if (!csw)
+ return SW_CLS_NONE;
+
+ return csw->class;
+}
+
+int default_decode(struct osim_decoded_data *dd,
+ const struct osim_file_desc *desc,
+ int len, uint8_t *data)
+{
+ struct osim_decoded_element *elem;
+
+ elem = element_alloc(dd, "Unknown Payload", ELEM_T_BYTES, ELEM_REPR_HEX);
+ elem->u.buf = talloc_memdup(elem, data, len);
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/src/sim/gsm_int.h b/src/shared/libosmocore/src/sim/gsm_int.h
new file mode 100644
index 00000000..54a2fbf2
--- /dev/null
+++ b/src/shared/libosmocore/src/sim/gsm_int.h
@@ -0,0 +1,17 @@
+#include <sys/types.h>
+#include <osmocom/sim/sim.h>
+
+int osim_int_cprof_add_gsm(struct osim_file_desc *mf);
+int osim_int_cprof_add_telecom(struct osim_file_desc *mf);
+
+int gsm_hpplmn_decode(struct osim_decoded_data *dd,
+ const struct osim_file_desc *desc,
+ int len, uint8_t *data);
+
+int gsm_lp_decode(struct osim_decoded_data *dd,
+ const struct osim_file_desc *desc,
+ int len, uint8_t *data);
+
+int gsm_imsi_decode(struct osim_decoded_data *dd,
+ const struct osim_file_desc *desc,
+ int len, uint8_t *data);
diff --git a/src/shared/libosmocore/src/sim/reader.c b/src/shared/libosmocore/src/sim/reader.c
new file mode 100644
index 00000000..e7169b5a
--- /dev/null
+++ b/src/shared/libosmocore/src/sim/reader.c
@@ -0,0 +1,274 @@
+/* Card reader abstraction for libosmosim */
+/*
+ * (C) 2012 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <netinet/in.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/sim/sim.h>
+
+
+#include "sim_int.h"
+
+/* remove the SW from end of the message */
+static int get_sw(struct msgb *resp)
+{
+ int ret;
+
+ if (!msgb_apdu_de(resp) || msgb_apdu_le(resp) < 2)
+ return -EIO;
+
+ ret = msgb_get_u16(resp);
+
+ return ret;
+}
+
+/* According to ISO7816-4 Annex A */
+static int transceive_apdu_t0(struct osim_card_hdl *st, struct msgb *amsg)
+{
+ struct osim_reader_hdl *rh = st->reader;
+ struct msgb *tmsg = msgb_alloc(1024, "TPDU");
+ struct osim_apdu_cmd_hdr *tpduh;
+ uint8_t *cur;
+ uint16_t sw;
+ int rc, num_resp = 0;
+
+ if (!tmsg)
+ return -ENOMEM;
+
+ /* create TPDU header from APDU header */
+ tpduh = (struct osim_apdu_cmd_hdr *) msgb_put(tmsg, sizeof(*tpduh));
+ memcpy(tpduh, msgb_apdu_h(amsg), sizeof(*tpduh));
+
+ switch (msgb_apdu_case(amsg)) {
+ case APDU_CASE_1:
+ tpduh->p3 = 0x00;
+ break;
+ case APDU_CASE_2S:
+ tpduh->p3 = msgb_apdu_le(amsg);
+ break;
+ case APDU_CASE_2E:
+ if (msgb_apdu_le(amsg) <= 256) {
+ /* case 2E.1 */
+ tpduh->p3 = msgb_apdu_le(amsg) & 0xff;
+ } else {
+ /* case 2E.2 */
+ tpduh->p3 = 0;
+ msgb_put_u16(tmsg, msgb_apdu_le(amsg));
+ }
+ break;
+ case APDU_CASE_3S:
+ case APDU_CASE_4S:
+ tpduh->p3 = msgb_apdu_lc(amsg);
+ cur = msgb_put(tmsg, tpduh->p3);
+ memcpy(cur, msgb_apdu_dc(amsg), tpduh->p3);
+ break;
+ case APDU_CASE_3E:
+ case APDU_CASE_4E:
+ if (msgb_apdu_lc(amsg) < 256) {
+ /* Case 3E.1 */
+ tpduh->p3 = msgb_apdu_lc(amsg);
+ } else {
+ /* Case 3E.2 */
+ /* FXIME: Split using ENVELOPE! */
+ return -1;
+ }
+ break;
+ }
+
+transceive_again:
+
+ /* store pointer to start of response */
+ tmsg->l3h = tmsg->tail;
+
+ /* transceive */
+ rc = rh->ops->transceive(st->reader, tmsg);
+ if (rc < 0) {
+ msgb_free(tmsg);
+ return rc;
+ }
+ msgb_apdu_sw(tmsg) = get_sw(tmsg);
+
+ /* increase number of responsese received */
+ num_resp++;
+
+ /* save SW */
+ sw = msgb_apdu_sw(tmsg);
+ printf("sw = 0x%04x\n", sw);
+ msgb_apdu_sw(amsg) = sw;
+
+ switch (msgb_apdu_case(amsg)) {
+ case APDU_CASE_1:
+ case APDU_CASE_3S:
+ /* just copy SW */
+ break;
+ case APDU_CASE_2S:
+case_2s:
+ switch (sw >> 8) {
+ case 0x67: /* Case 2S.2: Le definitely not accepted */
+ break;
+ case 0x6c: /* Case 2S.3: Le not accepted, La indicated */
+ tpduh->p3 = sw & 0xff;
+ /* re-issue the command with La as */
+ goto transceive_again;
+ break;
+ case 0x90:
+ /* Case 2S.1, fall-through */
+ case 0x91: case 0x92: case 0x93: case 0x94: case 0x95:
+ case 0x96: case 0x97: case 0x98: case 0x99: case 0x9a:
+ case 0x9b: case 0x9c: case 0x9d: case 0x9e: case 0x9f:
+ /* Case 2S.4 */
+ /* copy response data over */
+ cur = msgb_put(amsg, msgb_l3len(tmsg));
+ memcpy(cur, tmsg->l3h, msgb_l3len(tmsg));
+ }
+ break;
+ case APDU_CASE_4S:
+ /* FIXME: this is 4S.2 only for 2nd... response: */
+ if (num_resp >= 2)
+ goto case_2s;
+
+ switch (sw >> 8) {
+ case 0x60: case 0x62: case 0x63: case 0x64: case 0x65:
+ case 0x66: case 0x67: case 0x68: case 0x69: case 0x6a:
+ case 0x6b: case 0x6c: case 0x6d: case 0x6e: case 0x6f:
+ /* Case 4S.1: Command not accepted: just copy SW */
+ break;
+ case 0x90:
+ /* case 4S.2: Command accepted */
+ tpduh->ins = 0xC0;
+ tpduh->p1 = tpduh->p2 = 0;
+ tpduh->p3 = msgb_apdu_le(amsg);
+ /* strip off current result */
+ msgb_get(tmsg, msgb_length(tmsg)-sizeof(*tpduh));
+ goto transceive_again;
+ break;
+ case 0x61: /* Case 4S.3: command accepted with info added */
+ case 0x9F: /* FIXME: This is specific to SIM cards */
+ tpduh->ins = 0xC0;
+ tpduh->p1 = tpduh->p2 = 0;
+ tpduh->p3 = OSMO_MIN(msgb_apdu_le(amsg), sw & 0xff);
+ /* strip off current result */
+ msgb_get(tmsg, msgb_length(tmsg)-sizeof(*tpduh));
+ goto transceive_again;
+ break;
+ }
+ /* Case 4S.2: Command accepted: just copy SW */
+ /* Case 4S.4: Just copy SW */
+ break;
+ case APDU_CASE_2E:
+ if (msgb_apdu_le(amsg) <= 256) {
+ /* Case 2E.1: Le <= 256 */
+ goto case_2s;
+ }
+ switch (sw >> 8) {
+ case 0x67:
+ /* Case 2E.2a: wrong length, abort */
+ break;
+ case 0x6c:
+ /* Case 2E.2b: wrong length, La given */
+ tpduh->p3 = sw & 0xff;
+ /* re-issue the command with La as given */
+ goto transceive_again;
+ break;
+ case 0x90:
+ /* Case 2E.2c: */
+ break;
+ case 0x61:
+ /* Case 2E.2d: more data available */
+ /* FIXME: issue yet another GET RESPONSE */
+ break;
+ }
+ break;
+ case APDU_CASE_3E:
+ /* FIXME: handling for ENVELOPE splitting */
+ break;
+ case APDU_CASE_4E:
+ break;
+ }
+
+ msgb_free(tmsg);
+
+ /* compute total length of response data */
+ msgb_apdu_le(amsg) = amsg->tail - msgb_apdu_de(amsg);
+
+ return sw;
+}
+
+/* FIXME: T=1 According to ISO7816-4 Annex B */
+
+int osim_transceive_apdu(struct osim_chan_hdl *st, struct msgb *amsg)
+{
+ switch (st->card->proto) {
+ case OSIM_PROTO_T0:
+ return transceive_apdu_t0(st->card, amsg);
+ default:
+ return -ENOTSUP;
+ }
+}
+
+struct osim_reader_hdl *osim_reader_open(enum osim_reader_driver driver, int idx,
+ const char *name, void *ctx)
+{
+ const struct osim_reader_ops *ops;
+ struct osim_reader_hdl *rh;
+
+ switch (driver) {
+ case OSIM_READER_DRV_PCSC:
+ ops = &pcsc_reader_ops;
+ break;
+ default:
+ return NULL;
+ }
+
+ rh = ops->reader_open(idx, name, ctx);
+ if (!rh)
+ return NULL;
+ rh->ops = ops;
+
+ /* FIXME: for now we only do T=0 on all readers */
+ rh->proto_supported = (1 << OSIM_PROTO_T0);
+
+ return rh;
+}
+
+struct osim_card_hdl *osim_card_open(struct osim_reader_hdl *rh, enum osim_proto proto)
+{
+ struct osim_card_hdl *ch;
+
+ if (!(rh->proto_supported & (1 << proto)))
+ return NULL;
+
+ ch = rh->ops->card_open(rh, proto);
+ if (!ch)
+ return NULL;
+
+ ch->proto = proto;
+
+ return ch;
+}
diff --git a/src/shared/libosmocore/src/sim/reader_pcsc.c b/src/shared/libosmocore/src/sim/reader_pcsc.c
new file mode 100644
index 00000000..f020142e
--- /dev/null
+++ b/src/shared/libosmocore/src/sim/reader_pcsc.c
@@ -0,0 +1,159 @@
+/* PC/SC Card reader backend for libosmosim */
+/*
+ * (C) 2012 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/sim/sim.h>
+
+#include <wintypes.h>
+#include <winscard.h>
+
+#include "sim_int.h"
+
+#define PCSC_ERROR(rv, text) \
+if (rv != SCARD_S_SUCCESS) { \
+ fprintf(stderr, text ": %s (0x%lX)\n", pcsc_stringify_error(rv), rv); \
+ goto end; \
+} else { \
+ printf(text ": OK\n\n"); \
+}
+
+
+
+struct pcsc_reader_state {
+ SCARDCONTEXT hContext;
+ SCARDHANDLE hCard;
+ DWORD dwActiveProtocol;
+ const SCARD_IO_REQUEST *pioSendPci;
+ SCARD_IO_REQUEST pioRecvPci;
+ char *name;
+};
+
+static struct osim_reader_hdl *pcsc_reader_open(int num, const char *id, void *ctx)
+{
+ struct osim_reader_hdl *rh;
+ struct pcsc_reader_state *st;
+ LONG rc;
+ LPSTR mszReaders = NULL;
+ DWORD dwReaders;
+ unsigned int num_readers;
+ char *ptr;
+
+ /* FIXME: implement matching on id or num */
+
+ rh = talloc_zero(ctx, struct osim_reader_hdl);
+ st = rh->priv = talloc_zero(rh, struct pcsc_reader_state);
+
+ rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL,
+ &st->hContext);
+ PCSC_ERROR(rc, "SCardEstablishContext");
+
+ dwReaders = SCARD_AUTOALLOCATE;
+ rc = SCardListReaders(st->hContext, NULL, (LPSTR)&mszReaders, &dwReaders);
+ PCSC_ERROR(rc, "SCardListReaders");
+
+ num_readers = 0;
+ ptr = mszReaders;
+ while (*ptr != '\0') {
+ ptr += strlen(ptr)+1;
+ num_readers++;
+ }
+
+ if (num_readers == 0)
+ goto end;
+
+ st->name = talloc_strdup(rh, mszReaders);
+ st->dwActiveProtocol = -1;
+
+ return rh;
+end:
+ talloc_free(rh);
+ return NULL;
+}
+
+static struct osim_card_hdl *pcsc_card_open(struct osim_reader_hdl *rh,
+ enum osim_proto proto)
+{
+ struct pcsc_reader_state *st = rh->priv;
+ struct osim_card_hdl *card;
+ struct osim_chan_hdl *chan;
+ LONG rc;
+
+ if (proto != OSIM_PROTO_T0)
+ return NULL;
+
+ rc = SCardConnect(st->hContext, st->name, SCARD_SHARE_SHARED,
+ SCARD_PROTOCOL_T0, &st->hCard, &st->dwActiveProtocol);
+ PCSC_ERROR(rc, "SCardConnect");
+
+ st->pioSendPci = SCARD_PCI_T0;
+
+ card = talloc_zero(rh, struct osim_card_hdl);
+ INIT_LLIST_HEAD(&card->channels);
+ card->reader = rh;
+ rh->card = card;
+
+ /* create a default channel */
+ chan = talloc_zero(card, struct osim_chan_hdl);
+ chan->card = card;
+ llist_add(&chan->list, &card->channels);
+
+ return card;
+
+end:
+ return NULL;
+}
+
+
+static int pcsc_transceive(struct osim_reader_hdl *rh, struct msgb *msg)
+{
+ struct pcsc_reader_state *st = rh->priv;
+ DWORD rlen = msgb_tailroom(msg);
+ LONG rc;
+
+ printf("TX: %s\n", osmo_hexdump(msg->data, msg->len));
+
+ rc = SCardTransmit(st->hCard, st->pioSendPci, msg->data, msgb_length(msg),
+ &st->pioRecvPci, msg->tail, &rlen);
+ PCSC_ERROR(rc, "SCardEndTransaction");
+
+ printf("RX: %s\n", osmo_hexdump(msg->tail, rlen));
+ msgb_put(msg, rlen);
+ msgb_apdu_le(msg) = rlen;
+
+ return 0;
+end:
+ return -EIO;
+}
+
+const struct osim_reader_ops pcsc_reader_ops = {
+ .name = "PC/SC",
+ .reader_open = pcsc_reader_open,
+ .card_open = pcsc_card_open,
+ .transceive = pcsc_transceive,
+};
+
diff --git a/src/shared/libosmocore/src/sim/sim_int.h b/src/shared/libosmocore/src/sim/sim_int.h
new file mode 100644
index 00000000..7b07b834
--- /dev/null
+++ b/src/shared/libosmocore/src/sim/sim_int.h
@@ -0,0 +1,34 @@
+#ifndef _SIM_INT_H
+
+#include <osmocom/sim/sim.h>
+
+struct osim_decoded_element *
+element_alloc(struct osim_decoded_data *dd, const char *name,
+ enum osim_element_type type, enum osim_element_repr repr);
+
+struct osim_decoded_element *
+element_alloc_sub(struct osim_decoded_element *ee, const char *name,
+ enum osim_element_type type, enum osim_element_repr repr);
+
+extern const struct osim_card_sw ts102221_uicc_sw[0];
+
+int default_decode(struct osim_decoded_data *dd,
+ const struct osim_file_desc *desc,
+ int len, uint8_t *data);
+
+void add_filedesc(struct osim_file_desc *root, const struct osim_file_desc *in, int num);
+struct osim_file_desc *alloc_df(void *ctx, uint16_t fid, const char *name);
+struct osim_file_desc *
+add_df_with_ef(struct osim_file_desc *parent,
+ uint16_t fid, const char *name,
+ const struct osim_file_desc *in, int num);
+
+struct osim_file_desc *
+add_adf_with_ef(struct osim_file_desc *parent,
+ const uint8_t *adf_name, uint8_t adf_name_len,
+ const char *name, const struct osim_file_desc *in,
+ int num);
+
+extern const struct osim_reader_ops pcsc_reader_ops;
+
+#endif
diff --git a/src/shared/libosmocore/src/socket.c b/src/shared/libosmocore/src/socket.c
index 53205cd2..cdafadd0 100644
--- a/src/shared/libosmocore/src/socket.c
+++ b/src/shared/libosmocore/src/socket.c
@@ -1,3 +1,24 @@
+/*
+ * (C) 2011 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
#include "../config.h"
/*! \addtogroup socket
@@ -17,6 +38,7 @@
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
+#include <sys/un.h>
#include <netinet/in.h>
@@ -35,6 +57,7 @@
* \param[in] host remote host name or IP address in string form
* \param[in] port remote port number in host byte order
* \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
+ * \returns socket file descriptor on success; negative on error
*
* This function creates a new socket of the designated \a family, \a
* type and \a proto and optionally binds or connects it, depending on
@@ -48,33 +71,54 @@ int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
char portbuf[16];
if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
- (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
+ (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) {
+ fprintf(stderr, "invalid: both bind and connect flags set:"
+ " %s:%u\n", host, port);
return -EINVAL;
+ }
sprintf(portbuf, "%u", port);
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = family;
- hints.ai_socktype = type;
- hints.ai_flags = 0;
- hints.ai_protocol = proto;
+ if (type == SOCK_RAW) {
+ /* Workaround for glibc, that returns EAI_SERVICE (-8) if
+ * SOCK_RAW and IPPROTO_GRE is used.
+ */
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ } else {
+ hints.ai_socktype = type;
+ hints.ai_protocol = proto;
+ }
if (flags & OSMO_SOCK_F_BIND)
hints.ai_flags |= AI_PASSIVE;
rc = getaddrinfo(host, portbuf, &hints, &result);
if (rc != 0) {
- perror("getaddrinfo returned NULL");
+ fprintf(stderr, "getaddrinfo returned NULL: %s:%u: %s\n",
+ host, port, strerror(errno));
return -EINVAL;
}
for (rp = result; rp != NULL; rp = rp->ai_next) {
+ /* Workaround for glibc again */
+ if (type == SOCK_RAW) {
+ rp->ai_socktype = SOCK_RAW;
+ rp->ai_protocol = proto;
+ }
+
sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sfd == -1)
continue;
if (flags & OSMO_SOCK_F_NONBLOCK) {
if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
- perror("cannot set this socket unblocking");
+ fprintf(stderr,
+ "cannot set this socket unblocking:"
+ " %s:%u: %s\n",
+ host, port, strerror(errno));
close(sfd);
+ freeaddrinfo(result);
return -EINVAL;
}
}
@@ -86,7 +130,10 @@ int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
&on, sizeof(on));
if (rc < 0) {
- perror("cannot setsockopt socket");
+ fprintf(stderr,
+ "cannot setsockopt socket:"
+ " %s:%u: %s\n",
+ host, port, strerror(errno));
break;
}
if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
@@ -97,7 +144,8 @@ int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
freeaddrinfo(result);
if (rp == NULL) {
- perror("unable to connect/bind socket");
+ fprintf(stderr, "unable to connect/bind socket: %s:%u: %s\n",
+ host, port, strerror(errno));
return -ENODEV;
}
@@ -115,24 +163,17 @@ int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
return sfd;
}
-/*! \brief Initialize a socket and fill \ref osmo_fd
- * \param[out] osmocom file descriptor (will be filled in)
- * \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] host remote host name or IP address in string form
- * \param[in] port remote port number in host byte order
- * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
+/*! \brief fill \ref osmo_fd for a give sfd
+ * \param[out] ofd file descriptor (will be filled in)
+ * \param[in] sfd socket file descriptor
+ * \returns socket fd on success; negative on error
*
- * This function creates (and optionall binds/connects) a socket using
- * \ref osmo_sock_init, but also fills the \a ofd structure.
+ * This function fills the \a ofd structure.
*/
-int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
- const char *host, uint16_t port, unsigned int flags)
+static inline int osmo_fd_init_ofd(struct osmo_fd *ofd, int sfd)
{
- int sfd, rc;
+ int rc;
- sfd = osmo_sock_init(family, type, proto, host, port, flags);
if (sfd < 0)
return sfd;
@@ -148,11 +189,31 @@ int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
return sfd;
}
+/*! \brief Initialize a socket and fill \ref osmo_fd
+ * \param[out] ofd file descriptor (will be filled in)
+ * \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] host remote host name or IP address in string form
+ * \param[in] port remote port number in host byte order
+ * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
+ * \returns socket fd on success; negative on error
+ *
+ * This function creates (and optionall binds/connects) a socket using
+ * \ref osmo_sock_init, but also fills the \a ofd structure.
+ */
+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));
+}
+
/*! \brief Initialize a socket and fill \ref sockaddr
* \param[out] ss socket address (will be filled in)
* \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
* \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
* \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
+ * \returns socket fd on success; negative on error
*
* This function creates (and optionall binds/connects) a socket using
* \ref osmo_sock_init, but also fills the \a ss structure.
@@ -244,6 +305,94 @@ int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen)
return 0;
}
+/*! \brief 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
+ * \param[in] socket_path path to identify the socket
+ * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
+ * \returns socket fd on success; negative on error
+ *
+ * This function creates a new unix domain socket, \a
+ * type and \a proto and optionally binds or connects it, depending on
+ * the value of \a flags parameter.
+ */
+int osmo_sock_unix_init(uint16_t type, uint8_t proto,
+ const char *socket_path, unsigned int flags)
+{
+ struct sockaddr_un local;
+ int sfd, rc, on = 1;
+ unsigned int namelen;
+
+ if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
+ (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
+ return -EINVAL;
+
+ local.sun_family = AF_UNIX;
+ strncpy(local.sun_path, socket_path, sizeof(local.sun_path));
+ local.sun_path[sizeof(local.sun_path) - 1] = '\0';
+
+#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
+ local.sun_len = strlen(local.sun_path);
+#endif
+#if defined(BSD44SOCKETS) || defined(SUN_LEN)
+ namelen = SUN_LEN(&local);
+#else
+ namelen = strlen(local.sun_path) +
+ offsetof(struct sockaddr_un, sun_path);
+#endif
+
+ sfd = socket(AF_UNIX, type, proto);
+ if (sfd < 0)
+ return -1;
+
+ if (flags & OSMO_SOCK_F_CONNECT) {
+ rc = connect(sfd, (struct sockaddr *)&local, namelen);
+ if (rc < 0)
+ goto err;
+ } else {
+ unlink(local.sun_path);
+ rc = bind(sfd, (struct sockaddr *)&local, namelen);
+ if (rc < 0)
+ goto err;
+ }
+
+ if (flags & OSMO_SOCK_F_NONBLOCK) {
+ if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
+ perror("cannot set this socket unblocking");
+ close(sfd);
+ return -EINVAL;
+ }
+ }
+
+ if (flags & OSMO_SOCK_F_BIND) {
+ rc = listen(sfd, 10);
+ if (rc < 0)
+ goto err;
+ }
+
+ return sfd;
+err:
+ close(sfd);
+ return -1;
+}
+
+/*! \brief Initialize a unix domain socket and fill \ref osmo_fd
+ * \param[out] ofd file descriptor (will be filled in)
+ * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
+ * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
+ * \param[in] socket_path path to identify the socket
+ * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
+ * \returns socket fd on success; negative on error
+ *
+ * This function creates (and optionall binds/connects) a socket using
+ * \ref osmo_sock_unix_init, but also fills the \a ofd structure.
+ */
+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));
+}
+
#endif /* HAVE_SYS_SOCKET_H */
/*! @} */
diff --git a/src/shared/libosmocore/src/stat_item.c b/src/shared/libosmocore/src/stat_item.c
new file mode 100644
index 00000000..fcd6781e
--- /dev/null
+++ b/src/shared/libosmocore/src/stat_item.c
@@ -0,0 +1,268 @@
+/* utility routines for keeping conters about events and the event rates */
+
+/* (C) 2015 by Sysmocom s.f.m.c. GmbH
+ * (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/*! \addtogroup osmo_stat_item
+ * @{
+ */
+
+/*! \file stat_item.c */
+
+
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/stat_item.h>
+
+static LLIST_HEAD(osmo_stat_item_groups);
+static int32_t global_value_id = 0;
+
+static void *tall_stat_item_ctx;
+
+/*! \brief Allocate a new group of counters according to description
+ * \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->last_value_index = -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;
+}
+
+/*! \brief Free the memory for the specified group of counters */
+void osmo_stat_item_group_free(struct osmo_stat_item_group *grp)
+{
+ llist_del(&grp->list);
+ talloc_free(grp);
+}
+
+void osmo_stat_item_set(struct osmo_stat_item *item, int32_t value)
+{
+ item->last_offs += 1;
+ if (item->last_offs >= item->desc->num_values)
+ item->last_offs = 0;
+
+ global_value_id += 1;
+ if (global_value_id == OSMO_STAT_ITEM_NOVALUE_ID)
+ global_value_id += 1;
+
+ item->values[item->last_offs].value = value;
+ item->values[item->last_offs].id = global_value_id;
+}
+
+int osmo_stat_item_get_next(const struct osmo_stat_item *item, int32_t *next_idx,
+ int32_t *value)
+{
+ const struct osmo_stat_item_value *next_value;
+ const struct osmo_stat_item_value *item_value = NULL;
+ int idx_delta;
+ int next_offs;
+
+ next_offs = item->last_offs;
+ next_value = &item->values[next_offs];
+
+ while (next_value->id - *next_idx >= 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;
+
+ idx_delta = item_value->id + 1 - *next_idx;
+
+ *next_idx = item_value->id + 1;
+
+ return idx_delta;
+}
+
+/*! \brief Skip all values of this item and update idx accordingly */
+int osmo_stat_item_discard(const struct osmo_stat_item *item, int32_t *idx)
+{
+ int discarded = item->values[item->last_offs].id + 1 - *idx;
+ *idx = item->values[item->last_offs].id + 1;
+
+ return discarded;
+}
+
+/*! \brief Skip all values of all items and update idx accordingly */
+int osmo_stat_item_discard_all(int32_t *idx)
+{
+ int discarded = global_value_id + 1 - *idx;
+ *idx = global_value_id + 1;
+
+ return discarded;
+}
+
+/*! \brief Initialize the stat item module */
+int osmo_stat_item_init(void *tall_ctx)
+{
+ tall_stat_item_ctx = tall_ctx;
+
+ return 0;
+}
+
+/*! \brief Search for item group based on group name and index */
+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;
+}
+
+/*! \brief Search for item group based on group name */
+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;
+}
+
+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;
+}
+
+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;
+}
+
+/*! @} */
diff --git a/src/shared/libosmocore/src/statistics.c b/src/shared/libosmocore/src/statistics.c
index e28541ba..ad069cea 100644
--- a/src/shared/libosmocore/src/statistics.c
+++ b/src/shared/libosmocore/src/statistics.c
@@ -74,3 +74,11 @@ struct osmo_counter *osmo_counter_get_by_name(const char *name)
}
return NULL;
}
+
+int osmo_counter_difference(struct osmo_counter *ctr)
+{
+ int delta = ctr->value - ctr->previous;
+ ctr->previous = ctr->value;
+
+ return delta;
+}
diff --git a/src/shared/libosmocore/src/stats.c b/src/shared/libosmocore/src/stats.c
new file mode 100644
index 00000000..1efc8cd8
--- /dev/null
+++ b/src/shared/libosmocore/src/stats.c
@@ -0,0 +1,612 @@
+/*
+ * (C) 2015 by Sysmocom s.f.m.c. GmbH
+ *
+ * Author: Jacob Erlbeck <jerlbeck@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.
+ *
+ * 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/stats.h>
+
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/stat_item.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/statistics.h>
+#include <osmocom/core/msgb.h>
+
+#define STATS_DEFAULT_INTERVAL 5 /* secs */
+#define STATS_DEFAULT_BUFLEN 256
+
+static LLIST_HEAD(osmo_stats_reporter_list);
+static void *osmo_stats_ctx = NULL;
+static int is_initialised = 0;
+static int32_t current_stat_item_index = 0;
+
+static struct osmo_stats_config s_stats_config = {
+ .interval = STATS_DEFAULT_INTERVAL,
+};
+struct osmo_stats_config *osmo_stats_config = &s_stats_config;
+
+static struct osmo_timer_list osmo_stats_timer;
+
+static int osmo_stats_reporter_log_send_counter(struct osmo_stats_reporter *srep,
+ const struct rate_ctr_group *ctrg,
+ const struct rate_ctr_desc *desc,
+ int64_t value, int64_t delta);
+static int osmo_stats_reporter_log_send_item(struct osmo_stats_reporter *srep,
+ const struct osmo_stat_item_group *statg,
+ const struct osmo_stat_item_desc *desc, int64_t value);
+
+static int update_srep_config(struct osmo_stats_reporter *srep)
+{
+ int rc = 0;
+
+ if (srep->running) {
+ if (srep->close)
+ rc = srep->close(srep);
+ srep->running = 0;
+ }
+
+ if (!srep->enabled)
+ return rc;
+
+ if (srep->open)
+ rc = srep->open(srep);
+ else
+ rc = 0;
+
+ if (rc < 0)
+ srep->enabled = 0;
+ else
+ srep->running = 1;
+
+ srep->force_single_flush = 1;
+
+ return rc;
+}
+
+static void osmo_stats_timer_cb(void *data)
+{
+ int interval = osmo_stats_config->interval;
+
+ if (!llist_empty(&osmo_stats_reporter_list))
+ osmo_stats_report();
+
+ osmo_timer_schedule(&osmo_stats_timer, interval, 0);
+}
+
+static int start_timer()
+{
+ if (!is_initialised)
+ return -ESRCH;
+
+ osmo_stats_timer.cb = osmo_stats_timer_cb;
+ osmo_timer_schedule(&osmo_stats_timer, 0, 1);
+
+ return 0;
+}
+
+struct osmo_stats_reporter *osmo_stats_reporter_alloc(enum osmo_stats_reporter_type type,
+ const char *name)
+{
+ struct osmo_stats_reporter *srep;
+ srep = talloc_zero(osmo_stats_ctx, struct osmo_stats_reporter);
+ OSMO_ASSERT(srep);
+ srep->type = type;
+ if (name)
+ srep->name = talloc_strdup(srep, name);
+ srep->fd = -1;
+
+ llist_add(&srep->list, &osmo_stats_reporter_list);
+
+ return srep;
+}
+
+void osmo_stats_reporter_free(struct osmo_stats_reporter *srep)
+{
+ osmo_stats_reporter_disable(srep);
+ llist_del(&srep->list);
+ talloc_free(srep);
+}
+
+void osmo_stats_init(void *ctx)
+{
+ osmo_stats_ctx = ctx;
+ osmo_stat_item_discard_all(&current_stat_item_index);
+
+ is_initialised = 1;
+ start_timer();
+}
+
+struct osmo_stats_reporter *osmo_stats_reporter_find(enum osmo_stats_reporter_type type,
+ const char *name)
+{
+ struct osmo_stats_reporter *srep;
+ llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
+ if (srep->type != type)
+ continue;
+ if (srep->name != name) {
+ if (name == NULL || srep->name == NULL ||
+ strcmp(name, srep->name) != 0)
+ continue;
+ }
+ return srep;
+ }
+ return NULL;
+}
+
+int osmo_stats_reporter_set_remote_addr(struct osmo_stats_reporter *srep, const char *addr)
+{
+ int rc;
+ struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
+ struct in_addr inaddr;
+
+ if (!srep->have_net_config)
+ return -ENOTSUP;
+
+ OSMO_ASSERT(addr != NULL);
+
+ rc = inet_pton(AF_INET, addr, &inaddr);
+ if (rc <= 0)
+ return -EINVAL;
+
+ sock_addr->sin_addr = inaddr;
+ sock_addr->sin_family = AF_INET;
+ srep->dest_addr_len = sizeof(*sock_addr);
+
+ talloc_free(srep->dest_addr_str);
+ srep->dest_addr_str = talloc_strdup(srep, addr);
+
+ return update_srep_config(srep);
+}
+
+int osmo_stats_reporter_set_remote_port(struct osmo_stats_reporter *srep, int port)
+{
+ struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
+
+ if (!srep->have_net_config)
+ return -ENOTSUP;
+
+ srep->dest_port = port;
+ sock_addr->sin_port = htons(port);
+
+ return update_srep_config(srep);
+}
+
+int osmo_stats_reporter_set_local_addr(struct osmo_stats_reporter *srep, const char *addr)
+{
+ int rc;
+ struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->bind_addr;
+ struct in_addr inaddr;
+
+ if (!srep->have_net_config)
+ return -ENOTSUP;
+
+ if (addr) {
+ rc = inet_pton(AF_INET, addr, &inaddr);
+ if (rc <= 0)
+ return -EINVAL;
+ } else {
+ inaddr.s_addr = INADDR_ANY;
+ }
+
+ sock_addr->sin_addr = inaddr;
+ sock_addr->sin_family = AF_INET;
+ srep->bind_addr_len = addr ? sizeof(*sock_addr) : 0;
+
+ talloc_free(srep->bind_addr_str);
+ srep->bind_addr_str = addr ? talloc_strdup(srep, addr) : NULL;
+
+ return update_srep_config(srep);
+}
+
+int osmo_stats_reporter_set_mtu(struct osmo_stats_reporter *srep, int mtu)
+{
+ if (!srep->have_net_config)
+ return -ENOTSUP;
+
+ if (mtu < 0)
+ return -EINVAL;
+
+ srep->mtu = mtu;
+
+ return update_srep_config(srep);
+}
+
+int osmo_stats_reporter_set_max_class(struct osmo_stats_reporter *srep,
+ enum osmo_stats_class class_id)
+{
+ if (class_id == OSMO_STATS_CLASS_UNKNOWN)
+ return -EINVAL;
+
+ srep->max_class = class_id;
+
+ return 0;
+}
+
+int osmo_stats_set_interval(int interval)
+{
+ if (interval <= 0)
+ return -EINVAL;
+
+ osmo_stats_config->interval = interval;
+ if (is_initialised)
+ start_timer();
+
+ return 0;
+}
+
+int osmo_stats_reporter_set_name_prefix(struct osmo_stats_reporter *srep, const char *prefix)
+{
+ talloc_free(srep->name_prefix);
+ srep->name_prefix = prefix && strlen(prefix) > 0 ?
+ talloc_strdup(srep, prefix) : NULL;
+
+ return update_srep_config(srep);
+}
+
+int osmo_stats_reporter_enable(struct osmo_stats_reporter *srep)
+{
+ srep->enabled = 1;
+
+ return update_srep_config(srep);
+}
+
+int osmo_stats_reporter_disable(struct osmo_stats_reporter *srep)
+{
+ srep->enabled = 0;
+
+ return update_srep_config(srep);
+}
+
+/*** i/o helper functions ***/
+
+int osmo_stats_reporter_udp_open(struct osmo_stats_reporter *srep)
+{
+ int sock;
+ int rc;
+ int buffer_size = STATS_DEFAULT_BUFLEN;
+
+ if (srep->fd != -1 && srep->close)
+ srep->close(srep);
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock == -1)
+ return -errno;
+
+#if defined(__APPLE__) && !defined(MSG_NOSIGNAL)
+ {
+ static int val = 1;
+
+ rc = setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, (void*)&val, sizeof(val));
+ goto failed;
+ }
+#endif
+ if (srep->bind_addr_len > 0) {
+ rc = bind(sock, &srep->bind_addr, srep->bind_addr_len);
+ if (rc == -1)
+ goto failed;
+ }
+
+ srep->fd = sock;
+
+ if (srep->mtu > 0) {
+ buffer_size = srep->mtu - 20 /* IP */ - 8 /* UDP */;
+ srep->agg_enabled = 1;
+ }
+
+ srep->buffer = msgb_alloc(buffer_size, "stats buffer");
+
+ return 0;
+
+failed:
+ rc = -errno;
+ close(sock);
+
+ return rc;
+}
+
+int osmo_stats_reporter_udp_close(struct osmo_stats_reporter *srep)
+{
+ int rc;
+ if (srep->fd == -1)
+ return -EBADF;
+
+ osmo_stats_reporter_send_buffer(srep);
+
+ rc = close(srep->fd);
+ srep->fd = -1;
+ msgb_free(srep->buffer);
+ srep->buffer = NULL;
+ return rc == -1 ? -errno : 0;
+}
+
+int osmo_stats_reporter_send(struct osmo_stats_reporter *srep, const char *data,
+ int data_len)
+{
+ int rc;
+
+ rc = sendto(srep->fd, data, data_len,
+#ifdef MSG_NOSIGNAL
+ MSG_NOSIGNAL |
+#endif
+ MSG_DONTWAIT,
+ &srep->dest_addr, srep->dest_addr_len);
+
+ if (rc == -1)
+ rc = -errno;
+
+ return rc;
+}
+
+int osmo_stats_reporter_send_buffer(struct osmo_stats_reporter *srep)
+{
+ int rc;
+
+ if (!srep->buffer || msgb_length(srep->buffer) == 0)
+ return 0;
+
+ rc = osmo_stats_reporter_send(srep,
+ (const char *)msgb_data(srep->buffer), msgb_length(srep->buffer));
+
+ msgb_trim(srep->buffer, 0);
+
+ return rc;
+}
+
+/*** log reporter ***/
+
+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);
+
+ srep->have_net_config = 0;
+
+ srep->send_counter = osmo_stats_reporter_log_send_counter;
+ srep->send_item = osmo_stats_reporter_log_send_item;
+
+ return srep;
+}
+
+static int osmo_stats_reporter_log_send(struct osmo_stats_reporter *srep,
+ const char *type,
+ const char *name1, unsigned int index1, const char *name2, int value,
+ const char *unit)
+{
+ LOGP(DLSTATS, LOGL_INFO,
+ "stats t=%s p=%s g=%s i=%u n=%s v=%d u=%s\n",
+ type, srep->name_prefix ? srep->name_prefix : "",
+ name1 ? name1 : "", index1,
+ name2, value, unit ? unit : "");
+
+ return 0;
+}
+
+
+static int osmo_stats_reporter_log_send_counter(struct osmo_stats_reporter *srep,
+ const struct rate_ctr_group *ctrg,
+ const struct rate_ctr_desc *desc,
+ int64_t value, int64_t delta)
+{
+ if (ctrg)
+ return osmo_stats_reporter_log_send(srep, "c",
+ ctrg->desc->group_name_prefix,
+ ctrg->idx,
+ desc->name, value, NULL);
+ else
+ return osmo_stats_reporter_log_send(srep, "c",
+ NULL, 0,
+ desc->name, value, NULL);
+}
+
+static int osmo_stats_reporter_log_send_item(struct osmo_stats_reporter *srep,
+ const struct osmo_stat_item_group *statg,
+ const struct osmo_stat_item_desc *desc, int64_t value)
+{
+ return osmo_stats_reporter_log_send(srep, "i",
+ statg->desc->group_name_prefix, statg->idx,
+ desc->name, value, desc->unit);
+}
+
+/*** helper for reporting ***/
+
+static int osmo_stats_reporter_check_config(struct osmo_stats_reporter *srep,
+ unsigned int index, int class_id)
+{
+ if (class_id == OSMO_STATS_CLASS_UNKNOWN)
+ class_id = index != 0 ?
+ OSMO_STATS_CLASS_SUBSCRIBER : OSMO_STATS_CLASS_GLOBAL;
+
+ return class_id <= srep->max_class;
+}
+
+/*** generic rate counter support ***/
+
+static int osmo_stats_reporter_send_counter(struct osmo_stats_reporter *srep,
+ const struct rate_ctr_group *ctrg,
+ const struct rate_ctr_desc *desc,
+ int64_t value, int64_t delta)
+{
+ if (!srep->send_counter)
+ return 0;
+
+ return srep->send_counter(srep, ctrg, desc, value, delta);
+}
+
+static int rate_ctr_handler(
+ struct rate_ctr_group *ctrg, struct rate_ctr *ctr,
+ const struct rate_ctr_desc *desc, void *sctx_)
+{
+ struct osmo_stats_reporter *srep;
+ int64_t delta = rate_ctr_difference(ctr);
+
+ llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
+ if (!srep->running)
+ continue;
+
+ if (delta == 0 && !srep->force_single_flush)
+ continue;
+
+ if (!osmo_stats_reporter_check_config(srep,
+ ctrg->idx, ctrg->desc->class_id))
+ continue;
+
+ osmo_stats_reporter_send_counter(srep, ctrg, desc,
+ ctr->current, delta);
+
+ /* TODO: handle result (log?, inc counter(!)?) or remove it */
+ }
+
+ return 0;
+}
+
+static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_)
+{
+ rate_ctr_for_each_counter(ctrg, rate_ctr_handler, sctx_);
+
+ return 0;
+}
+
+/*** stat item support ***/
+
+static int osmo_stats_reporter_send_item(struct osmo_stats_reporter *srep,
+ const struct osmo_stat_item_group *statg,
+ const struct osmo_stat_item_desc *desc,
+ int32_t value)
+{
+ if (!srep->send_item)
+ return 0;
+
+ return srep->send_item(srep, statg, desc, value);
+}
+
+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 idx = current_stat_item_index;
+ int32_t value;
+ int have_value;
+
+ have_value = osmo_stat_item_get_next(item, &idx, &value) > 0;
+ if (!have_value)
+ /* Send the last value in case a flush is requested */
+ value = osmo_stat_item_get_last(item);
+
+ do {
+ llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
+ if (!srep->running)
+ continue;
+
+ if (!have_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);
+ }
+
+ if (!have_value)
+ break;
+
+ have_value = osmo_stat_item_get_next(item, &idx, &value) > 0;
+ } while (have_value);
+
+ return 0;
+}
+
+static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *sctx_)
+{
+ osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, sctx_);
+
+ return 0;
+}
+
+/*** osmo counter support ***/
+
+static int handle_counter(struct osmo_counter *counter, void *sctx_)
+{
+ struct osmo_stats_reporter *srep;
+ struct rate_ctr_desc desc = {0};
+ /* Fake a rate counter description */
+ desc.name = counter->name;
+ desc.description = counter->description;
+
+ int delta = osmo_counter_difference(counter);
+
+ llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
+ if (!srep->running)
+ continue;
+
+ if (delta == 0 && !srep->force_single_flush)
+ continue;
+
+ osmo_stats_reporter_send_counter(srep, NULL, &desc,
+ counter->value, delta);
+
+ /* TODO: handle result (log?, inc counter(!)?) */
+ }
+
+ return 0;
+}
+
+
+/*** main reporting function ***/
+
+static void flush_all_reporters()
+{
+ struct osmo_stats_reporter *srep;
+
+ llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
+ if (!srep->running)
+ continue;
+
+ osmo_stats_reporter_send_buffer(srep);
+ srep->force_single_flush = 0;
+ }
+}
+
+int osmo_stats_report()
+{
+ /* per group actions */
+ osmo_counters_for_each(handle_counter, NULL);
+ rate_ctr_for_each_group(rate_ctr_group_handler, NULL);
+ osmo_stat_item_for_each_group(osmo_stat_item_group_handler, NULL);
+
+ /* global actions */
+ osmo_stat_item_discard_all(&current_stat_item_index);
+ flush_all_reporters();
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/src/stats_statsd.c b/src/shared/libosmocore/src/stats_statsd.c
new file mode 100644
index 00000000..8b53881e
--- /dev/null
+++ b/src/shared/libosmocore/src/stats_statsd.c
@@ -0,0 +1,169 @@
+/*
+ * (C) 2015 by Sysmocom s.f.m.c. GmbH
+ *
+ * Author: Jacob Erlbeck <jerlbeck@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.
+ *
+ * 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/stats.h>
+
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/stat_item.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/stats.h>
+
+static int osmo_stats_reporter_statsd_send_counter(struct osmo_stats_reporter *srep,
+ const struct rate_ctr_group *ctrg,
+ const struct rate_ctr_desc *desc,
+ int64_t value, int64_t delta);
+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);
+
+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);
+
+ srep->have_net_config = 1;
+
+ srep->open = osmo_stats_reporter_udp_open;
+ srep->close = osmo_stats_reporter_udp_close;
+ srep->send_counter = osmo_stats_reporter_statsd_send_counter;
+ srep->send_item = osmo_stats_reporter_statsd_send_item;
+
+ return srep;
+}
+
+static int osmo_stats_reporter_statsd_send(struct osmo_stats_reporter *srep,
+ const char *name1, unsigned int index1, const char *name2, int64_t value,
+ const char *unit)
+{
+ char *buf;
+ int buf_size;
+ int nchars, rc = 0;
+ char *fmt = NULL;
+ char *prefix = srep->name_prefix;
+ int old_len = msgb_length(srep->buffer);
+
+ if (prefix) {
+ if (name1) {
+ if (index1 != 0)
+ fmt = "%1$s.%2$s.%6$u.%3$s:%4$d|%5$s";
+ else
+ fmt = "%1$s.%2$s.%3$s:%4$d|%5$s";
+ } else {
+ fmt = "%1$s.%2$0.0s%3$s:%4$d|%5$s";
+ }
+ } else {
+ prefix = "";
+ if (name1) {
+ if (index1 != 0)
+ fmt = "%1$s%2$s.%6$u.%3$s:%4$d|%5$s";
+ else
+ fmt = "%1$s%2$s.%3$s:%4$d|%5$s";
+ } else {
+ fmt = "%1$s%2$0.0s%3$s:%4$d|%5$s";
+ }
+ }
+
+ if (srep->agg_enabled) {
+ if (msgb_length(srep->buffer) > 0 &&
+ msgb_tailroom(srep->buffer) > 0)
+ {
+ msgb_put_u8(srep->buffer, '\n');
+ }
+ }
+
+ buf = (char *)msgb_put(srep->buffer, 0);
+ buf_size = msgb_tailroom(srep->buffer);
+
+ nchars = snprintf(buf, buf_size, fmt,
+ prefix, name1, name2,
+ value, unit, index1);
+
+ if (nchars >= buf_size) {
+ /* Truncated */
+ /* Restore original buffer (without trailing LF) */
+ msgb_trim(srep->buffer, old_len);
+ /* Send it */
+ rc = osmo_stats_reporter_send_buffer(srep);
+
+ /* Try again */
+ buf = (char *)msgb_put(srep->buffer, 0);
+ buf_size = msgb_tailroom(srep->buffer);
+
+ nchars = snprintf(buf, buf_size, fmt,
+ prefix, name1, name2,
+ value, unit, index1);
+
+ if (nchars >= buf_size)
+ return -EMSGSIZE;
+ }
+
+ if (nchars > 0)
+ msgb_trim(srep->buffer, msgb_length(srep->buffer) + nchars);
+
+ if (!srep->agg_enabled)
+ rc = osmo_stats_reporter_send_buffer(srep);
+
+ return rc;
+}
+
+static int osmo_stats_reporter_statsd_send_counter(struct osmo_stats_reporter *srep,
+ const struct rate_ctr_group *ctrg,
+ 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");
+}
+
+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)
+{
+ const char *unit = desc->unit;
+
+ if (unit == OSMO_STAT_ITEM_NO_UNIT) {
+ unit = "g";
+ if (value < 0)
+ osmo_stats_reporter_statsd_send(srep,
+ statg->desc->group_name_prefix,
+ statg->idx,
+ desc->name, 0, unit);
+ }
+ return osmo_stats_reporter_statsd_send(srep,
+ statg->desc->group_name_prefix,
+ statg->idx,
+ desc->name, value, unit);
+}
diff --git a/src/shared/libosmocore/src/strrb.c b/src/shared/libosmocore/src/strrb.c
new file mode 100644
index 00000000..8f925bd3
--- /dev/null
+++ b/src/shared/libosmocore/src/strrb.c
@@ -0,0 +1,171 @@
+/* Ringbuffer implementation, tailored for logging.
+ * This is a lossy ringbuffer. It keeps up to N of the newest messages,
+ * overwriting the oldest as newer ones come in.
+ *
+ * (C) 2012-2013, Katerina Barone-Adesi <kat.obsc@gmail.com>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/*! \file strrb.c
+ * \brief Lossy string ringbuffer for logging; keeps newest messages.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <string.h>
+
+#include <osmocom/core/strrb.h>
+
+/* Ringbuffer assumptions, invarients, and notes:
+ * - start is the index of the first used index slot in the ring buffer.
+ * - end is the index of the next index slot in the ring buffer.
+ * - start == end => buffer is empty
+ * - Consequence: the buffer can hold at most size - 1 messages
+ * (if this were not the case, full and empty buffers would be indistinguishable
+ * given the conventions in this implementation).
+ * - Whenever the ringbuffer is full, start is advanced. The second oldest
+ * message becomes unreachable by valid indexes (end is not a valid index)
+ * and the oldest message is overwritten (if there was a message there, which
+ * is the case unless this is the first time the ringbuffer becomes full).
+*/
+
+/*! \brief Create an empty, initialized osmo_strrb.
+ * \param[in] ctx The talloc memory context which should own this.
+ * \param[in] rb_size The number of message slots the osmo_strrb can hold.
+ * \returns A struct osmo_strrb* on success, NULL in case of error.
+ *
+ * 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 *rb = NULL;
+ unsigned int i;
+
+ rb = talloc_zero(ctx, struct osmo_strrb);
+ if (!rb)
+ goto alloc_error;
+
+ /* start and end are zero already, which is correct */
+ rb->size = rb_size;
+
+ rb->buffer = talloc_array(rb, char *, rb->size);
+ if (!rb->buffer)
+ goto alloc_error;
+ for (i = 0; i < rb->size; i++) {
+ rb->buffer[i] =
+ talloc_zero_size(rb->buffer, RB_MAX_MESSAGE_SIZE);
+ if (!rb->buffer[i])
+ goto alloc_error;
+ }
+
+ return rb;
+
+alloc_error: /* talloc_free(NULL) is safe */
+ talloc_free(rb);
+ return NULL;
+}
+
+/*! \brief Check if an osmo_strrb is empty.
+ * \param[in] rb The osmo_strrb to check.
+ * \returns True if the osmo_strrb is empty, false otherwise.
+ */
+bool osmo_strrb_is_empty(const struct osmo_strrb *rb)
+{
+ return rb->end == rb->start;
+}
+
+/*! \brief Return a pointer to the Nth string in the osmo_strrb.
+ * \param[in] rb The osmo_strrb to search.
+ * \param[in] string_index The index sought (N), zero-indexed.
+ *
+ * Return a pointer to the Nth string in the osmo_strrb.
+ * Return NULL if there is no Nth string.
+ * Note that N is zero-indexed.
+ * \returns A pointer to the target string on success, NULL in case of error.
+ */
+const char *osmo_strrb_get_nth(const struct osmo_strrb *rb,
+ unsigned int string_index)
+{
+ unsigned int offset = string_index + rb->start;
+
+ if ((offset >= rb->size) && (rb->start > rb->end))
+ offset -= rb->size;
+ if (_osmo_strrb_is_bufindex_valid(rb, offset))
+ return rb->buffer[offset];
+
+ return NULL;
+}
+
+bool _osmo_strrb_is_bufindex_valid(const struct osmo_strrb *rb,
+ unsigned int bufi)
+{
+ if (osmo_strrb_is_empty(rb))
+ return 0;
+ if (bufi >= rb->size)
+ return 0;
+ if (rb->start < rb->end)
+ return (bufi >= rb->start) && (bufi < rb->end);
+ return (bufi < rb->end) || (bufi >= rb->start);
+}
+
+/*! \brief Count the number of log messages in an osmo_strrb.
+ * \param[in] rb The osmo_strrb to count the elements of.
+ *
+ * \returns The number of log messages in the osmo_strrb.
+ */
+size_t osmo_strrb_elements(const struct osmo_strrb *rb)
+{
+ if (rb->end < rb->start)
+ return rb->end + (rb->size - rb->start);
+
+ return rb->end - rb->start;
+}
+
+/*! \brief Add a string to the osmo_strrb.
+ * \param[in] rb The osmo_strrb to add to.
+ * \param[in] data The string to add.
+ *
+ * Add a message to the osmo_strrb.
+ * Older messages will be overwritten as necessary.
+ * \returns 0 normally, 1 as a warning (ie, if data was truncated).
+ */
+int osmo_strrb_add(struct osmo_strrb *rb, const char *data)
+{
+ size_t len = strlen(data);
+ int ret = 0;
+
+ if (len >= RB_MAX_MESSAGE_SIZE) {
+ len = RB_MAX_MESSAGE_SIZE - 1;
+ ret = 1;
+ }
+
+ memcpy(rb->buffer[rb->end], data, len);
+ rb->buffer[rb->end][len] = '\0';
+
+ rb->end += 1;
+ rb->end %= rb->size;
+
+ /* The buffer is full; oldest message is forgotten - see notes above */
+ if (rb->end == rb->start) {
+ rb->start += 1;
+ rb->start %= rb->size;
+ }
+ return ret;
+}
diff --git a/src/shared/libosmocore/src/talloc.c b/src/shared/libosmocore/src/talloc.c
deleted file mode 100644
index d3a0690f..00000000
--- a/src/shared/libosmocore/src/talloc.c
+++ /dev/null
@@ -1,1804 +0,0 @@
-/*
- Samba Unix SMB/CIFS implementation.
-
- Samba trivial allocation library - new interface
-
- NOTE: Please read talloc_guide.txt for full documentation
-
- Copyright (C) Andrew Tridgell 2004
- Copyright (C) Stefan Metzmacher 2006
-
- ** NOTE! The following LGPL license applies to the talloc
- ** library. This does NOT imply that all of Samba is released
- ** under the LGPL
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- This library 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-
-/*
- inspired by http://swapped.cc/halloc/
-*/
-
-#ifdef _SAMBA_BUILD_
-#include "version.h"
-#if (SAMBA_VERSION_MAJOR<4)
-#include "includes.h"
-/* This is to circumvent SAMBA3's paranoid malloc checker. Here in this file
- * we trust ourselves... */
-#ifdef malloc
-#undef malloc
-#endif
-#ifdef realloc
-#undef realloc
-#endif
-#define _TALLOC_SAMBA3
-#endif /* (SAMBA_VERSION_MAJOR<4) */
-#endif /* _SAMBA_BUILD_ */
-
-#ifndef _TALLOC_SAMBA3
-//#include "replace.h"
-#include <unistd.h>
-#include <stdio.h>
-#include <stdbool.h>
-#define __USE_GNU
-#include <string.h>
-#undef __USE_GNU
-#include <osmocom/core/talloc.h>
-#define MIN(x,y) ((x) < (y) ? (x) : (y))
-#endif /* not _TALLOC_SAMBA3 */
-
-/* use this to force every realloc to change the pointer, to stress test
- code that might not cope */
-#define ALWAYS_REALLOC 0
-
-
-#define MAX_TALLOC_SIZE 0x10000000
-#define TALLOC_MAGIC 0xe814ec70
-#define TALLOC_FLAG_FREE 0x01
-#define TALLOC_FLAG_LOOP 0x02
-#define TALLOC_FLAG_POOL 0x04 /* This is a talloc pool */
-#define TALLOC_FLAG_POOLMEM 0x08 /* This is allocated in a pool */
-#define TALLOC_MAGIC_REFERENCE ((const char *)1)
-
-/* by default we abort when given a bad pointer (such as when talloc_free() is called
- on a pointer that came from malloc() */
-#ifndef TALLOC_ABORT
-#define TALLOC_ABORT(reason) abort()
-#endif
-
-#ifndef discard_const_p
-#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
-# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr)))
-#else
-# define discard_const_p(type, ptr) ((type *)(ptr))
-#endif
-#endif
-
-/* these macros gain us a few percent of speed on gcc */
-#if (__GNUC__ >= 3)
-/* the strange !! is to ensure that __builtin_expect() takes either 0 or 1
- as its first argument */
-#ifndef likely
-#define likely(x) __builtin_expect(!!(x), 1)
-#endif
-#ifndef unlikely
-#define unlikely(x) __builtin_expect(!!(x), 0)
-#endif
-#else
-#ifndef likely
-#define likely(x) (x)
-#endif
-#ifndef unlikely
-#define unlikely(x) (x)
-#endif
-#endif
-
-#ifdef __APPLE__
-/* taken from http://insanecoding.blogspot.com/2007/03/methods-for-safe-string-handling.html */
-size_t strnlen(const char *s, size_t n)
-{
- const char *p = (const char *)memchr(s, 0, n);
- return(p ? p-s : n);
-}
-#endif
-
-/* this null_context is only used if talloc_enable_leak_report() or
- talloc_enable_leak_report_full() is called, otherwise it remains
- NULL
-*/
-static void *null_context;
-static void *autofree_context;
-
-struct talloc_reference_handle {
- struct talloc_reference_handle *next, *prev;
- void *ptr;
-};
-
-typedef int (*talloc_destructor_t)(void *);
-
-struct talloc_chunk {
- struct talloc_chunk *next, *prev;
- struct talloc_chunk *parent, *child;
- struct talloc_reference_handle *refs;
- talloc_destructor_t destructor;
- const char *name;
- size_t size;
- unsigned flags;
-
- /*
- * "pool" has dual use:
- *
- * For the talloc pool itself (i.e. TALLOC_FLAG_POOL is set), "pool"
- * marks the end of the currently allocated area.
- *
- * For members of the pool (i.e. TALLOC_FLAG_POOLMEM is set), "pool"
- * is a pointer to the struct talloc_chunk of the pool that it was
- * allocated from. This way children can quickly find the pool to chew
- * from.
- */
- void *pool;
-};
-
-/* 16 byte alignment seems to keep everyone happy */
-#define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15)
-#define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc))
-
-static void (*talloc_abort_fn)(const char *reason);
-
-void talloc_set_abort_fn(void (*abort_fn)(const char *reason))
-{
- talloc_abort_fn = abort_fn;
-}
-
-static void talloc_abort(const char *reason)
-{
- if (!talloc_abort_fn) {
- TALLOC_ABORT(reason);
- }
-
- talloc_abort_fn(reason);
-}
-
-static void talloc_abort_double_free(void)
-{
- talloc_abort("Bad talloc magic value - double free");
-}
-
-static void talloc_abort_unknown_value(void)
-{
- talloc_abort("Bad talloc magic value - unknown value");
-}
-
-/* panic if we get a bad magic value */
-static inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr)
-{
- const char *pp = (const char *)ptr;
- struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE);
- if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) {
- if (tc->flags & TALLOC_FLAG_FREE) {
- talloc_abort_double_free();
- } else {
- talloc_abort_unknown_value();
- }
- }
- return tc;
-}
-
-/* hook into the front of the list */
-#define _TLIST_ADD(list, p) \
-do { \
- if (!(list)) { \
- (list) = (p); \
- (p)->next = (p)->prev = NULL; \
- } else { \
- (list)->prev = (p); \
- (p)->next = (list); \
- (p)->prev = NULL; \
- (list) = (p); \
- }\
-} while (0)
-
-/* remove an element from a list - element doesn't have to be in list. */
-#define _TLIST_REMOVE(list, p) \
-do { \
- if ((p) == (list)) { \
- (list) = (p)->next; \
- if (list) (list)->prev = NULL; \
- } else { \
- if ((p)->prev) (p)->prev->next = (p)->next; \
- if ((p)->next) (p)->next->prev = (p)->prev; \
- } \
- if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \
-} while (0)
-
-
-/*
- return the parent chunk of a pointer
-*/
-static inline struct talloc_chunk *talloc_parent_chunk(const void *ptr)
-{
- struct talloc_chunk *tc;
-
- if (unlikely(ptr == NULL)) {
- return NULL;
- }
-
- tc = talloc_chunk_from_ptr(ptr);
- while (tc->prev) tc=tc->prev;
-
- return tc->parent;
-}
-
-void *talloc_parent(const void *ptr)
-{
- struct talloc_chunk *tc = talloc_parent_chunk(ptr);
- return tc? TC_PTR_FROM_CHUNK(tc) : NULL;
-}
-
-/*
- find parents name
-*/
-const char *talloc_parent_name(const void *ptr)
-{
- struct talloc_chunk *tc = talloc_parent_chunk(ptr);
- return tc? tc->name : NULL;
-}
-
-/*
- A pool carries an in-pool object count count in the first 16 bytes.
- bytes. This is done to support talloc_steal() to a parent outside of the
- pool. The count includes the pool itself, so a talloc_free() on a pool will
- only destroy the pool if the count has dropped to zero. A talloc_free() of a
- pool member will reduce the count, and eventually also call free(3) on the
- pool memory.
-
- The object count is not put into "struct talloc_chunk" because it is only
- relevant for talloc pools and the alignment to 16 bytes would increase the
- memory footprint of each talloc chunk by those 16 bytes.
-*/
-
-#define TALLOC_POOL_HDR_SIZE 16
-
-static unsigned int *talloc_pool_objectcount(struct talloc_chunk *tc)
-{
- return (unsigned int *)((char *)tc + sizeof(struct talloc_chunk));
-}
-
-/*
- Allocate from a pool
-*/
-
-static struct talloc_chunk *talloc_alloc_pool(struct talloc_chunk *parent,
- size_t size)
-{
- struct talloc_chunk *pool_ctx = NULL;
- size_t space_left;
- struct talloc_chunk *result;
- size_t chunk_size;
-
- if (parent == NULL) {
- return NULL;
- }
-
- if (parent->flags & TALLOC_FLAG_POOL) {
- pool_ctx = parent;
- }
- else if (parent->flags & TALLOC_FLAG_POOLMEM) {
- pool_ctx = (struct talloc_chunk *)parent->pool;
- }
-
- if (pool_ctx == NULL) {
- return NULL;
- }
-
- space_left = ((char *)pool_ctx + TC_HDR_SIZE + pool_ctx->size)
- - ((char *)pool_ctx->pool);
-
- /*
- * Align size to 16 bytes
- */
- chunk_size = ((size + 15) & ~15);
-
- if (space_left < chunk_size) {
- return NULL;
- }
-
- result = (struct talloc_chunk *)pool_ctx->pool;
-
-#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED)
- VALGRIND_MAKE_MEM_UNDEFINED(result, size);
-#endif
-
- pool_ctx->pool = (void *)((char *)result + chunk_size);
-
- result->flags = TALLOC_MAGIC | TALLOC_FLAG_POOLMEM;
- result->pool = pool_ctx;
-
- *talloc_pool_objectcount(pool_ctx) += 1;
-
- return result;
-}
-
-/*
- Allocate a bit of memory as a child of an existing pointer
-*/
-static inline void *__talloc(const void *context, size_t size)
-{
- struct talloc_chunk *tc = NULL;
-
- if (unlikely(context == NULL)) {
- context = null_context;
- }
-
- if (unlikely(size >= MAX_TALLOC_SIZE)) {
- return NULL;
- }
-
- if (context != NULL) {
- tc = talloc_alloc_pool(talloc_chunk_from_ptr(context),
- TC_HDR_SIZE+size);
- }
-
- if (tc == NULL) {
- tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size);
- if (unlikely(tc == NULL)) return NULL;
- tc->flags = TALLOC_MAGIC;
- tc->pool = NULL;
- }
-
- tc->size = size;
- tc->destructor = NULL;
- tc->child = NULL;
- tc->name = NULL;
- tc->refs = NULL;
-
- if (likely(context)) {
- struct talloc_chunk *parent = talloc_chunk_from_ptr(context);
-
- if (parent->child) {
- parent->child->parent = NULL;
- tc->next = parent->child;
- tc->next->prev = tc;
- } else {
- tc->next = NULL;
- }
- tc->parent = parent;
- tc->prev = NULL;
- parent->child = tc;
- } else {
- tc->next = tc->prev = tc->parent = NULL;
- }
-
- return TC_PTR_FROM_CHUNK(tc);
-}
-
-/*
- * Create a talloc pool
- */
-
-void *talloc_pool(const void *context, size_t size)
-{
- void *result = __talloc(context, size + TALLOC_POOL_HDR_SIZE);
- struct talloc_chunk *tc;
-
- if (unlikely(result == NULL)) {
- return NULL;
- }
-
- tc = talloc_chunk_from_ptr(result);
-
- tc->flags |= TALLOC_FLAG_POOL;
- tc->pool = (char *)result + TALLOC_POOL_HDR_SIZE;
-
- *talloc_pool_objectcount(tc) = 1;
-
-#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
- VALGRIND_MAKE_MEM_NOACCESS(tc->pool, size);
-#endif
-
- return result;
-}
-
-/*
- setup a destructor to be called on free of a pointer
- the destructor should return 0 on success, or -1 on failure.
- if the destructor fails then the free is failed, and the memory can
- be continued to be used
-*/
-void _talloc_set_destructor(const void *ptr, int (*destructor)(void *))
-{
- struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
- tc->destructor = destructor;
-}
-
-/*
- increase the reference count on a piece of memory.
-*/
-int talloc_increase_ref_count(const void *ptr)
-{
- if (unlikely(!talloc_reference(null_context, ptr))) {
- return -1;
- }
- return 0;
-}
-
-/*
- helper for talloc_reference()
-
- this is referenced by a function pointer and should not be inline
-*/
-static int talloc_reference_destructor(struct talloc_reference_handle *handle)
-{
- struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr);
- _TLIST_REMOVE(ptr_tc->refs, handle);
- return 0;
-}
-
-/*
- more efficient way to add a name to a pointer - the name must point to a
- true string constant
-*/
-static inline void _talloc_set_name_const(const void *ptr, const char *name)
-{
- struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
- tc->name = name;
-}
-
-/*
- internal talloc_named_const()
-*/
-static inline void *_talloc_named_const(const void *context, size_t size, const char *name)
-{
- void *ptr;
-
- ptr = __talloc(context, size);
- if (unlikely(ptr == NULL)) {
- return NULL;
- }
-
- _talloc_set_name_const(ptr, name);
-
- return ptr;
-}
-
-/*
- make a secondary reference to a pointer, hanging off the given context.
- the pointer remains valid until both the original caller and this given
- context are freed.
-
- the major use for this is when two different structures need to reference the
- same underlying data, and you want to be able to free the two instances separately,
- and in either order
-*/
-void *_talloc_reference(const void *context, const void *ptr)
-{
- struct talloc_chunk *tc;
- struct talloc_reference_handle *handle;
- if (unlikely(ptr == NULL)) return NULL;
-
- tc = talloc_chunk_from_ptr(ptr);
- handle = (struct talloc_reference_handle *)_talloc_named_const(context,
- sizeof(struct talloc_reference_handle),
- TALLOC_MAGIC_REFERENCE);
- if (unlikely(handle == NULL)) return NULL;
-
- /* note that we hang the destructor off the handle, not the
- main context as that allows the caller to still setup their
- own destructor on the context if they want to */
- talloc_set_destructor(handle, talloc_reference_destructor);
- handle->ptr = discard_const_p(void, ptr);
- _TLIST_ADD(tc->refs, handle);
- return handle->ptr;
-}
-
-
-/*
- internal talloc_free call
-*/
-static inline int _talloc_free(void *ptr)
-{
- struct talloc_chunk *tc;
-
- if (unlikely(ptr == NULL)) {
- return -1;
- }
-
- tc = talloc_chunk_from_ptr(ptr);
-
- if (unlikely(tc->refs)) {
- int is_child;
- /* check this is a reference from a child or grantchild
- * back to it's parent or grantparent
- *
- * in that case we need to remove the reference and
- * call another instance of talloc_free() on the current
- * pointer.
- */
- is_child = talloc_is_parent(tc->refs, ptr);
- _talloc_free(tc->refs);
- if (is_child) {
- return _talloc_free(ptr);
- }
- return -1;
- }
-
- if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) {
- /* we have a free loop - stop looping */
- return 0;
- }
-
- if (unlikely(tc->destructor)) {
- talloc_destructor_t d = tc->destructor;
- if (d == (talloc_destructor_t)-1) {
- return -1;
- }
- tc->destructor = (talloc_destructor_t)-1;
- if (d(ptr) == -1) {
- tc->destructor = d;
- return -1;
- }
- tc->destructor = NULL;
- }
-
- if (tc->parent) {
- _TLIST_REMOVE(tc->parent->child, tc);
- if (tc->parent->child) {
- tc->parent->child->parent = tc->parent;
- }
- } else {
- if (tc->prev) tc->prev->next = tc->next;
- if (tc->next) tc->next->prev = tc->prev;
- }
-
- tc->flags |= TALLOC_FLAG_LOOP;
-
- while (tc->child) {
- /* we need to work out who will own an abandoned child
- if it cannot be freed. In priority order, the first
- choice is owner of any remaining reference to this
- pointer, the second choice is our parent, and the
- final choice is the null context. */
- void *child = TC_PTR_FROM_CHUNK(tc->child);
- const void *new_parent = null_context;
- if (unlikely(tc->child->refs)) {
- struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
- if (p) new_parent = TC_PTR_FROM_CHUNK(p);
- }
- if (unlikely(_talloc_free(child) == -1)) {
- if (new_parent == null_context) {
- struct talloc_chunk *p = talloc_parent_chunk(ptr);
- if (p) new_parent = TC_PTR_FROM_CHUNK(p);
- }
- talloc_steal(new_parent, child);
- }
- }
-
- tc->flags |= TALLOC_FLAG_FREE;
-
- if (tc->flags & (TALLOC_FLAG_POOL|TALLOC_FLAG_POOLMEM)) {
- struct talloc_chunk *pool;
- unsigned int *pool_object_count;
-
- pool = (tc->flags & TALLOC_FLAG_POOL)
- ? tc : (struct talloc_chunk *)tc->pool;
-
- pool_object_count = talloc_pool_objectcount(pool);
-
- if (*pool_object_count == 0) {
- talloc_abort("Pool object count zero!");
- }
-
- *pool_object_count -= 1;
-
- if (*pool_object_count == 0) {
- free(pool);
- }
- }
- else {
- free(tc);
- }
- return 0;
-}
-
-/*
- move a lump of memory from one talloc context to another return the
- ptr on success, or NULL if it could not be transferred.
- passing NULL as ptr will always return NULL with no side effects.
-*/
-void *_talloc_steal(const void *new_ctx, const void *ptr)
-{
- struct talloc_chunk *tc, *new_tc;
-
- if (unlikely(!ptr)) {
- return NULL;
- }
-
- if (unlikely(new_ctx == NULL)) {
- new_ctx = null_context;
- }
-
- tc = talloc_chunk_from_ptr(ptr);
-
- if (unlikely(new_ctx == NULL)) {
- if (tc->parent) {
- _TLIST_REMOVE(tc->parent->child, tc);
- if (tc->parent->child) {
- tc->parent->child->parent = tc->parent;
- }
- } else {
- if (tc->prev) tc->prev->next = tc->next;
- if (tc->next) tc->next->prev = tc->prev;
- }
-
- tc->parent = tc->next = tc->prev = NULL;
- return discard_const_p(void, ptr);
- }
-
- new_tc = talloc_chunk_from_ptr(new_ctx);
-
- if (unlikely(tc == new_tc || tc->parent == new_tc)) {
- return discard_const_p(void, ptr);
- }
-
- if (tc->parent) {
- _TLIST_REMOVE(tc->parent->child, tc);
- if (tc->parent->child) {
- tc->parent->child->parent = tc->parent;
- }
- } else {
- if (tc->prev) tc->prev->next = tc->next;
- if (tc->next) tc->next->prev = tc->prev;
- }
-
- tc->parent = new_tc;
- if (new_tc->child) new_tc->child->parent = NULL;
- _TLIST_ADD(new_tc->child, tc);
-
- return discard_const_p(void, ptr);
-}
-
-
-
-/*
- remove a secondary reference to a pointer. This undo's what
- talloc_reference() has done. The context and pointer arguments
- must match those given to a talloc_reference()
-*/
-static inline int talloc_unreference(const void *context, const void *ptr)
-{
- struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
- struct talloc_reference_handle *h;
-
- if (unlikely(context == NULL)) {
- context = null_context;
- }
-
- for (h=tc->refs;h;h=h->next) {
- struct talloc_chunk *p = talloc_parent_chunk(h);
- if (p == NULL) {
- if (context == NULL) break;
- } else if (TC_PTR_FROM_CHUNK(p) == context) {
- break;
- }
- }
- if (h == NULL) {
- return -1;
- }
-
- return _talloc_free(h);
-}
-
-/*
- remove a specific parent context from a pointer. This is a more
- controlled varient of talloc_free()
-*/
-int talloc_unlink(const void *context, void *ptr)
-{
- struct talloc_chunk *tc_p, *new_p;
- void *new_parent;
-
- if (ptr == NULL) {
- return -1;
- }
-
- if (context == NULL) {
- context = null_context;
- }
-
- if (talloc_unreference(context, ptr) == 0) {
- return 0;
- }
-
- if (context == NULL) {
- if (talloc_parent_chunk(ptr) != NULL) {
- return -1;
- }
- } else {
- if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) {
- return -1;
- }
- }
-
- tc_p = talloc_chunk_from_ptr(ptr);
-
- if (tc_p->refs == NULL) {
- return _talloc_free(ptr);
- }
-
- new_p = talloc_parent_chunk(tc_p->refs);
- if (new_p) {
- new_parent = TC_PTR_FROM_CHUNK(new_p);
- } else {
- new_parent = NULL;
- }
-
- if (talloc_unreference(new_parent, ptr) != 0) {
- return -1;
- }
-
- talloc_steal(new_parent, ptr);
-
- return 0;
-}
-
-/*
- add a name to an existing pointer - va_list version
-*/
-static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
-
-static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap)
-{
- struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
- tc->name = talloc_vasprintf(ptr, fmt, ap);
- if (likely(tc->name)) {
- _talloc_set_name_const(tc->name, ".name");
- }
- return tc->name;
-}
-
-/*
- add a name to an existing pointer
-*/
-const char *talloc_set_name(const void *ptr, const char *fmt, ...)
-{
- const char *name;
- va_list ap;
- va_start(ap, fmt);
- name = talloc_set_name_v(ptr, fmt, ap);
- va_end(ap);
- return name;
-}
-
-
-/*
- create a named talloc pointer. Any talloc pointer can be named, and
- talloc_named() operates just like talloc() except that it allows you
- to name the pointer.
-*/
-void *talloc_named(const void *context, size_t size, const char *fmt, ...)
-{
- va_list ap;
- void *ptr;
- const char *name;
-
- ptr = __talloc(context, size);
- if (unlikely(ptr == NULL)) return NULL;
-
- va_start(ap, fmt);
- name = talloc_set_name_v(ptr, fmt, ap);
- va_end(ap);
-
- if (unlikely(name == NULL)) {
- _talloc_free(ptr);
- return NULL;
- }
-
- return ptr;
-}
-
-/*
- return the name of a talloc ptr, or "UNNAMED"
-*/
-const char *talloc_get_name(const void *ptr)
-{
- struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
- if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) {
- return ".reference";
- }
- if (likely(tc->name)) {
- return tc->name;
- }
- return "UNNAMED";
-}
-
-
-/*
- check if a pointer has the given name. If it does, return the pointer,
- otherwise return NULL
-*/
-void *talloc_check_name(const void *ptr, const char *name)
-{
- const char *pname;
- if (unlikely(ptr == NULL)) return NULL;
- pname = talloc_get_name(ptr);
- if (likely(pname == name || strcmp(pname, name) == 0)) {
- return discard_const_p(void, ptr);
- }
- return NULL;
-}
-
-static void talloc_abort_type_missmatch(const char *location,
- const char *name,
- const char *expected)
-{
- const char *reason;
-
- reason = talloc_asprintf(NULL,
- "%s: Type mismatch: name[%s] expected[%s]",
- location,
- name?name:"NULL",
- expected);
- if (!reason) {
- reason = "Type mismatch";
- }
-
- talloc_abort(reason);
-}
-
-void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location)
-{
- const char *pname;
-
- if (unlikely(ptr == NULL)) {
- talloc_abort_type_missmatch(location, NULL, name);
- return NULL;
- }
-
- pname = talloc_get_name(ptr);
- if (likely(pname == name || strcmp(pname, name) == 0)) {
- return discard_const_p(void, ptr);
- }
-
- talloc_abort_type_missmatch(location, pname, name);
- return NULL;
-}
-
-/*
- this is for compatibility with older versions of talloc
-*/
-void *talloc_init(const char *fmt, ...)
-{
- va_list ap;
- void *ptr;
- const char *name;
-
- /*
- * samba3 expects talloc_report_depth_cb(NULL, ...)
- * reports all talloc'ed memory, so we need to enable
- * null_tracking
- */
- talloc_enable_null_tracking();
-
- ptr = __talloc(NULL, 0);
- if (unlikely(ptr == NULL)) return NULL;
-
- va_start(ap, fmt);
- name = talloc_set_name_v(ptr, fmt, ap);
- va_end(ap);
-
- if (unlikely(name == NULL)) {
- _talloc_free(ptr);
- return NULL;
- }
-
- return ptr;
-}
-
-/*
- this is a replacement for the Samba3 talloc_destroy_pool functionality. It
- should probably not be used in new code. It's in here to keep the talloc
- code consistent across Samba 3 and 4.
-*/
-void talloc_free_children(void *ptr)
-{
- struct talloc_chunk *tc;
-
- if (unlikely(ptr == NULL)) {
- return;
- }
-
- tc = talloc_chunk_from_ptr(ptr);
-
- while (tc->child) {
- /* we need to work out who will own an abandoned child
- if it cannot be freed. In priority order, the first
- choice is owner of any remaining reference to this
- pointer, the second choice is our parent, and the
- final choice is the null context. */
- void *child = TC_PTR_FROM_CHUNK(tc->child);
- const void *new_parent = null_context;
- if (unlikely(tc->child->refs)) {
- struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
- if (p) new_parent = TC_PTR_FROM_CHUNK(p);
- }
- if (unlikely(_talloc_free(child) == -1)) {
- if (new_parent == null_context) {
- struct talloc_chunk *p = talloc_parent_chunk(ptr);
- if (p) new_parent = TC_PTR_FROM_CHUNK(p);
- }
- talloc_steal(new_parent, child);
- }
- }
-
- if ((tc->flags & TALLOC_FLAG_POOL)
- && (*talloc_pool_objectcount(tc) == 1)) {
- tc->pool = ((char *)tc + TC_HDR_SIZE + TALLOC_POOL_HDR_SIZE);
-#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
- VALGRIND_MAKE_MEM_NOACCESS(
- tc->pool, tc->size - TALLOC_POOL_HDR_SIZE);
-#endif
- }
-}
-
-/*
- Allocate a bit of memory as a child of an existing pointer
-*/
-void *_talloc(const void *context, size_t size)
-{
- return __talloc(context, size);
-}
-
-/*
- externally callable talloc_set_name_const()
-*/
-void talloc_set_name_const(const void *ptr, const char *name)
-{
- _talloc_set_name_const(ptr, name);
-}
-
-/*
- create a named talloc pointer. Any talloc pointer can be named, and
- talloc_named() operates just like talloc() except that it allows you
- to name the pointer.
-*/
-void *talloc_named_const(const void *context, size_t size, const char *name)
-{
- return _talloc_named_const(context, size, name);
-}
-
-/*
- free a talloc pointer. This also frees all child pointers of this
- pointer recursively
-
- return 0 if the memory is actually freed, otherwise -1. The memory
- will not be freed if the ref_count is > 1 or the destructor (if
- any) returns non-zero
-*/
-int talloc_free(void *ptr)
-{
- return _talloc_free(ptr);
-}
-
-
-
-/*
- A talloc version of realloc. The context argument is only used if
- ptr is NULL
-*/
-void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name)
-{
- struct talloc_chunk *tc;
- void *new_ptr;
- bool malloced = false;
-
- /* size zero is equivalent to free() */
- if (unlikely(size == 0)) {
- _talloc_free(ptr);
- return NULL;
- }
-
- if (unlikely(size >= MAX_TALLOC_SIZE)) {
- return NULL;
- }
-
- /* realloc(NULL) is equivalent to malloc() */
- if (ptr == NULL) {
- return _talloc_named_const(context, size, name);
- }
-
- tc = talloc_chunk_from_ptr(ptr);
-
- /* don't allow realloc on referenced pointers */
- if (unlikely(tc->refs)) {
- return NULL;
- }
-
- /* don't let anybody try to realloc a talloc_pool */
- if (unlikely(tc->flags & TALLOC_FLAG_POOL)) {
- return NULL;
- }
-
- /* don't shrink if we have less than 1k to gain */
- if ((size < tc->size) && ((tc->size - size) < 1024)) {
- tc->size = size;
- return ptr;
- }
-
- /* by resetting magic we catch users of the old memory */
- tc->flags |= TALLOC_FLAG_FREE;
-
-#if ALWAYS_REALLOC
- new_ptr = malloc(size + TC_HDR_SIZE);
- if (new_ptr) {
- memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE);
- free(tc);
- }
-#else
- if (tc->flags & TALLOC_FLAG_POOLMEM) {
-
- new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE);
- *talloc_pool_objectcount((struct talloc_chunk *)
- (tc->pool)) -= 1;
-
- if (new_ptr == NULL) {
- new_ptr = malloc(TC_HDR_SIZE+size);
- malloced = true;
- }
-
- if (new_ptr) {
- memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE);
- }
- }
- else {
- new_ptr = realloc(tc, size + TC_HDR_SIZE);
- }
-#endif
- if (unlikely(!new_ptr)) {
- tc->flags &= ~TALLOC_FLAG_FREE;
- return NULL;
- }
-
- tc = (struct talloc_chunk *)new_ptr;
- tc->flags &= ~TALLOC_FLAG_FREE;
- if (malloced) {
- tc->flags &= ~TALLOC_FLAG_POOLMEM;
- }
- if (tc->parent) {
- tc->parent->child = tc;
- }
- if (tc->child) {
- tc->child->parent = tc;
- }
-
- if (tc->prev) {
- tc->prev->next = tc;
- }
- if (tc->next) {
- tc->next->prev = tc;
- }
-
- tc->size = size;
- _talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name);
-
- return TC_PTR_FROM_CHUNK(tc);
-}
-
-/*
- a wrapper around talloc_steal() for situations where you are moving a pointer
- between two structures, and want the old pointer to be set to NULL
-*/
-void *_talloc_move(const void *new_ctx, const void *_pptr)
-{
- const void **pptr = discard_const_p(const void *,_pptr);
- void *ret = _talloc_steal(new_ctx, *pptr);
- (*pptr) = NULL;
- return ret;
-}
-
-/*
- return the total size of a talloc pool (subtree)
-*/
-size_t talloc_total_size(const void *ptr)
-{
- size_t total = 0;
- struct talloc_chunk *c, *tc;
-
- if (ptr == NULL) {
- ptr = null_context;
- }
- if (ptr == NULL) {
- return 0;
- }
-
- tc = talloc_chunk_from_ptr(ptr);
-
- if (tc->flags & TALLOC_FLAG_LOOP) {
- return 0;
- }
-
- tc->flags |= TALLOC_FLAG_LOOP;
-
- total = tc->size;
- for (c=tc->child;c;c=c->next) {
- total += talloc_total_size(TC_PTR_FROM_CHUNK(c));
- }
-
- tc->flags &= ~TALLOC_FLAG_LOOP;
-
- return total;
-}
-
-/*
- return the total number of blocks in a talloc pool (subtree)
-*/
-size_t talloc_total_blocks(const void *ptr)
-{
- size_t total = 0;
- struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr);
-
- if (tc->flags & TALLOC_FLAG_LOOP) {
- return 0;
- }
-
- tc->flags |= TALLOC_FLAG_LOOP;
-
- total++;
- for (c=tc->child;c;c=c->next) {
- total += talloc_total_blocks(TC_PTR_FROM_CHUNK(c));
- }
-
- tc->flags &= ~TALLOC_FLAG_LOOP;
-
- return total;
-}
-
-/*
- return the number of external references to a pointer
-*/
-size_t talloc_reference_count(const void *ptr)
-{
- struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
- struct talloc_reference_handle *h;
- size_t ret = 0;
-
- for (h=tc->refs;h;h=h->next) {
- ret++;
- }
- return ret;
-}
-
-/*
- report on memory usage by all children of a pointer, giving a full tree view
-*/
-void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
- void (*callback)(const void *ptr,
- int depth, int max_depth,
- int is_ref,
- void *private_data),
- void *private_data)
-{
- struct talloc_chunk *c, *tc;
-
- if (ptr == NULL) {
- ptr = null_context;
- }
- if (ptr == NULL) return;
-
- tc = talloc_chunk_from_ptr(ptr);
-
- if (tc->flags & TALLOC_FLAG_LOOP) {
- return;
- }
-
- callback(ptr, depth, max_depth, 0, private_data);
-
- if (max_depth >= 0 && depth >= max_depth) {
- return;
- }
-
- tc->flags |= TALLOC_FLAG_LOOP;
- for (c=tc->child;c;c=c->next) {
- if (c->name == TALLOC_MAGIC_REFERENCE) {
- struct talloc_reference_handle *h = (struct talloc_reference_handle *)TC_PTR_FROM_CHUNK(c);
- callback(h->ptr, depth + 1, max_depth, 1, private_data);
- } else {
- talloc_report_depth_cb(TC_PTR_FROM_CHUNK(c), depth + 1, max_depth, callback, private_data);
- }
- }
- tc->flags &= ~TALLOC_FLAG_LOOP;
-}
-
-static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_depth, int is_ref, void *_f)
-{
- const char *name = talloc_get_name(ptr);
- FILE *f = (FILE *)_f;
-
- if (is_ref) {
- fprintf(f, "%*sreference to: %s\n", depth*4, "", name);
- return;
- }
-
- if (depth == 0) {
- fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n",
- (max_depth < 0 ? "full " :""), name,
- (unsigned long)talloc_total_size(ptr),
- (unsigned long)talloc_total_blocks(ptr));
- return;
- }
-
- fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n",
- depth*4, "",
- name,
- (unsigned long)talloc_total_size(ptr),
- (unsigned long)talloc_total_blocks(ptr),
- (int)talloc_reference_count(ptr), ptr);
-
-#if 0
- fprintf(f, "content: ");
- if (talloc_total_size(ptr)) {
- int tot = talloc_total_size(ptr);
- int i;
-
- for (i = 0; i < tot; i++) {
- if ((((char *)ptr)[i] > 31) && (((char *)ptr)[i] < 126)) {
- fprintf(f, "%c", ((char *)ptr)[i]);
- } else {
- fprintf(f, "~%02x", ((char *)ptr)[i]);
- }
- }
- }
- fprintf(f, "\n");
-#endif
-}
-
-/*
- report on memory usage by all children of a pointer, giving a full tree view
-*/
-void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f)
-{
- talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f);
- fflush(f);
-}
-
-/*
- report on memory usage by all children of a pointer, giving a full tree view
-*/
-void talloc_report_full(const void *ptr, FILE *f)
-{
- talloc_report_depth_file(ptr, 0, -1, f);
-}
-
-/*
- report on memory usage by all children of a pointer
-*/
-void talloc_report(const void *ptr, FILE *f)
-{
- talloc_report_depth_file(ptr, 0, 1, f);
-}
-
-/*
- report on any memory hanging off the null context
-*/
-static void talloc_report_null(void)
-{
- if (talloc_total_size(null_context) != 0) {
- talloc_report(null_context, stderr);
- }
-}
-
-/*
- report on any memory hanging off the null context
-*/
-static void talloc_report_null_full(void)
-{
- if (talloc_total_size(null_context) != 0) {
- talloc_report_full(null_context, stderr);
- }
-}
-
-/*
- enable tracking of the NULL context
-*/
-void talloc_enable_null_tracking(void)
-{
- if (null_context == NULL) {
- null_context = _talloc_named_const(NULL, 0, "null_context");
- }
-}
-
-/*
- disable tracking of the NULL context
-*/
-void talloc_disable_null_tracking(void)
-{
- _talloc_free(null_context);
- null_context = NULL;
-}
-
-/*
- enable leak reporting on exit
-*/
-void talloc_enable_leak_report(void)
-{
- talloc_enable_null_tracking();
- atexit(talloc_report_null);
-}
-
-/*
- enable full leak reporting on exit
-*/
-void talloc_enable_leak_report_full(void)
-{
- talloc_enable_null_tracking();
- atexit(talloc_report_null_full);
-}
-
-/*
- talloc and zero memory.
-*/
-void *_talloc_zero(const void *ctx, size_t size, const char *name)
-{
- void *p = _talloc_named_const(ctx, size, name);
-
- if (p) {
- memset(p, '\0', size);
- }
-
- return p;
-}
-
-/*
- memdup with a talloc.
-*/
-void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name)
-{
- void *newp = _talloc_named_const(t, size, name);
-
- if (likely(newp)) {
- memcpy(newp, p, size);
- }
-
- return newp;
-}
-
-static inline char *__talloc_strlendup(const void *t, const char *p, size_t len)
-{
- char *ret;
-
- ret = (char *)__talloc(t, len + 1);
- if (unlikely(!ret)) return NULL;
-
- memcpy(ret, p, len);
- ret[len] = 0;
-
- _talloc_set_name_const(ret, ret);
- return ret;
-}
-
-/*
- strdup with a talloc
-*/
-char *talloc_strdup(const void *t, const char *p)
-{
- if (unlikely(!p)) return NULL;
- return __talloc_strlendup(t, p, strlen(p));
-}
-
-/*
- strndup with a talloc
-*/
-char *talloc_strndup(const void *t, const char *p, size_t n)
-{
- if (unlikely(!p)) return NULL;
- return __talloc_strlendup(t, p, strnlen(p, n));
-}
-
-static inline char *__talloc_strlendup_append(char *s, size_t slen,
- const char *a, size_t alen)
-{
- char *ret;
-
- ret = talloc_realloc(NULL, s, char, slen + alen + 1);
- if (unlikely(!ret)) return NULL;
-
- /* append the string and the trailing \0 */
- memcpy(&ret[slen], a, alen);
- ret[slen+alen] = 0;
-
- _talloc_set_name_const(ret, ret);
- return ret;
-}
-
-/*
- * Appends at the end of the string.
- */
-char *talloc_strdup_append(char *s, const char *a)
-{
- if (unlikely(!s)) {
- return talloc_strdup(NULL, a);
- }
-
- if (unlikely(!a)) {
- return s;
- }
-
- return __talloc_strlendup_append(s, strlen(s), a, strlen(a));
-}
-
-/*
- * Appends at the end of the talloc'ed buffer,
- * not the end of the string.
- */
-char *talloc_strdup_append_buffer(char *s, const char *a)
-{
- size_t slen;
-
- if (unlikely(!s)) {
- return talloc_strdup(NULL, a);
- }
-
- if (unlikely(!a)) {
- return s;
- }
-
- slen = talloc_get_size(s);
- if (likely(slen > 0)) {
- slen--;
- }
-
- return __talloc_strlendup_append(s, slen, a, strlen(a));
-}
-
-/*
- * Appends at the end of the string.
- */
-char *talloc_strndup_append(char *s, const char *a, size_t n)
-{
- if (unlikely(!s)) {
- return talloc_strdup(NULL, a);
- }
-
- if (unlikely(!a)) {
- return s;
- }
-
- return __talloc_strlendup_append(s, strlen(s), a, strnlen(a, n));
-}
-
-/*
- * Appends at the end of the talloc'ed buffer,
- * not the end of the string.
- */
-char *talloc_strndup_append_buffer(char *s, const char *a, size_t n)
-{
- size_t slen;
-
- if (unlikely(!s)) {
- return talloc_strdup(NULL, a);
- }
-
- if (unlikely(!a)) {
- return s;
- }
-
- slen = talloc_get_size(s);
- if (likely(slen > 0)) {
- slen--;
- }
-
- return __talloc_strlendup_append(s, slen, a, strnlen(a, n));
-}
-
-#ifndef HAVE_VA_COPY
-#ifdef HAVE___VA_COPY
-#define va_copy(dest, src) __va_copy(dest, src)
-#else
-#define va_copy(dest, src) (dest) = (src)
-#endif
-#endif
-
-char *talloc_vasprintf(const void *t, const char *fmt, va_list ap)
-{
- int len;
- char *ret;
- va_list ap2;
- char c;
-
- /* this call looks strange, but it makes it work on older solaris boxes */
- va_copy(ap2, ap);
- len = vsnprintf(&c, 1, fmt, ap2);
- va_end(ap2);
- if (unlikely(len < 0)) {
- return NULL;
- }
-
- ret = (char *)__talloc(t, len+1);
- if (unlikely(!ret)) return NULL;
-
- va_copy(ap2, ap);
- vsnprintf(ret, len+1, fmt, ap2);
- va_end(ap2);
-
- _talloc_set_name_const(ret, ret);
- return ret;
-}
-
-
-/*
- Perform string formatting, and return a pointer to newly allocated
- memory holding the result, inside a memory pool.
- */
-char *talloc_asprintf(const void *t, const char *fmt, ...)
-{
- va_list ap;
- char *ret;
-
- va_start(ap, fmt);
- ret = talloc_vasprintf(t, fmt, ap);
- va_end(ap);
- return ret;
-}
-
-static inline char *__talloc_vaslenprintf_append(char *s, size_t slen,
- const char *fmt, va_list ap)
- PRINTF_ATTRIBUTE(3,0);
-
-static inline char *__talloc_vaslenprintf_append(char *s, size_t slen,
- const char *fmt, va_list ap)
-{
- ssize_t alen;
- va_list ap2;
- char c;
-
- va_copy(ap2, ap);
- alen = vsnprintf(&c, 1, fmt, ap2);
- va_end(ap2);
-
- if (alen <= 0) {
- /* Either the vsnprintf failed or the format resulted in
- * no characters being formatted. In the former case, we
- * ought to return NULL, in the latter we ought to return
- * the original string. Most current callers of this
- * function expect it to never return NULL.
- */
- return s;
- }
-
- s = talloc_realloc(NULL, s, char, slen + alen + 1);
- if (!s) return NULL;
-
- va_copy(ap2, ap);
- vsnprintf(s + slen, alen + 1, fmt, ap2);
- va_end(ap2);
-
- _talloc_set_name_const(s, s);
- return s;
-}
-
-/**
- * Realloc @p s to append the formatted result of @p fmt and @p ap,
- * and return @p s, which may have moved. Good for gradually
- * accumulating output into a string buffer. Appends at the end
- * of the string.
- **/
-char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap)
-{
- if (unlikely(!s)) {
- return talloc_vasprintf(NULL, fmt, ap);
- }
-
- return __talloc_vaslenprintf_append(s, strlen(s), fmt, ap);
-}
-
-/**
- * Realloc @p s to append the formatted result of @p fmt and @p ap,
- * and return @p s, which may have moved. Always appends at the
- * end of the talloc'ed buffer, not the end of the string.
- **/
-char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap)
-{
- size_t slen;
-
- if (unlikely(!s)) {
- return talloc_vasprintf(NULL, fmt, ap);
- }
-
- slen = talloc_get_size(s);
- if (likely(slen > 0)) {
- slen--;
- }
-
- return __talloc_vaslenprintf_append(s, slen, fmt, ap);
-}
-
-/*
- Realloc @p s to append the formatted result of @p fmt and return @p
- s, which may have moved. Good for gradually accumulating output
- into a string buffer.
- */
-char *talloc_asprintf_append(char *s, const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- s = talloc_vasprintf_append(s, fmt, ap);
- va_end(ap);
- return s;
-}
-
-/*
- Realloc @p s to append the formatted result of @p fmt and return @p
- s, which may have moved. Good for gradually accumulating output
- into a buffer.
- */
-char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- s = talloc_vasprintf_append_buffer(s, fmt, ap);
- va_end(ap);
- return s;
-}
-
-/*
- alloc an array, checking for integer overflow in the array size
-*/
-void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name)
-{
- if (count >= MAX_TALLOC_SIZE/el_size) {
- return NULL;
- }
- return _talloc_named_const(ctx, el_size * count, name);
-}
-
-/*
- alloc an zero array, checking for integer overflow in the array size
-*/
-void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name)
-{
- if (count >= MAX_TALLOC_SIZE/el_size) {
- return NULL;
- }
- return _talloc_zero(ctx, el_size * count, name);
-}
-
-/*
- realloc an array, checking for integer overflow in the array size
-*/
-void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name)
-{
- if (count >= MAX_TALLOC_SIZE/el_size) {
- return NULL;
- }
- return _talloc_realloc(ctx, ptr, el_size * count, name);
-}
-
-/*
- a function version of talloc_realloc(), so it can be passed as a function pointer
- to libraries that want a realloc function (a realloc function encapsulates
- all the basic capabilities of an allocation library, which is why this is useful)
-*/
-void *talloc_realloc_fn(const void *context, void *ptr, size_t size)
-{
- return _talloc_realloc(context, ptr, size, NULL);
-}
-
-
-static int talloc_autofree_destructor(void *ptr)
-{
- autofree_context = NULL;
- return 0;
-}
-
-static void talloc_autofree(void)
-{
- _talloc_free(autofree_context);
-}
-
-/*
- return a context which will be auto-freed on exit
- this is useful for reducing the noise in leak reports
-*/
-void *talloc_autofree_context(void)
-{
- if (autofree_context == NULL) {
- autofree_context = _talloc_named_const(NULL, 0, "autofree_context");
- talloc_set_destructor(autofree_context, talloc_autofree_destructor);
- atexit(talloc_autofree);
- }
- return autofree_context;
-}
-
-size_t talloc_get_size(const void *context)
-{
- struct talloc_chunk *tc;
-
- if (context == NULL)
- return 0;
-
- tc = talloc_chunk_from_ptr(context);
-
- return tc->size;
-}
-
-/*
- find a parent of this context that has the given name, if any
-*/
-void *talloc_find_parent_byname(const void *context, const char *name)
-{
- struct talloc_chunk *tc;
-
- if (context == NULL) {
- return NULL;
- }
-
- tc = talloc_chunk_from_ptr(context);
- while (tc) {
- if (tc->name && strcmp(tc->name, name) == 0) {
- return TC_PTR_FROM_CHUNK(tc);
- }
- while (tc && tc->prev) tc = tc->prev;
- if (tc) {
- tc = tc->parent;
- }
- }
- return NULL;
-}
-
-/*
- show the parentage of a context
-*/
-void talloc_show_parents(const void *context, FILE *file)
-{
- struct talloc_chunk *tc;
-
- if (context == NULL) {
- fprintf(file, "talloc no parents for NULL\n");
- return;
- }
-
- tc = talloc_chunk_from_ptr(context);
- fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context));
- while (tc) {
- fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc)));
- while (tc && tc->prev) tc = tc->prev;
- if (tc) {
- tc = tc->parent;
- }
- }
- fflush(file);
-}
-
-/*
- return 1 if ptr is a parent of context
-*/
-int talloc_is_parent(const void *context, const void *ptr)
-{
- struct talloc_chunk *tc;
-
- if (context == NULL) {
- return 0;
- }
-
- tc = talloc_chunk_from_ptr(context);
- while (tc) {
- if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1;
- while (tc && tc->prev) tc = tc->prev;
- if (tc) {
- tc = tc->parent;
- }
- }
- return 0;
-}
diff --git a/src/shared/libosmocore/src/timer.c b/src/shared/libosmocore/src/timer.c
index 6d4abc26..cc6d5ccd 100644
--- a/src/shared/libosmocore/src/timer.c
+++ b/src/shared/libosmocore/src/timer.c
@@ -23,9 +23,6 @@
*
*/
-/* These store the amount of time that we wait until next timer expires. */
-static struct timeval nearest;
-static struct timeval *nearest_p;
/*! \addtogroup timer
* @{
@@ -41,6 +38,10 @@ static struct timeval *nearest_p;
#include <osmocom/core/timer_compat.h>
#include <osmocom/core/linuxlist.h>
+/* These store the amount of time that we wait until next timer expires. */
+static struct timeval nearest;
+static struct timeval *nearest_p;
+
static struct rb_root timer_root = RB_ROOT;
static void __add_timer(struct osmo_timer_list *timer)
@@ -90,7 +91,7 @@ osmo_timer_schedule(struct osmo_timer_list *timer, int seconds, int microseconds
{
struct timeval current_time;
- gettimeofday(&current_time, NULL);
+ osmo_gettimeofday(&current_time, NULL);
timer->timeout.tv_sec = seconds;
timer->timeout.tv_usec = microseconds;
timeradd(&timer->timeout, &current_time, &timer->timeout);
@@ -128,7 +129,7 @@ int osmo_timer_pending(struct osmo_timer_list *timer)
/*! \brief compute the remaining time of a timer
* \param[in] timer the to-be-checked timer
- * \param[in] the current time (NULL if not known)
+ * \param[in] now the current time (NULL if not known)
* \param[out] remaining remaining time until timer fires
* \return 0 if timer has not expired yet, -1 if it has
*
@@ -141,10 +142,10 @@ int osmo_timer_remaining(const struct osmo_timer_list *timer,
{
struct timeval current_time;
- if (!now) {
- gettimeofday(&current_time, NULL);
- now = &current_time;
- }
+ if (!now)
+ osmo_gettimeofday(&current_time, NULL);
+ else
+ current_time = *now;
timersub(&timer->timeout, &current_time, remaining);
@@ -154,7 +155,9 @@ int osmo_timer_remaining(const struct osmo_timer_list *timer,
return 0;
}
-/*
+/*! \brief Determine time between now and the nearest timer
+ * \returns pointer to timeval of nearest timer, NULL if there is none
+ *
* if we have a nearest time return the delta between the current
* time and the time of the nearest timer.
* If the nearest timer timed out return NULL and then we will
@@ -184,15 +187,13 @@ static void update_nearest(struct timeval *cand, struct timeval *current)
}
}
-/*
- * Find the nearest time and update s_nearest_time
- */
+/*! \brief Find the nearest time and update nearest_p */
void osmo_timers_prepare(void)
{
struct rb_node *node;
struct timeval current;
- gettimeofday(&current, NULL);
+ osmo_gettimeofday(&current, NULL);
node = rb_first(&timer_root);
if (node) {
@@ -204,9 +205,7 @@ void osmo_timers_prepare(void)
}
}
-/*
- * fire all timers... and remove them
- */
+/*! \brief fire all timers... and remove them */
int osmo_timers_update(void)
{
struct timeval current_time;
@@ -215,7 +214,7 @@ int osmo_timers_update(void)
struct osmo_timer_list *this;
int work = 0;
- gettimeofday(&current_time, NULL);
+ osmo_gettimeofday(&current_time, NULL);
INIT_LLIST_HEAD(&timer_eviction_list);
for (node = rb_first(&timer_root); node; node = rb_next(node)) {
@@ -242,7 +241,8 @@ int osmo_timers_update(void)
restart:
llist_for_each_entry(this, &timer_eviction_list, list) {
osmo_timer_del(this);
- this->cb(this->data);
+ if (this->cb)
+ this->cb(this->data);
work = 1;
goto restart;
}
@@ -250,6 +250,8 @@ restart:
return work;
}
+/*! \brief Check how many timers we have in the system
+ * \returns number of \ref osmo_timer_list registered */
int osmo_timers_check(void)
{
struct rb_node *node;
diff --git a/src/shared/libosmocore/src/timer_gettimeofday.c b/src/shared/libosmocore/src/timer_gettimeofday.c
new file mode 100644
index 00000000..81a1598d
--- /dev/null
+++ b/src/shared/libosmocore/src/timer_gettimeofday.c
@@ -0,0 +1,58 @@
+/*
+ * (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Authors: Neels Hofmeyr <nhofmeyr@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.
+ *
+ * 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
+ * @{
+ */
+
+/*! \file timer_gettimeofday.c
+ */
+
+#include <stdbool.h>
+#include <sys/time.h>
+
+bool osmo_gettimeofday_override = false;
+struct timeval osmo_gettimeofday_override_time = { 23, 424242 };
+
+/*! \brief shim around gettimeofday to be able to set the time manually.
+ * To override, set osmo_gettimeofday_override == true and set the desired
+ * current time in osmo_gettimeofday_override_time. */
+int osmo_gettimeofday(struct timeval *tv, struct timezone *tz)
+{
+ if (osmo_gettimeofday_override) {
+ *tv = osmo_gettimeofday_override_time;
+ return 0;
+ }
+
+ return gettimeofday(tv, tz);
+}
+
+/*! \brief convenience function to advance the fake time.
+ * Add the given values to osmo_gettimeofday_override_time. */
+void osmo_gettimeofday_override_add(time_t secs, suseconds_t usecs)
+{
+ struct timeval val = { secs, usecs };
+ timeradd(&osmo_gettimeofday_override_time, &val,
+ &osmo_gettimeofday_override_time);
+}
+
+/*! @} */
diff --git a/src/shared/libosmocore/src/utils.c b/src/shared/libosmocore/src/utils.c
index cf0c9344..02c24435 100644
--- a/src/shared/libosmocore/src/utils.c
+++ b/src/shared/libosmocore/src/utils.c
@@ -1,3 +1,26 @@
+/*
+ * (C) 2011 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2011 by Sylvain Munaut <tnt@246tNt.com>
+ * (C) 2014 by Nils O. Selåsdal <noselasd@fiane.dyndns.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
#include <string.h>
#include <stdint.h>
@@ -5,6 +28,8 @@
#include <stdio.h>
#include <osmocom/core/utils.h>
+#include <osmocom/core/bit64gen.h>
+
/*! \addtogroup utils
* @{
@@ -18,9 +43,30 @@ static char namebuf[255];
* \param[in] vs Array of value_string tuples
* \param[in] val Value to be converted
* \returns pointer to human-readable string
+ *
+ * If val is found in vs, the array's string entry is returned. Otherwise, an
+ * "unknown" string containing the actual value is composed in a static buffer
+ * that is reused across invocations.
*/
const char *get_value_string(const struct value_string *vs, uint32_t val)
{
+ const char *str = get_value_string_or_null(vs, val);
+ if (str)
+ return str;
+
+ snprintf(namebuf, sizeof(namebuf), "unknown 0x%x", val);
+ namebuf[sizeof(namebuf) - 1] = '\0';
+ return namebuf;
+}
+
+/*! \brief get human-readable string or NULL for given value
+ * \param[in] vs Array of value_string tuples
+ * \param[in] val Value to be converted
+ * \returns pointer to human-readable string or NULL if val is not found
+ */
+const char *get_value_string_or_null(const struct value_string *vs,
+ uint32_t val)
+{
int i;
for (i = 0;; i++) {
@@ -30,8 +76,7 @@ const char *get_value_string(const struct value_string *vs, uint32_t val)
return vs[i].str;
}
- snprintf(namebuf, sizeof(namebuf), "unknown 0x%x", val);
- return namebuf;
+ return NULL;
}
/*! \brief get numeric value for given human-readable string
@@ -64,12 +109,21 @@ char osmo_bcd2char(uint8_t bcd)
return 'A' + (bcd - 0xa);
}
-/* only works for numbers in ascii */
+/*! \brief Convert number in ASCII to BCD value
+ * \param[in] c ASCII character
+ * \returns BCD encoded value of character
+ */
uint8_t osmo_char2bcd(char c)
{
return c - 0x30;
}
+/*! \brief Parse a string containing hexadecimal digits
+ * \param[in] str string containing ASCII encoded hexadecimal digits
+ * \param[out] b output buffer
+ * \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)
{
@@ -98,6 +152,7 @@ int osmo_hexparse(const char *str, uint8_t *b, int max_len)
}
static char hexd_buff[4096];
+static const char hex_chars[] = "0123456789abcdef";
static char *_osmo_hexdump(const unsigned char *buf, int len, char *delim)
{
@@ -106,13 +161,20 @@ static char *_osmo_hexdump(const unsigned char *buf, int len, char *delim)
hexd_buff[0] = 0;
for (i = 0; i < len; i++) {
+ const char *delimp = delim;
int len_remain = sizeof(hexd_buff) - (cur - hexd_buff);
- if (len_remain <= 0)
+ if (len_remain < 3)
break;
- int rc = snprintf(cur, len_remain, "%02x%s", buf[i], delim);
- if (rc <= 0)
- break;
- cur += rc;
+
+ *cur++ = hex_chars[buf[i] >> 4];
+ *cur++ = hex_chars[buf[i] & 0xf];
+
+ while (len_remain > 1 && *delimp) {
+ *cur++ = *delimp++;
+ len_remain--;
+ }
+
+ *cur = 0;
}
hexd_buff[sizeof(hexd_buff)-1] = 0;
return hexd_buff;
@@ -178,9 +240,13 @@ char *osmo_hexdump_nospc(const unsigned char *buf, int len)
return _osmo_hexdump(buf, len, "");
}
- /* Compat with previous typo to preserve abi */
+/* Compat with previous typo to preserve abi */
char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len)
+#if defined(__MACH__) && defined(__APPLE__)
+ ;
+#else
__attribute__((weak, alias("osmo_hexdump_nospc")));
+#endif
#include "../config.h"
#ifdef HAVE_CTYPE_H
@@ -212,4 +278,86 @@ void osmo_str2upper(char *out, const char *in)
}
#endif /* HAVE_CTYPE_H */
+/*! \brief Wishful thinking to generate a constant time compare
+ * \param[in] exp Expected data
+ * \param[in] rel Comparison value
+ * \param[in] count Number of bytes to compare
+ * \returns 1 in case \a exp equals \a rel; zero otherwise
+ *
+ * Compare count bytes of exp to rel. Return 0 if they are identical, 1
+ * otherwise. Do not return a mismatch on the first mismatching byte,
+ * but always compare all bytes, regardless. The idea is that the amount of
+ * matching bytes cannot be inferred from the time the comparison took. */
+int osmo_constant_time_cmp(const uint8_t *exp, const uint8_t *rel, const int count)
+{
+ int x = 0, i;
+
+ for (i = 0; i < count; ++i)
+ x |= exp[i] ^ rel[i];
+
+ /* if x is zero, all data was identical */
+ return x? 1 : 0;
+}
+
+/*! \brief Generic retrieval of 1..8 bytes as big-endian uint64_t
+ * \param[in] data Input data as byte-array
+ * \param[in] data_len Length of \a data in octets
+ * \returns uint64_t of \a data interpreted as big-endian
+ *
+ * This is like osmo_load64be_ext, except that if data_len is less than
+ * sizeof(uint64_t), the data is interpreted as the least significant bytes
+ * (osmo_load64be_ext loads them as the most significant bytes into the
+ * returned uint64_t). In this way, any integer size up to 64 bits can be
+ * decoded conveniently by using sizeof(), without the need to call specific
+ * numbered functions (osmo_load16, 32, ...). */
+uint64_t osmo_decode_big_endian(const uint8_t *data, size_t data_len)
+{
+ uint64_t value = 0;
+
+ while (data_len > 0) {
+ value = (value << 8) + *data;
+ data += 1;
+ data_len -= 1;
+ }
+
+ return value;
+}
+
+/*! \brief Generic big-endian encoding of big endian number up to 64bit
+ * \param[in] value unsigned integer value to be stored
+ * \param[in] data_len number of octets
+ * \returns static buffer containing big-endian stored value
+ *
+ * This is like osmo_store64be_ext, except that this returns a static buffer of
+ * the result (for convenience, but not threadsafe). If data_len is less than
+ * sizeof(uint64_t), only the least significant bytes of value are encoded. */
+uint8_t *osmo_encode_big_endian(uint64_t value, size_t data_len)
+{
+ static uint8_t buf[sizeof(uint64_t)];
+ OSMO_ASSERT(data_len <= ARRAY_SIZE(buf));
+ osmo_store64be_ext(value, buf, data_len);
+ return buf;
+}
/*! @} */
+
+/*! \brief Copy a C-string into a sized buffer
+ * \param[in] src source string
+ * \param[out] dst destination string
+ * \param[in] siz size of the \a dst string
+ * \returns length of source string
+ *
+ * Copies up to \a siz characters from \a src to \a dst, but ensures
+ * that the last character of \a dst is always a NUL character. May
+ * truncate \a src to do achieve this.
+ */
+size_t osmo_strlcpy(char *dst, const char *src, size_t siz)
+{
+ size_t ret = strlen(src);
+
+ if (siz) {
+ size_t len = (ret >= siz) ? siz - 1 : ret;
+ memcpy(dst, src, len);
+ dst[len] = '\0';
+ }
+ return ret;
+}
diff --git a/src/shared/libosmocore/src/vty/Makefile.am b/src/shared/libosmocore/src/vty/Makefile.am
index 61111235..e083a1ce 100644
--- a/src/shared/libosmocore/src/vty/Makefile.am
+++ b/src/shared/libosmocore/src/vty/Makefile.am
@@ -1,15 +1,16 @@
# 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=0:0:0
+# 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=3:0:0
-INCLUDES = $(all_includes) -I$(top_srcdir)/include
-AM_CFLAGS = -Wall
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
+AM_CFLAGS = -Wall $(TALLOC_CFLAGS)
if ENABLE_VTY
lib_LTLIBRARIES = libosmovty.la
libosmovty_la_SOURCES = buffer.c command.c vty.c vector.c utils.c \
- telnet_interface.c logging_vty.c
-libosmovty_la_LDFLAGS = -version-info $(LIBVERSION)
+ telnet_interface.c logging_vty.c stats_vty.c fsm_vty.c
+libosmovty_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined $(TALLOC_LIBS)
libosmovty_la_LIBADD = $(top_builddir)/src/libosmocore.la
endif
diff --git a/src/shared/libosmocore/src/vty/buffer.c b/src/shared/libosmocore/src/vty/buffer.c
index e385f9fd..8862da92 100644
--- a/src/shared/libosmocore/src/vty/buffer.c
+++ b/src/shared/libosmocore/src/vty/buffer.c
@@ -16,8 +16,8 @@
*
* 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., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
@@ -180,7 +180,7 @@ void buffer_put(struct buffer *b, const void *p, size_t size)
}
/* Insert character into the buffer. */
-void buffer_putc(struct buffer *b, u_char c)
+void buffer_putc(struct buffer *b, unsigned char c)
{
buffer_put(b, &c, 1);
}
@@ -299,7 +299,7 @@ buffer_flush_window(struct buffer * b, int fd, int width, int height,
zlog_warn("%s: growing iov array to %d; "
"width %d, height %d, size %lu",
__func__, iov_alloc, width, height,
- (u_long) b->size);
+ (unsigned long) b->size);
iov =
XREALLOC(MTYPE_TMP, iov,
iov_alloc * sizeof(*iov));
diff --git a/src/shared/libosmocore/src/vty/command.c b/src/shared/libosmocore/src/vty/command.c
index 7f83a5e4..587bd626 100644
--- a/src/shared/libosmocore/src/vty/command.c
+++ b/src/shared/libosmocore/src/vty/command.c
@@ -18,17 +18,17 @@ 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., 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <stdbool.h>
#include <syslog.h>
#include <errno.h>
#define _XOPEN_SOURCE
#include <unistd.h>
-#include <assert.h>
#include <ctype.h>
#include <time.h>
#include <sys/time.h>
@@ -39,6 +39,7 @@ Boston, MA 02111-1307, USA. */
#include <osmocom/vty/command.h>
#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
/*! \addtogroup command
* @{
@@ -146,7 +147,7 @@ static int cmp_desc(const void *p, const void *q)
return strcmp(a->cmd, b->cmd);
}
-static int is_config(struct vty *vty)
+static int is_config_child(struct vty *vty)
{
if (vty->node <= CONFIG_NODE)
return 0;
@@ -323,11 +324,7 @@ static vector cmd_make_descvec(const char *string, const char *descstr)
cp++;
}
if (*cp == '|') {
- if (!multiple) {
- fprintf(stderr, "Command parse error!: %s\n",
- string);
- exit(1);
- }
+ OSMO_ASSERT(multiple);
cp++;
}
@@ -405,6 +402,59 @@ const char *cmd_prompt(enum node_type node)
return cnode->prompt;
}
+/*!
+ * \brief escape all special asciidoc symbols
+ * \param unsafe string
+ * \return a new talloc char *
+ */
+char *osmo_asciidoc_escape(const char *inp)
+{
+ int _strlen;
+ char *out, *out_ptr;
+ int len = 0, i, j;
+
+ if (!inp)
+ return NULL;
+ _strlen = strlen(inp);
+
+ for (i = 0; i < _strlen; ++i) {
+ switch (inp[i]) {
+ case '|':
+ len += 2;
+ break;
+ default:
+ len += 1;
+ break;
+ }
+ }
+
+ out = talloc_size(NULL, len + 1);
+ if (!out)
+ return NULL;
+
+ out_ptr = out;
+
+#define ADD(out, str) \
+ for (j = 0; j < strlen(str); ++j) \
+ *(out++) = str[j];
+
+ for (i = 0; i < _strlen; ++i) {
+ switch (inp[i]) {
+ case '|':
+ ADD(out_ptr, "\\|");
+ break;
+ default:
+ *(out_ptr++) = inp[i];
+ break;
+ }
+ }
+
+#undef ADD
+
+ out_ptr[0] = '\0';
+ return out;
+}
+
static char *xml_escape(const char *inp)
{
int _strlen;
@@ -544,22 +594,36 @@ static int vty_dump_nodes(struct vty *vty)
return 0;
}
+/* \brief Check if a command with given string exists at given node */
+static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
+{
+ int i;
+
+ for (i = 0; i < vector_active(cnode->cmd_vector); ++i) {
+ struct cmd_element *elem;
+ elem = vector_slot(cnode->cmd_vector, i);
+ if (!elem->string)
+ continue;
+ if (!strcmp(elem->string, cmdstring))
+ return 1;
+ }
+ return 0;
+}
+
/*! \brief Install a command into a node
* \param[in] ntype Node Type
* \param[cmd] element to be installed
*/
-void install_element(enum node_type ntype, struct cmd_element *cmd)
+void install_element(int ntype, struct cmd_element *cmd)
{
struct cmd_node *cnode;
cnode = vector_slot(cmdvec, ntype);
- if (cnode == NULL) {
- fprintf(stderr,
- "Command node %d doesn't exist, please check it\n",
- ntype);
- exit(1);
- }
+ OSMO_ASSERT(cnode);
+ /* ensure no _identical_ command has been registered at this
+ * node so far */
+ OSMO_ASSERT(!check_element_exists(cnode, cmd->string));
vector_set(cnode->cmd_vector, cmd);
@@ -592,7 +656,7 @@ static char *zencrypt(const char *passwd)
struct timeval tv;
char *crypt(const char *, const char *);
- gettimeofday(&tv, 0);
+ osmo_gettimeofday(&tv, 0);
to64(&salt[0], random(), 3);
to64(&salt[3], tv.tv_usec, 3);
@@ -652,7 +716,8 @@ static vector cmd_node_vector(vector v, enum node_type ntype)
/* Completion match types. */
enum match_type {
- no_match,
+ no_match = 0,
+ any_match,
extend_match,
ipv4_prefix_match,
ipv4_match,
@@ -661,7 +726,7 @@ enum match_type {
range_match,
vararg_match,
partly_match,
- exact_match
+ exact_match,
};
static enum match_type cmd_ipv4_match(const char *str)
@@ -1103,12 +1168,100 @@ static int cmd_range_match(const char *range, const char *str)
return 1;
}
-/* Make completion match and return match type flag. */
+/* helper to retrieve the 'real' argument string from an optional argument */
+static char *
+cmd_deopt(const char *str)
+{
+ /* we've got "[blah]". We want to strip off the []s and redo the
+ * match check for "blah"
+ */
+ size_t len = strlen(str);
+ char *tmp;
+
+ if (len < 3)
+ return NULL;
+
+ /* tmp will hold a string of len-2 chars, so 'len' size is fine */
+ tmp = talloc_size(NULL, len);
+
+ memcpy(tmp, (str + 1), len - 2);
+ tmp[len - 2] = '\0';
+
+ return tmp;
+}
+
+static enum match_type
+cmd_match(const char *str, const char *command,
+ enum match_type min, bool recur)
+{
+
+ if (recur && CMD_OPTION(str))
+ {
+ enum match_type ret;
+ char *tmp = cmd_deopt(str);
+
+ /* this would be a bug in a command, however handle it gracefully
+ * as it we only discover it if a user tries to run it
+ */
+ if (tmp == NULL)
+ return no_match;
+
+ ret = cmd_match(tmp, command, min, false);
+
+ talloc_free(tmp);
+
+ return ret;
+ }
+ else if (CMD_VARARG(str))
+ return vararg_match;
+ else if (CMD_RANGE(str))
+ {
+ if (cmd_range_match(str, command))
+ return range_match;
+ }
+#ifdef HAVE_IPV6
+ else if (CMD_IPV6(str))
+ {
+ if (cmd_ipv6_match(command) >= min)
+ return ipv6_match;
+ }
+ else if (CMD_IPV6_PREFIX(str))
+ {
+ if (cmd_ipv6_prefix_match(command) >= min)
+ return ipv6_prefix_match;
+ }
+#endif /* HAVE_IPV6 */
+ else if (CMD_IPV4(str))
+ {
+ if (cmd_ipv4_match(command) >= min)
+ return ipv4_match;
+ }
+ else if (CMD_IPV4_PREFIX(str))
+ {
+ if (cmd_ipv4_prefix_match(command) >= min)
+ return ipv4_prefix_match;
+ }
+ else if (CMD_VARIABLE(str))
+ return extend_match;
+ else if (strncmp(command, str, strlen(command)) == 0)
+ {
+ if (strcmp(command, str) == 0)
+ return exact_match;
+ else if (partly_match >= min)
+ return partly_match;
+ }
+
+ return no_match;
+}
+
+/* Filter vector at the specified index and by the given command string, to
+ * the desired matching level (thus allowing part matches), and return match
+ * type flag.
+ */
static enum match_type
-cmd_filter_by_completion(char *command, vector v, unsigned int index)
+cmd_filter(char *command, vector v, unsigned int index, enum match_type level)
{
unsigned int i;
- const char *str;
struct cmd_element *cmd_element;
enum match_type match_type;
vector descvec;
@@ -1130,124 +1283,40 @@ cmd_filter_by_completion(char *command, vector v, unsigned int index)
for (j = 0; j < vector_active(descvec); j++)
if ((desc = vector_slot(descvec, j))) {
- str = desc->cmd;
+ enum match_type ret;
- if (CMD_VARARG(str)) {
- if (match_type <
- vararg_match)
- match_type =
- vararg_match;
- matched++;
- } else if (CMD_RANGE(str)) {
- if (cmd_range_match
- (str, command)) {
- if (match_type <
- range_match)
- match_type
- =
- range_match;
-
- matched++;
- }
- }
-#ifdef HAVE_IPV6
- else if (CMD_IPV6(str)) {
- if (cmd_ipv6_match
- (command)) {
- if (match_type <
- ipv6_match)
- match_type
- =
- ipv6_match;
-
- matched++;
- }
- } else if (CMD_IPV6_PREFIX(str)) {
- if (cmd_ipv6_prefix_match(command)) {
- if (match_type <
- ipv6_prefix_match)
- match_type
- =
- ipv6_prefix_match;
-
- matched++;
- }
- }
-#endif /* HAVE_IPV6 */
- else if (CMD_IPV4(str)) {
- if (cmd_ipv4_match
- (command)) {
- if (match_type <
- ipv4_match)
- match_type
- =
- ipv4_match;
-
- matched++;
- }
- } else if (CMD_IPV4_PREFIX(str)) {
- if (cmd_ipv4_prefix_match(command)) {
- if (match_type <
- ipv4_prefix_match)
- match_type
- =
- ipv4_prefix_match;
- matched++;
- }
- } else
- /* Check is this point's argument optional ? */
- if (CMD_OPTION(str)
- ||
- CMD_VARIABLE(str)) {
- if (match_type <
- extend_match)
- match_type =
- extend_match;
- matched++;
- } else
- if (strncmp
- (command, str,
- strlen(command)) ==
- 0) {
- if (strcmp(command, str)
- == 0)
- match_type =
- exact_match;
- else {
- if (match_type <
- partly_match)
- match_type
- =
- partly_match;
- }
+ ret = cmd_match (desc->cmd, command, level, true);
+
+ if (ret != no_match)
matched++;
- }
+
+ if (match_type < ret)
+ match_type = ret;
}
if (!matched)
vector_slot(v, i) = NULL;
}
}
- return match_type;
-}
-/* Filter vector by command character with index. */
-static enum match_type
-cmd_filter_by_string(char *command, vector v, unsigned int index)
-{
- unsigned int i;
- const char *str;
- struct cmd_element *cmd_element;
- enum match_type match_type;
- vector descvec;
- struct desc *desc;
-
- match_type = no_match;
+ if (match_type == no_match)
+ return no_match;
- /* If command and cmd_element string does not match set NULL to vector */
+ /* 2nd pass: We now know the 'strongest' match type for the index, so we
+ * go again and filter out commands whose argument (at this index) is
+ * 'weaker'. E.g., if we have 2 commands:
+ *
+ * foo bar <1-255>
+ * foo bar BLAH
+ *
+ * and the command string is 'foo bar 10', then we will get here with with
+ * 'range_match' being the strongest match. However, if 'BLAH' came
+ * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10").
+ *
+ * If we don't do a 2nd pass and filter it out, the higher-layers will
+ * consider this to be ambiguous.
+ */
for (i = 0; i < vector_active(v); i++)
if ((cmd_element = vector_slot(v, i)) != NULL) {
- /* If given index is bigger than max string vector of command,
- set NULL */
if (index >= vector_active(cmd_element->strvec))
vector_slot(v, i) = NULL;
else {
@@ -1259,89 +1328,18 @@ cmd_filter_by_string(char *command, vector v, unsigned int index)
for (j = 0; j < vector_active(descvec); j++)
if ((desc = vector_slot(descvec, j))) {
- str = desc->cmd;
+ enum match_type ret;
- if (CMD_VARARG(str)) {
- if (match_type <
- vararg_match)
- match_type =
- vararg_match;
- matched++;
- } else if (CMD_RANGE(str)) {
- if (cmd_range_match
- (str, command)) {
- if (match_type <
- range_match)
- match_type
- =
- range_match;
- matched++;
- }
- }
-#ifdef HAVE_IPV6
- else if (CMD_IPV6(str)) {
- if (cmd_ipv6_match
- (command) ==
- exact_match) {
- if (match_type <
- ipv6_match)
- match_type
- =
- ipv6_match;
- matched++;
- }
- } else if (CMD_IPV6_PREFIX(str)) {
- if (cmd_ipv6_prefix_match(command) == exact_match) {
- if (match_type <
- ipv6_prefix_match)
- match_type
- =
- ipv6_prefix_match;
- matched++;
- }
- }
-#endif /* HAVE_IPV6 */
- else if (CMD_IPV4(str)) {
- if (cmd_ipv4_match
- (command) ==
- exact_match) {
- if (match_type <
- ipv4_match)
- match_type
- =
- ipv4_match;
- matched++;
- }
- } else if (CMD_IPV4_PREFIX(str)) {
- if (cmd_ipv4_prefix_match(command) == exact_match) {
- if (match_type <
- ipv4_prefix_match)
- match_type
- =
- ipv4_prefix_match;
- matched++;
- }
- } else if (CMD_OPTION(str)
- || CMD_VARIABLE(str))
- {
- if (match_type <
- extend_match)
- match_type =
- extend_match;
+ ret = cmd_match(desc->cmd, command, any_match, true);
+
+ if (ret >= match_type)
matched++;
- } else {
- if (strcmp(command, str)
- == 0) {
- match_type =
- exact_match;
- matched++;
- }
- }
}
if (!matched)
vector_slot(v, i) = NULL;
}
}
+
return match_type;
}
@@ -1351,7 +1349,6 @@ is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
{
unsigned int i;
unsigned int j;
- const char *str = NULL;
struct cmd_element *cmd_element;
const char *matched = NULL;
vector descvec;
@@ -1366,22 +1363,22 @@ is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
for (j = 0; j < vector_active(descvec); j++)
if ((desc = vector_slot(descvec, j))) {
enum match_type ret;
+ const char *str = desc->cmd;
- str = desc->cmd;
+ if (CMD_OPTION(str))
+ if ((str = cmd_deopt(str)) == NULL)
+ continue;
switch (type) {
case exact_match:
- if (!
- (CMD_OPTION(str)
- || CMD_VARIABLE(str))
-&& strcmp(command, str) == 0)
+ if (!(CMD_VARIABLE (str))
+ && strcmp(command, str) == 0)
match++;
break;
case partly_match:
- if (!
- (CMD_OPTION(str)
- || CMD_VARIABLE(str))
-&& strncmp(command, str, strlen(command)) == 0) {
+ if (!(CMD_VARIABLE (str))
+ && strncmp(command, str, strlen (command)) == 0)
+ {
if (matched
&& strcmp(matched,
str) != 0)
@@ -1434,14 +1431,16 @@ is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
}
break;
case extend_match:
- if (CMD_OPTION(str)
- || CMD_VARIABLE(str))
+ if (CMD_VARIABLE (str))
match++;
break;
case no_match:
default:
break;
}
+
+ if (CMD_OPTION(desc->cmd))
+ talloc_free((void*)str);
}
if (!match)
vector_slot(v, i) = NULL;
@@ -1595,57 +1594,59 @@ cmd_describe_command_real(vector vline, struct vty *vty, int *status)
/* Filter commands. */
/* Only words precedes current word will be checked in this loop. */
- for (i = 0; i < index; i++)
- if ((command = vector_slot(vline, i))) {
- match =
- cmd_filter_by_completion(command, cmd_vector, i);
-
- if (match == vararg_match) {
- struct cmd_element *cmd_element;
- vector descvec;
- unsigned int j, k;
-
- for (j = 0; j < vector_active(cmd_vector); j++)
- if ((cmd_element =
- vector_slot(cmd_vector, j)) != NULL
- &&
- (vector_active
- (cmd_element->strvec))) {
- descvec =
- vector_slot(cmd_element->
- strvec,
- vector_active
- (cmd_element->
- strvec) - 1);
- for (k = 0;
- k < vector_active(descvec);
- k++) {
- struct desc *desc =
- vector_slot(descvec,
- k);
- vector_set(matchvec,
- desc);
- }
+ for (i = 0; i < index; i++) {
+ command = vector_slot(vline, i);
+ if (!command)
+ continue;
+
+ match = cmd_filter(command, cmd_vector, i, any_match);
+
+ if (match == vararg_match) {
+ struct cmd_element *cmd_element;
+ vector descvec;
+ unsigned int j, k;
+
+ for (j = 0; j < vector_active(cmd_vector); j++)
+ if ((cmd_element =
+ vector_slot(cmd_vector, j)) != NULL
+ &&
+ (vector_active(cmd_element->strvec))) {
+ descvec =
+ vector_slot(cmd_element->
+ strvec,
+ vector_active
+ (cmd_element->
+ strvec) - 1);
+ for (k = 0;
+ k < vector_active(descvec);
+ k++) {
+ struct desc *desc =
+ vector_slot(descvec,
+ k);
+ vector_set(matchvec,
+ desc);
}
+ }
- vector_set(matchvec, &desc_cr);
- vector_free(cmd_vector);
+ vector_set(matchvec, &desc_cr);
+ vector_free(cmd_vector);
- return matchvec;
- }
+ return matchvec;
+ }
- if ((ret =
- is_cmd_ambiguous(command, cmd_vector, i,
- match)) == 1) {
- vector_free(cmd_vector);
- *status = CMD_ERR_AMBIGUOUS;
- return NULL;
- } else if (ret == 2) {
- vector_free(cmd_vector);
- *status = CMD_ERR_NO_MATCH;
- return NULL;
- }
+ if ((ret = is_cmd_ambiguous(command, cmd_vector, i,
+ match)) == 1) {
+ vector_free(cmd_vector);
+ vector_free(matchvec);
+ *status = CMD_ERR_AMBIGUOUS;
+ return NULL;
+ } else if (ret == 2) {
+ vector_free(cmd_vector);
+ vector_free(matchvec);
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
}
+ }
/* Prepare match vector */
/* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
@@ -1653,50 +1654,52 @@ cmd_describe_command_real(vector vline, struct vty *vty, int *status)
/* Make sure that cmd_vector is filtered based on current word */
command = vector_slot(vline, index);
if (command)
- match = cmd_filter_by_completion(command, cmd_vector, index);
+ match = cmd_filter(command, cmd_vector, index, any_match);
/* Make description vector. */
- for (i = 0; i < vector_active(cmd_vector); i++)
- if ((cmd_element = vector_slot(cmd_vector, i)) != NULL) {
- const char *string = NULL;
- vector strvec = cmd_element->strvec;
+ for (i = 0; i < vector_active(cmd_vector); i++) {
+ const char *string = NULL;
+ vector strvec;
- /* if command is NULL, index may be equal to vector_active */
- if (command && index >= vector_active(strvec))
- vector_slot(cmd_vector, i) = NULL;
- else {
- /* Check if command is completed. */
- if (command == NULL
- && index == vector_active(strvec)) {
- string = "<cr>";
- if (!desc_unique_string
- (matchvec, string))
- vector_set(matchvec, &desc_cr);
- } else {
- unsigned int j;
- vector descvec =
- vector_slot(strvec, index);
- struct desc *desc;
-
- for (j = 0; j < vector_active(descvec);
- j++)
- if ((desc =
- vector_slot(descvec, j))) {
- string =
- cmd_entry_function_desc
- (command,
- desc->cmd);
- if (string) {
- /* Uniqueness check */
- if (!desc_unique_string(matchvec, string))
- vector_set
- (matchvec,
- desc);
- }
- }
+ cmd_element = vector_slot(cmd_vector, i);
+ if (!cmd_element)
+ continue;
+
+ if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
+ continue;
+
+ strvec = cmd_element->strvec;
+
+ /* if command is NULL, index may be equal to vector_active */
+ if (command && index >= vector_active(strvec))
+ vector_slot(cmd_vector, i) = NULL;
+ else {
+ /* Check if command is completed. */
+ if (command == NULL
+ && index == vector_active(strvec)) {
+ string = "<cr>";
+ if (!desc_unique_string(matchvec, string))
+ vector_set(matchvec, &desc_cr);
+ } else {
+ unsigned int j;
+ vector descvec = vector_slot(strvec, index);
+ struct desc *desc;
+
+ for (j = 0; j < vector_active(descvec); j++) {
+ desc = vector_slot(descvec, j);
+ if (!desc)
+ continue;
+ string = cmd_entry_function_desc
+ (command, desc->cmd);
+ if (!string)
+ continue;
+ /* Uniqueness check */
+ if (!desc_unique_string(matchvec, string))
+ vector_set(matchvec, desc);
}
}
}
+ }
vector_free(cmd_vector);
if (vector_slot(matchvec, 0) == NULL) {
@@ -1786,6 +1789,7 @@ static char **cmd_complete_command_real(vector vline, struct vty *vty,
if (vector_active(vline) == 0) {
*status = CMD_ERR_NO_MATCH;
+ vector_free(cmd_vector);
return NULL;
} else
index = vector_active(vline) - 1;
@@ -1798,7 +1802,7 @@ static char **cmd_complete_command_real(vector vline, struct vty *vty,
/* First try completion match, if there is exactly match return 1 */
match =
- cmd_filter_by_completion(command, cmd_vector, i);
+ cmd_filter(command, cmd_vector, i, any_match);
/* If there is exact match then filter ambiguous match else check
ambiguousness. */
@@ -1943,15 +1947,47 @@ char **cmd_complete_command(vector vline, struct vty *vty, int *status)
}
/* return parent node */
-/* MUST eventually converge on CONFIG_NODE */
-enum node_type vty_go_parent(struct vty *vty)
+/*
+ * This function MUST eventually converge on a node when called repeatedly,
+ * there must not be any cycles.
+ * All 'config' nodes shall converge on CONFIG_NODE.
+ * All other 'enable' nodes shall converge on ENABLE_NODE.
+ * All 'view' only nodes shall converge on VIEW_NODE.
+ * All other nodes shall converge on themselves or it must be ensured,
+ * that the user's rights are not extended anyhow by calling this function.
+ *
+ * Note that these requirements also apply to all functions that are used
+ * as go_parent_cb.
+ * Note also that this function relies on the is_config_child callback to
+ * recognize non-config nodes if go_parent_cb is not set.
+ */
+int vty_go_parent(struct vty *vty)
{
- assert(vty->node > CONFIG_NODE);
+ switch (vty->node) {
+ case AUTH_NODE:
+ case VIEW_NODE:
+ case ENABLE_NODE:
+ case CONFIG_NODE:
+ break;
- if (host.app_info->go_parent_cb)
- host.app_info->go_parent_cb(vty);
- else
- vty->node = CONFIG_NODE;
+ case AUTH_ENABLE_NODE:
+ vty->node = VIEW_NODE;
+ break;
+
+ case CFG_LOG_NODE:
+ case VTY_NODE:
+ vty->node = CONFIG_NODE;
+ break;
+
+ default:
+ if (host.app_info->go_parent_cb)
+ host.app_info->go_parent_cb(vty);
+ else if (is_config_child(vty))
+ vty->node = CONFIG_NODE;
+ else
+ vty->node = VIEW_NODE;
+ break;
+ }
return vty->node;
}
@@ -1980,9 +2016,8 @@ cmd_execute_command_real(vector vline, struct vty *vty,
if ((command = vector_slot(vline, index))) {
int ret;
- match =
- cmd_filter_by_completion(command, cmd_vector,
- index);
+ match = cmd_filter(command, cmd_vector, index,
+ any_match);
if (match == vararg_match)
break;
@@ -2110,7 +2145,7 @@ cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
/* Go to parent for config nodes to attempt to find the right command */
while (ret != CMD_SUCCESS && ret != CMD_WARNING
- && is_config(vty)) {
+ && is_config_child(vty)) {
vty_go_parent(vty);
ret = cmd_execute_command_real(vline, vty, cmd);
tried = 1;
@@ -2152,8 +2187,8 @@ cmd_execute_command_strict(vector vline, struct vty *vty,
if ((command = vector_slot(vline, index))) {
int ret;
- match = cmd_filter_by_string(vector_slot(vline, index),
- cmd_vector, index);
+ match = cmd_filter(vector_slot(vline, index),
+ cmd_vector, index, exact_match);
/* If command meets '.VARARG' then finish matching. */
if (match == vararg_match)
@@ -2258,7 +2293,7 @@ int config_from_file(struct vty *vty, FILE * fp)
/* Try again with setting node to CONFIG_NODE */
while (ret != CMD_SUCCESS && ret != CMD_WARNING
&& ret != CMD_ERR_NOTHING_TODO
- && vty->node != CONFIG_NODE && is_config(vty)) {
+ && is_config_child(vty)) {
vty_go_parent(vty);
ret = cmd_execute_command_strict(vline, vty, NULL);
}
@@ -2315,24 +2350,18 @@ gDEFUN(config_exit,
config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
{
switch (vty->node) {
+ case AUTH_NODE:
case VIEW_NODE:
case ENABLE_NODE:
- if (0) //vty_shell (vty))
- exit(0);
- else
- vty->status = VTY_CLOSE;
+ vty->status = VTY_CLOSE;
break;
case CONFIG_NODE:
vty->node = ENABLE_NODE;
vty_config_unlock(vty);
break;
- case VTY_NODE:
- vty->node = CONFIG_NODE;
- break;
- case CFG_LOG_NODE:
- vty->node = CONFIG_NODE;
- break;
default:
+ if (vty->node > CONFIG_NODE)
+ vty_go_parent (vty);
break;
}
return CMD_SUCCESS;
@@ -2342,19 +2371,24 @@ gDEFUN(config_exit,
gDEFUN(config_end,
config_end_cmd, "end", "End current mode and change to enable mode.")
{
- switch (vty->node) {
- case VIEW_NODE:
- case ENABLE_NODE:
- /* Nothing to do. */
- break;
- case CFG_LOG_NODE:
- case CONFIG_NODE:
- case VTY_NODE:
+ if (vty->node > ENABLE_NODE) {
+ int last_node = CONFIG_NODE;
+
+ /* Repeatedly call go_parent until a top node is reached. */
+ while (vty->node > CONFIG_NODE) {
+ if (vty->node == last_node) {
+ /* Ensure termination, this shouldn't happen. */
+ break;
+ }
+ last_node = vty->node;
+ vty_go_parent(vty);
+ }
+
vty_config_unlock(vty);
- vty->node = ENABLE_NODE;
- break;
- default:
- break;
+ if (vty->node > ENABLE_NODE)
+ vty->node = ENABLE_NODE;
+ vty->index = NULL;
+ vty->index_sub = NULL;
}
return CMD_SUCCESS;
}
@@ -2524,6 +2558,15 @@ DEFUN(config_write_file,
char *failed_file;
int rc;
+ if (host.app_info->config_is_consistent) {
+ rc = host.app_info->config_is_consistent(vty);
+ if (!rc) {
+ vty_out(vty, "Configuration is not consistent%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
if (host.config == NULL) {
vty_out(vty, "Can't save to configuration file, using vtysh.%s",
VTY_NEWLINE);
@@ -3337,7 +3380,7 @@ void host_config_set(const char *filename)
host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
}
-void install_default(enum node_type node)
+void install_default(int node)
{
install_element(node, &config_help_cmd);
install_element(node, &config_list_cmd);
@@ -3349,6 +3392,18 @@ void install_default(enum node_type node)
install_element(node, &show_running_config_cmd);
}
+void vty_install_default(int node)
+{
+ install_default(node);
+
+ install_element(node, &config_exit_cmd);
+
+ if (node >= CONFIG_NODE) {
+ /* It's not a top node. */
+ install_element(node, &config_end_cmd);
+ }
+}
+
/**
* \brief Write the current running config to a given file
* \param[in] vty the vty of the code
@@ -3427,8 +3482,7 @@ void cmd_init(int terminal)
}
if (terminal) {
- install_element(ENABLE_NODE, &config_exit_cmd);
- install_default(ENABLE_NODE);
+ vty_install_default(ENABLE_NODE);
install_element(ENABLE_NODE, &config_disable_cmd);
install_element(ENABLE_NODE, &config_terminal_cmd);
install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
@@ -3442,8 +3496,7 @@ void cmd_init(int terminal)
install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
install_element(ENABLE_NODE, &echo_cmd);
- install_default(CONFIG_NODE);
- install_element(CONFIG_NODE, &config_exit_cmd);
+ vty_install_default(CONFIG_NODE);
}
install_element(CONFIG_NODE, &hostname_cmd);
diff --git a/src/shared/libosmocore/src/vty/fsm_vty.c b/src/shared/libosmocore/src/vty/fsm_vty.c
new file mode 100644
index 00000000..422de9d9
--- /dev/null
+++ b/src/shared/libosmocore/src/vty/fsm_vty.c
@@ -0,0 +1,177 @@
+/* Osmocom FSM introspection via VTY */
+/* (C) 2016 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../config.h"
+
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/buffer.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/misc.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/linuxlist.h>
+
+/* we don't want to add this to a public header file; this is simply
+ * exported by libosmocore and used by libmsomvty but not for public
+ * consumption. */
+extern struct llist_head osmo_g_fsms;
+
+/*! \brief Print information about a FSM [class] to the given VTY
+ * \param vty The VTY to which to print
+ * \param[in] fsm The FSM class to print
+ */
+void vty_out_fsm(struct vty *vty, struct osmo_fsm *fsm)
+{
+ unsigned int i;
+ const struct value_string *evt_name;
+
+ vty_out(vty, "FSM Name: '%s', Log Subsys: '%s'%s", fsm->name,
+ log_category_name(fsm->log_subsys), VTY_NEWLINE);
+ /* list the events */
+ for (evt_name = fsm->event_names; evt_name->str != NULL; evt_name++) {
+ vty_out(vty, " Event %02u (0x%08x): '%s'%s", evt_name->value,
+ (1 << evt_name->value), evt_name->str, VTY_NEWLINE);
+ }
+ /* list the states */
+ vty_out(vty, " Number of States: %u%s", fsm->num_states, VTY_NEWLINE);
+ for (i = 0; i < fsm->num_states; i++) {
+ const struct osmo_fsm_state *state = &fsm->states[i];
+ vty_out(vty, " State %-20s InEvtMask: 0x%08x, OutStateMask: 0x%08x%s",
+ state->name, state->in_event_mask, state->out_state_mask,
+ VTY_NEWLINE);
+ }
+}
+
+/*! \brief Print a FSM instance to the given VTY
+ * \param vty The VTY to which to print
+ * \param[in] fsmi The FSM instance to print
+ */
+void vty_out_fsm_inst(struct vty *vty, struct osmo_fsm_inst *fsmi)
+{
+ struct osmo_fsm_inst *child;
+
+ vty_out(vty, "FSM Instance Name: '%s', ID: '%s'%s",
+ fsmi->name, fsmi->id, VTY_NEWLINE);
+ vty_out(vty, " Log-Level: '%s', State: '%s'%s",
+ log_level_str(fsmi->log_level),
+ osmo_fsm_state_name(fsmi->fsm, fsmi->state),
+ VTY_NEWLINE);
+ if (fsmi->T)
+ vty_out(vty, " Timer: %u%s", fsmi->T, VTY_NEWLINE);
+ if (fsmi->proc.parent) {
+ vty_out(vty, " Parent: '%s', Term-Event: '%s'%s",
+ fsmi->proc.parent->name,
+ osmo_fsm_event_name(fsmi->proc.parent->fsm,
+ fsmi->proc.parent_term_event),
+ VTY_NEWLINE);
+ }
+ llist_for_each_entry(child, &fsmi->proc.children, list) {
+ vty_out(vty, " Child: '%s'%s", child->name, VTY_NEWLINE);
+ }
+}
+
+#define SH_FSM_STR SHOW_STR "Show information about finite state machines\n"
+#define SH_FSMI_STR SHOW_STR "Show information about finite state machine instances\n"
+
+DEFUN(show_fsms, show_fsms_cmd,
+ "show fsm all",
+ SH_FSM_STR
+ "Display a list of all registered finite state machines\n")
+{
+ struct osmo_fsm *fsm;
+
+ llist_for_each_entry(fsm, &osmo_g_fsms, list)
+ vty_out_fsm(vty, fsm);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_fsm, show_fsm_cmd,
+ "show fsm NAME",
+ SH_FSM_STR
+ "Display information about a single named finite state machine\n")
+{
+ struct osmo_fsm *fsm;
+
+ fsm = osmo_fsm_find_by_name(argv[0]);
+ if (!fsm) {
+ vty_out(vty, "Error: FSM with name '%s' doesn't exist!%s",
+ argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vty_out_fsm(vty, fsm);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_fsm_insts, show_fsm_insts_cmd,
+ "show fsm-instances all",
+ SH_FSMI_STR
+ "Display a list of all FSM instances of all finite state machine")
+{
+ struct osmo_fsm *fsm;
+
+ llist_for_each_entry(fsm, &osmo_g_fsms, list) {
+ struct osmo_fsm_inst *fsmi;
+ llist_for_each_entry(fsmi, &fsm->instances, list)
+ vty_out_fsm_inst(vty, fsmi);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_fsm_inst, show_fsm_inst_cmd,
+ "show fsm-instances NAME",
+ SH_FSMI_STR
+ "Display a list of all FSM instances of the named finite state machine")
+{
+ struct osmo_fsm *fsm;
+ struct osmo_fsm_inst *fsmi;
+
+ fsm = osmo_fsm_find_by_name(argv[0]);
+ if (!fsm) {
+ vty_out(vty, "Error: FSM with name '%s' doesn't exist!%s",
+ argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ llist_for_each_entry(fsmi, &fsm->instances, list)
+ vty_out_fsm_inst(vty, fsmi);
+
+ return CMD_SUCCESS;
+}
+
+/*! \brief Install VTY commands for FSM introspection
+ * This installs a couple of VTY commands for introspection of FSM
+ * classes as well as FSM instances. Call this once from your
+ * application if you want to support those commands. */
+void osmo_fsm_vty_add_cmds(void)
+{
+ install_element_ve(&show_fsm_cmd);
+ install_element_ve(&show_fsms_cmd);
+ install_element_ve(&show_fsm_inst_cmd);
+ install_element_ve(&show_fsm_insts_cmd);
+}
diff --git a/src/shared/libosmocore/src/vty/logging_vty.c b/src/shared/libosmocore/src/vty/logging_vty.c
index d473f129..6004c479 100644
--- a/src/shared/libosmocore/src/vty/logging_vty.c
+++ b/src/shared/libosmocore/src/vty/logging_vty.c
@@ -1,6 +1,6 @@
/* OpenBSC logging helper for the VTY */
/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2009-2010 by Holger Hans Peter Freyther
+ * (C) 2009-2014 by Holger Hans Peter Freyther
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
@@ -27,8 +27,9 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
-
-//#include <openbsc/vty.h>
+#include <osmocom/core/strrb.h>
+#include <osmocom/core/loggingrb.h>
+#include <osmocom/core/gsmtap.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/buffer.h>
@@ -149,6 +150,40 @@ DEFUN(logging_prnt_timestamp,
return CMD_SUCCESS;
}
+DEFUN(logging_prnt_ext_timestamp,
+ logging_prnt_ext_timestamp_cmd,
+ "logging print extended-timestamp (0|1)",
+ LOGGING_STR "Log output settings\n"
+ "Configure log message timestamping\n"
+ "Don't prefix each log message\n"
+ "Prefix each log message with current timestamp with YYYYMMDDhhmmssnnn\n")
+{
+ struct log_target *tgt = osmo_log_vty2tgt(vty);
+
+ if (!tgt)
+ return CMD_WARNING;
+
+ log_set_print_extended_timestamp(tgt, atoi(argv[0]));
+ return CMD_SUCCESS;
+}
+
+DEFUN(logging_prnt_cat,
+ logging_prnt_cat_cmd,
+ "logging print category (0|1)",
+ LOGGING_STR "Log output settings\n"
+ "Configure log message\n"
+ "Don't prefix each log message\n"
+ "Prefix each log message with category/subsystem name\n")
+{
+ struct log_target *tgt = osmo_log_vty2tgt(vty);
+
+ if (!tgt)
+ return CMD_WARNING;
+
+ log_set_print_category(tgt, atoi(argv[0]));
+ return CMD_SUCCESS;
+}
+
DEFUN(logging_level,
logging_level_cmd,
NULL, /* cmdstr is dynamically set in logging_vty_add_cmds(). */
@@ -241,19 +276,30 @@ static void vty_print_logtarget(struct vty *vty, const struct log_info *info,
for (i = 0; i < info->num_cat; i++) {
const struct log_category *cat = &tgt->categories[i];
+ /* Skip categories that were not initialized */
+ if (!info->cat[i].name)
+ continue;
vty_out(vty, " %-10s %-10s %-8s %s%s",
info->cat[i].name+1, log_level_str(cat->loglevel),
cat->enabled ? "Enabled" : "Disabled",
info->cat[i].description,
VTY_NEWLINE);
}
+
+ vty_out(vty, " Log Filter 'ALL': %s%s",
+ tgt->filter_map & LOG_FILTER_ALL ? "Enabled" : "Disabled",
+ VTY_NEWLINE);
+
+ /* print application specific filters */
+ if (info->print_fn)
+ info->print_fn(vty, info, tgt);
}
#define SHOW_LOG_STR "Show current logging configuration\n"
DEFUN(show_logging_vty,
- show_logging_vty_cmd,
- "show logging vty",
+ show_logging_vty_cmd,
+ "show logging vty",
SHOW_STR SHOW_LOG_STR
"Show current logging configuration for this vty\n")
{
@@ -267,6 +313,33 @@ DEFUN(show_logging_vty,
return CMD_SUCCESS;
}
+DEFUN(show_alarms,
+ show_alarms_cmd,
+ "show alarms",
+ SHOW_STR SHOW_LOG_STR
+ "Show the contents of the logging ringbuffer\n")
+{
+ int i, num_alarms;
+ struct osmo_strrb *rb;
+ struct log_target *tgt = log_target_find(LOG_TGT_TYPE_STRRB, NULL);
+ if (!tgt) {
+ vty_out(vty, "%% No alarms, run 'log alarms <2-32700>'%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ rb = tgt->tgt_rb.rb;
+ num_alarms = osmo_strrb_elements(rb);
+
+ vty_out(vty, "%% Showing %i alarms%s", num_alarms, VTY_NEWLINE);
+
+ for (i = 0; i < num_alarms; i++)
+ vty_out(vty, "%% %s%s", osmo_strrb_get_nth(rb, i),
+ VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
gDEFUN(cfg_description, cfg_description_cmd,
"description .TEXT",
"Save human-readable decription of the object\n"
@@ -282,7 +355,7 @@ gDEFUN(cfg_description, cfg_description_cmd,
if (*dptr)
talloc_free(*dptr);
*dptr = argv_concat(argv, argc, 0);
- if (!dptr)
+ if (!*dptr)
return CMD_WARNING;
return CMD_SUCCESS;
@@ -426,6 +499,33 @@ DEFUN(cfg_no_log_syslog, cfg_no_log_syslog_cmd,
}
#endif /* HAVE_SYSLOG_H */
+DEFUN(cfg_log_gsmtap, cfg_log_gsmtap_cmd,
+ "log gsmtap [HOSTNAME]",
+ LOG_STR "Logging via GSMTAP\n"
+ "Host name to send the GSMTAP logging to (UDP port 4729)\n")
+{
+ const char *hostname = argv[0];
+ struct log_target *tgt;
+
+ tgt = log_target_find(LOG_TGT_TYPE_GSMTAP, hostname);
+ if (!tgt) {
+ tgt = log_target_create_gsmtap(hostname, GSMTAP_UDP_PORT,
+ host.app_info->name, false,
+ true);
+ if (!tgt) {
+ vty_out(vty, "%% Unable to create GSMTAP log%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ log_add_target(tgt);
+ }
+
+ vty->index = tgt;
+ vty->node = CFG_LOG_NODE;
+
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_log_stderr, cfg_log_stderr_cmd,
"log stderr",
LOG_STR "Logging via STDERR of the process\n")
@@ -510,6 +610,49 @@ DEFUN(cfg_no_log_file, cfg_no_log_file_cmd,
return CMD_SUCCESS;
}
+DEFUN(cfg_log_alarms, cfg_log_alarms_cmd,
+ "log alarms <2-32700>",
+ LOG_STR "Logging alarms to osmo_strrb\n"
+ "Maximum number of messages to log\n")
+{
+ struct log_target *tgt;
+ unsigned int rbsize = atoi(argv[0]);
+
+ tgt = log_target_find(LOG_TGT_TYPE_STRRB, NULL);
+ if (tgt)
+ log_target_destroy(tgt);
+
+ tgt = log_target_create_rb(rbsize);
+ if (!tgt) {
+ vty_out(vty, "%% Unable to create osmo_strrb (size %u)%s",
+ rbsize, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ log_add_target(tgt);
+
+ vty->index = tgt;
+ vty->node = CFG_LOG_NODE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_log_alarms, cfg_no_log_alarms_cmd,
+ "no log alarms",
+ NO_STR LOG_STR "Logging alarms to osmo_strrb\n")
+{
+ struct log_target *tgt;
+
+ tgt = log_target_find(LOG_TGT_TYPE_STRRB, NULL);
+ if (!tgt) {
+ vty_out(vty, "%% No osmo_strrb target found%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ log_target_destroy(tgt);
+
+ return CMD_SUCCESS;
+}
+
static int config_write_log_single(struct vty *vty, struct log_target *tgt)
{
int i;
@@ -533,16 +676,31 @@ static int config_write_log_single(struct vty *vty, struct log_target *tgt)
case LOG_TGT_TYPE_FILE:
vty_out(vty, "log file %s%s", tgt->tgt_file.fname, VTY_NEWLINE);
break;
+ case LOG_TGT_TYPE_STRRB:
+ vty_out(vty, "log alarms %zu%s",
+ log_target_rb_avail_size(tgt), VTY_NEWLINE);
+ break;
+ case LOG_TGT_TYPE_GSMTAP:
+ vty_out(vty, "log gsmtap %s%s",
+ tgt->tgt_gsmtap.hostname, VTY_NEWLINE);
+ break;
}
vty_out(vty, " logging filter all %u%s",
tgt->filter_map & LOG_FILTER_ALL ? 1 : 0, VTY_NEWLINE);
- /* FIXME: how to do this for filters outside of libosmocore? */
+ /* save filters outside of libosmocore, i.e. in app code */
+ if (osmo_log_info->save_fn)
+ osmo_log_info->save_fn(vty, osmo_log_info, tgt);
vty_out(vty, " logging color %u%s", tgt->use_color ? 1 : 0,
VTY_NEWLINE);
- vty_out(vty, " logging timestamp %u%s", tgt->print_timestamp ? 1 : 0,
- VTY_NEWLINE);
+ vty_out(vty, " logging print category %d%s",
+ tgt->print_category ? 1 : 0, VTY_NEWLINE);
+ if (tgt->print_ext_timestamp)
+ vty_out(vty, " logging print extended-timestamp 1%s", VTY_NEWLINE);
+ else
+ vty_out(vty, " logging timestamp %u%s",
+ tgt->print_timestamp ? 1 : 0, VTY_NEWLINE);
/* stupid old osmo logging API uses uppercase strings... */
osmo_str2lower(level_lower, log_level_str(tgt->loglevel));
@@ -552,6 +710,10 @@ static int config_write_log_single(struct vty *vty, struct log_target *tgt)
const struct log_category *cat = &tgt->categories[i];
char cat_lower[32];
+ /* skip empty entries in the array */
+ if (!osmo_log_info->cat[i].name)
+ continue;
+
/* stupid old osmo logging API uses uppercase strings... */
osmo_str2lower(cat_lower, osmo_log_info->cat[i].name+1);
osmo_str2lower(level_lower, log_level_str(cat->loglevel));
@@ -582,6 +744,8 @@ void logging_vty_add_cmds(const struct log_info *cat)
install_element_ve(&logging_fltr_all_cmd);
install_element_ve(&logging_use_clr_cmd);
install_element_ve(&logging_prnt_timestamp_cmd);
+ install_element_ve(&logging_prnt_ext_timestamp_cmd);
+ install_element_ve(&logging_prnt_cat_cmd);
install_element_ve(&logging_set_category_mask_cmd);
install_element_ve(&logging_set_category_mask_old_cmd);
@@ -590,22 +754,27 @@ void logging_vty_add_cmds(const struct log_info *cat)
logging_level_cmd.doc = log_vty_command_description(cat);
install_element_ve(&logging_level_cmd);
install_element_ve(&show_logging_vty_cmd);
+ install_element_ve(&show_alarms_cmd);
install_node(&cfg_log_node, config_write_log);
- install_default(CFG_LOG_NODE);
- install_element(CFG_LOG_NODE, &config_end_cmd);
+ vty_install_default(CFG_LOG_NODE);
install_element(CFG_LOG_NODE, &logging_fltr_all_cmd);
install_element(CFG_LOG_NODE, &logging_use_clr_cmd);
install_element(CFG_LOG_NODE, &logging_prnt_timestamp_cmd);
+ install_element(CFG_LOG_NODE, &logging_prnt_ext_timestamp_cmd);
+ install_element(CFG_LOG_NODE, &logging_prnt_cat_cmd);
install_element(CFG_LOG_NODE, &logging_level_cmd);
install_element(CONFIG_NODE, &cfg_log_stderr_cmd);
install_element(CONFIG_NODE, &cfg_no_log_stderr_cmd);
install_element(CONFIG_NODE, &cfg_log_file_cmd);
install_element(CONFIG_NODE, &cfg_no_log_file_cmd);
+ install_element(CONFIG_NODE, &cfg_log_alarms_cmd);
+ install_element(CONFIG_NODE, &cfg_no_log_alarms_cmd);
#ifdef HAVE_SYSLOG_H
install_element(CONFIG_NODE, &cfg_log_syslog_cmd);
install_element(CONFIG_NODE, &cfg_log_syslog_local_cmd);
install_element(CONFIG_NODE, &cfg_no_log_syslog_cmd);
#endif
+ install_element(CONFIG_NODE, &cfg_log_gsmtap_cmd);
}
diff --git a/src/shared/libosmocore/src/vty/stats_vty.c b/src/shared/libosmocore/src/vty/stats_vty.c
new file mode 100644
index 00000000..c03546b8
--- /dev/null
+++ b/src/shared/libosmocore/src/vty/stats_vty.c
@@ -0,0 +1,601 @@
+/* OpenBSC stats helper for the VTY */
+/* (C) 2009-2010 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
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../config.h"
+
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/buffer.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/misc.h>
+
+#include <osmocom/core/stats.h>
+#include <osmocom/core/statistics.h>
+#include <osmocom/core/rate_ctr.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"
+
+/* containing version info */
+extern struct host host;
+
+struct cmd_node cfg_stats_node = {
+ CFG_STATS_NODE,
+ "%s(config-stats)# ",
+ 1
+};
+
+static const struct value_string stats_class_strs[] = {
+ { OSMO_STATS_CLASS_GLOBAL, "global" },
+ { OSMO_STATS_CLASS_PEER, "peer" },
+ { OSMO_STATS_CLASS_SUBSCRIBER, "subscriber" },
+ { 0, NULL }
+};
+
+static struct osmo_stats_reporter *osmo_stats_vty2srep(struct vty *vty)
+{
+ if (vty->node == CFG_STATS_NODE)
+ return vty->index;
+
+ return NULL;
+}
+
+static int set_srep_parameter_str(struct vty *vty,
+ int (*fun)(struct osmo_stats_reporter *, const char *),
+ const char *val, const char *param_name)
+{
+ int rc;
+ struct osmo_stats_reporter *srep = osmo_stats_vty2srep(vty);
+ OSMO_ASSERT(srep);
+
+ rc = fun(srep, val);
+ if (rc < 0) {
+ vty_out(vty, "%% Unable to set %s: %s%s",
+ param_name, strerror(-rc), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+static int set_srep_parameter_int(struct vty *vty,
+ int (*fun)(struct osmo_stats_reporter *, int),
+ const char *val, const char *param_name)
+{
+ int rc;
+ int int_val;
+ struct osmo_stats_reporter *srep = osmo_stats_vty2srep(vty);
+ OSMO_ASSERT(srep);
+
+ int_val = atoi(val);
+
+ rc = fun(srep, int_val);
+ if (rc < 0) {
+ vty_out(vty, "%% Unable to set %s: %s%s",
+ param_name, strerror(-rc), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_stats_reporter_local_ip, cfg_stats_reporter_local_ip_cmd,
+ "local-ip ADDR",
+ "Set the IP address to which we bind locally\n"
+ "IP Address\n")
+{
+ return set_srep_parameter_str(vty, osmo_stats_reporter_set_local_addr,
+ argv[0], "local address");
+}
+
+DEFUN(cfg_no_stats_reporter_local_ip, cfg_no_stats_reporter_local_ip_cmd,
+ "no local-ip",
+ NO_STR
+ "Set the IP address to which we bind locally\n")
+{
+ return set_srep_parameter_str(vty, osmo_stats_reporter_set_local_addr,
+ NULL, "local address");
+}
+
+DEFUN(cfg_stats_reporter_remote_ip, cfg_stats_reporter_remote_ip_cmd,
+ "remote-ip ADDR",
+ "Set the remote IP address to which we connect\n"
+ "IP Address\n")
+{
+ return set_srep_parameter_str(vty, osmo_stats_reporter_set_remote_addr,
+ argv[0], "remote address");
+}
+
+DEFUN(cfg_stats_reporter_remote_port, cfg_stats_reporter_remote_port_cmd,
+ "remote-port <1-65535>",
+ "Set the remote port to which we connect\n"
+ "Remote port number\n")
+{
+ return set_srep_parameter_int(vty, osmo_stats_reporter_set_remote_port,
+ argv[0], "remote port");
+}
+
+DEFUN(cfg_stats_reporter_mtu, cfg_stats_reporter_mtu_cmd,
+ "mtu <100-65535>",
+ "Set the maximum packet size\n"
+ "Size in byte\n")
+{
+ return set_srep_parameter_int(vty, osmo_stats_reporter_set_mtu,
+ argv[0], "mtu");
+}
+
+DEFUN(cfg_no_stats_reporter_mtu, cfg_no_stats_reporter_mtu_cmd,
+ "no mtu",
+ NO_STR "Set the maximum packet size\n")
+{
+ return set_srep_parameter_int(vty, osmo_stats_reporter_set_mtu,
+ "0", "mtu");
+}
+
+DEFUN(cfg_stats_reporter_prefix, cfg_stats_reporter_prefix_cmd,
+ "prefix PREFIX",
+ "Set the item name prefix\n"
+ "The prefix string\n")
+{
+ return set_srep_parameter_str(vty, osmo_stats_reporter_set_name_prefix,
+ argv[0], "prefix string");
+}
+
+DEFUN(cfg_no_stats_reporter_prefix, cfg_no_stats_reporter_prefix_cmd,
+ "no prefix",
+ NO_STR
+ "Set the item name prefix\n")
+{
+ return set_srep_parameter_str(vty, osmo_stats_reporter_set_name_prefix,
+ "", "prefix string");
+}
+
+DEFUN(cfg_stats_reporter_level, cfg_stats_reporter_level_cmd,
+ "level (global|peer|subscriber)",
+ "Set the maximum group level\n"
+ "Report global groups only\n"
+ "Report global and network peer related groups\n"
+ "Report global, peer, and subscriber groups\n")
+{
+ int level = get_string_value(stats_class_strs, argv[0]);
+ int rc;
+ struct osmo_stats_reporter *srep = osmo_stats_vty2srep(vty);
+
+ OSMO_ASSERT(srep);
+ rc = osmo_stats_reporter_set_max_class(srep, level);
+ if (rc < 0) {
+ vty_out(vty, "%% Unable to set level: %s%s",
+ strerror(-rc), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return 0;
+}
+
+DEFUN(cfg_stats_reporter_enable, cfg_stats_reporter_enable_cmd,
+ "enable",
+ "Enable the reporter\n")
+{
+ int rc;
+ struct osmo_stats_reporter *srep = osmo_stats_vty2srep(vty);
+ OSMO_ASSERT(srep);
+
+ rc = osmo_stats_reporter_enable(srep);
+ if (rc < 0) {
+ vty_out(vty, "%% Unable to enable the reporter: %s%s",
+ strerror(-rc), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_stats_reporter_disable, cfg_stats_reporter_disable_cmd,
+ "disable",
+ "Disable the reporter\n")
+{
+ int rc;
+ struct osmo_stats_reporter *srep = osmo_stats_vty2srep(vty);
+ OSMO_ASSERT(srep);
+
+ rc = osmo_stats_reporter_disable(srep);
+ if (rc < 0) {
+ vty_out(vty, "%% Unable to disable the reporter: %s%s",
+ strerror(-rc), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+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")
+{
+ struct osmo_stats_reporter *srep;
+
+ srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL);
+ if (!srep) {
+ srep = osmo_stats_reporter_create_statsd(NULL);
+ if (!srep) {
+ vty_out(vty, "%% Unable to create statsd reporter%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ srep->max_class = OSMO_STATS_CLASS_GLOBAL;
+ /* TODO: if needed, add osmo_stats_add_reporter(srep); */
+ }
+
+ vty->index = srep;
+ vty->node = CFG_STATS_NODE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_stats_interval, cfg_stats_interval_cmd,
+ "stats interval <1-65535>",
+ CFG_STATS_STR "Set the reporting interval\n"
+ "Interval in seconds\n")
+{
+ int rc;
+ int interval = atoi(argv[0]);
+ rc = osmo_stats_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_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")
+{
+ struct osmo_stats_reporter *srep;
+
+ srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL);
+ if (!srep) {
+ vty_out(vty, "%% No statsd logging active%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ osmo_stats_reporter_free(srep);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_stats_reporter_log, cfg_stats_reporter_log_cmd,
+ "stats reporter log",
+ CFG_STATS_STR CFG_REPORTER_STR "Report to the logger\n")
+{
+ struct osmo_stats_reporter *srep;
+
+ srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL);
+ if (!srep) {
+ srep = osmo_stats_reporter_create_log(NULL);
+ if (!srep) {
+ vty_out(vty, "%% Unable to create log reporter%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ srep->max_class = OSMO_STATS_CLASS_GLOBAL;
+ /* TODO: if needed, add osmo_stats_add_reporter(srep); */
+ }
+
+ vty->index = srep;
+ vty->node = CFG_STATS_NODE;
+
+ return CMD_SUCCESS;
+}
+
+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")
+{
+ struct osmo_stats_reporter *srep;
+
+ srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL);
+ if (!srep) {
+ vty_out(vty, "%% No log reporting active%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ osmo_stats_reporter_free(srep);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_stats,
+ show_stats_cmd,
+ "show stats",
+ SHOW_STR SHOW_STATS_STR)
+{
+ vty_out_statistics_full(vty, "");
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_stats_level,
+ show_stats_level_cmd,
+ "show stats level (global|peer|subscriber)",
+ 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")
+{
+ int level = get_string_value(stats_class_strs, argv[0]);
+ vty_out_statistics_partial(vty, "", level);
+
+ return CMD_SUCCESS;
+}
+
+static int asciidoc_handle_counter(struct osmo_counter *counter, void *sctx_)
+{
+ struct vty *vty = sctx_;
+ char *name = osmo_asciidoc_escape(counter->name);
+ char *description = osmo_asciidoc_escape(counter->description);
+
+ /* | name | This document & | description | */
+ vty_out(vty, "| %s | <<ungroup_counter_%s>> | %s%s",
+ name,
+ name,
+ description ? description : "",
+ VTY_NEWLINE);
+
+ talloc_free(name);
+ talloc_free(description);
+
+ return 0;
+}
+
+static void asciidoc_counter_generate(struct vty *vty)
+{
+ 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);
+ vty_out(vty, "|===%s", VTY_NEWLINE);
+ vty_out(vty, "| Name | Reference | Description%s", VTY_NEWLINE);
+ osmo_counters_for_each(asciidoc_handle_counter, vty);
+ vty_out(vty, "|===%s", VTY_NEWLINE);
+}
+
+static int asciidoc_rate_ctr_handler(
+ struct rate_ctr_group *ctrg, struct rate_ctr *ctr,
+ const struct rate_ctr_desc *desc, void *sctx_)
+{
+ struct vty *vty = sctx_;
+ char *name = osmo_asciidoc_escape(desc->name);
+ char *description = osmo_asciidoc_escape(desc->description);
+ char *group_name_prefix = osmo_asciidoc_escape(ctrg->desc->group_name_prefix);
+
+ /* | Name | This document & | Description | */
+ vty_out(vty, "| %s | <<%s_%s>> | %s%s",
+ name,
+ group_name_prefix,
+ name,
+ description ? description : NULL,
+ VTY_NEWLINE);
+
+ /* description seems to be optional */
+ talloc_free(name);
+ talloc_free(group_name_prefix);
+ talloc_free(description);
+
+ return 0;
+}
+
+static int asciidoc_rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_)
+{
+ struct vty *vty = sctx_;
+
+ char *group_description = osmo_asciidoc_escape(ctrg->desc->group_description);
+ char *group_name_prefix = osmo_asciidoc_escape(ctrg->desc->group_name_prefix);
+
+ vty_out(vty, "// rate_ctr_group table %s%s", group_description, VTY_NEWLINE);
+ vty_out(vty, ".%s - %s %s", group_name_prefix, group_description, VTY_NEWLINE);
+ vty_out(vty, "[options=\"header\"]%s", VTY_NEWLINE);
+ vty_out(vty, "|===%s", VTY_NEWLINE);
+ vty_out(vty, "| Name | Reference | Description%s", VTY_NEWLINE);
+ rate_ctr_for_each_counter(ctrg, asciidoc_rate_ctr_handler, sctx_);
+ vty_out(vty, "|===%s", VTY_NEWLINE);
+
+ talloc_free(group_name_prefix);
+ talloc_free(group_description);
+
+ return 0;
+}
+
+static int asciidoc_osmo_stat_item_handler(
+ struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *sctx_)
+{
+ struct vty *vty = sctx_;
+
+ char *name = osmo_asciidoc_escape(item->desc->name);
+ char *description = osmo_asciidoc_escape(item->desc->description);
+ char *group_name_prefix = osmo_asciidoc_escape(statg->desc->group_name_prefix);
+ char *unit = osmo_asciidoc_escape(item->desc->unit);
+
+ /* | Name | Reference | Description | Unit | */
+ vty_out(vty, "| %s | <<%s_%s>> | %s | %s%s",
+ name,
+ group_name_prefix,
+ name,
+ description ? description : "",
+ unit ? unit : "",
+ VTY_NEWLINE);
+
+ talloc_free(name);
+ talloc_free(group_name_prefix);
+ talloc_free(description);
+ talloc_free(unit);
+
+ return 0;
+}
+
+static int asciidoc_osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *sctx_)
+{
+ char *group_name_prefix = osmo_asciidoc_escape(statg->desc->group_name_prefix);
+ char *group_description = osmo_asciidoc_escape(statg->desc->group_description);
+
+ struct vty *vty = sctx_;
+ vty_out(vty, "%s%s", group_description ? group_description : "" , VTY_NEWLINE);
+
+ vty_out(vty, "// osmo_stat_item_group table %s%s", group_description ? group_description : "", VTY_NEWLINE);
+ vty_out(vty, ".%s - %s %s", group_name_prefix, group_description ? group_description : "", VTY_NEWLINE);
+ vty_out(vty, "[options=\"header\"]%s", VTY_NEWLINE);
+ vty_out(vty, "|===%s", VTY_NEWLINE);
+ vty_out(vty, "| Name | Reference | Description | Unit%s", VTY_NEWLINE);
+ osmo_stat_item_for_each_item(statg, asciidoc_osmo_stat_item_handler, sctx_);
+ vty_out(vty, "|===%s", VTY_NEWLINE);
+
+ talloc_free(group_name_prefix);
+ talloc_free(group_description);
+
+ return 0;
+}
+
+DEFUN(show_stats_asciidoc_table,
+ show_stats_asciidoc_table_cmd,
+ "show asciidoc counters",
+ SHOW_STR "Asciidoc generation\n" "Generate table of all registered counters\n")
+{
+ vty_out(vty, "// autogenerated by show asciidoc counters%s", VTY_NEWLINE);
+ vty_out(vty, "These counters and their description based on %s %s (%s).%s%s",
+ host.app_info->name,
+ host.app_info->version,
+ host.app_info->name ? host.app_info->name : "", VTY_NEWLINE, VTY_NEWLINE);
+ /* 2x VTY_NEWLINE are intentional otherwise it would interpret the first table header
+ * as usual text*/
+ vty_out(vty, "// generating tables for rate_ctr_group%s", VTY_NEWLINE);
+ rate_ctr_for_each_group(asciidoc_rate_ctr_group_handler, vty);
+
+ 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, "// generating tables for osmo_counters%s", VTY_NEWLINE);
+ asciidoc_counter_generate(vty);
+ return CMD_SUCCESS;
+}
+
+static int config_write_stats_reporter(struct vty *vty, struct osmo_stats_reporter *srep)
+{
+ if (srep == NULL)
+ return 0;
+
+ switch (srep->type) {
+ case OSMO_STATS_REPORTER_STATSD:
+ vty_out(vty, "stats reporter statsd%s", VTY_NEWLINE);
+ break;
+ case OSMO_STATS_REPORTER_LOG:
+ vty_out(vty, "stats reporter log%s", VTY_NEWLINE);
+ break;
+ }
+
+ vty_out(vty, " disable%s", VTY_NEWLINE);
+
+ if (srep->have_net_config) {
+ if (srep->dest_addr_str)
+ vty_out(vty, " remote-ip %s%s",
+ srep->dest_addr_str, VTY_NEWLINE);
+ if (srep->dest_port)
+ vty_out(vty, " remote-port %d%s",
+ srep->dest_port, VTY_NEWLINE);
+ if (srep->bind_addr_str)
+ vty_out(vty, " local-ip %s%s",
+ srep->bind_addr_str, VTY_NEWLINE);
+ if (srep->mtu)
+ vty_out(vty, " mtu %d%s",
+ srep->mtu, VTY_NEWLINE);
+ }
+
+ if (srep->max_class)
+ vty_out(vty, " level %s%s",
+ get_value_string(stats_class_strs, srep->max_class),
+ VTY_NEWLINE);
+
+ if (srep->name_prefix && *srep->name_prefix)
+ vty_out(vty, " prefix %s%s",
+ srep->name_prefix, VTY_NEWLINE);
+ else
+ vty_out(vty, " no prefix%s", VTY_NEWLINE);
+
+ if (srep->enabled)
+ vty_out(vty, " enable%s", VTY_NEWLINE);
+
+ return 1;
+}
+
+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);
+
+ return 1;
+}
+
+void osmo_stats_vty_add_cmds()
+{
+ install_element_ve(&show_stats_cmd);
+ install_element_ve(&show_stats_level_cmd);
+
+ install_element(CONFIG_NODE, &cfg_stats_reporter_statsd_cmd);
+ install_element(CONFIG_NODE, &cfg_no_stats_reporter_statsd_cmd);
+ install_element(CONFIG_NODE, &cfg_stats_reporter_log_cmd);
+ install_element(CONFIG_NODE, &cfg_no_stats_reporter_log_cmd);
+ install_element(CONFIG_NODE, &cfg_stats_interval_cmd);
+
+ install_node(&cfg_stats_node, config_write_stats);
+ vty_install_default(CFG_STATS_NODE);
+
+ install_element(CFG_STATS_NODE, &cfg_stats_reporter_local_ip_cmd);
+ install_element(CFG_STATS_NODE, &cfg_no_stats_reporter_local_ip_cmd);
+ install_element(CFG_STATS_NODE, &cfg_stats_reporter_remote_ip_cmd);
+ install_element(CFG_STATS_NODE, &cfg_stats_reporter_remote_port_cmd);
+ install_element(CFG_STATS_NODE, &cfg_stats_reporter_mtu_cmd);
+ install_element(CFG_STATS_NODE, &cfg_no_stats_reporter_mtu_cmd);
+ install_element(CFG_STATS_NODE, &cfg_stats_reporter_prefix_cmd);
+ install_element(CFG_STATS_NODE, &cfg_no_stats_reporter_prefix_cmd);
+ install_element(CFG_STATS_NODE, &cfg_stats_reporter_level_cmd);
+ install_element(CFG_STATS_NODE, &cfg_stats_reporter_enable_cmd);
+ install_element(CFG_STATS_NODE, &cfg_stats_reporter_disable_cmd);
+
+ install_element_ve(&show_stats_asciidoc_table_cmd);
+}
diff --git a/src/shared/libosmocore/src/vty/telnet_interface.c b/src/shared/libosmocore/src/vty/telnet_interface.c
index 1abf141d..218f9abf 100644
--- a/src/shared/libosmocore/src/vty/telnet_interface.c
+++ b/src/shared/libosmocore/src/vty/telnet_interface.c
@@ -30,6 +30,7 @@
#include <osmocom/core/socket.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/logging.h>
+#include <osmocom/core/signal.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/buffer.h>
@@ -85,7 +86,14 @@ int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port)
server_socket.data = priv;
- return (rc < 0) ? -1 : 0;
+ if (rc < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Cannot bind telnet at %s %d\n",
+ ip, port);
+ return -1;
+ }
+
+ LOGP(DLGLOBAL, LOGL_NOTICE, "telnet at %s %d\n", ip, port);
+ return 0;
}
extern struct host host;
@@ -119,7 +127,7 @@ static int client_data(struct osmo_fd *fd, unsigned int what)
}
/* vty might have been closed from vithin vty_read() */
- if (!conn->vty)
+ if (rc == -EBADF)
return rc;
if (what & BSC_FD_WRITE) {
@@ -137,6 +145,7 @@ static int telnet_new_connection(struct osmo_fd *fd, unsigned int what)
struct sockaddr_in sockaddr;
socklen_t len = sizeof(sockaddr);
int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len);
+ int rc;
if (new_connection < 0) {
LOGP(0, LOGL_ERROR, "telnet accept failed\n");
@@ -149,7 +158,11 @@ static int telnet_new_connection(struct osmo_fd *fd, unsigned int what)
connection->fd.fd = new_connection;
connection->fd.when = BSC_FD_READ;
connection->fd.cb = client_data;
- osmo_fd_register(&connection->fd);
+ rc = osmo_fd_register(&connection->fd);
+ if (rc < 0) {
+ talloc_free(connection);
+ return rc;
+ }
llist_add_tail(&connection->entry, &active_connections);
connection->vty = vty_create(new_connection, connection);
@@ -166,12 +179,23 @@ static int telnet_new_connection(struct osmo_fd *fd, unsigned int what)
/*! \brief callback from core VTY code about VTY related events */
void vty_event(enum event event, int sock, struct vty *vty)
{
+ struct vty_signal_data sig_data = { 0, };
struct telnet_connection *connection = vty->priv;
- struct osmo_fd *bfd = &connection->fd;
+ struct osmo_fd *bfd;
if (vty->type != VTY_TERM)
return;
+ sig_data.event = event;
+ sig_data.sock = sock;
+ sig_data.vty = vty;
+ osmo_signal_dispatch(SS_L_VTY, S_VTY_EVENT, &sig_data);
+
+ if (!connection)
+ return;
+
+ bfd = &connection->fd;
+
switch (event) {
case VTY_READ:
bfd->when |= BSC_FD_READ;
@@ -181,7 +205,6 @@ void vty_event(enum event event, int sock, struct vty *vty)
break;
case VTY_CLOSED:
/* vty layer is about to free() vty */
- connection->vty = NULL;
telnet_close_client(bfd);
break;
default:
diff --git a/src/shared/libosmocore/src/vty/utils.c b/src/shared/libosmocore/src/vty/utils.c
index e9c0d2d7..27c1a854 100644
--- a/src/shared/libosmocore/src/vty/utils.c
+++ b/src/shared/libosmocore/src/vty/utils.c
@@ -24,12 +24,15 @@
#include <inttypes.h>
#include <string.h>
#include <ctype.h>
+#include <limits.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/stat_item.h>
#include <osmocom/core/utils.h>
+#include <osmocom/core/statistics.h>
#include <osmocom/vty/vty.h>
@@ -39,6 +42,31 @@
* @{
*/
+struct vty_out_context {
+ struct vty *vty;
+ const char *prefix;
+ int max_level;
+};
+
+static int rate_ctr_handler(
+ struct rate_ctr_group *ctrg, struct rate_ctr *ctr,
+ const struct rate_ctr_desc *desc, void *vctx_)
+{
+ struct vty_out_context *vctx = vctx_;
+ struct vty *vty = vctx->vty;
+
+ vty_out(vty, " %s%s: %8" PRIu64 " "
+ "(%" PRIu64 "/s %" PRIu64 "/m %" PRIu64 "/h %" PRIu64 "/d)%s",
+ vctx->prefix, desc->description, ctr->current,
+ ctr->intv[RATE_CTR_INTV_SEC].rate,
+ ctr->intv[RATE_CTR_INTV_MIN].rate,
+ ctr->intv[RATE_CTR_INTV_HOUR].rate,
+ ctr->intv[RATE_CTR_INTV_DAY].rate,
+ VTY_NEWLINE);
+
+ return 0;
+}
+
/*! \brief print a rate counter group to given VTY
* \param[in] vty The VTY to which it should be printed
* \param[in] prefix Any additional log prefix ahead of each line
@@ -47,20 +75,116 @@
void vty_out_rate_ctr_group(struct vty *vty, const char *prefix,
struct rate_ctr_group *ctrg)
{
- unsigned int i;
+ struct vty_out_context vctx = {vty, prefix};
vty_out(vty, "%s%s:%s", prefix, ctrg->desc->group_description, VTY_NEWLINE);
- for (i = 0; i < ctrg->desc->num_ctr; i++) {
- struct rate_ctr *ctr = &ctrg->ctr[i];
- vty_out(vty, " %s%s: %8" PRIu64 " "
- "(%" PRIu64 "/s %" PRIu64 "/m %" PRIu64 "/h %" PRIu64 "/d)%s",
- prefix, ctrg->desc->ctr_desc[i].description, ctr->current,
- ctr->intv[RATE_CTR_INTV_SEC].rate,
- ctr->intv[RATE_CTR_INTV_MIN].rate,
- ctr->intv[RATE_CTR_INTV_HOUR].rate,
- ctr->intv[RATE_CTR_INTV_DAY].rate,
+
+ rate_ctr_for_each_counter(ctrg, rate_ctr_handler, &vctx);
+}
+
+static int osmo_stat_item_handler(
+ struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *vctx_)
+{
+ 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 : "";
+
+ vty_out(vty, " %s%s: %8" PRIi32 " %s%s",
+ vctx->prefix, item->desc->description,
+ osmo_stat_item_get_last(item),
+ unit, VTY_NEWLINE);
+
+ return 0;
+}
+
+/*! \brief print a stat item group to given VTY
+ * \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
+ */
+void vty_out_stat_item_group(struct vty *vty, const char *prefix,
+ struct osmo_stat_item_group *statg)
+{
+ struct vty_out_context vctx = {vty, prefix};
+
+ 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);
+}
+
+static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *vctx_)
+{
+ struct vty_out_context *vctx = vctx_;
+ struct vty *vty = vctx->vty;
+
+ 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);
+
+ osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, vctx);
+
+ return 0;
+}
+
+static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *vctx_)
+{
+ struct vty_out_context *vctx = vctx_;
+ struct vty *vty = vctx->vty;
+
+ 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);
+
+ rate_ctr_for_each_counter(ctrg, rate_ctr_handler, vctx);
+
+ return 0;
+}
+
+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;
+
+ if (!counter->description)
+ description = counter->name;
+
+ vty_out(vty, " %s%s: %8lu%s",
+ vctx->prefix, description,
+ osmo_counter_get(counter), VTY_NEWLINE);
+
+ return 0;
+}
+
+void vty_out_statistics_partial(struct vty *vty, const char *prefix,
+ int max_level)
+{
+ struct vty_out_context vctx = {vty, prefix, max_level};
+
+ vty_out(vty, "%sUngrouped counters:%s", prefix, VTY_NEWLINE);
+ osmo_counters_for_each(handle_counter, &vctx);
+ rate_ctr_for_each_group(rate_ctr_group_handler, &vctx);
+ osmo_stat_item_for_each_group(osmo_stat_item_group_handler, &vctx);
+}
+
+void vty_out_statistics_full(struct vty *vty, const char *prefix)
+{
+ vty_out_statistics_partial(vty, prefix, INT_MAX);
}
/*! \brief Generate a VTY command string from value_string */
@@ -69,19 +193,20 @@ char *vty_cmd_string_from_valstr(void *ctx, const struct value_string *vals,
const char *end, int do_lower)
{
int len = 0, offset = 0, ret, rem;
- int size = strlen(prefix);
+ int size = strlen(prefix) + strlen(end);
+ int sep_len = strlen(sep);
const struct value_string *vs;
char *str;
for (vs = vals; vs->value || vs->str; vs++)
- size += strlen(vs->str) + 1;
+ size += strlen(vs->str) + sep_len;
rem = size;
str = talloc_zero_size(ctx, size);
if (!str)
return NULL;
- ret = snprintf(str + offset, rem, prefix);
+ ret = snprintf(str + offset, rem, "%s", prefix);
if (ret < 0)
goto err;
OSMO_SNPRINTF_RET(ret, rem, offset, len);
@@ -102,10 +227,10 @@ char *vty_cmd_string_from_valstr(void *ctx, const struct value_string *vals,
OSMO_SNPRINTF_RET(ret, rem, offset, len);
}
}
- offset--; /* to remove the trailing | */
- rem++;
+ offset -= sep_len; /* to remove the trailing sep */
+ rem += sep_len;
- ret = snprintf(str + offset, rem, end);
+ ret = snprintf(str + offset, rem, "%s", end);
if (ret < 0)
goto err;
OSMO_SNPRINTF_RET(ret, rem, offset, len);
diff --git a/src/shared/libosmocore/src/vty/vector.c b/src/shared/libosmocore/src/vty/vector.c
index 4012f24b..c5a99af3 100644
--- a/src/shared/libosmocore/src/vty/vector.c
+++ b/src/shared/libosmocore/src/vty/vector.c
@@ -15,8 +15,8 @@
*
* 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., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
@@ -25,7 +25,7 @@
#include <osmocom/vty/vector.h>
#include <osmocom/vty/vty.h>
#include <osmocom/core/talloc.h>
-#include <memory.h>
+#include <string.h>
void *tall_vty_vec_ctx;
diff --git a/src/shared/libosmocore/src/vty/vty.c b/src/shared/libosmocore/src/vty/vty.c
index 696766a9..88ed9374 100644
--- a/src/shared/libosmocore/src/vty/vty.c
+++ b/src/shared/libosmocore/src/vty/vty.c
@@ -75,6 +75,12 @@ vector Vvty_serv_thread;
char *vty_cwd = NULL;
+/* IP address passed to the 'line vty'/'bind' command.
+ * Setting the default as vty_bind_addr = "127.0.0.1" doesn't allow freeing, so
+ * use NULL and VTY_BIND_ADDR_DEFAULT instead. */
+static const char *vty_bind_addr = NULL;
+#define VTY_BIND_ADDR_DEFAULT "127.0.0.1"
+
/* Configure lock. */
static int vty_config;
@@ -276,7 +282,7 @@ int vty_out(struct vty *vty, const char *format, ...)
p = buf;
/* Pointer p must point out buffer. */
- buffer_put(vty->obuf, (u_char *) p, len);
+ buffer_put(vty->obuf, (unsigned char *) p, len);
/* If p is not different with buf, it is allocated buffer. */
if (p != buf)
@@ -623,11 +629,11 @@ vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
if (vty->sb_len != TELNET_NAWS_SB_LEN)
vty_out(vty,"RFC 1073 violation detected: telnet NAWS option "
"should send %d characters, but we received %lu",
- TELNET_NAWS_SB_LEN, (u_long)vty->sb_len);
+ TELNET_NAWS_SB_LEN, (unsigned long)vty->sb_len);
else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
vty_out(vty, "Bug detected: sizeof(vty->sb_buf) %lu < %d, "
"too small to handle the telnet NAWS option",
- (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
+ (unsigned long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
else
{
vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
@@ -982,7 +988,7 @@ static void vty_complete_command(struct vty *vty)
/* In case of 'help \t'. */
if (isspace((int)vty->buf[vty->length - 1]))
- vector_set(vline, '\0');
+ vector_set(vline, NULL);
matched = cmd_complete_command(vline, vty, &ret);
@@ -1092,9 +1098,9 @@ static void vty_describe_command(struct vty *vty)
/* In case of '> ?'. */
if (vline == NULL) {
vline = vector_init(1);
- vector_set(vline, '\0');
+ vector_set(vline, NULL);
} else if (isspace((int)vty->buf[vty->length - 1]))
- vector_set(vline, '\0');
+ vector_set(vline, NULL);
describe = cmd_describe_command(vline, vty, &ret);
@@ -1432,9 +1438,10 @@ int vty_read(struct vty *vty)
}
/* Check status. */
- if (vty->status == VTY_CLOSE)
+ if (vty->status == VTY_CLOSE) {
vty_close(vty);
- else {
+ return -EBADF;
+ } else {
vty_event(VTY_WRITE, vty_sock, vty);
vty_event(VTY_READ, vty_sock, vty);
}
@@ -1584,6 +1591,23 @@ DEFUN(no_vty_login,
return CMD_SUCCESS;
}
+/* vty bind */
+DEFUN(vty_bind, vty_bind_cmd, "bind A.B.C.D",
+ "Accept VTY telnet connections on local interface\n"
+ "Local interface IP address (default: " VTY_BIND_ADDR_DEFAULT ")\n")
+{
+ talloc_free((void*)vty_bind_addr);
+ vty_bind_addr = talloc_strdup(tall_vty_ctx, argv[0]);
+ return CMD_SUCCESS;
+}
+
+const char *vty_get_bind_addr(void)
+{
+ if (!vty_bind_addr)
+ return VTY_BIND_ADDR_DEFAULT;
+ return vty_bind_addr;
+}
+
DEFUN(service_advanced_vty,
service_advanced_vty_cmd,
"service advanced-vty",
@@ -1653,6 +1677,10 @@ static int vty_config_write(struct vty *vty)
if (!password_check)
vty_out(vty, " no login%s", VTY_NEWLINE);
+ /* bind */
+ if (vty_bind_addr && (strcmp(vty_bind_addr, VTY_BIND_ADDR_DEFAULT) != 0))
+ vty_out(vty, " bind %s%s", vty_bind_addr, VTY_NEWLINE);
+
vty_out(vty, "!%s", VTY_NEWLINE);
return CMD_SUCCESS;
@@ -1753,9 +1781,10 @@ void vty_init(struct vty_app_info *app_info)
install_element(ENABLE_NODE, &terminal_monitor_cmd);
install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
- install_default(VTY_NODE);
+ vty_install_default(VTY_NODE);
install_element(VTY_NODE, &vty_login_cmd);
install_element(VTY_NODE, &no_vty_login_cmd);
+ install_element(VTY_NODE, &vty_bind_cmd);
}
/*! \brief Read the configuration file using the VTY code
diff --git a/src/shared/libosmocore/src/write_queue.c b/src/shared/libosmocore/src/write_queue.c
index cef40f83..c7a43205 100644
--- a/src/shared/libosmocore/src/write_queue.c
+++ b/src/shared/libosmocore/src/write_queue.c
@@ -1,6 +1,6 @@
/* Generic write queue implementation */
/*
- * (C) 2010 by Holger Hans Peter Freyther
+ * (C) 2010-2016 by Holger Hans Peter Freyther
* (C) 2010 by On-Waves
*
* All Rights Reserved
@@ -21,7 +21,9 @@
*
*/
+#include <errno.h>
#include <osmocom/core/write_queue.h>
+#include <osmocom/core/logging.h>
/*! \addtogroup write_queue
* @{
@@ -32,6 +34,7 @@
/*! \brief Select loop function for write queue handling
* \param[in] fd osmocom file descriptor
* \param[in] what bit-mask of events that have happened
+ * \returns 0 on success; negative on error
*
* This function is provided so that it can be registered with the
* select loop abstraction code (\ref osmo_fd::cb).
@@ -39,14 +42,21 @@
int osmo_wqueue_bfd_cb(struct osmo_fd *fd, unsigned int what)
{
struct osmo_wqueue *queue;
+ int rc;
queue = container_of(fd, struct osmo_wqueue, bfd);
- if (what & BSC_FD_READ)
- queue->read_cb(fd);
+ if (what & BSC_FD_READ) {
+ rc = queue->read_cb(fd);
+ if (rc == -EBADF)
+ goto err_badfd;
+ }
- if (what & BSC_FD_EXCEPT)
- queue->except_cb(fd);
+ if (what & BSC_FD_EXCEPT) {
+ rc = queue->except_cb(fd);
+ if (rc == -EBADF)
+ goto err_badfd;
+ }
if (what & BSC_FD_WRITE) {
struct msgb *msg;
@@ -58,14 +68,19 @@ int osmo_wqueue_bfd_cb(struct osmo_fd *fd, unsigned int what)
--queue->current_length;
msg = msgb_dequeue(&queue->msg_queue);
- queue->write_cb(fd, msg);
+ rc = queue->write_cb(fd, msg);
msgb_free(msg);
+ if (rc == -EBADF)
+ goto err_badfd;
+
if (!llist_empty(&queue->msg_queue))
fd->when |= BSC_FD_WRITE;
}
}
+err_badfd:
+ /* Return value is not checked in osmo_select_main() */
return 0;
}
@@ -79,6 +94,7 @@ void osmo_wqueue_init(struct osmo_wqueue *queue, int max_length)
queue->current_length = 0;
queue->read_cb = NULL;
queue->write_cb = NULL;
+ queue->except_cb = NULL;
queue->bfd.cb = osmo_wqueue_bfd_cb;
INIT_LLIST_HEAD(&queue->msg_queue);
}
@@ -86,11 +102,15 @@ void osmo_wqueue_init(struct osmo_wqueue *queue, int max_length)
/*! \brief 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
*/
int osmo_wqueue_enqueue(struct osmo_wqueue *queue, struct msgb *data)
{
-// if (queue->current_length + 1 >= queue->max_length)
-// LOGP(DMSC, LOGL_ERROR, "The queue is full. Dropping not yet implemented.\n");
+ if (queue->current_length >= queue->max_length) {
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "wqueue(%p) is full. Rejecting msgb\n", queue);
+ return -ENOSPC;
+ }
++queue->current_length;
msgb_enqueue(&queue->msg_queue, data);
diff --git a/src/shared/libosmocore/tests/Makefile.am b/src/shared/libosmocore/tests/Makefile.am
index aaad0c84..b9eb8f23 100644
--- a/src/shared/libosmocore/tests/Makefile.am
+++ b/src/shared/libosmocore/tests/Makefile.am
@@ -1,25 +1,61 @@
-INCLUDES = $(all_includes) -I$(top_srcdir)/include
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
+AM_CFLAGS = -Wall $(TALLOC_CFLAGS)
+AM_LDFLAGS = $(TALLOC_LIBS)
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 \
gsm0808/gsm0808_test gsm0408/gsm0408_test \
- gb/bssgp_fc_test logging/logging_test
+ gb/bssgp_fc_test gb/gprs_bssgp_test gb/gprs_ns_test \
+ gprs/gprs_test kasumi/kasumi_test gea/gea_test \
+ logging/logging_test fr/fr_test codec/codec_test \
+ loggingrb/loggingrb_test strrb/strrb_test \
+ vty/vty_test comp128/comp128_test utils/utils_test \
+ smscb/gsm0341_test stats/stats_test \
+ bitvec/bitvec_test msgb/msgb_test bits/bitcomp_test \
+ tlv/tlv_test gsup/gsup_test oap/oap_test fsm/fsm_test \
+ write_queue/wqueue_test
+
if ENABLE_MSGFILE
check_PROGRAMS += msgfile/msgfile_test
endif
+if ENABLE_PCSC
+check_PROGRAMS += sim/sim_test
+endif
+
+utils_utils_test_SOURCES = utils/utils_test.c
+utils_utils_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
+
+stats_stats_test_SOURCES = stats/stats_test.c
+stats_stats_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
+
a5_a5_test_SOURCES = a5/a5_test.c
-a5_a5_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
+a5_a5_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libgsmint.la
+
+kasumi_kasumi_test_SOURCES = kasumi/kasumi_test.c
+kasumi_kasumi_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libgsmint.la
+
+comp128_comp128_test_SOURCES = comp128/comp128_test.c
+comp128_comp128_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
auth_milenage_test_SOURCES = auth/milenage_test.c
auth_milenage_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
+gea_gea_test_SOURCES = gea/gea_test.c
+gea_gea_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
+
bits_bitrev_test_SOURCES = bits/bitrev_test.c
bits_bitrev_test_LDADD = $(top_builddir)/src/libosmocore.la
+bitvec_bitvec_test_SOURCES = bitvec/bitvec_test.c
+bitvec_bitvec_test_LDADD = $(top_builddir)/src/libosmocore.la
+
+bits_bitcomp_test_SOURCES = bits/bitcomp_test.c
+bits_bitcomp_test_LDADD = $(top_builddir)/src/libosmocore.la
+
conv_conv_test_SOURCES = conv/conv_test.c
-conv_conv_test_LDADD = $(top_builddir)/src/libosmocore.la
+conv_conv_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libgsmint.la
gsm0808_gsm0808_test_SOURCES = gsm0808/gsm0808_test.c
gsm0808_gsm0808_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
@@ -27,15 +63,24 @@ gsm0808_gsm0808_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/
gsm0408_gsm0408_test_SOURCES = gsm0408/gsm0408_test.c
gsm0408_gsm0408_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
+gprs_gprs_test_SOURCES = gprs/gprs_test.c
+gprs_gprs_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
+
lapd_lapd_test_SOURCES = lapd/lapd_test.c
lapd_lapd_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
+msgb_msgb_test_SOURCES = msgb/msgb_test.c
+msgb_msgb_test_LDADD = $(top_builddir)/src/libosmocore.la
+
msgfile_msgfile_test_SOURCES = msgfile/msgfile_test.c
msgfile_msgfile_test_LDADD = $(top_builddir)/src/libosmocore.la
smscb_smscb_test_SOURCES = smscb/smscb_test.c
smscb_smscb_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
+smscb_gsm0341_test_SOURCES = smscb/gsm0341_test.c
+smscb_gsm0341_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
+
sms_sms_test_SOURCES = sms/sms_test.c
sms_sms_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
@@ -46,11 +91,55 @@ ussd_ussd_test_SOURCES = ussd/ussd_test.c
ussd_ussd_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
gb_bssgp_fc_test_SOURCES = gb/bssgp_fc_test.c
-gb_bssgp_fc_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gb/libosmogb.la
+gb_bssgp_fc_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gb/libosmogb.la \
+ $(top_builddir)/src/gsm/libosmogsm.la
+
+gb_gprs_bssgp_test_SOURCES = gb/gprs_bssgp_test.c
+gb_gprs_bssgp_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gb/libosmogb.la $(LIBRARY_DL) \
+ $(top_builddir)/src/gsm/libosmogsm.la
+
+gb_gprs_ns_test_SOURCES = gb/gprs_ns_test.c
+gb_gprs_ns_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gb/libosmogb.la $(LIBRARY_DL) \
+ $(top_builddir)/src/gsm/libosmogsm.la
logging_logging_test_SOURCES = logging/logging_test.c
logging_logging_test_LDADD = $(top_builddir)/src/libosmocore.la
+fr_fr_test_SOURCES = fr/fr_test.c
+fr_fr_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gb/libosmogb.la $(LIBRARY_DL) \
+ $(top_builddir)/src/gsm/libosmogsm.la
+
+codec_codec_test_SOURCES = codec/codec_test.c
+codec_codec_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/codec/libosmocodec.la
+
+loggingrb_loggingrb_test_SOURCES = loggingrb/loggingrb_test.c
+loggingrb_loggingrb_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/vty/libosmovty.la
+
+strrb_strrb_test_SOURCES = strrb/strrb_test.c
+strrb_strrb_test_LDADD = $(top_builddir)/src/libosmocore.la
+
+vty_vty_test_SOURCES = vty/vty_test.c
+vty_vty_test_LDADD = $(top_builddir)/src/vty/libosmovty.la $(top_builddir)/src/libosmocore.la
+
+sim_sim_test_SOURCES = sim/sim_test.c
+sim_sim_test_LDADD = $(top_builddir)/src/sim/libosmosim.la $(top_builddir)/src/libosmocore.la \
+ $(top_builddir)/src/gsm/libosmogsm.la
+
+tlv_tlv_test_SOURCES = tlv/tlv_test.c
+tlv_tlv_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(top_builddir)/src/libosmocore.la
+
+gsup_gsup_test_SOURCES = gsup/gsup_test.c
+gsup_gsup_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(top_builddir)/src/libosmocore.la
+
+oap_oap_test_SOURCES = oap/oap_test.c
+oap_oap_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(top_builddir)/src/libosmocore.la
+
+fsm_fsm_test_SOURCES = fsm/fsm_test.c
+fsm_fsm_test_LDADD = $(top_builddir)/src/libosmocore.la
+
+write_queue_wqueue_test_SOURCES = write_queue/wqueue_test.c
+write_queue_wqueue_test_LDADD = $(top_builddir)/src/libosmocore.la
+
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
:;{ \
@@ -76,8 +165,20 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \
lapd/lapd_test.ok gsm0408/gsm0408_test.ok \
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 \
+ gprs/gprs_test.ok kasumi/kasumi_test.ok codec/codec_test.ok \
msgfile/msgfile_test.ok msgfile/msgconfig.cfg \
- logging/logging_test.ok logging/logging_test.err
+ logging/logging_test.ok logging/logging_test.err \
+ fr/fr_test.ok loggingrb/logging_test.ok \
+ loggingrb/logging_test.err strrb/strrb_test.ok \
+ vty/vty_test.ok comp128/comp128_test.ok \
+ utils/utils_test.ok stats/stats_test.ok \
+ bitvec/bitvec_test.ok msgb/msgb_test.ok bits/bitcomp_test.ok \
+ sim/sim_test.ok tlv/tlv_test.ok gsup/gsup_test.ok \
+ oap/oap_test.ok fsm/fsm_test.ok fsm/fsm_test.err \
+ write_queue/wqueue_test.ok
+
+DISTCLEANFILES = atconfig atlocal
TESTSUITE = $(srcdir)/testsuite
@@ -91,7 +192,6 @@ installcheck-local: atconfig $(TESTSUITE)
clean-local:
test ! -f '$(TESTSUITE)' || \
$(SHELL) '$(TESTSUITE)' --clean
- $(RM) -f atconfig
AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te
AUTOTEST = $(AUTOM4TE) --language=autotest
diff --git a/src/shared/libosmocore/tests/a5/a5_test.c b/src/shared/libosmocore/tests/a5/a5_test.c
index 14436f19..6d7cc3c4 100644
--- a/src/shared/libosmocore/tests/a5/a5_test.c
+++ b/src/shared/libosmocore/tests/a5/a5_test.c
@@ -2,11 +2,16 @@
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
+#include <stdbool.h>
#include <osmocom/core/bits.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/a5.h>
+// make compiler happy
+void _a5_3(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct);
+void _a5_4(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct);
+
static const uint8_t key[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef };
static const uint32_t fn = 123456;
static const uint8_t dl[] = {
@@ -36,24 +41,48 @@ static const uint8_t ul[] = {
0x80, 0xba, 0xab, 0xc0, 0x59, 0x26, 0x40,
};
-static const char *
-binstr(ubit_t *d, int n)
+static inline bool print_a5(int n, int k, const char * dir, const ubit_t * out, const char * block)
{
- static char str[256];
- int i;
+ uint8_t len = 114 / 8 + 1, buf[len], res[len];
+ printf("A5/%d - %s: %s => ", n, dir, osmo_ubit_dump(out, 114));
+ osmo_hexparse(block, res, len);
+ osmo_ubit2pbit(buf, out, 114);
+ if (0 != memcmp(buf, res, len)) {
+ printf("FAIL\nGOT: [%d] %s\nEXP: [%d] %s\n", k, osmo_hexdump_nospc(buf, len), k, osmo_hexdump_nospc(res, len));
+ return false;
+ }
+ printf("OK\n");
+ return true;
+}
- for (i=0; i<n; i++)
- str[i] = d[i] ? '1' : '0';
+static inline bool test_a53(const char * kc, uint32_t count, const char * block1, const char * block2)
+{
+ ubit_t dlout[114], ulout[114];
+ uint8_t key[8];
- str[i] = '\0';
+ osmo_hexparse(kc, key, 8);
+ _a5_3(key, count, dlout, NULL, false);
+ _a5_3(key, count, NULL, ulout, false);
- return str;
+ 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)
+{
+ ubit_t dlout[114], ulout[114];
+ uint8_t key[16];
+
+ osmo_hexparse(kc, key, 16);
+ _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);
+}
+
+
int main(int argc, char **argv)
{
- ubit_t exp[114];
- ubit_t out[114];
+ ubit_t exp[114], out[114];
int n, i;
for (n=0; n<3; n++) {
@@ -66,13 +95,13 @@ int main(int argc, char **argv)
osmo_a5(n, key, fn, out, NULL);
- printf("A5/%d - DL: %s", n, binstr(out, 114));
+ printf("A5/%d - DL: %s", n, osmo_ubit_dump(out, 114));
if (!memcmp(exp, out, 114))
printf(" => OK\n");
else {
printf(" => BAD\n");
- printf(" Expected: %s", binstr(out, 114));
+ printf(" Expected: %s", osmo_ubit_dump(out, 114));
fprintf(stderr, "[!] A5/%d DL failed", n);
exit(1);
}
@@ -82,17 +111,31 @@ int main(int argc, char **argv)
osmo_a5(n, key, fn, NULL, out);
- printf("A5/%d - UL: %s", n, binstr(out, 114));
+ printf("A5/%d - UL: %s", n, osmo_ubit_dump(out, 114));
if (!memcmp(exp, out, 114))
printf(" => OK\n");
else {
printf(" => BAD\n");
- printf(" Expected: %s", binstr(out, 114));
+ printf(" Expected: %s", osmo_ubit_dump(out, 114));
fprintf(stderr, "[!] A5/%d UL failed", n);
exit(1);
}
}
+// test vectors from 3GPP TS 55.217 and TS 55.218
+ test_a53("2BD6459F82C5BC00", 0x24F20F, "889EEAAF9ED1BA1ABBD8436232E440", "5CA3406AA244CF69CF047AADA2DF40");
+ test_a53("952C49104881FF48", 0x061272, "FB4D5FBCEE13A33389285686E9A5C0", "25090378E0540457C57E367662E440");
+ test_a53("EFA8B2229E720C2A", 0x33FD3F, "0E4015755A336469C3DD8680E30340", "6F10669E2B4E18B042431A28E47F80");
+ test_a53("952C49104881FF48", 0x061527, "AB7DB38A573A325DAA76E4CB800A40", "4C4B594FEA9D00FE8978B7B7BC1080");
+ test_a53("3451F23A43BD2C87", 0x0E418C, "75F7C4C51560905DFBA05E46FB54C0", "192C95353CDF979E054186DF15BF00");
+ test_a53("CAA2639BE82435CF", 0x2FF229, "301437E4D4D6565D4904C631606EC0", "F0A3B8795E264D3E1A82F684353DC0");
+ test_a53("7AE67E87400B9FA6", 0x2F24E5, "F794290FEF643D2EA348A7796A2100", "CB6FA6C6B8A705AF9FEFE975818500");
+ test_a53("58AF69935540698B", 0x05446B, "749CA4E6B691E5A598C461D5FE4740", "31C9E444CD04677ADAA8A082ADBC40");
+ test_a53("017F81E5F236FE62", 0x156B26, "2A6976761E60CC4E8F9F52160276C0", "A544D8475F2C78C35614128F1179C0");
+ test_a53("1ACA8B448B767B39", 0x0BC3B5, "A4F70DC5A2C9707F5FA1C60EB10640", "7780B597B328C1400B5C74823E8500");
+ test_a54("3D43C388C9581E337FF1F97EB5C1F85E", 0x35D2CF, "A2FE3034B6B22CC4E33C7090BEC340", "170D7497432FF897B91BE8AECBA880");
+ test_a54("A4496A64DF4F399F3B4506814A3E07A1", 0x212777, "89CDEE360DF9110281BCF57755A040", "33822C0C779598C9CBFC49183AF7C0");
+
return 0;
}
diff --git a/src/shared/libosmocore/tests/a5/a5_test.ok b/src/shared/libosmocore/tests/a5/a5_test.ok
index 4497e14a..cefcdb6f 100644
--- a/src/shared/libosmocore/tests/a5/a5_test.ok
+++ b/src/shared/libosmocore/tests/a5/a5_test.ok
@@ -4,3 +4,27 @@ A5/1 - DL: 110010111010001001010101011101100001011101011101001110110001110001111
A5/1 - UL: 110110010000001101011110000011110010101011101100000100111001101000000101110101001010100001111011101100010110010010 => OK
A5/2 - DL: 010001011001110010001000110000111000001010110111111111111011001110011000110100101111100101101110000011110001010010 => OK
A5/2 - UL: 111100000011101010101100110111101110001101011011010111100110010110000000101110101010101111000000010110010010011001 => OK
+A5/3 - DL: 100010001001111011101010101011111001111011010001101110100001101010111011110110000100001101100010001100101110010001 => OK
+A5/3 - UL: 010111001010001101000000011010101010001001000100110011110110100111001111000001000111101010101101101000101101111101 => OK
+A5/3 - DL: 111110110100110101011111101111001110111000010011101000110011001110001001001010000101011010000110111010011010010111 => OK
+A5/3 - UL: 001001010000100100000011011110001110000001010100000001000101011111000101011111100011011001110110011000101110010001 => OK
+A5/3 - DL: 000011100100000000010101011101010101101000110011011001000110100111000011110111011000011010000000111000110000001101 => OK
+A5/3 - UL: 011011110001000001100110100111100010101101001110000110001011000001000010010000110001101000101000111001000111111110 => OK
+A5/3 - DL: 101010110111110110110011100010100101011100111010001100100101110110101010011101101110010011001011100000000000101001 => OK
+A5/3 - UL: 010011000100101101011001010011111110101010011101000000001111111010001001011110001011011110110111101111000001000010 => OK
+A5/3 - DL: 011101011111011111000100110001010001010101100000100100000101110111111011101000000101111001000110111110110101010011 => OK
+A5/3 - UL: 000110010010110010010101001101010011110011011111100101111001111000000101010000011000011011011111000101011011111100 => OK
+A5/3 - DL: 001100000001010000110111111001001101010011010110010101100101110101001001000001001100011000110001011000000110111011 => OK
+A5/3 - UL: 111100001010001110111000011110010101111000100110010011010011111000011010100000101111011010000100001101010011110111 => OK
+A5/3 - DL: 111101111001010000101001000011111110111101100100001111010010111010100011010010001010011101111001011010100010000100 => OK
+A5/3 - UL: 110010110110111110100110110001101011100010100111000001011010111110011111111011111110100101110101100000011000010100 => OK
+A5/3 - DL: 011101001001110010100100111001101011011010010001111001011010010110011000110001000110000111010101111111100100011101 => OK
+A5/3 - UL: 001100011100100111100100010001001100110100000100011001110111101011011010101010001010000010000010101011011011110001 => OK
+A5/3 - DL: 001010100110100101110110011101100001111001100000110011000100111010001111100111110101001000010110000000100111011011 => OK
+A5/3 - UL: 101001010100010011011000010001110101111100101100011110001100001101010110000101000001001010001111000100010111100111 => OK
+A5/3 - DL: 101001001111011100001101110001011010001011001001011100000111111101011111101000011100011000001110101100010000011001 => OK
+A5/3 - UL: 011101111000000010110101100101111011001100101000110000010100000000001011010111000111010010000010001111101000010100 => OK
+A5/4 - DL: 101000101111111000110000001101001011011010110010001011001100010011100011001111000111000010010000101111101100001101 => OK
+A5/4 - UL: 000101110000110101110100100101110100001100101111111110001001011110111001000110111110100010101110110010111010100010 => OK
+A5/4 - DL: 100010011100110111101110001101100000110111111001000100010000001010000001101111001111010101110111010101011010000001 => OK
+A5/4 - UL: 001100111000001000101100000011000111011110010101100110001100100111001011111111000100100100011000001110101111011111 => OK
diff --git a/src/shared/libosmocore/tests/atlocal.in b/src/shared/libosmocore/tests/atlocal.in
new file mode 100644
index 00000000..cd275323
--- /dev/null
+++ b/src/shared/libosmocore/tests/atlocal.in
@@ -0,0 +1 @@
+enable_sim_test='@ENABLE_PCSC@'
diff --git a/src/shared/libosmocore/tests/auth/milenage_test.c b/src/shared/libosmocore/tests/auth/milenage_test.c
index 7c996f02..473be92a 100644
--- a/src/shared/libosmocore/tests/auth/milenage_test.c
+++ b/src/shared/libosmocore/tests/auth/milenage_test.c
@@ -7,6 +7,8 @@
#include <osmocom/crypt/auth.h>
#include <osmocom/core/utils.h>
+int milenage_opc_gen(uint8_t *opc, const uint8_t *k, const uint8_t *op);
+
static void dump_auth_vec(struct osmo_auth_vector *vec)
{
printf("RAND:\t%s\n", osmo_hexdump(vec->rand, sizeof(vec->rand)));
@@ -48,6 +50,9 @@ static int opc_test(const struct osmo_sub_auth_data *aud)
const uint8_t op[16] = { 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0 };
#endif
+ printf("MILENAGE supported: %d\n",
+ osmo_auth_supported(osmo_auth_alg_parse("MILENAGE")));
+
rc = milenage_opc_gen(opc, aud->u.umts.k, op);
printf("OP:\t%s\n", osmo_hexdump(op, sizeof(op)));
@@ -88,7 +93,7 @@ int main(int argc, char **argv)
if (rc < 0) {
printf("AUTS failed\n");
} else {
- printf("AUTS success: SEQ.MS = %lu\n", test_aud.u.umts.sqn);
+ printf("AUTS success: SEQ.MS = %llu\n", (unsigned long long)test_aud.u.umts.sqn);
}
opc_test(&test_aud);
diff --git a/src/shared/libosmocore/tests/auth/milenage_test.ok b/src/shared/libosmocore/tests/auth/milenage_test.ok
index 00ffc222..49146a55 100644
--- a/src/shared/libosmocore/tests/auth/milenage_test.ok
+++ b/src/shared/libosmocore/tests/auth/milenage_test.ok
@@ -6,5 +6,6 @@ RES: e9 fc 88 cc c8 a3 53 81
SRES: 21 5f db 4d
Kc: 6d e8 16 a7 59 a4 29 12
AUTS success: SEQ.MS = 33
+MILENAGE supported: 1
OP: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
OPC: c6 a1 3b 37 87 8f 5b 82 6f 4f 81 62 a1 c8 d8 79
diff --git a/src/shared/libosmocore/tests/bits/bitcomp_test.c b/src/shared/libosmocore/tests/bits/bitcomp_test.c
new file mode 100644
index 00000000..587dd72b
--- /dev/null
+++ b/src/shared/libosmocore/tests/bits/bitcomp_test.c
@@ -0,0 +1,54 @@
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <time.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/core/bitcomp.h>
+
+static char lol[1024]; // for pretty-printing
+
+int main(int argc, char **argv)
+{
+ srand(time(NULL));
+
+ struct bitvec bv, out;
+ uint8_t i = 20, test[i], data[i];
+
+ bv.data_len = i;
+ bv.data = test;
+ out.data_len = i;
+ out.data = data;
+ bitvec_zero(&bv);
+ bitvec_zero(&out);
+
+ printf("\nrunning static tests...\n");
+
+ printf("\nTEST1:\n 00110111 01000111 10000001 1111\n");
+ bitvec_zero(&bv);
+ bitvec_set_uint(&bv, 0x374781F, 28); bitvec_to_string_r(&bv, lol); printf("%s", lol);
+
+ printf("\nEncoded:\n%d", osmo_t4_encode(&bv)); bitvec_to_string_r(&bv, lol); printf("%s", lol);
+ printf(" [%d]\nExpected:\n0 11011110 10001000 01110101 01100101 100 [35]\n", bv.cur_bit);
+
+ bitvec_zero(&bv);
+ bitvec_set_uint(&bv, 0xDE887565, 32);
+ bitvec_set_uint(&bv, 4, 3);
+ bitvec_to_string_r(&bv, lol);
+ printf(" %s [%d]\n", lol, bv.cur_bit);
+
+ printf("\nTEST2:\n 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 00000000 00\n");
+ bitvec_zero(&bv);
+ bitvec_set_uint(&bv, 0xFFFFFFFF, 32);
+ bitvec_set_uint(&bv, 0xFFFFFFFF, 32);
+ bitvec_set_uint(&bv, 0xFFFFFC00, 26); bitvec_to_string_r(&bv, lol); printf("%s", lol);
+ printf("\nEncoded:\n%d", osmo_t4_encode(&bv)); bitvec_to_string_r(&bv, lol); printf("%s", lol);
+ printf(" [%d]\nExpected:\n1 11011101 01000001 00 [18]\n", bv.cur_bit);
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/tests/bits/bitcomp_test.ok b/src/shared/libosmocore/tests/bits/bitcomp_test.ok
new file mode 100644
index 00000000..d2ac16a2
--- /dev/null
+++ b/src/shared/libosmocore/tests/bits/bitcomp_test.ok
@@ -0,0 +1,19 @@
+
+running static tests...
+
+TEST1:
+ 00110111 01000111 10000001 1111
+ 00110111 01000111 10000001 1111
+Encoded:
+-1 00110111 01000111 10000001 1111 [28]
+Expected:
+0 11011110 10001000 01110101 01100101 100 [35]
+ 11011110 10001000 01110101 01100101 100 [35]
+
+TEST2:
+ 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 00000000 00
+ 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 00000000 00
+Encoded:
+1 11011101 01000001 00 [18]
+Expected:
+1 11011101 01000001 00 [18]
diff --git a/src/shared/libosmocore/tests/bits/bitrev_test.c b/src/shared/libosmocore/tests/bits/bitrev_test.c
index 5eca990a..ed3939a3 100644
--- a/src/shared/libosmocore/tests/bits/bitrev_test.c
+++ b/src/shared/libosmocore/tests/bits/bitrev_test.c
@@ -1,20 +1,235 @@
-
+#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
+#include <time.h>
+#include <stdbool.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/bits.h>
static const uint8_t input[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
static const uint8_t exp_out[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
+static char s[18];
+
+enum END {LE, BE};
+
+static inline const char *
+end2str(enum END e)
+{
+ if (e == LE) return "LE";
+ return "BE";
+}
+
+
+/* convenience wrappers */
+
+static inline uint64_t
+load64(enum END e, const uint8_t *buf, unsigned nbytes)
+{
+ return (e == BE) ? osmo_load64be_ext(buf, nbytes) : osmo_load64le_ext(buf, nbytes);
+}
+
+static inline uint32_t
+load32(enum END e, const uint8_t *buf, unsigned nbytes)
+{
+ return (e == BE) ? osmo_load32be_ext(buf, nbytes) : osmo_load32le_ext(buf, nbytes);
+}
+
+static inline uint16_t
+load16(enum END e, const uint8_t *buf)
+{
+ return (e == BE) ? osmo_load16be(buf) : osmo_load16le(buf);
+}
+
+static inline void
+store64(enum END e, uint64_t t, uint8_t *buf, unsigned nbytes)
+{
+ (e == BE) ? osmo_store64be_ext(t, buf, nbytes) : osmo_store64le_ext(t, buf, nbytes);
+}
+
+static inline void
+store32(enum END e, uint64_t t, uint8_t *buf, unsigned nbytes)
+{
+ (e == BE) ? osmo_store32be_ext(t, buf, nbytes) : osmo_store32le_ext(t, buf, nbytes);
+}
+
+static inline void
+store16(enum END e, uint64_t t, uint8_t *buf)
+{
+ (e == BE) ? osmo_store16be(t, buf) : osmo_store16le(t, buf);
+}
+
+
+/* helper functions */
+
+static inline bool
+printcheck(bool chk, unsigned nbytes, enum END e, bool b)
+{
+ if (!chk) {
+ printf("%u %s FAILED", nbytes * 8, end2str(e));
+ return true;
+ }
+ printf("%u %s OK", nbytes * 8, end2str(e));
+ return b;
+}
+
+static inline bool
+dumpcheck(const char *dump, const char *s, unsigned nbytes, bool chk, enum END e, bool b)
+{
+ bool x = printcheck(chk, nbytes, e, b);
+ if (!dump) return x;
+
+ int m = memcmp(s, dump, nbytes);
+ if (0 == m) {
+ printf(", storage OK");
+ return x;
+ }
+ printf(", [%d]", m);
+
+ return true;
+}
+
+
+/* printcheckXX(): load/store 'test' and check against 'expected' value, compare to 'dump' buffer if given and print if necessary */
+
+static inline void
+printcheck64(enum END e, unsigned nbytes, uint64_t test, uint64_t expected, const char *dump, bool print)
+{
+ uint8_t buf[nbytes];
+
+ store64(e, test, buf, nbytes);
+
+ char *s = osmo_hexdump_nospc(buf, nbytes);
+ uint64_t result = load64(e, buf, nbytes);
+
+ print = dumpcheck(dump, s, nbytes, result == expected, e, print);
+
+ if (print)
+ printf(": buffer %s known buffer %s loaded %.16" PRIx64 " expected %.16" PRIx64, s, dump, result, expected);
+ printf("\n");
+}
+
+static inline void
+printcheck32(enum END e, unsigned nbytes, uint32_t test, uint32_t expected, const char *dump, bool print)
+{
+ uint8_t buf[nbytes];
+
+ store32(e, test, buf, nbytes);
+
+ char *s = osmo_hexdump_nospc(buf, nbytes);
+ uint32_t result = load32(e, buf, nbytes);
+
+ print = dumpcheck(dump, s, nbytes, result == expected, e, print);
+
+ if (print)
+ printf(": buffer %s known buffer %s loaded %.8" PRIx32 " expected %.8" PRIx32, s, dump, result, expected);
+ printf("\n");
+}
+
+static inline void
+printcheck16(enum END e, uint32_t test, uint32_t expected, const char *dump, bool print)
+{
+ uint8_t buf[2];
+
+ store16(e, test, buf);
+
+ char *s = osmo_hexdump_nospc(buf, 2);
+ uint16_t result = load16(e, buf);
+
+ print = dumpcheck(dump, s, 2, result == expected, e, print);
+
+ if (print)
+ printf(": buffer %s known buffer %s loaded %.4" PRIx16 " expected %.4" PRIx16, s, dump, result, expected);
+ printf("\n");
+}
+
+
+/* compute expected value - zero excessive bytes */
+
+static inline uint64_t
+exp64(enum END e, unsigned nbytes, uint64_t value)
+{
+ uint8_t adj = 64 - nbytes * 8;
+ uint64_t v = value << adj;
+ return (e == LE) ? v >> adj : v;
+}
+
+static inline uint32_t
+exp32(enum END e, unsigned nbytes, uint32_t value)
+{
+ uint8_t adj = 32 - nbytes * 8;
+ uint32_t v = value << adj;
+ return (e == LE) ? v >> adj : v;
+}
+
+
+/* run actual tests - if 'test' is 0 than generate random test value internally */
+
+static inline void
+check64(uint64_t test, uint64_t expected, unsigned nbytes, enum END e)
+{
+ bool print = true;
+ if (0 == test && 0 == expected) {
+ test = ((uint64_t)rand() << 32) + rand();
+ expected = exp64(e, nbytes, test);
+ print = false;
+ }
+ snprintf(s, 17, "%.16" PRIx64, expected);
+ printcheck64(e, nbytes, test, expected, (BE == e) ? s : NULL, print);
+}
+
+static inline void
+check32(uint32_t test, uint32_t expected, unsigned nbytes, enum END e)
+{
+ bool print = true;
+ if (0 == test && 0 == expected) {
+ test = rand();
+ expected = exp32(e, nbytes, test);
+ print = false;
+ }
+ snprintf(s, 17, "%.8" PRIx32, expected);
+ printcheck32(e, nbytes, test, expected, (BE == e) ? s : NULL, print);
+}
+
+static inline void
+check16(uint16_t test, enum END e)
+{
+ bool print = true;
+ if (0 == test) {
+ test = (uint16_t)rand();
+ print = false;
+ }
+ snprintf(s, 17, "%.4" PRIx16, test);
+ printcheck16(e, test, test, (BE == e) ? s : NULL, print);
+}
+
+static void sh_chk(const uint8_t *in, uint8_t len, unsigned int nib, bool r)
+{
+ uint8_t x[len];
+ int bytes = nib/2 + (nib & 1);
+ OSMO_ASSERT(len >= bytes);
+ memset(x, 0xcc, len);
+ if (r)
+ osmo_nibble_shift_right(x, in, nib);
+ else
+ osmo_nibble_shift_left_unal(x, in, nib);
+
+ printf("[%u] %s IN: %s, nibble %u:", len, r ? "R" : "L",
+ osmo_hexdump_nospc(in, len), nib);
+ /* do NOT combine those printfs: osmo_hexdump* use static buffer which
+ WILL screw things up in that case */
+ printf("\n OUT: %s\n", osmo_hexdump_nospc(x, bytes));
+}
int main(int argc, char **argv)
{
uint8_t out[ARRAY_SIZE(input)];
unsigned int offs;
+ srand(time(NULL));
+
for (offs = 0; offs < sizeof(out); offs++) {
uint8_t *start = out + offs;
uint8_t len = sizeof(out) - offs;
@@ -32,5 +247,62 @@ int main(int argc, char **argv)
printf("\n");
}
+ printf("checking byte packing...\n");
+
+ printf("running static tests...\n");
+
+ check64(0xDEADBEEFF00DCAFE, 0xDEADBEEFF00DCAFE, 8, BE);
+ check64(0xDEADBEEFF00DCAFE, 0xADBEEFF00DCAFE00, 7, BE);
+ check64(0xDEADBEEFF00DCAFE, 0xBEEFF00DCAFE0000, 6, BE);
+ check64(0xDEADBEEFF00DCAFE, 0xEFF00DCAFE000000, 5, BE);
+
+ check64(0xDEADBEEFF00DCAFE, 0xDEADBEEFF00DCAFE, 8, LE);
+ check64(0xDEADBEEFF00DCAFE, 0x00ADBEEFF00DCAFE, 7, LE);
+ check64(0xDEADBEEFF00DCAFE, 0x0000BEEFF00DCAFE, 6, LE);
+ check64(0xDEADBEEFF00DCAFE, 0x000000EFF00DCAFE, 5, LE);
+
+ check32(0xBABEFACE, 0xBABEFACE, 4, BE);
+ check32(0xBABEFACE, 0xBEFACE00, 3, BE);
+
+ check32(0xBABEFACE, 0xBABEFACE, 4, LE);
+ check32(0xBABEFACE, 0x00BEFACE, 3, LE);
+
+ check16(0xB00B, BE);
+ check16(0xB00B, LE);
+
+ printf("running random tests...\n");
+
+ check64(0, 0, 8, BE);
+ check64(0, 0, 7, BE);
+ check64(0, 0, 6, BE);
+ check64(0, 0, 5, BE);
+
+ check64(0, 0, 8, LE);
+ check64(0, 0, 7, LE);
+ check64(0, 0, 6, LE);
+ check64(0, 0, 5, LE);
+
+ check32(0, 0, 4, BE);
+ check32(0, 0, 3, BE);
+
+ check32(0, 0, 4, LE);
+ check32(0, 0, 3, LE);
+
+ check16(0, BE);
+ check16(0, LE);
+
+ printf("running nibble tests...\n");
+
+ const uint8_t in1[] = { 0xF0, 0x0D, 0xCA, 0xFE, 0xDE, 0xAD, 0xBE, 0xEF },
+ in2[] = { 0xB0, 0x0B, 0xBA, 0xBE, 0xFA, 0xCE };
+
+ for (offs = 0; offs < 13; offs++) {
+ sh_chk(in1, ARRAY_SIZE(in1), offs, true);
+ sh_chk(in1, ARRAY_SIZE(in1), offs, false);
+ sh_chk(in2, ARRAY_SIZE(in2), offs, true);
+ /* in2 is too short to shift left 12 nibbles */
+ if (offs < 12)
+ sh_chk(in2, ARRAY_SIZE(in2), offs, false);
+ }
return 0;
}
diff --git a/src/shared/libosmocore/tests/bits/bitrev_test.ok b/src/shared/libosmocore/tests/bits/bitrev_test.ok
index 47f402f4..d2fb12ce 100644
--- a/src/shared/libosmocore/tests/bits/bitrev_test.ok
+++ b/src/shared/libosmocore/tests/bits/bitrev_test.ok
@@ -22,3 +22,137 @@ REVERSED: 02 01
INORDER: 80
REVERSED: 01
+checking byte packing...
+running static tests...
+64 BE OK, storage OK: buffer deadbeeff00dcafe known buffer deadbeeff00dcafe loaded deadbeeff00dcafe expected deadbeeff00dcafe
+56 BE OK, storage OK: buffer adbeeff00dcafe known buffer adbeeff00dcafe00 loaded adbeeff00dcafe00 expected adbeeff00dcafe00
+48 BE OK, storage OK: buffer beeff00dcafe known buffer beeff00dcafe0000 loaded beeff00dcafe0000 expected beeff00dcafe0000
+40 BE OK, storage OK: buffer eff00dcafe known buffer eff00dcafe000000 loaded eff00dcafe000000 expected eff00dcafe000000
+64 LE OK: buffer feca0df0efbeadde known buffer (null) loaded deadbeeff00dcafe expected deadbeeff00dcafe
+56 LE OK: buffer feca0df0efbead known buffer (null) loaded 00adbeeff00dcafe expected 00adbeeff00dcafe
+48 LE OK: buffer feca0df0efbe known buffer (null) loaded 0000beeff00dcafe expected 0000beeff00dcafe
+40 LE OK: buffer feca0df0ef known buffer (null) loaded 000000eff00dcafe expected 000000eff00dcafe
+32 BE OK, storage OK: buffer babeface known buffer babeface loaded babeface expected babeface
+24 BE OK, storage OK: buffer beface known buffer beface00 loaded beface00 expected beface00
+32 LE OK: buffer cefabeba known buffer (null) loaded babeface expected babeface
+24 LE OK: buffer cefabe known buffer (null) loaded 00beface expected 00beface
+16 BE OK, storage OK: buffer b00b known buffer b00b loaded b00b expected b00b
+16 LE OK: buffer 0bb0 known buffer (null) loaded b00b expected b00b
+running random tests...
+64 BE OK, storage OK
+56 BE OK, storage OK
+48 BE OK, storage OK
+40 BE OK, storage OK
+64 LE OK
+56 LE OK
+48 LE OK
+40 LE OK
+32 BE OK, storage OK
+24 BE OK, storage OK
+32 LE OK
+24 LE OK
+16 BE OK, storage OK
+16 LE OK
+running nibble tests...
+[8] R IN: f00dcafedeadbeef, nibble 0:
+ OUT:
+[8] L IN: f00dcafedeadbeef, nibble 0:
+ OUT:
+[6] R IN: b00bbabeface, nibble 0:
+ OUT:
+[6] L IN: b00bbabeface, nibble 0:
+ OUT:
+[8] R IN: f00dcafedeadbeef, nibble 1:
+ OUT: cc
+[8] L IN: f00dcafedeadbeef, nibble 1:
+ OUT: cc
+[6] R IN: b00bbabeface, nibble 1:
+ OUT: cc
+[6] L IN: b00bbabeface, nibble 1:
+ OUT: cc
+[8] R IN: f00dcafedeadbeef, nibble 2:
+ OUT: 0f
+[8] L IN: f00dcafedeadbeef, nibble 2:
+ OUT: 00
+[6] R IN: b00bbabeface, nibble 2:
+ OUT: 0b
+[6] L IN: b00bbabeface, nibble 2:
+ OUT: 00
+[8] R IN: f00dcafedeadbeef, nibble 3:
+ OUT: 0f00
+[8] L IN: f00dcafedeadbeef, nibble 3:
+ OUT: 00d0
+[6] R IN: b00bbabeface, nibble 3:
+ OUT: 0b00
+[6] L IN: b00bbabeface, nibble 3:
+ OUT: 00b0
+[8] R IN: f00dcafedeadbeef, nibble 4:
+ OUT: 0f00
+[8] L IN: f00dcafedeadbeef, nibble 4:
+ OUT: 00dc
+[6] R IN: b00bbabeface, nibble 4:
+ OUT: 0b00
+[6] L IN: b00bbabeface, nibble 4:
+ OUT: 00bb
+[8] R IN: f00dcafedeadbeef, nibble 5:
+ OUT: 0f00dc
+[8] L IN: f00dcafedeadbeef, nibble 5:
+ OUT: 00dca0
+[6] R IN: b00bbabeface, nibble 5:
+ OUT: 0b00bb
+[6] L IN: b00bbabeface, nibble 5:
+ OUT: 00bba0
+[8] R IN: f00dcafedeadbeef, nibble 6:
+ OUT: 0f00dc
+[8] L IN: f00dcafedeadbeef, nibble 6:
+ OUT: 00dcaf
+[6] R IN: b00bbabeface, nibble 6:
+ OUT: 0b00bb
+[6] L IN: b00bbabeface, nibble 6:
+ OUT: 00bbab
+[8] R IN: f00dcafedeadbeef, nibble 7:
+ OUT: 0f00dcaf
+[8] L IN: f00dcafedeadbeef, nibble 7:
+ OUT: 00dcafe0
+[6] R IN: b00bbabeface, nibble 7:
+ OUT: 0b00bbab
+[6] L IN: b00bbabeface, nibble 7:
+ OUT: 00bbabe0
+[8] R IN: f00dcafedeadbeef, nibble 8:
+ OUT: 0f00dcaf
+[8] L IN: f00dcafedeadbeef, nibble 8:
+ OUT: 00dcafed
+[6] R IN: b00bbabeface, nibble 8:
+ OUT: 0b00bbab
+[6] L IN: b00bbabeface, nibble 8:
+ OUT: 00bbabef
+[8] R IN: f00dcafedeadbeef, nibble 9:
+ OUT: 0f00dcafed
+[8] L IN: f00dcafedeadbeef, nibble 9:
+ OUT: 00dcafede0
+[6] R IN: b00bbabeface, nibble 9:
+ OUT: 0b00bbabef
+[6] L IN: b00bbabeface, nibble 9:
+ OUT: 00bbabefa0
+[8] R IN: f00dcafedeadbeef, nibble 10:
+ OUT: 0f00dcafed
+[8] L IN: f00dcafedeadbeef, nibble 10:
+ OUT: 00dcafedea
+[6] R IN: b00bbabeface, nibble 10:
+ OUT: 0b00bbabef
+[6] L IN: b00bbabeface, nibble 10:
+ OUT: 00bbabefac
+[8] R IN: f00dcafedeadbeef, nibble 11:
+ OUT: 0f00dcafedea
+[8] L IN: f00dcafedeadbeef, nibble 11:
+ OUT: 00dcafedead0
+[6] R IN: b00bbabeface, nibble 11:
+ OUT: 0b00bbabefac
+[6] L IN: b00bbabeface, nibble 11:
+ OUT: 00bbabeface0
+[8] R IN: f00dcafedeadbeef, nibble 12:
+ OUT: 0f00dcafedea
+[8] L IN: f00dcafedeadbeef, nibble 12:
+ OUT: 00dcafedeadb
+[6] R IN: b00bbabeface, nibble 12:
+ OUT: 0b00bbabefac
diff --git a/src/shared/libosmocore/tests/bitvec/bitvec_test.c b/src/shared/libosmocore/tests/bitvec/bitvec_test.c
new file mode 100644
index 00000000..d0bc30c4
--- /dev/null
+++ b/src/shared/libosmocore/tests/bitvec/bitvec_test.c
@@ -0,0 +1,291 @@
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <time.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/bitvec.h>
+#include <osmocom/core/bits.h>
+
+static char lol[1024]; // we pollute this with printed vectors
+static inline void test_rl(const struct bitvec *bv)
+{
+ bitvec_to_string_r(bv, lol);
+ printf("%s [%d] RL0=%d, RL1=%d\n", lol, bv->cur_bit, bitvec_rl(bv, false), bitvec_rl(bv, true));
+}
+
+static inline void test_shift(struct bitvec *bv, unsigned n)
+{
+ bitvec_to_string_r(bv, lol);
+ printf("%s << %d:\n", lol, n);
+ bitvec_shiftl(bv, n);
+ bitvec_to_string_r(bv, lol);
+ printf("%s\n", lol);
+}
+
+static inline void test_get(struct bitvec *bv, unsigned n)
+{
+ bitvec_to_string_r(bv, lol);
+ printf("%s [%d]", lol, bv->cur_bit);
+ int16_t x = bitvec_get_int16_msb(bv, n);
+ uint8_t tmp[2];
+ osmo_store16be(x, &tmp);
+ printf(" -> %d (%u bit) ["OSMO_BIN_SPEC" "OSMO_BIN_SPEC"]:\n", x, n, OSMO_BIN_PRINT(tmp[0]), OSMO_BIN_PRINT(tmp[1]));
+ bitvec_to_string_r(bv, lol);
+ printf("%s [%d]\n", lol, bv->cur_bit);
+}
+
+static inline void test_fill(struct bitvec *bv, unsigned n, enum bit_value val)
+{
+ bitvec_to_string_r(bv, lol);
+ unsigned bvlen = bv->cur_bit;
+ int fi = bitvec_fill(bv, n, val);
+ printf("%c> FILL %s [%d] -%d-> [%d]:\n", bit_value_to_char(val), lol, bvlen, n, fi);
+ bitvec_to_string_r(bv, lol);
+ printf(" %s [%d]\n\n", lol, bv->cur_bit);
+}
+
+static inline void test_spare(struct bitvec *bv, unsigned n)
+{
+ bitvec_to_string_r(bv, lol);
+ unsigned bvlen = bv->cur_bit;
+ int sp = bitvec_spare_padding(bv, n);
+ printf("%c> SPARE %s [%d] -%d-> [%d]:\n", bit_value_to_char(L), lol, bvlen, n, sp);
+ bitvec_to_string_r(bv, lol);
+ printf(" %s [%d]\n\n", lol, bv->cur_bit);
+}
+
+static inline void test_set(struct bitvec *bv, enum bit_value bit)
+{
+ bitvec_to_string_r(bv, lol);
+ unsigned bvlen = bv->cur_bit;
+ int set = bitvec_set_bit(bv, bit);
+ printf("%c> SET %s [%d] ++> [%d]:\n", bit_value_to_char(bit), lol, bvlen, set);
+ bitvec_to_string_r(bv, lol);
+ printf(" %s [%d]\n\n", lol, bv->cur_bit);
+}
+
+static void test_byte_ops()
+{
+ struct bitvec bv;
+ const uint8_t *in = (const uint8_t *)"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ uint8_t out[26 + 2];
+ uint8_t data[64];
+ int i;
+ int rc;
+ int in_size = strlen((const char *)in);
+
+ printf("=== start %s ===\n", __func__);
+
+ bv.data = data;
+ bv.data_len = sizeof(data);
+
+ for (i = 0; i < 32; i++) {
+ /* Write to bitvec */
+ memset(data, 0x00, sizeof(data));
+ bv.cur_bit = i;
+ rc = bitvec_set_uint(&bv, 0x7e, 8);
+ OSMO_ASSERT(rc >= 0);
+ rc = bitvec_set_bytes(&bv, in, in_size);
+ OSMO_ASSERT(rc >= 0);
+ rc = bitvec_set_uint(&bv, 0x7e, 8);
+ OSMO_ASSERT(rc >= 0);
+
+ printf("bitvec: %s\n", osmo_hexdump(bv.data, bv.data_len));
+
+ /* Read from bitvec */
+ memset(out, 0xff, sizeof(out));
+ bv.cur_bit = i;
+ rc = bitvec_get_uint(&bv, 8);
+ OSMO_ASSERT(rc == 0x7e);
+ rc = bitvec_get_bytes(&bv, out + 1, in_size);
+ OSMO_ASSERT(rc >= 0);
+ rc = bitvec_get_uint(&bv, 8);
+ OSMO_ASSERT(rc == 0x7e);
+
+ printf("out: %s\n", osmo_hexdump(out, sizeof(out)));
+
+ OSMO_ASSERT(out[0] == 0xff);
+ OSMO_ASSERT(out[in_size+1] == 0xff);
+ OSMO_ASSERT(memcmp(in, out + 1, in_size) == 0);
+ }
+
+ printf("=== end %s ===\n", __func__);
+}
+
+static void test_unhex(const char *hex)
+{
+ int rc;
+ struct bitvec b;
+ uint8_t d[64] = {0};
+ b.data = d;
+ b.data_len = sizeof(d);
+ b.cur_bit = 0;
+
+ rc = bitvec_unhex(&b, hex);
+ printf("%d -=> cur_bit=%u\n", rc, b.cur_bit);
+ printf("%s\n", osmo_hexdump_nospc(d, 64));
+ printf("%s\n", hex);
+}
+
+static inline void test_array_item(unsigned t, struct bitvec *b, unsigned int n,
+ uint32_t *array, unsigned int p)
+{
+ unsigned int i, x, y;
+ bitvec_zero(b);
+ x = b->cur_bit;
+ i = bitvec_add_array(b, array, n, true, t);
+ y = b->cur_bit;
+ bitvec_add_array(b, array, n, false, t);
+ printf("\nbits: %u, est: %u, real: %u, x: %u, y: %u\n",
+ t, i, b->cur_bit, x, y);
+ for (i = 0; i < p; i++) {
+ printf(OSMO_BIT_SPEC " ", OSMO_BIT_PRINT(b->data[i]));
+ if (0 == (i + 1) % 15)
+ printf("\n");
+ }
+}
+
+static inline void test_bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits,
+ int result )
+{
+ int num = 0;
+ int readIndex = bv->cur_bit;
+ OSMO_ASSERT(bv->cur_bit < max_bits);
+ num = bitvec_rl_curbit(bv, b, max_bits);
+ readIndex += num;
+ OSMO_ASSERT(bv->cur_bit == readIndex);
+ OSMO_ASSERT(num == result);
+}
+
+static void test_array()
+{
+ struct bitvec b;
+ uint8_t d[4096];
+ b.data = d;
+ b.data_len = sizeof(d);
+
+ unsigned int i, n = 64;
+ uint32_t array[n];
+ for (i = 0; i < n; i++) {
+ array[i] = i * i * i + i;
+ printf("0x%x ", array[i]);
+ }
+
+ test_array_item(3, &b, n, array, n);
+ test_array_item(9, &b, n, array, n * 2);
+ test_array_item(17, &b, n, array, n * 3);
+}
+
+int main(int argc, char **argv)
+{
+ struct bitvec bv;
+ uint8_t i = 8, test[i];
+
+ memset(test, 0, i);
+ bv.data_len = i;
+ bv.data = test;
+ bv.cur_bit = 0;
+
+ printf("test shifting...\n");
+
+ bitvec_set_uint(&bv, 0x0E, 7);
+ test_shift(&bv, 3);
+ test_shift(&bv, 17);
+ bitvec_set_uint(&bv, 0, 32);
+ bitvec_set_uint(&bv, 0x0A, 7);
+ test_shift(&bv, 24);
+
+ printf("checking RL functions...\n");
+
+ bitvec_zero(&bv);
+ test_rl(&bv);
+ bitvec_set_uint(&bv, 0x000F, 32);
+ test_rl(&bv);
+ bitvec_shiftl(&bv, 18);
+ test_rl(&bv);
+ bitvec_set_uint(&bv, 0x0F, 8);
+ test_rl(&bv);
+ bitvec_zero(&bv);
+ bitvec_set_uint(&bv, 0xFF, 8);
+ test_rl(&bv);
+ bitvec_set_uint(&bv, 0xFE, 7);
+ test_rl(&bv);
+ bitvec_set_uint(&bv, 0, 17);
+ test_rl(&bv);
+ bitvec_shiftl(&bv, 18);
+ test_rl(&bv);
+
+ printf("probing bit access...\n");
+
+ bitvec_zero(&bv);
+ bitvec_set_uint(&bv, 0x3747817, 32);
+ bitvec_shiftl(&bv, 10);
+
+ test_get(&bv, 2);
+ test_get(&bv, 7);
+ test_get(&bv, 9);
+ test_get(&bv, 13);
+ test_get(&bv, 16);
+ test_get(&bv, 42);
+
+ printf("feeling bit fills...\n");
+
+ test_set(&bv, ONE);
+ test_fill(&bv, 3, ZERO);
+ test_spare(&bv, 38);
+ test_spare(&bv, 43);
+ test_spare(&bv, 1);
+ test_spare(&bv, 7);
+ test_fill(&bv, 5, ONE);
+ test_fill(&bv, 3, L);
+
+ printf("byte me...\n");
+
+ test_byte_ops();
+ test_unhex("48282407a6a074227201000b2b2b2b2b2b2b2b2b2b2b2b");
+ test_unhex("47240c00400000000000000079eb2ac9402b2b2b2b2b2b");
+ test_unhex("47283c367513ba333004242b2b2b2b2b2b2b2b2b2b2b2b");
+ test_unhex("DEADFACE000000000000000000000000000000BEEFFEED");
+ test_unhex("FFFFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB");
+
+ printf("arrr...\n");
+
+ test_array();
+
+ printf("\nbitvec_runlength....\n");
+
+ bitvec_zero(&bv);
+ bitvec_set_uint(&bv, 0xff, 8);
+ bv.cur_bit -= 8;
+ test_bitvec_rl_curbit(&bv, 1, 64, 8);
+
+ bitvec_zero(&bv);
+ bitvec_set_uint(&bv, 0xfc, 8);
+ bv.cur_bit -= 8;
+ test_bitvec_rl_curbit(&bv, 1, 64, 6);
+
+ bitvec_zero(&bv);
+ test_bitvec_rl_curbit(&bv, 0, 52, 52);
+
+ bitvec_zero(&bv);
+ bitvec_set_uint(&bv, 0xfc, 8);
+ bv.cur_bit -= 2;
+ test_bitvec_rl_curbit(&bv, 0, 64, 58);
+
+ bitvec_zero(&bv);
+ bitvec_set_uint(&bv, 0x07, 8);
+ bitvec_set_uint(&bv, 0xf8, 8);
+ bv.cur_bit -= 11;
+ test_bitvec_rl_curbit(&bv, 1, 64, 8);
+
+ bitvec_zero(&bv);
+ test_bitvec_rl_curbit(&bv, 1, 64, 0);
+
+ printf("\nbitvec ok.\n");
+ return 0;
+}
diff --git a/src/shared/libosmocore/tests/bitvec/bitvec_test.ok b/src/shared/libosmocore/tests/bitvec/bitvec_test.ok
new file mode 100644
index 00000000..62819736
--- /dev/null
+++ b/src/shared/libosmocore/tests/bitvec/bitvec_test.ok
@@ -0,0 +1,171 @@
+test shifting...
+ 0001110 << 3:
+ 1110
+ 1110 << 17:
+
+ 00000000 00000000 00000000 00000000 0001010 << 24:
+ 00000000 0001010
+checking RL functions...
+ [0] RL0=0, RL1=0
+ 00000000 00000000 00000000 00001111 [32] RL0=28, RL1=0
+ 00000000 001111 [14] RL0=10, RL1=0
+ 00000000 00111100 001111 [22] RL0=10, RL1=0
+ 11111111 [8] RL0=0, RL1=8
+ 11111111 1111110 [15] RL0=0, RL1=14
+ 11111111 11111100 00000000 00000000 [32] RL0=0, RL1=14
+ 00000000 000000 [14] RL0=14, RL1=0
+probing bit access...
+ 11010001 11100000 010111 [22] -> 3 (2 bit) [00000000 00000011]:
+ 11010001 11100000 010111 [22]
+ 11010001 11100000 010111 [22] -> 104 (7 bit) [00000000 01101000]:
+ 11010001 11100000 010111 [22]
+ 11010001 11100000 010111 [22] -> 419 (9 bit) [00000001 10100011]:
+ 11010001 11100000 010111 [22]
+ 11010001 11100000 010111 [22] -> 6716 (13 bit) [00011010 00111100]:
+ 11010001 11100000 010111 [22]
+ 11010001 11100000 010111 [22] -> -22 (16 bit) [11111111 11101010]:
+ 11010001 11100000 010111 [22]
+ 11010001 11100000 010111 [22] -> -22 (42 bit) [11111111 11101010]:
+ 11010001 11100000 010111 [22]
+feeling bit fills...
+1> SET 11010001 11100000 010111 [22] ++> [0]:
+ 11010001 11100000 0101111 [23]
+
+0> FILL 11010001 11100000 0101111 [23] -3-> [0]:
+ 11010001 11100000 01011110 00 [26]
+
+L> SPARE 11010001 11100000 01011110 00 [26] -38-> [0]:
+ 11010001 11100000 01011110 00101011 0010101 [39]
+
+L> SPARE 11010001 11100000 01011110 00101011 0010101 [39] -43-> [0]:
+ 11010001 11100000 01011110 00101011 00101011 0010 [44]
+
+L> SPARE 11010001 11100000 01011110 00101011 00101011 0010 [44] -1-> [0]:
+ 11010001 11100000 01011110 00101011 00101011 0010 [44]
+
+L> SPARE 11010001 11100000 01011110 00101011 00101011 0010 [44] -7-> [0]:
+ 11010001 11100000 01011110 00101011 00101011 0010 [44]
+
+1> FILL 11010001 11100000 01011110 00101011 00101011 0010 [44] -5-> [0]:
+ 11010001 11100000 01011110 00101011 00101011 00101111 1 [49]
+
+L> FILL 11010001 11100000 01011110 00101011 00101011 00101111 1 [49] -3-> [0]:
+ 11010001 11100000 01011110 00101011 00101011 00101111 1010 [52]
+
+byte me...
+=== start test_byte_ops ===
+bitvec: 7e 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 7e 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 00 00 00 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 3f 20 a1 21 a2 22 a3 23 a4 24 a5 25 a6 26 a7 27 a8 28 a9 29 aa 2a ab 2b ac 2c ad 3f 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 00 00 00 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 1f 90 50 90 d1 11 51 91 d2 12 52 92 d3 13 53 93 d4 14 54 94 d5 15 55 95 d6 16 56 9f 80 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 00 00 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 0f c8 28 48 68 88 a8 c8 e9 09 29 49 69 89 a9 c9 ea 0a 2a 4a 6a 8a aa ca eb 0b 2b 4f c0 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 00 00 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 07 e4 14 24 34 44 54 64 74 84 94 a4 b4 c4 d4 e4 f5 05 15 25 35 45 55 65 75 85 95 a7 e0 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 00 00 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 03 f2 0a 12 1a 22 2a 32 3a 42 4a 52 5a 62 6a 72 7a 82 8a 92 9a a2 aa b2 ba c2 ca d3 f0 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 00 00 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 01 f9 05 09 0d 11 15 19 1d 21 25 29 2d 31 35 39 3d 41 45 49 4d 51 55 59 5d 61 65 69 f8 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 00 00 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 fc 82 84 86 88 8a 8c 8e 90 92 94 96 98 9a 9c 9e a0 a2 a4 a6 a8 aa ac ae b0 b2 b4 fc 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 00 00 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 7e 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 7e 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 00 00 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 3f 20 a1 21 a2 22 a3 23 a4 24 a5 25 a6 26 a7 27 a8 28 a9 29 aa 2a ab 2b ac 2c ad 3f 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 00 00 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 1f 90 50 90 d1 11 51 91 d2 12 52 92 d3 13 53 93 d4 14 54 94 d5 15 55 95 d6 16 56 9f 80 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 00 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 0f c8 28 48 68 88 a8 c8 e9 09 29 49 69 89 a9 c9 ea 0a 2a 4a 6a 8a aa ca eb 0b 2b 4f c0 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 00 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 07 e4 14 24 34 44 54 64 74 84 94 a4 b4 c4 d4 e4 f5 05 15 25 35 45 55 65 75 85 95 a7 e0 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 00 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 03 f2 0a 12 1a 22 2a 32 3a 42 4a 52 5a 62 6a 72 7a 82 8a 92 9a a2 aa b2 ba c2 ca d3 f0 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 00 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 01 f9 05 09 0d 11 15 19 1d 21 25 29 2d 31 35 39 3d 41 45 49 4d 51 55 59 5d 61 65 69 f8 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 00 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 00 fc 82 84 86 88 8a 8c 8e 90 92 94 96 98 9a 9c 9e a0 a2 a4 a6 a8 aa ac ae b0 b2 b4 fc 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 00 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 00 7e 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 7e 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 00 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 00 3f 20 a1 21 a2 22 a3 23 a4 24 a5 25 a6 26 a7 27 a8 28 a9 29 aa 2a ab 2b ac 2c ad 3f 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 00 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 00 1f 90 50 90 d1 11 51 91 d2 12 52 92 d3 13 53 93 d4 14 54 94 d5 15 55 95 d6 16 56 9f 80 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 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 00 0f c8 28 48 68 88 a8 c8 e9 09 29 49 69 89 a9 c9 ea 0a 2a 4a 6a 8a aa ca eb 0b 2b 4f c0 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 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 00 07 e4 14 24 34 44 54 64 74 84 94 a4 b4 c4 d4 e4 f5 05 15 25 35 45 55 65 75 85 95 a7 e0 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 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 00 03 f2 0a 12 1a 22 2a 32 3a 42 4a 52 5a 62 6a 72 7a 82 8a 92 9a a2 aa b2 ba c2 ca d3 f0 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 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 00 01 f9 05 09 0d 11 15 19 1d 21 25 29 2d 31 35 39 3d 41 45 49 4d 51 55 59 5d 61 65 69 f8 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 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 00 00 fc 82 84 86 88 8a 8c 8e 90 92 94 96 98 9a 9c 9e a0 a2 a4 a6 a8 aa ac ae b0 b2 b4 fc 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 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 00 00 7e 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 7e 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 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 00 00 3f 20 a1 21 a2 22 a3 23 a4 24 a5 25 a6 26 a7 27 a8 28 a9 29 aa 2a ab 2b ac 2c ad 3f 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 00 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 00 00 1f 90 50 90 d1 11 51 91 d2 12 52 92 d3 13 53 93 d4 14 54 94 d5 15 55 95 d6 16 56 9f 80 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 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 00 00 0f c8 28 48 68 88 a8 c8 e9 09 29 49 69 89 a9 c9 ea 0a 2a 4a 6a 8a aa ca eb 0b 2b 4f c0 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 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 00 00 07 e4 14 24 34 44 54 64 74 84 94 a4 b4 c4 d4 e4 f5 05 15 25 35 45 55 65 75 85 95 a7 e0 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 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 00 00 03 f2 0a 12 1a 22 2a 32 3a 42 4a 52 5a 62 6a 72 7a 82 8a 92 9a a2 aa b2 ba c2 ca d3 f0 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 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 00 00 01 f9 05 09 0d 11 15 19 1d 21 25 29 2d 31 35 39 3d 41 45 49 4d 51 55 59 5d 61 65 69 f8 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 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+bitvec: 00 00 00 00 fc 82 84 86 88 8a 8c 8e 90 92 94 96 98 9a 9c 9e a0 a2 a4 a6 a8 aa ac ae b0 b2 b4 fc 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 00
+out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
+=== end test_byte_ops ===
+1 -=> cur_bit=184
+48282407a6a074227201000b2b2b2b2b2b2b2b2b2b2b2b0000000000000000000000000000000000000000000000000000000000000000000000000000000000
+48282407a6a074227201000b2b2b2b2b2b2b2b2b2b2b2b
+1 -=> cur_bit=184
+47240c00400000000000000079eb2ac9402b2b2b2b2b2b0000000000000000000000000000000000000000000000000000000000000000000000000000000000
+47240c00400000000000000079eb2ac9402b2b2b2b2b2b
+1 -=> cur_bit=184
+47283c367513ba333004242b2b2b2b2b2b2b2b2b2b2b2b0000000000000000000000000000000000000000000000000000000000000000000000000000000000
+47283c367513ba333004242b2b2b2b2b2b2b2b2b2b2b2b
+1 -=> cur_bit=184
+deadface000000000000000000000000000000beeffeed0000000000000000000000000000000000000000000000000000000000000000000000000000000000
+DEADFACE000000000000000000000000000000BEEFFEED
+0 -=> cur_bit=512
+fffffaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+FFFFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
+arrr...
+0x0 0x2 0xa 0x1e 0x44 0x82 0xde 0x15e 0x208 0x2e2 0x3f2 0x53e 0x6cc 0x8a2 0xac6 0xd3e 0x1010 0x1342 0x16da 0x1ade 0x1f54 0x2442 0x29ae 0x2f9e 0x3618 0x3d22 0x44c2 0x4cfe 0x55dc 0x5f62 0x6996 0x747e 0x8020 0x8c82 0x99aa 0xa79e 0xb664 0xc602 0xd67e 0xe7de 0xfa28 0x10d62 0x12192 0x136be 0x14cec 0x16422 0x17c66 0x195be 0x1b030 0x1cbc2 0x1e87a 0x2065e 0x22574 0x245c2 0x2674e 0x28a1e 0x2ae38 0x2d3a2 0x2fa62 0x3227e 0x34bfc 0x376e2 0x3a336 0x3d0fe
+bits: 3, est: 257, real: 257, x: 0, y: 0
+1...1.1. 1.1.111. 11..1.1. 111.111. 1...1.1. 1.1.111. 11..1.1. 111.111. 1...1.1. 1.1.111. 11..1.1. 111.111. 1...1.1. 1.1.111. 11..1.1.
+111.111. 1...1.1. 1.1.111. 11..1.1. 111.111. 1...1.1. 1.1.111. 11..1.1. 111.111. 1...1.1. 1.1.111. 11..1.1. 111.111. 1...1.1. 1.1.111.
+11..1.1. 111.111. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........
+........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........
+........ ........ ........ ........
+bits: 9, est: 641, real: 641, x: 0, y: 0
+1....... ..1..... ..1.1... ..1.1.1. ...1111. 1..1...1 ..1.1... ..1.1.11 .1111.11 .1.1111. 1.....1. ..1.111. ..1.1111 11..1.11 ..11111.
+1.11..11 ..1.1.1. ..1.1.11 ...11.11 ..11111. 1....1.. ..11.1.. ..1.1.11 .11.1.1. 11.1111. 11.1.1.1 ..1..1.. ..1.111. 1.111.11 1..1111.
+1....11. ..11..1. ..1.1.11 ....1.1. 1111111. 1111.111 ..11.11. ..1.111. .1.11.1. .111111. 1...1... ..1.1... ..1.111. 1.1.1.11 1..1111.
+1..11..1 ..1..... ..1.1..1 11111.11 11.1111. 1...1.1. ..11.11. ..1.111. .1..1.1. 1.11111. 1.111.11 ..1...1. ..1.1..1 1..11.11 1.11111.
+1...11.. ..1111.. ..1.1..1 111.1.1. .1.1111. 11.111.1 ..1111.. ..1.11.1 ..111.1. ...1111. 1...111. ..111.1. ..1.1..1 1...1.1. .111111.
+11111111 ..1.111. ..1.11.. 11.11.1. 1111111. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........
+........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........
+........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........
+........ ........ ........ ........ ........ ........ ........ ........
+bits: 17, est: 1153, real: 1153, x: 0, y: 0
+1....... ........ ..1..... ........ ..1.1... ........ ..1.1.1. ........ ...1111. 1....... ...1...1 ..1..... ....1... ..1.1... ......11
+.1111.1. .......1 .1.1111. 1....... 1.....1. ..1..... ..1.111. ..1.1... ....1111 11..1.1. .....1.1 ..11111. 1......1 1.11..11 ..1.....
+1...1.1. ..1.1... ..1.1.11 ...11.1. ....11.1 ..11111. 1....1.. .....1.. ..1....1 ..11.1.. ..1.1... .1.11.11 .11.1.1. ...11.1. 11.1111.
+1....111 11.1.1.1 ..1...1. .1...1.. ..1.1... 1.1..11. 1.111.1. ..1.1111 1..1111. 1...11.1 1....11. ..1...11 11.1..1. ..1.1..1 ...1..11
+....1.1. .1..11.. 1111111. 1..1.1.1 .111.111 ..1..1.1 1111.11. ..1.1..1 1.1..11. .1.11.1. .111.1.. .111111. 1.1..... ....1... ..1.1...
+11..1... ..1.1.1. .11..11. 1.1.1.1. 1.1..111 1..1111. 1.1.11.1 1..11..1 ..1.11.. .11..... ..1.1.11 .1.11..1 11111.1. 111..111 11.1111.
+1.11111. 1...1.1. ..11.... 11.1.11. ..1.11.. 1....11. .1..1.11 ..11.11. 1.11111. 11.1..11 ..111.11 ..11.11. .1....1. ..1.11.1 1111...1
+1..11.11 1..1.1.1 1.11111. 111.11.. ....11.. ..1111.. 1.1111.. ..1.1111 1.1....1 111.1.1. .....11. .1.1111. 1...1..1 .1.111.1 ..1..1..
+.1.111.. ..1.1..1 1..111.1 ..111.1. 1...1.1. ...1111. 1.1.1.11 1...111. ..1.11.1 ..111.1. ..1.1.11 111.1..1 1...1.11 ..1...1. .111111.
+11.1..1. 11111111 ..11.111 .11.111. ..1.111. 1...11.. 11.11.11 11.1.... 1111111. ........ ........ ........ ........ ........ ........
+........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........
+........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........
+........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........
+bitvec_runlength....
+
+bitvec ok.
diff --git a/src/shared/libosmocore/tests/codec/codec_test.c b/src/shared/libosmocore/tests/codec/codec_test.c
new file mode 100644
index 00000000..f944f38b
--- /dev/null
+++ b/src/shared/libosmocore/tests/codec/codec_test.c
@@ -0,0 +1,120 @@
+/*
+ * (C) 2016 by Sysmocom s.f.m.c. GmbH
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/codec/codec.h>
+
+const uint8_t sid_update[] = {0x20, 0x44, 0x29, 0xc2, 0x92, 0x91, 0xf4};
+const uint8_t sid_first[] = {0x20, 0x44, 0x00, 0x00, 0x00, 0x00, 0x04};
+
+#define PAYLOAD_LEN 34
+#define SID_LEN 7
+
+static const char * cmpr(int a, int b)
+{
+ return (a == b) ? "OK" : "BAD";
+}
+
+static void test_sid_dec(const uint8_t *t, size_t len)
+{
+ uint8_t cmr, tmp[SID_LEN], *t2 = NULL;
+ enum osmo_amr_type ft;
+ enum osmo_amr_quality bfi;
+ int8_t sti, cmi;
+ if (t) {
+ memcpy(tmp, t, SID_LEN);
+ t2 = tmp;
+ }
+ int rc = osmo_amr_rtp_dec(t2, len, &cmr, &cmi, &ft, &bfi, &sti);
+ if (rc < 0)
+ return;
+ printf("[%d] decode RTP %s%s: FT %s, CMR %s, CMI is %d, SID type %s\t",
+ rc, osmo_hexdump(tmp, len), cmpr(bfi, AMR_GOOD),
+ get_value_string(osmo_amr_type_names, ft),
+ get_value_string(osmo_amr_type_names, cmr),
+ cmi, sti ? "UPDATE" : "FIRST");
+ if (sti == -1)
+ printf("FAIL: incompatible STI for SID\n");
+ rc = osmo_amr_rtp_enc(tmp, cmr, ft, bfi);
+ printf("[%d] encode [%d]\n", rc, memcmp(tmp, t, SID_LEN));
+}
+
+static void test_amr_rt(uint8_t _cmr, enum osmo_amr_type _ft,
+ enum osmo_amr_quality _bfi)
+{
+ uint8_t cmr, payload[PAYLOAD_LEN];
+ enum osmo_amr_type ft;
+ enum osmo_amr_quality bfi;
+ int8_t sti, cmi;
+ int rc, re = osmo_amr_rtp_enc(payload, _cmr, _ft, _bfi);
+ rc = osmo_amr_rtp_dec(payload, PAYLOAD_LEN, &cmr, &cmi, &ft, &bfi, &sti);
+ printf("[%d/%d] %s, CMR: %s, FT: %s, BFI: %s, CMI: %d, STI: %d\n", re,
+ rc, get_value_string(osmo_amr_type_names, ft),
+ cmpr(_cmr, cmr), cmpr(_ft, ft), cmpr(_bfi, bfi), cmi, sti);
+}
+
+uint8_t fr[] = {0xd8, 0xa9, 0xb5, 0x1d, 0xda, 0xa8, 0x82, 0xcc, 0xec, 0x52,
+ 0x29, 0x05, 0xa8, 0xc3, 0xe3, 0x0e, 0xb0, 0x89, 0x7a, 0xee,
+ 0x42, 0xca, 0xc4, 0x97, 0x22, 0xe6, 0x9e, 0xa8, 0xb8, 0xec,
+ 0x52, 0x26, 0xbd};
+uint8_t sid_fr[] = {0xd7, 0x27, 0x93, 0xe5, 0xe3, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+uint8_t hr[] = {0x06, 0x46, 0x76, 0xb1, 0x8e, 0x48, 0x9a, 0x2f, 0x5e, 0x4c,
+ 0x22, 0x2b, 0x62, 0x25};
+uint8_t sid_hr[] = {0x03, 0x8e, 0xb6, 0xcb, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff};
+
+static void test_sid_xr(uint8_t *t, size_t len, bool hr)
+{
+ printf("%s SID ? %s:: %d\n", hr ? "HR" : "FR", osmo_hexdump(t, len),
+ hr ? osmo_hr_check_sid(t, len) : osmo_fr_check_sid(t, len));
+}
+
+int main(int argc, char **argv)
+{
+ printf("AMR RTP payload decoder test:\n");
+ test_sid_dec(sid_first, 7);
+ test_sid_dec(sid_update, 7);
+ test_sid_dec(NULL, 7);
+ test_amr_rt(0, AMR_NO_DATA, AMR_BAD);
+ test_amr_rt(0, AMR_NO_DATA, AMR_GOOD);
+ test_amr_rt(AMR_12_2, AMR_12_2, AMR_BAD);
+ test_amr_rt(AMR_12_2, AMR_12_2, AMR_GOOD);
+ test_amr_rt(AMR_7_40, AMR_7_40, AMR_BAD);
+ test_amr_rt(AMR_7_40, AMR_7_40, AMR_GOOD);
+ printf("FR RTP payload SID test:\n");
+ test_sid_xr(sid_fr, 33, false);
+ test_sid_xr(fr, 33, false);
+
+ printf("HR RTP payload SID test:\n");
+ test_sid_xr(sid_hr, 14, true);
+ test_sid_xr(hr, 14, true);
+
+ return 0;
+}
+
+
diff --git a/src/shared/libosmocore/tests/codec/codec_test.ok b/src/shared/libosmocore/tests/codec/codec_test.ok
new file mode 100644
index 00000000..80c4a099
--- /dev/null
+++ b/src/shared/libosmocore/tests/codec/codec_test.ok
@@ -0,0 +1,15 @@
+AMR RTP payload decoder test:
+[9] decode RTP 20 44 00 00 00 00 04 OK: FT AMR SID, CMR AMR 5,90 kbit/s, CMI is 2, SID type FIRST [9] encode [0]
+[9] decode RTP 20 44 29 c2 92 91 f4 OK: FT AMR SID, CMR AMR 5,90 kbit/s, CMI is 2, SID type UPDATE [9] encode [0]
+[2/2] No Data/NA, CMR: OK, FT: OK, BFI: OK, CMI: -1, STI: -1
+[2/2] No Data/NA, CMR: OK, FT: OK, BFI: OK, CMI: -1, STI: -1
+[33/33] AMR 12,2 kbit/s (GSM-EFR), CMR: OK, FT: OK, BFI: OK, CMI: -1, STI: -1
+[33/33] AMR 12,2 kbit/s (GSM-EFR), CMR: OK, FT: OK, BFI: OK, CMI: -1, STI: -1
+[21/21] AMR 7,40 kbit/s (TDMA-EFR), CMR: OK, FT: OK, BFI: OK, CMI: -1, STI: -1
+[21/21] AMR 7,40 kbit/s (TDMA-EFR), CMR: OK, FT: OK, BFI: OK, CMI: -1, STI: -1
+FR RTP payload SID test:
+FR SID ? d7 27 93 e5 e3 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 :: 1
+FR SID ? d8 a9 b5 1d da a8 82 cc ec 52 29 05 a8 c3 e3 0e b0 89 7a ee 42 ca c4 97 22 e6 9e a8 b8 ec 52 26 bd :: 0
+HR RTP payload SID test:
+HR SID ? 03 8e b6 cb ff ff ff ff ff ff ff ff ff ff :: 1
+HR SID ? 06 46 76 b1 8e 48 9a 2f 5e 4c 22 2b 62 25 :: 0
diff --git a/src/shared/libosmocore/tests/comp128/comp128_test.c b/src/shared/libosmocore/tests/comp128/comp128_test.c
new file mode 100644
index 00000000..07c98fa4
--- /dev/null
+++ b/src/shared/libosmocore/tests/comp128/comp128_test.c
@@ -0,0 +1,2125 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/comp128v23.h>
+#include <osmocom/crypt/auth.h>
+
+static struct osmo_sub_auth_data test_aux2 = {
+ .type = OSMO_AUTH_TYPE_GSM,
+ .algo = OSMO_AUTH_ALG_COMP128v2,
+ .u.gsm = {
+ .ki = { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA },
+ }
+};
+
+static struct osmo_sub_auth_data test_aux3 = {
+ .type = OSMO_AUTH_TYPE_GSM,
+ .algo = OSMO_AUTH_ALG_COMP128v3,
+ .u.gsm = {
+ .ki = { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA },
+ }
+};
+
+void print_check(int rc, char *res, struct osmo_auth_vector *vec)
+{
+ uint8_t buf[12];
+ osmo_hexparse(res, buf, 12);
+ if (0 != memcmp(buf, vec->sres, 4)) {
+ printf("%d FAIL SRES:\n", rc);
+ printf("OUT: %s\n", osmo_hexdump_nospc(vec->sres, 4));
+ printf("EXP: %s\n", osmo_hexdump_nospc(buf, 4));
+ }
+ if (0 != memcmp(buf+4, vec->kc, 8)) {
+ printf("%d FAIL Kc:\n", rc);
+ printf("OUT: %s\n", osmo_hexdump_nospc(vec->kc, 8));
+ printf("EXP: %s\n", osmo_hexdump_nospc(buf+4, 8));
+ } else
+ printf("%d OK\n", rc);
+}
+
+void test_comp128v3(char *rand, char *res)
+{
+ struct osmo_auth_vector _vec;
+ struct osmo_auth_vector *vec = &_vec;
+ uint8_t _rand[16];
+ osmo_hexparse(rand, _rand, 16);
+ int rc = osmo_auth_gen_vec(vec, &test_aux3, _rand);
+ print_check(rc, res, vec);
+}
+
+void test_comp128v2(char *rand, char *res)
+{
+ struct osmo_auth_vector _vec;
+ struct osmo_auth_vector *vec = &_vec;
+ uint8_t _rand[16];
+ osmo_hexparse(rand, _rand, 16);
+ int rc = osmo_auth_gen_vec(vec, &test_aux2, _rand);
+ print_check(rc, res, vec);
+}
+
+int main(int argc, char **argv)
+{
+ printf("COMP128v2 support: %d\n",
+ osmo_auth_supported(osmo_auth_alg_parse("COMP128v2")));
+ printf("COMP128v3 support: %d\n",
+ osmo_auth_supported(osmo_auth_alg_parse("COMP128v3")));
+
+test_comp128v2("00000000000000000000000000000000", "34B4225BF16B96E118A85800");
+test_comp128v2("00102030405060708090A0B0C0D0E0F0", "A892A8EFD6D33E3650372C00");
+test_comp128v2("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "F699F0BABA87114F0350BC00");
+test_comp128v2("000102030405060708090A0B0C0D0E0F", "A5B4C7CA0514C4E1B25CBC00");
+test_comp128v2("F918FB140A2D63E10B9B3354C93D5816", "C1AD6FE372383E0D6AAC7C00");
+test_comp128v2("CBFE7E4156F94F1E6ECA59C194A9BED4", "3DEF079216A74B97B4ADC800");
+test_comp128v2("B65CB9DDC667BBDA3F493FB4BA2CA2E9", "A846E7EA48C2E85F1C115800");
+test_comp128v2("23E0F6ED14ED2596A3B11453786E7C2C", "73280CD483E7DCDE26F94400");
+test_comp128v2("CB5CD183A1D26D1CF75D5987D20CAE75", "055DF715899926BD0D6A0400");
+test_comp128v2("1E99C6D07A0914E101FC2094406FB46F", "0A500680A860B1678C749400");
+test_comp128v2("F9213F7452CC291C6B64F990DFC219CE", "6273439D25CA01E321505C00");
+test_comp128v2("21C5CCC4CDDF8D016F82788BF2863718", "404F028B1F31A67A71734400");
+test_comp128v2("1561F279CC3FC8D7FDF7703C3755003B", "7081294CCCF3AF9D87CBBC00");
+test_comp128v2("360EBBC222F116936F55772663FE0131", "D30CD39641B7771E2CDA2800");
+test_comp128v2("D7FDC4BEFF3E172607BCAFD0FFB8414E", "656222C3E481A1AE7B42BC00");
+test_comp128v2("D832114007244CEB3B05AA50F60A1DC3", "EAE9BC2A4D1DD802E9ADF000");
+test_comp128v2("CCB8CAA7DC27AF527F75385BC9CC943A", "2A43B559362A6DDED82E4800");
+test_comp128v2("48BD98472929D5D66FDEF005FD1B67D5", "0EA9FCAF684D3416C187C800");
+test_comp128v2("97A67270ABC7807CF3F7D2D668D9EF2C", "510DA1D26FA933C86D0D2C00");
+test_comp128v2("5CF1FC3916B9631D04E763003CB0AFB1", "EE2E83CF08A00B0A1966C800");
+test_comp128v2("18F91C80B5CDC5A5A95A187EF66AB0C0", "0182E8AFDC4092FEE382F000");
+test_comp128v2("248B68303F09476AE2E72342636E6D9E", "D78DF0808D19FBF8B703D400");
+test_comp128v2("340D4544F8559C61EDF7F4216E70C576", "2480A75BF304AC7AC73F3800");
+test_comp128v2("CCFE030B6436D949082ACA45077C9193", "85A18AEA5B9BC2B7A376F400");
+test_comp128v2("3D0754700F6A1D2C704921EC67AF3ED2", "2D337465329B474F67975800");
+test_comp128v2("9165BF37C63326F8CE9CD03F9A1378BC", "DCAEA3C710D0D67DD5B3F400");
+test_comp128v2("9DCFA432DFC4F1FF19A441F32A5D8597", "F010B99B63B7E6F75DE6C400");
+test_comp128v2("1DE9D2AC71DF86E5A599017A692CB4B8", "EB93C952E8C616D6C2979400");
+test_comp128v2("261326154E44C794F5A91935182CFF40", "B27DE0650A1FC35AB045EC00");
+test_comp128v2("3F299BB3EE46BBB4ADB5493B4A2CBE5E", "67838DEDAA2242B6BB237800");
+test_comp128v2("CFD033BE76FE261250042457B40445A2", "863CA702CF08A47B3CBD5800");
+test_comp128v2("FCCF9EE60A80EC9F7BE413A50CE60869", "14D1B0D8254D474C999CC000");
+test_comp128v2("A966B4A739358CFD6EF22A9A660785DF", "6894BA1EF235BFA46A0E7000");
+test_comp128v2("C1F8AD801A7441908B0597E186EBF2C0", "C4E9F13F0C19A76F7EC9CC00");
+test_comp128v2("ACDF541A969C68703E5DE37ED0B7CA09", "832463CBF662B2A72D4BF400");
+test_comp128v2("274F7ECBD2AF639D9479FF4C73815E1E", "D1D5AA0F491ED442716D4C00");
+test_comp128v2("3E4FC9C886E4E5A8EE89AF61231857CB", "323C4D161E55610BCB0CCC00");
+test_comp128v2("E0F4130624669860131BD80D85A3F1F2", "07DAEF70D14DFB9988C5D800");
+test_comp128v2("DD8C39429A107F263B2A5F596A501905", "1417946345034965053FB000");
+test_comp128v2("A95D7E54A42604BAB557A0BDA4F50CC1", "D1683BA6DF50CAC2508CA400");
+test_comp128v2("140C6662233AD66D5AC825D80A9A8272", "A0FAA92970DC0E98AB406000");
+test_comp128v2("EEA61DDED13D0E0F2DCCB71ADD551B7B", "7240AF352D215FE4B4762C00");
+test_comp128v2("E4C84D1860B03C3DC2010FB6AB3E4C47", "30576C1054878023B505D800");
+test_comp128v2("747AF0FE3B3E903AEB6191EA01DC9F4E", "4CBAD22E8500464CFD7E4800");
+test_comp128v2("E607B88BE14E5CEE43594EF51A0725BB", "1D79DBF69D7E2350225D9C00");
+test_comp128v2("4002BDDC83EF54ABAE5442B0A8E976B9", "6CB6D7673428D2D2E84BC800");
+test_comp128v2("AB8B27FE3F278CFDAE085F236A0A28E5", "06407FCB02EBF61123C57000");
+test_comp128v2("6883B190F8EC18305BAAF6429E817BC4", "C17A6B9AB444C1A9DD448800");
+test_comp128v2("18DC6B25398F64FAA4A740C617E9D591", "F88F9FA28DE0500403AC6400");
+test_comp128v2("2735F287D3AEDDD35C204628322D3899", "00F5692A5F350599985AC400");
+test_comp128v2("5FD7E3F81F3671F547B94D9F6BE7A348", "FC9DCBBB4FD0580F9D196C00");
+test_comp128v2("4A316F08F912FC534D8E204129C78974", "855C814274800A5C45B3B000");
+test_comp128v2("960CB8BE1B5025F3EBBB55EB0D79890E", "E1CAF3B52DDA529D0B694000");
+test_comp128v2("282461C3FEF2ACC7F53E640AAA8279FA", "52F8D1327B2A1B5319025800");
+test_comp128v2("BA4DF10402E2E5956DE8DA1886CF14E3", "DB2D97F66B679B4D12661800");
+test_comp128v2("33B82F56356D44D6A790ADD4F4CE9FDC", "694EF7DB3E35F50726159800");
+test_comp128v2("01FDE145E42A4A0F7CC97B8CF4A216C1", "4391900FCA30173E63A39C00");
+test_comp128v2("61259899F937B7403B2EC70ED9D32384", "F51BAF50F8CE1A64DCD25000");
+test_comp128v2("0E6F0DC32693A7EF677C37BC44CE8600", "C110634B037031745F401800");
+test_comp128v2("1283E739566C46FC1041C2EDF46ACA63", "4C74EFAB8A6522A010F09C00");
+test_comp128v2("CE7B368B575950EE69D82D3CEEF59E7D", "FE9FF88A94F0A490335EFC00");
+test_comp128v2("DB0D3AE84486ED465CFD507F6B321E2F", "76D1679FAABBBF1FD3E79000");
+test_comp128v2("BBA5E23EED1E43EB3AFCE2ABC5FDF71F", "022E2C5CFE2B4C3EBDC41000");
+test_comp128v2("B0B9EDB4BB979E3CA9630F8CCC3FCAA2", "1775CD2F9C4A6DCE9503C400");
+test_comp128v2("4A68C3FBD3A74468D486308DD1412D31", "77249441DF2502778FF3E400");
+test_comp128v2("9AB0D6DCD8251E4BBB78BFACF5823C7A", "E7499452B5C45C1A7D15BC00");
+test_comp128v2("B62C6CD3A87425B29DA8E226B28C3617", "D4A1FC8107EFFD9ABDB01400");
+test_comp128v2("A094A045959D9A589F3BBD28D908CBAE", "A20ADDC80477942E1B390800");
+test_comp128v2("3C76356D4AB3CDEB6AF36AE27653676F", "F192C4B354CFDC97186CD400");
+test_comp128v2("04ADB833E08A91A26BA932A9BE9DC4FE", "A86826C01F6B291290F21C00");
+test_comp128v2("0CD25169C9CE123475F394ED818CDACC", "F0219A28F767E219F029D800");
+test_comp128v2("758F9B775D80F66C2F46F45C13D14EA6", "A20754DA43C43FC4C387D800");
+test_comp128v2("501418ED3CFC481399ABE1522CE9FB1E", "42764FF7641AB6D55C796000");
+test_comp128v2("2D7333EFC9B3DB86C43ED4A440BB02D3", "9AE0F630A52EE42058852C00");
+test_comp128v2("BEE9E64A0165F7EDEDED06BB7AC0D11C", "B436CC2DA8B4E083D0967400");
+test_comp128v2("A342BA3D24D7CE59E44C5F1E6F3F28AB", "47BC33CA9A6D94596C8B0C00");
+test_comp128v2("423580E3B8B42BE68E2FCF0805A369A7", "34A0F45FE614F578B9504400");
+test_comp128v2("C81DF1E755DCA874A97FE2C8A2030A45", "2B5D9AE2B659D2920CAF9800");
+test_comp128v2("00B065F6BBD7142C0D810D81348143E1", "0CB323266C1993A44D335000");
+test_comp128v2("D0DE9876CCA615D16BE541659FD8E0A8", "F313600462BC4C2BC056DC00");
+test_comp128v2("1C02F9836A1C0C334AC68873C1A95DE2", "E913C4B6BEDC8F011042CC00");
+test_comp128v2("CC2686858921BD2A970F3364C1BD8154", "CD6AB5F8BE1967E637F11C00");
+test_comp128v2("EC5A44CE58F654A751AD1F87C7468E22", "E5B372CDD142BA7523ADE800");
+test_comp128v2("F7B93A2ECA12B2F0A7AD2B550781A311", "AC1D56004C3DB4248A2B9800");
+test_comp128v2("95A26343BDAE219F9A77E2D68B8D9715", "B8E2274BA75A01A1B844FC00");
+test_comp128v2("D9EF207AF47750EBF69E81669A0D7168", "90BE7292A33AFF976AF50000");
+test_comp128v2("CB095420C757A88E23E845F90010CE60", "5D5267614E4868BC9019EC00");
+test_comp128v2("43DA2FBE69364674ABE7A67EA1079F7A", "11602A06755795316CC2D000");
+test_comp128v2("9F3145932F5712D7767278650BF038C3", "62F7DA68B860F0EAC85AF000");
+test_comp128v2("6AC7C8825DE45A64EE61E175A4212CA2", "1564273CFC0D44D3AA813000");
+test_comp128v2("A81B3F8E35F3B77A681E581E81992245", "DEE4D4C1C210AD3A88208400");
+test_comp128v2("DA6EDF93A83A4F3390C7A74448F3EA58", "DADEFCC4C903978B5D968000");
+test_comp128v2("FEABCD9A4566FAB5CCC225649A9B51E8", "990248308A158336AD8CB400");
+test_comp128v2("F5A9320721897F9C4010CC26806DCC6C", "4E9483834C11A3258B735400");
+test_comp128v2("6A3BD4D5FC62586DA382EEB13EC5B98B", "C0BC1F742AF062B2C57F6800");
+test_comp128v2("657502B51422A9DF5D02215A3211720D", "ACD94B7C1B42B827ABB4B000");
+test_comp128v2("E601807D685AC33884F10BEE2B0F7155", "8E555A2E5483666FF8E9EC00");
+test_comp128v2("D081597E0807CDCC345E64EE06A656EE", "252F83F942D3CBF225A5BC00");
+test_comp128v2("AE911B26A2AAAE1690541AD7C4AEC12C", "C09DFAD45B847939AA6F5C00");
+test_comp128v2("D32460B8EA2DE870AEEF9591E2B945E0", "415F30A507DFB65D21E92C00");
+test_comp128v2("3EF290EAC6ED5506430691F6652AD256", "BFDA7F8A4AA126AD5D832C00");
+test_comp128v2("96E50BC3013669B9453CECED3379BB9E", "3127EBB45E2170FF88B6B800");
+test_comp128v2("50CD8707C942E07374A604419F328627", "997A146971E7F9F173930000");
+test_comp128v2("F5D436A55F52CF178EC755D9B851C2A5", "526DA1545DB57E300BD2AC00");
+test_comp128v2("59D00634939E1296D628D762D098359B", "29CB71E3C1D6D8F598FCC800");
+test_comp128v2("A50B3BB3252C3C9E16C12396F3894607", "A1107E2B8910AAD9F4098400");
+test_comp128v2("89F8344B973576425ABDFE3466A7C99F", "3335275BFB59F6DD11903C00");
+test_comp128v2("17095FF0861CCDBBE7EC9DDD97CF9FD8", "1952EB05C39002CB2533F800");
+test_comp128v2("00319ECA71633B48AF1CF6050FF612EC", "8DD0BD48A4287D3987225800");
+test_comp128v2("FE2BC8ECEC5406BAD2992A903A516162", "4DF20DADD02875F1F6DB8C00");
+test_comp128v2("2719B57237F55AA6F967EFAFFAB491D3", "E9584869A08303981DDF3000");
+test_comp128v2("E9895484AC5350C6020D47BECFE5FB53", "183CE56373954D52E0BFF000");
+test_comp128v2("2E4CCA78B5FD8FEAE8F593DA046F7D39", "B2EAAA3360B2AABD8923C400");
+test_comp128v2("F3B1A9F9D5F83540037EA0EA78A8DE90", "12147C976D7BB13BFE9BCC00");
+test_comp128v2("DD32C4C62D3CE942B5FFBEB6D4A5F2F8", "45407DABB4B14DDFC8FA5800");
+test_comp128v2("B34F7F0BD6B2D777C0817F82E3052AC3", "FDC03BCD2DBD999561344800");
+test_comp128v2("83780E5C93048E6A2A060346ED2BC544", "6AF2A4F18731A281B75B5C00");
+test_comp128v2("B64A86EE50BB1C02134F11CB431C5069", "36AC5A0B2CB95F686E3F1000");
+test_comp128v2("FCFEB28D9732B937496FE96CD11B24B1", "E23CB44D586FE9FD19FCF000");
+test_comp128v2("61664DE891B324D7C1402E7B80C68B17", "EE3A9940C7A09853F8D9FC00");
+test_comp128v2("8B173E8CA34CF9DA56DBE336196941EA", "F703154C9730C5AA58FBD800");
+test_comp128v2("4D21217A48E9EFE24B4704EC10B85C6D", "D56C16F7F97B8CEC06BEF800");
+test_comp128v2("FFC69A387F0435BD58C13FCB913F60DB", "5F63F9197F0028ABC70F4000");
+test_comp128v2("AEDEB9AEF7408837DD4BE51498ECA4AC", "1216B27EBE081A8B58880000");
+test_comp128v2("14A053B1F95E86448F73D2FFEFC7FE3B", "2B9BD41CABF325CB93BD8C00");
+test_comp128v2("6881901AF0FE3333627FEE84795A70FA", "651BD7D5B6DB33C5D1B0EC00");
+test_comp128v2("196577B50A4C9BF7A208E4533219D468", "5E251C34B40822FCC476C800");
+test_comp128v2("2ED5196DC635F8603196BE9549BED8AF", "BA9B3440ABEF67B108421C00");
+test_comp128v2("FE09F4C92966BA06163AA644D6CE5D7D", "E07AA3D335E25B9303671000");
+test_comp128v2("18A117FDE347F64B3CA01F0F10659596", "4059BD729905578A62A29000");
+test_comp128v2("6877CF2C157251B5A97904F8EB337B8B", "2B1A0A31984ED54C1D3E4C00");
+test_comp128v2("4236F749801A93FE1FD95324A24CE1C8", "2092ADDFF5D50EE60E4ADC00");
+test_comp128v2("B9A4020F7EFDA03BCFB46FE31200708E", "80685309ED5215AD37C5C400");
+test_comp128v2("574B8C41C4594499D037D3BF5084F167", "218AF13232BA26852E95B800");
+test_comp128v2("5D7E99E0D35742E0D80A85F243606F28", "1B54FEDD20031F7C11533000");
+test_comp128v2("40EB7A728783CFB17363744C8960C966", "ABBD5EC9F78951FC73C11C00");
+test_comp128v2("6C7826A1F1B40323CF57C7909A83E2D2", "91C94F666B08EBA4EF7E0C00");
+test_comp128v2("61E2456FB48EFBDB946DF0A3F298BF41", "9E39FFDC1C8573E4DF224800");
+test_comp128v2("0B3BE969A08BF4CE4BE98D6CC0BC7309", "152512315F2CD6A3EC1D3400");
+test_comp128v2("A6C41AB03860DD83608098111AAEB4E9", "57A99A60C2B9DA9E26C85C00");
+test_comp128v2("6CD88694601229E809C13775C4E761E5", "18017C263C41817B9ED6A000");
+test_comp128v2("53F16F6A6B6D2513CBBD0D63CBCFF990", "115CC0B613458C1F17DA3400");
+test_comp128v2("4FAC9C89DEAD012D875350CCCD6DF9B0", "3CA402F00396AE4AE375F000");
+test_comp128v2("61C960DAE03E40FC1F56BC1C71D37E06", "4E5128E99E89BC59D51BE000");
+test_comp128v2("4DD845C43A81DF92E90B83504E159B85", "35C382EFF558E66A0822F400");
+test_comp128v2("2315D98737F65C690EE46743434DC239", "36A2E24DC3B5834D198F2400");
+test_comp128v2("A63680047A7750B42313F2D3B878662A", "C3D10EB4D222A75C63383800");
+test_comp128v2("3934F208F33EB583661F36869C73FBA8", "890536E7B8FEB54E97526400");
+test_comp128v2("4A9F494AE875A5314FC40DEEB9DB9985", "A14AE3B8501B7B5885947800");
+test_comp128v2("E6BC8770CA7ADC109185683CF4666D9F", "69748080DBE0959AACCDBC00");
+test_comp128v2("D2EBF8E248B694CFFD41BA7428668159", "1401C448FD719A421363C800");
+test_comp128v2("A7AF83C2D4448241547B3425A8CD34C0", "5A8B14DBC9E50CD5C3616000");
+test_comp128v2("D95594E46451C524B586913F80965237", "830C1CF57919CF7C3CDC2800");
+test_comp128v2("C00B2713CC2E4E2355DA502E1B521001", "FDDFA77D186A5B4BDBD12400");
+test_comp128v2("0B8D598E6F13F1236DACC84E456F0E01", "B471887B4A040637264DE800");
+test_comp128v2("C652FDF2912983742E177C0EA30F2E61", "C42F84FD9CD6E7FC37536400");
+test_comp128v2("6B655E5F7B56B51D7E254A9AE69E44C8", "48721AE298AF66D7CDCC9000");
+test_comp128v2("5A900B611A5B17D82D4A2EC8E6544090", "F4A51C7F4E896653E5140000");
+test_comp128v2("554C91377E43AD28215056E8AEA5DF4C", "B566C947F785B9B830080400");
+test_comp128v2("54725198690B36F0C6A4DE05B0E79E47", "BBBBDA64D8B739E6D5038C00");
+test_comp128v2("0BF03AF40DED5898A8D1C657846ECDE3", "4EE99919522D100349F9F000");
+test_comp128v2("971B0989CDE6313ACE7690BD2C2BEE09", "590684B1A33F255BB9D40400");
+test_comp128v2("32E1BE35019575931694A33B7B0AA049", "BAA62EC15FCD29F7BCC9A000");
+test_comp128v2("7B601512FB8D061C1B6F7FA423733F8C", "82C1C97CC83A920346C47800");
+test_comp128v2("F0A3B5BDC514E4F20060D83A61A9C711", "684138DD3D04B3B15E11CC00");
+test_comp128v2("4F2F20EEE54D82CBA6FA087B242CA6A8", "7F445E9AF2735B2C29E33000");
+test_comp128v2("8F357399AAD7B59AD790D55C55FBE3CC", "86DB5E7D9F1E83A870067000");
+test_comp128v2("511141F30BE586D039080ECD3822F2EA", "D358B8FFEBE9FF38F0EF6800");
+test_comp128v2("B62E2C043BDCC5A1741ECE60DDE598CC", "D5E1BBBA61272C8E9375E000");
+test_comp128v2("1F0F950D094BF35F9657D68E5018B9A8", "BD37BD448CF9A5DDD7E87000");
+test_comp128v2("DE30802335604EDE8F943169A72E6A68", "EC184C8434883302B00D1400");
+test_comp128v2("3DBB9938C7C4B14E1D5D07DD616B4E0E", "AC5A0D23DA694B6128547C00");
+test_comp128v2("D26257D4C9CE19B09774A9E0C86E335A", "4C8C3DA2D021243593C7EC00");
+test_comp128v2("B7CD31F340EE67C49D651E1C3042D2C1", "A20D985A6C900427C48CB000");
+test_comp128v2("D763E4A31FCD50F595E4666BD91B8EA1", "3BAD6E389FF168688284A000");
+test_comp128v2("41DEF6924472D84434CE2C7D96881F3A", "B0EE0C304599D3AC58018800");
+test_comp128v2("0CDEFB71C93D7B5EBBD6CFD85EDF9C12", "7C225B5EE5301B2F71E1B800");
+test_comp128v2("C2A41CBC63F2F8D34FF1E47FC9B7660C", "DF4D16A7EFB8B8BB68450000");
+test_comp128v2("7F490D52DF1330100CC6D4EEAF8253EF", "C501BF91F2BFF6B811393400");
+test_comp128v2("124A768E3593AF51297D2740AD0200A9", "4CE1215F737876BD5E89C800");
+test_comp128v2("39B8F95D2406756066A53C4E705F5190", "017D2D2F27C605B783B7E800");
+test_comp128v2("1F4511450C91823B333BF3372B1616CB", "7EDE3A2F55D86D2692B00800");
+test_comp128v2("0762815735FF7B22BB73A35F2B98BCF3", "C5F15A6612CE3312682BF400");
+test_comp128v2("3E3A81D6C95B5BE0866EA8E077241131", "0F8E225A21CEE88570BF8800");
+test_comp128v2("CDC633D112AB14B86799836799589EE0", "41D8946A92C3F5DD4D4F5400");
+test_comp128v2("E1655B3EADFAD31AD5A077A22DF28AFC", "16D609AEE5FC4A24CCDD4000");
+test_comp128v2("C73E501E952DD572189853B6BB173A4D", "A9E890D3DC0B1D4A0B295C00");
+test_comp128v2("A1D2F09DF9A48878D0124C469BD7F413", "35DF82BD4899DC7437B6D400");
+test_comp128v2("BE1CA7C52223B34080503DDD9F84FB7E", "8449AFF4E3C8F31BD76DFC00");
+test_comp128v2("44D343E09285E36C1ACC9494753A8584", "624DB556732775366D708C00");
+test_comp128v2("61D91B4E360F4F5C7EA25DFFDB081AA7", "8276AF99F7EF234573014400");
+test_comp128v2("FDC7AD732965351CEC95D5AF89254921", "7E1DB73398B30B957DF8CC00");
+test_comp128v2("1A944BAAFE83C95B9349782F5974B663", "5A51D44FBD0A2BAE38195400");
+test_comp128v2("2941043DC4CD9975CCD5B57B11055787", "5A506CD76E9E008F4141C000");
+test_comp128v2("E5547C0BE6192FDC79A0E77EC8E7D726", "6CCCCB1940F444EBD2FDCC00");
+test_comp128v2("A53F937180266161BEFF102A4AF616C5", "5287128B3152083765EA9C00");
+test_comp128v2("9A15EFCB21BF0E8002FEDADF8542D8B9", "4EFF43E5E4F8FEE77F8FFC00");
+test_comp128v2("99F84D1074D0E4A89AB99E79894D990E", "01A6BEC2FB1491F16AD90800");
+test_comp128v2("7C09FC1D74F44970D826CD75BD76E4C6", "4AB57C72DE6516B54FDFA000");
+test_comp128v2("34C365219A6CB004FFAC21098E9BF4F8", "E8DACB225DA9120AE8B7F800");
+test_comp128v2("64938FBDE2C7C8BD27DE7DE71D936F8F", "EE4AE4535176DB9BF2B7D400");
+test_comp128v2("D6F53EF3D8213BD92E370F9565119A36", "D3F4E0550CA140F329096000");
+test_comp128v2("A3728A2E35B9109737BF41A7CE3BA88E", "AD7857ACE57489FCE8475400");
+test_comp128v2("C5A46566E17F20CBB22ACBA0829B3A35", "53188D1D79B9AC71E12B7C00");
+test_comp128v2("49C327A37B243C502211059ADC2B928E", "92D49AE27BAF11CD61DF3400");
+test_comp128v2("4C013269FB730DD9B4B87867F285692E", "7F977070217337C997B27C00");
+test_comp128v2("C08BDB2794795CFA4AB1626D23AA49EB", "4248294453FCAC270C119400");
+test_comp128v2("6650F32A56DB39C02711D06D361AE5A2", "AC1869DD89ECFC26E19C9800");
+test_comp128v2("DD10CB3DEA808A7D0C1F666AA3FDBED7", "15CBA9F20FCA32D7F03C8400");
+test_comp128v2("C088FC4AABC5D08B3B842E90883EF20C", "B64B2FB41EB4CEA224AAD400");
+test_comp128v2("9C3658205884DDE93389FC595DA95F3F", "A9C1907D30E26D51BA496C00");
+test_comp128v2("D386F98BEC1386870DD353EF8BEFD0EC", "9F2DAB5417E2537C78DF1400");
+test_comp128v2("32D13296BD30E931C2EF187B9C07E676", "099E66F116225F2E5FCA5400");
+test_comp128v2("1B3F10C760DB7647994D6C1AB8B0E27C", "88B59F43AB9F746C3AC3D400");
+test_comp128v2("7F56EFEA97E53ADBD8A28E6784D4454C", "A3A64F72AD0A3D1414876800");
+test_comp128v2("DC15015AF77CA3472D9C722ED2C6765D", "202ECCA412CA0903D2E46C00");
+test_comp128v2("5DF5D4D07734814F50D24C28D4D133D6", "75445158435F68E6138F0800");
+test_comp128v2("0AA8ECCADE7C3DDE1E33CCCA904F87A4", "7C36F6063668D0871E35C400");
+test_comp128v2("D5DA6CFA0EA9C893E0C31EAB8F07C265", "A4B70423146ED051AB678C00");
+test_comp128v2("CA0DAB9743D4641EDA047DDCE7AECCA3", "43B666DCFFAF925608CA3000");
+test_comp128v2("C8B74AC8B6096E5884601F4E1DDBD174", "1E8E15E0F1743837DB3CC000");
+test_comp128v2("5F60A4B2AC4B3FE14259CFF7B9B9B9DA", "6EB7E1212271898211BB8C00");
+test_comp128v2("E3CD19D5573DFFD6E493C4DD7337758F", "13130B7A1E66FF00ADD89C00");
+test_comp128v2("9836A1AD51EE33A05C1AB926D13177F3", "803B815E3D4A015D69AE0800");
+test_comp128v2("2A96EE958A39E9F57FD81B873F32F0C6", "82825219B94D4148364CEC00");
+test_comp128v2("DEF7C00F0ED8BFA5E29FB0316BA10293", "C1599BCEA9E04812F33B3800");
+test_comp128v2("CF8A6B0BCC3285827DB9D471479A515A", "60236043F506676EE874A400");
+test_comp128v2("4332F66E5C8FE9B2A374A807ADD3489C", "E76B4BD39247441C69731800");
+test_comp128v2("B6B1A9D4F4B82AC72D820EF143F52BF1", "BFCE82DD74C8ABF9F4C3F800");
+test_comp128v2("41955313F015B3E391555E7EFD20D94A", "CFAE1992CBAFDC38113E1400");
+test_comp128v2("99F3AF88D90DA2AB226EEDA86816B73C", "385E9AABC4B633422F7F4000");
+test_comp128v2("E497BC27A587261123B53562665A0421", "EA6B9CAF5418DEE2B0C48C00");
+test_comp128v2("A881F3D91DFBBB74427980D76B6331B2", "E1258AFE12F8EFE020B58C00");
+test_comp128v2("8A5FB6205C5BBA89FE76DF2D32C9667D", "04608F77B0B37BC1645E1800");
+test_comp128v2("E282B8E50A4C89F996967FE3C97D4376", "A34FA42A7429F439A09F7C00");
+test_comp128v2("1BEA1A3C719CFF988D49FEB1E66BD4C5", "58095EA3DFF0E4F55C649400");
+test_comp128v2("D21207460046447600836E81933C2987", "5D3B6182D866461F597F5800");
+test_comp128v2("86DC003A1E7352404118C4A6BDF522D2", "522B03EB0142EB27B813C800");
+test_comp128v2("FFF92D1920F3AAAB2ECE380817DB739C", "8D193BADBFAB72BF8AB02800");
+test_comp128v2("6D169D93541BDE4543214B3905A573F9", "3026B85156E7E48078DD4800");
+test_comp128v2("5E8B626C2461A81224E29EE751E578E4", "DCEC75A33337B20D084DB000");
+test_comp128v2("E5DF990790E1F9D245F0840E90C845F9", "5D43D9C00D5DFEEDA9632400");
+test_comp128v2("4F1733D5BDDB0FC3BCFF3A095EA63FAD", "C3190679E66A34EF6A7FB800");
+test_comp128v2("47E6B7544D18B2FE7A6910368080CF69", "8AFAC9EA29F6FD5EF3CBF400");
+test_comp128v2("A015E3BC2CC8B20D1F9BD245BE00D935", "6F8DDCB4C9CECC28493E8000");
+test_comp128v2("850745B3A941130D04F76F07BB7DBD2D", "CE82AA1D16E2D020A1BE0000");
+test_comp128v2("F9EE213796256B136B6503C06FECB90E", "BCB277A4EF1AA561D1341000");
+test_comp128v2("326CD7C58707E7D934772FA1A14E16FA", "B8DE63537B49F9985ACFCC00");
+test_comp128v2("8D495E6E54EEDDDF0EEF870FE4952568", "CC5F16CD030F0E8CD1A58800");
+test_comp128v2("74E8879783B60C994471D29F7D06168B", "376BC13CA72BB41E5C3DDC00");
+test_comp128v2("9EC9382241ED63D96B4D1C32344E1AE0", "D83B4BECB683510AB6948400");
+test_comp128v2("F35E046CB410739F29DDBE66DD5F37D5", "1A5A5308BA48B041DB62D800");
+test_comp128v2("286C8D04A4E136AD2871F6173640D84A", "7A89F034584E9F8550028C00");
+test_comp128v2("A16B18F2C459C143EEFF761842BB578F", "484459E01603A7A52320AC00");
+test_comp128v2("A8188E3D3E1C1BB3193E45E3D300FDE2", "C1B8C534338CF19DE6EC4800");
+test_comp128v2("5C04D2F4663E98094BBF664207CA5AD8", "35187E89549EBFCC31894800");
+test_comp128v2("21637583D2915E0AC78C44BF8D6B8529", "73D4A069B4EBE3A7F9B3C800");
+test_comp128v2("7D30DF857DAED4EBC23AEFF59558B1B1", "1905B164D13A1493F43E7800");
+test_comp128v2("0761A9113C550E71D4A001D6F2651E62", "191662FEB0B15243BAB64000");
+test_comp128v2("1DF95FF090CAF75AC07F888E70F60BCD", "58EB8C5A5CECAAF234DDB800");
+test_comp128v2("2A4E9F6AAE94DE7A282FE59371E75034", "14B519C690ACA88942B1A800");
+test_comp128v2("50A4BA3A673775C82A273690ED2FE960", "92EB9C62BF57A3462A081400");
+test_comp128v2("89E484024EF1C40A108D261A39298AFD", "2F2903D5DC9FFC4AAA17DC00");
+test_comp128v2("10DCA6BDA94F80E7239120EDF9231AC7", "67B830467AA2C76B8A83B400");
+test_comp128v2("70A961DF5CCF1C45D26AF50541C3ECCB", "B19AC7232C7E7ACEDFF6F800");
+test_comp128v2("7B338E1357DBAF14CB0D1AB035928F71", "83EE66B91C1CFAFBE8C5A000");
+test_comp128v2("F68F8CB5C1EAD184564C0B518D2DF608", "35E056CBD8D692CB54555400");
+test_comp128v2("A0B4F049C406ED5FCC1AE8FC690BD520", "9556CEF79B478627D5D4E800");
+test_comp128v2("9E8F808AAC6301867025A4C7B247FBD8", "58950816A5768A4FCCCFFC00");
+test_comp128v2("AC5DAB1E100A506A6114696EDE6A064F", "94A4116760CE7D8897D08400");
+test_comp128v2("8601AA0166D16DBE7320C9158D0700EF", "F4E00766FA137211703F6000");
+test_comp128v2("7D0E95EBA4BD16E3C6B538FB723FAB93", "3D231451211EACBA3DD42800");
+test_comp128v2("EB41290ABEDE397978C898B02219908D", "5EB704A737FD1C97848CC400");
+test_comp128v2("230DAD3A95D90EC20EBC8EC573781B06", "CC0A164A00B54DBF2BA99400");
+test_comp128v2("0769460FA069E0DE021693AF2B923CB3", "4AD863E830BE2F6013FD8C00");
+test_comp128v2("DCF8186B27A4D60588375B514EE8217B", "6BA6F69D924AB5B806C22400");
+test_comp128v2("E17C33D2F1B42CC9CC4BB68C372B86AB", "A315B3B4BBF2AFACF82EB400");
+test_comp128v2("C74127FCD69A7DEDAF3EC080DD794C2B", "A288120EC5E5D774E60EE800");
+test_comp128v2("D148DEDDC094E552A75B362858807E76", "037A7E48A6E230437E1AE400");
+test_comp128v2("91F0824B24B3190318CB89B0758B6821", "D608EA0088C201318F3A4400");
+test_comp128v2("7F5D92535D449D7412E16856C9A9CC83", "8C126E8E0155CAF47607AC00");
+test_comp128v2("22D7AFB76E71CF4AA209B7B5B8711551", "7C7707A33EB1CAC94FE51C00");
+test_comp128v2("8848CEADDCF76EC13D5D5309594E7A58", "7E141BD3C032FAAADDF1C400");
+test_comp128v2("76D1736CF997CBD643A242F805917ADE", "4A422DED87839DE4F8639800");
+test_comp128v2("96B7E7197A05CBDDE68CD566E833F907", "62E205A674974554C7F5A000");
+test_comp128v2("928AFCA1A1DB27340BC5689F9ADCC3FD", "9A7A16C8AAA1A89D28CBAC00");
+test_comp128v2("566561B80926991BC84FF3D782C43533", "4C67249BE59008455BAD8400");
+test_comp128v2("AD7CCE408FE4D908234C4B9B7AD4586F", "54FF6E91AEFCACE32C6B0400");
+test_comp128v2("270BE9B26BFD8E8F399D63CE342C3AD6", "BD3B66DBED973FD195CA9800");
+test_comp128v2("6B09662F3C200C2079B15CCA03EE8550", "BAA793152333F7B78DE07400");
+test_comp128v2("52A0DE611C43E8A2AA08C73B79122EF0", "8B619DFBFDCA21ED3246A800");
+test_comp128v2("089249127245C7364E2DA1F4B12B4174", "1BB9BECD4723DAB665D9B000");
+test_comp128v2("78A4E8D674E7A9BAF1A2EEC2BCA52C83", "1F4A8220DC3B790194AB3000");
+test_comp128v2("D69D8E89A9DB9FDF5008A490DCA35932", "97F23433085310FEF92B0800");
+test_comp128v2("303EC59372B82FEEF9776A77B89AD1EF", "D62B685B0469F2DCB8431000");
+test_comp128v2("BF25B6FBA7D5B955F81A196F603DB6DD", "9EEF5B576A0D5904CFE16400");
+test_comp128v2("A034EDBF71F3789C7AD0B7DD3E2983B7", "F3955289B3B925D317E41C00");
+test_comp128v2("0F64D014BCA2B53B1FB6C5267C6AA869", "176AD9DFEF9FD87C1B26FC00");
+test_comp128v2("E8A8E6665D25A32DEFED1BBFE032178D", "635E89FD4FAAFAEE81A6E800");
+test_comp128v2("D61E52FB7BDDB6B08D79E5727701E327", "1A8FC1CFEAC2D83C73BEE800");
+test_comp128v2("F526D40E8A4791776F76B754BCB6002E", "42230C725900932FEEB32C00");
+test_comp128v2("33274BEAE8FCC71C0547FCD8370F53D1", "A647CFBD2B6015A5071F2800");
+test_comp128v2("71B260044E7818F625536196647D609D", "9F6CAE8F4BA7E18167D1F800");
+test_comp128v2("494D5B1DD29C4CE82825A0DE60D75B28", "747D3A2FD2456A63434BB400");
+test_comp128v2("D57BC3C7180DC541ABFE858D10462A05", "171FA63B591C6B7002CB9C00");
+test_comp128v2("75CDED92F2DA05D5F5A180371FE51F0C", "8D6064E886661C7493535400");
+test_comp128v2("0A188392250D0CDB92FD3E224332F7B0", "788924D7FA3700DBC29A5800");
+test_comp128v2("15D4AC837FC73BA6C4A0FE899D09718F", "7CCF880D84106A31F5F07400");
+test_comp128v2("AC453FA3FDB5BF207156144B24AD1E7B", "F9DEC85BC2B8D3996D094800");
+test_comp128v2("71F2AD4E06BB7F988C5004A0357F6970", "DF76F1CA8D1203DCFB8F5400");
+test_comp128v2("9ACF6CF19BB44FEB4D95541AFC4A14A0", "261B9A333A06FCCEA0D19000");
+test_comp128v2("3E26329310742282F2B9D1E3DC25033A", "15020ADE64D319AA40610000");
+test_comp128v2("A6DDCDBA584140D229FB839D398F78A7", "3073F2C778550F75EC707000");
+test_comp128v2("D3AD058E5268095511BEA6976BA94D02", "6C2CC3C6B0325705D3393800");
+test_comp128v2("E4F0FF1E5296A93BDC84A3B0E03CD043", "5DF3547DC840423AD633F800");
+test_comp128v2("DC5F59D24CD8961B0872CD4264F240E3", "4038666247DF1CBE432D0000");
+test_comp128v2("BC9CE92196AEFEDB4A9C93D9CA8A006C", "3EA4747DE9A8B930EC780C00");
+test_comp128v2("A7FDBBF43164D9C679C09F010ED57120", "A0D97A4D4B329DAC3C1D9400");
+test_comp128v2("885F62DAEC03DC5C7C3B6D2985F1989F", "8E8C3BB5767DEB1A84B8AC00");
+test_comp128v2("2BF7BCEA0DAEEFED5F1B9BF67C59C42F", "3E83E6A4BDA567C0C1C37C00");
+test_comp128v2("9A90204CA9EBEDDB0E8E270C232430F4", "FE65162A4E81EAD4C0FAF000");
+test_comp128v2("983A16E1E9F06C1E16CA66D0024295A8", "A31960B3BCA0409A77D76400");
+test_comp128v2("894A65F0863621F6F4B0F7D2DB52AF61", "EABDE0292515ACBA40C8E800");
+test_comp128v2("F12067DCF27C55BBEE33B0856124CD88", "82F1FFC3EB594F53CDF39400");
+test_comp128v2("81821C13A89580DAEFA775D37D75C58E", "6DE6DC3F4AA7EBCF3BF69400");
+test_comp128v2("296F16BACCB5B66E5316A21A82E603AC", "CC5D42E68A362BC4EB60E400");
+test_comp128v2("5AD7081575AB7E65BA8DF2DE3B6B7909", "CEC011CC38CA5ED64CB93000");
+test_comp128v2("84AB79B6A3A1F005563210592BE8604B", "63D53A5C888A72E5A18E0000");
+test_comp128v2("06DB39540FFAFC31D275F3E56EB15824", "88AEDFAFE13E7632EA7CC000");
+test_comp128v2("9E38292FDB67242052D0A445014910D0", "41BBF707586B1FCCCDE90400");
+test_comp128v2("A371E96FAD96E1545D8E8808A07E07E3", "6A58E0D91F95F6DBB18CCC00");
+test_comp128v2("BB10FDDCFD8C047EB0B51E96048CE459", "2DC85575F29CA4F11FC36C00");
+test_comp128v2("5B4307D70E43F25A9CFFFD56370BBC94", "9F3DD6CE52DAA89234550C00");
+test_comp128v2("42FA4C38D32C541EC2F1AD27DECA6C67", "13D083584650096DF3789C00");
+test_comp128v2("B8FDEDEDCB6E23CF14E6EEA25DC419F7", "09F8ED29BB3C6C40A9196800");
+test_comp128v2("2F83B170565F4977A89E3E09477CA1B3", "E1053E83AD4F056439AF0000");
+test_comp128v2("7C2EC5FEA03C78F65D7D711DE30D76A6", "CC39D08EBE81752FB84A6000");
+test_comp128v2("1EE80FBE225CD938D1800459985B61EA", "688E5F40E8C20BCD7E90AC00");
+test_comp128v2("0F3D06972170FCE7CE9C7511293846D9", "D90B439D68E4F9229AF30000");
+test_comp128v2("B15AAFD4778B30CBEBA6123A2DCF242D", "416073391C2A09D498896000");
+test_comp128v2("F39B05FDE23CD3513FC1F9426CFFF0C0", "C833CCF0BA49B3417F93C800");
+test_comp128v2("D778CA42BBA1BF18D77DFF99BBEC939B", "F3878F86134C23289EFE4C00");
+test_comp128v2("E54F1991A18C175859242DB288BC581D", "589458131E50C6D50B946400");
+test_comp128v2("3C5BEB096F9D117872C665B719EE1477", "C39175020428B806D442F400");
+test_comp128v2("9412AB06825CA3411BBE178480A7D1EB", "70B4716B4A43ADD6217EA400");
+test_comp128v2("06EBAED1B90E8A2D25C3AB1973905D5D", "ECB251D81D64096720ABA800");
+test_comp128v2("FD229FC2ABD4D5CB1E24FD63EA77AD66", "4E74DCC86725C9249311E000");
+test_comp128v2("D87E8FE1157A93AC00DD5D2948ECCAA3", "4EB7C5F4A40577BA424F0C00");
+test_comp128v2("30884DEB37B796F54A3C04F3A86F010D", "3A2F6B2DB3E7387C9B85F400");
+test_comp128v2("1BAC80F40908D0C03313C33CAE26F5DC", "9646ED009469732EC3EBCC00");
+test_comp128v2("CD302E7008E2683A7244AADB9417E501", "7839F2AE68BC906AFC06E800");
+test_comp128v2("1348D0CD2910306D9118E9796E303C1E", "FED0DE5F032BB902E4354C00");
+test_comp128v2("70C3FB8FB3CA6942BD7C1DF5EDB10180", "7E9B37DB18F1AFC4D28BB000");
+test_comp128v2("A4150041B47229B850660FDB84B65828", "D8A37D0FB9B2675AAFB9DC00");
+test_comp128v2("5D67CF90EB0C63FB4951C40B5F91259E", "7CFDC3D570F1E7C38BBE5000");
+test_comp128v2("88017172C4612F91C89EC43868C6FDF8", "4E838FF2E0008654C5CF0000");
+test_comp128v2("23B4A866497F2C4A67353B396936CFC1", "3FA08D495D5063E30EB6F400");
+test_comp128v2("ED2550ACBCC9821894FBDF3DBDC93297", "A45019C0B1E78F60BA837C00");
+test_comp128v2("43C6573D014B23C691BB0DFD2040E9C9", "2901DE457C41F792D77D2C00");
+test_comp128v2("AD204E6AF70B423ED521A4B3AEBF3358", "AF6620C0D42EC71B29135000");
+test_comp128v2("EBF14EDEEC94BE21F6E2521841B71B4F", "C32B0095A611EE1FCF8A0800");
+test_comp128v2("E4354B7F94D37F8974E8912FE63E65ED", "A8844009E0A67032FCB39C00");
+test_comp128v2("5F5F2AD08E071C324A2A0A6FFC77B441", "61BB6EBDFFEA61A3DA29E400");
+test_comp128v2("FAA048F624863639327522D41B31D392", "971B2475B2806A4741497400");
+test_comp128v2("E98C633FB91EE72FC4F0CB18119A67E1", "FBDAE08872C1CCEC77EA8800");
+test_comp128v2("6A8815193A45732FF6CC06AFD49F9316", "F26191BB253A328C7567B400");
+test_comp128v2("FC8FF54ABE8958DB01DBE87E8178A69C", "E691DDCFEF1AB2B6B069A000");
+test_comp128v2("027469E5A3803DC4C2876D95C1C678C6", "2438B8CBCA2A88A931EB9C00");
+test_comp128v2("5159283F05722548C74A737741CD6509", "D46ADFFFCB068AF092F12000");
+test_comp128v2("6AC3B272DD8CC8BB745A8D0B36C211A1", "DDFF97F3223A4B6BC232F800");
+test_comp128v2("8AA83372D1D5EC88F72ED111E27A448E", "2E229F0F997B25213222F000");
+test_comp128v2("3E7608319DCB2A0E65EC9AE2084EA982", "C8B76DFD9A01A240E1B5AC00");
+test_comp128v2("2841232946DE470D2FF755C70C19905E", "DDAA67B52DCB035E9A2CF400");
+test_comp128v2("A944542DF2373E12823C99DEA8DF170B", "0808D4ECBC4D77A0158FD800");
+test_comp128v2("A2138146A6A0E2E642053DFDAA9E16BC", "74EF1291ADB07C95E02E3400");
+test_comp128v2("0DDFDD1D9A82399E61C371CEA1F9CE38", "B58BFCFC24A6D75BCB169800");
+test_comp128v2("9FB3D625B946962B54E2C17B20C42D45", "D7D987D5BF9DEDA710CA8400");
+test_comp128v2("1B55779FB61B29B8BD86771D0BF16D63", "3686F4642D1F419AF0698C00");
+test_comp128v2("39E34A86EC6FFB7055E7FAC2E0C974C4", "D271890EA753A7883B0D3800");
+test_comp128v2("B4A93386400F4CA3FDCA51846BE80335", "457CB81CF3489FE6AE1BA400");
+test_comp128v2("A313371F69544947FCC573B943DD9183", "9F548C3892CFF8A485074C00");
+test_comp128v2("2012EFE25DC9AF67C46C0CBCF2EC7591", "4E80A17F41BD8EA8C55F1000");
+test_comp128v2("486BD31661FFCE74A65CE0D558850D9F", "B613B59771B8352D12C27C00");
+test_comp128v2("2186CAAECA47991101726EC2C6F6E67A", "BEDAEA0EA1943FE1FF221400");
+test_comp128v2("4D42D4D87D70821014D0CD86BC38DA4D", "A2D95578B1FA0E16B0C3B000");
+test_comp128v2("4DA30AC34F7EE91EFBBDC6DF5555B76D", "6937A53FF44D36E61351DC00");
+test_comp128v2("795C15464D421AF29BBA1907DD10F028", "629184CDABC11E3CA0C56400");
+test_comp128v2("B3CE0E7908B649E875005DE7E7FD1FD0", "24C51AEF24FF49A4A4747000");
+test_comp128v2("D957396D9E3C4AF53C89E45C54DC0A5B", "17161024C0F22E10A7430400");
+test_comp128v2("43B416FD921D9BA3CDA47EA123386302", "9810CA384A6F6647E51EEC00");
+test_comp128v2("0B03AE1B7D664FC5B96E2A914C633D89", "DC3FCC1CB0099F7414302C00");
+test_comp128v2("4A6BEE40BA96A40B7CE9E6844DFA73AC", "E9EA864579D55C68B4B9D000");
+test_comp128v2("70FDC71A73F676D9C4EA330754B901C7", "049ECEC4A332DF7B51D98800");
+test_comp128v2("7E61A21AF6B328C26E1D7325DFA25B36", "D7D91E06B4C43EDB795A8400");
+test_comp128v2("C666E78CA92F658FA60C350504BC09C6", "4445B12A7082935CBEF71C00");
+test_comp128v2("55020E9C0982E00235CE187B034B8591", "3B0A8F08523A46377BB9B000");
+test_comp128v2("ED046D0B7B672F496ABF1C835EAA3C84", "64C19D9FA085AE2270B45C00");
+test_comp128v2("175A73A9CA9507BC08465DFFA9B8A496", "59D1449FB47C046EEF89A000");
+test_comp128v2("07C70D392BD451EE506780DFBD5DC83F", "12B436EB13A2F5EE6F3B2400");
+test_comp128v2("F91C460017B34AA789A219167F5224B4", "D4557BAA009D10CF37E70400");
+test_comp128v2("1DA7C45EA6A64514FFA689538ED5ED53", "4C6C7DB8436151A89E15D000");
+test_comp128v2("E87DC47A13D472C5DB246BC2F8D2DB72", "F587FF0ADB6832FBD5257800");
+test_comp128v2("FC7EA44A9CE859DD79E3D01FCFEDE8DF", "7A8E075D75CEC76E2E79D400");
+test_comp128v2("6A03EA7604753B3C008D3DB463A7442B", "47CCC2AB07276A6A9BF0C400");
+test_comp128v2("0017953B084C3B7424D0687D9C02A837", "4A0117D0F840E0A91E2C6000");
+test_comp128v2("EEF338BCB48C15A37795C69AFC7C570E", "EB007C6F30EFF093E499D400");
+test_comp128v2("53A6A76FED38FF6D62777B449E5B548C", "F00CDCCCC10ECB062682A000");
+test_comp128v2("5C70D559D6F8B8DD99751D56C418FA35", "E3C5F362FF9ACACA40A3A400");
+test_comp128v2("9CA9F9FEFE37D57BF633754AF7B3634C", "2534834DE550FC11D9268000");
+test_comp128v2("0C23013C1C3C0954638F9414DF49E0B8", "A51D6BD87F72607CF707E800");
+test_comp128v2("76D341F1D8D1C65514A96277AA457C54", "8900B6FD332891AFE38D5400");
+test_comp128v2("647466BE40774C458ACE1760FB2CBFFC", "EB85994EA6C7F2A785620000");
+test_comp128v2("DB0CC7E41AC5D78A9A09184DF4F90553", "21A4C2684DA076CDC5F05C00");
+test_comp128v2("582C3F28F5E1380932C65D3544B7BDDA", "2006B84F9FD789AB7EF88000");
+test_comp128v2("5F823650EEE1F31CEC3AADEFC452E429", "80C0352D91A88126384F8400");
+test_comp128v2("7D0288577305FB4CF1979056E527E0B2", "F37749459DF1409694865C00");
+test_comp128v2("45566B3BCE18DA92823C95C1D28BC600", "63290D3ABC575FFE70B0E800");
+test_comp128v2("BC54B44285B1123AF6D3EDA43A5446A7", "0787BD187870B36B47403C00");
+test_comp128v2("AFF23EA0922CAA395D87CE8F71E686FF", "EA9A6507116E59F885954800");
+test_comp128v2("81406FA900C3787CEFA83332ECE83ADD", "B9A1B23CF74881FA72F13400");
+test_comp128v2("17ED1CA689D152C83DDE7C21298B1621", "B9DF2AD19F79E00D39BC7800");
+test_comp128v2("BD24FA43CF93AB8F615A2CB55205954E", "8FB777F02793A4A9E96BF800");
+test_comp128v2("6A60A9D4076A4668ABDC434CFF4F4BF1", "3874A5322A30FC61EE2EFC00");
+test_comp128v2("B4A18EC0063E6B403DE774801F83366A", "ED58E3518559614FF3CDC400");
+test_comp128v2("5B4F77B27473E3E9E9F37A270B85AF0C", "3BE0FBEDB313262BC0BFD800");
+test_comp128v2("AB88BAA258597D5459F9B1279809543E", "67E5E4AE690246C0D9E9A000");
+test_comp128v2("A2951495A11BADAC3A91F653349FDB05", "3E6FBD4969707A42327F8C00");
+test_comp128v2("AF7D610513988261234BFDCA9174D852", "7D1721E9A2057DBDFFC78000");
+test_comp128v2("ACC7B916F1D53C75BBB5EAC0C0C9155F", "5B50D39BE87E86DB3FF7C000");
+test_comp128v2("463C7145B20B01098C16142DCD76F3E8", "9D4B45528F381B44F58C8000");
+test_comp128v2("0E5E028DDFA0A3C0C879FA07C9FE0B11", "366B62B32BB6DBA922F9C800");
+test_comp128v2("131524709A0F2C72CF897E34BF7AD533", "8B444B299DE38B10C9931C00");
+test_comp128v2("B2BD87BFF31207E0584989577E010E25", "58798AFBADD4F313CD4E4C00");
+test_comp128v2("878C4B8B7047A04F24B85A039CF1CA08", "134AAC5663497B78E9ED6000");
+test_comp128v2("4372DFCE989ADEAF69B3CE5541F2D44B", "5A4EAC4D5B411199843EC800");
+test_comp128v2("F4FFEF059262212C6A9440F4BBA67303", "2147ADD96FADA5C3B0A82C00");
+test_comp128v2("ECAF923AE9D823A2B5AA234584FA1956", "A7345726E548029DE8C13800");
+test_comp128v2("BF988D41A1D5C109D0706B2068ADFE14", "C3CF7CD2693FFD82DCDF1C00");
+test_comp128v2("449990955002E4111D56E0C20946C6BC", "63437E1008262606709BAC00");
+test_comp128v2("2EE20958580CE4F8A8BFEFFCB1D28D0A", "C5F9DBA0853C4B619979B400");
+test_comp128v2("B6AB470BD27B24DFCE750B3EC08EE38F", "2E4FF932B76DC48617597C00");
+test_comp128v2("8AC1D7BB4F58B97A4973F3BE4AFDE91C", "5E1785F6777316F60EA67400");
+test_comp128v2("495AC23A71E47B790E70823F557301A7", "97A06E87B1351A61E82E4000");
+test_comp128v2("F62F24627A42462892EF3AEDFBD86BCE", "FAAB2671E38D5F25CC2DF000");
+test_comp128v2("60372B92D3EE37510355F7CA2FF1AD89", "248B52F48F946258FEBB3800");
+test_comp128v2("3A6CF14AE01A0CA1562D639A97AE49C7", "31F7D19EAB76B3A4015E7800");
+test_comp128v2("D0FCAFC71D9A145F839F11688295A8C8", "7772C8610A17D87744943400");
+test_comp128v2("842DD75BB5B1E7E201E248663A83CF62", "4C43DDCC9F35ECB2ECBAF800");
+test_comp128v2("45AC11764243D64DFCDF35443F498A11", "CA15ECFAD53D0E487DA51000");
+test_comp128v2("F0A05481CCC3E9759495DDF4D061F7FF", "5284304B591163CF2AA35400");
+test_comp128v2("FCAB0F4F0DC5DDB74F6F45C3DE45A890", "1430B0D94E51B3F0970B2800");
+test_comp128v2("96705A918F3F7DC4779EEE959DF56DDC", "11CD9894491DBACE093D5400");
+test_comp128v2("3324BCE404ABB6FFA1B815FCE3653504", "10B5BF4FAD33E64AA1446400");
+test_comp128v2("69C0EC0F65DB292E47D77861648220C1", "BF307A6E2EAAC31C03ACD000");
+test_comp128v2("2CD92262C563D16CDA7076A3916B025D", "C3DC96C3F8982607CB34F000");
+test_comp128v2("3874E5B8CA16D381A0B33E8846EF7678", "826BF9A7CE95C140A965CC00");
+test_comp128v2("3368842420F16B5476313F02B16B3CDF", "0C34B5F2BBD5B6D336B5A000");
+test_comp128v2("669E584D4DDB2E1EAE7E914C8541F311", "B9246D668912C36EBCD2D400");
+test_comp128v2("8D428F26986A1C746CCA262426C7C0B2", "7BDB274C86342B7068033C00");
+test_comp128v2("02B97CAD06351C5C3CF86D396020026D", "CB2BE5BC456ED73635071C00");
+test_comp128v2("B4696D369884E4941D0E087B32E09D2B", "2BB0A12257D862AAA55E0000");
+test_comp128v2("4EAB87DA796EC3488C342BDF311AEF22", "23A0BCDE4B0DCC2C8D89F800");
+test_comp128v2("DCEBEE5F1C4C7A4E8A83B6D4CB236AEA", "84C90910A45004A63EEA0C00");
+test_comp128v2("55855957EB252F0C59A6B1EC4506B9A3", "9C693CF049FE93DD6D755400");
+test_comp128v2("FDE4AE6CCA16EF7FBC9AFE8067FD4E16", "1E9A9CEDCA101D2193375000");
+test_comp128v2("3600B73545F54A51BFFF6ADF932AEBAE", "7CC316918A7AFFCA973BEC00");
+test_comp128v2("202D67833ED8B105BDF66722B7E64F7F", "540ECE8E6B38E99DF7FBB800");
+test_comp128v2("1868360A46FACED93D287EB937EED141", "EBDFE734375B0DAEE15E8000");
+test_comp128v2("F65A50219A711BA33E5227885C5BD27C", "E87D7741411E095602402400");
+test_comp128v2("FCD82A0A445AC9373D96BB9CE8AF6B61", "517805CE2B3DD5310B6DE000");
+test_comp128v2("AC6AC085B92AB68E52620E350E1BB3F0", "758E969F192A9B2B20B20C00");
+test_comp128v2("E5A32C2C398BE819497923869A8D3316", "97FC978698E4FF67A0F0DC00");
+test_comp128v2("C7BC59BE121995B4AEB0332CED5A1198", "B5CC69159E1C6047190D4400");
+test_comp128v2("827E9C6B80997F053AE704994119544A", "C073B256DE3FBBEB72696400");
+test_comp128v2("7702362250B6F5F6F323230C598F9051", "1BE3AB6CA52CB8A98041E000");
+test_comp128v2("00E5A0BD996A84C310238A05A5AE7495", "A31D2FDB0196FA415CD15000");
+test_comp128v2("8D9CC3F666512DF3DB2056CBE7F62684", "1026ED1657B49F41317A5C00");
+test_comp128v2("BDFA3EF9D36448BAE383A49FD6A977E1", "BB2F552110C1F5872EDD6800");
+test_comp128v2("18D87AA310A0CB9B3CC8EF54439FFCC4", "FC770426413A7100746FCC00");
+test_comp128v2("992813CB979D33BFBAC54D55CEB4B73C", "D0148FCED4882E99B5550C00");
+test_comp128v2("833856A2E8B0EE42A5EFDFE505A2A98E", "C21F2CA3950B24C3D5718000");
+test_comp128v2("D16805B6CFAC637A2A39924114036E5D", "322B554745D2B0DC386FAC00");
+test_comp128v2("2E627A1B378BDD2A3ACC7486963D69C1", "00B201FDFC3C4AB6C6633000");
+test_comp128v2("86F68E8A8FD9FE2457E9E1FE47D2523A", "E4FC49F68FFEF7ECBF3F2400");
+test_comp128v2("F72BF5374387A730D780174FFFCF41EF", "80396F47B01D853208C4E800");
+test_comp128v2("07A2299E7A5F5D56C0070412811B02BB", "EC7B17DB8911F53B2F97BC00");
+test_comp128v2("EE2F57A690134B89D2E4B1C219CD7E03", "F492970E081E648CD3366000");
+test_comp128v2("B2DB578B1D559A2778CBCB0216EE5144", "B3253ED8496B17C05D7B7C00");
+test_comp128v2("6C80254249001719EA40C51F2C4BCD4C", "36E4172968201F89069D3400");
+test_comp128v2("D1C28E94E15762073DBEB812F5F1BEE2", "DBED6738EA5E202530C78800");
+test_comp128v2("0F784B5CAF051C4FE137D2FE59A28C00", "D6008F9F45FB8CC50677F000");
+test_comp128v2("39B71CEAC0104DBA6743E2EC285BF7DE", "01FAF3FD3A79579778F7BC00");
+test_comp128v2("261756F9DB55FDFC1116091B8D6BD58D", "50FCAC1DD42FE079AF7BB000");
+test_comp128v2("57E47170F70AB261FA998017D838199F", "88EE9756212F1BDA4E12BC00");
+test_comp128v2("02DE5B5F4AC153124891E768B5E3A1B6", "B42185D92AAB6A18859F3400");
+test_comp128v2("224E9EEF82B29CF691BC9463D9FFC129", "DB92040E46BAB90675E89800");
+test_comp128v2("524B0B8ED727F299E59957FA271A50B5", "456AE3A856C6FF5A6BFDB000");
+test_comp128v2("242A954E612E1F0194CE8493A4CD1C18", "1F607A7E1DB1212B24A6E400");
+test_comp128v2("A61AA82441B24BDD06AA6BB2CE77CF27", "58FF4B1B3DFBB3E3726E6C00");
+test_comp128v2("E01B82CC43D3D72D8CCD60C92C62EE09", "C00041D883E5BAD0BF03AC00");
+test_comp128v2("3AE3E93F044396DFBB5A529A26DA2553", "DB62C24686FE8014B399EC00");
+test_comp128v2("653A180F0F7C98B94130A5C40BD11EBE", "9B9BCBC09E09E8D559ED1000");
+test_comp128v2("CB92A03B9A262683AA1A39CCF8866BA5", "BA1BDB72BF6C2A94D92E4000");
+test_comp128v2("C79C8FC1619F413BDEDA3F9F8FB7AB19", "2334AFE1C799B9815C7C4C00");
+test_comp128v2("71DC6F9B718457B72CBF948304DF4168", "13230113D8C0A23FD5BA9C00");
+test_comp128v2("A11D3C634DAC5379B5970D401EB6C512", "CF9956024F33B4A965371400");
+test_comp128v2("05B7355A6962EEB393814579339EBC53", "CA1CD0788A724158BC84D400");
+test_comp128v2("D494D03F59245A4FD88A87C4E842F8B3", "C8409116E5BA783DA8B89000");
+test_comp128v2("FA2235589D8182BF643DD68FBD3924B6", "447CC4020F9F87565B5B5800");
+test_comp128v2("3537CDFE59252A2054E7EC1BC16C5061", "9C8D45FB1480BE389112FC00");
+test_comp128v2("D81AC6DF80593B62E4CC6D51EBF682BE", "DF6CAEF62BC536826FCD7800");
+test_comp128v2("EF6FEDBC6F65EC72E71DD457D7F01E39", "545AC9FC051774F6ECD97800");
+test_comp128v2("A926134D19884E0C5DFDFED7E5D43926", "315E8EF19510592D48DE8C00");
+test_comp128v2("D40A68048F84E3C89159DCDC2F6CA432", "4E27DD965DCF6CEB40489000");
+test_comp128v2("E13A16536ABEDC92A087707F6962664B", "F54DF012133B0CC1D91B8400");
+test_comp128v2("443F8F2A487FE8CA0E4E36CB0394FA2F", "FE5A205335BA7EB4F9EB1C00");
+test_comp128v2("4CBDFF21AE06C34FBCF8BCE67A33ECE0", "1A80583C4CDFF82675F59400");
+test_comp128v2("CE08B54548CDE074E31630B0C5F2545E", "47904CB4205016ABEDD7FC00");
+test_comp128v2("16C810823D7C5BF3D5A7DF540BE927BE", "BA3C7853EE826A7FC1E8CC00");
+test_comp128v2("493E34068D98BE1CB0DC488E4215EB44", "A10DFDCC8CF7E13AA9F48000");
+test_comp128v2("2B16221E154B8E737705BFE4EF686EEA", "FBE1504BD0786AB3CE8D5800");
+test_comp128v2("40067BF44F7EBEEC97866D1D2B589C0E", "76C385ABD7A3198D4B2D0800");
+test_comp128v2("AEE48475101B133756C5260C974C3CDC", "B6B159E0B62A3E135CA3E400");
+test_comp128v2("7C337455E7D04CDABF9100A0DCB518C0", "7FEF52780DDF19198AEB3400");
+test_comp128v2("34509DDB0528245C753B34D0A470161E", "79608012018A272CFC4FBC00");
+test_comp128v2("B82EC07003C40E7F69415CFB29090181", "E31D8D19A1038B6F3D25BC00");
+test_comp128v2("101CE5DD0A72E07C3759B23BD07CB713", "13389C0B65706D05753F4C00");
+test_comp128v2("E3508CC1421B61CC1829F1AC5000A3E4", "F3A0E53D7E27598C99901400");
+test_comp128v2("BE906939FEB3D53EE5E3B3F09AF34392", "44F8E711171F64FE3B7A9400");
+test_comp128v2("58AE77015B346DC96112C7463CE44CF5", "217BCBDD9A7A15E4E3B9AC00");
+test_comp128v2("01EB1E42FA9BFECEA63771A8EC8FF608", "1282EA813670CDA82B028800");
+test_comp128v2("0287CC4F44011FFB7BFC45C753A7E764", "EDC47B4AD2F99E044B773000");
+test_comp128v2("FFAC099B033FA5CE08C1E10751436B0D", "774EB65C534D5C554A4E8800");
+test_comp128v2("AB57C444B024972E3CF20C2F885D7211", "8C6F76D58DB1181A94989400");
+test_comp128v2("66B10CEDC32FC796527D98A4B9052E0E", "48BDC529943EC0D5E66CF000");
+test_comp128v2("9D4FC614B4A5ECBF84C40E3F2B7B3B0D", "BAD7D26D697368A3931A5C00");
+test_comp128v2("7529E5C6269C75FCABA03362FFAF0BA8", "4428CA3F074553B7CD193C00");
+test_comp128v2("290F258C9AE1A88D5DB89D1C963ED8CB", "49A6A7D3EE75322C985B5800");
+test_comp128v2("4A635EEB118CF0401AA539B3E78D9BC2", "E75EB6229EBFCD7651869C00");
+test_comp128v2("A0A8CC5650A8C449C27358D13F5D149D", "33D344289813FD1762951400");
+test_comp128v2("00347953F59FEE734FA747B5BBB492EC", "163D84E824A58AEA21C45400");
+test_comp128v2("9C1DB2550ABDC1D83D3C6234C1F7216D", "DA743AF98691235ADBCB9400");
+test_comp128v2("566FFA71F19564E0334521F522D247A9", "E40AB25D08CEB2A2E2961000");
+test_comp128v2("2BE2558950270CE4EA4F041311B700B0", "9D3B8DC6DF02EE2215575000");
+test_comp128v2("2C7CF5A9C76A34069A0086D67162CAE8", "C7C27DFBC668647941BFB800");
+test_comp128v2("C6704C0DA2E47E546BF089365FBB7EEF", "3EB8772558CE07528DAFBC00");
+test_comp128v2("A4789958665D25892122D9B7838A7665", "E3AEA74A7E4487671C72B000");
+test_comp128v2("0359DF86F25D1EF335EDECE2F1300D89", "62C3FD4450596914203B4400");
+test_comp128v2("34BC3AAB3035AAAA631514F56C19D85C", "9C6C6F2C7BF109B71BC24000");
+test_comp128v2("2AE6301993E8679EE9783099EE4B74C4", "5827D1C48907242EC9693000");
+test_comp128v2("8E688E8C19E4842AC61D673A47502BD8", "E937B1C11E000FE617CD9400");
+test_comp128v2("635D70F76E8DD5A60C93E636555643A3", "1FC3F91D21ABDB110560D000");
+test_comp128v2("CFBD75A9EF7C590502CB305E255B32B7", "5AA46577C4A19E56222EAC00");
+test_comp128v2("52017B0F5A735EB4B4195B41C5ECDDAF", "141288BAA0C78E43A21F8800");
+test_comp128v2("9B7F9780EE1DB174EB3DE3E0DCB4430B", "67DFFA9CDC3456FD0ACB4400");
+test_comp128v2("CCB9CCC90211E81ED80A3663FE9520DD", "B296494F844BE19BD0BA5800");
+test_comp128v2("094C25D5E5F556578FEC3D09E1F18FBE", "2ACF6FC60CE2E75D0A26C800");
+test_comp128v2("8EC3B41444043CA925036ED10D358A08", "5026E7548AE49E190927A000");
+test_comp128v2("90E7DF0DE887D9A66C7481D56918E113", "4B84AF649EA0CD5F0840D000");
+test_comp128v2("968EE15F77994D3215AD0B3B830C8022", "353867C1CAEC7F26927B1800");
+test_comp128v2("1DB07381378EFF68414A6DF0B19CFA74", "2DA3CF74819513D2FA280C00");
+test_comp128v2("905553A290DD902F230AB5334EC3463A", "013A682A7041996B08865C00");
+test_comp128v2("6C61792C1391DCBEA18157DA3A5383F7", "61BE5406A149B4FF8B5E9400");
+test_comp128v2("8C1D6A7204459D8394710449B1D13BCE", "CEFE606815A3CE4E25379400");
+test_comp128v2("52A2764A059CED2CC46E04FACAE047AB", "B9ED555DE46B9F27FA452800");
+test_comp128v2("583303EC3575D11BF8340CC10F6FB42A", "DA213F2F44A4DF52BFE50C00");
+test_comp128v2("ABDB49A4B210381D9622AD5822150096", "F3672C97FEFC462BDB03BC00");
+test_comp128v2("711639E35CA2CA66EE65C4A45CDD6380", "536057FBB46AA0D25803FC00");
+test_comp128v2("0C8DB765506BFF298F89022E51C308B0", "89EA94F579D333C66670CC00");
+test_comp128v2("EE21F43BC9268966A546FDD100C3D841", "D4593228E6274177E5750000");
+test_comp128v2("EA1A2B1A515C72C4F4097AA555C93705", "E1DDDB18724B2B66A8D08400");
+test_comp128v2("0C9B8393D38BDF1065B162DF707D3009", "B878385FDB1AFC41854B6C00");
+test_comp128v2("EC0493D80DA33AF6FF5FC42D62870052", "4ADA9E7AF7F353A9D9EC5800");
+test_comp128v2("E41565124D918E28E7ED57AE770D7633", "C8C8CAEB0DF9667A2D34D400");
+test_comp128v2("6F4B3D805D5DCB5672FA617066A4B2BB", "346EA70E1588E99C88A47800");
+test_comp128v2("8A66D7FC4CF30B989EE579AA08872713", "4BE79F8A52FF99AE1C710000");
+test_comp128v2("8C901D0043F0660C0A9BC60171ABDAB8", "148A92FF28DBB5CB7012DC00");
+test_comp128v2("B80E227C8575C331DA9AE0F4050D218D", "4CE2F914C3EB439A03393C00");
+test_comp128v2("9ECF1E38C9B9103C6185B00F3C4D93BD", "CB38318E5B3CF543530D4C00");
+test_comp128v2("BAC15FB21C609560794F4C965685E290", "46B7D216BCBA27E2A4085000");
+test_comp128v2("C4AA0670C7B8A667728DA0D594069C99", "12D51B9D3C7909002613F000");
+test_comp128v2("D099EF68D7E3D1D47F23AD15AEE4B5F3", "93622C70D1D635CCF84E7800");
+test_comp128v2("B33945EE00E962EEEBDE6A8FF4742E4B", "509F4F51D9BEB63CF9B59000");
+test_comp128v2("0D473BB0B5BD4DEB25DEE36BB376C10E", "3FD579DB817D8A510BAF6400");
+test_comp128v2("A14218445FC5A8E72C96B1F27D73AB52", "A3BEF92801B0252DF5DB1C00");
+test_comp128v2("494C095A54E5CDB1A1F6095C42D760E4", "15A68280AEB04086C3638400");
+test_comp128v2("8F90256E47BE4678036609D34B89544E", "2396E34F472750F9D3524000");
+test_comp128v2("5C0927F8F0FD2307F2C6FECE9243F06A", "87AFF076277855F812670400");
+test_comp128v2("C636207195E46AE24C49F4F9D0B786F0", "86C6671A2A8BA28EC6918400");
+test_comp128v2("A7AD2E1B41F3761C59B29F0BFC5890F0", "10574CB3947EDCD7E9595400");
+test_comp128v2("C9FE56B04AC1EDDB9E04E352E8199A3A", "5C1ABFC9A21F5D95E1DA0C00");
+test_comp128v2("C3974645E95D98E4F9274CF62B0D885E", "ACCE97C26AA1DD1BB9340800");
+test_comp128v2("82D095C92DF409D3473ECA08A5A81B65", "30BA06CE2F1AD2A3EAE8D000");
+test_comp128v2("BFAA576B9A290496C47C3D498F8D4FE6", "B43D9C5448CC3F754EA0A800");
+test_comp128v2("040B228CF2C6002BE385DEFD7DB55725", "62D7DDCF277DDBBB84289400");
+test_comp128v2("F1E565C0B52EB2B1E24C17AA3848652A", "9F8A216B9C64DC5A03833400");
+test_comp128v2("2E8A846052A19AF00098BF1AE1FDA9EB", "2C5BF370EA47936AE4802C00");
+test_comp128v2("ACE3EC57D2DBFF11B9B8A81B4406FCB9", "6689168B45E7E9084283D000");
+test_comp128v2("7414C5D415AB5574A82668573BB04166", "AFA7009AE75A5B6E1C8AF000");
+test_comp128v2("A986D44920217FD956E7C4CBC3DE0527", "F9BF62C6B3C722659717AC00");
+test_comp128v2("A08A9B02F531CB9D580DF8176A3A994E", "D379914524C2ED1D1B693000");
+test_comp128v2("4C0F9E924F063A6243865DE1123CA045", "D7F6FEB5091DA38F0D1F9800");
+test_comp128v2("D55B8C7718CF349F906071C76555C986", "564E13F008AA9129A319E400");
+test_comp128v2("0613FF46EFDEB6A663D915B86A9D6C0B", "E75538CAD55D599EC6718C00");
+test_comp128v2("B00D4FA90CD2DA85E1E220AEEFE2C5EC", "ADE280F0C17C8BF566451C00");
+test_comp128v2("FD380C653CBF3A927A769324D9E00324", "3DD21D2EA170DDDFD5E16000");
+test_comp128v2("A9172812C62A2FB789825C71C23BE469", "765EA88CBA94AA15DD3BB800");
+test_comp128v2("59FA5C6FA03F2BD23E35CAFCAF58FD46", "546D5EE3655E43632DD94C00");
+test_comp128v2("F6A4D79BF1DA9F46E41241CD2CD723FF", "74ADD5456F2CD3059AAF3400");
+test_comp128v2("46077741E91463DCBB61F3BA88599848", "24D72BDB319C6EA927B37400");
+test_comp128v2("46E2AFDEF73C2242C2EDA4C690C91E1A", "23B988C1308602402A4FC000");
+test_comp128v2("C240BF323A1B10CF791397D47340B26B", "F13F3AD8A00FF908FC6BD800");
+test_comp128v2("87839B0D4AB1B531495D00751BDCB435", "ADAD6944C3392006CC85E800");
+test_comp128v2("635EFEF876F6318332BE61BE6B4B83E5", "4E763956FC5B9B2C37C09C00");
+test_comp128v2("4A4A4AAFB83EC7B93840A3DADA92F1E9", "7032880172E7BCFB8C72E000");
+test_comp128v2("915C8330D241274251BD71B7E6D37699", "E2003179F7CB405965011000");
+test_comp128v2("E20E7DC2DF7BAE037EC7D880B3C26E90", "992AADDC12E81003488C9800");
+test_comp128v2("EEB75A96C92304E40C2E0E806E918E9A", "BCA49B63865490E279409800");
+test_comp128v2("BB1806312EF173FB73897A3D0BE6A053", "F731BB08A833760754AE0800");
+test_comp128v2("64E576287B81C3442F95345C078D22DB", "B5DEBC603B1ECE7161C95000");
+test_comp128v2("DCB334F708467997480B458DB66356E3", "CFBD2049D829D9C42D26E800");
+test_comp128v2("39EC58A24300E932FF9258EBA374ED62", "A489F81E6E16D0F436977C00");
+test_comp128v2("52FE65AB5569A4DDC91DB5EB747AD872", "712CF42C24F212EE4B80B000");
+test_comp128v2("9401DD3CF0C494B91A3CB28A677A6F9F", "6958E170DB5879834C90AC00");
+test_comp128v2("A7A70C832ACD99447B4DA063BB40A249", "8C4337552EC9121D9C0CB000");
+test_comp128v2("F777485D595BCDAED8AA529E91A161F1", "97DE44F1E89882C3A91A5000");
+test_comp128v2("8D25D899D7458F37D0F26C7D5FD3C0C9", "1E9604A46215B01EE4C11400");
+test_comp128v2("2C75E9C40BBE9AC71B03E2B1A4FD8F77", "BA6F3343BB0A50DFC9F54800");
+test_comp128v2("5E2FBA59B74C51357D5944849B5D5B01", "D5312B4F4774EF49E9318400");
+test_comp128v2("B48D0294C24464CC796F7665C639A3A5", "DEF4ECF98FBCE2A4DF450000");
+test_comp128v2("E1EE10EFABD753F49C13D0B6FFE567F1", "C6CF830856A1AAB6750F1C00");
+test_comp128v2("328BE773AB9ACABE5EBDB67A72DAB593", "1246E6852A5B78EFC63F1400");
+test_comp128v2("C6FA835395102B135A0DF22ED100A30C", "F7EC6507B3D8882E9F86E800");
+test_comp128v2("42BA23DC01CE115FEE284E30D93C2EE1", "85061F767191A64FB6DF3800");
+test_comp128v2("590C81F77828CC50FAE2322F88F2FF72", "4428F98DAC165B709AF39000");
+test_comp128v2("DD5A394D5AF47943FFFCEED795E55131", "7BF03394EA112244A2E53000");
+test_comp128v2("E4B350364A61282E9C7E109C200BB59D", "22A39338B073F44ACC3A7400");
+test_comp128v2("2EC97054F229452094AB6E95713E2B73", "C7DC6BC4D8586FD0A278A400");
+test_comp128v2("9949BBF69464815BBADB53732B830972", "221299171F63C0DF3AEF7800");
+test_comp128v2("AE7021704039DB7C25139864145D2BD2", "1945B987B54CB70E5D718C00");
+test_comp128v2("31E7C45E5C16698DB87FDC4414521085", "68EE8B49C4E327CD7EFF1000");
+test_comp128v2("2C9301A7630A80058E6016DF1E61F67C", "22E69372E777618A21335800");
+test_comp128v2("8BA8B91866F892579CF8EB18BFBCD4E9", "F3E2B573F70A66FB55C23C00");
+test_comp128v2("312372D0F65B447765758D9CAF2434D9", "93609D380DFF429A870F0C00");
+test_comp128v2("CA787B420C4399138BE2317A0DC94249", "9D8372124BE92CCA0D06B000");
+test_comp128v2("27DAE7C8BAE72D001FC9002353E9B64E", "4E15629B933D694F68733C00");
+test_comp128v2("26BAA1215821CA9F90E4CD0690C10F2E", "58103A993006A0C200359000");
+test_comp128v2("A3D6F246FB435A7FF58E1E88B434AD71", "685673795E247655A1B4A800");
+test_comp128v2("FF468FF55671C17282B41DD246D2792D", "26BDB78D824FF8CDBA768800");
+test_comp128v2("600272165F44AF4C0E537AB5D8E66736", "3C79315DB6DAAEF9EA724400");
+test_comp128v2("508F035C9376D9FCC41AAA7D1CC764A8", "41FD7AF4639235E4A02C1800");
+test_comp128v2("F4D22B29DE342EEA7CD8E4E3F43A4943", "CD833E80A4BF33DF3CFA2000");
+test_comp128v2("58843C6F5856B56CA9198695EFE17CF0", "5676CE8A0AE61F33C3ABF400");
+test_comp128v2("6E3C73DB4F5CC678DCC7BFDEE568E826", "A247316EF8F882D42FCCBC00");
+test_comp128v2("D37AE74AD0489776723124F6CE81D42D", "78355AEA1296483A3CBE8800");
+test_comp128v2("F43BA41AD0DB41DA4F54FF5D6FEE4771", "DC1D9BAFDDD5EE83C1B8AC00");
+test_comp128v2("B65FB4E50DD0050E79BAF78FD2F4B9BF", "B0E291BAF797A8275AD71400");
+test_comp128v2("8DC778A2ADA661942266E0B86CD7DA91", "B9D294C0291018F04A09E400");
+test_comp128v2("B6E293ECCA4A56F1D93C8242D5E6756C", "25ED1EE23A3C11FCE7D57000");
+test_comp128v2("4B5B86CCE2090F22424EDA2C512197C8", "A365A6A867B02EE9343CD800");
+test_comp128v2("9F250AC936FDA89F1966C3D3812BFBF4", "3A4AB781109515A1139EE800");
+test_comp128v2("87CF86E273A7A8BE687D1802005D2A5E", "8ED03BDAA9E92833A6727000");
+test_comp128v2("98CA9A659D2C4DB1A235DD54BA36EB77", "0AECFC5BE2B7C1DC6D0BC400");
+test_comp128v2("53EA0452158CD2DF5F5AE2DCAAC86BFF", "29A163B42A2D557A70974400");
+test_comp128v2("74B58541693349756299EE79D408C415", "56D28DFB499FDB30B87CA400");
+test_comp128v2("16E25268D3F1498A42525626C2440505", "39FCE1A8505CD73BA1DAB400");
+test_comp128v2("737D32452FEC46C026B249E7D77B9A80", "9D515ADFCD93432FE73A8800");
+test_comp128v2("ADD0543912E4A632AC4C0814294751F9", "59D694C394485E65DADDBC00");
+test_comp128v2("0F30160AF9D726612C00476DA06F2FD1", "6EEBE451744257F9435F6400");
+test_comp128v2("0198B362FF964F890256674B225AE2EF", "DAB5FF67D8925B0DADD74C00");
+test_comp128v2("F99975FC4D5AD6FDA57655CF1DC35FB3", "8396623C8B44C2904815EC00");
+test_comp128v2("762CD396934B5E7916EB48D07E3D8383", "D597556F2C650DCDAB686800");
+test_comp128v2("ACFD433259569169FEE586564F056F31", "CE6C1FC52C2CC10E886B6400");
+test_comp128v2("C0C3AA12D8EFA599621B8219AEDDB39D", "F46A4FF31C0FB6ADA7FC3000");
+test_comp128v2("88964441874790C6B1AD12C6BB907060", "CC9729FDA25819796E90D000");
+test_comp128v2("BF65C58D03260691C1267E992128F675", "BC7F343F69A3B6C09C114000");
+test_comp128v2("B13CE2B597DA27FB9DE4B9ADFF717495", "87BE699F1748597BCFA46800");
+test_comp128v2("A2680E79DC76B80EE268FEE9C1EC945D", "18E19922978E90020F353800");
+test_comp128v2("0B31B79AB27925553119466A3C845C49", "041D5919C3CA2542AE91AC00");
+test_comp128v2("FFCA938AC5DDF97C526941838527CE9E", "FF7CAB0E892D9AD987AE2800");
+test_comp128v2("36D5B7252A7B57BE2D4E06879E6975C4", "427F72A3254D4BA24C1E6000");
+test_comp128v2("E4FEB6B8FB01025D6C479852FEA000C1", "825845CFF96383778D0ED800");
+test_comp128v2("E138E7CD58527B7022BD1553C4E493F4", "71AE9F089F8B970EA986F400");
+test_comp128v2("F0FD7F38EC52265FC33D60DC9553A5D7", "140CEF85332BCDDBC3B26400");
+test_comp128v2("4F0BDF1C4BE4A35C065EAAEFAE646A01", "96E64BBB007D4761869BF000");
+test_comp128v2("03706861BAEEF0DC1CEA996C3EA93D92", "C2CF16AE93CB94D5A85FB400");
+test_comp128v2("B1259AB7C681443C290E0C1BF5224DC1", "DAD36B50AA8455AD5D6F8C00");
+test_comp128v2("E225F155FA5CA852E151B7AD3BC40394", "9F480C67DF663A51B91C5000");
+test_comp128v2("30098EF69DBBDD25FB9C87315D3020F1", "51D39392BCF956FF0F34F000");
+test_comp128v2("749E0FD7B6ECD4B45E80C6E54A14BD0D", "44F77EF7111682F411EA7800");
+test_comp128v2("5A5444F0E7E7C61CFB031F86E62EAFF6", "2F65BCBA94C07D4346DF7C00");
+test_comp128v2("C9C7A48094A579C8A989B28848DED187", "CB88C47F30FAB985582B0800");
+test_comp128v2("50449C950CE2DB6E0DF22609E005E175", "64EB723CBF43873DD2320000");
+test_comp128v2("9DEEC383005D68C05C665CF484E371FE", "F941903FC1062E6FB20AFC00");
+test_comp128v2("FB3BB14E175FA9172A2741E04F139B5C", "4E9CD42E9C244AB3690E8000");
+test_comp128v2("24C01EBBC6B68F9BD4850D1BC360FEBF", "BDFFBF809BA31A516463A400");
+test_comp128v2("FBF4841145F14A97DDE18D3ADE4CA794", "5CF60E45C59FAEAC54A66C00");
+test_comp128v2("B3AD72EE04894CB4B446CCB338E31656", "39C9B3FA3B84200BD76B8C00");
+test_comp128v2("3BFD7004020C1DE138BB6C78F2042D89", "5BB03480D9B6CA5180F82000");
+test_comp128v2("024D288A0F95E4633BD0B05CD5B700FA", "39DFD93B2CBEBD1CBE42F400");
+test_comp128v2("1AEC0EE2B2E999AB36583974C7668FDC", "68FA512A754892098A4F7800");
+test_comp128v2("6BE0D7EC8A11ED5969386ED31F2AC1A0", "37161F968FC6655AFE02E000");
+test_comp128v2("FD087BD12284E354ADA00FBD47EA0B99", "B1E477DCAE13CA518EEB7800");
+test_comp128v2("5E342819ED86F7026BF73B76227A8CEE", "B002DE9D67667700161BEC00");
+test_comp128v2("75D09A20C3586EC73944AB4C216D44F5", "8AD9F7E3FD40F9637AA0E400");
+test_comp128v2("0E360DD744AA863F6E0341840510AA1B", "478446EF066F048C82A65800");
+test_comp128v2("C048603EDA30D4858F8F6C48A9563B41", "4D53E54088CA31C0AD9A6400");
+test_comp128v2("759445A6138231604DF7C13CCAE4BDEB", "5CB44BCCD6460A0950CAB000");
+test_comp128v2("A91CF2204877ED8903E7B1AC9348BB22", "915775CBBD20EE58C44B0C00");
+test_comp128v2("E21BA0D598D2FC61442E32449589112F", "4A83081F3332DCC326F5DC00");
+test_comp128v2("04F3AA9BA2A1D95E8CDEA6FC34308017", "D6082B73B98D161CBD653800");
+test_comp128v2("594A2A8792456A71705BDC4E89E2521E", "65929B7C79B8DEF106487000");
+test_comp128v2("446D9EEB6FA4D645BB682CB86B8277B0", "5AC860D938A44B12E17F0000");
+test_comp128v2("C951406FF94B294E9276024B879B0661", "AC12B1BC46CBCB5AF2B0DC00");
+test_comp128v2("4E542E0D75649646B084DE8FB0285C77", "65BB3D00B13D1EF9E5173800");
+test_comp128v2("A1268CF4BAF41B5D12BB865F0E3D8B22", "3BE83D7A359D3597AB3B2800");
+test_comp128v2("36C3C451F2325C18FCDD73CC0FF21B18", "4B6718390394E16180E97000");
+test_comp128v2("CE257F14B53AC96792EA2CE8B4A4E58E", "A519EDEB91EFC18B1256F000");
+test_comp128v2("3F14F22F59C516912AF25CA62FA8F053", "F3BA543B2C728E3FA4D30800");
+test_comp128v2("3A4F5220261EAD5516E8C517BD2ED831", "DD66EE67A690B8633E51E800");
+test_comp128v2("495C634F6667460C254D97B35445707A", "C6F29CFA4B9CAED36E270800");
+test_comp128v2("66EB5313B0C83A64FD376BE02E03166C", "5E895656A03CE5C83A4DC000");
+test_comp128v2("35889435A0DCD4B799BE7695320B69CD", "ABFE28829FE27DE9E31B3000");
+test_comp128v2("183B1F81EE25E31AB5DAAA33D015CBA6", "E80C570BEF8028830FB68000");
+test_comp128v2("F92B33E2BF63BF05291C09F0213FF72D", "9FC5D30A549A662B0278A400");
+test_comp128v2("E12E30F732A3A2A206467BB3F174FF2D", "FBF985953AC4308EC8678000");
+test_comp128v2("E66C7ADE2AB71A9DDF8BA3A18F9C1302", "E2F0A7D8972CA55D5873E800");
+test_comp128v2("59DF68C04CB938BB9CAFF8EBBB6C6CA5", "A12CE62E9B86E592D647C800");
+test_comp128v2("5688229644E7F91AD75B47BAAE320C27", "445E1ACDA1CEB5490A7B5400");
+test_comp128v2("8B26CFC065CE0BC44A97E33A5A07345C", "75F0660EB010AB40421F8C00");
+test_comp128v2("77BE38B4815AF6DFBE17DD48BA9AB1C9", "A8F61F6D8CF832A592B4B800");
+test_comp128v2("5E73AD699EEE8DE8460F2C01C2077CD6", "CA6BFA4A79B9104550A07C00");
+test_comp128v2("1B546F3B274639D31AE0ECF3C378D180", "5A668DBD6AE23D628EBD1800");
+test_comp128v2("57887332BE21016DAA2200E69A5E850C", "937CBB32F8A08E24E9550800");
+test_comp128v2("F11C61AB9A2DCF1D73D2566C07F2F90B", "70BA40635D38BC070B1A0C00");
+test_comp128v2("2DFE7BFB3589E3EB3E77895EDF0130C3", "7C0BF94042656E559D11C800");
+test_comp128v2("C7F9F26B1CB9C77524810B97C649137B", "CC9F502181117A6015AEBC00");
+test_comp128v2("E3579A2F7B2267295B14F6A2B2F09937", "FE52FA20DF09C6BAD47E9800");
+test_comp128v2("7A2C147FF0868F41FF61BD482C7E21BA", "81B79CC89B3DC23A023F5800");
+test_comp128v2("ED5E4033949163C5AB820CA16C51AFF6", "A82B3722F3589AC19CFA9C00");
+test_comp128v2("22AB205407EF30ECCDDA49B779ADA405", "9B570A9C9E89BB11D1C04C00");
+test_comp128v2("BD5EFBC04BE376CADFE0207ACD8AF095", "AB4D42353B1DA514E410AC00");
+test_comp128v2("785180CB3BC2FE8792E424C407F12437", "03BF1E622929CAA732929000");
+test_comp128v2("7E1DBB304169F6DE4F12151FDEA7B4E4", "F74A4BA566896445694F0000");
+test_comp128v2("6A1668D9BF09C4B9087F7640A835AE9C", "C467FA442E9052B1A1944400");
+test_comp128v2("096E3A437ED6A342BBA60D4484EB348C", "AF5684AA97791DB5F4F04000");
+test_comp128v2("218223AE24CF97E6BFFFFF1F646ED3AC", "912BAC5C763BAFB866356800");
+test_comp128v2("32E34FC02745575D7844046B5EC92297", "753BA5F36C33B0A8FF19C800");
+test_comp128v2("78193A44A12EF231CA47659678D49122", "B812FB79D7769618FB98D400");
+test_comp128v2("4C79BDF37D02F924936515FA9191535C", "3A3EAA1A02A8770D634AC400");
+test_comp128v2("9149129D54A114706A17C92268E95788", "4BAE933AF24AC862AA02C400");
+test_comp128v2("827123B8A0AFFF76512B9A4247F18263", "4E176C62EF8993DC49BCC000");
+test_comp128v2("B7D362E0CAB7936076570676D3B00926", "F7142C9528BDE0C3410DDC00");
+test_comp128v2("C4835D3873B027F4A04E01E09F10B567", "6CBDDC9F268BB2523D371400");
+test_comp128v2("2C71B6B495E2280B5ADA4882926012E6", "6608CAED6ECE6BBF4E4C6C00");
+test_comp128v2("4DFAD94ADEFED514A8336CF78DF291E3", "BC9E3927D64A5AE83FD09400");
+test_comp128v2("39E9E060A7A89B40951170834EB358D0", "882709FBBF4C15A53ACAA800");
+test_comp128v2("93FA22B2882936519A894C83FB71D7CA", "10CBC4E60938FF6E02BF9C00");
+test_comp128v2("6D3054E4F523088E4672B051B4830D87", "8C235DDFDC54AC96423FD000");
+test_comp128v2("9A9AD3AE912E007D1B7A773CE5995F5A", "C7440E0B278459AAA5452000");
+test_comp128v2("5DE6E922819457D8A17A4495C432CC78", "346894BB2BB3E5396360F000");
+test_comp128v2("CA9D461DE4EFE173DD0F70B21F0EF2E4", "897F09AACA45A443DF046400");
+test_comp128v2("6CC4A0AD6363AA75173ECDDEF1867500", "5E1369255B428A3CAFE91000");
+test_comp128v2("A83B7718967EC4E71FF17A13E504979D", "8ED613C3B5EFDCADB43EA800");
+test_comp128v2("920D2CB5A77A118740641007D01050DA", "5CA75E88F9DDE10943C2E800");
+test_comp128v2("A25AA14FAC524225CF34796E3B48D4E1", "486C8EC0519E9CF7BE04BC00");
+test_comp128v2("079234F699B3D4F4E10E81A6F6CB4AF2", "953C821F1B60AFA612147400");
+test_comp128v2("2C0FA3B7884EC1F49A1FD64D25D918CF", "66B1BE35A97A35B552B54000");
+test_comp128v2("4809694A853437CEBC1430403F2C0AC1", "8B52048DACE2B2426E64D400");
+test_comp128v2("CA0B1E50C790195CF83E867BBBA1B11F", "8A7B4F4A857CBB91D743FC00");
+test_comp128v2("750AD2FEB9321E8161BDCC3737F294CA", "3AFA341A9F80B2C6DECFA400");
+test_comp128v2("EFA25251D61F8DED93F2DD2F5C773DF0", "B05617847AAD49659499E000");
+test_comp128v2("4372D23AD9D5985A321D47FF3B94311E", "2F0458EEAE7C37C9B0D28C00");
+test_comp128v2("0E4D5FFBCC246C0B694D0BAD0C6E01AB", "BB9E49591CDEA7B38E42C400");
+test_comp128v2("C2AF47D04C203797F5EC2CC841A3CEC5", "58EFB4158198FF36194F6400");
+test_comp128v2("5A7F52669369320D3CD5883FA4EA3F2B", "6CCC2ECD466D42B7A4305400");
+test_comp128v2("C50ED70A1FCF421FE6D3E68BAF7D90A8", "DB98CC5763982C52885DEC00");
+test_comp128v2("09483A1EED2CF60CCE0DF670E077459C", "17C5625B1E4B638831C73800");
+test_comp128v2("9E8BE0C3DF99181656D71A665B2F6E78", "8930954F0BEE0ECCE8FF7800");
+test_comp128v2("2E1A3948DF968DACB8B50C7BE01F7457", "1AFC087AF7C3DFB650B5BC00");
+test_comp128v2("DC70952E477B9CC06C507BEE1B1D0DF5", "2CD73DFE8A454951F8F5B000");
+test_comp128v2("20C31EC84115A792269692DD24318D5E", "710E521A571C20908E4FDC00");
+test_comp128v2("57EDCE38D0847365DDC3E2476AC3E1F2", "9C8BA4C70BFFA8D374427800");
+test_comp128v2("2FE14DF7EC89DCFC40F4532DA4CD8FBF", "E929007EF952A2CBCAA1E800");
+test_comp128v2("C891106C395E6FE27CB057717252D1D0", "426667B142F5B2C6DA154800");
+test_comp128v2("E00C2B6A32C78A39943C5C47F2662676", "DFF8A3A85B25DC99325CEC00");
+test_comp128v2("FEC57F0F123AD7D36B4B6F35703F64F2", "0E5DD6F7FDA06422FF0C2C00");
+test_comp128v2("7EA718A0C7FA4D3C942BEDBFF0E8C27F", "0494E06696570FBC07B20800");
+test_comp128v2("5202C1B460DF93CFC147F50DF0DB8AD2", "909CBBBB1234814F20B94C00");
+test_comp128v2("A5BEC24FA1715EE2A004AA64ADB891D8", "79D301F620BB0AF9F434D800");
+test_comp128v2("FE558B21568C8EC4F96ED18492327DD4", "20D3E5E9D2FD6C7B33644400");
+test_comp128v2("72CD73EC048C026C289501852295D8AE", "A7BE173CBA66C90364D90C00");
+test_comp128v2("68359A95139538BDFDA335F9766CB52F", "D9153BE65D53CA5E79DFF800");
+test_comp128v2("FEED7553983FBBD1D9758393FDD5F1B7", "B8CEF24E0140134FCA4E4000");
+test_comp128v2("0A07B08315437682AFB159924B0686BD", "4CAFF446C895B91BE7A06800");
+test_comp128v2("3C326933EE7F409FF467A907DF0036F3", "F80FA736C483EA30DF9A7000");
+test_comp128v2("95F16978198BE120A3080E7339C4DC16", "61B588B440110F1176018C00");
+test_comp128v2("4FB88DCEA84BEC2D61D59D26AAB1E5B2", "63B99FB3D4828DF14A632400");
+test_comp128v2("99052813B4FE1DE320C9D18250D7EC0A", "99B838E84079665C7AE32C00");
+test_comp128v2("58B92356701F585C5DEF34A1867E1164", "6E4C4F64265374992B89F000");
+test_comp128v2("D4730E1A155968372A2796C007DB61C2", "F24DBC3188928DD7F6DEB400");
+test_comp128v2("31F061BE095DD8F0CB9B12915F677513", "432999E45E51F50F160E8000");
+test_comp128v2("A8FA54FD77B174B01419DEC483C3BCFD", "960214386FC602BD0B634800");
+test_comp128v2("B69C34EB57A1E9BAFEA2FC8BDDDACB97", "D31496A364D337F21BA43400");
+test_comp128v2("E3DEFCCF108C176B427C6D558DAB2257", "FD4F744BDFF1BD5408F63400");
+test_comp128v2("634413A6F936B67FB937E47831BA1744", "3D2045CEBCE10C5E3945FC00");
+test_comp128v2("761FDBAD45F01BB614C990C5DE842A48", "7A29FE2AAB86F4F7BC520800");
+test_comp128v2("EC9ED9B997C283EDFAD1D86D2B4A7164", "058544343376B8BE3F87B000");
+test_comp128v2("81627CD5C956F5B71A9C0ED4E521420D", "4E313907E89F9F9B55398000");
+test_comp128v2("3DCD0B3984C8247B8562EF6237E9545F", "17BB7D71DC7324EE7C747400");
+test_comp128v2("6F27674CBCD953B4059F3B55F78E8795", "14900F7EC6BC6B801CF05400");
+test_comp128v2("ABCE668D4532111068E0094A3A100E4D", "A18AF444D2BDC12B60B2F400");
+test_comp128v2("280E9EBF56FAAD5DB2C5CF73CE296DEB", "20055334D6BD65B69284B000");
+test_comp128v2("84608515AAF1D869F9EE47B6EF48EF0C", "78BA397766592C4E05A7E400");
+test_comp128v2("1692DA2B7086E7AA8D3F3035C7107359", "87194D864264ABD4CB900400");
+test_comp128v2("07DE064FA13210922ED16199034CA965", "2ADF1D09B4ADD164B76D1000");
+test_comp128v2("75EF82FBCC14256470722FADF3776AE5", "CD3DDE9AA2529DD7A8C8F800");
+test_comp128v2("B99DC3A08EB5341A32F8C62E4BFEE43D", "F417AB93249FE05D579B5800");
+test_comp128v2("A4183E5E094E733AE11BDD7F6331043C", "90655E9480FB2602C4D18000");
+test_comp128v2("28D40D70C58A581E2BA5DB2D8312BA8B", "535906A3650D559BBC8DB800");
+test_comp128v2("13B5B284571B20D9EE9799C96BD40FC4", "B71466E5B1F11AD6B8AFA400");
+test_comp128v2("0029C96AEEAE094A86983794BD582AF6", "836AAE69B149DD54877A2000");
+test_comp128v2("DBF2EC98F31ABBA2722DBCF89EC6A44E", "68343042FC4F198337C3BC00");
+test_comp128v2("083D0ED162BF1A80574B5906CC1F4AAA", "FA3ECFC6779C1AB430E4BC00");
+test_comp128v2("4E1D795034EC45B8F9821D6CA8203BDB", "E816931A5308C59819AC2C00");
+test_comp128v2("4EE4972E7F60987243A9799E63D662A3", "B16939018348AF781859E000");
+test_comp128v2("849CF4F89D3233F3E584CE0C129D4BFA", "D9C2178D63D98597ADCA8C00");
+test_comp128v2("57EDC728BCC2B30DA9B7BED3393C3CF9", "CE8A219B97FC38CF8EB29400");
+test_comp128v2("9A3E2A0D88F960CBB1808EE7211C6362", "B4753C1822EE5832EAC3D800");
+test_comp128v2("032219016ED376CF2D9CD997FB3ED6A6", "9D99910AFB22549BFA59CC00");
+test_comp128v2("6465F4B9C6DBA457BE7EB0580642E4EA", "349CEB847C741124EECD5400");
+test_comp128v2("EC5EAFC6E0CA5C7811E4DBE5188454D0", "FF3B8B9C7E49CAEB00BA7400");
+test_comp128v2("FC779DF1C75EC65275971CA4DDA54445", "BDF0ACA83F71F5795FF91400");
+test_comp128v2("97167B93AEAC688D65F5CCA5AA540628", "A8934C20176826D8E9210C00");
+test_comp128v2("D6FFDE9C6396DBADD186625777F0EB1C", "25D38BAB18E0B1A4561EC400");
+test_comp128v2("BBABE99B5DA9F492794D1B33F2E69306", "A2F9EB555D1860FCEB90D400");
+test_comp128v2("31336FEC645D23BFBDB2F17A7D051ACD", "DFE297E427C7EBBB85125800");
+test_comp128v2("F4CEA98E6B5D08FFDDEF9E83F02EFF76", "0793F349B87CE001C3D24000");
+test_comp128v2("03C233F35A8980F0167C9C01FD381026", "1B936C46CFBC6210208A5C00");
+test_comp128v2("83937A0CE40B24304B9FD30B2ADE62C8", "81449A34B68F541FC0D26800");
+test_comp128v2("CF885925FF754C3D2E0F17FEAD4AB540", "FB623A02D722E99948756C00");
+test_comp128v2("B263174AD8926BDBDEEC5E9A9E92424E", "4F2D03FD412BB66F8FB45000");
+test_comp128v2("3493E0EE360CCA3459BAD5F44C23E804", "0C327FCEC6FCD5C9BD410400");
+test_comp128v2("1CBE4486B6754A05800548C706835524", "A5B423FCA3BC87386CBCEC00");
+test_comp128v2("96057C428E150BBCBD9AAA18405BC04A", "0683236771BBE74B4A2E1800");
+test_comp128v2("F2B319F79573629E56759FF5BAC67303", "C89F69CBFCAFB5CB5F5BE400");
+test_comp128v2("1DB5E2D4F572F50D2DC637921E46D270", "8B09544DFDD6BA2F6D8F8400");
+test_comp128v2("8EAB71FD2C9F62866607E317946B8515", "7B0C4057E9CF10535B275800");
+test_comp128v2("9033CFBD34995F33F7530BF78C207EB2", "34D5E0DF065F9CD9373DB800");
+test_comp128v2("55B56528FE385672E006DF3E5F15593C", "71D3C14D2EEA2465AD0FC400");
+test_comp128v2("52A4AFF178091E85625C39583A935096", "7EAA20BD18083FAE1FED9800");
+test_comp128v2("7F3F6A273A8A75EEEA484F270209DE61", "695C9E3DE158905DB1B9B400");
+test_comp128v2("B607EFEFC1959FA8B32E0979F241CD7F", "C3B8B80D01C9390D837DD000");
+test_comp128v2("B5D74887A6DC4F652ADC13A10D4CDD9B", "19CC8430EDFC5FC268973800");
+test_comp128v2("A09F045DD589B5223037DFEE3519A376", "E60E7F22341E66A3F3EE1C00");
+test_comp128v2("F3E0ABB2B1E4F00647F2D5C236A1B26F", "7A776258F5C5649E5E201800");
+test_comp128v2("C47160A0897A8422C528D7BFC8D20188", "D1D32C73E3DCA58B70BB3400");
+test_comp128v2("F779D231ADDA823C8F9EE13C5CFF8327", "F1D9065561B6D0591BDAF000");
+test_comp128v2("BAA3CD7BE838696F2F8F7161D6C8BC07", "2F4835836D00DE30A77A2800");
+test_comp128v2("29D8058453F0FB8777E0116247D1624B", "D626DCE8C83BBCAEB1E60C00");
+test_comp128v2("962F26AB8CC6CFE7004EBB98219F6E46", "3114E64D5E558C1CBBCC4400");
+test_comp128v2("B4C608BC11A358456FD44A38ABECD461", "1EF6FD55FB22D74591F2DC00");
+test_comp128v2("DBC87B90E5CC736D89A063C8E7EA12C0", "69A047C836101660FA0EE800");
+test_comp128v2("E2FB2C67E647416BF2A58284677FACC5", "6995DFA7822EFFB01826D400");
+test_comp128v2("6D2FF6CD3C9A5038B9B33755C6DDB410", "4A047503796C5AF0A83D7800");
+test_comp128v2("3D20566CFBFCBED16DF8D3A0284A85E6", "835C3F58AE388B93F74E4400");
+test_comp128v2("88F65F3F649962491D3E0FA045EE23EC", "9612855A987ADDA693BCCC00");
+test_comp128v2("C6ABB80355AD24C621F34965507BDBB3", "98603A29610E403253C30C00");
+test_comp128v2("C37280108B16E450EEED07BA7575FEEC", "2B1FC2879493BEAB8BA75C00");
+test_comp128v2("F637E1EFDD8A8F577040337DE4817A7D", "00E125367FED4AAD7277E000");
+test_comp128v2("EDE6AF5E598389D9BA050D5527405DE6", "904AA4D51E003AD87D170400");
+test_comp128v2("AAEAA56781A803C19F7FD64ED2ED2DB8", "EFA8298CB630D5D9C3325000");
+test_comp128v2("1B7AC6EB7CFE9DB7CBD20B3AC8BE73F2", "46DE7FDFC06B8AE2F3A4C000");
+test_comp128v2("E6A8681B7B066BA7CBC43139B22C3DDA", "BA6F5DCB9895FFD2509C6C00");
+test_comp128v2("A12237D9124C39E638D56647E6A86452", "D25F86768AA3E961C69C0400");
+test_comp128v2("07066B004388D3DFB3B04A64AD83B7F7", "7650268E5DDA7BD220CF0800");
+test_comp128v2("55A2BA46DE50A3C16EDF3100416146F0", "5FA039D0354C912D161E5000");
+test_comp128v2("6109A82102094EA00C2DC1F5D1458EEF", "8D84E7F5DF9FBA1102E23C00");
+test_comp128v2("F37ED30ED80B697C3C71DC8CF41CB486", "AFA2019028CBC07C803FF800");
+test_comp128v2("80D00951B03829654F56D8577B2319DD", "259B7737DEA5AC6586C9CC00");
+test_comp128v2("4ADE88B1C1450BD2C04DB298627E794D", "11F7BD5CD2F0457A912F5C00");
+test_comp128v2("6B8CDA8600BB3D20418AC2D6AB2BE0FB", "6CB450249C7A80539ECCC400");
+test_comp128v2("AF5D6A632867A1AED9B0591504B6C52A", "8976868E00E168C2BFCBD000");
+test_comp128v2("D7881B90D486C90EFF4389A071039F0D", "F652A1C5A41ABBF0AF09BC00");
+test_comp128v2("31744AC378C3A56E2E599A052133E3A1", "990811B8536A91B40FDF3000");
+test_comp128v2("DB828FC7E2BA095663B1CAC857B1C882", "991E252E53D405FA7584CC00");
+test_comp128v2("7789790E921E29D8CB10D2E90A6CFAD7", "C0615EBDF99A548693DB0400");
+test_comp128v2("631510BEDF7A2CF31DA6E69F9AC78378", "6D875FDC78C70F8BCF206000");
+test_comp128v2("41D1BA21CE28CB116716247CD1567A1D", "94903B7659431E454569A800");
+test_comp128v2("0F98273C34B90E685BD7873383CB3BC7", "0C738DC3E630AC6FDAE9C400");
+test_comp128v2("75BCBDB5B2E23F46B6366229281E5487", "68E6EF3AD2A857847D050C00");
+test_comp128v2("A2293CC54F3395A790F86AA56A95E357", "B2FCBED0F540FB5ECEC9AC00");
+test_comp128v2("6E92707ADB71C3808BA4E241DF3EDCF2", "D45945189B01B92C0ECBCC00");
+test_comp128v2("4B469FF0DD32AC00928D4770977EA271", "C20B5CC8B92FF28E449FD400");
+test_comp128v2("A0FC84522EECA927DFA68F9D07C70B0C", "451A254F4DEDE634F514D400");
+test_comp128v2("FE996981505A918727D5BD232B0E157C", "646E03E626B160D204C3B000");
+test_comp128v2("4F388F8FFBDA93A421167BD44F368647", "5F6F12C381E2E75AE300A000");
+test_comp128v2("0086CFA9CA7E7AFE28C4497FD19AE133", "928AEEB2868BEDA13F275000");
+test_comp128v2("4359ACB771F941FB8F52513D01CED563", "393130BF04B3D914DBCFF800");
+test_comp128v2("A19C58E94C9EF7C363A627A72EE4C57C", "B1A84094A806E28A43B57800");
+test_comp128v2("C165902B95C0CB08ED5DC0FAF3580F2D", "6CCB395862241FA3661D6800");
+test_comp128v2("26BC352460FD073FE015A410A7CFA935", "61E88484BB88EB782141E400");
+test_comp128v2("4C3F3A58186A0DB703180BE8E4AD5387", "EB8671AD139925539E964400");
+test_comp128v2("E9CC86936DF7AECCA2B86736689DF416", "ED230A13BF046D8507B77C00");
+test_comp128v2("2D90CEDC4BB18209C39572AC480BA46A", "E011AF24697A323D4CD4D400");
+test_comp128v2("D769D834E79706D9D620286FD60B1882", "35943940C4DA30A49167C400");
+test_comp128v2("FDAD1FA63E891B0D3BF81D8629FCEF99", "1C2DC750B9127257FC288800");
+test_comp128v2("825721F453B177BFEF0BAE758AF1AD2A", "77052E1CE9499B1B7867F400");
+test_comp128v2("2F3D588F1FE8E4CA01533D4A0DD9B4DA", "141618BC5D4E72E62A9F7400");
+test_comp128v2("1B8A0B4D6916EF18BAD7CADCE39D2833", "AEF6463AB65B838A5109A800");
+test_comp128v2("B32247244EC86BEEFC52D7C2491E9D74", "116F80B9090964FD599D3800");
+test_comp128v2("3BE4339301C93DED261879486C6A8D90", "8E23D0F627E7802D1A3CD800");
+test_comp128v2("C903F42E92B38595CE04FD53910EA6A1", "7E92DE635B26DC6051E8F400");
+test_comp128v2("A24E8A73014E79FA74FBC2CF1CBAC711", "0989A5AB4FA9CBD9AD0D3C00");
+test_comp128v2("2F3F558FA77482A4CC9922AFFC7802BE", "281A9A26975E9C768A7E1C00");
+test_comp128v2("82910421BEFAACBAEBB5EB41DC83208C", "A2D604FA378CF8119DB73800");
+test_comp128v2("53E78DBF2B389F15A2371DCCEE1E1DA2", "8ACB8E30CDBF4AF3839FFC00");
+test_comp128v2("559E459FD9ECE082963014F6055E0E36", "436F9DDD7F7E58599A5ABC00");
+test_comp128v2("719BF029031FC8D8650BC2699D5DDD18", "A66649CF28F4FCEC3C4E4C00");
+test_comp128v2("723B50440DE3F461C4E5BB5BC9CCB5F0", "B8686043FB0F1413AA938400");
+test_comp128v2("17D769E91662028370034FEFD2876A25", "635E411815C51636EF944400");
+test_comp128v2("0C102DA3F7A14B3A85B2A92A31CA4789", "0EC89144BABEF0E7095C5C00");
+test_comp128v2("AADB189813213B480EC68B9678281753", "AB74FA76105F492560E98800");
+test_comp128v2("5919335F3F34EB77A0D13D8790E78DE2", "8B1EAD6A1613CB2D66A6E400");
+test_comp128v2("6822677E661136749615304BC7FE951A", "5C94BA4C4C4C6A1A15AB8800");
+test_comp128v2("2CC83DC6F20C2A7CF26E091238784A90", "9AFF51719A5284B6CD051400");
+test_comp128v2("5729A297D31DCA3C80C1AC10AC176B22", "D00E1485CBA3A95D55FE6C00");
+test_comp128v2("8C93C54A70BC96C53D439450A3FF9807", "FEC899718D29F53351630400");
+test_comp128v2("85CDF4DB93CC4C8317EB38D365154735", "091A1DD86B98DC9CC88E7C00");
+test_comp128v2("2ECDC19D1D190EE4E6AEF05FF23EEDE5", "5CBE8BB86ADDEF96A8CCB000");
+test_comp128v2("0797E696BB2890ED056F4EB0528AE0CA", "793D86F92A994DFB06BC1C00");
+test_comp128v2("C066293DCF36C84E6B3CFE96F94F9C47", "5F12425ECEF96D7AD1DE2800");
+test_comp128v2("BBF3B6F26357F2D1079ACE9DFDFC5ACF", "1BBE191A388CC485ACC73C00");
+test_comp128v2("B4A0038D686FBA95824576415B778DCC", "AC22616B710B0813F74BF400");
+test_comp128v2("23D7856727E8712982B14CF92F15BEA3", "C5FBA55A7DE5D2383803B400");
+test_comp128v2("56CA39A1C3CBFD2B69F2655FC24FE648", "C483786DD70F46392ECFF400");
+test_comp128v2("06EB61F4D242712C3DF57BEFD0CDEFE1", "3F1ACAC22225021770211C00");
+test_comp128v2("36C3122A765088E042535DD98BC9ED18", "4AFE30D76B2A655E030ECC00");
+test_comp128v2("DF71036C59630C04C024ACD193606F4E", "C69F93BC5CD672FA60646400");
+test_comp128v2("0A52F96B6B364526C5A69E166586D25A", "CCB7F20B61EBFAF3A43D1400");
+test_comp128v2("C945FFBA58D767D0D10D9F782CEE0187", "C9843051992470B4CE005400");
+test_comp128v2("FDFA26664DEC8A4924CB13A5FC20EE17", "6CD559CDDB082F8DA4352800");
+test_comp128v2("12A863FDE1C0EE9B6D16DE221CFCB61A", "F78ABDD98DCA48EF73190C00");
+test_comp128v2("3A5F7A1CB88C2C9956749106395F96F4", "34BC4920C153F06D3B7E3400");
+test_comp128v2("604BDD6E1FFA40CF8C6F96DFCDD62DA8", "4984353CD8F24EEF1B6D7400");
+test_comp128v2("9587947D970D63EBFE4AD39D0E82F512", "339EAC3E342374FC2A164C00");
+test_comp128v2("1A6CD41A12ADD4531D8296CECDB12374", "3790B825CC997DA4B7A7D000");
+test_comp128v2("D6E632FAEB35738451AD453137E9F3BE", "5EE75C98E3F881BE277A8400");
+test_comp128v2("DD3FDEFF3C7F70BD04683F6DB3418F41", "BD09AC2921A2DB430D86F000");
+test_comp128v2("527D3DC8958F7E48800A4061F1B4C52D", "616B6E288F8484D1C5FC3400");
+test_comp128v2("49C88A8C09113B1B20755F2BD890084F", "4939E9DE1A45020E61931C00");
+test_comp128v2("F8028EC02D6E7E00021452C58B0B60C7", "E5D8BED53BFDDC5D32820400");
+test_comp128v2("205E4C48735F32916A0E1D3F3A0B6B21", "4AD530D94EA8D498044B4800");
+test_comp128v2("B8690268A6F86560B3C5359C6BF6FA3A", "6A92D7E521805F27C5E67000");
+test_comp128v2("5ACCD297E16C479EF11A21BFE70358D7", "FCF7BD1B1EA29CF4373EA400");
+test_comp128v2("74B8193542AE198C23C6031F08FF43CA", "780DBFC6135777BDF9F79000");
+test_comp128v2("CA25D7D6F552A94C5481386366D1A0F5", "99547CBD65A09C3CA2985C00");
+test_comp128v2("DA5C2F5CF869E6D16ED05925CDD78840", "4934C178087623626F070C00");
+test_comp128v2("B1851E619BA5D0A36461F6267B6A9413", "90A284CA11E5A5AC86EFC400");
+test_comp128v2("994A371B63FE48129BCB11984A2604D9", "5BDF08835EEF4D01CBE41800");
+test_comp128v2("0AE26AB49E6158225D94BF39FDB29EBA", "83B81B517E7C54D578B5B400");
+test_comp128v2("E64B2E37042CB00C5C9624BA2EBB6DB1", "F059311F4B7EE022146C8400");
+test_comp128v2("FCD24B6C00C5FBAAD39224D54B3FE4D2", "C8B1E798A5299804F9F06C00");
+test_comp128v2("FEA2A9307DBD872BEA7C42B4517FF856", "FCDB2B1870F46D34F2BD9000");
+test_comp128v2("D6F8D8379339F4B9B6C6C7EA4941880D", "B02031965E6AAD7912746800");
+test_comp128v2("6C7D5314CD07485E80A9CBC3536E8E3C", "F625C41964CFF56A0CB92800");
+test_comp128v2("7B1B09636A93D1D6089F3E8C45939ECF", "AB411511CC23630BB1068C00");
+test_comp128v2("60BC3B66ACCB2FD8FBA2D180C345DDD4", "66ADE7E8DD5FD245FE67D000");
+test_comp128v2("080B4DA8017F1C8076BEAEC2ED4C23D7", "67219CE095584AB1F3CE0400");
+test_comp128v2("36B9E1DD94CE2C7D5C50B4F4782666D7", "837763889C34FFB59C0BF400");
+test_comp128v2("7426357487507712D1544AF62DDE03DD", "E272A3916D75F94A9D18E800");
+test_comp128v2("E3EC5110BF23B8292F7440D236070240", "F98D822F01565E3F776C1400");
+test_comp128v2("1E12B4E8AF0B527E971E7B0F1912E6AD", "8092FAAEF7EF58F4B96CE400");
+test_comp128v2("FF63EF8928F00762BD9204F407F224A2", "8954E660D5557692BF693400");
+test_comp128v2("E6063EE906F69B00C636C2FEAC781869", "56EB75B9916E5DE0192A5C00");
+test_comp128v2("967DD337ADAE9A73E570CDCA4BDF866E", "0A5806A9778A0B3E031BB800");
+test_comp128v2("98D1CA43D4C21509F98120B0D6A1E7DF", "4CC4B4C66ABD3A5DA0638400");
+test_comp128v2("1D97DF816F20FF7ADD1E41D01138B49F", "625867D3F8DC80B255FB2000");
+test_comp128v2("E3946EEF6DAFEB57B967823218D603C2", "9FBD7D5310778C193D99B400");
+test_comp128v2("8E81C1A706E1C873CF8AA371322021D3", "C588BF56F7CC23581225BC00");
+test_comp128v2("20DA7808951EF3A9429114C3462F5747", "30DF8359FE34CE5C0374F800");
+test_comp128v2("B0E6E1CA7B8C4E025636F5A66D09D29E", "D45B3366E8740A43367BC800");
+test_comp128v2("60003F3AD24A644CD2D69EE03C01EF0E", "DAE6302331046B94B6741800");
+test_comp128v2("A6E7C6E2615E1D7C356737AC52EE514F", "FE3A2765EAEAFC0F938D2C00");
+test_comp128v2("DB1A6886B5DC43DD77A0261E1AFF8B2F", "B98EF65CC4BA464524735400");
+test_comp128v2("C0A0C0D4B354E14F2ABF41F602630AA1", "18A96D6E34F8FD4798DA3800");
+test_comp128v2("62BE756646EB90390EC21E6D21BB587D", "10401EE31F4A5BF98EDA6000");
+test_comp128v2("7F2B44B17C9EC961778E1255E9B688A1", "C272B7FD9ACF088ADA573800");
+test_comp128v2("C433B7A9DD753EDE601E7FCA21334158", "3B189C41DDFE0D439C0F3400");
+test_comp128v2("5D998A557E253D40A32DA7CF97D5A29E", "EEC272A2BAD8F690B5F4CC00");
+test_comp128v2("8D1B821D9FCF2BE276D723A435F9954F", "85004EEC5849D04F018DF800");
+test_comp128v2("C8A5404B37F54719B284515CFB9B4001", "4AB5D01ABC448CF3105C6800");
+test_comp128v2("9D3B27999D28125B877274228906EE0D", "DA8C14CD5CFD377A8376BC00");
+test_comp128v2("E5F666FAE14359D65D2F6EB5EA45961E", "94AA2EB983B386BCF21B2800");
+test_comp128v2("06F16C45CE7ED4E6201646F0C750D419", "1593E4E1BCCEC056923B9C00");
+test_comp128v2("82FABF6D7B45CE4C64EB0C6E9DC502E8", "F6487E29D164CAC32D86D000");
+test_comp128v2("541F1AC3AFD74DC7C70D6575555AD55D", "86D04EC42CAE37F01ACF6000");
+test_comp128v2("C597908361D898A0413B7A66354A6065", "14AD14240E876852E6226800");
+test_comp128v2("200438F0979DB724420285D43B2179AD", "F293B889DABDE43C1BE6B400");
+test_comp128v2("739E0F37032ED78A0913D042A8C3FE4A", "8DD8DBC7F999094FD4F0E400");
+test_comp128v2("16A8AD7D1C5631CFE5F4CB9CB9ED9163", "6D3F95B914469F0ABBB81800");
+test_comp128v2("AC81B47EAB5649E55A9F6E0B93B4BF49", "2BA8B6AD2E1E2B4902330C00");
+test_comp128v2("6B0267728AD6B956D254EAD2B770FB37", "C03BE3BC733D51F61B1C7C00");
+test_comp128v2("10B1A550E224F719E76D98ACCC2C85D5", "7FC28C88085940FB0C555400");
+test_comp128v2("E71F7EBF5F6D8804D1C223EB722BB46C", "58742C27AD8676AF08918000");
+test_comp128v2("4C3F8FDDD8FCE4D20174F6286CDF71F1", "7CFD68608E450A2D7D20A000");
+test_comp128v2("99F18874776E15A839C2A3E7C321EDB3", "58B30EF52C693A4B40BB0000");
+test_comp128v2("8AC814B518EE9ED843E35E8EE9C25409", "C0FDDF957DE317A4FF92FC00");
+test_comp128v2("17768D68621DA8385EF1348B274F372C", "44E1FDBBAB1D9EC1E358C000");
+test_comp128v2("AB55D41FCAA07EC8E8C94315F698D793", "5A2F087594F2156AF1DE9800");
+test_comp128v2("6CB52163A5B7FFE27654B0310B572B6E", "98AD77BE4612570C68FD3C00");
+test_comp128v2("CAC07183EEB34F38E70334AB594FC570", "5927855548220C340D283800");
+test_comp128v2("71B14D3013D7651AB586A96FDB2C55C4", "CC9BF8358407E57A3B448800");
+test_comp128v2("6081353FC28741F50C7B103D6B2907C6", "AEB5F267FC32315C39DF4400");
+test_comp128v2("6AF53DD62899A5F9336C3A52898EB0CE", "9BA4DB7E121A278A11EAD800");
+test_comp128v2("2B746DC2022342A402ED432B27F6B243", "13305D58C682BFF823F3C400");
+test_comp128v2("B16EAB6CF2E07F8A3AD61D2DAC6496C6", "4B890108A901763F0ABB1000");
+test_comp128v2("20E2CEA085C2CF1B0C4706E4F8CE9196", "21EA802F554471BAE0D64400");
+test_comp128v2("D1CE68C2764772B1264B93A71E305D3A", "297C675B8A2A277866A7F800");
+test_comp128v2("504B4718D7CD40663A3FA48E87AD07EC", "ADAB984B10C1A3D0A14A1000");
+test_comp128v2("C964C6996F55F06AE5320FDE2406007E", "A023AD3226510F472CCE6000");
+test_comp128v2("BB308E478D79556474FA4E4B53FC151D", "5CD9540DC7AC8A3566D90C00");
+test_comp128v2("29EA1AC88BB26824AE83CF126990FD0A", "69E85D6AD0832BEF5629A000");
+test_comp128v2("873D1B21E6080852EC53F65086D30974", "0F17A7DC95E6E47C6ED51400");
+
+test_comp128v3("00000000000000000000000000000000", "34B4225BF16B96E118A85986");
+test_comp128v3("00102030405060708090A0B0C0D0E0F0", "A892A8EFD6D33E3650372F78");
+test_comp128v3("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "F699F0BABA87114F0350BD8B");
+test_comp128v3("000102030405060708090A0B0C0D0E0F", "A5B4C7CA0514C4E1B25CBFED");
+test_comp128v3("C2FD8EB45A807D375CEC72D3599C1E04", "F123A4B0D526DA2AEB199FCC");
+test_comp128v3("059BD4242157FEE159683C03B1530CF1", "8CD58D5C05D24653C730F1B4");
+test_comp128v3("6A00543B7F4AA6F5938E3915F1A04F61", "9CB62587973B8079FFDDAA68");
+test_comp128v3("B022CB9F45B6F2E05986825EB60ED3A2", "DD465462764C701D8E07230B");
+test_comp128v3("9C36DFE0444A53EA8D64666EEF095829", "68DB77768CFA36F5D9584C2F");
+test_comp128v3("65EB5166053C36EBE4149CA8E032F85B", "8236AA79048BF9FD23F22CC4");
+test_comp128v3("FE7F96A0CA6743F4FB43725B65C98CEF", "C3D19C91C0BE1A8A80866991");
+test_comp128v3("CA93097AC1F7FAD6AB300291D7378320", "47D77CC1AAAFF848FD02EBB6");
+test_comp128v3("7ECCE6BCB6A960BE9F347FA4BAFF9806", "3F9CE85AFB1B11B747E41A1C");
+test_comp128v3("537CECFE605EF84CEC6AACC26748D8DB", "23AD4EFE3BBD5437ACD58FEC");
+test_comp128v3("77DA6AAB1D63954F7EB121F69939F087", "3782AA51C410DF352D5BA4C6");
+test_comp128v3("6E6989BE6CEE7154543770AE80B1EF0D", "E3EA331104D11B0340EF41C5");
+test_comp128v3("FA3C9311948C43A88A81F9F151218628", "DCAA5F8ABAE6FD1FE3D994A9");
+test_comp128v3("CBAB8FBD3E6268579024BBE465A085AB", "7A08AD916948AC88F285F679");
+test_comp128v3("D9E9545BBAB220B79C11B4268E4AF22E", "76F8883C06A19A0F3AA0FD70");
+test_comp128v3("1F0260A9AE9FEE9D4463888C7E2B58DB", "08EE26FD13A86CD1AAD5B368");
+test_comp128v3("9A3451F2C657E4BEDC052AC7E42A731E", "A8279A6015E5935099372857");
+test_comp128v3("F1B858F9D05C34D05BD5D0483B037071", "F31AD0315CC5E86B76B97ECD");
+test_comp128v3("67E29E6639A94DE8CF774F21D5FCD211", "6ACFB2184DD373148D5D582C");
+test_comp128v3("66287B28DA9E5A4A3674842A416580E3", "0EAEDE610EB9744B2B95913A");
+test_comp128v3("06CB6DE4CF812D00C494C2CC088D7FA8", "D41C2FB8F696C84D882DCF6E");
+test_comp128v3("06A7F31DE8BC76B51A3D43968930B60C", "9CEA8DC24E04224753B9D218");
+test_comp128v3("29D36234576AD8395163D1CC9826167C", "16D467073F5958EA424A09F5");
+test_comp128v3("0A41667DE9BA1AC860500C4ECB359EE3", "22AC7CEFE26F3A4CB98E4B23");
+test_comp128v3("15999866101B177430FE614EB17E76D9", "A1332C1FCE97402B4A743D8F");
+test_comp128v3("C02AB81126B1990F77F810B82B797475", "3CA34AC67EAAB7048DEC15B3");
+test_comp128v3("47061308D6D2A8B4DAEE2AC7A44D6842", "0282A3DF9C9A981A22572231");
+test_comp128v3("8BB120BF99F7F51DBF66B7CBEF239874", "3B915FC6905D571E0244A15B");
+test_comp128v3("2BA369767520B8C1E2CA69166546D1CF", "D9FA973CBA46C79E08993ED8");
+test_comp128v3("E867952D2484B31C4DE74554B8CFD8DA", "D2C4A14D3FF5550E776E783C");
+test_comp128v3("51A70B24DB7DFC4DABC254C6CD9BA81D", "FF770276E357757BF17B6A03");
+test_comp128v3("D79459E2FE6E8E17510C4649E5EBBD43", "DD587C63DA11D7A39B0E51AB");
+test_comp128v3("992511517261548767BC3A5EA0269459", "3123ADD2EBF5C4D7B3F4197C");
+test_comp128v3("38112A87B58DB889B06F70A35B2BB039", "EA4365FEED09E1A34304A763");
+test_comp128v3("4DC4F985E69146A65C0A853EF5C307D6", "57B9BE9FB46D951171CF1DBB");
+test_comp128v3("F08DFB812F799107F8A817C6A93504B5", "5F67F38F8F729A1A34468A50");
+test_comp128v3("2E7AD4225A84CAF9662B79D8AA4EC60B", "9CE2261C86FEC23836F8C25C");
+test_comp128v3("904503A6A4B61660F24330E74C49183C", "8D434E4F0B2518ABAF9C92FF");
+test_comp128v3("FC0B25783A74FE16F97AF6EEC84799C7", "E4869E47D43D385ADD6FED7B");
+test_comp128v3("B351F09F9FC46188103A266FA50BC54F", "DA4E687A478A4C3EB77EA5EA");
+test_comp128v3("453DE3EDBBC72BC9C65335B47536EB38", "2CC578A40472AD4C336D3880");
+test_comp128v3("250133698D0068AAD95A2E0FA356FE47", "F7BDAD3677641DC625C40A3A");
+test_comp128v3("482A117B4A39F9DD842E67F376E4AC8A", "C7C280868DFE252421111382");
+test_comp128v3("B7B435E33F800DEC7881A5AA6DC7795B", "68ECFEF6FBCE86F0145B2710");
+test_comp128v3("5528C93204329236879A8D0EB66EEDF6", "5A3B116AD3A228DF0C148E35");
+test_comp128v3("2BD00A4ABA3837E76379B9C0257CE95B", "DBAE1F2F91E090C7F250A576");
+test_comp128v3("897FAEE5AD4A10D443E1E581E1FADD2A", "5E0FAC279B3DB617F1D79E74");
+test_comp128v3("50C13D10436296F6511294F0659346B4", "6774D9611D81D2B28EF62CB8");
+test_comp128v3("42F5D66DDC05442E1B394FC346AFB794", "AE323C87CC24BB8816555868");
+test_comp128v3("23978478FFFB8DFFA9160898AA04E67B", "F9E28ACDC3FEF9C732506146");
+test_comp128v3("0C736BC43FA36DDE8B66F9E5F5F38468", "836E479A4C3F9060A5048129");
+test_comp128v3("3D0F54C3DF060F76A908CE871B5A15CA", "2C8672011E95771FB6A01EC8");
+test_comp128v3("F300DF8463DCCB4D8222A7021B005E92", "19243A7269EB1F75402A01DC");
+test_comp128v3("7177B3EC6658F41C7245E773138A0F8E", "3790D6633E9FCB095F1D823B");
+test_comp128v3("1A2FE0B12CB713888781DCE0E90B4776", "CB65E9845237344F55D7AE1A");
+test_comp128v3("1BCB63AF37AC2DF53B077E413B588001", "00DB17E0FE288237A333C4A9");
+test_comp128v3("F0A650FE37B236732D4B889CA6A6C835", "7FC46A51EC2B7BE0C66E1A6F");
+test_comp128v3("68B09A32A5459C6F6E5472109FC164E1", "C8F1AF34F21E61D9CD2E6839");
+test_comp128v3("1B57DD3CCEFE0F33E04EF164B25A124E", "A8486968444DAFB51C552932");
+test_comp128v3("3CA34C720BEB4E2FEFEA6D04BA4FD876", "2B1C5B57D1249DACE613F90D");
+test_comp128v3("CAF61E2E3773AC8891E12638871595C9", "4E03646AB273D7835417418A");
+test_comp128v3("B1433F3F651E0E478A12AD2163681E48", "0204AEAE7AA03ED2B87985A6");
+test_comp128v3("907DB9ADC521BEAA511AE36FE1A8084F", "96E8BC8BFA3054AE764A8BC2");
+test_comp128v3("BBD2E95BE518C72529FC67918EF9AC4C", "9461716327C1CA6ECD8E3DD5");
+test_comp128v3("5EFA59CB1257BCEBD7C52842485B1A7C", "FA69B871271C6D3BBE9C2C0F");
+test_comp128v3("1B170AF028368F19F26CBEE3EA04B69C", "9801411E3847223CF3F6F5DF");
+test_comp128v3("8A03489DB2DA7957AC574307457C539F", "18265685F3F9FDE17DF568E9");
+test_comp128v3("9DE46F89BE1AEEE7E08E6E6C96136B4B", "948500624AA36F84F8A9144D");
+test_comp128v3("3502F3348EF421C546A22C60F73D82BB", "3EB807C5B084B783A6ABEAA4");
+test_comp128v3("266F0F71417652FDA262E209ABA9813A", "1AD344CCB780727F29F539A2");
+test_comp128v3("F89ED91B773DB362E3B535ADE806309E", "6154B2555DAA2536747C8C00");
+test_comp128v3("95898AC8F59AE30AE6C131F433609984", "F4231FEE2955D325C6358E8E");
+test_comp128v3("FCF177BED3855914164948383A7BFCD3", "B013BDAFD7B01F6D12635E71");
+test_comp128v3("A41287144BE0462B64F3BF9FC79AFA28", "11FF56DE220314FC01D2F427");
+test_comp128v3("C37A6607EB0CC90AAE0E805987BA2BFF", "96AA9F8F1B82C816479F6F30");
+test_comp128v3("B30688EDF5883129A3EEE07EFBD716CF", "2484F97D0E207448CFFCCABC");
+test_comp128v3("F49EC19D30C40E62ED346BB1E973B3C5", "7F3DB7AD4F1E679D1C1BA290");
+test_comp128v3("AA60565959700AFA4E24383A7852F98E", "40CA139963F8B07D82F93631");
+test_comp128v3("72673BD9D3ECF3BC9948B6F96D44000B", "A29BDBCBAA7C7DFB9ADF7DDB");
+test_comp128v3("F764D588E2AF04E183D9DC2008E54159", "892163F30BD023B629FEEBFC");
+test_comp128v3("6225365E542C73635002C5D0F49D79E8", "9ABE4CC8526046189A15E8DE");
+test_comp128v3("578FCABE430EB1F154DB178035C87340", "A08DCD277DAFEDDE2CF34B7F");
+test_comp128v3("90D2277F8E73308C9FF488C22C64A371", "BDC38FC4489A714E45D465AD");
+test_comp128v3("CCF9D01093489E9322A92CC5B45EDAF4", "524D9D6323724755C3A58301");
+test_comp128v3("DC7AA07884338A1CA74A149C920B7113", "804DF3B4AF8F2EC1EFD23E30");
+test_comp128v3("B49C7B29E5DC67EE200FC77742DDEE5E", "5A6B21FD6EF3D215C82D7033");
+test_comp128v3("943410D203597F271EB914B045471F9A", "243BAB53856F7401082F722B");
+test_comp128v3("A66F889E48C971C19BB1ACE8BA14C7D1", "53BC19A4C99196271B6DDCDC");
+test_comp128v3("2C1987A822D55252A53F4C5165A9A6E0", "695CA9F5FE4FB59944E42198");
+test_comp128v3("6E2A72A29D3E40CF17720A2EB2729F54", "277189F03E8DCE092840239B");
+test_comp128v3("5756340ED21CCF06BD9CAB1B1B6B1F83", "F514DEB77B49396051473F97");
+test_comp128v3("8F4D4E3BD46C79E859EA6C4B1707115A", "FA8DFF24FDDC28232DC7A86F");
+test_comp128v3("6133F53D525646BAEE9303FD23A6A957", "4A1797DFA2F0382CE2C8A691");
+test_comp128v3("E4079E18FBB564363AD12F51E06CE783", "367F320EA19755B931A57645");
+test_comp128v3("7DE5A96E6249F511894B554941711A59", "B9D48BA0539A31A9532462A8");
+test_comp128v3("AFD50CEBA82EC1FD6EE45FF2ABE23BBE", "3A1327E46668F8A9C4F703B0");
+test_comp128v3("0106CFA6F81EAD4369327F7064B87D16", "3F432307ED0F3C057C5F2712");
+test_comp128v3("CE3DB3EC969DAA637C83C99083F1F8A0", "7CED22DC3DC07E253C978C97");
+test_comp128v3("C3392F85F96A21B777F33EFD11A832A9", "247BF07ADD6941713F40A327");
+test_comp128v3("5A815EC556143C56EA7CB787305F8EC8", "BFF611BF4369FB38215FAF6A");
+test_comp128v3("49ABB28EBD9106B5F204CC1AA2EEAE97", "0A9E54DD9C485068CCFD153F");
+test_comp128v3("DEACE7DEE61E242671C0EF0E441C1B6B", "B6FD390C7404DAD7D4359416");
+test_comp128v3("9A327503C873F7679DBC3271382093C7", "189B498F1D693458BD79489C");
+test_comp128v3("6546D5569FDAA7E85217D87DC34C7E0C", "16FE55376BD68D78B6161E58");
+test_comp128v3("0B695FEA192283985799C9CBD614C37A", "8D71BFD1C8A1559770C77689");
+test_comp128v3("11E2A6E032B79DC02CCA50CDF257FEF1", "C8E97ED1703AE1CA0CFD14B4");
+test_comp128v3("15C788FB019580F2EAEC04B3F074D96F", "F83FCEA965FE9D0A141EE058");
+test_comp128v3("24463A9F5279C2F684EB2F17B9C0B793", "10E3C959293B21D9B4293BBC");
+test_comp128v3("A5C0D212FD189818BED2B980CD771710", "21777B0F52239D696F7BEE2F");
+test_comp128v3("6B38A49118E0A121DF29B8781E71C89F", "F922FE3B5C9A2826818B51E0");
+test_comp128v3("100F07B6881EA8482EEAB29E695630D2", "A70F52D10C35A99831518DD0");
+test_comp128v3("5478D20CB06C9D9EE3E5B159BAE6EFBC", "CBA83BF92463F9B14A6882AA");
+test_comp128v3("2B1EAEED654AE3FDDC35BAA926D45B0F", "B2AE2D718F554D6855936C83");
+test_comp128v3("827512AB9C1C73CCB8AE1A292233C563", "0B512886B3CED2AF94A54153");
+test_comp128v3("312063D0754C824DA712B64D2A6B33A2", "8C3C7D5EAE0CC1BC2141AEF1");
+test_comp128v3("C41B044B098F5D6773229480C84D66EF", "36A53378F71AA1014DDDE622");
+test_comp128v3("06CA7A392F7FB360BE218ABE86020D80", "0D7C440E7D4D7C36077390E9");
+test_comp128v3("E8F4DAFA1CF388ED27662B6F234BF3B2", "D755DE9D6A83D78A02EEBD2D");
+test_comp128v3("7451425964131EA8FDEEDC3D51ECCF9E", "4FAB527C28B1F4BDB34158FC");
+test_comp128v3("BA511CCBC0A07FFC45233241E1224340", "13683A5A03D2A1927F0ACDD8");
+test_comp128v3("8CB04EA4DB020D5D4EE3812B38AB84CB", "26648E563AB2075E2B5C7281");
+test_comp128v3("04A2631D325386F77579CDEAA967C37A", "C1DD35C987CA7918F4044197");
+test_comp128v3("A5BE4CC4A516147408183FDEE9DA1D65", "696A0827A2FFA3C73A10404C");
+test_comp128v3("36C43B5BCE7BECE6FB2DE03F4BE709E7", "FFD099DE6A2C089BED7D35D2");
+test_comp128v3("35EA9D9C382CB460467F8B8D1145917F", "40D9401342FE480A8A009E2E");
+test_comp128v3("E00D2EB361732C08E2C3125DB7D2E3F2", "491710E39159DBC5ADE0DBAB");
+test_comp128v3("F587457CAEF8E5BAFBC129641BF8EC69", "CE15ACA01CD0077FDAA76A87");
+test_comp128v3("BFE148C7255EAADCE3753A7CB3E1FDB2", "46BF49FEB914AB4ED667E4E0");
+test_comp128v3("A8452A0CF7A80F80C4D4EB5C7E7969DC", "C04AB5C52A9B1C5964B0B86B");
+test_comp128v3("BF289490E673C0B56DB6E768B3CC0A48", "856BD7044AF5997C29FC5138");
+test_comp128v3("1DA9BF8EABE527222D4B7301F5F6E14A", "3D61A9F9DB5C452DD7C26F95");
+test_comp128v3("036DBC1FC7537E77FA4CBDF33DFF927F", "44CC56A1DC3A5408286C3EBF");
+test_comp128v3("A26A8816BC2735427354CD88742487B6", "BBCCC740C5571A4653805B58");
+test_comp128v3("FB57CEE47FB84A4CF354FFB02BD7F27E", "CC74DB56925160F24B998789");
+test_comp128v3("6B7D14B36BEFA7AFD23BED5C89274817", "7FE28DAB2C64E290FE8D8CD1");
+test_comp128v3("783B2B4B5D240551B0D1C5073C86EAE3", "BF879E67B031772A214137FC");
+test_comp128v3("A5C5D0E238A3C63E36ABE8CBB8D3A755", "C637492282DDADF2344C5F12");
+test_comp128v3("F60AFBED2788482DCE9012B308F91018", "A6D07EDAD84C22F55D578795");
+test_comp128v3("FF29BAAE83F0D4BDAEDF57439B240E2E", "C88A6075AE55B48C41413694");
+test_comp128v3("BD2EE48803F736F741F7BC539143B6B7", "6AF648295D9D5BB30994F0DF");
+test_comp128v3("8759E7150969DE74EA60E6DC6B0605FE", "DCECE445325ADB93295C208C");
+test_comp128v3("E8A3873E185A8130171809B1C670E466", "A52DC25C7BA9A8B5D35A3447");
+test_comp128v3("CFE9F56C387AF2EECB6C16BD73100E84", "9F0AAF87BBE52A0D82B0F79C");
+test_comp128v3("504D313E64F204B950E1FCBA766E6A10", "2A6E346A487840A9FDC91C91");
+test_comp128v3("BB973244B0AB827B73463C26EABEDFF4", "CAA52660E1D03128283903BB");
+test_comp128v3("099D40CC50BECB4943D5417116A02A43", "184B3CD556245A75AC3B2E95");
+test_comp128v3("6F936FB2AEF98A81E78E4D190DF9D876", "591FF4491319AE943B7E3F5A");
+test_comp128v3("ED889EE0177981FB3054558A3D93F5D9", "C3A3682BE186AC971EAE5B96");
+test_comp128v3("D610FF2C9B5E26A58C3852B1CF968675", "CAA5295895B7A4167F1FAE69");
+test_comp128v3("60608D5017ADBEC3DCEC4F36DC2CCADB", "3850062836D6D9C342A5754F");
+test_comp128v3("1607142E96C20A6D55887D473E1C1ABA", "57B3B068E892BCC25B81BAD0");
+test_comp128v3("0173C6DB9751FC3246158C2F1082842F", "D24D9DE4DFF90F1862A202AB");
+test_comp128v3("15A4DFEECD687B26B2398404FE651E26", "124DB27D43E78C9E1F22329B");
+test_comp128v3("8C04FCE7248101CA8434190F8F3F18E7", "DEDA9836E027014878678B27");
+test_comp128v3("A1F308C74C95344CDA356907739190DC", "34E78710601E8D992F9AF14F");
+test_comp128v3("6CB50D780B92072B37F757ED6E63B0C6", "A2AA5D1A550999067BB69BA3");
+test_comp128v3("5E2BFEC3FEE9ACE4CBF6B5441346F5DA", "18617E4E20754E060463F495");
+test_comp128v3("B28D0F02AFCB8CC20235898B355CF3A1", "E1A687503828B8AF3C8EC33A");
+test_comp128v3("E7A8045111133E9BD905248B3E3D9E8A", "98B3E202A3A5A3FA76358768");
+test_comp128v3("CD33375D0823C4C5B6CEB0F6B4E39F8D", "92FB4EB73E56C32476604CDB");
+test_comp128v3("4D8318B9642BAE26C791643DF0F53080", "BE8371F5FB7CEEA806AA292C");
+test_comp128v3("9DABE054F9D33BCE9795748681EE640C", "CDEBCCD1A2CA52EE3907F9AB");
+test_comp128v3("16AE48F67D14618AD12F93B7D5E3A74B", "29D03897577684C1CC2C1A60");
+test_comp128v3("1D544B9110BAE6EBB3FB9BF625DC725D", "4309E6F048FF37D9D21E7EDC");
+test_comp128v3("563E947E114D9A470A52F05745B59B58", "9EAE363902AD89A2EA744C7B");
+test_comp128v3("0804E1F159B228C2B549965B0628B600", "7874B39866D29960E6A4EA9E");
+test_comp128v3("AED48B489D0D2C9E81AC8D5E6A15084A", "45FAA83D1F868D1E1509A444");
+test_comp128v3("FACBAF33A3409556CD177817673BBE7E", "396965C351C7668CD26E734C");
+test_comp128v3("A9919EFE9BEEC2374E74FCA514442152", "9116234214E63C5C488B384B");
+test_comp128v3("870EDD564454CB858A51E67D360D217E", "6A159AF76162DABBD6D0DF63");
+test_comp128v3("E66FBF9271A50B84A5D7AB53E7373176", "30ACE630BED634AF6D1DD338");
+test_comp128v3("1B561ED7F3EEC916D48BE068611A49FB", "47AE71282F48B4F5892F49F7");
+test_comp128v3("400472731C0366CF0BE540DBFA745BD3", "12EFAB1FA8A873483C53F0CA");
+test_comp128v3("DAE6DFAD532BABC40BB959C970D8814B", "40ECC9C908605D2AD7C04044");
+test_comp128v3("61F1FDE5DA854053A07508662FD5198B", "56DE93DEBF9302DA18649DE2");
+test_comp128v3("BFF8B0D12F533A3E3C9C46BB8B73D4E3", "8DB85E1EC6AF6F2720F44165");
+test_comp128v3("172A3E21168D80ECFAD1E4E122C0B1A9", "60CD03545AA93CF3EE8B9A5E");
+test_comp128v3("F304D41493FEC89AECE06C355269D24F", "BDA5728AF5174BFE8A1DE7A6");
+test_comp128v3("7223C3B61097610DB9D86BE0E5C7425A", "EF3B366F60DB819EA4CE3CC8");
+test_comp128v3("73D79A145D0F20AACCFCE879398A14D9", "6B90D4161C02C37583E93EC4");
+test_comp128v3("D2A3297B864EA9FABB396211FB8D7DC5", "43BC011FDDB44128F061841D");
+test_comp128v3("FBF84C03307C92DBF77E819748CA693A", "562EFBF4DB6078A949AFF824");
+test_comp128v3("70D8437A9120D5F1EECC400CB8F009F7", "A3585AC211D316CA9BAF8349");
+test_comp128v3("AD70A2CFA9289909520AC3E442066E95", "4DE9364411A3D22E18E05EA9");
+test_comp128v3("200DF7FF1CA3C90B9CE7BA3E1622C243", "87C54F689FAF75861A864E36");
+test_comp128v3("3558987AA0E60869CCD2AAB41000171D", "F88E5005DDA6A32A86B46323");
+test_comp128v3("76C03334BEECF8488345EFE09AFEBA8F", "7A8B21E14808D8FDE12DEC82");
+test_comp128v3("6C34C7E0F992FFD107CB3C21AFD8FCF2", "7223C34459BE33199E397C97");
+test_comp128v3("F567B76CC9075EADFD5B1F5BB995DAD8", "3A4D7CAE9189475274EB3A26");
+test_comp128v3("FE553460124B7240AD5927F199968901", "66628D72D37687E345B5B80A");
+test_comp128v3("20A361BE827F77F8752324D0DF0191B7", "7BF2B29BD955EE9DF8A0A462");
+test_comp128v3("89E668A5719753E733E483449D26FC35", "59E64E6FFA910A75BA6BBA60");
+test_comp128v3("EC23E756AD6C5A8EB93FE43FD1743407", "296AD8FCB4BE13EB61403D22");
+test_comp128v3("4C78D75FAC41A829171137A924C008FD", "B5B12D7F28E1C3CF0BBA182B");
+test_comp128v3("1582B13A55A441793F24364F4E79D70A", "4FD6AA11A62B3DAEE7AD9B01");
+test_comp128v3("F83F6CF2845E6049268A98937165CF46", "90C14A957BDC4877443E00E8");
+test_comp128v3("89A39A6F6D0EFC34FB44B9D94E1FF998", "B42B60ABBBC80A57030893E0");
+test_comp128v3("506069010FBBD8FB9B621E1E2499CFDB", "E143B413238339E5F1691BA7");
+test_comp128v3("F05CDF287428D1F8A82F8D2B90AD2D45", "97F6CA366943F4B6CE9FF71B");
+test_comp128v3("250C4E224C7EEED75B5C47E9B281F038", "22468D1F2D1F8BF1D39859FA");
+test_comp128v3("D8C1F4CEDF1F26B67CE6AED5CF27346B", "652F8F8149F1819CA8C863CA");
+test_comp128v3("821DCF113144A74A0696107A511BDA50", "3D8BB5E6B9C0F05E46D96D75");
+test_comp128v3("21BB340C501240AC021FFAE616ADA466", "876909CD1586AC2DF5FC7AE6");
+test_comp128v3("F877A1C9FD4DA1F3E375314677BD07CD", "F373090EBB599E4E9EF9F429");
+test_comp128v3("26845DAB23924FD243CE4BA118AFC18F", "039F20C260E2A343D36182FF");
+test_comp128v3("0C74932F7693F4D4F7E0418E41879F5B", "B7C468AD4CD5728DC6565CFA");
+test_comp128v3("9AF335A2CC31464188BD38CF75937FA2", "1D1A99E64812A75CEF4596A9");
+test_comp128v3("A9F5E25ECAA22CC54A2637CB8DC01FFF", "A756214C3895B523FB63A378");
+test_comp128v3("42FB25801DC657140C86C1C7F18F2F01", "590D9B13514090C19B0000BB");
+test_comp128v3("CFA06688034DC8F1A474C5CB230FF52B", "5937F4916B9D698E3FABE3DE");
+test_comp128v3("C711B7789802A2E87BD9C892ADF37666", "B885298A5DBA44735E11AC17");
+test_comp128v3("0754A2EB8464B56CD205F6956C88869D", "CE314473BF4B1E1B6C7BF179");
+test_comp128v3("4EF0E8BE04D929C3AF6BF71852A72A30", "E14518550A295F9866046EFE");
+test_comp128v3("E845124E430E41B9F7AAD910F93DC6C8", "B478A5A1128ABBE224610BC3");
+test_comp128v3("7FB474511BD214140C90B521798743A9", "E8BE27301A6EF0E481A68DDC");
+test_comp128v3("C727D9EFB0A70769934515C95015E3FD", "C33D3FBA7B6B71F7F5B1E18E");
+test_comp128v3("64ED08124194B3590CAF7263A77E688E", "58FB6C2A5678A5FE49C6DF04");
+test_comp128v3("3384D1E460CDBA2E9306042D35C2DAF8", "53249E60E0137005D6C0582F");
+test_comp128v3("4E4E2DAA21826A569BEB3E89C3F75954", "0B51B269D2EF17CC2AC97F8C");
+test_comp128v3("7783A5B5BBCCF29C2AD09070882EDDAA", "F0365CC0D5656AB7C9EEC1D0");
+test_comp128v3("362BC8026D00F70FAC9406ED186655D4", "261C2E9B772917B953719AFC");
+test_comp128v3("DA0E8368EEE2F2946AB1D284CFB58D75", "2AFF64A0A7796A645F8F6569");
+test_comp128v3("1C3F461143F2E728E463F6AA04835328", "98F8FB6C03A8F04E5DA6BD80");
+test_comp128v3("1BFD760F78B82698E764E2F009E83A10", "ADB4E618176A154B4077D28B");
+test_comp128v3("BD4833A790171E25A3FBB40DFCA805CD", "BCA4151338D53087CE12FEA2");
+test_comp128v3("012135788F5DE698BA7865313D838D1F", "B6E7203A49F125E0EF781021");
+test_comp128v3("CE3713587AD50A75828DBD3B27C4227E", "F1E1EC13C4DD8F300801DFA0");
+test_comp128v3("8CA1F5693F9485DA47665289F9B7994C", "820A5683642DC787C4E86A46");
+test_comp128v3("412A89798D2ACDFFE18E5C1E4D7316A7", "D93057561BBC893843682759");
+test_comp128v3("00CD5CE9424EC374FC60FB76B70EECBF", "88A74D19F682E1A255932C53");
+test_comp128v3("038994C83ADEB2F74D8C8839F53437F0", "A3F15A3800700FBF4036A9B6");
+test_comp128v3("5378C9DF92AAF716A3E3479FC93F9B6B", "A1FA256354C249210CE3F4A6");
+test_comp128v3("3201667073288CAACF46F3BEBAE0367A", "345D24DCF43872214332792E");
+test_comp128v3("7C88491C3A4AF5934C112299476F96F2", "DDA8299BAFAE19127326C0E4");
+test_comp128v3("A09EA9002485129676578DBC2D29970E", "19E2856AA1822D8C382BFCE6");
+test_comp128v3("0249D8409D94C899CB3211101F44E706", "34F666DF84C3F4861F031B70");
+test_comp128v3("393735BBB4F295F78875118F2282FEFD", "1172696A8F3AD17FF4CDC32D");
+test_comp128v3("880A34A01DB11B02983984409F474598", "78E946DE142861000EA71378");
+test_comp128v3("8D2D885D4E8D0FEDF57329B9CA684481", "5C839ED7586932AF9267D4EE");
+test_comp128v3("D9A319465292440E4EC40AD4311412BD", "34C4833EAB737BB515DC3E29");
+test_comp128v3("03928DA8EE9E6EF58F8BDD25383BD746", "FBD0B65369592420F4AC13E7");
+test_comp128v3("871B76BCCE398DA305C05583328F3D0B", "10070DBD7C735D468E99FEFC");
+test_comp128v3("2D8329DFF082425E4865ED82BEB77D2B", "6891E87151FABC787888D502");
+test_comp128v3("1AD6B061F1E920431D52B7C4D7A6814A", "1ABA71E6777298078DC34E9A");
+test_comp128v3("6A3113748345C8D03408EC03C67AA757", "C073219B699FF4EFE6BD7F20");
+test_comp128v3("E78DEA79071AE8EBB147A5BA7530F6C2", "7F90BBB9710C26C21B7B306D");
+test_comp128v3("10A9BD1DF9BC3D57CE9878D0AFA3171C", "8BA97E65B5C543E81A0968E9");
+test_comp128v3("691DA2346C6D0FDB814C07F9A243DF31", "14331E4947AD140A7C1A6ECB");
+test_comp128v3("CE6961F7424C872568BB6FFA0D7C00EA", "05543EA7314FB4A0012243D9");
+test_comp128v3("BEF85F199B780AEC2D7E75FECD14F54D", "DBED42B3388D1DCB8FCCE224");
+test_comp128v3("B08AF472657A3742598FB1BC3E3DC702", "776AE1440D3ECC1771317076");
+test_comp128v3("1F585700F880CE42F2C10AA2B2D9F743", "CB2B3444973841C5300DC71B");
+test_comp128v3("25E7C4BF79991617715EA342FDD16E9F", "A4360B1247A7112B4525966F");
+test_comp128v3("EE3964F72F1F85062AF2AE995B43A511", "672886691D327820EA6639D6");
+test_comp128v3("0F4DA216B41A2ED1777CBA97B4674B1B", "B4A8EA97BED0613AF1C51AB8");
+test_comp128v3("66A36F7EC04C45F3948403E381E3D5AA", "9782454E51F13175CE23C4C6");
+test_comp128v3("E88D31F0EB83D24F7D26371129763FBC", "18DB2B166C64F39868EE8D97");
+test_comp128v3("8C13DE138071D1F51FCB8D6FE388E038", "662291FFC942AF9D67AECE01");
+test_comp128v3("F05DCF03504184FDA2616467DF5795A0", "BD52678F949A9FB2B389E5E2");
+test_comp128v3("4F4F788BEC2EE583ACF059B0ADE014CC", "747EAF9274A438F5AC993B1A");
+test_comp128v3("C37A8A7C38765AC31CDA16786D732F1E", "D3411AE9A8133D3577EB52E0");
+test_comp128v3("E4B2DC6D7322CC108231C199330860B1", "0B8183CDB4774373E772B3AB");
+test_comp128v3("642BA8B13B3A34684C6D526A5FD2D752", "902123CAFC2DCF346D63151B");
+test_comp128v3("EDB994505213703314C42A325D55A40C", "83D68942220B5A33CD61FB1F");
+test_comp128v3("4D1399046DA2D653405F1F1940E875C8", "4750475855BE7402E5D1C5C1");
+test_comp128v3("5C435B4BC5E2F9651ECE8EE13D6E25A2", "D8876ABEFACB10BC3AC52E34");
+test_comp128v3("8D9A5A7C9CBA7A07DCFAEA0E4DAB2725", "C58EB014A932C3116E65DE5D");
+test_comp128v3("09A2B5AECF349CB2D56B7A39872E4163", "C1FC34A7A8D16531914737B4");
+test_comp128v3("28EC2B7795CD5FD29D0D88A33D0E107C", "C4254A6DC983AC85BFDCC948");
+test_comp128v3("3BD04EB44E9025675CEEA1EE80A4EFD6", "CD3F1B6EA3FDD0DE0EC87DC2");
+test_comp128v3("B8FE9194D8EDD327E9D5EC30E09CE619", "C2D257CA005B12DAB1C57E99");
+test_comp128v3("9FF0A2CE81526677BECE7093E03F40B7", "F639DA208C14F5D5E9B9155B");
+test_comp128v3("D8523C03A2DE75F6421D0BC91F02359C", "4E3BD1349917A5406A619CF1");
+test_comp128v3("860466FE29D625755A732EF72620BF12", "DB53D349D3D90BFD6C7B5097");
+test_comp128v3("96DCD22792D5381A5B33392650D8FE83", "64C9D371D87E92B9BAFF2BFD");
+test_comp128v3("4482BCC96F8D8E5FD6EE2A3A302316C2", "15D4F790CE455848506F6917");
+test_comp128v3("A53C321889AD33DE74846729AD0482B5", "6819059E37B8BCB2CD3BB056");
+test_comp128v3("CB6FBE7ACE4F1F336B7535F43CE3FCE3", "36FEB6CC91753307137675E5");
+test_comp128v3("32323174AE66387A960E21E3E48A36C2", "7941EB9CFAAF2FCF2D9D4F9D");
+test_comp128v3("A2F01BB704E0A56C51D985BC170C2516", "2DBFDD6D224B73B8E3BA0F93");
+test_comp128v3("B2FD8D3C4135A5F53C9E9CB2E857C3B0", "783971383699EAAB8A969788");
+test_comp128v3("FBFE997F54C815A9769C576DC5557918", "A6EFB070C41375FA9077AD0B");
+test_comp128v3("A04DE27D214F2BAE0AED7427D2D57228", "74A3F90EF7E7E5F9535ACB23");
+test_comp128v3("6E47EF035F98F3D57BC9C1CB574D6799", "FCF439F7297B000610E5BC95");
+test_comp128v3("9F7C7310B4AE3FBEA22077FE53884CD4", "D2D20CA60E5D657DF4AC1A57");
+test_comp128v3("2B5456E3EDE3A798519D70DA24FDC14E", "8786A30EB8DFDE39EF411441");
+test_comp128v3("3EB8F29B8FFCE34CFE924A4C8B412BEB", "FBF694C68239868881135AB5");
+test_comp128v3("9112F7C42E8F86ADC612FFEF62BCF031", "A9A0EC605E0B7D959D94F3C9");
+test_comp128v3("3B9E8C729F6BD2A647103C87B50084FF", "C7C6B0CA4CF9067D70D50450");
+test_comp128v3("721720697921EEF72389D70412DC9BD2", "2E3A7C1632E8038E1DF4710D");
+test_comp128v3("F4B192A4752141D528FDFE57403D1D29", "7EC20B5FFD6D4F380EED6885");
+test_comp128v3("96ABB2AD544F3B40B2302317A00AE6C9", "3AACD18243723B98834DE29E");
+test_comp128v3("475BC853DB0993F15E4DDA017849EAA3", "E356B15F48DE8C2A841CF3DF");
+test_comp128v3("DB6E877FDF36CD13D291111A2A52706A", "20685B9CA28B27D1B267808B");
+test_comp128v3("AF844AFF605D6CA75D32C8D76911FA5F", "05FAD3B3B7DF158D73F68D46");
+test_comp128v3("B7BD6D23A35ADAA7A24698768989CCBB", "2D4B6CE203755ED6DE54D1BD");
+test_comp128v3("F22358724D39699DC5F3CE252E5CB388", "1362F31DED7766967CDF75FD");
+test_comp128v3("8DEED8937A8635D058E1D3B2937C1F14", "FED61D4167F6721F3D42A96A");
+test_comp128v3("C8F471A85DF24F8694B66679B8F3AA05", "0C53F26A0AA68A554F02BFDF");
+test_comp128v3("FBFFD9900A7DEFA9FFAAAC445CEB9FC8", "3A77E657D27C1AB33CC527EF");
+test_comp128v3("57D09B46F380628DC5529A22E6CBB191", "FF53F69D116A083581928053");
+test_comp128v3("2EC3BC236DABD021D37F1777AADD9678", "AC7EB38011974A02ABEAB3F9");
+test_comp128v3("B79834352EA64EB93D000257045EE658", "F9EAA0D8DC11FF29D6B516E9");
+test_comp128v3("655B9375AC8864762424A4A0D47B7BD3", "4646C2ED5F21B2EE15379E4D");
+test_comp128v3("6E022D6981D611DB9C6E1D71BC5F3829", "E9421D6DA7103E98C42BC348");
+test_comp128v3("E82BC59288987CC4900809B3F93A258C", "765C26D3449490E464D3A68E");
+test_comp128v3("D88E96E65B3E75EBEC71A167FC745022", "9CE53769A506228A186ADB5D");
+test_comp128v3("DE5DEBB9E0A8676C9AFD507CC4584B13", "4011F610990EEA93D7CE122A");
+test_comp128v3("4285D4CFB1769D1A24E50EA2E22E9897", "981AF6169C7B6C2F9D35EBBD");
+test_comp128v3("662AA01C92C099B9F1E6202E196893FC", "A18AC40821D530216226AF89");
+test_comp128v3("02AC385F2414C82B3471B647689AB849", "E94F1A9A2178C9FF103B0844");
+test_comp128v3("0F51172514E1CD57F69EF867599C3DC8", "3982621C739ED323AAAA7C37");
+test_comp128v3("F3525F3F535F4E6DF13BECC1D8FB1455", "F62AFDBE83135B71413D79D3");
+test_comp128v3("F92792BC56E1E01FF0E02D970FAFFC4E", "7BA8768FC9DE99379058D8F7");
+test_comp128v3("2B52F1DCB4966196D138C68DD54E5919", "E461FD2030A5BF5D6EE94D1D");
+test_comp128v3("2A185CE97AFEBF1571FE5D75D3BD575C", "97C86A5C1573579F9C21F2A3");
+test_comp128v3("B41B160321225CE25C4E33E040056747", "F571B0467D59F5B9CCF118D6");
+test_comp128v3("F670778BBA33B3995EABDF9CE23E1F2F", "7EC8E7AEB2F5F754DBF3E2C8");
+test_comp128v3("9F01509B4208F00FE9AC5F302E913FD0", "57EC36681223C3B6D4926342");
+test_comp128v3("FA147D70166C6DDE3DF84175679D182B", "10BBA85D4FEAD1DD02D2A997");
+test_comp128v3("CC0F7F90199AA8DC130452DD284E1472", "1A02ED61BFF438D4C5CA0141");
+test_comp128v3("76ED3C6965EDE96C8D4E1E8ECD9F56DC", "6F3A549C817F60E6DAA3EDC9");
+test_comp128v3("4B337D2FDC0E5FE89A8E552DC45CD4C5", "ECED242B4B6A1D78DE53A425");
+test_comp128v3("C2E1E0773A8030D18A18B9D272425875", "BA7CB02898761CDC4873BFD3");
+test_comp128v3("A0B7D58F803E71297F445D449F7D692C", "7D106558576E6F817F16B949");
+test_comp128v3("F9E967729FA158B2BBADB67F03F24077", "6909F02053F635627288732B");
+test_comp128v3("E11EE60D5050CE4C9D0B03410C5ECAE6", "71C3F54E1E05B0F7DAD5C400");
+test_comp128v3("02F33A269A8DC9F28CA3C370A15E8844", "F1947CDB239461CA161485F5");
+test_comp128v3("3EAB022CAE7D0FF5171CCF46470D2B1A", "EC68B69C5D6B56DB8009520A");
+test_comp128v3("D8D4A183A00EE3AE6342CD895CC722FE", "1AA84577A5A893B64CE59077");
+test_comp128v3("8C4AFE74025F7228BEC7F0F338143284", "7D0F91326ED356A09110688B");
+test_comp128v3("0A42C179A055958B374DDB41CCB29EEE", "0D2943530809487742DFA541");
+test_comp128v3("D7ED82A3EE21516F5B5E9AC6F7C461A5", "6366910A49AAAEA42C9030D7");
+test_comp128v3("86D87077BC84F98E3937097EEAB02ED5", "2A9ABE810CF32B7A58CA3F3C");
+test_comp128v3("880C564BBCEFC7442CE047715FF7D734", "0CFCFAC0132393C12C1604E3");
+test_comp128v3("DC31B4989AD996F310386615015ACE5E", "5CC175EAB000EA51A1FFE224");
+test_comp128v3("392104223345E1D18364CBEA4BC3BFD3", "E264A4072A07CD9B6680274E");
+test_comp128v3("5F7D8CD69434C38C21342F0AA1BA95B2", "A6054BD785E94219C6B13676");
+test_comp128v3("0583E23F330357A0ED6C19F4B1170687", "D4420333B9B73E98C9CDBEBD");
+test_comp128v3("BEBA72E43E8B849A8AB7DE5E384F4FFF", "5C8E69DDEDACC0A2E2C53C49");
+test_comp128v3("E924217A8D6DC3349F1227E479B75ED3", "EE4F62ED04137D5789593131");
+test_comp128v3("D816518AD87D4EFCE7596BF5CB44F4E7", "05064CA57A799CC2BE53F4DA");
+test_comp128v3("915D72694C6A437D7B0F426F09E052D6", "D424688D264C9C6C4DF27328");
+test_comp128v3("A670F35E061E9939F3BCDC59FE3713BD", "6D01F2C7E14F078D905D0CC5");
+test_comp128v3("5C4F891E7FA9033123808EA080057854", "CC073D07C363B207B01E7A92");
+test_comp128v3("18179F59C553186751D81E19B525844B", "5A85AB9293AA28FF5FAB717E");
+test_comp128v3("C2B28E2A5F646D7C1D8CD149FC5CFCBC", "C92B159FBE27D67E60BC4979");
+test_comp128v3("B4359F02926FB5CFE5CA5A8D1CA4DD9A", "3F3BE16228D0E16613464E71");
+test_comp128v3("DEE387A6D3DE2596552C22CC63DE4C51", "0C00FAC805524991C3279C9D");
+test_comp128v3("D51C0E2C76902EF329A51E1F0D7BCCD2", "2B22C1C141C8048A35731F8F");
+test_comp128v3("D5E486BEEB5F24C1DA0E8845A9B7A814", "EFC378D93A3973CC122E8D4B");
+test_comp128v3("5854C608946E292D53C10BB4217FFCFE", "1C9F8CC7D50860B4B3A0D942");
+test_comp128v3("B7AF5AC0E19E193F7D8E6F39A574735B", "548B1F53379D01046A6A56CC");
+test_comp128v3("34E91F46730F97BB9FB5EB234FD1F8EA", "63AA742D508678B048EA9C39");
+test_comp128v3("341BA1C17F4B3404873D4FAA11360CFE", "72FB0649FA0BF6BB586BD4D8");
+test_comp128v3("9E3FB44A03A77C0CE5A61A6A1401D171", "6CC44D57F3A12BF4EFD1A8EE");
+test_comp128v3("6F736586A0EEF3449DF0B60D94FDF437", "9CD22D8E908B8604E50AEB38");
+test_comp128v3("3DD7BBAA9219A684937E01ED3C4BB765", "979F4315ADAEEFEB2FA425A8");
+test_comp128v3("14DA6A52901E8307A0A670AE479F86CA", "2BCB1F00A750C44FD8796227");
+test_comp128v3("0CCFC8E22D343F1F742109B298C70B6D", "8A2774F0FCA16945E112A963");
+test_comp128v3("6EFFA300E7B42409B6C0AB2F7702DE10", "03775D043B0E74A09DD3C682");
+test_comp128v3("6E32DD9C06363D3861E0CA8757F6F1E6", "7A93227B2D9D42E1E9BC575D");
+test_comp128v3("EA9C1AE547A7A703FB9927071741BBDE", "E21A228CBE7A6CB571B048A3");
+test_comp128v3("9C02C6007B9520A2C91AD461F0D036B0", "1152183D03228FA0824CB02B");
+test_comp128v3("D05F647A9D52588A8080FABC23C8FBE2", "081E7E132E7FA762FEC9615A");
+test_comp128v3("C69B4F862714E7DE0F164AFB86FA749A", "A6EA4793326C53FCDD068BFE");
+test_comp128v3("8AAA18EA7C2BF9AA2CF2FEBC67A2D237", "A844FDC8F08B88FA76203076");
+test_comp128v3("99EB982A955878D476D0DCF26F4F2549", "4CEFC03D8877AE5A4948132D");
+test_comp128v3("E67E1D6202D1579CB6CE1019C356365C", "359F17EFCEBBAF1871BB23B0");
+test_comp128v3("51E24C6EAE298E24032801B915284529", "A4D4A479C4A563C887CDAB23");
+test_comp128v3("A680BBE906B789E8EABF3A2105F729BD", "003D877DB7245BD9494FAA4F");
+test_comp128v3("B8C0B91B3E4D721C64AB693D3661F0A8", "E6BD97DF792D7247ED7470A6");
+test_comp128v3("C209970974C5FB03F13CC37DC5AB14D9", "CE35FF0D37FA85538CE400E0");
+test_comp128v3("AA776ED1EDA8F6365E7B6C309C2D14C0", "44363E12F52D707C3A603CFC");
+test_comp128v3("63B00CC8C764D6349953A48FA7C9257A", "926B6CD0BA38598834ED10B0");
+test_comp128v3("DCADDE2690C1A93C5E2726A62AAE2C7D", "FBE1015B5DA87469EFC57492");
+test_comp128v3("37EAF2D85AFD72C0262AA7DCD7429557", "33EB97675FC486E25CA4015D");
+test_comp128v3("AD2B37FCBFF34E2A3E298C4D0E889335", "F1FB1A310DE2FDF67C14D823");
+test_comp128v3("21E5FC1E51A73A330400ACE4C4620506", "D5C7DEF1981B1E4777A03D0A");
+test_comp128v3("5715725A11F18EAF719F33AD86259A07", "51A55915E4606D4BB5293F18");
+test_comp128v3("C3F809C472F506B2B1E857DC01069423", "05206E64B7887F0DC6496ECE");
+test_comp128v3("93F2E9C05B6A1C6BDC847F30C173335F", "59A69CA2FF24C63F4081368D");
+test_comp128v3("BE45C95A8747E9AB6CA17343FCBE1EA9", "2584335E68BAE8EA10F0218E");
+test_comp128v3("680BA0B4E836CB3878E5D0DEF1C2E896", "E62B658EEA7A6D7A43D59686");
+test_comp128v3("2D3653DC920D22B620131542E79336C0", "F95FC2F730472BB634C51FE0");
+test_comp128v3("C5519276EB1657216B1EC10FB4A93F56", "3C425689A01D4D8029631CBD");
+test_comp128v3("20BCB9E98C1415A8EDF358D37539315B", "BE8FC33FE00171B5785F850D");
+test_comp128v3("444B9544EE15034F9B8C15E25488BBDC", "192A693DB42C3C1A80AE4D59");
+test_comp128v3("74838C600CAB9BED85163A7DEA529D7C", "45BE629E6266098B2637BABB");
+test_comp128v3("BA67F869BE79821954A4C23546F5A620", "8B1D92C591984DF390B34396");
+test_comp128v3("BAE1A926B0C4C0F224D15BF6480BBB57", "B47B558C9333834B06901440");
+test_comp128v3("3C5A480FBEC0C7DB36FFE0A4D0714EFE", "6324B2C4EC3557EFBB6F0A3A");
+test_comp128v3("79D99D9DAD7E91564058816E40216884", "AE065CF21B8617F8206DF134");
+test_comp128v3("652F5B2A342B02A44BF35CE9BD99B1F1", "27530B5DFE9656356422E110");
+test_comp128v3("298878C7092089346E99538E4B530732", "1E99C17F976097BF59727225");
+test_comp128v3("FCE036213AA9AE0890D6B4C4BBE1B819", "2F406CE178B194BA4223F014");
+test_comp128v3("7CFB133F107F8FAAF1105550EE7153B2", "E9219A1A70AF922F7F6335C0");
+test_comp128v3("2E5CAE7355EC3D5082C3AC9D2D4C1D74", "111F2E0C0435C381F5F0443D");
+test_comp128v3("A432251736F441174D9BF11302FC8A0B", "6D85327CBC7D05E5B498061D");
+test_comp128v3("864C3D7719506623594FD357343FAB3A", "99E9C7CE8A73118896FDFA73");
+test_comp128v3("8060C76EA57A22E835FC003E36FBE134", "5237739EF805E1A8B0175638");
+test_comp128v3("39B499359679F3DF5F20EC816087037F", "EED3A9367C317D2C93F7350F");
+test_comp128v3("3B125A61F19898D982A68D4747F3AB27", "E4565B4CD803A7E706F7648C");
+test_comp128v3("59E0CA0FC910ED47CE9541E983256AB5", "86D49D08A5CF0A80AD3D5471");
+test_comp128v3("1509F82B32C4D1CBD190289F2ABE2A4F", "181C407D8EB4F6228F3829D2");
+test_comp128v3("A5A1461145641E786BEBD6A7216DE8DC", "94431F516F754C875F978737");
+test_comp128v3("059B3DA9442AB954CAF99206B5BE89AD", "651C10359109860AF1718475");
+test_comp128v3("AA9D00B5EE743F509F23AFB30D2DC459", "FDB81785C720C703A71500C9");
+test_comp128v3("55D1E3F5EA6945E329135BE531AE5AA2", "EE17C77D4FFFCC83060A77A6");
+test_comp128v3("6874E0DAA246D283EEBA23D56647A2D1", "E7018B195236562B25504B68");
+test_comp128v3("B97E607938AB98651325079D9873FC2F", "7264AB0E73B77C3BCD9C59E2");
+test_comp128v3("97242D0FFC2DDC9D58DCB99F47FDB54D", "FFA3866BD5962ED89FEA8F43");
+test_comp128v3("7105A4274D2BB5F3964D306E0B84BC00", "362122F98251B36C8D265AE7");
+test_comp128v3("85A013070F50C8F318EA9D2C297B32B8", "2DC9C0B8598E482E3419CADF");
+test_comp128v3("7F9D1A3A528BF1503E0659C4599BAF4D", "426C7085EA709094FB9E9737");
+test_comp128v3("E40D0BF9B1BDB25BB8CEF5F2C6E42EEE", "0A24F4C8561666FACEB3FA68");
+test_comp128v3("99F0E31E63033B10C013EBBEB5A38F25", "D04FDC9CE5590DAEEC9D2071");
+test_comp128v3("ACED4722C9A93AA8653984D1F748AAF3", "F24EA9777C8BA77D01531F9E");
+test_comp128v3("C24AD9258F77CD859AAD2E9929D0F86B", "BCEDF6F871E3EA3157A11783");
+test_comp128v3("C3F2BFEAD419E0677BCCC0BBBA0F0526", "B5677CC99B8ECE1CC3E6A8E5");
+test_comp128v3("754FECEB3814DD16FCCB048240C569F8", "DEFDCB968FE746EF38F62121");
+test_comp128v3("858D87A91898E0E017AE65A8E96E009B", "13239CEC1BBA2B7080B21A45");
+test_comp128v3("5420B3F002F35796CD870C9EF308E305", "2AD04D7F4DEB18EBFC6380E2");
+test_comp128v3("75B4876D6E2469EEAD26C4CBF4AA3A74", "BC66DF1BFE59D3EA4B51056E");
+test_comp128v3("68BAC8C22D18BBAFA5746D8BAF3919D9", "E125668BA81A618FAD0A626E");
+test_comp128v3("61A5CB8A7FE9124A50581DE1F88B4CE3", "4713490084F1061AC0D01E61");
+test_comp128v3("48A114454ECCCD8F73B798AC77397379", "D69DC6F8710A5DDC1C097681");
+test_comp128v3("7295FAE9C0FB3EF6DCCD703EA74BFA6C", "0EAC8CD8DC71849E3C8D11D7");
+test_comp128v3("41EFD59DFD1E7A03F6608E0E7BD475C6", "9276BF8FD0EFE2CEC453E0CD");
+test_comp128v3("C5D1CB28DEAFD2DE2D83F9670C193867", "F8C00545BEA262A59508A28F");
+test_comp128v3("F1341091B0AAD50914EC51A0DA1E8DB6", "0C1CCCA9988F7891593B5B3D");
+test_comp128v3("15B47603920EDDE3B34B7842547DDF58", "75E90A950B489CF1B3FF84E0");
+test_comp128v3("AE9B3B9176219A4A8AC1AFEC7C152267", "54212EDF5C06DD53A5360B30");
+test_comp128v3("640A58C1432588AA65E3B88E8A66326E", "1864A1790FA618FC9AB896C0");
+test_comp128v3("BBDB0C7B620366A7E9F3BB5AD634CA0F", "A059B19B2641F578227C1F99");
+test_comp128v3("FF31F65075488B3C9C4271D31087052A", "B81CD905D39B5FDA2E4A454E");
+test_comp128v3("33D44783AD83F85CEC49E9710B8230E7", "68085BE1594AF4350BB72575");
+test_comp128v3("DB70BDF5382D01D9F38D868298BBB2D4", "E67ABBB646268CA0DD275928");
+test_comp128v3("1ABA1A66B8F22CF69576CE375FAB4EA0", "182F2FB770A7F636B2B97AAA");
+test_comp128v3("2CCC1B002E74532B12BFCE0F75F080EF", "1F298C218193E0F3ED2959D6");
+test_comp128v3("A2E879EB6F21E729463FF02A5367088F", "CCEA2AB95BC3214EAC9E627E");
+test_comp128v3("5325A7BD8CD28B4C8009AFF45E545987", "5BF39891DE27F5C1D971D6A6");
+test_comp128v3("E422ACCAFEBBA23C330372593E0EDCE5", "EFBD096CA871AE04B041A3FA");
+test_comp128v3("AF4F883267B43FB627A6CED4D39D3297", "3F3DE74071C3A880C23A2B12");
+test_comp128v3("FD49F0326311DC823D757F635197572E", "907E8D9EA29C29D0DD4FAB21");
+test_comp128v3("FCE21FB08EF39CBE431CBE3023C4DB27", "73ACE1A839774DEA18A34375");
+test_comp128v3("A0BAE1DF873FE7A7D9048D635F1F14EF", "F3074BBA4F277FBF5A7F91D9");
+test_comp128v3("B37C38BB80328F3C97A977F2ED254174", "AC6039F5FC06E302415DDC31");
+test_comp128v3("665CE0C255C84288F7A75A5F81926F6A", "55234AC615BB3356CD0E41A1");
+test_comp128v3("8E7AA9F32253F42EF7F2D68EFBEB21AC", "04762BF970D1F2DECF22252D");
+test_comp128v3("A1BE05726059C3F312787A379F585DE2", "094D1F7842C129B549BCEE85");
+test_comp128v3("58B1B3DC7636E88130647FD67B68B08E", "8486978922E8D4D23A075885");
+test_comp128v3("01DF6BC1C5CEE8C177C59CFFB4370F8E", "B71D112215E3D942CC8B156B");
+test_comp128v3("E21294A89D9132C2DAA27BE40B719293", "00DBB154E82102ECAAE060A4");
+test_comp128v3("35B192D8BF541EB6EE622972AF7A640C", "4137A9C7D970F4A79B0C0826");
+test_comp128v3("457C3690BCC9955B543D2147A98B8BEF", "1E6860FC647A0AAB87131B5B");
+test_comp128v3("7CBCAFB6658EDF3F504659B024251EFF", "D0BE0503E3D82D3545C0FF71");
+test_comp128v3("247DA9A5A1C41600098D805C941BD8EF", "0B620D456A380440F69C42B0");
+test_comp128v3("96C7ADF6EB63AE953A9AD69CFDEC2ECD", "7B0035A1982AEECC937A9463");
+test_comp128v3("4998CF82AA9184C7AAC721A14EDE612A", "8B953D21C3805FCBDB26DC03");
+test_comp128v3("6D6C2397F816501C1C2A54026A87A891", "5E4E4DA6DF8439447F40AB24");
+test_comp128v3("9296C69A474A588CAA17E769F09E650E", "F2454421E40AF0469806ED3F");
+test_comp128v3("B5E72F91519E8C1676DE816B80E8ADCD", "A3A9B91A7FC134B9D01551BD");
+test_comp128v3("7BCBBFBE158DC5930C71CA5752DB2993", "20AEED525F372D089FA1A1C8");
+test_comp128v3("9F5DAE81ABAFEB880B768EC43240DECC", "AD83E8DD85824241E62C971F");
+test_comp128v3("9A0E44698E7CFA4A85C50490D45B5D52", "B10B5377EDB51812B6F29C8B");
+test_comp128v3("57248389353A263554EB4D9980CCC158", "D1881105398CD380A6B43608");
+test_comp128v3("276F117BDD45E2A3B9E8A034CCBFAD36", "31C05360EC269EDAEF5B60CF");
+test_comp128v3("58B44C471348805E3472F3B3CFCDF804", "C6C8C30D56B10D1D9BB0C187");
+test_comp128v3("7B6B3A243E90EE58132A0C4E648BA8D9", "741851B256F0927411A5E180");
+test_comp128v3("48B2B680CDD3420DA07D8043834FFA25", "8A4DE29E3AD6AFFCA0B4D62D");
+test_comp128v3("9AB7967D54AA825C370EB756206BD293", "3F9C0A891963C81E9C98532E");
+test_comp128v3("05415E6C0388DBD47BA5B90075C80AD1", "FDFBB592A74BB2B7DC06605D");
+test_comp128v3("A16149E3963E97DE8616C5B2D5C69BCD", "28A88DB95E7F29561D90AD81");
+test_comp128v3("B440410CE552432A8CD1C8CFB44D4E17", "9A8DC4CD1584016C16C71170");
+test_comp128v3("282F5FC1F1DB7045E95C4D2CEF993331", "278540F1D12DA5AACCCCA5F2");
+test_comp128v3("69CB46C66FEC0563177C54A92389B55C", "266B6C7FEB0A70ED61325A02");
+test_comp128v3("8A996783B3ED992261F42900F14EEDE0", "E09AC5B6CB1C5D8919A65CF1");
+test_comp128v3("A0D7319B6F633463D5D2678D9535DE6A", "96CF081192B489B4942226CC");
+test_comp128v3("A9163934E4B6F027E9D1B897089E5B8E", "7C33CF678EAC9D79A4B8DF71");
+test_comp128v3("7FA59B5C3098D6A4EA607ACE8EBDFCF6", "09E20383EF90CD41BCFB9E3C");
+test_comp128v3("545266A2AC95D9DC7523D9CC26811772", "76E1DD1B438A449674311F7F");
+test_comp128v3("AD404E88DD3ABACDC4B15C02F41F9DE9", "2EFC6B2B406E29D1E947AF1C");
+test_comp128v3("92CED35256204E31EC3549F58B15C7B0", "1E212698E114E761E5B663C5");
+test_comp128v3("EB1F247E0D56A25EF5718ED36380EEBD", "01BBF677F47C754FF502EBAC");
+test_comp128v3("32083C4DBFF7FE1FA500529BEC94F689", "587DE353144F1B3BA54447E0");
+test_comp128v3("3C6742A6E91A429CC202BB838BB717B8", "CA7694D92FD84F2F45AA6613");
+test_comp128v3("4C6A5D02313B182340FEECDB0A9CB3B2", "416B5C878D90B6481EF1DDBE");
+test_comp128v3("04C814633D2B0BF3B03D3B2F5CFB9C43", "A9E3C68FD77D1B30D415180C");
+test_comp128v3("E6908C7AEFB2819798F070BF57354A8C", "EEAE98FAF4225799B577D6B5");
+test_comp128v3("D8B8B932852D90A70F245B3E01E0623E", "E978D99AE21FA544E0FF7750");
+test_comp128v3("B2B1C9AA587FFAA004D2DE4743EB7DB6", "BEA2D1F69952283D80F4CBCC");
+test_comp128v3("C891C1DDE6D0688136A82B79CB8DD541", "E6E6CBF549BAC1BCC230DC82");
+test_comp128v3("E5619F847A4005AE32AA9751EB407CF8", "F9AA6BB21CE75E21372A5ABB");
+test_comp128v3("E6FE23F42B4B5FE8B333A1428E7EC789", "A84BC66F58C540B8BDA0CC71");
+test_comp128v3("0B784416AEABB2135791774CA7FF04FD", "9AB68C5F642C64C82567E299");
+test_comp128v3("42AFC9A45375E548CE1EF6E49C907FF3", "A756D8B5D20EFF34C4C5D18E");
+test_comp128v3("61334E0DB09793458FF1E7874BD5BB56", "FF1EB9D76FF1B2E49564419D");
+test_comp128v3("471A3B7B06D76797970D836EF664519D", "C51C2ADB60D331AE5AA94282");
+test_comp128v3("26D42F7E2DCB2C1FEB76511033C7D986", "A28858C9C4E7FA2CC6F99576");
+test_comp128v3("8FA6CCDCD58890BCBEDBC29EE019A002", "B394C74C41F3FCA60AD2ED04");
+test_comp128v3("FE6B666AA3EF9CBA6094007E3F1B4F24", "9246AE183103570DBB6E1AE3");
+test_comp128v3("7A18F941E8B893C7D27D63F5F4E14985", "345E27AC263C7C0585F25D06");
+test_comp128v3("C3E4A515BF1C087D1F3AEE498D3C9F4A", "B3A6BBEAB3459BD607310A3F");
+test_comp128v3("DE2024BDD217B94AA602EB502C9FAF4A", "9104CB600FEDEFA6365F34D2");
+test_comp128v3("61636EA7E19138D4BBEA213AD61543AE", "90992C68472147762B9FEEE8");
+test_comp128v3("FAB3BFF88D340A5423E32C9596BC8B57", "7FA6B3679298BB21B282A350");
+test_comp128v3("4D37D80ED1C7F445F73A66F59C11FCF0", "110889228DE97F9AFE93A1BE");
+test_comp128v3("A6D10F7561451F50B85255AD5BD912D3", "668622414C3922D25727F3C7");
+test_comp128v3("E6A2AC428310CAF276CB6035CB8EB9BD", "8F3E6B6B0E0485B69F011B6D");
+test_comp128v3("68204E1B48BDB4F5D89F8A970B2CCACD", "D404A42390D66EB423F42554");
+test_comp128v3("98CFEE98EDA627B7680EF7EDBB4CE14C", "A3B4D01986D64F0AD563F7EB");
+test_comp128v3("D06325445759A1D117B09B2E8F4C94C2", "F5F33FCC60A8E84325C3CF5E");
+test_comp128v3("92A8B5DC09AC530E8A5CF2F38AFF22F9", "2DDC1FB79A084C64EA4BE75A");
+test_comp128v3("B5521DC9BCD55EC9CFB2158A120BAE06", "D30F832834176C330CC43514");
+test_comp128v3("C14B9437622D990C05C3FFCB734DA52A", "D0CA95116EBF7A6261CCF2EB");
+test_comp128v3("75E4718349BE6677212F218679338FD8", "837DC56CE8A0EB5D01903DB0");
+test_comp128v3("937F3F6779B2D4EBD5B7A35E3DC89D57", "A86B1F588435C042D6C7642C");
+test_comp128v3("352D597B542F025B4D18A42C9B50E426", "AF86E90EB92C60A7B0B942E5");
+test_comp128v3("195505E0551B87975CCE1A27F0B1DE1A", "7960D8F66F1D52C8623223DF");
+test_comp128v3("23AD7E24D5C114B17F629337C20B2267", "430761488833363E43CCF38D");
+test_comp128v3("258C233904FE9E5DF8E21E6F5AE217F7", "5A03D16A770893B19751D022");
+test_comp128v3("CB9F5D69C3189F895C6D541588F6F6B1", "AD173ABFA217584CF3E33648");
+test_comp128v3("1D41DB0E40598D7AFD4500E3B3B1934A", "884D68E5EE36AA82FC277648");
+test_comp128v3("A825B23D799E59BF6AC02ECEC986D308", "16731DAC8BC5B9FFE165452F");
+test_comp128v3("5A02958E967EFB560ADFAA471FBE545E", "CD455E83E810D06DAC2DA40A");
+test_comp128v3("E0BA11F212EFD783B1B2D7C1D94CFB02", "BB3D1F7CF901443C564AF018");
+test_comp128v3("6D2B6B980D7A84ABC255FD36D78F73C9", "9A9EB3E89127EC8C59F2F219");
+test_comp128v3("29F7F3DD366FD417FB27FF070C6C64D9", "90A88C2971EF2519A9286ED5");
+test_comp128v3("398096054AAE11F1466EC25C31EB488B", "FE8C1282B3EC0BCC245DC0D4");
+test_comp128v3("4727E5FA42CC306C94BF5BC60BA5D04B", "0979930740A16964D4E8DC9D");
+test_comp128v3("00FA2643930D4CAB0B437D4538A9A5CC", "D7199ABE41A62FA9A21C940D");
+test_comp128v3("4BA3FC019B73B6D50671AA008136D42F", "632173C95CFD3E845734F21A");
+test_comp128v3("DCF66A88B4E29275FEAD88B7FA30432E", "CAFCCD143CAE517ECED695CE");
+test_comp128v3("65945975D5C9C172830BA2C9E82FBCEE", "C124C3DB68DAC92C77D9D77B");
+test_comp128v3("88F1F56B2114A44495DD4758A27DEE2D", "8AE73C7FE5A4D3AFBA04735C");
+test_comp128v3("98D15F929E3C088915CF014704953670", "D40859F7D1B9B0BE218DF3A0");
+test_comp128v3("2A5CE1BDCB03D4A716F94FA058AA4A8D", "3C52E2C038B420B1148FFE5A");
+test_comp128v3("06E140F81908655D7EB5CAB5BA500EAB", "EB33656525860992973EC912");
+test_comp128v3("CA5F889F82FF6D1BC8A37EE7F4CBCD8C", "5A05C0D894B353837EA89DCB");
+test_comp128v3("18EF57BF7CE6A833096851B002085B85", "836B27AE1ACCFB7E70705D78");
+test_comp128v3("03C91A567AA4F70F8FAE96090B1735EB", "6A9AFF2DEF414785C740FA96");
+test_comp128v3("87A6F4595E81766503F3A69ED488CF30", "1CEDAC452A7E2A8F0C6F0898");
+test_comp128v3("A6D2C603B63B9C2DB78C7603FBBA2E56", "4D87573C92C742AD679C5F47");
+test_comp128v3("38F20E83B0B6B0ADECCD8AEEBFA0D699", "174B64864AB6D9ACB49E9974");
+test_comp128v3("FAC8FECBB568C411B8A026E8101EAFCB", "DD1C6136805B948F8A752F88");
+test_comp128v3("444530537C31B55D744B3ECDAE7351F8", "79452F3E1F9D9CAD82B87F8C");
+test_comp128v3("857CC0770DCA36D9022EDAB273E28663", "83392240C1AF37AE62A628EF");
+test_comp128v3("57E9FAD98FEDEE806AF218277C3C5D41", "7CCCC317E62BA74A9C76D519");
+test_comp128v3("B5824EEB55DCD5A494FF1A90587A823B", "286597939FE17FB2632AC770");
+test_comp128v3("2B279D4E24EB13A17401CACFEE936E04", "5218F1BB472334ED7E1638FA");
+test_comp128v3("1EC44F30AD61EFB4E0DAF670C3B2AB70", "9996814A92FCDB239CE26937");
+test_comp128v3("4FCF2FCBA29AB5869EA97742E1B84F36", "4C4AEBEFFF5F38B19E115879");
+test_comp128v3("12690AD6A685FC5E12F9F2C8BC3B0EBC", "4E637991268BBECEB730DCB7");
+test_comp128v3("D9F82CD33E3B58A7A12CCD5CE0263795", "60B29E63DDB9E1662CAB28AA");
+test_comp128v3("9338545BE3BC0BEBB1147A60A901D050", "87A06D1628D7F632AA0B09FE");
+test_comp128v3("430A9DDFFBFDCDB35CF25C2FBD5FE048", "3CFDE615E8709673BBF5F81D");
+test_comp128v3("9142B3536CBC753D11927BE8106E06BF", "7AA594617CF61D637A708F6C");
+test_comp128v3("3035ECECE2B602952946B25C768FFF52", "790963DE796E2AD0F5609414");
+test_comp128v3("238844A8C5D4EE87E1E3DBB9730AF9E1", "EBADA4E0EB87ACAE64160331");
+test_comp128v3("2EE2ECCD9C8353F0DDDB3DE1FB564845", "9639CD9CB0B49815D3CEBDA5");
+test_comp128v3("4CCF70032015B8E5DC13EE6DF5D9766C", "4199007AFF50F079DD974CC9");
+test_comp128v3("940EC0E6CC164B97CE7A620569BAF28D", "8F4FDA8480B3DB9879680E17");
+test_comp128v3("E9AE188BDDE66B3F5854C3EF4B039A52", "E2C4FA22AE53E117546C6A9D");
+test_comp128v3("77933DBD0A5BEC33112F9984918AE86B", "3BFF6F18700B52ECCE2C84C7");
+test_comp128v3("18B1D51FAEAD1B22B013DEACB696F70C", "A60CA3558AF36179BB73000A");
+test_comp128v3("AC8C43FFCA7B3BE74B75C4FE2161DDD2", "0A94334EA63F06450687F5A2");
+test_comp128v3("A1F89ECC18A10A50802BDA9425C04646", "04D28F46E4CB41BF9A42A595");
+test_comp128v3("E1EF29848E8859D78699F0BC084FFBBF", "FAAD7FD942FA72B031577F66");
+test_comp128v3("1A6E390B309FFE210B5F08E60C9A38C6", "AEB7315860F4D0720BB9F041");
+test_comp128v3("B574C2AA7D8F662B40A8B32502003397", "1DF4B893D380E8976DD338E3");
+test_comp128v3("92D42585A0375621E0CE7624BC8A9EB0", "A5806E604AF5D548D7680484");
+test_comp128v3("EDF884875394558E62621E8017BC4F2A", "4716D1CBC44A8B3EFEAF67A9");
+test_comp128v3("B7E1B515FE3CDC08B58C5A9954DEAC7C", "4D639CF494DDFD648BB5AD8E");
+test_comp128v3("7B7E1FD35769F032A5253E103D39C9F1", "ECE54620684332950785D8BE");
+test_comp128v3("0679BCD97C8EEA6B4E3A6D9A6DE33835", "CF055324EBBAB90814F42FBF");
+test_comp128v3("2DC3110C86D5C7E1D4D3D545EE24FC2C", "07938CAEBE1712F5AE1E3ACD");
+test_comp128v3("AE1B1164BDBBEAAB83ABE0C419DDE545", "9A5C9BA8D8F26A7CF64AF101");
+test_comp128v3("B0754B5A1DD4B70D6668EC8EB4B39C10", "52FB1BF1CE5FC6A231F0F05E");
+test_comp128v3("EC45EBD7DD6E36E070B10B353CCF9D5C", "B729AF7EA1195B39374B58E7");
+test_comp128v3("F61666A78A70D5FB48828AF5EF5AA2E7", "D27EC7914E016A7FCFF71C54");
+test_comp128v3("50BD44366D78AF6A69F20093BBD5E0A3", "17A8D6BF04FD4962D8E160BE");
+test_comp128v3("29C0FA93802FAB90949F502AF5E3F2FF", "A8061D733AE08D36228A60C6");
+test_comp128v3("CDF57CEB499D1531687C8E6BEC30F2AD", "2FE50295265CB2EE759C723D");
+test_comp128v3("CBE08B4E5F61E801F929658192748795", "6616AA22DB8D0F9B383553F7");
+test_comp128v3("AF1E33D1EB2364CC5EE33ACB28D96BF1", "7A57FA9E969374C8E3C47BB3");
+test_comp128v3("A836E1719AE7E54F8A98ECAF902F587C", "80938563139ED354B2C3FFA8");
+test_comp128v3("39BEEBBC77D4CC9FB568B738E7D02F71", "34087FA85A9AAC1586847BE1");
+test_comp128v3("EC546393DD23CF2F2862384E8C3B146B", "BB153B83A3908B4E64712AD1");
+test_comp128v3("96BD017521C07684145B6F8C6A001308", "854845992A4DF09576663A15");
+test_comp128v3("F60B146739D623E691FFE695283958A2", "D4E66CF06941040DCACA85CF");
+test_comp128v3("881FF49FE99FF0FBA2ADB26C9E27276D", "0ACAF775BB38337A1E344200");
+test_comp128v3("3714AF1C4056F4F1A0334428E70EAE31", "675C9D5C711980E4568EF851");
+test_comp128v3("76ADE089A96DC337F6DF5AA2AD9B9379", "A4BB7572C86755C40F67668C");
+test_comp128v3("FC2E67BF2FDB79D53509A1B712C3338F", "A08FDFE14B6377B9593425DC");
+test_comp128v3("9B1BF9D0A2191C33F6D3119A057E9ECF", "A40A99883EC4D2F5E66F135F");
+test_comp128v3("8D2D20EA10C7B5E377E110CC2C471298", "D771870E3342A158CF5BC565");
+test_comp128v3("20DBA5BA5C7364DAF94894FA2E3133EB", "D5ED6537C0B3343D2E3DB575");
+test_comp128v3("5B045A56105D5FB8E568F5A18107F339", "DB2DB2118ED8A75AEC9F17EE");
+test_comp128v3("6A6EC373C993C65460F60D5C2F1E2C42", "AFC9C8DBB8D37070636E8A83");
+test_comp128v3("78D42F93ACCE494C7C48F8B3FEB833BC", "56C4A07071992807A24873FD");
+test_comp128v3("877087DBDE0778182AEDF1835188A17A", "A5AEBA67FD0C0053573562D1");
+test_comp128v3("C5D36C7B00BC21E37EE7C8BD2344A2B1", "9B87A0E5CD0F142730217AAB");
+test_comp128v3("2848480D8E779C0190C1FAE7572E7A4A", "8F1512191748CB0A91FDB205");
+test_comp128v3("C3863CD1CBCB481366B8B1C1F6FF8530", "0766720770BE1247317F7B64");
+test_comp128v3("BB71FAB97A20ED16A391A519A9EC193F", "486EC0A9E17EE343578837C0");
+test_comp128v3("052E5A2C31CEF3E3695A1397853D45D5", "936CA82F9FE6A435C4EE4DE2");
+test_comp128v3("7B6306FDF353E730770E66604F8466CF", "4121273261A8C8394BCE6FD5");
+test_comp128v3("9A6FC4AFFD8C84F8818AF4AF49FDD53D", "5763599BEEC27160A6AF64BD");
+test_comp128v3("D61D855CEBE64691EB99EB73E3758FA7", "E5D9845F2515E2BD741E912C");
+test_comp128v3("6982C1E0C933006920542445974B0459", "AD5EBEEDACF13A80488D11FB");
+test_comp128v3("8D36A5A66DD0059C76B2C0510EC41E25", "4AF22EF5C1DB27880E8F1B1F");
+test_comp128v3("CD58C379E73A4923555FF88714E16CF4", "D30AE0937D8729472D0FC3C0");
+test_comp128v3("E04F522692CBEE4B7F0315AD9C51D72D", "D96F380954E06B17783E0391");
+test_comp128v3("BBE2C80970B92F6ADC1AE1525A197DAF", "9FCEBC453C0C2F496D2B7B0B");
+test_comp128v3("D8CC3E92B9C35D22B2FBB94706521276", "BBF9E042F4E5EEDDC95EBA85");
+test_comp128v3("30B4398C6A943E1FE0C3C512BD4BC788", "8503AC4FEB9FDF7CAB207F62");
+test_comp128v3("42176E40D3F1446A15D9AF3BAEAA97D1", "FBB1A09D0DC843BAA4151147");
+test_comp128v3("2D9269B04D899888C4DFD9D18A14EC62", "9319967214C5E69AF7CC83A5");
+test_comp128v3("577A7773EDAF8B1A958F7786EAEF9777", "F32957EF68CF821FE3C4C844");
+test_comp128v3("62BD892681623A3893599E5EDC068BF7", "52B4A34186CA802DC7650385");
+test_comp128v3("A8FF2DED49942BCF15D92184DB625AEC", "2B7ECBDD13D65F645A1D71BC");
+test_comp128v3("2A375F080809C959288ECBE28177B8FE", "A15C38E02FF392874C2477F3");
+test_comp128v3("F24A400691C85CFA11801DB8358EDB28", "AED4274B44DA2CE47C646927");
+test_comp128v3("2A605DD2D6CD2532034962B7B341C92F", "90C1FE26F8FE8BFAA6CCDBD4");
+test_comp128v3("4A0EE729A82478AB404AF83758EABB77", "9FB249FB258CC32EA8C2CE1A");
+test_comp128v3("77E87DCD362B3011042A2D50DA174118", "50FEB8FE43E76FBD9FA6DB41");
+test_comp128v3("05099369CBEF98698783BA40403A34A2", "D6F60A72FB0E5B8B693A2191");
+test_comp128v3("33817E9D63C8FE1DDE0025FDE7DB2FEC", "2CB474A91467A18E2A9CC760");
+test_comp128v3("D237DCCFF06B1DB84E8D2BE3554FCAE9", "8501DE0D5ACF9E38D7363AFB");
+test_comp128v3("377F98A0C49FB4150128986DCE4E7359", "25D8CAAA3EFEE5D7102D6039");
+test_comp128v3("B00DBA3CC58C9C7ABF27447431B16300", "9221FB5259BE9C96B161459F");
+test_comp128v3("20E5A41ACB1B3853D6A31C52DB5DD12E", "67928229F5693E291B98E732");
+test_comp128v3("DF9CCAEC4EAEA10523ACF122D4224B0F", "D6912B17D1ED650EDBFA5DA6");
+test_comp128v3("030C9B40FF53EDDB9097EACF7DE5030A", "931B4F1E46FD35AB1E5B0F58");
+test_comp128v3("977A5F4C3C961719DB5EB52D0B38B1FB", "F8E13B7190E28876833BF22E");
+test_comp128v3("D726AFD1E009A57B50C7751EBCF50A3A", "455B65855ACAB653892103D3");
+test_comp128v3("0066B204988D12934052DD78451B3C38", "7A14D3B1095D6E7778043C48");
+test_comp128v3("310344BE7D063F69CE10EEE11B3D3325", "D9D916E2A9E0EB1D884F8817");
+test_comp128v3("BE1E399FDAC7463EC2AE1BE7CC281C15", "761BD212E485B0C0FB45CD19");
+test_comp128v3("3BCA508E2BBE57BC3F0373052B3BC3D9", "8EFE95BCDA4BD9EDB5DF843C");
+test_comp128v3("F2FDDB7EC0126349130702C354D9FF5C", "212B7A28EBB7D9F43B25FF93");
+test_comp128v3("476582F5421F58306A3C57C8F6CDED9A", "1A591D59BCEA8D9B7D10A2B8");
+test_comp128v3("AC58FDECC86B561D6A148B4074872BE5", "4BA6D4EFC468467C570168BE");
+test_comp128v3("CFE2C743B197342B2C6E33058DCEADF5", "D683B57875B63428A0523913");
+test_comp128v3("D0921D159B743A3E644DD3787FC767F6", "5729DB51AA3905FCF6D11CF5");
+test_comp128v3("22A292888CA8C313B785F970B490C261", "7AB4DC3F26954848629108E5");
+test_comp128v3("996E7453208FCE8F62601B741B187880", "129FA10FA3D67ECE7A49E8E2");
+test_comp128v3("B3915B7F245EE8CA2613C5DBFA40B36B", "AC6CF8265CA6021E6D28F477");
+test_comp128v3("4865C31CE54C3BB99E53591AC8979198", "2650D862DA70A85B9FB4503A");
+test_comp128v3("FC16BFBF1FADDB8A3DED51D1ADD8C391", "F3CD6C256753896320EC4D9A");
+test_comp128v3("1D27C082749B362A9E079528CE462144", "49BBBBF8AB5CFF59005105E9");
+test_comp128v3("19308C8E873FE8D050BDDD797A029CCD", "476AF62908F1421B62E0E334");
+test_comp128v3("1426F94934BB10E6B4EBAD64D2A54A2B", "02C6B814B58120E5F96B4107");
+test_comp128v3("AF1E5C627A5533D575A8CECA5B1117BB", "9304DD11B97DB619F303A9C8");
+test_comp128v3("58547645FA672D474940279877918D62", "BA40A424762D75DD60C52373");
+test_comp128v3("3A56DCC21C13902FC625643FB25C987B", "57E685340A4A237414207C48");
+test_comp128v3("B851E274F9E1E7545ADDEDE0DBA5D2BD", "AE14288C0BD0877319194E8A");
+test_comp128v3("7627A495FF15D16F5DFC3BA784DCC243", "5772AAE27CAB453D3F6EC374");
+test_comp128v3("34CCAF8288808B8281740C29BE6E71B7", "C8CC08C9299FC6A61C92B17A");
+test_comp128v3("17795498DFB7074869664325E892F88E", "E0319808163DA661053AEE0D");
+test_comp128v3("7A4338E4C6CAED253D81CA943F266933", "12FD13AE57F3492999AC0020");
+test_comp128v3("9C70DA6910BEDF88184ED2CC8A630A64", "D13FA04CA1CEE854310D2469");
+test_comp128v3("51C87035E8757BE513382458A614D592", "6643B6F05BD94565D7292AE0");
+test_comp128v3("890B1AB6B918B6FB910A874F1774097E", "5E8292F81CFC8D81DEC5C71E");
+test_comp128v3("093495E9E1A906FF8A02BE13F27EBCAC", "E4700F7002C7311ADFDD0AD3");
+test_comp128v3("18534EA254A6E58AD279F2E75ECD1A2A", "F7CA1F8D9D942211356F15AD");
+test_comp128v3("E1B25EA19D9FE6E6C075DD10D7178DA9", "DAF3C83EE49AA9C795967BA3");
+test_comp128v3("3919CCE828CAA532B0D12734DE55C183", "F9FCA32B5411A7A5802D8953");
+test_comp128v3("056CA833057E0B16285F753AD6FC4925", "D99B2F3AEDD8CB9229BBEC0D");
+test_comp128v3("4E900241483908832374039E44B365DD", "CFCD7BF4E9C80C302555C5E6");
+test_comp128v3("434E2A4081A6AAA495F6324A27A2BEE6", "546C2AE028F7324B31EAD660");
+test_comp128v3("542BB3F650D3FD2197D651F2AA3B4CB8", "779CCC12B9A81203BF6E64E0");
+test_comp128v3("85C002B4D3C22F62CA29B5A200016AEF", "DBFE7A507BF3581FDBC223B3");
+test_comp128v3("A0D62D296D3DE757A2B97313CDFDBF28", "F6A883DBB001D5A06FA56A18");
+test_comp128v3("758F8139CC9EDB9E1046D5D71AC6ACFB", "A11C1322F33B0C7147ACF210");
+test_comp128v3("9854BB439AC95310546DDCD6AF8C9EE4", "75DFC039FC4A5AC6541750D7");
+test_comp128v3("162B05A3D90E4F691CF4FDA964F9D08D", "BD38EC87D8FBA954372B5C2C");
+test_comp128v3("3B870DC087FFF1CFE1FF0E7A3E5809FF", "D8926C4CE7F8A1B73803DEB8");
+test_comp128v3("74DC9116A986A4C5623CCFFE99151940", "E384D58528BA6593998B47F8");
+test_comp128v3("4F0AE2A1169E2C0CA32D76222F82AFDA", "D0D1CF2A17E32F33E0BC5D85");
+test_comp128v3("6E401FA58FAFE8C370CDD180384503D4", "410C2B70E0AF9ED19AD1A655");
+test_comp128v3("B600D795E20D5072AAEC3F12FED9831C", "8B195E4EDDFFBEA4FD2078CD");
+test_comp128v3("84F03AD81AC842234924AEACBFD9E007", "C4841B6926907A9ACE38F692");
+test_comp128v3("A9BB6DD52A6131A6324B2200A94A4C37", "6135DD345339D952D055778D");
+test_comp128v3("8C76285968EE5CE3C5BC053389320472", "483B0678643EC1B079587398");
+test_comp128v3("85CE181363376F133C27395F546810A6", "9942C4C95A9803060EF4E9C5");
+test_comp128v3("0EF7F03976FD4F35877D81E51773F788", "E07E11CCD12DECB4ECFD52F4");
+test_comp128v3("9E051CE8CD789364F3D4514664637125", "37016F051186B84AB6C0017C");
+test_comp128v3("3B969219C864C6EBC0E28E998E723EFD", "06FDA82FD788237C82EFC648");
+test_comp128v3("941D4CA3583D84AD49E8B36043A26B3B", "711A06F246EFD1CB875555EC");
+test_comp128v3("42452AB4BBA8816B642A7A2B2CB32746", "4963261C47864C816D91D757");
+test_comp128v3("EFDC72ECFC87BAF583836B28CD402AEB", "8FFA4E3012A7358F04C87DDC");
+test_comp128v3("6A4A1F77C1F84217AD574B06BADA8D8D", "7C615CB1B0C19C1B5108B2D0");
+test_comp128v3("612B7A4023CE381E2DA777DB5670EF19", "0F3C0B32C0CFA19A1E371569");
+test_comp128v3("F2FE60B316249CFC37FC844738B6776D", "094BD3DFB26EE7787C67A768");
+test_comp128v3("F16427D34518986545D6C34CE3DE0F25", "E61CAA8E90A56C33E64B314E");
+test_comp128v3("8CC551123C8D0E5F06CE4CCC4C5CD2D6", "4A12AC7EC803DE9CCE4F5BD2");
+test_comp128v3("0CAF4FCF0116F09688F70DB5BAC4FCF7", "7BB52ED93C9753BCD663B3CB");
+test_comp128v3("570A449DE9CF697AD0F857BA3B4ED10A", "F4D5A26EB7CFE1DB99F235B9");
+test_comp128v3("E7492BB6C9641FDC442007FADEE378C2", "47BB8A378D5FD4618BAAD3FC");
+test_comp128v3("4BF5A78BEB85E6EE45587980AA8997D1", "856007831C77BAEED3FB74B7");
+test_comp128v3("C467D3AA1E425853300115E2852A2C22", "79D0A87198628A4DD0715A81");
+test_comp128v3("166E20A0937416E40585C4725A2639A9", "E60A8BF1DE5F8FEF98FCAD74");
+test_comp128v3("FF60302DDA32C9B15B810FDFF066C3A8", "066BACD0B62EE1F2DFF67E5C");
+test_comp128v3("0CF0CC0B334C5FD676AA5ABD29DB391C", "A8C1FF13A05A3C2AFADF07B7");
+test_comp128v3("3C6F0B6C1F4145802A9B4DAFF7595919", "B21DEA3B36A10ADE95DFDA5E");
+test_comp128v3("59E49926C0291B47EBAF1E08B8FC2E6A", "29CB0AAFC26985131F99553C");
+test_comp128v3("24A9969D2A435B8E8B8F425AAA089862", "6358ED4DD9679D461F39870B");
+test_comp128v3("CEBF9A54B65EE526C4F5962B744BC8E9", "CFBD78D18CE385CD80AE77D4");
+test_comp128v3("766D7612E2649493E42E8729651E3915", "B543D6DF887B827AFE7A49C4");
+test_comp128v3("8D9C226BDE5412DD4CDC3B82143A831D", "18C01DD500E50DC6BF028E93");
+test_comp128v3("AC89833635DA629FB52A15D4603AED87", "5865D89B38AC00FC8277D3AF");
+test_comp128v3("75C5B6B1886B874B64A935443CB167A4", "B71CEE0596FBA77B740403C9");
+test_comp128v3("A154255A2AFF8779A6E0396E3FAE226D", "CCD287D9A032F1D350D3EDEC");
+test_comp128v3("3493696275EBF6764767DD177EBA8F68", "9D0F44BEFE7C57E7A342E055");
+test_comp128v3("48B4168389CC95EF4EA289767AA4CBDF", "77DC39A48A7C1944B4AFD3AD");
+test_comp128v3("9F1EE8FC7A3886E78B1A21338B64837D", "419BC21C840CA23E21C29EC9");
+test_comp128v3("B1EA444B1E3E7A548FDDA7FF64D2E602", "4977A7DE73FD33B1D97D58FE");
+test_comp128v3("68F8AE28016004B285403B23E021BB2B", "CA330C40333234E36F460166");
+test_comp128v3("7F4F1775A34BE9711260B1F977E152EC", "7BC572ED3D38FFBA5488732D");
+test_comp128v3("3901121148BCE143BD22B87CB212EF88", "5D3212E5298C99705B7AC932");
+test_comp128v3("DC61F4169C4BC0B3C54437F622977834", "D4506840401DF6554635D857");
+test_comp128v3("5A051696E25C5B4852D48331780441CB", "BA534B6CA3EBAF8CF044826F");
+test_comp128v3("841B5804EAB0F86CDE97139A2C6B180F", "278FBF615BF11F7FE79EAF23");
+test_comp128v3("F1488D2026FE51161531EB1966D59BAC", "A3CDC43DD0FAC00FD463B36E");
+test_comp128v3("13913340133F4E555B5BFFADDB325BF2", "6E07FFB5F8E5DFE6FF97BC2E");
+test_comp128v3("9111742825025FF594EFF2F058048BF2", "BD0896C75C8E80FD28C0C583");
+test_comp128v3("BD1CB3D3F4E34057AF30029403CA3E54", "3EDF25FC2A8068B3EB1DB74C");
+test_comp128v3("C054E12BAFDF2452238738A499CC283D", "B7212228FAD9A97F935AB498");
+test_comp128v3("F52B105E60C0CB09793BC8CD7AEEE955", "323153E1E96C18F34AC12A74");
+test_comp128v3("2FD896ADBD204A8EC457DD6AE4446DCA", "78DA31F53A33D4BEA3F7A1E6");
+test_comp128v3("03C67D7FB9F90F3B8416249FEDE60208", "3C625218B4B96297314882EC");
+test_comp128v3("FD0C8286A0EC5A245BBCCED275905A72", "FAD0C4B702D988EF237D72DB");
+test_comp128v3("A93176818C64D31AE11DB44936738D59", "665AE6D0A8E0E0FCF7FE4204");
+test_comp128v3("7801F9B77C8EA23586FF132942D674A9", "C6E394BDAEEB9F1797D9A6EA");
+test_comp128v3("4FA3B9E855330723B00CE8E3B7FD0F2D", "6C615F2A5E6E5BB3E3F4E496");
+test_comp128v3("F3AAEFD08C1A3711672606593FBB55DC", "916E07B07A2BECC7D73C5C7B");
+test_comp128v3("A8A2B83CC57E49E38B3116FB032AB7C2", "A4264C771ABF08AB7B9BF457");
+test_comp128v3("B169A6F3D14D65AB435A411580C72BE0", "91EC0B1EBCB8F77237ECE573");
+test_comp128v3("0D43F218A959414940569C4F80B5F9F1", "A2FCC9A9F23685617EA963CE");
+test_comp128v3("AD5A7D67CBA2D953A064FA3F0C7B426E", "9D2F2AF67DF7016C4078365B");
+test_comp128v3("A1F412EC7A762D4D86B524DB41A03CC5", "B46893F2D972D64E0C56595D");
+test_comp128v3("4FEE01CAB67DB39FE9E67E62D1EAF9FC", "B84477FBD1FD9217E64D21F5");
+test_comp128v3("2C557D29B854DC9F895D95B3D9A71C83", "5FC57C5E4DDD05E6D4125643");
+test_comp128v3("78BAA554D65A8E13CC4AE5F244FDFA65", "05C6EC3E2A5CB9469981D932");
+test_comp128v3("8026E9D238C5ABB9F3453D505358379B", "0A1D29C53AC98815E7A834A5");
+test_comp128v3("3960A9455AAB630A81F8379CB8254B81", "3ED3FD6E9716E4CD21F791D9");
+test_comp128v3("E9D5E86088C4798AAE8EC68FF93B8B6A", "1BCD6B153FBB8C98DCFE2BB8");
+test_comp128v3("E27A3FAEE5ED88DB4184F48A312A955A", "C0A83F05F7F5D8718FE8A810");
+test_comp128v3("3ABF186BF4A9EE6DE4BDD164A9EB3CEA", "0B85C14D123EB7FBEB22BC64");
+test_comp128v3("2B4FEDF3F46178F97BBCB8DC4B6A7AEF", "9B47E11E0BA56D68AE00ABD6");
+test_comp128v3("DF9DB131DD1927D7523C996A8CCC7956", "AFA7F27615E6AABD484B3750");
+test_comp128v3("3151C7838F64ACE68776C88E23DC26F1", "7A18AA3641D5750C64165993");
+test_comp128v3("AFACB079853517002305BA6CB7D7876F", "09851BB26A04A16B792B284C");
+test_comp128v3("620666076D2D088AD2F61F4B4DEC00DB", "63D6377AE4D5077130A3D5FD");
+test_comp128v3("81181C3B04A2FB3E90A2ECB3A09DF44F", "CB46C3E3ADC28DA7A9F12CBE");
+test_comp128v3("9B485FC8D34D39B84D619B0FA0B91DF1", "0366966D53CB7BDE4BF394C1");
+test_comp128v3("DE43D7FFAF046211613FAA0672781DDE", "653E1665CF9679361F428A57");
+test_comp128v3("78C2DFA82F4520991FD3507A142AABD2", "4FBE0E4158E403B8D8AA3269");
+test_comp128v3("6E11061572E6C5755F897773043DB7D4", "7E654E600CEAFE38C6337807");
+test_comp128v3("3FB2EF05BD30839A1F11EEA2BC4C3ACC", "CAF07B3BBA6A5CD6757F0C5E");
+test_comp128v3("6D2557CB6B0ABDAC48E074C3CF3EEA55", "909EEF3E8014ABD0C3141328");
+test_comp128v3("ED451E3A8E2618FAE17CF8C9C544EA12", "D43D8B75C4370BD8DA08289E");
+test_comp128v3("1A7EF4A0F0C6F89DFA9020D69425B363", "BFA698344E2EC5C260D3A3CA");
+test_comp128v3("39FF53E64F2B4B8E445981EC8B04DE8F", "926623ACF4217A7D4DE94C15");
+test_comp128v3("7FB4E8BCE634F763D8FE2CD361057705", "3001951E5CDE9C320AABCBA6");
+test_comp128v3("744DE4EF2893C68A63D340FE40E2C1CE", "420982D52145AB5A54B5EADE");
+test_comp128v3("C8ECBED4C9D3639D3F1E5F4F34F682A1", "24865C4F6792107A3365B515");
+test_comp128v3("D00ED1D1D9CF74C256AB08C8621FA824", "665B7FA2B71AEB7C2DFF7724");
+test_comp128v3("83745C78C893B3F786C728DEF6A48A57", "6872F7917E0FEF0F22D5EA61");
+test_comp128v3("7FBB66885417679E5970AE6B72C5BE4A", "84E39E1931E4CA690551F64D");
+test_comp128v3("AF9D46D06E926C66835517B0155C8ECD", "150A4C90CF275DA03AA0A342");
+test_comp128v3("236722E40A7FE9AF48008DA60028B057", "28C9E79D9B3B9433BA2C8AE8");
+test_comp128v3("374143C31BF20F54B0300181515ECCEF", "028C56AE7D60DB723E0A4983");
+test_comp128v3("A84BEB57AB7EFA419243F145D7B81623", "4E00F03F82C080039BD00EF5");
+test_comp128v3("078CFA828AAC69931B30F86E3451B81E", "AE382636F9F94C763B8070A8");
+test_comp128v3("1F9476007304E4C4A3C1F14A27CB2EEA", "476B9D26577B59CB3E1C3C16");
+test_comp128v3("9DBAF388DAE2EB27B76759C2CCACBE8B", "263C08E1450EAEDF987EDCE7");
+test_comp128v3("9C2BF5F3EFC3DEA34B4EC3F633280B03", "41B8D9727A5E8469EDB661F0");
+test_comp128v3("AC7D476A7CD104A4C4B152FE82899E39", "A3B40003A9FD7894C517C8E3");
+test_comp128v3("178B204DF182A0560778E5B90D1EE170", "E77BF4BCA640C82BD2665C9B");
+test_comp128v3("9AEF8B5694B926BC4A55DED5259163A2", "55A731EF32A6CA3E833F50B4");
+test_comp128v3("6B8D7482E2E1A96F6B776DBE6F7E5661", "CC7E378D8C7AF20E8FCFA0A2");
+test_comp128v3("2AC44A80A5FE1003DD24AA4C6864BC61", "EF7AAE0833F9C3888473EC56");
+test_comp128v3("C260EAC6C71D7B7D35C5BE1CCB34212A", "716DC830699958E175632ABD");
+test_comp128v3("5946CA2D7232076E2DBEC266E794D1A4", "338283CB5CD91817A465438B");
+test_comp128v3("210EAA6C850967ACCF3325487329849F", "29D05D8C1655A641764CEAF0");
+test_comp128v3("393C4E5602EF6301890D599C7766CA53", "85EF9BF7A469BF0005D5AB2D");
+test_comp128v3("5ADE7491694A75A5CE0959996473031A", "78A42538B39E12BED126DD63");
+test_comp128v3("670A72D60EC6606E4E798EBB1D4AEA45", "3EB374EDE44B276A0B05F01D");
+test_comp128v3("23916D367C0E00CF13B2BA5849293281", "3BCEC47F6FF94E05EB2756D7");
+test_comp128v3("3D37E626165E8CFDA7F3503EE96D78AF", "7DB25F0039486AA1C17DEEBC");
+test_comp128v3("730EC7F1A1334D7A9FA161ECB8FE839D", "4DFCF9EDFE4056DAFBFDEDCC");
+test_comp128v3("BF7A806A9945108833CB3C136032A33D", "68CB935008CA401B29CDFE2C");
+test_comp128v3("148D1ADC6281ED1ECEEB4C61A5D5974B", "6611E49991CA4CCC35CA4BBC");
+test_comp128v3("5D941E9A26889E243CC7BA63356B8769", "B2F69A7288ED0794C8D8F3BB");
+test_comp128v3("52176D9C830733245657C8866CB3FD34", "07D624B781E45E1AB2885D43");
+test_comp128v3("B6677C3BD296A6AF1E3D08CB803D9F29", "6DA6E7C25EEFC908BC9CF2ED");
+test_comp128v3("35BC2C889362E17CC46F1DC4C69DE5AE", "9B632BFDC79828C2EAE40ACD");
+test_comp128v3("A8FE6732AC535DB718039CBB7FA0B0A1", "CED6008EBFC492271022666F");
+test_comp128v3("9CCB764C102F715FA8F6A77B9C681A29", "E63D04D6553D5A1272EC93FF");
+test_comp128v3("F430C9CCD6AC2C5448DFC20AEAC26B78", "A268C88D3C4AAC5C3C40B971");
+test_comp128v3("CD2A3D23F134BCD78AD204776884DF1D", "09C2DF4686D239A55FECF7C2");
+test_comp128v3("C6CA21CE9B44435BB2DA88DD15B1E1A6", "23E6DAB765FA303816298AC7");
+test_comp128v3("7F7A993FDE676BE6063A83BCCA2F4CA7", "A0C1BD80DBE3CDDF9157ED84");
+test_comp128v3("84D99115B8B2820C52546580B5A71517", "EF30D73FC9C7712BC1F4E834");
+test_comp128v3("8F104901412EE55AD48EC8A70C0D920A", "F9758C4357FCDDCFF5D7878C");
+test_comp128v3("B6E970122768A8B60D6ECF3590256587", "449F36D3222CBF4262F7052F");
+test_comp128v3("9456329E3E51725FF4306C1BF1FC84C5", "47E7AF27620A89B4F646FB8D");
+test_comp128v3("A320F5AB902AB2DA39A19819EC3DAE64", "F6BBE92D2F19DEE5593071F4");
+test_comp128v3("ACF41B8B0345BB7C0611BAA2F78CE719", "1FBC5EAEE93BD973D6AA6F6F");
+test_comp128v3("511765C125FBEE4D4E5BA1B339214FEA", "6B1389AE53C01A07793BE30D");
+test_comp128v3("960398E8F44A9F26F51694BDE61DE6E8", "1EBE854FECE2AB72356FF067");
+test_comp128v3("43172468FF0E5D538B047E89D9FAF128", "968204B1B443C19E2E861632");
+test_comp128v3("EA690D56A40B0E4810CAA7C3F48ACD9B", "2B46CF65F8EE650856000B67");
+test_comp128v3("CB2728D1C06FA1653325838B6786ED19", "C757A09F686434C6D2AF8FBB");
+test_comp128v3("4ED8EFD706EC13D2D8ADB73E922BDB96", "F188C54E0B4FADFEEEDA9E98");
+test_comp128v3("DE4E12D86637EC29DAF8366BE5A17078", "927929B7DC0C251D1B2BB0B7");
+test_comp128v3("2E241E86614830FC4AFBC8A9B994BC11", "37D46D82906800EA6197658F");
+test_comp128v3("FE24429FB354D1BE7F27D1220D7801EF", "A3D69BDD8EB8C24A6A19BC09");
+test_comp128v3("C1C835697AF1C95B77CB5D09ECBF402F", "684525124857136F39ADE1DD");
+test_comp128v3("AE8AD487E7DD8B1D8CABB82C4B3183CB", "EDED085BBF5084415D4E0205");
+test_comp128v3("70BD0DB40F3D976E4907DCBA3E40FEE1", "EFE6A6F67370348AE283ECCF");
+test_comp128v3("5B1072BA60B98806208700F6BBCB338F", "4C374E86BEBA04BAF4A2E11C");
+test_comp128v3("536C7B7A6D773299B839416916D78B6D", "1F09C90AD9654A66EDEC86C2");
+test_comp128v3("024FF49B1CA2AC1056801B2B9FF0BDF1", "3CB058ECA6914AD9AB4E58F9");
+test_comp128v3("3F73F9C6FD5F80388DE7CD0D447E34C0", "9D4EEE0478937DC2D7EFAC6E");
+test_comp128v3("56C45BC9B76D6D98B674258314ABD03F", "9307A97F0128F4A8A062C418");
+test_comp128v3("6EEDC53430538D16AEE578433AC82C87", "6BFE03DA1A89F83253866536");
+test_comp128v3("15526C4A50256C554663A59AFA31E799", "33F71C34F358D46AD9618212");
+test_comp128v3("31FFA6A17A8A7537CCF03A58E7F32A28", "52F193B5D4238BCBA227B4C7");
+test_comp128v3("C7EEE147EE90896BD4375E96B11D467D", "712F540C52D2B47177F8087D");
+test_comp128v3("218825B240C7C411F7A140834E3C6439", "7659E16CBE00DBEE2C89CA18");
+test_comp128v3("8A61111A6B336543BF5ECCEB68F7D15C", "2E895DC6ED3A9C291F169FA4");
+test_comp128v3("05582B24643BF38B4F0B95044533098F", "484D01822E1106E27D0DF3C4");
+test_comp128v3("A209067823F038C99E35FBBCA3BDE95A", "0DCD65DF9F21B41F6A1E5C8E");
+test_comp128v3("62EE981575D6A3250E3BA64DC029F078", "C7A87C7FA9F0372FE89BF1C4");
+test_comp128v3("062C73ABD56F61A3A42DAEE0F8EFD09D", "F5660C6FFCDA6350574A887B");
+test_comp128v3("2F4F96A5A4062B572F7BBE311ECF6492", "3F67F5A619A34CE49DC41D7E");
+test_comp128v3("B360977080E47CD9B3ED66ED22091F4C", "23822C6017E8BA385838AD0A");
+test_comp128v3("E6E7FBD7603CD52AC2EC8CE72041DD0A", "F76BC82C5FF94A2F0B411FD0");
+test_comp128v3("F53314B138E7CCAFB2F7131185918AEA", "7788F96D0ACEA0A2645D52A7");
+test_comp128v3("D6B452B402B2851C2012C30029645FBA", "311CA2C5A76C27E8C7A58685");
+test_comp128v3("AFDAF1BA6EF954C620B12F16762B35AE", "189701B5967B2D96457EC247");
+test_comp128v3("642D3D046F4F1EBD0C775B06592CDB27", "D054138B51038F23BD0171D8");
+test_comp128v3("CDB86368F91E71CE0FC34A70F6FD65C2", "74D76011A1912FA279AD1CC8");
+test_comp128v3("9FEDAF38BA24A6C82BBA68DA95B71176", "7FE4FCFBF42536F899ED7360");
+test_comp128v3("FEF1D522E84CB983A5090B4151780376", "1489794A18B7CBD81B3E6E57");
+test_comp128v3("861435ACCB03FB2D1BFCA69782DC469D", "4B4D585395AD649C89FE6139");
+test_comp128v3("995AD2985AD4E5A1F8802B90B7F8ECA2", "993C1F391FBAEDF577E144E0");
+test_comp128v3("742263AF7482CE41D4EF0B03A3AD9B7E", "ADB826CAEF2799A7A88DC866");
+test_comp128v3("479337DF35E98C67741FAD81CF3365B9", "FCD508F6FC1ABBE522E0B691");
+test_comp128v3("C7B92DE42D76D646284A2C2039F584B7", "C541B2C7568E4817DFF95FAB");
+test_comp128v3("D0C3DDC48EC35DA67B369A9DE0CCFFCC", "8AA975041C6BD0DA7586DF38");
+test_comp128v3("599B31C8C3852C1C23DD19A7ACD55C11", "07D62DF63575B19B89DA071F");
+test_comp128v3("0F269A0EA3E8EFEF5C3C52D43D002225", "A34DB9124623C3271061BAA6");
+test_comp128v3("6AD44C3FDCF70DD91323CC62CFB02D4E", "D17293017E5E46B0F054C9F7");
+test_comp128v3("BFE112DD9EBF7E917C561F72750B8912", "8825188DA2B08941B8089631");
+test_comp128v3("B87A63D3DCAB1BAA9A185650D52F98BA", "781E9ED97865B4EAEB24F256");
+test_comp128v3("15F374A04F54435C4986C3CEADE49078", "3803413B6A6B7D342431E19B");
+test_comp128v3("9597CEB0C4C1DE570C408C732C4AAEFF", "FFFCC0E7778720989F5D207B");
+test_comp128v3("9EA551BB7EEAB54B2CB0B7D5A4B46984", "12670590F5569DF36255182B");
+test_comp128v3("E0A14DD9D10CA02AAB400CDD6D4CC8DF", "3B8D453873F43514FBFD6E61");
+test_comp128v3("6D7D889972B4415B2C4BB94147A8B0C3", "9AB50F45E012C1C13DC0C123");
+test_comp128v3("BD143BC0B969AB78A4C77AEF75DF577B", "46BA7B2AACE2BE1EAE041BAA");
+test_comp128v3("AFA3644F3A1B2E3C949409CD1A9FD9EE", "4784E15B8459FC8C31BC5A09");
+test_comp128v3("F68A2E4BC14FF93B6E8AED6603681E44", "08C81D5D7ACCE6421A3A83C8");
+test_comp128v3("8958835A3177994FD9E234A64E8DD402", "4AB52CC30160C00D0CB8149E");
+test_comp128v3("FC5A744BCC4C3A64731A56568F1824CD", "7691B4A850CC3F520A27CAED");
+test_comp128v3("A0BC0414793FD7CD91C4901ED37CEFEE", "50387A5169C4BEF0DFABA169");
+test_comp128v3("CECD899ECEEA771F48B27ADAE869A0FA", "AC47989CED4D7ECC858B3316");
+test_comp128v3("227AE04F7CD994EC2C5028C28EB91C60", "2CF79C8535D09CBE08C99BC4");
+test_comp128v3("E1EFAED76F88CB13D36D6FA9341E6F72", "D0939986C168DF7A71F8692B");
+test_comp128v3("0BB39C124E6009D6F192BE076CE0B912", "07ECF9210CD0BA6CB7D0A331");
+test_comp128v3("66B94A56E5ED1CA6D8D13B716C826EFD", "AB586CD5C53D9B44AC9CF827");
+test_comp128v3("74BA0547304F65181E09067ADE3944A8", "1BC03458AF15F80379307560");
+test_comp128v3("E5BDCE39E73DED6127D19DC0D2B07509", "2AA15E6321BC75606E161299");
+test_comp128v3("5FA9FB4F33B5931DFD0B608D3673CD8B", "2EF8B7A870C0B16E2A2E0771");
+test_comp128v3("591C4B52914FD9906794F9B437297932", "57D657E1F9EE91A75BAA281C");
+test_comp128v3("88A97F10BDFC5900CE69B02CD4927D5F", "49B141185B37C4BFE45770A6");
+test_comp128v3("B699392799957BDD5F7578F616FE052D", "E9FAD536A1195A43049FD4FB");
+test_comp128v3("CFDE65C7EEAB7B468E5EB53B59560E56", "EBDD36D040A23090DD2B7C8C");
+test_comp128v3("7DD1288548E9EE0F59C7AB71ED93F458", "570BEE5B5B720E1C71CC4188");
+test_comp128v3("F8A953657955164A202FD05D61ADDF18", "ADB41A9171F3B9EE64CDA252");
+test_comp128v3("8496897F262DB3DA5B1492BD910EE533", "A116D7BB8E39511500CBB3C9");
+test_comp128v3("3143866A1BFF58CB64A6AAD00A8B98A0", "88A823BB6C87C33D326F89E5");
+test_comp128v3("BF2FA6BFFB949B5EF935D4A70A1AD000", "059EB7C4093E12A3D67AF43B");
+test_comp128v3("22D61E9979690AE0F70A2097CF75617A", "4CEA847615CAAB386441D6D3");
+test_comp128v3("8BA2EEFC875953E279A0BBFF3E40D8EF", "EEB30C7A21BF5CFF8C016E76");
+test_comp128v3("0054786656570CAF86CDA3E2658CBC4A", "A8D77AF4C0D2968BF86F7EBC");
+test_comp128v3("CD789413163D67BEB5A8B4B2041AC8C3", "1E8EFC2C49634EF3D3A76663");
+test_comp128v3("D5C2E3294F8E3F7EDDB41D8038E8AE4F", "DFB267CA183A97071445EBCD");
+test_comp128v3("4547BAA42EC0A2AAA5E4A51A40F508C7", "D726295FDD1F03CC38C0D510");
+test_comp128v3("30675DADA51DD427E74150DAA3D08E47", "D98EC07B336FF2636664CEDA");
+test_comp128v3("E2049EFBF5A6B4DBFC1D827F7B6F4F79", "8A5E61E9C25DABB569047DBB");
+test_comp128v3("4DAEAD6EFAFC7B2D5B7599CB572D966A", "3C7F91652C4F50ECAE96E31C");
+test_comp128v3("421C75DAE44E53BF661EC64716C3315F", "B7C0ECAAD15C95E5A230B0BC");
+test_comp128v3("3DB086094870B8992C429827A41388CE", "932A80FADA930997A72E765B");
+test_comp128v3("1AED87BACBA9546CEFD7577149B343CF", "37496BAE7BAB169805B8A404");
+test_comp128v3("1D4C0838314C7B2E969CA01BE86628E8", "FDA25570F91EE000DF4A462D");
+test_comp128v3("E2CFEBC9DF035EED50D61010B97114DF", "507A0EE2F0E2377E9617D79A");
+test_comp128v3("B7C41BDBCB3DC0D686FB4F2A246F1EBE", "9A8B1932BE12002117B66C60");
+test_comp128v3("7F3C6DDFAAB07F2655835AD684C1EAF8", "84F746F4206E7B9D70DCE400");
+test_comp128v3("4BE5B436297DD7EFE78F43B50E9D83AE", "6A7351376DEBD0C0045287F5");
+test_comp128v3("810A6D4C4BCC9B442FCC2D17302ABD52", "BE8DFEFD0040A9171DFD1028");
+test_comp128v3("70BCCBE54FA56D6C760B2A30F12DFE3C", "D066D8EAE15177FB53E82557");
+test_comp128v3("685C7FF8585763703654DA98CA2ADD46", "E3AA8A4EEFF90AE719F7C9A1");
+test_comp128v3("70EF0027B67540B854A5678C2FFDC575", "58B0CE51BE4916D04D84D042");
+test_comp128v3("AA3ED4404B99F8C48BC481239F470693", "C7184C0EC24D703FE74FE294");
+test_comp128v3("9E5EF17C6BEAA0E6B65199ED365DDAB3", "9EE43A1ED3DBFD931A317464");
+test_comp128v3("2E9403A2E3000BA5C4E0D27C2EC64E4C", "813E15EBE8D0B17B5823E4BE");
+test_comp128v3("0173781A0EB21F505712948BE75B8458", "1E3CA90D5A9E8A6464C8E36D");
+test_comp128v3("3C088C10489234D6328F501EDBC98C00", "9D7C6B66C8630DC1F3F5312D");
+test_comp128v3("A6EE02A9C039B2D0FC48FD8A345F1892", "0A822A5118F13C0B8734AF15");
+test_comp128v3("BE8C4CBEAF4F1F1A9A3695894F9F2A99", "CE0FCD1BC48933F43895E002");
+test_comp128v3("128432DC7C3146F692965FB0D0A3308D", "770FB50A91CC47B0382B00E0");
+test_comp128v3("C88C48F3C4D3EC720F01BF9C6A65265C", "8F6DF2A2261D41952A2C382A");
+test_comp128v3("B971116F61EB90F27D34457F37AD39D9", "2D33847A8C32844FDD307D51");
+test_comp128v3("227551D0863419A949DD59F36CC83C5D", "829E83017A42C31FB195868D");
+test_comp128v3("03234F66F921406777377B0DF204A6DE", "C43127089A8D4F7250387C9D");
+test_comp128v3("D88DF421C6DE2284789ED5D54F45F03D", "3495E7918C60F295548DB637");
+test_comp128v3("C3FA2BC662694EEBEE4781F73CBB18C4", "F691B5C42013705DE1C5AE33");
+test_comp128v3("2DDA9DD56CF3E1FA5BC6B9118623A733", "469BDF4A8704D356A9A14B2F");
+test_comp128v3("C6A403AE0667AF03479DA7D96BC52127", "4996261F8E580F85B515239D");
+test_comp128v3("9B2F270E808E89FE9FBF7910D371A99A", "7904A08879D7368E8D28AB78");
+test_comp128v3("346CF33E3A1CB2ECCBB0293B22718FE0", "4A381CFA638346B0AF50A973");
+test_comp128v3("9DBDF0D1632047D2E033CD99585D4EF4", "8D52FC66020546B7F4C0AA7D");
+test_comp128v3("C0C92335F55CAFA75C8F32ECC8A8382E", "AAFF1231A4FF3657E2A2F050");
+test_comp128v3("F98D69B9C10B8180D8CCEB6F8512EFC8", "ECBAA8308702D56AB4B8042E");
+test_comp128v3("D18711BD38803D6528A0ACC4A2D13F44", "FE40B2A809ED1B288A23EFED");
+test_comp128v3("B45EB08D49D283FE8D5CD8736CFBB0BA", "2DC3543A5FC96E5D77C6A8C6");
+test_comp128v3("E5A19F4E5A95B41A6B467A4BB61FAF6D", "FDDE48FF9ABA9E1772A997F1");
+test_comp128v3("DEC185B0F7E9E915A245BCDFAA61B9EE", "B4E8B8317BB9B202C1216D39");
+test_comp128v3("7CF79DD706BB5431EA0505C5A9FD02DD", "B25B155F0F33758DA3E34314");
+test_comp128v3("D7FB84CABA49F0DCDA6CF9BD8DC7706E", "47350A6046056A36028A17EB");
+test_comp128v3("1552B8D34DA464D3306574792B48A3D9", "E941C62F7B37C94DA75700C7");
+test_comp128v3("BC7D021BCD887C891AD03F35E0A15BAF", "BE126713B56707925500E8FB");
+test_comp128v3("9134777CBFF4D521A3BFA344F7A0B437", "FDBDA66AC1D9ADAD8755DFE7");
+test_comp128v3("7C5F8F8A6BCE5887333A3CAD627717E6", "31F970EEF6CD0EB0A2CF4916");
+test_comp128v3("DF8CB343C509A1AD4F2EA7115FCCDB06", "693FC7E51AEE34C25011590B");
+test_comp128v3("A91315BD7A33231FD527B607D84FC46E", "1830B3DC10BDF6965B2C9228");
+test_comp128v3("AD2E8EE1869FF0D47F58E63BC83318BE", "5FC704AB4A24BB4EE7D4E116");
+test_comp128v3("6DEAB545F792A82A34768E828C52895D", "2C2573C04319579657238F81");
+test_comp128v3("002EE13DDD62BB00513824409FCC1E3C", "714637C12FBC3464601C51B9");
+test_comp128v3("1B7F26BDA08083628375BEF7D154A39D", "70863DE4A86FFCBEB170F92D");
+test_comp128v3("50260BE0CBC61779A3974F29E8F759AA", "0977C5842CB846C0F9B7C801");
+test_comp128v3("7CBE72435D57253A7F17B38DBDC91592", "FE586E3BB775847DD4D53896");
+test_comp128v3("AD828B0B2057DC6664ED791C5D07B0F0", "2DA0521A402DB87C23EAA90F");
+test_comp128v3("4D15F0C7057F79CA95112266A0EC2372", "C8E87F6A99CD77F1E6C52BDB");
+test_comp128v3("2603498B980BB24F3CE98CDCE7132DEC", "3D120C7D2A0626188F107796");
+test_comp128v3("7D3F0B786427ACCC65036320D6A87E0D", "90EB68AFCE491FF4FC1B935C");
+test_comp128v3("CEF85C041E9B497E104280592B4E20A5", "D7088EDD39E9441B42FA5425");
+test_comp128v3("28423C88529A282FEB63168012C7C9CA", "8541314D155ED02F6180F8C4");
+test_comp128v3("3D00F94DF50F19EB4FABAEABCFD6D2D0", "CBDC0CBBA58873A08254EBBB");
+test_comp128v3("2770701DD269B0577098A27093E4CBEA", "D433D01C27279F7871B8CFBE");
+test_comp128v3("A6196F4218B78A98B4744669E89272A4", "6D9355226838299D764B92FD");
+test_comp128v3("C2746977B2BDFC92BFBD7B108E31888F", "13349E628F46C2871403E8BD");
+test_comp128v3("EEC4692F450CC83E13E2B71D59B3A3A6", "8373F07A23AA5A770F09475E");
+test_comp128v3("C6016D020BC4C0FFB135065862E023E1", "C65DD883032F002242741716");
+test_comp128v3("7EC7083310F0065F3B96E8472A3B13D1", "1023CB8704E3F094C4254B40");
+test_comp128v3("CF0F35022BD195F113924F7FE3170685", "F91BEF9CEBB0E62E17D189F1");
+test_comp128v3("13B6AB833AA9181A0D0589C321E1D641", "B2393292651E5C5FAC10EEA2");
+test_comp128v3("78E0922DFDD9664537CA30DE31608273", "2BB017C76BEC45864CE8B5DA");
+test_comp128v3("92D6133ADB98116A1A8D5233C0D82798", "A638E5FB37FD99BDAE373B67");
+test_comp128v3("E63DD02079DC3B5E6EB39366B31C321B", "6F90B73C6489D8259C852982");
+test_comp128v3("18A08A6C828005707749B55A9B372486", "D5EB1D8073248AD37B8F239D");
+test_comp128v3("D27A6AFDA367D22533F4A41A08E728D6", "C0C1517C33F09E91EAC4779A");
+test_comp128v3("9DB473E798478DA64AF4ED702F4BC9F2", "3022D423A1C7629E82A5A24E");
+test_comp128v3("D9B45909B8FB54224223494EEA6E2E30", "032151B6AB3213BB7DDFD85D");
+test_comp128v3("8031C87FC0B6482EE41FDAE9452ECB50", "B6944CB7B74EAB9C478F1C52");
+test_comp128v3("D75BDA282D511A8FDA9D2A668C9C8DAF", "2C95EC0DD014289FFB35213F");
+test_comp128v3("CEF23CA86FACB24BC87C7A6F8E3B1C88", "18A4524E271A78D2DE5E7499");
+test_comp128v3("FF647B9CC719F05B6FC23549E39B1357", "0C865AB29E48C3C797B021D6");
+test_comp128v3("27F8DA6420EAD9D9577BB23FBC8505DF", "C3C5015754C0AFA87182B618");
+test_comp128v3("FF8354FB4DF2F4DCAD9810E6D6BE2701", "82C123077C870F46B60D29BD");
+test_comp128v3("036E56648887DBAA96FFA30E83D8AD82", "B8DCA4880CAD8DB0E51FD51C");
+test_comp128v3("86E00865EFAA86388A9005B52F005717", "9241332752219C7BB5A5D3CD");
+test_comp128v3("C9629A55E7894963AD2EEC2468FE7DBB", "E1136ACDFFD9517A555B0F27");
+test_comp128v3("E09B731993F83B3F5B3870A764951ABE", "9BF47B15A4384C738487A24B");
+test_comp128v3("2378E55D7C30DFC2977F4B1DC4F9F5E8", "48EB70F03A3CCB87CD82A158");
+test_comp128v3("6184EA341945118E0646CBA5D58384B4", "77675FA63C5BD4A41AC05381");
+test_comp128v3("3BFDF98A86FD921FB72992EC14F7BB45", "F538CEDE6978E01EAACBB45B");
+test_comp128v3("4456A5A28E7312B13115462951C92AFC", "72A818CF0DC16D1CF61AAB95");
+test_comp128v3("FDD3E2786D4F2CFD9480D66573BCFC9C", "2841008AEF343BB28D103C8D");
+test_comp128v3("6DD6D61B2289A9410CFD9714CCECD4C8", "F59AB2D1B59D311242978F3F");
+test_comp128v3("F167C9441706277D09DDCDD7F7E52A64", "126858DE4A4F5819448B7834");
+test_comp128v3("980FF11DA5B60F457D8AD89A85974578", "379707AA8D96C5D76FA14C03");
+test_comp128v3("70AAC833D245454AE89349A7446EE506", "5D79BA0B6FFD9631AF1CB4C7");
+test_comp128v3("927181350C74438DE3B9E8098A617E9B", "F763FBD8F7F514F54B18A736");
+test_comp128v3("CE2FC9FE401D5EEDE241E70202A918BB", "6D93D8C321F8333F0AEA5D7D");
+test_comp128v3("D0052E34EB985536E784846E9328E485", "4AE323A62EEF67882838712A");
+test_comp128v3("F0258B4E9282FCC224A3D6CD3B03CBD9", "91BE1348792DB54DB4CE8DED");
+test_comp128v3("D14CDA342B363D1D643CA37F69459559", "9B5263789B687DA01AD2408F");
+test_comp128v3("3D1B125A5450D9985C1FEB85BBC60A50", "36BEC97C697AA64958AC0150");
+test_comp128v3("89F95D62593A08D9C22E451C7FB8C5BA", "D57A8B45E9EAA8AB3A514325");
+test_comp128v3("5B1FD080E4A66D7057CFADEF6FED9146", "1BCDA51E01D6A6BD34B4447F");
+test_comp128v3("AFA91C8D534BF172752D8A5A1E5ABF18", "1171B4FE506D4A96CDDEC1A3");
+test_comp128v3("928BCB824199B826E519EADA61A4C251", "EB2F19E5D73C846D8C714D3E");
+test_comp128v3("F53692F3FB3982697190167751544A29", "7AD742D7360FA6C3D379B2B7");
+test_comp128v3("35E981691DFC03F3594134BBB7666048", "E80AAB73B59F47BF4EE3ACA2");
+test_comp128v3("E1C591DED46E4151A2758DEE40739154", "46FE19CD4CFFB3A2A20C2DED");
+test_comp128v3("FDDF6A5BB92A46D2E62A4D892D62DA3E", "91A0067C6F79DCDD23291EB7");
+test_comp128v3("2D57BDF8192CA18115B7D8475314A217", "9714FD544700667211D17C04");
+test_comp128v3("9B362EB227A8F692CBCAAE2A35126D4A", "6A9616C6A43C428F826A47F7");
+test_comp128v3("AFA7288D0D58EADB8C9786402A6E64D8", "997C242B49CB8E562D648454");
+test_comp128v3("DC52408D8CA5204B565EDC5A0FC1F62A", "EE9CB66B77A507D22BF44138");
+test_comp128v3("FCF61DE9035595B243208EC41671C65A", "2FF077600EA3FA5A0D767078");
+test_comp128v3("9A583D3FDD3982A15876623AA3030000", "19A047A8A76A867653D4911C");
+test_comp128v3("2DCFD0800243D3D0D713A0FE4B1C41C9", "C64FC0E63E24B5990B10208E");
+test_comp128v3("72213EB6319FADAE5FFE5DBD4FD3F03E", "8BB15C1A20487D9F61DCBB2A");
+test_comp128v3("2895AC8B667126F52244CEC0831D2C11", "179EC0708E768B387560A03E");
+test_comp128v3("7091C9ABA945FF32B3CD9818DE84AEE5", "2107211D6BC2DC36418D81EC");
+test_comp128v3("219370CB0713962417F979DED2342075", "BF57FBAF865A8BADEA9AABE7");
+test_comp128v3("F836740255B8ECB99BA7AEE2CE8C53BB", "A3A5D83BA6B66D2EC39B4035");
+test_comp128v3("E31DC4205A93AC332F96CA5E7BC9FCA7", "73483A4971376226A7D00F88");
+test_comp128v3("A5128C271F394B9FF303E9D169451808", "E189B4B86326531262853FCB");
+test_comp128v3("7F8AB312C58E36A6FF9B80D9FA784D4F", "77BEDAC4AF6EBD00DDCA8663");
+test_comp128v3("DD6FAC2574A4358D54BF123B5B2E4D47", "29D903403B472B3968A0D511");
+test_comp128v3("85CAE759E11DCD58B02C500C539C1FD2", "EA65F4805AED4E612A71FB27");
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/tests/comp128/comp128_test.ok b/src/shared/libosmocore/tests/comp128/comp128_test.ok
new file mode 100644
index 00000000..cdac5f25
--- /dev/null
+++ b/src/shared/libosmocore/tests/comp128/comp128_test.ok
@@ -0,0 +1,2050 @@
+COMP128v2 support: 1
+COMP128v3 support: 1
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
+0 OK
diff --git a/src/shared/libosmocore/tests/conv/conv_test.c b/src/shared/libosmocore/tests/conv/conv_test.c
index 54e043e9..608f829b 100644
--- a/src/shared/libosmocore/tests/conv/conv_test.c
+++ b/src/shared/libosmocore/tests/conv/conv_test.c
@@ -1,10 +1,12 @@
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <time.h>
#include <osmocom/core/bits.h>
#include <osmocom/core/conv.h>
#include <osmocom/core/utils.h>
+#include <osmocom/gsm/gsm0503.h>
#define MAX_LEN_BITS 512
#define MAX_LEN_BYTES (512/8)
@@ -14,107 +16,6 @@
/* Test codes */
/* ------------------------------------------------------------------------ */
-/* GSM xCCH -> Non-recursive code, flushed, not punctured */
-static const uint8_t conv_gsm_xcch_next_output[][2] = {
- { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
- { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
- { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
- { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
-};
-
-static const uint8_t conv_gsm_xcch_next_state[][2] = {
- { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
- { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
- { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
- { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
-};
-
-static const struct osmo_conv_code conv_gsm_xcch = {
- .N = 2,
- .K = 5,
- .len = 224,
- .term = CONV_TERM_FLUSH,
- .next_output = conv_gsm_xcch_next_output,
- .next_state = conv_gsm_xcch_next_state,
-};
-
-
-/* GSM TCH/AFS 7.95 -> Recursive code, flushed, with puncturing */
-static const uint8_t conv_gsm_tch_afs_7_95_next_output[][2] = {
- { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
- { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
- { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
- { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
- { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
- { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
- { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
- { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
- { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
- { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
- { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
- { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
- { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
- { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
- { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
- { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
-};
-
-static const uint8_t conv_gsm_tch_afs_7_95_next_state[][2] = {
- { 0, 1 }, { 2, 3 }, { 5, 4 }, { 7, 6 },
- { 9, 8 }, { 11, 10 }, { 12, 13 }, { 14, 15 },
- { 16, 17 }, { 18, 19 }, { 21, 20 }, { 23, 22 },
- { 25, 24 }, { 27, 26 }, { 28, 29 }, { 30, 31 },
- { 33, 32 }, { 35, 34 }, { 36, 37 }, { 38, 39 },
- { 40, 41 }, { 42, 43 }, { 45, 44 }, { 47, 46 },
- { 49, 48 }, { 51, 50 }, { 52, 53 }, { 54, 55 },
- { 56, 57 }, { 58, 59 }, { 61, 60 }, { 63, 62 },
- { 1, 0 }, { 3, 2 }, { 4, 5 }, { 6, 7 },
- { 8, 9 }, { 10, 11 }, { 13, 12 }, { 15, 14 },
- { 17, 16 }, { 19, 18 }, { 20, 21 }, { 22, 23 },
- { 24, 25 }, { 26, 27 }, { 29, 28 }, { 31, 30 },
- { 32, 33 }, { 34, 35 }, { 37, 36 }, { 39, 38 },
- { 41, 40 }, { 43, 42 }, { 44, 45 }, { 46, 47 },
- { 48, 49 }, { 50, 51 }, { 53, 52 }, { 55, 54 },
- { 57, 56 }, { 59, 58 }, { 60, 61 }, { 62, 63 },
-};
-
-static const uint8_t conv_gsm_tch_afs_7_95_next_term_output[] = {
- 0, 3, 5, 6, 5, 6, 0, 3, 3, 0, 6, 5, 6, 5, 3, 0,
- 4, 7, 1, 2, 1, 2, 4, 7, 7, 4, 2, 1, 2, 1, 7, 4,
- 7, 4, 2, 1, 2, 1, 7, 4, 4, 7, 1, 2, 1, 2, 4, 7,
- 3, 0, 6, 5, 6, 5, 3, 0, 0, 3, 5, 6, 5, 6, 0, 3,
-};
-
-static const uint8_t conv_gsm_tch_afs_7_95_next_term_state[] = {
- 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
- 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
- 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
- 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
-};
-
-static int conv_gsm_tch_afs_7_95_puncture[] = {
- 1, 2, 4, 5, 8, 22, 70, 118, 166, 214, 262, 310,
- 317, 319, 325, 332, 334, 341, 343, 349, 356, 358, 365, 367,
- 373, 380, 382, 385, 389, 391, 397, 404, 406, 409, 413, 415,
- 421, 428, 430, 433, 437, 439, 445, 452, 454, 457, 461, 463,
- 469, 476, 478, 481, 485, 487, 490, 493, 500, 502, 503, 505,
- 506, 508, 509, 511, 512,
- -1, /* end */
-};
-
-static const struct osmo_conv_code conv_gsm_tch_afs_7_95 = {
- .N = 3,
- .K = 7,
- .len = 165,
- .term = CONV_TERM_FLUSH,
- .next_output = conv_gsm_tch_afs_7_95_next_output,
- .next_state = conv_gsm_tch_afs_7_95_next_state,
- .next_term_output = conv_gsm_tch_afs_7_95_next_term_output,
- .next_term_state = conv_gsm_tch_afs_7_95_next_term_state,
- .puncture = conv_gsm_tch_afs_7_95_puncture,
-};
-
-
/* GMR-1 TCH3 Speech -> Non recursive code, tail-biting, punctured */
static const uint8_t conv_gmr1_tch3_speech_next_output[][2] = {
{ 0, 3 }, { 1, 2 }, { 3, 0 }, { 2, 1 },
@@ -219,18 +120,6 @@ static const struct osmo_conv_code conv_wimax_fch = {
.next_state = conv_wimax_fch_next_state,
};
-
-/* Random code -> Non recursive code, direct truncation, non-punctured */
-static const struct osmo_conv_code conv_trunc = {
- .N = 2,
- .K = 5,
- .len = 224,
- .term = CONV_TERM_TRUNCATION,
- .next_output = conv_gsm_xcch_next_output,
- .next_state = conv_gsm_xcch_next_state,
-};
-
-
/* ------------------------------------------------------------------------ */
/* Test vectors */
/* ------------------------------------------------------------------------ */
@@ -245,87 +134,6 @@ struct conv_test_vector {
pbit_t vec_out[MAX_LEN_BYTES];
};
-static const struct conv_test_vector tests[] = {
- {
- .name = "GSM xCCH (non-recursive, flushed, not punctured)",
- .code = &conv_gsm_xcch,
- .in_len = 224,
- .out_len = 456,
- .has_vec = 1,
- .vec_in = { 0xf3, 0x1d, 0xb4, 0x0c, 0x4d, 0x1d, 0x9d, 0xae,
- 0xc0, 0x0a, 0x42, 0x57, 0x13, 0x60, 0x80, 0x96,
- 0xef, 0x23, 0x7e, 0x4c, 0x1d, 0x96, 0x24, 0x19,
- 0x17, 0xf2, 0x44, 0x99 },
- .vec_out = { 0xe9, 0x4d, 0x70, 0xab, 0xa2, 0x87, 0xf0, 0xe7,
- 0x04, 0x14, 0x7c, 0xab, 0xaf, 0x6b, 0xa1, 0x16,
- 0xeb, 0x30, 0x00, 0xde, 0xc8, 0xfd, 0x0b, 0x85,
- 0x80, 0x41, 0x4a, 0xcc, 0xd3, 0xc0, 0xd0, 0xb6,
- 0x26, 0xe5, 0x4e, 0x32, 0x49, 0x69, 0x38, 0x17,
- 0x33, 0xab, 0xaf, 0xb6, 0xc1, 0x08, 0xf3, 0x9f,
- 0x8c, 0x75, 0x6a, 0x4e, 0x08, 0xc4, 0x20, 0x5f,
- 0x8f },
- },
- {
- .name = "GSM TCH/AFS 7.95 (recursive, flushed, punctured)",
- .code = &conv_gsm_tch_afs_7_95,
- .in_len = 165,
- .out_len = 448,
- .has_vec = 1,
- .vec_in = { 0x87, 0x66, 0xc3, 0x58, 0x09, 0xd4, 0x06, 0x59,
- 0x10, 0xbf, 0x6b, 0x7f, 0xc8, 0xed, 0x72, 0xaa,
- 0xc1, 0x3d, 0xf3, 0x1e, 0xb0 },
- .vec_out = { 0x92, 0xbc, 0xde, 0xa0, 0xde, 0xbe, 0x01, 0x2f,
- 0xbe, 0xe4, 0x61, 0x32, 0x4d, 0x4f, 0xdc, 0x41,
- 0x43, 0x0d, 0x15, 0xe0, 0x23, 0xdd, 0x18, 0x91,
- 0xe5, 0x36, 0x2d, 0xb7, 0xd9, 0x78, 0xb8, 0xb1,
- 0xb7, 0xcb, 0x2f, 0xc0, 0x52, 0x8f, 0xe2, 0x8c,
- 0x6f, 0xa6, 0x79, 0x88, 0xed, 0x0c, 0x2e, 0x9e,
- 0xa1, 0x5f, 0x45, 0x4a, 0xfb, 0xe6, 0x5a, 0x9c },
- },
- {
- .name = "GMR-1 TCH3 Speech (non-recursive, tail-biting, punctured)",
- .code = &conv_gmr1_tch3_speech,
- .in_len = 48,
- .out_len = 72,
- .has_vec = 1,
- .vec_in = { 0x4d, 0xcb, 0xfc, 0x72, 0xf4, 0x8c },
- .vec_out = { 0xc0, 0x86, 0x63, 0x4b, 0x8b, 0xd4, 0x6a, 0x76, 0xb2 },
- },
- {
- .name = "WiMax FCH (non-recursive, tail-biting, not punctured)",
- .code = &conv_wimax_fch,
- .in_len = 48,
- .out_len = 96,
- .has_vec = 1,
- .vec_in = { 0xfc, 0xa0, 0xa0, 0xfc, 0xa0, 0xa0 },
- .vec_out = { 0x19, 0x42, 0x8a, 0xed, 0x21, 0xed, 0x19, 0x42,
- 0x8a, 0xed, 0x21, 0xed },
- },
- {
- .name = "??? (non-recursive, direct truncation, not punctured)",
- .code = &conv_trunc,
- .in_len = 224,
- .out_len = 448,
- .has_vec = 1,
- .vec_in = { 0xe5, 0xe0, 0x85, 0x7e, 0xf7, 0x08, 0x19, 0x5a,
- 0xb9, 0xad, 0x82, 0x37, 0x98, 0x8b, 0x26, 0xb9,
- 0x81, 0x26, 0x9c, 0x75, 0xaf, 0xf3, 0xcb, 0x07,
- 0xac, 0x63, 0xe2, 0x9c,
- },
- .vec_out = { 0xea, 0x3b, 0x55, 0x0c, 0xd3, 0xf7, 0x85, 0x69,
- 0xe5, 0x79, 0x83, 0xd3, 0xc3, 0x9f, 0xb8, 0x61,
- 0x21, 0x63, 0x51, 0x18, 0xac, 0xcd, 0x32, 0x49,
- 0x53, 0x5c, 0x13, 0x1d, 0xbe, 0x05, 0x11, 0x63,
- 0x5c, 0xc3, 0x42, 0x05, 0x1c, 0x68, 0x0a, 0xb4,
- 0x61, 0x15, 0xaa, 0x4d, 0x94, 0xed, 0xb3, 0x3a,
- 0x5d, 0x1b, 0x09, 0xc2, 0x99, 0x01, 0xec, 0x68 },
- },
- { /* end */ },
-};
-
-
-
-
/* ------------------------------------------------------------------------ */
/* Main */
/* ------------------------------------------------------------------------ */
@@ -338,29 +146,100 @@ fill_random(ubit_t *b, int n)
b[i] = random() & 1;
}
-static void
-ubit_to_sbit(sbit_t *dst, ubit_t *src, int n)
-{
- int i;
- for (i=0; i<n; i++)
- dst[i] = src[i] ? -127 : 127;
-}
-
-static void
-sbit_to_ubit(ubit_t *dst, sbit_t *src, int n)
-{
- int i;
- for (i=0; i<n; i++)
- dst[i] = src[i] < 0;
-}
-
-
-int main(int argc, char argv[])
+int main(int argc, char *argv[])
{
const struct conv_test_vector *tst;
ubit_t *bu0, *bu1;
sbit_t *bs;
+/* Random code -> Non recursive code, direct truncation, non-punctured */
+ const struct osmo_conv_code conv_trunc = {
+ .N = 2,
+ .K = 5,
+ .len = 224,
+ .term = CONV_TERM_TRUNCATION,
+ .next_output = gsm0503_xcch.next_output,
+ .next_state = gsm0503_xcch.next_state,
+ };
+
+ const struct conv_test_vector tests[] = {
+ {
+ .name = "GSM xCCH (non-recursive, flushed, not punctured)",
+ .code = &gsm0503_xcch,
+ .in_len = 224,
+ .out_len = 456,
+ .has_vec = 1,
+ .vec_in = { 0xf3, 0x1d, 0xb4, 0x0c, 0x4d, 0x1d, 0x9d, 0xae,
+ 0xc0, 0x0a, 0x42, 0x57, 0x13, 0x60, 0x80, 0x96,
+ 0xef, 0x23, 0x7e, 0x4c, 0x1d, 0x96, 0x24, 0x19,
+ 0x17, 0xf2, 0x44, 0x99 },
+ .vec_out = { 0xe9, 0x4d, 0x70, 0xab, 0xa2, 0x87, 0xf0, 0xe7,
+ 0x04, 0x14, 0x7c, 0xab, 0xaf, 0x6b, 0xa1, 0x16,
+ 0xeb, 0x30, 0x00, 0xde, 0xc8, 0xfd, 0x0b, 0x85,
+ 0x80, 0x41, 0x4a, 0xcc, 0xd3, 0xc0, 0xd0, 0xb6,
+ 0x26, 0xe5, 0x4e, 0x32, 0x49, 0x69, 0x38, 0x17,
+ 0x33, 0xab, 0xaf, 0xb6, 0xc1, 0x08, 0xf3, 0x9f,
+ 0x8c, 0x75, 0x6a, 0x4e, 0x08, 0xc4, 0x20, 0x5f,
+ 0x8f },
+ },
+ {
+ .name = "GSM TCH/AFS 7.95 (recursive, flushed, punctured)",
+ .code = &gsm0503_tch_afs_7_95,
+ .in_len = 165,
+ .out_len = 448,
+ .has_vec = 1,
+ .vec_in = { 0x87, 0x66, 0xc3, 0x58, 0x09, 0xd4, 0x06, 0x59,
+ 0x10, 0xbf, 0x6b, 0x7f, 0xc8, 0xed, 0x72, 0xaa,
+ 0xc1, 0x3d, 0xf3, 0x1e, 0xb0 },
+ .vec_out = { 0x92, 0xbc, 0xde, 0xa0, 0xde, 0xbe, 0x01, 0x2f,
+ 0xbe, 0xe4, 0x61, 0x32, 0x4d, 0x4f, 0xdc, 0x41,
+ 0x43, 0x0d, 0x15, 0xe0, 0x23, 0xdd, 0x18, 0x91,
+ 0xe5, 0x36, 0x2d, 0xb7, 0xd9, 0x78, 0xb8, 0xb1,
+ 0xb7, 0xcb, 0x2f, 0xc0, 0x52, 0x8f, 0xe2, 0x8c,
+ 0x6f, 0xa6, 0x79, 0x88, 0xed, 0x0c, 0x2e, 0x9e,
+ 0xa1, 0x5f, 0x45, 0x4a, 0xfb, 0xe6, 0x5a, 0x9c },
+ },
+ {
+ .name = "GMR-1 TCH3 Speech (non-recursive, tail-biting, punctured)",
+ .code = &conv_gmr1_tch3_speech,
+ .in_len = 48,
+ .out_len = 72,
+ .has_vec = 1,
+ .vec_in = { 0x4d, 0xcb, 0xfc, 0x72, 0xf4, 0x8c },
+ .vec_out = { 0xc0, 0x86, 0x63, 0x4b, 0x8b, 0xd4, 0x6a, 0x76, 0xb2 },
+ },
+ {
+ .name = "WiMax FCH (non-recursive, tail-biting, not punctured)",
+ .code = &conv_wimax_fch,
+ .in_len = 48,
+ .out_len = 96,
+ .has_vec = 1,
+ .vec_in = { 0xfc, 0xa0, 0xa0, 0xfc, 0xa0, 0xa0 },
+ .vec_out = { 0x19, 0x42, 0x8a, 0xed, 0x21, 0xed, 0x19, 0x42,
+ 0x8a, 0xed, 0x21, 0xed },
+ },
+ {
+ .name = "??? (non-recursive, direct truncation, not punctured)",
+ .code = &conv_trunc,
+ .in_len = 224,
+ .out_len = 448,
+ .has_vec = 1,
+ .vec_in = { 0xe5, 0xe0, 0x85, 0x7e, 0xf7, 0x08, 0x19, 0x5a,
+ 0xb9, 0xad, 0x82, 0x37, 0x98, 0x8b, 0x26, 0xb9,
+ 0x81, 0x26, 0x9c, 0x75, 0xaf, 0xf3, 0xcb, 0x07,
+ 0xac, 0x63, 0xe2, 0x9c,
+ },
+ .vec_out = { 0xea, 0x3b, 0x55, 0x0c, 0xd3, 0xf7, 0x85, 0x69,
+ 0xe5, 0x79, 0x83, 0xd3, 0xc3, 0x9f, 0xb8, 0x61,
+ 0x21, 0x63, 0x51, 0x18, 0xac, 0xcd, 0x32, 0x49,
+ 0x53, 0x5c, 0x13, 0x1d, 0xbe, 0x05, 0x11, 0x63,
+ 0x5c, 0xc3, 0x42, 0x05, 0x1c, 0x68, 0x0a, 0xb4,
+ 0x61, 0x15, 0xaa, 0x4d, 0x94, 0xed, 0xb3, 0x3a,
+ 0x5d, 0x1b, 0x09, 0xc2, 0x99, 0x01, 0xec, 0x68 },
+ },
+ { /* end */ },
+ };
+
srandom(time(NULL));
bu0 = malloc(sizeof(ubit_t) * MAX_LEN_BITS);
@@ -421,7 +300,7 @@ int main(int argc, char argv[])
printf("[..] Decoding: ");
- ubit_to_sbit(bs, bu0, l);
+ osmo_ubit2sbit(bs, bu0, l);
l = osmo_conv_decode(tst->code, bs, bu1);
if (l != 0) {
@@ -456,7 +335,7 @@ int main(int argc, char argv[])
return -1;
}
- ubit_to_sbit(bs, bu1, l);
+ osmo_ubit2sbit(bs, bu1, l);
l = osmo_conv_decode(tst->code, bs, bu1);
if (l != 0) {
diff --git a/src/shared/libosmocore/tests/fr/fr_test.c b/src/shared/libosmocore/tests/fr/fr_test.c
new file mode 100644
index 00000000..980259be
--- /dev/null
+++ b/src/shared/libosmocore/tests/fr/fr_test.c
@@ -0,0 +1,79 @@
+/*
+ * (C) 2012 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU 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/>.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <osmocom/core/application.h>
+
+#include <osmocom/gprs/gprs_ns.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <dlfcn.h>
+
+int (*real_socket)(int, int, int);
+
+static int GR_SOCKET = -1;
+
+static void resolve_real(void)
+{
+ if (real_socket)
+ return;
+ real_socket = dlsym(RTLD_NEXT, "socket");
+}
+
+int socket(int domain, int type, int protocol)
+{
+ int fd;
+
+ resolve_real();
+ if (domain != AF_INET || type != SOCK_RAW || protocol != IPPROTO_GRE)
+ return (*real_socket)(domain, type, protocol);
+
+ /* Now call socket with a normal UDP/IP socket and assign to GR_SOCKET */
+ fd = (*real_socket)(domain, SOCK_DGRAM, IPPROTO_UDP);
+ GR_SOCKET = fd;
+ return fd;
+}
+
+void bssgp_prim_cb()
+{
+}
+
+static const struct log_info log_info = {};
+
+int main(int argc, char **argv)
+{
+ int rc;
+ struct gprs_ns_inst *nsi;
+
+ log_init(&log_info, NULL);
+
+ nsi = gprs_ns_instantiate(NULL, NULL);
+ nsi->frgre.enabled = 1;
+
+ rc = gprs_ns_frgre_listen(nsi);
+ printf("Result: %s\n", rc == GR_SOCKET ? "PASSED" : "FAILED");
+ return rc == GR_SOCKET ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+
diff --git a/src/shared/libosmocore/debian/docs b/src/shared/libosmocore/tests/fr/fr_test.err
index e69de29b..e69de29b 100644
--- a/src/shared/libosmocore/debian/docs
+++ b/src/shared/libosmocore/tests/fr/fr_test.err
diff --git a/src/shared/libosmocore/tests/fr/fr_test.ok b/src/shared/libosmocore/tests/fr/fr_test.ok
new file mode 100644
index 00000000..6a928840
--- /dev/null
+++ b/src/shared/libosmocore/tests/fr/fr_test.ok
@@ -0,0 +1 @@
+Result: PASSED
diff --git a/src/shared/libosmocore/tests/fsm/fsm_test.c b/src/shared/libosmocore/tests/fsm/fsm_test.c
new file mode 100644
index 00000000..7a644213
--- /dev/null
+++ b/src/shared/libosmocore/tests/fsm/fsm_test.c
@@ -0,0 +1,157 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/fsm.h>
+
+enum {
+ DMAIN,
+};
+
+static void *g_ctx;
+
+
+enum test_fsm_states {
+ ST_NULL = 0,
+ ST_ONE,
+ ST_TWO,
+};
+
+enum test_fsm_evt {
+ EV_A,
+ EV_B,
+};
+
+static void test_fsm_null(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case EV_A:
+ OSMO_ASSERT(data == (void *) 23);
+ osmo_fsm_inst_state_chg(fi, ST_ONE, 0, 0);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+static void test_fsm_one(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case EV_B:
+ OSMO_ASSERT(data == (void *) 42);
+ osmo_fsm_inst_state_chg(fi,ST_TWO, 1, 2342);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+static int test_fsm_tmr_cb(struct osmo_fsm_inst *fi)
+{
+ OSMO_ASSERT(fi->T == 2342);
+ OSMO_ASSERT(fi->state == ST_TWO);
+ LOGP(DMAIN, LOGL_INFO, "Timer\n");
+
+ exit(0);
+}
+
+static struct osmo_fsm_state test_fsm_states[] = {
+ [ST_NULL] = {
+ .in_event_mask = (1 << EV_A),
+ .out_state_mask = (1 << ST_ONE),
+ .name = "NULL",
+ .action = test_fsm_null,
+ },
+ [ST_ONE]= {
+ .in_event_mask = (1 << EV_B),
+ .out_state_mask = (1 << ST_TWO),
+ .name = "ONE",
+ .action= test_fsm_one,
+ },
+ [ST_TWO]= {
+ .in_event_mask = 0,
+ .name = "TWO",
+ .action = NULL,
+ },
+};
+
+static struct osmo_fsm fsm = {
+ .name = "Test FSM",
+ .states = test_fsm_states,
+ .num_states = ARRAY_SIZE(test_fsm_states),
+ .log_subsys = DMAIN,
+};
+
+static struct osmo_fsm_inst *foo(void)
+{
+ struct osmo_fsm_inst *fi;
+
+ LOGP(DMAIN, LOGL_INFO, "Checking FSM allocation\n");
+ fi = osmo_fsm_inst_alloc(&fsm, g_ctx, NULL, LOGL_DEBUG, NULL);
+ OSMO_ASSERT(fi);
+ OSMO_ASSERT(fi->fsm == &fsm);
+ OSMO_ASSERT(!strncmp(osmo_fsm_inst_name(fi), fsm.name, strlen(fsm.name)));
+ OSMO_ASSERT(fi->state == ST_NULL);
+ OSMO_ASSERT(fi->log_level == LOGL_DEBUG);
+
+ /* Try invalid state transition */
+ osmo_fsm_inst_dispatch(fi, EV_B, (void *) 42);
+ OSMO_ASSERT(fi->state == ST_NULL);
+
+ /* Legitimate state transition */
+ osmo_fsm_inst_dispatch(fi, EV_A, (void *) 23);
+ OSMO_ASSERT(fi->state == ST_ONE);
+
+ /* Legitimate transition with timer */
+ fsm.timer_cb = test_fsm_tmr_cb;
+ osmo_fsm_inst_dispatch(fi, EV_B, (void *) 42);
+ OSMO_ASSERT(fi->state == ST_TWO);
+
+
+ return fi;
+}
+
+static const struct log_info_cat default_categories[] = {
+ [DMAIN] = {
+ .name = "DMAIN",
+ .description = "Main",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+};
+
+static const struct log_info log_info = {
+ .cat = default_categories,
+ .num_cat = ARRAY_SIZE(default_categories),
+};
+
+int main(int argc, char **argv)
+{
+ struct log_target *stderr_target;
+ struct osmo_fsm_inst *finst;
+
+ osmo_fsm_log_addr(false);
+
+ log_init(&log_info, NULL);
+ stderr_target = log_target_create_stderr();
+ log_add_target(stderr_target);
+ log_set_print_filename(stderr_target, 0);
+
+ g_ctx = NULL;
+ osmo_fsm_register(&fsm);
+
+ finst = foo();
+
+ while (1) {
+ osmo_select_main(0);
+ }
+ osmo_fsm_inst_free(finst);
+ osmo_fsm_unregister(&fsm);
+ exit(0);
+}
diff --git a/src/shared/libosmocore/tests/fsm/fsm_test.err b/src/shared/libosmocore/tests/fsm/fsm_test.err
new file mode 100644
index 00000000..c9021bbd
--- /dev/null
+++ b/src/shared/libosmocore/tests/fsm/fsm_test.err
@@ -0,0 +1,11 @@
+Checking FSM allocation
+Test FSM{NULL}: Allocated
+Test FSM{NULL}: Received Event 1
+Test FSM{NULL}: Event 1 not permitted
+Test FSM{NULL}: Received Event 0
+Test FSM{NULL}: state_chg to ONE
+Test FSM{ONE}: Received Event 1
+Test FSM{ONE}: state_chg to TWO
+Test FSM{TWO}: Timeout of T2342
+Timer
+ \ No newline at end of file
diff --git a/src/shared/libosmocore/tests/fsm/fsm_test.ok b/src/shared/libosmocore/tests/fsm/fsm_test.ok
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/shared/libosmocore/tests/fsm/fsm_test.ok
diff --git a/src/shared/libosmocore/tests/gb/bssgp_fc_test.c b/src/shared/libosmocore/tests/gb/bssgp_fc_test.c
index f74be300..d77f141a 100644
--- a/src/shared/libosmocore/tests/gb/bssgp_fc_test.c
+++ b/src/shared/libosmocore/tests/gb/bssgp_fc_test.c
@@ -7,6 +7,7 @@
#include <stdint.h>
#include <string.h>
#include <getopt.h>
+#include <unistd.h>
#include <osmocom/core/application.h>
#include <osmocom/core/utils.h>
@@ -21,7 +22,7 @@ int get_centisec_diff(void)
{
struct timeval tv;
struct timeval now;
- gettimeofday(&now, NULL);
+ osmo_gettimeofday(&now, NULL);
timersub(&now, &tv_start, &tv);
@@ -42,17 +43,23 @@ static int fc_out_cb(struct bssgp_flow_control *fc, struct msgb *msg,
unsigned int csecs = get_centisec_diff();
csecs = round_decisec(csecs);
- printf("%u: FC OUT Nr %lu\n", csecs, (unsigned long) msg);
+ printf("%u: FC OUT Nr %lu\n", csecs, (unsigned long) msg->cb[0]);
+ msgb_free(msg);
+ return 0;
}
static int fc_in(struct bssgp_flow_control *fc, unsigned int pdu_len)
{
+ struct msgb *msg;
unsigned int csecs = get_centisec_diff();
csecs = round_decisec(csecs);
- printf("%u: FC IN Nr %lu\n", csecs, in_ctr);
- bssgp_fc_in(fc, (struct msgb *) in_ctr, pdu_len, NULL);
- in_ctr++;
+ msg = msgb_alloc(1, "fc test");
+ msg->cb[0] = in_ctr++;
+
+ printf("%u: FC IN Nr %lu\n", csecs, msg->cb[0]);
+ bssgp_fc_in(fc, msg, pdu_len, NULL);
+ return 0;
}
@@ -66,7 +73,7 @@ static void test_fc(uint32_t bucket_size_max, uint32_t bucket_leak_rate,
bssgp_fc_init(fc, bucket_size_max, bucket_leak_rate, max_queue_depth,
fc_out_cb);
- gettimeofday(&tv_start, NULL);
+ osmo_gettimeofday(&tv_start, NULL);
for (i = 0; i < pdu_count; i++) {
fc_in(fc, pdu_len);
diff --git a/src/shared/libosmocore/tests/gb/bssgp_fc_tests.err b/src/shared/libosmocore/tests/gb/bssgp_fc_tests.err
index 2f285af8..113a3594 100644
--- a/src/shared/libosmocore/tests/gb/bssgp_fc_tests.err
+++ b/src/shared/libosmocore/tests/gb/bssgp_fc_tests.err
@@ -1,23 +1,3 @@
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
Single PDU (size=1000) is larger than maximum bucket size (100)!
Single PDU (size=1000) is larger than maximum bucket size (100)!
Single PDU (size=1000) is larger than maximum bucket size (100)!
@@ -38,13 +18,3 @@ Single PDU (size=1000) is larger than maximum bucket size (100)!
Single PDU (size=1000) is larger than maximum bucket size (100)!
Single PDU (size=1000) is larger than maximum bucket size (100)!
Single PDU (size=1000) is larger than maximum bucket size (100)!
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
-BSSGP-FC: fc_timer_cb() but still not able to send PDU of 10 bytes
diff --git a/src/shared/libosmocore/tests/gb/gprs_bssgp_test.c b/src/shared/libosmocore/tests/gb/gprs_bssgp_test.c
new file mode 100644
index 00000000..3de05ddb
--- /dev/null
+++ b/src/shared/libosmocore/tests/gb/gprs_bssgp_test.c
@@ -0,0 +1,318 @@
+/* Test routines for the BSSGP implementation in libosmogb
+ *
+ * (C) 2014 by sysmocom s.f.m.c. GmbH
+ * Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
+ *
+ * Skeleton based on bssgp_fc_test.c
+ * (C) 2012 by Harald Welte <laforge@gnumonks.org>
+ */
+
+#undef _GNU_SOURCE
+#define _GNU_SOURCE
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/prim.h>
+#include <osmocom/gprs/gprs_bssgp.h>
+#include <osmocom/gprs/gprs_ns.h>
+#include <osmocom/gprs/gprs_bssgp_bss.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <dlfcn.h>
+
+#define BSS_NSEI 0x0b55
+
+static struct osmo_prim_hdr last_oph = {0};
+
+/* override */
+ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
+ const struct sockaddr *dest_addr, socklen_t addrlen)
+{
+ typedef ssize_t (*sendto_t)(int, const void *, size_t, int,
+ const struct sockaddr *, socklen_t);
+ static sendto_t real_sendto = NULL;
+ uint32_t dest_host = htonl(((struct sockaddr_in *)dest_addr)->sin_addr.s_addr);
+
+ if (!real_sendto)
+ real_sendto = dlsym(RTLD_NEXT, "sendto");
+
+ fprintf(stderr, "MESSAGE to 0x%08x, msg length %zu\n%s\n",
+ dest_host, len, osmo_hexdump(buf, len));
+
+ return len;
+}
+
+/* override */
+int gprs_ns_callback(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
+ struct msgb *msg, uint16_t bvci)
+{
+ fprintf(stderr, "CALLBACK, event %d, msg length %td, bvci 0x%04x\n%s\n\n",
+ event, msgb_bssgp_len(msg), bvci,
+ osmo_hexdump(msgb_bssgph(msg), msgb_bssgp_len(msg)));
+ return 0;
+}
+
+struct msgb *last_ns_tx_msg = NULL;
+
+/* override */
+int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg)
+{
+ msgb_free(last_ns_tx_msg);
+ last_ns_tx_msg = msg;
+
+ return msgb_length(msg);
+}
+
+int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ printf("BSSGP primitive, SAP %d, prim = %d, op = %d, msg = %s\n",
+ oph->sap, oph->primitive, oph->operation, msgb_hexdump(oph->msg));
+
+ last_oph.sap = oph->sap;
+ last_oph.primitive = oph->primitive;
+ last_oph.operation = oph->operation;
+ last_oph.msg = NULL;
+ return -1;
+}
+
+static void msgb_bssgp_send_and_free(struct msgb *msg)
+{
+ msgb_nsei(msg) = BSS_NSEI;
+
+ bssgp_rcvmsg(msg);
+
+ msgb_free(msg);
+}
+
+static void send_bssgp_supend(enum bssgp_pdu_type pdu_type, uint32_t tlli)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ uint32_t tlli_be = htonl(tlli);
+ uint8_t rai[] = {0x0f, 0xf1, 0x80, 0x20, 0x37, 0x00};
+
+ msgb_v_put(msg, pdu_type);
+ msgb_tvlv_put(msg, BSSGP_IE_TLLI, sizeof(tlli_be), (uint8_t *)&tlli_be);
+ msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, sizeof(rai), &rai[0]);
+
+ msgb_bssgp_send_and_free(msg);
+}
+
+static void send_bssgp_resume(enum bssgp_pdu_type pdu_type, uint32_t tlli)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ uint32_t tlli_be = htonl(tlli);
+ uint8_t rai[] = {0x0f, 0xf1, 0x80, 0x20, 0x37, 0x00};
+ uint8_t suspend_ref = 1;
+
+ msgb_v_put(msg, pdu_type);
+ msgb_tvlv_put(msg, BSSGP_IE_TLLI, sizeof(tlli_be), (uint8_t *)&tlli_be);
+ msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, sizeof(rai), &rai[0]);
+ msgb_tvlv_put(msg, BSSGP_IE_SUSPEND_REF_NR, 1, &suspend_ref);
+
+ msgb_bssgp_send_and_free(msg);
+}
+
+static void test_bssgp_suspend_resume(void)
+{
+ const uint32_t tlli = 0xf0123456;
+
+ printf("----- %s START\n", __func__);
+ memset(&last_oph, 0, sizeof(last_oph));
+
+ send_bssgp_supend(BSSGP_PDUT_SUSPEND, tlli);
+ OSMO_ASSERT(last_oph.primitive == PRIM_BSSGP_GMM_SUSPEND);
+
+ send_bssgp_resume(BSSGP_PDUT_RESUME, tlli);
+ OSMO_ASSERT(last_oph.primitive == PRIM_BSSGP_GMM_RESUME);
+
+ printf("----- %s END\n", __func__);
+}
+
+static void send_bssgp_status(enum gprs_bssgp_cause cause, uint16_t *bvci)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ uint8_t cause_ = cause;
+
+ msgb_v_put(msg, BSSGP_PDUT_STATUS);
+ msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause_);
+ if (bvci) {
+ uint16_t bvci_ = htons(*bvci);
+ msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &bvci_);
+ }
+
+ msgb_bssgp_send_and_free(msg);
+}
+
+static void test_bssgp_status(void)
+{
+ uint16_t bvci;
+
+ printf("----- %s START\n", __func__);
+
+ send_bssgp_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL);
+ OSMO_ASSERT(last_oph.primitive == PRIM_NM_STATUS);
+
+ /* Enforce prim != PRIM_NM_STATUS */
+ last_oph.primitive = PRIM_NM_LLC_DISCARDED;
+
+ bvci = 1234;
+ send_bssgp_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci);
+ OSMO_ASSERT(last_oph.primitive == PRIM_NM_STATUS);
+
+ printf("----- %s END\n", __func__);
+}
+
+static void test_bssgp_bad_reset()
+{
+ struct msgb *msg;
+ uint16_t bvci_be = htons(2);
+ uint8_t cause = BSSGP_CAUSE_OML_INTERV;
+
+ printf("----- %s START\n", __func__);
+ msg = bssgp_msgb_alloc();
+
+ msgb_v_put(msg, BSSGP_PDUT_BVC_RESET);
+ msgb_tvlv_put(msg, BSSGP_IE_BVCI, sizeof(bvci_be), (uint8_t *)&bvci_be);
+ msgb_tvlv_put(msg, BSSGP_IE_CAUSE, sizeof(cause), &cause);
+
+ msgb_bvci(msg) = 0xbad;
+
+ msgb_bssgp_send_and_free(msg);
+
+ printf("----- %s END\n", __func__);
+}
+
+static void test_bssgp_flow_control_bvc(void)
+{
+ struct bssgp_bvc_ctx bctx = {
+ .nsei = 0x1234,
+ .bvci = 0x5678,
+ };
+ const uint8_t tag = 42;
+ const uint32_t bmax = 0x1022 * 100;
+ const uint32_t rate = 0xc040 / 8 * 100;
+ const uint32_t bmax_ms = bmax / 2;
+ const uint32_t rate_ms = rate / 2;
+ uint8_t ratio = 0x78;
+ uint32_t qdelay = 0x1144 * 10;
+ int rc;
+
+ static uint8_t expected_simple_msg[] = {
+ 0x26,
+ 0x1e, 0x81, 0x2a, /* tag */
+ 0x05, 0x82, 0x10, 0x22, /* Bmax */
+ 0x03, 0x82, 0xc0, 0x40, /* R */
+ 0x01, 0x82, 0x08, 0x11, /* Bmax_MS */
+ 0x1c, 0x82, 0x60, 0x20, /* R_MS */
+ };
+
+ static uint8_t expected_ext_msg[] = {
+ 0x26,
+ 0x1e, 0x81, 0x2a, /* tag */
+ 0x05, 0x82, 0x10, 0x22, /* Bmax */
+ 0x03, 0x82, 0xc0, 0x40, /* R */
+ 0x01, 0x82, 0x08, 0x11, /* Bmax_MS */
+ 0x1c, 0x82, 0x60, 0x20, /* R_MS */
+ 0x3c, 0x81, 0x78, /* ratio */
+ 0x06, 0x82, 0x11, 0x44, /* Qdelay */
+ };
+
+ printf("----- %s START\n", __func__);
+
+ rc = bssgp_tx_fc_bvc(&bctx, tag, bmax, rate, bmax_ms, rate_ms,
+ NULL, NULL);
+
+ OSMO_ASSERT(rc >= 0);
+ OSMO_ASSERT(last_ns_tx_msg != NULL);
+ printf("Got message: %s\n", msgb_hexdump(last_ns_tx_msg));
+ OSMO_ASSERT(msgb_length(last_ns_tx_msg) == sizeof(expected_simple_msg));
+ OSMO_ASSERT(0 == memcmp(msgb_data(last_ns_tx_msg),
+ expected_simple_msg, sizeof(expected_simple_msg)));
+
+ rc = bssgp_tx_fc_bvc(&bctx, tag, bmax, rate, bmax_ms, rate_ms,
+ &ratio, &qdelay);
+
+ OSMO_ASSERT(rc >= 0);
+ OSMO_ASSERT(last_ns_tx_msg != NULL);
+ printf("Got message: %s\n", msgb_hexdump(last_ns_tx_msg));
+ OSMO_ASSERT(msgb_length(last_ns_tx_msg) == sizeof(expected_ext_msg));
+ OSMO_ASSERT(0 == memcmp(msgb_data(last_ns_tx_msg),
+ expected_ext_msg, sizeof(expected_ext_msg)));
+
+ msgb_free(last_ns_tx_msg);
+ last_ns_tx_msg = NULL;
+
+ printf("----- %s END\n", __func__);
+}
+
+static void test_bssgp_msgb_copy()
+{
+ struct msgb *msg, *msg2;
+ uint16_t bvci_be = htons(2);
+ uint8_t cause = BSSGP_CAUSE_OML_INTERV;
+
+ printf("----- %s START\n", __func__);
+ msg = bssgp_msgb_alloc();
+
+ msg->l3h = msgb_data(msg);
+ msgb_v_put(msg, BSSGP_PDUT_BVC_RESET);
+ msgb_tvlv_put(msg, BSSGP_IE_BVCI, sizeof(bvci_be), (uint8_t *)&bvci_be);
+ msgb_tvlv_put(msg, BSSGP_IE_CAUSE, sizeof(cause), &cause);
+
+ msgb_bvci(msg) = 0xbad;
+ msgb_nsei(msg) = 0xbee;
+
+ printf("Old msgb: %s\n", msgb_hexdump(msg));
+ msg2 = bssgp_msgb_copy(msg, "test");
+ printf("New msgb: %s\n", msgb_hexdump(msg2));
+
+ OSMO_ASSERT(msgb_bvci(msg2) == 0xbad);
+ OSMO_ASSERT(msgb_nsei(msg2) == 0xbee);
+ OSMO_ASSERT(msgb_l3(msg2) == msgb_data(msg2));
+ OSMO_ASSERT(msgb_bssgph(msg2) == msgb_data(msg2));
+ OSMO_ASSERT(msgb_bssgp_len(msg2) == msgb_length(msg2));
+
+ msgb_free(msg);
+ msgb_free(msg2);
+
+ printf("----- %s END\n", __func__);
+}
+
+static struct log_info info = {};
+
+int main(int argc, char **argv)
+{
+ struct sockaddr_in bss_peer= {0};
+
+ osmo_init_logging(&info);
+ log_set_use_color(osmo_stderr_target, 0);
+ log_set_print_filename(osmo_stderr_target, 0);
+
+ bssgp_nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
+
+ bss_peer.sin_family = AF_INET;
+ bss_peer.sin_port = htons(32000);
+ bss_peer.sin_addr.s_addr = htonl(0x7f0000ff);
+
+ gprs_ns_nsip_connect(bssgp_nsi, &bss_peer, BSS_NSEI, BSS_NSEI+1);
+
+
+ printf("===== BSSGP test START\n");
+ test_bssgp_suspend_resume();
+ test_bssgp_status();
+ test_bssgp_bad_reset();
+ test_bssgp_flow_control_bvc();
+ test_bssgp_msgb_copy();
+ printf("===== BSSGP test END\n\n");
+
+ exit(EXIT_SUCCESS);
+}
diff --git a/src/shared/libosmocore/tests/gb/gprs_bssgp_test.ok b/src/shared/libosmocore/tests/gb/gprs_bssgp_test.ok
new file mode 100644
index 00000000..c5b3e7d1
--- /dev/null
+++ b/src/shared/libosmocore/tests/gb/gprs_bssgp_test.ok
@@ -0,0 +1,21 @@
+===== BSSGP test START
+----- test_bssgp_suspend_resume START
+BSSGP primitive, SAP 16777219, prim = 3, op = 0, msg = 0b 1f 84 f0 12 34 56 1b 86 0f f1 80 20 37 00
+BSSGP primitive, SAP 16777219, prim = 4, op = 0, msg = 0e 1f 84 f0 12 34 56 1b 86 0f f1 80 20 37 00 1d 81 01
+----- test_bssgp_suspend_resume END
+----- test_bssgp_status START
+BSSGP primitive, SAP 16777221, prim = 11, op = 2, msg = 41 07 81 27
+BSSGP primitive, SAP 16777221, prim = 11, op = 2, msg = 41 07 81 05 04 82 04 d2
+----- test_bssgp_status END
+----- test_bssgp_bad_reset START
+----- test_bssgp_bad_reset END
+----- test_bssgp_flow_control_bvc START
+Got message: 26 1e 81 2a 05 82 10 22 03 82 c0 40 01 82 08 11 1c 82 60 20
+Got message: 26 1e 81 2a 05 82 10 22 03 82 c0 40 01 82 08 11 1c 82 60 20 3c 81 78 06 82 11 44
+----- test_bssgp_flow_control_bvc END
+----- test_bssgp_msgb_copy START
+Old msgb: [L3]> 22 04 82 00 02 07 81 08
+New msgb: [L3]> 22 04 82 00 02 07 81 08
+----- test_bssgp_msgb_copy END
+===== BSSGP test END
+
diff --git a/src/shared/libosmocore/tests/gb/gprs_ns_test.c b/src/shared/libosmocore/tests/gb/gprs_ns_test.c
new file mode 100644
index 00000000..4c6fd391
--- /dev/null
+++ b/src/shared/libosmocore/tests/gb/gprs_ns_test.c
@@ -0,0 +1,971 @@
+/* test routines for NS connection handling
+ * (C) 2013 by sysmocom s.f.m.c. GmbH
+ * Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
+ */
+
+#undef _GNU_SOURCE
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <dlfcn.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/signal.h>
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/gprs/gprs_msgb.h>
+#include <osmocom/gprs/gprs_ns.h>
+#include <osmocom/gprs/gprs_bssgp.h>
+
+#define REMOTE_BSS_ADDR 0x01020304
+#define REMOTE_SGSN_ADDR 0x05060708
+
+#define SGSN_NSEI 0x0100
+
+static int sent_pdu_type = 0;
+
+static int gprs_process_message(struct gprs_ns_inst *nsi, const char *text,
+ struct sockaddr_in *peer, const unsigned char* data,
+ size_t data_len);
+
+static void send_ns_reset(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr,
+ enum ns_cause cause, uint16_t nsvci, uint16_t nsei)
+{
+ /* GPRS Network Service, PDU type: NS_RESET,
+ */
+ unsigned char msg[12] = {
+ 0x02, 0x00, 0x81, 0x01, 0x01, 0x82, 0x11, 0x22,
+ 0x04, 0x82, 0x11, 0x22
+ };
+
+ msg[3] = cause;
+ msg[6] = nsvci / 256;
+ msg[7] = nsvci % 256;
+ msg[10] = nsei / 256;
+ msg[11] = nsei % 256;
+
+ gprs_process_message(nsi, "RESET", src_addr, msg, sizeof(msg));
+}
+
+static void send_ns_reset_ack(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr,
+ uint16_t nsvci, uint16_t nsei)
+{
+ /* GPRS Network Service, PDU type: NS_RESET_ACK,
+ */
+ unsigned char msg[9] = {
+ 0x03, 0x01, 0x82, 0x11, 0x22,
+ 0x04, 0x82, 0x11, 0x22
+ };
+
+ msg[3] = nsvci / 256;
+ msg[4] = nsvci % 256;
+ msg[7] = nsei / 256;
+ msg[8] = nsei % 256;
+
+ gprs_process_message(nsi, "RESET_ACK", src_addr, msg, sizeof(msg));
+}
+
+static void send_ns_alive(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr)
+{
+ /* GPRS Network Service, PDU type: NS_ALIVE */
+ unsigned char msg[1] = {
+ 0x0a
+ };
+
+ gprs_process_message(nsi, "ALIVE", src_addr, msg, sizeof(msg));
+}
+
+static void send_ns_alive_ack(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr)
+{
+ /* GPRS Network Service, PDU type: NS_ALIVE_ACK */
+ unsigned char msg[1] = {
+ 0x0b
+ };
+
+ gprs_process_message(nsi, "ALIVE_ACK", src_addr, msg, sizeof(msg));
+}
+
+static void send_ns_unblock(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr)
+{
+ /* GPRS Network Service, PDU type: NS_UNBLOCK */
+ unsigned char msg[1] = {
+ 0x06
+ };
+
+ gprs_process_message(nsi, "UNBLOCK", src_addr, msg, sizeof(msg));
+}
+
+static void send_ns_unblock_ack(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr)
+{
+ /* GPRS Network Service, PDU type: NS_UNBLOCK_ACK */
+ unsigned char msg[1] = {
+ 0x07
+ };
+
+ gprs_process_message(nsi, "UNBLOCK_ACK", src_addr, msg, sizeof(msg));
+}
+
+static void send_ns_unitdata(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr,
+ uint16_t nsbvci,
+ const unsigned char *bssgp_msg, size_t bssgp_msg_size)
+{
+ /* GPRS Network Service, PDU type: NS_UNITDATA */
+ unsigned char msg[4096] = {
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ OSMO_ASSERT(bssgp_msg_size <= sizeof(msg) - 4);
+
+ msg[2] = nsbvci / 256;
+ msg[3] = nsbvci % 256;
+ memcpy(msg + 4, bssgp_msg, bssgp_msg_size);
+
+ gprs_process_message(nsi, "UNITDATA", src_addr, msg, bssgp_msg_size + 4);
+}
+
+static void send_bssgp_reset(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr,
+ uint16_t bvci)
+{
+ /* GPRS Network Service, PDU type: NS_UNITDATA, BVCI 0
+ * BSSGP RESET */
+ unsigned char msg[22] = {
+ 0x22, 0x04, 0x82, 0x4a,
+ 0x2e, 0x07, 0x81, 0x08, 0x08, 0x88, 0x10, 0x20,
+ 0x30, 0x40, 0x50, 0x60, 0x10, 0x00
+ };
+
+ msg[3] = bvci / 256;
+ msg[4] = bvci % 256;
+
+ send_ns_unitdata(nsi, src_addr, 0, msg, sizeof(msg));
+}
+
+static void setup_ns(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr,
+ uint16_t nsvci, uint16_t nsei)
+{
+ printf("Setup NS-VC: remote 0x%08x:%d, "
+ "NSVCI 0x%04x(%d), NSEI 0x%04x(%d)\n\n",
+ ntohl(src_addr->sin_addr.s_addr), ntohs(src_addr->sin_port),
+ nsvci, nsvci, nsei, nsei);
+
+ send_ns_reset(nsi, src_addr, NS_CAUSE_OM_INTERVENTION, nsvci, nsei);
+ send_ns_alive(nsi, src_addr);
+ send_ns_unblock(nsi, src_addr);
+ send_ns_alive_ack(nsi, src_addr);
+}
+
+static void setup_bssgp(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr,
+ uint16_t bvci) __attribute__((__unused__));
+
+static void setup_bssgp(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr,
+ uint16_t bvci)
+{
+ printf("Setup BSSGP: remote 0x%08x:%d, "
+ "BVCI 0x%04x(%d)\n\n",
+ ntohl(src_addr->sin_addr.s_addr), ntohs(src_addr->sin_port),
+ bvci, bvci);
+
+ send_bssgp_reset(nsi, src_addr, bvci);
+}
+
+/* GPRS Network Service, PDU type: NS_RESET,
+ * Cause: O&M intervention, NS VCI: 0x1122, NSEI 0x1122
+ */
+static const unsigned char gprs_ns_reset[12] = {
+ 0x02, 0x00, 0x81, 0x01, 0x01, 0x82, 0x11, 0x22,
+ 0x04, 0x82, 0x11, 0x22
+};
+
+/* GPRS Network Service, PDU type: NS_RESET,
+ * Cause: O&M intervention, NS VCI: 0x3344, NSEI 0x1122
+ */
+static const unsigned char gprs_ns_reset_vci2[12] = {
+ 0x02, 0x00, 0x81, 0x01, 0x01, 0x82, 0x33, 0x44,
+ 0x04, 0x82, 0x11, 0x22
+};
+
+/* GPRS Network Service, PDU type: NS_RESET,
+ * Cause: O&M intervention, NS VCI: 0x1122, NSEI 0x3344
+ */
+static const unsigned char gprs_ns_reset_nsei2[12] = {
+ 0x02, 0x00, 0x81, 0x01, 0x01, 0x82, 0x11, 0x22,
+ 0x04, 0x82, 0x33, 0x44
+};
+
+/* GPRS Network Service, PDU type: NS_ALIVE */
+static const unsigned char gprs_ns_alive[1] = {
+ 0x0a
+};
+
+/* GPRS Network Service, PDU type: NS_STATUS,
+ * Cause: PDU not compatible with the protocol state
+ * PDU: NS_ALIVE
+ */
+static const unsigned char gprs_ns_status_invalid_alive[7] = {
+ 0x08, 0x00, 0x81, 0x0a, 0x02, 0x81, 0x0a
+};
+
+/* GPRS Network Service, PDU type: NS_ALIVE_ACK */
+static const unsigned char gprs_ns_alive_ack[1] = {
+ 0x0b
+};
+
+/* GPRS Network Service, PDU type: NS_UNBLOCK */
+static const unsigned char gprs_ns_unblock[1] = {
+ 0x06
+};
+
+
+/* GPRS Network Service, PDU type: NS_STATUS,
+ * Cause: PDU not compatible with the protocol state
+ * PDU: NS_RESET_ACK, NS VCI: 0x1122, NSEI 0x1122
+ */
+static const unsigned char gprs_ns_status_invalid_reset_ack[15] = {
+ 0x08, 0x00, 0x81, 0x0a, 0x02, 0x89, 0x03, 0x01,
+ 0x82, 0x11, 0x22, 0x04, 0x82, 0x11, 0x22
+};
+
+/* GPRS Network Service, PDU type: NS_UNITDATA, BVCI 0 */
+static const unsigned char gprs_bssgp_reset[22] = {
+ 0x00, 0x00, 0x00, 0x00, 0x22, 0x04, 0x82, 0x4a,
+ 0x2e, 0x07, 0x81, 0x08, 0x08, 0x88, 0x10, 0x20,
+ 0x30, 0x40, 0x50, 0x60, 0x10, 0x00
+};
+
+int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg,
+ struct sockaddr_in *saddr, enum gprs_ns_ll ll);
+
+/* override */
+int gprs_ns_callback(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
+ struct msgb *msg, uint16_t bvci)
+{
+ printf("CALLBACK, event %d, msg length %td, bvci 0x%04x\n%s\n\n",
+ event, msgb_bssgp_len(msg), bvci,
+ osmo_hexdump(msgb_bssgph(msg), msgb_bssgp_len(msg)));
+ return 0;
+}
+
+/* override */
+ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
+ const struct sockaddr *dest_addr, socklen_t addrlen)
+{
+ typedef ssize_t (*sendto_t)(int, const void *, size_t, int,
+ const struct sockaddr *, socklen_t);
+ static sendto_t real_sendto = NULL;
+ uint32_t dest_host = htonl(((struct sockaddr_in *)dest_addr)->sin_addr.s_addr);
+
+ if (!real_sendto)
+ real_sendto = dlsym(RTLD_NEXT, "sendto");
+
+ sent_pdu_type = len > 0 ? ((uint8_t *)buf)[0] : -1;
+
+ if (dest_host == REMOTE_BSS_ADDR)
+ printf("MESSAGE to BSS, msg length %zu\n%s\n\n", len, osmo_hexdump(buf, len));
+ else if (dest_host == REMOTE_SGSN_ADDR)
+ printf("MESSAGE to SGSN, msg length %zu\n%s\n\n", len, osmo_hexdump(buf, len));
+ else
+ return real_sendto(sockfd, buf, len, flags, dest_addr, addrlen);
+
+ return len;
+}
+
+/* override */
+int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg)
+{
+ typedef int (*gprs_ns_sendmsg_t)(struct gprs_ns_inst *nsi, struct msgb *msg);
+ static gprs_ns_sendmsg_t real_gprs_ns_sendmsg = NULL;
+ uint16_t bvci = msgb_bvci(msg);
+ uint16_t nsei = msgb_nsei(msg);
+
+ unsigned char *buf = msg->data;
+ size_t len = msg->len;
+
+ if (!real_gprs_ns_sendmsg)
+ real_gprs_ns_sendmsg = dlsym(RTLD_NEXT, "gprs_ns_sendmsg");
+
+ if (nsei == SGSN_NSEI)
+ printf("NS UNITDATA MESSAGE to SGSN, BVCI 0x%04x, msg length %zu\n%s\n\n",
+ bvci, len, osmo_hexdump(buf, len));
+ else
+ printf("NS UNITDATA MESSAGE to BSS, BVCI 0x%04x, msg length %zu\n%s\n\n",
+ bvci, len, osmo_hexdump(buf, len));
+
+ return real_gprs_ns_sendmsg(nsi, msg);
+}
+
+static void dump_rate_ctr_group(FILE *stream, const char *prefix,
+ struct rate_ctr_group *ctrg)
+{
+ unsigned int i;
+
+ for (i = 0; i < ctrg->desc->num_ctr; i++) {
+ struct rate_ctr *ctr = &ctrg->ctr[i];
+ if (ctr->current && !strchr(ctrg->desc->ctr_desc[i].name, '.'))
+ fprintf(stream, " %s%s: %llu%s",
+ prefix, ctrg->desc->ctr_desc[i].description,
+ (long long)ctr->current,
+ "\n");
+ };
+}
+
+/* Signal handler for signals from NS layer */
+static int test_signal(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct ns_signal_data *nssd = signal_data;
+
+ if (subsys != SS_L_NS)
+ return 0;
+
+ switch (signal) {
+ case S_NS_RESET:
+ printf("==> got signal NS_RESET, NS-VC 0x%04x/%s\n",
+ nssd->nsvc->nsvci,
+ gprs_ns_ll_str(nssd->nsvc));
+ break;
+
+ case S_NS_ALIVE_EXP:
+ printf("==> got signal NS_ALIVE_EXP, NS-VC 0x%04x/%s\n",
+ nssd->nsvc->nsvci,
+ gprs_ns_ll_str(nssd->nsvc));
+ break;
+
+ case S_NS_BLOCK:
+ printf("==> got signal NS_BLOCK, NS-VC 0x%04x/%s\n",
+ nssd->nsvc->nsvci,
+ gprs_ns_ll_str(nssd->nsvc));
+ break;
+
+ case S_NS_UNBLOCK:
+ printf("==> got signal NS_UNBLOCK, NS-VC 0x%04x/%s\n",
+ nssd->nsvc->nsvci,
+ gprs_ns_ll_str(nssd->nsvc));
+ break;
+
+ case S_NS_REPLACED:
+ printf("==> got signal NS_REPLACED: 0x%04x/%s",
+ nssd->nsvc->nsvci,
+ gprs_ns_ll_str(nssd->nsvc));
+ printf(" -> 0x%04x/%s\n",
+ nssd->old_nsvc->nsvci,
+ gprs_ns_ll_str(nssd->old_nsvc));
+ break;
+
+ case S_NS_MISMATCH:
+ printf("==> got signal NS_MISMATCH: 0x%04x/%s pdu=%d, ie=%d\n",
+ nssd->nsvc->nsvci, gprs_ns_ll_str(nssd->nsvc),
+ nssd->pdu_type, nssd->ie_type);
+ break;
+
+ default:
+ printf("==> got signal %d, NS-VC 0x%04x/%s\n", signal,
+ nssd->nsvc->nsvci,
+ gprs_ns_ll_str(nssd->nsvc));
+ break;
+ }
+
+ return 0;
+}
+
+static int gprs_process_message(struct gprs_ns_inst *nsi, const char *text, struct sockaddr_in *peer, const unsigned char* data, size_t data_len)
+{
+ struct msgb *msg;
+ int ret;
+ if (data_len > NS_ALLOC_SIZE - NS_ALLOC_HEADROOM) {
+ fprintf(stderr, "message too long: %zu\n", data_len);
+ return -1;
+ }
+
+ msg = gprs_ns_msgb_alloc();
+ memmove(msg->data, data, data_len);
+ msg->l2h = msg->data;
+ msgb_put(msg, data_len);
+
+ printf("PROCESSING %s from 0x%08x:%d\n%s\n\n",
+ text, ntohl(peer->sin_addr.s_addr), ntohs(peer->sin_port),
+ osmo_hexdump(data, data_len));
+
+ ret = gprs_ns_rcvmsg(nsi, msg, peer, GPRS_NS_LL_UDP);
+
+ printf("result (%s) = %d\n\n", text, ret);
+
+ msgb_free(msg);
+
+ return ret;
+}
+
+static int gprs_send_message(struct gprs_ns_inst *nsi, const char *text,
+ uint16_t nsei, uint16_t bvci,
+ const unsigned char* data, size_t data_len)
+{
+ struct msgb *msg;
+ int ret;
+ if (data_len > NS_ALLOC_SIZE - NS_ALLOC_HEADROOM) {
+ fprintf(stderr, "message too long: %zu\n", data_len);
+ return -1;
+ }
+
+ msg = gprs_ns_msgb_alloc();
+ memmove(msg->data, data, data_len);
+ msg->l2h = msg->data;
+ msgb_put(msg, data_len);
+
+ msgb_nsei(msg) = nsei;
+ msgb_bvci(msg) = bvci;
+
+ printf("SENDING %s to NSEI 0x%04x, BVCI 0x%04x\n", text, nsei, bvci);
+
+ ret = gprs_ns_sendmsg(nsi, msg);
+
+ printf("result (%s) = %d\n\n", text, ret);
+
+ return ret;
+}
+
+static void gprs_dump_nsi(struct gprs_ns_inst *nsi)
+{
+ struct gprs_nsvc *nsvc;
+
+ printf("Current NS-VCIs:\n");
+ llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
+ struct sockaddr_in *peer = &(nsvc->ip.bts_addr);
+ printf(" VCI 0x%04x, NSEI 0x%04x, peer 0x%08x:%d%s%s%s\n",
+ nsvc->nsvci, nsvc->nsei,
+ ntohl(peer->sin_addr.s_addr), ntohs(peer->sin_port),
+ nsvc->state & NSE_S_BLOCKED ? ", blocked" : "",
+ nsvc->state & NSE_S_ALIVE ? "" : ", dead",
+ nsvc->nsvci_is_valid ? "" : ", invalid VCI"
+ );
+ dump_rate_ctr_group(stdout, " ", nsvc->ctrg);
+ }
+ printf("\n");
+}
+
+static int expire_nsvc_timer(struct gprs_nsvc *nsvc)
+{
+ int rc;
+
+ if (!osmo_timer_pending(&nsvc->timer))
+ return -1;
+
+ rc = nsvc->timer_mode;
+ osmo_timer_del(&nsvc->timer);
+
+ nsvc->timer.cb(nsvc->timer.data);
+
+ return rc;
+}
+
+static void test_nsvc()
+{
+ struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
+ struct sockaddr_in peer[1] = {{0},};
+ struct gprs_nsvc *nsvc;
+ int i;
+
+ peer[0].sin_family = AF_INET;
+ peer[0].sin_port = htons(1111);
+ peer[0].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR);
+
+ for (i=0; i<4; ++i) {
+ printf("--- Create via RESET (round %d) ---\n\n", i);
+
+ send_ns_reset(nsi, &peer[0], NS_CAUSE_OM_INTERVENTION,
+ 0x1001, 0x1000);
+ gprs_dump_nsi(nsi);
+
+ printf("--- Delete nsvc object (round %d)---\n\n", i);
+
+ nsvc = gprs_nsvc_by_nsvci(nsi, 0x1001);
+ OSMO_ASSERT(nsvc != NULL);
+ gprs_nsvc_delete(nsvc);
+
+ gprs_dump_nsi(nsi);
+ }
+
+ gprs_ns_destroy(nsi);
+ nsi = NULL;
+
+ printf("--- Process timers ---\n\n");
+ /* wait for rate_ctr_timer expiry */
+ usleep(1100000);
+ /* ensure termination */
+ alarm(2);
+ osmo_timers_update();
+ alarm(0);
+}
+
+static void test_ignored_messages()
+{
+ struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
+ struct sockaddr_in peer[1] = {{0},};
+
+ peer[0].sin_family = AF_INET;
+ peer[0].sin_port = htons(1111);
+ peer[0].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR);
+
+ printf("--- Send unexpected NS STATUS (should not be answered)---\n\n");
+ /* Do not respond, see 3GPP TS 08.16, 7.5.1 */
+ gprs_process_message(nsi, "STATUS", &peer[0],
+ gprs_ns_status_invalid_alive,
+ sizeof(gprs_ns_status_invalid_alive));
+
+ printf("--- Send unexpected NS ALIVE ACK (should not be answered)---\n\n");
+ /* Ignore this, see 3GPP TS 08.16, 7.4.1 */
+ send_ns_alive_ack(nsi, &peer[0]);
+
+ printf("--- Send unexpected NS RESET ACK (should not be answered)---\n\n");
+ /* Ignore this, see 3GPP TS 08.16, 7.3.1 */
+ send_ns_reset_ack(nsi, &peer[0], 0xe001, 0xe000);
+
+ gprs_ns_destroy(nsi);
+ nsi = NULL;
+}
+
+static void test_bss_port_changes()
+{
+ struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
+ struct sockaddr_in peer[4] = {{0},};
+
+ peer[0].sin_family = AF_INET;
+ peer[0].sin_port = htons(1111);
+ peer[0].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR);
+ peer[1].sin_family = AF_INET;
+ peer[1].sin_port = htons(2222);
+ peer[1].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR);
+ peer[2].sin_family = AF_INET;
+ peer[2].sin_port = htons(3333);
+ peer[2].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR);
+ peer[3].sin_family = AF_INET;
+ peer[3].sin_port = htons(4444);
+ peer[3].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR);
+
+ printf("--- Setup, send BSSGP RESET ---\n\n");
+
+ setup_ns(nsi, &peer[0], 0x1122, 0x1122);
+ gprs_dump_nsi(nsi);
+ gprs_process_message(nsi, "BSSGP RESET", &peer[0],
+ gprs_bssgp_reset, sizeof(gprs_bssgp_reset));
+
+ printf("--- Peer port changes, RESET, message remains unchanged ---\n\n");
+
+ send_ns_reset(nsi, &peer[1], NS_CAUSE_OM_INTERVENTION, 0x1122, 0x1122);
+ gprs_dump_nsi(nsi);
+
+ printf("--- Peer port changes, RESET, VCI changes ---\n\n");
+
+ send_ns_reset(nsi, &peer[2], NS_CAUSE_OM_INTERVENTION, 0x3344, 0x1122);
+ gprs_dump_nsi(nsi);
+
+ printf("--- Peer port changes, RESET, NSEI changes ---\n\n");
+
+ send_ns_reset(nsi, &peer[3], NS_CAUSE_OM_INTERVENTION, 0x1122, 0x3344);
+ gprs_dump_nsi(nsi);
+
+ printf("--- Peer port 3333, RESET, VCI is changed back ---\n\n");
+
+ send_ns_reset(nsi, &peer[2], NS_CAUSE_OM_INTERVENTION, 0x1122, 0x1122);
+ gprs_dump_nsi(nsi);
+
+ printf("--- Peer port 4444, RESET, NSEI is changed back ---\n\n");
+
+ send_ns_reset(nsi, &peer[3], NS_CAUSE_OM_INTERVENTION, 0x1122, 0x1122);
+ gprs_dump_nsi(nsi);
+
+ gprs_ns_destroy(nsi);
+ nsi = NULL;
+}
+
+static void test_bss_reset_ack()
+{
+ struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
+ struct sockaddr_in peer[4] = {{0},};
+ struct gprs_nsvc *nsvc;
+ struct sockaddr_in *nse[4];
+ int rc;
+
+ peer[0].sin_family = AF_INET;
+ peer[0].sin_port = htons(1111);
+ peer[0].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR);
+ peer[1].sin_family = AF_INET;
+ peer[1].sin_port = htons(2222);
+ peer[1].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR);
+ peer[2].sin_family = AF_INET;
+ peer[2].sin_port = htons(3333);
+ peer[2].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR);
+ peer[3].sin_family = AF_INET;
+ peer[3].sin_port = htons(4444);
+ peer[3].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR);
+
+ nse[0] = &peer[0];
+ nse[1] = &peer[1];
+
+ printf("--- Setup VC 1 BSS -> SGSN ---\n\n");
+
+ setup_ns(nsi, nse[0], 0x1001, 0x1000);
+ gprs_dump_nsi(nsi);
+
+ printf("--- Setup VC 2 BSS -> SGSN ---\n\n");
+
+ setup_ns(nsi, nse[1], 0x2001, 0x2000);
+ gprs_dump_nsi(nsi);
+
+ printf("--- Setup VC 1 SGSN -> BSS ---\n\n");
+
+ nsvc = gprs_nsvc_by_nsvci(nsi, 0x1001);
+ gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION);
+ send_ns_reset_ack(nsi, nse[0], 0x1001, 0x1000);
+ gprs_dump_nsi(nsi);
+
+ printf("--- Exchange NSEI 1 + 2 links ---\n\n");
+
+ nse[1] = &peer[0];
+ nse[0] = &peer[1];
+
+ printf("--- Setup VC 2 SGSN -> BSS (hits NSEI 1) ---\n\n");
+
+ nsvc = gprs_nsvc_by_nsvci(nsi, 0x2001);
+ gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION);
+ send_ns_reset_ack(nsi, nse[0], 0x1001, 0x1000);
+ gprs_dump_nsi(nsi);
+
+ printf("--- Setup VC 2 SGSN -> BSS (hits NSEI 2) ---\n\n");
+
+ nsvc = gprs_nsvc_by_nsvci(nsi, 0x2001);
+ rc = gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION);
+ OSMO_ASSERT(rc < 0);
+
+ printf("--- Setup VC 1 SGSN -> BSS (hits NSEI 1) ---\n\n");
+
+ nsvc = gprs_nsvc_by_nsvci(nsi, 0x1001);
+ gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION);
+ send_ns_reset_ack(nsi, nse[0], 0x1001, 0x1000);
+ gprs_dump_nsi(nsi);
+
+ printf("--- Setup VC 2 BSS -> SGSN ---\n\n");
+
+ setup_ns(nsi, nse[1], 0x2001, 0x2000);
+ gprs_dump_nsi(nsi);
+
+ /* Test 3GPP TS 08.16, 7.3.1, 3rd paragraph. */
+ /* This is not rejected because the NSEI has been
+ * assigned dynamically and not by configuration.
+ * This is not strictly spec conformant. */
+
+ printf("--- RESET with invalid NSEI, BSS -> SGSN ---\n\n");
+
+ send_ns_reset(nsi, nse[0], NS_CAUSE_OM_INTERVENTION,
+ 0x1001, 0xf000);
+ gprs_dump_nsi(nsi);
+
+ /* Test 3GPP TS 08.16, 7.3.1, 2nd paragraph. */
+ /* This is not rejected because the NSEI has been
+ * assigned dynamically and not by configuration.
+ * This is not strictly spec conformant. */
+
+ printf("--- RESET with invalid NSVCI, BSS -> SGSN ---\n\n");
+
+ send_ns_reset(nsi, nse[0], NS_CAUSE_OM_INTERVENTION,
+ 0xf001, 0x1000);
+ gprs_dump_nsi(nsi);
+
+ printf("--- RESET with old NSEI, NSVCI, BSS -> SGSN ---\n\n");
+
+ send_ns_reset(nsi, nse[0], NS_CAUSE_OM_INTERVENTION,
+ 0x1001, 0x1000);
+ gprs_dump_nsi(nsi);
+
+ /* Test 3GPP TS 08.16, 7.3.1, 5th paragraph. */
+
+ printf("--- Unexpected RESET_ACK VC 1, BSS -> SGSN ---\n\n");
+
+ send_ns_reset_ack(nsi, nse[0], 0x1001, 0x1000);
+ gprs_dump_nsi(nsi);
+
+ /* Test 3GPP TS 08.16, 7.3.1, 4th paragraph. */
+
+ printf("--- RESET_ACK with invalid NSEI, BSS -> SGSN ---\n\n");
+
+ nsvc = gprs_nsvc_by_nsvci(nsi, 0x1001);
+ gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION);
+ send_ns_reset_ack(nsi, nse[0], 0x1001, 0xf000);
+ gprs_dump_nsi(nsi);
+
+ /* Test 3GPP TS 08.16, 7.3.1, 4th paragraph. */
+
+ printf("--- RESET_ACK with invalid NSVCI, BSS -> SGSN ---\n\n");
+
+ nsvc = gprs_nsvc_by_nsvci(nsi, 0x1001);
+ gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION);
+ send_ns_reset_ack(nsi, nse[0], 0xf001, 0x1000);
+ gprs_dump_nsi(nsi);
+
+ /* Test crossing RESET and UNBLOCK_ACK */
+
+ printf("--- RESET (BSS -> SGSN) crossing an UNBLOCK_ACK (SGSN -> BSS) ---\n\n");
+
+ setup_ns(nsi, nse[0], 0x1001, 0x1000);
+ nsvc = gprs_nsvc_by_nsvci(nsi, 0x1001);
+ gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION);
+ OSMO_ASSERT(nsvc->state & NSE_S_BLOCKED);
+ OSMO_ASSERT(nsvc->state & NSE_S_RESET);
+ send_ns_unblock_ack(nsi, nse[0]);
+ gprs_dump_nsi(nsi);
+ send_ns_reset_ack(nsi, nse[0], 0x1001, 0x1000);
+ expire_nsvc_timer(nsvc);
+ OSMO_ASSERT(nsvc->state & NSE_S_BLOCKED);
+ OSMO_ASSERT(nsvc->state & NSE_S_RESET);
+ send_ns_reset_ack(nsi, nse[0], 0x1001, 0x1000);
+ gprs_dump_nsi(nsi);
+
+ gprs_ns_destroy(nsi);
+ nsi = NULL;
+}
+
+
+static void test_sgsn_reset()
+{
+ struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
+ struct sockaddr_in sgsn_peer= {0};
+ struct gprs_nsvc *nsvc;
+
+ sgsn_peer.sin_family = AF_INET;
+ sgsn_peer.sin_port = htons(32000);
+ sgsn_peer.sin_addr.s_addr = htonl(REMOTE_SGSN_ADDR);
+
+ gprs_dump_nsi(nsi);
+
+ printf("--- Setup SGSN connection, BSS -> SGSN ---\n\n");
+
+ gprs_ns_nsip_connect(nsi, &sgsn_peer, SGSN_NSEI, SGSN_NSEI+1);
+ send_ns_reset_ack(nsi, &sgsn_peer, SGSN_NSEI+1, SGSN_NSEI);
+ send_ns_alive_ack(nsi, &sgsn_peer);
+ send_ns_unblock_ack(nsi, &sgsn_peer);
+ gprs_dump_nsi(nsi);
+
+ printf("--- RESET, SGSN -> BSS ---\n\n");
+
+ send_ns_reset(nsi, &sgsn_peer, NS_CAUSE_OM_INTERVENTION,
+ SGSN_NSEI+1, SGSN_NSEI);
+ gprs_dump_nsi(nsi);
+
+ /* Test 3GPP TS 08.16, 7.3.1, 3rd paragraph. */
+
+ printf("--- RESET with invalid NSEI, SGSN -> BSS ---\n\n");
+
+ send_ns_reset(nsi, &sgsn_peer, NS_CAUSE_OM_INTERVENTION,
+ SGSN_NSEI+1, 0xf000);
+ gprs_dump_nsi(nsi);
+
+ /* Test 3GPP TS 08.16, 7.3.1, 2nd paragraph. */
+
+ printf("--- RESET with invalid NSVCI, SGSN -> BSS ---\n\n");
+
+ send_ns_reset(nsi, &sgsn_peer, NS_CAUSE_OM_INTERVENTION,
+ 0xf001, SGSN_NSEI);
+ gprs_dump_nsi(nsi);
+
+ printf("--- RESET, SGSN -> BSS ---\n\n");
+
+ send_ns_reset(nsi, &sgsn_peer, NS_CAUSE_OM_INTERVENTION,
+ SGSN_NSEI+1, SGSN_NSEI);
+ gprs_dump_nsi(nsi);
+
+ /* Test 3GPP TS 08.16, 7.3.1, 5th paragraph. */
+
+ printf("--- Unexpected RESET_ACK VC 1, BSS -> SGSN ---\n\n");
+
+ send_ns_reset_ack(nsi, &sgsn_peer, SGSN_NSEI+1, SGSN_NSEI);
+ gprs_dump_nsi(nsi);
+
+ /* Test 3GPP TS 08.16, 7.3.1, 4th paragraph. */
+
+ printf("--- RESET_ACK with invalid NSEI, BSS -> SGSN ---\n\n");
+
+ nsvc = gprs_nsvc_by_nsvci(nsi, SGSN_NSEI+1);
+ gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION);
+ send_ns_reset_ack(nsi, &sgsn_peer, SGSN_NSEI+1, 0xe000);
+ gprs_dump_nsi(nsi);
+
+ /* Test 3GPP TS 08.16, 7.3.1, 4th paragraph. */
+
+ printf("--- RESET_ACK with invalid NSVCI, BSS -> SGSN ---\n\n");
+
+ nsvc = gprs_nsvc_by_nsvci(nsi, SGSN_NSEI+1);
+ gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION);
+ send_ns_reset_ack(nsi, &sgsn_peer, 0xe001, SGSN_NSEI);
+ gprs_dump_nsi(nsi);
+
+
+ gprs_ns_destroy(nsi);
+ nsi = NULL;
+}
+
+static void test_sgsn_reset_invalid_state()
+{
+ struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
+ struct sockaddr_in sgsn_peer= {0};
+ struct gprs_nsvc *nsvc;
+ int retry;
+ uint8_t dummy_sdu[] = {0x01, 0x02, 0x03, 0x04};
+
+ sgsn_peer.sin_family = AF_INET;
+ sgsn_peer.sin_port = htons(32000);
+ sgsn_peer.sin_addr.s_addr = htonl(REMOTE_SGSN_ADDR);
+
+ gprs_dump_nsi(nsi);
+
+ printf("=== %s ===\n", __func__);
+ printf("--- Setup SGSN connection, BSS -> SGSN ---\n\n");
+
+ gprs_ns_nsip_connect(nsi, &sgsn_peer, SGSN_NSEI, SGSN_NSEI+1);
+ OSMO_ASSERT(sent_pdu_type == NS_PDUT_RESET);
+ send_ns_reset_ack(nsi, &sgsn_peer, SGSN_NSEI+1, SGSN_NSEI);
+ OSMO_ASSERT(sent_pdu_type == NS_PDUT_ALIVE);
+ send_ns_alive_ack(nsi, &sgsn_peer);
+ OSMO_ASSERT(sent_pdu_type == NS_PDUT_UNBLOCK);
+ send_ns_unblock_ack(nsi, &sgsn_peer);
+ gprs_dump_nsi(nsi);
+ nsvc = gprs_nsvc_by_nsvci(nsi, SGSN_NSEI+1);
+ OSMO_ASSERT(nsvc->state == NSE_S_ALIVE);
+ OSMO_ASSERT(nsvc->remote_state == NSE_S_ALIVE);
+
+ printf("--- Time out local test procedure ---\n\n");
+
+ OSMO_ASSERT(expire_nsvc_timer(nsvc) == NSVC_TIMER_TNS_TEST);
+ OSMO_ASSERT(expire_nsvc_timer(nsvc) == NSVC_TIMER_TNS_ALIVE);
+
+ for (retry = 1; retry <= nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]; ++retry)
+ OSMO_ASSERT(expire_nsvc_timer(nsvc) == NSVC_TIMER_TNS_ALIVE);
+
+ OSMO_ASSERT(nsvc->state == NSE_S_BLOCKED);
+
+ printf("--- Remote test procedure continues ---\n\n");
+
+ send_ns_alive(nsi, &sgsn_peer);
+ OSMO_ASSERT(sent_pdu_type == NS_PDUT_RESET);
+
+ printf("--- Don't send a NS_RESET_ACK message (pretend it is lost) ---\n\n");
+
+ sent_pdu_type = -1;
+ send_ns_alive(nsi, &sgsn_peer);
+ OSMO_ASSERT(sent_pdu_type == -1);
+
+ send_ns_reset_ack(nsi, &sgsn_peer, SGSN_NSEI+1, SGSN_NSEI);
+ OSMO_ASSERT(sent_pdu_type == NS_PDUT_ALIVE);
+
+ send_ns_alive_ack(nsi, &sgsn_peer);
+ OSMO_ASSERT(nsvc->state == (NSE_S_ALIVE | NSE_S_BLOCKED));
+ OSMO_ASSERT(nsvc->remote_state == (NSE_S_ALIVE | NSE_S_BLOCKED));
+ OSMO_ASSERT(sent_pdu_type == NS_PDUT_UNBLOCK);
+
+ send_ns_unblock_ack(nsi, &sgsn_peer);
+ OSMO_ASSERT(nsvc->state == NSE_S_ALIVE);
+ OSMO_ASSERT(nsvc->remote_state == NSE_S_ALIVE);
+
+ send_ns_unitdata(nsi, &sgsn_peer, 0x1234, dummy_sdu, sizeof(dummy_sdu));
+
+ gprs_ns_destroy(nsi);
+ nsi = NULL;
+}
+
+static void test_sgsn_output()
+{
+ struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
+ struct sockaddr_in sgsn_peer= {0};
+
+ sgsn_peer.sin_family = AF_INET;
+ sgsn_peer.sin_port = htons(32000);
+ sgsn_peer.sin_addr.s_addr = htonl(REMOTE_SGSN_ADDR);
+
+ gprs_dump_nsi(nsi);
+
+ printf("--- Send message to SGSN ---\n\n");
+
+ gprs_send_message(nsi, "BSSGP RESET", SGSN_NSEI, 0,
+ gprs_bssgp_reset+4, sizeof(gprs_bssgp_reset)-4);
+
+ printf("--- Setup dead connection to SGSN ---\n\n");
+
+ gprs_ns_nsip_connect(nsi, &sgsn_peer, SGSN_NSEI, SGSN_NSEI+1);
+ gprs_dump_nsi(nsi);
+
+ printf("--- Send message to SGSN ---\n\n");
+
+ gprs_send_message(nsi, "BSSGP RESET", SGSN_NSEI, 0,
+ gprs_bssgp_reset+4, sizeof(gprs_bssgp_reset)-4);
+
+ printf("--- Make connection to SGSN alive ---\n\n");
+
+ send_ns_reset_ack(nsi, &sgsn_peer, SGSN_NSEI+1, SGSN_NSEI);
+ send_ns_alive_ack(nsi, &sgsn_peer);
+ gprs_dump_nsi(nsi);
+
+ printf("--- Send message to SGSN ---\n\n");
+
+ gprs_send_message(nsi, "BSSGP RESET", SGSN_NSEI, 0,
+ gprs_bssgp_reset+4, sizeof(gprs_bssgp_reset)-4);
+
+ printf("--- Unblock connection to SGSN ---\n\n");
+
+ send_ns_unblock_ack(nsi, &sgsn_peer);
+ send_ns_alive(nsi, &sgsn_peer);
+ gprs_dump_nsi(nsi);
+
+ printf("--- Send message to SGSN ---\n\n");
+
+ gprs_send_message(nsi, "BSSGP RESET", SGSN_NSEI, 0,
+ gprs_bssgp_reset+4, sizeof(gprs_bssgp_reset)-4);
+
+ printf("--- Send empty message with BVCI to SGSN ---\n\n");
+
+ gprs_send_message(nsi, "[empty]", SGSN_NSEI, 0x0102,
+ gprs_bssgp_reset, 0);
+
+
+ gprs_ns_destroy(nsi);
+ nsi = NULL;
+}
+
+
+int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ return -1;
+}
+
+static struct log_info info = {};
+
+int main(int argc, char **argv)
+{
+ osmo_init_logging(&info);
+ log_set_use_color(osmo_stderr_target, 0);
+ log_set_print_filename(osmo_stderr_target, 0);
+ osmo_signal_register_handler(SS_L_NS, &test_signal, NULL);
+
+ log_set_print_filename(osmo_stderr_target, 0);
+ log_set_log_level(osmo_stderr_target, LOGL_INFO);
+
+ setlinebuf(stdout);
+
+ printf("===== NS protocol test START\n");
+ test_nsvc();
+ test_ignored_messages();
+ test_bss_port_changes();
+ test_bss_reset_ack();
+ test_sgsn_reset();
+ test_sgsn_reset_invalid_state();
+ test_sgsn_output();
+ printf("===== NS protocol test END\n\n");
+
+ exit(EXIT_SUCCESS);
+}
diff --git a/src/shared/libosmocore/tests/gb/gprs_ns_test.ok b/src/shared/libosmocore/tests/gb/gprs_ns_test.ok
new file mode 100644
index 00000000..b0c81e44
--- /dev/null
+++ b/src/shared/libosmocore/tests/gb/gprs_ns_test.ok
@@ -0,0 +1,1024 @@
+===== NS protocol test START
+--- Create via RESET (round 0) ---
+
+PROCESSING RESET from 0x01020304:1111
+02 00 81 01 01 82 10 01 04 82 10 00
+
+==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111
+MESSAGE to BSS, msg length 9
+03 01 82 10 01 04 82 10 00
+
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET) = 9
+
+Current NS-VCIs:
+ VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111, blocked
+
+--- Delete nsvc object (round 0)---
+
+Current NS-VCIs:
+
+--- Create via RESET (round 1) ---
+
+PROCESSING RESET from 0x01020304:1111
+02 00 81 01 01 82 10 01 04 82 10 00
+
+==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111
+MESSAGE to BSS, msg length 9
+03 01 82 10 01 04 82 10 00
+
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET) = 9
+
+Current NS-VCIs:
+ VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111, blocked
+
+--- Delete nsvc object (round 1)---
+
+Current NS-VCIs:
+
+--- Create via RESET (round 2) ---
+
+PROCESSING RESET from 0x01020304:1111
+02 00 81 01 01 82 10 01 04 82 10 00
+
+==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111
+MESSAGE to BSS, msg length 9
+03 01 82 10 01 04 82 10 00
+
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET) = 9
+
+Current NS-VCIs:
+ VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111, blocked
+
+--- Delete nsvc object (round 2)---
+
+Current NS-VCIs:
+
+--- Create via RESET (round 3) ---
+
+PROCESSING RESET from 0x01020304:1111
+02 00 81 01 01 82 10 01 04 82 10 00
+
+==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111
+MESSAGE to BSS, msg length 9
+03 01 82 10 01 04 82 10 00
+
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET) = 9
+
+Current NS-VCIs:
+ VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111, blocked
+
+--- Delete nsvc object (round 3)---
+
+Current NS-VCIs:
+
+--- Process timers ---
+
+--- Send unexpected NS STATUS (should not be answered)---
+
+PROCESSING STATUS from 0x01020304:1111
+08 00 81 0a 02 81 0a
+
+result (STATUS) = 0
+
+--- Send unexpected NS ALIVE ACK (should not be answered)---
+
+PROCESSING ALIVE_ACK from 0x01020304:1111
+0b
+
+result (ALIVE_ACK) = 0
+
+--- Send unexpected NS RESET ACK (should not be answered)---
+
+PROCESSING RESET_ACK from 0x01020304:1111
+03 01 82 e0 01 04 82 e0 00
+
+result (RESET_ACK) = 0
+
+--- Setup, send BSSGP RESET ---
+
+Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1122(4386), NSEI 0x1122(4386)
+
+PROCESSING RESET from 0x01020304:1111
+02 00 81 01 01 82 11 22 04 82 11 22
+
+==> got signal NS_RESET, NS-VC 0x1122/1.2.3.4:1111
+MESSAGE to BSS, msg length 9
+03 01 82 11 22 04 82 11 22
+
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET) = 9
+
+PROCESSING ALIVE from 0x01020304:1111
+0a
+
+MESSAGE to BSS, msg length 1
+0b
+
+result (ALIVE) = 1
+
+PROCESSING UNBLOCK from 0x01020304:1111
+06
+
+==> got signal NS_UNBLOCK, NS-VC 0x1122/1.2.3.4:1111
+MESSAGE to BSS, msg length 1
+07
+
+result (UNBLOCK) = 1
+
+PROCESSING ALIVE_ACK from 0x01020304:1111
+0b
+
+result (ALIVE_ACK) = 0
+
+Current NS-VCIs:
+ VCI 0x1122, NSEI 0x1122, peer 0x01020304:1111
+
+PROCESSING BSSGP RESET from 0x01020304:1111
+00 00 00 00 22 04 82 4a 2e 07 81 08 08 88 10 20 30 40 50 60 10 00
+
+CALLBACK, event 0, msg length 18, bvci 0x0000
+22 04 82 4a 2e 07 81 08 08 88 10 20 30 40 50 60 10 00
+
+result (BSSGP RESET) = 0
+
+--- Peer port changes, RESET, message remains unchanged ---
+
+PROCESSING RESET from 0x01020304:2222
+02 00 81 01 01 82 11 22 04 82 11 22
+
+==> got signal NS_RESET, NS-VC 0x1122/1.2.3.4:2222
+MESSAGE to BSS, msg length 9
+03 01 82 11 22 04 82 11 22
+
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET) = 9
+
+Current NS-VCIs:
+ VCI 0x1122, NSEI 0x1122, peer 0x01020304:2222, blocked
+
+--- Peer port changes, RESET, VCI changes ---
+
+PROCESSING RESET from 0x01020304:3333
+02 00 81 01 01 82 33 44 04 82 11 22
+
+==> got signal NS_RESET, NS-VC 0x3344/1.2.3.4:3333
+MESSAGE to BSS, msg length 9
+03 01 82 33 44 04 82 11 22
+
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET) = 9
+
+Current NS-VCIs:
+ VCI 0x3344, NSEI 0x1122, peer 0x01020304:3333, blocked
+ VCI 0x1122, NSEI 0x1122, peer 0x01020304:2222, blocked
+
+--- Peer port changes, RESET, NSEI changes ---
+
+PROCESSING RESET from 0x01020304:4444
+02 00 81 01 01 82 11 22 04 82 33 44
+
+==> got signal NS_RESET, NS-VC 0x1122/1.2.3.4:4444
+MESSAGE to BSS, msg length 9
+03 01 82 11 22 04 82 33 44
+
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET) = 9
+
+Current NS-VCIs:
+ VCI 0x3344, NSEI 0x1122, peer 0x01020304:3333, blocked
+ VCI 0x1122, NSEI 0x3344, peer 0x01020304:4444, blocked
+ NS-VC changed NSEI count : 1
+
+--- Peer port 3333, RESET, VCI is changed back ---
+
+PROCESSING RESET from 0x01020304:3333
+02 00 81 01 01 82 11 22 04 82 11 22
+
+==> got signal NS_REPLACED: 0x1122/1.2.3.4:4444 -> 0x3344/1.2.3.4:3333
+==> got signal NS_RESET, NS-VC 0x1122/1.2.3.4:3333
+MESSAGE to BSS, msg length 9
+03 01 82 11 22 04 82 11 22
+
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET) = 9
+
+Current NS-VCIs:
+ VCI 0x3344, NSEI 0x1122, peer 0x00000000:0, blocked
+ VCI 0x1122, NSEI 0x1122, peer 0x01020304:3333, blocked
+ NS-VC replaced other count: 1
+ NS-VC changed NSEI count : 2
+
+--- Peer port 4444, RESET, NSEI is changed back ---
+
+PROCESSING RESET from 0x01020304:4444
+02 00 81 01 01 82 11 22 04 82 11 22
+
+==> got signal NS_RESET, NS-VC 0x1122/1.2.3.4:4444
+MESSAGE to BSS, msg length 9
+03 01 82 11 22 04 82 11 22
+
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET) = 9
+
+Current NS-VCIs:
+ VCI 0x3344, NSEI 0x1122, peer 0x00000000:0, blocked
+ VCI 0x1122, NSEI 0x1122, peer 0x01020304:4444, blocked
+ NS-VC replaced other count: 1
+ NS-VC changed NSEI count : 2
+
+--- Setup VC 1 BSS -> SGSN ---
+
+Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096)
+
+PROCESSING RESET from 0x01020304:1111
+02 00 81 01 01 82 10 01 04 82 10 00
+
+==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111
+MESSAGE to BSS, msg length 9
+03 01 82 10 01 04 82 10 00
+
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET) = 9
+
+PROCESSING ALIVE from 0x01020304:1111
+0a
+
+MESSAGE to BSS, msg length 1
+0b
+
+result (ALIVE) = 1
+
+PROCESSING UNBLOCK from 0x01020304:1111
+06
+
+==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111
+MESSAGE to BSS, msg length 1
+07
+
+result (UNBLOCK) = 1
+
+PROCESSING ALIVE_ACK from 0x01020304:1111
+0b
+
+result (ALIVE_ACK) = 0
+
+Current NS-VCIs:
+ VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111
+
+--- Setup VC 2 BSS -> SGSN ---
+
+Setup NS-VC: remote 0x01020304:2222, NSVCI 0x2001(8193), NSEI 0x2000(8192)
+
+PROCESSING RESET from 0x01020304:2222
+02 00 81 01 01 82 20 01 04 82 20 00
+
+==> got signal NS_RESET, NS-VC 0x2001/1.2.3.4:2222
+MESSAGE to BSS, msg length 9
+03 01 82 20 01 04 82 20 00
+
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET) = 9
+
+PROCESSING ALIVE from 0x01020304:2222
+0a
+
+MESSAGE to BSS, msg length 1
+0b
+
+result (ALIVE) = 1
+
+PROCESSING UNBLOCK from 0x01020304:2222
+06
+
+==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:2222
+MESSAGE to BSS, msg length 1
+07
+
+result (UNBLOCK) = 1
+
+PROCESSING ALIVE_ACK from 0x01020304:2222
+0b
+
+result (ALIVE_ACK) = 0
+
+Current NS-VCIs:
+ VCI 0x2001, NSEI 0x2000, peer 0x01020304:2222
+ VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111
+
+--- Setup VC 1 SGSN -> BSS ---
+
+MESSAGE to BSS, msg length 12
+02 00 81 01 01 82 10 01 04 82 10 00
+
+PROCESSING RESET_ACK from 0x01020304:1111
+03 01 82 10 01 04 82 10 00
+
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET_ACK) = 1
+
+Current NS-VCIs:
+ VCI 0x2001, NSEI 0x2000, peer 0x01020304:2222
+ VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111, blocked
+ NS-VC Block count : 1
+
+--- Exchange NSEI 1 + 2 links ---
+
+--- Setup VC 2 SGSN -> BSS (hits NSEI 1) ---
+
+MESSAGE to BSS, msg length 12
+02 00 81 01 01 82 20 01 04 82 20 00
+
+PROCESSING RESET_ACK from 0x01020304:2222
+03 01 82 10 01 04 82 10 00
+
+==> got signal NS_REPLACED: 0x1001/1.2.3.4:1111 -> 0x2001/1.2.3.4:2222
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET_ACK) = 1
+
+Current NS-VCIs:
+ VCI 0x2001, NSEI 0x2000, peer 0x00000000:0, blocked, dead
+ VCI 0x1001, NSEI 0x1000, peer 0x01020304:2222, blocked
+ NS-VC Block count : 2
+ NS-VC replaced other count: 1
+
+--- Setup VC 2 SGSN -> BSS (hits NSEI 2) ---
+
+--- Setup VC 1 SGSN -> BSS (hits NSEI 1) ---
+
+MESSAGE to BSS, msg length 12
+02 00 81 01 01 82 10 01 04 82 10 00
+
+PROCESSING RESET_ACK from 0x01020304:2222
+03 01 82 10 01 04 82 10 00
+
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET_ACK) = 1
+
+Current NS-VCIs:
+ VCI 0x2001, NSEI 0x2000, peer 0x00000000:0, blocked, dead
+ VCI 0x1001, NSEI 0x1000, peer 0x01020304:2222, blocked
+ NS-VC Block count : 3
+ NS-VC replaced other count: 1
+
+--- Setup VC 2 BSS -> SGSN ---
+
+Setup NS-VC: remote 0x01020304:1111, NSVCI 0x2001(8193), NSEI 0x2000(8192)
+
+PROCESSING RESET from 0x01020304:1111
+02 00 81 01 01 82 20 01 04 82 20 00
+
+==> got signal NS_RESET, NS-VC 0x2001/1.2.3.4:1111
+MESSAGE to BSS, msg length 9
+03 01 82 20 01 04 82 20 00
+
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET) = 9
+
+PROCESSING ALIVE from 0x01020304:1111
+0a
+
+MESSAGE to BSS, msg length 1
+0b
+
+result (ALIVE) = 1
+
+PROCESSING UNBLOCK from 0x01020304:1111
+06
+
+==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:1111
+MESSAGE to BSS, msg length 1
+07
+
+result (UNBLOCK) = 1
+
+PROCESSING ALIVE_ACK from 0x01020304:1111
+0b
+
+result (ALIVE_ACK) = 0
+
+Current NS-VCIs:
+ VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111
+ VCI 0x1001, NSEI 0x1000, peer 0x01020304:2222, blocked
+ NS-VC Block count : 3
+ NS-VC replaced other count: 1
+
+--- RESET with invalid NSEI, BSS -> SGSN ---
+
+PROCESSING RESET from 0x01020304:2222
+02 00 81 01 01 82 10 01 04 82 f0 00
+
+==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:2222
+MESSAGE to BSS, msg length 9
+03 01 82 10 01 04 82 f0 00
+
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET) = 9
+
+Current NS-VCIs:
+ VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111
+ VCI 0x1001, NSEI 0xf000, peer 0x01020304:2222, blocked
+ NS-VC Block count : 3
+ NS-VC replaced other count: 1
+ NS-VC changed NSEI count : 1
+
+--- RESET with invalid NSVCI, BSS -> SGSN ---
+
+PROCESSING RESET from 0x01020304:2222
+02 00 81 01 01 82 f0 01 04 82 10 00
+
+==> got signal NS_REPLACED: 0xf001/0.0.0.0:0 -> 0x1001/1.2.3.4:2222
+==> got signal NS_RESET, NS-VC 0xf001/1.2.3.4:2222
+MESSAGE to BSS, msg length 9
+03 01 82 f0 01 04 82 10 00
+
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET) = 9
+
+Current NS-VCIs:
+ VCI 0xf001, NSEI 0x1000, peer 0x01020304:2222, blocked
+ NS-VC replaced other count: 1
+ VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111
+ VCI 0x1001, NSEI 0xf000, peer 0x00000000:0, blocked
+ NS-VC Block count : 3
+ NS-VC replaced other count: 1
+ NS-VC changed NSEI count : 1
+
+--- RESET with old NSEI, NSVCI, BSS -> SGSN ---
+
+PROCESSING RESET from 0x01020304:2222
+02 00 81 01 01 82 10 01 04 82 10 00
+
+==> got signal NS_REPLACED: 0x1001/0.0.0.0:0 -> 0xf001/1.2.3.4:2222
+==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:2222
+MESSAGE to BSS, msg length 9
+03 01 82 10 01 04 82 10 00
+
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET) = 9
+
+Current NS-VCIs:
+ VCI 0xf001, NSEI 0x1000, peer 0x00000000:0, blocked
+ NS-VC replaced other count: 1
+ VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111
+ VCI 0x1001, NSEI 0x1000, peer 0x01020304:2222, blocked
+ NS-VC Block count : 3
+ NS-VC replaced other count: 2
+ NS-VC changed NSEI count : 2
+
+--- Unexpected RESET_ACK VC 1, BSS -> SGSN ---
+
+PROCESSING RESET_ACK from 0x01020304:2222
+03 01 82 10 01 04 82 10 00
+
+result (RESET_ACK) = 0
+
+Current NS-VCIs:
+ VCI 0xf001, NSEI 0x1000, peer 0x00000000:0, blocked
+ NS-VC replaced other count: 1
+ VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111
+ VCI 0x1001, NSEI 0x1000, peer 0x01020304:2222, blocked
+ NS-VC Block count : 3
+ NS-VC replaced other count: 2
+ NS-VC changed NSEI count : 2
+
+--- RESET_ACK with invalid NSEI, BSS -> SGSN ---
+
+MESSAGE to BSS, msg length 12
+02 00 81 01 01 82 10 01 04 82 10 00
+
+PROCESSING RESET_ACK from 0x01020304:2222
+03 01 82 10 01 04 82 f0 00
+
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET_ACK) = 1
+
+Current NS-VCIs:
+ VCI 0xf001, NSEI 0x1000, peer 0x00000000:0, blocked
+ NS-VC replaced other count: 1
+ VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111
+ VCI 0x1001, NSEI 0xf000, peer 0x01020304:2222, blocked
+ NS-VC Block count : 4
+ NS-VC replaced other count: 2
+ NS-VC changed NSEI count : 3
+
+--- RESET_ACK with invalid NSVCI, BSS -> SGSN ---
+
+MESSAGE to BSS, msg length 12
+02 00 81 01 01 82 10 01 04 82 f0 00
+
+PROCESSING RESET_ACK from 0x01020304:2222
+03 01 82 f0 01 04 82 10 00
+
+==> got signal NS_REPLACED: 0xf001/0.0.0.0:0 -> 0x1001/1.2.3.4:2222
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET_ACK) = 1
+
+Current NS-VCIs:
+ VCI 0xf001, NSEI 0x1000, peer 0x01020304:2222, blocked
+ NS-VC Block count : 1
+ NS-VC replaced other count: 2
+ VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111
+ VCI 0x1001, NSEI 0xf000, peer 0x00000000:0, blocked, dead
+ NS-VC Block count : 4
+ NS-VC replaced other count: 2
+ NS-VC changed NSEI count : 3
+
+--- RESET (BSS -> SGSN) crossing an UNBLOCK_ACK (SGSN -> BSS) ---
+
+Setup NS-VC: remote 0x01020304:2222, NSVCI 0x1001(4097), NSEI 0x1000(4096)
+
+PROCESSING RESET from 0x01020304:2222
+02 00 81 01 01 82 10 01 04 82 10 00
+
+==> got signal NS_REPLACED: 0x1001/0.0.0.0:0 -> 0xf001/1.2.3.4:2222
+==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:2222
+MESSAGE to BSS, msg length 9
+03 01 82 10 01 04 82 10 00
+
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET) = 9
+
+PROCESSING ALIVE from 0x01020304:2222
+0a
+
+MESSAGE to BSS, msg length 1
+0b
+
+result (ALIVE) = 1
+
+PROCESSING UNBLOCK from 0x01020304:2222
+06
+
+==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:2222
+MESSAGE to BSS, msg length 1
+07
+
+result (UNBLOCK) = 1
+
+PROCESSING ALIVE_ACK from 0x01020304:2222
+0b
+
+result (ALIVE_ACK) = 0
+
+MESSAGE to BSS, msg length 12
+02 00 81 01 01 82 10 01 04 82 10 00
+
+PROCESSING UNBLOCK_ACK from 0x01020304:2222
+07
+
+==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:2222
+result (UNBLOCK_ACK) = 0
+
+Current NS-VCIs:
+ VCI 0xf001, NSEI 0x1000, peer 0x00000000:0, blocked
+ NS-VC Block count : 1
+ NS-VC replaced other count: 2
+ VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111
+ VCI 0x1001, NSEI 0x1000, peer 0x01020304:2222
+ NS-VC Block count : 4
+ NS-VC replaced other count: 3
+ NS-VC changed NSEI count : 4
+
+PROCESSING RESET_ACK from 0x01020304:2222
+03 01 82 10 01 04 82 10 00
+
+result (RESET_ACK) = 0
+
+MESSAGE to BSS, msg length 12
+02 00 81 01 01 82 10 01 04 82 10 00
+
+PROCESSING RESET_ACK from 0x01020304:2222
+03 01 82 10 01 04 82 10 00
+
+MESSAGE to BSS, msg length 1
+0a
+
+result (RESET_ACK) = 1
+
+Current NS-VCIs:
+ VCI 0xf001, NSEI 0x1000, peer 0x00000000:0, blocked
+ NS-VC Block count : 1
+ NS-VC replaced other count: 2
+ VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111
+ VCI 0x1001, NSEI 0x1000, peer 0x01020304:2222, blocked
+ NS-VC Block count : 5
+ NS-VC replaced other count: 3
+ NS-VC changed NSEI count : 4
+
+Current NS-VCIs:
+
+--- Setup SGSN connection, BSS -> SGSN ---
+
+MESSAGE to SGSN, msg length 12
+02 00 81 01 01 82 01 01 04 82 01 00
+
+PROCESSING RESET_ACK from 0x05060708:32000
+03 01 82 01 01 04 82 01 00
+
+MESSAGE to SGSN, msg length 1
+0a
+
+result (RESET_ACK) = 1
+
+PROCESSING ALIVE_ACK from 0x05060708:32000
+0b
+
+MESSAGE to SGSN, msg length 1
+06
+
+result (ALIVE_ACK) = 1
+
+PROCESSING UNBLOCK_ACK from 0x05060708:32000
+07
+
+==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000
+result (UNBLOCK_ACK) = 0
+
+Current NS-VCIs:
+ VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000
+ NS-VC Block count : 1
+
+--- RESET, SGSN -> BSS ---
+
+PROCESSING RESET from 0x05060708:32000
+02 00 81 01 01 82 01 01 04 82 01 00
+
+==> got signal NS_RESET, NS-VC 0x0101/5.6.7.8:32000
+MESSAGE to SGSN, msg length 9
+03 01 82 01 01 04 82 01 00
+
+MESSAGE to SGSN, msg length 1
+0a
+
+result (RESET) = 9
+
+Current NS-VCIs:
+ VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000, blocked
+ NS-VC Block count : 1
+
+--- RESET with invalid NSEI, SGSN -> BSS ---
+
+PROCESSING RESET from 0x05060708:32000
+02 00 81 01 01 82 01 01 04 82 f0 00
+
+==> got signal NS_MISMATCH: 0x0101/5.6.7.8:32000 pdu=2, ie=4
+MESSAGE to SGSN, msg length 9
+03 01 82 01 01 04 82 01 00
+
+result (RESET) = 0
+
+Current NS-VCIs:
+ VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000, blocked
+ NS-VC Block count : 1
+ NSEI was invalid count : 1
+
+--- RESET with invalid NSVCI, SGSN -> BSS ---
+
+PROCESSING RESET from 0x05060708:32000
+02 00 81 01 01 82 f0 01 04 82 01 00
+
+==> got signal NS_MISMATCH: 0x0101/5.6.7.8:32000 pdu=2, ie=1
+MESSAGE to SGSN, msg length 9
+03 01 82 01 01 04 82 01 00
+
+result (RESET) = 0
+
+Current NS-VCIs:
+ VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000, blocked
+ NS-VC Block count : 1
+ NS-VCI was invalid count : 1
+ NSEI was invalid count : 1
+
+--- RESET, SGSN -> BSS ---
+
+PROCESSING RESET from 0x05060708:32000
+02 00 81 01 01 82 01 01 04 82 01 00
+
+==> got signal NS_RESET, NS-VC 0x0101/5.6.7.8:32000
+MESSAGE to SGSN, msg length 9
+03 01 82 01 01 04 82 01 00
+
+MESSAGE to SGSN, msg length 1
+0a
+
+result (RESET) = 9
+
+Current NS-VCIs:
+ VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000, blocked
+ NS-VC Block count : 1
+ NS-VCI was invalid count : 1
+ NSEI was invalid count : 1
+
+--- Unexpected RESET_ACK VC 1, BSS -> SGSN ---
+
+PROCESSING RESET_ACK from 0x05060708:32000
+03 01 82 01 01 04 82 01 00
+
+result (RESET_ACK) = 0
+
+Current NS-VCIs:
+ VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000, blocked
+ NS-VC Block count : 1
+ NS-VCI was invalid count : 1
+ NSEI was invalid count : 1
+
+--- RESET_ACK with invalid NSEI, BSS -> SGSN ---
+
+MESSAGE to SGSN, msg length 12
+02 00 81 01 01 82 01 01 04 82 01 00
+
+PROCESSING RESET_ACK from 0x05060708:32000
+03 01 82 01 01 04 82 e0 00
+
+==> got signal NS_MISMATCH: 0x0101/5.6.7.8:32000 pdu=3, ie=4
+result (RESET_ACK) = -22
+
+Current NS-VCIs:
+ VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000, blocked, dead
+ NS-VC Block count : 1
+ NS-VCI was invalid count : 1
+ NSEI was invalid count : 2
+
+--- RESET_ACK with invalid NSVCI, BSS -> SGSN ---
+
+MESSAGE to SGSN, msg length 12
+02 00 81 01 01 82 01 01 04 82 01 00
+
+PROCESSING RESET_ACK from 0x05060708:32000
+03 01 82 e0 01 04 82 01 00
+
+==> got signal NS_MISMATCH: 0x0101/5.6.7.8:32000 pdu=3, ie=1
+result (RESET_ACK) = -22
+
+Current NS-VCIs:
+ VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000, blocked, dead
+ NS-VC Block count : 1
+ NS-VCI was invalid count : 2
+ NSEI was invalid count : 2
+
+Current NS-VCIs:
+
+=== test_sgsn_reset_invalid_state ===
+--- Setup SGSN connection, BSS -> SGSN ---
+
+MESSAGE to SGSN, msg length 12
+02 00 81 01 01 82 01 01 04 82 01 00
+
+PROCESSING RESET_ACK from 0x05060708:32000
+03 01 82 01 01 04 82 01 00
+
+MESSAGE to SGSN, msg length 1
+0a
+
+result (RESET_ACK) = 1
+
+PROCESSING ALIVE_ACK from 0x05060708:32000
+0b
+
+MESSAGE to SGSN, msg length 1
+06
+
+result (ALIVE_ACK) = 1
+
+PROCESSING UNBLOCK_ACK from 0x05060708:32000
+07
+
+==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000
+result (UNBLOCK_ACK) = 0
+
+Current NS-VCIs:
+ VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000
+ NS-VC Block count : 1
+
+--- Time out local test procedure ---
+
+MESSAGE to SGSN, msg length 1
+0a
+
+MESSAGE to SGSN, msg length 1
+0a
+
+MESSAGE to SGSN, msg length 1
+0a
+
+MESSAGE to SGSN, msg length 1
+0a
+
+MESSAGE to SGSN, msg length 1
+0a
+
+MESSAGE to SGSN, msg length 1
+0a
+
+MESSAGE to SGSN, msg length 1
+0a
+
+MESSAGE to SGSN, msg length 1
+0a
+
+MESSAGE to SGSN, msg length 1
+0a
+
+MESSAGE to SGSN, msg length 1
+0a
+
+MESSAGE to SGSN, msg length 1
+0a
+
+==> got signal NS_ALIVE_EXP, NS-VC 0x0101/5.6.7.8:32000
+==> got signal NS_BLOCK, NS-VC 0x0101/5.6.7.8:32000
+--- Remote test procedure continues ---
+
+PROCESSING ALIVE from 0x05060708:32000
+0a
+
+MESSAGE to SGSN, msg length 12
+02 00 81 0a 01 82 01 01 04 82 01 00
+
+result (ALIVE) = 12
+
+--- Don't send a NS_RESET_ACK message (pretend it is lost) ---
+
+PROCESSING ALIVE from 0x05060708:32000
+0a
+
+result (ALIVE) = 0
+
+PROCESSING RESET_ACK from 0x05060708:32000
+03 01 82 01 01 04 82 01 00
+
+MESSAGE to SGSN, msg length 1
+0a
+
+result (RESET_ACK) = 1
+
+PROCESSING ALIVE_ACK from 0x05060708:32000
+0b
+
+MESSAGE to SGSN, msg length 1
+06
+
+result (ALIVE_ACK) = 1
+
+PROCESSING UNBLOCK_ACK from 0x05060708:32000
+07
+
+==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000
+result (UNBLOCK_ACK) = 0
+
+PROCESSING UNITDATA from 0x05060708:32000
+00 00 12 34 01 02 03 04
+
+CALLBACK, event 0, msg length 4, bvci 0x1234
+01 02 03 04
+
+result (UNITDATA) = 0
+
+Current NS-VCIs:
+
+--- Send message to SGSN ---
+
+SENDING BSSGP RESET to NSEI 0x0100, BVCI 0x0000
+NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18
+22 04 82 4a 2e 07 81 08 08 88 10 20 30 40 50 60 10 00
+
+result (BSSGP RESET) = -22
+
+--- Setup dead connection to SGSN ---
+
+MESSAGE to SGSN, msg length 12
+02 00 81 01 01 82 01 01 04 82 01 00
+
+Current NS-VCIs:
+ VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000, blocked, dead
+
+--- Send message to SGSN ---
+
+SENDING BSSGP RESET to NSEI 0x0100, BVCI 0x0000
+NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18
+22 04 82 4a 2e 07 81 08 08 88 10 20 30 40 50 60 10 00
+
+result (BSSGP RESET) = -16
+
+--- Make connection to SGSN alive ---
+
+PROCESSING RESET_ACK from 0x05060708:32000
+03 01 82 01 01 04 82 01 00
+
+MESSAGE to SGSN, msg length 1
+0a
+
+result (RESET_ACK) = 1
+
+PROCESSING ALIVE_ACK from 0x05060708:32000
+0b
+
+MESSAGE to SGSN, msg length 1
+06
+
+result (ALIVE_ACK) = 1
+
+Current NS-VCIs:
+ VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000, blocked
+ NS-VC Block count : 1
+
+--- Send message to SGSN ---
+
+SENDING BSSGP RESET to NSEI 0x0100, BVCI 0x0000
+NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18
+22 04 82 4a 2e 07 81 08 08 88 10 20 30 40 50 60 10 00
+
+result (BSSGP RESET) = -16
+
+--- Unblock connection to SGSN ---
+
+PROCESSING UNBLOCK_ACK from 0x05060708:32000
+07
+
+==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000
+result (UNBLOCK_ACK) = 0
+
+PROCESSING ALIVE from 0x05060708:32000
+0a
+
+MESSAGE to SGSN, msg length 1
+0b
+
+result (ALIVE) = 1
+
+Current NS-VCIs:
+ VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000
+ NS-VC Block count : 1
+
+--- Send message to SGSN ---
+
+SENDING BSSGP RESET to NSEI 0x0100, BVCI 0x0000
+NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18
+22 04 82 4a 2e 07 81 08 08 88 10 20 30 40 50 60 10 00
+
+MESSAGE to SGSN, msg length 22
+00 00 00 00 22 04 82 4a 2e 07 81 08 08 88 10 20 30 40 50 60 10 00
+
+result (BSSGP RESET) = 22
+
+--- Send empty message with BVCI to SGSN ---
+
+SENDING [empty] to NSEI 0x0100, BVCI 0x0102
+NS UNITDATA MESSAGE to SGSN, BVCI 0x0102, msg length 0
+
+
+MESSAGE to SGSN, msg length 4
+00 00 01 02
+
+result ([empty]) = 4
+
+===== NS protocol test END
+
diff --git a/src/shared/libosmocore/tests/gea/gea_test.c b/src/shared/libosmocore/tests/gea/gea_test.c
new file mode 100644
index 00000000..ebccaafd
--- /dev/null
+++ b/src/shared/libosmocore/tests/gea/gea_test.c
@@ -0,0 +1,68 @@
+#include <osmocom/core/bits.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/crypt/gprs_cipher.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+
+static inline void print_check(char *res, uint8_t *out, uint16_t len)
+{
+ uint8_t buf[len];
+ osmo_hexparse(res, buf, len);
+ if (0 != memcmp(buf, out, len)) {
+ printf("FAIL:\n");
+ printf("OUT: %s\n", osmo_hexdump_nospc(out, len));
+ printf("EXP: %s\n", osmo_hexdump_nospc(buf, len));
+ } else
+ printf("\n");
+}
+
+static inline void test_gea(bool v4, char *kc, uint32_t iv, int dir,
+ uint16_t len, char *res)
+{
+ uint8_t out[len], ck[256];
+ printf("len %d, dir %d, INPUT 0x%X -> ", len, dir, iv);
+ osmo_hexparse(kc, ck, len);
+ int t = gprs_cipher_run(out, len, v4 ? GPRS_ALGO_GEA4 : GPRS_ALGO_GEA3, ck,
+ iv, dir);
+ printf("%s ", t < 0 ? strerror(-t) : "OK");
+ print_check(res, out, len);
+}
+
+static inline void real_gea(uint32_t iov_ui, uint8_t sapi, uint32_t lfn,
+ uint32_t oc, enum gprs_cipher_direction d, char *kc,
+ uint16_t len, char *res)
+{
+ test_gea(false, kc, gprs_cipher_gen_input_ui(iov_ui, sapi, lfn, oc), d,
+ len, res);
+}
+
+int main(int argc, char **argv)
+{
+ printf("GEA3 support: %d\n", gprs_cipher_supported(GPRS_ALGO_GEA3));
+ printf("GEA4 support: %d\n", gprs_cipher_supported(GPRS_ALGO_GEA4));
+
+ /* GEA3 test vectors according to 3GPP TS 55.217 V6.1.0 and 55.218 V6.1.0 */
+ test_gea(false, "2BD6459F82C5BC00", 0x8E9421A3, 0, 59, "5F359709DE950D0105B17B6C90194280F880B48DCCDC2AFEED415DBEF4354EEBB21D073CCBBFB2D706BD7AFFD371FC96E3970D143DCB2624054826");
+ test_gea(false, "952C49104881FF48", 0x5064DB71, 0, 59, "FDC03D738C8E14FF0320E59AAF75760799E9DA78DD8F888471C4AEAAC1849633A26CD84F459D265B83D7D9B9A0B1E54F4D75E331640DF19E0DB0E0");
+ test_gea(false, "EFA8B2229E720C2A", 0x4BDBD5E5, 1, 59, "4718A2ADFC90590949DDADAB406EC3B925F1AF1214673909DAAB96BB4C18B1374BB1E99445A81CC856E47C6E49E9DBB9873D0831B2175CA1E109BA");
+ test_gea(false, "3451F23A43BD2C87", 0x893FE14F, 0, 59, "B46B1E284E3F8B63B86D9DF0915CFCEDDF2F061895BF9F82BF2593AE4847E94A4626C393CF8941CE15EA7812690D8415B88C5730FE1F5D410E16A2");
+ test_gea(false, "CAA2639BE82435CF", 0x8FE17885, 1, 59, "9FEFAF155A26CF35603E727CDAA87BA067FD84FF98A50B7FF0EC8E95A0FB70E79CB93DEE2B7E9AB59D050E1262401571F349C68229DDF0DECC4E85");
+ test_gea(false, "1ACA8B448B767B39", 0x4F7BC3B5, 0, 59, "514F6C3A3B5A55CA190092F7BB6E80EF3EDB738FCDCE2FF90BB387DDE75BBC32A04A67B898A3DFB8198FFFC37D437CF69E7F9C13B51A868720E750");
+
+ /* GEA4 test vectors according to 3GPP TS 55.226 V9.0.0 */
+ test_gea(true, "D3C5D592327FB11C4035C6680AF8C6D1", 0x0A3A59B4, 0, 51, "6E217CE41EBEFB5EC8094C15974290065E42BABC9AE35654A53085CE68DFA4426A2FF0AD4AF3341006A3F84B7613ACB4FBDC34");
+ test_gea(true, "3D43C388C9581E337FF1F97EB5C1F85E", 0x48571AB9, 0, 59, "FC7314EF00A63ED0116F236C5D25C54EEC56A5B71F9F18B4D7941F84E422ACBDE5EEA9A204679002D14F312F3DEE2A1AC917C3FBDC3696143C0F5D");
+ test_gea(true, "A4496A64DF4F399F3B4506814A3E07A1", 0xEB04ADE2, 1, 59, "2AEB5970FB06B718027D048488AAF24FB3B74EA4A6B1242FF85B108FF816A303C72757D9AAD862B835D1D287DBC141D0A28D79D87BB137CD1198CD");
+
+ /* GEA3 test with real data */
+ real_gea(0, 3, 26, 0, GPRS_CIPH_SGSN2MS, "0c09c6ed723a8400", 199, "b08edbbb7f0532ccbd9fef6e1917fa6815e6d7fa4c9ee629f89299ef9a5541bb23f05875487113c5d5166b7fd0bd04215c60cf8dc404f6cbbfb137191e87f5250a311e34c583f7d8a35346e35f287722b6f234b95b67037ee0711b25fbf8ee3fcb354ee2ba1981e57ce3eeaa4f7401a6a8328994c25b359821e4e7ee242f7daf4e597e006b9cce0b9e86d97cd1ff83a021203b93e83457f64f9794d4388e9b7c509ca5ad278b10082fa20d7ac48aea4cb787c19fcbcea95d63bc17e840380adc86688bcf293f2e");
+ real_gea(0, 3, 25, 0, GPRS_CIPH_SGSN2MS, "0c09c6ed723a8400", 77, "91d2a1a85f6bb00429d76dcd1ca27f50c52e079ee9e990afc8635e25fa8c1007fac52e77035fd09821db893843c7a0666c4e69b5ecd3c9e4e7dc3405e4535115a650eebf698674e248ef13575c");
+ real_gea(0, 3, 20, 0, GPRS_CIPH_MS2SGSN, "bf4575e165fec400", 134, "c43845418e7fc4b3651bc9c3cc9af0163373126c0b31f85d192280e20c981f426dc4a0514a377f76da3d1672c6a0f463513608b3291bacd5d17bb44c8cc5383c3cc85de94e9c594e0fd61d4f2b74b452c1edf07eb04e0e67f352337cc0fd932936841fa41ee5ff0d8f3fad9625a9dec1f12726b74595a1c40d429926ba7e8461f3fa2ae2c0d3");
+ real_gea(0, 3, 21, 0, GPRS_CIPH_MS2SGSN, "bf4575e165fec400", 65, "7b4fc1922c183e6f61e8d2317216ed1d2497477d6f84947f8318df42621ad9affc0c42ba2fd63e06bce4720598d5ae919ca2996f2f1feaea2aa79827692471fd0a");
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/tests/gea/gea_test.ok b/src/shared/libosmocore/tests/gea/gea_test.ok
new file mode 100644
index 00000000..c2987521
--- /dev/null
+++ b/src/shared/libosmocore/tests/gea/gea_test.ok
@@ -0,0 +1,15 @@
+GEA3 support: 1
+GEA4 support: 1
+len 59, dir 0, INPUT 0x8E9421A3 -> OK
+len 59, dir 0, INPUT 0x5064DB71 -> OK
+len 59, dir 1, INPUT 0x4BDBD5E5 -> OK
+len 59, dir 0, INPUT 0x893FE14F -> OK
+len 59, dir 1, INPUT 0x8FE17885 -> OK
+len 59, dir 0, INPUT 0x4F7BC3B5 -> OK
+len 51, dir 0, INPUT 0xA3A59B4 -> OK
+len 59, dir 0, INPUT 0x48571AB9 -> OK
+len 59, dir 1, INPUT 0xEB04ADE2 -> OK
+len 199, dir 1, INPUT 0x9800001A -> OK
+len 77, dir 1, INPUT 0x98000019 -> OK
+len 134, dir 0, INPUT 0x98000014 -> OK
+len 65, dir 0, INPUT 0x98000015 -> OK
diff --git a/src/shared/libosmocore/tests/gprs/gprs_test.c b/src/shared/libosmocore/tests/gprs/gprs_test.c
new file mode 100644
index 00000000..be80e5c0
--- /dev/null
+++ b/src/shared/libosmocore/tests/gprs/gprs_test.c
@@ -0,0 +1,122 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/gsm/apn.h>
+
+static void apn_round_trip(const uint8_t *input, size_t len, const char *wanted_output)
+{
+ char output[len ? len : 1];
+ uint8_t encoded[len + 50];
+ char *out_str;
+ int enc_len;
+
+ /* decode and verify we have what we want */
+ out_str = osmo_apn_to_str(output, input, len);
+ OSMO_ASSERT(out_str);
+ OSMO_ASSERT(out_str == &output[0]);
+ OSMO_ASSERT(strlen(out_str) == strlen(wanted_output));
+ OSMO_ASSERT(strcmp(out_str, wanted_output) == 0);
+
+ /* encode and verify it */
+ if (len != 0) {
+ enc_len = osmo_apn_from_str(encoded, ARRAY_SIZE(encoded), wanted_output);
+ OSMO_ASSERT(enc_len == len);
+ OSMO_ASSERT(memcmp(encoded, input, enc_len) == 0);
+ } else {
+ enc_len = osmo_apn_from_str(encoded, 0, wanted_output);
+ OSMO_ASSERT(enc_len == -1);
+ }
+}
+
+static void test_gsm_03_03_apn(void)
+{
+
+ {
+ /* test invalid writes */
+ const uint8_t ref[10] = { 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF };
+ uint8_t output[10];
+ int enc_len;
+
+ memcpy(output, ref, ARRAY_SIZE(output));
+ enc_len = osmo_apn_from_str(output, 0, "");
+ OSMO_ASSERT(enc_len == -1);
+ OSMO_ASSERT(memcmp(ref, output, ARRAY_SIZE(ref)) == 0);
+
+ memcpy(output, ref, ARRAY_SIZE(output));
+ enc_len = osmo_apn_from_str(output, 0, "foo");
+ OSMO_ASSERT(enc_len == -1);
+ OSMO_ASSERT(memcmp(ref, output, ARRAY_SIZE(ref)) == 0);
+
+ memcpy(output, ref, ARRAY_SIZE(output));
+ enc_len = osmo_apn_from_str(output, 1, "foo");
+ OSMO_ASSERT(enc_len == -1);
+ OSMO_ASSERT(memcmp(ref + 1, output + 1, ARRAY_SIZE(ref) - 1) == 0);
+
+ memcpy(output, ref, ARRAY_SIZE(output));
+ enc_len = osmo_apn_from_str(output, 2, "foo");
+ OSMO_ASSERT(enc_len == -1);
+ OSMO_ASSERT(memcmp(ref + 2, output + 2, ARRAY_SIZE(ref) - 2) == 0);
+
+ memcpy(output, ref, ARRAY_SIZE(output));
+ enc_len = osmo_apn_from_str(output, 3, "foo");
+ OSMO_ASSERT(enc_len == -1);
+ OSMO_ASSERT(memcmp(ref + 3, output + 3, ARRAY_SIZE(ref) - 3) == 0);
+ }
+
+ {
+ /* single empty label */
+ uint8_t input[] = { 0x0 };
+ const char *output = "";
+ apn_round_trip(input, ARRAY_SIZE(input), output);
+ }
+
+ {
+ /* no label */
+ uint8_t input[] = { };
+ const char *output = "";
+ apn_round_trip(input, ARRAY_SIZE(input), output);
+ }
+
+ {
+ /* single label with A */
+ uint8_t input[] = { 0x1, 65 };
+ const char *output = "A";
+ apn_round_trip(input, ARRAY_SIZE(input), output);
+ OSMO_ASSERT(osmo_apn_to_str(NULL, input, ARRAY_SIZE(input) - 1) == NULL);
+ }
+
+ {
+ uint8_t input[] = { 0x3, 65, 66, 67, 0x2, 90, 122 };
+ const char *output = "ABC.Zz";
+ char tmp[strlen(output) + 1];
+ apn_round_trip(input, ARRAY_SIZE(input), output);
+ OSMO_ASSERT(osmo_apn_to_str(tmp, input, ARRAY_SIZE(input) - 1) == NULL);
+ OSMO_ASSERT(osmo_apn_to_str(tmp, input, ARRAY_SIZE(input) - 2) == NULL);
+ OSMO_ASSERT(osmo_apn_to_str(tmp, input, ARRAY_SIZE(input) - 4) == NULL);
+ OSMO_ASSERT(osmo_apn_to_str(tmp, input, ARRAY_SIZE(input) - 5) == NULL);
+ OSMO_ASSERT(osmo_apn_to_str(tmp, input, ARRAY_SIZE(input) - 6) == NULL);
+ }
+}
+
+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)
+{
+ osmo_init_logging(&info);
+
+ test_gsm_03_03_apn();
+
+ printf("Done.\n");
+ return EXIT_SUCCESS;
+}
diff --git a/src/shared/libosmocore/tests/gprs/gprs_test.ok b/src/shared/libosmocore/tests/gprs/gprs_test.ok
new file mode 100644
index 00000000..619c5618
--- /dev/null
+++ b/src/shared/libosmocore/tests/gprs/gprs_test.ok
@@ -0,0 +1 @@
+Done.
diff --git a/src/shared/libosmocore/tests/gsm0408/gsm0408_test.c b/src/shared/libosmocore/tests/gsm0408/gsm0408_test.c
index 077063be..f922a4fd 100644
--- a/src/shared/libosmocore/tests/gsm0408/gsm0408_test.c
+++ b/src/shared/libosmocore/tests/gsm0408/gsm0408_test.c
@@ -20,10 +20,13 @@
#include <string.h>
#include <stdio.h>
+#include <stdlib.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/gsm48_ie.h>
+#include <osmocom/gsm/gsm48.h>
#include <osmocom/gsm/mncc.h>
+#include <osmocom/core/backtrace.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
@@ -127,7 +130,28 @@ static int test_bearer_cap()
return 0;
}
+static void test_mid_from_tmsi(void)
+{
+ static const uint8_t res[] = { 0x17, 0x05, 0xf4, 0xaa, 0xbb, 0xcc, 0xdd };
+
+
+ uint32_t tmsi = 0xAABBCCDD;
+ uint8_t buf[3 + sizeof(uint32_t)];
+
+ printf("Simple TMSI encoding test....");
+
+ memset(&buf, 0xFE, sizeof(buf));
+ gsm48_generate_mid_from_tmsi(buf, tmsi);
+
+ OSMO_ASSERT(memcmp(buf, res, sizeof(res)) == 0);
+ printf("passed\n");
+}
+
int main(int argc, char **argv)
{
+ msgb_talloc_ctx_init(NULL, 0);
test_bearer_cap();
+ test_mid_from_tmsi();
+
+ return EXIT_SUCCESS;
}
diff --git a/src/shared/libosmocore/tests/gsm0408/gsm0408_test.ok b/src/shared/libosmocore/tests/gsm0408/gsm0408_test.ok
index 5ce19e63..4a6d78b9 100644
--- a/src/shared/libosmocore/tests/gsm0408/gsm0408_test.ok
+++ b/src/shared/libosmocore/tests/gsm0408/gsm0408_test.ok
@@ -1,2 +1,3 @@
Test `CSD 9600/V.110/transparent' passed
Test `Speech, all codecs' passed
+Simple TMSI encoding test....passed
diff --git a/src/shared/libosmocore/tests/gsm0808/gsm0808_test.c b/src/shared/libosmocore/tests/gsm0808/gsm0808_test.c
index 7e5e97b5..98502b75 100644
--- a/src/shared/libosmocore/tests/gsm0808/gsm0808_test.c
+++ b/src/shared/libosmocore/tests/gsm0808/gsm0808_test.c
@@ -26,7 +26,7 @@
#define VERIFY(msg, data, len) \
if (msgb_l3len(msg) != len) { \
printf("%s:%d Length don't match: %d vs. %d. %s\n", \
- __func__, __LINE__, msgb_l3len(msg), len, \
+ __func__, __LINE__, msgb_l3len(msg), (int) len, \
osmo_hexdump(msg->l3h, msgb_l3len(msg))); \
abort(); \
} else if (memcmp(msg->l3h, data, len) != 0) { \
diff --git a/src/shared/libosmocore/tests/gsup/gsup_test.c b/src/shared/libosmocore/tests/gsup/gsup_test.c
new file mode 100644
index 00000000..7af16b76
--- /dev/null
+++ b/src/shared/libosmocore/tests/gsup/gsup_test.c
@@ -0,0 +1,259 @@
+#include <string.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/application.h>
+#include <osmocom/gsm/gsup.h>
+
+#define VERBOSE_FPRINTF(...)
+
+/* Tests for osmo_gsup_messages.c */
+
+#define TEST_IMSI_IE 0x01, 0x08, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0xf5
+#define TEST_IMSI_STR "123456789012345"
+
+static void test_gsup_messages_dec_enc(void)
+{
+ int test_idx;
+ int rc;
+ uint8_t buf[1024];
+
+ static const uint8_t send_auth_info_req[] = {
+ 0x08,
+ TEST_IMSI_IE
+ };
+
+ static const uint8_t send_auth_info_err[] = {
+ 0x09,
+ TEST_IMSI_IE,
+ 0x02, 0x01, 0x07 /* GPRS no allowed */
+ };
+
+ static const uint8_t send_auth_info_res[] = {
+ 0x0a,
+ TEST_IMSI_IE,
+ 0x03, 0x22, /* Auth tuple */
+ 0x20, 0x10,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ 0x21, 0x04,
+ 0x21, 0x22, 0x23, 0x24,
+ 0x22, 0x08,
+ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+ 0x03, 0x22, /* Auth tuple */
+ 0x20, 0x10,
+ 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+ 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90,
+ 0x21, 0x04,
+ 0xa1, 0xa2, 0xa3, 0xa4,
+ 0x22, 0x08,
+ 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8,
+ };
+
+ static const uint8_t update_location_req[] = {
+ 0x04,
+ TEST_IMSI_IE,
+ };
+
+ static const uint8_t update_location_err[] = {
+ 0x05,
+ TEST_IMSI_IE,
+ 0x02, 0x01, 0x07 /* GPRS no allowed */
+ };
+
+ static const uint8_t update_location_res[] = {
+ 0x06,
+ TEST_IMSI_IE,
+ 0x08, 0x07, /* MSISDN of the subscriber */
+ 0x91, 0x94, 0x61, 0x46, 0x32, 0x24, 0x43,
+ 0x09, 0x07, /* HLR-Number of the subscriber */
+ 0x91, 0x83, 0x52, 0x38, 0x48, 0x83, 0x93,
+ 0x04, 0x00, /* PDP info complete */
+ 0x05, 0x15,
+ 0x10, 0x01, 0x01,
+ 0x11, 0x02, 0xf1, 0x21, /* IPv4 */
+ 0x12, 0x09, 0x04, 't', 'e', 's', 't', 0x03, 'a', 'p', 'n',
+ 0x13, 0x01, 0x02,
+ 0x05, 0x11,
+ 0x10, 0x01, 0x02,
+ 0x11, 0x02, 0xf1, 0x21, /* IPv4 */
+ 0x12, 0x08, 0x03, 'f', 'o', 'o', 0x03, 'a', 'p', 'n',
+ };
+
+ static const uint8_t location_cancellation_req[] = {
+ 0x1c,
+ TEST_IMSI_IE,
+ 0x06, 0x01, 0x00,
+ };
+
+ static const uint8_t location_cancellation_err[] = {
+ 0x1d,
+ TEST_IMSI_IE,
+ 0x02, 0x01, 0x03 /* Illegal MS */
+ };
+
+ static const uint8_t location_cancellation_res[] = {
+ 0x1e,
+ TEST_IMSI_IE,
+ };
+
+ static const uint8_t purge_ms_req[] = {
+ 0x0c,
+ TEST_IMSI_IE,
+ };
+
+ static const uint8_t purge_ms_err[] = {
+ 0x0d,
+ TEST_IMSI_IE,
+ 0x02, 0x01, 0x03, /* Illegal MS */
+ };
+
+ static const uint8_t purge_ms_res[] = {
+ 0x0e,
+ TEST_IMSI_IE,
+ 0x07, 0x00,
+ };
+
+ static const struct test {
+ char *name;
+ const uint8_t *data;
+ size_t data_len;
+ } test_messages[] = {
+ {"Send Authentication Info Request",
+ send_auth_info_req, sizeof(send_auth_info_req)},
+ {"Send Authentication Info Error",
+ send_auth_info_err, sizeof(send_auth_info_err)},
+ {"Send Authentication Info Result",
+ send_auth_info_res, sizeof(send_auth_info_res)},
+ {"Update Location Request",
+ update_location_req, sizeof(update_location_req)},
+ {"Update Location Error",
+ update_location_err, sizeof(update_location_err)},
+ {"Update Location Result",
+ update_location_res, sizeof(update_location_res)},
+ {"Location Cancellation Request",
+ location_cancellation_req, sizeof(location_cancellation_req)},
+ {"Location Cancellation Error",
+ location_cancellation_err, sizeof(location_cancellation_err)},
+ {"Location Cancellation Result",
+ location_cancellation_res, sizeof(location_cancellation_res)},
+ {"Purge MS Request",
+ purge_ms_req, sizeof(purge_ms_req)},
+ {"Purge MS Error",
+ purge_ms_err, sizeof(purge_ms_err)},
+ {"Purge MS Result",
+ purge_ms_res, sizeof(purge_ms_res)},
+ };
+
+ printf("Test GSUP message decoding/encoding\n");
+
+ for (test_idx = 0; test_idx < ARRAY_SIZE(test_messages); test_idx++) {
+ const struct test *t = &test_messages[test_idx];
+ struct osmo_gsup_message gm = {0};
+ struct msgb *msg = msgb_alloc(4096, "gsup_test");
+
+ printf(" Testing %s\n", t->name);
+
+ rc = osmo_gsup_decode(t->data, t->data_len, &gm);
+ OSMO_ASSERT(rc >= 0);
+
+ osmo_gsup_encode(msg, &gm);
+
+ fprintf(stderr, " generated message: %s\n", msgb_hexdump(msg));
+ fprintf(stderr, " original message: %s\n", osmo_hexdump(t->data, t->data_len));
+ fprintf(stderr, " IMSI: %s\n", gm.imsi);
+ OSMO_ASSERT(strcmp(gm.imsi, TEST_IMSI_STR) == 0);
+ OSMO_ASSERT(msgb_length(msg) == t->data_len);
+ OSMO_ASSERT(memcmp(msgb_data(msg), t->data, t->data_len) == 0);
+
+ msgb_free(msg);
+ }
+
+ /* simple truncation test */
+ for (test_idx = 0; test_idx < ARRAY_SIZE(test_messages); test_idx++) {
+ int j;
+ const struct test *t = &test_messages[test_idx];
+ int ie_end = t->data_len;
+ struct osmo_gsup_message gm = {0};
+ int counter = 0;
+ int parse_err = 0;
+
+ for (j = t->data_len - 1; j >= 0; --j) {
+ rc = osmo_gsup_decode(t->data, j, &gm);
+ counter += 1;
+
+ VERBOSE_FPRINTF(stderr,
+ " partial message decoding: "
+ "orig_len = %d, trunc = %d, rc = %d, ie_end = %d\n",
+ t->data_len, j, rc, ie_end);
+ if (rc >= 0) {
+ VERBOSE_FPRINTF(stderr,
+ " remaing partial message: %s\n",
+ osmo_hexdump(t->data + j, ie_end - j));
+
+ OSMO_ASSERT(j <= ie_end - 2);
+ OSMO_ASSERT(t->data[j+0] <= OSMO_GSUP_KC_IE);
+ OSMO_ASSERT(t->data[j+1] <= ie_end - j - 2);
+
+ ie_end = j;
+ } else {
+ parse_err += 1;
+ }
+ }
+
+ fprintf(stderr,
+ " message %d: tested %d truncations, %d parse failures\n",
+ test_idx, counter, parse_err);
+ }
+
+ /* message modification test (relies on ASAN or valgrind being used) */
+ for (test_idx = 0; test_idx < ARRAY_SIZE(test_messages); test_idx++) {
+ int j;
+ const struct test *t = &test_messages[test_idx];
+ struct osmo_gsup_message gm = {0};
+ uint8_t val;
+ int counter = 0;
+ int parse_err = 0;
+
+ OSMO_ASSERT(sizeof(buf) >= t->data_len);
+
+ for (j = t->data_len - 1; j >= 0; --j) {
+ memcpy(buf, t->data, t->data_len);
+ val = 0;
+ do {
+ VERBOSE_FPRINTF(stderr,
+ "t = %d, len = %d, val = %d\n",
+ test_idx, j, val);
+ buf[j] = val;
+ rc = osmo_gsup_decode(buf, t->data_len, &gm);
+ counter += 1;
+ if (rc < 0)
+ parse_err += 1;
+
+ val += 1;
+ } while (val != (uint8_t)256);
+ }
+
+ fprintf(stderr,
+ " message %d: tested %d modifications, %d parse failures\n",
+ test_idx, counter, parse_err);
+ }
+}
+
+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)
+{
+ osmo_init_logging(&info);
+
+ test_gsup_messages_dec_enc();
+
+ printf("Done.\n");
+ return EXIT_SUCCESS;
+}
diff --git a/src/shared/libosmocore/tests/gsup/gsup_test.ok b/src/shared/libosmocore/tests/gsup/gsup_test.ok
new file mode 100644
index 00000000..1285897c
--- /dev/null
+++ b/src/shared/libosmocore/tests/gsup/gsup_test.ok
@@ -0,0 +1,14 @@
+Test GSUP message decoding/encoding
+ Testing Send Authentication Info Request
+ Testing Send Authentication Info Error
+ Testing Send Authentication Info Result
+ Testing Update Location Request
+ Testing Update Location Error
+ Testing Update Location Result
+ Testing Location Cancellation Request
+ Testing Location Cancellation Error
+ Testing Location Cancellation Result
+ Testing Purge MS Request
+ Testing Purge MS Error
+ Testing Purge MS Result
+Done.
diff --git a/src/shared/libosmocore/tests/kasumi/kasumi_test.c b/src/shared/libosmocore/tests/kasumi/kasumi_test.c
new file mode 100644
index 00000000..a4b1f8d1
--- /dev/null
+++ b/src/shared/libosmocore/tests/kasumi/kasumi_test.c
@@ -0,0 +1,136 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/kasumi.h>
+
+/* Test vectors are taken from TS 135 202 */
+
+inline static int _compare_mem(uint8_t * x, uint8_t * y, size_t len)
+{
+ if (0 != memcmp(x, y, len)) {
+ printf ("X: %s\t", osmo_hexdump_nospc(x, len));
+ printf ("Y: %s\n", osmo_hexdump_nospc(y, len));
+ return 0;
+ }
+ return 1;
+}
+
+inline static void test_expansion(uint8_t * test_key, uint16_t * _KLi1, uint16_t * _KLi2, uint16_t * _KOi1, uint16_t * _KOi2, uint16_t * _KOi3, uint16_t * _KIi1, uint16_t * _KIi2, uint16_t * _KIi3, uint16_t * _KLi1_r, uint16_t * _KLi2_r, uint16_t * _KOi1_r, uint16_t * _KOi2_r, uint16_t * _KOi3_r, uint16_t * _KIi1_r, uint16_t * _KIi2_r, uint16_t * _KIi3_r)
+{
+ _kasumi_key_expand(test_key, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3);
+ int passed = 1;
+ passed = _compare_mem((uint8_t *)_KLi1, (uint8_t *)_KLi1_r, 16);
+ passed = _compare_mem((uint8_t *)_KLi2, (uint8_t *)_KLi2_r, 16);
+ passed = _compare_mem((uint8_t *)_KOi1, (uint8_t *)_KOi1_r, 16);
+ passed = _compare_mem((uint8_t *)_KOi2, (uint8_t *)_KOi2_r, 16);
+ passed = _compare_mem((uint8_t *)_KOi3, (uint8_t *)_KOi3_r, 16);
+ passed = _compare_mem((uint8_t *)_KIi1, (uint8_t *)_KIi1_r, 16);
+ passed = _compare_mem((uint8_t *)_KIi2, (uint8_t *)_KIi2_r, 16);
+ passed = _compare_mem((uint8_t *)_KIi3, (uint8_t *)_KIi3_r, 16);
+ printf(passed ? " OK. " : "FAILED!");
+}
+
+int main(int argc, char **argv)
+{
+ uint16_t _KLi1[8], _KLi2[8], _KOi1[8], _KOi2[8], _KOi3[8], _KIi1[8], _KIi2[8], _KIi3[8], _KLi1_r[8], _KLi2_r[8], _KOi1_r[8], _KOi2_r[8], _KOi3_r[8], _KIi1_r[8], _KIi2_r[8], _KIi3_r[8];
+
+ printf("testing KASUMI key expansion and encryption (ETSI TS 135 203):\n");
+ printf("KASUMI Test Set 1...");
+
+ uint8_t _test_key1[] = {0x2B, 0xD6, 0x45, 0x9F, 0x82, 0xC5, 0xB3, 0x00, 0x95, 0x2C, 0x49, 0x10, 0x48, 0x81, 0xFF, 0x48};
+ _KLi1_r[0] = 0x57AC; _KLi1_r[1] = 0x8B3E; _KLi1_r[2] = 0x058B; _KLi1_r[3] = 0x6601; _KLi1_r[4] = 0x2A59; _KLi1_r[5] = 0x9220; _KLi1_r[6] = 0x9102; _KLi1_r[7] = 0xFE91;
+ _KLi2_r[0] = 0x0B6E; _KLi2_r[1] = 0x7EEF; _KLi2_r[2] = 0x6BF0; _KLi2_r[3] = 0xF388; _KLi2_r[4] = 0x3ED5; _KLi2_r[5] = 0xCD58; _KLi2_r[6] = 0x2AF5; _KLi2_r[7] = 0x00F8;
+ _KOi1_r[0] = 0xB3E8; _KOi1_r[1] = 0x58B0; _KOi1_r[2] = 0x6016; _KOi1_r[3] = 0xA592; _KOi1_r[4] = 0x2209; _KOi1_r[5] = 0x1029; _KOi1_r[6] = 0xE91F; _KOi1_r[7] = 0x7AC5;
+ _KOi2_r[0] = 0x1049; _KOi2_r[1] = 0x8148; _KOi2_r[2] = 0x48FF; _KOi2_r[3] = 0xD62B; _KOi2_r[4] = 0x9F45; _KOi2_r[5] = 0xC582; _KOi2_r[6] = 0x00B3; _KOi2_r[7] = 0x2C95;
+ _KOi3_r[0] = 0x2910; _KOi3_r[1] = 0x1FE9; _KOi3_r[2] = 0xC57A; _KOi3_r[3] = 0xE8B3; _KOi3_r[4] = 0xB058; _KOi3_r[5] = 0x1660; _KOi3_r[6] = 0x92A5; _KOi3_r[7] = 0x0922;
+ _KIi1_r[0] = 0x6BF0; _KIi1_r[1] = 0xF388; _KIi1_r[2] = 0x3ED5; _KIi1_r[3] = 0xCD58; _KIi1_r[4] = 0x2AF5; _KIi1_r[5] = 0x00F8; _KIi1_r[6] = 0x0B6E; _KIi1_r[7] = 0x7EEF;
+ _KIi2_r[0] = 0x7EEF; _KIi2_r[1] = 0x6BF0; _KIi2_r[2] = 0xF388; _KIi2_r[3] = 0x3ED5; _KIi2_r[4] = 0xCD58; _KIi2_r[5] = 0x2AF5; _KIi2_r[6] = 0x00F8; _KIi2_r[7] = 0x0B6E;
+ _KIi3_r[0] = 0xCD58; _KIi3_r[1] = 0x2AF5; _KIi3_r[2] = 0x00F8; _KIi3_r[3] = 0x0B6E; _KIi3_r[4] = 0x7EEF; _KIi3_r[5] = 0x6BF0; _KIi3_r[6] = 0xF388; _KIi3_r[7] = 0x3ED5;
+ test_expansion(_test_key1, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3, _KLi1_r, _KLi2_r, _KOi1_r, _KOi2_r, _KOi3_r, _KIi1_r, _KIi2_r, _KIi3_r);
+
+ if (0xDF1F9B251C0BF45F == _kasumi(0xEA024714AD5C4D84, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3))
+ printf("OK.");
+ else
+ printf("FAILED!");
+
+ printf("\nKASUMI Test Set 2...");
+
+ uint8_t _test_key2[] = {0x8C, 0xE3, 0x3E, 0x2C, 0xC3, 0xC0, 0xB5, 0xFC, 0x1F, 0x3D, 0xE8, 0xA6, 0xDC, 0x66, 0xB1, 0xF3};
+ _KLi1_r[0] = 0x19C7; _KLi1_r[1] = 0x7C58; _KLi1_r[2] = 0x8781; _KLi1_r[3] = 0x6BF9; _KLi1_r[4] = 0x3E7A; _KLi1_r[5] = 0xD14D; _KLi1_r[6] = 0xB8CD; _KLi1_r[7] = 0x63E7;
+ _KLi2_r[0] = 0x4A6B; _KLi2_r[1] = 0x7813; _KLi2_r[2] = 0xE1E1; _KLi2_r[3] = 0x523E; _KLi2_r[4] = 0xAA32; _KLi2_r[5] = 0x83E3; _KLi2_r[6] = 0x8DC0; _KLi2_r[7] = 0x7B4B;
+ _KOi1_r[0] = 0xC587; _KOi1_r[1] = 0x7818; _KOi1_r[2] = 0xBF96; _KOi1_r[3] = 0xE7A3; _KOi1_r[4] = 0x14DD; _KOi1_r[5] = 0x8CDB; _KOi1_r[6] = 0x3E76; _KOi1_r[7] = 0x9C71;
+ _KOi2_r[0] = 0xA6E8; _KOi2_r[1] = 0x66DC; _KOi2_r[2] = 0xF3B1; _KOi2_r[3] = 0xE38C; _KOi2_r[4] = 0x2C3E; _KOi2_r[5] = 0xC0C3; _KOi2_r[6] = 0xFCB5; _KOi2_r[7] = 0x3D1F;
+ _KOi3_r[0] = 0xDB8C; _KOi3_r[1] = 0x763E; _KOi3_r[2] = 0x719C; _KOi3_r[3] = 0x87C5; _KOi3_r[4] = 0x1878; _KOi3_r[5] = 0x96BF; _KOi3_r[6] = 0xA3E7; _KOi3_r[7] = 0xDD14;
+ _KIi1_r[0] = 0xE1E1; _KIi1_r[1] = 0x523E; _KIi1_r[2] = 0xAA32; _KIi1_r[3] = 0x83E3; _KIi1_r[4] = 0x8DC0; _KIi1_r[5] = 0x7B4B; _KIi1_r[6] = 0x4A6B; _KIi1_r[7] = 0x7813;
+ _KIi2_r[0] = 0x7813; _KIi2_r[1] = 0xE1E1; _KIi2_r[2] = 0x523E; _KIi2_r[3] = 0xAA32; _KIi2_r[4] = 0x83E3; _KIi2_r[5] = 0x8DC0; _KIi2_r[6] = 0x7B4B; _KIi2_r[7] = 0x4A6B;
+ _KIi3_r[0] = 0x83E3; _KIi3_r[1] = 0x8DC0; _KIi3_r[2] = 0x7B4B; _KIi3_r[3] = 0x4A6B; _KIi3_r[4] = 0x7813; _KIi3_r[5] = 0xE1E1; _KIi3_r[6] = 0x523E; _KIi3_r[7] = 0xAA32;
+ test_expansion(_test_key2, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3, _KLi1_r, _KLi2_r, _KOi1_r, _KOi2_r, _KOi3_r, _KIi1_r, _KIi2_r, _KIi3_r);
+
+ if (0xDE551988CEB2F9B7 == _kasumi(0xD3C5D592327FB11C, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3))
+ printf("OK.");
+ else
+ printf("FAILED!");
+
+ printf("\nKASUMI Test Set 3...");
+
+ uint8_t _test_key3[] = {0x40, 0x35, 0xC6, 0x68, 0x0A, 0xF8, 0xC6, 0xD1, 0xA8, 0xFF, 0x86, 0x67, 0xB1, 0x71, 0x40, 0x13};
+ _KLi1_r[0] = 0x806A; _KLi1_r[1] = 0x8CD1; _KLi1_r[2] = 0x15F0; _KLi1_r[3] = 0x8DA3; _KLi1_r[4] = 0x51FF; _KLi1_r[5] = 0x0CCF; _KLi1_r[6] = 0x62E3; _KLi1_r[7] = 0x8026;
+ _KLi2_r[0] = 0x8353; _KLi2_r[1] = 0x0B3E; _KLi2_r[2] = 0x5623; _KLi2_r[3] = 0x3CFF; _KLi2_r[4] = 0xC725; _KLi2_r[5] = 0x7203; _KLi2_r[6] = 0x4116; _KLi2_r[7] = 0x830F;
+ _KOi1_r[0] = 0xCD18; _KOi1_r[1] = 0x5F01; _KOi1_r[2] = 0xDA38; _KOi1_r[3] = 0x1FF5; _KOi1_r[4] = 0xCCF0; _KOi1_r[5] = 0x2E36; _KOi1_r[6] = 0x0268; _KOi1_r[7] = 0x06A8;
+ _KOi2_r[0] = 0x6786; _KOi2_r[1] = 0x71B1; _KOi2_r[2] = 0x1340; _KOi2_r[3] = 0x3540; _KOi2_r[4] = 0x68C6; _KOi2_r[5] = 0xF80A; _KOi2_r[6] = 0xD1C6; _KOi2_r[7] = 0xFFA8;
+ _KOi3_r[0] = 0x362E; _KOi3_r[1] = 0x6802; _KOi3_r[2] = 0xA806; _KOi3_r[3] = 0x18CD; _KOi3_r[4] = 0x015F; _KOi3_r[5] = 0x38DA; _KOi3_r[6] = 0xF51F; _KOi3_r[7] = 0xF0CC;
+ _KIi1_r[0] = 0x5623; _KIi1_r[1] = 0x3CFF; _KIi1_r[2] = 0xC725; _KIi1_r[3] = 0x7203; _KIi1_r[4] = 0x4116; _KIi1_r[5] = 0x830F; _KIi1_r[6] = 0x8353; _KIi1_r[7] = 0x0B3E;
+ _KIi2_r[0] = 0x0B3E; _KIi2_r[1] = 0x5623; _KIi2_r[2] = 0x3CFF; _KIi2_r[3] = 0xC725; _KIi2_r[4] = 0x7203; _KIi2_r[5] = 0x4116; _KIi2_r[6] = 0x830F; _KIi2_r[7] = 0x8353;
+ _KIi3_r[0] = 0x7203; _KIi3_r[1] = 0x4116; _KIi3_r[2] = 0x830F; _KIi3_r[3] = 0x8353; _KIi3_r[4] = 0x0B3E; _KIi3_r[5] = 0x5623; _KIi3_r[6] = 0x3CFF; _KIi3_r[7] = 0xC725;
+ test_expansion(_test_key3, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3, _KLi1_r, _KLi2_r, _KOi1_r, _KOi2_r, _KOi3_r, _KIi1_r, _KIi2_r, _KIi3_r);
+
+ if (0x4592B0E78690F71B == _kasumi(0x62A540981BA6F9B7, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3))
+ printf("OK.");
+ else
+ printf("FAILED!");
+
+ printf("\nKASUMI Test Set 4...");
+ uint8_t _test_key4[] = {0x3A, 0x3B, 0x39, 0xB5, 0xC3, 0xF2, 0x37, 0x6D, 0x69, 0xF7, 0xD5, 0x46, 0xE5, 0xF8, 0x5D, 0x43};
+ uint64_t I4 = 0xCA49C1C75771AB0B, i;
+ _kasumi_key_expand(_test_key4, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3);
+
+ for (i = 0; i < 50; i++)
+ I4 = _kasumi(I4, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3);
+
+ if (0x738BAD4C4A690802 == I4) printf(" OK.\n"); else printf("FAILED!");
+
+
+ uint8_t gamma[32];
+
+ uint8_t _Key1[] = {0x2B, 0xD6, 0x45, 0x9F, 0x82, 0xC5, 0xBC, 0x00, 0x2B, 0xD6, 0x45, 0x9F, 0x82, 0xC5, 0xBC, 0x00},
+ _gamma1[] = {0x88, 0x9E, 0xEA, 0xAF, 0x9E, 0xD1, 0xBA, 0x1A, 0xBB, 0xD8, 0x43, 0x62, 0x32, 0xE4, 0x57, 0x28, 0xD0, 0x1A, 0xA8, 0x91, 0x33, 0xDA, 0x73, 0xC1, 0x1E, 0xAB, 0x68, 0xB7, 0xD8, 0x9B, 0xC8, 0x41};
+ _kasumi_kgcore(0xF, 0, 0x0024F20F, 0, _Key1, gamma, 228);
+ printf ("KGCORE Test Set 1: %d\n", _compare_mem(gamma, _gamma1, 32));
+
+ uint8_t _Key2[] = {0x95, 0x2C, 0x49, 0x10, 0x48, 0x81, 0xFF, 0x48, 0x95, 0x2C, 0x49, 0x10, 0x48, 0x81, 0xFF, 0x48},
+ _gamma2[] = {0xFB, 0x4D, 0x5F, 0xBC, 0xEE, 0x13, 0xA3, 0x33, 0x89, 0x28, 0x56, 0x86, 0xE9, 0xA5, 0xC9, 0x42, 0x40, 0xDE, 0x38, 0x15, 0x01, 0x15, 0xF1, 0x5F, 0x8D, 0x9D, 0x98, 0xB9, 0x1A, 0x94, 0xB2, 0x96};
+ _kasumi_kgcore(0xF, 0, 0x00061272, 0, _Key2, gamma, 228);
+ printf ("KGCORE Test Set 2: %d\n", _compare_mem(gamma, _gamma2, 32));
+
+ uint8_t _Key3[] = {0xEF, 0xA8, 0xB2, 0x22, 0x9E, 0x72, 0x0C, 0x2A, 0xEF, 0xA8, 0xB2, 0x22, 0x9E, 0x72, 0x0C, 0x2A},
+ _gamma3[] = {0x0E, 0x40, 0x15, 0x75, 0x5A, 0x33, 0x64, 0x69, 0xC3, 0xDD, 0x86, 0x80, 0xE3, 0x03, 0x5B, 0xC4, 0x19, 0xA7, 0x8A, 0xD3, 0x86, 0x2C, 0x10, 0x90, 0xC6, 0x8A, 0x39, 0x1F, 0xE8, 0xA6, 0xAD, 0xEB};
+ _kasumi_kgcore(0xF, 0, 0x0033FD3F, 0, _Key3, gamma, 228);
+ printf ("KGCORE Test Set 3: %d\n", _compare_mem(gamma, _gamma3, 32));
+
+ uint8_t _Key4[] = {0x5A, 0xCB, 0x1D, 0x64, 0x4C, 0x0D, 0x51, 0x20, 0x4E, 0xA5, 0x5A, 0xCB, 0x1D, 0x64, 0x4C, 0x0D},
+ _gamma4[] = {0xE0, 0x95, 0x30, 0x6A, 0xD5, 0x08, 0x6E, 0x2E, 0xAC, 0x7F, 0x31, 0x07, 0xDE, 0x4F, 0xA2, 0x2D, 0xC1, 0xDF, 0xC9, 0x7D, 0x5B, 0xC5, 0x66, 0x1D, 0xD6, 0x09, 0x6F, 0x47, 0x6A, 0xED, 0xC6, 0x4B};
+ _kasumi_kgcore(0xF, 0, 0x00156B26, 0, _Key4, gamma, 228);
+ printf ("KGCORE Test Set 4: %d\n", _compare_mem(gamma, _gamma4, 32));
+
+ uint8_t _Key5[] = {0xD3, 0xC5, 0xD5, 0x92, 0x32, 0x7F, 0xB1, 0x1C, 0x40, 0x35, 0xC6, 0x68, 0x0A, 0xF8, 0xC6, 0xD1},
+ _gamma5[] = {0xDC, 0xE6, 0x43, 0x62, 0xAB, 0x5F, 0x89, 0xC1, 0x1E, 0xF0, 0xB3, 0x05, 0x16, 0x65, 0x70, 0xF4, 0x88, 0x9D, 0x55, 0x11, 0xE9, 0xE3, 0x57, 0x5D, 0x06, 0x2B, 0x5C, 0xED, 0x60, 0x39, 0x50, 0x6A};
+ _kasumi_kgcore(0xF, 0, 0x000A59B4, 0, _Key5, gamma, 228);
+ printf ("KGCORE Test Set 5: %d\n", _compare_mem(gamma, _gamma5, 32));
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/tests/kasumi/kasumi_test.ok b/src/shared/libosmocore/tests/kasumi/kasumi_test.ok
new file mode 100644
index 00000000..2c2af4c7
--- /dev/null
+++ b/src/shared/libosmocore/tests/kasumi/kasumi_test.ok
@@ -0,0 +1,10 @@
+testing KASUMI key expansion and encryption (ETSI TS 135 203):
+KASUMI Test Set 1... OK. OK.
+KASUMI Test Set 2... OK. OK.
+KASUMI Test Set 3... OK. OK.
+KASUMI Test Set 4... OK.
+KGCORE Test Set 1: 1
+KGCORE Test Set 2: 1
+KGCORE Test Set 3: 1
+KGCORE Test Set 4: 1
+KGCORE Test Set 5: 1
diff --git a/src/shared/libosmocore/tests/lapd/lapd_test.c b/src/shared/libosmocore/tests/lapd/lapd_test.c
index d58bec65..e3223143 100644
--- a/src/shared/libosmocore/tests/lapd/lapd_test.c
+++ b/src/shared/libosmocore/tests/lapd/lapd_test.c
@@ -1,6 +1,7 @@
/*
* (C) 2011 by Holger Hans Peter Freyther
* (C) 2011 by On-Waves
+ * (C) 2014 by Daniel Willmann <dwillmann@sysmocom.de>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
@@ -19,7 +20,9 @@
*
*/
+#include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
#include <osmocom/gsm/lapdm.h>
#include <osmocom/gsm/rsl.h>
@@ -33,14 +36,8 @@
abort(); \
}
-#define ASSERT(exp) \
- if (!(exp)) { \
- printf("Assert failed %s %s:%d\n", #exp, __FILE__, __LINE__); \
- abort(); \
- }
-
-
static struct log_info info = {};
+static int dummy_l1_header_len = 0;
struct lapdm_polling_state {
struct lapdm_channel *bts;
@@ -54,7 +51,8 @@ static struct msgb *msgb_from_array(const uint8_t *data, int len)
{
struct msgb *msg = msgb_alloc_headroom(4096, 128, "data");
msg->l3h = msgb_put(msg, len);
- memcpy(msg->l3h, data, len);
+ if (data && len)
+ memcpy(msg->l3h, data, len);
return msg;
}
@@ -66,10 +64,10 @@ static const uint8_t cm[] = {
0x29, 0x47, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
};
-static const uint8_t cm_padded[] = {
- 0x05, 0x24, 0x31, 0x03, 0x50, 0x18, 0x93, 0x08,
- 0x29, 0x47, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
- 0x2b, 0x2b, 0x2b, 0x2b
+static const uint8_t ua[] = {
+ 0x01, 0x73, 0x41, 0x05, 0x24, 0x31, 0x03, 0x50,
+ 0x18, 0x93, 0x08, 0x29, 0x47, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x2b, 0x2b, 0x2b, 0x2b
};
static const uint8_t mm[] = {
@@ -81,12 +79,25 @@ static const uint8_t dummy1[] = {
0xab, 0x03, 0x30, 0x60, 0x06,
};
+static const uint8_t rel_req[] = {
+ 0x02, 0x07, 0x01, 0x0a, 0x02, 0x40, 0x14, 0x01
+};
+
+static const uint8_t est_req_sdcch_sapi3[] = {
+ 0x02, 0x04, 0x01, 0x20, 0x02, 0x03
+};
+
+static const uint8_t est_req_sacch_sapi3[] = {
+ 0x02, 0x04, 0x01, 0x0b, 0x02, 0x43
+};
+
static struct msgb *create_cm_serv_req(void)
{
struct msgb *msg;
msg = msgb_from_array(cm, sizeof(cm));
rsl_rll_push_l3(msg, RSL_MT_EST_REQ, 0, 0, 1);
+ msgb_push(msg, dummy_l1_header_len);
return msg;
}
@@ -96,9 +107,10 @@ static struct msgb *create_mm_id_req(void)
msg = msgb_from_array(mm, sizeof(mm));
msg->l2h = msg->data + 3;
- ASSERT(msgb_l2len(msg) == 12);
+ OSMO_ASSERT(msgb_l2len(msg) == 12);
msg->l3h = msg->l2h + 6;
- ASSERT(msgb_l3len(msg) == 6);
+ OSMO_ASSERT(msgb_l3len(msg) == 6);
+ msgb_push(msg, dummy_l1_header_len);
return msg;
}
@@ -108,8 +120,9 @@ static struct msgb *create_empty_msg(void)
struct msgb *msg;
msg = msgb_from_array(NULL, 0);
- ASSERT(msgb_l3len(msg) == 0);
+ OSMO_ASSERT(msgb_l3len(msg) == 0);
rsl_rll_push_l3(msg, RSL_MT_DATA_REQ, 0, 0, 1);
+ msgb_push(msg, dummy_l1_header_len);
return msg;
}
@@ -119,6 +132,29 @@ static struct msgb *create_dummy_data_req(void)
msg = msgb_from_array(dummy1, sizeof(dummy1));
rsl_rll_push_l3(msg, RSL_MT_DATA_REQ, 0, 0, 1);
+ msgb_push(msg, dummy_l1_header_len);
+ return msg;
+}
+
+static struct msgb *create_rel_req(void)
+{
+ struct msgb *msg;
+
+ msg = msgb_from_array(rel_req, sizeof(rel_req));
+ msg->l2h = msg->data;
+ msgb_push(msg, dummy_l1_header_len);
+ msg->l3h = msg->l2h + sizeof(struct abis_rsl_rll_hdr);
+ return msg;
+}
+
+static struct msgb *create_est_req(const uint8_t *est_req, size_t est_req_size)
+{
+ struct msgb *msg;
+
+ msg = msgb_from_array(est_req, est_req_size);
+ msg->l2h = msg->data;
+ msgb_push(msg, dummy_l1_header_len);
+ msg->l3h = msg->l2h + sizeof(struct abis_rsl_rll_hdr);
return msg;
}
@@ -140,7 +176,77 @@ static int send(struct msgb *in_msg, struct lapdm_channel *chan)
pp.u.data.link_id = 0;
/* feed into the LAPDm code of libosmogsm */
rc = lapdm_phsap_up(&pp.oph, &chan->lapdm_dcch);
- ASSERT(rc == 0 || rc == -EBUSY);
+ OSMO_ASSERT(rc == 0 || rc == -EBUSY);
+ return 0;
+}
+
+/* Receive from L1 */
+static int send_buf(const uint8_t *buf, size_t len, struct lapdm_channel *chan)
+{
+ struct osmo_phsap_prim pp;
+ struct msgb *msg;
+ int rc;
+
+ msg = msgb_from_array(buf, len);
+ msg->l2h = msg->l3h;
+ msg->l3h = NULL;
+ osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_DATA,
+ PRIM_OP_INDICATION, msg);
+
+ /* LAPDm requires those... */
+ pp.u.data.chan_nr = 0;
+ pp.u.data.link_id = 0;
+ /* feed into the LAPDm code of libosmogsm */
+ rc = lapdm_phsap_up(&pp.oph, &chan->lapdm_dcch);
+ OSMO_ASSERT(rc == 0 || rc == -EBUSY);
+ return 0;
+}
+
+/* Receive from L3 */
+static int enqueue_buf(const uint8_t *buf, size_t len, int sapi, struct lapdm_channel *chan)
+{
+ struct osmo_dlsap_prim dp;
+ struct msgb *msg;
+ int rc;
+ struct lapdm_datalink *dl;
+
+ dl = lapdm_datalink_for_sapi(&chan->lapdm_dcch, sapi);
+ OSMO_ASSERT(dl);
+
+ msg = msgb_from_array(buf, len);
+ osmo_prim_init(&dp.oph, 0, PRIM_DL_DATA, PRIM_OP_REQUEST, msg);
+
+ rc = lapd_recv_dlsap(&dp, &dl->dl.lctx);
+ OSMO_ASSERT(rc == 0 || rc == -EBUSY);
+ return 0;
+}
+
+static int send_sabm(struct lapdm_channel *chan, int sapi, const uint8_t *data, size_t len)
+{
+ struct osmo_phsap_prim pp;
+ struct msgb *msg;
+ int rc;
+
+ OSMO_ASSERT(sapi == 0 || sapi == 3);
+
+ msg = msgb_alloc_headroom(128, 64, "PH-DATA.ind");
+ osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_DATA,
+ PRIM_OP_INDICATION, msg);
+ /* copy over actual MAC block */
+ msg->l2h = msgb_put(msg, 3 + len);
+ msg->l2h[0] = 0x01 | (sapi << 2);
+ msg->l2h[1] = 0x3f;
+ msg->l2h[2] = 0x01 | (len << 2);
+
+ if (len > 0)
+ memcpy(msg->l2h + 3, data, len);
+
+ /* LAPDm requires those... */
+ pp.u.data.chan_nr = 0;
+ pp.u.data.link_id = 0;
+ /* feed into the LAPDm code of libosmogsm */
+ rc = lapdm_phsap_up(&pp.oph, &chan->lapdm_dcch);
+ OSMO_ASSERT(rc == 0 || rc == -EBUSY);
return 0;
}
@@ -157,12 +263,14 @@ static int bts_to_ms_tx_cb(struct msgb *in_msg, struct lapdm_entity *le, void *_
if (state->bts_read == 0) {
printf("BTS: Verifying CM request.\n");
- ASSERT(msgb_l3len(in_msg) == ARRAY_SIZE(cm_padded));
- ASSERT(memcmp(in_msg->l3h, cm_padded, ARRAY_SIZE(cm_padded)) == 0);
+ OSMO_ASSERT(msgb_l3len(in_msg) == ARRAY_SIZE(cm));
+ OSMO_ASSERT(memcmp(in_msg->l3h, cm,
+ ARRAY_SIZE(cm)) == 0);
} else if (state->bts_read == 1) {
printf("BTS: Verifying dummy message.\n");
- ASSERT(msgb_l3len(in_msg) == ARRAY_SIZE(dummy1));
- ASSERT(memcmp(in_msg->l3h, dummy1, ARRAY_SIZE(dummy1)) == 0);
+ OSMO_ASSERT(msgb_l3len(in_msg) == ARRAY_SIZE(dummy1));
+ OSMO_ASSERT(memcmp(in_msg->l3h, dummy1,
+ ARRAY_SIZE(dummy1)) == 0);
} else {
printf("BTS: Do not know to verify: %d\n", state->bts_read);
}
@@ -182,6 +290,52 @@ static int ms_to_bts_l1_cb(struct osmo_prim_hdr *oph, void *_ctx)
/* i stuff it into the LAPDm channel of the BTS */
rc = send(oph->msg, state->bts);
msgb_free(oph->msg);
+ return rc;
+}
+
+static int dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp,
+ const char *queue_name)
+{
+ int rc;
+ int l2_header_len;
+ int l3_len = 0;
+
+ /* Take message from queue */
+ rc = lapdm_phsap_dequeue_prim(le, pp);
+
+ fprintf(stderr, "dequeue: got rc %d: %s\n", rc,
+ rc <= 0 ? strerror(-rc) : "-");
+
+ if (rc < 0)
+ return rc;
+
+ l2_header_len = msgb_l2len(pp->oph.msg);
+ if (msgb_l3(pp->oph.msg)) {
+ l3_len = msgb_l3len(pp->oph.msg);
+ l2_header_len -= l3_len;
+ } else
+ fprintf(stderr, "MSGB: L3 is undefined\n");
+
+ if (l2_header_len < 0 || l2_header_len > pp->oph.msg->data_len) {
+ fprintf(stderr,
+ "MSGB inconsistent: data = %p, l2 = %p, l3 = %p, tail = %p\n",
+ pp->oph.msg->data,
+ pp->oph.msg->l2h,
+ pp->oph.msg->l3h,
+ pp->oph.msg->tail);
+ l2_header_len = -1;
+ }
+
+ printf("Took message from %s queue: "
+ "L2 header size %d, L3 size %d, "
+ "SAP %#x, %d/%d, Link 0x%02x\n",
+ queue_name,
+ l2_header_len, l3_len,
+ pp->oph.sap, pp->oph.primitive, pp->oph.operation,
+ pp->u.data.link_id);
+ printf("Message: %s\n", msgb_hexdump(pp->oph.msg));
+
+ return rc;
}
static int ms_to_bts_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *_ctx)
@@ -194,23 +348,23 @@ static int ms_to_bts_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *_ctx
struct abis_rsl_rll_hdr hdr;
printf("MS: Verifying incoming primitive.\n");
- ASSERT(msg->len == sizeof(struct abis_rsl_rll_hdr) + 3);
+ OSMO_ASSERT(msg->len == sizeof(struct abis_rsl_rll_hdr) + 3);
/* verify the header */
memset(&hdr, 0, sizeof(hdr));
rsl_init_rll_hdr(&hdr, RSL_MT_EST_CONF);
hdr.c.msg_discr |= ABIS_RSL_MDISC_TRANSP;
- ASSERT(memcmp(msg->data, &hdr, sizeof(hdr)) == 0);
+ OSMO_ASSERT(memcmp(msg->data, &hdr, sizeof(hdr)) == 0);
/* Verify the added RSL_IE_L3_INFO but we have a bug here */
- ASSERT(msg->data[6] == RSL_IE_L3_INFO);
+ OSMO_ASSERT(msg->data[6] == RSL_IE_L3_INFO);
#warning "RSL_IE_L3_INFO 16 bit length is wrong"
- /* ASSERT(msg->data[7] == 0x0 && msg->data[8] == 0x9c); */
- /* this should be 0x0 and 0x0... but we have a bug */
+ /* This should be okay but it is actually 0x0, 0x9c on ia-32 */
+ /* OSMO_ASSERT(msg->data[7] == 0x0 && msg->data[8] == 0x0); */
} else if (state->ms_read == 1) {
printf("MS: Verifying incoming MM message: %d\n", msgb_l3len(msg));
- ASSERT(msgb_l3len(msg) == 3);
- ASSERT(memcmp(msg->l3h, &mm[12], msgb_l3len(msg)) == 0);
+ OSMO_ASSERT(msgb_l3len(msg) == 3);
+ OSMO_ASSERT(memcmp(msg->l3h, &mm[12], msgb_l3len(msg)) == 0);
} else {
printf("MS: Do not know to verify: %d\n", state->ms_read);
}
@@ -258,61 +412,360 @@ static void test_lapdm_polling()
/* 2. Poll on the BTS for sending out a confirmation */
printf("\nConfirming\n");
- ASSERT(test_state.bts_read == 1)
- rc = lapdm_phsap_dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp);
+ OSMO_ASSERT(test_state.bts_read == 1);
+ rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH");
CHECK_RC(rc);
- ASSERT(pp.oph.msg->data == pp.oph.msg->l2h);
+ OSMO_ASSERT(pp.oph.msg->data == pp.oph.msg->l2h);
send(pp.oph.msg, &ms_to_bts_channel);
msgb_free(pp.oph.msg);
- ASSERT(test_state.ms_read == 1);
+ OSMO_ASSERT(test_state.ms_read == 1);
/* 3. Send some data to the MS */
printf("\nSending back to MS\n");
lapdm_rslms_recvmsg(create_mm_id_req(), &bts_to_ms_channel);
- rc = lapdm_phsap_dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp);
+ rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH");
CHECK_RC(rc);
send(pp.oph.msg, &ms_to_bts_channel);
msgb_free(pp.oph.msg);
- ASSERT(test_state.ms_read == 2);
+ OSMO_ASSERT(test_state.ms_read == 2);
/* verify that there is nothing more to poll */
- rc = lapdm_phsap_dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp);
- ASSERT(rc < 0);
+ rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH");
+ OSMO_ASSERT(rc < 0);
/* 3. And back to the BTS */
printf("\nSending back to BTS\n");
- ASSERT(test_state.ms_read == 2);
+ OSMO_ASSERT(test_state.ms_read == 2);
lapdm_rslms_recvmsg(create_dummy_data_req(), &ms_to_bts_channel);
/* 4. And back to the MS, but let's move data/l2h apart */
- ASSERT(test_state.bts_read == 2)
- ASSERT(test_state.ms_read == 2);
- rc = lapdm_phsap_dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp);
+ OSMO_ASSERT(test_state.bts_read == 2);
+ OSMO_ASSERT(test_state.ms_read == 2);
+ rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH");
CHECK_RC(rc);
send(pp.oph.msg, &ms_to_bts_channel);
- ASSERT(test_state.ms_read == 2);
+ OSMO_ASSERT(test_state.ms_read == 2);
msgb_free(pp.oph.msg);
/* verify that there is nothing more to poll */
- rc = lapdm_phsap_dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp);
- ASSERT(rc < 0);
+ rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH");
+ OSMO_ASSERT(rc < 0);
+ rc = dequeue_prim(&bts_to_ms_channel.lapdm_acch, &pp, "ACCH");
+ OSMO_ASSERT(rc < 0);
/* check sending an empty L3 message fails */
rc = lapdm_rslms_recvmsg(create_empty_msg(), &bts_to_ms_channel);
- ASSERT(rc == -1);
- ASSERT(test_state.ms_read == 2);
+ OSMO_ASSERT(rc == -1);
+ OSMO_ASSERT(test_state.ms_read == 2);
/* clean up */
lapdm_channel_exit(&bts_to_ms_channel);
lapdm_channel_exit(&ms_to_bts_channel);
+
+ /* Check if exit is idempotent */
+ lapdm_channel_exit(&bts_to_ms_channel);
+ lapdm_channel_exit(&ms_to_bts_channel);
+}
+
+static void test_lapdm_contention_resolution()
+{
+ printf("I test contention resultion by having two mobiles collide and "
+ "first mobile repeating SABM.\n");
+
+ int rc;
+ struct lapdm_polling_state test_state;
+ struct osmo_phsap_prim pp;
+ uint8_t *cm2;
+
+ /* Configure LAPDm on both sides */
+ struct lapdm_channel bts_to_ms_channel;
+ memset(&bts_to_ms_channel, 0, sizeof(bts_to_ms_channel));
+
+ memset(&test_state, 0, sizeof(test_state));
+ test_state.bts = &bts_to_ms_channel;
+
+ /* BTS to MS in polling mode */
+ lapdm_channel_init(&bts_to_ms_channel, LAPDM_MODE_BTS);
+ lapdm_channel_set_flags(&bts_to_ms_channel, LAPDM_ENT_F_POLLING_ONLY);
+ lapdm_channel_set_l1(&bts_to_ms_channel, NULL, &test_state);
+ lapdm_channel_set_l3(&bts_to_ms_channel, bts_to_ms_tx_cb, &test_state);
+
+ /* Send SABM MS 1, we must get UA */
+ send_sabm(&bts_to_ms_channel, 0, cm, sizeof(cm));
+ rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH");
+ CHECK_RC(rc);
+ OSMO_ASSERT(memcmp(pp.oph.msg->l2h, ua, ARRAY_SIZE(ua)) == 0);
+
+ /* Send SABM MS 2, we must get nothing, due to collision */
+ cm2 = malloc(sizeof(cm));
+ memcpy(cm2, cm, sizeof(cm));
+ cm2[0] += 1;
+ send_sabm(&bts_to_ms_channel, 0, cm2, sizeof(cm2));
+ free(cm2);
+ rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH");
+ OSMO_ASSERT(rc == -ENODEV);
+
+ /* Send SABM MS 1 again, we must get UA gain */
+ send_sabm(&bts_to_ms_channel, 0, cm, sizeof(cm));
+ rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH");
+ CHECK_RC(rc);
+ OSMO_ASSERT(memcmp(pp.oph.msg->l2h, ua, ARRAY_SIZE(ua)) == 0);
+
+ /* clean up */
+ lapdm_channel_exit(&bts_to_ms_channel);
+
+ /* idempotent */
+ lapdm_channel_exit(&bts_to_ms_channel);
+}
+
+static void test_lapdm_early_release()
+{
+ printf("I test RF channel release of an unestablished channel.\n");
+
+ int rc;
+ struct lapdm_polling_state test_state;
+
+ /* Configure LAPDm on both sides */
+ struct lapdm_channel bts_to_ms_channel;
+ memset(&bts_to_ms_channel, 0, sizeof(bts_to_ms_channel));
+
+ memset(&test_state, 0, sizeof(test_state));
+ test_state.bts = &bts_to_ms_channel;
+
+ /* BTS to MS in polling mode */
+ lapdm_channel_init(&bts_to_ms_channel, LAPDM_MODE_BTS);
+ lapdm_channel_set_flags(&bts_to_ms_channel, LAPDM_ENT_F_POLLING_ONLY);
+ lapdm_channel_set_l1(&bts_to_ms_channel, NULL, &test_state);
+ lapdm_channel_set_l3(&bts_to_ms_channel, bts_to_ms_tx_cb, &test_state);
+
+ /* Send the release request */
+ rc = lapdm_rslms_recvmsg(create_rel_req(), &bts_to_ms_channel);
+ OSMO_ASSERT(rc == -EINVAL);
+
+ /* clean up */
+ lapdm_channel_exit(&bts_to_ms_channel);
+
+ /* Check if exit is idempotent */
+ lapdm_channel_exit(&bts_to_ms_channel);
+}
+
+static void lapdm_establish(const uint8_t *est_req, size_t est_req_size)
+{
+ int rc;
+ struct lapdm_polling_state test_state;
+ struct osmo_phsap_prim pp;
+ struct msgb *msg;
+
+ /* Configure LAPDm on both sides */
+ struct lapdm_channel bts_to_ms_channel;
+ memset(&bts_to_ms_channel, 0, sizeof(bts_to_ms_channel));
+
+ memset(&test_state, 0, sizeof(test_state));
+ test_state.bts = &bts_to_ms_channel;
+
+ /* BTS to MS in polling mode */
+ lapdm_channel_init(&bts_to_ms_channel, LAPDM_MODE_BTS);
+ lapdm_channel_set_flags(&bts_to_ms_channel, LAPDM_ENT_F_POLLING_ONLY);
+ lapdm_channel_set_l1(&bts_to_ms_channel, NULL, &test_state);
+ lapdm_channel_set_l3(&bts_to_ms_channel, bts_to_ms_tx_cb, &test_state);
+
+ /* Send the release request */
+ msg = create_est_req(est_req, est_req_size);
+ rc = lapdm_rslms_recvmsg(msg, &bts_to_ms_channel);
+ fprintf(stderr, "recvmsg: got rc %d: %s\n", rc, rc <= 0 ? strerror(-rc) : "???");
+ OSMO_ASSERT(rc == 0);
+
+ /* Take message from queue */
+ rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH");
+ if (rc < 0)
+ rc = dequeue_prim(&bts_to_ms_channel.lapdm_acch, &pp, "ACCH");
+
+ CHECK_RC(rc);
+
+ OSMO_ASSERT(pp.oph.msg->data == msgb_l2(pp.oph.msg));
+
+ rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH");
+ OSMO_ASSERT(rc < 0);
+ rc = dequeue_prim(&bts_to_ms_channel.lapdm_acch, &pp, "ACCH");
+ OSMO_ASSERT(rc < 0);
+
+ /* clean up */
+ lapdm_channel_exit(&bts_to_ms_channel);
+
+ /* idempotent */
+ lapdm_channel_exit(&bts_to_ms_channel);
+}
+
+static void test_lapdm_establishment()
+{
+ printf("I test RF channel establishment.\n");
+ printf("Testing SAPI3/SDCCH\n");
+ lapdm_establish(est_req_sdcch_sapi3, sizeof(est_req_sdcch_sapi3));
+ printf("Testing SAPI3/SACCH\n");
+ lapdm_establish(est_req_sacch_sapi3, sizeof(est_req_sacch_sapi3));
+}
+
+
+const uint8_t cm_chg[] = {
+ 0x01, 0x00, 0x49, 0x06, 0x16, 0x03, 0x33,
+ 0x59, 0xa6, 0x20, 0x0a, 0x20, 0x04, 0x04,
+ 0x2f, 0x65, 0x23, 0x02, 0x00, 0x24, 0x04
+};
+
+const uint8_t cm_chg_ack[] = {
+ 0x01, 0x21, 0x01
+};
+
+const uint8_t gprs_susp[] = {
+ 0x01, 0x02, 0x35, 0x06, 0x34, 0xe3, 0xd4,
+ 0xd2, 0x6f, 0x09, 0xf1, 0x07, 0x00, 0x01, 0x00, 0x02
+};
+
+const uint8_t cipher_cmd[] = {
+ 0x06, 0x35, 0x01
+};
+
+/* The cipher command we send to the MS after updating our N(R) */
+const uint8_t cipher_cmd_out[] = {
+ 0x03, 0x40, 0x0d, 0x06, 0x35, 0x01
+};
+
+uint8_t cipher_compl[] = {
+ 0x01, 0x24, 0x09, 0x06, 0x32
+};
+
+uint8_t cipher_compl_ack[] = {
+ 0x01, 0x61, 0x01
+};
+
+static const uint8_t ua_sms[] = {
+ 0x0d, 0x73, 0x01
+};
+
+const uint8_t cp_data_1[] = {
+ 0x0d, 0x00, 0x53, 0x59, 0x01, 0x5c, 0x00,
+ 0x4c, 0x00, 0x06, 0x91, 0x86, 0x77, 0x07,
+ 0x00, 0xf9, 0x51, 0x11, 0x76, 0x05, 0x81, 0x29, 0x32
+};
+
+const uint8_t cp_data_1_ack[] = {
+ 0x0d, 0x21, 0x01
+};
+
+static int bts_to_ms_dummy_tx_cb(struct msgb *in_msg, struct lapdm_entity *le, void *_ctx)
+{
+ printf("%s: MS->BTS(us) message %d\n", __func__, msgb_length(in_msg));
+ msgb_free(in_msg);
+
+ return 0;
+}
+
+static void dump_queue(struct llist_head *head)
+{
+ struct msgb* msg;
+
+ printf("\nDumping queue:\n");
+ llist_for_each_entry(msg, head, list)
+ printf("%s\n", msgb_hexdump(msg));
+ printf("\n");
+}
+
+static void test_lapdm_desync()
+{
+ printf("I test if desync problems exist in LAPDm\n");
+
+ int rc;
+ struct osmo_phsap_prim pp;
+
+ /* Configure LAPDm on both sides */
+ struct lapdm_channel bts_to_ms_channel;
+ memset(&bts_to_ms_channel, 0, sizeof(bts_to_ms_channel));
+
+ /* BTS to MS in polling mode */
+ lapdm_channel_init(&bts_to_ms_channel, LAPDM_MODE_BTS);
+ lapdm_channel_set_flags(&bts_to_ms_channel, LAPDM_ENT_F_POLLING_ONLY);
+ lapdm_channel_set_l1(&bts_to_ms_channel, NULL, NULL);
+ lapdm_channel_set_l3(&bts_to_ms_channel, bts_to_ms_dummy_tx_cb, NULL);
+ struct lapdm_datalink *dl = lapdm_datalink_for_sapi(&bts_to_ms_channel.lapdm_dcch, 0);
+ dl->mctx.dl = dl;
+ dl->dl.lctx.dl = &dl->dl;
+
+ /* Send SABM MS 1, we must get UA */
+ printf("\nEstablishing SAPI=0\n");
+ send_sabm(&bts_to_ms_channel, 0, cm, sizeof(cm));
+
+ dump_queue(&dl->dl.tx_queue);
+
+ rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH");
+ CHECK_RC(rc);
+ OSMO_ASSERT(memcmp(pp.oph.msg->l2h, ua, ARRAY_SIZE(ua)) == 0);
+
+ printf("\nSending Classmark Change\n");
+ send_buf(cm_chg, sizeof(cm_chg), &bts_to_ms_channel);
+ dump_queue(&dl->dl.tx_queue);
+
+ rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH");
+ CHECK_RC(rc);
+ OSMO_ASSERT(memcmp(pp.oph.msg->l2h, cm_chg_ack, ARRAY_SIZE(cm_chg_ack)) == 0);
+
+ printf("\nEnqueueing Ciphering Mode Command\n");
+ enqueue_buf(cipher_cmd, sizeof(cipher_cmd), 0, &bts_to_ms_channel);
+ dump_queue(&dl->dl.tx_queue);
+
+ printf("\nSending GPRS Suspend Request\n");
+ send_buf(gprs_susp, sizeof(gprs_susp), &bts_to_ms_channel);
+ dump_queue(&dl->dl.tx_queue);
+
+ rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH");
+ CHECK_RC(rc);
+ OSMO_ASSERT(memcmp(pp.oph.msg->l2h, cipher_cmd_out, ARRAY_SIZE(cipher_cmd_out)) == 0);
+
+ printf("\nSending Cipher Mode Complete\n");
+ send_buf(cipher_compl, sizeof(cipher_compl), &bts_to_ms_channel);
+ dump_queue(&dl->dl.tx_queue);
+
+ rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH");
+ CHECK_RC(rc);
+ OSMO_ASSERT(memcmp(pp.oph.msg->l2h, cipher_compl_ack, ARRAY_SIZE(cipher_compl_ack)) == 0);
+
+ printf("\nEstablishing SAPI=3\n");
+ send_sabm(&bts_to_ms_channel, 3, NULL, 0);
+ dump_queue(&dl->dl.tx_queue);
+
+ rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH");
+ CHECK_RC(rc);
+ OSMO_ASSERT(memcmp(pp.oph.msg->l2h, ua_sms, ARRAY_SIZE(ua_sms)) == 0);
+
+ printf("\nSending CP-DATA\n");
+ send_buf(cp_data_1, sizeof(cp_data_1), &bts_to_ms_channel);
+ dump_queue(&dl->dl.tx_queue);
+
+ rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH");
+ CHECK_RC(rc);
+ OSMO_ASSERT(memcmp(pp.oph.msg->l2h, cp_data_1_ack, ARRAY_SIZE(cp_data_1_ack)) == 0);
+
+ /* clean up */
+ lapdm_channel_exit(&bts_to_ms_channel);
+
+ /* Check if exit is idempotent */
+ lapdm_channel_exit(&bts_to_ms_channel);
}
int main(int argc, char **argv)
{
osmo_init_logging(&info);
+ /* Prevent the test from segfaulting */
+ dummy_l1_header_len = 0;
test_lapdm_polling();
+
+ dummy_l1_header_len = 3;
+ test_lapdm_early_release();
+ test_lapdm_contention_resolution();
+ test_lapdm_establishment();
+ test_lapdm_desync();
+
printf("Success.\n");
return 0;
diff --git a/src/shared/libosmocore/tests/lapd/lapd_test.ok b/src/shared/libosmocore/tests/lapd/lapd_test.ok
index d67a0a80..e188e27e 100644
--- a/src/shared/libosmocore/tests/lapd/lapd_test.ok
+++ b/src/shared/libosmocore/tests/lapd/lapd_test.ok
@@ -1,14 +1,18 @@
I do some very simple LAPDm test.
Establishing link.
ms_to_bts_l1_cb: MS(us) -> BTS prim message
-bts_to_ms_tx_cb: MS->BTS(us) message 29
+bts_to_ms_tx_cb: MS->BTS(us) message 25
BTS: Verifying CM request.
Confirming
+Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x00
+Message: [L2]> 01 73 41 [L3]> 05 24 31 03 50 18 93 08 29 47 80 00 00 00 00 80 2b 2b 2b 2b
ms_to_bts_tx_cb: BTS->MS(us) message 9
MS: Verifying incoming primitive.
Sending back to MS
+Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x00
+Message: [L2]> 03 00 0d [L3]> 05 04 0d 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
ms_to_bts_tx_cb: BTS->MS(us) message 12
MS: Verifying incoming MM message: 3
ms_to_bts_l1_cb: MS(us) -> BTS prim message
@@ -17,4 +21,79 @@ Sending back to BTS
ms_to_bts_l1_cb: MS(us) -> BTS prim message
bts_to_ms_tx_cb: MS->BTS(us) message 14
BTS: Verifying dummy message.
+Took message from DCCH queue: L2 header size 23, L3 size 0, SAP 0x1000000, 0/0, Link 0x00
+Message: [L2]> 01 21 01 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+I test RF channel release of an unestablished channel.
+I test contention resultion by having two mobiles collide and first mobile repeating SABM.
+bts_to_ms_tx_cb: MS->BTS(us) message 25
+BTS: Verifying CM request.
+Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x00
+Message: [L2]> 01 73 41 [L3]> 05 24 31 03 50 18 93 08 29 47 80 00 00 00 00 80 2b 2b 2b 2b
+Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x00
+Message: [L2]> 01 73 41 [L3]> 05 24 31 03 50 18 93 08 29 47 80 00 00 00 00 80 2b 2b 2b 2b
+I test RF channel establishment.
+Testing SAPI3/SDCCH
+Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x03
+Message: [L2]> 0f 3f 01 [L3]> 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+Testing SAPI3/SACCH
+Took message from ACCH queue: L2 header size 5, L3 size 18, SAP 0x1000000, 0/0, Link 0x43
+Message: [L2]> 00 00 0f 3f 01 [L3]> 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+I test if desync problems exist in LAPDm
+
+Establishing SAPI=0
+bts_to_ms_dummy_tx_cb: MS->BTS(us) message 25
+
+Dumping queue:
+00 00 17 [L2]> 01 73 41 [L3]> 05 24 31 03 50 18 93 08 29 47 80 00 00 00 00 80
+
+Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x00
+Message: [L2]> 01 73 41 [L3]> 05 24 31 03 50 18 93 08 29 47 80 00 00 00 00 80 2b 2b 2b 2b
+
+Sending Classmark Change
+bts_to_ms_dummy_tx_cb: MS->BTS(us) message 27
+
+Dumping queue:
+00 00 17 [L2]> 01 21 01
+
+Took message from DCCH queue: L2 header size 23, L3 size 0, SAP 0x1000000, 0/0, Link 0x00
+Message: [L2]> 01 21 01 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+
+Enqueueing Ciphering Mode Command
+
+Dumping queue:
+00 00 17 [L2]> 03 20 0d [L3]> 06 35 01
+
+
+Sending GPRS Suspend Request
+bts_to_ms_dummy_tx_cb: MS->BTS(us) message 22
+
+Dumping queue:
+00 00 17 [L2]> 03 40 0d [L3]> 06 35 01
+
+Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x00
+Message: [L2]> 03 40 0d [L3]> 06 35 01 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+
+Sending Cipher Mode Complete
+bts_to_ms_dummy_tx_cb: MS->BTS(us) message 11
+
+Dumping queue:
+00 00 17 [L2]> 01 61 01
+
+Took message from DCCH queue: L2 header size 23, L3 size 0, SAP 0x1000000, 0/0, Link 0x00
+Message: [L2]> 01 61 01 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+
+Establishing SAPI=3
+bts_to_ms_dummy_tx_cb: MS->BTS(us) message 6
+
+Dumping queue:
+
+Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x03
+Message: [L2]> 0d 73 01 [L3]> 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+
+Sending CP-DATA
+
+Dumping queue:
+
+Took message from DCCH queue: L2 header size 23, L3 size 0, SAP 0x1000000, 0/0, Link 0x03
+Message: [L2]> 0d 21 01 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
Success.
diff --git a/src/shared/libosmocore/tests/logging/logging_test.c b/src/shared/libosmocore/tests/logging/logging_test.c
index fd62db5a..ce8aac81 100644
--- a/src/shared/libosmocore/tests/logging/logging_test.c
+++ b/src/shared/libosmocore/tests/logging/logging_test.c
@@ -21,12 +21,17 @@
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
+#include <stdlib.h>
+
enum {
DRLL,
DCC,
DMM,
};
+static int filter_called = 0;
+static int select_output = 0;
+
static const struct log_info_cat default_categories[] = {
[DRLL] = {
.name = "DRLL",
@@ -48,11 +53,21 @@ static const struct log_info_cat default_categories[] = {
},
};
+static int test_filter(const struct log_context *ctx, struct log_target *target)
+{
+ filter_called += 1;
+ /* omit everything */
+ return select_output;
+}
+
const struct log_info log_info = {
.cat = default_categories,
.num_cat = ARRAY_SIZE(default_categories),
+ .filter_fn = test_filter,
};
+extern struct log_info *osmo_log_info;
+
int main(int argc, char **argv)
{
struct log_target *stderr_target;
@@ -62,15 +77,55 @@ int main(int argc, char **argv)
log_add_target(stderr_target);
log_set_all_filter(stderr_target, 1);
log_set_print_filename(stderr_target, 0);
+ log_set_print_category(stderr_target, 1);
+ log_set_use_color(stderr_target, 0);
log_parse_category_mask(stderr_target, "DRLL:DCC");
log_parse_category_mask(stderr_target, "DRLL");
+
+ select_output = 0;
+
DEBUGP(DCC, "You should not see this\n");
+ if (log_check_level(DMM, LOGL_DEBUG) != 0)
+ fprintf(stderr, "log_check_level did not catch this case\n");
log_parse_category_mask(stderr_target, "DRLL:DCC");
DEBUGP(DRLL, "You should see this\n");
+ OSMO_ASSERT(log_check_level(DRLL, LOGL_DEBUG) != 0);
DEBUGP(DCC, "You should see this\n");
+ OSMO_ASSERT(log_check_level(DCC, LOGL_DEBUG) != 0);
DEBUGP(DMM, "You should not see this\n");
+ OSMO_ASSERT(log_check_level(DMM, LOGL_DEBUG) == 0);
+ OSMO_ASSERT(filter_called == 0);
+
+ log_set_all_filter(stderr_target, 0);
+ DEBUGP(DRLL, "You should not see this and filter is called\n");
+ OSMO_ASSERT(filter_called == 1);
+ OSMO_ASSERT(log_check_level(DRLL, LOGL_DEBUG) == 0);
+ OSMO_ASSERT(filter_called == 2);
+
+ DEBUGP(DRLL, "You should not see this\n");
+ OSMO_ASSERT(filter_called == 3);
+ select_output = 1;
+ DEBUGP(DRLL, "You should see this\n");
+ OSMO_ASSERT(filter_called == 5); /* called twice on output */
+
+ /* Make sure out-of-bounds category maps to DLGLOBAL */
+ log_parse_category_mask(stderr_target, "DLGLOBAL,1");
+ /* For IDs out of bounds of the overall osmo_log_info array */
+ DEBUGP(osmo_log_info->num_cat + 1, "You should see this on DLGLOBAL (a)\n");
+ DEBUGP(osmo_log_info->num_cat + 100, "You should see this on DLGLOBAL (b)\n");
+ DEBUGP(osmo_log_info->num_cat, "You should see this on DLGLOBAL (c)\n");
+ /* For IDs out of bounds of the user categories part */
+ DEBUGP(log_info.num_cat + 1, "You should see this on DLGLOBAL (d)\n");
+ DEBUGP(log_info.num_cat, "You should see this on DLGLOBAL (e)\n");
+
+ /* Check log_set_category_filter() with internal categories */
+ log_parse_category_mask(stderr_target, "DLGLOBAL,3");
+ DEBUGP(DLGLOBAL, "You should not see this (DLGLOBAL not on DEBUG)\n");
+ log_set_category_filter(stderr_target, DLGLOBAL, 1, LOGL_DEBUG);
+ DEBUGP(DLGLOBAL, "You should see this (DLGLOBAL on DEBUG)\n");
+
return 0;
}
diff --git a/src/shared/libosmocore/tests/logging/logging_test.err b/src/shared/libosmocore/tests/logging/logging_test.err
index b59d2e83..17b3cad0 100644
--- a/src/shared/libosmocore/tests/logging/logging_test.err
+++ b/src/shared/libosmocore/tests/logging/logging_test.err
@@ -1,3 +1,9 @@
-You should see this
-You should see this
- \ No newline at end of file
+DRLL You should see this
+DCC You should see this
+DRLL You should see this
+DLGLOBAL You should see this on DLGLOBAL (a)
+DLGLOBAL You should see this on DLGLOBAL (b)
+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)
diff --git a/src/shared/libosmocore/tests/loggingrb/logging_test.err b/src/shared/libosmocore/tests/loggingrb/logging_test.err
new file mode 100644
index 00000000..b59d2e83
--- /dev/null
+++ b/src/shared/libosmocore/tests/loggingrb/logging_test.err
@@ -0,0 +1,3 @@
+You should see this
+You should see this
+ \ No newline at end of file
diff --git a/src/shared/libosmocore/tests/loggingrb/logging_test.ok b/src/shared/libosmocore/tests/loggingrb/logging_test.ok
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/shared/libosmocore/tests/loggingrb/logging_test.ok
diff --git a/src/shared/libosmocore/tests/loggingrb/loggingrb_test.c b/src/shared/libosmocore/tests/loggingrb/loggingrb_test.c
new file mode 100644
index 00000000..2e2c717b
--- /dev/null
+++ b/src/shared/libosmocore/tests/loggingrb/loggingrb_test.c
@@ -0,0 +1,81 @@
+/* simple test for the debug interface */
+/*
+ * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2012-2013 by Katerina Barone-Adesi <kat.obsc@gmail.com>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/loggingrb.h>
+
+enum {
+ DRLL,
+ DCC,
+ DMM,
+};
+
+static const struct log_info_cat default_categories[] = {
+ [DRLL] = {
+ .name = "DRLL",
+ .description = "A-bis Radio Link Layer (RLL)",
+ .color = "\033[1;31m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [DCC] = {
+ .name = "DCC",
+ .description = "Layer3 Call Control (CC)",
+ .color = "\033[1;32m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [DMM] = {
+ .name = NULL,
+ .description = "Layer3 Mobility Management (MM)",
+ .color = "\033[1;33m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+};
+
+const struct log_info log_info = {
+ .cat = default_categories,
+ .num_cat = ARRAY_SIZE(default_categories),
+};
+
+int main(int argc, char **argv)
+{
+ struct log_target *ringbuf_target;
+
+ log_init(&log_info, NULL);
+ ringbuf_target = log_target_create_rb(0x1000);
+ log_add_target(ringbuf_target);
+ log_set_all_filter(ringbuf_target, 1);
+ log_set_print_filename(ringbuf_target, 0);
+
+ log_parse_category_mask(ringbuf_target, "DRLL:DCC");
+ log_parse_category_mask(ringbuf_target, "DRLL");
+ DEBUGP(DCC, "You should not see this\n");
+
+ log_parse_category_mask(ringbuf_target, "DRLL:DCC");
+ DEBUGP(DRLL, "You should see this\n");
+ DEBUGP(DCC, "You should see this\n");
+ DEBUGP(DMM, "You should not see this\n");
+ fprintf(stderr, "%s", log_target_rb_get(ringbuf_target, 0));
+ fprintf(stderr, "%s", log_target_rb_get(ringbuf_target, 1));
+ OSMO_ASSERT(!log_target_rb_get(ringbuf_target, 2));
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/tests/msgb/msgb_test.c b/src/shared/libosmocore/tests/msgb/msgb_test.c
new file mode 100644
index 00000000..ac103829
--- /dev/null
+++ b/src/shared/libosmocore/tests/msgb/msgb_test.c
@@ -0,0 +1,294 @@
+/*
+ * (C) 2014 by On-Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+#include <setjmp.h>
+
+#include <errno.h>
+
+#include <string.h>
+
+#define CHECK_RC(rc) \
+ if (rc != 0) { \
+ printf("Operation failed rc=%d on %s:%d\n", rc, __FILE__, __LINE__); \
+ abort(); \
+ }
+
+static jmp_buf jmp_env;
+static int jmp_env_valid = 0;
+static void osmo_panic_raise(const char *fmt, va_list args)
+{
+ /*
+ * The args can include pointer values which are not suitable for
+ * regression testing. So just write the (hopefully constant) format
+ * string to stdout and write the full message to stderr.
+ */
+ printf("%s", fmt);
+ vfprintf(stderr, fmt, args);
+ if (!jmp_env_valid)
+ abort();
+ longjmp(jmp_env, 1);
+}
+
+/* Note that this does not nest */
+#define OSMO_PANIC_TRY(pE) (osmo_panic_try(pE, setjmp(jmp_env)))
+
+static int osmo_panic_try(volatile int *exception, int setjmp_result)
+{
+ jmp_env_valid = setjmp_result == 0;
+ *exception = setjmp_result;
+
+ if (setjmp_result)
+ fprintf(stderr, "Exception caught: %d\n", setjmp_result);
+
+ return *exception == 0;
+}
+
+static void test_msgb_api()
+{
+ struct msgb *msg = msgb_alloc_headroom(4096, 128, "data");
+ unsigned char *cptr = NULL;
+ int rc;
+
+ printf("Testing the msgb API\n");
+
+ printf("Buffer: %s\n", msgb_hexdump(msg));
+ OSMO_ASSERT(msgb_test_invariant(msg));
+ cptr = msg->l1h = msgb_put(msg, 4);
+ printf("put(4) -> data%+td\n", cptr - msg->data);
+ printf("Buffer: %s\n", msgb_hexdump(msg));
+ OSMO_ASSERT(msgb_test_invariant(msg));
+ cptr = msg->l2h = msgb_put(msg, 4);
+ printf("put(4) -> data%+td\n", cptr - msg->data);
+ printf("Buffer: %s\n", msgb_hexdump(msg));
+ OSMO_ASSERT(msgb_test_invariant(msg));
+ cptr = msg->l3h = msgb_put(msg, 4);
+ printf("put(4) -> data%+td\n", cptr - msg->data);
+ printf("Buffer: %s\n", msgb_hexdump(msg));
+ OSMO_ASSERT(msgb_test_invariant(msg));
+ cptr = msg->l4h = msgb_put(msg, 4);
+ printf("put(4) -> data%+td\n", cptr - msg->data);
+ printf("Buffer: %s\n", msgb_hexdump(msg));
+ OSMO_ASSERT(msgb_test_invariant(msg));
+ OSMO_ASSERT(msgb_length(msg) == 16);
+ cptr = msgb_push(msg, 4);
+ printf("push(4) -> data%+td\n", cptr - msg->data);
+ printf("Buffer: %s\n", msgb_hexdump(msg));
+ OSMO_ASSERT(msgb_test_invariant(msg));
+ OSMO_ASSERT(msgb_length(msg) == 20);
+ rc = msgb_trim(msg, 16);
+ printf("trim(16) -> %d\n", rc);
+ CHECK_RC(rc);
+ OSMO_ASSERT(msgb_test_invariant(msg));
+ printf("Buffer: %s\n", msgb_hexdump(msg));
+ OSMO_ASSERT(msgb_length(msg) == 16);
+
+ cptr = msgb_get(msg, 4);
+ printf("get(4) -> data%+td\n", cptr - msg->data);
+ printf("Buffer: %s\n", msgb_hexdump(msg));
+ OSMO_ASSERT(msgb_test_invariant(msg));
+ OSMO_ASSERT(msgb_length(msg) == 12);
+
+ printf("Test msgb_hexdump\n");
+ msg->l1h = msg->head;
+ printf("Buffer: %s\n", msgb_hexdump(msg));
+ msg->l3h = msg->data;
+ printf("Buffer: %s\n", msgb_hexdump(msg));
+ msg->l3h = msg->head - 1;
+ printf("Buffer: %s\n", msgb_hexdump(msg));
+
+ msgb_free(msg);
+}
+
+static void test_msgb_api_errors()
+{
+ struct msgb *msg = msgb_alloc_headroom(4096, 128, "data");
+ volatile int e = 0;
+ int rc;
+
+ printf("Testing the msgb API error handling\n");
+
+ osmo_set_panic_handler(osmo_panic_raise);
+
+ if (OSMO_PANIC_TRY(&e))
+ msgb_trim(msg, -1);
+ OSMO_ASSERT(e != 0);
+
+ rc = msgb_trim(msg, 4096 + 500);
+ OSMO_ASSERT(rc == -1);
+
+ msgb_free(msg);
+ osmo_set_panic_handler(NULL);
+}
+
+static void test_msgb_copy()
+{
+ struct msgb *msg = msgb_alloc_headroom(4096, 128, "data");
+ struct msgb *msg2;
+ int i;
+
+ printf("Testing msgb_copy\n");
+
+ msg->l1h = msgb_put(msg, 20);
+ msg->l2h = msgb_put(msg, 20);
+ msg->l3h = msgb_put(msg, 20);
+ msg->l4h = msgb_put(msg, 20);
+
+ OSMO_ASSERT(msgb_length(msg) == 80);
+ for (i = 0; i < msgb_length(msg); i++)
+ msg->data[i] = (uint8_t)i;
+
+ msg2 = msgb_copy(msg, "copy");
+
+ OSMO_ASSERT(msgb_length(msg) == msgb_length(msg2));
+ 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);
+
+ for (i = 0; i < msgb_length(msg2); i++)
+ OSMO_ASSERT(msg2->data[i] == (uint8_t)i);
+
+ printf("Src: %s\n", msgb_hexdump(msg));
+ printf("Dst: %s\n", msgb_hexdump(msg));
+
+ msgb_free(msg);
+ msgb_free(msg2);
+}
+
+static void test_msgb_resize_area()
+{
+ struct msgb *msg = msgb_alloc_headroom(4096, 128, "data");
+ int rc;
+ volatile int e = 0;
+ int i, saved_i;
+ uint8_t *cptr, *old_l3h;
+
+ osmo_set_panic_handler(osmo_panic_raise);
+
+ rc = msgb_resize_area(msg, msg->data, 0, 0);
+ OSMO_ASSERT(rc >= 0);
+
+ if (OSMO_PANIC_TRY(&e))
+ msgb_resize_area(msg, NULL, 0, 0);
+ OSMO_ASSERT(e != 0);
+
+ if (OSMO_PANIC_TRY(&e))
+ msgb_resize_area(msg, NULL, 0, 0);
+ OSMO_ASSERT(e != 0);
+
+ if (OSMO_PANIC_TRY(&e))
+ msgb_resize_area(msg, msg->data, 20, 0);
+ OSMO_ASSERT(e != 0);
+
+ if (OSMO_PANIC_TRY(&e))
+ msgb_resize_area(msg, msg->data, -1, 0);
+ OSMO_ASSERT(e != 0);
+
+ if (OSMO_PANIC_TRY(&e))
+ msgb_resize_area(msg, msg->data, 0, -1);
+ OSMO_ASSERT(e != 0);
+
+ printf("Testing msgb_resize_area\n");
+
+ msg->l1h = msgb_put(msg, 20);
+ msg->l2h = msgb_put(msg, 20);
+ msg->l3h = msgb_put(msg, 20);
+ msg->l4h = msgb_put(msg, 20);
+
+ for (i = 0; i < msgb_length(msg); i++)
+ msg->data[i] = (uint8_t)i;
+
+ printf("Original: %s\n", msgb_hexdump(msg));
+
+ /* Extend area */
+ saved_i = msg->l3h[0];
+ old_l3h = msg->l3h;
+
+ rc = msgb_resize_area(msg, msg->l2h, 20, 20 + 30);
+
+ /* Reset the undefined part to allow printing the buffer to stdout */
+ memset(old_l3h, 0, msg->l3h - old_l3h);
+
+ printf("Extended: %s\n", msgb_hexdump(msg));
+
+ OSMO_ASSERT(rc >= 0);
+ OSMO_ASSERT(msgb_length(msg) == 80 + 30);
+ OSMO_ASSERT(msgb_l1len(msg) == 80 + 30);
+ OSMO_ASSERT(msgb_l2len(msg) == 60 + 30);
+ OSMO_ASSERT(msgb_l3len(msg) == 40);
+ OSMO_ASSERT(msg->tail - msg->l4h == 20);
+
+ for (cptr = msgb_data(msg), i = 0; cptr < old_l3h; cptr++, i++)
+ OSMO_ASSERT(*cptr == (uint8_t)i);
+
+ for (cptr = msg->l3h, i = saved_i; cptr < msg->tail; cptr++, i++)
+ OSMO_ASSERT(*cptr == (uint8_t)i);
+
+ rc = msgb_resize_area(msg, msg->l2h, 50, 8000);
+ OSMO_ASSERT(rc == -1);
+
+ /* Shrink area */
+ saved_i = msg->l4h[0];
+ OSMO_ASSERT(saved_i == (uint8_t)(msg->l4h[-1] + 1));
+
+ rc = msgb_resize_area(msg, msg->l3h, 20, 10);
+
+ printf("Shrinked: %s\n", msgb_hexdump(msg));
+
+ OSMO_ASSERT(rc >= 0);
+ OSMO_ASSERT(msgb_length(msg) == 80 + 30 - 10);
+ OSMO_ASSERT(msgb_l1len(msg) == 80 + 30 - 10);
+ OSMO_ASSERT(msgb_l2len(msg) == 60 + 30 - 10);
+ OSMO_ASSERT(msgb_l3len(msg) == 40 - 10);
+ OSMO_ASSERT(msg->tail - msg->l4h == 20);
+
+ OSMO_ASSERT(msg->l4h[0] != msg->l4h[-1] - 1);
+
+ for (cptr = msg->l4h, i = saved_i; cptr < msg->tail; cptr++, i++)
+ OSMO_ASSERT(*cptr == (uint8_t)i);
+
+ rc = msgb_resize_area(msg, msg->l2h, 50, 8000);
+ OSMO_ASSERT(rc == -1);
+
+ msgb_free(msg);
+
+ osmo_set_panic_handler(NULL);
+}
+
+static struct log_info info = {};
+
+int main(int argc, char **argv)
+{
+ osmo_init_logging(&info);
+
+ test_msgb_api();
+ test_msgb_api_errors();
+ test_msgb_copy();
+ test_msgb_resize_area();
+
+ printf("Success.\n");
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/tests/msgb/msgb_test.ok b/src/shared/libosmocore/tests/msgb/msgb_test.ok
new file mode 100644
index 00000000..6603fe71
--- /dev/null
+++ b/src/shared/libosmocore/tests/msgb/msgb_test.ok
@@ -0,0 +1,35 @@
+Testing the msgb API
+Buffer:
+put(4) -> data+0
+Buffer: [L1]> 00 00 00 00
+put(4) -> data+4
+Buffer: [L1]> 00 00 00 00 [L2]> 00 00 00 00
+put(4) -> data+8
+Buffer: [L1]> 00 00 00 00 [L2]> 00 00 00 00 [L3]> 00 00 00 00
+put(4) -> data+12
+Buffer: [L1]> 00 00 00 00 [L2]> 00 00 00 00 [L3]> 00 00 00 00 [L4]> 00 00 00 00
+push(4) -> data+0
+Buffer: 00 00 00 00 [L1]> 00 00 00 00 [L2]> 00 00 00 00 [L3]> 00 00 00 00 [L4]> 00 00 00 00
+trim(16) -> 0
+Buffer: 00 00 00 00 [L1]> 00 00 00 00 [L2]> 00 00 00 00 [L3]> 00 00 00 00 [L4]>
+get(4) -> data+12
+Buffer: 00 00 00 00 [L1]> 00 00 00 00 [L2]> 00 00 00 00 [L3]> (L4=tail+4)
+Test msgb_hexdump
+Buffer: (L1=data-124) 00 00 00 00 00 00 00 00 [L2]> 00 00 00 00 [L3]> (L4=tail+4)
+Buffer: (L1=data-124) 00 00 00 00 00 00 00 00 [L2]> (L3+8) 00 00 00 00 (L4=tail+4)
+Buffer: (L1=data-124) 00 00 00 00 00 00 00 00 [L2]> 00 00 00 00 (L3 out of range) (L4=tail+4)
+Testing the msgb API error handling
+msgb(%p): Negative length is not allowed
+Testing msgb_copy
+Src: [L1]> 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 [L2]> 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 [L3]> 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b [L4]> 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
+Dst: [L1]> 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 [L2]> 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 [L3]> 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b [L4]> 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
+msgb(%p): Sub area is not fully contained in the msg data
+msgb(%p): Sub area is not fully contained in the msg data
+msgb(%p): Sub area is not fully contained in the msg data
+msgb(%p): Negative sizes are not allowed
+msgb(%p): Negative sizes are not allowed
+Testing msgb_resize_area
+Original: [L1]> 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 [L2]> 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 [L3]> 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b [L4]> 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
+Extended: [L1]> 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 [L2]> 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 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 [L3]> 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b [L4]> 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
+Shrinked: [L1]> 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 [L2]> 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 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 [L3]> 28 29 2a 2b 2c 2d 2e 2f 30 31 [L4]> 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
+Success.
diff --git a/src/shared/libosmocore/tests/msgfile/msgfile_test.c b/src/shared/libosmocore/tests/msgfile/msgfile_test.c
index ed7aa978..2684b6a4 100644
--- a/src/shared/libosmocore/tests/msgfile/msgfile_test.c
+++ b/src/shared/libosmocore/tests/msgfile/msgfile_test.c
@@ -20,6 +20,7 @@
*/
#include <osmocom/core/msgfile.h>
+#include <osmocom/core/talloc.h>
#include <stdio.h>
@@ -45,6 +46,7 @@ int main(int argc, char **argv)
/* todo use msgfile_test.c.in and replace the path */
entries = osmo_config_list_parse(NULL, "msgconfig.cfg");
dump_entries(entries);
+ talloc_free(entries);
return 0;
}
diff --git a/src/shared/libosmocore/tests/oap/Makefile.am b/src/shared/libosmocore/tests/oap/Makefile.am
new file mode 100644
index 00000000..06ccf338
--- /dev/null
+++ b/src/shared/libosmocore/tests/oap/Makefile.am
@@ -0,0 +1,37 @@
+AM_CPPFLAGS = \
+ $(all_includes) \
+ -I$(top_srcdir)/include \
+ $(NULL)
+
+AM_CFLAGS = \
+ -Wall \
+ -ggdb3 \
+ $(LIBOSMOCORE_CFLAGS) \
+ $(LIBOSMOGSM_CFLAGS) \
+ $(NULL)
+
+EXTRA_DIST = \
+ oap_test.ok \
+ $(NULL)
+
+if HAVE_LIBGTP
+if HAVE_LIBCARES
+noinst_PROGRAMS = \
+ oap_test \
+ $(NULL)
+endif
+endif
+
+oap_test_SOURCES = \
+ oap_test.c \
+ $(NULL)
+
+oap_test_LDADD = \
+ $(top_builddir)/src/gprs/oap.o \
+ $(top_builddir)/src/gprs/oap_messages.o \
+ $(top_builddir)/src/gprs/gprs_utils.o \
+ $(top_builddir)/src/libcommon/libcommon.a \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOGSM_LIBS) \
+ -lrt
+
diff --git a/src/shared/libosmocore/tests/oap/oap_test.c b/src/shared/libosmocore/tests/oap/oap_test.c
new file mode 100644
index 00000000..ccf49069
--- /dev/null
+++ b/src/shared/libosmocore/tests/oap/oap_test.c
@@ -0,0 +1,182 @@
+/* Test Osmocom Authentication Protocol */
+/*
+ * (C) 2016 by sysmocom s.f.m.c. GmbH
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/oap.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+
+static struct msgb *oap_encoded(const struct osmo_oap_message *oap_msg)
+{
+ struct msgb *msgb = msgb_alloc_headroom(1000, 64, __func__);
+ OSMO_ASSERT(msgb);
+ osmo_oap_encode(msgb, oap_msg);
+ return msgb;
+}
+
+static bool encode_decode_makes_same_msg(struct osmo_oap_message *oap_msg)
+{
+ struct osmo_oap_message oap_msg_decoded;
+ struct msgb *msgb;
+ int rc;
+ bool result;
+ memset(&oap_msg_decoded, 0, sizeof(oap_msg_decoded));
+
+ msgb = oap_encoded(oap_msg);
+ printf("encoded message:\n%s\n",
+ osmo_hexdump((void*)msgb_l2(msgb), msgb_l2len(msgb)));
+ rc = osmo_oap_decode(&oap_msg_decoded, msgb_l2(msgb), msgb_l2len(msgb));
+
+ if (rc) {
+ printf("osmo_oap_decode() returned error: %d\n", rc);
+ result = false;
+ goto free_msgb;
+ }
+
+ rc = memcmp(oap_msg, &oap_msg_decoded, sizeof(oap_msg_decoded));
+ if (rc) {
+ printf("decoded message mismatches encoded message\n");
+ printf("original:\n%s\n",
+ osmo_hexdump((void*)oap_msg, sizeof(*oap_msg)));
+ printf("en- and decoded:\n%s\n",
+ osmo_hexdump((void*)&oap_msg_decoded, sizeof(oap_msg_decoded)));
+ result = false;
+ goto free_msgb;
+ }
+
+ printf("ok\n");
+ result = true;
+
+free_msgb:
+ talloc_free(msgb);
+ return result;
+}
+
+static void test_oap_messages_dec_enc(void)
+{
+ printf("Testing OAP messages\n");
+
+ struct osmo_oap_message oap_msg;
+
+#define CLEAR() memset(&oap_msg, 0, sizeof(oap_msg))
+#define CHECK() OSMO_ASSERT(encode_decode_makes_same_msg(&oap_msg))
+
+ printf("- Register Request\n");
+ CLEAR();
+ oap_msg.message_type = OAP_MSGT_REGISTER_REQUEST;
+ oap_msg.client_id = 0x2342;
+ CHECK();
+
+ printf("- Register Error\n");
+ CLEAR();
+ oap_msg.message_type = OAP_MSGT_REGISTER_ERROR;
+ oap_msg.cause = GMM_CAUSE_PROTO_ERR_UNSPEC;
+ CHECK();
+
+ printf("- Register Result\n");
+ CLEAR();
+ oap_msg.message_type = OAP_MSGT_REGISTER_RESULT;
+ CHECK();
+
+ printf("- Challenge Request, no rand, no autn\n");
+ CLEAR();
+ oap_msg.message_type = OAP_MSGT_CHALLENGE_REQUEST;
+ oap_msg.rand_present = 0;
+ oap_msg.autn_present = 0;
+ CHECK();
+
+ printf("- Challenge Request, with rand, no autn\n");
+ CLEAR();
+ oap_msg.message_type = OAP_MSGT_CHALLENGE_REQUEST;
+ osmo_hexparse("0102030405060708090a0b0c0d0e0f10",
+ oap_msg.rand, 16);
+ oap_msg.rand_present = 1;
+ oap_msg.autn_present = 0;
+ CHECK();
+
+ printf("- Challenge Request, no rand, with autn\n");
+ CLEAR();
+ oap_msg.message_type = OAP_MSGT_CHALLENGE_REQUEST;
+ oap_msg.rand_present = 0;
+ osmo_hexparse("cec4e3848a33000086781158ca40f136",
+ oap_msg.autn, 16);
+ oap_msg.autn_present = 1;
+ CHECK();
+
+ printf("- Challenge Request, with rand, with autn\n");
+ CLEAR();
+ oap_msg.message_type = OAP_MSGT_CHALLENGE_REQUEST;
+ osmo_hexparse("0102030405060708090a0b0c0d0e0f10",
+ oap_msg.rand, 16);
+ oap_msg.rand_present = 1;
+ osmo_hexparse("cec4e3848a33000086781158ca40f136",
+ oap_msg.autn, 16);
+ oap_msg.autn_present = 1;
+ CHECK();
+
+ printf("- Challenge Error\n");
+ CLEAR();
+ oap_msg.message_type = OAP_MSGT_CHALLENGE_ERROR;
+ oap_msg.cause = GMM_CAUSE_GSM_AUTH_UNACCEPT;
+ CHECK();
+
+ printf("- Challenge Result\n");
+ CLEAR();
+ oap_msg.message_type = OAP_MSGT_CHALLENGE_RESULT;
+ osmo_hexparse("0102030405060708",
+ oap_msg.xres, 8);
+ oap_msg.xres_present = 1;
+ CHECK();
+
+ printf("- Sync Request\n");
+ CLEAR();
+ oap_msg.message_type = OAP_MSGT_SYNC_REQUEST;
+ osmo_hexparse("102030405060708090a0b0c0d0e0f001",
+ oap_msg.auts, 16);
+ oap_msg.auts_present = 1;
+ CHECK();
+
+ /* Sync Error and Sync Result are not used in OAP */
+}
+
+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)
+{
+ osmo_init_logging(&info);
+
+ test_oap_messages_dec_enc();
+
+ printf("Done.\n");
+ return EXIT_SUCCESS;
+}
diff --git a/src/shared/libosmocore/tests/oap/oap_test.ok b/src/shared/libosmocore/tests/oap/oap_test.ok
new file mode 100644
index 00000000..9260d442
--- /dev/null
+++ b/src/shared/libosmocore/tests/oap/oap_test.ok
@@ -0,0 +1,42 @@
+Testing OAP messages
+- Register Request
+encoded message:
+04 30 02 23 42
+ok
+- Register Error
+encoded message:
+05 02 01 6f
+ok
+- Register Result
+encoded message:
+06
+ok
+- Challenge Request, no rand, no autn
+encoded message:
+08
+ok
+- Challenge Request, with rand, no autn
+encoded message:
+08 20 10 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10
+ok
+- Challenge Request, no rand, with autn
+encoded message:
+08 23 10 ce c4 e3 84 8a 33 00 00 86 78 11 58 ca 40 f1 36
+ok
+- Challenge Request, with rand, with autn
+encoded message:
+08 20 10 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 23 10 ce c4 e3 84 8a 33 00 00 86 78 11 58 ca 40 f1 36
+ok
+- Challenge Error
+encoded message:
+09 02 01 17
+ok
+- Challenge Result
+encoded message:
+0a 24 08 01 02 03 04 05 06 07 08
+ok
+- Sync Request
+encoded message:
+0c 25 10 10 20 30 40 50 60 70 80 90 a0 b0 c0 d0 e0 f0 01
+ok
+Done.
diff --git a/src/shared/libosmocore/tests/sim/sim_test.c b/src/shared/libosmocore/tests/sim/sim_test.c
new file mode 100644
index 00000000..425ce11d
--- /dev/null
+++ b/src/shared/libosmocore/tests/sim/sim_test.c
@@ -0,0 +1,58 @@
+/*
+ * (C) 2016 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <osmocom/sim/sim.h>
+#include <osmocom/sim/class_tables.h>
+
+const uint8_t sim_sel_mf[] = { 0xA0, 0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00 };
+const uint8_t usim_sel_mf[] = { 0x00, 0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00 };
+const uint8_t uicc_tprof[] = { 0x80, 0x10, 0x00, 0x00, 0x02, 0x01, 0x02 };
+const uint8_t uicc_tprof_wrong_class[] = { 0x00, 0x10, 0x00, 0x00, 0x02, 0x01, 0x02 };
+const uint8_t uicc_read[] = { 0x00, 0xB0, 0x00, 0x00, 0x10 };
+const uint8_t uicc_upd[] = { 0x00, 0xD6, 0x00, 0x00, 0x02, 0x01, 0x02 };
+
+#define APDU_CASE_ASSERT(x, y) \
+ do { \
+ printf("Testing " #x "\n"); \
+ int rc = osim_determine_apdu_case(&osim_uicc_sim_cic_profile, x); \
+ if (rc != y) \
+ printf("%d (actual) != %d (intended)\n", rc, y); \
+ OSMO_ASSERT(rc == y); \
+ } while (0)
+
+static void test_cla_ins_tbl(void)
+{
+ APDU_CASE_ASSERT(sim_sel_mf, 4);
+ APDU_CASE_ASSERT(usim_sel_mf, 4);
+ APDU_CASE_ASSERT(uicc_tprof, 3);
+ APDU_CASE_ASSERT(uicc_tprof_wrong_class, 0);
+ APDU_CASE_ASSERT(uicc_read, 2);
+ APDU_CASE_ASSERT(uicc_upd, 3);
+}
+
+int main(int argc, char **argv)
+{
+ test_cla_ins_tbl();
+ return EXIT_SUCCESS;
+}
diff --git a/src/shared/libosmocore/tests/sim/sim_test.ok b/src/shared/libosmocore/tests/sim/sim_test.ok
new file mode 100644
index 00000000..7d3f986d
--- /dev/null
+++ b/src/shared/libosmocore/tests/sim/sim_test.ok
@@ -0,0 +1,6 @@
+Testing sim_sel_mf
+Testing usim_sel_mf
+Testing uicc_tprof
+Testing uicc_tprof_wrong_class
+Testing uicc_read
+Testing uicc_upd
diff --git a/src/shared/libosmocore/tests/sms/sms_test.c b/src/shared/libosmocore/tests/sms/sms_test.c
index 6df4b623..776c45a1 100644
--- a/src/shared/libosmocore/tests/sms/sms_test.c
+++ b/src/shared/libosmocore/tests/sms/sms_test.c
@@ -22,10 +22,20 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <osmocom/core/msgb.h>
+
+#include <osmocom/gsm/protocol/gsm_03_40.h>
+
#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/gsm0411_utils.h>
+
+#include <osmocom/core/msgb.h>
#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/application.h>
+
+struct log_info fake_log_info = {};
+
struct test_case {
const uint8_t *input;
const uint16_t input_length;
@@ -116,14 +126,14 @@ static const uint8_t concatenated_part2_enc[] = {
static const struct test_case test_multiple_encode[] =
{
{
- .input = concatenated_text,
+ .input = (const uint8_t *) concatenated_text,
.expected = concatenated_part1_enc,
.expected_octet_length = sizeof(concatenated_part1_enc),
.expected_septet_length = concatenated_part1_septet_length,
.ud_hdr_ind = 1,
},
{
- .input = concatenated_text,
+ .input = (const uint8_t *) concatenated_text,
.expected = concatenated_part2_enc,
.expected_octet_length = sizeof(concatenated_part2_enc),
.expected_septet_length = concatenated_part2_septet_length,
@@ -134,28 +144,28 @@ static const struct test_case test_multiple_encode[] =
static const struct test_case test_encode[] =
{
{
- .input = simple_text,
+ .input = (const uint8_t *) simple_text,
.expected = simple_enc,
.expected_octet_length = sizeof(simple_enc),
.expected_septet_length = simple_septet_length,
.ud_hdr_ind = 0,
},
{
- .input = escape_text,
+ .input = (const uint8_t *) escape_text,
.expected = escape_enc,
.expected_octet_length = sizeof(escape_enc),
.expected_septet_length = escape_septet_length,
.ud_hdr_ind = 0,
},
{
- .input = enhanced_text,
+ .input = (const uint8_t *) enhanced_text,
.expected = enhanced_enc,
.expected_octet_length = sizeof(enhanced_enc),
.expected_septet_length = enhanced_septet_length,
.ud_hdr_ind = 0,
},
{
- .input = enhancedV2_text,
+ .input = (const uint8_t *) enhancedV2_text,
.expected = enhancedV2_enc,
.expected_octet_length = sizeof(enhancedV2_enc),
.expected_septet_length = enhancedV2_septet_length,
@@ -168,89 +178,166 @@ static const struct test_case test_decode[] =
{
.input = simple_enc,
.input_length = sizeof(simple_enc),
- .expected = simple_text,
+ .expected = (const uint8_t *) simple_text,
.expected_septet_length = simple_septet_length,
.ud_hdr_ind = 0,
},
{
.input = escape_enc,
.input_length = sizeof(escape_enc),
- .expected = escape_text,
+ .expected = (const uint8_t *) escape_text,
.expected_septet_length = escape_septet_length,
.ud_hdr_ind = 0,
},
{
.input = enhanced_enc,
.input_length = sizeof(enhanced_enc),
- .expected = enhanced_text,
+ .expected = (const uint8_t *) enhanced_text,
.expected_septet_length = enhanced_septet_length,
.ud_hdr_ind = 0,
},
{
.input = enhancedV2_enc,
.input_length = sizeof(enhancedV2_enc),
- .expected = enhancedV2_text,
+ .expected = (const uint8_t *) enhancedV2_text,
.expected_septet_length = enhancedV2_septet_length,
.ud_hdr_ind = 0,
},
{
.input = concatenated_part1_enc,
.input_length = sizeof(concatenated_part1_enc),
- .expected = splitted_text_part1,
+ .expected = (const uint8_t *) splitted_text_part1,
.expected_septet_length = concatenated_part1_septet_length_with_header,
.ud_hdr_ind = 1,
},
{
.input = concatenated_part2_enc,
.input_length = sizeof(concatenated_part2_enc),
- .expected = splitted_text_part2,
+ .expected = (const uint8_t *) splitted_text_part2,
.expected_septet_length = concatenated_part2_septet_length_with_header,
.ud_hdr_ind = 1,
},
};
+static void test_octet_return()
+{
+ char out[256];
+ int oct, septets;
+
+ printf("Encoding some tests and printing number of septets/octets\n");
+
+ septets = gsm_7bit_encode_n((uint8_t *) out, sizeof(out), "test1234", &oct);
+ printf("SEPTETS: %d OCTETS: %d\n", septets, oct);
+
+ printf("Done\n");
+}
+
+static void test_gen_oa(void)
+{
+ uint8_t oa[12];
+ int len;
+
+ printf("Testing gsm340_gen_oa\n");
+
+ /* first try... */
+ len = gsm340_gen_oa(oa, ARRAY_SIZE(oa), GSM340_TYPE_UNKNOWN,
+ GSM340_PLAN_ISDN, "12345678901234567891");
+ OSMO_ASSERT(len == 12);
+ printf("Result: len(%d) data(%s)\n", len, osmo_hexdump(oa, len));
+ len = gsm340_gen_oa(oa, ARRAY_SIZE(oa), GSM340_TYPE_NATIONAL,
+ GSM340_PLAN_ISDN, "12345678901234567891");
+ OSMO_ASSERT(len == 12);
+ printf("Result: len(%d) data(%s)\n", len, osmo_hexdump(oa, len));
+
+ /* long input.. will fail and just prints the header*/
+ len = gsm340_gen_oa(oa, ARRAY_SIZE(oa), GSM340_TYPE_INTERNATIONAL,
+ GSM340_PLAN_ISDN, "123456789123456789120");
+ OSMO_ASSERT(len == 2);
+ printf("Result: len(%d) data(%s)\n", len, osmo_hexdump(oa, len));
+
+ /* try the alpha numeric encoding */
+ len = gsm340_gen_oa(oa, ARRAY_SIZE(oa), GSM340_TYPE_ALPHA_NUMERIC,
+ GSM340_PLAN_UNKNOWN, "OpenBSC");
+ OSMO_ASSERT(len == 9);
+ printf("Result: len(%d) data(%s)\n", len, osmo_hexdump(oa, len));
+
+ /* long alpha numeric text */
+ len = gsm340_gen_oa(oa, ARRAY_SIZE(oa), GSM340_TYPE_ALPHA_NUMERIC,
+ GSM340_PLAN_UNKNOWN, "OpenBSCabcdefghijklm");
+ OSMO_ASSERT(len == 12);
+ printf("Result: len(%d) data(%s)\n", len, osmo_hexdump(oa, len));
+}
+
int main(int argc, char** argv)
{
printf("SMS testing\n");
- struct msgb *msg;
uint8_t i;
-
+ uint16_t buffer_size;
uint8_t octet_length;
+ int octets_written;
+ uint8_t computed_octet_length;
uint8_t septet_length;
- uint8_t gsm_septet_length;
uint8_t coded[256];
uint8_t tmp[160];
uint8_t septet_data[256];
- uint8_t ud_header[6];
+ int nchars;
char result[256];
+ /* Fake logging. */
+ osmo_init_logging(&fake_log_info);
+
/* test 7-bit encoding */
for (i = 0; i < ARRAY_SIZE(test_encode); ++i) {
+ /* Test legacy function (return value only) */
+ septet_length = gsm_7bit_encode(coded,
+ (const char *) test_encode[i].input);
+ printf("Legacy encode case %d: "
+ "septet length %d (expected %d)\n"
+ , i
+ , septet_length, test_encode[i].expected_septet_length
+ );
+ OSMO_ASSERT (septet_length == test_encode[i].expected_septet_length);
+
+ /* Test new function */
memset(coded, 0x42, sizeof(coded));
- septet_length = gsm_7bit_encode(coded, test_encode[i].input);
- octet_length = gsm_get_octet_len(septet_length);
- if (octet_length != test_encode[i].expected_octet_length) {
- fprintf(stderr, "Encode case %d: Octet length failure. Got %d, expected %d\n",
- i, octet_length, test_encode[i].expected_octet_length);
- return -1;
- }
-
- if (septet_length != test_encode[i].expected_septet_length){
- fprintf(stderr, "Encode case %d: Septet length failure. Got %d, expected %d\n",
- i, septet_length, test_encode[i].expected_septet_length);
- return -1;
- }
-
- if (memcmp(coded, test_encode[i].expected, octet_length) != 0) {
- fprintf(stderr, "Encoded content does not match for case %d\n",
- i);
- return -1;
+ septet_length = gsm_7bit_encode_n(coded, sizeof(coded),
+ (const char *) test_encode[i].input,
+ &octets_written);
+ computed_octet_length = gsm_get_octet_len(septet_length);
+ printf("Encode case %d: "
+ "Octet length %d (expected %d, computed %d), "
+ "septet length %d (expected %d)\n"
+ , i
+ , octets_written, test_encode[i].expected_octet_length, computed_octet_length
+ , septet_length, test_encode[i].expected_septet_length
+ );
+
+ OSMO_ASSERT (octets_written == test_encode[i].expected_octet_length);
+ OSMO_ASSERT (octets_written == computed_octet_length);
+ OSMO_ASSERT (memcmp(coded, test_encode[i].expected, octets_written) == 0);
+ OSMO_ASSERT (septet_length == test_encode[i].expected_septet_length);
+
+ /* check buffer limiting */
+ memset(coded, 0xaa, sizeof(coded));
+
+ for (buffer_size = 0;
+ buffer_size < test_encode[i].expected_octet_length + 1
+ && buffer_size < sizeof(coded) - 1;
+ ++buffer_size)
+ {
+ gsm_7bit_encode_n(coded, buffer_size,
+ (const char *) test_encode[i].input,
+ &octets_written);
+
+ OSMO_ASSERT(octets_written <= buffer_size);
+ OSMO_ASSERT(coded[buffer_size] == 0xaa);
}
}
/* Test: encode multiple SMS */
- int number_of_septets = gsm_septet_encode(septet_data, test_multiple_encode[0].input);
+ int number_of_septets = gsm_septet_encode(septet_data, (const char *) test_multiple_encode[0].input);
+ (void) number_of_septets;
/* SMS part 1 */
memset(tmp, 0x42, sizeof(tmp));
@@ -268,11 +355,7 @@ int main(int argc, char** argv)
memset(coded, 0x42, sizeof(coded));
memcpy(coded, tmp, octet_length + 6);
- if (memcmp(coded, test_multiple_encode[0].expected, octet_length) != 0) {
- fprintf(stderr, "Multiple-SMS encoded content does not match for part 1\n");
- return -1;
- }
-
+ OSMO_ASSERT(memcmp(coded, test_multiple_encode[0].expected, octet_length) == 0);
/* SMS part 2 */
memset(tmp, 0x42, sizeof(tmp));
@@ -290,30 +373,48 @@ int main(int argc, char** argv)
memset(coded, 0x42, sizeof(coded));
memcpy(coded, tmp, octet_length + 6);
- if (memcmp(coded, test_multiple_encode[1].expected, octet_length) != 0) {
- fprintf(stderr, "Multiple-SMS encoded content does not match for part 2\n");
- return -1;
- }
-
-
+ OSMO_ASSERT(memcmp(coded, test_multiple_encode[1].expected, octet_length) == 0);
/* test 7-bit decoding */
for (i = 0; i < ARRAY_SIZE(test_decode); ++i) {
- memset(result, 0x42, sizeof(coded));
- septet_length = gsm_7bit_decode_hdr(result, test_decode[i].input,
+ /* Test legacy function (return value only) */
+ if (!test_decode[i].ud_hdr_ind) {
+ nchars = gsm_7bit_decode(result, test_decode[i].input,
+ test_decode[i].expected_septet_length);
+ printf("Legacy decode case %d: "
+ "return value %d (expected %d)\n",
+ i, nchars, test_decode[i].expected_septet_length);
+ }
+
+ /* Test new function */
+ memset(result, 0x42, sizeof(result));
+ nchars = gsm_7bit_decode_n_hdr(result, sizeof(result), test_decode[i].input,
test_decode[i].expected_septet_length, test_decode[i].ud_hdr_ind);
+ printf("Decode case %d: return value %d (expected %zu)\n", i, nchars, strlen(result));
- if (strcmp(result, test_decode[i].expected) != 0) {
- fprintf(stderr, "Test case %d failed to decode.\n", i);
- return -1;
- }
- if (septet_length != test_decode[i].expected_septet_length) {
- fprintf(stderr, "Decode case %d: Septet length failure. Got %d, expected %d\n",
- i, septet_length, test_decode[i].expected_septet_length);
- return -1;
+ OSMO_ASSERT(strcmp(result, (const char *) test_decode[i].expected) == 0);
+ OSMO_ASSERT(nchars == strlen(result));
+
+ /* check buffer limiting */
+ memset(result, 0xaa, sizeof(result));
+
+ for (buffer_size = 1;
+ buffer_size < test_decode[i].expected_septet_length + 1
+ && buffer_size < sizeof(result) - 1;
+ ++buffer_size)
+ {
+ nchars = gsm_7bit_decode_n_hdr(result, buffer_size, test_decode[i].input,
+ test_decode[i].expected_septet_length, test_decode[i].ud_hdr_ind);
+
+ OSMO_ASSERT(nchars <= buffer_size);
+ OSMO_ASSERT(result[buffer_size] == (char)0xaa);
+ OSMO_ASSERT(result[nchars] == '\0');
}
}
+ test_octet_return();
+ test_gen_oa();
+
printf("OK\n");
return 0;
}
diff --git a/src/shared/libosmocore/tests/sms/sms_test.ok b/src/shared/libosmocore/tests/sms/sms_test.ok
index d0e09838..fa536eaa 100644
--- a/src/shared/libosmocore/tests/sms/sms_test.ok
+++ b/src/shared/libosmocore/tests/sms/sms_test.ok
@@ -1,2 +1,29 @@
SMS testing
+Legacy encode case 0: septet length 9 (expected 9)
+Encode case 0: Octet length 8 (expected 8, computed 8), septet length 9 (expected 9)
+Legacy encode case 1: septet length 41 (expected 41)
+Encode case 1: Octet length 36 (expected 36, computed 36), septet length 41 (expected 41)
+Legacy encode case 2: septet length 39 (expected 39)
+Encode case 2: Octet length 35 (expected 35, computed 35), septet length 39 (expected 39)
+Legacy encode case 3: septet length 40 (expected 40)
+Encode case 3: Octet length 35 (expected 35, computed 35), septet length 40 (expected 40)
+Legacy decode case 0: return value 9 (expected 9)
+Decode case 0: return value 9 (expected 9)
+Legacy decode case 1: return value 41 (expected 41)
+Decode case 1: return value 40 (expected 40)
+Legacy decode case 2: return value 39 (expected 39)
+Decode case 2: return value 31 (expected 31)
+Legacy decode case 3: return value 40 (expected 40)
+Decode case 3: return value 32 (expected 32)
+Decode case 4: return value 153 (expected 153)
+Decode case 5: return value 40 (expected 40)
+Encoding some tests and printing number of septets/octets
+SEPTETS: 8 OCTETS: 7
+Done
+Testing gsm340_gen_oa
+Result: len(12) data(14 81 21 43 65 87 09 21 43 65 87 19 )
+Result: len(12) data(14 a1 21 43 65 87 09 21 43 65 87 19 )
+Result: len(2) data(00 91 )
+Result: len(9) data(0e d0 4f 78 d9 2d 9c 0e 01 )
+Result: len(12) data(14 d0 4f 78 d9 2d 9c 0e c3 e2 31 19 )
OK
diff --git a/src/shared/libosmocore/tests/smscb/gsm0341_test.c b/src/shared/libosmocore/tests/smscb/gsm0341_test.c
new file mode 100644
index 00000000..c400f5c8
--- /dev/null
+++ b/src/shared/libosmocore/tests/smscb/gsm0341_test.c
@@ -0,0 +1,73 @@
+/*
+ * (C) 2014 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <osmocom/gsm/protocol/gsm_03_41.h>
+#include <osmocom/gsm/gsm0341.h>
+#include <osmocom/gsm/gsm_utils.h>
+#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 *cbmsg;
+ int text_len = strlen(text);
+ /* assuming default GSM alphabet, the encoded payload cannot be
+ * longer than the input text */
+ uint8_t payload[text_len];
+ int payload_octets;
+
+ 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);
+
+ printf("%s\n", osmo_hexdump_nospc((uint8_t *)cbmsg, sizeof(*cbmsg)+payload_octets));
+
+ return cbmsg;
+}
+
+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];
+
+ if (argc > 1)
+ msg_id = atoi(argv[1]);
+
+ if (argc > 2)
+ text = argv[2];
+
+ strncpy(tbuf, text, GSM341_MAX_CHARS);
+ if (strlen(text) < GSM341_MAX_CHARS)
+ memset(tbuf+strlen(text), GSM341_7BIT_PADDING,
+ sizeof(tbuf)-strlen(text));
+ tbuf[GSM341_MAX_CHARS] = 0;
+
+ gen_msg_from_text(msg_id, tbuf);
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/shared/libosmocore/tests/smscb/smscb_test.c b/src/shared/libosmocore/tests/smscb/smscb_test.c
index e10e12d8..5925f69b 100644
--- a/src/shared/libosmocore/tests/smscb/smscb_test.c
+++ b/src/shared/libosmocore/tests/smscb/smscb_test.c
@@ -21,6 +21,7 @@
#include <osmocom/gsm/protocol/gsm_03_41.h>
#include <stdio.h>
+#include <arpa/inet.h>
static uint8_t smscb_msg[] = { 0x40, 0x10, 0x05, 0x0d, 0x01, 0x11 };
diff --git a/src/shared/libosmocore/tests/stats/stats_test.c b/src/shared/libosmocore/tests/stats/stats_test.c
new file mode 100644
index 00000000..75ddf181
--- /dev/null
+++ b/src/shared/libosmocore/tests/stats/stats_test.c
@@ -0,0 +1,462 @@
+/* tests for statistics */
+/*
+ * (C) 2015 Sysmocom s.m.f.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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/stat_item.h>
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/stats.h>
+
+#include <stdio.h>
+
+enum test_ctr {
+ TEST_A_CTR,
+ TEST_B_CTR,
+};
+
+static const struct rate_ctr_desc ctr_description[] = {
+ [TEST_A_CTR] = { "ctr.a", "The A counter value"},
+ [TEST_B_CTR] = { "ctr.b", "The B counter value"},
+};
+
+static const struct rate_ctr_group_desc ctrg_desc = {
+ .group_name_prefix = "ctr-test.one",
+ .group_description = "Counter test number 1",
+ .num_ctr = ARRAY_SIZE(ctr_description),
+ .ctr_desc = ctr_description,
+ .class_id = OSMO_STATS_CLASS_SUBSCRIBER,
+};
+
+enum test_items {
+ TEST_A_ITEM,
+ TEST_B_ITEM,
+};
+
+static const struct osmo_stat_item_desc item_description[] = {
+ [TEST_A_ITEM] = { "item.a", "The A value", "ma", 4, -1 },
+ [TEST_B_ITEM] = { "item.b", "The B value", "kb", 7, -1 },
+};
+
+static const struct osmo_stat_item_group_desc statg_desc = {
+ .group_name_prefix = "test.one",
+ .group_description = "Test number 1",
+ .num_items = ARRAY_SIZE(item_description),
+ .item_desc = item_description,
+ .class_id = OSMO_STATS_CLASS_PEER,
+};
+
+static void stat_test(void)
+{
+ struct osmo_stat_item_group *statg =
+ osmo_stat_item_group_alloc(NULL, &statg_desc, 0);
+
+ struct osmo_stat_item_group *sgrp2;
+ const struct osmo_stat_item *sitem1, *sitem2;
+ int rc;
+ int32_t value;
+ int32_t rd_a = 0;
+ int32_t rd_b = 0;
+ int i;
+
+ OSMO_ASSERT(statg != NULL);
+
+ sgrp2 = osmo_stat_item_get_group_by_name_idx("test.one", 0);
+ OSMO_ASSERT(sgrp2 == statg);
+
+ sgrp2 = osmo_stat_item_get_group_by_name_idx("test.one", 1);
+ OSMO_ASSERT(sgrp2 == NULL);
+
+ sgrp2 = osmo_stat_item_get_group_by_name_idx("test.two", 0);
+ OSMO_ASSERT(sgrp2 == NULL);
+
+ sitem1 = osmo_stat_item_get_by_name(statg, "item.c");
+ OSMO_ASSERT(sitem1 == NULL);
+
+ sitem1 = osmo_stat_item_get_by_name(statg, "item.a");
+ OSMO_ASSERT(sitem1 != NULL);
+ OSMO_ASSERT(sitem1 == statg->items[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]);
+
+ 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], &rd_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], &rd_a, &value);
+ OSMO_ASSERT(rc > 0);
+ OSMO_ASSERT(value == 1);
+
+ rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
+ OSMO_ASSERT(rc == 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], &rd_a, &value);
+ OSMO_ASSERT(rc > 0);
+ OSMO_ASSERT(value == i);
+
+ rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value);
+ OSMO_ASSERT(rc > 0);
+ OSMO_ASSERT(value == 1000 + i);
+ }
+
+ /* 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], &rd_a, &value);
+ OSMO_ASSERT(rc > 0);
+ OSMO_ASSERT(value == i-1);
+
+ rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value);
+ OSMO_ASSERT(rc > 0);
+ OSMO_ASSERT(value == 1000 + i-1);
+ }
+
+ rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
+ OSMO_ASSERT(rc > 0);
+ OSMO_ASSERT(value == 64);
+
+ rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value);
+ OSMO_ASSERT(rc > 0);
+ 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);
+ }
+
+ rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
+ OSMO_ASSERT(rc > 0);
+ OSMO_ASSERT(value == 93);
+
+ for (i = 94; i <= 96; i++) {
+ rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
+ OSMO_ASSERT(rc > 0);
+ OSMO_ASSERT(value == i);
+ }
+
+ rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value);
+ OSMO_ASSERT(rc > 0);
+ OSMO_ASSERT(value == 1000 + 90);
+
+ for (i = 91; i <= 96; i++) {
+ rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value);
+ OSMO_ASSERT(rc > 0);
+ 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], &rd_a);
+ OSMO_ASSERT(rc > 0);
+
+ rc = osmo_stat_item_discard(statg->items[TEST_A_ITEM], &rd_a);
+ OSMO_ASSERT(rc == 0);
+
+ rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_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], &rd_a, &value);
+ OSMO_ASSERT(rc > 0);
+ OSMO_ASSERT(value == 98);
+
+ rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
+ OSMO_ASSERT(rc == 0);
+
+ /* Test Discard (all items) */
+ osmo_stat_item_set(statg->items[TEST_A_ITEM], 99);
+ osmo_stat_item_set(statg->items[TEST_A_ITEM], 100);
+ osmo_stat_item_set(statg->items[TEST_A_ITEM], 101);
+ osmo_stat_item_set(statg->items[TEST_B_ITEM], 99);
+ osmo_stat_item_set(statg->items[TEST_B_ITEM], 100);
+
+ rc = osmo_stat_item_discard_all(&rd_a);
+ rc = osmo_stat_item_discard_all(&rd_b);
+
+ rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
+ OSMO_ASSERT(rc == 0);
+ rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value);
+ OSMO_ASSERT(rc == 0);
+
+ osmo_stat_item_group_free(statg);
+
+ sgrp2 = osmo_stat_item_get_group_by_name_idx("test.one", 0);
+ OSMO_ASSERT(sgrp2 == NULL);
+}
+
+/*** stats reporter tests ***/
+
+/* define a special stats reporter for testing */
+
+static int send_count;
+
+enum {
+ OSMO_STATS_REPORTER_TEST = OSMO_STATS_REPORTER_LOG + 1,
+};
+
+static int stats_reporter_test_send_counter(struct osmo_stats_reporter *srep,
+ const struct rate_ctr_group *ctrg,
+ const struct rate_ctr_desc *desc,
+ int64_t value, int64_t delta)
+{
+ const char *group_name = ctrg ? ctrg->desc->group_name_prefix : "";
+
+ printf(" %s: counter p=%s g=%s i=%u n=%s v=%lld d=%lld\n",
+ srep->name,
+ srep->name_prefix ? srep->name_prefix : "",
+ group_name, ctrg ? ctrg->idx : 0,
+ desc->name, (long long)value, (long long)delta);
+
+ send_count += 1;
+ return 0;
+}
+
+static int stats_reporter_test_send_item(struct osmo_stats_reporter *srep,
+ const struct osmo_stat_item_group *statg,
+ const struct osmo_stat_item_desc *desc, int value)
+{
+ printf(" %s: item p=%s g=%s i=%u n=%s v=%d u=%s\n",
+ srep->name,
+ srep->name_prefix ? srep->name_prefix : "",
+ statg->desc->group_name_prefix, statg->idx,
+ desc->name, value, desc->unit ? desc->unit : "");
+
+ send_count += 1;
+ return 0;
+}
+
+static int stats_reporter_test_open(struct osmo_stats_reporter *srep)
+{
+ printf(" %s: open\n", srep->name);
+ return 0;
+}
+
+static int stats_reporter_test_close(struct osmo_stats_reporter *srep)
+{
+ printf(" %s: close\n", srep->name);
+ return 0;
+}
+
+static struct osmo_stats_reporter *stats_reporter_create_test(const char *name)
+{
+ struct osmo_stats_reporter *srep;
+ srep = osmo_stats_reporter_alloc(OSMO_STATS_REPORTER_TEST, name);
+
+ srep->have_net_config = 0;
+
+ srep->open = stats_reporter_test_open;
+ srep->close = stats_reporter_test_close;
+ srep->send_counter = stats_reporter_test_send_counter;
+ srep->send_item = stats_reporter_test_send_item;
+
+ return srep;
+}
+
+
+static void test_reporting()
+{
+ struct osmo_stats_reporter *srep1, *srep2, *srep;
+ struct osmo_stat_item_group *statg1, *statg2;
+ struct rate_ctr_group *ctrg1, *ctrg2;
+ void *stats_ctx = talloc_named_const(NULL, 1, "stats test context");
+
+ int rc;
+
+ printf("Start test: %s\n", __func__);
+
+ /* Allocate counters and items */
+ statg1 = osmo_stat_item_group_alloc(stats_ctx, &statg_desc, 1);
+ OSMO_ASSERT(statg1 != NULL);
+ statg2 = osmo_stat_item_group_alloc(stats_ctx, &statg_desc, 2);
+ OSMO_ASSERT(statg2 != NULL);
+ ctrg1 = rate_ctr_group_alloc(stats_ctx, &ctrg_desc, 1);
+ OSMO_ASSERT(ctrg1 != NULL);
+ ctrg2 = rate_ctr_group_alloc(stats_ctx, &ctrg_desc, 2);
+ OSMO_ASSERT(ctrg2 != NULL);
+
+ srep1 = stats_reporter_create_test("test1");
+ OSMO_ASSERT(srep1 != NULL);
+
+ srep2 = stats_reporter_create_test("test2");
+ OSMO_ASSERT(srep2 != NULL);
+
+ srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_TEST, "test1");
+ OSMO_ASSERT(srep == srep1);
+ srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_TEST, "test2");
+ OSMO_ASSERT(srep == srep2);
+
+ rc = osmo_stats_reporter_enable(srep1);
+ OSMO_ASSERT(rc >= 0);
+ OSMO_ASSERT(srep1->force_single_flush);
+ rc = osmo_stats_reporter_set_max_class(srep1, OSMO_STATS_CLASS_SUBSCRIBER);
+ OSMO_ASSERT(rc >= 0);
+
+ rc = osmo_stats_reporter_enable(srep2);
+ OSMO_ASSERT(rc >= 0);
+ OSMO_ASSERT(srep2->force_single_flush);
+ rc = osmo_stats_reporter_set_max_class(srep2, OSMO_STATS_CLASS_SUBSCRIBER);
+ OSMO_ASSERT(rc >= 0);
+
+ printf("report (initial):\n");
+ send_count = 0;
+ osmo_stats_report();
+ OSMO_ASSERT(send_count == 16);
+
+ printf("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 == 8);
+
+ printf("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 == 12);
+
+ printf("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 == 16);
+
+ printf("report (srep2 disabled):\n");
+ /* force single flush */
+ srep1->force_single_flush = 1;
+ 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 == 8);
+
+ printf("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 == 8);
+
+ printf("report (should be empty):\n");
+ send_count = 0;
+ osmo_stats_report();
+ OSMO_ASSERT(send_count == 0);
+
+ printf("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);
+
+ printf("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);
+
+ printf("report (remove statg1, ctrg1):\n");
+ /* force single flush */
+ srep1->force_single_flush = 1;
+ 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 == 8);
+
+ printf("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 == 4);
+
+ printf("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 == 2);
+
+ printf("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);
+
+ printf("report (remove ctrg2, should be empty):\n");
+ rate_ctr_group_free(ctrg2);
+ send_count = 0;
+ osmo_stats_report();
+ OSMO_ASSERT(send_count == 0);
+
+ /* Leak check */
+ OSMO_ASSERT(talloc_total_blocks(stats_ctx) == 1);
+ talloc_free(stats_ctx);
+
+ printf("End test: %s\n", __func__);
+}
+
+int main(int argc, char **argv)
+{
+ static const struct log_info log_info = {};
+ log_init(&log_info, NULL);
+
+ osmo_stat_item_init(NULL);
+
+ stat_test();
+ test_reporting();
+ return 0;
+}
diff --git a/src/shared/libosmocore/tests/stats/stats_test.ok b/src/shared/libosmocore/tests/stats/stats_test.ok
new file mode 100644
index 00000000..a0c001b9
--- /dev/null
+++ b/src/shared/libosmocore/tests/stats/stats_test.ok
@@ -0,0 +1,108 @@
+Start test: test_reporting
+ test1: open
+ test2: open
+report (initial):
+ 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.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=2 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.a v=0 d=0
+ test2: counter p= g=ctr-test.one i=1 n=ctr.b 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
+ 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
+ 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
+ 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
+ test1: item p= g=test.one i=1 n=item.b v=-1 u=kb
+report (srep1 global):
+ 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
+ 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
+ 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
+report (srep1 peer):
+ 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
+ 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
+ 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
+ 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
+ test1: item p= g=test.one i=1 n=item.b v=-1 u=kb
+report (srep1 subscriber):
+ 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.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=2 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.a v=0 d=0
+ test2: counter p= g=ctr-test.one i=1 n=ctr.b 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
+ 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
+ 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
+ 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
+ test1: item p= g=test.one i=1 n=item.b v=-1 u=kb
+report (srep2 disabled):
+ test2: close
+ test1: 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
+ test1: 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
+ test1: 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
+ 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
+report (srep2 enabled, no flush forced):
+ test2: open
+ 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
+ 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
+ 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
+report (should be empty):
+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
+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 (remove statg1, ctrg1):
+ 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.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=2 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
+ test1: item p= g=test.one i=2 n=item.b v=-1 u=kb
+report (remove srep1):
+ test1: close
+ 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
+ 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
+report (remove statg2):
+ 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
+report (remove srep2):
+ test2: close
+report (remove ctrg2, should be empty):
+End test: test_reporting
diff --git a/src/shared/libosmocore/tests/strrb/strrb_test.c b/src/shared/libosmocore/tests/strrb/strrb_test.c
new file mode 100644
index 00000000..6140ac9b
--- /dev/null
+++ b/src/shared/libosmocore/tests/strrb/strrb_test.c
@@ -0,0 +1,225 @@
+/* (C) 2012-2013 by Katerina Barone-Adesi <kat.obsc@gmail.com>
+ * All Rights Reserved
+ *
+ * This program is iree 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.
+ *
+ * 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 <string.h>
+
+#include <osmocom/core/strrb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+
+struct osmo_strrb *rb0, *rb1, *rb2, *rb3, *rb4, *rb5;
+
+#define STR0 "hello"
+#define STR1 "a"
+#define STR2 "world"
+#define STR3 "sky"
+#define STR4 "moon"
+
+#define TESTSIZE 3
+
+void init_rbs(void)
+{
+ rb0 = osmo_strrb_create(NULL, TESTSIZE);
+
+ rb1 = osmo_strrb_create(NULL, TESTSIZE);
+ osmo_strrb_add(rb1, STR0);
+
+ rb2 = osmo_strrb_create(NULL, TESTSIZE);
+ osmo_strrb_add(rb2, STR0);
+ osmo_strrb_add(rb2, STR1);
+
+ rb3 = osmo_strrb_create(NULL, TESTSIZE);
+ osmo_strrb_add(rb3, STR0);
+ osmo_strrb_add(rb3, STR1);
+ osmo_strrb_add(rb3, STR2);
+
+ rb4 = osmo_strrb_create(NULL, TESTSIZE);
+ osmo_strrb_add(rb4, STR0);
+ osmo_strrb_add(rb4, STR1);
+ osmo_strrb_add(rb4, STR2);
+ osmo_strrb_add(rb4, STR3);
+
+ rb5 = osmo_strrb_create(NULL, TESTSIZE);
+ osmo_strrb_add(rb5, STR0);
+ osmo_strrb_add(rb5, STR1);
+ osmo_strrb_add(rb5, STR2);
+ osmo_strrb_add(rb5, STR3);
+ osmo_strrb_add(rb5, STR4);
+}
+
+void free_rbs(void)
+{
+ talloc_free(rb0);
+ talloc_free(rb1);
+ talloc_free(rb2);
+ talloc_free(rb3);
+ talloc_free(rb4);
+ talloc_free(rb5);
+}
+
+void test_offset_valid(void)
+{
+ OSMO_ASSERT(_osmo_strrb_is_bufindex_valid(rb1, 0));
+ OSMO_ASSERT(!_osmo_strrb_is_bufindex_valid(rb1, 1));
+ OSMO_ASSERT(!_osmo_strrb_is_bufindex_valid(rb1, 2));
+
+ OSMO_ASSERT(!_osmo_strrb_is_bufindex_valid(rb3, 0));
+ OSMO_ASSERT(_osmo_strrb_is_bufindex_valid(rb3, 1));
+ OSMO_ASSERT(_osmo_strrb_is_bufindex_valid(rb3, 2));
+
+ OSMO_ASSERT(_osmo_strrb_is_bufindex_valid(rb4, 0));
+ OSMO_ASSERT(!_osmo_strrb_is_bufindex_valid(rb4, 1));
+ OSMO_ASSERT(_osmo_strrb_is_bufindex_valid(rb4, 2));
+
+ OSMO_ASSERT(_osmo_strrb_is_bufindex_valid(rb5, 0));
+ OSMO_ASSERT(_osmo_strrb_is_bufindex_valid(rb5, 1));
+ OSMO_ASSERT(!_osmo_strrb_is_bufindex_valid(rb5, 2));
+}
+
+void test_elems(void)
+{
+ OSMO_ASSERT(osmo_strrb_elements(rb0) == 0);
+ OSMO_ASSERT(osmo_strrb_elements(rb1) == 1);
+ OSMO_ASSERT(osmo_strrb_elements(rb2) == 2);
+ OSMO_ASSERT(osmo_strrb_elements(rb3) == 2);
+}
+
+void test_getn(void)
+{
+ OSMO_ASSERT(!osmo_strrb_get_nth(rb0, 0));
+ OSMO_ASSERT(!strcmp(STR0, osmo_strrb_get_nth(rb2, 0)));
+ OSMO_ASSERT(!strcmp(STR1, osmo_strrb_get_nth(rb2, 1)));
+ OSMO_ASSERT(!strcmp(STR1, osmo_strrb_get_nth(rb3, 0)));
+ OSMO_ASSERT(!strcmp(STR2, osmo_strrb_get_nth(rb3, 1)));
+ OSMO_ASSERT(!osmo_strrb_get_nth(rb3, 2));
+}
+
+void test_getn_wrap(void)
+{
+ OSMO_ASSERT(!strcmp(STR2, osmo_strrb_get_nth(rb4, 0)));
+ OSMO_ASSERT(!strcmp(STR3, osmo_strrb_get_nth(rb4, 1)));
+
+ OSMO_ASSERT(!strcmp(STR3, osmo_strrb_get_nth(rb5, 0)));
+ OSMO_ASSERT(!strcmp(STR4, osmo_strrb_get_nth(rb5, 1)));
+}
+
+void test_add(void)
+{
+ struct osmo_strrb *rb = osmo_strrb_create(NULL, 4);
+ OSMO_ASSERT(rb->start == 0);
+ OSMO_ASSERT(rb->end == 0);
+
+ osmo_strrb_add(rb, "a");
+ osmo_strrb_add(rb, "b");
+ osmo_strrb_add(rb, "c");
+ OSMO_ASSERT(rb->start == 0);
+ OSMO_ASSERT(rb->end == 3);
+ OSMO_ASSERT(osmo_strrb_elements(rb) == 3);
+
+ osmo_strrb_add(rb, "d");
+ OSMO_ASSERT(rb->start == 1);
+ OSMO_ASSERT(rb->end == 0);
+ OSMO_ASSERT(osmo_strrb_elements(rb) == 3);
+ OSMO_ASSERT(!strcmp("b", osmo_strrb_get_nth(rb, 0)));
+ OSMO_ASSERT(!strcmp("c", osmo_strrb_get_nth(rb, 1)));
+ OSMO_ASSERT(!strcmp("d", osmo_strrb_get_nth(rb, 2)));
+
+ osmo_strrb_add(rb, "e");
+ OSMO_ASSERT(rb->start == 2);
+ OSMO_ASSERT(rb->end == 1);
+ OSMO_ASSERT(!strcmp("c", osmo_strrb_get_nth(rb, 0)));
+ OSMO_ASSERT(!strcmp("d", osmo_strrb_get_nth(rb, 1)));
+ OSMO_ASSERT(!strcmp("e", osmo_strrb_get_nth(rb, 2)));
+
+ osmo_strrb_add(rb, "f");
+ OSMO_ASSERT(rb->start == 3);
+ OSMO_ASSERT(rb->end == 2);
+ OSMO_ASSERT(!strcmp("d", osmo_strrb_get_nth(rb, 0)));
+ OSMO_ASSERT(!strcmp("e", osmo_strrb_get_nth(rb, 1)));
+ OSMO_ASSERT(!strcmp("f", osmo_strrb_get_nth(rb, 2)));
+
+ osmo_strrb_add(rb, "g");
+ OSMO_ASSERT(rb->start == 0);
+ OSMO_ASSERT(rb->end == 3);
+ OSMO_ASSERT(!strcmp("e", osmo_strrb_get_nth(rb, 0)));
+ OSMO_ASSERT(!strcmp("f", osmo_strrb_get_nth(rb, 1)));
+ OSMO_ASSERT(!strcmp("g", osmo_strrb_get_nth(rb, 2)));
+
+ osmo_strrb_add(rb, "h");
+ OSMO_ASSERT(rb->start == 1);
+ OSMO_ASSERT(rb->end == 0);
+ OSMO_ASSERT(!strcmp("f", osmo_strrb_get_nth(rb, 0)));
+ OSMO_ASSERT(!strcmp("g", osmo_strrb_get_nth(rb, 1)));
+ OSMO_ASSERT(!strcmp("h", osmo_strrb_get_nth(rb, 2)));
+
+ talloc_free(rb);
+}
+
+void test_long_msg(void)
+{
+ struct osmo_strrb *rb = osmo_strrb_create(NULL, 2);
+ int test_size = RB_MAX_MESSAGE_SIZE + 7;
+ char *tests1, *tests2;
+ const char *rb_content;
+ int i;
+
+ tests1 = malloc(test_size);
+ tests2 = malloc(test_size);
+ /* Be certain allocating memory worked before continuing */
+ OSMO_ASSERT(tests1);
+ OSMO_ASSERT(tests2);
+
+ for (i = 0; i < RB_MAX_MESSAGE_SIZE; i += 2) {
+ tests1[i] = 'a';
+ tests1[i + 1] = 'b';
+ }
+ tests1[i] = '\0';
+
+ osmo_strrb_add(rb, tests1);
+ strcpy(tests2, tests1);
+
+ /* Verify that no stale data from test1 is lingering... */
+ bzero(tests1, test_size);
+ free(tests1);
+
+ rb_content = osmo_strrb_get_nth(rb, 0);
+ OSMO_ASSERT(!strncmp(tests2, rb_content, RB_MAX_MESSAGE_SIZE - 1));
+ OSMO_ASSERT(!rb_content[RB_MAX_MESSAGE_SIZE - 1]);
+ OSMO_ASSERT(strlen(rb_content) == RB_MAX_MESSAGE_SIZE - 1);
+
+ free(tests2);
+ talloc_free(rb);
+}
+
+int main(int argc, char **argv)
+{
+ init_rbs();
+ test_offset_valid();
+ test_elems();
+ test_getn();
+ test_getn_wrap();
+ test_add();
+ test_long_msg();
+ printf("All tests passed\n");
+
+ free_rbs();
+ return 0;
+}
diff --git a/src/shared/libosmocore/tests/strrb/strrb_test.ok b/src/shared/libosmocore/tests/strrb/strrb_test.ok
new file mode 100644
index 00000000..9ac5124e
--- /dev/null
+++ b/src/shared/libosmocore/tests/strrb/strrb_test.ok
@@ -0,0 +1 @@
+All tests passed
diff --git a/src/shared/libosmocore/tests/testsuite.at b/src/shared/libosmocore/tests/testsuite.at
index 1cfae03c..426c74cd 100644
--- a/src/shared/libosmocore/tests/testsuite.at
+++ b/src/shared/libosmocore/tests/testsuite.at
@@ -6,26 +6,49 @@ AT_BANNER([Regression tests.])
AT_SETUP([a5])
AT_KEYWORDS([a5])
cat $abs_srcdir/a5/a5_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/a5/a5_test], [], [expout])
+AT_CHECK([$abs_top_builddir/tests/a5/a5_test], [0], [expout])
AT_CLEANUP
-AT_SETUP([bssgp-fc])
-AT_KEYWORDS([bssgp-fc])
-cat $abs_srcdir/gb/bssgp_fc_tests.ok > expout
-cat $abs_srcdir/gb/bssgp_fc_tests.err > experr
-AT_CHECK([$abs_top_srcdir/tests/gb/bssgp_fc_tests.sh $abs_top_builddir/tests/gb], [], [expout], [experr])
+AT_SETUP([kasumi])
+AT_KEYWORDS([kasumi])
+cat $abs_srcdir/kasumi/kasumi_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/kasumi/kasumi_test], [0], [expout])
AT_CLEANUP
AT_SETUP([bits])
AT_KEYWORDS([bits])
cat $abs_srcdir/bits/bitrev_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/bits/bitrev_test], [], [expout])
+AT_CHECK([$abs_top_builddir/tests/bits/bitrev_test], [0], [expout])
+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_CLEANUP
+
+AT_SETUP([bitcomp])
+AT_KEYWORDS([bitcomp])
+cat $abs_srcdir/bits/bitcomp_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/bits/bitcomp_test], [0], [expout])
AT_CLEANUP
AT_SETUP([conv])
AT_KEYWORDS([conv])
cat $abs_srcdir/conv/conv_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/conv/conv_test], [], [expout])
+AT_CHECK([$abs_top_builddir/tests/conv/conv_test], [0], [expout])
+AT_CLEANUP
+
+AT_SETUP([msgb])
+AT_KEYWORDS([msgb])
+cat $abs_srcdir/msgb/msgb_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/msgb/msgb_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([gea])
+AT_KEYWORDS([gea])
+cat $abs_srcdir/gea/gea_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/gea/gea_test], [0], [expout])
AT_CLEANUP
if ENABLE_MSGFILE
@@ -33,61 +56,175 @@ AT_SETUP([msgfile])
AT_KEYWORDS([msgfile])
cp $abs_srcdir/msgfile/msgconfig.cfg .
cat $abs_srcdir/msgfile/msgfile_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/msgfile/msgfile_test], [], [expout])
+AT_CHECK([$abs_top_builddir/tests/msgfile/msgfile_test], [0], [expout])
AT_CLEANUP
endif
AT_SETUP([sms])
AT_KEYWORDS([sms])
cat $abs_srcdir/sms/sms_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/sms/sms_test], [], [expout])
+AT_CHECK([$abs_top_builddir/tests/sms/sms_test], [0], [expout])
AT_CLEANUP
AT_SETUP([smscb])
AT_KEYWORDS([smscb])
cat $abs_srcdir/smscb/smscb_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/smscb/smscb_test], [], [expout])
-AT_CLEANUP
-
-AT_SETUP([timer])
-AT_KEYWORDS([timer])
-cat $abs_srcdir/timer/timer_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/timer/timer_test -s 5], [], [expout], [ignore])
+AT_CHECK([$abs_top_builddir/tests/smscb/smscb_test], [0], [expout])
AT_CLEANUP
AT_SETUP([ussd])
AT_KEYWORDS([ussd])
cat $abs_srcdir/ussd/ussd_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/ussd/ussd_test], [], [expout], [ignore])
+AT_CHECK([$abs_top_builddir/tests/ussd/ussd_test], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([auth])
AT_KEYWORDS([auth])
cat $abs_srcdir/auth/milenage_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/auth/milenage_test], [], [expout], [ignore])
+AT_CHECK([$abs_top_builddir/tests/auth/milenage_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([comp128])
+AT_KEYWORDS([comp128])
+cat $abs_srcdir/comp128/comp128_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/comp128/comp128_test], [0], [expout])
AT_CLEANUP
AT_SETUP([lapd])
AT_KEYWORDS([lapd])
cat $abs_srcdir/lapd/lapd_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/lapd/lapd_test], [], [expout], [ignore])
+AT_CHECK([$abs_top_builddir/tests/lapd/lapd_test], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([gsm0808])
AT_KEYWORDS([gsm0808])
cat $abs_srcdir/gsm0808/gsm0808_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/gsm0808/gsm0808_test], [], [expout], [ignore])
+AT_CHECK([$abs_top_builddir/tests/gsm0808/gsm0808_test], [0], [expout], [ignore])
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], [], [expout], [ignore])
+AT_CHECK([$abs_top_builddir/tests/gsm0408/gsm0408_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([gprs])
+AT_KEYWORDS([gprs])
+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])
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], [], [expout], [experr])
+AT_CHECK([$abs_top_builddir/tests/logging/logging_test], [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([codec])
+AT_KEYWORDS([codec])
+cat $abs_srcdir/codec/codec_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/codec/codec_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([fr])
+AT_KEYWORDS([fr])
+cat $abs_srcdir/fr/fr_test.ok > expout
+cat $abs_srcdir/fr/fr_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/fr/fr_test], [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([loggingrb])
+AT_KEYWORDS([loggingrb])
+cat $abs_srcdir/loggingrb/logging_test.ok > expout
+cat $abs_srcdir/loggingrb/logging_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/loggingrb/loggingrb_test], [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([strrb])
+AT_KEYWORDS([strrb])
+cat $abs_srcdir/strrb/strrb_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/strrb/strrb_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([vty])
+AT_KEYWORDS([vty])
+cat $abs_srcdir/vty/vty_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/vty/vty_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([gprs-bssgp])
+AT_KEYWORDS([gprs-bssgp])
+cat $abs_srcdir/gb/gprs_bssgp_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/gb/gprs_bssgp_test], [0], [expout], [ignore])
+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])
+AT_CLEANUP
+
+AT_SETUP([utils])
+AT_KEYWORDS([utils])
+cat $abs_srcdir/utils/utils_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/utils/utils_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([stats])
+AT_KEYWORDS([stats])
+cat $abs_srcdir/stats/stats_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/stats/stats_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([write_queue])
+AT_KEYWORDS([write_queue])
+cat $abs_srcdir/write_queue/wqueue_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/write_queue/wqueue_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([bssgp-fc])
+AT_KEYWORDS([bssgp-fc])
+cat $abs_srcdir/gb/bssgp_fc_tests.ok > expout
+cat $abs_srcdir/gb/bssgp_fc_tests.err > experr
+AT_CHECK([$abs_top_srcdir/tests/gb/bssgp_fc_tests.sh $abs_top_builddir/tests/gb], [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([sim])
+AT_KEYWORDS([sim])
+AT_CHECK([test "x$enable_sim_test" = xyes || exit 77])
+cat $abs_srcdir/sim/sim_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/sim/sim_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([timer])
+AT_KEYWORDS([timer])
+cat $abs_srcdir/timer/timer_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/timer/timer_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([tlv])
+AT_KEYWORDS([tlv])
+cat $abs_srcdir/tlv/tlv_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/tlv/tlv_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([gsup])
+AT_KEYWORDS([gsup])
+cat $abs_srcdir/gsup/gsup_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/gsup/gsup_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([fsm])
+AT_KEYWORDS([fsm])
+cat $abs_srcdir/fsm/fsm_test.ok > expout
+cat $abs_srcdir/fsm/fsm_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/fsm/fsm_test], [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([oap])
+AT_KEYWORDS([oap])
+cat $abs_srcdir/oap/oap_test.ok > expout
+touch experr
+AT_CHECK([$abs_top_builddir/tests/oap/oap_test], [0], [expout], [experr])
AT_CLEANUP
diff --git a/src/shared/libosmocore/tests/timer/timer_test.c b/src/shared/libosmocore/tests/timer/timer_test.c
index ba3127d4..066dc72d 100644
--- a/src/shared/libosmocore/tests/timer/timer_test.c
+++ b/src/shared/libosmocore/tests/timer/timer_test.c
@@ -33,7 +33,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);
@@ -54,29 +54,30 @@ struct test_timer {
};
/* number of test steps. We add fact(steps) timers in the whole test. */
-#define MAIN_TIMER_NSTEPS 16
+#define MAIN_TIMER_NSTEPS 8
/* time between two steps, in secs. */
#define TIME_BETWEEN_STEPS 1
-/* timer imprecision that we accept for this test: 10 milliseconds. */
-#define TIMER_PRES_SECS 0
-#define TIMER_PRES_USECS 20000
+/* how much time elapses between checks, in microsecs */
+#define TIME_BETWEEN_TIMER_CHECKS 423210
static int timer_nsteps = MAIN_TIMER_NSTEPS;
static unsigned int expired_timers = 0;
static unsigned int total_timers = 0;
static unsigned int too_late = 0;
+static unsigned int too_soon = 0;
static void main_timer_fired(void *data)
{
unsigned int *step = data;
unsigned int add_in_this_step;
int i;
+ printf("main_timer_fired()\n");
if (*step == timer_nsteps) {
- fprintf(stderr, "Main timer has finished, please, "
- "wait a bit for the final report.\n");
+ printf("Main timer has finished, please, "
+ "wait a bit for the final report.\n");
return;
}
/* add 2^step pair of timers per step. */
@@ -87,18 +88,21 @@ static void main_timer_fired(void *data)
v = talloc_zero(NULL, struct test_timer);
if (v == NULL) {
- fprintf(stderr, "timer_test: OOM!\n");
+ printf("timer_test: OOM!\n");
return;
}
- gettimeofday(&v->start, NULL);
+ osmo_gettimeofday(&v->start, NULL);
v->timer.cb = secondary_timer_fired;
v->timer.data = v;
- unsigned int seconds = (random() % 10) + 1;
+ unsigned int seconds = (i & 0x7) + 1;
v->stop.tv_sec = v->start.tv_sec + seconds;
+ v->stop.tv_usec = v->start.tv_usec;
osmo_timer_schedule(&v->timer, seconds, 0);
llist_add(&v->head, &timer_test_list);
+ printf("scheduled timer at %d.%06d\n",
+ (int)v->stop.tv_sec, (int)v->stop.tv_usec);
}
- fprintf(stderr, "added %d timers in step %u (expired=%u)\n",
+ printf("added %d timers in step %u (expired=%u)\n",
add_in_this_step, *step, expired_timers);
total_timers += add_in_this_step;
osmo_timer_schedule(&main_timer, TIME_BETWEEN_STEPS, 0);
@@ -108,52 +112,66 @@ static void main_timer_fired(void *data)
static void secondary_timer_fired(void *data)
{
struct test_timer *v = data, *this, *tmp;
- struct timeval current, res, precision = { 1, 0 };
+ struct timeval current, res;
+ struct timeval precision = { 0, TIME_BETWEEN_TIMER_CHECKS + 1};
+ int i, deleted;
- gettimeofday(&current, NULL);
+ osmo_gettimeofday(&current, NULL);
timersub(&current, &v->stop, &res);
if (timercmp(&res, &precision, >)) {
- fprintf(stderr, "ERROR: timer %p has expired too late!\n",
- v->timer);
+ printf("ERROR: timer has expired too late:"
+ " wanted %d.%06d now %d.%06d diff %d.%06d\n",
+ (int)v->stop.tv_sec, (int)v->stop.tv_usec,
+ (int)current.tv_sec, (int)current.tv_usec,
+ (int)res.tv_sec, (int)res.tv_usec);
too_late++;
}
+ else if (timercmp(&current, &v->stop, <)) {
+ printf("ERROR: timer has expired too soon:"
+ " wanted %d.%06d now %d.%06d diff %d.%06d\n",
+ (int)v->stop.tv_sec, (int)v->stop.tv_usec,
+ (int)current.tv_sec, (int)current.tv_usec,
+ (int)res.tv_sec, (int)res.tv_usec);
+ too_soon++;
+ }
+ else
+ printf("timer fired on time: %d.%06d (+ %d.%06d)\n",
+ (int)v->stop.tv_sec, (int)v->stop.tv_usec,
+ (int)res.tv_sec, (int)res.tv_usec);
llist_del(&v->head);
talloc_free(data);
expired_timers++;
if (expired_timers == total_timers) {
- fprintf(stdout, "test over: added=%u expired=%u too_late=%u \n",
- total_timers, expired_timers, too_late);
+ printf("test over: added=%u expired=%u too_soon=%u too_late=%u\n",
+ total_timers, expired_timers, too_soon, too_late);
exit(EXIT_SUCCESS);
}
- /* randomly (10%) deletion of timers. */
+ /* "random" deletion of timers. */
+ i = 0;
+ deleted = 0;
llist_for_each_entry_safe(this, tmp, &timer_test_list, head) {
- if ((random() % 100) < 10) {
+ i ++;
+ if (!(i & 0x3)) {
osmo_timer_del(&this->timer);
llist_del(&this->head);
talloc_free(this);
- expired_timers++;
+ deleted++;
}
}
-}
-
-static void alarm_handler(int signum)
-{
- fprintf(stderr, "ERROR: We took too long to run the timer test, "
- "something seems broken, aborting.\n");
- exit(EXIT_FAILURE);
+ expired_timers += deleted;
+ printf("early deleted %d timers, %d still active\n", deleted,
+ total_timers - expired_timers);
}
int main(int argc, char *argv[])
{
int c;
+ int steps;
- if (signal(SIGALRM, alarm_handler) == SIG_ERR) {
- perror("cannot register signal handler");
- exit(EXIT_FAILURE);
- }
+ osmo_gettimeofday_override = true;
while ((c = getopt_long(argc, argv, "s:", NULL, NULL)) != -1) {
switch(c) {
@@ -170,23 +188,25 @@ int main(int argc, char *argv[])
}
}
- fprintf(stdout, "Running timer test for %u steps, accepting "
- "imprecision of %u.%.6u seconds\n",
- timer_nsteps, TIMER_PRES_SECS, TIMER_PRES_USECS);
+ steps = ((MAIN_TIMER_NSTEPS * TIME_BETWEEN_STEPS + 20) * 1e6)
+ / TIME_BETWEEN_TIMER_CHECKS;
- osmo_timer_schedule(&main_timer, 1, 0);
+ printf("Running timer test for %d iterations,"
+ " %d steps of %d msecs each\n",
+ timer_nsteps, steps, TIME_BETWEEN_TIMER_CHECKS / 1000);
- /* if the test takes too long, we may consider that the timer scheduler
- * has hung. We set some maximum wait time which is the double of the
- * maximum timeout randomly set (10 seconds, worst case) plus the
- * number of steps (since some of them are reset each step). */
- alarm(2 * (10 + timer_nsteps));
+ osmo_timer_schedule(&main_timer, 1, 0);
#ifdef HAVE_SYS_SELECT_H
- while (1) {
- osmo_select_main(0);
+ while (steps--) {
+ printf("%d.%06d\n", (int)osmo_gettimeofday_override_time.tv_sec,
+ (int)osmo_gettimeofday_override_time.tv_usec);
+ osmo_timers_prepare();
+ osmo_timers_update();
+ osmo_gettimeofday_override_add(0, TIME_BETWEEN_TIMER_CHECKS);
}
#else
- fprintf(stdout, "Select not supported on this platform!\n");
+ printf("Select not supported on this platform!\n");
#endif
+ return 0;
}
diff --git a/src/shared/libosmocore/tests/timer/timer_test.ok b/src/shared/libosmocore/tests/timer/timer_test.ok
index 1bb382ee..75b11c7d 100644
--- a/src/shared/libosmocore/tests/timer/timer_test.ok
+++ b/src/shared/libosmocore/tests/timer/timer_test.ok
@@ -1,2 +1,370 @@
-Running timer test for 5 steps, accepting imprecision of 0.020000 seconds
-test over: added=31 expired=31 too_late=0
+Running timer test for 8 iterations, 66 steps of 423 msecs each
+23.424242
+23.847452
+24.270662
+24.693872
+main_timer_fired()
+scheduled timer at 25.693872
+added 1 timers in step 0 (expired=0)
+25.117082
+25.540292
+25.963502
+main_timer_fired()
+scheduled timer at 26.963502
+scheduled timer at 27.963502
+added 2 timers in step 1 (expired=0)
+timer fired on time: 25.693872 (+ 0.269630)
+early deleted 0 timers, 2 still active
+26.386712
+26.809922
+27.233132
+main_timer_fired()
+scheduled timer at 28.233132
+scheduled timer at 29.233132
+scheduled timer at 30.233132
+scheduled timer at 31.233132
+added 4 timers in step 2 (expired=1)
+timer fired on time: 26.963502 (+ 0.269630)
+early deleted 1 timers, 4 still active
+27.656342
+28.079552
+timer fired on time: 27.963502 (+ 0.116050)
+early deleted 0 timers, 3 still active
+28.502762
+main_timer_fired()
+scheduled timer at 29.502762
+scheduled timer at 30.502762
+scheduled timer at 31.502762
+scheduled timer at 32.502762
+scheduled timer at 33.502762
+scheduled timer at 34.502762
+scheduled timer at 35.502762
+scheduled timer at 36.502762
+added 8 timers in step 3 (expired=4)
+28.925972
+29.349182
+timer fired on time: 29.233132 (+ 0.116050)
+early deleted 2 timers, 8 still active
+29.772392
+main_timer_fired()
+scheduled timer at 30.772392
+scheduled timer at 31.772392
+scheduled timer at 32.772392
+scheduled timer at 33.772392
+scheduled timer at 34.772392
+scheduled timer at 35.772392
+scheduled timer at 36.772392
+scheduled timer at 37.772392
+scheduled timer at 30.772392
+scheduled timer at 31.772392
+scheduled timer at 32.772392
+scheduled timer at 33.772392
+scheduled timer at 34.772392
+scheduled timer at 35.772392
+scheduled timer at 36.772392
+scheduled timer at 37.772392
+added 16 timers in step 4 (expired=7)
+30.195602
+30.618812
+timer fired on time: 30.502762 (+ 0.116050)
+early deleted 5 timers, 18 still active
+timer fired on time: 30.233132 (+ 0.385680)
+early deleted 4 timers, 13 still active
+31.042022
+main_timer_fired()
+scheduled timer at 32.042022
+scheduled timer at 33.042022
+scheduled timer at 34.042022
+scheduled timer at 35.042022
+scheduled timer at 36.042022
+scheduled timer at 37.042022
+scheduled timer at 38.042022
+scheduled timer at 39.042022
+scheduled timer at 32.042022
+scheduled timer at 33.042022
+scheduled timer at 34.042022
+scheduled timer at 35.042022
+scheduled timer at 36.042022
+scheduled timer at 37.042022
+scheduled timer at 38.042022
+scheduled timer at 39.042022
+scheduled timer at 32.042022
+scheduled timer at 33.042022
+scheduled timer at 34.042022
+scheduled timer at 35.042022
+scheduled timer at 36.042022
+scheduled timer at 37.042022
+scheduled timer at 38.042022
+scheduled timer at 39.042022
+scheduled timer at 32.042022
+scheduled timer at 33.042022
+scheduled timer at 34.042022
+scheduled timer at 35.042022
+scheduled timer at 36.042022
+scheduled timer at 37.042022
+scheduled timer at 38.042022
+scheduled timer at 39.042022
+added 32 timers in step 5 (expired=18)
+31.465232
+timer fired on time: 31.233132 (+ 0.232100)
+early deleted 11 timers, 33 still active
+31.888442
+timer fired on time: 31.772392 (+ 0.116050)
+early deleted 8 timers, 24 still active
+32.311652
+main_timer_fired()
+scheduled timer at 33.311652
+scheduled timer at 34.311652
+scheduled timer at 35.311652
+scheduled timer at 36.311652
+scheduled timer at 37.311652
+scheduled timer at 38.311652
+scheduled timer at 39.311652
+scheduled timer at 40.311652
+scheduled timer at 33.311652
+scheduled timer at 34.311652
+scheduled timer at 35.311652
+scheduled timer at 36.311652
+scheduled timer at 37.311652
+scheduled timer at 38.311652
+scheduled timer at 39.311652
+scheduled timer at 40.311652
+scheduled timer at 33.311652
+scheduled timer at 34.311652
+scheduled timer at 35.311652
+scheduled timer at 36.311652
+scheduled timer at 37.311652
+scheduled timer at 38.311652
+scheduled timer at 39.311652
+scheduled timer at 40.311652
+scheduled timer at 33.311652
+scheduled timer at 34.311652
+scheduled timer at 35.311652
+scheduled timer at 36.311652
+scheduled timer at 37.311652
+scheduled timer at 38.311652
+scheduled timer at 39.311652
+scheduled timer at 40.311652
+scheduled timer at 33.311652
+scheduled timer at 34.311652
+scheduled timer at 35.311652
+scheduled timer at 36.311652
+scheduled timer at 37.311652
+scheduled timer at 38.311652
+scheduled timer at 39.311652
+scheduled timer at 40.311652
+scheduled timer at 33.311652
+scheduled timer at 34.311652
+scheduled timer at 35.311652
+scheduled timer at 36.311652
+scheduled timer at 37.311652
+scheduled timer at 38.311652
+scheduled timer at 39.311652
+scheduled timer at 40.311652
+scheduled timer at 33.311652
+scheduled timer at 34.311652
+scheduled timer at 35.311652
+scheduled timer at 36.311652
+scheduled timer at 37.311652
+scheduled timer at 38.311652
+scheduled timer at 39.311652
+scheduled timer at 40.311652
+scheduled timer at 33.311652
+scheduled timer at 34.311652
+scheduled timer at 35.311652
+scheduled timer at 36.311652
+scheduled timer at 37.311652
+scheduled timer at 38.311652
+scheduled timer at 39.311652
+scheduled timer at 40.311652
+added 64 timers in step 6 (expired=39)
+32.734862
+33.158072
+timer fired on time: 33.042022 (+ 0.116050)
+early deleted 21 timers, 66 still active
+timer fired on time: 33.042022 (+ 0.116050)
+early deleted 16 timers, 49 still active
+33.581282
+main_timer_fired()
+scheduled timer at 34.581282
+scheduled timer at 35.581282
+scheduled timer at 36.581282
+scheduled timer at 37.581282
+scheduled timer at 38.581282
+scheduled timer at 39.581282
+scheduled timer at 40.581282
+scheduled timer at 41.581282
+scheduled timer at 34.581282
+scheduled timer at 35.581282
+scheduled timer at 36.581282
+scheduled timer at 37.581282
+scheduled timer at 38.581282
+scheduled timer at 39.581282
+scheduled timer at 40.581282
+scheduled timer at 41.581282
+scheduled timer at 34.581282
+scheduled timer at 35.581282
+scheduled timer at 36.581282
+scheduled timer at 37.581282
+scheduled timer at 38.581282
+scheduled timer at 39.581282
+scheduled timer at 40.581282
+scheduled timer at 41.581282
+scheduled timer at 34.581282
+scheduled timer at 35.581282
+scheduled timer at 36.581282
+scheduled timer at 37.581282
+scheduled timer at 38.581282
+scheduled timer at 39.581282
+scheduled timer at 40.581282
+scheduled timer at 41.581282
+scheduled timer at 34.581282
+scheduled timer at 35.581282
+scheduled timer at 36.581282
+scheduled timer at 37.581282
+scheduled timer at 38.581282
+scheduled timer at 39.581282
+scheduled timer at 40.581282
+scheduled timer at 41.581282
+scheduled timer at 34.581282
+scheduled timer at 35.581282
+scheduled timer at 36.581282
+scheduled timer at 37.581282
+scheduled timer at 38.581282
+scheduled timer at 39.581282
+scheduled timer at 40.581282
+scheduled timer at 41.581282
+scheduled timer at 34.581282
+scheduled timer at 35.581282
+scheduled timer at 36.581282
+scheduled timer at 37.581282
+scheduled timer at 38.581282
+scheduled timer at 39.581282
+scheduled timer at 40.581282
+scheduled timer at 41.581282
+scheduled timer at 34.581282
+scheduled timer at 35.581282
+scheduled timer at 36.581282
+scheduled timer at 37.581282
+scheduled timer at 38.581282
+scheduled timer at 39.581282
+scheduled timer at 40.581282
+scheduled timer at 41.581282
+scheduled timer at 34.581282
+scheduled timer at 35.581282
+scheduled timer at 36.581282
+scheduled timer at 37.581282
+scheduled timer at 38.581282
+scheduled timer at 39.581282
+scheduled timer at 40.581282
+scheduled timer at 41.581282
+scheduled timer at 34.581282
+scheduled timer at 35.581282
+scheduled timer at 36.581282
+scheduled timer at 37.581282
+scheduled timer at 38.581282
+scheduled timer at 39.581282
+scheduled timer at 40.581282
+scheduled timer at 41.581282
+scheduled timer at 34.581282
+scheduled timer at 35.581282
+scheduled timer at 36.581282
+scheduled timer at 37.581282
+scheduled timer at 38.581282
+scheduled timer at 39.581282
+scheduled timer at 40.581282
+scheduled timer at 41.581282
+scheduled timer at 34.581282
+scheduled timer at 35.581282
+scheduled timer at 36.581282
+scheduled timer at 37.581282
+scheduled timer at 38.581282
+scheduled timer at 39.581282
+scheduled timer at 40.581282
+scheduled timer at 41.581282
+scheduled timer at 34.581282
+scheduled timer at 35.581282
+scheduled timer at 36.581282
+scheduled timer at 37.581282
+scheduled timer at 38.581282
+scheduled timer at 39.581282
+scheduled timer at 40.581282
+scheduled timer at 41.581282
+scheduled timer at 34.581282
+scheduled timer at 35.581282
+scheduled timer at 36.581282
+scheduled timer at 37.581282
+scheduled timer at 38.581282
+scheduled timer at 39.581282
+scheduled timer at 40.581282
+scheduled timer at 41.581282
+scheduled timer at 34.581282
+scheduled timer at 35.581282
+scheduled timer at 36.581282
+scheduled timer at 37.581282
+scheduled timer at 38.581282
+scheduled timer at 39.581282
+scheduled timer at 40.581282
+scheduled timer at 41.581282
+scheduled timer at 34.581282
+scheduled timer at 35.581282
+scheduled timer at 36.581282
+scheduled timer at 37.581282
+scheduled timer at 38.581282
+scheduled timer at 39.581282
+scheduled timer at 40.581282
+scheduled timer at 41.581282
+added 128 timers in step 7 (expired=78)
+34.004492
+34.427702
+timer fired on time: 34.311652 (+ 0.116050)
+early deleted 44 timers, 132 still active
+timer fired on time: 34.311652 (+ 0.116050)
+early deleted 32 timers, 99 still active
+timer fired on time: 34.311652 (+ 0.116050)
+early deleted 24 timers, 74 still active
+34.850912
+main_timer_fired()
+Main timer has finished, please, wait a bit for the final report.
+35.274122
+35.697332
+timer fired on time: 35.581282 (+ 0.116050)
+early deleted 18 timers, 55 still active
+timer fired on time: 35.581282 (+ 0.116050)
+early deleted 13 timers, 41 still active
+timer fired on time: 35.581282 (+ 0.116050)
+early deleted 10 timers, 30 still active
+36.120542
+36.543752
+timer fired on time: 36.311652 (+ 0.232100)
+early deleted 7 timers, 22 still active
+timer fired on time: 36.311652 (+ 0.232100)
+early deleted 5 timers, 16 still active
+36.966962
+timer fired on time: 36.581282 (+ 0.385680)
+early deleted 3 timers, 12 still active
+37.390172
+timer fired on time: 37.042022 (+ 0.348150)
+early deleted 2 timers, 9 still active
+37.813382
+timer fired on time: 37.581282 (+ 0.232100)
+early deleted 2 timers, 6 still active
+38.236592
+38.659802
+39.083012
+39.506222
+timer fired on time: 39.311652 (+ 0.194570)
+early deleted 1 timers, 4 still active
+39.929432
+timer fired on time: 39.581282 (+ 0.348150)
+early deleted 0 timers, 3 still active
+40.352642
+40.775852
+timer fired on time: 40.581282 (+ 0.194570)
+early deleted 0 timers, 2 still active
+41.199062
+41.622272
+timer fired on time: 41.581282 (+ 0.040990)
+early deleted 0 timers, 1 still active
+timer fired on time: 41.581282 (+ 0.040990)
+test over: added=255 expired=255 too_soon=0 too_late=0
diff --git a/src/shared/libosmocore/tests/tlv/tlv_test.c b/src/shared/libosmocore/tests/tlv/tlv_test.c
new file mode 100644
index 00000000..c571c3bd
--- /dev/null
+++ b/src/shared/libosmocore/tests/tlv/tlv_test.c
@@ -0,0 +1,250 @@
+#include <osmocom/gsm/tlv.h>
+
+static void check_tlv_parse(uint8_t **data, size_t *data_len,
+ uint8_t exp_tag, size_t exp_len, const uint8_t *exp_val)
+{
+ uint8_t *value;
+ size_t value_len;
+ uint8_t tag;
+ int rc;
+ uint8_t *saved_data = *data;
+ size_t saved_data_len = *data_len;
+
+ rc = osmo_match_shift_tlv(data, data_len, exp_tag ^ 1, NULL, NULL);
+ OSMO_ASSERT(rc == 0);
+
+ rc = osmo_match_shift_tlv(data, data_len, exp_tag, &value, &value_len);
+ OSMO_ASSERT(rc == (int)value_len + 2);
+ OSMO_ASSERT(value_len == exp_len);
+ OSMO_ASSERT(memcmp(value, exp_val, exp_len) == 0);
+
+ /* restore data/data_len */
+ *data = saved_data;
+ *data_len = saved_data_len;
+
+ rc = osmo_shift_tlv(data, data_len, &tag, &value, &value_len);
+ OSMO_ASSERT(rc == (int)value_len + 2);
+ OSMO_ASSERT(tag == exp_tag);
+ OSMO_ASSERT(value_len == exp_len);
+ OSMO_ASSERT(memcmp(value, exp_val, exp_len) == 0);
+}
+
+static void check_tv_fixed_match(uint8_t **data, size_t *data_len,
+ uint8_t tag, size_t len, const uint8_t *exp_val)
+{
+ uint8_t *value;
+ int rc;
+
+ rc = osmo_match_shift_tv_fixed(data, data_len, tag ^ 1, len, NULL);
+ OSMO_ASSERT(rc == 0);
+
+ rc = osmo_match_shift_tv_fixed(data, data_len, tag, len, &value);
+ OSMO_ASSERT(rc == (int)len + 1);
+ OSMO_ASSERT(memcmp(value, exp_val, len) == 0);
+}
+
+static void check_v_fixed_shift(uint8_t **data, size_t *data_len,
+ size_t len, const uint8_t *exp_val)
+{
+ uint8_t *value;
+ int rc;
+
+ rc = osmo_shift_v_fixed(data, data_len, len, &value);
+ OSMO_ASSERT(rc == (int)len);
+ OSMO_ASSERT(memcmp(value, exp_val, len) == 0);
+}
+
+static void check_lv_shift(uint8_t **data, size_t *data_len,
+ size_t exp_len, const uint8_t *exp_val)
+{
+ uint8_t *value;
+ size_t value_len;
+ int rc;
+
+ rc = osmo_shift_lv(data, data_len, &value, &value_len);
+ OSMO_ASSERT(rc == (int)value_len + 1);
+ OSMO_ASSERT(value_len == exp_len);
+ OSMO_ASSERT(memcmp(value, exp_val, exp_len) == 0);
+}
+
+static void check_tlv_match_data_len(size_t data_len, uint8_t tag, size_t len,
+ const uint8_t *test_data)
+{
+ uint8_t buf[300] = {0};
+
+ uint8_t *unchanged_ptr = buf - 1;
+ size_t unchanged_len = 0xdead;
+ size_t tmp_data_len = data_len;
+ uint8_t *value = unchanged_ptr;
+ size_t value_len = unchanged_len;
+ uint8_t *data = buf;
+
+ OSMO_ASSERT(data_len <= sizeof(buf));
+
+ tlv_put(data, tag, len, test_data);
+ if (data_len < len + 2) {
+ OSMO_ASSERT(-1 == osmo_match_shift_tlv(&data, &tmp_data_len,
+ tag, &value, &value_len));
+ OSMO_ASSERT(tmp_data_len == 0);
+ OSMO_ASSERT(data == buf + data_len);
+ OSMO_ASSERT(value == unchanged_ptr);
+ OSMO_ASSERT(value_len == unchanged_len);
+ } else {
+ OSMO_ASSERT(0 <= osmo_match_shift_tlv(&data, &tmp_data_len,
+ tag, &value, &value_len));
+ OSMO_ASSERT(value != unchanged_ptr);
+ OSMO_ASSERT(value_len != unchanged_len);
+ }
+}
+
+static void check_tv_fixed_match_data_len(size_t data_len,
+ uint8_t tag, size_t len,
+ const uint8_t *test_data)
+{
+ uint8_t buf[300] = {0};
+
+ uint8_t *unchanged_ptr = buf - 1;
+ size_t tmp_data_len = data_len;
+ uint8_t *value = unchanged_ptr;
+ uint8_t *data = buf;
+
+ OSMO_ASSERT(data_len <= sizeof(buf));
+
+ tv_fixed_put(data, tag, len, test_data);
+
+ if (data_len < len + 1) {
+ OSMO_ASSERT(-1 == osmo_match_shift_tv_fixed(&data, &tmp_data_len,
+ tag, len, &value));
+ OSMO_ASSERT(tmp_data_len == 0);
+ OSMO_ASSERT(data == buf + data_len);
+ OSMO_ASSERT(value == unchanged_ptr);
+ } else {
+ OSMO_ASSERT(0 <= osmo_match_shift_tv_fixed(&data, &tmp_data_len,
+ tag, len, &value));
+ OSMO_ASSERT(value != unchanged_ptr);
+ }
+}
+
+static void check_v_fixed_shift_data_len(size_t data_len,
+ size_t len, const uint8_t *test_data)
+{
+ uint8_t buf[300] = {0};
+
+ uint8_t *unchanged_ptr = buf - 1;
+ size_t tmp_data_len = data_len;
+ uint8_t *value = unchanged_ptr;
+ uint8_t *data = buf;
+
+ OSMO_ASSERT(data_len <= sizeof(buf));
+
+ memcpy(data, test_data, len);
+
+ if (data_len < len) {
+ OSMO_ASSERT(-1 == osmo_shift_v_fixed(&data, &tmp_data_len,
+ len, &value));
+ OSMO_ASSERT(tmp_data_len == 0);
+ OSMO_ASSERT(data == buf + data_len);
+ OSMO_ASSERT(value == unchanged_ptr);
+ } else {
+ OSMO_ASSERT(0 <= osmo_shift_v_fixed(&data, &tmp_data_len,
+ len, &value));
+ OSMO_ASSERT(value != unchanged_ptr);
+ }
+}
+
+static void check_lv_shift_data_len(size_t data_len,
+ size_t len, const uint8_t *test_data)
+{
+ uint8_t buf[300] = {0};
+
+ uint8_t *unchanged_ptr = buf - 1;
+ size_t unchanged_len = 0xdead;
+ size_t tmp_data_len = data_len;
+ uint8_t *value = unchanged_ptr;
+ size_t value_len = unchanged_len;
+ uint8_t *data = buf;
+
+ lv_put(data, len, test_data);
+ if (data_len < len + 1) {
+ OSMO_ASSERT(-1 == osmo_shift_lv(&data, &tmp_data_len,
+ &value, &value_len));
+ OSMO_ASSERT(tmp_data_len == 0);
+ OSMO_ASSERT(data == buf + data_len);
+ OSMO_ASSERT(value == unchanged_ptr);
+ OSMO_ASSERT(value_len == unchanged_len);
+ } else {
+ OSMO_ASSERT(0 <= osmo_shift_lv(&data, &tmp_data_len,
+ &value, &value_len));
+ OSMO_ASSERT(value != unchanged_ptr);
+ OSMO_ASSERT(value_len != unchanged_len);
+ }
+}
+
+static void test_tlv_shift_functions()
+{
+ uint8_t test_data[1024];
+ uint8_t buf[1024];
+ uint8_t *data_end;
+ unsigned i, len;
+ uint8_t *data;
+ size_t data_len;
+ const uint8_t tag = 0x1a;
+
+ printf("Test shift functions\n");
+
+ for (i = 0; i < ARRAY_SIZE(test_data); i++)
+ test_data[i] = (uint8_t)i;
+
+ for (len = 0; len < 256; len++) {
+ const unsigned iterations = sizeof(buf) / (len + 2) / 4;
+
+ memset(buf, 0xee, sizeof(buf));
+ data_end = data = buf;
+
+ for (i = 0; i < iterations; i++) {
+ data_end = tlv_put(data_end, tag, len, test_data);
+ data_end = tv_fixed_put(data_end, tag, len, test_data);
+ /* v_fixed_put */
+ memcpy(data_end, test_data, len);
+ data_end += len;
+ data_end = lv_put(data_end, len, test_data);
+ }
+
+ data_len = data_end - data;
+ OSMO_ASSERT(data_len <= sizeof(buf));
+
+ for (i = 0; i < iterations; i++) {
+ check_tlv_parse(&data, &data_len, tag, len, test_data);
+ check_tv_fixed_match(&data, &data_len, tag, len, test_data);
+ check_v_fixed_shift(&data, &data_len, len, test_data);
+ check_lv_shift(&data, &data_len, len, test_data);
+ }
+
+ OSMO_ASSERT(data == data_end);
+
+ /* Test at end of data */
+
+ OSMO_ASSERT(-1 == osmo_match_shift_tlv(&data, &data_len, tag, NULL, NULL));
+ OSMO_ASSERT(-1 == osmo_match_shift_tv_fixed(&data, &data_len, tag, len, NULL));
+ OSMO_ASSERT((len ? -1 : 0) == osmo_shift_v_fixed(&data, &data_len, len, NULL));
+ OSMO_ASSERT(-1 == osmo_shift_lv(&data, &data_len, NULL, NULL));
+
+ /* Test invalid data_len */
+ for (data_len = 0; data_len <= len + 2 + 1; data_len += 1) {
+ check_tlv_match_data_len(data_len, tag, len, test_data);
+ check_tv_fixed_match_data_len(data_len, tag, len, test_data);
+ check_v_fixed_shift_data_len(data_len, len, test_data);
+ check_lv_shift_data_len(data_len, len, test_data);
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ //osmo_init_logging(&info);
+
+ test_tlv_shift_functions();
+
+ printf("Done.\n");
+ return EXIT_SUCCESS;
+}
diff --git a/src/shared/libosmocore/tests/tlv/tlv_test.ok b/src/shared/libosmocore/tests/tlv/tlv_test.ok
new file mode 100644
index 00000000..de159bfb
--- /dev/null
+++ b/src/shared/libosmocore/tests/tlv/tlv_test.ok
@@ -0,0 +1,2 @@
+Test shift functions
+Done.
diff --git a/src/shared/libosmocore/tests/ussd/ussd_test.c b/src/shared/libosmocore/tests/ussd/ussd_test.c
index 55384f10..40b4317a 100644
--- a/src/shared/libosmocore/tests/ussd/ussd_test.c
+++ b/src/shared/libosmocore/tests/ussd/ussd_test.c
@@ -22,6 +22,7 @@
#include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
#include <osmocom/gsm/gsm0480.h>
+#include <osmocom/gsm/gsm_utils.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -33,17 +34,23 @@ static const uint8_t ussd_request[] = {
0x01, 0x7f, 0x01, 0x00
};
+static const uint8_t interrogate_ss[] = {
+ 0x0b, 0x7b, 0x1c, 0x0d, 0xa1, 0x0b, 0x02, 0x01,
+ 0x03, 0x02, 0x01, 0x0e, 0x30, 0x03, 0x04, 0x01,
+ 0x21, 0x7f, 0x01, 0x00
+};
+
static int parse_ussd(const uint8_t *_data, int len)
{
uint8_t *data;
int rc;
- struct ussd_request req;
+ struct ss_request req;
struct gsm48_hdr *hdr;
data = malloc(len);
memcpy(data, _data, len);
hdr = (struct gsm48_hdr *) &data[0];
- rc = gsm0480_decode_ussd_request(hdr, len, &req);
+ rc = gsm0480_decode_ss_request(hdr, len, &req);
free(data);
return rc;
@@ -53,14 +60,14 @@ static int parse_mangle_ussd(const uint8_t *_data, int len)
{
uint8_t *data;
int rc;
- struct ussd_request req;
+ struct ss_request req;
struct gsm48_hdr *hdr;
data = malloc(len);
memcpy(data, _data, len);
hdr = (struct gsm48_hdr *) &data[0];
hdr->data[1] = len - sizeof(*hdr) - 2;
- rc = gsm0480_decode_ussd_request(hdr, len, &req);
+ rc = gsm0480_decode_ss_request(hdr, len, &req);
free(data);
return rc;
@@ -68,17 +75,66 @@ static int parse_mangle_ussd(const uint8_t *_data, int len)
struct log_info info = {};
+static void test_7bit_ussd(const char *text, const char *encoded_hex, const char *appended_after_decode)
+{
+ uint8_t coded[256];
+ char decoded[256];
+ int octets_written;
+ int buffer_size;
+ int nchars;
+
+ printf("original = %s\n", osmo_hexdump((uint8_t *)text, strlen(text)));
+ gsm_7bit_encode_n_ussd(coded, sizeof(coded), text, &octets_written);
+ printf("encoded = %s\n", osmo_hexdump(coded, octets_written));
+
+ OSMO_ASSERT(strcmp(encoded_hex, osmo_hexdump_nospc(coded, octets_written)) == 0);
+
+ gsm_7bit_decode_n_ussd(decoded, sizeof(decoded), coded, octets_written * 8 / 7);
+ octets_written = strlen(decoded);
+ printf("decoded = %s\n\n", osmo_hexdump((uint8_t *)decoded, octets_written));
+
+ OSMO_ASSERT(strncmp(text, decoded, strlen(text)) == 0);
+ OSMO_ASSERT(strcmp(appended_after_decode, decoded + strlen(text)) == 0);
+
+ /* check buffer limiting */
+ memset(decoded, 0xaa, sizeof(decoded));
+
+ for (buffer_size = 1; buffer_size < sizeof(decoded) - 1; ++buffer_size)
+ {
+ nchars = gsm_7bit_decode_n_ussd(decoded, buffer_size, coded, octets_written * 8 / 7);
+ OSMO_ASSERT(nchars <= buffer_size);
+ OSMO_ASSERT(decoded[buffer_size] == (char)0xaa);
+ OSMO_ASSERT(decoded[nchars] == '\0');
+ }
+
+ memset(coded, 0xaa, sizeof(coded));
+
+ for (buffer_size = 0; buffer_size < sizeof(coded) - 1; ++buffer_size)
+ {
+ gsm_7bit_encode_n_ussd(coded, buffer_size, text, &octets_written);
+ OSMO_ASSERT(octets_written <= buffer_size);
+ OSMO_ASSERT(coded[buffer_size] == 0xaa);
+ }
+}
+
int main(int argc, char **argv)
{
- struct ussd_request req;
+ struct ss_request req;
const int size = sizeof(ussd_request);
int i;
+ struct msgb *msg;
osmo_init_logging(&info);
- gsm0480_decode_ussd_request((struct gsm48_hdr *) ussd_request, size, &req);
- printf("Tested if it still works. Text was: %s\n", req.text);
+ memset(&req, 0, sizeof(req));
+ gsm0480_decode_ss_request((struct gsm48_hdr *) ussd_request, size, &req);
+ printf("Tested if it still works. Text was: %s\n", req.ussd_text);
+ memset(&req, 0, sizeof(req));
+ gsm0480_decode_ss_request((struct gsm48_hdr *) interrogate_ss, size, &req);
+ OSMO_ASSERT(strlen((char *) req.ussd_text) == 0);
+ OSMO_ASSERT(req.ss_code == 33);
+ printf("interrogateSS CFU text..'%s' code %d\n", req.ussd_text, req.ss_code);
printf("Testing parsing a USSD request and truncated versions\n");
@@ -93,5 +149,26 @@ int main(int argc, char **argv)
printf("Result for %d is %d\n", rc, i);
}
+ printf("<CR> case test for 7 bit encode\n");
+ test_7bit_ussd("01234567", "b0986c46abd96e", "");
+ test_7bit_ussd("0123456", "b0986c46abd91a", "");
+ test_7bit_ussd("01234567\r", "b0986c46abd96e0d", "");
+ /* The appended \r is compliant to GSM 03.38 section 6.1.2.3.1: */
+ test_7bit_ussd("0123456\r", "b0986c46abd91a0d", "\r");
+ test_7bit_ussd("012345\r", "b0986c46ab351a", "");
+
+ printf("Checking GSM 04.80 USSD message generation.\n");
+
+ test_7bit_ussd("", "", "");
+ msg = gsm0480_create_unstructuredSS_Notify (0x00, "");
+ printf ("Created unstructuredSS_Notify (0x00): %s\n",
+ osmo_hexdump(msgb_data(msg), msgb_length(msg)));
+ msgb_free (msg);
+
+ test_7bit_ussd("forty-two", "e6b79c9e6fd1ef6f", "");
+ msg = gsm0480_create_unstructuredSS_Notify (0x42, "forty-two");
+ printf ("Created unstructuredSS_Notify (0x42): %s\n",
+ osmo_hexdump(msgb_data(msg), msgb_length(msg)));
+ msgb_free (msg);
return 0;
}
diff --git a/src/shared/libosmocore/tests/ussd/ussd_test.ok b/src/shared/libosmocore/tests/ussd/ussd_test.ok
index 1b6316e8..69ea53c3 100644
--- a/src/shared/libosmocore/tests/ussd/ussd_test.ok
+++ b/src/shared/libosmocore/tests/ussd/ussd_test.ok
@@ -1,4 +1,5 @@
Tested if it still works. Text was: **321#
+interrogateSS CFU text..'' code 33
Testing parsing a USSD request and truncated versions
Result for 1 is 28
Result for 1 is 27
@@ -51,3 +52,35 @@ Result for 0 is 8
Result for 0 is 7
Result for 0 is 6
Result for 1 is 5
+<CR> case test for 7 bit encode
+original = 30 31 32 33 34 35 36 37
+encoded = b0 98 6c 46 ab d9 6e
+decoded = 30 31 32 33 34 35 36 37
+
+original = 30 31 32 33 34 35 36
+encoded = b0 98 6c 46 ab d9 1a
+decoded = 30 31 32 33 34 35 36
+
+original = 30 31 32 33 34 35 36 37 0d
+encoded = b0 98 6c 46 ab d9 6e 0d
+decoded = 30 31 32 33 34 35 36 37 0d
+
+original = 30 31 32 33 34 35 36 0d
+encoded = b0 98 6c 46 ab d9 1a 0d
+decoded = 30 31 32 33 34 35 36 0d 0d
+
+original = 30 31 32 33 34 35 0d
+encoded = b0 98 6c 46 ab 35 1a
+decoded = 30 31 32 33 34 35 0d
+
+Checking GSM 04.80 USSD message generation.
+original =
+encoded =
+decoded =
+
+Created unstructuredSS_Notify (0x00): 30 08 04 01 0f 04 00 04 01 00
+original = 66 6f 72 74 79 2d 74 77 6f
+encoded = e6 b7 9c 9e 6f d1 ef 6f
+decoded = 66 6f 72 74 79 2d 74 77 6f
+
+Created unstructuredSS_Notify (0x42): 30 10 04 01 0f 04 08 e6 b7 9c 9e 6f d1 ef 6f 04 01 42
diff --git a/src/shared/libosmocore/tests/utils/utils_test.c b/src/shared/libosmocore/tests/utils/utils_test.c
new file mode 100644
index 00000000..1a79baa3
--- /dev/null
+++ b/src/shared/libosmocore/tests/utils/utils_test.c
@@ -0,0 +1,108 @@
+/* tests for utilities of libmsomcore */
+/*
+ * (C) 2014 Holger Hans Peter Freyther
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <osmocom/gsm/ipa.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+
+#include <stdio.h>
+
+static void hexdump_test(void)
+{
+ uint8_t data[4098];
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(data); ++i)
+ data[i] = i & 0xff;
+
+ printf("Plain dump\n");
+ printf("%s\n", osmo_hexdump(data, 4));
+
+ printf("Corner case\n");
+ printf("%s\n", osmo_hexdump(data, ARRAY_SIZE(data)));
+ printf("%s\n", osmo_hexdump_nospc(data, ARRAY_SIZE(data)));
+}
+
+static void test_idtag_parsing(void)
+{
+ struct tlv_parsed tvp;
+ int rc;
+
+ static uint8_t data[] = {
+ 0x01, 0x08,
+ 0x01, 0x07,
+ 0x01, 0x02,
+ 0x01, 0x03,
+ 0x01, 0x04,
+ 0x01, 0x05,
+ 0x01, 0x01,
+ 0x01, 0x00,
+ 0x11, 0x23, 0x4e, 0x6a, 0x28, 0xd2, 0xa2, 0x53, 0x3a, 0x2a, 0x82, 0xa7, 0x7a, 0xef, 0x29, 0xd4, 0x44, 0x30,
+ 0x11, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ rc = ipa_ccm_idtag_parse_off(&tvp, data, sizeof(data), 1);
+ OSMO_ASSERT(rc == 0);
+
+ OSMO_ASSERT(TLVP_PRESENT(&tvp, 8));
+ OSMO_ASSERT(TLVP_LEN(&tvp, 8) == 0);
+
+ OSMO_ASSERT(TLVP_PRESENT(&tvp, 7));
+ OSMO_ASSERT(TLVP_LEN(&tvp, 7) == 0);
+
+ OSMO_ASSERT(TLVP_PRESENT(&tvp, 2));
+ OSMO_ASSERT(TLVP_LEN(&tvp, 2) == 0);
+
+ OSMO_ASSERT(TLVP_PRESENT(&tvp, 3));
+ OSMO_ASSERT(TLVP_LEN(&tvp, 3) == 0);
+
+ OSMO_ASSERT(TLVP_PRESENT(&tvp, 4));
+ OSMO_ASSERT(TLVP_LEN(&tvp, 4) == 0);
+
+ OSMO_ASSERT(TLVP_PRESENT(&tvp, 5));
+ OSMO_ASSERT(TLVP_LEN(&tvp, 5) == 0);
+
+ OSMO_ASSERT(TLVP_PRESENT(&tvp, 1));
+ OSMO_ASSERT(TLVP_LEN(&tvp, 1) == 0);
+
+ OSMO_ASSERT(TLVP_PRESENT(&tvp, 0));
+ OSMO_ASSERT(TLVP_LEN(&tvp, 0) == 0);
+
+ OSMO_ASSERT(TLVP_PRESENT(&tvp, 0x23));
+ OSMO_ASSERT(TLVP_LEN(&tvp, 0x23) == 16);
+
+ OSMO_ASSERT(TLVP_PRESENT(&tvp, 0x24));
+ OSMO_ASSERT(TLVP_LEN(&tvp, 0x24) == 16);
+
+ OSMO_ASSERT(!TLVP_PRESENT(&tvp, 0x25));
+}
+
+int main(int argc, char **argv)
+{
+ static const struct log_info log_info = {};
+ log_init(&log_info, NULL);
+
+ hexdump_test();
+ test_idtag_parsing();
+ return 0;
+}
diff --git a/src/shared/libosmocore/tests/utils/utils_test.ok b/src/shared/libosmocore/tests/utils/utils_test.ok
new file mode 100644
index 00000000..50a57473
--- /dev/null
+++ b/src/shared/libosmocore/tests/utils/utils_test.ok
@@ -0,0 +1,5 @@
+Plain dump
+00 01 02 03
+Corner case
+00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54
+000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe
diff --git a/src/shared/libosmocore/tests/vty/vty_test.c b/src/shared/libosmocore/tests/vty/vty_test.c
new file mode 100644
index 00000000..865c93e9
--- /dev/null
+++ b/src/shared/libosmocore/tests/vty/vty_test.c
@@ -0,0 +1,334 @@
+/* (C) 2013 by Jacob Erlbeck <jerlbeck@sysmocom.de>
+ * All Rights Reserved
+ *
+ * This program is iree 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.
+ *
+ * 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 <string.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/stats.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/signal.h>
+#include <osmocom/vty/misc.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/buffer.h>
+#include <osmocom/vty/logging.h>
+#include <osmocom/vty/stats.h>
+
+static enum event last_vty_connection_event = -1;
+
+static void test_cmd_string_from_valstr(void)
+{
+ char *cmd;
+ const struct value_string printf_seq_vs[] = {
+ { .value = 42, .str = "[foo%s%s%s%s%s]"},
+ { .value = 43, .str = "[bar%s%s%s%s%s]"},
+ { .value = 0, .str = NULL}
+ };
+
+ printf("Going to test vty_cmd_string_from_valstr()\n");
+
+ /* check against character strings that could break printf */
+
+ cmd = vty_cmd_string_from_valstr (NULL, printf_seq_vs, "[prefix%s%s%s%s%s]", "[sep%s%s%s%s%s]", "[end%s%s%s%s%s]", 1);
+ printf ("Tested with %%s-strings, resulting cmd = '%s'\n", cmd);
+ talloc_free (cmd);
+}
+
+static int do_vty_command(struct vty *vty, const char *cmd)
+{
+ vector vline;
+ int ret;
+
+ printf("Going to execute '%s'\n", cmd);
+ vline = cmd_make_strvec(cmd);
+ ret = cmd_execute_command(vline, vty, NULL, 0);
+ cmd_free_strvec(vline);
+ printf("Returned: %d, Current node: %d '%s'\n", ret, vty->node, cmd_prompt(vty->node));
+ return ret;
+}
+
+/* Handle the events from telnet_interface.c */
+static int vty_event_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *_signal_data)
+{
+ struct vty_signal_data *signal_data;
+
+ if (subsys != SS_L_VTY)
+ return 0;
+ if (signal != S_VTY_EVENT)
+ return 0;
+
+ signal_data = _signal_data;
+ last_vty_connection_event = signal_data->event;
+
+ fprintf(stderr, "Got VTY event: %d\n", signal_data->event);
+ return 0;
+}
+
+struct vty_test {
+ int sock[2];
+};
+
+static struct vty* create_test_vty(struct vty_test *data)
+{
+ struct vty *vty;
+ /* Fake connection. */
+ socketpair(AF_UNIX, SOCK_STREAM, 0, data->sock);
+
+ vty = vty_create(data->sock[0], NULL);
+ OSMO_ASSERT(vty != NULL);
+ OSMO_ASSERT(vty->status != VTY_CLOSE);
+
+ return vty;
+}
+
+static void destroy_test_vty(struct vty_test *data, struct vty *vty)
+{
+ vty_close(vty);
+ OSMO_ASSERT(last_vty_connection_event == VTY_CLOSED);
+}
+
+static void test_node_tree_structure(void)
+{
+ struct vty_test test;
+ struct vty *vty;
+
+ printf("Going to test VTY node tree structure\n");
+ vty = create_test_vty(&test);
+
+ OSMO_ASSERT(do_vty_command(vty, "enable") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == ENABLE_NODE);
+
+ OSMO_ASSERT(do_vty_command(vty, "configure terminal") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == CONFIG_NODE);
+ OSMO_ASSERT(do_vty_command(vty, "exit") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == ENABLE_NODE);
+
+ OSMO_ASSERT(do_vty_command(vty, "configure terminal") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == CONFIG_NODE);
+ OSMO_ASSERT(do_vty_command(vty, "end") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == ENABLE_NODE);
+
+ OSMO_ASSERT(do_vty_command(vty, "configure terminal") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == CONFIG_NODE);
+ OSMO_ASSERT(do_vty_command(vty, "log stderr") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == CFG_LOG_NODE);
+ OSMO_ASSERT(do_vty_command(vty, "exit") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == CONFIG_NODE);
+ OSMO_ASSERT(do_vty_command(vty, "log stderr") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == CFG_LOG_NODE);
+ OSMO_ASSERT(do_vty_command(vty, "end") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == ENABLE_NODE);
+
+ OSMO_ASSERT(do_vty_command(vty, "configure terminal") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == CONFIG_NODE);
+ OSMO_ASSERT(do_vty_command(vty, "line vty") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == VTY_NODE);
+ OSMO_ASSERT(do_vty_command(vty, "exit") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == CONFIG_NODE);
+ OSMO_ASSERT(do_vty_command(vty, "line vty") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == VTY_NODE);
+ OSMO_ASSERT(do_vty_command(vty, "end") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == ENABLE_NODE);
+
+
+ /* Check searching the parents nodes for matching commands. */
+ OSMO_ASSERT(do_vty_command(vty, "configure terminal") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == CONFIG_NODE);
+ OSMO_ASSERT(do_vty_command(vty, "log stderr") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == CFG_LOG_NODE);
+ OSMO_ASSERT(do_vty_command(vty, "line vty") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == VTY_NODE);
+ OSMO_ASSERT(do_vty_command(vty, "log stderr") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == CFG_LOG_NODE);
+ OSMO_ASSERT(do_vty_command(vty, "end") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == ENABLE_NODE);
+
+ /* Check for final 'exit' (connection close). */
+ OSMO_ASSERT(do_vty_command(vty, "exit") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == ENABLE_NODE);
+ OSMO_ASSERT(vty->status == VTY_CLOSE);
+
+ destroy_test_vty(&test, vty);
+}
+
+static void check_srep_vty_config(struct vty* vty,
+ struct osmo_stats_reporter *srep)
+{
+ OSMO_ASSERT(srep->enabled == 0);
+
+ OSMO_ASSERT(do_vty_command(vty, "prefix myprefix") == CMD_SUCCESS);
+ OSMO_ASSERT(srep->name_prefix != NULL);
+ OSMO_ASSERT(strcmp(srep->name_prefix, "myprefix") == 0);
+ OSMO_ASSERT(do_vty_command(vty, "no prefix") == CMD_SUCCESS);
+ OSMO_ASSERT(srep->name_prefix == NULL || strlen(srep->name_prefix) == 0);
+
+ OSMO_ASSERT(srep->max_class == OSMO_STATS_CLASS_GLOBAL);
+ OSMO_ASSERT(do_vty_command(vty, "level peer") == CMD_SUCCESS);
+ OSMO_ASSERT(srep->max_class == OSMO_STATS_CLASS_PEER);
+ OSMO_ASSERT(do_vty_command(vty, "level subscriber") == CMD_SUCCESS);
+ OSMO_ASSERT(srep->max_class == OSMO_STATS_CLASS_SUBSCRIBER);
+ OSMO_ASSERT(do_vty_command(vty, "level global") == CMD_SUCCESS);
+ OSMO_ASSERT(srep->max_class == OSMO_STATS_CLASS_GLOBAL);
+ OSMO_ASSERT(do_vty_command(vty, "level foobar") == CMD_ERR_NO_MATCH);
+
+ if (srep->have_net_config) {
+ OSMO_ASSERT(do_vty_command(vty, "remote-ip 127.0.0.99") ==
+ CMD_SUCCESS);
+ OSMO_ASSERT(srep->dest_addr_str &&
+ strcmp(srep->dest_addr_str, "127.0.0.99") == 0);
+ OSMO_ASSERT(do_vty_command(vty, "remote-ip 678.0.0.99") ==
+ CMD_WARNING);
+ OSMO_ASSERT(srep->dest_addr_str &&
+ strcmp(srep->dest_addr_str, "127.0.0.99") == 0);
+
+ OSMO_ASSERT(do_vty_command(vty, "remote-port 12321") ==
+ CMD_SUCCESS);
+ OSMO_ASSERT(srep->dest_port == 12321);
+
+ OSMO_ASSERT(srep->bind_addr_str == NULL);
+ OSMO_ASSERT(do_vty_command(vty, "local-ip 127.0.0.98") ==
+ CMD_SUCCESS);
+ OSMO_ASSERT(srep->bind_addr_str &&
+ strcmp(srep->bind_addr_str, "127.0.0.98") == 0);
+ OSMO_ASSERT(do_vty_command(vty, "no local-ip") == CMD_SUCCESS);
+ OSMO_ASSERT(srep->bind_addr_str == NULL);
+
+ OSMO_ASSERT(srep->mtu == 0);
+ OSMO_ASSERT(do_vty_command(vty, "mtu 987") == CMD_SUCCESS);
+ OSMO_ASSERT(srep->mtu == 987);
+ OSMO_ASSERT(do_vty_command(vty, "no mtu") == CMD_SUCCESS);
+ OSMO_ASSERT(srep->mtu == 0);
+ };
+
+ OSMO_ASSERT(do_vty_command(vty, "enable") == CMD_SUCCESS);
+ OSMO_ASSERT(srep->enabled != 0);
+ OSMO_ASSERT(do_vty_command(vty, "disable") == CMD_SUCCESS);
+ OSMO_ASSERT(srep->enabled == 0);
+}
+
+static void test_stats_vty(void)
+{
+ struct osmo_stats_reporter *srep;
+ struct vty_test test;
+ struct vty *vty;
+
+ printf("Going to test VTY configuration of the stats subsystem\n");
+ vty = create_test_vty(&test);
+
+ /* Go to config node */
+ OSMO_ASSERT(do_vty_command(vty, "enable") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == ENABLE_NODE);
+ OSMO_ASSERT(do_vty_command(vty, "configure terminal") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == CONFIG_NODE);
+
+ /* Try to create invalid reporter */
+ OSMO_ASSERT(do_vty_command(vty, "stats reporter foobar") ==
+ CMD_ERR_NO_MATCH);
+
+ /* Set reporting interval */
+ OSMO_ASSERT(do_vty_command(vty, "stats interval 42") == CMD_SUCCESS);
+ OSMO_ASSERT(osmo_stats_config->interval == 42);
+
+ /* Create log reporter */
+ srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL);
+ OSMO_ASSERT(srep == NULL);
+ OSMO_ASSERT(do_vty_command(vty, "stats reporter log") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == CFG_STATS_NODE);
+ srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL);
+ OSMO_ASSERT(srep != NULL);
+ OSMO_ASSERT(srep->type == OSMO_STATS_REPORTER_LOG);
+ check_srep_vty_config(vty, srep);
+ OSMO_ASSERT(do_vty_command(vty, "exit") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == CONFIG_NODE);
+
+ /* Create statsd reporter */
+ srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL);
+ OSMO_ASSERT(srep == NULL);
+ OSMO_ASSERT(do_vty_command(vty, "stats reporter statsd") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == CFG_STATS_NODE);
+ srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL);
+ OSMO_ASSERT(srep != NULL);
+ OSMO_ASSERT(srep->type == OSMO_STATS_REPORTER_STATSD);
+ check_srep_vty_config(vty, srep);
+ OSMO_ASSERT(do_vty_command(vty, "exit") == CMD_SUCCESS);
+ OSMO_ASSERT(vty->node == CONFIG_NODE);
+
+ /* Destroy log reporter */
+ OSMO_ASSERT(osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL));
+ OSMO_ASSERT(do_vty_command(vty, "no stats reporter log") == CMD_SUCCESS);
+ OSMO_ASSERT(!osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL));
+
+ /* Destroy statsd reporter */
+ OSMO_ASSERT(osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL));
+ OSMO_ASSERT(do_vty_command(vty, "no stats reporter statsd") == CMD_SUCCESS);
+ OSMO_ASSERT(!osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL));
+
+ destroy_test_vty(&test, vty);
+}
+
+int main(int argc, char **argv)
+{
+ struct vty_app_info vty_info = {
+ .name = "VtyTest",
+ .version = 0,
+ .go_parent_cb = NULL,
+ .is_config_node = NULL,
+ };
+
+ const struct log_info_cat default_categories[] = {};
+
+ const struct log_info log_info = {
+ .cat = default_categories,
+ .num_cat = ARRAY_SIZE(default_categories),
+ };
+ void *stats_ctx = talloc_named_const(NULL, 1, "stats test context");
+
+ osmo_signal_register_handler(SS_L_VTY, vty_event_cb, NULL);
+
+ /* Fake logging. */
+ osmo_init_logging(&log_info);
+
+ /* Init stats */
+ osmo_stats_init(stats_ctx);
+
+ vty_init(&vty_info);
+
+ /* Setup VTY commands */
+ logging_vty_add_cmds(&log_info);
+ osmo_stats_vty_add_cmds();
+
+ test_cmd_string_from_valstr();
+ test_node_tree_structure();
+ test_stats_vty();
+
+ /* Leak check */
+ OSMO_ASSERT(talloc_total_blocks(stats_ctx) == 1);
+
+ printf("All tests passed\n");
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/tests/vty/vty_test.ok b/src/shared/libosmocore/tests/vty/vty_test.ok
new file mode 100644
index 00000000..a9e283d9
--- /dev/null
+++ b/src/shared/libosmocore/tests/vty/vty_test.ok
@@ -0,0 +1,113 @@
+Going to test vty_cmd_string_from_valstr()
+Tested with %s-strings, resulting cmd = '[prefix%s%s%s%s%s][foo%s%s%s%s%s][sep%s%s%s%s%s][bar%s%s%s%s%s][end%s%s%s%s%s]'
+Going to test VTY node tree structure
+Going to execute 'enable'
+Returned: 0, Current node: 3 '%s# '
+Going to execute 'configure terminal'
+Returned: 0, Current node: 4 '%s(config)# '
+Going to execute 'exit'
+Returned: 0, Current node: 3 '%s# '
+Going to execute 'configure terminal'
+Returned: 0, Current node: 4 '%s(config)# '
+Going to execute 'end'
+Returned: 0, Current node: 3 '%s# '
+Going to execute 'configure terminal'
+Returned: 0, Current node: 4 '%s(config)# '
+Going to execute 'log stderr'
+Returned: 0, Current node: 7 '%s(config-log)# '
+Going to execute 'exit'
+Returned: 0, Current node: 4 '%s(config)# '
+Going to execute 'log stderr'
+Returned: 0, Current node: 7 '%s(config-log)# '
+Going to execute 'end'
+Returned: 0, Current node: 3 '%s# '
+Going to execute 'configure terminal'
+Returned: 0, Current node: 4 '%s(config)# '
+Going to execute 'line vty'
+Returned: 0, Current node: 9 '%s(config-line)# '
+Going to execute 'exit'
+Returned: 0, Current node: 4 '%s(config)# '
+Going to execute 'line vty'
+Returned: 0, Current node: 9 '%s(config-line)# '
+Going to execute 'end'
+Returned: 0, Current node: 3 '%s# '
+Going to execute 'configure terminal'
+Returned: 0, Current node: 4 '%s(config)# '
+Going to execute 'log stderr'
+Returned: 0, Current node: 7 '%s(config-log)# '
+Going to execute 'line vty'
+Returned: 0, Current node: 9 '%s(config-line)# '
+Going to execute 'log stderr'
+Returned: 0, Current node: 7 '%s(config-log)# '
+Going to execute 'end'
+Returned: 0, Current node: 3 '%s# '
+Going to execute 'exit'
+Returned: 0, Current node: 3 '%s# '
+Going to test VTY configuration of the stats subsystem
+Going to execute 'enable'
+Returned: 0, Current node: 3 '%s# '
+Going to execute 'configure terminal'
+Returned: 0, Current node: 4 '%s(config)# '
+Going to execute 'stats reporter foobar'
+Returned: 2, Current node: 4 '%s(config)# '
+Going to execute 'stats interval 42'
+Returned: 0, Current node: 4 '%s(config)# '
+Going to execute 'stats reporter log'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'prefix myprefix'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'no prefix'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'level peer'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'level subscriber'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'level global'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'level foobar'
+Returned: 2, Current node: 8 '%s(config-stats)# '
+Going to execute 'enable'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'disable'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'exit'
+Returned: 0, Current node: 4 '%s(config)# '
+Going to execute 'stats reporter statsd'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'prefix myprefix'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'no prefix'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'level peer'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'level subscriber'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'level global'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'level foobar'
+Returned: 2, Current node: 8 '%s(config-stats)# '
+Going to execute 'remote-ip 127.0.0.99'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'remote-ip 678.0.0.99'
+Returned: 1, Current node: 8 '%s(config-stats)# '
+Going to execute 'remote-port 12321'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'local-ip 127.0.0.98'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'no local-ip'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'mtu 987'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'no mtu'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'enable'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'disable'
+Returned: 0, Current node: 8 '%s(config-stats)# '
+Going to execute 'exit'
+Returned: 0, Current node: 4 '%s(config)# '
+Going to execute 'no stats reporter log'
+Returned: 0, Current node: 4 '%s(config)# '
+Going to execute 'no stats reporter statsd'
+Returned: 0, Current node: 4 '%s(config)# '
+All tests passed
diff --git a/src/shared/libosmocore/tests/write_queue/wqueue_test.c b/src/shared/libosmocore/tests/write_queue/wqueue_test.c
new file mode 100644
index 00000000..827e4e84
--- /dev/null
+++ b/src/shared/libosmocore/tests/write_queue/wqueue_test.c
@@ -0,0 +1,81 @@
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/write_queue.h>
+
+static const struct log_info_cat default_categories[] = {
+};
+
+static const struct log_info log_info = {
+ .cat = default_categories,
+ .num_cat = ARRAY_SIZE(default_categories),
+};
+
+static void test_wqueue_limit(void)
+{
+ struct msgb *msg;
+ struct osmo_wqueue wqueue;
+ int rc;
+
+ osmo_wqueue_init(&wqueue, 0);
+ OSMO_ASSERT(wqueue.max_length == 0);
+ OSMO_ASSERT(wqueue.current_length == 0);
+ OSMO_ASSERT(wqueue.read_cb == NULL);
+ OSMO_ASSERT(wqueue.write_cb == NULL);
+ OSMO_ASSERT(wqueue.except_cb == NULL);
+
+ /* try to add and fail */
+ msg = msgb_alloc(4096, "msg1");
+ rc = osmo_wqueue_enqueue(&wqueue, msg);
+ OSMO_ASSERT(rc < 0);
+
+ /* add one and fail on the second */
+ wqueue.max_length = 1;
+ rc = osmo_wqueue_enqueue(&wqueue, msg);
+ OSMO_ASSERT(rc == 0);
+ OSMO_ASSERT(wqueue.current_length == 1);
+ msg = msgb_alloc(4096, "msg2");
+ rc = osmo_wqueue_enqueue(&wqueue, msg);
+ OSMO_ASSERT(rc < 0);
+
+ /* add one more */
+ wqueue.max_length = 2;
+ rc = osmo_wqueue_enqueue(&wqueue, msg);
+ OSMO_ASSERT(rc == 0);
+ OSMO_ASSERT(wqueue.current_length == 2);
+
+ /* release everything */
+ osmo_wqueue_clear(&wqueue);
+ OSMO_ASSERT(wqueue.current_length == 0);
+ OSMO_ASSERT(wqueue.max_length == 2);
+
+ /* Add two, fail on the third, free it and the queue */
+ msg = msgb_alloc(4096, "msg3");
+ rc = osmo_wqueue_enqueue(&wqueue, msg);
+ OSMO_ASSERT(rc == 0);
+ OSMO_ASSERT(wqueue.current_length == 1);
+ msg = msgb_alloc(4096, "msg4");
+ rc = osmo_wqueue_enqueue(&wqueue, msg);
+ OSMO_ASSERT(rc == 0);
+ OSMO_ASSERT(wqueue.current_length == 2);
+ msg = msgb_alloc(4096, "msg5");
+ rc = osmo_wqueue_enqueue(&wqueue, msg);
+ OSMO_ASSERT(rc < 0);
+ OSMO_ASSERT(wqueue.current_length == 2);
+ msgb_free(msg);
+ osmo_wqueue_clear(&wqueue);
+}
+
+int main(int argc, char **argv)
+{
+ struct log_target *stderr_target;
+
+ log_init(&log_info, NULL);
+ stderr_target = log_target_create_stderr();
+ log_add_target(stderr_target);
+ log_set_print_filename(stderr_target, 0);
+
+ test_wqueue_limit();
+
+ printf("Done\n");
+ return 0;
+}
diff --git a/src/shared/libosmocore/tests/write_queue/wqueue_test.ok b/src/shared/libosmocore/tests/write_queue/wqueue_test.ok
new file mode 100644
index 00000000..a965a70e
--- /dev/null
+++ b/src/shared/libosmocore/tests/write_queue/wqueue_test.ok
@@ -0,0 +1 @@
+Done
diff --git a/src/shared/libosmocore/utils/Makefile.am b/src/shared/libosmocore/utils/Makefile.am
index 4e7869e4..e95f546d 100644
--- a/src/shared/libosmocore/utils/Makefile.am
+++ b/src/shared/libosmocore/utils/Makefile.am
@@ -1,10 +1,19 @@
if ENABLE_UTILITIES
-INCLUDES = $(all_includes) -I$(top_srcdir)/include
-noinst_PROGRAMS = osmo-arfcn osmo-auc-gen
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include $(TALLOC_CFLAGS)
+AM_CFLAGS = -Wall
+
+bin_PROGRAMS = osmo-arfcn osmo-auc-gen
osmo_arfcn_SOURCES = osmo-arfcn.c
osmo_arfcn_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
osmo_auc_gen_SOURCES = osmo-auc-gen.c
osmo_auc_gen_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
+
+if ENABLE_PCSC
+noinst_PROGRAMS = osmo-sim-test
+osmo_sim_test_SOURCES = osmo-sim-test.c
+osmo_sim_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la $(top_builddir)/src/sim/libosmosim.la $(PCSC_LIBS)
+osmo_sim_test_CFLAGS = $(PCSC_CFLAGS)
+endif
endif
diff --git a/src/shared/libosmocore/utils/conv_codes_gsm.py b/src/shared/libosmocore/utils/conv_codes_gsm.py
new file mode 100644
index 00000000..1bee6db1
--- /dev/null
+++ b/src/shared/libosmocore/utils/conv_codes_gsm.py
@@ -0,0 +1,706 @@
+#!/usr/bin/python2
+
+from conv_gen import ConvolutionalCode
+from conv_gen import poly
+
+# Polynomials according to 3GPP TS 05.03 Annex B
+G0 = poly(0, 3, 4)
+G1 = poly(0, 1, 3, 4)
+G2 = poly(0, 2, 4)
+G3 = poly(0, 1, 2, 3, 4)
+G4 = poly(0, 2, 3, 5, 6)
+G5 = poly(0, 1, 4, 6)
+G6 = poly(0, 1, 2, 3, 4, 6)
+G7 = poly(0, 1, 2, 3, 6)
+
+# Shared polynomials
+shared_polys = {
+ "xcch" : [
+ ( G0, 1 ),
+ ( G1, 1 ),
+ ],
+ "mcs" : [
+ ( G4, 1 ),
+ ( G7, 1 ),
+ ( G5, 1 ),
+ ],
+}
+
+# Convolutional code definitions
+conv_codes = [
+ # xCCH definition
+ ConvolutionalCode(
+ 224,
+ shared_polys["xcch"],
+ name = "xcch",
+ description = [
+ "xCCH convolutional code:",
+ "228 bits blocks, rate 1/2, k = 5",
+ "G0 = 1 + D3 + D4",
+ "G1 = 1 + D + D3 + D4",
+ ]
+ ),
+
+ # RACH definition
+ ConvolutionalCode(
+ 14,
+ shared_polys["xcch"],
+ name = "rach",
+ description = ["RACH convolutional code"]
+ ),
+
+ # SCH definition
+ ConvolutionalCode(
+ 35,
+ shared_polys["xcch"],
+ name = "sch",
+ description = ["SCH convolutional code"]
+ ),
+
+ # CS2 definition
+ ConvolutionalCode(
+ 290,
+ shared_polys["xcch"],
+ puncture = [
+ 15, 19, 23, 27, 31, 35, 43, 47, 51, 55, 59, 63, 67, 71,
+ 75, 79, 83, 91, 95, 99, 103, 107, 111, 115, 119, 123, 127, 131,
+ 139, 143, 147, 151, 155, 159, 163, 167, 171, 175, 179, 187, 191, 195,
+ 199, 203, 207, 211, 215, 219, 223, 227, 235, 239, 243, 247, 251, 255,
+ 259, 263, 267, 271, 275, 283, 287, 291, 295, 299, 303, 307, 311, 315,
+ 319, 323, 331, 335, 339, 343, 347, 351, 355, 359, 363, 367, 371, 379,
+ 383, 387, 391, 395, 399, 403, 407, 411, 415, 419, 427, 431, 435, 439,
+ 443, 447, 451, 455, 459, 463, 467, 475, 479, 483, 487, 491, 495, 499,
+ 503, 507, 511, 515, 523, 527, 531, 535, 539, 543, 547, 551, 555, 559,
+ 563, 571, 575, 579, 583, 587, -1
+ ],
+ name = "cs2",
+ description = [
+ "CS2 convolutional code:",
+ "G0 = 1 + D3 + D4",
+ "G1 = 1 + D + D3 + D4",
+ ]
+ ),
+
+ # CS3 definition
+ ConvolutionalCode(
+ 334,
+ shared_polys["xcch"],
+ puncture = [
+ 15, 17, 21, 23, 27, 29, 33, 35, 39, 41, 45, 47, 51, 53,
+ 57, 59, 63, 65, 69, 71, 75, 77, 81, 83, 87, 89, 93, 95,
+ 99, 101, 105, 107, 111, 113, 117, 119, 123, 125, 129, 131, 135, 137,
+ 141, 143, 147, 149, 153, 155, 159, 161, 165, 167, 171, 173, 177, 179,
+ 183, 185, 189, 191, 195, 197, 201, 203, 207, 209, 213, 215, 219, 221,
+ 225, 227, 231, 233, 237, 239, 243, 245, 249, 251, 255, 257, 261, 263,
+ 267, 269, 273, 275, 279, 281, 285, 287, 291, 293, 297, 299, 303, 305,
+ 309, 311, 315, 317, 321, 323, 327, 329, 333, 335, 339, 341, 345, 347,
+ 351, 353, 357, 359, 363, 365, 369, 371, 375, 377, 381, 383, 387, 389,
+ 393, 395, 399, 401, 405, 407, 411, 413, 417, 419, 423, 425, 429, 431,
+ 435, 437, 441, 443, 447, 449, 453, 455, 459, 461, 465, 467, 471, 473,
+ 477, 479, 483, 485, 489, 491, 495, 497, 501, 503, 507, 509, 513, 515,
+ 519, 521, 525, 527, 531, 533, 537, 539, 543, 545, 549, 551, 555, 557,
+ 561, 563, 567, 569, 573, 575, 579, 581, 585, 587, 591, 593, 597, 599,
+ 603, 605, 609, 611, 615, 617, 621, 623, 627, 629, 633, 635, 639, 641,
+ 645, 647, 651, 653, 657, 659, 663, 665, 669, 671, -1
+ ],
+ name = "cs3",
+ description = [
+ "CS3 convolutional code:",
+ "G0 = 1 + D3 + D4",
+ "G1 = 1 + D + D3 + D4",
+ ]
+ ),
+
+ # TCH_AFS_12_2 definition
+ ConvolutionalCode(
+ 250,
+ [
+ ( 1, 1 ),
+ ( G1, G0 ),
+ ],
+ puncture = [
+ 321, 325, 329, 333, 337, 341, 345, 349, 353, 357, 361, 363,
+ 365, 369, 373, 377, 379, 381, 385, 389, 393, 395, 397, 401,
+ 405, 409, 411, 413, 417, 421, 425, 427, 429, 433, 437, 441,
+ 443, 445, 449, 453, 457, 459, 461, 465, 469, 473, 475, 477,
+ 481, 485, 489, 491, 493, 495, 497, 499, 501, 503, 505, 507,
+ -1
+ ],
+ name = 'tch_afs_12_2',
+ description = [
+ "TCH/AFS 12.2 kbits convolutional code:",
+ "250 bits block, rate 1/2, punctured",
+ "G0/G0 = 1",
+ "G1/G0 = 1 + D + D3 + D4 / 1 + D3 + D4",
+ ]
+ ),
+
+ # TCH_AFS_10_2 definition
+ ConvolutionalCode(
+ 210,
+ [
+ ( G1, G3 ),
+ ( G2, G3 ),
+ ( 1, 1 ),
+ ],
+ puncture = [
+ 1, 4, 7, 10, 16, 19, 22, 28, 31, 34, 40, 43,
+ 46, 52, 55, 58, 64, 67, 70, 76, 79, 82, 88, 91,
+ 94, 100, 103, 106, 112, 115, 118, 124, 127, 130, 136, 139,
+ 142, 148, 151, 154, 160, 163, 166, 172, 175, 178, 184, 187,
+ 190, 196, 199, 202, 208, 211, 214, 220, 223, 226, 232, 235,
+ 238, 244, 247, 250, 256, 259, 262, 268, 271, 274, 280, 283,
+ 286, 292, 295, 298, 304, 307, 310, 316, 319, 322, 325, 328,
+ 331, 334, 337, 340, 343, 346, 349, 352, 355, 358, 361, 364,
+ 367, 370, 373, 376, 379, 382, 385, 388, 391, 394, 397, 400,
+ 403, 406, 409, 412, 415, 418, 421, 424, 427, 430, 433, 436,
+ 439, 442, 445, 448, 451, 454, 457, 460, 463, 466, 469, 472,
+ 475, 478, 481, 484, 487, 490, 493, 496, 499, 502, 505, 508,
+ 511, 514, 517, 520, 523, 526, 529, 532, 535, 538, 541, 544,
+ 547, 550, 553, 556, 559, 562, 565, 568, 571, 574, 577, 580,
+ 583, 586, 589, 592, 595, 598, 601, 604, 607, 609, 610, 613,
+ 616, 619, 621, 622, 625, 627, 628, 631, 633, 634, 636, 637,
+ 639, 640, -1
+ ],
+ name = 'tch_afs_10_2',
+ description = [
+ "TCH/AFS 10.2 kbits convolutional code:",
+ "G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4",
+ "G2/G3 = 1 + D2 + D4 / 1 + D + D2 + D3 + D4",
+ "G3/G3 = 1",
+ ]
+ ),
+
+ # TCH_AFS_7_95 definition
+ ConvolutionalCode(
+ 165,
+ [
+ ( 1, 1 ),
+ ( G5, G4 ),
+ ( G6, G4 ),
+ ],
+ puncture = [
+ 1, 2, 4, 5, 8, 22, 70, 118, 166, 214, 262, 310,
+ 317, 319, 325, 332, 334, 341, 343, 349, 356, 358, 365, 367,
+ 373, 380, 382, 385, 389, 391, 397, 404, 406, 409, 413, 415,
+ 421, 428, 430, 433, 437, 439, 445, 452, 454, 457, 461, 463,
+ 469, 476, 478, 481, 485, 487, 490, 493, 500, 502, 503, 505,
+ 506, 508, 509, 511, 512, -1
+ ],
+ name = 'tch_afs_7_95',
+ description = [
+ "TCH/AFS 7.95 kbits convolutional code:",
+ "G4/G4 = 1",
+ "G5/G4 = 1 + D + D4 + D6 / 1 + D2 + D3 + D5 + D6",
+ "G6/G4 = 1 + D + D2 + D3 + D4 + D6 / 1 + D2 + D3 + D5 + D6",
+ ]
+ ),
+
+ # TCH_AFS_7_4 definition
+ ConvolutionalCode(
+ 154,
+ [
+ ( G1, G3 ),
+ ( G2, G3 ),
+ ( 1, 1 ),
+ ],
+ puncture = [
+ 0, 355, 361, 367, 373, 379, 385, 391, 397, 403, 409, 415,
+ 421, 427, 433, 439, 445, 451, 457, 460, 463, 466, 468, 469,
+ 471, 472, -1
+ ],
+ name = 'tch_afs_7_4',
+ description = [
+ "TCH/AFS 7.4 kbits convolutional code:",
+ "G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4",
+ "G2/G3 = 1 + D2 + D4 / 1 + D + D2 + D3 + D4",
+ "G3/G3 = 1",
+ ]
+ ),
+
+ # TCH_AFS_6_7 definition
+ ConvolutionalCode(
+ 140,
+ [
+ ( G1, G3 ),
+ ( G2, G3 ),
+ ( 1, 1 ),
+ ( 1, 1 ),
+ ],
+ puncture = [
+ 1, 3, 7, 11, 15, 27, 39, 55, 67, 79, 95, 107,
+ 119, 135, 147, 159, 175, 187, 199, 215, 227, 239, 255, 267,
+ 279, 287, 291, 295, 299, 303, 307, 311, 315, 319, 323, 327,
+ 331, 335, 339, 343, 347, 351, 355, 359, 363, 367, 369, 371,
+ 375, 377, 379, 383, 385, 387, 391, 393, 395, 399, 401, 403,
+ 407, 409, 411, 415, 417, 419, 423, 425, 427, 431, 433, 435,
+ 439, 441, 443, 447, 449, 451, 455, 457, 459, 463, 465, 467,
+ 471, 473, 475, 479, 481, 483, 487, 489, 491, 495, 497, 499,
+ 503, 505, 507, 511, 513, 515, 519, 521, 523, 527, 529, 531,
+ 535, 537, 539, 543, 545, 547, 549, 551, 553, 555, 557, 559,
+ 561, 563, 565, 567, 569, 571, 573, 575, -1
+ ],
+ name = 'tch_afs_6_7',
+ description = [
+ "TCH/AFS 6.7 kbits convolutional code:",
+ "G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4",
+ "G2/G3 = 1 + D2 + D4 / 1 + D + D2 + D3 + D4",
+ "G3/G3 = 1",
+ "G3/G3 = 1",
+ ]
+ ),
+
+ # TCH_AFS_5_9 definition
+ ConvolutionalCode(
+ 124,
+ [
+ ( G4, G6 ),
+ ( G5, G6 ),
+ ( 1, 1),
+ ( 1, 1),
+ ],
+ puncture = [
+ 0, 1, 3, 5, 7, 11, 15, 31, 47, 63, 79, 95,
+ 111, 127, 143, 159, 175, 191, 207, 223, 239, 255, 271, 287,
+ 303, 319, 327, 331, 335, 343, 347, 351, 359, 363, 367, 375,
+ 379, 383, 391, 395, 399, 407, 411, 415, 423, 427, 431, 439,
+ 443, 447, 455, 459, 463, 467, 471, 475, 479, 483, 487, 491,
+ 495, 499, 503, 507, 509, 511, 512, 513, 515, 516, 517, 519,
+ -1
+ ],
+ name = 'tch_afs_5_9',
+ description = [
+ "TCH/AFS 5.9 kbits convolutional code:",
+ "124 bits",
+ "G4/G6 = 1 + D2 + D3 + D5 + D6 / 1 + D + D2 + D3 + D4 + D6",
+ "G5/G6 = 1 + D + D4 + D6 / 1 + D + D2 + D3 + D4 + D6",
+ "G6/G6 = 1",
+ "G6/G6 = 1",
+ ]
+ ),
+
+ # TCH_AFS_5_15 definition
+ ConvolutionalCode(
+ 109,
+ [
+ ( G1, G3 ),
+ ( G1, G3 ),
+ ( G2, G3 ),
+ ( 1, 1 ),
+ ( 1, 1 ),
+ ],
+ puncture = [
+ 0, 4, 5, 9, 10, 14, 15, 20, 25, 30, 35, 40,
+ 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160,
+ 170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 280,
+ 290, 300, 310, 315, 320, 325, 330, 334, 335, 340, 344, 345,
+ 350, 354, 355, 360, 364, 365, 370, 374, 375, 380, 384, 385,
+ 390, 394, 395, 400, 404, 405, 410, 414, 415, 420, 424, 425,
+ 430, 434, 435, 440, 444, 445, 450, 454, 455, 460, 464, 465,
+ 470, 474, 475, 480, 484, 485, 490, 494, 495, 500, 504, 505,
+ 510, 514, 515, 520, 524, 525, 529, 530, 534, 535, 539, 540,
+ 544, 545, 549, 550, 554, 555, 559, 560, 564, -1
+ ],
+ name = 'tch_afs_5_15',
+ description = [
+ "TCH/AFS 5.15 kbits convolutional code:",
+ "G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4",
+ "G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4",
+ "G2/G3 = 1 + D2 + D4 / 1 + D + D2 + D3 + D4",
+ "G3/G3 = 1",
+ "G3/G3 = 1",
+ ]
+ ),
+
+ # TCH_AFS_4_75 definition
+ ConvolutionalCode(
+ 101,
+ [
+ ( G4, G6 ),
+ ( G4, G6 ),
+ ( G5, G6 ),
+ ( 1, 1 ),
+ ( 1, 1 ),
+ ],
+ puncture = [
+ 0, 1, 2, 4, 5, 7, 9, 15, 25, 35, 45, 55,
+ 65, 75, 85, 95, 105, 115, 125, 135, 145, 155, 165, 175,
+ 185, 195, 205, 215, 225, 235, 245, 255, 265, 275, 285, 295,
+ 305, 315, 325, 335, 345, 355, 365, 375, 385, 395, 400, 405,
+ 410, 415, 420, 425, 430, 435, 440, 445, 450, 455, 459, 460,
+ 465, 470, 475, 479, 480, 485, 490, 495, 499, 500, 505, 509,
+ 510, 515, 517, 519, 520, 522, 524, 525, 526, 527, 529, 530,
+ 531, 532, 534, -1
+ ],
+ name = 'tch_afs_4_75',
+ description = [
+ "TCH/AFS 4.75 kbits convolutional code:",
+ "G4/G6 = 1 + D2 + D3 + D5 + D6 / 1 + D + D2 + D3 + D4 + D6",
+ "G4/G6 = 1 + D2 + D3 + D5 + D6 / 1 + D + D2 + D3 + D4 + D6",
+ "G5/G6 = 1 + D + D4 + D6 / 1 + D + D2 + D3 + D4 + D6",
+ "G6/G6 = 1",
+ "G6/G6 = 1",
+ ]
+ ),
+
+ # TCH_FR definition
+ ConvolutionalCode(
+ 185,
+ shared_polys["xcch"],
+ name = "tch_fr",
+ description = ["TCH/F convolutional code"]
+ ),
+
+ # TCH_HR definition
+ ConvolutionalCode(
+ 98,
+ [
+ ( G4, 1 ),
+ ( G5, 1 ),
+ ( G6, 1 ),
+ ],
+ puncture = [
+ 1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34,
+ 37, 40, 43, 46, 49, 52, 55, 58, 61, 64, 67, 70,
+ 73, 76, 79, 82, 85, 88, 91, 94, 97, 100, 103, 106,
+ 109, 112, 115, 118, 121, 124, 127, 130, 133, 136, 139, 142,
+ 145, 148, 151, 154, 157, 160, 163, 166, 169, 172, 175, 178,
+ 181, 184, 187, 190, 193, 196, 199, 202, 205, 208, 211, 214,
+ 217, 220, 223, 226, 229, 232, 235, 238, 241, 244, 247, 250,
+ 253, 256, 259, 262, 265, 268, 271, 274, 277, 280, 283, 295,
+ 298, 301, 304, 307, 310, -1,
+ ],
+ name = "tch_hr",
+ description = ["TCH/H convolutional code"]
+ ),
+
+ # TCH_AHS_7_95 definition
+ ConvolutionalCode(
+ 129,
+ [
+ ( 1, 1 ),
+ ( G1, G0 ),
+ ],
+ puncture = [
+ 1, 3, 5, 7, 11, 15, 19, 23, 27, 31, 35, 43,
+ 47, 51, 55, 59, 63, 67, 71, 79, 83, 87, 91, 95,
+ 99, 103, 107, 115, 119, 123, 127, 131, 135, 139, 143, 151,
+ 155, 159, 163, 167, 171, 175, 177, 179, 183, 185, 187, 191,
+ 193, 195, 197, 199, 203, 205, 207, 211, 213, 215, 219, 221,
+ 223, 227, 229, 231, 233, 235, 239, 241, 243, 247, 249, 251,
+ 255, 257, 259, 261, 263, 265, -1,
+ ],
+ name = "tch_ahs_7_95",
+ description = ["TCH/AHS 7.95 kbits convolutional code"]
+ ),
+
+ # TCH_AHS_7_4 definition
+ ConvolutionalCode(
+ 126,
+ [
+ ( 1, 1 ),
+ ( G1, G0 ),
+ ],
+ puncture = [
+ 1, 3, 7, 11, 19, 23, 27, 35, 39, 43, 51, 55,
+ 59, 67, 71, 75, 83, 87, 91, 99, 103, 107, 115, 119,
+ 123, 131, 135, 139, 143, 147, 151, 155, 159, 163, 167, 171,
+ 175, 179, 183, 187, 191, 195, 199, 203, 207, 211, 215, 219,
+ 221, 223, 227, 229, 231, 235, 237, 239, 243, 245, 247, 251,
+ 253, 255, 257, 259, -1,
+ ],
+ name = "tch_ahs_7_4",
+ description = ["TCH/AHS 7.4 kbits convolutional code"]
+ ),
+
+ # TCH_AHS_6_7 definition
+ ConvolutionalCode(
+ 116,
+ [
+ ( 1, 1 ),
+ ( G1, G0 ),
+ ],
+ puncture = [
+ 1, 3, 9, 19, 29, 39, 49, 59, 69, 79, 89, 99,
+ 109, 119, 129, 139, 149, 159, 167, 169, 177, 179, 187, 189,
+ 197, 199, 203, 207, 209, 213, 217, 219, 223, 227, 229, 231,
+ 233, 235, 237, 239, -1,
+ ],
+ name = "tch_ahs_6_7",
+ description = ["TCH/AHS 6.7 kbits convolutional code"]
+ ),
+
+ # TCH_AHS_5_9 definition
+ ConvolutionalCode(
+ 108,
+ [
+ ( 1, 1 ),
+ ( G1, G0 ),
+ ],
+ puncture = [
+ 1, 15, 71, 127, 139, 151, 163, 175, 187, 195, 203, 211,
+ 215, 219, 221, 223, -1,
+ ],
+ name = "tch_ahs_5_9",
+ description = ["TCH/AHS 5.9 kbits convolutional code"]
+ ),
+
+ # TCH_AHS_5_15 definition
+ ConvolutionalCode(
+ 97,
+ [
+ ( G1, G3 ),
+ ( G2, G3 ),
+ ( 1, 1 ),
+ ],
+ puncture = [
+ 0, 1, 3, 4, 6, 9, 12, 15, 18, 21, 27, 33,
+ 39, 45, 51, 54, 57, 63, 69, 75, 81, 87, 90, 93,
+ 99, 105, 111, 117, 123, 126, 129, 135, 141, 147, 153, 159,
+ 162, 165, 168, 171, 174, 177, 180, 183, 186, 189, 192, 195,
+ 198, 201, 204, 207, 210, 213, 216, 219, 222, 225, 228, 231,
+ 234, 237, 240, 243, 244, 246, 249, 252, 255, 256, 258, 261,
+ 264, 267, 268, 270, 273, 276, 279, 280, 282, 285, 288, 289,
+ 291, 294, 295, 297, 298, 300, 301, -1,
+ ],
+ name = "tch_ahs_5_15",
+ description = ["TCH/AHS 5.15 kbits convolutional code"]
+ ),
+
+ # TCH_AHS_4_75 definition
+ ConvolutionalCode(
+ 89,
+ [
+ ( 1, 1 ),
+ ( G5, G4 ),
+ ( G6, G4 ),
+ ],
+ puncture = [
+ 1, 2, 4, 5, 7, 8, 10, 13, 16, 22, 28, 34,
+ 40, 46, 52, 58, 64, 70, 76, 82, 88, 94, 100, 106,
+ 112, 118, 124, 130, 136, 142, 148, 151, 154, 160, 163, 166,
+ 172, 175, 178, 184, 187, 190, 196, 199, 202, 208, 211, 214,
+ 220, 223, 226, 232, 235, 238, 241, 244, 247, 250, 253, 256,
+ 259, 262, 265, 268, 271, 274, 275, 277, 278, 280, 281, 283,
+ 284, -1,
+ ],
+ name = "tch_ahs_4_75",
+ description = ["TCH/AHS 4.75 kbits convolutional code"]
+ ),
+
+ # EDGE MCS1_DL_HDR definition
+ ConvolutionalCode(
+ 36,
+ shared_polys["mcs"],
+ name = "mcs1_dl_hdr",
+ term_type = "CONV_TERM_TAIL_BITING",
+ description = [
+ "EDGE MCS-1 DL header convolutional code:",
+ "42 bits blocks, rate 1/3, k = 7",
+ "G4 = 1 + D2 + D3 + D5 + D6",
+ "G7 = 1 + D + D2 + D3 + D6",
+ "G5 = 1 + D + D4 + D6"
+ ]
+ ),
+
+ # EDGE MCS1_UL_HDR definition
+ ConvolutionalCode(
+ 39,
+ shared_polys["mcs"],
+ name = "mcs1_ul_hdr",
+ term_type = "CONV_TERM_TAIL_BITING",
+ description = [
+ "EDGE MCS-1 UL header convolutional code:",
+ "45 bits blocks, rate 1/3, k = 7",
+ "G4 = 1 + D2 + D3 + D5 + D6",
+ "G7 = 1 + D + D2 + D3 + D6",
+ "G5 = 1 + D + D4 + D6"
+ ]
+ ),
+
+ # EDGE MCS1 definition
+ ConvolutionalCode(
+ 190,
+ shared_polys["mcs"],
+ name = "mcs1",
+ description = [
+ "EDGE MCS-1 data convolutional code:",
+ "196 bits blocks, rate 1/3, k = 7",
+ "G4 = 1 + D2 + D3 + D5 + D6",
+ "G7 = 1 + D + D2 + D3 + D6",
+ "G5 = 1 + D + D4 + D6"
+ ]
+ ),
+
+ # EDGE MCS2 definition
+ ConvolutionalCode(
+ 238,
+ shared_polys["mcs"],
+ name = "mcs2",
+ description = [
+ "EDGE MCS-2 data convolutional code:",
+ "244 bits blocks, rate 1/3, k = 7",
+ "G4 = 1 + D2 + D3 + D5 + D6",
+ "G7 = 1 + D + D2 + D3 + D6",
+ "G5 = 1 + D + D4 + D6"
+ ]
+ ),
+
+ # EDGE MCS3 definition
+ ConvolutionalCode(
+ 310,
+ shared_polys["mcs"],
+ name = "mcs3",
+ description = [
+ "EDGE MCS-3 data convolutional code:",
+ "316 bits blocks, rate 1/3, k = 7",
+ "G4 = 1 + D2 + D3 + D5 + D6",
+ "G7 = 1 + D + D2 + D3 + D6",
+ "G5 = 1 + D + D4 + D6"
+ ]
+ ),
+
+ # EDGE MCS4 definition
+ ConvolutionalCode(
+ 366,
+ shared_polys["mcs"],
+ name = "mcs4",
+ description = [
+ "EDGE MCS-4 data convolutional code:",
+ "372 bits blocks, rate 1/3, k = 7",
+ "G4 = 1 + D2 + D3 + D5 + D6",
+ "G7 = 1 + D + D2 + D3 + D6",
+ "G5 = 1 + D + D4 + D6"
+ ]
+ ),
+
+ # EDGE MCS5_DL_HDR definition
+ ConvolutionalCode(
+ 33,
+ shared_polys["mcs"],
+ name = "mcs5_dl_hdr",
+ term_type = "CONV_TERM_TAIL_BITING",
+ description = [
+ "EDGE MCS-5 DL header convolutional code:",
+ "39 bits blocks, rate 1/3, k = 7",
+ "G4 = 1 + D2 + D3 + D5 + D6",
+ "G7 = 1 + D + D2 + D3 + D6",
+ "G5 = 1 + D + D4 + D6"
+ ]
+ ),
+
+ # EDGE MCS5_UL_HDR definition
+ ConvolutionalCode(
+ 45,
+ shared_polys["mcs"],
+ name = "mcs5_ul_hdr",
+ term_type = "CONV_TERM_TAIL_BITING",
+ description = [
+ "EDGE MCS-5 UL header convolutional code:",
+ "51 bits blocks, rate 1/3, k = 7",
+ "G4 = 1 + D2 + D3 + D5 + D6",
+ "G7 = 1 + D + D2 + D3 + D6",
+ "G5 = 1 + D + D4 + D6"
+ ]
+ ),
+
+ # EDGE MCS5 definition
+ ConvolutionalCode(
+ 462,
+ shared_polys["mcs"],
+ name = "mcs5",
+ description = [
+ "EDGE MCS-5 data convolutional code:",
+ "468 bits blocks, rate 1/3, k = 7",
+ "G4 = 1 + D2 + D3 + D5 + D6",
+ "G7 = 1 + D + D2 + D3 + D6",
+ "G5 = 1 + D + D4 + D6"
+ ]
+ ),
+
+ # EDGE MCS6 definition
+ ConvolutionalCode(
+ 606,
+ shared_polys["mcs"],
+ name = "mcs6",
+ description = [
+ "EDGE MCS-6 data convolutional code:",
+ "612 bits blocks, rate 1/3, k = 7",
+ "G4 = 1 + D2 + D3 + D5 + D6",
+ "G7 = 1 + D + D2 + D3 + D6",
+ "G5 = 1 + D + D4 + D6"
+ ]
+ ),
+
+ # EDGE MCS7_DL_HDR definition
+ ConvolutionalCode(
+ 45,
+ shared_polys["mcs"],
+ name = "mcs7_dl_hdr",
+ term_type = "CONV_TERM_TAIL_BITING",
+ description = [
+ "EDGE MCS-7 DL header convolutional code:",
+ "51 bits blocks, rate 1/3, k = 7",
+ "G4 = 1 + D2 + D3 + D5 + D6",
+ "G7 = 1 + D + D2 + D3 + D6",
+ "G5 = 1 + D + D4 + D6"
+ ]
+ ),
+
+ # EDGE MCS7_UL_HDR definition
+ ConvolutionalCode(
+ 54,
+ shared_polys["mcs"],
+ name = "mcs7_ul_hdr",
+ term_type = "CONV_TERM_TAIL_BITING",
+ description = [
+ "EDGE MCS-7 UL header convolutional code:",
+ "60 bits blocks, rate 1/3, k = 7",
+ "G4 = 1 + D2 + D3 + D5 + D6",
+ "G7 = 1 + D + D2 + D3 + D6",
+ "G5 = 1 + D + D4 + D6"
+ ]
+ ),
+
+ # EDGE MCS7 definition
+ ConvolutionalCode(
+ 462,
+ shared_polys["mcs"],
+ name = "mcs7",
+ description = [
+ "EDGE MCS-7 data convolutional code:",
+ "468 bits blocks, rate 1/3, k = 7",
+ "G4 = 1 + D2 + D3 + D5 + D6",
+ "G7 = 1 + D + D2 + D3 + D6",
+ "G5 = 1 + D + D4 + D6"
+ ]
+ ),
+
+ # EDGE MCS8 definition
+ ConvolutionalCode(
+ 558,
+ shared_polys["mcs"],
+ name = "mcs8",
+ description = [
+ "EDGE MCS-8 data convolutional code:",
+ "564 bits blocks, rate 1/3, k = 7",
+ "G4 = 1 + D2 + D3 + D5 + D6",
+ "G7 = 1 + D + D2 + D3 + D6",
+ "G5 = 1 + D + D4 + D6"
+ ]
+ ),
+
+ # EDGE MCS9 definition
+ ConvolutionalCode(
+ 606,
+ shared_polys["mcs"],
+ name = "mcs9",
+ description = [
+ "EDGE MCS-9 data convolutional code:",
+ "612 bits blocks, rate 1/3, k = 7",
+ "G4 = 1 + D2 + D3 + D5 + D6",
+ "G7 = 1 + D + D2 + D3 + D6",
+ "G5 = 1 + D + D4 + D6"
+ ]
+ ),
+]
diff --git a/src/shared/libosmocore/utils/conv_gen.py b/src/shared/libosmocore/utils/conv_gen.py
new file mode 100644
index 00000000..60580edd
--- /dev/null
+++ b/src/shared/libosmocore/utils/conv_gen.py
@@ -0,0 +1,290 @@
+#!/usr/bin/python2
+
+mod_license = """
+/*
+ * Copyright (C) 2011-2016 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2016 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.
+ *
+ * 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.
+ */
+"""
+
+import sys, os, math
+from functools import reduce
+import conv_codes_gsm
+
+class ConvolutionalCode(object):
+
+ def __init__(self, block_len, polys, name,
+ description = None, puncture = [], term_type = None):
+ # Save simple params
+ self.block_len = block_len
+ self.k = 1
+ self.puncture = puncture
+ self.rate_inv = len(polys)
+ self.term_type = term_type
+
+ # Infos
+ self.name = name
+ self.description = description
+
+ # Handle polynomials (and check for recursion)
+ self.polys = [(1, 1) if x[0] == x[1] else x for x in polys]
+
+ # Determine the polynomial degree
+ for (x, y) in polys:
+ self.k = max(self.k, int(math.floor(math.log(max(x, y), 2))))
+ self.k = self.k + 1
+
+ self.poly_divider = 1
+ rp = [x[1] for x in self.polys if x[1] != 1]
+ if rp:
+ if not all([x == rp[0] for x in rp]):
+ raise ValueError("Bad polynomials: "
+ "Can't have multiple different divider polynomials!")
+
+ if not all([x[0] == 1 for x in polys if x[1] == 1]):
+ raise ValueError("Bad polynomials: "
+ "Can't have a '1' divider with a non '1' dividend "
+ "in a recursive code")
+
+ self.poly_divider = rp[0]
+
+ @property
+ def recursive(self):
+ return self.poly_divider != 1
+
+ @property
+ def _state_mask(self):
+ return (1 << (self.k - 1)) - 1
+
+ def next_state(self, state, bit):
+ nb = combine(
+ (state << 1) | bit,
+ self.poly_divider,
+ self.k,
+ )
+ return ((state << 1) | nb) & self._state_mask
+
+ def next_term_state(self, state):
+ return (state << 1) & self._state_mask
+
+ def next_output(self, state, bit, ns = None):
+ # Next state bit
+ if ns is None:
+ ns = self.next_state(state, bit)
+
+ src = (ns & 1) | (state << 1)
+
+ # Scan polynomials
+ rv = []
+ for p_n, p_d in self.polys:
+ if self.recursive and p_d == 1:
+ # No choice ... (systematic output in recursive case)
+ o = bit
+ else:
+ o = combine(src, p_n, self.k)
+ rv.append(o)
+
+ return rv
+
+ def next_term_output(self, state, ns = None):
+ # Next state bit
+ if ns is None:
+ ns = self.next_term_state(state)
+
+ src = (ns & 1) | (state << 1)
+
+ # Scan polynomials
+ rv = []
+ for p_n, p_d in self.polys:
+ if self.recursive and p_d == 1:
+ # Systematic output are replaced when in 'termination' mode
+ o = combine(src, self.poly_divider, self.k)
+ else:
+ o = combine(src, p_n, self.k)
+ rv.append(o)
+
+ return rv
+
+ def next(self, state, bit):
+ ns = self.next_state(state, bit)
+ nb = self.next_output(state, bit, ns = ns)
+ return ns, nb
+
+ def next_term(self, state):
+ ns = self.next_term_state(state)
+ nb = self.next_term_output(state, ns = ns)
+ return ns, nb
+
+ def _print_term(self, fi, num_states, pack = False):
+ items = []
+
+ for state in range(num_states):
+ if pack:
+ x = pack(self.next_term_output(state))
+ else:
+ x = self.next_term_state(state)
+
+ items.append(x)
+
+ # Up to 12 numbers should be placed per line
+ print_formatted(items, "%3d, ", 12, fi)
+
+ def _print_x(self, fi, num_states, pack = False):
+ items = []
+
+ for state in range(num_states):
+ if pack:
+ x0 = pack(self.next_output(state, 0))
+ x1 = pack(self.next_output(state, 1))
+ else:
+ x0 = self.next_state(state, 0)
+ x1 = self.next_state(state, 1)
+
+ items.append((x0, x1))
+
+ # Up to 4 blocks should be placed per line
+ print_formatted(items, "{ %2d, %2d }, ", 4, fi)
+
+ def _print_puncture(self, fi):
+ # Up to 12 numbers should be placed per line
+ print_formatted(self.puncture, "%3d, ", 12, fi)
+
+ def print_state_and_output(self, fi):
+ pack = lambda n: \
+ sum([x << (self.rate_inv - i - 1) for i, x in enumerate(n)])
+ num_states = 1 << (self.k - 1)
+
+ fi.write("static const uint8_t %s_state[][2] = {\n" % self.name)
+ self._print_x(fi, num_states)
+ fi.write("};\n\n")
+
+ fi.write("static const uint8_t %s_output[][2] = {\n" % self.name)
+ self._print_x(fi, num_states, pack)
+ fi.write("};\n\n")
+
+ if self.recursive:
+ fi.write("static const uint8_t %s_term_state[] = {\n" % self.name)
+ self._print_term(fi, num_states)
+ fi.write("};\n\n")
+
+ fi.write("static const uint8_t %s_term_output[] = {\n" % self.name)
+ self._print_term(fi, num_states, pack)
+ fi.write("};\n\n")
+
+ def gen_tables(self, pref, fi, shared_tables = None):
+ # Do not print shared tables
+ if shared_tables is None:
+ self.print_state_and_output(fi)
+ table_pref = self.name
+ else:
+ table_pref = shared_tables
+
+ if len(self.puncture):
+ fi.write("static const int %s_puncture[] = {\n" % self.name)
+ self._print_puncture(fi)
+ fi.write("};\n\n")
+
+ # Write description as a multi-line comment
+ if self.description is not None:
+ fi.write("/**\n")
+ for line in self.description:
+ fi.write(" * %s\n" % line)
+ fi.write(" */\n")
+
+ # Print a final convolutional code definition
+ fi.write("const struct osmo_conv_code %s_%s = {\n" % (pref, self.name))
+ fi.write("\t.N = %d,\n" % self.rate_inv)
+ fi.write("\t.K = %d,\n" % self.k)
+ fi.write("\t.len = %d,\n" % self.block_len)
+ fi.write("\t.next_output = %s_output,\n" % table_pref)
+ fi.write("\t.next_state = %s_state,\n" % table_pref)
+
+ if self.term_type is not None:
+ fi.write("\t.term = %s,\n" % self.term_type)
+
+ if self.recursive:
+ fi.write("\t.next_term_output = %s_term_output,\n" % table_pref)
+ fi.write("\t.next_term_state = %s_term_state,\n" % table_pref)
+
+ if len(self.puncture):
+ fi.write("\t.puncture = %s_puncture,\n" % self.name)
+ fi.write("};\n\n")
+
+poly = lambda *args: sum([(1 << x) for x in args])
+
+def combine(src, sel, nb):
+ x = src & sel
+ fn_xor = lambda x, y: x ^ y
+ return reduce(fn_xor, [(x >> n) & 1 for n in range(nb)])
+
+def print_formatted(items, format, count, fi):
+ counter = 0
+
+ # Print initial indent
+ fi.write("\t")
+
+ for item in items:
+ if counter > 0 and counter % count == 0:
+ fi.write("\n\t")
+
+ fi.write(format % item)
+ counter += 1
+
+ fi.write("\n")
+
+def print_shared(fi, shared_polys):
+ for (name, polys) in shared_polys.items():
+ # HACK
+ code = ConvolutionalCode(0, polys, name = name)
+ code.print_state_and_output(fi)
+
+def generate_codes(codes, path, prefix):
+ # Open a new file for writing
+ f = open(os.path.join(path, prefix + "_conv.c"), 'w')
+ f.write(mod_license + "\n")
+ f.write("#include <stdint.h>\n")
+ f.write("#include <osmocom/core/conv.h>\n\n")
+
+ # Print shared tables first
+ if hasattr(codes, "shared_polys"):
+ print_shared(f, codes.shared_polys)
+
+ # Generate the tables one by one
+ for code in codes.conv_codes:
+ sys.stderr.write("Generate '%s' definition\n" % code.name)
+
+ # Check whether shared polynomials are used
+ shared = None
+ if hasattr(codes, "shared_polys"):
+ for (name, polys) in codes.shared_polys.items():
+ if code.polys == polys:
+ shared = name
+ break
+
+ code.gen_tables(prefix, f, shared_tables = shared)
+
+if __name__ == '__main__':
+ path = sys.argv[1] if len(sys.argv) > 1 else os.getcwd()
+
+ sys.stderr.write("Generating convolutional codes...\n")
+
+ # Generate GSM specific codes
+ generate_codes(conv_codes_gsm, path, "gsm0503")
+
+ sys.stderr.write("Generation complete.\n")
diff --git a/src/shared/libosmocore/utils/osmo-arfcn.c b/src/shared/libosmocore/utils/osmo-arfcn.c
index 15adbca2..5103c97b 100644
--- a/src/shared/libosmocore/utils/osmo-arfcn.c
+++ b/src/shared/libosmocore/utils/osmo-arfcn.c
@@ -34,9 +34,8 @@ enum program_mode {
MODE_F2A,
};
-static int arfcn2freq(char *arfcn_str)
+static int arfcn2freq(int arfcn)
{
- int arfcn = atoi(arfcn_str);
uint16_t freq10u, freq10d;
if (arfcn < 0 || arfcn > 0xffff) {
@@ -53,25 +52,52 @@ static int arfcn2freq(char *arfcn_str)
}
printf("ARFCN %4d: Uplink %4u.%1u MHz / Downlink %4u.%1u MHz\n",
- arfcn, freq10u/10, freq10u%10, freq10d/10, freq10d%10);
+ arfcn & ~ARFCN_FLAG_MASK,
+ freq10u/10, freq10u%10, freq10d/10, freq10d%10);
return 0;
}
+static int freq2arfcn(int freq10, int uplink)
+{
+ uint16_t arfcn;
+
+ if (uplink != 0 && uplink != 1) {
+ fprintf(stderr, "Need to specify uplink or downlink\n");
+ return -EINVAL;
+ }
+
+ arfcn = gsm_freq102arfcn(freq10, uplink);
+
+ if (arfcn == 0xffff) {
+ fprintf(stderr, "Unable to find matching ARFCN\n");
+ return -EINVAL;
+ }
+
+ printf("%s: ARFCN %4d\n",
+ gsm_band_name(gsm_arfcn2band(arfcn)),
+ arfcn & ~ARFCN_FLAG_MASK);
+ return 0;
+}
+
static void help(const char *progname)
{
- printf("Usage: %s [-h] [-a arfcn] [-f freq] [-u|-d]\n",
+ printf("Usage: %s [-h] [-p] [-a arfcn] [-f freq] [-u|-d]\n",
progname);
}
int main(int argc, char **argv)
{
+ int arfcn, freq, pcs = 0, uplink = -1;
int opt;
char *param;
enum program_mode mode = MODE_NONE;
- while ((opt = getopt(argc, argv, "a:f:ud")) != -1) {
+ while ((opt = getopt(argc, argv, "pa:f:ud")) != -1) {
switch (opt) {
+ case 'p':
+ pcs = 1;
+ break;
case 'a':
mode = MODE_A2F;
param = optarg;
@@ -80,6 +106,12 @@ int main(int argc, char **argv)
mode = MODE_F2A;
param = optarg;
break;
+ case 'u':
+ uplink = 1;
+ break;
+ case 'd':
+ uplink = 0;
+ break;
case 'h':
help(argv[0]);
exit(0);
@@ -95,7 +127,14 @@ int main(int argc, char **argv)
exit(2);
break;
case MODE_A2F:
- arfcn2freq(param);
+ arfcn = atoi(param);
+ if (pcs)
+ arfcn |= ARFCN_PCS;
+ arfcn2freq(arfcn);
+ break;
+ case MODE_F2A:
+ freq = (int)(atof(param) * 10.0f);
+ freq2arfcn(freq, uplink);
break;
}
diff --git a/src/shared/libosmocore/utils/osmo-auc-gen.c b/src/shared/libosmocore/utils/osmo-auc-gen.c
index 7a3c124c..3b3e5577 100644
--- a/src/shared/libosmocore/utils/osmo-auc-gen.c
+++ b/src/shared/libosmocore/utils/osmo-auc-gen.c
@@ -25,8 +25,11 @@
#include <stdio.h>
#include <errno.h>
#include <string.h>
+#include <time.h>
#include <getopt.h>
#include <unistd.h>
+#include <inttypes.h>
+#include <time.h>
#include <osmocom/crypt/auth.h>
#include <osmocom/core/utils.h>
@@ -45,18 +48,18 @@ static void dump_triplets_dat(struct osmo_auth_vector *vec)
static void dump_auth_vec(struct osmo_auth_vector *vec)
{
- printf("RAND:\t%s\n", osmo_hexdump(vec->rand, sizeof(vec->rand)));
+ printf("RAND:\t%s\n", osmo_hexdump_nospc(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));
+ 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));
}
if (vec->auth_types & OSMO_AUTH_TYPE_GSM) {
- printf("SRES:\t%s\n", osmo_hexdump(vec->sres, sizeof(vec->sres)));
- printf("Kc:\t%s\n", osmo_hexdump(vec->kc, sizeof(vec->kc)));
+ printf("SRES:\t%s\n", osmo_hexdump_nospc(vec->sres, sizeof(vec->sres)));
+ printf("Kc:\t%s\n", osmo_hexdump_nospc(vec->kc, sizeof(vec->kc)));
}
}
@@ -73,7 +76,7 @@ static void help()
"-k --key\tSpecify Ki / K\n"
"-o --opc\tSpecify OPC (only for 3G)\n"
"-O --op\tSpecify OP (only for 3G)\n"
- "-a --amf\tSpecify AMF (only for 3G)\n"
+ "-f --amf\tSpecify AMF (only for 3G)\n"
"-s --sqn\tSpecify SQN (only for 3G)\n"
"-A --auts\tSpecify AUTS (only for 3G)\n"
"-r --rand\tSpecify random value\n"
@@ -212,12 +215,15 @@ int main(int argc, char **argv)
}
if (!rand_is_set) {
+ int i;
printf("WARNING: We're using really weak random numbers!\n\n");
srand(time(NULL));
- *(uint32_t *)&_rand[0] = rand();
- *(uint32_t *)(&_rand[4]) = rand();
- *(uint32_t *)(&_rand[8]) = rand();
- *(uint32_t *)(&_rand[12]) = rand();
+
+ for (i = 0; i < 4; ++i) {
+ uint32_t r;
+ r = rand();
+ memcpy(&_rand[i*4], &r, 4);
+ }
}
if (test_aud.type == OSMO_AUTH_TYPE_NONE ||
@@ -246,7 +252,7 @@ int main(int argc, char **argv)
dump_auth_vec(vec);
if (auts_is_set)
- printf("AUTS success: SEQ.MS = %lu\n", test_aud.u.umts.sqn);
+ printf("AUTS success: SEQ.MS = %" PRIu64 "\n", test_aud.u.umts.sqn);
exit(0);
}
diff --git a/src/shared/libosmocore/utils/osmo-sim-test.c b/src/shared/libosmocore/utils/osmo-sim-test.c
new file mode 100644
index 00000000..7b79c589
--- /dev/null
+++ b/src/shared/libosmocore/utils/osmo-sim-test.c
@@ -0,0 +1,420 @@
+/* libosmosim test application - currently simply dumps a USIM */
+/* (C) 2012 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/sim/sim.h>
+#include <osmocom/gsm/tlv.h>
+
+
+/* FIXME: this needs to be moved to card_fs_uicc.c */
+
+/* 11.1.1 */
+static struct msgb *_select_file(struct osim_chan_hdl *st, uint8_t p1, uint8_t p2,
+ const uint8_t *data, uint8_t data_len)
+{
+ struct msgb *msg, *resp;
+ char *dst;
+
+ msg = osim_new_apdumsg(0x00, 0xA4, p1, p2, data_len, 256);
+ dst = msgb_put(msg, data_len);
+ memcpy(dst, data, data_len);
+
+ osim_transceive_apdu(st, msg);
+
+ return msg;
+}
+
+/* 11.1.1 */
+static struct msgb *select_adf(struct osim_chan_hdl *st, const uint8_t *adf, uint8_t adf_len)
+{
+ int sw;
+
+ return _select_file(st, 0x04, 0x04, adf,adf_len);
+}
+
+/* 11.1.1 */
+static struct msgb *select_file(struct osim_chan_hdl *st, uint16_t fid)
+{
+ uint16_t cfid = htons(fid);
+
+ return _select_file(st, 0x00, 0x04, (uint8_t *)&cfid, 2);
+}
+
+/* 11.1.9 */
+static int verify_pin(struct osim_chan_hdl *st, uint8_t pin_nr, uint8_t *pin)
+{
+ struct msgb *msg;
+ char *pindst;
+ int sw;
+
+ if (strlen(pin) > 8)
+ return -EINVAL;
+
+ msg = osim_new_apdumsg(0x00, 0x20, 0x00, pin_nr, 8, 0);
+ pindst = msgb_put(msg, 8);
+ memset(pindst, 0xFF, 8);
+ strncpy(pindst, pin, strlen(pin));
+
+ return osim_transceive_apdu(st, msg);
+}
+
+/* 11.1.5 */
+static struct msgb *read_record_nr(struct osim_chan_hdl *st, uint8_t rec_nr, uint16_t rec_size)
+{
+ struct msgb *msg;
+
+ msg = osim_new_apdumsg(0x00, 0xB2, rec_nr, 0x04, 0, rec_size);
+
+ osim_transceive_apdu(st, msg);
+
+ return msg;
+}
+
+/* 11.1.6 */
+static struct msgb *update_record_nr(struct osim_chan_hdl *st, uint8_t rec_nr,
+ const uint8_t *data, uint16_t rec_size)
+{
+ struct msgb *msg;
+ uint8_t *cur;
+
+ msg = osim_new_apdumsg(0x00, 0xDC, rec_nr, 0x04, rec_size, 0);
+ cur = msgb_put(msg, rec_size);
+ memcpy(cur, data, rec_size);
+
+ osim_transceive_apdu(st, msg);
+
+ return msg;
+}
+
+/* 11.1.3 */
+static struct msgb *read_binary(struct osim_chan_hdl *st, uint16_t offset, uint16_t len)
+{
+ struct msgb *msg;
+
+ if (offset > 0x7fff || len > 256)
+ return NULL;
+
+ msg = osim_new_apdumsg(0x00, 0xB0, offset >> 8, offset & 0xff, 0, len & 0xff);
+
+ osim_transceive_apdu(st, msg);
+
+ return msg;
+}
+
+/* 11.1.4 */
+static struct msgb *update_binary(struct osim_chan_hdl *st, uint16_t offset,
+ const uint8_t *data, uint16_t len)
+{
+ struct msgb *msg;
+ uint8_t *cur;
+
+ if (offset > 0x7fff || len > 256)
+ return NULL;
+
+ msg = osim_new_apdumsg(0x00, 0xD6, offset >> 8, offset & 0xff, len & 0xff, 0);
+ cur = msgb_put(msg, len);
+ memcpy(cur, data, len);
+
+ osim_transceive_apdu(st, msg);
+
+ return msg;
+}
+
+
+
+static int dump_fcp_template(struct tlv_parsed *tp)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tp->lv); i++) {
+ if (TLVP_PRESENT(tp, i))
+ printf("Tag 0x%02x (%s): %s\n", i,
+ get_value_string(ts102221_fcp_vals, i),
+ osmo_hexdump(TLVP_VAL(tp, i), TLVP_LEN(tp, i)));
+ }
+
+ return 0;
+}
+
+static int dump_fcp_template_msg(struct msgb *msg)
+{
+ struct tlv_parsed tp;
+ int rc;
+
+ rc = tlv_parse(&tp, &ts102221_fcp_tlv_def, msgb_apdu_de(msg)+2, msgb_apdu_le(msg)-2, 0, 0);
+ if (rc < 0)
+ return rc;
+
+ return dump_fcp_template(&tp);
+}
+
+struct osim_fcp_fd_decoded {
+ enum osim_file_type type;
+ enum osim_ef_type ef_type;
+ uint16_t rec_len;
+ uint8_t num_rec;
+};
+
+static const enum osim_file_type iso2ftype[8] = {
+ [0] = TYPE_EF,
+ [1] = TYPE_EF_INT,
+ [7] = TYPE_DF,
+};
+
+static const enum osim_ef_type iso2eftype[8] = {
+ [1] = EF_TYPE_TRANSP,
+ [2] = EF_TYPE_RECORD_FIXED,
+ [6] = EF_TYPE_RECORD_CYCLIC,
+};
+
+static int osim_fcp_fd_decode(struct osim_fcp_fd_decoded *ofd, const uint8_t *fcp, int fcp_len)
+{
+ memset(ofd, 0, sizeof(*ofd));
+
+ if (fcp_len != 2 && fcp_len != 5)
+ return -EINVAL;
+
+ ofd->type = iso2ftype[(fcp[0] >> 3) & 7];
+ if (ofd->type != TYPE_DF)
+ ofd->ef_type = iso2eftype[fcp[0] & 7];
+
+ if (fcp[1] != 0x21)
+ return -EINVAL;
+
+ if (fcp_len >= 5) {
+ ofd->rec_len = ntohs(*(uint16_t *)(fcp+2));
+ ofd->num_rec = fcp[4];
+ }
+
+ return 0;
+}
+
+extern struct osim_card_profile *osim_cprof_usim(void *ctx);
+
+static struct msgb *try_select_adf_usim(struct osim_chan_hdl *st)
+{
+ struct tlv_parsed tp;
+ struct osim_fcp_fd_decoded ofd;
+ struct msgb *msg, *msg2;
+ uint8_t *cur;
+ int rc, i;
+
+ msg = select_file(st, 0x2f00);
+ rc = tlv_parse(&tp, &ts102221_fcp_tlv_def, msgb_apdu_de(msg)+2, msgb_apdu_le(msg)-2, 0, 0);
+ if (rc < 0)
+ return NULL;
+
+ dump_fcp_template(&tp);
+
+ if (!TLVP_PRESENT(&tp, UICC_FCP_T_FILE_DESC) ||
+ TLVP_LEN(&tp, UICC_FCP_T_FILE_DESC) < 5) {
+ msgb_free(msg);
+ return NULL;
+ }
+
+ rc = osim_fcp_fd_decode(&ofd, TLVP_VAL(&tp, UICC_FCP_T_FILE_DESC),
+ TLVP_LEN(&tp, UICC_FCP_T_FILE_DESC));
+ if (rc < 0) {
+ msgb_free(msg);
+ return NULL;
+ }
+
+ if (ofd.type != TYPE_EF || ofd.ef_type != EF_TYPE_RECORD_FIXED) {
+ msgb_free(msg);
+ return NULL;
+ }
+
+ msgb_free(msg);
+
+ printf("ofd rec_len = %u, num_rec = %u\n", ofd.rec_len, ofd.num_rec);
+
+ for (i = 0; i < ofd.num_rec; i++) {
+ msg = read_record_nr(st, i+1, ofd.rec_len);
+ if (!msg)
+ return NULL;
+
+ cur = msgb_apdu_de(msg);
+ if (msgb_apdu_le(msg) < 5) {
+ msgb_free(msg);
+ return NULL;
+ }
+
+ if (cur[0] != 0x61 || cur[1] < 0x03 || cur[1] > 0x7f ||
+ cur[2] != 0x4F || cur[3] < 0x01 || cur[3] > 0x10) {
+ msgb_free(msg);
+ return NULL;
+ }
+
+ /* FIXME: actually check if it is an AID that we support, or
+ * iterate until we find one that we support */
+
+ msg2 = select_adf(st, cur+4, cur[3]);
+
+ /* attach the USIM profile, FIXME: do this based on AID match */
+ st->card->prof = osim_cprof_usim(st->card);
+ st->cwd = osim_file_desc_find_name(st->card->prof->mf, "ADF.USIM");
+
+ msgb_free(msg);
+
+ return msg2;
+ }
+
+ return NULL;
+}
+
+static int dump_file(struct osim_chan_hdl *chan, uint16_t fid)
+{
+ struct tlv_parsed tp;
+ struct osim_fcp_fd_decoded ffdd;
+ struct msgb *msg, *rmsg;
+ int rc, i, offset;
+
+ msg = select_file(chan, fid);
+ if (!msg) {
+ printf("Unable to select file\n");
+ return -EIO;
+ }
+ printf("SW: %s\n", osim_print_sw(chan->card, msgb_apdu_sw(msg)));
+ if (msgb_apdu_sw(msg) != 0x9000) {
+ printf("status 0x%04x selecting file\n", msgb_apdu_sw(msg));
+ goto out;
+ }
+
+ rc = tlv_parse(&tp, &ts102221_fcp_tlv_def, msgb_apdu_de(msg)+2, msgb_apdu_le(msg)-2, 0, 0);
+ if (rc < 0) {
+ printf("Unable to parse FCP\n");
+ goto out;
+ }
+
+ if (!TLVP_PRESENT(&tp, UICC_FCP_T_FILE_DESC) ||
+ TLVP_LEN(&tp, UICC_FCP_T_FILE_DESC) < 2) {
+ printf("No file descriptor present ?!?\n");
+ goto out;
+ }
+
+ rc = osim_fcp_fd_decode(&ffdd, TLVP_VAL(&tp, UICC_FCP_T_FILE_DESC),
+ TLVP_LEN(&tp, UICC_FCP_T_FILE_DESC));
+ if (rc < 0) {
+ printf("Unable to decode File Descriptor\n");
+ goto out;
+ }
+
+ if (ffdd.type != TYPE_EF) {
+ printf("File Type != EF\n");
+ goto out;
+ }
+
+ printf("EF type: %u\n", ffdd.ef_type);
+
+ switch (ffdd.ef_type) {
+ case EF_TYPE_RECORD_FIXED:
+ for (i = 0; i < ffdd.num_rec; i++) {
+ rmsg = read_record_nr(chan, i+1, ffdd.rec_len);
+ if (!rmsg)
+ return -EIO;
+ printf("SW: %s\n", osim_print_sw(chan->card, msgb_apdu_sw(msg)));
+ printf("Rec %03u: %s\n", i+1,
+ osmo_hexdump(msgb_apdu_de(rmsg), msgb_apdu_le(rmsg)));
+ }
+ break;
+ case EF_TYPE_TRANSP:
+ if (!TLVP_PRESENT(&tp, UICC_FCP_T_FILE_SIZE))
+ goto out;
+ i = ntohs(*(uint16_t *)TLVP_VAL(&tp, UICC_FCP_T_FILE_SIZE));
+ printf("File size: %d bytes\n", i);
+
+ for (offset = 0; offset < i-1; ) {
+ uint16_t remain_len = i - offset;
+ uint16_t read_len = OSMO_MIN(remain_len, 256);
+ rmsg = read_binary(chan, offset, read_len);
+ if (!rmsg)
+ return -EIO;
+ offset += read_len;
+ printf("Content: %s\n",
+ osmo_hexdump(msgb_apdu_de(rmsg), msgb_apdu_le(rmsg)));
+ }
+ break;
+ default:
+ goto out;
+ }
+
+out:
+ msgb_free(msg);
+ return -EINVAL;
+}
+
+int main(int argc, char **argv)
+{
+ struct osim_reader_hdl *reader;
+ struct osim_card_hdl *card;
+ struct osim_chan_hdl *chan;
+ struct msgb *msg;
+ int rc;
+
+ reader = osim_reader_open(OSIM_READER_DRV_PCSC, 0, "", NULL);
+ if (!reader)
+ exit(1);
+ card = osim_card_open(reader, OSIM_PROTO_T0);
+ if (!card)
+ exit(2);
+ chan = llist_entry(card->channels.next, struct osim_chan_hdl, list);
+ if (!chan)
+ exit(3);
+
+ msg = try_select_adf_usim(chan);
+ if (!msg || msgb_apdu_sw(msg) != 0x9000)
+ exit(4);
+ dump_fcp_template_msg(msg);
+ msgb_free(msg);
+
+ msg = select_file(chan, 0x6fc5);
+ dump_fcp_template_msg(msg);
+ printf("SW: %s\n", osim_print_sw(chan->card, msgb_apdu_sw(msg)));
+ msgb_free(msg);
+
+ verify_pin(chan, 1, "1653");
+
+ msg = select_file(chan, 0x6f06);
+ dump_fcp_template_msg(msg);
+ msgb_free(msg);
+
+ {
+ struct osim_file_desc *ofd;
+ llist_for_each_entry(ofd, &chan->cwd->child_list, list) {
+ struct msgb *m;
+ printf("\n\n================ %s (%s) ==================\n",
+ ofd->short_name, ofd->long_name);
+
+ m = select_file(chan, ofd->fid);
+ dump_fcp_template_msg(m);
+ msgb_free(m);
+ dump_file(chan, ofd->fid);
+ }
+ }
+
+ exit(0);
+}