Compare commits

...

257 Commits

Author SHA1 Message Date
Oliver Smith 705a48e8f7 debian/postinst: add checks, be verbose
Do not attempt to change permissions/ownership if the package gets
upgraded from a version higher than the next release.

Do not fail if the user deleted the config file.

Be verbose when changing permissions.

Related: OS#4107
Change-Id: I853097a13e27b2ebd0b940117c8f5f4b3ea49b9a
2024-05-14 15:20:34 +02:00
Oliver Smith abfe4e1643 contrib: remove rpm spec file
Related: https://osmocom.org/news/255
Related: OS#6446
Change-Id: Iff549b7695512c76514ed8915c4807c455e2bdcc
2024-05-08 14:40:55 +02:00
Oliver Smith f3cc0d9e57 .deb/.rpm: various fixes related to non-root
* Explicitly chown /var/lib/osmocom to osmocom:osmocom, instead of
  relying on systemd to do it when the service starts up. This does not
  work with the systemd versions in debian 10 and almalinux 8.
* deb: Use "useradd" instead of the interactive "adduser" perl script
  from Debian. This makes it consistent with how we do it in rpm, and
  avoids the dependency on "adduser".
* deb: Consistently use tabs through the file, instead of mixing tabs
  and spaces.
* deb: Remove support for the "dpkg-statoverride --list" logic. This
  seems to be a rather obscure feature to override permissions for
  certain files or directories, for which it does not seem to be a good
  idea to make the postinst script less maintainable. Something similar
  can be achieved by using your own Osmocom config file in a different
  path with different permissions.

Related: OS#4107
Change-Id: Ie34e7aa65e576cf1742a33530a6f44d2344c39d0
2024-04-26 15:08:44 +02:00
Max ee75a07dfd .deb/.rpm: add osmocom user during package install
Create osmocom user & group during package installation.
Fix the configuration dir/files permission to match.

Related: OS#4107
Tweaked-By: Oliver Smith <osmith@sysmocom.de>
Change-Id: I63e147961f93d62904ae64f5f3794839d047e4a6
2024-04-24 16:20:22 +02:00
Pau Espin da7bc78cb4 ss7_asp: Update osmo_stream read_cb2 functions to accommodate for new API param
libosmo-netif (not yet released) stream_{cli,srv} osmo_io read_cb API was
updated to provide read result status. Hence, now API users (ss7_asp)
can account for lower layer errors and act properly, like it used to
do with the previous ofd backend.

This commit partially reverts some error code paths removed in
9257cd896e when converting code to use
osmo_io osmo_stream backend.

Change-Id: I579f4101a9e2874e310ff78e4571f38cfe8dfab0
Depends: libosmo-netif.git Change-Id I395c75ff1e9904757ce1d767a9ac2f779593c4c8
2024-04-17 16:31:45 +02:00
Pau Espin ef237e1876 Fix wrong string prefix in log line
Change-Id: Ifd16ff671d842ae90ec70c2e819a43174f3aa09a
2024-04-17 16:04:53 +02:00
Pau Espin 7bf18b43a1 xua_asp_fsm: Fix 'Event AS_ASSIGNED not permitted'
The event case handling (ignore it) was added in a commit some time ago,
but it forgot to add it to the allstate_event_mask in the xua_fsm (it
was added only in the ipa_fsm, which is the one using it).

This bug should in theory be harmless since the only code dispatching
the signal is not checking the return code of osmo_fsm_inst_dispatch().

Related: SYS#5914
Fixes: 5744469021
Change-Id: Iaead46bbc40b923763bc3dbe4d24d8952822de4a
2024-04-16 20:43:28 +02:00
Vadim Yanitskiy 513e937a11 fix log_sctp_notification(): remove unused variable
This unused variable causes failures in Jenkins master build job.

Change-Id: Ifc563a7fe9006b6e412f1104cbc7e4d9b8ccae3f
Fixes: 2c9ba16 "ipa: Use pseudo-random number for SLS in IPA->M3UA direction"
2024-04-01 00:57:07 +07:00
Harald Welte 2c9ba16c2c ipa: Use pseudo-random number for SLS in IPA->M3UA direction
In Change-Id Ice7bab997b84cfed00c7d6d780c70f4e9fac6002 we introduced
code that would make the LSB of the file descriptor be used as SLS
when passing packets from IPA in M3UA direction.

This did however not achieve sufficient entropy in real-world use cases.

In this change, we change over to allocating a pseudo-random SLS to each
IPA connection at the time it is established;  We then assign that SLS
to each packet received on that IPA connection.

Change-Id: Ia4e66d660b6057338f66a47fffc8a0d32759f733
Related: SYS#6543
Closes: SYS#6802
2024-03-30 10:48:50 +00:00
Harald Welte 4fc2477a66 README.md: Add links to forum and issue tracker
Change-Id: If5b028d5de19fb456ec91a7f94612131ee42c47c
2024-03-23 13:28:43 +01:00
Harald Welte 4714dc23ee README.md: Update; improve markdown syntax
Change-Id: Ifb24635f9debc873bb3416226c43984104be270c
2024-03-23 13:28:43 +01:00
Harald Welte 236445d9cf Add funding link to github mirror
see https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository

Change-Id: I9c3300966e5511ad5650a5128a3e6d5dd6b98605
2024-03-23 13:21:28 +01:00
Harald Welte 471f859d77 Use same msgb allocation size like before osmo_io introduction
In Change-Id Ia1910f3b99d918ec2a34d5304c3f40ba015c25c9 we introduced
osmo_io support for xUA + IPA.  However, when we did that, the msgb
allocation sizes of libosmo-sigtran were neglected, and rather the
defaults of osmo_io used.

This commit returns libosmo-sigtran to the exact msgb allocation +
headroom sizes used before the osmo_io migration.

Related: OS#5752
Closes: OS#6403
Depends: libosmo-netif.git Change-Id Ie19c8294ddb12dfe5e0fd44e047c47e6f9cbd384
Change-Id: I0c6dcff4523e4c04aae43a4585b5e0c3617ef1a6
2024-03-16 10:18:40 +01:00
Harald Welte 8e12612e17 osmo_ss7_asp: fix uninitialized return value in xua_cli_read_cb()
libosmo-sccp/src/osmo_ss7_asp.c:1019:16: error: 'rc' may be used uninitialized [-Werror=maybe-uninitialized]
 1019 |         return rc;

Change-Id: I3b1ace12eb5bb6d6b37f3374bf9986026796e166
2024-03-15 14:34:19 +01:00
Harald Welte e099fb9d69 cosmetic: rename ss7_asp_*_conn_cb to ss7_asp_*_conn_rx_cb
The old/existing name says it is a connection call-back, but not what
kind of all-back; let's introduce 'rx' to indicate it is a receive
call-back.

Change-Id: Iaaef8128d4a26ea75fbce7067a8ab935a319beb4
2024-03-14 08:07:28 +01:00
Harald Welte 9257cd896e xua + ipa: Add support for I/O in OSMO_IO mode
This switches osmo_stream_{cli,srv} over to using the OSMO_IO
mode instead of the classic OSMO_FD mode.  The difference is that
we no longer read/write directly to a file descriptor, but we pass
message buffers to/from the library.

This in turn allows the library to use more efficient I/O mechanisms
as osmo_io backend, for example the Linux kernel io_uring.

This re-introduces Change-Id: I7d02037990f4af405839309510dc6c04e36c3369
which was previously reverted due to regressions caused by a missing
change in libosmo-netif.

Depends: libosmo-netif.git I6cf5bad5f618e71c80017960c38009b089dbd6a1
Depends: libosmocore.git I89eb519b22d21011d61a7855b2364bc3c295df82
Closes: OS#5752
Change-Id: Ia1910f3b99d918ec2a34d5304c3f40ba015c25c9
2024-03-13 22:18:36 +01:00
Harald Welte c72c2d383c Revert "xua + ipa: Add support for I/O in OSMO_IO mode"
This reverts commit d4ec8e7f9f
which caused severe regressions in the TTCN-3 tests: All STP_Tests_M3UA
are failing, as are STP_Tests.* and SCCP_Tests_RAW.TC_process_rx_ludt

Change-Id: I708a5fe0481b14e1b0cdc86149ffc86ee7b5be59
2024-03-07 08:49:30 +01:00
Harald Welte d4ec8e7f9f xua + ipa: Add support for I/O in OSMO_IO mode
This switches osmo_stream_{cli,srv} over to using the OSMO_IO
mode instead of the classic OSMO_FD mode.  The difference is that
we no longer read/write directly to a file descriptor, but we pass
message buffers to/from the library.

This in turn allows the library to use more efficient I/O mechanisms
as osmo_io backend, for example the Linux kernel io_uring.

Change-Id: I7d02037990f4af405839309510dc6c04e36c3369
Depends: libosmo-netif.git I6cf5bad5f618e71c80017960c38009b089dbd6a1
Depends: libosmocore.git I89eb519b22d21011d61a7855b2364bc3c295df82
Closes: OS#5752
2024-03-06 16:14:16 +01:00
Vadim Yanitskiy 542027f051 xua_accept_cb(): distinguish ASPs using different trans_proto
This fixes a problem found by TTCN-3 testcases: two ASPs can have
identical socket address/port, but different transport protocols.
We need to take this into account in ss7_asp_find_by_socket_addr().

Change-Id: I28aab37e8967de51ad2714543fd235d407e304c5
Related: osmo-ttcn3-hacks.git I1e2a887aa22f317783b3207494fd707d7b426439
Related: SYS#5424
2024-03-04 02:50:03 +07:00
Harald Welte dfa6e681d8 fix regression in sctp-role / transport-role
We saw some fall-out in automatic testing where configs with sctp-role
would suddenly save as transport-role.  Let's avoid this by keeping
sctp-role for users of SCTP.

Change-Id: Ic666b62948880445e030c637298d4e369b4c7e8e
Fixes: 4d7e201 "VTY: rename 'sctp-role' to 'transport-role', add an alias"
Related: SYS#5424, OS#6380
2024-02-29 17:20:41 +07:00
Vadim Yanitskiy 4d7e20193c VTY: rename 'sctp-role' to 'transport-role', add an alias
Now that we're adding support for M3UA-over-TCP, the transport layer
role is no longer an SCTP specific paremeter, but rather a generic one.
This is also the case for the OSMO_SS7_ASP_PROT_IPA, which employs TCP,
and for which we can also choose between the client and server role.

The 'sctp-role' now becomes an alias to 'transport-role', so that
we keep backwards compatibility with old config files.

Change-Id: Iab6c898181d79a5ed2bea767ee90e55bc3af16a5
Related: SYS#5424
2024-02-27 00:45:56 +07:00
Vadim Yanitskiy 22929b1c04 Implement M3UA-over-TCP (in addition to SCTP)
RFC 4666 section 1.3.1 states that "TCP MAY be used as the underlying
common transport protocol" under certain scenarios.  There is even
IANA-allocated TCP port 2905 for that purpose (see section 1.4.8).

Since TCP is a stream oriented protocol, so we need to handle message
boundaries ourselves by reading the M3UA header to know the PDU length.

Change-Id: I8c76d271472befacbeb998a93bbdc9e8660d9b5d
Related: SYS#5424
2024-02-27 00:45:31 +07:00
Vadim Yanitskiy 85055b5ca2 osmo_ss7_asp_find_by_name(): improve docs and coding style
Change-Id: Ia90776c9c3efc3402d59c4f10e5f2cf26274b3f8
Related: SYS#5424
2024-02-27 00:33:45 +07:00
Vadim Yanitskiy 246f7cd881 xua_asp_send_xlm_prim(): use LOGPFSML() to print more context
The LOGPFSML() prints current state of an ASP FSM instance,
which is useful for debugging.

Change-Id: I28f3f89a8158bbedc224922a6ec151b1bbd6c7c0
2024-02-27 00:33:45 +07:00
Vadim Yanitskiy ad9af625e5 cosmetic: reduce nesting in osmo_ss7_as_find_by_proto()
Change-Id: I16e460b91f57bea6606c111335257d717d5423d0
Related: SYS#5424
2024-02-27 00:33:45 +07:00
Vadim Yanitskiy 285800966f cosmetic: xua_cli_connect_cb(): fix typo in a comment
Change-Id: I8fb57dc60c34ed3db2ea5394c3c452604217de91
2024-02-27 00:33:45 +07:00
Vadim Yanitskiy 5951f1f6d5 examples/sccp_demo_user: fix default ASP protocol in help
We actually use OSMO_SS7_ASP_PROT_M3UA by default.

Change-Id: I8481fd8707ed7985071c5c64ee621d210fb4d493
2024-02-19 21:18:39 +00:00
Vadim Yanitskiy 1c155860ea fix xua_msg_dump(): convert message length to the host's byte order
This fixes bogus messages like this one:

  Received MGMT_ERR 'Invalid Routing Context':
      HDR=(MGMT:ERROR,V=1,LEN=268435456), PART(T=Error Code,L=4,D=00000019)
                          ^^^^^^^^^^^^^

Change-Id: I516e486fb7b51a25e33965ed5a0f12ab4488d240
2024-02-16 04:01:22 +07:00
Vadim Yanitskiy 5e80ca8083 return CMD_WARNING if osmo_ss7_xua_server_create() fails
Change-Id: I12bbfd48e16166c0f508ccbf2ed4fa786e786082
Related: SYS#5424
2024-02-15 05:58:54 +07:00
Harald Welte 4120e0d2fc vty_test_runner: Don't wait for more than 10s when connecting to sockets
Change-Id: Iadb437ce0bafb28baa978d287dbb1006ec97f236
2024-02-10 16:07:06 +01:00
Harald Welte c934cadaeb vty_test_runner: Avoid bogus erro messages like "AssertionError: False is not true"
"self.assertTrue(False)" is abusing python unittests.  If you want to
fail, either call the fail() method, or simply raise an exception...

Change-Id: Ib683d6e166a9fca22dd2eb26e066e47034cda750
2024-02-10 16:07:06 +01:00
Harald Welte eae5eb498a Add new "cs7 instance <0-15> asp NAME disconnect" VTY command
This allows the user to administratively disconnect the current
transport connection of a given ASP.

If issued on the client side, the default layer manager will trigger
an automatic re-connect.  If issued on the server side, we expect the
client will re-connect.

Change-Id: I2077121ab860fafb70951454d029c3afa9ee2818
2024-02-10 16:07:06 +01:00
Harald Welte 8000b23106 xua_asp_fsm: Log ERROR message when we send a MGMT:ERR message
We've always been logging received MGMT:ERR messages, but we somehow
didn't log the transmission of them.  So whenever osmo-stp generates
some error to a connected peer, the peer would show it, but not osmo-stp
itself.  Let's fix that.

Change-Id: I50c05409646fd47e70d904fb95bbc2fa15703b3e
2024-02-10 14:48:59 +01:00
Vadim Yanitskiy 02d664b1e4 build: include README.md into the release tarball
Change-Id: I4ffd3738b3230d7b2cb4f8ebfa5d59f9e6dca27f
2024-01-26 23:27:59 +07:00
Pau Espin 5744469021 ipa: Use ASP name as ipa_unit_name on dynamic ASPs
A recent patch fixed a long problem where the ASP name (instead of
expected AS name) was used as ipa_unit_name in IPA based ASPs.
For server side it doesn't matter much, sense anyway the ipa_unit_name
is actually restored on the struct with what's received in IPA GET_RESP
message (see ipa_asp_fsm_wait_id_resp()). So the fix was actually for
the client side in the scenario where a non-dynamic ASP with an assigned
AS was configured in the VTY.

However, dynamic ASPs usually have no assigned AS (because in general it
is really not created/configured, as the ASP is created on the flight).
As a result, the recent patch (see "Fixes" below), broke dynamic ASPs
case because from then one ipa_asp_fsm_start() would fail and terminate
the FSM because ipa_find_as_for_asp() was returning NULL.

So improve the recent patch by applying the previous logic for dynamic
ASPs:
* On the server side, it really doesn't matter since as mentioned, the
  field will be repopulated later on, but allows the code to avoid
  terminating the FSM and hence be brought up and be ready to receive
  clients.
* On the client case, this is how dynamic IPA ASPs were ment to be used
  when they were introduced anyway (use ASP as ipa_unit_id, meaning
  "AS name" == "ASP name").

Furthermore, on the client side, the non-dynamic IPA ASPs need their
bring up be delayed until assigned to an AS, because the AS name is sent
in ipa_unit_name field in Tx IPA ID RESP.
This usually happens at a later point than ASP (FSM) creation, because
first the ASP object is created (through VTY or API) and then assigned
to an AS through osmo_ss7_as_add_asp() usually from a later "asp" vty
command in the "as" node.

Fixes: 65741dca05
Change-Id: I0a741449450c998253b1e44a76a3b7fc224e0903
Related: SYS#5914
2023-12-12 19:18:27 +01:00
Pau Espin 59bab0187c cosmetic: Fix typo in function doc
Change-Id: I1c4fe7d1b3a5cdd4c2c9c942e292a7135467d9e2
2023-12-12 16:57:50 +01:00
Pau Espin 00ec53c057 vty: Introduce show cs7 instance asp-assoc-status
Change-Id: I96ef4c0500991c9b86ab5991fb338ea20a18ff33
Depends: libosmocore.git Change-Id I3e1c84526b006baff435bbbca49dc6cf7d201cf5
Depends: libosmo-netif.git Change-Id I78a0bd8279a04f4011c7273e0f542981308e482f
Related: SYS#6636
2023-12-12 16:07:46 +01:00
Pau Espin c86bdae6f5 vty: Introduce show cs7 instance asp-remaddr
Depends: libosmocore.git Change-Id I3e1c84526b006baff435bbbca49dc6cf7d201cf5
Depends: libosmo-netif.git Change-Id I78a0bd8279a04f4011c7273e0f542981308e482f
Related: SYS#6636
Change-Id: Ie5ac1d0ee74d2b977b1f5319cd88566df7994fd0
2023-12-12 16:07:46 +01:00
Pau Espin 483894bfd1 vty: Introduce cmd 'show cs7 instance asp name ASP_NAME'
Allow printing only a specific asp by name. Useful when user is only
interested in a uniqe ASP and there's lots of them configured.

Related: SYS#6636
Change-Id: I08426272069ce5f3c8403b08dcaf686547bee336
2023-12-12 16:07:45 +01:00
Pau Espin a431b9230b vty: show cs7 instance asp: Print loc and rem addr retrieved from socket
Until now, we were in general printing the list of remote addresses that
were stored in config, because we didn't have an easy way to retrieve
the addresses from the socket. Since recently some libosmocore APIs make
that easy, so use them now.
If the socket is not yet created, then log the addrress list from the config.

Furthermore, take the chance to print now both local and remote
addresses.

Related: SYS#6636
Change-Id: I607e4c2dd37f07bf1c07c88681918184e92202ea
2023-12-12 16:07:17 +01:00
Pau Espin bbecd572a0 vty: show cs7 instance asp: Swap columns SCTP Role and Remote Address list
The Remote Address is by far the potentially largest column, as well as
the one with more variable length, so move it to the end for better formatting.

Change-Id: I4854219f8898266ae47b9117ef79dbad30a5b0fd
2023-12-12 15:54:19 +01:00
Pau Espin 6ad714de98 vty: Retrieve IP addr set from sk when dumping xUA server
Until now we simply printed back the configured set of IP addresses, not
the one retrieved from the socket at the time, because we didn't have
any easy means to retrieve multiple addresses from a socket.
This is possible since recently using libosmocore APIs. Use them.

Depends: libosmo-netif.git Change-Id I1bd3f790d93af74c150938a59108b882ad2820f3
Depends: libosmocore.git Change-Id I18a0e1a652a3e8ef3e97154355eb1d07a14ef0bd
Related: SYS#6636
Change-Id: I331b6e2fe11cd97e286b7ba684d4a17b8055f9d4
2023-12-12 15:54:13 +01:00
Pau Espin 65741dca05 ipa: Fix client setting unit_id with ASP name instead of AS
This was broken since ever. The client was submitting the ASP name
 in the unit_id field during IPA handshake, and the server was
expecting it to contain the AS, so it failed with message:
"Cannot find any definition for IPA Unit Name '%s'" in
ipa_asp_fsm_wait_id_resp().

Fixes: 5f0a8df34c
Change-Id: I249964e171f578726439c40e01ae85aa447afada
2023-12-07 19:36:57 +01:00
Harald Welte 04ca01eaeb port from osmo_stream_*_get_ofd() to osmo_stream_srv_get_fd()
The existing/old osmo_stream_{srv,cli}_get_ofd() API functions are
only compatible with OSMO_FD mode of libosmo-netif/stream.  In the
OSMO_IO mode, there is no osmo_fd involved, and hence those functions
are illegal to call.

Let's instead migrate over to the new osmo_stream_{srv,cli}_get_fd()
which directly return the file descriptor integer value.

Change-Id: I12c66badfb4bdfdfe71f1716de960d353d3548b1
Related: OS#5752
2023-11-25 17:28:59 +01:00
Pau Espin 42ed1bf15d sccp_scoc: Fix remote PC not assigned preventing RSLD to be sent in st WAIT_CONN_CONF
Scenario: RUA Connect triggers SCCP CR towards peer, and we move to
CONN_PEND_OUT state where we expect to receive CC.
However, if some timer (like X31) times out before we receive CC (eg
because CC takes a long time to come), we end up in state
WAIT_CONN_CONF.

In that state, according to Figure C.2/Q.714 (sheet 2 of 7), among other
possibilite we wait for CC, and if it arrives, we send an RLSD to the
peer to inform him that we released the conn, and wait
for the peer to ack the RLSD, then release all state.

Given the scenario above, scoc_fsm_wait_conn_conf() was not assigning
the received OPC from the CC to the conn->remote_pc (unlike
scoc_fsm_conn_pend_out() which does it properly). As a result, when
trying to send teh RLSD it would fail and never transmit RLSD, taking
then a long time to release through T(rel) (10-20 seconds), and probably
longer in the peer (T(iar) or similar?).

Rua Connect triggers tx of SCCP CC:
Received SCCP User Primitive (N-CONNECT.request)
XUA_AS(as-clnt-msc-0)[0x55f11c7df980]{AS_ACTIVE}: Received Event AS-TRANSFER.req //Tx CC
SCCP-SCOC(929)[0x55f11c909c90]{IDLE}: State change to CONN_PEND_OUT (no timeout)
...
X31 timeout triggers state change:
map_sccp(...-SCCP-929)[0x55f11c909820]{wait_cc}: Timeout of X31
SCCP-SCOC(929)[0x55f11c909c90]{CONN_PEND_OUT}: Received Event N-DISCONNECT.req
SCCP-SCOC(929)[0x55f11c909c90]{CONN_PEND_OUT}: State change to WAIT_CONN_CONF (no timeout)
...
CC arrives, but conn_remote_pc is not properly assigned and tx of RLSD fails:
SCCP-SCOC(929)[0x55f11c909c90]{WAIT_CONN_CONF}: Received Event RCOC-CONNECT_CONFIRM.ind
MTP-TRANSFER.req from SCCP without DPC?!? called=RI=0 // PROBLEM HERE!!!!
SCCP-SCOC(929)[0x55f11c909c90]{WAIT_CONN_CONF}: State change to DISCONN_PEND (no timeout)
...
SCCP-SCOC(929)[0x55f11c909c90]{DISCONN_PEND}: Received Event T(rel)_expired

Related: SYS#6616
Change-Id: I9f9f78c92dd95f38af7b03037e60a1c993d7e5b0
2023-10-31 15:51:50 +00:00
Max 427c462484 cosmetic: add spec reference
Related: OS#5579
Change-Id: I4d6bf9d0e681db779770b2fb815f7f9c5e608b15
2023-10-31 09:37:32 +00:00
Pau Espin 87ed6e7b53 sccp_demo_user: Allow initiating conns from vty in server mode
This allows starting SCCP conns from within sccp_demo_user, which in
turn allows testing more scenarios in osmo-ttcn3-hacks.git
SCCP_RAW_Tests testsuite.

Related: SYS#6616
Change-Id: I7d5b3534c496dca8a3f3e66025af554bbe860c04
2023-10-30 19:06:57 +01:00
Harald Welte b24cdce2d9 Fix counting received IPA packets in server mode
When operating an IPA ASP in server mode, we were not counting the
packets using the related stat item:

For example:

OsmoSTP# show stats
Ungrouped counters:
SIGTRAN Application Server Process (6)('asp-dyn-5'):
 Total number of packets received:        0 (0/s 0/m 0/h 0/d)
 Total number of packets transmitted: 235370503 (129/s 7682/m 442447/h 11342653/d)

This patch adds the missing counter increment.

Change-Id: I75881d115b5c2c2567c4731bcf2cabe11f367117
Closes: SYS#6600
2023-10-17 13:03:01 +02:00
Pau Espin 08001775c6 xua_server: asp: Support removing local addresses
The socket is reconfigured (and hence reopened) upon VTY node exit if
changes were stored in the address list of the xua_server
(osmo_stream_srv_link).

Related: OS#4607
Change-Id: I942edff7695efeea7753f22e31c2098c201290ff
2023-10-04 11:21:05 +00:00
Pau Espin c5a4b772b1 asp: Support removing local & remote addresses
The local address is removed dynamically from the socket if it is
already created. For remote addresses, it doesn't make any sense (and
there's no API to do so) because the remote address list passed to it is
only used at connect() time, when the socket is created. During the
initial hanshake, the remote provides its own list of remote addresses.

Related: OS#6077
Related: OS#4607
Depends: libosmocore.git Change-Id Ifc6e7d643c2a0c53f479bfd0d5c36d08c0c01953

Change-Id: I554aee92285bd72eb90c6daf47b37055cb3067aa
2023-10-04 11:21:05 +00:00
Pau Espin 613e25305b sccp: Drop unused local variable
Change-Id: I7f829f1c285d01faeaad317262ddd51f39e42886
2023-10-04 11:20:52 +00:00
Pau Espin f8aaa75852 asp: Support adding new local addresses after the ASP was started
Related: OS#6077
Depends: libosmocore.git Change-Id Ifc6e7d643c2a0c53f479bfd0d5c36d08c0c01953
Change-Id: I8318d2693f3a9c479b18ab1d660431c1ec77c004
2023-10-02 13:15:51 +02:00
Pau Espin 44cf7c8231 asp: Apply SCTP peer primary address changes after the ASP was started
Related: OS#6077
Change-Id: I088b9a59ebfd85f3ce4a26f28bf41472d9b9da60
2023-09-29 18:24:01 +02:00
Pau Espin 3ed3b2c2ed asp: Apply SCTP primary address changes after the ASP was started
Related: OS#6077
Change-Id: I2beb597b2e98d8983177fb21882a461219b3aa58
2023-09-29 18:23:39 +02:00
Pau Espin 4a744e2234 Split osmo_ss7_as functionalities to its own file
Change-Id: I6e39dcb594ffe918ba117fce08cae7b5a6fd2dcc
2023-09-29 17:36:32 +02:00
Pau Espin e39a104727 Factor our osmo_ss7_as allocation to its own function
This makes it easier to find out where the AS struct is allocated, plus
also split between inst code looking up + checking + allocating and the
code doing the allocation and initialization of the AS.

Change-Id: Ie237ec8ac4b2e15b76fce3b3a56f47a59fdcc76e
2023-09-29 17:36:32 +02:00
Pau Espin adbf243e1b Split osmo_ss7_asp_peer functionalities to its own file
Change-Id: I4fe457dc0ee1e904e05423557cfba2505b315a75
2023-09-29 17:36:21 +02:00
Pau Espin 07e788fc94 asp: Factor out helper function to match host in asp_peer
Change-Id: I6f13b285be09982ec83107ff96ade4dabf843815
2023-09-29 16:50:02 +02:00
Pau Espin 785d160da8 cosmetic: src/Makefile: reformat to one item per line
Change-Id: I9efb883d2a56723300a66b7e13c7aa079f2e4600
2023-09-29 16:34:11 +02:00
Harald Welte 743deba334 sccp: Introduce initial support for SCCP LUDT + LUDTS messages
* LUDT/LUDTS is now properly received and passed along to the app.
* LUDT/LUDTS is now used if the data to transmit spans more than 255
  bytes.

Related: SYS#6566
Change-Id: Ic91abfc921f5e4f36045bfa325333112cddd9fa6
2023-09-21 16:35:40 +02:00
Pau Espin 033072fc2d cosmetic: sccp_types.h: Fix typo in comment
Change-Id: Ia75502874e3a07dbd6b685a6721adaedb429e904
2023-09-21 12:59:12 +02:00
Pau Espin 945b252a48 sccp2sua: Produce smaller output if msg contains no optional section
Change-Id: I5c8fcb30b76ac7bafd4a99bc3004049644e7e3f9
2023-09-21 10:32:10 +02:00
Pau Espin 1dab088701 sscp2sua: Constify several more parameters
Change-Id: I0d0997a137f65bccdf8eeda70d95f3f908d2d91a
2023-09-20 18:39:25 +02:00
Pau Espin 88de6d403b sccp2sua: remove unused msg param from sccp_{addr,data}_to_sua_ptr
Change-Id: If4a8283614639d456d2bdf2b31873e28c68f59bc
2023-09-20 18:32:14 +02:00
Pau Espin 0d7f99fd59 xua_msg: Implement xua_msg_dump() using OSMO_STRBUF
This fixes a buffer overflow when a big message (eg containing long
unitada, LUDT) is passed.

Change-Id: I3f91586a96df2d683865715dabb4d6bc042fb33f
2023-09-20 18:17:42 +02:00
Pau Espin c4c444ec7e sccp2sua: constify params of sccp_ptr_part_consistent()
Change-Id: I8011e5cf26d535120829c3925693a2bb3efdb864
2023-09-20 14:43:21 +02:00
Pau Espin 15efbbdba4 Bump version: 1.7.0.56-abf26-dirty → 1.8.0
Change-Id: I563bba2eaca7f569a5216fb8268122c0f55ef48b
2023-09-12 14:03:25 +02:00
Pau Espin abf26ce1ad Allow configuring per-ASP SCTP INIT parameters
Related: SYS#6558
Depends: libosmo-netif.git Change-Id I5343c7659881b29e0201e72badbc2d07e1ef2dca
Change-Id: I4c104f0313b549efab7fae2e81a163474e9bcd4b
2023-09-08 18:14:52 +02:00
Pau Espin 13a341e813 asp,xua_srv: Use new osmo_stream API to request sockopt SCTP AUTH/ASCONF SUPPORTED
Support to enable AUTH/ASCONF in the SCTP socket was added recently in
libosmocore and libosmo-netif, in order to support the Peer Primary
Address features used by the libosmo-sccp code.
The code to request the AUTH/ASCONF support through setsockopt() was
internally applied transparently by lisbosmo-netif's osmo_stream. This
is not 100% disarable since other users of the library may not need/want
that behavior.
As a result, libosmo-netif's osmo_stream no longer enables the SCTP
AUTH/ASCONF support by default, but it must be enabled through
the new osmo_stream_{cli,srv_link}_set_param() API.

This change in behavior of the API/implementation can be done because
all these new features are pretty new and no release of
libosmocore/libosmo-netif/libosmo-sccp has been released yet.

Related: SYS#6501
Related: SYS#6558
Depends: libosmo-netif.git Change-Id I2607c1c926a625986cd851adc65dd8b4de83d6ab
Change-Id: I16c97fc148792aa3e39b7414899660990c39dfff
2023-09-08 18:14:37 +02:00
Pau Espin 0937875983 ss7: Split asp and xua_server into their own files
This allows having a clearer picture of the several entities involved,
as well as simplifying the files.

In the future, we can do the same for AS, route, etc. and leave osmo_ss7
for the "instance" object.

Change-Id: I3d43268459d61b0b9f9bec34bf31dc0851fa5e48
2023-09-07 13:14:51 +02:00
Pau Espin 08cb70e158 ss7: Drop unneeded else branch after early return
Change-Id: I845be6bfbf36ec8a38697ca92420483a9d41aa6c
2023-09-07 12:59:44 +02:00
Pau Espin d01c45918d ss7: Rename internal APIs to avoid exporting them in so file
Change-Id: Ibb22c1dc1ec73fc574cc3d38664874114f681c37
2023-09-07 12:48:52 +02:00
Harald Welte 4c1751ddca m3ua: Add some TODO comments on where we fall short of our potential
Change-Id: I75d7b8f3fb6a06f6941b6dff4072287fdbb1d33e
2023-09-01 10:39:20 +02:00
Harald Welte b93e1d0120 ipa: use LSBs of file descriptor as SLS in IPA->M3UA direction
when IPA/SCCPlite traffic traverses the osmo_ss7 stack, so far the
SLS of the MTP3 label was always set to 0 (default initialization).

However, in order to achieve a better distribution of signaling traffic
in SS7 networks that actually utilize the SLS to select any links (or
ASPs), it makes sense to use non-zero values.

Instead of introducing some kind of new, configurable attribute for
each ASP (IPA client connection), let's simply use the lower 4 bits of
the file descriptor integer.  Those file descriptors should have a
roughly equal distribution, resulting in traffic (from multiple IPA
clients) to be spread across the 4-bit SLS number space.

Also, this mechanism ensures that traffic from one IPA client will
always get the same SLS and hence routed the same way in any underlying
CS7/SS7 network.

Change-Id: Ice7bab997b84cfed00c7d6d780c70f4e9fac6002
Related: SYS#6543
2023-09-01 10:28:14 +02:00
Pau Espin 1edd02bb3b asp: Monitor SCTP_PEER_ADDR_CHANGE events to re-apply configured Primary Address
The kernel doesn't store state about a "always-preferred" Primary
address, it simply applies the requested one at the time the request is
done. If afterwards the remote address becomes unavailable, the kernel
will end up giving up on it and selecting anoter one as primary address.

If the VTY-configured Primary Address become available after a while
(signaled by the kernel in the SCTP_PEER_ADDR_CHANGE), make sure to
apply it again.

Similary, if the peer sends us an ASCONF changing our Primary Address,
we should discard that and apply/overwrite *iff* the user explicitly
VTY-configured one (kernel announces the change through
SCTP_ADDR_MADE_PRIM in this scenario).

Related: OS#6076
Change-Id: I2e54e6f9e424350474db6dec6ab604b33a03f88b
2023-08-21 13:01:47 +02:00
Pau Espin 8b8458d153 asp: Allow setting IP address as SCTP primary upon conn establishment
This is an initial implementation to set the local SCTP Primary address
as well as the peer's Primary Address (through SCTP ASCONF messages).
The Primary address is only set upon conn establishment (after connect()
 for clients, or accept() for severs), which means no logic is
introduced to make sure that primary address is kept during the entire
operation of the SCTP association (for instance if the Primary address
becomes unavailable and the stack changes the primary, and later on that
address becomes available again, the primary addres won't be set back to
the initially configured one).

Related: OS#6076
Change-Id: I4a9fc1a4ad82ed20ece328bc53fca58299d744ca
2023-08-21 12:23:52 +02:00
Pau Espin 58fd289443 ss7: Use libosmo-netif's osmo_stream_{cli,srv}_recv() APIs
Since libosmo-netif.git Change-Id
I4cb94d264109f1b763cccd44c6ba049cc7509319, we receive SCTP notifications
in both client and srv, so there's no real need to use sctp_recvmsg()
directly.

Change-Id: If3d78b636e8e224aa9c8597d0b242e29d3e3c84e
2023-08-07 19:58:14 +02:00
Pau Espin f9852ae077 ss7: Log addr and event state for SCTP_PEER_ADDR_CHANGE
Change-Id: I989f0180dab25cd6fb1a0c0a49c04a03be118ba3
2023-08-07 17:11:49 +02:00
Pau Espin e798ebdf6d tests/vty: test multiple local/remote IP addresses
Change-Id: I9e8c6499b26edea271ee8d8c4ab86506e765e000
2023-08-03 17:37:53 +02:00
Pau Espin a18f907aaf cosmetic: Fix pointer location format
Caught by linter:
ERROR:POINTER_LOCATION: "foo* const* bar" should be "foo * const*bar"

Change-Id: Icb3adff115e77fca273fdbfda6c7de1ddf1b61cd
2023-08-03 17:14:08 +02:00
Pau Espin 4ddaee9362 asp: Make ASP default LM timers VTY configurable
Related: SYS#6511
Change-Id: Ib62e19f2e528e8b2792cbb51a5900dc3463ebd06
2023-07-11 14:56:26 +02:00
Pau Espin 5cf725b191 ss7: Refactor osmo_ss7_asp_find_or_create()
Move allocation code to its own function. Simplify
osmo_ss7_asp_find_or_create() code paths.

Change-Id: Ibbf20c63fc1d2b22a7c5d750002f94795ee31f26
2023-07-11 14:55:17 +02:00
Pau Espin c086a8ed42 sccp: Use tdef to implement osmo_sccp_timers
Related: OS#4372
Change-Id: I1dbe49a83a1bcddf074d5e638babd065834a0ebd
2023-07-11 14:55:14 +02:00
Harald Welte 1532791798 M3UA/SUA: Don't add empty routing context IE in DUNA/DAVA/DUPU
Before this patch, the code generating a DUNA or DAVA message would
potentially generate a ROUTE_CTX IE with zero-length content, rather
than skipping such an IE altogether if no routing context[s] are given.

Change-Id: I19d0382cd2d428a91ac716182b9d86dcdc2c2ebd
Related: SYS#6511
2023-07-10 08:36:40 +00:00
Pau Espin 7afbc23c2d xua_default_lm_fsm.c: Log rx ASP_UP timeout with NOTICE level
Some user run into this scenario recently and it was difficult to find
out why the SCTP links were being restarted. Add a new log line with
NOTICE level explicitly stating the reason to restart the ASP.

Related: SYS#6511
Change-Id: I5a388d2d96bcf1cbb76981c476abf37dbe213df0
2023-07-09 07:18:31 +00:00
Pau Espin a8aab23588 vty: Improve output of show cs7 instance <0-15> asp
Role and SCTP Role are now printed. Several formatting issues are fixed
or improved.

Related: SYS#6488
Change-Id: Id22bd4e74fb6f79950adba58862d379203d36760
2023-06-27 16:49:55 +02:00
Pau Espin 1470379c27 Set stream_cli/srv name to contain ASP + sockname
Upon connect/accept, update the name to contain both the ASP and the
sockname of the established socked. This allows easily identifying the
stream based on IP layer as well as upper xUA layer.

Example logs:
"""
stream.c:1193 SRV(m3ua,::1:2905) accept()ed new link from ::1 to port 2905
stream.c:1672 SRVCONN(asp-dyn-0,(r=::1:43568<->l=::1:2905)) connected read/write (what=0x1)
stream.c:1589 SRVCONN(asp-dyn-0,(r=::1:43568<->l=::1:2905)) message received
"""
"""
stream.c:521 CLICONN(asp0){CONNECTING} connection established
stream.c:552 CLICONN(asp0,(r=::1:2905<->l=::ffff:127.0.0.2:35911)){CONNECTED} connected read
stream.c:401 CLICONN(asp0,(r=::1:2905<->l=::ffff:127.0.0.2:35911)){CONNECTED} message received
"""

Depends: libosmo-netif.git I539a0d29d11348efe702f971965a55cf56db5c59
Change-Id: I05bc5f6c7795f62c1814c1c774287b41ee85a475
2023-06-20 19:52:46 +02:00
Pau Espin af768c716a Add osmo_ss7_asp getters for name and proto
Some apps such as osmo-bsc are accessing some of those fields, so better
add a getter API to make it easier to update the struct contents.

Change-Id: If9acfca1abc9ab7eb6d2388f548d7ee577b9c5ac
2023-06-20 14:11:46 +02:00
Pau Espin 5d30f648f4 Forbid partial VTY configurations of ASPs with name asp-clnt-*
A recent commit (83db938859) changed the
behavior of default "sctp-role" and "role" values of ASPs named
asp-clnt-* which are used by osmo_sccp_simple_client_on_ss7_id(), in
order to avoid having different default values when than function is
used, which is totally confusing for end users.

As it was already informed when submitting the mentioned commit, it
changes the behavior on that specific case, and that made some users
start having problems without a proper "your config is wrong!" error.

This commit addresses it by basically forbiding that exact use case,
that is, partially defining an asp-clnt-* ASP through VTY without
explicitly configuring a "role" and "sctp-role". With this patch,
function osmo_sccp_simple_client_on_ss7_id() will print a proper error
informing the user and will return NULL, potentially triggering an early
program exit which can then easily be fixed by using proper
configuration.

Related: SYS#6486
Change-Id: I65b5fad2ec06a9d9c521f1e3ce8aab633da95a47
2023-06-16 18:18:35 +02:00
Pau Espin 83db938859 osmo_sccp_simple_client_on_ss7_id(): Always follow VTY config of ASP if it was explicitly defined in VTY
The VTY config defaults are "role sg" and  "sctp-role server".
However, if not set explicitly in VTY,
osmo_sccp_simple_client_on_ss7_id() was replacing them to "role asp" and
"sctp-role client".
This is fine if the ASP is not defined/created at the VTY (aka
dynamically created with no config), but if the ASP is defined in the
VTY it becomes really confusing, since it's picking different defaults
than the usual ones.

Hence, follow always the same VTY defaults in
osmo_sccp_simple_client_on_ss7_id().

As a result of this change:
- Programs not defining the ASP over VTY keep the same behavior (ASP is
  created with role=asp and sctp-role=client)
- Programs defining ASP over VTY (eg. "asp asp-clnt-msc-0 5000 0 m3ua")
  explicitly setting "role" and "sctp-role": keep the same behavior
- Programs defining ASP over VTY but not explicitly setting "role" or
  "sctp-role", will get now the usual defaults instead.

So in summary, strange cases where osmo_sccp_simple_client_on_ss7_id()
is used (osmo-bsc, osmo-hnbgw, osmo-msc) and the dynamically created
ASP is created through VTY, then the config file must also explicitly
define the following lines:
role asp
sctp-role client

This patch also changes the "show running-config" VTY cmd to also always
show the configured role and sctp-role values, which are of vital
importance to understand a given setup.

Change-Id: Ie81987265118e48173211f88b27a006115dc62d4
2023-06-13 20:07:18 +02:00
Pau Espin 0aff5d101d osmo_sccp_simple_client_on_ss7_id(): Support ASP explicitly configured as sctp server
Right now, if a user configures an cs7 instance, as and asp (with
sctp-role server) in the VTY, then the function
osmo_sccp_simple_client_on_ss7_id() (used by osmo-bsc, osmo-msc,
osmo-hnbgw, etc.) will silently change the config to be SCTP client.
This is really confusing, since the user configured explicitly the ASP
as server but ends up running as client.
Instead, if the user explicitly configured the ASP as SCTP server,
allow it (after validating the user also properly configured a xUA
server through VTY).

Change-Id: I20de33edb8751a515d6719c49efadfc387dd85f8
2023-06-13 20:05:58 +02:00
Harald Welte 2a0ccafbf8 cosmetic: fix various typos in comments, log messages and VTY strings
Change-Id: I9a8afc07ae278100ffe34407db0e196f0c9ce8af
2023-06-13 17:24:27 +02:00
Pau Espin 8698f273d2 .gitignore: Ignore *.pyc
vty/config tests were added recently, which use python and end up
generating __pycache__/osmoappdesc.cpython-311.pyc when run.

Ignore those files as done in other projects such as osmo-bsc.

Change-Id: I8129a4a124457411c8163089ec2bac32f3e1c2b7
2023-06-08 13:57:01 +02:00
Oliver Smith 7e6bce7322 systemd: depend on networking-online.target
Related: SYS#6400
Change-Id: I7e88d7f48fb1f359fc9147acf3e69525c856d4e8
2023-05-26 14:10:12 +02:00
Vadim Yanitskiy 1f50d451a0 copyright: fix typo: sysmocom s/s.m.f.c./s.f.m.c./ GmbH
Change-Id: Ibb6e904b3a2bb6e569bed76cad5e0c982e055612
2023-05-18 17:22:26 +07:00
Neels Hofmeyr 083ea4b1fc add public API: osmo_sccp_instance_next_conn_id()
In struct osmo_sccp_instance, we have a next_id, to find an unused SCCP
connection ID. This is used for inbound SCCP connections.

At the same time, in each of our SCCP client programs, we have a
separate mechanism to find a connection ID for outbound connections.

See for example bsc_sccp.c: bsc_sccp_inst_next_conn_id()

It makes much more sense for callers to use the same
osmo_sccp_instance->next_id counter:

- Currently the inbound and outbound counters go out of sync: For
  example, in osmo-bsc, if we have three MS doing a usual Complete Layer
  3, osmo-bsc's counter increments by three. If we then have one
  Handover Required coming back from the MSC, i.e. inbound SCCP
  connection, the sccp_inst->next_id is behind by three, and will
  iterate SCCP connections three times before finding an unused id.

- Each implementation has to take care to properly wrap the ID in its
  valid range, e.g. in osmo-bsc:

                test_id = (test_id + 1) & 0x00FFFFFF;
                if (OSMO_UNLIKELY(test_id == 0x00FFFFFF))
                        test_id = 0;

Add public API so that callers can benefit from the internal
osmo_sccp_instance->next_id and do not need to duplicate the code for
staying within the proper range.

Change-Id: If59d524fbe1088a59ae1b69908e2d4bf67113439
2023-05-10 02:17:07 +02:00
Neels Hofmeyr 8bc881f31a sccp_scoc.c: fix infinite loop on conn ID exhaustion
Looking for an unused SCCP connection ID has no exit condition if all
connection IDs are in use. However unlikely it is that there are
16777215 active connections on one SCCP instance, add an exit condition.

Do so by implementing the ID lookup in a new separate function, which
qualifies for public API (upcoming patch).

So far, use an exit condition closest to previous behavior: iterate the
entire SCCP conn id number space before exiting in failure.

Change-Id: Ib64e0cb1ae0cc8b7bebcb2a352d4068b496b304a
2023-05-10 02:17:04 +02:00
Neels Hofmeyr d5139548f2 add osmo_sccp_set_max_optional_data()
So far, the optional data limit can only be modified via cs7 VTY,
because struct osmo_sccp_instance is private. Provide public API to set
this limit from C.

Change-Id: If3d22a0f65a7ed0be043027652402b32c356e322
2023-05-10 02:00:25 +02:00
Oliver Smith 42ebd023b7 debian: set compat level to 10
Related: OS#5958
Change-Id: I6ee4aace6bf7c246338408c41f790189d75d5488
2023-04-27 12:27:46 +00:00
Neels Hofmeyr a0fbeeb4d2 SCCP: implement variable limit on Optional Data (CR,CC,CREF,RLSD)
When the Optional Data surpasses 130 bytes, it is not sent as part of
SCCP CR, CC, CREF or RLSD messages, but gets sent separately in a Data
Form 1.

Make this 130 user configurable. This is specified to be 130 bytes
exactly, but to interop with non-conforming peers, make this limit
adjustable per cs7 instance, via osmo_sccp_vty_init().

Add and test new VTY config:

	cs7 instance N
	 sccp max-optional-data (<0-999999>|standard)

Related: ITU-T Q.713 4.2 to 4.5
Related: Ia68dad973ef18513b52f5accb5264c557c7295ea osmo-ttcn3-hacks
Related: SYS#6423
Change-Id: If35697234796af8943691b2de62218e7dc93a08c
2023-04-24 18:45:37 +02:00
Harald Welte 460ba631b5 ASPAC/ASPIA: Don't add routing context IE in ASP-role if routing context 0
If there is only one routing key (and hence routing context, and hence
AS) within one ASP, *and* the routing context is configured as 0 (zero),
we should not include the routing context IE.  This is the way how
libosmo-sigtran has always been special-casing routing context 0 meaning
"routing context is not used".  However, that was only working in SG
role (typical STP use case).  When operating in ASP role, the
special-case handling was missing, causing a routing context IE
containing zero to be included in the related transmitted M3UA ASPAC
and ASPIA messages.

Change-Id: I5ce89b393a3b950ab7fd1eace9038718c9efcc51
Closes: OS#6003
2023-04-15 20:34:33 +08:00
Harald Welte a54c714df3 cosmetic: Fix log message in routing table add error case
* crate -> create (typo)
* linkset -> AS (we're adding a route for an AS here, not a linkset)

Change-Id: I901aa68f64de3bee1766c356a1403b6ca57d5237
2023-04-15 01:35:21 +08:00
Neels Hofmeyr c3604fe9a9 comment: fix typo 'OSMO_ASCU_'
Change-Id: I1c0f501a7f828499c6112cab8aece20f7c34c118
2023-04-05 02:30:59 +02:00
Neels Hofmeyr 078622128a add two value_string[]s related to N-PCSTATE
Related: SYS#6319
Change-Id: I873ea30e8f64280487769668c748c67ce4ff4023
2023-04-05 02:30:59 +02:00
Vadim Yanitskiy 79c64ab555 osmoappdesc.py: also test doc/examples/osmo-stp-multihome.cfg
Change-Id: Ib6d0b0cdcfad165ad34d304069843c71f6eb06de
2023-03-30 02:36:26 +07:00
Vadim Yanitskiy 463888c1dc tests: execute osmotest{vty,config}.py against osmo-stp
Change-Id: Idd57757f251dd1c4d1c74d275b4c5d8beff304e5
2023-03-30 02:35:23 +07:00
Vadim Yanitskiy c5cae83315 tests: $(BUILT_SOURCES) is not defined, depend on osmo-stp
Change-Id: I4864f4d78095187f566bcde772b71665fa6e4729
2023-03-29 19:16:11 +07:00
Neels Hofmeyr 2e476c692b fix sccp_scoc state S_WAIT_CONN_CONF out_state_mask
In sccp_scoc_states, allow the state transitions present in
S_WAIT_CONN_CONF's event handler function scoc_fsm_wait_conn_conf(), and
thus fix handling of N-DISCONNECT while waiting for SCCP Conn Confirm.

Fixes this error:

 DLSCCP ERROR SCCP-SCOC(1016){WAIT_CONN_CONF}: transition to state IDLE not permitted! (sccp_scoc.c:1213)

S_WAIT_CONN_CONF happens when the caller sends an N-DISCONNECT during
S_CONN_PEND_OUT -- that means, while we are waiting for a CC from the
peer, the caller ends the conn. SCCP-SCOC still waits for the CC first.

When S_WAIT_CONN_CONF expires, the intended path is state change to
S_IDLE, which then deallocates the connection. Allow this state
transition from S_WAIT_CONN_CONF to S_IDLE.

When S_WAIT_CONN_CONF receives a CC, the intended path is to send a
RELRE and state change to S_DISCONN_PEND. Allow this state transition
from S_WAIT_CONN_CONF to S_DISCONN_PEND.

Change-Id: I8145e53124cabd76bd2cee159ab01306a1afaa27
2023-03-24 03:26:42 +01:00
Vadim Yanitskiy 2790599e56 tests: use -no-install libtool flag to avoid ./lt-* scripts
This option should be used for any executables which are used only
for testing, or for generating other files and are consequently never
installed.  By specifying this option, we are telling Libtool that
the executable it links will only ever be executed from where it is
built in the build tree.  Libtool is usually able to considerably
speed up the link process for such executables.

Change-Id: I9758aaaa56b2453f33f90400342ebd1fd412ec3a
2023-03-13 10:54:34 +00:00
Vadim Yanitskiy 002b4d29f9 tests/*/Makefile.am: -Wall is not a preprocessor flag
Change-Id: I1a143636dfc5d4ea85938472fd367ac88e3cc4ae
2023-03-13 10:54:34 +00:00
Vadim Yanitskiy 0e065be375 {examples,stp}/Makefile.am: fix typo in $(COVERAGE_[C]FLAGS)
Change-Id: I8dc7119765df97f44a6f2c5d6eecec847fcc46dc
2023-03-13 10:54:34 +00:00
Pau Espin 1e288b9614 sccp: Introduce APIs to get/set osmo_sccp_instance priv data pointer
The field already exists in the struct, but it's not really used
anywhere internally yet (and was not available externally).
Let's make it available to users of the API, similar to osmo_sccp_user_{get,set}_priv().
This way apps can easily store per-sccp-instance state.

Change-Id: I5643e6b14590b1478b3c8dabc8a7a619f5505409
2023-03-13 11:20:31 +01:00
Pau Espin 253c45307e Implement sccp_instance->connections as rbtree
As a result we move from:
INSERT=O(1) SEARCH=O(n) REMOVE=O(1)

to:
INSERT=O(log(N)) SEARCH=O(log(N)) REMOVE=O(log(N))

So we get rid of O(n) complexity every time we need to find a conn
object based on conn_id. When a big number of SCCP conns is handled,
this saves a lot of CPU time every time a conn needs to be looked up,
for instance each time a message is received.
For instance, given 1500 SCCP conns, searching is ~10 steps while it
took 1500 steps beforehand.

Morever, since when creating a new conn_id the code looks if it exists
prior to it, that means in practice inserting used to took O(n), while
now it takes O(log(N)).

Change-Id: I28ac67038207e2fad89ea291629cec5b2f912461
2023-03-10 17:56:42 +01:00
Pau Espin a19cf68c65 cosmetic: sccp.h: Fix trailing whitespace
Change-Id: I6b9cb5acffdf690f65e14dbdda7213b2e75c7ee1
2023-03-10 17:33:39 +01:00
Pau Espin d66c70208f sccp_scpc.c: Simplify conn_create()
Drop local variable with no real use, dereference sccp_instance pointer
once instead of potentially thousands of times.

Change-Id: Iee333fb38d7a37877c37c1de9719a6b67d9e8ed3
2023-03-10 16:25:38 +01:00
Pau Espin f031be9141 Avoid allocating conn_id 0x00FFFFFF
The 0x00FFFFFF source local reference is reserved in M3UA/SCCP, hence
avoid allocating a conn_id with that value since later on when reused as
a source local reference it would fail to be encoded.

Change-Id: Ifcf710ef6024286a1cc3473d6ea3f858552b9926
2023-03-10 16:21:30 +01:00
arehbein 41089524a3 Transition to use of 'telnet_init_default'
Related: OS#5809
Change-Id: Icc57c68337d55c6594c1c36e9bf41624d11dab0a
2023-02-28 10:32:07 +00:00
Vadim Yanitskiy 36d3877483 mtp_pcap: remove unused static_assert() macro definition
Change-Id: Ib0419a2de3aa09a2e1fba1fc2628f12295d83bda
2023-02-24 18:16:00 +07:00
Neels Hofmeyr f9b0746d7d fixup for: add osmo_scu_prim_name_c() / _buf()
I would like to tweak the names for the recently added functions, so
that they are more clear -- quickly before we release it or anyone uses
these.

Depends: libosmocore I9f43428af654a5674ac3035fe4db1394aac7a7af
Related: I4c1998fd7fee7282d107846dae2cff4b5ceb3a7b
Change-Id: If381f537ab91af1feef7f0e51921217f27e18e6a
2023-02-22 16:02:51 +01:00
Max 6e8241356b SS7: do not attempt transfer if AS is down
The attempt to route message via AS which is down will fail anyway:
let's make it explicit.

Add osmo_ss7_as_down() and use it to check AS state before transferring the message.

Change-Id: I0d5f3b6265e7fdaa79e32fbc30f829ef79e7dad1
2023-02-21 08:51:22 +00:00
Oliver Smith e753f21047 Run struct_endianness.py
Ensure there is no diff to prepare to run this in CI.

Related: OS#5884
Change-Id: I3909fd5a406e21f348ad8c1d5adc267551bfe2b2
2023-02-20 09:50:38 +01:00
Neels Hofmeyr 4c761ba304 add osmo_scu_prim_name_c() / _buf()
Add non-legacy string functions for osmo_scu_prim_name in the form of
_buf() and _c() signatures.

So far there is only osmo_scu_prim_name() using a static buffer.
Implement that using osmo_scu_prim_name_buf().

Change-Id: I4c1998fd7fee7282d107846dae2cff4b5ceb3a7b
2023-02-18 02:45:14 +01:00
Neels Hofmeyr ed4e7dbf86 sccp_scmg: ignore PCSTATE.indication
Silence the error log for PCSTATE.indication prims.

When running the HNBGW_Tests.ttcn suite, the osmo-hnbgw LOGL_ERROR is
spammed with messages like:

 DLSCCP ERROR unsupported SCCP user primitive N-PCSTATE.indication (sccp_scmg.c:298)

Add this prim to scmg_prim_cb() to just log on DEBUG that it is ignored.

Related: OS#5679
Change-Id: I5fd38afea94f48ed2f2fcd2d9baa8ec22a571b6b
2023-02-18 02:45:14 +01:00
Pau Espin dc9713a672 Bump version: 1.6.0.20-8b67-dirty → 1.7.0
Change-Id: I431dc58d5c4adfcb2c4ff19a95b31853618034b5
2023-02-07 14:37:23 +01:00
Harald Welte 8b671b073f cosmetic: fix typo in comment
Change-Id: I2e2b5ec36da9b50dcf13a0d7f497a9ca16e6a7a3
2022-12-10 12:52:58 +01:00
Harald Welte 99b912f94a SCOC: dump SUA header when logging "mismatching remote pc"
Provide some more information in the logs when this happens

Change-Id: Idd889b57e8f842e22e5ed54bea0821dcec0cd8ce
2022-12-10 12:51:47 +01:00
Pau Espin 2f648b2daf sccp: Validate local references > 0x00fffffe are not used
SUA allows references of 4 bytes, but SCCP/M3UA doesn't.

SUA: RFC3868 sec 3.10.4:
      The source reference number is a 4 octet long integer.
      This isallocated by the source SUA instance.
M3UA/SCCP: ITU-T Q.713 sec 3.3:
      The "source local reference" parameter field is a three-octet field containing a reference number
      which is generated and used by the local node to identify the connection section after the connection
      section is set up.
      The coding "all ones" is reserved for future use.

Related: SYS#6211
Change-Id: Ia547346bdae54a032d2198ecd4972fb3f8dd073e
2022-11-23 18:11:58 +01:00
Oliver Smith bd92ec7256 contrib/jenkins.sh: add missing 'make check'
Change-Id: I09b7e2223d19c9e055ecddf3b2690cdea26b9be7
2022-11-11 10:19:19 +01:00
Oliver Smith ef0d676741 tests: fix old-style function definition
Fix for:
  xua_test.c:575:6: error: old-style function definition [-Werror=old-style-definition]

Change-Id: Iccc942f9f25fb091045d906eb8be658b54c58529
2022-11-11 10:13:24 +01:00
Harald Welte 2ce7b758e3 Add -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition
... if --enable-werror is used

Change-Id: If344ef88121111db5365b8cee3cf3cd1283303c1
2022-11-03 11:58:36 +01:00
Max 71fc322431 Set working directory in systemd service file
By default systemd will execute service with root directory
(or home directory for user instance) which might result in
attempts to create files in unexpected place. Let's set it
to 'osmocom' subdir of state directory
(/var/lib for system instance) instead.

Related: OS#4821
Change-Id: If21e3471ec129892ff8b410db30d8ce0e4014e05
2022-09-09 18:39:08 +07:00
Max e5d615efdc SIGTRAN: cache Optional Data for SCCP CR/CC/RLSD
The length limit of optional Data parameter is 130 bytes according to ITU-T Rec Q.713 §4.2..§4.5. If we receive CR, CC or
RLSD message with bigger data - cache it if necessary and send via separate DT1 message after connection becomes active.

Fixes: OS#5579
Change-Id: I0033faf9da393418930252233ce74d62cd1cef8a
2022-09-07 17:36:30 +07:00
Max 56b7700910 SIGTRAN: arrange the comments in the encoder to match the spec
Related: OS#5579
Change-Id: Ib986137057856afb8725541d912db210a9a46294
2022-09-07 17:33:03 +07:00
Max e7228dbdd2 examples: update vty code
* document optional data parameter
* use argc to check for optional parameter presence

Related: OS#5579
Change-Id: I52d0823b7843aa61594e728a0fac19d884ff78fe
2022-08-22 20:33:12 +07:00
Max bb760c5915 SIGTRAN: add osmo_sccp_tx_disconn_data() helper
SCCP RLSD message might have up to 130 bytes of optional data according to ITU-T Rec Q.713 §4.5 - add helper which
allows sending it and use it in example code.

Related: OS#5579
Change-Id: I92ae22d2cab5863245fba3d904a300055fda34fe
2022-08-22 20:33:12 +07:00
Max 768f36440f SIGTRAN: error if attempting to send exceedingly big data
Previously DT1 message sent via osmo_sccp_tx_data() was silently truncating data if it was over 256 bytes. Let's fail
explicitly and let caller handle this.

Related: OS#5579
Change-Id: I8a67bc40080eb1405ab3b0df874e3ea20941a850
2022-08-22 18:36:51 +07:00
Max 196ddcbb05 SIGTRAN: add function to check connection existence
Add convenience helper to check if particular connection ID exists and use it to
properly report errors when attempting to send messages over non-existent connections.

Change-Id: Iffedf55b4c292ee6b2f97bcdeef6dc13c050ce01
2022-08-22 18:36:51 +07:00
Max 28a8e3e836 SIGTRAN: don't advance FSM on failed connection request
Return proper error code from packet encoding routine and check for it before switching FSM state as it creates
confusing mismatch with actual protocol state.

Related: OS#5579
Change-Id: I8431c77632014e2551d1da779afddffcd1bb541c
2022-08-22 18:33:23 +07:00
Max 43d7455089 Add basic readme for example code
Related: OS#5579
Change-Id: I4c76a48a76ca9afc960ea2f08229bacc79b6fe77
2022-08-21 21:53:40 +07:00
Max f4301b7f45 [cosmetic] sccp_scoc.c: constify function parameters
Related: OS#5579
Change-Id: I398e3efa3e097de8907617cfdf363e1d3b96f666
2022-08-21 21:52:45 +07:00
Max d388dead51 SCCP: Log more data on CR size error
Related: OS#5579
Change-Id: I4d7fc44fde5b900907ec5ebec0450040a764802a
2022-08-18 21:15:19 +07:00
Max 4f9c66dc0f SCCP: enforce optional data length limits
Limit length of optional Data parameter to 130 bytes to conform with ITU-T Rec Q.713 §4.2..§4.5 while receiving SCCP messages.

Related: OS#5579
Change-Id: Icc3bd0a71b29cf61a259c5d97e7dd85beb4397bd
2022-08-17 11:36:04 +07:00
Max 12e76b8859 cosmetic: improve usage help of example code
* document -C option for example server
* move misplaced comma
* drop empty #define from example code

Change-Id: I342dc227afbfe6c3540eb3285677c568d2fe3166
2022-08-17 11:27:25 +07:00
Pau Espin e6cecee78c xua_snm: assert to guard against NULL ptr
This is similarly done for same IE in other functions, so let's do it
here too in order to make coverity happy, and avoid random access ptr
probably ending up in obscure crash.

Fixes: Coverity CID#272994
Change-Id: I72059ffaa608bb4f5c4bd274645878e0b31ed6e0
2022-06-29 18:23:44 +02:00
Pau Espin 1032df2d88 Bump version: 1.5.0.20-e9cc-dirty → 1.6.0
Change-Id: Ibe4efb64b8de506a007559e2fcf13cc9dc0d9d60
2022-06-28 18:22:14 +02:00
Harald Welte e9cc5c9376 update git URLs (git -> https; gitea)
Change-Id: Idac4924a077b5389e85efaf62081589fc3de06ad
2022-06-17 23:18:23 +02:00
Harald Welte 206583846c [cosmetic] sccp_scoc: Fix typo in comment
Change-Id: I26168658b58250f58c3616e51010b969df8029a4
2022-06-17 22:20:28 +02:00
Harald Welte 70802f76d9 [cosmetic] sccp_scoc.c: fix comments about corresponding SCCP msg types
Change-Id: I0d7a81f72147f8022ddadfe28d04679097dfa492
2022-06-16 21:40:47 +02:00
Harald Welte d51a8de505 M3UA/SUA: Implement handling of SCON (signaling congestion)
If we receive any M3UA/SUA SNM SCON mesasages, distribute them
to any other active ASP to make everyone aware of the congestion
situation.

This makes STP_Tests_M3UA.TC_ssnm_distribution_scon pass and hence
should turn the entire osmo-stp test suite "green"

Change-Id: Iac7aeba980fbbd8b58f8872a29ba10745eb0a730
2022-05-05 18:45:42 +02:00
Vadim Yanitskiy 7d8a674e88 tests: use 'check_PROGRAMS' instead of 'noinst_PROGRAMS'
When using 'check_PROGRAMS', autoconf/automake generates smarter
Makefiles, so that the test programs are not being compiled during
the normal 'make all', but only during 'make check'.

Change-Id: Icca22778831b043358acf0482948dbff32a11256
2022-04-13 19:55:33 +03:00
Pau Espin 64dda770c3 Add ss7 instances to osmo_ss7_instances in creation order
Otherwise when configuring ss7 instances in numerical order in the VTY
and then printing the VTY configuration they end up ordered this way:
cs7 instance 2
cs7 instance 1
cs7 instance 0

Related: SYS#5912
Change-Id: Id4d0a20cc5b0811b505b2d1051d496f8bd17d54c
2022-03-29 13:13:56 +02:00
Harald Welte 218789e833 sccp_demo_user: enable stats / rate_counter timers
Change-Id: I135704d401de2cf439e51882e22323b31db14a2a
2022-02-24 20:40:24 +01:00
Harald Welte a1bf0c96ec sccp_demo_user: Allow user to specify logmask on command line
Change-Id: Ie1cf37b3501e26330b038b7ee17a5273d81c92d8
2022-02-24 20:40:24 +01:00
Harald Welte 3424c5ed99 sccp_demo_user: make sure the command-line specified point codes are used
Change-Id: I983066199e5d8f056895fdad370e3b4439b4dba4
2022-02-24 20:40:24 +01:00
Harald Welte d8c7a40a60 sccp_demo_user: Add 'show stats' to VTY
this allows us to inspect statistics of the demo user via VTY

Change-Id: Ica48d4e0c9cedb9adb94ae08c5201207b8836e1c
2022-02-24 20:40:24 +01:00
Harald Welte d0cd3157b7 sccp_demo_user: Add command line argument to specify protocol (m3ua/ipa)
Change-Id: Iecb03df43709903b1cfe248e68d6624b995c563d
2022-02-21 22:27:57 +01:00
Pau Espin 7b4aea4aa6 xua_asp_fsm: Fix gcc false positive warning
As of GCC 11.1.0, it starts printing a warning about uninitialized
variable. It is a false positive since tmode is really only used in the
case where traffic_mode is not zero, in which case tmode is set.

Let's restrict the scope of tmode to fix the issue and also make it
clearer where the variable is used.

"""
In file included from /git/libosmo-sccp/src/xua_asp_fsm.c:25:
/git/libosmo-sccp/src/xua_asp_fsm.c: In function ‘xua_asp_fsm_inactive’:
/git/libosmo-sccp/include/osmocom/sigtran/osmo_ss7.h:274:16: error: ‘tmode’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
  274 |         return get_value_string(osmo_ss7_as_traffic_mode_vals, mode);
      |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/git/libosmo-sccp/src/xua_asp_fsm.c:476:39: note: ‘tmode’ was declared here
  476 |         enum osmo_ss7_as_traffic_mode tmode;
      |
"""

Change-Id: I4fc38724aba3a3f178ba0b45444e1394db44d039
2022-02-01 16:59:52 +00:00
Oliver Smith ebccb824a0 osmo_ss7_vty: add osmo_sccp_addr_by_name_local
Add a new function similar to osmo_sccp_addr_by_name, but search in a
specific ss7 instance's addressbook instead of searching in the global
address book. This is needed for osmo-bsc-nat, which uses two separate
instances at the same time.

Related: SYS#5560
Change-Id: I0f38b0d038b0dd8cd355e7284e5b56d438811bd9
2022-02-01 11:08:34 +00:00
Oliver Smith e3e79e2806 include/osmocom/sigtran/sccp_sap.h: cosmetic fixes
Move return type to the same line as the name of the function to follow
coding guidelines.

Change-Id: I10475aa395a1cfd49ff62c758f99f9689a0038d0
2022-02-01 11:08:34 +00:00
Philipp Maier 5cac653a98 sccp_user: do not force the role ASP when configured differently
The current implementation of osmo_sccp_simple_client forces the ASP to
run in ASP role. Even then when the user has configured it differently
via VTY. The osmo_sccp_simple_client should respect the VTY
configuration.

Change-Id: Ib57c513407747d36e503a4fb01c50c69dea0cb85
Related: SYS#5796
2022-01-26 11:02:02 +01:00
Pau Espin d9e54d7a34 xua_default_lm_fsm.c: Fix typo in comment
Change-Id: I4095878fb05c3128be556ab36dfc7ab88d309c9a
2022-01-21 10:57:03 +01:00
Oliver Smith 853ce2ec13 osmo_ss7_instance_destroy: use for_each_entry_safe
Fix segfault in each loop.

Change-Id: Ieaec3584375b0e6b943638fe1e50b4d74a627216
2021-12-21 18:58:20 +01:00
Oliver Smith 0aa3164b93 src/sccp_scmg: fix typo
Change-Id: Ifd6264da2da2e7cc62485dcde9b6c462d4858626
2021-12-21 18:58:20 +01:00
Oliver Smith 043275129c treewide: remove FSF address
Remove the paragraph about writing to the Free Software Foundation's
mailing address. The FSF has changed addresses in the past, and may do
so again. In 2021 this is not useful, let's rather have a bit less
boilerplate at the start of source files.

Change-Id: Ia450b630e0b60b38835f599c93985bbe97c50d2f
2021-12-14 12:54:05 +00:00
Pau Espin 77700b6eb5 ss7: Use sctp value_string from libosmo-netif
Depends: libosmo-netif Change-Id Ica6f01851fb94e31f4ef097494bb1b8a95597ba6
Change-Id: I42323eb5d02d4f67daac7b0a4365f5f6f6057666
2021-11-23 15:50:04 +01:00
Pau Espin 2d9e84b703 Bump version: 1.4.0.39-484f-dirty → 1.5.0
Change-Id: I963499ba9f78924660127312f4469b0781fed805
2021-11-16 14:44:42 +01:00
Harald Welte 484fc3875b osmo_ss7: properly unlink rate_counter on AS/ASP free
When introducing rate_couters, I forgot to call
rate_ctr_group_free().  I thought free'ing the parent object
via talloc is sufficient, but that obviously misses the point that
rate_counters have an internal linked list from which they must be
unlinked.

Change-Id: I8d27f025c22776d0153d867e36c073ef716eb974
2021-11-16 11:20:11 +01:00
Harald Welte e9727688cd rate_ctr: Use legal counter group prefixes / names (no '.' allowed)
This avoids log messages like
DLGLOBAL <0016> rate_ctr.c:92 'sigtran.as' is not a valid counter group identifier

Change-Id: I08666a1c3c1345cd3b0e55d544f6ac4a6df62fbf
2021-11-16 11:13:11 +01:00
Harald Welte f71096a2b0 spelling fixes in comments
Change-Id: I4ecd9a1c5241cfd3a3e1daf05f7826876371369f
2021-11-15 18:05:04 +01:00
Harald Welte ff7404c742 stp: Add basic RX/TX rate counters on AS and ASP level
This adds some very basic rx/px rate counters to the SS7 AS and ASP

OsmoSTP> show rate-counters
SIGTRAN Application Server 0 (as-rkm-1):
             rx:msu:total:      86078 (1888/s 86078/m 0/h 0/d)
             tx:msu:total:          0 (0/s 0/m 0/h 0/d)
SIGTRAN Application Server Process 0 (asp-dyn-0):
         rx:packets:total:      86081 (1888/s 86081/m 0/h 0/d)
         tx:packets:total:          5 (0/s 5/m 0/h 0/d)

Change-Id: Idb811ca81adfe47152d484f6b981e661dc569e15
2021-11-15 18:05:01 +01:00
Harald Welte 5ad97995e9 sccp_scmg: Add missing line eneding to log statement
Change-Id: I7ec5c86fbef429cd1e23be87b773823f2e736f2f
2021-11-14 20:36:21 +01:00
Harald Welte 8aa64eb92a sccp: Add minimalistic support for XUDT / XUDTS
XUDT and XUDTS can be used in two situations:

a) because the sender wants to use segmentation
b) because the sender wants to include a hop counter

In this patch, we implement support for case "b" only.

Change-Id: Ic5b9486f1aeb4bb90cfe702a7ce996f5d82ded2c
Related: OS#5281, SYS#5674
2021-10-26 16:27:14 +02:00
Pau Espin 8275949838 osmo_ss7.c: Fix typo in doc for osmo_ss7_asp_peer_add_host()
Change-Id: Idba9ed3dc1d8968d304f88fdb1ebef422dd982e9
2021-10-07 13:38:58 +02:00
Pau Espin f594b7dfd2 osmo_sccp_simple_client_on_ss7_id(): Allow set internally proper IPv4/v6 default hosts
Allow user apps to relay the decision of proper default local/remote
hosts values to the library. Proper configuration of these addresses can
be quite cumbersome to get correctly, or confusing at least. That's due
to the multi-homing feature of SCTP where both IPv4 and IPv6 are
involved.

We were already doing this kind of automatic setup in
osmo_ss7_vty_go_parent(), where we do validations and assign addresses
based on IPv6 availability.
On the other hand, apps using osmo_sccp_simple_client_on_ss7_id() API
usually pass  "localhost" as default address. In Linux, when IPv6 is enabled
localhost is resolved as "::1", and "127.0.0.1" when IPv6 is disabled.

Let's instead allow apps to relay the setup to the lib, so same addresses
can be applied both during VTY command as well as if there was no VTY
setup at all.

Related: OS#5186
Change-Id: I82e203612571b7651d758d8148661f706a1642ba
2021-10-07 13:38:32 +02:00
Pau Espin c83f3a870d cosmetic: Fix typos in documentation
Change-Id: I1164490c140cc5ca82e5977d24f5971ba216014b
2021-10-05 16:09:24 +02:00
Pau Espin 4dfb938f9c cosmetic: Fix extra empty line
Change-Id: Ibd499fc9376523e6dcfe8ba6792603c237db59f5
2021-10-05 15:59:02 +02:00
Oliver Smith 76985a9aa2 m3ua: fix m3ua_encode_dupu
Fix wrong header and swapped user / cause values (see RFC 4666). This
makes TC_ssnm_distribution_dupu pass.

Change-Id: I717b64d13d12a2781c90e4d2f83643331797bed4
2021-07-23 17:13:19 +02:00
Oliver Smith 1a75755f9b m3ua: reject deregistration of static routing keys
Make STP_Tests_M3UA.TC_rkm_unreg_never_registered pass.

Closes: OS#4239
Change-Id: Ie5d84d06e7d5d32c8a22f770f71a5449f4a78484
2021-07-23 10:02:16 +02:00
Eric Wild 387304c41d m3ua: fix leaky usage of m3ua_tx_xua_asp
m3ua_tx_xua_asp will at some point convert the xua msg to a msgb by
copying and then send it, the xua msg still needs to be freed by the
caller.

Closes: OS#5185
Change-Id: Id8584b99f30f2db602d4d129e4114821697272ab
2021-07-11 21:10:21 +02:00
Philipp Maier b3cb0c14bb osmo_ss7_vty: allow users to inspect routes also in ASP role
When libosmo-sigtran runs in ASP role, then the entire routing table
introspection (show) and routing table configuration VTY commands are
not present. Editing the routing table manually only makes sense in SG
role, but it is still useful to be able to inspect the routing table in
ASP and SG mode.

Change-Id: Ieaef4f0344b5b77ff5047013e9da1e938004e97c
Related: SYS#5392
2021-06-17 15:51:29 +02:00
Philipp Maier b4f1b2cb8b osmo_ss7_vty: automatically create routes for routing key
Operators may set up a routing key in each AS node. However, this does
not mean that there is also a route added to the routing table. If the
default route is not sufficient (e.g. if multiple AS are defined), then
operators are epected to put matching routes into the routing table.
However, when libosmo-sigtran runs in ASP role, the VTY commands that
allow to routing table changes are not present.

In an ASP role we are in an endpoint situation, so no complex routing
is required, in most cases (single AS) even the default route is enough
but to ensure that each AS will get a route, add routing tables
automatically when running in ASP role.

Change-Id: Ic60b36983232308250e591dbad576aaafdd6b586
Related: SYS#5392
2021-06-16 17:53:04 +02:00
Pau Espin ee10347957 ipa_asp_fsm: Support server starting handshake with ID_GET or ID_ACK
The behavior here since to have changed at least a couple times in
history, always apparently breaking some compatibility:

* libosmo-sccp.git a0dd986f55
  (SCCPLite MSC sends IPA ID ACK at startup) [10th July 2018]
* libosmo-sccp.git 9c0fae14d1
  (Reverts back to sending IPA ID GET at startup [29th April 2021]
* osmo-ttcn3-hacks.git 3bf31d216a18c1d6a6e298a592f873beea322939
  (Changes server emulation to send IPA ID ACK when BSC connects to it) [24th August 2018)

So it seems the proper way is to start handshake:
CLI <- SRV: IPA ID GET
CLI -> SRV: IPA ID RESP
CLI <- SRV: IPA ID ACK
CLI -> SRV: IPA ID ACK

However, it seems some SCCPLite MSCs (acting as IPA srv) skip the first
ID GET + ID RESP handshake and go directly for ACKs:
CLI <- SRV: IPA ID ACK
CLI -> SRV: IPA ID ACK

So, let's make everybody happy and support both cases in the client FSM.
If server sends us IPA ID ACK first, simply send back an IPA ID ACK and
be done with it, otherwise if it sends us an IPA ID GET, go for the full
handshake.

Change-Id: Ie9968ce8cd8582deb583024ff3e46736a07883fe
2021-06-09 13:40:27 +02:00
Pau Espin e4e047bbec cosmetic: xua_asp_fsm.c: reorder functions in file
Reorder functions to follow usual order in FSM files:
1- structs and helper functions
2- for each state: first _oneneter, then the action func
3- generic state fsm functions (timers, all_state)
4- struct osmo_fsm_state
5- FSM allocator and public functions

Change-Id: Ic143db3dda48750effddaa0cafadf960f5b5c38c
2021-06-09 12:59:50 +02:00
Vadim Yanitskiy ce72cdff6e VTY: write_one_asp(): fix 1 << 31 cannot be represented by 'int'
Change-Id: Ib144df42baed16aeffa5b56afd0c17960ee6004e
Closes: OS#5158
2021-05-21 17:54:47 +02:00
Harald Welte 2f8b645ecb sccp_scrc: Ensure we have not just SSN but at least OPC+SSN in CallingParty
There are implementations out there which send us traffic, specifically
in this case SCMG (SST) that has only SSN in both Called and Calling
Party. This means the inbound SST message is routed correctly to the
local SCCP user of libosmo-sigtran. But when that local SCCP user
responds with inverting Called/Calling Party, the new destination again
just contains a SSN.

As a result, we don't know where to route the message (we always need a PC).

Change-Id: Id66ae960ebe3cb3b09c6dd5454f9ac9c073f46d7
Closes: OS#5146
2021-05-13 22:00:14 +02:00
Harald Welte 3639af549c m3ua/sua: Add new snm_inactive quirk
This quirk allows the M3UA + SUA code to accept SSNM/SNM traffic despite
being in AS-INACTIVE state.  This is forbidden by the RFCs but there
are some implementations that apparently just don't care what is
specified.

Change-Id: I193dd546b3e3c00e29f192d0d1bf7819b3e194be
Closes: OS#5148
2021-05-13 18:11:53 +02:00
Harald Welte 0f769392a5 m3ua/sua: Add quirk for allowing inbound DAUD from SG in ASP role.
The M3UA RFC talks about this message being used in ASP->SG direction,
not the other way around.

Closes: OS#5147
Change-Id: I36ff172b47142a877b37bbd149073bef35b36a74
2021-05-13 18:11:53 +02:00
Harald Welte e9b21ff256 osmo_ss7: introduce notion of configurable 'quirks'
A quirk is an implementation work-around in order to establish
interoperability with another implementation, either a buggy one or
one that follows a different interpretation of a given spec.

For now, we introduce a first quirk affecting when we (in ASP role)
send an ASP-ACTIVE message to the SG.

Closes: OS#5145
Change-Id: Idd947ea39d743eb1bc9342ad9d098036821da45b
2021-05-13 18:11:51 +02:00
Harald Welte 2cf796a2a5 ss7_asp_test.vty: Allow more asp node commands after shutdown
this will be required once we add new commands (quirk) in the
next few patches.

Change-Id: If1068cd94782df236948101166a76586e9845762
2021-05-11 20:36:31 +02:00
Harald Welte ab85cdf1ab Constrain connection ID allocation to 24 bits
We currently use the local connection ID on the SCU SAP also as local
reference on the wire-line SCCP messages.  However, the latter only has
a 24 bit range, so we should make sure to wrap accordingly on
allocation.

Change-Id: I414d29271da48ac0b05a688ce9e949a66e4d0d92
Closes: OS#3921
Related: OS#3871
2021-05-10 11:14:01 +02:00
Harald Welte a7c4f97348 ss7_vty: Print actual connected port number in case of IPA server
In IPA, unlike M3UA/SUA, we often have clients connecting from
random/unknown ports. In such cases, the configured remote port is '0'.

Let's use getsockname to determine the actual source ip/port of the
connected client (if any) during "show ... asp"

Change-Id: I1327a46d0b74c572d2ad828a958090af53b9fa37
Closes: SYS#5429
2021-05-03 19:31:56 +00:00
Harald Welte c288852320 Revert "osmo_ss7: free the sock_name string once an ASP socket is closed"
This reverts commit 03e3b04234.

It caused a regression, as apparently some code is using the sock_name
string even after the close:

DLSS7 osmo_ss7.c:1676 0: asp-asp-dyn-0: xua_srv_conn_cb(): sctp_recvmsg() returned 12 (flags=0x8080)
DLSS7 osmo_ss7.c:1608 0: asp-asp-dyn-0: xUA SRV SCTP NOTIFICATION 32773 flags=0x0
DLSS7 osmo_ss7.c:1621 0: asp-asp-dyn-0: xUA SRV SHUTDOWN_EVENT
DLSS7 osmo_ss7.c:1875 asp-dyn-0: connection closed
DLSS7 osmo_ss7.c:1881 XUA_ASP(asp-dyn-0){ASP_INACTIVE}: Received Event SCTP-COMM_DOWN.ind
DLSS7 xua_asp_fsm.c:669 XUA_ASP(asp-dyn-0){ASP_INACTIVE}: state_chg to ASP_DOWN
DLSS7 xua_asp_fsm.c:113 0: asp-asp-dyn-0: No Layer Manager, dropping M-ASP_DOWN.indication
DLSS7 xua_asp_fsm.c:113 0: asp-asp-dyn-0: No Layer Manager, dropping M-SCTP_RELEASE.indication
DLSS7 osmo_ss7.c:1442 0: asp-asp-dyn-0: Destroying ASP
DLSS7 osmo_ss7.c:1449 XUA_ASP(asp-dyn-0){ASP_DOWN}: Terminating (cause = OSMO_FSM_TERM_REQUEST)
DLSS7 osmo_ss7.c:1449 XUA_ASP(asp-dyn-0){ASP_DOWN}: Freeing instance
DLSS7 fsm.c:573 XUA_ASP(asp-dyn-0){ASP_DOWN}: Deallocated
=================================================================
==2928584==ERROR: AddressSanitizer: heap-use-after-free on address 0x618000004d28 at pc 0x7fd6cddeaff8 bp 0x7ffe978fbad0 sp 0x7ffe978fbac8
READ of size 8 at 0x618000004d28 thread T0
    #0 0x7fd6cddeaff7 in xua_srv_conn_closed_cb (/space/home/laforge/projects/git/libosmo-sccp/src/.libs/libosmo-sigtran.so.5+0x17bff7)
    #1 0x7fd6cc443d0f in osmo_stream_srv_destroy (/usr/local/lib/libosmonetif.so.8+0x7ed0f)
    #2 0x7fd6cdde8ce8 in xua_srv_conn_cb (/space/home/laforge/projects/git/libosmo-sccp/src/.libs/libosmo-sigtran.so.5+0x179ce8)
    #3 0x7fd6cc44285e in osmo_stream_srv_read (/usr/local/lib/libosmonetif.so.8+0x7d85e)
    #4 0x7fd6cc44331d in osmo_stream_srv_cb (/usr/local/lib/libosmonetif.so.8+0x7e31d)
    #5 0x7fd6cd88a1c7 in poll_disp_fds (/usr/local/lib/libosmocore.so.17+0x10d1c7)
    #6 0x7fd6cd88a30d in _osmo_select_main (/usr/local/lib/libosmocore.so.17+0x10d30d)
    #7 0x7fd6cd88a32c in osmo_select_main (/usr/local/lib/libosmocore.so.17+0x10d32c)
    #8 0x557aab05c078 in main /space/home/laforge/projects/git/libosmo-sccp/stp/stp_main.c:267
    #9 0x7fd6ccc50d09 in __libc_start_main ../csu/libc-start.c:308
    #10 0x557aab05b389 in _start (/space/home/laforge/projects/git/libosmo-sccp/stp/.libs/osmo-stp+0x3389)

Change-Id: I72f83114408e7a54d1f3072338fa6f189bf4064f
2021-04-30 15:51:22 +02:00
Harald Welte 03e3b04234 osmo_ss7: free the sock_name string once an ASP socket is closed
It's confusing to keep around a string representation of what peer the
socket was previously connected to.

Change-Id: I00d47fc355bfe24915653767ad75c1f491c060d5
2021-04-30 12:06:20 +02:00
Harald Welte a25251a15b don't do explicit NULL checks before calling talloc_free()
Change-Id: I97a072ca32820ad34785ac6a54b00ed51b519305
2021-04-30 12:03:07 +02:00
Harald Welte 9c0fae14d1 xua_asp_fsm: Fix IPA client role
The IPA server worked as expected, but the IPA client has some clear
logic bug that prevented it from working.  It shows that we never
really use any of that IPA/SCCPlite stuff after years in spec-compliant
SIGTRAN land.

A client now first waits for the IPA_REQ, sends its IPA_RESP, then
waits for the ACK, ...

Change-Id: Icfc32cad7d65c94dc21754b8f879afcf34d34a92
2021-04-29 21:10:30 +02:00
Harald Welte d9098703fa xua_as_fsm: Only delete a route for an IPA AS if we created one earlier
The current code would potentially delete a route that was statically present in the
configuration in the following situation:

* route exists in config
* identical route is attempted to be added at AS-ACTIVE time, but fails
* route is unconditionally deleted at AS-DOWN time
* user now does 'write file' and has lost a route

Let's make sure we only delete the route if we added it previously.

Change-Id: I9ad5f7ebe0790e6c186b8ea1b12f204860a00cd2
Related: SYS#5422
2021-04-29 21:10:30 +02:00
Harald Welte 40c4461c3a osmo_ss7: Truncate route 'mask' to point code bit length
Otherwise we run into the problem that a route with mask 0xffffff
differs from one with a mask of 0x3fff despite having only 14 bit
point code length and them being logically equal.

Change-Id: I5d5c828de45724d93a0461bb0dd7858fd8378acd
Related: SYS#5422
2021-04-29 20:07:34 +02:00
Harald Welte 5b0ec1ce67 ipa: Move automatic route add/del from ASP to AS level
SS7 routes operate on AS level, not ASP level.  However, the
automatic SS7 route creation/destruction for IPA was implemented
at the ASP level.  This works for single-connection ASs, but
obviously fails in load-share situations:  We attempt to add the
same route several times, and we delete it at the first ASP
disconnect, even while other ASPs still exist.

This patch moves the IPA route creation/deletion from the ASP level
to the AS level.  When the AS becomes active, the route is added;
when it goes to DOWN state, it is removed.

Change-Id: Idb602beae3e9bc19f7bd96355c02ec8dfd9c5d6c
2021-04-26 19:43:31 +00:00
Harald Welte 6b799aeb67 Don't create duplicate routes in osmo_ss7_route_create()
Let's refuse to create exactly identical routes

Change-Id: I520415d4499a4017dfdbdfc3cd67522e1bbd1627
Related: SYS#5422
2021-04-26 13:06:57 +02:00
Harald Welte 37a3b1f07d ipa_asp_fsm: Fix AS lookup from IPA ASP
Contrary to proper SIGTRAN, IPA/SCCPlite cannot support multiple
AS within one ASP.  When looking up the AS from the ASP, we cannot
blindly use routing context 0 to find the AS, as there may very well
be multiple IPA AS, and all of those have routing context 0.

As a result, the exiting look-up by osmo_ss7_as_find_by_rctx(inst, 0)
will return the wrong AS, and we will try to add/delete routes for
a completely different AS when ASPs are coming up or going down.

Instead, we need to use xua_find_as_for_asp() in order do the look-up:
It will resolve the single AS within the ASP.

Change-Id: Id295daf84f6ba1cc56cbe1761f874bea329e17ea
Cloess: SYS#5422
2021-04-26 08:54:09 +00:00
Harald Welte 8a43eb4186 osmo_ss7: Log route destruction (like we log route creation)
Change-Id: I503cc97db5f907610074cb513299a3a8505e00e5
2021-04-25 18:49:17 +02:00
Harald Welte f414447bfd don't log "SCTP connection close" if an IPA/TCP connection closes
The xua_srv_conn_closed_cb() function is shared/generic.  Let's simply
write "connection closed" to avoid any confusion.  As the ASP name is
printed, it should be clear which L4 protocol was used.

Change-Id: I506ccc2665a6b0af0fde3961e7e7937af7a81219
2021-04-25 18:49:17 +02:00
Harald Welte fe770ee0b1 ipa_asp_fsm_del_route(): Log what happens if we bail out early
Let's inform the user about situations in which we'd want to delete
the route for an IPA client, but we run into some problem and
bail out early.

Change-Id: Ie3f57d22901f169afb2c844476b5839cc36752aa
Related: SYS#5422
2021-04-25 15:01:33 +02:00
Harald Welte 9db6213620 ipa: Use VTY-configured default network indicator
When we receive a message from an IPA/SCCPlite connection, we only
have SCCP global titles and no underlying M3UA.  We since have
to introduce a fake M3UA header.  While we correctly set the SI,
OPC and DPC, we didn't set the NI to what is configured as default
for the cs7 instance in the VTY.  For international, this problem
was hidden by the fact that international is '0' and hence our
default memory initialization.

Change-Id: I02c618fa0a0aa2a859fcd56397df9637043c8e6e
Closes: SYS#5421
2021-04-25 14:29:30 +02:00
Harald Welte e5ffedf526 Add README.md describing what this repo is about
Change-Id: I965dadf1ef4a8340f6995ec745607c28e7bb1f89
2021-03-21 22:56:42 +01:00
Pau Espin 6304393363 Bump version: 1.3.0.51-6858-dirty → 1.4.0
Change-Id: I017147905ffb69829d010f3e8416c8c5d80e7040
2021-02-23 17:57:33 +01:00
Pau Espin 685816f97f stp: generate coredump and exit upon SIGABRT received
Previous code relied on abort() switching sigaction to SIG_FDL +
retriggering SIGABRT in case the signal handler returns, which would
then generate the coredump + terminate the process.
However, if a SIGABRT is received from somewhere else (kill -SIGABRT),
then the process would print the talloc report and continue running,
which is not desired.

Change-Id: Idca8e360968cb6998591737348ce520954e251b2
Fixes: OS#4865
2021-02-23 17:40:46 +01:00
Harald Welte 2181d644bc manuals: generate vty reference xml at build time
Rremove osmo-stp_vty_reference from the source tree.

In manuals/Makefile.am use the new BUILT_REFERENCE_XML feature recently added
to osmo-gsm-manuals, and add a build target to generate the XML using the new
osmo-stp --vty-ref-xml cmdline switch.

Change-Id: I5bcbbdf7b737d2ce36ea877bc78c8cf191a64e1b
Depends: I613d692328050a036d05b49a436ab495fc2087ba
Related: OS#5041
2021-02-23 17:20:08 +01:00
Harald Welte 6d8d9b3ced main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
Change-Id: Ia5abf7457ee7e97ec3fcd5520e5ef82ef808667a
Related: OS#5041
Depends: Ie2022a7f9e167e5ceacf15350c037dd43768ff40
2021-02-23 17:20:08 +01:00
Harald Welte 7eedb6b03c Add osmo_ss7_pointcode_print_buf() to API
Like osmo_ss7_pointcode_print(), but prints into an user-supplied
buffer.

Change-Id: I5fc7d7746eb90a9d404b6b50bf9ded6789a1c33c
2021-02-23 17:20:08 +01:00
Pau Espin d7ecf7a150 tests: Replace deprecated API log_set_print_filename
Change-Id: Id09f903fc5008d074763441a26d71c4fbaf394d8
2021-02-19 13:10:35 +01:00
Pau Espin 25234f9158 gitignore: Ignore autofoo *~ churn
Change-Id: Ia9f01761b595d9b082d78b05d71ff549b8e33d91
2021-02-19 13:06:27 +01:00
Pau Espin 5cac145d2c tests: Explicitly drop category from log
Let's disable category here since we don't care about its formatting here.

In any case, every test relying on logging output validation should
always explicitly state the config to avoid issues in the future if
default values change.

Change-Id: I899e63ab2702bf25514f6585fb45f5bbf60a9ac9
Related: OS#5034
2021-02-19 13:06:02 +01:00
Harald Welte afbcc5d532 xua_snm: Implement handling of DUPU messages
A DUPU message in SUA and M3UA indicates the unavailability of
a (MTP-level) user, i.e. the entire SCCP, ISUP, ... is not available.

If we receive a DUPU (destination user part unavailable) message in ASP
role, then we must
 * distribute it to any other ASPs for which we operate in SG mode
 * pass it as MTP-STATUS.ind to SCCP, which can then generates
   N-PCSTATE.ind to the SCCP User

Change-Id: I1559ed0f761a8495b222df48c6bd43798e220471
2021-02-10 19:58:38 +01:00
Harald Welte 7daae9bd67 sccp: more SCMG handling: SSA and SSP message handling
When a SSP (Subsystem Prohibited) or SSA (Subsystem Available) SCMG
message is received, we must generate the respective primitives towards
the SCCP user.

Change-Id: I149166a25113f5d3e3536f9297bf89ff3139b9e3
2021-02-10 19:58:00 +01:00
Harald Welte 0d53666108 sua: per-ssn DUNA/DAVA notification
Unlike M3UA, in SUA a DUNA/DAVA message can contain not just the point
code that became available / unavailable, but it can also include a SSN.

In that case, it is just the SSN that became available/unavailable, and
not the entire point code.  Hence, a N-STATE.ind and not a N-PCSTATE.ind
must be delivered to the SCCP user.

Change-Id: Ie9a45b905bc17e7b695e15fe12ba4bbadcd032bf
2021-02-10 19:56:03 +01:00
Harald Welte bf85d6f433 xua: introduce xua_msg_get_u32p() helper
Change-Id: Id7780074b82bc668ae148456750b1a01799decd1
2021-02-08 18:11:12 +01:00
Harald Welte d2dc5ed987 sccp: Add minimalistic SCMG implementation
SCMG (SCCP Management) is a special sub-system that normally resides
at SSN=1.  In Osmocom we so far ignored its existence.  However,
in terms of interop with other implementation, we should implement
at least some basic features.

The only procedure implemented in this initial commit is the response
to an incoming SST (Subsystem Test) message.  If we don't respond to
this message, a remote SCCP entity could assume the SSN is dead on
our side, rendering communication impossible.

Change-Id: I04b162476f7652ef0540b5ea7299e9447efd1d09
2021-02-08 18:11:08 +01:00
Harald Welte 943affdd48 sccp: Notify users of point code available/unavailable
* add N-PCSTATE.ind and N-STATE.ind definitions to SCCP user SAP
* add minimal SCMG (SCCP Management) and LBCS (Local Broadcast)
* generate MTP-PAUSE.ind/MTP-RESUME.ind based on received xUA DUNA/DAVA
* generate N-PCSTATE.ind towards the local SCCP users

Change-Id: Idb799f7d7ab329ad12f07b7cbe6336da0891ae92
Related: OS#2623, OS#3403, OS#4701
2021-02-08 18:00:56 +01:00
Harald Welte 6cb841b92b xua: Implement SNM availability/unavailability messaging
M3UA and SUA have one sub-protocol called [S]SNM, through which the
SG informs the ASP about certain destinations (point codes) becoming
available (DAVA) or unavailable (DUNA) in the SS7 network.

This patch adds support for
* generating DAVA/DUAN on a SGP when the AS FSM changes to/from AS-ACTIVE
* receiving DAVA/DUNA on an ASP and informing other "SG role" AS/ASP
* processing DAUD from ASP received by SG, generating relate DAVA/DUNA
  responses

Related: OS#2623
Change-Id: Id92be4691b0fd77598a6edb642c028bbd8c5b623
2021-02-08 18:00:56 +01:00
Harald Welte f5d90c46e1 SUA: Verify routing context IE of incoming CL and CO messages
When receiving user-data (connectionless / connection-oriented),
we must make sure that there either
a) no routing context IE in the message, and only one AS within the ASP, or
b) a valid routing context IE for an AS within the ASP

This important input validation has been done in M3UA for a long time,
but somehow never been implemented on the SUA side so far.

Change-Id: Icc232250513009137add3b45fecbb5d2a07c0645
2021-02-08 11:42:33 +01:00
Harald Welte a0228a74e0 m3ua: Move find_as_for_asp() to shared xua_find_as_for_asp()
This way the function can be re-used by SUA.

Change-Id: I0dfc5a7a24dd068002e837dc47eb0778c503cac5
2021-02-08 11:42:30 +01:00
Harald Welte aabb38a77b m3ua: re-factor m3ua_rx_xfer(): Externalize AS for ASP lookup
Let's factor-out the lookup of the AS into the separate function
find_as_for_asp().  This enables us to reuse this code in upcoming
support for SNM messages.

Change-Id: If58ea24efe7d54994a7ca2f0a97944bd297a8cc6
2021-02-08 10:41:03 +00:00
Harald Welte bdf5ae5f02 Introduce osmo_ss7_asp_get_log_subsys()
This will allow us to write generic code that uses DLM3UA/DSUA depending
on the ASP protocol.

Change-Id: I7c015b3a2727deff4fc4e6f3bc7bdeeb57e86166
2021-02-08 10:40:46 +00:00
Harald Welte 1153675012 xua_msg: Add xua_msg*_get_str() to obtain string IE
We copy the contents to a static thread-local buffer to ensure
zero termination of the string received by a remote entity.

Change-Id: I8cbb7aeaf0cb64db0ce01c21e5fca9ab3cd932b6
2021-02-08 10:40:46 +00:00
Harald Welte 2aebd133ac sccp_sap: Fix string for N-PCSTATE primitive
Change-Id: Ib42f46661e27b1730badc3647ca2e021e93021b7
2021-02-07 18:28:48 +00:00
Harald Welte 3bd913398a osmo_ss7: const-ify input arguments to osmo_ss7_as_has_asp()
Change-Id: Id23d90ffea855680cd7d4a16b89e652eed0cf39f
2021-02-07 12:43:47 +01:00
Harald Welte 30c2edb099 xua_msg: const-ify input arguments to xua_*_get_u32()
Change-Id: I517943bd11d73195de9418fb1c5d4151dab77873
2021-02-07 12:43:47 +01:00
Harald Welte c584d87d0c xua_msg_add_data() Mark input argument as 'const'
We only read from it.

Change-Id: I12c17273b9d64a084f59d91fc06ae1512d70855b
2021-02-06 17:45:33 +01:00
Oliver Smith 20642478f3 xua_test: fix gcc 4 + -std=gnu11 error
Fix 'error: initializer element is not constant' with debian 8's gcc
4.9.2, triggered by XUA_HDR. Create a new _XUA_HDR without the type cast,
and use it inside of const struct definitions in xua_test.c. The new
macro is needed, because removing the type cast from the original
XUA_HDR would break other uses.

Related: OS#5004
Change-Id: I890432ee976043d012b01023f7dd2cfecf79d115
2021-02-02 16:22:01 +01:00
Oliver Smith ed11cc8385 configure.ac: set -std=gnu11
Change-Id: Iabe929a29a3c7fed2726329215097f7254cf20ca
2021-01-28 09:28:00 +00:00
Oliver Smith 1e2ff4a8b8 contrib/jenkins: don't build osmo-gsm-manuals
Related: OS#4912
Change-Id: I511b2e1f4c3a9e0897cff4241ab5df12327de10d
2021-01-13 13:10:28 +01:00
Harald Welte 48e2988539 update VTY / copyright message
* extend year to 2020
* Pau and Vadim have contributed significatnly
* fix typo (ot -> to)

Change-Id: I5c23e704dd958faf450b2427ff706ac65d4848f4
2020-11-11 20:46:45 +01:00
Pau Espin e7c8067df0 contrib/jenkins: Enable parallel make in make distcheck
Change-Id: I667b245e468b9a2bcc5c9a1a04d59a940e71b24c
Related: OS#4421
2020-10-12 18:28:55 +02:00
Philipp Maier 320c71d45c vty: add attributes to VTY commands indicating when they apply
Change-Id: If77aea2223891663d465f162614ce8db18168c09
Related: SYS#4937, OS#1601
2020-10-06 22:50:10 +02:00
Vadim Yanitskiy b4bac73245 vty: use install_lib_element() and install_lib_element_ve()
See https://lists.osmocom.org/pipermail/openbsc/2020-October/013278.html.

Change-Id: I727e27f4d4d9550e34cb0073134a9ed7faae3c66
Depends: I8baf31ace93c536421893c2aa4e3d9d298dcbcc6
Related: SYS#4937
2020-10-04 16:48:35 +07:00
Neels Hofmeyr 7a4f614ba9 add osmo_sccp_addr_to_id_c()
Change-Id: Iadd34a167a7712796b2501f6a75b5a8d26a828eb
2020-09-23 21:13:18 +02:00
Neels Hofmeyr 134e463230 add osmo_sccp_addr_to_str_c osmo_sccp_inst_addr_to_str_c
Change-Id: Ia7f9d891ea92abd20855374b936aac8b28ae15df
2020-09-22 22:47:24 +02:00
Pau Espin c6a598d21c sccp_helpers: Support printing IPV6 SCCP addresses
Change-Id: Ibdae89ed251e46814a4af65c6384225575a29039
2020-09-14 12:35:21 +02:00
Pau Espin 2221ad87f4 xua_msg: Get Rid of confusing double network byte order swap
The ipv4 addr in addr->ip.v4 is already in network byte order, as it's
usual in struct in_addr (see sua_addr_parse_part()).
So the code in there was simply calling ntohl() because
msgb_t16l16vp_put_u32() is calling htonl() on the given parameter before
storing it. Let's simply use msgb_t16l16vp_put() directly and avoid a
double byte swap which only adds confusion.

Change-Id: I70a94ee1b459d56116f0c6a6c7c3b778a939b7ea
2020-09-14 12:35:21 +02:00
Pau Espin 95e2ec698d sua: Support SUA_IEI_IPv6
Change-Id: I5bd69e8400fd2d9251b5d4edd9bf3514e1194b5a
2020-09-14 12:16:02 +02:00
Neels Hofmeyr b114b73e5d add OSMO_SCCP_ADDR_T_MASK for osmo_sccp_addr_cmp()
Avoid magic number 0xffffffff and rather provide a mask value for all address
components.

Change-Id: I13ff0858e496c4392b8e1590d62f7eb80f191a07
2020-09-05 00:10:01 +02:00
Pau Espin 44456920e5 Fix default ASP local addr if remote addr contains only IPv4
Otherwise it would end up attempting to connect "::" -> "1.2.3.4" which
would fail during osmo_sock_init2_multiaddr(). If local address is unset
(default), but only IPv4 addresses are set on the remote, then one must
use "0.0.0.0" instead.

Change-Id: I33faf1291e9105bba2e816c01a6b4854cf13d5e0
2020-09-04 10:55:12 +00:00
Neels Hofmeyr ec3e7e43ab enum osmo_sccp_ssn: rename to OSMO_SCCP_SSN_SMLC_BSSAP_LE
Quoting 3GPP TS 23.003 8.2:

 1111 1010 BSC (BSSAP-LE);
 1111 1011 MSC (BSSAP-LE);
 1111 1100 SMLC (BSSAP-LE);

Hence the SMLC one should also be named *_BSSAP_LE.

I'm certain no other osmocom code is using this SSN yet, but anyway keep a
backwards compat shim #define.

Change-Id: I3e0c1be0ebbd3883d024174d1e7e9167a8281cfb
2020-09-03 23:56:13 +02:00
Pau Espin b35dc5fc81 Fix (2) change in ss7 server default listen addr, keeps backward-compatibility behavior
This patch is a fixup or extension of commit
96d348efbf, which only fixed the default
values for "asp" VTY node, but not for the xua server's "listen" node.
As a result, without this patch, by default the SCTP server socket will
only listen on IPv4's "0.0.0.0", since NULL is resolved by getaddrinfo
to it instead of "::" as first candidate.

Fixes: 96d348efbf
Change-Id: Ifbc8df854d1f1e9b07b11911ad5da8cdf9f2080a
2020-09-01 13:40:16 +02:00
Pau Espin d94d58c523 Fix matching IPv6 anyaddr :: in osmo_ss7_asp_find_by_socket_addr
Change-Id: I36dd8f1978e3b7b785660745378c9cf77a21a566
2020-08-28 17:14:32 +02:00
Pau Espin 2d5b4ef2be Validate correctly against IPv6 addresses in osmo_ss7_asp_peer_set_hosts()
Until now, host list validation was only taking into account a set of
ipv4-only addresses. As a set can contain now IPv4 and Ipv6 addresses at
the same time, we need to do ANYADDAR validation against addresses of
that specific version inside the set.

Change-Id: I18f3cc59149d478259d7afc456bdc5213c1406e5
2020-08-28 17:14:32 +02:00
Pau Espin 96d348efbf Fix change in ss7 server default listen addr, keeps backward-compatibility behavior
Previous commit changed the default bind/listen address of the server
from NULL (0.0.0.0) to 127.0.0.1, hence breaking some setups where no
"local-ip" was defined and hence from then on were only listening on
localhost by default.

Let's instead bind to "::" if IPv6 is available, which covers any IPv6
and/or IPv4 address. If not available, keep binding to 0.0.0.0.
Similarly, if IPv6 is available set default remote to both ::1 and
127.0.0.1 to allow it working against processes listening on IPv4 or
IPv6 addresses.

Change-Id: Id4718267df2390f70cec519042dc12bac0cd2876
2020-08-28 17:14:32 +02:00
Pau Espin 531ba5d729 Revert "Revert "Introduce SS7 IPv6 support""
Actual fix for the issue is applied in next commit.

This reverts commit ee81289297.

Change-Id: I8b66dd97f47f1b2b72c101b9ce4444e75eb744c0
2020-08-28 17:14:29 +02:00
Pau Espin fc3d9a963e vty: log and return warning if adding ss7 host to set fails
Change-Id: Ib86ef3c3db6096a1e38690b9e598a58387e6b38e
2020-08-28 16:27:02 +02:00
Neels Hofmeyr ee81289297 Revert "Introduce SS7 IPv6 support"
This reverts commit 0b39f2cf7b.

Reason for revert:

Breaks ttcn test suites (at least for osmo-bsc) with osmo-stp error log:
"MTP-TRANSFER.req for DPC 187: no route!"

The breakage is fixed by only reverting the NULL -> "localhost" change
back to NULL. But the commit log indicated a reason for this, so rather
reverting the entire commit for now.

Change-Id: Ia97832f4e3ed646457d5c6eeba27352f1153edec
2020-08-28 08:10:06 +00:00
Pau Espin ce88150c05 Fix finding ASP on IPv6 connections
Change-Id: I09226a5cecc37dd4676acd61c2051befe5234cb3
2020-08-24 10:34:54 +02:00
Pau Espin 0b39f2cf7b Introduce SS7 IPv6 support
In osmo_ss7_vty_go_parent, "127.0.0.1" is changed to "localhost" to let
local NSS decide whether to use IPv4 or IPv6. In newish systems, IPv6
::1 will be selected since IPv6 takes precedence over IPv4.

Similarly, the default source addr needs to be changed from NULL to "localhost"
since for some yet unknwon reason, getaddrinfo(AF_UNSPEC, NULL) returns
first IPv4 "0.0.0.0" and later "::", which is inconsistent with
getaddrinfo("localhost") result, resulting in src=IPv4(0.0.0.0) and
dst=IPv6(::1), which is incompatible and will fail.
In any case, this change doesn't affect users of osmo_sccp_simple_client
because the APIs set both src and dst addresses.

Change-Id: I69c48819b70635c92fa404cafd917af7802d517c
Depends: libosmo-netif.git Change-Id Ie6bb17a9af6ca21d5e350f9c9d2d74c97c5a00af
2020-08-24 10:34:38 +02:00
Pau Espin 67eddd6aa2 configure.ac: Fix trailing whitespace
Change-Id: I7df2b2cd0f8fa2f4f9a1e345735aa944f93e95a4
2020-08-20 12:30:32 +02:00
Pau Espin 002197560d Support setting rt-prio and cpu-affinity mask through VTY
Depends: libosmocore.git Change-Id If76a4bd2cc7b3c7adf5d84790a944d78be70e10a
Depends: osmo-gsm-masnuals.git Change-Id Icd75769ef630c3fa985fc5e2154d5521689cdd3c
Related: SYS#4986
Change-Id: Ib9faa68c1cd09a026909c792a1fd5557c9893fe3
2020-08-20 08:41:36 +00:00
Pau Espin 661c555729 doc: Update VTY reference xml file
Change-Id: I83312c3f22af54db346f4babc9470ff9432ceae4
2020-08-20 08:41:31 +00:00
Vadim Yanitskiy 2b5d9a4862 debian/control: change maintainer to the Osmocom team / mailing list
Change-Id: Ib149fd7716343d0e441c4671832efd05a62d67da
2020-08-13 15:00:53 +00:00
90 changed files with 8175 additions and 4280 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
open_collective: osmocom

2
.gitignore vendored
View File

@ -13,6 +13,7 @@ bsc_msc_ip
bsc_mgcp
*.*~
*.sw?
*~
#configure
aclocal.m4
@ -66,6 +67,7 @@ stp/osmo-stp
*.pc
config.*
*.pyc
tags
/Doxyfile

View File

@ -8,7 +8,7 @@ pkgconfig_DATA = libosmo-sccp.pc libosmo-mtp.pc libosmo-sigtran.pc libosmo-xua.p
EXTRA_DIST = \
.version \
contrib/libosmo-sccp.spec.in \
README.md \
debian \
git-version-gen \
osmoappdesc.py \

77
README.md Normal file
View File

@ -0,0 +1,77 @@
libosmo-sccp - Osmocom SCCP, SIGTRAN and STP
============================================
This repository contains
* *libosmo-sigtran*, a C-language library implementation of a variety of telecom signaling protocols, such as M3UA, SUA, SCCP
(connection oriented and connectionless)
* *OsmoSTP*, a SS7 Transfer Point that can be used to act as router and translator between M3UA, SUA and/or
SCCPlite
* *libosmo-sccp*, a legacy C-language [static] library that we used in prehistoric osmocom code before we had
libosmo-sigtran.
Homepage
--------
The official homepage of libosmo-sccp is at <https://osmocom.org/projects/libosmo-sccp/wiki>
The official homepage of osmo-stp is at <https://osmocom.org/projects/osmo-stp/wiki>
GIT Repository
--------------
You can clone from the official git repository using
git clone https://gitea.osmocom.org/osmocom/libosmo-sccp
There is a web interface at <https://gitea.osmocom.org/osmocom/libosmo-sccp>
Documentation
-------------
osmo-stp User Manuals and VTY reference manuals are [optionally] built in PDF form
as part of the build process.
Pre-rendered PDF version of the current "master" can be found at
[User Manual](https://ftp.osmocom.org/docs/latest/osmostp-usermanual.pdf)
as well as the VTY reference manuals
* [VTY Reference Manual for osmo-stp](https://ftp.osmocom.org/docs/latest/osmostp-vty-reference.pdf)
Forum
-----
We welcome any libosmo-sigtran + osmo-stp related discussions in the
[Cellular Network Infrastructure -> 2G/3G Core Network](https://discourse.osmocom.org/c/cni/2g-3g-cn/)
section of the osmocom discourse (web based Forum).
Mailing List
------------
Discussions related to osmo-stp are happening on the
openbsc@lists.osmocom.org mailing list, please see
https://lists.osmocom.org/mailman/listinfo/openbsc for subscription
options and the list archive.
Please observe the [Osmocom Mailing List
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
when posting.
Issue Tracker
-------------
We use the issue trackers of smocom.org for tracking the state of bug reports and feature requests. Feel free
to submit any issues you may find, or help us out by resolving existing issues.
* [osmo-stp issue tracker](https://osmocom.org/projects/osmo-stp/issues)
* [libosmo-sigtran issue tracker](https://osmocom.org/projects/libosmo-sccp/issues)
Contributing
------------
Our coding standards are described at
<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
We us a gerrit based patch submission/review process for managing contributions. Please see
<https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit> for more details
The current patch queue can be seen at <https://gerrit.osmocom.org/#/q/project:libosmo-sccp+status:open>

View File

@ -7,3 +7,14 @@
# 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
libosmocore >1.9.0 osmo_sock_multiaddr_{add,del}_local_addr()
libosmo-netif >1.4.0 osmo_stream_{srv,cli}_get_fd()
libosmocore >1.9.0 osmo_sock_multiaddr_get_ip_and_port(), osmo_multiaddr_ip_and_port_snprintf()
libosmocore >1.9.0 osmo_sock_sctp_get_peer_addr_info()
libosmo-netif >1.4.0 osmo_sctp_spinfo_state_str(), osmo_sctp_sstat_state_str()
libosmo-sigtran ABI change struct osmo_ss7_asp: new field(s) at the end
libosmo-sigtran ABI change struct osmo_xua_server: new field(s) at the end
libosmo-sigtran API added osmo_ss7_asp_get_trans_proto()
libosmo-sigtran API added osmo_ss7_asp_{find2,find_or_create2}()
libosmo-sigtran API added osmo_ss7_xua_server_{find2,create2}()
libosmo-netif >1.4.0 osmo_io SCTP support

View File

@ -9,6 +9,8 @@ AC_CONFIG_AUX_DIR([.])
AM_INIT_AUTOMAKE([dist-bzip2])
AC_CONFIG_TESTDIR(tests)
CFLAGS="$CFLAGS -std=gnu11"
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@ -22,7 +24,7 @@ AC_PROG_CC
AC_PROG_INSTALL
LT_INIT
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
AS_CASE(["$LD"],[*clang*],
[AS_CASE(["${host_os}"],
[*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
@ -34,10 +36,10 @@ if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
fi
PKG_PROG_PKG_CONFIG([0.20])
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.3.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.3.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.3.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.7.0)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.4.0)
old_LIBS=$LIBS
AC_SEARCH_LIBS([sctp_recvmsg], [sctp], [
@ -75,6 +77,7 @@ if test x"$werror" = x"yes"
then
WERROR_FLAGS="-Werror"
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition"
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
CFLAGS="$CFLAGS $WERROR_FLAGS"
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
@ -168,7 +171,7 @@ if test "x$enable_ext_tests" = "xyes" ; then
AM_PATH_PYTHON
AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmo_verify_transcript_vty.py,yes)
if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then
AC_MSG_ERROR([Please install git://osmocom.org/python/osmo-python-tests to run the VTY/CTRL tests.])
AC_MSG_ERROR([Please install https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests to run the VTY/CTRL tests.])
fi
fi
AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
@ -203,6 +206,5 @@ AC_OUTPUT(
doc/manuals/Makefile
contrib/Makefile
contrib/systemd/Makefile
contrib/libosmo-sccp.spec
Doxyfile
Makefile)

View File

@ -36,7 +36,6 @@ osmo-build-dep.sh libosmo-netif
# Additional configure options and depends
CONFIG=""
if [ "$WITH_MANUALS" = "1" ]; then
osmo-build-dep.sh osmo-gsm-manuals
CONFIG="--enable-manuals"
fi
@ -51,13 +50,14 @@ set -x
autoreconf --install --force
./configure --enable-sanitize --enable-werror --enable-external-tests $CONFIG
$MAKE $PARALLEL_MAKE
$MAKE $PARALLEL_MAKE check || cat-testlogs.sh
DISTCHECK_CONFIGURE_FLAGS="--enable-external-tests $CONFIG" \
$MAKE distcheck \
$MAKE $PARALLEL_MAKE distcheck \
|| cat-testlogs.sh
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
make -C "$base/doc/manuals" publish
fi
$MAKE maintainer-clean
$MAKE $PARALLEL_MAKE maintainer-clean
osmo-clean-workspace.sh

View File

@ -1,188 +0,0 @@
#
# spec file for package libosmo-sccp
#
# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
# Avoid "E: lto-no-text-in-archive"
# https://en.opensuse.org/openSUSE:LTO#Static_libraries
%if 0%{?suse_version}
%global _lto_cflags %{?_lto_cflags} -ffat-lto-objects
%endif
Name: libosmo-sccp
Version: @VERSION@
Release: 0
Summary: Osmocom library for the A-bis interface between BTS and BSC
License: AGPL-3.0-or-later AND GPL-2.0-or-later
Group: Hardware/Mobile
URL: https://osmocom.org/projects/libosmo-sccp
Source: %{name}-%{version}.tar.xz
BuildRequires: automake >= 1.6
BuildRequires: libtool >= 2
BuildRequires: lksctp-tools-devel
BuildRequires: pkgconfig >= 0.20
%if 0%{?suse_version}
BuildRequires: systemd-rpm-macros
%endif
BuildRequires: xz
BuildRequires: pkgconfig(libosmo-netif) >= 0.4.0
BuildRequires: pkgconfig(libosmocore) >= 1.0.0
BuildRequires: pkgconfig(libosmogsm) >= 1.0.0
BuildRequires: pkgconfig(libosmovty) >= 1.0.0
%{?systemd_requires}
%description
SCCP is a network layer protocol that provides extended routing, flow
control, segmentation, connection-orientation, and error correction
facilities in Signaling System 7 telecommunications networks. SCCP is
heavily used in cellular networks such as GSM.
%package -n libosmo-mtp-devel
Summary: Development files for the Osmocom MTP library
License: GPL-2.0-or-later
Group: Development/Libraries/C and C++
%description -n libosmo-mtp-devel
MTP is part of SS7 used for communication in Public Switched
Telephone Networks.
This subpackage contains the development files for the Osmocom MTP
library.
%package -n libosmo-sccp-devel
Summary: Development files for the Osmocom SCCP library
License: GPL-2.0-or-later
Group: Development/Libraries/C and C++
%description -n libosmo-sccp-devel
SCCP is a network layer protocol that provides routing, flow control,
segmentation, connection-orientation, and error correction facilities
in SS7 telecommunications networks.
This subpackage contains the development files for the Osmocom SCCP
library.
%package -n libosmo-sigtran5
Summary: Osmocom SIGTRAN library
License: GPL-2.0-or-later
Group: System/Libraries
%description -n libosmo-sigtran5
Osmocom implementation of (parts of) SIGTRAN.
%package -n libosmo-sigtran-devel
Summary: Development files for the Osmocom sigtran library
License: GPL-2.0-or-later
Group: Development/Libraries/C and C++
Requires: libosmo-sigtran5 = %{version}
%description -n libosmo-sigtran-devel
Osmocom implementation of (parts of) SIGTRAN.
This subpackage contains the development files for the Osmocom
SIGTRAN library.
%package -n libosmo-xua-devel
Summary: Development files for the Osmocom M2UA library
License: GPL-2.0-or-later
Group: Development/Libraries/C and C++
Requires: libosmo-sigtran-devel = %{version}
%description -n libosmo-xua-devel
M2UA provides an SCTP adaptation layer for MTP level 2 user messages
and service interface across an IP network.
This subpackage contains the development files for the Osmocom M2UA
library.
%package -n osmo-stp
Summary: Osmocom SIGTRAN STP (Signaling Transfer Point)
License: GPL-2.0-or-later
Group: Productivity/Telephony/Servers
%description -n osmo-stp
This is the Osmocom (Open Source Mobile Communications) implementation
of a Signaling Transfer Point (STP) for SS7/SIGTRAN telecommunication
networks. At this point it is a very minimal implementation, missing
lots of the functionality usually present in a STP, such as Global Title
Routing, Global Title Translation.
%prep
%setup -q
%build
echo "%{version}" >.tarball-version
autoreconf -fiv
%configure \
--enable-shared \
--includedir="%{_includedir}/%{name}" \
--docdir="%{_docdir}/%{name}" \
--with-systemdsystemunitdir=%{_unitdir}
make %{?_smp_mflags}
%install
%make_install
find %{buildroot} -type f -name "*.la" -delete -print
%check
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%post -n libosmo-sigtran5 -p /sbin/ldconfig
%postun -n libosmo-sigtran5 -p /sbin/ldconfig
%if 0%{?suse_version}
%preun -n osmo-stp %service_del_preun osmo-stp.service
%postun -n osmo-stp %service_del_postun osmo-stp.service
%pre -n osmo-stp %service_add_pre osmo-stp.service
%post -n osmo-stp %service_add_post osmo-stp.service
%endif
%files -n libosmo-mtp-devel
%dir %{_includedir}/%{name}
%dir %{_includedir}/%{name}/osmocom
%{_includedir}/%{name}/osmocom/mtp/
%{_libdir}/libmtp.a
%{_libdir}/pkgconfig/libosmo-mtp.pc
%files -n libosmo-sccp-devel
%dir %{_includedir}/%{name}
%dir %{_includedir}/%{name}/osmocom
%{_includedir}/%{name}/osmocom/sccp/
%{_libdir}/libsccp.a
%{_libdir}/pkgconfig/libosmo-sccp.pc
%files -n libosmo-sigtran5
%{_libdir}/libosmo-sigtran.so.5*
%files -n libosmo-sigtran-devel
%dir %{_includedir}/%{name}
%dir %{_includedir}/%{name}/osmocom
%{_includedir}/%{name}/osmocom/sigtran
%{_libdir}/libosmo-sigtran.so
%{_libdir}/libosmo-sigtran.a
%{_libdir}/pkgconfig/libosmo-sigtran.pc
%files -n libosmo-xua-devel
%{_libdir}/libxua.a
%{_libdir}/pkgconfig/libosmo-xua.pc
%files -n osmo-stp
%{_bindir}/osmo-stp
%dir %{_sysconfdir}/osmocom
%config(noreplace) %{_sysconfdir}/osmocom/osmo-stp.cfg
%{_unitdir}/osmo-stp.service
%dir %{_docdir}/%{name}
%dir %{_docdir}/%{name}/examples
%dir %{_docdir}/%{name}/examples/osmo-stp
%{_docdir}/%{name}/examples/osmo-stp/osmo-stp.cfg
%{_docdir}/%{name}/examples/osmo-stp/osmo-stp-multihome.cfg
%changelog

View File

@ -1,10 +1,16 @@
[Unit]
Description=Osmocom STP (Signal Transfer Point)
Documentation=https://osmocom.org/projects/osmo-stp/wiki
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Restart=always
StateDirectory=osmocom
WorkingDirectory=%S/osmocom
User=osmocom
Group=osmocom
ExecStart=/usr/bin/osmo-stp -c /etc/osmocom/osmo-stp.cfg
RestartSec=2

View File

@ -3,7 +3,7 @@
# this script executes m3ua-testtool against osmo-stp. It assumes that
# it is called from within libosmo-sccp/contrib/test and also assumes
# that adjacent to the libosmo-sccp, there's a check-out of
# git://git.osmocom.org/nplab/m3ua-testtool
# https://gitea.osmocom.org/nplab/m3ua-testtool
# the top of the libosmo-sccp git repository
TOPDIR=../../

View File

@ -3,7 +3,7 @@
# this script executes m3ua-testtool against osmo-stp. It assumes that
# it is called from within libosmo-sccp/contrib/test and also assumes
# that adjacent to the libosmo-sccp, there's a check-out of
# git://git.osmocom.org/nplab/m3ua-testtool
# https://gitea.osmocom.org/nplab/sua-testtool
# the top of the libosmo-sccp git repository
TOPDIR=../../

264
debian/changelog vendored
View File

@ -1,3 +1,267 @@
libosmo-sccp (1.8.0) unstable; urgency=medium
[ Neels Hofmeyr ]
* sccp_scmg: ignore PCSTATE.indication
* add osmo_scu_prim_name_c() / _buf()
* fixup for: add osmo_scu_prim_name_c() / _buf()
* fix sccp_scoc state S_WAIT_CONN_CONF out_state_mask
* add two value_string[]s related to N-PCSTATE
* comment: fix typo 'OSMO_ASCU_'
* SCCP: implement variable limit on Optional Data (CR,CC,CREF,RLSD)
* add osmo_sccp_set_max_optional_data()
* sccp_scoc.c: fix infinite loop on conn ID exhaustion
* add public API: osmo_sccp_instance_next_conn_id()
[ Oliver Smith ]
* Run struct_endianness.py
* debian: set compat level to 10
* systemd: depend on networking-online.target
[ Max ]
* SS7: do not attempt transfer if AS is down
[ Vadim Yanitskiy ]
* mtp_pcap: remove unused static_assert() macro definition
* {examples,stp}/Makefile.am: fix typo in $(COVERAGE_[C]FLAGS)
* tests/*/Makefile.am: -Wall is not a preprocessor flag
* tests: use -no-install libtool flag to avoid ./lt-* scripts
* tests: $(BUILT_SOURCES) is not defined, depend on osmo-stp
* tests: execute osmotest{vty,config}.py against osmo-stp
* osmoappdesc.py: also test doc/examples/osmo-stp-multihome.cfg
* copyright: fix typo: sysmocom s/s.m.f.c./s.f.m.c./ GmbH
[ arehbein ]
* Transition to use of 'telnet_init_default'
[ Pau Espin Pedrol ]
* Avoid allocating conn_id 0x00FFFFFF
* sccp_scpc.c: Simplify conn_create()
* cosmetic: sccp.h: Fix trailing whitespace
* Implement sccp_instance->connections as rbtree
* sccp: Introduce APIs to get/set osmo_sccp_instance priv data pointer
* .gitignore: Ignore *.pyc
* osmo_sccp_simple_client_on_ss7_id(): Support ASP explicitly configured as sctp server
* osmo_sccp_simple_client_on_ss7_id(): Always follow VTY config of ASP if it was explicitly defined in VTY
* Forbid partial VTY configurations of ASPs with name asp-clnt-*
* Add osmo_ss7_asp getters for name and proto
* Set stream_cli/srv name to contain ASP + sockname
* vty: Improve output of show cs7 instance <0-15> asp
* xua_default_lm_fsm.c: Log rx ASP_UP timeout with NOTICE level
* sccp: Use tdef to implement osmo_sccp_timers
* ss7: Refactor osmo_ss7_asp_find_or_create()
* asp: Make ASP default LM timers VTY configurable
* cosmetic: Fix pointer location format
* tests/vty: test multiple local/remote IP addresses
* ss7: Log addr and event state for SCTP_PEER_ADDR_CHANGE
* ss7: Use libosmo-netif's osmo_stream_{cli,srv}_recv() APIs
* asp: Allow setting IP address as SCTP primary upon conn establishment
* asp: Monitor SCTP_PEER_ADDR_CHANGE events to re-apply configured Primary Address
* ss7: Rename internal APIs to avoid exporting them in so file
* ss7: Drop unneeded else branch after early return
* ss7: Split asp and xua_server into their own files
* asp,xua_srv: Use new osmo_stream API to request sockopt SCTP AUTH/ASCONF SUPPORTED
* Allow configuring per-ASP SCTP INIT parameters
[ Harald Welte ]
* cosmetic: Fix log message in routing table add error case
* ASPAC/ASPIA: Don't add routing context IE in ASP-role if routing context 0
* cosmetic: fix various typos in comments, log messages and VTY strings
* M3UA/SUA: Don't add empty routing context IE in DUNA/DAVA/DUPU
* ipa: use LSBs of file descriptor as SLS in IPA->M3UA direction
* m3ua: Add some TODO comments on where we fall short of our potential
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 12 Sep 2023 14:03:24 +0200
libosmo-sccp (1.7.0) unstable; urgency=medium
[ Pau Espin Pedrol ]
* xua_snm: assert to guard against NULL ptr
* sccp: Validate local references > 0x00fffffe are not used
[ Max ]
* cosmetic: improve usage help of example code
* SCCP: enforce optional data length limits
* SCCP: Log more data on CR size error
* [cosmetic] sccp_scoc.c: constify function parameters
* Add basic readme for example code
* SIGTRAN: don't advance FSM on failed connection request
* SIGTRAN: add function to check connection existence
* SIGTRAN: error if attempting to send exceedingly big data
* SIGTRAN: add osmo_sccp_tx_disconn_data() helper
* examples: update vty code
* SIGTRAN: arrange the comments in the encoder to match the spec
* SIGTRAN: cache Optional Data for SCCP CR/CC/RLSD
* Set working directory in systemd service file
[ Harald Welte ]
* Add -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition
* SCOC: dump SUA header when logging "mismatching remote pc"
* cosmetic: fix typo in comment
[ Oliver Smith ]
* tests: fix old-style function definition
* contrib/jenkins.sh: add missing 'make check'
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 07 Feb 2023 14:37:23 +0100
libosmo-sccp (1.6.0) unstable; urgency=medium
[ Pau Espin Pedrol ]
* ss7: Use sctp value_string from libosmo-netif
* xua_default_lm_fsm.c: Fix typo in comment
* xua_asp_fsm: Fix gcc false positive warning
* Add ss7 instances to osmo_ss7_instances in creation order
[ Oliver Smith ]
* treewide: remove FSF address
* src/sccp_scmg: fix typo
* osmo_ss7_instance_destroy: use for_each_entry_safe
* include/osmocom/sigtran/sccp_sap.h: cosmetic fixes
* osmo_ss7_vty: add osmo_sccp_addr_by_name_local
[ Philipp Maier ]
* sccp_user: do not force the role ASP when configured differently
[ Harald Welte ]
* sccp_demo_user: Add command line argument to specify protocol (m3ua/ipa)
* sccp_demo_user: Add 'show stats' to VTY
* sccp_demo_user: make sure the command-line specified point codes are used
* sccp_demo_user: Allow user to specify logmask on command line
* sccp_demo_user: enable stats / rate_counter timers
* M3UA/SUA: Implement handling of SCON (signaling congestion)
* [cosmetic] sccp_scoc.c: fix comments about corresponding SCCP msg types
* [cosmetic] sccp_scoc: Fix typo in comment
* update git URLs (git -> https; gitea)
[ Vadim Yanitskiy ]
* tests: use 'check_PROGRAMS' instead of 'noinst_PROGRAMS'
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 28 Jun 2022 18:22:13 +0200
libosmo-sccp (1.5.0) unstable; urgency=medium
[ Harald Welte ]
* Add README.md describing what this repo is about
* ipa: Use VTY-configured default network indicator
* ipa_asp_fsm_del_route(): Log what happens if we bail out early
* don't log "SCTP connection close" if an IPA/TCP connection closes
* osmo_ss7: Log route destruction (like we log route creation)
* ipa_asp_fsm: Fix AS lookup from IPA ASP
* Don't create duplicate routes in osmo_ss7_route_create()
* ipa: Move automatic route add/del from ASP to AS level
* osmo_ss7: Truncate route 'mask' to point code bit length
* xua_as_fsm: Only delete a route for an IPA AS if we created one earlier
* xua_asp_fsm: Fix IPA client role
* don't do explicit NULL checks before calling talloc_free()
* osmo_ss7: free the sock_name string once an ASP socket is closed
* Revert "osmo_ss7: free the sock_name string once an ASP socket is closed"
* ss7_vty: Print actual connected port number in case of IPA server
* Constrain connection ID allocation to 24 bits
* ss7_asp_test.vty: Allow more asp node commands after shutdown
* osmo_ss7: introduce notion of configurable 'quirks'
* m3ua/sua: Add quirk for allowing inbound DAUD from SG in ASP role.
* m3ua/sua: Add new snm_inactive quirk
* sccp_scrc: Ensure we have not just SSN but at least OPC+SSN in CallingParty
* sccp: Add minimalistic support for XUDT / XUDTS
* sccp_scmg: Add missing line eneding to log statement
* stp: Add basic RX/TX rate counters on AS and ASP level
* spelling fixes in comments
* rate_ctr: Use legal counter group prefixes / names (no '.' allowed)
* osmo_ss7: properly unlink rate_counter on AS/ASP free
[ Vadim Yanitskiy ]
* VTY: write_one_asp(): fix 1 << 31 cannot be represented by 'int'
[ Pau Espin Pedrol ]
* cosmetic: xua_asp_fsm.c: reorder functions in file
* ipa_asp_fsm: Support server starting handshake with ID_GET or ID_ACK
* cosmetic: Fix extra empty line
* cosmetic: Fix typos in documentation
* osmo_sccp_simple_client_on_ss7_id(): Allow set internally proper IPv4/v6 default hosts
* osmo_ss7.c: Fix typo in doc for osmo_ss7_asp_peer_add_host()
[ Philipp Maier ]
* osmo_ss7_vty: automatically create routes for routing key
* osmo_ss7_vty: allow users to inspect routes also in ASP role
[ Eric ]
* m3ua: fix leaky usage of m3ua_tx_xua_asp
[ Oliver Smith ]
* m3ua: reject deregistration of static routing keys
* m3ua: fix m3ua_encode_dupu
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 16 Nov 2021 14:44:42 +0100
libosmo-sccp (1.4.0) unstable; urgency=medium
[ Vadim Yanitskiy ]
* debian/control: change maintainer to the Osmocom team / mailing list
* vty: use install_lib_element() and install_lib_element_ve()
[ Pau Espin Pedrol ]
* doc: Update VTY reference xml file
* Support setting rt-prio and cpu-affinity mask through VTY
* configure.ac: Fix trailing whitespace
* Introduce SS7 IPv6 support
* Fix finding ASP on IPv6 connections
* vty: log and return warning if adding ss7 host to set fails
* Revert "Revert "Introduce SS7 IPv6 support""
* Fix change in ss7 server default listen addr, keeps backward-compatibility behavior
* Validate correctly against IPv6 addresses in osmo_ss7_asp_peer_set_hosts()
* Fix matching IPv6 anyaddr :: in osmo_ss7_asp_find_by_socket_addr
* Fix (2) change in ss7 server default listen addr, keeps backward-compatibility behavior
* Fix default ASP local addr if remote addr contains only IPv4
* sua: Support SUA_IEI_IPv6
* xua_msg: Get Rid of confusing double network byte order swap
* sccp_helpers: Support printing IPV6 SCCP addresses
* contrib/jenkins: Enable parallel make in make distcheck
* tests: Explicitly drop category from log
* gitignore: Ignore autofoo *~ churn
* tests: Replace deprecated API log_set_print_filename
* stp: generate coredump and exit upon SIGABRT received
[ neels ]
* Revert "Introduce SS7 IPv6 support"
[ Neels Hofmeyr ]
* enum osmo_sccp_ssn: rename to OSMO_SCCP_SSN_SMLC_BSSAP_LE
* add OSMO_SCCP_ADDR_T_MASK for osmo_sccp_addr_cmp()
* add osmo_sccp_addr_to_str_c osmo_sccp_inst_addr_to_str_c
* add osmo_sccp_addr_to_id_c()
[ Philipp Maier ]
* vty: add attributes to VTY commands indicating when they apply
[ Harald Welte ]
* update VTY / copyright message
* xua_msg_add_data() Mark input argument as 'const'
* xua_msg: const-ify input arguments to xua_*_get_u32()
* osmo_ss7: const-ify input arguments to osmo_ss7_as_has_asp()
* sccp_sap: Fix string for N-PCSTATE primitive
* xua_msg: Add xua_msg*_get_str() to obtain string IE
* Introduce osmo_ss7_asp_get_log_subsys()
* m3ua: re-factor m3ua_rx_xfer(): Externalize AS for ASP lookup
* m3ua: Move find_as_for_asp() to shared xua_find_as_for_asp()
* SUA: Verify routing context IE of incoming CL and CO messages
* xua: Implement SNM availability/unavailability messaging
* sccp: Notify users of point code available/unavailable
* sccp: Add minimalistic SCMG implementation
* xua: introduce xua_msg_get_u32p() helper
* sua: per-ssn DUNA/DAVA notification
* sccp: more SCMG handling: SSA and SSP message handling
* xua_snm: Implement handling of DUPU messages
* Add osmo_ss7_pointcode_print_buf() to API
* main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
* manuals: generate vty reference xml at build time
[ Oliver Smith ]
* contrib/jenkins: don't build osmo-gsm-manuals
* configure.ac: set -std=gnu11
* xua_test: fix gcc 4 + -std=gnu11 error
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 23 Feb 2021 17:57:33 +0100
libosmo-sccp (1.3.0) unstable; urgency=medium
[ Neels Hofmeyr ]

2
debian/compat vendored
View File

@ -1 +1 @@
9
10

24
debian/control vendored
View File

@ -1,11 +1,11 @@
Source: libosmo-sccp
Maintainer: Harald Welte <laforge@gnumonks.org>
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
Section: libs
Priority: optional
Build-Depends: debhelper (>= 9),
Build-Depends: debhelper (>= 10),
autotools-dev,
pkg-config,
libosmocore-dev (>= 1.3.0),
libosmocore-dev (>= 1.9.0),
autoconf,
automake,
libtool,
@ -13,12 +13,12 @@ Build-Depends: debhelper (>= 9),
git,
doxygen,
libdpkg-perl,
libosmo-netif-dev (>= 0.7.0),
libosmo-netif-dev (>= 1.4.0),
libsctp-dev,
osmo-gsm-manuals-dev
osmo-gsm-manuals-dev (>= 1.5.0)
Standards-Version: 3.9.7
Vcs-Git: git://git.osmocom.org/libosmo-sccp.git
Vcs-Browser: http://git.osmocom.org/libosmo-sccp/
Vcs-Git: https://gitea.osmocom.org/osmocom/libosmo-sccp
Vcs-Browser: https://gitea.osmocom.org/osmocom/libosmo-sccp
Homepage: https://projects.osmocom.org/projects/libosmo-sccp
Package: libosmo-sccp-dev
@ -34,7 +34,7 @@ Description: Development files for libsccp, libmtp and libxua
.
This package contains the development files for the library.
Package: libosmo-sigtran5
Package: libosmo-sigtran9
Section: libs
Architecture: any
Multi-Arch: same
@ -53,7 +53,7 @@ Package: libosmo-sigtran-dbg
Architecture: any
Section: debug
Multi-Arch: same
Depends: libosmo-sigtran5 (= ${binary:Version}),
Depends: libosmo-sigtran9 (= ${binary:Version}),
${misc:Depends}
Description: Documentation for the Osmocom SIGTRAN library
libosmo-sigtran is a shared library containing SS7/SIGTRAN related functionality,
@ -68,7 +68,7 @@ Package: libosmo-sigtran-doc
Architecture: all
Section: doc
Depends: ${misc:Depends},
libosmo-sigtran5 (= ${binary:Version}),
libosmo-sigtran9 (= ${binary:Version}),
libjs-jquery
Description: Documentation for the Osmocom SIGTRAN library
libosmo-sigtran is a shared library containing SS7/SIGTRAN related functionality,
@ -83,7 +83,7 @@ Architecture: any
Multi-Arch: same
Section: libdevel
Depends: ${misc:Depends},
libosmo-sigtran5 (= ${binary:Version}),
libosmo-sigtran9 (= ${binary:Version}),
libosmocore-dev,
libosmo-netif-dev
Description: Development headers for the Osmocom SIGTRAN library
@ -98,7 +98,7 @@ Package: osmo-stp
Architecture: any
Multi-Arch: same
Section: net
Depends: libosmo-sigtran5,
Depends: libosmo-sigtran9,
${shlibs:Depends},
${misc:Depends}
Description: Osmocom SIGTRAN STP (Signaling Transfer Point)

38
debian/osmo-stp.postinst vendored Executable file
View File

@ -0,0 +1,38 @@
#!/bin/sh -e
case "$1" in
configure)
# Create the osmocom group and user (if it doesn't exist yet)
if ! getent group osmocom >/dev/null; then
groupadd --system osmocom
fi
if ! getent passwd osmocom >/dev/null; then
useradd \
--system \
--gid osmocom \
--home-dir /var/lib/osmocom \
--shell /sbin/nologin \
--comment "Open Source Mobile Communications" \
osmocom
fi
# Fix permissions of previous (root-owned) install (OS#4107)
if dpkg --compare-versions "$2" le "1.9.0"; then
if [ -e /etc/osmocom/osmo-stp.cfg ]; then
chown -v osmocom:osmocom /etc/osmocom/osmo-stp.cfg
chmod -v 0660 /etc/osmocom/osmo-stp.cfg
fi
if [ -d /etc/osmocom ]; then
chown -v root:osmocom /etc/osmocom
chmod -v 2775 /etc/osmocom
fi
mkdir -p /var/lib/osmocom
chown -R -v osmocom:osmocom /var/lib/osmocom
fi
;;
esac
# dh_installdeb(1) will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#

2
debian/rules vendored
View File

@ -12,7 +12,7 @@ VERSION := $(shell echo '$(DEBVERS)' | sed -e 's/[+-].*//' -e 's/~//g')
dh $@ --with autoreconf
override_dh_strip:
dh_strip -plibosmo-sigtran5 --dbg-package=libosmo-sigtran-dbg
dh_strip -plibosmo-sigtran9 --dbg-package=libosmo-sigtran-dbg
dh_strip -posmo-stp --dbg-package=osmo-stp-dbg
override_dh_install:

View File

@ -20,3 +20,4 @@ cs7 instance 0
accept-asp-connections dynamic-permitted
local-ip 127.0.0.2
local-ip 127.0.0.1
local-ip ::1

View File

@ -12,6 +12,12 @@ if BUILD_MANUALS
aoip-mgw-options.pdf: $(srcdir)/aoip-mgw-options.adoc $(srcdir)/mgw/*.msc
VTY_REFERENCE = osmostp-vty-reference.xml
BUILT_REFERENCE_XML = $(builddir)/vty/osmo-stp_vty_reference.xml
$(builddir)/vty/osmo-stp_vty_reference.xml: $(top_builddir)/stp/osmo-stp
mkdir -p $(builddir)/vty
$(top_builddir)/stp/osmo-stp --vty-ref-xml > $@
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
OSMO_REPOSITORY=osmo-stp

View File

@ -18,6 +18,8 @@ include::./common/chapters/vty.adoc[]
include::./common/chapters/logging.adoc[]
include::./common/chapters/vty_cpu_sched.adoc[]
//include::./common/chapters/bts.adoc[]
//include::../OsmoNITB/chapters/bts-examples.adoc[]
@ -31,4 +33,3 @@ include::./common/chapters/bibliography.adoc[]
include::./common/chapters/glossary.adoc[]
include::./common/chapters/gfdl.adoc[]

View File

@ -0,0 +1,2 @@
<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>
</vtydoc>

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS=-Wall -g $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMONETIF_CFLAGS) $(COVERAGE_FLAGS)
AM_CFLAGS=-Wall -g $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMONETIF_CFLAGS) $(COVERAGE_CFLAGS)
AM_LDFLAGS=$(COVERAGE_LDFLAGS)
noinst_HEADERS = internal.h

21
examples/README Normal file
View File

@ -0,0 +1,21 @@
This example code is handy in illustrating libosmo-sigtran library use as well as experimenting with SCCP protocol.
Run it as follows:
Server:
./sccp_demo_user -d DLINP,5:DLSS7,5:DLSCCP,1:DSCCP,1:DMAIN,1
Client:
./sccp_demo_user -d DLINP,5:DLSS7,5:DLSCCP,1:DSCCP,1:DMAIN,1 -c
Server's vty:
telnet 127.0.0.1 2324
Client's vty:
telnet 127.0.0.2 2325
On the client side, after entering privileged mode by typing 'enable', you can switch to demo user mode with 'sccp-user' command.
This gives several additional commands, for example:
'called-addr-ssn 202' - selecting "echo" application (201 is "refuser", 203 is "callback")
'connect-req 10 aaaaaaaaaaaaaaaaaaa' - sending N-CONNECT message with ID=12 and optional data
See vty's help for further details.
Calling 'show cs7 instance 0 sccp connections' in privileged mode on the server will show currently active SCCP connections.

View File

@ -12,6 +12,9 @@ enum {
struct osmo_sccp_user;
extern struct osmo_sccp_addr g_calling_addr;
extern struct osmo_sccp_addr g_called_addr;
int sccp_test_user_vty_install(struct osmo_sccp_instance *inst, int ssn);
int sccp_test_server_init(struct osmo_sccp_instance *sccp);

View File

@ -11,9 +11,12 @@
#include <osmocom/core/logging.h>
#include <osmocom/core/application.h>
#include <osmocom/core/fsm.h>
#include <osmocom/core/stats.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/stats.h>
#include <osmocom/vty/misc.h>
#include <osmocom/sigtran/osmo_ss7.h>
@ -28,16 +31,17 @@ static const char *config_file;
static struct osmo_sccp_instance *g_sccp;
static struct osmo_sccp_instance *sua_server_helper(int local_port, const char *local_address, int local_pc,
static struct osmo_sccp_instance *sua_server_helper(enum osmo_ss7_asp_protocol protocol,
int local_port, const char *local_address, int local_pc,
int remote_port, const char *remote_address, int remote_pc)
{
struct osmo_sccp_instance *sccp;
sccp = osmo_sccp_simple_server(NULL, local_pc, OSMO_SS7_ASP_PROT_M3UA, local_port, local_address);
sccp = osmo_sccp_simple_server(NULL, local_pc, protocol, local_port, local_address);
if (sccp == NULL)
return NULL;
osmo_sccp_simple_server_add_clnt(sccp, OSMO_SS7_ASP_PROT_M3UA, "client", remote_pc, local_port,
osmo_sccp_simple_server_add_clnt(sccp, protocol, "client", remote_pc, local_port,
remote_port, remote_address);
return sccp;
@ -101,7 +105,6 @@ static struct vty_app_info vty_info = {
#define DEFAULT_LOCAL_PORT_CLIENT M3UA_PORT
#define DEFAULT_REMOTE_PORT_CLIENT DEFAULT_LOCAL_PORT_SERVER
#define DEFAULT_REMOTE_PORT_SERVER DEFAULT_LOCAL_PORT_CLIENT
#define DEFAULT_REMOTE_PORT_SERVER_STR DEFAULT_LOCAL_PORT_CLIENT_STR
#define DEFAULT_PC_SERVER 1
#define DEFAULT_PC_CLIENT 23
@ -110,13 +113,16 @@ static void usage(void) {
" [-r REMOTE_ADDRESS[:REMOTE_PORT]]\n"
" [-L LOCAL_POINT_CODE] [-R REMOTE_POINT_CODE]\n"
"Options:\n"
" -p: protocol to use (m3ua, sua, ipa; default is m3ua)\n"
" -c: Run in client mode (default is server mode)\n"
" -C filename The config file to use\n"
" -l: local IP address and SCTP port (default is %s:%d in server mode,\n"
" %s:%d in client mode)\n"
" -r: remote IP address and SCTP port (default is %s:%d in server mode,\n"
" %s:%d in client mode)\n"
" -L: local point code (default is %d in server mode, %d in client mode)\n"
" -R: remote point code (default is %d in server mode, %d in client mode)\n",
" -R: remote point code (default is %d in server mode, %d in client mode)\n"
" -d: LOGMASK (libosmocore log mask string, e.g. -d DLINP,1:DLSS7,2)\n",
DEFAULT_LOCAL_ADDRESS_SERVER, DEFAULT_LOCAL_PORT_SERVER,
DEFAULT_LOCAL_ADDRESS_CLIENT, DEFAULT_LOCAL_PORT_CLIENT,
DEFAULT_REMOTE_ADDRESS_SERVER, DEFAULT_REMOTE_PORT_SERVER,
@ -179,9 +185,19 @@ int main(int argc, char **argv)
int remote_port = DEFAULT_REMOTE_PORT_SERVER;
int remote_pc = DEFAULT_PC_CLIENT;
bool lflag = false, rflag = false, Lflag = false, Rflag = false;
enum osmo_ss7_asp_protocol protocol = OSMO_SS7_ASP_PROT_M3UA;
while ((ch = getopt(argc, argv, "cl:r:L:R:C:")) != -1) {
void *tall_ctx = talloc_named_const(NULL, 1, "sccp_demo_user");
init_logging(tall_ctx);
while ((ch = getopt(argc, argv, "p:cl:r:L:R:C:d:")) != -1) {
switch (ch) {
case 'p':
rc = get_string_value(osmo_ss7_asp_protocol_vals, optarg);
if (rc < 0)
exit(1);
protocol = rc;
break;
case 'c':
client = true;
@ -228,6 +244,9 @@ int main(int argc, char **argv)
case 'C':
config_file = optarg;
break;
case 'd':
log_parse_category_mask(osmo_stderr_target, optarg);
break;
default:
usage();
}
@ -243,14 +262,15 @@ int main(int argc, char **argv)
signal(SIGUSR1, &signal_handler);
signal(SIGUSR2, &signal_handler);
void *tall_ctx = talloc_named_const(NULL, 1, "sccp_demo_user");
init_logging(tall_ctx);
osmo_stats_init(tall_ctx);
rate_ctr_init(tall_ctx);
OSMO_ASSERT(osmo_ss7_init() == 0);
osmo_fsm_log_addr(false);
vty_info.tall_ctx = tall_ctx;
vty_init(&vty_info);
logging_vty_add_cmds();
osmo_talloc_vty_add_cmds();
osmo_stats_vty_add_cmds();
osmo_fsm_vty_add_cmds();
osmo_ss7_vty_init_asp(NULL);
osmo_sccp_vty_init();
@ -265,15 +285,14 @@ int main(int argc, char **argv)
}
}
rc = telnet_init_dynif(NULL, NULL, config_file ? vty_get_bind_addr() : local_address,
2324+client);
rc = telnet_init_default(NULL, NULL, 2324 + client);
if (rc < 0) {
perror("Error binding VTY port");
exit(1);
}
if (client) {
g_sccp = osmo_sccp_simple_client(NULL, "client", local_pc, OSMO_SS7_ASP_PROT_M3UA,
g_sccp = osmo_sccp_simple_client(NULL, "client", local_pc, protocol,
local_port, local_address, remote_port, remote_address);
if (g_sccp == NULL) {
perror("Could not create SCCP client");
@ -281,14 +300,17 @@ int main(int argc, char **argv)
}
sccp_test_user_vty_install(g_sccp, OSMO_SCCP_SSN_BSSAP);
} else {
g_sccp = sua_server_helper(local_port, local_address, local_pc,
g_sccp = sua_server_helper(protocol, local_port, local_address, local_pc,
remote_port, remote_address, remote_pc);
if (g_sccp == NULL) {
perror("Could not create SCCP server");
exit(1);
}
sccp_test_server_init(g_sccp);
sccp_test_user_vty_install(g_sccp, OSMO_SCCP_SSN_BSSAP);
}
g_calling_addr.pc = local_pc;
g_called_addr.pc = remote_pc;
while (1) {
osmo_select_main(0);

View File

@ -13,13 +13,13 @@
static struct osmo_sccp_user *g_scu;
static struct osmo_sccp_addr g_calling_addr = {
struct osmo_sccp_addr g_calling_addr = {
.presence = OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC,
.ri = OSMO_SCCP_RI_SSN_PC,
.pc = 23,
};
static struct osmo_sccp_addr g_called_addr = {
struct osmo_sccp_addr g_called_addr = {
.presence = OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC,
.ssn = 1,
.ri = OSMO_SCCP_RI_SSN_PC,
@ -38,35 +38,42 @@ DEFUN(scu_called_ssn, scu_called_ssn_cmd,
DEFUN(scu_conn_req, scu_conn_req_cmd,
"connect-req <0-16777216> [DATA]",
"N-CONNECT.req\n"
"Connection ID\n")
"Connection ID\n"
"Optional Data\n")
{
struct osmo_sccp_user *scu = vty->index;
int conn_id = atoi(argv[0]);
const char *data = argv[1];
int rc, conn_id = atoi(argv[0]);
rc = osmo_sccp_tx_conn_req(scu, conn_id, &g_calling_addr, &g_called_addr,
(const uint8_t *)argv[1], (argc > 1) ? strlen(argv[1]) + 1 : 0);
if (rc < 0) {
vty_out(vty, "Error while sending N-CONNECT.req: %s%s", strerror(-rc), VTY_NEWLINE);
return CMD_WARNING;
}
osmo_sccp_tx_conn_req(scu, conn_id, &g_calling_addr, &g_called_addr,
(const uint8_t *)data, data ? strlen(data)+1 : 0);
return CMD_SUCCESS;
}
DEFUN(scu_conn_resp, scu_conn_resp_cmd,
"connect-resp <0-16777216> [DATA]",
"N-CONNET.resp\n"
"Connection ID\n")
"Connection ID\n"
"Optional Data\n")
{
struct osmo_sccp_user *scu = vty->index;
int conn_id = atoi(argv[0]);
const char *data = argv[1];
osmo_sccp_tx_conn_resp(scu, conn_id, NULL,
(const uint8_t *)data, data ? strlen(data)+1 : 0);
osmo_sccp_tx_conn_resp(scu, conn_id, NULL, (const uint8_t *)argv[1], (argc > 1) ? strlen(argv[1]) + 1 : 0);
return CMD_SUCCESS;
}
DEFUN(scu_data_req, scu_data_req_cmd,
"data-req <0-16777216> DATA",
"N-DATA.req\n"
"Connection ID\n")
"N-DATA.req\n"
"Connection ID\n"
"Data\n")
{
struct osmo_sccp_user *scu = vty->index;
int conn_id = atoi(argv[0]);
@ -89,14 +96,16 @@ DEFUN(scu_unitdata_req, scu_unitdata_req_cmd,
}
DEFUN(scu_disc_req, scu_disc_req_cmd,
"disconnect-req <0-16777216>",
"disconnect-req <0-16777216> [DATA]",
"N-DISCONNT.req\n"
"Connection ID\n")
"Connection ID\n"
"Optional Data\n")
{
struct osmo_sccp_user *scu = vty->index;
int conn_id = atoi(argv[0]);
osmo_sccp_tx_disconn(scu, conn_id, NULL, 42);
osmo_sccp_tx_disconn_data(scu, conn_id, NULL, 42, (const uint8_t *)argv[1], (argc > 1) ? strlen(argv[1]) + 1 : 0);
return CMD_SUCCESS;
}

View File

@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#pragma once
@ -131,7 +127,7 @@ struct mtp_level_3_hdr {
spare : 2,
ni : 2;
#elif OSMO_IS_BIG_ENDIAN
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t ni:2, spare:2, ser_ind:4;
#endif
uint32_t addr;
@ -143,7 +139,7 @@ struct mtp_level_3_cmn {
uint8_t h0 : 4,
h1 : 4;
#elif OSMO_IS_BIG_ENDIAN
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t h1:4, h0:4;
#endif
} __attribute__((packed));
@ -154,7 +150,7 @@ struct mtp_level_3_mng {
uint8_t spare : 4,
length : 4;
#elif OSMO_IS_BIG_ENDIAN
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t length:4, spare:4;
#endif
uint8_t data[0];
@ -174,7 +170,7 @@ struct sccp_con_ctrl_prt_mgt {
uint8_t mul_ind : 2,
spare : 6;
#elif OSMO_IS_BIG_ENDIAN
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t spare:6, mul_ind:2;
#endif
} __attribute__((packed));

View File

@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef mtp_pcap_h
#define mtp_pcap_h

View File

@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef SCCP_H
@ -124,7 +120,7 @@ int sccp_connection_close(struct sccp_connection *connection, int cause);
int sccp_connection_free(struct sccp_connection *connection);
/**
* internal..
* internal..
*/
int sccp_connection_force_free(struct sccp_connection *conn);

View File

@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef SCCP_TYPES_H
@ -28,6 +24,9 @@
#include <osmocom/core/endian.h>
#include <osmocom/core/utils.h>
#define SCCP_MAX_OPTIONAL_DATA 130
#define SCCP_MAX_DATA 256
/* Table 1/Q.713 - SCCP message types */
enum sccp_message_types {
SCCP_MSG_TYPE_CR = 1,
@ -106,7 +105,7 @@ struct sccp_called_party_address {
routing_indicator : 1,
reserved : 1;
#elif OSMO_IS_BIG_ENDIAN
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t reserved:1, routing_indicator:1, global_title_indicator:4, ssn_indicator:1, point_code_indicator:1;
#endif
uint8_t data[0];
@ -121,7 +120,7 @@ struct sccp_signalling_point_code {
uint8_t msb : 6,
reserved : 2;
#elif OSMO_IS_BIG_ENDIAN
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t reserved:2, msb:6;
#endif
} __attribute__((packed));
@ -163,7 +162,7 @@ struct sccp_global_title {
uint8_t nature_of_addr_ind : 7,
odd_even : 1;
#elif OSMO_IS_BIG_ENDIAN
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t odd_even:1, nature_of_addr_ind:7;
#endif
uint8_t data[0];
@ -446,6 +445,9 @@ struct sccp_data_ext_unitdata {
data
#endif
/* optional */
uint8_t optional_start;
#if OPTIONAL
segmentation
importance
@ -475,9 +477,70 @@ struct sccp_data_ext_unitdata_service {
data
#endif
/* optional */
uint8_t optional_start;
#if OPTIONAL
segmentation
importancd
importance
#endif
uint8_t data[0];
} __attribute__((packed));
/* Long unitdata (LUDT), ITU-T Q.713 4.20 */
struct sccp_data_long_unitdata {
/* mandatory */
uint8_t type;
uint8_t proto_class;
uint8_t hop_counter;
/* variable */
uint16_t variable_called;
uint16_t variable_calling;
uint16_t variable_data;
/* optional */
uint16_t optional_start;
#if VARIABLE
called party address
calling party address
data
#endif
#if OPTIONAL
segmentation
importance
#endif
uint8_t data[0];
} __attribute__((packed));
/* Long unitdata service (LUDTS), ITU-T Q.713 4.21 */
struct sccp_data_long_unitdata_service {
/* mandantory */
uint8_t type;
uint8_t return_cause;
uint8_t hop_counter;
/* variable */
uint16_t variable_called;
uint16_t variable_calling;
uint16_t variable_data;
/* optional */
uint16_t optional_start;
#if VARIABLE
called party address
calling party address
data
#endif
#if OPTIONAL
segmentation
importance
#endif
uint8_t data[0];

View File

@ -3,5 +3,6 @@ sigtran_HEADERS = xua_types.h xua_msg.h m2ua_types.h sccp_sap.h \
sigtrandir = $(includedir)/osmocom/sigtran
sigtran_prot_HEADERS = protocol/sua.h protocol/m3ua.h protocol/mtp.h
sigtran_prot_HEADERS = protocol/sua.h protocol/m3ua.h protocol/mtp.h \
protocol/sccp_scmg.h
sigtran_protdir = $(includedir)/osmocom/sigtran/protocol

View File

@ -9,6 +9,7 @@
#include <osmocom/core/msgb.h>
#include <osmocom/core/prim.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/rate_ctr.h>
extern struct llist_head osmo_ss7_instances;
@ -24,6 +25,7 @@ int osmo_ss7_find_free_rctx(struct osmo_ss7_instance *inst);
bool osmo_ss7_pc_is_local(struct osmo_ss7_instance *inst, uint32_t pc);
int osmo_ss7_pointcode_parse(struct osmo_ss7_instance *inst, const char *str);
int osmo_ss7_pointcode_parse_mask_or_len(struct osmo_ss7_instance *inst, const char *in);
const char *osmo_ss7_pointcode_print_buf(char *buf, size_t buf_len, const struct osmo_ss7_instance *inst, uint32_t pc);
const char *osmo_ss7_pointcode_print(const struct osmo_ss7_instance *inst, uint32_t pc);
const char *osmo_ss7_pointcode_print2(const struct osmo_ss7_instance *inst, uint32_t pc);
@ -113,6 +115,7 @@ int osmo_ss7_instance_set_pc_fmt(struct osmo_ss7_instance *inst,
struct osmo_sccp_instance *osmo_ss7_ensure_sccp(struct osmo_ss7_instance *inst);
uint8_t osmo_ss7_pc_width(const struct osmo_ss7_pc_fmt *pc_fmt);
uint32_t osmo_ss7_pc_normalize(const struct osmo_ss7_pc_fmt *pc_fmt, uint32_t pc);
/***********************************************************************
* MTP Users (Users of MTP, such as SCCP or ISUP)
@ -308,6 +311,9 @@ struct osmo_ss7_as {
/*! Were we allocated by "simple client" support? */
bool simple_client_allocated;
/*! Rate Counter Group */
struct rate_ctr_group *ctrg;
struct {
char *name;
char *description;
@ -344,8 +350,9 @@ osmo_ss7_as_find_or_create(struct osmo_ss7_instance *inst, const char *name,
int osmo_ss7_as_add_asp(struct osmo_ss7_as *as, const char *asp_name);
int osmo_ss7_as_del_asp(struct osmo_ss7_as *as, const char *asp_name);
void osmo_ss7_as_destroy(struct osmo_ss7_as *as);
bool osmo_ss7_as_has_asp(struct osmo_ss7_as *as,
struct osmo_ss7_asp *asp);
bool osmo_ss7_as_has_asp(const struct osmo_ss7_as *as,
const struct osmo_ss7_asp *asp);
bool osmo_ss7_as_down(const struct osmo_ss7_as *as);
bool osmo_ss7_as_active(const struct osmo_ss7_as *as);
bool osmo_ss7_as_tmode_compatible_xua(struct osmo_ss7_as *as, uint32_t m3ua_tmt);
void osmo_ss7_asp_disconnect(struct osmo_ss7_asp *asp);
@ -359,6 +366,8 @@ struct osmo_ss7_asp_peer {
char *host[OSMO_SOCK_MAX_ADDRS];
size_t host_cnt;
uint16_t port;
/* index in "hosts" array marking the SCTP Primary Address, -1 if no explicit Primary Address set */
int idx_primary;
};
enum osmo_ss7_asp_admin_state {
@ -410,6 +419,9 @@ struct osmo_ss7_asp {
/*! Were we allocated by "simple client" support? */
bool simple_client_allocated;
/*! Rate Counter Group */
struct rate_ctr_group *ctrg;
/*! Pending message for non-blocking IPA read */
struct msgb *pending_msg;
@ -421,36 +433,82 @@ struct osmo_ss7_asp {
bool is_server;
enum osmo_ss7_asp_role role;
bool role_set_by_vty;
bool trans_role_set_by_vty;
struct osmo_ss7_asp_peer local;
struct osmo_ss7_asp_peer remote;
uint8_t qos_class;
uint32_t quirks;
/* T_defs used by the default_lm: */
struct osmo_tdef *T_defs_lm;
struct {
bool num_ostreams_present;
bool max_instreams_present;
bool max_attempts_present;
bool max_init_timeo_present;
uint16_t num_ostreams_value;
uint16_t max_instreams_value;
uint16_t max_attempts_value;
uint16_t max_init_timeo_value; /* ms */
} sctp_init;
/*! The underlaying transport protocol (one of IPPROTO_*) */
int trans_proto;
} cfg;
};
/*! Peer SG doesn't send NTFY(AS-INACTIVE) after ASP-UP procedure */
#define OSMO_SS7_ASP_QUIRK_NO_NOTIFY 0x00000001
/*! Accept DAUD in ASP role (RFC states only permitted in ASP->SG role) */
#define OSMO_SS7_ASP_QUIRK_DAUD_IN_ASP 0x00000002
/*! Accept SSNM even if ASP is in AS-INACTIVE state */
#define OSMO_SS7_ASP_QUIRK_SNM_INACTIVE 0x00000004
int osmo_ss7_asp_peer_snprintf(char* buf, size_t buf_len, struct osmo_ss7_asp_peer *peer);
void osmo_ss7_asp_peer_init(struct osmo_ss7_asp_peer *peer);
int osmo_ss7_asp_peer_set_hosts(struct osmo_ss7_asp_peer *peer, void *talloc_ctx,
const char* const* hosts, size_t host_cnt);
const char *const*hosts, size_t host_cnt);
int osmo_ss7_asp_peer_set_hosts2(struct osmo_ss7_asp_peer *peer, void *talloc_ctx,
const char *const*hosts, size_t host_cnt, int idx_primary);
int osmo_ss7_asp_peer_add_host(struct osmo_ss7_asp_peer *peer, void *talloc_ctx, const char *host);
int osmo_ss7_asp_peer_add_host2(struct osmo_ss7_asp_peer *peer, void *talloc_ctx, const char *host, bool is_primary_addr);
int osmo_ss7_asp_peer_del_host(struct osmo_ss7_asp_peer *peer, const char *host);
struct osmo_ss7_asp *
osmo_ss7_asp_find_by_name(struct osmo_ss7_instance *inst, const char *name);
struct osmo_ss7_asp
*osmo_ss7_asp_find_by_proto(struct osmo_ss7_as *as,
enum osmo_ss7_asp_protocol proto);
struct osmo_ss7_asp *
osmo_ss7_asp_find_by_proto(struct osmo_ss7_as *as,
enum osmo_ss7_asp_protocol proto);
struct osmo_ss7_asp *
osmo_ss7_asp_find(struct osmo_ss7_instance *inst, const char *name,
uint16_t remote_port, uint16_t local_port,
enum osmo_ss7_asp_protocol proto);
enum osmo_ss7_asp_protocol proto)
OSMO_DEPRECATED("Use osmo_ss7_asp_find2() instead");
struct osmo_ss7_asp *
osmo_ss7_asp_find2(struct osmo_ss7_instance *inst, const char *name,
uint16_t remote_port, uint16_t local_port,
int trans_proto, enum osmo_ss7_asp_protocol proto);
struct osmo_ss7_asp *
osmo_ss7_asp_find_or_create(struct osmo_ss7_instance *inst, const char *name,
uint16_t remote_port, uint16_t local_port,
enum osmo_ss7_asp_protocol proto);
enum osmo_ss7_asp_protocol proto)
OSMO_DEPRECATED("Use osmo_ss7_asp_find_or_create2() instead");
struct osmo_ss7_asp *
osmo_ss7_asp_find_or_create2(struct osmo_ss7_instance *inst, const char *name,
uint16_t remote_port, uint16_t local_port,
int trans_proto, enum osmo_ss7_asp_protocol proto);
void osmo_ss7_asp_destroy(struct osmo_ss7_asp *asp);
int osmo_ss7_asp_send(struct osmo_ss7_asp *asp, struct msgb *msg);
int osmo_ss7_asp_restart(struct osmo_ss7_asp *asp);
int osmo_ss7_asp_use_default_lm(struct osmo_ss7_asp *asp, int log_level);
bool osmo_ss7_asp_active(const struct osmo_ss7_asp *asp);
int osmo_ss7_asp_get_log_subsys(const struct osmo_ss7_asp *asp);
const char *osmo_ss7_asp_get_name(const struct osmo_ss7_asp *asp);
enum osmo_ss7_asp_protocol osmo_ss7_asp_get_proto(const struct osmo_ss7_asp *asp);
int osmo_ss7_asp_get_trans_proto(const struct osmo_ss7_asp *asp);
/*! Weak function to handle payload for unknown/unsupported PPID or IPA StreamID.
* This function can be overridden by application code to implement whatever handling
@ -493,16 +551,38 @@ struct osmo_xua_server {
bool accept_dyn_reg;
struct osmo_ss7_asp_peer local;
enum osmo_ss7_asp_protocol proto;
struct {
bool num_ostreams_present;
bool max_instreams_present;
uint16_t num_ostreams_value;
uint16_t max_instreams_value;
} sctp_init;
/*! The underlaying transport protocol (one of IPPROTO_*) */
int trans_proto;
} cfg;
};
struct osmo_xua_server *
osmo_ss7_xua_server_find(struct osmo_ss7_instance *inst, enum osmo_ss7_asp_protocol proto,
uint16_t local_port);
osmo_ss7_xua_server_find(struct osmo_ss7_instance *inst,
enum osmo_ss7_asp_protocol proto,
uint16_t local_port)
OSMO_DEPRECATED("Use osmo_ss7_xua_server_find2() instead");
struct osmo_xua_server *
osmo_ss7_xua_server_find2(struct osmo_ss7_instance *inst,
int trans_proto,
enum osmo_ss7_asp_protocol proto,
uint16_t local_port);
struct osmo_xua_server *
osmo_ss7_xua_server_create(struct osmo_ss7_instance *inst, enum osmo_ss7_asp_protocol proto,
uint16_t local_port, const char *local_host);
osmo_ss7_xua_server_create(struct osmo_ss7_instance *inst,
enum osmo_ss7_asp_protocol proto,
uint16_t local_port, const char *local_host)
OSMO_DEPRECATED("Use osmo_ss7_xua_server_create2() instead");
struct osmo_xua_server *
osmo_ss7_xua_server_create2(struct osmo_ss7_instance *inst,
int trans_proto, enum osmo_ss7_asp_protocol proto,
uint16_t local_port, const char *local_host);
int
osmo_ss7_xua_server_bind(struct osmo_xua_server *xs);
@ -512,6 +592,7 @@ osmo_ss7_xua_server_set_local_host(struct osmo_xua_server *xs, const char *local
int
osmo_ss7_xua_server_set_local_hosts(struct osmo_xua_server *xs, const char **local_hosts, size_t local_host_cnt);
int osmo_ss7_xua_server_add_local_host(struct osmo_xua_server *xs, const char *local_host);
int osmo_ss7_xua_server_del_local_host(struct osmo_xua_server *xs, const char *local_host);
void osmo_ss7_xua_server_destroy(struct osmo_xua_server *xs);
struct osmo_sccp_instance *
@ -546,6 +627,8 @@ osmo_sccp_simple_server_add_clnt(struct osmo_sccp_instance *inst,
int local_port, int remote_port,
const char *remote_ip);
void osmo_sccp_set_max_optional_data(struct osmo_sccp_instance *inst, int val);
enum osmo_ss7_as_traffic_mode osmo_ss7_tmode_from_xua(uint32_t in);
int osmo_ss7_tmode_to_xua(enum osmo_ss7_as_traffic_mode tmod);

View File

@ -22,3 +22,18 @@ enum mtp_si_ni00 {
};
extern const struct value_string mtp_si_vals[];
/* Chapter 15.17.5 of Q.705 */
enum mtp_unavail_cause {
MTP_UNAVAIL_C_UNKNOWN = 0x0,
MTP_UNAVAIL_C_UNEQUIP_REM_USER = 0x1,
MTP_UNAVAIL_C_INACC_REM_USER = 0x2,
/* reserved */
};
extern const struct value_string mtp_unavail_cause_vals[];
static inline const char *mtp_unavail_cause_str(enum mtp_unavail_cause cs) {
return get_value_string(mtp_unavail_cause_vals, cs);
}

View File

@ -0,0 +1,25 @@
#pragma once
/* SCCP Management as per Section 5.3 of ITU-T Q.713 */
enum sccp_scmg_msg_type {
SCCP_SCMG_MSGT_SSA = 0x01, /* Subsystem Allowed */
SCCP_SCMG_MSGT_SSP = 0x02, /* Subsystem Prohibited */
SCCP_SCMG_MSGT_SST = 0x03, /* Subsystem Status Test */
SCCP_SCMG_MSGT_SOR = 0x04, /* Subsystem Out-of-service Request */
SCCP_SCMG_MSGT_SOG = 0x05, /* Subsystem Out-of-service Grant */
SCCP_SCMG_MSGT_SSC = 0x06, /* Subsystem Congested */
};
struct sccp_scmg_msg {
uint8_t msg_type; /* enum sccp_scmg_msg_type */
uint8_t affected_ssn;
uint16_t affected_pc;
uint8_t smi;
/* one octet, only in case of SSC */
uint8_t ssc_congestion_lvl[0];
} __attribute__ ((packed));
extern const struct value_string sccp_scmg_msgt_names[];
static inline const char *sccp_scmg_msgt_name(enum sccp_scmg_msg_type msgt)
{ return get_value_string(sccp_scmg_msgt_names, msgt); }

View File

@ -44,6 +44,10 @@ int osmo_sccp_tx_disconn(struct osmo_sccp_user *scu, uint32_t conn_id,
const struct osmo_sccp_addr *resp_addr,
uint32_t cause);
int osmo_sccp_tx_disconn_data(struct osmo_sccp_user *scu, uint32_t conn_id,
const struct osmo_sccp_addr *resp_addr,
uint32_t cause, const uint8_t *data, size_t len);
int osmo_sccp_tx_conn_resp_msg(struct osmo_sccp_user *scu, uint32_t conn_id,
const struct osmo_sccp_addr *resp_addr,
struct msgb *msg);
@ -54,5 +58,20 @@ int osmo_sccp_tx_conn_resp(struct osmo_sccp_user *scu, uint32_t conn_id,
char *osmo_sccp_gt_dump(const struct osmo_sccp_gt *gt);
char *osmo_sccp_addr_dump(const struct osmo_sccp_addr *addr);
int osmo_sccp_inst_addr_to_str_buf(char *buf, size_t buf_len, const struct osmo_sccp_instance *sccp,
const struct osmo_sccp_addr *addr);
char *osmo_sccp_inst_addr_to_str_c(void *ctx, const struct osmo_sccp_instance *sccp,
const struct osmo_sccp_addr *addr);
int osmo_sccp_addr_to_str_buf(char *buf, size_t buf_len, const struct osmo_ss7_instance *ss7,
const struct osmo_sccp_addr *addr);
char *osmo_sccp_addr_to_str_c(void *ctx, const struct osmo_ss7_instance *ss7, const struct osmo_sccp_addr *addr);
int osmo_sccp_addr_to_id_buf(char *buf, size_t buf_len, const struct osmo_ss7_instance *ss7,
const struct osmo_sccp_addr *addr);
char *osmo_sccp_addr_to_id_c(void *ctx, const struct osmo_ss7_instance *ss7, const struct osmo_sccp_addr *addr);
bool osmo_sccp_conn_id_exists(const struct osmo_sccp_instance *inst, uint32_t id);
char *osmo_sccp_addr_name(const struct osmo_ss7_instance *ss7, const struct osmo_sccp_addr *addr);
char *osmo_sccp_inst_addr_name(const struct osmo_sccp_instance *sccp, const struct osmo_sccp_addr *addr);

View File

@ -51,6 +51,7 @@ enum osmo_scu_prim_type {
#define OSMO_SCCP_ADDR_T_SSN 0x0004 /* subsystem number */
#define OSMO_SCCP_ADDR_T_IPv4 0x0008
#define OSMO_SCCP_ADDR_T_IPv6 0x0010
#define OSMO_SCCP_ADDR_T_MASK 0x001f
/* Q.713 3.4.1 + RFC 3868 3.10.2.3 */
enum osmo_sccp_routing_ind {
@ -131,11 +132,42 @@ enum osmo_sccp_ssn {
OSMO_SCCP_SSN_PCAP = 249,
OSMO_SCCP_SSN_BSC_BSSAP_LE = 250,
OSMO_SCCP_SSN_MSC_BSSAP_LE = 251,
OSMO_SCCP_SSN_SMLC_BSSAP = 252,
OSMO_SCCP_SSN_SMLC_BSSAP_LE = 252,
OSMO_SCCP_SSN_BSS_OAM = 253,
OSMO_SCCP_SSN_BSSAP = 254,
};
/* Q.711 6.3.2.2.5 Signalling point status */
enum osmo_sccp_sp_status {
OSMO_SCCP_SP_S_INACCESSIBLE = 1,
OSMO_SCCP_SP_S_CONGESTED = 2,
OSMO_SCCP_SP_S_ACCESSIBLE = 3,
};
extern const struct value_string osmo_sccp_sp_status_names[];
static inline const char *osmo_sccp_sp_status_name(enum osmo_sccp_sp_status val)
{
return get_value_string(osmo_sccp_sp_status_names, val);
}
/* Q.711 6.3.2.2.6 Remote SCCP status */
enum osmo_sccp_rem_sccp_status {
OSMO_SCCP_REM_SCCP_S_AVAILABLE = 1,
OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN = 2,
OSMO_SCCP_REM_SCCP_S_UNEQUIPPED = 3,
OSMO_SCCP_REM_SCCP_S_INACCESSIBLE = 4,
OSMO_SCCP_REM_SCCP_S_CONGESTED = 5,
};
extern const struct value_string osmo_sccp_rem_sccp_status_names[];
static inline const char *osmo_sccp_rem_sccp_status_name(enum osmo_sccp_rem_sccp_status val)
{
return get_value_string(osmo_sccp_rem_sccp_status_names, val);
}
/* legacy shim for name change */
#define OSMO_SCCP_SSN_SMLC_BSSAP OSMO_SCCP_SSN_SMLC_BSSAP_LE
extern const struct value_string osmo_sccp_ssn_names[];
static inline const char *osmo_sccp_ssn_name(enum osmo_sccp_ssn val)
{ return get_value_string(osmo_sccp_ssn_names, val); }
@ -222,6 +254,22 @@ struct osmo_scu_notice_param {
/* user data */
};
/* OSMO_SCU_PRIM_N_STATE */
struct osmo_scu_state_param {
uint32_t affected_pc;
uint32_t affected_ssn;
bool user_in_service; /* true: UIS; false: UOS */
uint32_t ssn_multiplicity_ind;
};
/* OSMO_SCU_PRIM_N_PCSTATE */
struct osmo_scu_pcstate_param {
uint32_t affected_pc;
uint32_t restricted_importance_level;
enum osmo_sccp_sp_status sp_status;
enum osmo_sccp_rem_sccp_status remote_sccp_status;
};
struct osmo_scu_prim {
struct osmo_prim_hdr oph;
union {
@ -231,12 +279,22 @@ struct osmo_scu_prim {
struct osmo_scu_reset_param reset;
struct osmo_scu_unitdata_param unitdata;
struct osmo_scu_notice_param notice;
struct osmo_scu_state_param state;
struct osmo_scu_pcstate_param pcstate;
} u;
};
#define msgb_scu_prim(msg) ((struct osmo_scu_prim *)(msg)->l1h)
char *osmo_scu_prim_name(struct osmo_prim_hdr *oph);
extern const struct value_string osmo_scu_prim_type_names[];
static inline const char *osmo_scu_prim_type_name(enum osmo_scu_prim_type val)
{
return get_value_string(osmo_scu_prim_type_names, val);
}
int osmo_scu_prim_hdr_name_buf(char *buf, size_t buflen, const struct osmo_prim_hdr *oph);
char *osmo_scu_prim_hdr_name_c(void *ctx, const struct osmo_prim_hdr *oph);
char *osmo_scu_prim_name(const struct osmo_prim_hdr *oph);
struct osmo_ss7_instance;
struct osmo_sccp_instance;
@ -244,32 +302,30 @@ struct osmo_sccp_user;
void osmo_sccp_vty_init(void);
struct osmo_sccp_instance *
osmo_sccp_instance_create(struct osmo_ss7_instance *ss7, void *priv);
struct osmo_sccp_instance *osmo_sccp_instance_create(struct osmo_ss7_instance *ss7, void *priv);
void osmo_sccp_instance_destroy(struct osmo_sccp_instance *inst);
struct osmo_ss7_instance *osmo_sccp_get_ss7(const struct osmo_sccp_instance *sccp);
struct osmo_sccp_instance *osmo_sccp_get_sccp(const struct osmo_sccp_user *scu);
void osmo_sccp_set_priv(struct osmo_sccp_instance *sccp, void *priv);
void *osmo_sccp_get_priv(struct osmo_sccp_instance *sccp);
void osmo_sccp_user_unbind(struct osmo_sccp_user *scu);
void osmo_sccp_user_set_priv(struct osmo_sccp_user *scu, void *priv);
void *osmo_sccp_user_get_priv(struct osmo_sccp_user *scu);
struct osmo_sccp_user *
osmo_sccp_user_bind_pc(struct osmo_sccp_instance *inst, const char *name,
osmo_prim_cb prim_cb, uint16_t ssn, uint32_t pc);
struct osmo_sccp_user *osmo_sccp_user_bind_pc(struct osmo_sccp_instance *inst, const char *name,
osmo_prim_cb prim_cb, uint16_t ssn, uint32_t pc);
struct osmo_sccp_user *
osmo_sccp_user_bind(struct osmo_sccp_instance *inst, const char *name,
osmo_prim_cb prim_cb, uint16_t ssn);
struct osmo_sccp_user *
osmo_sccp_user_find(struct osmo_sccp_instance *inst, uint16_t ssn, uint32_t pc);
struct osmo_sccp_user *osmo_sccp_user_bind(struct osmo_sccp_instance *inst, const char *name,
osmo_prim_cb prim_cb, uint16_t ssn);
struct osmo_sccp_user *osmo_sccp_user_find(struct osmo_sccp_instance *inst, uint16_t ssn, uint32_t pc);
int osmo_sccp_user_sap_down(struct osmo_sccp_user *scu, struct osmo_prim_hdr *oph);
int osmo_sccp_user_sap_down_nofree(struct osmo_sccp_user *scu, struct osmo_prim_hdr *oph);
struct osmo_ss7_instance *
osmo_sccp_addr_by_name(struct osmo_sccp_addr *dest_addr,
const char *name);
struct osmo_ss7_instance *osmo_sccp_addr_by_name(struct osmo_sccp_addr *dest_addr, const char *name);
int osmo_sccp_addr_by_name_local(struct osmo_sccp_addr *dest_addr, const char *name,
const struct osmo_ss7_instance *inst);
const char *osmo_sccp_name_by_addr(const struct osmo_sccp_addr *addr);
@ -283,3 +339,5 @@ int osmo_sccp_addr_ri_cmp(const struct osmo_sccp_addr *a, const struct osmo_sccp
int osmo_sccp_gt_cmp(const struct osmo_sccp_gt *a, const struct osmo_sccp_gt *b);
const char *osmo_sccp_user_name(struct osmo_sccp_user *scu);
int osmo_sccp_instance_next_conn_id(struct osmo_sccp_instance *sccp);

View File

@ -74,7 +74,7 @@ void osmo_xua_msg_tall_ctx_init(void *ctx);
struct xua_msg *xua_msg_alloc(void);
void xua_msg_free(struct xua_msg *msg);
int xua_msg_add_data(struct xua_msg *msg, uint16_t tag, uint16_t len, uint8_t *dat);
int xua_msg_add_data(struct xua_msg *msg, uint16_t tag, uint16_t len, const uint8_t *dat);
struct xua_msg_part *xua_msg_find_tag(const struct xua_msg *msg, uint16_t tag);
int xua_msg_free_tag(struct xua_msg *xua, uint16_t tag);
@ -89,8 +89,12 @@ struct xua_msg *xua_from_nested(struct xua_msg_part *outer);
int msgb_t16l16vp_put(struct msgb *msg, uint16_t tag, uint16_t len, const uint8_t *data);
int msgb_t16l16vp_put_u32(struct msgb *msg, uint16_t tag, uint32_t val);
int xua_msg_add_u32(struct xua_msg *xua, uint16_t iei, uint32_t val);
uint32_t xua_msg_part_get_u32(struct xua_msg_part *part);
uint32_t xua_msg_get_u32(struct xua_msg *xua, uint16_t iei);
uint32_t xua_msg_part_get_u32(const struct xua_msg_part *part);
uint32_t xua_msg_get_u32(const struct xua_msg *xua, uint16_t iei);
const uint32_t *xua_msg_get_u32p(const struct xua_msg *xua, uint16_t iei, uint32_t *out);
const char *xua_msg_part_get_str(const struct xua_msg_part *part);
const char *xua_msg_get_str(const struct xua_msg *xua, uint16_t iei);
int xua_msg_get_len(const struct xua_msg *xua, uint16_t iei);
void xua_part_add_gt(struct msgb *msg, const struct osmo_sccp_gt *gt);
int xua_msg_add_sccp_addr(struct xua_msg *xua, uint16_t iei, const struct osmo_sccp_addr *addr);

View File

@ -1,7 +1,8 @@
#!/usr/bin/env python3
app_configs = {
"osmo-stp": ["doc/examples/osmo-stp.cfg"],
"osmo-stp": ["doc/examples/osmo-stp.cfg",
"doc/examples/osmo-stp-multihome.cfg"],
}
apps = [(4239, "stp/osmo-stp", "OsmoSTP", "osmo-stp"),

View File

@ -2,7 +2,13 @@ AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) \
$(LIBOSMONETIF_CFLAGS)
noinst_HEADERS = sccp_internal.h xua_asp_fsm.h xua_as_fsm.h xua_internal.h
noinst_HEADERS = \
sccp_internal.h \
ss7_internal.h \
xua_asp_fsm.h \
xua_as_fsm.h \
xua_internal.h \
$(NULL)
# Legacy static libs
@ -25,13 +31,44 @@ lib_LTLIBRARIES = libosmo-sigtran.la
# 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=5:0:0
LIBVERSION=9:0:0
libosmo_sigtran_la_SOURCES = \
ipa.c \
m3ua.c \
osmo_ss7.c \
osmo_ss7_as.c \
osmo_ss7_asp.c \
osmo_ss7_asp_peer.c \
osmo_ss7_hmrt.c \
osmo_ss7_vty.c \
osmo_ss7_xua_srv.c \
sccp2sua.c \
sccp_helpers.c \
sccp_lbcs.c \
sccp_sap.c \
sccp_sclc.c \
sccp_scmg.c \
sccp_scrc.c \
sccp_scoc.c \
sccp_types.c \
sccp_user.c \
sccp_vty.c \
sua.c \
xua_asp_fsm.c \
xua_as_fsm.c \
xua_default_lm_fsm.c \
xua_msg.c \
xua_rkm.c \
xua_shared.c \
xua_snm.c \
$(NULL)
libosmo_sigtran_la_SOURCES = sccp_sap.c sua.c m3ua.c xua_msg.c sccp_helpers.c \
sccp2sua.c sccp_scrc.c sccp_sclc.c sccp_scoc.c \
sccp_user.c sccp_types.c xua_rkm.c xua_default_lm_fsm.c \
osmo_ss7.c osmo_ss7_hmrt.c xua_asp_fsm.c xua_as_fsm.c \
osmo_ss7_vty.c sccp_vty.c ipa.c
libosmo_sigtran_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined -export-symbols-regex '^osmo_'
libosmo_sigtran_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) \
$(LIBOSMONETIF_LIBS) $(LIBSCTP_LIBS)
libosmo_sigtran_la_LIBADD = \
$(LIBOSMONETIF_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(LIBSCTP_LIBS) \
$(NULL)

View File

@ -47,6 +47,7 @@
#include <osmocom/sigtran/protocol/mtp.h>
#include "xua_internal.h"
#include "ss7_internal.h"
#include "xua_asp_fsm.h"
@ -130,11 +131,11 @@ static int ipa_rx_msg_ccm(struct osmo_ss7_asp *asp, struct msgb *msg)
return 0;
}
static struct osmo_ss7_as *find_as_for_asp(struct osmo_ss7_asp *asp)
struct osmo_ss7_as *ipa_find_as_for_asp(struct osmo_ss7_asp *asp)
{
struct osmo_ss7_as *as;
/* in the IPA case, weassume there is a 1:1 mapping between the
/* in the IPA case, we assume there is a 1:1 mapping between the
* ASP and the AS. An AS without ASP means there is no
* connection, and an ASP without AS means that we don't (yet?)
* know the identity of the peer */
@ -210,12 +211,12 @@ static struct msgb *patch_sccp_with_pc(struct osmo_ss7_asp *asp, struct msgb *sc
return sccp_msg_out;
}
static int ipa_rx_msg_sccp(struct osmo_ss7_asp *asp, struct msgb *msg)
static int ipa_rx_msg_sccp(struct osmo_ss7_asp *asp, struct msgb *msg, uint8_t sls)
{
int rc;
struct m3ua_data_hdr data_hdr;
struct xua_msg *xua;
struct osmo_ss7_as *as = find_as_for_asp(asp);
struct osmo_ss7_as *as = ipa_find_as_for_asp(asp);
uint32_t opc, dpc;
if (!as) {
@ -224,6 +225,8 @@ static int ipa_rx_msg_sccp(struct osmo_ss7_asp *asp, struct msgb *msg)
return -1;
}
rate_ctr_inc2(as->ctrg, SS7_AS_CTR_RX_MSU_TOTAL);
/* pull the IPA header */
msgb_pull_to_l2(msg);
@ -273,6 +276,8 @@ static int ipa_rx_msg_sccp(struct osmo_ss7_asp *asp, struct msgb *msg)
data_hdr.si = MTP_SI_SCCP;
data_hdr.opc = osmo_htonl(opc);
data_hdr.dpc = osmo_htonl(dpc);
data_hdr.sls = sls;
data_hdr.ni = as->inst->cfg.network_indicator;
/* Create M3UA message in XUA structure */
xua = m3ua_xfer_from_data(&data_hdr, msgb_l2(msg), msgb_l2len(msg));
msgb_free(msg);
@ -288,8 +293,9 @@ static int ipa_rx_msg_sccp(struct osmo_ss7_asp *asp, struct msgb *msg)
/*! \brief process M3UA message received from socket
* \param[in] asp Application Server Process receiving \a msg
* \param[in] msg received message buffer. Callee takes ownership!
* \param[in] sls The SLS (signaling link selector) field to use in the generated M3UA header
* \returns 0 on success; negative on error */
int ipa_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg)
int ipa_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg, uint8_t sls)
{
struct ipaccess_head *hh;
int rc;
@ -297,7 +303,7 @@ int ipa_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg)
OSMO_ASSERT(asp->cfg.proto == OSMO_SS7_ASP_PROT_IPA);
/* osmo_ipa_process_msg() will already have verified length
* consistency and set up l2h poiter */
* consistency and set up l2h pointer */
hh = (struct ipaccess_head *) msg->l1h;
switch (hh->proto) {
@ -305,7 +311,7 @@ int ipa_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg)
rc = ipa_rx_msg_ccm(asp, msg);
break;
case IPAC_PROTO_SCCP:
rc = ipa_rx_msg_sccp(asp, msg);
rc = ipa_rx_msg_sccp(asp, msg, sls);
break;
default:
rc = ss7_asp_rx_unknown(asp, hh->proto, msg);

View File

@ -44,6 +44,7 @@
#include "xua_as_fsm.h"
#include "xua_asp_fsm.h"
#include "xua_internal.h"
#include "ss7_internal.h"
#define M3UA_MSGB_SIZE 1500
@ -309,9 +310,6 @@ void m3ua_dh_to_xfer_param(struct osmo_mtp_transfer_param *param,
(mdh->ni & 0x3 << 6);
}
#define M3UA_MSG_SIZE 2048
#define M3UA_MSG_HEADROOM 512
struct msgb *m3ua_msgb_alloc(const char *name)
{
if (!name)
@ -448,7 +446,7 @@ int m3ua_decode_notify(struct osmo_xlm_prim_notify *npar, void *ctx,
}
/***********************************************************************
* Transmitting M3UA messsages to SCTP
* Transmitting M3UA messages to SCTP
***********************************************************************/
/* Convert M3UA from xua_msg to msgb and set PPID/stream */
@ -461,9 +459,11 @@ static struct msgb *m3ua_to_msg(struct xua_msg *xua)
return NULL;
}
if (xua->hdr.msg_class == M3UA_MSGC_XFER)
if (xua->hdr.msg_class == M3UA_MSGC_XFER) {
/* TODO: M3UA RFC says that multiple different streams within the SCTP association
* *may* be used, for example, by using the SLS value. Not required but makes sense. */
msgb_sctp_stream(msg) = 1;
else
} else
msgb_sctp_stream(msg) = 0;
msgb_sctp_ppid(msg) = M3UA_PPID;
@ -503,7 +503,7 @@ int m3ua_tx_xua_as(struct osmo_ss7_as *as, struct xua_msg *xua)
return -1;
/* send the msg to the AS for transmission. The AS FSM might
* (depending on its state) enqueue it before trnsmission */
* (depending on its state) enqueue it before transmission */
rc = osmo_fsm_inst_dispatch(as->fi, XUA_AS_E_TRANSFER_REQ, msg);
if (rc < 0)
msgb_free(msg);
@ -511,7 +511,7 @@ int m3ua_tx_xua_as(struct osmo_ss7_as *as, struct xua_msg *xua)
}
/***********************************************************************
* Receiving M3UA messsages from SCTP
* Receiving M3UA messages from SCTP
***********************************************************************/
/* obtain the destination point code from a M3UA message in XUA fmt * */
@ -532,28 +532,12 @@ struct m3ua_data_hdr *data_hdr_from_m3ua(struct xua_msg *xua)
return data_hdr;
}
/* if given ASP only has one AS, return that AS */
static struct osmo_ss7_as *find_single_as_for_asp(struct osmo_ss7_asp *asp)
{
struct osmo_ss7_as *as, *as_found = NULL;
llist_for_each_entry(as, &asp->inst->as_list, list) {
if (!osmo_ss7_as_has_asp(as, asp))
continue;
/* check if we already had found another AS within this ASP -> not unique */
if (as_found)
return NULL;
as_found = as;
}
return as_found;
}
static int m3ua_rx_xfer(struct osmo_ss7_asp *asp, struct xua_msg *xua)
{
struct xua_msg_part *rctx_ie = xua_msg_find_tag(xua, M3UA_IEI_ROUTE_CTX);
struct m3ua_data_hdr *dh;
struct osmo_ss7_as *as;
int rc;
LOGPASP(asp, DLM3UA, LOGL_DEBUG, "m3ua_rx_xfer\n");
@ -565,34 +549,11 @@ static int m3ua_rx_xfer(struct osmo_ss7_asp *asp, struct xua_msg *xua)
return M3UA_ERR_UNSUPP_MSG_TYPE;
}
if (rctx_ie) {
uint32_t rctx = xua_msg_part_get_u32(rctx_ie);
/* Use routing context IE to look up the AS for which the
* message was received. */
as = osmo_ss7_as_find_by_rctx(asp->inst, rctx);
if (!as) {
LOGPASP(asp, DLM3UA, LOGL_ERROR, "%s(): invalid routing context: %u\n",
__func__, rctx);
return M3UA_ERR_INVAL_ROUT_CTX;
}
rc = xua_find_as_for_asp(&as, asp, rctx_ie);
if (rc)
return rc;
/* Verify that this ASP is part of the AS. */
if (!osmo_ss7_as_has_asp(as, asp)) {
LOGPASP(asp, DLM3UA, LOGL_ERROR,
"%s(): This Application Server Process is not part of the AS %s "
"resolved by routing context %u\n", __func__, (as)->cfg.name, rctx);
return M3UA_ERR_NO_CONFGD_AS_FOR_ASP;
}
} else {
/* no explicit routing context; this only works if there is only one AS in the ASP */
as = find_single_as_for_asp(asp);
if (!as) {
LOGPASP(asp, DLM3UA, LOGL_ERROR,
"%s(): ASP sent M3UA without Routing Context IE but unable to uniquely "
"identify the AS for this message\n", __func__);
return M3UA_ERR_INVAL_ROUT_CTX;
}
}
rate_ctr_inc2(as->ctrg, SS7_AS_CTR_RX_MSU_TOTAL);
/* FIXME: check for AS state == ACTIVE */
@ -718,6 +679,8 @@ static int m3ua_rx_asp(struct osmo_ss7_asp *asp, struct xua_msg *xua)
return 0;
}
static int m3ua_rx_snm(struct osmo_ss7_asp *asp, struct xua_msg *xua);
/*! \brief process M3UA message received from socket
* \param[in] asp Application Server Process receiving \ref msg
* \param[in] msg received message buffer
@ -730,7 +693,7 @@ int m3ua_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg)
OSMO_ASSERT(asp->cfg.proto == OSMO_SS7_ASP_PROT_M3UA);
/* caller owns msg memory, we shall neither free it here nor
* keep references beyond the executin of this function and its
* keep references beyond the execution of this function and its
* callees */
xua = xua_from_msg(M3UA_VERSION, msgb_length(msg), msgb_data(msg));
@ -778,10 +741,7 @@ int m3ua_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg)
rc = m3ua_rx_rkm(asp, xua);
break;
case M3UA_MSGC_SNM:
/* FIXME */
LOGPASP(asp, DLM3UA, LOGL_NOTICE, "Received unsupported M3UA "
"Message Class %u\n", xua->hdr.msg_class);
err = m3ua_gen_error_msg(M3UA_ERR_UNSUPP_MSG_CLASS, msg);
rc = m3ua_rx_snm(asp, xua);
break;
default:
LOGPASP(asp, DLM3UA, LOGL_NOTICE, "Received unknown M3UA "
@ -794,10 +754,249 @@ int m3ua_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg)
err = m3ua_gen_error_msg(rc, msg);
out:
if (err)
if (err) {
m3ua_tx_xua_asp(asp, err);
xua_msg_free(err);
}
xua_msg_free(xua);
return rc;
}
/***********************************************************************
* SSNM msg generation
***********************************************************************/
/* 3.4.1 Destination Unavailable (DUNA) */
static struct xua_msg *m3ua_encode_duna(const uint32_t *rctx, unsigned int num_rctx,
const uint32_t *aff_pc, unsigned int num_aff_pc,
const char *info_string)
{
struct xua_msg *xua = xua_msg_alloc();
xua->hdr = XUA_HDR(M3UA_MSGC_SNM, M3UA_SNM_DUNA);
xua->hdr.version = M3UA_VERSION;
if (rctx && num_rctx)
xua_msg_add_data(xua, M3UA_IEI_ROUTE_CTX, num_rctx * sizeof(*rctx), (const uint8_t *)rctx);
xua_msg_add_data(xua, M3UA_IEI_AFFECTED_PC, num_aff_pc * sizeof(*aff_pc), (const uint8_t *) aff_pc);
if (info_string) {
xua_msg_add_data(xua, M3UA_IEI_INFO_STRING,
strlen(info_string)+1,
(const uint8_t *) info_string);
}
return xua;
}
/* 3.4.2 Destination Available (DAVA) */
static struct xua_msg *m3ua_encode_dava(const uint32_t *rctx, unsigned int num_rctx,
const uint32_t *aff_pc, unsigned int num_aff_pc,
const char *info_string)
{
/* encoding is exactly identical to DUNA */
struct xua_msg *xua = m3ua_encode_duna(rctx, num_rctx, aff_pc, num_aff_pc, info_string);
if (xua)
xua->hdr.msg_type = M3UA_SNM_DAVA;
return xua;
}
#if 0 /* not used so far */
/* 3.4.3 Destination Available (DAUD) */
static struct xua_msg *m3ua_encode_daud(const uint32_t *rctx, unsigned int num_rctx,
const uint32_t *aff_pc, unsigned int num_aff_pc,
const char *info_string)
{
/* encoding is exactly identical to DUNA */
struct xua_msg *xua = m3ua_encode_duna(rctx, num_rctx, aff_pc, num_aff_pc, info_string);
if (xua)
xua->hdr.msg_type = M3UA_SNM_DAUD;
return xua;
}
#endif
/* 3.4.5 Destination User Part Unavailable (DUPU) */
static struct xua_msg *m3ua_encode_dupu(const uint32_t *rctx, unsigned int num_rctx,
uint32_t dpc, uint16_t user, uint16_t cause,
const char *info_string)
{
struct xua_msg *xua = xua_msg_alloc();
uint32_t user_cause = (cause << 16) | user;
xua->hdr = XUA_HDR(M3UA_MSGC_SNM, M3UA_SNM_DUPU);
xua->hdr.version = M3UA_VERSION;
if (rctx && num_rctx)
xua_msg_add_data(xua, M3UA_IEI_ROUTE_CTX, num_rctx * sizeof(*rctx), (const uint8_t *)rctx);
xua_msg_add_u32(xua, M3UA_IEI_AFFECTED_PC, dpc);
xua_msg_add_u32(xua, M3UA_IEI_USER_CAUSE, user_cause);
if (info_string) {
xua_msg_add_data(xua, M3UA_IEI_INFO_STRING,
strlen(info_string)+1,
(const uint8_t *) info_string);
}
return xua;
}
/*! Transmit SSNM DUNA/DAVA message indicating [un]availability of certain point code[s]
* \param[in] asp ASP through which to transmit message. Must be ACTIVE.
* \param[in] rctx array of Routing Contexts in network byte order.
* \param[in] num_rctx number of rctx
* \param[in] aff_pc array of 'Affected Point Code' in network byte order.
* \param[in] num_aff_pc number of aff_pc
* \param[in] info_string optional information string (can be NULL).
* \param[in] available are aff_pc now available (true) or unavailable (false) */
void m3ua_tx_snm_available(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
const uint32_t *aff_pc, unsigned int num_aff_pc,
const char *info_string, bool available)
{
struct xua_msg *xua;
if (available)
xua = m3ua_encode_dava(rctx, num_rctx, aff_pc, num_aff_pc, info_string);
else
xua = m3ua_encode_duna(rctx, num_rctx, aff_pc, num_aff_pc, info_string);
m3ua_tx_xua_asp(asp, xua);
xua_msg_free(xua);
}
/*! Transmit SSNM SCON message indicating congestion
* \param[in] asp ASP through which to transmit message. Must be ACTIVE.
* \param[in] rctx array of Routing Contexts in network byte order.
* \param[in] num_rctx number of rctx
* \param[in] aff_pc array of 'Affected Point Code' in network byte order.
* \param[in] num_aff_pc number of aff_pc
* \param[in] concerned_dpc optional concerned DPC (can be NULL)
* \param[in] cong_level optional congestion level (can be NULL)
* \param[in] info_string optional information string (can be NULL). */
void m3ua_tx_snm_congestion(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
const uint32_t *aff_pc, unsigned int num_aff_pc,
const uint32_t *concerned_dpc, const uint8_t *cong_level,
const char *info_string)
{
struct xua_msg *xua = xua_msg_alloc();
xua->hdr = XUA_HDR(M3UA_MSGC_SNM, M3UA_SNM_SCON);
xua->hdr.version = M3UA_VERSION;
if (rctx && num_rctx)
xua_msg_add_data(xua, M3UA_IEI_ROUTE_CTX, num_rctx * sizeof(*rctx), (const uint8_t *)rctx);
xua_msg_add_data(xua, M3UA_IEI_AFFECTED_PC, num_aff_pc * sizeof(*aff_pc), (const uint8_t *) aff_pc);
if (concerned_dpc)
xua_msg_add_u32(xua, M3UA_IEI_CONC_DEST, *concerned_dpc & 0xffffff);
if (cong_level)
xua_msg_add_u32(xua, M3UA_IEI_CONG_IND, *cong_level & 0xff);
if (info_string)
xua_msg_add_data(xua, M3UA_IEI_INFO_STRING, strlen(info_string)+1, (const uint8_t *) info_string);
m3ua_tx_xua_asp(asp, xua);
xua_msg_free(xua);
}
/*! Transmit SSNM DUPU message indicating user unavailability.
* \param[in] asp ASP through which to transmit message. Must be ACTIVE.
* \param[in] rctx array of Routing Contexts in network byte order.
* \param[in] num_rctx number of rctx
* \param[in] dpc affected point code
* \param[in] user the user (SI) that is unavailable
* \param[in] cause the cause of the user unavailability
* \param[in] info_string optional information string (can be NULL). */
void m3ua_tx_dupu(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
uint32_t dpc, uint16_t user, uint16_t cause, const char *info_str)
{
struct xua_msg *xua = m3ua_encode_dupu(rctx, num_rctx, dpc, user, cause, info_str);
m3ua_tx_xua_asp(asp, xua);
xua_msg_free(xua);
}
/* received SNM message on ASP side */
static int m3ua_rx_snm_asp(struct osmo_ss7_asp *asp, struct xua_msg *xua)
{
struct osmo_ss7_as *as = NULL;
struct xua_msg_part *rctx_ie = xua_msg_find_tag(xua, M3UA_IEI_ROUTE_CTX);
int rc;
rc = xua_find_as_for_asp(&as, asp, rctx_ie);
if (rc)
return rc;
/* report those up the stack so both other ASPs and local SCCP users can be notified */
switch (xua->hdr.msg_type) {
case M3UA_SNM_DUNA:
xua_snm_rx_duna(asp, as, xua);
break;
case M3UA_SNM_DAVA:
xua_snm_rx_dava(asp, as, xua);
break;
case M3UA_SNM_DUPU:
xua_snm_rx_dupu(asp, as, xua);
break;
case M3UA_SNM_SCON:
xua_snm_rx_scon(asp, as, xua);
break;
case M3UA_SNM_DRST:
LOGPASP(asp, DLM3UA, LOGL_NOTICE, "Received unsupported M3UA SNM message type %u\n",
xua->hdr.msg_type);
/* silently ignore those to not confuse the sender */
break;
case M3UA_SNM_DAUD:
/* RFC states only permitted in ASP->SG direction, not reverse. But some
* equipment still sends it to us as ASP ?!? */
if (asp->cfg.quirks & OSMO_SS7_ASP_QUIRK_DAUD_IN_ASP) {
LOGPASP(asp, DLM3UA, LOGL_NOTICE, "quirk daud_in_asp active: Accepting DAUD "
"despite being in ASP role\n");
xua_snm_rx_daud(asp, xua);
} else {
LOGPASP(asp, DLM3UA, LOGL_ERROR, "DAUD not permitted in ASP role\n");
return M3UA_ERR_UNSUPP_MSG_TYPE;
}
break;
default:
return M3UA_ERR_UNSUPP_MSG_TYPE;
}
return 0;
}
/* received SNM message on SG side */
static int m3ua_rx_snm_sg(struct osmo_ss7_asp *asp, struct xua_msg *xua)
{
switch (xua->hdr.msg_type) {
case M3UA_SNM_DAUD: /* Audit: ASP inquires about availability of Point Codes */
xua_snm_rx_daud(asp, xua);
break;
default:
return M3UA_ERR_UNSUPP_MSG_TYPE;
}
return 0;
}
static int m3ua_rx_snm(struct osmo_ss7_asp *asp, struct xua_msg *xua)
{
/* SNM only permitted in ACTIVE state */
if (asp->fi->state != XUA_ASP_S_ACTIVE) {
if (asp->fi->state == XUA_ASP_S_INACTIVE &&
asp->cfg.quirks & OSMO_SS7_ASP_QUIRK_SNM_INACTIVE) {
LOGPASP(asp, DLM3UA, LOGL_NOTICE, "quirk snm_inactive active: "
"Accepting SNM in state %s\n", osmo_fsm_inst_state_name(asp->fi));
} else {
LOGPASP(asp, DLM3UA, LOGL_ERROR, "Rx M3UA SNM not permitted "
"while ASP in state %s\n", osmo_fsm_inst_state_name(asp->fi));
return M3UA_ERR_UNEXPECTED_MSG;
}
}
switch (asp->cfg.role) {
case OSMO_SS7_ASP_ROLE_SG:
return m3ua_rx_snm_sg(asp, xua);
case OSMO_SS7_ASP_ROLE_ASP:
return m3ua_rx_snm_asp(asp, xua);
default:
return M3UA_ERR_UNSUPP_MSG_CLASS;
}
}

View File

@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <osmocom/mtp/mtp_pcap.h>
@ -28,8 +24,6 @@
#include <unistd.h>
#define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
/*
* pcap writing of the misdn load
* pcap format is from http://wiki.wireshark.org/Development/LibpcapFileFormat

File diff suppressed because it is too large Load Diff

213
src/osmo_ss7_as.c Normal file
View File

@ -0,0 +1,213 @@
/* Core SS7 AS Handling */
/* (C) 2015-2017 by Harald Welte <laforge@gnumonks.org>
* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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 <string.h>
#include <errno.h>
#include <unistd.h>
#include <inttypes.h>
#include <osmocom/sigtran/osmo_ss7.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/logging.h>
#include "ss7_internal.h"
#include "xua_as_fsm.h"
#include "xua_asp_fsm.h"
/***********************************************************************
* SS7 Application Server
***********************************************************************/
struct value_string osmo_ss7_as_traffic_mode_vals[] = {
{ OSMO_SS7_AS_TMOD_BCAST, "broadcast" },
{ OSMO_SS7_AS_TMOD_LOADSHARE, "loadshare" },
{ OSMO_SS7_AS_TMOD_ROUNDROBIN, "round-robin" },
{ OSMO_SS7_AS_TMOD_OVERRIDE, "override" },
{ 0, NULL }
};
static const struct rate_ctr_desc ss7_as_rcd[] = {
[SS7_AS_CTR_RX_MSU_TOTAL] = { "rx:msu:total", "Total number of MSU received" },
[SS7_AS_CTR_TX_MSU_TOTAL] = { "tx:msu:total", "Total number of MSU transmitted" },
};
static const struct rate_ctr_group_desc ss7_as_rcgd = {
.group_name_prefix = "sigtran_as",
.group_description = "SIGTRAN Application Server",
.num_ctr = ARRAY_SIZE(ss7_as_rcd),
.ctr_desc = ss7_as_rcd,
};
static unsigned int g_ss7_as_rcg_idx;
/*! \brief Allocate an Application Server
* \param[in] inst SS7 Instance on which we operate
* \param[in] name Name of Application Server
* \param[in] proto Protocol of Application Server
* \returns pointer to Application Server on success; NULL otherwise */
struct osmo_ss7_as *ss7_as_alloc(struct osmo_ss7_instance *inst, const char *name,
enum osmo_ss7_asp_protocol proto)
{
struct osmo_ss7_as *as;
as = talloc_zero(inst, struct osmo_ss7_as);
if (!as)
return NULL;
as->ctrg = rate_ctr_group_alloc(as, &ss7_as_rcgd, g_ss7_as_rcg_idx++);
if (!as->ctrg) {
talloc_free(as);
return NULL;
}
rate_ctr_group_set_name(as->ctrg, name);
as->inst = inst;
as->cfg.name = talloc_strdup(as, name);
as->cfg.proto = proto;
as->cfg.mode = OSMO_SS7_AS_TMOD_OVERRIDE;
as->cfg.recovery_timeout_msec = 2000;
as->cfg.routing_key.l_rk_id = ss7_find_free_l_rk_id(inst);
as->fi = xua_as_fsm_start(as, LOGL_DEBUG);
llist_add_tail(&as->list, &inst->as_list);
return as;
}
/*! \brief Add given ASP to given AS
* \param[in] as Application Server to which \ref asp is added
* \param[in] asp Application Server Process to be added to \ref as
* \returns 0 on success; negative in case of error */
int osmo_ss7_as_add_asp(struct osmo_ss7_as *as, const char *asp_name)
{
struct osmo_ss7_asp *asp;
unsigned int i;
OSMO_ASSERT(ss7_initialized);
asp = osmo_ss7_asp_find_by_name(as->inst, asp_name);
if (!asp)
return -ENODEV;
LOGPAS(as, DLSS7, LOGL_INFO, "Adding ASP %s to AS\n", asp->cfg.name);
if (osmo_ss7_as_has_asp(as, asp))
return 0;
for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
if (!as->cfg.asps[i]) {
as->cfg.asps[i] = asp;
osmo_fsm_inst_dispatch(asp->fi, XUA_ASP_E_AS_ASSIGNED, as);
return 0;
}
}
return -ENOSPC;
}
/*! \brief Delete given ASP from given AS
* \param[in] as Application Server from which \ref asp is deleted
* \param[in] asp Application Server Process to delete from \ref as
* \returns 0 on success; negative in case of error */
int osmo_ss7_as_del_asp(struct osmo_ss7_as *as, const char *asp_name)
{
struct osmo_ss7_asp *asp;
unsigned int i;
OSMO_ASSERT(ss7_initialized);
asp = osmo_ss7_asp_find_by_name(as->inst, asp_name);
if (!asp)
return -ENODEV;
LOGPAS(as, DLSS7, LOGL_INFO, "Removing ASP %s from AS\n", asp->cfg.name);
for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
if (as->cfg.asps[i] == asp) {
as->cfg.asps[i] = NULL;
return 0;
}
}
return -EINVAL;
}
/*! \brief Destroy given Application Server
* \param[in] as Application Server to destroy */
void osmo_ss7_as_destroy(struct osmo_ss7_as *as)
{
struct osmo_ss7_route *rt, *rt2;
OSMO_ASSERT(ss7_initialized);
LOGPAS(as, DLSS7, LOGL_INFO, "Destroying AS\n");
if (as->fi)
osmo_fsm_inst_term(as->fi, OSMO_FSM_TERM_REQUEST, NULL);
/* find any routes pointing to this AS and remove them */
llist_for_each_entry_safe(rt, rt2, &as->inst->rtable_system->routes, list) {
if (rt->dest.as == as)
osmo_ss7_route_destroy(rt);
}
as->inst = NULL;
llist_del(&as->list);
rate_ctr_group_free(as->ctrg);
talloc_free(as);
}
/*! \brief Determine if given AS contains ASP
* \param[in] as Application Server in which to look for \ref asp
* \param[in] asp Application Server Process to look for in \ref as
* \returns true in case \ref asp is part of \ref as; false otherwise */
bool osmo_ss7_as_has_asp(const struct osmo_ss7_as *as,
const struct osmo_ss7_asp *asp)
{
unsigned int i;
OSMO_ASSERT(ss7_initialized);
for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
if (as->cfg.asps[i] == asp)
return true;
}
return false;
}
/*! Determine if given AS is in the active state.
* \param[in] as Application Server.
* \returns true in case as is active; false otherwise. */
bool osmo_ss7_as_active(const struct osmo_ss7_as *as)
{
if (!as->fi)
return false;
return as->fi->state == XUA_AS_S_ACTIVE;
}
/*! Determine if given AS is in the down state.
* \param[in] as Application Server.
* \returns true in case as is down; false otherwise. */
bool osmo_ss7_as_down(const struct osmo_ss7_as *as)
{
OSMO_ASSERT(as);
if (!as->fi)
return true;
return as->fi->state == XUA_AS_S_DOWN;
}

1246
src/osmo_ss7_asp.c Normal file

File diff suppressed because it is too large Load Diff

294
src/osmo_ss7_asp_peer.c Normal file
View File

@ -0,0 +1,294 @@
/* SS7 ASP Peer (one endpoint of a conn) */
/* (C) 2015-2017 by Harald Welte <laforge@gnumonks.org>
* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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 <string.h>
#include <errno.h>
#include <unistd.h>
#include <inttypes.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/sigtran/osmo_ss7.h>
#include "ss7_internal.h"
/***********************************************************************
* SS7 Application Server Process peer
***********************************************************************/
void osmo_ss7_asp_peer_init(struct osmo_ss7_asp_peer *peer)
{
memset(peer, 0, sizeof(*peer));
peer->idx_primary = -1;
}
int osmo_ss7_asp_peer_snprintf(char *buf, size_t buf_len, struct osmo_ss7_asp_peer *peer)
{
int len = 0, offset = 0, rem = buf_len;
int ret, i;
char *after;
char *primary;
if (buf_len < 3)
return -EINVAL;
if (peer->host_cnt > 1) {
ret = snprintf(buf, rem, "(");
if (ret < 0)
return ret;
OSMO_SNPRINTF_RET(ret, rem, offset, len);
}
for (i = 0; i < peer->host_cnt; i++) {
primary = (peer->idx_primary == i) ? "*" : "";
if (peer->host_cnt == 1)
after = "";
else
after = (i == (peer->host_cnt - 1)) ? ")" : "|";
ret = snprintf(buf + offset, rem, "%s%s%s", peer->host[i] ? : "0.0.0.0", primary, after);
OSMO_SNPRINTF_RET(ret, rem, offset, len);
}
ret = snprintf(buf + offset, rem, ":%u", peer->port);
if (ret < 0)
return ret;
OSMO_SNPRINTF_RET(ret, rem, offset, len);
return len;
}
/*! \brief Set (copy) addresses for a given ASP peer. Previous addresses are freed.
* \param[in] peer Application Server Process peer whose addresses are to be set.
* \param[in] talloc_ctx talloc context used to allocate new addresses.
* \param[in] hosts Array of strings containing IP addresses.
* \param[in] host_cnt Number of strings in hosts
* \param[in] idx_primary Index in "hosts" array marking the SCTP Primary Address, -1 if no explicit Primary Address set
* \returns 0 on success; negative otherwise */
int osmo_ss7_asp_peer_set_hosts2(struct osmo_ss7_asp_peer *peer, void *talloc_ctx, const char *const*hosts, size_t host_cnt, int idx_primary)
{
int i = 0;
if (idx_primary >= (int)host_cnt || idx_primary < -1)
return -EINVAL;
if (host_cnt > ARRAY_SIZE(peer->host))
return -EINVAL;
for (; i < host_cnt; i++)
osmo_talloc_replace_string(talloc_ctx, &peer->host[i], hosts[i]);
for (; i < peer->host_cnt; i++) {
talloc_free(peer->host[i]);
peer->host[i] = NULL;
}
peer->host_cnt = host_cnt;
peer->idx_primary = idx_primary;
return 0;
}
/*! \brief Set (copy) addresses for a given ASP peer. Previous addresses are freed.
* \param[in] peer Application Server Process peer whose addresses are to be set.
* \param[in] talloc_ctx talloc context used to allocate new addresses.
* \param[in] hosts Array of strings containing IP addresses.
* \param[in] host_cnt Number of strings in hosts
* \returns 0 on success; negative otherwise */
int osmo_ss7_asp_peer_set_hosts(struct osmo_ss7_asp_peer *peer, void *talloc_ctx, const char *const*hosts, size_t host_cnt)
{
return osmo_ss7_asp_peer_set_hosts2(peer, talloc_ctx, hosts, host_cnt, -1);
}
/* Is string formatted IPv4/v6 addr considered IN(6)ADDR_ANY? */
static inline bool host_is_ip_anyaddr(const char *host, bool is_v6)
{
/* NULL addr is resolved as 0.0.0.0 (IPv4) by getaddrinfo(), most
* probably for backward-compatibility reasons.
*/
return is_v6 ? (host && !strcmp(host, "::"))
: (!host || !strcmp(host, "0.0.0.0"));
}
/*! \brief Append (copy) address to a given ASP peer. Previous addresses are kept.
* \param[in] peer Application Server Process peer the address is appended to.
* \param[in] talloc_ctx talloc context used to allocate new address.
* \param[in] host string containing an IP address.
* \param[in] is_primary_addr whether this IP address is to be added as SCTP Primary Address
* \returns 0 on success; negative otherwise */
int osmo_ss7_asp_peer_add_host2(struct osmo_ss7_asp_peer *peer, void *talloc_ctx,
const char *host, bool is_primary_addr)
{
int i;
bool new_is_v6 = osmo_ip_str_type(host) == AF_INET6;
bool new_is_any = host_is_ip_anyaddr(host, new_is_v6);
struct osmo_sockaddr_str addr_str;
if (osmo_sockaddr_str_from_str(&addr_str, host, 0) < 0)
return -EINVAL;
if (new_is_any) {
/* Makes no sense to have INET(6)_ANY many times, or INET(6)_ANY
* together with specific addresses, be it of same or different
* IP version: */
if (peer->host_cnt != 0)
return -EINVAL;
/* Makes no sense to have INET(6)_ANY as primary: */
if (is_primary_addr)
return -EINVAL;
if (peer->host_cnt >= ARRAY_SIZE(peer->host))
return -EINVAL;
osmo_talloc_replace_string(talloc_ctx, &peer->host[peer->host_cnt], host);
peer->host_cnt++;
return 0;
}
/* Makes no sense to add specific address to set if INET(6)_ANY
* is already set, be it from same or different IP version: */
for (i = 0; i < peer->host_cnt; i++) {
bool iter_is_v6 = osmo_ip_str_type(peer->host[i]) == AF_INET6;
if (host_is_ip_anyaddr(peer->host[i], iter_is_v6))
return -EINVAL;
}
/* Reached this point, no INET(6)_ANY address is set nor we are adding an INET(6)_ANY address. */
/* Check if address already exists, and then if primary flags need to be changed: */
for (i = 0; i < peer->host_cnt; i++) {
struct osmo_sockaddr_str it_addr_str;
bool it_is_primary;
osmo_sockaddr_str_from_str(&it_addr_str, peer->host[i], 0);
if (osmo_sockaddr_str_cmp(&addr_str, &it_addr_str) != 0)
continue;
it_is_primary = (peer->idx_primary == i);
if (is_primary_addr == it_is_primary) {
/* Nothing to do, return below */
} else if (is_primary_addr && !it_is_primary) {
/* Mark it as primary: */
peer->idx_primary = i;
} else { /* if (!is_primary_addr && it_is_primary) { */
/* mark it as non-primary: */
peer->idx_primary = -1;
}
return 0;
}
if (peer->host_cnt >= ARRAY_SIZE(peer->host))
return -EINVAL;
osmo_talloc_replace_string(talloc_ctx, &peer->host[peer->host_cnt], host);
if (is_primary_addr)
peer->idx_primary = peer->host_cnt;
peer->host_cnt++;
return 0;
}
/*! \brief Remove address from a given ASP peer.
* \param[in] peer Application Server Process peer the address is removed from.
* \param[in] host string containing an IP address.
* \returns 0 on success; negative otherwise */
int osmo_ss7_asp_peer_del_host(struct osmo_ss7_asp_peer *peer, const char *host)
{
int i;
struct osmo_sockaddr_str addr_str;
bool found = false;
if (osmo_sockaddr_str_from_str(&addr_str, host, 0) < 0)
return -EINVAL;
for (i = 0; i < peer->host_cnt; i++) {
if (strcmp(host, peer->host[i]) == 0) {
found = true;
break;
}
}
if (!found)
return -ENOENT;
/* If current primary points to addr being removed, unset it: */
if (peer->idx_primary == i)
peer->idx_primary = -1;
/* If it's after it, update it together with sliding done further below: */
else if (peer->idx_primary > i)
peer->idx_primary--;
/* Free addr to remove: */
TALLOC_FREE(peer->host[i]);
/* Move the rest of the array: */
for (; i < peer->host_cnt - 1; i++)
peer->host[i] = peer->host[i + 1];
peer->host[i] = NULL;
/* Update array size: */
peer->host_cnt--;
return 0;
}
/*! \brief Append (copy) address to a given ASP peer. Previous addresses are kept.
* \param[in] peer Application Server Process peer the address is appended to.
* \param[in] talloc_ctx talloc context used to allocate new address.
* \param[in] host string containing an IP address.
* \returns 0 on success; negative otherwise */
int osmo_ss7_asp_peer_add_host(struct osmo_ss7_asp_peer *peer, void *talloc_ctx,
const char *host)
{
return osmo_ss7_asp_peer_add_host2(peer, talloc_ctx, host, false);
}
bool ss7_asp_peer_match_host(const struct osmo_ss7_asp_peer *peer, const char *host, bool host_is_v6)
{
unsigned int i;
for (i = 0; i < peer->host_cnt; i++) {
bool iter_is_v6 = osmo_ip_str_type(peer->host[i]) == AF_INET6;
bool iter_is_anyaddr = host_is_ip_anyaddr(peer->host[i], iter_is_v6);
/* "::" (v6) covers "0.0.0.0" (v4), but not otherwise */
if ((iter_is_v6 != host_is_v6) && !(iter_is_v6 && iter_is_anyaddr))
continue;
if (iter_is_anyaddr || !strcmp(peer->host[i], host))
return true;
}
return false;
}
/*! \brief Find the exact IP address match and return its index in the array
* \param[in] peer Application Server Process peer where the address is looked up.
* \param[in] host string containing an IP address.
* \returns >=0 on success containing the index of the host; negative otherwise */
int ss7_asp_peer_find_host(const struct osmo_ss7_asp_peer *peer, const char *host)
{
unsigned int i;
for (i = 0; i < peer->host_cnt; i++) {
if (strcmp(host, peer->host[i]) == 0)
return i;
}
return -1;
}

View File

@ -35,6 +35,7 @@
#include <osmocom/sigtran/protocol/m3ua.h>
#include "xua_internal.h"
#include "ss7_internal.h"
/* convert from M3UA message to MTP-TRANSFER.ind osmo_mtp_prim */
struct osmo_mtp_prim *m3ua_to_xfer_ind(struct xua_msg *xua)
@ -227,6 +228,14 @@ static int hmrt_message_for_routing(struct osmo_ss7_instance *inst,
dpc, osmo_ss7_pointcode_print(inst, dpc), rt_name);
}
if (osmo_ss7_as_down(as)) {
LOGP(DLSS7, LOGL_ERROR, "Unable to route HMRT message: the AS %s is down\n",
as->cfg.name);
return -ENETDOWN;
}
rate_ctr_inc2(as->ctrg, SS7_AS_CTR_TX_MSU_TOTAL);
switch (as->cfg.proto) {
case OSMO_SS7_ASP_PROT_M3UA:
DEBUGP(DLSS7, "rt->dest.as proto is M3UA for dpc=%u=%s\n",

File diff suppressed because it is too large Load Diff

374
src/osmo_ss7_xua_srv.c Normal file
View File

@ -0,0 +1,374 @@
/* Core SS7 xUA Server */
/* (C) 2015-2017 by Harald Welte <laforge@gnumonks.org>
* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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 <string.h>
#include <errno.h>
#include <unistd.h>
#include <inttypes.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <osmocom/sigtran/osmo_ss7.h>
#include <osmocom/sigtran/mtp_sap.h>
#include <osmocom/sigtran/protocol/mtp.h>
#include <osmocom/sigtran/protocol/sua.h>
#include <osmocom/sigtran/protocol/m3ua.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/select.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/core/osmo_io.h>
#include <osmocom/netif/stream.h>
#include <osmocom/netif/ipa.h>
#include <osmocom/netif/sctp.h>
#include "sccp_internal.h"
#include "xua_internal.h"
#include "ss7_internal.h"
#include "xua_asp_fsm.h"
#include "xua_as_fsm.h"
/***********************************************************************
* SS7 xUA Server
***********************************************************************/
/* server has accept()ed a new SCTP association, let's find the ASP for
* it (if any) */
static int xua_accept_cb(struct osmo_stream_srv_link *link, int fd)
{
struct osmo_xua_server *oxs = osmo_stream_srv_link_get_data(link);
struct osmo_stream_srv *srv;
struct osmo_ss7_asp *asp;
char *sock_name = osmo_sock_get_name(link, fd);
const char *proto_name = get_value_string(osmo_ss7_asp_protocol_vals, oxs->cfg.proto);
int rc = 0;
LOGP(DLSS7, LOGL_INFO, "%s: New %s connection accepted\n", sock_name, proto_name);
srv = osmo_stream_srv_create2(oxs, link, fd, NULL);
if (!srv) {
LOGP(DLSS7, LOGL_ERROR, "%s: Unable to create stream server "
"for connection\n", sock_name);
close(fd);
talloc_free(sock_name);
return -1;
}
osmo_iofd_set_alloc_info(osmo_stream_srv_get_iofd(srv), M3UA_MSG_SIZE, M3UA_MSG_HEADROOM);
switch (oxs->cfg.proto) {
case OSMO_SS7_ASP_PROT_IPA:
osmo_stream_srv_set_read_cb(srv, ss7_asp_ipa_srv_conn_rx_cb);
osmo_stream_srv_set_segmentation_cb(srv, osmo_ipa_segmentation_cb);
break;
case OSMO_SS7_ASP_PROT_M3UA:
if (oxs->cfg.trans_proto == IPPROTO_SCTP)
osmo_stream_srv_set_read_cb(srv, &ss7_asp_xua_srv_conn_rx_cb);
else if (oxs->cfg.trans_proto == IPPROTO_TCP) {
osmo_stream_srv_set_read_cb(srv, &ss7_asp_m3ua_tcp_srv_conn_rx_cb);
osmo_stream_srv_set_segmentation_cb(srv, xua_tcp_segmentation_cb);
} else
OSMO_ASSERT(0);
break;
default:
OSMO_ASSERT(oxs->cfg.trans_proto == IPPROTO_SCTP);
osmo_stream_srv_set_read_cb(srv, &ss7_asp_xua_srv_conn_rx_cb);
osmo_stream_srv_set_segmentation_cb(srv, NULL);
break;
}
osmo_stream_srv_set_closed_cb(srv, ss7_asp_xua_srv_conn_closed_cb);
asp = ss7_asp_find_by_socket_addr(fd, oxs->cfg.trans_proto);
if (asp) {
LOGP(DLSS7, LOGL_INFO, "%s: matched connection to ASP %s\n",
sock_name, asp->cfg.name);
/* we need to check if we already have a socket associated, and close it. Otherwise it might
* happen that both the listen-fd for this accept() and the old socket are marked 'readable'
* during the same scheduling interval, and we're processing them in the "wrong" order, i.e.
* we first see the accept of the new fd before we see the close on the old fd */
if (asp->server) {
LOGPASP(asp, DLSS7, LOGL_FATAL, "accept of new connection from %s before old was closed "
"-> close old one\n", sock_name);
osmo_stream_srv_set_data(asp->server, NULL);
osmo_stream_srv_destroy(asp->server);
asp->server = NULL;
}
} else {
if (!oxs->cfg.accept_dyn_reg) {
LOGP(DLSS7, LOGL_NOTICE, "%s: %s connection without matching "
"ASP definition and no dynamic registration enabled, terminating\n",
sock_name, proto_name);
} else {
char namebuf[32];
static uint32_t dyn_asp_num = 0;
snprintf(namebuf, sizeof(namebuf), "asp-dyn-%u", dyn_asp_num++);
asp = osmo_ss7_asp_find_or_create2(oxs->inst, namebuf, 0, 0,
oxs->cfg.trans_proto,
oxs->cfg.proto);
if (asp) {
char hostbuf[INET6_ADDRSTRLEN];
const char *hostbuf_ptr = &hostbuf[0];
char portbuf[16];
osmo_sock_get_ip_and_port(fd, hostbuf, sizeof(hostbuf), portbuf, sizeof(portbuf), false);
LOGP(DLSS7, LOGL_INFO, "%s: created dynamic ASP %s\n",
sock_name, asp->cfg.name);
asp->cfg.is_server = true;
asp->cfg.role = OSMO_SS7_ASP_ROLE_SG;
asp->cfg.local.port = oxs->cfg.local.port;
asp->cfg.remote.port = atoi(portbuf);
asp->dyn_allocated = true;
asp->server = srv;
osmo_ss7_asp_peer_set_hosts(&asp->cfg.local, asp,
(const char * const*)oxs->cfg.local.host,
oxs->cfg.local.host_cnt);
osmo_ss7_asp_peer_set_hosts(&asp->cfg.remote, asp,
&hostbuf_ptr, 1);
osmo_ss7_asp_restart(asp);
}
}
if (!asp) {
osmo_stream_srv_destroy(srv);
talloc_free(sock_name);
return -1;
}
llist_add_tail(&asp->siblings, &oxs->asp_list);
}
/* update the ASP reference back to the server over which the
* connection came in */
asp->server = srv;
asp->xua_server = oxs;
/* update the ASP socket name */
talloc_free(asp->sock_name);
asp->sock_name = talloc_reparent(link, asp, sock_name);
osmo_stream_srv_set_name(asp->server, asp->cfg.name);
/* make sure the conn_cb() is called with the asp as private
* data */
osmo_stream_srv_set_data(srv, asp);
if (oxs->cfg.trans_proto == IPPROTO_SCTP) {
rc = ss7_asp_apply_peer_primary_address(asp);
rc = ss7_asp_apply_primary_address(asp);
}
/* send M-SCTP_ESTABLISH.ind to Layer Manager */
osmo_fsm_inst_dispatch(asp->fi, XUA_ASP_E_SCTP_EST_IND, 0);
xua_asp_send_xlm_prim_simple(asp, OSMO_XLM_PRIM_M_SCTP_ESTABLISH, PRIM_OP_INDICATION);
return rc;
}
/*! \brief create a new xUA server configured with given ip/port
* \param[in] inst SS7 Instance on which we operate
* \param[in] trans_proto transport protocol to use (one of IPPROTO_*)
* \param[in] proto protocol (xUA variant) to use
* \param[in] local_port local SCTP port to bind/listen to
* \param[in] local_host local IP address to bind/listen to (optional)
* \returns callee-allocated \ref osmo_xua_server in case of success
*/
struct osmo_xua_server *
osmo_ss7_xua_server_create2(struct osmo_ss7_instance *inst,
int trans_proto, enum osmo_ss7_asp_protocol proto,
uint16_t local_port, const char *local_host)
{
struct osmo_xua_server *oxs;
if (!ss7_asp_protocol_check_trans_proto(proto, trans_proto)) {
LOGP(DLSCCP, LOGL_ERROR,
"ASP protocol '%s' with transport protocol %d is not supported",
osmo_ss7_asp_protocol_name(proto), trans_proto);
return NULL;
}
OSMO_ASSERT(ss7_initialized);
oxs = talloc_zero(inst, struct osmo_xua_server);
if (!oxs)
return NULL;
LOGP(DLSS7, LOGL_INFO, "Creating %s Server %s:%u\n",
get_value_string(osmo_ss7_asp_protocol_vals, proto), local_host, local_port);
INIT_LLIST_HEAD(&oxs->asp_list);
oxs->cfg.trans_proto = trans_proto;
oxs->cfg.proto = proto;
oxs->cfg.local.port = local_port;
oxs->server = osmo_stream_srv_link_create(oxs);
osmo_stream_srv_link_set_name(oxs->server, osmo_ss7_asp_protocol_name(proto));
osmo_stream_srv_link_set_data(oxs->server, oxs);
osmo_stream_srv_link_set_accept_cb(oxs->server, xua_accept_cb);
osmo_stream_srv_link_set_nodelay(oxs->server, true);
osmo_stream_srv_link_set_port(oxs->server, oxs->cfg.local.port);
osmo_stream_srv_link_set_proto(oxs->server, trans_proto);
osmo_ss7_xua_server_set_local_host(oxs, local_host);
LOGP(DLSS7, LOGL_INFO, "Created %s server on %s:%" PRIu16 "\n",
get_value_string(osmo_ss7_asp_protocol_vals, proto), local_host, local_port);
oxs->inst = inst;
llist_add_tail(&oxs->list, &inst->xua_servers);
/* The SUA code internally needs SCCP to work */
if (proto == OSMO_SS7_ASP_PROT_SUA)
osmo_ss7_ensure_sccp(inst);
return oxs;
}
/*! \brief create a new xUA server configured with given ip/port
* \param[in] ctx talloc allocation context
* \param[in] proto protocol (xUA variant) to use
* \param[in] local_port local SCTP port to bind/listen to
* \param[in] local_host local IP address to bind/listen to (optional)
* \returns callee-allocated \ref osmo_xua_server in case of success
*/
struct osmo_xua_server *
osmo_ss7_xua_server_create(struct osmo_ss7_instance *inst,
enum osmo_ss7_asp_protocol proto,
uint16_t local_port, const char *local_host)
{
const int trans_proto = ss7_default_trans_proto_for_asp_proto(proto);
return osmo_ss7_xua_server_create2(inst, trans_proto, proto,
local_port, local_host);
}
/*! \brief Set the xUA server to bind/listen to the currently configured ip/port
* \param[in] xs xUA server to operate
* \returns 0 on success, negative value on error.
*/
int
osmo_ss7_xua_server_bind(struct osmo_xua_server *xs)
{
char buf[512];
int rc;
uint8_t byte;
const char *proto = get_value_string(osmo_ss7_asp_protocol_vals, xs->cfg.proto);
rc = osmo_ss7_asp_peer_snprintf(buf, sizeof(buf), &xs->cfg.local);
if (rc < 0) {
LOGP(DLSS7, LOGL_INFO, "Failed parsing %s Server osmo_ss7_asp_peer\n", proto);
} else {
LOGP(DLSS7, LOGL_INFO, "(Re)binding %s Server to %s\n",
proto, buf);
}
/* Applying xUA Server config which may have changed through VTY on the srv_link before opening it: */
byte = 1; /*AUTH is needed by ASCONF. enable, don't abort socket creation if AUTH can't be enabled */
osmo_stream_srv_link_set_param(xs->server, OSMO_STREAM_SRV_LINK_PAR_SCTP_SOCKOPT_AUTH_SUPPORTED, &byte, sizeof(byte));
byte = 1; /* enable, don't abort socket creation if ASCONF can't be enabled */
osmo_stream_srv_link_set_param(xs->server, OSMO_STREAM_SRV_LINK_PAR_SCTP_SOCKOPT_ASCONF_SUPPORTED, &byte, sizeof(byte));
if (xs->cfg.sctp_init.num_ostreams_present)
osmo_stream_srv_link_set_param(xs->server, OSMO_STREAM_SRV_LINK_PAR_SCTP_INIT_NUM_OSTREAMS,
&xs->cfg.sctp_init.num_ostreams_value,
sizeof(xs->cfg.sctp_init.num_ostreams_value));
if (xs->cfg.sctp_init.max_instreams_present)
osmo_stream_srv_link_set_param(xs->server, OSMO_STREAM_SRV_LINK_PAR_SCTP_INIT_MAX_INSTREAMS,
&xs->cfg.sctp_init.max_instreams_value,
sizeof(xs->cfg.sctp_init.max_instreams_value));
return osmo_stream_srv_link_open(xs->server);
}
int
osmo_ss7_xua_server_set_local_host(struct osmo_xua_server *xs, const char *local_host)
{
return osmo_ss7_xua_server_set_local_hosts(xs, &local_host, 1);
}
int
osmo_ss7_xua_server_set_local_hosts(struct osmo_xua_server *xs, const char **local_hosts, size_t local_host_cnt)
{
int rc;
OSMO_ASSERT(ss7_initialized);
rc = osmo_ss7_asp_peer_set_hosts(&xs->cfg.local, xs, local_hosts, local_host_cnt);
if (rc < 0)
return rc;
return osmo_stream_srv_link_set_addrs(xs->server, (const char **)xs->cfg.local.host, xs->cfg.local.host_cnt);
}
int
osmo_ss7_xua_server_add_local_host(struct osmo_xua_server *xs, const char *local_host)
{
int rc;
rc = osmo_ss7_asp_peer_add_host(&xs->cfg.local, xs, local_host);
if (rc < 0)
return rc;
return osmo_stream_srv_link_set_addrs(xs->server, (const char **)xs->cfg.local.host, xs->cfg.local.host_cnt);
}
int
osmo_ss7_xua_server_del_local_host(struct osmo_xua_server *xs, const char *local_host)
{
int rc;
rc = osmo_ss7_asp_peer_del_host(&xs->cfg.local, local_host);
if (rc < 0)
return rc;
return osmo_stream_srv_link_set_addrs(xs->server, (const char **)xs->cfg.local.host, xs->cfg.local.host_cnt);
}
bool ss7_xua_server_set_default_local_hosts(struct osmo_xua_server *oxs)
{
/* If no local addr was set, or erased after _create(): */
if (!oxs->cfg.local.host_cnt) {
/* "::" Covers both IPv4 and IPv6 */
if (ss7_ipv6_sctp_supported("::", true))
osmo_ss7_xua_server_set_local_host(oxs, "::");
else
osmo_ss7_xua_server_set_local_host(oxs, "0.0.0.0");
return true;
}
return false;
}
void osmo_ss7_xua_server_destroy(struct osmo_xua_server *xs)
{
struct osmo_ss7_asp *asp, *asp2;
if (xs->server) {
osmo_stream_srv_link_close(xs->server);
osmo_stream_srv_link_destroy(xs->server);
}
/* iterate and close all connections established in relation
* with this server */
llist_for_each_entry_safe(asp, asp2, &xs->asp_list, siblings)
osmo_ss7_asp_destroy(asp);
llist_del(&xs->list);
talloc_free(xs);
}

View File

@ -18,12 +18,9 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <errno.h>
#include <string.h>
#include <osmocom/core/msgb.h>
@ -31,7 +28,7 @@
#include <osmocom/core/logging.h>
#include <osmocom/core/endian.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/sccp/sccp_types.h>
#include <osmocom/sccp/sccp.h>
// Unassigned debug area
@ -230,6 +227,12 @@ int _sccp_parse_connection_request(struct msgb *msgb, struct sccp_parse_result *
}
if (optional_data.data_len != 0) {
if (optional_data.data_len > SCCP_MAX_OPTIONAL_DATA) {
LOGP(DSCCP, LOGL_ERROR,
"optional data has length %u exceeding max of %u according to ITU-T Rec. Q.713 §4.2\n",
optional_data.data_len, SCCP_MAX_OPTIONAL_DATA);
return -EMSGSIZE;
}
msgb->l3h = &msgb->l2h[optional_data.data_start];
result->data_len = optional_data.data_len;
} else {
@ -264,6 +267,12 @@ int _sccp_parse_connection_released(struct msgb *msgb, struct sccp_parse_result
result->destination_local_reference = &rls->destination_local_reference;
if (optional_data.data_len != 0) {
if (optional_data.data_len > SCCP_MAX_OPTIONAL_DATA) {
LOGP(DSCCP, LOGL_ERROR,
"optional data has length %u exceeding max of %u according to ITU-T Rec. Q.713 §4.5\n",
optional_data.data_len, SCCP_MAX_OPTIONAL_DATA);
return -EMSGSIZE;
}
msgb->l3h = &msgb->l2h[optional_data.data_start];
result->data_len = optional_data.data_len;
} else {
@ -301,6 +310,12 @@ int _sccp_parse_connection_refused(struct msgb *msgb, struct sccp_parse_result *
/* optional data */
if (optional_data.data_len != 0) {
if (optional_data.data_len > SCCP_MAX_OPTIONAL_DATA) {
LOGP(DSCCP, LOGL_ERROR,
"optional data has length %u exceeding max of %u according to ITU-T Rec. Q.713 §4.4\n",
optional_data.data_len, SCCP_MAX_OPTIONAL_DATA);
return -EMSGSIZE;
}
msgb->l3h = &msgb->l2h[optional_data.data_start];
result->data_len = optional_data.data_len;
} else {
@ -338,6 +353,12 @@ int _sccp_parse_connection_confirm(struct msgb *msgb, struct sccp_parse_result *
}
if (optional_data.data_len != 0) {
if (optional_data.data_len > SCCP_MAX_OPTIONAL_DATA) {
LOGP(DSCCP, LOGL_ERROR,
"optional data has length %u exceeding max of %u according to ITU-T Rec. Q.713 §4.3\n",
optional_data.data_len, SCCP_MAX_OPTIONAL_DATA);
return -EMSGSIZE;
}
msgb->l3h = &msgb->l2h[optional_data.data_start];
result->data_len = optional_data.data_len;
} else {
@ -819,16 +840,15 @@ struct msgb *sccp_create_cr(const struct sccp_source_reference *src_ref,
struct msgb *request;
struct sccp_connection_request *req;
uint8_t *data;
uint8_t extra_size = 3 + 1;
int called_len;
if (l3_data && (l3_length < 3 || l3_length > 130)) {
LOGP(DSCCP, LOGL_ERROR, "Invalid amount of data... %zu\n", l3_length);
return NULL;
if (l3_data) {
if (l3_length < 3 || l3_length > SCCP_MAX_OPTIONAL_DATA) {
LOGP(DSCCP, LOGL_ERROR, "Invalid amount of data... %zu\n", l3_length);
return NULL;
}
}
if (l3_data)
extra_size += 2 + l3_length;
request = msgb_alloc_headroom(SCCP_MSG_SIZE,
SCCP_MSG_HEADROOM, "sccp connection request");
request->l2h = &request->data[0];

View File

@ -26,6 +26,7 @@
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <inttypes.h>
#include <osmocom/sccp/sccp.h>
#include <osmocom/core/linuxlist.h>
@ -337,10 +338,17 @@ static int sccp_addr_to_sua(struct xua_msg *xua, uint16_t iei, const uint8_t *ad
}
/*! \brief convenience wrapper around sccp_addr_to_sua() for variable mandatory addresses */
static int sccp_addr_to_sua_ptr(struct xua_msg *xua, uint16_t iei, struct msgb *msg, uint8_t *ptr_addr)
static int sccp_addr_to_sua_ptr(struct xua_msg *xua, uint16_t iei, const uint8_t *ptr_addr, bool ptr_addr_is_long)
{
uint8_t *addr = ptr_addr + *ptr_addr + 1;
unsigned int addrlen = *(ptr_addr + *ptr_addr);
const uint8_t *addr;
unsigned int addrlen;
if (ptr_addr_is_long) /* +1: Distance from MSB of pointer */
addr = ptr_addr + 1 + osmo_load16le(ptr_addr);
else
addr = ptr_addr + *ptr_addr;
addrlen = *addr;
addr++;
return sccp_addr_to_sua(xua, iei, addr, addrlen);
}
@ -349,7 +357,7 @@ static int sccp_addr_to_sua_ptr(struct xua_msg *xua, uint16_t iei, struct msgb *
* \param msg user-provided message buffer to which address shall be * appended
* \param[in] part SUA wire format binary address
* \returns 0 in case of success; negative on error */
static int sua_addr_to_sccp(struct msgb *msg, struct xua_msg_part *part)
static int sua_addr_to_sccp(struct msgb *msg, const struct xua_msg_part *part)
{
struct osmo_sccp_addr osa;
int rc;
@ -367,9 +375,10 @@ static int sua_addr_to_sccp(struct msgb *msg, struct xua_msg_part *part)
/*! \brief Add a "SCCP Variable Mandatory Part" (Address format) to the given msgb
* \param msg Message buffer to which part shall be added
* \param[out] var_ptr pointer to relative pointer in SCCP header
* \param[in] var_ptr_is_long Whether the var_ptr field is 2 bytes long (network order)
* \param[in] xua xUA message from which to use address
* \param[in] iei xUA information element identifier of address */
static int sccp_add_var_addr(struct msgb *msg, uint8_t *var_ptr, struct xua_msg *xua, uint16_t iei)
static int sccp_add_var_addr(struct msgb *msg, uint8_t *var_ptr, bool var_ptr_is_long, const struct xua_msg *xua, uint16_t iei)
{
struct xua_msg_part *part = xua_msg_find_tag(xua, iei);
uint8_t *lenbyte;
@ -382,7 +391,10 @@ static int sccp_add_var_addr(struct msgb *msg, uint8_t *var_ptr, struct xua_msg
/* first allocate one byte for the length */
lenbyte = msgb_put(msg, 1);
/* update the relative pointer to the length byte */
*var_ptr = lenbyte - var_ptr;
if (var_ptr_is_long) /* -1: Distance from MSB of pointer */
osmo_store16le(lenbyte - var_ptr - 1, var_ptr);
else
*var_ptr = lenbyte - var_ptr;
/* then append the encoded SCCP address */
rc = sua_addr_to_sccp(msg, part);
@ -400,7 +412,7 @@ static int sccp_add_var_addr(struct msgb *msg, uint8_t *var_ptr, struct xua_msg
* \param[out] var_ptr pointer to relative pointer in SCCP header
* \param[in] xua xUA message from which to use source data
* \param[in] iei xUA information element identifier of source data */
static int sccp_add_variable_part(struct msgb *msg, uint8_t *var_ptr, struct xua_msg *xua, uint16_t iei)
static int sccp_add_variable_part(struct msgb *msg, uint8_t *var_ptr, const struct xua_msg *xua, uint16_t iei)
{
struct xua_msg_part *part = xua_msg_find_tag(xua, iei);
uint8_t *lenbyte;
@ -425,14 +437,45 @@ static int sccp_add_variable_part(struct msgb *msg, uint8_t *var_ptr, struct xua
return part->len;
}
/*! \brief Add a "SCCP Long Variable Mandatory Part" to the given msgb
* \param msg Message buffer to which part shall be added
* \param[out] var_ptr pointer to relative pointer in SCCP header
* \param[in] xua xUA message from which to use source data
* \param[in] iei xUA information element identifier of source data */
static int sccp_add_long_variable_part(struct msgb *msg, uint8_t *var_ptr, const struct xua_msg *xua, uint16_t iei)
{
struct xua_msg_part *part = xua_msg_find_tag(xua, iei);
uint8_t *lenbyte;
uint8_t *cur;
if (!part) {
LOGP(DLSUA, LOGL_ERROR, "Cannot find IEI %u in SUA message\n", iei);
return -ENODEV;
}
/* first allocate 2 bytes for the length */
lenbyte = msgb_put(msg, 2);
/* update the relative pointer to the length byte.
* This is only used in LUDT and LUDTS, so the pointer is always 2 bytes.
* -1: Distance from MSB of pointer */
osmo_store16le(lenbyte - var_ptr - 1, var_ptr);
/* then append the encoded SCCP address */
cur = msgb_put(msg, part->len);
memcpy(cur, part->dat, part->len);
/* store the encoded length of the address, LSB first */
osmo_store16le(part->len, lenbyte);
return part->len;
}
/*! \brief validate that SCCP part with pointer + length doesn't exceed msg tail
* \param[in] msg Message containing SCCP address
* \param[in] ptr_addr pointer to byte with relative SCCP pointer
* \returns true if OK; false if message inconsistent */
static bool sccp_ptr_part_consistent(struct msgb *msg, uint8_t *ptr_addr)
static bool sccp_ptr_part_consistent(const struct msgb *msg, const uint8_t *ptr_addr)
{
uint8_t *ptr;
const uint8_t *ptr;
/* check the address of the relative pointer is within msg */
if (ptr_addr < msg->data || ptr_addr > msg->tail) {
@ -454,15 +497,68 @@ static bool sccp_ptr_part_consistent(struct msgb *msg, uint8_t *ptr_addr)
return true;
}
/*! \brief convenience wrapper around xua_msg_add_data() for variable mandatory data */
static int sccp_data_to_sua_ptr(struct xua_msg *xua, uint16_t iei, struct msgb *msg, uint8_t *ptr_addr)
/*! \brief validate that SCCP part with long pointer (2 bytes) + length doesn't exceed msg tail
* \param[in] msg Message containing SCCP address (LUDT or LUDTS)
* \param[in] ptr_addr pointer to byte with relative SCCP long pointer (uint16_t, 2 bytes in network order)
* \param[in] len_is_long whether the length field at the starting of the value field pointer to by ptr_addr is 2 bytes long.
* \returns true if OK; false if message inconsistent */
static bool sccp_longptr_part_consistent(const struct msgb *msg, const uint8_t *ptr_addr, bool len_is_long)
{
uint8_t *addr = ptr_addr + *ptr_addr + 1;
const uint8_t *ptr;
uint8_t offs;
uint16_t len;
/* check the address of the relative pointer is within msg */
if (ptr_addr < msg->data || ptr_addr > msg->tail) {
LOGP(DLSUA, LOGL_ERROR, "ptr_addr outside msg boundary\n");
return false;
}
/* +1: Distance from MSB of pointer */
ptr = ptr_addr + 1 + osmo_load16le(ptr_addr);
if (ptr > msg->tail) {
LOGP(DLSUA, LOGL_ERROR, "ptr %p points outside msg boundary %p\n", ptr, msg->tail);
return false;
}
/* at destination of relative pointer is the length */
if (len_is_long) {
offs = 2;
len = osmo_load16le(ptr);
} else {
offs = 1;
len = *ptr;
}
if (ptr + offs + len > msg->tail) {
LOGP(DLSUA, LOGL_ERROR, "ptr + len points outside msg boundary\n");
return false;
}
return true;
}
/*! \brief convenience wrapper around xua_msg_add_data() for variable mandatory data */
static int sccp_data_to_sua_ptr(struct xua_msg *xua, uint16_t iei, const uint8_t *ptr_addr)
{
const uint8_t *addr = ptr_addr + *ptr_addr + 1;
unsigned int addrlen = *(ptr_addr + *ptr_addr);
return xua_msg_add_data(xua, iei, addrlen, addr);
}
/*! \brief convenience wrapper around xua_msg_add_data() for variable mandatory data */
static int sccp_longdata_to_sua_ptr(struct xua_msg *xua, uint16_t iei, const uint8_t *ptr_addr)
{
const uint16_t ptr_value = osmo_load16le(ptr_addr);
/* +1: Distance from MSB of pointer */
const uint8_t *addrlen_ptr = ptr_addr + 1 + ptr_value;
/* +2: size of length field */
const uint8_t *addr = addrlen_ptr + 2;
unsigned int addrlen = osmo_load16le(addrlen_ptr);
return xua_msg_add_data(xua, iei, addrlen, addr);
}
/*! \brief Convert a given SCCP option to SUA and add it to given xua_msg
* \param xua caller-provided xUA message to which option is to be added
* \param[in] sccp_opt_type SCCP option type (PNC)
@ -470,7 +566,7 @@ static int sccp_data_to_sua_ptr(struct xua_msg *xua, uint16_t iei, struct msgb *
* \param[in] opt pointer to wire-format encoded SCCP option data
* \returns 0 in case of success; negative on error */
static int xua_msg_add_sccp_opt(struct xua_msg *xua, uint8_t sccp_opt_type,
uint16_t opt_len, uint8_t *opt)
uint16_t opt_len, const uint8_t *opt)
{
switch (sccp_opt_type) {
case SCCP_PNC_DESTINATION_LOCAL_REFERENCE:
@ -586,7 +682,7 @@ static void msgb_put_sccp_opt(struct msgb *msg, uint8_t pnc, uint8_t len, const
* \param msg caller-provided message buffer to which option is to be appended
* \param[in] opt xUA option/IE (messge part) to be converted+added
* \returns 0 in case of success; negative on error */
static int sccp_msg_add_sua_opt(enum sccp_message_types type, struct msgb *msg, struct xua_msg_part *opt)
static int sccp_msg_add_sua_opt(enum sccp_message_types type, struct msgb *msg, const struct xua_msg_part *opt)
{
uint32_t tmp32;
uint8_t pnc, *lenptr;
@ -696,22 +792,34 @@ static int sccp_msg_add_sua_opt(enum sccp_message_types type, struct msgb *msg,
/*! \brief convert SCCP optional part to list of SUA options
* \param[in] msg Message buffer holding SCCP message
* \param[in] ptr_opt address of relative pointer to optional part
* \param[in] ptr_opt_is_long whether ptr_opt is a long pointer (2 bytes, network order)
* \param xua caller-provided xUA message to which options are added
* \returns \ref xua in case of success, NULL on error (xua not freed!) */
static struct xua_msg *sccp_to_xua_opt(struct msgb *msg, uint8_t *ptr_opt, struct xua_msg *xua)
static struct xua_msg *sccp_to_xua_opt(const struct msgb *msg, const uint8_t *ptr_opt, bool ptr_opt_is_long, struct xua_msg *xua)
{
uint8_t *opt_start, *oneopt;
const uint8_t *opt_start, *oneopt;
uint16_t ptr_opt_value;
/* some bounds checking */
if (ptr_opt < msg->data || ptr_opt > msg->tail)
if (ptr_opt < msg->data)
return NULL;
if (ptr_opt > msg->tail - (ptr_opt_is_long ? 2 : 1))
return NULL;
if (ptr_opt_is_long)
ptr_opt_value = osmo_load16le(ptr_opt);
else
ptr_opt_value = *ptr_opt;
/* Q.713 section 2.3 "Coding of pointers": pointer value all zeros used
to indicate that no optional param is present. */
if (*ptr_opt == 0)
if (ptr_opt_value == 0)
return xua;
opt_start = ptr_opt + *ptr_opt;
if (ptr_opt_is_long) /* +1: Distance from MSB of pointer */
opt_start = ptr_opt + 1 + ptr_opt_value;
else
opt_start = ptr_opt + ptr_opt_value;
if (opt_start > msg->tail)
return NULL;
@ -982,37 +1090,55 @@ static bool sccp_option_permitted(enum sccp_message_types type, const struct xua
return false;
}
static int xua_ies_to_sccp_opts(struct msgb *msg, uint8_t *ptr_opt,
enum sccp_message_types type, struct xua_msg *xua)
static int xua_ies_to_sccp_opts(struct msgb *msg, uint8_t *ptr_opt, bool ptr_opt_is_long,
enum sccp_message_types type, const struct xua_msg *xua)
{
struct xua_msg_part *part;
/* store relative pointer to start of optional part */
*ptr_opt = msg->tail - ptr_opt;
const struct xua_msg_part *part;
bool any_added = false;
uint8_t *old_tail = msg->tail;
llist_for_each_entry(part, &xua->headers, entry) {
/* make sure we don't add a SCCP option for information
* that is already present in mandatory fixed or
* mandatory variable parts of the header */
if (!sccp_is_mandatory(type, part) && sccp_option_permitted(type, part))
if (!sccp_is_mandatory(type, part) && sccp_option_permitted(type, part)) {
sccp_msg_add_sua_opt(type, msg, part);
any_added = true;
}
}
if (any_added) {
msgb_put_u8(msg, SCCP_PNC_END_OF_OPTIONAL);
/* store relative pointer to start of optional part */
if (ptr_opt_is_long) /* -1: Distance from MSB of pointer */
osmo_store16le(old_tail - ptr_opt - 1, ptr_opt);
else
*ptr_opt = old_tail - ptr_opt;
} else {
/* If nothing was added, simply update the pointer to 0 to signal the optional part is omitted. */
ptr_opt[0] = 0;
if (ptr_opt_is_long)
ptr_opt[1] = 0;
}
msgb_put_u8(msg, SCCP_PNC_END_OF_OPTIONAL);
return 0;
}
/* store a 'local reference' as big-eidian 24bit value at local_ref */
static void store_local_ref(struct sccp_source_reference *local_ref, struct xua_msg *xua, uint16_t iei)
/* store a 'local reference' as big-endian 24bit value at local_ref */
static int store_local_ref(struct sccp_source_reference *local_ref, const struct xua_msg *xua, uint16_t iei)
{
uint32_t tmp32 = xua_msg_get_u32(xua, iei);
if (tmp32 > 0x00fffffe) {
LOGP(DLSUA, LOGL_ERROR, "SUA->SCCP: Local Reference value 0x%" PRIx32 " > 0x00fffffe\n", tmp32);
return -1;
}
local_ref->octet1 = (tmp32 >> 16) & 0xff;
local_ref->octet2 = (tmp32 >> 8) & 0xff;
local_ref->octet3 = tmp32 & 0xff;
return 0;
}
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
static struct xua_msg *sccp_to_xua_cr(struct msgb *msg, struct xua_msg *xua)
static struct xua_msg *sccp_to_xua_cr(const struct msgb *msg, struct xua_msg *xua)
{
struct sccp_connection_request *req = (struct sccp_connection_request *)msg->l2h;
@ -1022,30 +1148,33 @@ static struct xua_msg *sccp_to_xua_cr(struct msgb *msg, struct xua_msg *xua)
/* Variable Part */
if (!sccp_ptr_part_consistent(msg, &req->variable_called))
return NULL;
sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, msg, &req->variable_called);
sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, &req->variable_called, false);
/* Optional Part */
return sccp_to_xua_opt(msg, &req->optional_start, xua);
return sccp_to_xua_opt(msg, &req->optional_start, false, xua);
}
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
static int sua_to_sccp_cr(struct msgb *msg, struct xua_msg *xua)
static int sua_to_sccp_cr(struct msgb *msg, const struct xua_msg *xua)
{
struct sccp_connection_request *req;
int rc;
req = (struct sccp_connection_request *) msgb_put(msg, sizeof(*req));
/* Fixed Part */
req->type = SCCP_MSG_TYPE_CR;
req->proto_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS);
store_local_ref(&req->source_local_reference, xua, SUA_IEI_SRC_REF);
rc = store_local_ref(&req->source_local_reference, xua, SUA_IEI_SRC_REF);
if (rc < 0)
return rc;
/* Variable Part */
sccp_add_var_addr(msg, &req->variable_called, xua, SUA_IEI_DEST_ADDR);
sccp_add_var_addr(msg, &req->variable_called, false, xua, SUA_IEI_DEST_ADDR);
/* Optional Part */
return xua_ies_to_sccp_opts(msg, &req->optional_start, req->type, xua);
return xua_ies_to_sccp_opts(msg, &req->optional_start, false, req->type, xua);
}
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
static struct xua_msg *sccp_to_xua_cc(struct msgb *msg, struct xua_msg *xua)
static struct xua_msg *sccp_to_xua_cc(const struct msgb *msg, struct xua_msg *xua)
{
struct sccp_connection_confirm *cnf = (struct sccp_connection_confirm *)msg->l2h;
@ -1054,83 +1183,96 @@ static struct xua_msg *sccp_to_xua_cc(struct msgb *msg, struct xua_msg *xua)
xua_msg_add_u32(xua, SUA_IEI_DEST_REF, load_24be(&cnf->destination_local_reference));
xua_msg_add_u32(xua, SUA_IEI_SRC_REF, load_24be(&cnf->source_local_reference));
/* Optional Part */
return sccp_to_xua_opt(msg, &cnf->optional_start, xua);
return sccp_to_xua_opt(msg, &cnf->optional_start, false, xua);
}
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
static int sua_to_sccp_cc(struct msgb *msg, struct xua_msg *xua)
static int sua_to_sccp_cc(struct msgb *msg, const struct xua_msg *xua)
{
struct sccp_connection_confirm *cnf;
int rc;
cnf = (struct sccp_connection_confirm *) msgb_put(msg, sizeof(*cnf));
/* Fixed Part */
cnf->type = SCCP_MSG_TYPE_CC;
cnf->proto_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS);
store_local_ref(&cnf->destination_local_reference, xua, SUA_IEI_DEST_REF);
store_local_ref(&cnf->source_local_reference, xua, SUA_IEI_SRC_REF);
rc = store_local_ref(&cnf->destination_local_reference, xua, SUA_IEI_DEST_REF);
if (rc < 0)
return rc;
rc = store_local_ref(&cnf->source_local_reference, xua, SUA_IEI_SRC_REF);
if (rc < 0)
return rc;
/* Optional Part */
return xua_ies_to_sccp_opts(msg, &cnf->optional_start, cnf->type, xua);
return xua_ies_to_sccp_opts(msg, &cnf->optional_start, false, cnf->type, xua);
}
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
static struct xua_msg *sccp_to_xua_cref(struct msgb *msg, struct xua_msg *xua)
static struct xua_msg *sccp_to_xua_cref(const struct msgb *msg, struct xua_msg *xua)
{
struct sccp_connection_refused *ref = (struct sccp_connection_refused *)msg->l2h;
const struct sccp_connection_refused *ref = (const struct sccp_connection_refused *)msg->l2h;
/* Fixed Part */
xua_msg_add_u32(xua, SUA_IEI_DEST_REF, load_24be(&ref->destination_local_reference));
xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_REFUSAL | ref->cause);
/* Optional Part */
return sccp_to_xua_opt(msg, &ref->optional_start, xua);
return sccp_to_xua_opt(msg, &ref->optional_start, false, xua);
}
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
static int sua_to_sccp_cref(struct msgb *msg, struct xua_msg *xua)
static int sua_to_sccp_cref(struct msgb *msg, const struct xua_msg *xua)
{
struct sccp_connection_refused *ref;
int rc;
ref = (struct sccp_connection_refused *) msgb_put(msg, sizeof(*ref));
/* Fixed Part */
ref->type = SCCP_MSG_TYPE_CREF;
store_local_ref(&ref->destination_local_reference, xua, SUA_IEI_DEST_REF);
rc = store_local_ref(&ref->destination_local_reference, xua, SUA_IEI_DEST_REF);
if (rc < 0)
return rc;
ref->cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE) & 0xff;
/* Optional Part */
return xua_ies_to_sccp_opts(msg, &ref->optional_start, ref->type, xua);
return xua_ies_to_sccp_opts(msg, &ref->optional_start, false, ref->type, xua);
}
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
static struct xua_msg *sccp_to_xua_rlsd(struct msgb *msg, struct xua_msg *xua)
static struct xua_msg *sccp_to_xua_rlsd(const struct msgb *msg, struct xua_msg *xua)
{
struct sccp_connection_released *rlsd = (struct sccp_connection_released *)msg->l2h;
const struct sccp_connection_released *rlsd = (const struct sccp_connection_released *)msg->l2h;
/* Fixed Part */
xua_msg_add_u32(xua, SUA_IEI_DEST_REF, load_24be(&rlsd->destination_local_reference));
xua_msg_add_u32(xua, SUA_IEI_SRC_REF, load_24be(&rlsd->source_local_reference));
xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_RELEASE | rlsd->release_cause);
/* Optional Part */
return sccp_to_xua_opt(msg, &rlsd->optional_start, xua);
return sccp_to_xua_opt(msg, &rlsd->optional_start, false, xua);
}
static int sua_to_sccp_rlsd(struct msgb *msg, struct xua_msg *xua)
static int sua_to_sccp_rlsd(struct msgb *msg, const struct xua_msg *xua)
{
struct sccp_connection_released *rlsd;
int rc;
rlsd =(struct sccp_connection_released *) msgb_put(msg, sizeof(*rlsd));
/* Fixed Part */
rlsd->type = SCCP_MSG_TYPE_RLSD;
store_local_ref(&rlsd->destination_local_reference, xua, SUA_IEI_DEST_REF);
store_local_ref(&rlsd->source_local_reference, xua, SUA_IEI_SRC_REF);
rc = store_local_ref(&rlsd->destination_local_reference, xua, SUA_IEI_DEST_REF);
if (rc < 0)
return rc;
rc = store_local_ref(&rlsd->source_local_reference, xua, SUA_IEI_SRC_REF);
if (rc < 0)
return rc;
rlsd->release_cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE) & 0xff;
/* Optional Part */
return xua_ies_to_sccp_opts(msg, &rlsd->optional_start, rlsd->type, xua);
return xua_ies_to_sccp_opts(msg, &rlsd->optional_start, false, rlsd->type, xua);
}
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
static struct xua_msg *sccp_to_xua_rlc(struct msgb *msg, struct xua_msg *xua)
static struct xua_msg *sccp_to_xua_rlc(const struct msgb *msg, struct xua_msg *xua)
{
struct sccp_connection_release_complete *rlc;
rlc = (struct sccp_connection_release_complete *) msg->l2h;
const struct sccp_connection_release_complete *rlc;
rlc = (const struct sccp_connection_release_complete *) msg->l2h;
/* Fixed Part */
xua_msg_add_u32(xua, SUA_IEI_DEST_REF, load_24be(&rlc->destination_local_reference));
@ -1138,22 +1280,27 @@ static struct xua_msg *sccp_to_xua_rlc(struct msgb *msg, struct xua_msg *xua)
return xua;
}
static int sua_to_sccp_rlc(struct msgb *msg, struct xua_msg *xua)
static int sua_to_sccp_rlc(struct msgb *msg, const struct xua_msg *xua)
{
struct sccp_connection_release_complete *rlc;
int rc;
rlc = (struct sccp_connection_release_complete *) msgb_put(msg, sizeof(*rlc));
/* Fixed Part */
rlc->type = SCCP_MSG_TYPE_RLC;
store_local_ref(&rlc->destination_local_reference, xua, SUA_IEI_DEST_REF);
store_local_ref(&rlc->source_local_reference, xua, SUA_IEI_SRC_REF);
rc = store_local_ref(&rlc->destination_local_reference, xua, SUA_IEI_DEST_REF);
if (rc < 0)
return rc;
rc = store_local_ref(&rlc->source_local_reference, xua, SUA_IEI_SRC_REF);
if (rc < 0)
return rc;
return 0;
}
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
static struct xua_msg *sccp_to_xua_dt1(struct msgb *msg, struct xua_msg *xua)
static struct xua_msg *sccp_to_xua_dt1(const struct msgb *msg, struct xua_msg *xua)
{
struct sccp_data_form1 *dt1 = (struct sccp_data_form1 *) msg->l2h;
const struct sccp_data_form1 *dt1 = (const struct sccp_data_form1 *) msg->l2h;
/* Fixed Part */
xua_msg_add_u32(xua, SUA_IEI_DEST_REF, load_24be(&dt1->destination_local_reference));
@ -1161,18 +1308,21 @@ static struct xua_msg *sccp_to_xua_dt1(struct msgb *msg, struct xua_msg *xua)
/* Variable Part */
if (!sccp_ptr_part_consistent(msg, &dt1->variable_start))
return NULL;
sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, msg, &dt1->variable_start);
sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, &dt1->variable_start);
return xua;
}
static int sua_to_sccp_dt1(struct msgb *msg, struct xua_msg *xua)
static int sua_to_sccp_dt1(struct msgb *msg, const struct xua_msg *xua)
{
struct sccp_data_form1 *dt1;
int rc;
dt1 = (struct sccp_data_form1 *) msgb_put(msg, sizeof(*dt1));
/* Fixed Part */
dt1->type = SCCP_MSG_TYPE_DT1;
store_local_ref(&dt1->destination_local_reference, xua, SUA_IEI_DEST_REF);
rc = store_local_ref(&dt1->destination_local_reference, xua, SUA_IEI_DEST_REF);
if (rc < 0)
return rc;
dt1->segmenting = xua_msg_get_u32(xua, SUA_IEI_SEGMENTATION);
/* Variable Part */
sccp_add_variable_part(msg, &dt1->variable_start, xua, SUA_IEI_DATA);
@ -1180,82 +1330,276 @@ static int sua_to_sccp_dt1(struct msgb *msg, struct xua_msg *xua)
}
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
static struct xua_msg *sccp_to_xua_udt(struct msgb *msg, struct xua_msg *xua)
static struct xua_msg *sccp_to_xua_udt(const struct msgb *msg, struct xua_msg *xua)
{
struct sccp_data_unitdata *udt = (struct sccp_data_unitdata *)msg->l2h;
const struct sccp_data_unitdata *udt = (const struct sccp_data_unitdata *)msg->l2h;
/* Fixed Part */
xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, udt->proto_class);
/* Variable Part */
if (!sccp_ptr_part_consistent(msg, &udt->variable_called))
return NULL;
sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, msg, &udt->variable_called);
sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, &udt->variable_called, false);
if (!sccp_ptr_part_consistent(msg, &udt->variable_calling))
return NULL;
sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, msg, &udt->variable_calling);
sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, &udt->variable_calling, false);
if (!sccp_ptr_part_consistent(msg, &udt->variable_data))
return NULL;
sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, msg, &udt->variable_data);
sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, &udt->variable_data);
return xua;
}
static int sua_to_sccp_udt(struct msgb *msg, struct xua_msg *xua)
static int sua_to_sccp_xudt(struct msgb *msg, const struct xua_msg *xua);
static int sua_to_sccp_ludt(struct msgb *msg, const struct xua_msg *xua);
static int sua_to_sccp_udt(struct msgb *msg, const struct xua_msg *xua)
{
struct sccp_data_unitdata *udt;
/* Use LUDT if length exceeds 255 (single byte length field) */
/* TODO: start using LUDT sooner if called/calling party contain GT or if
* segmentation and/or importance present, see Q.715 Section 8.3.2 */
if (xua_msg_get_len(xua, SUA_IEI_DATA) > 255)
return sua_to_sccp_ludt(msg, xua);
/* Use XUDT if we have a hop counter on the SUA side */
if (xua_msg_find_tag(xua, SUA_IEI_S7_HOP_CTR))
return sua_to_sccp_xudt(msg, xua);
udt = (struct sccp_data_unitdata *) msgb_put(msg, sizeof(*udt));
/* Fixed Part */
udt->type = SCCP_MSG_TYPE_UDT;
udt->proto_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS);
/* Variable Part */
sccp_add_var_addr(msg, &udt->variable_called, xua, SUA_IEI_DEST_ADDR);
sccp_add_var_addr(msg, &udt->variable_calling, xua, SUA_IEI_SRC_ADDR);
sccp_add_var_addr(msg, &udt->variable_called, false, xua, SUA_IEI_DEST_ADDR);
sccp_add_var_addr(msg, &udt->variable_calling, false, xua, SUA_IEI_SRC_ADDR);
sccp_add_variable_part(msg, &udt->variable_data, xua, SUA_IEI_DATA);
return 0;
}
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
static struct xua_msg *sccp_to_xua_udts(struct msgb *msg, struct xua_msg *xua)
static struct xua_msg *sccp_to_xua_xudt(const struct msgb *msg, struct xua_msg *xua)
{
struct sccp_data_unitdata_service *udts;
udts =(struct sccp_data_unitdata_service *)msg->l2h;
const struct sccp_data_ext_unitdata *xudt = (const struct sccp_data_ext_unitdata *)msg->l2h;
/* Fixed Part */
xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, xudt->proto_class);
xua_msg_add_u32(xua, SUA_IEI_S7_HOP_CTR, xudt->hop_counter);
/* Variable Part */
if (!sccp_ptr_part_consistent(msg, &xudt->variable_called))
return NULL;
sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, &xudt->variable_called, false);
if (!sccp_ptr_part_consistent(msg, &xudt->variable_calling))
return NULL;
sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, &xudt->variable_calling, false);
if (!sccp_ptr_part_consistent(msg, &xudt->variable_data))
return NULL;
sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, &xudt->variable_data);
/* Optional Part */
return sccp_to_xua_opt(msg, &xudt->optional_start, false, xua);
}
static int sua_to_sccp_xudt(struct msgb *msg, const struct xua_msg *xua)
{
struct sccp_data_ext_unitdata *xudt;
/* Use LUDT if length exceeds 255 (single byte length field) */
/* TODO: start using LUDTS sooner if called/calling party contain GT or if
* segmentation and/or importance present, see Q.715 Section 8.3.2 */
if (xua_msg_get_len(xua, SUA_IEI_DATA) > 255)
return sua_to_sccp_ludt(msg, xua);
xudt = (struct sccp_data_ext_unitdata *) msgb_put(msg, sizeof(*xudt));
/* Fixed Part */
xudt->type = SCCP_MSG_TYPE_XUDT;
xudt->proto_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS);
xudt->hop_counter = xua_msg_get_u32(xua, SUA_IEI_S7_HOP_CTR);
/* Variable Part */
sccp_add_var_addr(msg, &xudt->variable_called, false, xua, SUA_IEI_DEST_ADDR);
sccp_add_var_addr(msg, &xudt->variable_calling, false, xua, SUA_IEI_SRC_ADDR);
sccp_add_variable_part(msg, &xudt->variable_data, xua, SUA_IEI_DATA);
/* Optional Part */
return xua_ies_to_sccp_opts(msg, &xudt->optional_start, false, xudt->type, xua);
}
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
static struct xua_msg *sccp_to_xua_ludt(struct msgb *msg, struct xua_msg *xua)
{
struct sccp_data_long_unitdata *ludt = (struct sccp_data_long_unitdata *)msg->l2h;
/* Fixed Part */
xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, ludt->proto_class);
xua_msg_add_u32(xua, SUA_IEI_S7_HOP_CTR, ludt->hop_counter);
/* Variable Part */
if (!sccp_longptr_part_consistent(msg, (uint8_t *)&ludt->variable_called, false))
return NULL;
sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, (uint8_t *)&ludt->variable_called, true);
if (!sccp_longptr_part_consistent(msg, (uint8_t *)&ludt->variable_calling, false))
return NULL;
sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, (uint8_t *)&ludt->variable_calling, true);
if (!sccp_longptr_part_consistent(msg, (uint8_t *)&ludt->variable_data, true))
return NULL;
sccp_longdata_to_sua_ptr(xua, SUA_IEI_DATA, (uint8_t *)&ludt->variable_data);
/* Optional Part */
return sccp_to_xua_opt(msg, (uint8_t *)&ludt->optional_start, true, xua);
}
static int sua_to_sccp_ludt(struct msgb *msg, const struct xua_msg *xua)
{
struct sccp_data_long_unitdata *ludt;
ludt = (struct sccp_data_long_unitdata *) msgb_put(msg, sizeof(*ludt));
/* Fixed Part */
ludt->type = SCCP_MSG_TYPE_LUDT;
ludt->proto_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS);
ludt->hop_counter = xua_msg_get_u32(xua, SUA_IEI_S7_HOP_CTR);
/* Variable Part */
sccp_add_var_addr(msg, (uint8_t *)&ludt->variable_called, true, xua, SUA_IEI_DEST_ADDR);
sccp_add_var_addr(msg, (uint8_t *)&ludt->variable_calling, true, xua, SUA_IEI_SRC_ADDR);
sccp_add_long_variable_part(msg, (uint8_t *)&ludt->variable_data, xua, SUA_IEI_DATA);
/* Optional Part */
return xua_ies_to_sccp_opts(msg, (uint8_t *)&ludt->optional_start, true, ludt->type, xua);
}
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
static struct xua_msg *sccp_to_xua_udts(const struct msgb *msg, struct xua_msg *xua)
{
const struct sccp_data_unitdata_service *udts;
udts = (const struct sccp_data_unitdata_service *)msg->l2h;
/* Fixed Part */
xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_RETURN | udts->return_cause);
/* Variable Part */
if (!sccp_ptr_part_consistent(msg, &udts->variable_called))
return NULL;
sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, msg, &udts->variable_called);
sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, &udts->variable_called, false);
if (!sccp_ptr_part_consistent(msg, &udts->variable_calling))
return NULL;
sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, msg, &udts->variable_calling);
sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, &udts->variable_calling, false);
if (!sccp_ptr_part_consistent(msg, &udts->variable_data))
return NULL;
sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, msg, &udts->variable_data);
sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, &udts->variable_data);
return xua;
}
static int sua_to_sccp_udts(struct msgb *msg, struct xua_msg *xua)
static int sua_to_sccp_xudts(struct msgb *msg, const struct xua_msg *xua);
static int sua_to_sccp_ludts(struct msgb *msg, const struct xua_msg *xua);
static int sua_to_sccp_udts(struct msgb *msg, const struct xua_msg *xua)
{
struct sccp_data_unitdata_service *udts;
/* Use LUDTS if length exceeds 255 (single byte length field) */
/* TODO: start using LUDTS sooner if called/calling party contain GT,
* see Q.715 Section 8.3.2 */
if (xua_msg_get_len(xua, SUA_IEI_DATA) > 255)
return sua_to_sccp_ludts(msg, xua);
/* Use XUDTS if we have a hop counter */
if (xua_msg_find_tag(xua, SUA_IEI_S7_HOP_CTR))
return sua_to_sccp_xudts(msg, xua);
udts = (struct sccp_data_unitdata_service *) msgb_put(msg, sizeof(*udts));
/* Fixed Part */
udts->type = SCCP_MSG_TYPE_UDTS;
udts->return_cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE) & 0xff;
/* Variable Part */
sccp_add_var_addr(msg, &udts->variable_called, xua, SUA_IEI_DEST_ADDR);
sccp_add_var_addr(msg, &udts->variable_calling, xua, SUA_IEI_SRC_ADDR);
sccp_add_var_addr(msg, &udts->variable_called, false, xua, SUA_IEI_DEST_ADDR);
sccp_add_var_addr(msg, &udts->variable_calling, false, xua, SUA_IEI_SRC_ADDR);
sccp_add_variable_part(msg, &udts->variable_data, xua, SUA_IEI_DATA);
return 0;
}
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
static struct xua_msg *sccp_to_xua_it(struct msgb *msg, struct xua_msg *xua)
static struct xua_msg *sccp_to_xua_xudts(const struct msgb *msg, struct xua_msg *xua)
{
struct sccp_data_it *it = (struct sccp_data_it *)msg->l2h;
const struct sccp_data_ext_unitdata_service *xudts;
xudts = (const struct sccp_data_ext_unitdata_service *)msg->l2h;
/* Fixed Part */
xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_RETURN | xudts->return_cause);
xua_msg_add_u32(xua, SUA_IEI_S7_HOP_CTR, xudts->hop_counter);
/* Variable Part */
if (!sccp_ptr_part_consistent(msg, (uint8_t *)&xudts->variable_called))
return NULL;
sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, &xudts->variable_called, false);
if (!sccp_ptr_part_consistent(msg, (uint8_t *)&xudts->variable_calling))
return NULL;
sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, &xudts->variable_calling, false);
if (!sccp_ptr_part_consistent(msg, (uint8_t *)&xudts->variable_data))
return NULL;
sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, &xudts->variable_data);
/* Optional Part */
return sccp_to_xua_opt(msg, &xudts->optional_start, false, xua);
}
static int sua_to_sccp_xudts(struct msgb *msg, const struct xua_msg *xua)
{
struct sccp_data_ext_unitdata_service *xudts;
xudts = (struct sccp_data_ext_unitdata_service *) msgb_put(msg, sizeof(*xudts));
/* Fixed Part */
xudts->type = SCCP_MSG_TYPE_XUDTS;
xudts->return_cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE) & 0xff;
xudts->hop_counter = xua_msg_get_u32(xua, SUA_IEI_S7_HOP_CTR);
/* Variable Part */
sccp_add_var_addr(msg, &xudts->variable_called, false, xua, SUA_IEI_DEST_ADDR);
sccp_add_var_addr(msg, &xudts->variable_calling, false, xua, SUA_IEI_SRC_ADDR);
sccp_add_variable_part(msg, &xudts->variable_data, xua, SUA_IEI_DATA);
/* Optional Part */
return xua_ies_to_sccp_opts(msg, &xudts->optional_start, false, xudts->type, xua);
}
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
static struct xua_msg *sccp_to_xua_ludts(const struct msgb *msg, struct xua_msg *xua)
{
const struct sccp_data_long_unitdata_service *ludts;
ludts = (const struct sccp_data_long_unitdata_service *)msg->l2h;
/* Fixed Part */
xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_RETURN | ludts->return_cause);
xua_msg_add_u32(xua, SUA_IEI_S7_HOP_CTR, ludts->hop_counter);
/* Variable Part */
if (!sccp_longptr_part_consistent(msg, (uint8_t *)&ludts->variable_called, false))
return NULL;
sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, (uint8_t *)&ludts->variable_called, true);
if (!sccp_longptr_part_consistent(msg, (uint8_t *)&ludts->variable_calling, false))
return NULL;
sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, (uint8_t *)&ludts->variable_calling, true);
if (!sccp_longptr_part_consistent(msg, (uint8_t *)&ludts->variable_data, true))
return NULL;
sccp_longdata_to_sua_ptr(xua, SUA_IEI_DATA, (uint8_t *)&ludts->variable_data);
/* Optional Part */
return sccp_to_xua_opt(msg, (uint8_t *)&ludts->optional_start, true, xua);
}
static int sua_to_sccp_ludts(struct msgb *msg, const struct xua_msg *xua)
{
struct sccp_data_long_unitdata_service *ludts;
ludts = (struct sccp_data_long_unitdata_service *) msgb_put(msg, sizeof(*ludts));
/* Fixed Part */
ludts->type = SCCP_MSG_TYPE_LUDTS;
ludts->return_cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE) & 0xff;
ludts->hop_counter = xua_msg_get_u32(xua, SUA_IEI_S7_HOP_CTR);
/* Variable Part */
sccp_add_var_addr(msg, (uint8_t *)&ludts->variable_called, true, xua, SUA_IEI_DEST_ADDR);
sccp_add_var_addr(msg, (uint8_t *)&ludts->variable_calling, true, xua, SUA_IEI_SRC_ADDR);
sccp_add_long_variable_part(msg, (uint8_t *)&ludts->variable_data, xua, SUA_IEI_DATA);
/* Optional Part */
return xua_ies_to_sccp_opts(msg, (uint8_t *)&ludts->optional_start, true, ludts->type, xua);
}
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
static struct xua_msg *sccp_to_xua_it(const struct msgb *msg, struct xua_msg *xua)
{
const struct sccp_data_it *it = (const struct sccp_data_it *)msg->l2h;
/* Fixed Part */
xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, it->proto_class);
@ -1268,16 +1612,21 @@ static struct xua_msg *sccp_to_xua_it(struct msgb *msg, struct xua_msg *xua)
return xua;
}
static int sua_to_sccp_it(struct msgb *msg, struct xua_msg *xua)
static int sua_to_sccp_it(struct msgb *msg, const struct xua_msg *xua)
{
struct sccp_data_it *it;
int rc;
it = (struct sccp_data_it *) msgb_put(msg, sizeof(*it));
/* Fixed Part */
it->type = SCCP_MSG_TYPE_IT;
it->proto_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS);
store_local_ref(&it->destination_local_reference, xua, SUA_IEI_DEST_REF);
store_local_ref(&it->source_local_reference, xua, SUA_IEI_SRC_REF);
rc = store_local_ref(&it->destination_local_reference, xua, SUA_IEI_DEST_REF);
if (rc < 0)
return rc;
rc = store_local_ref(&it->source_local_reference, xua, SUA_IEI_SRC_REF);
if (rc < 0)
return rc;
if ((it->proto_class & 0xF) == 3) {
//it->sequencing
it->credit = xua_msg_get_u32(xua, SUA_IEI_CREDIT);
@ -1287,9 +1636,9 @@ static int sua_to_sccp_it(struct msgb *msg, struct xua_msg *xua)
}
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
static struct xua_msg *sccp_to_xua_err(struct msgb *msg, struct xua_msg *xua)
static struct xua_msg *sccp_to_xua_err(const struct msgb *msg, struct xua_msg *xua)
{
struct sccp_proto_err *err = (struct sccp_proto_err *)msg->l2h;
const struct sccp_proto_err *err = (const struct sccp_proto_err *)msg->l2h;
/* Fixed Part */
xua_msg_add_u32(xua, SUA_IEI_DEST_REF, load_24be(&err->destination_local_reference));
@ -1297,14 +1646,17 @@ static struct xua_msg *sccp_to_xua_err(struct msgb *msg, struct xua_msg *xua)
return xua;
}
static int sua_to_sccp_err(struct msgb *msg, struct xua_msg *xua)
static int sua_to_sccp_err(struct msgb *msg, const struct xua_msg *xua)
{
struct sccp_proto_err *err;
int rc;
err = (struct sccp_proto_err *) msgb_put(msg, sizeof(*err));
/* Fixed Part */
err->type = SCCP_MSG_TYPE_ERR;
store_local_ref(&err->destination_local_reference, xua, SUA_IEI_DEST_REF);
rc = store_local_ref(&err->destination_local_reference, xua, SUA_IEI_DEST_REF);
if (rc < 0)
return rc;
err->error_cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE) & 0xff;
return 0;
}
@ -1376,6 +1728,26 @@ struct xua_msg *osmo_sccp_to_xua(struct msgb *msg)
if (!sccp_to_xua_err(msg, xua))
goto malformed;
return xua;
case SCCP_MSG_TYPE_XUDT:
xua->hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT);
if (!sccp_to_xua_xudt(msg, xua))
goto malformed;
return xua;
case SCCP_MSG_TYPE_XUDTS:
xua->hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDR);
if (!sccp_to_xua_xudts(msg, xua))
goto malformed;
return xua;
case SCCP_MSG_TYPE_LUDT:
xua->hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT);
if (!sccp_to_xua_ludt(msg, xua))
goto malformed;
return xua;
case SCCP_MSG_TYPE_LUDTS:
xua->hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDR);
if (!sccp_to_xua_ludts(msg, xua))
goto malformed;
return xua;
/* Unsupported Message Types */
case SCCP_MSG_TYPE_DT2:
case SCCP_MSG_TYPE_AK:
@ -1383,10 +1755,6 @@ struct xua_msg *osmo_sccp_to_xua(struct msgb *msg)
case SCCP_MSG_TYPE_EA:
case SCCP_MSG_TYPE_RSR:
case SCCP_MSG_TYPE_RSC:
case SCCP_MSG_TYPE_XUDT:
case SCCP_MSG_TYPE_XUDTS:
case SCCP_MSG_TYPE_LUDT:
case SCCP_MSG_TYPE_LUDTS:
LOGP(DLSUA, LOGL_ERROR, "Unsupported SCCP message %s\n",
osmo_sccp_msg_type_name(msg->l2h[0]));
xua_msg_free(xua);

View File

@ -1,7 +1,7 @@
/* SCCP User SAP helper functions */
/* (C) 2015-2017 by Harald Welte <laforge@gnumonks.org>
* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0+
@ -21,6 +21,7 @@
*
*/
#include <errno.h>
#include <string.h>
#include <stdbool.h>
@ -28,6 +29,7 @@
#include <arpa/inet.h>
#include <netinet/in.h>
#include <osmocom/sccp/sccp_types.h>
#include <osmocom/sigtran/sccp_sap.h>
#include <osmocom/sigtran/sccp_helpers.h>
@ -152,9 +154,21 @@ int osmo_sccp_tx_conn_req_msg(struct osmo_sccp_user *scu, uint32_t conn_id,
int osmo_sccp_tx_data(struct osmo_sccp_user *scu, uint32_t conn_id,
const uint8_t *data, unsigned int len)
{
struct msgb *msg = scu_msgb_alloc(__func__);
struct msgb *msg;
struct osmo_scu_prim *prim;
if (!osmo_sccp_conn_id_exists(scu->inst, conn_id)) {
LOGP(DLSCCP, LOGL_ERROR, "N-DATA.req TX error: unable to find connection ID (local_ref) %u\n", conn_id);
return -ENOTCONN;
}
if (len > SCCP_MAX_DATA) {
LOGP(DLSCCP, LOGL_ERROR, "N-DATA.req TX error: amount of data %u > %u - ITU-T Rec. Q.713 §4.7 limit\n",
len, SCCP_MAX_DATA);
return -EMSGSIZE;
}
msg = scu_msgb_alloc(__func__);
prim = (struct osmo_scu_prim *) msgb_put(msg, sizeof(*prim));
osmo_prim_init(&prim->oph, SCCP_SAP_USER,
OSMO_SCU_PRIM_N_DATA,
@ -179,14 +193,20 @@ int osmo_sccp_tx_data_msg(struct osmo_sccp_user *scu, uint32_t conn_id,
}
/* N-DISCONNECT.req */
int osmo_sccp_tx_disconn(struct osmo_sccp_user *scu, uint32_t conn_id,
const struct osmo_sccp_addr *resp_addr,
uint32_t cause)
int osmo_sccp_tx_disconn_data(struct osmo_sccp_user *scu, uint32_t conn_id,
const struct osmo_sccp_addr *resp_addr,
uint32_t cause, const uint8_t *data, size_t len)
{
struct msgb *msg = scu_msgb_alloc(__func__);
struct msgb *msg;
struct osmo_scu_prim *prim;
struct osmo_scu_disconn_param *param;
if (!osmo_sccp_conn_id_exists(scu->inst, conn_id)) {
LOGP(DLSCCP, LOGL_ERROR, "N-DISCONNECT.req TX error: unable to find connection ID (local_ref) %u\n", conn_id);
return -ENOTCONN;
}
msg = scu_msgb_alloc(__func__);
prim = (struct osmo_scu_prim *) msgb_put(msg, sizeof(*prim));
osmo_prim_init(&prim->oph, SCCP_SAP_USER,
OSMO_SCU_PRIM_N_DISCONNECT,
@ -199,9 +219,21 @@ int osmo_sccp_tx_disconn(struct osmo_sccp_user *scu, uint32_t conn_id,
param->conn_id = conn_id;
param->cause = cause;
if (data && len) {
msg->l2h = msgb_put(msg, len);
memcpy(msg->l2h, data, len);
}
return osmo_sccp_user_sap_down(scu, &prim->oph);
}
int osmo_sccp_tx_disconn(struct osmo_sccp_user *scu, uint32_t conn_id,
const struct osmo_sccp_addr *resp_addr,
uint32_t cause)
{
return osmo_sccp_tx_disconn_data(scu, conn_id, resp_addr, cause, NULL, 0);
}
/* N-CONNECT.resp */
int osmo_sccp_tx_conn_resp_msg(struct osmo_sccp_user *scu, uint32_t conn_id,
const struct osmo_sccp_addr *resp_addr,
@ -210,6 +242,11 @@ int osmo_sccp_tx_conn_resp_msg(struct osmo_sccp_user *scu, uint32_t conn_id,
struct osmo_scu_prim *prim;
struct osmo_scu_connect_param *param;
if (!osmo_sccp_conn_id_exists(scu->inst, conn_id)) {
LOGP(DLSCCP, LOGL_ERROR, "N-CONNECT.resp TX error: unable to find connection ID (local_ref) %u\n", conn_id);
return -ENOTCONN;
}
msg->l2h = msg->data;
prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim));
@ -289,6 +326,7 @@ char *osmo_sccp_addr_dump(const struct osmo_sccp_addr *addr)
{
static char buf[256];
bool comma = false;
char ipbuf[INET6_ADDRSTRLEN];
buf[0] = '\0';
@ -299,7 +337,9 @@ char *osmo_sccp_addr_dump(const struct osmo_sccp_addr *addr)
if (addr->presence & OSMO_SCCP_ADDR_T_SSN)
append_to_buf(buf, sizeof(buf), &comma, "SSN=%u", addr->ssn);
if (addr->presence & OSMO_SCCP_ADDR_T_IPv4)
append_to_buf(buf, sizeof(buf), &comma, "IP=%s", inet_ntoa(addr->ip.v4));
append_to_buf(buf, sizeof(buf), &comma, "IP=%s", inet_ntop(AF_INET, &addr->ip.v4, ipbuf, sizeof(ipbuf)));
else if (addr->presence & OSMO_SCCP_ADDR_T_IPv6)
append_to_buf(buf, sizeof(buf), &comma, "IP=%s", inet_ntop(AF_INET6, &addr->ip.v6, ipbuf, sizeof(ipbuf)));
if (addr->gt.gti != OSMO_SCCP_GTI_NO_GT || addr->presence & OSMO_SCCP_ADDR_T_GT)
append_to_buf(buf, sizeof(buf), &comma, "GTI=%u", addr->gt.gti);
if (addr->presence & OSMO_SCCP_ADDR_T_GT)
@ -308,31 +348,84 @@ char *osmo_sccp_addr_dump(const struct osmo_sccp_addr *addr)
return buf;
}
/* Like osmo_sccp_addr_dump() but print human readable representations instead of raw values. */
static int sccp_addr_to_str_buf(char *buf, size_t buf_len, const struct osmo_ss7_instance *ss7,
const struct osmo_sccp_addr *addr, char sep_char)
{
struct osmo_strbuf sb = { .buf = buf, .len = buf_len };
char ipbuf[INET6_ADDRSTRLEN];
OSMO_STRBUF_PRINTF(sb, "RI=%s", osmo_sccp_routing_ind_name(addr->ri));
if (addr->presence & OSMO_SCCP_ADDR_T_PC)
OSMO_STRBUF_PRINTF(sb, "%cPC=%s", sep_char, osmo_ss7_pointcode_print(ss7, addr->pc));
if (addr->presence & OSMO_SCCP_ADDR_T_SSN)
OSMO_STRBUF_PRINTF(sb, "%cSSN=%s", sep_char, osmo_sccp_ssn_name(addr->ssn));
if (addr->presence & OSMO_SCCP_ADDR_T_IPv4)
OSMO_STRBUF_PRINTF(sb, "%cIP=%s", sep_char, inet_ntop(AF_INET, &addr->ip.v4, ipbuf, sizeof(ipbuf)));
else if (addr->presence & OSMO_SCCP_ADDR_T_IPv6)
OSMO_STRBUF_PRINTF(sb, "%cIP=%s", sep_char, inet_ntop(AF_INET6, &addr->ip.v6, ipbuf, sizeof(ipbuf)));
if (addr->gt.gti != OSMO_SCCP_GTI_NO_GT || addr->presence & OSMO_SCCP_ADDR_T_GT)
OSMO_STRBUF_PRINTF(sb, "%cGTI=%s", sep_char, osmo_sccp_gti_name(addr->gt.gti));
if (addr->presence & OSMO_SCCP_ADDR_T_GT)
OSMO_STRBUF_PRINTF(sb, "%cGT=(%s)", sep_char, osmo_sccp_gt_dump(&addr->gt));
return sb.chars_needed;
}
int osmo_sccp_addr_to_str_buf(char *buf, size_t buf_len, const struct osmo_ss7_instance *ss7,
const struct osmo_sccp_addr *addr)
{
return sccp_addr_to_str_buf(buf, buf_len, ss7, addr, ',');
}
char *osmo_sccp_addr_to_str_c(void *ctx, const struct osmo_ss7_instance *ss7, const struct osmo_sccp_addr *addr)
{
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_sccp_addr_to_str_buf, ss7, addr)
}
/*! like osmo_sccp_addr_to_str_buf, but using only characters passing osmo_identifier_valid(). Useful for FSM and CTRL
* IDs.
*
* The advantage over using osmo_sccp_addr_to_str_buf() followed by osmo_identifier_sanitize_buf() is that here, the
* address elements are separated by ':', while osmo_identifier_sanitize_buf() would replace all characters with the
* same, e.g. '-'.
*/
int osmo_sccp_addr_to_id_buf(char *buf, size_t buf_len, const struct osmo_ss7_instance *ss7,
const struct osmo_sccp_addr *addr)
{
int rc = sccp_addr_to_str_buf(buf, buf_len, ss7, addr, ':');
/* inet_ntop() and osmo_sccp_gt_dump() may have written non-id chars. */
osmo_identifier_sanitize_buf(buf, "", '-');
return rc;
}
char *osmo_sccp_addr_to_id_c(void *ctx, const struct osmo_ss7_instance *ss7, const struct osmo_sccp_addr *addr)
{
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_sccp_addr_to_id_buf, ss7, addr)
}
/* Rather use osmo_sccp_addr_to_str_buf() or osmo_sccp_addr_to_str_c() to not use a static buffer */
char *osmo_sccp_addr_name(const struct osmo_ss7_instance *ss7, const struct osmo_sccp_addr *addr)
{
static char buf[256];
bool comma = false;
buf[0] = '\0';
append_to_buf(buf, sizeof(buf), &comma, "RI=%s", osmo_sccp_routing_ind_name(addr->ri));
if (addr->presence & OSMO_SCCP_ADDR_T_PC)
append_to_buf(buf, sizeof(buf), &comma, "PC=%s", osmo_ss7_pointcode_print(ss7, addr->pc));
if (addr->presence & OSMO_SCCP_ADDR_T_SSN)
append_to_buf(buf, sizeof(buf), &comma, "SSN=%s", osmo_sccp_ssn_name(addr->ssn));
if (addr->presence & OSMO_SCCP_ADDR_T_IPv4)
append_to_buf(buf, sizeof(buf), &comma, "IP=%s", inet_ntoa(addr->ip.v4));
if (addr->gt.gti != OSMO_SCCP_GTI_NO_GT || addr->presence & OSMO_SCCP_ADDR_T_GT)
append_to_buf(buf, sizeof(buf), &comma, "GTI=%s", osmo_sccp_gti_name(addr->gt.gti));
if (addr->presence & OSMO_SCCP_ADDR_T_GT)
append_to_buf(buf, sizeof(buf), &comma, "GT=(%s)", osmo_sccp_gt_dump(&addr->gt));
osmo_sccp_addr_to_str_buf(buf, sizeof(buf), ss7, addr);
return buf;
}
/* Derive ss7 from the sccp instance and call osmo_sccp_addr_name() with that.
int osmo_sccp_inst_addr_to_str_buf(char *buf, size_t buf_len, const struct osmo_sccp_instance *sccp,
const struct osmo_sccp_addr *addr)
{
return osmo_sccp_addr_to_str_buf(buf, buf_len, sccp? sccp->ss7 : NULL, addr);
}
char *osmo_sccp_inst_addr_to_str_c(void *ctx, const struct osmo_sccp_instance *sccp,
const struct osmo_sccp_addr *addr)
{
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_sccp_inst_addr_to_str_buf, sccp, addr);
}
/* Rather use osmo_sccp_inst_addr_to_str_buf() or osmo_sccp_inst_addr_to_str_c() to not use a static buffer.
* Derive ss7 from the sccp instance and call osmo_sccp_addr_name() with that.
* If sccp is passed as NULL, simply use the default point code format. */
char *osmo_sccp_inst_addr_name(const struct osmo_sccp_instance *sccp, const struct osmo_sccp_addr *addr)
{

View File

@ -2,14 +2,19 @@
#include <osmocom/core/fsm.h>
#include <osmocom/core/prim.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/linuxrbtree.h>
#include <osmocom/core/tdef.h>
#include <osmocom/sigtran/sccp_sap.h>
#include <osmocom/sigtran/osmo_ss7.h>
#include <osmocom/sigtran/protocol/mtp.h>
#define SCCP_STR "Signalling Connection Control Part\n"
/* Appendix C.4 of Q.714 */
enum osmo_sccp_timer {
OSMO_SCCP_TIMER_CONN_EST,
/* 0 kept unused on purpose since it's handled specially by osmo_fsm */
OSMO_SCCP_TIMER_CONN_EST = 1,
OSMO_SCCP_TIMER_IAS,
OSMO_SCCP_TIMER_IAR,
OSMO_SCCP_TIMER_REL,
@ -19,30 +24,21 @@ enum osmo_sccp_timer {
OSMO_SCCP_TIMER_RESET,
OSMO_SCCP_TIMER_REASSEMBLY,
/* This must remain the last item: */
OSMO_SCCP_TIMERS_COUNT
OSMO_SCCP_TIMERS_LEN
};
struct osmo_sccp_timer_val {
uint32_t s;
uint32_t us;
};
extern const struct osmo_sccp_timer_val osmo_sccp_timer_defaults[];
extern const struct osmo_tdef osmo_sccp_timer_defaults[OSMO_SCCP_TIMERS_LEN];
extern const struct value_string osmo_sccp_timer_names[];
static inline const char *osmo_sccp_timer_name(enum osmo_sccp_timer val)
{ return get_value_string(osmo_sccp_timer_names, val); }
extern const struct value_string osmo_sccp_timer_descriptions[];
static inline const char *osmo_sccp_timer_description(enum osmo_sccp_timer val)
{ return get_value_string(osmo_sccp_timer_descriptions, val); }
/* an instance of the SCCP stack */
struct osmo_sccp_instance {
/* entry in global list of ss7 instances */
struct llist_head list;
/* list of 'struct sccp_connection' in this instance */
struct llist_head connections;
/* rbtree root of 'struct sccp_connection' in this instance */
struct rb_root connections;
/* list of SCCP users in this instance */
struct llist_head users;
/* routing context to be used in all outbound messages */
@ -54,7 +50,9 @@ struct osmo_sccp_instance {
struct osmo_ss7_user ss7_user;
struct osmo_sccp_timer_val timers[OSMO_SCCP_TIMERS_COUNT];
struct osmo_tdef *tdefs;
uint32_t max_optional_data;
};
struct osmo_sccp_user {
@ -124,8 +122,18 @@ extern struct osmo_fsm sccp_scoc_fsm;
void sccp_scoc_show_connections(struct vty *vty, struct osmo_sccp_instance *inst);
const struct osmo_sccp_timer_val *osmo_sccp_timer_get(const struct osmo_sccp_instance *inst,
enum osmo_sccp_timer timer,
bool default_if_unset);
void osmo_sccp_vty_write_cs7_node(struct vty *vty, const char *indent, struct osmo_sccp_instance *inst);
/* Local Broadcast (LBCS) */
void sccp_lbcs_local_bcast_pcstate(struct osmo_sccp_instance *inst,
const struct osmo_scu_pcstate_param *pcstate);
void sccp_lbcs_local_bcast_state(struct osmo_sccp_instance *inst,
const struct osmo_scu_state_param *state);
/* SCCP Management (SCMG) */
void sccp_scmg_rx_ssn_allowed(struct osmo_sccp_instance *inst, uint32_t dpc, uint32_t ssn, uint32_t smi);
void sccp_scmg_rx_ssn_prohibited(struct osmo_sccp_instance *inst, uint32_t dpc, uint32_t ssn, uint32_t smi);
void sccp_scmg_rx_mtp_pause(struct osmo_sccp_instance *inst, uint32_t dpc);
void sccp_scmg_rx_mtp_resume(struct osmo_sccp_instance *inst, uint32_t dpc);
void sccp_scmg_rx_mtp_status(struct osmo_sccp_instance *inst, uint32_t dpc, enum mtp_unavail_cause cause);
int sccp_scmg_init(struct osmo_sccp_instance *inst);

68
src/sccp_lbcs.c Normal file
View File

@ -0,0 +1,68 @@
/* SCCP Local Broadcast (LBCS) according to ITU-T Q.713/Q.714 */
/* (C) 2021 by Harald Welte <laforge@gnumonks.org>
* All Rights reserved
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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 <string.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/fsm.h>
#include <osmocom/sigtran/sccp_sap.h>
#include <osmocom/sigtran/protocol/sua.h>
#include <osmocom/sccp/sccp_types.h>
#include "xua_internal.h"
#include "sccp_internal.h"
/* perform a "local broadcast" of a N-PCSTATE.ind */
void sccp_lbcs_local_bcast_pcstate(struct osmo_sccp_instance *inst,
const struct osmo_scu_pcstate_param *pcstate)
{
struct osmo_sccp_user *scu;
llist_for_each_entry(scu, &inst->users, list) {
struct msgb *upmsg = sccp_msgb_alloc(__func__);
struct osmo_scu_prim *prim = (struct osmo_scu_prim *) msgb_put(upmsg, sizeof(*prim));
osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_PCSTATE,
PRIM_OP_INDICATION, upmsg);
prim->u.pcstate = *pcstate;
sccp_user_prim_up(scu, prim);
}
}
/* perform a "local broadcast" of a N-STATE.ind */
void sccp_lbcs_local_bcast_state(struct osmo_sccp_instance *inst,
const struct osmo_scu_state_param *state)
{
struct osmo_sccp_user *scu;
llist_for_each_entry(scu, &inst->users, list) {
struct msgb *upmsg = sccp_msgb_alloc(__func__);
struct osmo_scu_prim *prim = (struct osmo_scu_prim *) msgb_put(upmsg, sizeof(*prim));
osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_STATE,
PRIM_OP_INDICATION, upmsg);
prim->u.state = *state;
sccp_user_prim_up(scu, prim);
}
}

View File

@ -25,7 +25,7 @@
#include <osmocom/sigtran/sccp_sap.h>
const struct value_string osmo_scu_prim_names[] = {
const struct value_string osmo_scu_prim_type_names[] = {
{ OSMO_SCU_PRIM_N_CONNECT, "N-CONNECT" },
{ OSMO_SCU_PRIM_N_DATA, "N-DATA" },
{ OSMO_SCU_PRIM_N_EXPEDITED_DATA, "N-EXPEDITED-DATA" },
@ -37,22 +37,37 @@ const struct value_string osmo_scu_prim_names[] = {
/* management */
{ OSMO_SCU_PRIM_N_COORD, "N-COORD" },
{ OSMO_SCU_PRIM_N_STATE, "N-STATE" },
{ OSMO_SCU_PRIM_N_PCSTATE, "N-PCSATE" },
{ OSMO_SCU_PRIM_N_PCSTATE, "N-PCSTATE" },
{ 0, NULL }
};
static char prim_name_buf[128];
char *osmo_scu_prim_name(struct osmo_prim_hdr *oph)
char *osmo_scu_prim_name(const struct osmo_prim_hdr *oph)
{
const char *name = get_value_string(osmo_scu_prim_names, oph->primitive);
snprintf(prim_name_buf, sizeof(prim_name_buf), "%s.%s", name,
get_value_string(osmo_prim_op_names, oph->operation));
osmo_scu_prim_hdr_name_buf(prim_name_buf, sizeof(prim_name_buf), oph);
return prim_name_buf;
}
int osmo_scu_prim_hdr_name_buf(char *buf, size_t buflen, const struct osmo_prim_hdr *oph)
{
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
if (!oph) {
OSMO_STRBUF_PRINTF(sb, "null");
return sb.chars_needed;
}
OSMO_STRBUF_PRINTF(sb, "%s.%s",
osmo_scu_prim_type_name(oph->primitive),
osmo_prim_operation_name(oph->operation));
return sb.chars_needed;
}
char *osmo_scu_prim_hdr_name_c(void *ctx, const struct osmo_prim_hdr *oph)
{
OSMO_NAME_C_IMPL(ctx, 32, "ERROR", osmo_scu_prim_hdr_name_buf, oph)
}
#include <osmocom/sigtran/sigtran_sap.h>
@ -105,6 +120,22 @@ const struct value_string osmo_sccp_gti_names[] = {
{ 0, NULL }
};
const struct value_string osmo_sccp_sp_status_names[] = {
{ OSMO_SCCP_SP_S_INACCESSIBLE, "INACCESSIBLE" },
{ OSMO_SCCP_SP_S_CONGESTED, "CONGESTED" },
{ OSMO_SCCP_SP_S_ACCESSIBLE, "ACCESSIBLE" },
{}
};
const struct value_string osmo_sccp_rem_sccp_status_names[] = {
{ OSMO_SCCP_REM_SCCP_S_AVAILABLE, "AVAILABLE" },
{ OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN, "UNAVAILABLE_UNKNOWN" },
{ OSMO_SCCP_REM_SCCP_S_UNEQUIPPED, "UNEQUIPPED" },
{ OSMO_SCCP_REM_SCCP_S_INACCESSIBLE, "INACCESSIBLE" },
{ OSMO_SCCP_REM_SCCP_S_CONGESTED, "CONGESTED" },
{}
};
const struct value_string osmo_sccp_ssn_names[] = {
{ OSMO_SCCP_SSN_MGMT, "MGMT" },
{ OSMO_SCCP_SSN_ISUP, "ISUP" },

View File

@ -36,7 +36,7 @@
* However, all SCCP features can be expressed in SUA.
*
* The code only supports Class 2. No support for Class 3 is intended,
* but patches are of course alwys welcome.
* but patches are of course always welcome.
*
* Missing other features:
* * Segmentation/Reassembly support
@ -119,7 +119,7 @@ static int xua_gen_encode_and_send(struct osmo_sccp_user *scu, uint32_t event,
* The caller is required to free oph->msg, otherwise the same as sccp_sclc_user_sap_down().
* \param[in] scu SCCP User who is sending the primitive
* \param[on] oph Osmocom primitive header of the primitive
* \returns 0 on success; negtive in case of error */
* \returns 0 on success; negative in case of error */
int sccp_sclc_user_sap_down_nofree(struct osmo_sccp_user *scu, struct osmo_prim_hdr *oph)
{
struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
@ -143,7 +143,7 @@ int sccp_sclc_user_sap_down_nofree(struct osmo_sccp_user *scu, struct osmo_prim_
* Implies a msgb_free(oph->msg), otherwise the same as sccp_sclc_user_sap_down_nofree().
* \param[in] scu SCCP User who is sending the primitive
* \param[on] oph Osmocom primitive header of the primitive
* \returns 0 on success; negtive in case of error */
* \returns 0 on success; negative in case of error */
int sccp_sclc_user_sap_down(struct osmo_sccp_user *scu, struct osmo_prim_hdr *oph)
{
struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;

318
src/sccp_scmg.c Normal file
View File

@ -0,0 +1,318 @@
/* SCCP Management (SCMG) according to ITU-T Q.713/Q.714 */
/* (C) 2021 by Harald Welte <laforge@gnumonks.org>
* All Rights reserved
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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 <string.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/fsm.h>
#include <osmocom/sigtran/sccp_sap.h>
#include <osmocom/sigtran/protocol/sua.h>
#include <osmocom/sigtran/protocol/sccp_scmg.h>
#include <osmocom/sccp/sccp_types.h>
#include "xua_internal.h"
#include "sccp_internal.h"
/* ITU-T Q.714 5.3.3 Subsystem allowed */
void sccp_scmg_rx_ssn_allowed(struct osmo_sccp_instance *inst, uint32_t dpc, uint32_t ssn, uint32_t smi)
{
struct osmo_scu_state_param state;
/* 1) Instruct the translation function to update the translation tables */
/* 2) Mark as "allowed" the status of that subsystem. */
/* 3) Initiate a local broadcast of "User-in-service" information for the allowed subsystem */
state = (struct osmo_scu_state_param) {
.affected_pc = dpc,
.affected_ssn = ssn,
.user_in_service = true,
.ssn_multiplicity_ind = smi,
};
sccp_lbcs_local_bcast_state(inst, &state);
/* 4) Discontinue the subsystem status test if such a test was in progress */
/* 5) Initiate a broadcast of Subsystem-Allowed messages to concerned signalling points. */
}
/* ITU-T Q.714 5.3.2 Subsystem prohibited */
void sccp_scmg_rx_ssn_prohibited(struct osmo_sccp_instance *inst, uint32_t dpc, uint32_t ssn, uint32_t smi)
{
struct osmo_scu_state_param state;
/* 1) instruct the translation function to update the translation tables */
/* 2) mark as "prohibited" the status of that subsystem */
/* 3) initiate a local broadcast of "User-out-of-service" information */
state = (struct osmo_scu_state_param) {
.affected_pc = dpc,
.affected_ssn = ssn,
.user_in_service = false,
.ssn_multiplicity_ind = smi,
};
sccp_lbcs_local_bcast_state(inst, &state);
/* 4) initiate the subsystem status test procedure if the prohibited subsystem is not local */
/* 5) initiate a broadcast of Subsystem-Prohibited messages to concerned SP */
/* 6) cancel "ignore subsystem status test" and the associated timer if in progress and if
* the newly prohibited subsystem resides at the local node. */
}
/*! brief MTP -> SNM (MTP-PAUSE.ind) - inability to providing MTP service Q.714 5.2.2 */
void sccp_scmg_rx_mtp_pause(struct osmo_sccp_instance *inst, uint32_t dpc)
{
struct osmo_scu_pcstate_param pcstate;
/* 1) Informs the translation function to update the translation tables. */
/* 2) SCCP management marks as "prohibited" the status of the remote signalling point, the
remote SCCP and each subsystem at the remote signalling point. */
/* 3) Discontinues all subsystem status tests (including SSN = 1) */
/* 4) local broadcast of "user-out-of-service" for each SSN at that dest
* [this would require us to track SSNs at each PC, which we don't] */
/* 5) local broadcast of "signaling point inaccessible" */
/* 6) local broadcast of "remote SCCP unavailable" */
pcstate = (struct osmo_scu_pcstate_param) {
.affected_pc = dpc,
.restricted_importance_level = 0,
.sp_status = OSMO_SCCP_SP_S_INACCESSIBLE,
.remote_sccp_status = OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN,
};
sccp_lbcs_local_bcast_pcstate(inst, &pcstate);
}
/*! brief MTP -> SNM (MTP-RESUME.ind) - ability of providing the MTP service Q.714 5.2.3 */
void sccp_scmg_rx_mtp_resume(struct osmo_sccp_instance *inst, uint32_t dpc)
{
struct osmo_scu_pcstate_param pcstate;
/* 1) Sets the congestion state of that signalling point */
/* 2) Instructs the translation function to update the translation tables. */
/* 3) Marks as "allowed" the status of that destination, and the SCCP */
/* 4) - not applicable */
/* 5) Marks as "allowed" the status of remote subsystems */
/* 6) local broadcast of "signalling point accessible" */
/* 7) local broadcast of "remote SCCP accessible" */
pcstate = (struct osmo_scu_pcstate_param) {
.affected_pc = dpc,
.restricted_importance_level = 0,
.sp_status = OSMO_SCCP_SP_S_ACCESSIBLE,
.remote_sccp_status = OSMO_SCCP_REM_SCCP_S_AVAILABLE,
};
sccp_lbcs_local_bcast_pcstate(inst, &pcstate);
/* 8) local broadcast of "user-in-service"
* [this would require us to track SSNs at each PC, which we don't] */
}
void sccp_scmg_rx_mtp_status(struct osmo_sccp_instance *inst, uint32_t dpc, enum mtp_unavail_cause cause)
{
struct osmo_scu_pcstate_param pcstate;
/* 1) Informs the translation function to update the translation tables. */
/* 2) In the case where the SCCP has received an MTP-STATUS indication primitive relating to
Mark the status of the SCCP and each SSN for the relevant destination to "prohibited"
and initiates a subsystem status test with SSN = 1. If the cause in the MTP-STATUS
indication primitive indicates "unequipped user", then no subsystem status test is
initiated. */
/* 3) Discontinues all subsystem status tests (including SSN = 1) if an MTP-STATUS
indication primitive is received with a cause of "unequipped SCCP". The SCCP
discontinues all subsystem status tests, except for SSN = 1, if an MTP-STATUS
indication primitive is received with a cause of either "unknown" or "inaccessible" */
switch (cause) {
case MTP_UNAVAIL_C_UNKNOWN:
case MTP_UNAVAIL_C_UNEQUIP_REM_USER:
case MTP_UNAVAIL_C_INACC_REM_USER:
break;
}
/* 4) local broadcast of "user-out-of-service" for each SSN at that dest
* [this would require us to track SSNs at each PC, which we don't] */
/* 6) local broadcast of "remote SCCP unavailable" */
pcstate = (struct osmo_scu_pcstate_param) {
.affected_pc = dpc,
.restricted_importance_level = 0,
.sp_status = OSMO_SCCP_SP_S_ACCESSIBLE,
.remote_sccp_status = OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN,
};
sccp_lbcs_local_bcast_pcstate(inst, &pcstate);
}
const struct value_string sccp_scmg_msgt_names[] = {
{ SCCP_SCMG_MSGT_SSA, "SSA (Subsystem Allowed)" },
{ SCCP_SCMG_MSGT_SSP, "SSP (Subsystem Prohibited)" },
{ SCCP_SCMG_MSGT_SST, "SST (Subsystem Status Test)" },
{ SCCP_SCMG_MSGT_SOR, "SOR (Subsystem Out-of-service Request)" },
{ SCCP_SCMG_MSGT_SOG, "SOG (Subsystem Out-of-service Grant)" },
{ SCCP_SCMG_MSGT_SSC, "SSC (Subsystem Congested)" },
{ 0, NULL }
};
static int sccp_scmg_tx(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *calling_addr,
const struct osmo_sccp_addr *called_addr,
uint8_t msg_type, uint8_t ssn, uint16_t pc, uint8_t smi, uint8_t *ssc_cong_lvl)
{
struct msgb *msg = sccp_msgb_alloc(__func__);
struct osmo_scu_prim *prim;
struct osmo_scu_unitdata_param *param;
struct sccp_scmg_msg *scmg;
/* fill primitive header */
prim = (struct osmo_scu_prim *) msgb_put(msg, sizeof(*prim));
param = &prim->u.unitdata;
memcpy(&param->calling_addr, calling_addr, sizeof(*calling_addr));
memcpy(&param->called_addr, called_addr, sizeof(*called_addr));
osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_REQUEST, msg);
/* Fill the actual SCMG message */
msg->l2h = msgb_put(msg, sizeof(*scmg));
scmg = (struct sccp_scmg_msg *) msg->l2h;
scmg->msg_type = msg_type;
scmg->affected_ssn = ssn;
scmg->affected_pc = pc;
scmg->smi = smi;
/* add congestion level in case of SSC message */
if (msg_type == SCCP_SCMG_MSGT_SSC) {
msgb_put(msg, 1);
OSMO_ASSERT(ssc_cong_lvl);
scmg->ssc_congestion_lvl[1] = *ssc_cong_lvl;
}
return osmo_sccp_user_sap_down(scu, &prim->oph);
}
/* Subsystem Allowed received */
static int scmg_rx_ssa(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *calling_addr,
const struct osmo_sccp_addr *called_addr, const struct sccp_scmg_msg *ssa)
{
/* Q.714 5.3.3 */
if (ssa->affected_ssn == SCCP_SSN_MANAGEMENT)
return 0;
/* if the SSN is not marked as prohibited, ignore */
/* Q.714 5.3.2.2 a) */
sccp_scmg_rx_ssn_allowed(scu->inst, ssa->affected_pc, ssa->affected_ssn, ssa->smi);
/* If the remote SCCP, at which the subsystem reported in the SSA message resides, is marked
* inaccessible, then the message is treated as an implicit indication of SCCP restart */
return 0;
}
/* Subsystem Prohibited received */
static int scmg_rx_ssp(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *calling_addr,
const struct osmo_sccp_addr *called_addr, const struct sccp_scmg_msg *ssp)
{
/* Q.714 5.3.2.2 a) */
sccp_scmg_rx_ssn_prohibited(scu->inst, ssp->affected_pc, ssp->affected_ssn, ssp->smi);
return 0;
}
/* Subsystem Test received */
static int scmg_rx_sst(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *calling_addr,
const struct osmo_sccp_addr *called_addr, const struct sccp_scmg_msg *sst)
{
/* Q.714 5.3.4.3 Actions at the receiving side (of SST) */
/* check "ignore subsystem status test" and bail out */
/* check if SSN in question is available. If yes, return SSA. If not, ignore */
scu = sccp_user_find(scu->inst, sst->affected_ssn, sst->affected_pc);
if (!scu)
return 0;
/* is subsystem available? */
if (0 /* !subsys_available(scu) */)
return 0;
return sccp_scmg_tx(scu, called_addr, calling_addr, SCCP_SCMG_MSGT_SSA,
sst->affected_ssn, sst->affected_pc, 0, NULL);
}
static int scmg_rx(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *calling_addr,
const struct osmo_sccp_addr *called_addr, const struct sccp_scmg_msg *scmg)
{
switch (scmg->msg_type) {
case SCCP_SCMG_MSGT_SSA:
return scmg_rx_ssa(scu, calling_addr, called_addr, scmg);
case SCCP_SCMG_MSGT_SSP:
return scmg_rx_ssp(scu, calling_addr, called_addr, scmg);
case SCCP_SCMG_MSGT_SST:
return scmg_rx_sst(scu, calling_addr, called_addr, scmg);
case SCCP_SCMG_MSGT_SOR:
case SCCP_SCMG_MSGT_SOG:
case SCCP_SCMG_MSGT_SSC:
default:
LOGP(DLSCCP, LOGL_NOTICE, "Rx unsupported SCCP SCMG %s, ignoring\n",
sccp_scmg_msgt_name(scmg->msg_type));
break;
}
return 0;
}
/* main entry point for SCCP user primitives from SCRC/SCOC */
static int scmg_prim_cb(struct osmo_prim_hdr *oph, void *_scu)
{
struct osmo_sccp_user *scu = _scu;
struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
struct osmo_scu_unitdata_param *param;
struct sccp_scmg_msg *scmg;
int rc = 0;
switch (OSMO_PRIM_HDR(oph)) {
case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
param = &prim->u.unitdata;
scmg = msgb_l2(oph->msg);
/* ensure minimum length based on message type */
if (msgb_l2len(oph->msg) < sizeof(*scmg)) {
rc = -1;
break;
}
if (scmg->msg_type == SCCP_SCMG_MSGT_SSC && msgb_l2len(oph->msg) < sizeof(*scmg)+1) {
rc = -1;
break;
}
/* interestingly, PC is specified to be encoded in little endian ?!? */
scmg->affected_pc = osmo_load16le(&scmg->affected_pc);
rc = scmg_rx(scu, &param->calling_addr, &param->called_addr, scmg);
break;
case OSMO_PRIM(OSMO_SCU_PRIM_N_PCSTATE, PRIM_OP_INDICATION):
LOGP(DLSCCP, LOGL_DEBUG, "Ignoring SCCP user primitive %s\n", osmo_scu_prim_name(oph));
break;
default:
LOGP(DLSCCP, LOGL_ERROR, "unsupported SCCP user primitive %s\n",
osmo_scu_prim_name(oph));
break;
}
msgb_free(oph->msg);
return rc;
}
/* register SCMG as SCCP user for SSN=1 */
int sccp_scmg_init(struct osmo_sccp_instance *inst)
{
struct osmo_sccp_user *scu;
scu = osmo_sccp_user_bind(inst, "SCCP Management", scmg_prim_cb, SCCP_SSN_MANAGEMENT);
if (!scu)
return -1;
return 0;
}

View File

@ -36,7 +36,7 @@
* However, all SCCP features can be expressed in SUA.
*
* The code only supports Class 2. No support for Class 3 is intended,
* but patches are of course alwys welcome.
* but patches are of course always welcome.
*
* Missing other features:
* * Segmentation/Reassembly support
@ -46,14 +46,17 @@
* * use of multiple Routing Contexts in SUA case
*/
#include <errno.h>
#include <string.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/linuxrbtree.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/fsm.h>
#include <osmocom/sigtran/sccp_helpers.h>
#include <osmocom/sigtran/sccp_sap.h>
#include <osmocom/sigtran/protocol/sua.h>
#include <osmocom/sccp/sccp_types.h>
@ -70,8 +73,8 @@
/* a logical connection within the SCCP instance */
struct sccp_connection {
/* part of osmo_sccp_instance.list */
struct llist_head list;
/* entry in (struct sccp_instance)->connections */
struct rb_node node;
/* which instance are we part of? */
struct osmo_sccp_instance *inst;
/* which user owns us? */
@ -80,7 +83,7 @@ struct sccp_connection {
/* remote point code */
uint32_t remote_pc;
/* local/remote addresses and identiies */
/* local/remote addresses and identities */
struct osmo_sccp_addr calling_addr;
struct osmo_sccp_addr called_addr;
/* SCCP connection identifier. Only relevant across the SCCP User SAP,
@ -90,7 +93,7 @@ struct sccp_connection {
/* SCCP Remote Connection Reference. Allocated by the remote
* SCCP stack to uniquely identify a SCCP connection on its end.
* We don't interpret it, but simply cache it here so we can use
* it whever sending data to the peer. Only relevant over the
* it whenever sending data to the peer. Only relevant over the
* wire, not to be used across the SCCP user SAP */
uint32_t remote_ref;
@ -98,6 +101,8 @@ struct sccp_connection {
uint32_t sccp_class;
uint32_t release_cause; /* WAIT_CONN_CONF */
struct msgb *opt_data_cache;
/* incoming (true) or outgoing (false) */
bool incoming;
@ -229,6 +234,31 @@ static const struct osmo_prim_event_map scu_scoc_event_map[] = {
* Timer Handling
***********************************************************************/
/* Mostly pasted from Appendix C.4 of ITU-T Q.714 (05/2001) -- some of their descriptions are quite
* unintelligible out of context, for which we have our own description here. */
const struct osmo_tdef osmo_sccp_timer_defaults[OSMO_SCCP_TIMERS_LEN] = {
{ .T = OSMO_SCCP_TIMER_CONN_EST, .default_val = 1*60, .unit = OSMO_TDEF_S,
.desc = "Waiting for connection confirm message, 1 to 2 minutes" },
{ .T = OSMO_SCCP_TIMER_IAS, .default_val = 7*60, .unit = OSMO_TDEF_S,
.desc = "Send keep-alive: on an idle connection, delay before sending an Idle Timer message, 5 to 10 minutes" }, /* RFC 3868 Ch. 8. */
{ .T = OSMO_SCCP_TIMER_IAR, .default_val = 15*60, .unit = OSMO_TDEF_S,
.desc = "Receive keep-alive: on an idle connection, delay until considering a connection as stale, 11 to 21 minutes" }, /* RFC 3868 Ch. 8. */
{ .T = OSMO_SCCP_TIMER_REL, .default_val = 10, .unit = OSMO_TDEF_S,
.desc = "Waiting for release complete message, 10 to 20 seconds" },
{ .T = OSMO_SCCP_TIMER_REPEAT_REL, .default_val = 10, .unit = OSMO_TDEF_S,
.desc = "Waiting for release complete message; or to repeat sending released message after the initial expiry, 10 to 20 seconds" },
{ .T = OSMO_SCCP_TIMER_INT, .default_val = 1*60, .unit = OSMO_TDEF_S,
.desc = "Waiting for release complete message; or to release connection resources, freeze the LRN and "
"alert a maintenance function after the initial expiry, extending to 1 minute" },
{ .T = OSMO_SCCP_TIMER_GUARD, .default_val = 23*60, .unit = OSMO_TDEF_S,
.desc = "Waiting to resume normal procedure for temporary connection sections during the restart procedure, 23 to 25 minutes" },
{ .T = OSMO_SCCP_TIMER_RESET, .default_val = 10, .unit = OSMO_TDEF_S,
.desc = "Waiting to release temporary connection section or alert maintenance function after reset request message is sent, 10 to 20 seconds" },
{ .T = OSMO_SCCP_TIMER_REASSEMBLY, .default_val = 10, .unit = OSMO_TDEF_S,
.desc = "Waiting to receive all the segments of the remaining segments, single segmented message after receiving the first segment, 10 to 20 seconds" },
{}
};
/* Appendix C.4 of ITU-T Q.714 */
const struct value_string osmo_sccp_timer_names[] = {
{ OSMO_SCCP_TIMER_CONN_EST, "conn_est" },
@ -243,93 +273,16 @@ const struct value_string osmo_sccp_timer_names[] = {
{}
};
/* Mostly pasted from Appendix C.4 of ITU-T Q.714 (05/2001) -- some of their descriptions are quite
* unintelligible out of context, for which we have our own description here. */
const struct value_string osmo_sccp_timer_descriptions[] = {
{ OSMO_SCCP_TIMER_CONN_EST,
"Waiting for connection confirm message, 1 to 2 minutes" },
{ OSMO_SCCP_TIMER_IAS,
"Send keep-alive: on an idle connection, delay before sending an Idle Timer message,"
" 5 to 10 minutes" },
{ OSMO_SCCP_TIMER_IAR,
"Receive keep-alive: on an idle connection, delay until considering a connection as stale,"
" 11 to 21 minutes" },
{ OSMO_SCCP_TIMER_REL,
"Waiting for release complete message, 10 to 20 seconds" },
{ OSMO_SCCP_TIMER_REPEAT_REL,
"Waiting for release complete message; or to repeat sending released message after the initial"
" expiry, 10 to 20 seconds" },
{ OSMO_SCCP_TIMER_INT,
"Waiting for release complete message; or to release connection resources, freeze the LRN and"
" alert a maintenance function after the initial expiry, extending to 1 minute" },
{ OSMO_SCCP_TIMER_GUARD,
"Waiting to resume normal procedure for temporary connection sections during the restart"
" procedure, 23 to 25 minutes" },
{ OSMO_SCCP_TIMER_RESET,
"Waiting to release temporary connection section or alert maintenance function after reset"
" request message is sent, 10 to 20 seconds" },
{ OSMO_SCCP_TIMER_REASSEMBLY,
"Waiting to receive all the segments of the remaining segments, single segmented message after"
" receiving the first segment, 10 to 20 seconds" },
{}
};
/* Appendix C.4 of ITU-T Q.714 */
const struct osmo_sccp_timer_val osmo_sccp_timer_defaults[] = {
[OSMO_SCCP_TIMER_CONN_EST] = { .s = 1 * 60, },
[OSMO_SCCP_TIMER_IAS] = { .s = 7 * 60, }, /* RFC 3868 Ch. 8. */
[OSMO_SCCP_TIMER_IAR] = { .s = 15 * 60, }, /* RFC 3868 Ch. 8. */
[OSMO_SCCP_TIMER_REL] = { .s = 10, },
[OSMO_SCCP_TIMER_REPEAT_REL] = { .s = 10, },
[OSMO_SCCP_TIMER_INT] = { .s = 1 * 60, },
[OSMO_SCCP_TIMER_GUARD] = { .s = 23 * 60, },
[OSMO_SCCP_TIMER_RESET] = { .s = 10, },
[OSMO_SCCP_TIMER_REASSEMBLY] = { .s = 10, },
};
osmo_static_assert(ARRAY_SIZE(osmo_sccp_timer_defaults) == OSMO_SCCP_TIMERS_COUNT,
osmo_static_assert(ARRAY_SIZE(osmo_sccp_timer_defaults) == (OSMO_SCCP_TIMERS_LEN) &&
ARRAY_SIZE(osmo_sccp_timer_names) == (OSMO_SCCP_TIMERS_LEN),
assert_osmo_sccp_timers_count);
/* Look up an SCCP timer value as configured in the osmo_ss7_instance.
* If no user defined value is set, return the global default from osmo_sccp_timer_defaults instead.
* However, if default_if_unset is passed false, return NULL in case there is no user defined setting, or
* in case it is identical to the global default. */
const struct osmo_sccp_timer_val *osmo_sccp_timer_get(const struct osmo_sccp_instance *inst,
enum osmo_sccp_timer timer,
bool default_if_unset)
{
const struct osmo_sccp_timer_val *val;
const struct osmo_sccp_timer_val *def;
OSMO_ASSERT(timer >= 0
&& timer < ARRAY_SIZE(inst->timers)
&& timer < ARRAY_SIZE(osmo_sccp_timer_defaults));
val = &inst->timers[timer];
def = &osmo_sccp_timer_defaults[timer];
/* Assert that all timer definitions have a sane global default */
OSMO_ASSERT(def->s || def->us);
if (val->s || val->us) {
if (!default_if_unset && val->s == def->s && val->us == def->us)
return NULL;
return val;
}
if (!default_if_unset)
return NULL;
/* If unset, use the global default. */
return def;
}
static void sccp_timer_schedule(const struct sccp_connection *conn,
struct osmo_timer_list *timer,
enum osmo_sccp_timer timer_name)
{
const struct osmo_sccp_timer_val *val = osmo_sccp_timer_get(conn->inst, timer_name, true);
osmo_timer_schedule(timer, val->s, val->us);
const unsigned long val_sec = osmo_tdef_get(conn->inst->tdefs, timer_name, OSMO_TDEF_S, -1);
osmo_timer_schedule(timer, val_sec, 0);
}
/* T(ias) has expired, send a COIT message to the peer */
@ -445,17 +398,55 @@ static void conn_stop_connect_timer(struct sccp_connection *conn)
static void conn_destroy(struct sccp_connection *conn);
static struct sccp_connection *conn_find_by_id(struct osmo_sccp_instance *inst, uint32_t id)
static struct sccp_connection *conn_find_by_id(const struct osmo_sccp_instance *inst, uint32_t id)
{
struct sccp_connection *conn;
const struct rb_node *node = inst->connections.rb_node;
llist_for_each_entry(conn, &inst->connections, list) {
if (conn->conn_id == id)
while (node) {
conn = container_of(node, struct sccp_connection, node);
if (id < conn->conn_id)
node = node->rb_left;
else if (id > conn->conn_id)
node = node->rb_right;
else
return conn;
}
return NULL;
}
static int conn_add_node(struct osmo_sccp_instance *inst, struct sccp_connection *conn)
{
struct rb_node **n = &(inst->connections.rb_node);
struct rb_node *parent = NULL;
while (*n) {
struct sccp_connection *it;
it = container_of(*n, struct sccp_connection, node);
parent = *n;
if (conn->conn_id < it->conn_id) {
n = &((*n)->rb_left);
} else if (conn->conn_id > it->conn_id) {
n = &((*n)->rb_right);
} else {
LOGP(DLSCCP, LOGL_ERROR,
"Trying to reserve already reserved conn_id %u\n", conn->conn_id);
return -EEXIST;
}
}
rb_link_node(&conn->node, parent, n);
rb_insert_color(&conn->node, &inst->connections);
return 0;
}
bool osmo_sccp_conn_id_exists(const struct osmo_sccp_instance *inst, uint32_t id)
{
return conn_find_by_id(inst, id) ? true : false;
}
#define INIT_TIMER(x, fn, priv) do { (x)->cb = fn; (x)->data = priv; } while (0)
/* allocate + init a SCCP Connection with given ID */
@ -468,7 +459,10 @@ static struct sccp_connection *conn_create_id(struct osmo_sccp_user *user, uint3
conn->inst = user->inst;
conn->user = user;
llist_add_tail(&conn->list, &user->inst->connections);
if (conn_add_node(user->inst, conn) < 0) {
talloc_free(conn);
return NULL;
}
INIT_TIMER(&conn->t_conn, conn_tmr_cb, conn);
INIT_TIMER(&conn->t_ias, tx_inact_tmr_cb, conn);
@ -485,7 +479,7 @@ static struct sccp_connection *conn_create_id(struct osmo_sccp_user *user, uint3
conn->fi = osmo_fsm_inst_alloc(&sccp_scoc_fsm, conn, conn,
LOGL_DEBUG, name);
if (!conn->fi) {
llist_del(&conn->list);
rb_erase(&conn->node, &user->inst->connections);
talloc_free(conn);
return NULL;
}
@ -493,26 +487,66 @@ static struct sccp_connection *conn_create_id(struct osmo_sccp_user *user, uint3
return conn;
}
/* Return an unused SCCP connection ID.
* Callers should check the returned value: on negative return value, there are no unused IDs available.
* \param[in] sccp The SCCP instance to determine a new connection ID for.
* \return unused ID on success (range [0x0, 0x00fffffe]) or negative on elapsed max_attempts without an unused id (<0).
*/
int osmo_sccp_instance_next_conn_id(struct osmo_sccp_instance *sccp)
{
int max_attempts = 0x00FFFFFE;
/* SUA: RFC3868 sec 3.10.4:
* The source reference number is a 4 octet long integer.
* This is allocated by the source SUA instance.
* M3UA/SCCP: ITU-T Q.713 sec 3.3:
* The "source local reference" parameter field is a three-octet field containing a
* reference number which is generated and used by the local node to identify the
* connection section after the connection section is set up.
* The coding "all ones" is reserved for future use.
* Hence, as we currently use the connection ID also as local reference,
* let's simply use 24 bit ids to fit all link types (excluding 0x00ffffff).
*/
while (OSMO_LIKELY((max_attempts--) > 0)) {
/* Optimized modulo operation (% 0x00FFFFFE) using bitwise AND plus CMP: */
sccp->next_id = (sccp->next_id + 1) & 0x00FFFFFF;
if (OSMO_UNLIKELY(sccp->next_id == 0x00FFFFFF))
sccp->next_id = 0;
if (!conn_find_by_id(sccp, sccp->next_id))
return sccp->next_id;
}
return -1;
}
/* Search for next free connection ID and allocate conn */
static struct sccp_connection *conn_create(struct osmo_sccp_user *user)
{
uint32_t conn_id;
do {
conn_id = user->inst->next_id++;
} while (conn_find_by_id(user->inst, conn_id));
int conn_id = osmo_sccp_instance_next_conn_id(user->inst);
if (conn_id < 0)
return NULL;
return conn_create_id(user, conn_id);
}
static void conn_opt_data_clear_cache(struct sccp_connection *conn)
{
if (conn->opt_data_cache) {
msgb_free(conn->opt_data_cache);
conn->opt_data_cache = NULL;
}
}
/* destroy a SCCP connection state, releasing all timers, terminating
* FSM and releasing associated memory */
static void conn_destroy(struct sccp_connection *conn)
{
conn_opt_data_clear_cache(conn);
conn_stop_connect_timer(conn);
conn_stop_inact_timers(conn);
conn_stop_release_timers(conn);
llist_del(&conn->list);
rb_erase(&conn->node, &conn->inst->connections);
osmo_fsm_inst_term(conn->fi, OSMO_FSM_TERM_REQUEST, NULL);
@ -566,11 +600,115 @@ static int xua_gen_relre_and_send(struct sccp_connection *conn, uint32_t cause,
return 0;
}
/* Send cached optional data (if any) from expected message type and clear cache */
static void xua_opt_data_send_cache(struct sccp_connection *conn, int exp_type, uint8_t msg_class)
{
const struct xua_dialect *dialect = &xua_dialect_sua;
const struct xua_msg_class *xmc = dialect->class[msg_class];
if (!conn->opt_data_cache)
return;
if (conn->opt_data_cache->cb[0] != exp_type) {
/* Caller (from the FSM) knows what was the source of Optional Data we're sending.
* Compare this information with source of Optional Data recorded while caching
* to make sure we're on the same page.
*/
LOGP(DLSCCP, LOGL_ERROR, "unexpected message type %s != cache source %s\n",
xua_class_msg_name(xmc, exp_type), xua_class_msg_name(xmc, conn->opt_data_cache->cb[0]));
} else {
osmo_sccp_tx_data(conn->user, conn->conn_id, msgb_data(conn->opt_data_cache), msgb_length(conn->opt_data_cache));
}
conn_opt_data_clear_cache(conn);
}
/* Check if optional data should be dropped, log given error message if so */
static bool xua_drop_data_check_drop(const struct osmo_scu_prim *prim, unsigned lim, const char *message)
{
if (msgb_l2len(prim->oph.msg) > lim) {
LOGP(DLSCCP, LOGL_ERROR,
"%s: dropping optional data with length %u > %u - %s\n",
osmo_scu_prim_name(&prim->oph), msgb_l2len(prim->oph.msg), lim, message);
return true;
}
return false;
}
/* Cache the optional data (if necessary)
* returns true if Optional Data should be kept while encoding the message */
static bool xua_opt_data_cache_keep(struct sccp_connection *conn, const struct osmo_scu_prim *prim, int msg_type)
{
uint8_t *buf;
uint32_t max_optional_data = conn->inst->max_optional_data;
if (xua_drop_data_check_drop(prim, SCCP_MAX_DATA, "cache overrun"))
return false;
if (msgb_l2len(prim->oph.msg) > max_optional_data) {
if (conn->opt_data_cache) {
/* Caching optional data, but there already is optional data occupying the cache: */
LOGP(DLSCCP, LOGL_ERROR, "replacing unsent %u bytes of optional data cache with %s optional data\n",
msgb_length(conn->opt_data_cache), osmo_scu_prim_name(&prim->oph));
msgb_trim(conn->opt_data_cache, 0);
} else {
conn->opt_data_cache = msgb_alloc_c(conn, SCCP_MAX_DATA, "SCCP optional data cache for CR/CC/RLSD");
}
buf = msgb_put(conn->opt_data_cache, msgb_l2len(prim->oph.msg));
memcpy(buf, msgb_l2(prim->oph.msg), msgb_l2len(prim->oph.msg));
conn->opt_data_cache->cb[0] = msg_type;
return false;
}
return true;
}
/* Check optional Data size limit, cache if necessary, return indication whether original opt data should be sent */
static bool xua_opt_data_length_lim(struct sccp_connection *conn, const struct osmo_scu_prim *prim, int msg_type)
{
uint32_t max_optional_data = conn->inst->max_optional_data;
if (!(prim && msgb_l2(prim->oph.msg) && msgb_l2len(prim->oph.msg)))
return false;
switch (msg_type) {
case SUA_CO_CORE: /* §4.2 Connection request (CR) */
case SUA_CO_COAK: /* §4.3 Connection confirm (CC) */
return xua_opt_data_cache_keep(conn, prim, msg_type);
case SUA_CO_COREF: /* §4.4 Connection refused (CREF) */
if (xua_drop_data_check_drop(prim, max_optional_data, "over ITU-T Rec. Q.713 §4.4 limit")) {
/* From the state diagrams in ITU-T Rec Q.714, there's no way to send DT1 neither before nor after CREF
* at this point, so the only option we have is to drop optional data:
* see Figure C.3 / Q.714 (sheet 2 of 6) */
return false;
}
break;
case SUA_CO_RELRE: /* §4.5 Released (RLSD) */
if (msgb_l2len(prim->oph.msg) > max_optional_data) {
if (xua_drop_data_check_drop(prim, SCCP_MAX_DATA, "protocol error"))
return false;
/* There's no need to cache the optional data since the connection is still active at this point:
* Send the Optional Data in a DT1 ahead of the RLSD, because it is too large to be sent in one message.
*/
osmo_sccp_tx_data(conn->user, conn->conn_id, msgb_l2(prim->oph.msg), msgb_l2len(prim->oph.msg));
return false;
}
break;
default:
return true;
}
return true;
}
/* generate a 'struct xua_msg' of requested type from connection +
* primitive data */
static struct xua_msg *xua_gen_msg_co(struct sccp_connection *conn, uint32_t event,
struct osmo_scu_prim *prim, int msg_type)
const struct osmo_scu_prim *prim, int msg_type)
{
bool encode_opt_data = xua_opt_data_length_lim(conn, prim, msg_type);
struct xua_msg *xua = xua_msg_alloc();
if (!xua)
@ -587,10 +725,11 @@ static struct xua_msg *xua_gen_msg_co(struct sccp_connection *conn, uint32_t eve
/* optional: sequence number (class 3 only) */
if (conn->calling_addr.presence)
xua_msg_add_sccp_addr(xua, SUA_IEI_SRC_ADDR, &conn->calling_addr);
/* optional: hop count; importance; priority; credit */
if (prim && msgb_l2(prim->oph.msg) && msgb_l2len(prim->oph.msg))
xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg),
msgb_l2(prim->oph.msg));
/* optional: data */
if (encode_opt_data)
xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg), msgb_l2(prim->oph.msg));
/* optional: hop count */
/* optional: importance */
break;
case SUA_CO_COAK: /* Connect Acknowledge == SCCP CC */
xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_COAK);
@ -608,11 +747,12 @@ static struct xua_msg *xua_gen_msg_co(struct sccp_connection *conn, uint32_t eve
* parameter */
if (conn->calling_addr.presence)
xua_msg_add_sccp_addr(xua, SUA_IEI_DEST_ADDR, &conn->calling_addr);
if (prim && msgb_l2(prim->oph.msg) && msgb_l2len(prim->oph.msg))
xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg),
msgb_l2(prim->oph.msg));
/* optional: data */
if (encode_opt_data)
xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg), msgb_l2(prim->oph.msg));
/* optional: importance */
break;
case SUA_CO_RELRE: /* Release Request == SCCP REL */
case SUA_CO_RELRE: /* Release Request == SCCP RLSD */
if (!prim)
goto prim_needed;
xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_RELRE);
@ -620,17 +760,16 @@ static struct xua_msg *xua_gen_msg_co(struct sccp_connection *conn, uint32_t eve
xua_msg_add_u32(xua, SUA_IEI_DEST_REF, conn->remote_ref);
xua_msg_add_u32(xua, SUA_IEI_SRC_REF, conn->conn_id);
xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_RELEASE | prim->u.disconnect.cause);
/* optional: data */
if (encode_opt_data)
xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg), msgb_l2(prim->oph.msg));
/* optional: importance */
if (prim && msgb_l2(prim->oph.msg) && msgb_l2len(prim->oph.msg))
xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg),
msgb_l2(prim->oph.msg));
break;
case SUA_CO_RELCO: /* Release Confirm == SCCP RLSD */
case SUA_CO_RELCO: /* Release Confirm == SCCP RLC */
xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_RELCO);
xua_msg_add_u32(xua, SUA_IEI_ROUTE_CTX, conn->inst->route_ctx);
xua_msg_add_u32(xua, SUA_IEI_DEST_REF, conn->remote_ref);
xua_msg_add_u32(xua, SUA_IEI_SRC_REF, conn->conn_id);
/* optional: importance */
break;
case SUA_CO_CODT: /* Connection Oriented Data Transfer == SCCP DT1 */
if (!prim)
@ -663,11 +802,10 @@ static struct xua_msg *xua_gen_msg_co(struct sccp_connection *conn, uint32_t eve
/* conditional: dest addr */
if (conn->calling_addr.presence)
xua_msg_add_sccp_addr(xua, SUA_IEI_DEST_ADDR, &conn->calling_addr);
/* optional: importance */
/* optional: data */
if (prim && msgb_l2(prim->oph.msg) && msgb_l2len(prim->oph.msg))
xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg),
msgb_l2(prim->oph.msg));
if (encode_opt_data)
xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg), msgb_l2(prim->oph.msg));
/* optional: importance */
break;
/* FIXME */
default:
@ -684,15 +822,17 @@ prim_needed:
return NULL;
}
/* generate xua_msg, encode it and send it to SCRC */
/* generate xua_msg, encode it and send it to SCRC
* returns 0 on success, negative on error
*/
static int xua_gen_encode_and_send(struct sccp_connection *conn, uint32_t event,
struct osmo_scu_prim *prim, int msg_type)
const struct osmo_scu_prim *prim, int msg_type)
{
struct xua_msg *xua;
xua = xua_gen_msg_co(conn, event, prim, msg_type);
if (!xua)
return -1;
return -ENOMEM;
/* amend this with point code information; Many CO msgs
* includes neither called nor calling party address! */
@ -804,7 +944,7 @@ static void scu_gen_encode_and_send(struct sccp_connection *conn, uint32_t event
/***********************************************************************
* Actual SCCP Connection Oriented Control (SCOC) Finite Stte Machine
* Actual SCCP Connection Oriented Control (SCOC) Finite State Machine
***********************************************************************/
/* Figure C.2/Q.714 (sheet 1 of 7) and C.3/Q.714 (sheet 1 of 6) */
@ -814,6 +954,7 @@ static void scoc_fsm_idle(struct osmo_fsm_inst *fi, uint32_t event, void *data)
struct osmo_scu_prim *prim = NULL;
struct osmo_scu_connect_param *uconp;
struct xua_msg *xua = NULL;
int rc;
switch (event) {
case SCOC_E_SCU_N_CONN_REQ:
@ -824,10 +965,14 @@ static void scoc_fsm_idle(struct osmo_fsm_inst *fi, uint32_t event, void *data)
conn->calling_addr = uconp->calling_addr;
conn->sccp_class = uconp->sccp_class;
/* generate + send CR PDU to SCRC */
xua_gen_encode_and_send(conn, event, prim, SUA_CO_CORE);
/* start connection timer */
conn_start_connect_timer(conn);
osmo_fsm_inst_state_chg(fi, S_CONN_PEND_OUT, 0, 0);
rc = xua_gen_encode_and_send(conn, event, prim, SUA_CO_CORE);
if (rc < 0)
LOGPFSML(fi, LOGL_ERROR, "Failed to initiate connection: %s\n", strerror(-rc));
else {
/* start connection timer */
conn_start_connect_timer(conn);
osmo_fsm_inst_state_chg(fi, S_CONN_PEND_OUT, 0, 0);
}
break;
#if 0
case SCOC_E_SCU_N_TYPE1_REQ:
@ -898,11 +1043,13 @@ static void scoc_fsm_conn_pend_in(struct osmo_fsm_inst *fi, uint32_t event, void
/* start inactivity timers */
conn_start_inact_timers(conn);
osmo_fsm_inst_state_chg(fi, S_ACTIVE, 0, 0);
xua_opt_data_send_cache(conn, SUA_CO_COAK, SUA_MSGC_CO);
break;
case SCOC_E_SCU_N_DISC_REQ:
prim = data;
/* release resources: implicit */
xua_gen_encode_and_send(conn, event, prim, SUA_CO_COREF);
/* N. B: we've ignored CREF sending errors as there's no recovery option anyway */
osmo_fsm_inst_state_chg(fi, S_IDLE, 0, 0);
break;
}
@ -979,6 +1126,11 @@ static void scoc_fsm_conn_pend_out(struct osmo_fsm_inst *fi, uint32_t event, voi
conn->remote_pc = xua->mtp.opc;
osmo_fsm_inst_state_chg(fi, S_ACTIVE, 0, 0);
/* If CR which was used to initiate this connection had excessive Optional Data which we had to cache,
* now is the time to send it: the connection is already active but we hadn't notified upper layers about it
* so we have the connection all to ourselves and can use it to transmit "leftover" data via DT1 */
xua_opt_data_send_cache(conn, SUA_CO_CORE, xua->hdr.msg_class);
/* N-CONNECT.conf to user */
scu_gen_encode_and_send(conn, event, xua, OSMO_SCU_PRIM_N_CONNECT,
PRIM_OP_CONFIRM);
@ -1008,6 +1160,13 @@ static void scoc_fsm_wait_conn_conf(struct osmo_fsm_inst *fi, uint32_t event, vo
conn_stop_connect_timer(conn);
/* associate rem ref to conn */
conn->remote_ref = xua_msg_get_u32(xua, SUA_IEI_SRC_REF);
/* 3.1.4.2 The node sending the CC message (identified
* by the parameter OPC contained in the
* MTP-TRANSFER.indication primitive which conveyed the
* CC message [plus the MTP-SAP instance]) is associated
* with the connection section. */
conn->remote_pc = xua->mtp.opc;
/* released to SCRC */
xua_gen_relre_and_send(conn, conn->release_cause, NULL);
/* start rel timer */
@ -1283,6 +1442,8 @@ static const struct osmo_fsm_state sccp_scoc_states[] = {
S(SCOC_E_CONN_TMR_EXP) |
S(SCOC_E_RCOC_CREF_IND) |
S(SCOC_E_RCOC_ROUT_FAIL_IND),
.out_state_mask = S(S_IDLE) |
S(S_DISCONN_PEND),
},
};
@ -1518,7 +1679,7 @@ static void tx_rlsd_from_xua_twoway(struct sccp_connection *conn,
xua_msg_free(xua);
}
/* process received message for unasigned local reference */
/* process received message for unassigned local reference */
static void sccp_scoc_rx_unass_local_ref(struct osmo_sccp_instance *inst,
struct xua_msg *xua)
{
@ -1589,11 +1750,12 @@ static void sccp_scoc_rx_inval_opc(struct sccp_connection *conn,
struct xua_msg *xua)
{
LOGP(DLSCCP, LOGL_NOTICE,
"Received message for opc=%u=%s on conn with mismatching remote pc=%u=%s\n",
"Received message %s for opc=%u=%s on conn with mismatching remote pc=%u=%s\n",
xua_hdr_dump(xua, &xua_dialect_sua),
xua->mtp.opc, osmo_ss7_pointcode_print(conn->inst->ss7, xua->mtp.opc),
conn->remote_pc, osmo_ss7_pointcode_print2(conn->inst->ss7, conn->remote_pc));
/* we have received a message with invalid origin PC and thus
* apply the action indiacted in Table B.2/Q.714 */
* apply the action indicated in Table B.2/Q.714 */
switch (xua->hdr.msg_type) {
case SUA_CO_RELRE: /* RLSD */
case SUA_CO_RESRE: /* RSR */
@ -1628,7 +1790,7 @@ void sccp_scoc_rx_from_scrc(struct osmo_sccp_instance *inst,
/* we basically try to convert the SUA message into an event,
* and then dispatch the event to the connection-specific FSM.
* If it is a CORE (Connect REquest), we create the connection
* (and imlpicitly its FSM) first */
* (and implicitly its FSM) first */
if (xua->hdr.msg_type == SUA_CO_CORE) {
scu = sccp_find_user(inst, xua);
@ -1785,10 +1947,12 @@ int osmo_sccp_user_sap_down(struct osmo_sccp_user *scu, struct osmo_prim_hdr *op
void sccp_scoc_flush_connections(struct osmo_sccp_instance *inst)
{
struct sccp_connection *conn, *conn2;
llist_for_each_entry_safe(conn, conn2, &inst->connections, list)
struct rb_node *node;
while ((node = rb_first(&inst->connections))) {
struct sccp_connection *conn = container_of(node, struct sccp_connection, node);
conn_destroy(conn);
}
}
#include <osmocom/vty/vty.h>
@ -1821,11 +1985,14 @@ static void vty_show_connection(struct vty *vty, struct sccp_connection *conn)
void sccp_scoc_show_connections(struct vty *vty, struct osmo_sccp_instance *inst)
{
struct sccp_connection *conn;
struct rb_node *node;
vty_out(vty, "I Local Conn. Remote %s", VTY_NEWLINE);
vty_out(vty, "O Ref SSN PC State Ref SSN PC %s", VTY_NEWLINE);
vty_out(vty, "- ------ --- ------- ---------------- ------ --- -------%s", VTY_NEWLINE);
llist_for_each_entry(conn, &inst->connections, list)
for (node = rb_first(&inst->connections); node; node = rb_next(node)) {
conn = container_of(node, struct sccp_connection, node);
vty_show_connection(vty, conn);
}
}

View File

@ -85,7 +85,7 @@ static int sua2sccp_tx_m3ua(struct osmo_sccp_instance *inst,
return -1;
}
/* 2) wrap into MTP-TRANSFER.req primtiive */
/* 2) wrap into MTP-TRANSFER.req primitive */
msg->l2h = msg->data;
omp = (struct osmo_mtp_prim *) msgb_push(msg, sizeof(*omp));
osmo_prim_init(&omp->oph, MTP_SAP_USER,
@ -109,7 +109,7 @@ static int sua2sccp_tx_m3ua(struct osmo_sccp_instance *inst,
return osmo_ss7_user_mtp_xfer_req(s7i, omp);
}
/* Gererate MTP-TRANSFER.req from xUA message */
/* Generate MTP-TRANSFER.req from xUA message */
static int gen_mtp_transfer_req_xua(struct osmo_sccp_instance *inst,
struct xua_msg *xua,
const struct osmo_sccp_addr *called)
@ -230,7 +230,7 @@ static int scrc_node_7(struct osmo_sccp_instance *inst,
/* Connection Oriented? */
if (sua_is_connectionless(xua)) {
/* TODO: Perform Capability Test */
/* TODO: Canges Needed? */
/* TODO: Changes Needed? */
if (0) {
/* Changes Needed -> SCLC */
return 0;
@ -440,6 +440,26 @@ int sccp_scrc_rx_sclc_msg(struct osmo_sccp_instance *inst,
return scrc_local_out_common(inst, xua, &called);
}
/* ensure the CallingParty address doesn't just contain SSN, but at least SSN+PC */
static void ensure_opc_in_calling_ssn(struct osmo_sccp_instance *inst,
struct xua_msg *xua)
{
struct osmo_sccp_addr calling;
sua_addr_parse(&calling, xua, SUA_IEI_SRC_ADDR);
/* if we route on SSN and only have a SSN in the address... */
if (calling.ri == OSMO_SCCP_RI_SSN_PC &&
calling.presence == OSMO_SCCP_ADDR_T_SSN) {
/* add the M3UA OPC to the address to ensure that the recipient
* can actually respond back to the source */
calling.presence |= OSMO_SCCP_ADDR_T_PC;
calling.pc = xua->mtp.opc;
xua_msg_free_tag(xua, SUA_IEI_SRC_ADDR);
xua_msg_add_sccp_addr(xua, SUA_IEI_SRC_ADDR, &calling);
}
}
/* Figure C.1/Q.714 Sheet 1 of 12, after we converted the
* MTP-TRANSFER.ind to SUA. */
int scrc_rx_mtp_xfer_ind_xua(struct osmo_sccp_instance *inst,
@ -461,6 +481,9 @@ int scrc_rx_mtp_xfer_ind_xua(struct osmo_sccp_instance *inst,
}
/* We only treat connectionless and CR below */
/* ensure we have at least OPC+SSN and not just SSN in CallingParty (OS#5146) */
ensure_opc_in_calling_ssn(inst, xua);
sua_addr_parse(&called, xua, SUA_IEI_DEST_ADDR);
/* Route on GT? */

View File

@ -35,9 +35,11 @@
#include <osmocom/sigtran/mtp_sap.h>
#include <osmocom/sigtran/protocol/mtp.h>
#include <osmocom/sigtran/sccp_helpers.h>
#include <osmocom/sccp/sccp_types.h>
#include "sccp_internal.h"
#include "xua_internal.h"
#include "ss7_internal.h"
/*! \brief Find a SCCP User registered for given PC+SSN or SSN only
* First search all users with a valid PC for a full PC+SSN match.
@ -220,6 +222,7 @@ struct osmo_sccp_instance *
osmo_sccp_instance_create(struct osmo_ss7_instance *ss7, void *priv)
{
struct osmo_sccp_instance *inst;
int rc;
inst = talloc_zero(ss7, struct osmo_sccp_instance);
if (!inst)
@ -227,13 +230,23 @@ osmo_sccp_instance_create(struct osmo_ss7_instance *ss7, void *priv)
inst->ss7 = ss7;
inst->priv = priv;
INIT_LLIST_HEAD(&inst->connections);
INIT_LLIST_HEAD(&inst->users);
inst->ss7_user.inst = ss7;
inst->ss7_user.name = "SCCP";
inst->ss7_user.prim_cb = mtp_user_prim_cb;
inst->ss7_user.priv = inst;
inst->max_optional_data = SCCP_MAX_OPTIONAL_DATA;
inst->tdefs = talloc_memdup(inst, osmo_sccp_timer_defaults,
sizeof(osmo_sccp_timer_defaults));
osmo_tdefs_reset(inst->tdefs);
rc = sccp_scmg_init(inst);
if (rc < 0) {
talloc_free(inst);
return NULL;
}
osmo_ss7_user_register(ss7, MTP_SI_SCCP, &inst->ss7_user);
@ -257,6 +270,16 @@ void osmo_sccp_instance_destroy(struct osmo_sccp_instance *inst)
talloc_free(inst);
}
void osmo_sccp_set_priv(struct osmo_sccp_instance *sccp, void *priv)
{
sccp->priv = priv;
}
void *osmo_sccp_get_priv(struct osmo_sccp_instance *sccp)
{
return sccp->priv;
}
/*! \brief derive a basic local SCCP-Address from a given SCCP instance.
* \param[out] dest_addr pointer to output address memory
* \param[in] inst SCCP instance
@ -342,8 +365,8 @@ int osmo_sccp_gt_cmp(const struct osmo_sccp_gt *a, const struct osmo_sccp_gt *b)
* The SCCP addresses' Routing Indicator is not compared, see osmo_sccp_addr_ri_cmp().
* \param[in] a left side.
* \param[in] b right side.
* \param[in] presence_criteria A bitmask of OSMO_SCCP_ADDR_T_* values, or 0xffffffff to compare all parts, except the
* routing indicator.
* \param[in] presence_criteria A bitmask of OSMO_SCCP_ADDR_T_* values, or OSMO_SCCP_ADDR_T_MASK to compare all parts,
* except the routing indicator.
* \return -1 if a < b, 1 if a > b, and 0 if all checked values match.
*/
int osmo_sccp_addr_cmp(const struct osmo_sccp_addr *a, const struct osmo_sccp_addr *b, uint32_t presence_criteria)
@ -468,10 +491,10 @@ const char *osmo_sccp_user_name(struct osmo_sccp_user *scu)
* \param[in] name human readable name
* \param[in] default_pc pointcode to be used on missing VTY setting
* \param[in] prot protocol to be used (e.g OSMO_SS7_ASP_PROT_M3UA)
* \param[in] default_local_port local port to be usd on missing VTY setting
* \param[in] default_local_ip local IP-address to be usd on missing VTY setting
* \param[in] default_remote_port remote port to be usd on missing VTY setting
* \param[in] default_remote_ip remote IP-address to be usd on missing VTY setting
* \param[in] default_local_port local port to be used on missing VTY setting
* \param[in] default_local_ip local IP-address to be used on missing VTY setting (NULL: use library own defaults)
* \param[in] default_remote_port remote port to be used on missing VTY setting
* \param[in] default_remote_ip remote IP-address to be used on missing VTY setting (NULL: use library own defaults)
* \returns callee-allocated SCCP instance on success; NULL on error */
struct osmo_sccp_instance *
@ -492,6 +515,9 @@ osmo_sccp_simple_client_on_ss7_id(void *ctx, uint32_t ss7_id, const char *name,
struct osmo_ss7_asp *asp;
bool asp_created = false;
char *as_name, *asp_name = NULL;
int trans_proto;
trans_proto = ss7_default_trans_proto_for_asp_proto(prot);
/*! The function will examine the given CS7 instance and its sub
* components (as, asp, etc.). If necessary it will allocate
@ -602,26 +628,69 @@ osmo_sccp_simple_client_on_ss7_id(void *ctx, uint32_t ss7_id, const char *name,
asp_name = talloc_asprintf(ctx, "asp-clnt-%s", name);
LOGP(DLSCCP, LOGL_NOTICE, "%s: No unassociated ASP for %s, creating new ASP %s\n",
name, osmo_ss7_asp_protocol_name(prot), asp_name);
asp =
osmo_ss7_asp_find_or_create(ss7, asp_name,
default_remote_port,
default_local_port,
prot);
asp = osmo_ss7_asp_find_or_create2(ss7, asp_name,
default_remote_port,
default_local_port,
trans_proto, prot);
talloc_free(asp_name);
if (!asp)
goto out_rt;
asp_created = true;
osmo_ss7_asp_peer_set_hosts(&asp->cfg.local, asp, &default_local_ip, 1);
osmo_ss7_asp_peer_set_hosts(&asp->cfg.remote, asp, &default_remote_ip, 1);
/* Ensure that the ASP we use is set to operate as a client. */
asp->cfg.is_server = false;
/* Ensure that the ASP we use is set to role ASP. */
asp->cfg.role = OSMO_SS7_ASP_ROLE_ASP;
if (default_local_ip)
osmo_ss7_asp_peer_set_hosts(&asp->cfg.local, asp, &default_local_ip, 1);
if (default_remote_ip)
osmo_ss7_asp_peer_set_hosts(&asp->cfg.remote, asp, &default_remote_ip, 1);
/* Make sure proper defaults are applied if app didn't provide specific default values */
ss7_asp_set_default_peer_hosts(asp);
asp->simple_client_allocated = true;
}
osmo_ss7_as_add_asp(as, asp->cfg.name);
}
/* Ensure that the ASP we use is set to client mode. */
asp->cfg.is_server = false;
asp->cfg.role = OSMO_SS7_ASP_ROLE_ASP;
/* Extra sanity checks if the ASP asp-clnt-* was pre-configured over VTY: */
if (!asp->simple_client_allocated) {
/* Forbid ASPs defined through VTY that are not entirely
* configured. "role" and "transport-role" must be explicitly provided:
*/
if (!asp->cfg.role_set_by_vty) {
LOGP(DLSCCP, LOGL_ERROR,
"%s: ASP %s defined in VTY but 'role' was not set there, please set it.\n",
name, asp->cfg.name);
goto out_asp;
}
if (!asp->cfg.trans_role_set_by_vty) {
LOGP(DLSCCP, LOGL_ERROR,
"%s: ASP %s defined in VTY but 'transport-role' was not set there, please set it.\n",
name, asp->cfg.name);
goto out_asp;
}
/* If ASP was configured through VTY it may be explicitly configured as
* SCTP server. It may be a bit confusing since this function is to create
* a "SCCP simple client", but this allows users of this API such as
* osmo-hnbgw to support transport-role server if properly configured through VTY.
*/
if (asp->cfg.is_server) {
struct osmo_xua_server *xs;
LOGP(DLSCCP, LOGL_NOTICE,
"%s: Requesting an SCCP simple client on ASP %s configured with 'transport-role server'\n",
name, asp->cfg.name);
xs = osmo_ss7_xua_server_find2(ss7,
asp->cfg.trans_proto, prot,
asp->cfg.local.port);
if (!xs) {
LOGP(DLSCCP, LOGL_ERROR, "%s: Requesting an SCCP simple client on ASP %s configured "
"with 'transport-role server' but no matching xUA server was configured!\n",
name, asp->cfg.name);
goto out_asp;
}
}
}
/* Restart ASP */
if (prot != OSMO_SS7_ASP_PROT_IPA)
@ -657,10 +726,10 @@ out_ss7:
* \param[in] name human readable name
* \param[in] default_pc pointcode to be used on missing VTY setting
* \param[in] prot protocol to be used (e.g OSMO_SS7_ASP_PROT_M3UA)
* \param[in] default_local_port local port to be usd on missing VTY setting
* \param[in] default_local_ip local IP-address to be usd on missing VTY setting
* \param[in] default_remote_port remote port to be usd on missing VTY setting
* \param[in] default_remote_ip remote IP-address to be usd on missing VTY setting
* \param[in] default_local_port local port to be used on missing VTY setting
* \param[in] default_local_ip local IP-address to be used on missing VTY setting
* \param[in] default_remote_port remote port to be used on missing VTY setting
* \param[in] default_remote_ip remote IP-address to be used on missing VTY setting
* \returns callee-allocated SCCP instance on success; NULL on error */
struct osmo_sccp_instance *
osmo_sccp_simple_client(void *ctx, const char *name, uint32_t default_pc,
@ -690,8 +759,11 @@ osmo_sccp_simple_server_on_ss7_id(void *ctx, uint32_t ss7_id, uint32_t pc,
{
struct osmo_ss7_instance *ss7;
struct osmo_xua_server *xs;
int trans_proto;
int rc;
trans_proto = ss7_default_trans_proto_for_asp_proto(prot);
if (local_port < 0)
local_port = osmo_ss7_asp_protocol_port(prot);
@ -701,7 +773,7 @@ osmo_sccp_simple_server_on_ss7_id(void *ctx, uint32_t ss7_id, uint32_t pc,
return NULL;
ss7->cfg.primary_pc = pc;
xs = osmo_ss7_xua_server_create(ss7, prot, local_port, local_ip);
xs = osmo_ss7_xua_server_create2(ss7, trans_proto, prot, local_port, local_ip);
if (!xs)
goto out_ss7;
@ -746,6 +818,9 @@ osmo_sccp_simple_server_add_clnt(struct osmo_sccp_instance *inst,
struct osmo_ss7_asp *asp;
struct osmo_xua_server *oxs;
char *as_name, *asp_name;
int trans_proto;
trans_proto = ss7_default_trans_proto_for_asp_proto(prot);
if (local_port < 0)
local_port = osmo_ss7_asp_protocol_port(prot);
@ -766,10 +841,12 @@ osmo_sccp_simple_server_add_clnt(struct osmo_sccp_instance *inst,
if (!rt)
goto out_as;
asp = osmo_ss7_asp_find_or_create(ss7, asp_name, remote_port, local_port, prot);
asp = osmo_ss7_asp_find_or_create2(ss7, asp_name,
remote_port, local_port,
trans_proto, prot);
if (!asp)
goto out_rt;
oxs = osmo_ss7_xua_server_find(ss7, prot, local_port);
oxs = osmo_ss7_xua_server_find2(ss7, asp->cfg.trans_proto, prot, local_port);
if (!oxs)
goto out_asp;
if (osmo_ss7_asp_peer_set_hosts(&asp->cfg.local, asp,
@ -800,6 +877,24 @@ out_strings:
return NULL;
}
/*! Adjust the upper bound for the optional data length (the payload) for CR, CC, CREF and RLSD messages.
* For any Optional Data part larger than this value in octets, send CR, CC, CREF and RLSD messages without any payload,
* and send the data payload in a separate Data Form 1 message. ITU-T Q.713 sections 4.2 thru 4.5 define a limit of 130
* bytes for the 'Data' parameter. This limit can be adjusted here. May be useful for interop with nonstandard SCCP
* peers.
* \param[in] sccp SCCP instance to reconfigure.
* \param[in] val Number of bytes to set as upper bound for the optional data length, or pass a negative value to set
* the standard value of SCCP_MAX_OPTIONAL_DATA == 130, which conforms to ITU-T Q.713.
*/
void osmo_sccp_set_max_optional_data(struct osmo_sccp_instance *inst, int val)
{
if (!inst)
return;
if (val < 0)
val = SCCP_MAX_OPTIONAL_DATA;
inst->max_optional_data = val;
}
/*! \brief get the SS7 instance that is related to the given SCCP instance
* \param[in] sccp SCCP instance
* \returns SS7 instance; NULL if sccp was NULL */

View File

@ -37,6 +37,8 @@
#include <osmocom/sigtran/osmo_ss7.h>
#include <osmocom/sigtran/protocol/mtp.h>
#include <osmocom/sccp/sccp_types.h>
#include "xua_internal.h"
#include "sccp_internal.h"
@ -143,14 +145,13 @@ DEFUN(show_sccp_connections, show_sccp_connections_cmd,
/* sccp-timer <name> <1-999999>
* (cmdstr and doc are dynamically generated from osmo_sccp_timer_names.) */
DEFUN(sccp_timer, sccp_timer_cmd,
NULL, NULL)
DEFUN_ATTR(sccp_timer, sccp_timer_cmd,
NULL, NULL, CMD_ATTR_IMMEDIATE)
{
struct osmo_ss7_instance *ss7 = vty->index;
enum osmo_sccp_timer timer = get_string_value(osmo_sccp_timer_names, argv[0]);
struct osmo_sccp_timer_val set_val = { .s = atoi(argv[1]) };
if (timer < 0 || timer >= OSMO_SCCP_TIMERS_COUNT) {
if (timer <= 0 || timer >= OSMO_SCCP_TIMERS_LEN) {
vty_out(vty, "%% Invalid timer: %s%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
@ -161,16 +162,39 @@ DEFUN(sccp_timer, sccp_timer_cmd,
return CMD_WARNING;
}
ss7->sccp->timers[timer] = set_val;
OSMO_ASSERT(ss7->sccp->tdefs);
osmo_tdef_set(ss7->sccp->tdefs, timer, atoi(argv[1]), OSMO_TDEF_S);
return CMD_SUCCESS;
}
static const char *osmo_sccp_timer_val_name(const struct osmo_sccp_timer_val *val)
DEFUN_ATTR(sccp_max_optional_data, sccp_max_optional_data_cmd,
"sccp max-optional-data (<0-999999>|standard)",
"Configure SCCP behavior\n"
"Adjust the upper bound for the optional data length (the payload) for CR, CC, CREF and RLSD messages."
" For any Optional Data part larger than this value in octets, send CR, CC, CREF and RLSD"
" messages without any payload, and send the data payload in a separate Data Form 1 message."
" ITU-T Q.713 sections 4.2 thru 4.5 define a limit of 130 bytes for the 'Data' parameter. This limit can be"
" adjusted here. May be useful for interop with nonstandard SCCP peers.\n"
"Set a non-standard maximum allowed number of bytes\n"
"Use the ITU-T Q.713 4.2 to 4.5 standard value of 130\n",
CMD_ATTR_IMMEDIATE)
{
static char buf[16];
struct osmo_ss7_instance *ss7 = vty->index;
int val;
snprintf(buf, sizeof(buf), "%u", val->s);
return buf;
if (!strcmp(argv[0], "standard"))
val = -1;
else
val = atoi(argv[0]);
osmo_ss7_ensure_sccp(ss7);
if (!ss7->sccp) {
vty_out(vty, "%% Error: cannot instantiate SCCP instance%s", VTY_NEWLINE);
return CMD_WARNING;
}
osmo_sccp_set_max_optional_data(ss7->sccp, val);
return CMD_SUCCESS;
}
static void gen_sccp_timer_cmd_strs(struct cmd_element *cmd)
@ -187,19 +211,19 @@ static void gen_sccp_timer_cmd_strs(struct cmd_element *cmd)
"Configure SCCP timer values, see ITU-T Q.714\n");
for (i = 0; osmo_sccp_timer_names[i].str; i++) {
const struct osmo_sccp_timer_val *def;
const struct osmo_tdef *def;
enum osmo_sccp_timer timer;
timer = osmo_sccp_timer_names[i].value;
def = &osmo_sccp_timer_defaults[timer];
OSMO_ASSERT(timer >= 0 && timer < OSMO_SCCP_TIMERS_COUNT);
def = osmo_tdef_get_entry((struct osmo_tdef *)&osmo_sccp_timer_defaults, timer);
OSMO_ASSERT(def);
osmo_talloc_asprintf(tall_vty_ctx, cmd_str, "%s%s",
i ? "|" : "",
osmo_sccp_timer_name(timer));
osmo_talloc_asprintf(tall_vty_ctx, doc_str, "%s (default: %s)\n",
osmo_sccp_timer_description(timer),
osmo_sccp_timer_val_name(def));
osmo_sccp_timer_names[i].str);
osmo_talloc_asprintf(tall_vty_ctx, doc_str, "%s (default: %lu)\n",
def->desc,
def->default_val);
}
osmo_talloc_asprintf(tall_vty_ctx, cmd_str, ") <1-999999>");
@ -215,18 +239,22 @@ static void write_sccp_timers(struct vty *vty, const char *indent,
{
int i;
for (i = 0; i < ARRAY_SIZE(inst->timers); i++) {
const struct osmo_sccp_timer_val *val = osmo_sccp_timer_get(inst, i, default_if_unset);
if (!val)
for (i = 0; osmo_sccp_timer_names[i].str; i++) {
const struct osmo_tdef *tdef = osmo_tdef_get_entry(inst->tdefs, osmo_sccp_timer_names[i].value);
if (!tdef)
continue;
vty_out(vty, "%ssccp-timer %s %s%s", indent, osmo_sccp_timer_name(i),
osmo_sccp_timer_val_name(val), VTY_NEWLINE);
if (!default_if_unset && tdef->val == tdef->default_val)
continue;
vty_out(vty, "%ssccp-timer %s %lu%s", indent, osmo_sccp_timer_names[i].str,
tdef->val, VTY_NEWLINE);
}
}
void osmo_sccp_vty_write_cs7_node(struct vty *vty, const char *indent, struct osmo_sccp_instance *inst)
{
write_sccp_timers(vty, indent, inst, false);
if (inst->max_optional_data != SCCP_MAX_OPTIONAL_DATA)
vty_out(vty, "%ssccp max-optional-data %u%s", indent, inst->max_optional_data, VTY_NEWLINE);
}
DEFUN(show_sccp_timers, show_sccp_timers_cmd,
@ -255,11 +283,12 @@ DEFUN(show_sccp_timers, show_sccp_timers_cmd,
void osmo_sccp_vty_init(void)
{
install_element_ve(&show_sccp_users_cmd);
install_element_ve(&show_sccp_user_ssn_cmd);
install_element_ve(&show_sccp_connections_cmd);
install_lib_element_ve(&show_sccp_users_cmd);
install_lib_element_ve(&show_sccp_user_ssn_cmd);
install_lib_element_ve(&show_sccp_connections_cmd);
install_element_ve(&show_sccp_timers_cmd);
install_lib_element_ve(&show_sccp_timers_cmd);
gen_sccp_timer_cmd_strs(&sccp_timer_cmd);
install_element(L_CS7_NODE, &sccp_timer_cmd);
install_lib_element(L_CS7_NODE, &sccp_timer_cmd);
install_lib_element(L_CS7_NODE, &sccp_max_optional_data_cmd);
}

52
src/ss7_internal.h Normal file
View File

@ -0,0 +1,52 @@
#pragma once
/* Internal header used by libosmo-sccp, not available publicly for lib users */
#include <stdbool.h>
#include <stdint.h>
#include <osmocom/sigtran/osmo_ss7.h>
extern bool ss7_initialized;
uint32_t ss7_find_free_l_rk_id(struct osmo_ss7_instance *inst);
bool ss7_ipv6_sctp_supported(const char *host, bool bind);
struct osmo_ss7_as *ss7_as_alloc(struct osmo_ss7_instance *inst, const char *name,
enum osmo_ss7_asp_protocol proto);
struct osmo_ss7_asp *ss7_asp_alloc(struct osmo_ss7_instance *inst, const char *name,
uint16_t remote_port, uint16_t local_port,
int trans_proto, enum osmo_ss7_asp_protocol proto);
bool ss7_asp_set_default_peer_hosts(struct osmo_ss7_asp *asp);
bool ss7_asp_is_started(const struct osmo_ss7_asp *asp);
int ss7_asp_get_fd(const struct osmo_ss7_asp *asp);
struct osmo_ss7_asp *ss7_asp_find_by_socket_addr(int fd, int trans_proto);
bool ss7_asp_protocol_check_trans_proto(enum osmo_ss7_asp_protocol proto, int trans_proto);
int ss7_default_trans_proto_for_asp_proto(enum osmo_ss7_asp_protocol proto);
int ss7_asp_ipa_srv_conn_rx_cb(struct osmo_stream_srv *conn, int res, struct msgb *msg);
int ss7_asp_xua_srv_conn_rx_cb(struct osmo_stream_srv *conn, int res, struct msgb *msg);
int ss7_asp_m3ua_tcp_srv_conn_rx_cb(struct osmo_stream_srv *conn, int res, struct msgb *msg);
int ss7_asp_xua_srv_conn_closed_cb(struct osmo_stream_srv *srv);
int ss7_asp_apply_peer_primary_address(const struct osmo_ss7_asp *asp);
int ss7_asp_apply_primary_address(const struct osmo_ss7_asp *asp);
int ss7_asp_apply_new_local_address(const struct osmo_ss7_asp *asp, unsigned int loc_idx);
int ss7_asp_apply_drop_local_address(const struct osmo_ss7_asp *asp, unsigned int loc_idx);
bool ss7_asp_peer_match_host(const struct osmo_ss7_asp_peer *peer, const char *host, bool host_is_v6);
int ss7_asp_peer_find_host(const struct osmo_ss7_asp_peer *peer, const char *host);
bool ss7_xua_server_set_default_local_hosts(struct osmo_xua_server *oxs);
int xua_tcp_segmentation_cb(struct msgb *msg);
enum ss7_as_ctr {
SS7_AS_CTR_RX_MSU_TOTAL,
SS7_AS_CTR_TX_MSU_TOTAL,
};
enum ss7_asp_ctr {
SS7_ASP_CTR_PKT_RX_TOTAL,
SS7_ASP_CTR_PKT_RX_UNKNOWN,
SS7_ASP_CTR_PKT_TX_TOTAL,
};

286
src/sua.c
View File

@ -1,6 +1,6 @@
/* Minimal implementation of RFC 3868 - SCCP User Adaptation Layer */
/* (C) 2015-2017 by Harald Welte <laforge@gnumonks.org>
/* (C) 2015-2021 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0+
@ -46,6 +46,7 @@
#include "xua_asp_fsm.h"
#include "xua_internal.h"
#include "sccp_internal.h"
#include "ss7_internal.h"
/* Appendix C.4 of Q.714 (all in milliseconds) */
#define CONNECTION_TIMER ( 1 * 60 * 100)
@ -179,13 +180,15 @@ static const uint16_t coit_mand_ies[] = {
SUA_IEI_ROUTE_CTX, SUA_IEI_PROTO_CLASS, SUA_IEI_SRC_REF,
SUA_IEI_DEST_REF, 0
};
/* ITU-T Rec Q.713 */
static const struct value_string sua_co_msgt_names[] = {
{ SUA_CO_CODT, "CODT" },
{ SUA_CO_CODA, "CODA" },
{ SUA_CO_CORE, "CORE" },
{ SUA_CO_COAK, "COAK" },
{ SUA_CO_COREF, "COREF" },
{ SUA_CO_RELRE, "RELRE" },
{ SUA_CO_CORE, "CORE" }, /* §4.2 Connection request (CR) */
{ SUA_CO_COAK, "COAK" }, /* §4.3 Connection confirm (CC) */
{ SUA_CO_COREF, "COREF" }, /* §4.4 Connection refused (CREF) */
{ SUA_CO_RELRE, "RELRE" }, /* §4.5 Released (RLSD) */
{ SUA_CO_RELCO, "RELCO" },
{ SUA_CO_RESRE, "RESRE" },
{ SUA_CO_RESCO, "RESCO" },
@ -257,7 +260,7 @@ static struct xua_msg *sua_gen_error_msg(uint32_t err_code, struct msgb *msg)
}
/***********************************************************************
* Transmitting SUA messsages to SCTP
* Transmitting SUA messages to SCTP
***********************************************************************/
static struct msgb *sua_to_msg(struct xua_msg *xua)
@ -315,7 +318,7 @@ int sua_tx_xua_as(struct osmo_ss7_as *as, struct xua_msg *xua)
return -1;
/* send the msg to the AS for transmission. The AS FSM might
* (depending on its state) enqueue it before trnsmission */
* (depending on its state) enqueue it before transmission */
rc = osmo_fsm_inst_dispatch(as->fi, XUA_AS_E_TRANSFER_REQ, msg);
if (rc < 0)
msgb_free(msg);
@ -323,7 +326,7 @@ int sua_tx_xua_as(struct osmo_ss7_as *as, struct xua_msg *xua)
}
/***********************************************************************
* Receiving SUA messsages from SCTP
* Receiving SUA messages from SCTP
***********************************************************************/
/*! \brief Decode SUA Global Title according to RFC3868 Section 3.10.2.3
@ -426,7 +429,7 @@ int sua_addr_parse_part(struct osmo_sccp_addr *out,
* probably want to have a separate general parsing function storing
* the subparts in xua_msg_part. But before we do, we should find more
* users of this subpart parsing and be aware of the performance
* tradeoff.
* trade-off.
*/
while (pos + sizeof(*par) < param->len) {
@ -467,6 +470,13 @@ int sua_addr_parse_part(struct osmo_sccp_addr *out,
out->ip.v4.s_addr = *p32;
out->presence |= OSMO_SCCP_ADDR_T_IPv4;
break;
case SUA_IEI_IPv6:
if (par_datalen != 16)
goto subpar_fail;
/* no endian conversion, both network order */
memcpy(&out->ip.v6, par->data, 16);
out->presence |= OSMO_SCCP_ADDR_T_IPv6;
break;
default:
LOGP(DLSUA, LOGL_ERROR, "SUA IEI 0x%04x: Unknown subpart tag %hd\n",
param->tag, par_tag);
@ -503,10 +513,19 @@ int sua_addr_parse(struct osmo_sccp_addr *out, struct xua_msg *xua, uint16_t iei
/* connectionless messages received from socket */
static int sua_rx_cl(struct osmo_ss7_asp *asp, struct xua_msg *xua)
{
struct xua_msg_part *rctx_ie = xua_msg_find_tag(xua, SUA_IEI_ROUTE_CTX);
struct osmo_sccp_instance *inst = asp->inst->sccp;
struct osmo_ss7_as *as;
int rc;
OSMO_ASSERT(xua->hdr.msg_class == SUA_MSGC_CL);
rc = xua_find_as_for_asp(&as, asp, rctx_ie);
if (rc)
return rc;
rate_ctr_inc2(as->ctrg, SS7_AS_CTR_RX_MSU_TOTAL);
switch (xua->hdr.msg_type) {
case 0: /* Reserved, permitted by ETSI TS 101 592 5.2.3.2 */
case SUA_CL_CLDT:
@ -524,10 +543,19 @@ static int sua_rx_cl(struct osmo_ss7_asp *asp, struct xua_msg *xua)
/* connection-oriented messages received from socket */
static int sua_rx_co(struct osmo_ss7_asp *asp, struct xua_msg *xua)
{
struct xua_msg_part *rctx_ie = xua_msg_find_tag(xua, SUA_IEI_ROUTE_CTX);
struct osmo_sccp_instance *inst = asp->inst->sccp;
struct osmo_ss7_as *as;
int rc;
OSMO_ASSERT(xua->hdr.msg_class == SUA_MSGC_CO);
rc = xua_find_as_for_asp(&as, asp, rctx_ie);
if (rc)
return rc;
rate_ctr_inc2(as->ctrg, SS7_AS_CTR_RX_MSU_TOTAL);
switch (xua->hdr.msg_type) {
case 0: /* Reserved, permitted by ETSI TS 101 592 5.2.3.2 */
case SUA_CO_CORE:
@ -593,7 +621,7 @@ static int sua_rx_mgmt_ntfy(struct osmo_ss7_asp *asp, struct xua_msg *xua)
if (ntfy.info_string)
talloc_free(ntfy.info_string);
/* TODO: should we report this soemwhere? */
/* TODO: should we report this somewhere? */
return 0;
}
@ -640,6 +668,8 @@ static int sua_rx_asp(struct osmo_ss7_asp *asp, struct xua_msg *xua)
return 0;
}
static int sua_rx_snm(struct osmo_ss7_asp *asp, struct xua_msg *xua);
/*! \brief process SUA message received from socket
* \param[in] asp Application Server Process receiving \ref msg
* \param[in] msg received message buffer
@ -652,7 +682,7 @@ int sua_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg)
OSMO_ASSERT(asp->cfg.proto == OSMO_SS7_ASP_PROT_SUA);
/* caller owns msg memory, we shall neither free it here nor
* keep references beyon the execution of this function and its
* keep references beyond the execution of this function and its
* callees. */
if (!asp->inst->sccp) {
@ -715,6 +745,8 @@ int sua_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg)
rc = sua_rx_mgmt(asp, xua);
break;
case SUA_MSGC_SNM:
rc = sua_rx_snm(asp, xua);
break;
case SUA_MSGC_RKM:
/* FIXME */
LOGPASP(asp, DLSUA, LOGL_NOTICE, "Received unsupported SUA "
@ -740,3 +772,235 @@ out:
return rc;
}
/***********************************************************************
* SSNM msg generation
***********************************************************************/
/* 3.4.1 Destination Unavailable (DUNA) */
static struct xua_msg *sua_encode_duna(const uint32_t *rctx, unsigned int num_rctx,
const uint32_t *aff_pc, unsigned int num_aff_pc,
const uint32_t *ssn, const uint32_t *smi, const char *info_string)
{
struct xua_msg *xua = xua_msg_alloc();
xua->hdr = XUA_HDR(SUA_MSGC_SNM, SUA_SNM_DUNA);
xua->hdr.version = SUA_VERSION;
if (rctx && num_rctx)
xua_msg_add_data(xua, SUA_IEI_ROUTE_CTX, num_rctx * 4, (const uint8_t *)rctx);
xua_msg_add_data(xua, SUA_IEI_AFFECTED_PC, num_aff_pc * 4, (const uint8_t *) aff_pc);
if (ssn)
xua_msg_add_u32(xua, SUA_IEI_SSN, *ssn);
if (smi)
xua_msg_add_u32(xua, SUA_IEI_SSN, *smi);
if (info_string) {
xua_msg_add_data(xua, SUA_IEI_INFO_STRING,
strlen(info_string)+1,
(const uint8_t *) info_string);
}
return xua;
}
/* 3.4.2 Destination Available (DAVA) */
static struct xua_msg *sua_encode_dava(const uint32_t *rctx, unsigned int num_rctx,
const uint32_t *aff_pc, unsigned int num_aff_pc,
const uint32_t *ssn, const uint32_t *smi, const char *info_string)
{
/* encoding is exactly identical to DUNA */
struct xua_msg *xua = sua_encode_duna(rctx, num_rctx, aff_pc, num_aff_pc, ssn, smi, info_string);
if (xua)
xua->hdr.msg_type = SUA_SNM_DAVA;
return xua;
}
#if 0 /* not used so far */
/* 3.4.3 Destination Available (DAUD) */
static struct xua_msg *sua_encode_daud(const uint32_t *rctx, unsigned int num_rctx,
const uint32_t *aff_pc, unsigned int num_aff_pc,
const uint32_t *ssn, const uint32_t *smi, const char *info_string)
{
/* encoding is exactly identical to DUNA */
struct xua_msg *xua = sua_encode_duna(rctx, num_rctx, aff_pc, num_aff_pc, ssn, smi, info_string);
if (xua)
xua->hdr.msg_type = SUA_SNM_DAUD;
return xua;
}
#endif
/* 3.4.5 Destination User Part Unavailable (DUPU) */
static struct xua_msg *sua_encode_dupu(const uint32_t *rctx, unsigned int num_rctx,
uint32_t dpc, uint16_t user, uint16_t cause,
const char *info_string)
{
struct xua_msg *xua = xua_msg_alloc();
uint32_t user_cause = (user << 16) | cause;
xua->hdr = XUA_HDR(SUA_MSGC_SNM, SUA_SNM_DUNA);
xua->hdr.version = SUA_VERSION;
if (rctx && num_rctx)
xua_msg_add_data(xua, SUA_IEI_ROUTE_CTX, num_rctx * sizeof(*rctx), (const uint8_t *)rctx);
xua_msg_add_u32(xua, SUA_IEI_AFFECTED_PC, dpc);
xua_msg_add_u32(xua, SUA_IEI_USER_CAUSE, user_cause);
if (info_string) {
xua_msg_add_data(xua, SUA_IEI_INFO_STRING, strlen(info_string)+1,
(const uint8_t *) info_string);
}
return xua;
}
/*! Transmit SSNM DUNA/DAVA message indicating [un]availability of certain point code[s]
* \param[in] asp ASP through whihc to transmit message. Must be ACTIVE.
* \param[in] rctx array of Routing Contexts in network byte order.
* \param[in] num_rctx number of rctx
* \param[in] aff_pc array of 'Affected Point Code' in network byte order.
* \param[in] num_aff_pc number of aff_pc
* \param[in] aff_ssn affected SSN (optional)
* \param[in] smi subsystem multiplicity indicator (optional)
* \param[in] info_string optional information strng (can be NULL).
* \param[in] available are aff_pc now available (true) or unavailable (false) */
void sua_tx_snm_available(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
const uint32_t *aff_pc, unsigned int num_aff_pc, const uint32_t *aff_ssn,
const uint32_t *smi, const char *info_string, bool available)
{
struct xua_msg *xua;
if (available)
xua = sua_encode_dava(rctx, num_rctx, aff_pc, num_aff_pc, aff_ssn, smi, info_string);
else
xua = sua_encode_duna(rctx, num_rctx, aff_pc, num_aff_pc, aff_ssn, smi, info_string);
sua_tx_xua_asp(asp, xua);
}
/*! Transmit SSNM SCON message indicating congestion
* \param[in] asp ASP through which to transmit message. Must be ACTIVE.
* \param[in] rctx array of Routing Contexts in network byte order.
* \param[in] num_rctx number of rctx
* \param[in] aff_pc array of 'Affected Point Code' in network byte order.
* \param[in] num_aff_pc number of aff_pc
* \param[in] ssn optional SSN (can be NULL)
* \param[in] cong_level optional congestion level (can be NULL)
* \param[in] info_string optional information string (can be NULL). */
void sua_tx_snm_congestion(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
const uint32_t *aff_pc, unsigned int num_aff_pc, const uint32_t *ssn,
const uint8_t cong_level, const char *info_string)
{
struct xua_msg *xua = xua_msg_alloc();
xua->hdr = XUA_HDR(SUA_MSGC_SNM, SUA_SNM_SCON);
xua->hdr.version = SUA_VERSION;
if (rctx && num_rctx)
xua_msg_add_data(xua, SUA_IEI_ROUTE_CTX, num_rctx * sizeof(*rctx), (const uint8_t *)rctx);
xua_msg_add_data(xua, SUA_IEI_AFFECTED_PC, num_aff_pc * sizeof(*aff_pc), (const uint8_t *) aff_pc);
if (ssn)
xua_msg_add_u32(xua, SUA_IEI_SSN, *ssn);
xua_msg_add_u32(xua, SUA_IEI_CONG_LEVEL, cong_level);
if (info_string)
xua_msg_add_data(xua, SUA_IEI_INFO_STRING, strlen(info_string)+1, (const uint8_t *) info_string);
}
/*! Transmit SSNM DUPU message indicating user unavailability.
* \param[in] asp ASP through which to transmit message. Must be ACTIVE.
* \param[in] rctx array of Routing Contexts in network byte order.
* \param[in] num_rctx number of rctx
* \param[in] dpc affected point code
* \param[in] user the user (SI) that is unavailable
* \param[in] cause the cause of the user unavailability
* \param[in] info_string optional information string (can be NULL). */
void sua_tx_dupu(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
uint32_t dpc, uint16_t user, uint16_t cause, const char *info_str)
{
struct xua_msg *xua = sua_encode_dupu(rctx, num_rctx, dpc, user, cause, info_str);
sua_tx_xua_asp(asp, xua);
}
/* received SNM message on ASP side */
static int sua_rx_snm_asp(struct osmo_ss7_asp *asp, struct xua_msg *xua)
{
struct osmo_ss7_as *as = NULL;
struct xua_msg_part *rctx_ie = xua_msg_find_tag(xua, SUA_IEI_ROUTE_CTX);
int rc;
rc = xua_find_as_for_asp(&as, asp, rctx_ie);
if (rc)
return rc;
switch (xua->hdr.msg_type) {
case SUA_SNM_DUNA:
xua_snm_rx_duna(asp, as, xua);
break;
case SUA_SNM_DAVA:
xua_snm_rx_dava(asp, as, xua);
break;
case SUA_SNM_DUPU:
xua_snm_rx_dupu(asp, as, xua);
break;
case SUA_SNM_SCON:
case SUA_SNM_DRST:
LOGPASP(asp, DLSUA, LOGL_NOTICE, "Received unsupported SUA SNM message type %u\n",
xua->hdr.msg_type);
/* silently ignore those to not confuse the sender */
break;
case SUA_SNM_DAUD:
/* RFC states only permitted in ASP->SG direction, not reverse. But some
* equipment still sends it to us as ASP ?!? */
if (asp->cfg.quirks & OSMO_SS7_ASP_QUIRK_DAUD_IN_ASP) {
LOGPASP(asp, DLSUA, LOGL_NOTICE, "quirk daud_in_asp active: Accepting DAUD "
"despite being in ASP role\n");
xua_snm_rx_daud(asp, xua);
} else {
LOGPASP(asp, DLSUA, LOGL_ERROR, "DAUD not permitted in ASP role\n");
return SUA_ERR_UNSUPP_MSG_TYPE;
}
break;
default:
return SUA_ERR_UNSUPP_MSG_TYPE;
}
return 0;
}
/* received SNM message on SG side */
static int sua_rx_snm_sg(struct osmo_ss7_asp *asp, struct xua_msg *xua)
{
switch (xua->hdr.msg_type) {
case SUA_SNM_DAUD: /* Audit: ASP inquires about availability of Point Codes */
xua_snm_rx_daud(asp, xua);
break;
default:
return SUA_ERR_UNSUPP_MSG_TYPE;
}
return 0;
}
static int sua_rx_snm(struct osmo_ss7_asp *asp, struct xua_msg *xua)
{
/* SNM only permitted in ACTIVE state */
if (asp->fi->state != XUA_ASP_S_ACTIVE) {
if (asp->fi->state == XUA_ASP_S_INACTIVE && asp->cfg.quirks & OSMO_SS7_ASP_QUIRK_SNM_INACTIVE) {
LOGPASP(asp, DLSUA, LOGL_NOTICE, "quirk snm_inactive active: "
"Accepting SSNM in state %s\n", osmo_fsm_inst_state_name(asp->fi));
} else {
LOGPASP(asp, DLM3UA, LOGL_ERROR, "Rx SUA SSNM not permitted "
"while ASP in state %s\n", osmo_fsm_inst_state_name(asp->fi));
return SUA_ERR_UNEXPECTED_MSG;
}
}
switch (asp->cfg.role) {
case OSMO_SS7_ASP_ROLE_SG:
return sua_rx_snm_sg(asp, xua);
case OSMO_SS7_ASP_ROLE_ASP:
return sua_rx_snm_asp(asp, xua);
default:
return SUA_ERR_UNSUPP_MSG_CLASS;
}
}

View File

@ -75,6 +75,26 @@ static int as_notify_all_asp(struct osmo_ss7_as *as, struct osmo_xlm_prim_notify
return sent;
}
/* determine which role (SG/ASP/IPSP) we operate in */
static int get_local_role(struct osmo_ss7_as *as)
{
unsigned int i;
/* this is a bit tricky. "osmo_ss7_as" has no configuration of a role,
* only the ASPs have. As they all must be of the same role, let's simply
* find the first one and return its role */
for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
struct osmo_ss7_asp *asp = as->cfg.asps[i];
if (!asp)
continue;
return asp->cfg.role;
}
/* we don't have any ASPs in this AS? Strange */
return -1;
}
static struct osmo_ss7_asp *xua_as_select_asp_override(struct osmo_ss7_as *as)
{
struct osmo_ss7_asp *asp;
@ -138,6 +158,8 @@ int xua_as_transmit_msg(struct osmo_ss7_as *as, struct msgb *msg)
asp = xua_as_select_asp_override(as);
break;
case OSMO_SS7_AS_TMOD_LOADSHARE:
/* TODO: actually use the SLS value to ensure same SLS goes through same ASP. Not
* strictly required by M3UA RFC, but would fit the overall principle. */
case OSMO_SS7_AS_TMOD_ROUNDROBIN:
asp = xua_as_select_asp_roundrobin(as);
break;
@ -180,8 +202,78 @@ struct xua_as_fsm_priv {
struct osmo_timer_list t_r;
struct llist_head queued_msgs;
} recovery;
bool ipa_route_created;
};
/* is the given AS one with a single ASP of IPA type? */
static bool is_single_ipa_asp(struct osmo_ss7_as *as)
{
unsigned int asp_count = 0;
int i;
for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
struct osmo_ss7_asp *asp = as->cfg.asps[i];
if (!asp)
continue;
asp_count++;
if (asp->cfg.proto != OSMO_SS7_ASP_PROT_IPA)
return false;
}
if (asp_count == 1)
return true;
return false;
}
static void ipa_add_route(struct osmo_fsm_inst *fi)
{
struct xua_as_fsm_priv *xafp = (struct xua_as_fsm_priv *) fi->priv;
struct osmo_ss7_as *as = xafp->as;
struct osmo_ss7_instance *inst = as->inst;
if (osmo_ss7_route_find_dpc_mask(inst->rtable_system, as->cfg.routing_key.pc, 0xffffff))
return;
/* As opposed to M3UA, there is no RKM and we have to implicitly
* automatically add a route once an IPA connection has come up */
if (osmo_ss7_route_create(inst->rtable_system, as->cfg.routing_key.pc, 0xffffff, as->cfg.name))
xafp->ipa_route_created = true;
}
static void ipa_del_route(struct osmo_fsm_inst *fi)
{
struct xua_as_fsm_priv *xafp = (struct xua_as_fsm_priv *) fi->priv;
struct osmo_ss7_as *as = xafp->as;
struct osmo_ss7_instance *inst = as->inst;
struct osmo_ss7_route *rt;
/* don't delete a route if we added none */
if (!xafp->ipa_route_created)
return;
/* find the route which we have created if we ever reached ipa_asp_fsm_wait_id_ack2 */
rt = osmo_ss7_route_find_dpc_mask(inst->rtable_system, as->cfg.routing_key.pc, 0xffffff);
/* no route found, bail out */
if (!rt) {
LOGPFSML(fi, LOGL_NOTICE, "Attempting to delete route for this IPA AS, but cannot "
"find route for DPC %s. Did you manually delete it?\n",
osmo_ss7_pointcode_print(inst, as->cfg.routing_key.pc));
return;
}
/* route points to different AS, bail out */
if (rt->dest.as != as) {
LOGPFSML(fi, LOGL_NOTICE, "Attempting to delete route for this IPA ASP, but found "
"route for DPC %s points to different AS (%s)\n",
osmo_ss7_pointcode_print(inst, as->cfg.routing_key.pc), rt->dest.as->cfg.name);
return;
}
osmo_ss7_route_destroy(rt);
xafp->ipa_route_created = false;
}
/* is any other ASP in this AS in state != DOWN? */
static bool check_any_other_asp_not_down(struct osmo_ss7_as *as, struct osmo_ss7_asp *asp_cmp)
{
@ -289,12 +381,16 @@ static void xua_as_fsm_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
npar.status_info = M3UA_NOTIFY_I_AS_INACT;
break;
case XUA_AS_S_ACTIVE:
if (is_single_ipa_asp(as))
ipa_add_route(fi);
npar.status_info = M3UA_NOTIFY_I_AS_ACT;
break;
case XUA_AS_S_PENDING:
npar.status_info = M3UA_NOTIFY_I_AS_PEND;
break;
case XUA_AS_S_DOWN:
if (is_single_ipa_asp(as))
ipa_del_route(fi);
/* RFC4666 sec 4.3.2 AS States:
If we end up here, it means no ASP is ACTIVE or INACTIVE,
meaning no ASP can have already configured the traffic mode
@ -318,6 +414,17 @@ static void xua_as_fsm_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
/* TODO: ASP-Id of ASP triggering this state change */
as_notify_all_asp(xafp->as, &npar);
/* only if we are the SG, we must start broadcasting availability information
* to everyone else */
if (get_local_role(xafp->as) == OSMO_SS7_ASP_ROLE_SG) {
/* advertise availability of the routing key to others */
uint32_t aff_pc = htonl(as->cfg.routing_key.pc);
if (old_state != XUA_AS_S_ACTIVE && fi->state == XUA_AS_S_ACTIVE)
xua_snm_pc_available(as, &aff_pc, 1, NULL, true);
else if (old_state == XUA_AS_S_ACTIVE && fi->state != XUA_AS_S_ACTIVE)
xua_snm_pc_available(as, &aff_pc, 1, NULL, false);
}
};
static void xua_as_fsm_inactive(struct osmo_fsm_inst *fi, uint32_t event, void *data)

View File

@ -29,6 +29,7 @@
#include "xua_asp_fsm.h"
#include "xua_as_fsm.h"
#include "xua_internal.h"
#define S(x) (1 << (x))
@ -64,6 +65,8 @@ static const struct value_string xua_asp_event_names[] = {
{ XUA_ASP_E_ASPSM_BEAT, "ASPSM_BEAT" },
{ XUA_ASP_E_ASPSM_BEAT_ACK, "ASPSM_BEAT_ACK" },
{ XUA_ASP_E_AS_ASSIGNED, "AS_ASSIGNED" },
{ IPA_ASP_E_ID_RESP, "IPA_CCM_ID_RESP" },
{ IPA_ASP_E_ID_GET, "IPA_CCM_ID_GET" },
{ IPA_ASP_E_ID_ACK, "IPA_CCM_ID_ACK" },
@ -109,8 +112,8 @@ void xua_asp_send_xlm_prim(struct osmo_ss7_asp *asp, struct osmo_xlm_prim *prim)
if (lm && lm->prim_cb)
lm->prim_cb(&prim->oph, asp);
else {
LOGPASP(asp, DLSS7, LOGL_DEBUG, "No Layer Manager, dropping %s\n",
osmo_xlm_prim_name(&prim->oph));
LOGPFSML(asp->fi, LOGL_DEBUG, "No Layer Manager, dropping %s\n",
osmo_xlm_prim_name(&prim->oph));
}
msgb_free(prim->oph.msg);
@ -180,8 +183,15 @@ static int xua_msg_add_asp_rctx(struct xua_msg *xua, struct osmo_ss7_asp *asp)
}
}
/* add xUA IE with routing contests to the message (if any) */
if (i)
if (i) {
/* bail out (and not add the IE) if there's only one routing context (and hence
* only one AS) within this ASP, and that routing context is zero, meaning no routing
* context IE shall be used */
if (i == 1 && rctx[0] == 0)
return 0;
xua_msg_add_data(xua, M3UA_IEI_ROUTE_CTX, i*sizeof(uint32_t), (uint8_t *)rctx);
}
/* return count of routing contexts added */
return i;
@ -283,6 +293,8 @@ static int peer_send_error(struct osmo_fsm_inst *fi, uint32_t err_code)
struct xua_msg *xua = xua_msg_alloc();
struct msgb *msg;
LOGPFSML(fi, LOGL_ERROR, "Tx MGMT_ERR '%s'\n", get_value_string(m3ua_err_names, err_code));
xua->hdr = XUA_HDR(SUA_MSGC_MGMT, SUA_MGMT_ERR);
xua->hdr.version = SUA_VERSION;
xua_msg_add_u32(xua, SUA_IEI_ERR_CODE, err_code);
@ -337,6 +349,21 @@ static const uint32_t evt_ack_map[_NUM_XUA_ASP_E] = {
[XUA_ASP_E_ASPSM_BEAT] = XUA_ASP_E_ASPSM_BEAT_ACK,
};
/* Helper function to dispatch an ASP->AS event to all AS of which this
* ASP is a memmber. Ignores routing contexts for now. */
static void dispatch_to_all_as(struct osmo_fsm_inst *fi, uint32_t event)
{
struct xua_asp_fsm_priv *xafp = fi->priv;
struct osmo_ss7_asp *asp = xafp->asp;
struct osmo_ss7_instance *inst = asp->inst;
struct osmo_ss7_as *as;
llist_for_each_entry(as, &inst->as_list, list) {
if (!osmo_ss7_as_has_asp(as, asp))
continue;
osmo_fsm_inst_dispatch(as->fi, event, asp);
}
}
/* check if expected message was received + stop t_ack */
static void check_stop_t_ack(struct osmo_fsm_inst *fi, uint32_t event)
@ -380,6 +407,16 @@ static void check_stop_t_ack(struct osmo_fsm_inst *fi, uint32_t event)
} \
} while(0)
/***************
** FSM states **
***************/
static void xua_asp_fsm_down_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
dispatch_to_all_as(fi, XUA_ASPAS_ASP_DOWN_IND);
}
static void xua_asp_fsm_down(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct xua_asp_fsm_priv *xafp = fi->priv;
@ -435,25 +472,9 @@ static void xua_asp_fsm_down(struct osmo_fsm_inst *fi, uint32_t event, void *dat
}
}
/* Helper function to dispatch an ASP->AS event to all AS of which this
* ASP is a memmber. Ignores routing contexts for now. */
static void dispatch_to_all_as(struct osmo_fsm_inst *fi, uint32_t event)
static void xua_asp_fsm_inactive_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct xua_asp_fsm_priv *xafp = fi->priv;
struct osmo_ss7_asp *asp = xafp->asp;
struct osmo_ss7_instance *inst = asp->inst;
struct osmo_ss7_as *as;
llist_for_each_entry(as, &inst->as_list, list) {
if (!osmo_ss7_as_has_asp(as, asp))
continue;
osmo_fsm_inst_dispatch(as->fi, event, asp);
}
}
static void xua_asp_fsm_down_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
dispatch_to_all_as(fi, XUA_ASPAS_ASP_DOWN_IND);
dispatch_to_all_as(fi, XUA_ASPAS_ASP_INACTIVE_IND);
}
static void xua_asp_fsm_inactive(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@ -463,7 +484,6 @@ static void xua_asp_fsm_inactive(struct osmo_fsm_inst *fi, uint32_t event, void
struct osmo_ss7_as *as;
struct xua_msg *xua_in;
uint32_t traf_mode = 0;
enum osmo_ss7_as_traffic_mode tmode;
struct xua_msg_part *part;
int i;
@ -505,7 +525,6 @@ static void xua_asp_fsm_inactive(struct osmo_fsm_inst *fi, uint32_t event, void
peer_send_error(fi, M3UA_ERR_UNSUPP_TRAF_MOD_TYP);
return;
}
tmode = osmo_ss7_tmode_from_xua(traf_mode);
}
if ((part = xua_msg_find_tag(xua_in, M3UA_IEI_ROUTE_CTX))) {
for (i = 0; i < part->len / sizeof(uint32_t); i++) {
@ -519,6 +538,7 @@ static void xua_asp_fsm_inactive(struct osmo_fsm_inst *fi, uint32_t event, void
}
if (traf_mode) { /* if the peer has specified a traffic mode at all */
enum osmo_ss7_as_traffic_mode tmode = osmo_ss7_tmode_from_xua(traf_mode);
llist_for_each_entry(as, &asp->inst->as_list, list) {
if (!osmo_ss7_as_has_asp(as, asp))
continue;
@ -570,9 +590,9 @@ static void xua_asp_fsm_inactive(struct osmo_fsm_inst *fi, uint32_t event, void
}
}
static void xua_asp_fsm_inactive_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
static void xua_asp_fsm_active_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
dispatch_to_all_as(fi, XUA_ASPAS_ASP_INACTIVE_IND);
dispatch_to_all_as(fi, XUA_ASPAS_ASP_ACTIVE_IND);
}
static void xua_asp_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@ -651,11 +671,6 @@ static void xua_asp_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *d
}
}
static void xua_asp_fsm_active_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
dispatch_to_all_as(fi, XUA_ASPAS_ASP_ACTIVE_IND);
}
static void xua_asp_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct xua_asp_fsm_priv *xafp = fi->priv;
@ -676,6 +691,9 @@ static void xua_asp_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *dat
case XUA_ASP_E_ASPSM_BEAT_ACK:
/* FIXME: stop timer, if any */
break;
case XUA_ASP_E_AS_ASSIGNED:
/* Ignore, only used in IPA asps so far. */
break;
default:
break;
}
@ -751,7 +769,8 @@ struct osmo_fsm xua_asp_fsm = {
.allstate_event_mask = S(XUA_ASP_E_SCTP_COMM_DOWN_IND) |
S(XUA_ASP_E_SCTP_RESTART_IND) |
S(XUA_ASP_E_ASPSM_BEAT) |
S(XUA_ASP_E_ASPSM_BEAT_ACK),
S(XUA_ASP_E_ASPSM_BEAT_ACK) |
S(XUA_ASP_E_AS_ASSIGNED),
.allstate_action = xua_asp_allstate,
.cleanup = xua_asp_fsm_cleanup,
};
@ -837,18 +856,22 @@ enum ipa_asp_fsm_t {
static int get_fd_from_iafp(struct ipa_asp_fsm_priv *iafp)
{
struct osmo_ss7_asp *asp = iafp->asp;
struct osmo_fd *ofd;
int fd;
if (asp->server)
ofd = osmo_stream_srv_get_ofd(asp->server);
fd = osmo_stream_srv_get_fd(asp->server);
else if (asp->client)
ofd = osmo_stream_cli_get_ofd(asp->client);
fd = osmo_stream_cli_get_fd(asp->client);
else
return -1;
return ofd->fd;
return fd;
}
/***************
** FSM states **
***************/
/* Server + Client: Initial State, wait for M-ASP-UP.req */
static void ipa_asp_fsm_down(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
@ -866,7 +889,7 @@ static void ipa_asp_fsm_down(struct osmo_fsm_inst *fi, uint32_t event, void *dat
}
} else {
/* Client: We simply wait for an ID GET */
osmo_fsm_inst_state_chg(fi, IPA_ASP_S_WAIT_ID_ACK, 10, T_WAIT_ID_ACK);
osmo_fsm_inst_state_chg(fi, IPA_ASP_S_WAIT_ID_GET, 10, T_WAIT_ID_GET);
}
break;
}
@ -938,23 +961,11 @@ out_err:
/* Server: We're waiting for an ID ACK */
static void ipa_asp_fsm_wait_id_ack2(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct ipa_asp_fsm_priv *iafp = fi->priv;
struct osmo_ss7_asp *asp = iafp->asp;
struct osmo_ss7_instance *inst = asp->inst;
/* We use routing-context '0' here, as that's the only one we support in IPA */
struct osmo_ss7_as *as = osmo_ss7_as_find_by_rctx(inst, 0);
OSMO_ASSERT(as);
switch (event) {
case IPA_ASP_E_ID_ACK:
/* ACK received, we can go to active state now. The
* ACTIVE onenter function will inform the AS */
osmo_fsm_inst_state_chg(fi, IPA_ASP_S_ACTIVE, 0, 0);
/* As opposed to M3UA, there is no RKM and we have to implicitly automatically add
* a route once an IPA connection has come up */
osmo_ss7_route_create(inst->rtable_system, as->cfg.routing_key.pc, 0xffffff,
as->cfg.name);
break;
}
}
@ -967,6 +978,7 @@ static void ipa_asp_fsm_wait_id_get(struct osmo_fsm_inst *fi, uint32_t event, vo
struct msgb *msg_get, *msg_resp;
const uint8_t *req_data;
int data_len;
int fd;
switch (event) {
case IPA_ASP_E_ID_GET:
@ -989,6 +1001,16 @@ static void ipa_asp_fsm_wait_id_get(struct osmo_fsm_inst *fi, uint32_t event, vo
osmo_ss7_asp_send(asp, msg_resp);
osmo_fsm_inst_state_chg(fi, IPA_ASP_S_WAIT_ID_ACK, 10, T_WAIT_ID_ACK);
break;
case IPA_ASP_E_ID_ACK:
/* Some SCCPLite MSCs are known to send an ACK directly instead
* of GET. Support them and skip the GET+RESP handshake by
* sending ACK2 to server directly */
fd = get_fd_from_iafp(iafp);
if (fd >= 0) {
ipaccess_send_id_ack(fd);
osmo_fsm_inst_state_chg(fi, IPA_ASP_S_ACTIVE, 0, 0);
}
break;
}
}
@ -1010,29 +1032,10 @@ static void ipa_asp_fsm_wait_id_ack(struct osmo_fsm_inst *fi, uint32_t event, vo
}
}
static void ipa_asp_fsm_del_route(struct ipa_asp_fsm_priv *iafp)
static void ipa_asp_fsm_active_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct osmo_ss7_asp *asp = iafp->asp;
struct osmo_ss7_instance *inst = asp->inst;
/* We use routing-context '0' here, as that's the only one we support in IPA */
struct osmo_ss7_as *as = osmo_ss7_as_find_by_rctx(inst, 0);
struct osmo_ss7_route *rt;
OSMO_ASSERT(as);
/* find the route which we have created if we ever reached ipa_asp_fsm_wait_id_ack2 */
rt = osmo_ss7_route_find_dpc_mask(inst->rtable_system, as->cfg.routing_key.pc, 0xffffff);
/* no route found, bail out */
if (!rt)
return;
/* route points to different AS, bail out */
if (rt->dest.as != as)
return;
osmo_ss7_route_destroy(rt);
/* FIXME: Why don't we also delete this timer if we return early above?
* FIXME: Where is this timer even scheduled? */
osmo_timer_del(&iafp->pong_timer);
dispatch_to_all_as(fi, XUA_ASPAS_ASP_INACTIVE_IND);
dispatch_to_all_as(fi, XUA_ASPAS_ASP_ACTIVE_IND);
}
/* Server + Client: We're actively transmitting user data */
@ -1041,17 +1044,20 @@ static void ipa_asp_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *d
switch (event) {
case XUA_ASP_E_M_ASP_DOWN_REQ:
case XUA_ASP_E_M_ASP_INACTIVE_REQ:
ipa_asp_fsm_del_route(fi->priv);
osmo_fsm_inst_state_chg(fi, IPA_ASP_S_DOWN, 0, 0);
break;
}
}
static void ipa_asp_fsm_inactive_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
dispatch_to_all_as(fi, XUA_ASPAS_ASP_INACTIVE_IND);
}
static void ipa_asp_fsm_inactive(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case XUA_ASP_E_M_ASP_DOWN_REQ:
ipa_asp_fsm_del_route(fi->priv);
osmo_fsm_inst_state_chg(fi, IPA_ASP_S_DOWN, 0, 0);
break;
}
@ -1060,6 +1066,7 @@ static void ipa_asp_fsm_inactive(struct osmo_fsm_inst *fi, uint32_t event, void
static void ipa_asp_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct ipa_asp_fsm_priv *iafp = fi->priv;
struct osmo_ss7_as *as;
int fd;
switch (event) {
@ -1079,22 +1086,20 @@ static void ipa_asp_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *dat
/* stop timer, if any */
osmo_timer_del(&iafp->pong_timer);
break;
case XUA_ASP_E_AS_ASSIGNED:
as = data;
osmo_talloc_replace_string(iafp->ipa_unit, &iafp->ipa_unit->unit_name, as->cfg.name);
/* Now that the AS is known, start the client side: */
if (iafp->role == OSMO_SS7_ASP_ROLE_ASP && fi->state == IPA_ASP_S_DOWN) {
LOGPFSML(fi, LOGL_NOTICE, "Bringing up ASP now once it has been assigned to an AS\n");
osmo_fsm_inst_dispatch(fi, XUA_ASP_E_M_ASP_UP_REQ, NULL);
}
break;
default:
break;
}
}
static void ipa_asp_fsm_active_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
dispatch_to_all_as(fi, XUA_ASPAS_ASP_INACTIVE_IND);
dispatch_to_all_as(fi, XUA_ASPAS_ASP_ACTIVE_IND);
}
static void ipa_asp_fsm_inactive_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
dispatch_to_all_as(fi, XUA_ASPAS_ASP_INACTIVE_IND);
}
static void ipa_pong_timer_cb(void *_fi)
{
struct osmo_fsm_inst *fi = _fi;
@ -1119,7 +1124,7 @@ static const struct osmo_fsm_state ipa_asp_states[] = {
[IPA_ASP_S_DOWN] = {
.in_event_mask = S(XUA_ASP_E_M_ASP_UP_REQ) |
S(XUA_ASP_E_SCTP_EST_IND),
.out_state_mask = S(IPA_ASP_S_WAIT_ID_ACK) |
.out_state_mask = S(IPA_ASP_S_WAIT_ID_GET) |
S(IPA_ASP_S_WAIT_ID_RESP),
.name = "ASP_DOWN",
.action = ipa_asp_fsm_down,
@ -1144,8 +1149,10 @@ static const struct osmo_fsm_state ipa_asp_states[] = {
},
/* Client Side */
[IPA_ASP_S_WAIT_ID_GET] = {
.in_event_mask = S(IPA_ASP_E_ID_GET),
.out_state_mask = S(IPA_ASP_S_WAIT_ID_ACK),
.in_event_mask = S(IPA_ASP_E_ID_GET) |
S(IPA_ASP_E_ID_ACK), /* support broken MSCs skipping GET+RESP */
.out_state_mask = S(IPA_ASP_S_WAIT_ID_ACK) |
S(IPA_ASP_S_ACTIVE), /* support broken MSCs skipping GET+RESP */
.name = "WAIT_ID_GET",
.action = ipa_asp_fsm_wait_id_get,
},
@ -1176,11 +1183,6 @@ static const struct osmo_fsm_state ipa_asp_states[] = {
},
};
static void ipa_asp_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
ipa_asp_fsm_del_route(fi->priv);
}
struct osmo_fsm ipa_asp_fsm = {
.name = "IPA_ASP",
.states = ipa_asp_states,
@ -1191,13 +1193,13 @@ struct osmo_fsm ipa_asp_fsm = {
.allstate_event_mask = S(XUA_ASP_E_SCTP_COMM_DOWN_IND) |
S(XUA_ASP_E_SCTP_RESTART_IND) |
S(XUA_ASP_E_ASPSM_BEAT) |
S(XUA_ASP_E_ASPSM_BEAT_ACK),
S(XUA_ASP_E_ASPSM_BEAT_ACK) |
S(XUA_ASP_E_AS_ASSIGNED),
.allstate_action = ipa_asp_allstate,
.cleanup = ipa_asp_fsm_cleanup,
};
/*! \brief Start a new ASP finite stae machine for given ASP
/*! \brief Start a new ASP finite state machine for given ASP
* \param[in] asp Application Server Process for which to start FSM
* \param[in] role Role (ASP, SG, IPSP) of this FSM
* \param[in] log_level Logging Level for ASP FSM logging
@ -1207,6 +1209,9 @@ static struct osmo_fsm_inst *ipa_asp_fsm_start(struct osmo_ss7_asp *asp,
{
struct osmo_fsm_inst *fi;
struct ipa_asp_fsm_priv *iafp;
struct osmo_ss7_as *as = ipa_find_as_for_asp(asp);
const char *unit_name;
bool can_start = true;
/* allocate as child of AS? */
fi = osmo_fsm_inst_alloc(&ipa_asp_fsm, asp, NULL, log_level, asp->cfg.name);
@ -1216,16 +1221,33 @@ static struct osmo_fsm_inst *ipa_asp_fsm_start(struct osmo_ss7_asp *asp,
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
return NULL;
}
if (as) {
unit_name = as->cfg.name;
} else if (asp->dyn_allocated) {
LOGPFSML(fi, LOGL_INFO, "Dynamic ASP is not assigned to any AS, "
"using ASP name instead of AS name as ipa_unit_name\n");
unit_name = asp->cfg.name;
} else {
/* ASP in client mode will be brought up when this ASP is added
* to an AS, see XUA_ASP_E_AS_ASSIGNED. */
if (role == OSMO_SS7_ASP_ROLE_ASP) {
LOGPFSML(fi, LOGL_NOTICE, "ASP is not assigned to any AS. ASP bring up delayed\n");
can_start = false;
}
unit_name = asp->cfg.name;
}
iafp->role = role;
iafp->asp = asp;
iafp->ipa_unit = talloc_zero(iafp, struct ipaccess_unit);
iafp->ipa_unit->unit_name = talloc_strdup(iafp->ipa_unit, asp->cfg.name);
iafp->ipa_unit->unit_name = talloc_strdup(iafp->ipa_unit, unit_name);
iafp->pong_timer.cb = ipa_pong_timer_cb;
iafp->pong_timer.data = fi;
fi->priv = iafp;
if (role == OSMO_SS7_ASP_ROLE_ASP)
if (can_start && role == OSMO_SS7_ASP_ROLE_ASP)
osmo_fsm_inst_dispatch(fi, XUA_ASP_E_M_ASP_UP_REQ, NULL);
return fi;

View File

@ -28,6 +28,9 @@ enum xua_asp_event {
XUA_ASP_E_ASPSM_BEAT,
XUA_ASP_E_ASPSM_BEAT_ACK,
/* The ASP was added to an AS. data: (struct osmo_ss7_as *) */
XUA_ASP_E_AS_ASSIGNED,
/* IPA specific */
IPA_ASP_E_ID_RESP,
IPA_ASP_E_ID_ACK,

View File

@ -1,5 +1,5 @@
/* Default XUA Layer Manager */
/* (C) 2017 by Harald Welte <laforge@gnumonks.org>
/* (C) 2017-2021 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0+
@ -76,17 +76,53 @@ static const struct value_string lm_event_names[] = {
{ 0, NULL }
};
enum lm_timer {
T_WAIT_ASP_UP,
T_WAIT_NOTIFY,
T_WAIT_NOTIFY_RKM,
T_WAIT_RK_REG_RESP,
/***********************************************************************
* Timer Handling
***********************************************************************/
const struct osmo_tdef ss7_asp_lm_timer_defaults[SS7_ASP_LM_TIMERS_LEN] = {
{ .T = SS7_ASP_LM_T_WAIT_ASP_UP, .default_val = 20, .unit = OSMO_TDEF_S,
.desc = "Restart ASP after timeout waiting for ASP UP (SG role) / ASP UP ACK (ASP role) (s)" },
{ .T = SS7_ASP_LM_T_WAIT_NOTIFY, .default_val = 2, .unit = OSMO_TDEF_S,
.desc = "Restart ASP after timeout waiting for NOTIFY (s)" },
{ .T = SS7_ASP_LM_T_WAIT_NOTIY_RKM, .default_val = 20, .unit = OSMO_TDEF_S,
.desc = "Restart ASP after timeout waiting for NOTIFY after RKM registration (s)" },
{ .T = SS7_ASP_LM_T_WAIT_RK_REG_RESP, .default_val = 10, .unit = OSMO_TDEF_S,
.desc = "Restart ASP after timeout waiting for RK_REG_RESP (s)" },
{}
};
/* Appendix C.4 of ITU-T Q.714 */
const struct value_string ss7_asp_lm_timer_names[] = {
{ SS7_ASP_LM_T_WAIT_ASP_UP, "wait_asp_up" },
{ SS7_ASP_LM_T_WAIT_NOTIFY, "wait_notify" },
{ SS7_ASP_LM_T_WAIT_NOTIY_RKM, "wait_notify_rkm" },
{ SS7_ASP_LM_T_WAIT_RK_REG_RESP, "wait_rk_reg_resp" },
{}
};
osmo_static_assert(ARRAY_SIZE(ss7_asp_lm_timer_defaults) == (SS7_ASP_LM_TIMERS_LEN) &&
ARRAY_SIZE(ss7_asp_lm_timer_names) == (SS7_ASP_LM_TIMERS_LEN),
assert_ss7_asp_lm_timer_count);
static const struct osmo_tdef_state_timeout lm_fsm_timeouts[32] = {
[S_IDLE] = { },
[S_WAIT_ASP_UP] = { .T = SS7_ASP_LM_T_WAIT_ASP_UP },
[S_WAIT_NOTIFY] = { .T = SS7_ASP_LM_T_WAIT_NOTIFY }, /* SS7_ASP_LM_T_WAIT_NOTIY_RKM if coming from S_RKM_REG */
[S_RKM_REG] = { .T = SS7_ASP_LM_T_WAIT_RK_REG_RESP },
[S_ACTIVE] = { },
};
struct lm_fsm_priv {
struct osmo_ss7_asp *asp;
};
#define lm_fsm_state_chg(fi, NEXT_STATE) \
osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \
lm_fsm_timeouts, \
((struct lm_fsm_priv *)(fi->priv))->asp->cfg.T_defs_lm, \
-1)
static struct osmo_ss7_as *find_first_as_in_asp(struct osmo_ss7_asp *asp)
{
struct osmo_ss7_as *as;
@ -138,8 +174,8 @@ static void lm_idle(struct osmo_fsm_inst *fi, uint32_t event, void *data)
switch (event) {
case LM_E_SCTP_EST_IND:
/* Try to transition to ASP-UP, wait for 20s */
osmo_fsm_inst_state_chg(fi, S_WAIT_ASP_UP, 20, T_WAIT_ASP_UP);
/* Try to transition to ASP-UP, wait to receive message for a few seconds */
lm_fsm_state_chg(fi, S_WAIT_ASP_UP);
osmo_fsm_inst_dispatch(lmp->asp->fi, XUA_ASP_E_M_ASP_UP_REQ, NULL);
break;
}
@ -149,9 +185,9 @@ static void lm_wait_asp_up(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case LM_E_ASP_UP_CONF:
/* ASP is sup, wait for some time if any NOTIFY
/* ASP is up, wait for some time if any NOTIFY
* indications about AS in this ASP are received */
osmo_fsm_inst_state_chg(fi, S_WAIT_NOTIFY, 2, T_WAIT_NOTIFY);
lm_fsm_state_chg(fi, S_WAIT_NOTIFY);
break;
}
}
@ -164,16 +200,24 @@ static int lm_timer_cb(struct osmo_fsm_inst *fi)
struct osmo_ss7_as *as;
switch (fi->T) {
case T_WAIT_ASP_UP:
case SS7_ASP_LM_T_WAIT_ASP_UP:
/* we have been waiting for the ASP to come up, but it
* failed to do so */
LOGPFSML(fi, LOGL_NOTICE, "Peer didn't send any ASP_UP in time! Restarting ASP\n");
restart_asp(fi);
break;
case T_WAIT_NOTIFY:
case SS7_ASP_LM_T_WAIT_NOTIFY:
if (lmp->asp->cfg.quirks & OSMO_SS7_ASP_QUIRK_NO_NOTIFY) {
/* some implementations don't send the NOTIFY which they SHOULD
* according to RFC4666 (see OS#5145) */
LOGPFSM(fi, "quirk no_notify active; locally emulate AS-INACTIVE.ind\n");
osmo_fsm_inst_dispatch(fi, LM_E_AS_INACTIVE_IND, NULL);
break;
}
/* No AS has reported via NOTIFY that is was
* (statically) configured at the SG for this ASP, so
* let's dynamically register */
osmo_fsm_inst_state_chg(fi, S_RKM_REG, 10, T_WAIT_RK_REG_RESP);
lm_fsm_state_chg(fi, S_RKM_REG);
prim = xua_xlm_prim_alloc(OSMO_XLM_PRIM_M_RK_REG, PRIM_OP_REQUEST);
OSMO_ASSERT(prim);
as = find_first_as_in_asp(lmp->asp);
@ -187,12 +231,12 @@ static int lm_timer_cb(struct osmo_fsm_inst *fi)
prim->u.rk_reg.traf_mode = as->cfg.mode;
osmo_xlm_sap_down(lmp->asp, &prim->oph);
break;
case T_WAIT_NOTIFY_RKM:
case SS7_ASP_LM_T_WAIT_NOTIY_RKM:
/* No AS has reported via NOTIFY even after dynamic RKM
* configuration */
restart_asp(fi);
break;
case T_WAIT_RK_REG_RESP:
case SS7_ASP_LM_T_WAIT_RK_REG_RESP:
/* timeout of registration of routing key */
restart_asp(fi);
break;
@ -212,7 +256,7 @@ static void lm_wait_notify(struct osmo_fsm_inst *fi, uint32_t event, void *data)
if (oxp->u.notify.status_type == M3UA_NOTIFY_T_STATCHG &&
(oxp->u.notify.status_info == M3UA_NOTIFY_I_AS_INACT ||
oxp->u.notify.status_info == M3UA_NOTIFY_I_AS_PEND)) {
osmo_fsm_inst_state_chg(fi, S_ACTIVE, 0, 0);
lm_fsm_state_chg(fi, S_ACTIVE);
osmo_fsm_inst_dispatch(lmp->asp->fi, XUA_ASP_E_M_ASP_ACTIVE_REQ, NULL);
}
break;
@ -221,7 +265,7 @@ static void lm_wait_notify(struct osmo_fsm_inst *fi, uint32_t event, void *data)
* the SG, and that this AS is currently inactive */
/* request the ASP to go into active state (which
* hopefully will bring the AS to active, too) */
osmo_fsm_inst_state_chg(fi, S_ACTIVE, 0, 0);
lm_fsm_state_chg(fi, S_ACTIVE);
osmo_fsm_inst_dispatch(lmp->asp->fi, XUA_ASP_E_M_ASP_ACTIVE_REQ, NULL);
break;
}
@ -229,6 +273,7 @@ static void lm_wait_notify(struct osmo_fsm_inst *fi, uint32_t event, void *data)
static void lm_rkm_reg(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct lm_fsm_priv *lmp = fi->priv;
struct osmo_xlm_prim *oxp;
int rc;
@ -239,14 +284,15 @@ static void lm_rkm_reg(struct osmo_fsm_inst *fi, uint32_t event, void *data)
LOGPFSML(fi, LOGL_NOTICE, "Received RKM_REG_RSP with negative result\n");
restart_asp(fi);
} else {
unsigned long timeout_sec;
rc = handle_reg_conf(fi, oxp->u.rk_reg.key.l_rk_id, oxp->u.rk_reg.key.context);
if (rc < 0)
restart_asp(fi);
/* RKM registration was successful, we can
* transition to WAIT_NOTIFY state and assume
* that an NOTIFY/AS-INACTIVE arrives within 20
* seconds */
osmo_fsm_inst_state_chg(fi, S_WAIT_NOTIFY, 20, T_WAIT_NOTIFY_RKM);
/* RKM registration was successful, we can transition to WAIT_NOTIFY
* state and assume that an NOTIFY/AS-INACTIVE arrives within
* T_WAIT_NOTIFY_RKM seconds */
timeout_sec = osmo_tdef_get(lmp->asp->cfg.T_defs_lm, SS7_ASP_LM_T_WAIT_NOTIY_RKM, OSMO_TDEF_S, -1);
osmo_fsm_inst_state_chg(fi, S_WAIT_NOTIFY, timeout_sec, SS7_ASP_LM_T_WAIT_NOTIY_RKM);
}
break;
}

View File

@ -1,8 +1,12 @@
#pragma once
#include <osmocom/core/tdef.h>
#include <osmocom/sigtran/osmo_ss7.h>
#include <osmocom/sigtran/xua_msg.h>
#define M3UA_MSG_SIZE 2048
#define M3UA_MSG_HEADROOM 512
struct osmo_sccp_addr;
struct m3ua_data_hdr;
@ -18,10 +22,33 @@ struct msgb *osmo_sua_to_sccp(struct xua_msg *xua);
int sua_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg);
int sua_tx_xua_as(struct osmo_ss7_as *as, struct xua_msg *xua);
void sua_tx_snm_available(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
const uint32_t *aff_pc, unsigned int num_aff_pc, const uint32_t *aff_ssn,
const uint32_t *smi, const char *info_string, bool available);
void sua_tx_snm_congestion(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
const uint32_t *aff_pc, unsigned int num_aff_pc, const uint32_t *ssn,
const uint8_t cong_level, const char *info_string);
void sua_tx_dupu(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
uint32_t dpc, uint16_t user, uint16_t cause, const char *info_str);
struct osmo_mtp_prim *m3ua_to_xfer_ind(struct xua_msg *xua);
int m3ua_hmdc_rx_from_l2(struct osmo_ss7_instance *inst, struct xua_msg *xua);
int m3ua_tx_xua_as(struct osmo_ss7_as *as, struct xua_msg *xua);
void m3ua_tx_snm_available(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
const uint32_t *aff_pc, unsigned int num_aff_pc,
const char *info_string, bool available);
void m3ua_tx_snm_congestion(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
const uint32_t *aff_pc, unsigned int num_aff_pc,
const uint32_t *concerned_dpc, const uint8_t *cong_level,
const char *info_string);
void m3ua_tx_dupu(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
uint32_t dpc, uint16_t user, uint16_t cause, const char *info_str);
void xua_snm_rx_daud(struct osmo_ss7_asp *asp, struct xua_msg *xua);
void xua_snm_rx_duna(struct osmo_ss7_asp *asp, struct osmo_ss7_as *as, struct xua_msg *xua);
void xua_snm_rx_dava(struct osmo_ss7_asp *asp, struct osmo_ss7_as *as, struct xua_msg *xua);
void xua_snm_rx_dupu(struct osmo_ss7_asp *asp, struct osmo_ss7_as *as, struct xua_msg *xua);
void xua_snm_rx_scon(struct osmo_ss7_asp *asp, struct osmo_ss7_as *as, struct xua_msg *xua);
int m3ua_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg);
struct msgb *m3ua_msgb_alloc(const char *name);
@ -59,6 +86,25 @@ void xua_asp_send_xlm_prim_simple(struct osmo_ss7_asp *asp,
enum osmo_xlm_prim_type prim_type,
enum osmo_prim_operation op);
void xua_snm_pc_available(struct osmo_ss7_as *as, const uint32_t *aff_pc,
unsigned int num_aff_pc, const char *info_str, bool available);
enum ss7_asp_lm_timer {
/* 0 kept unused on purpose since it's handled specially by osmo_fsm */
SS7_ASP_LM_T_WAIT_ASP_UP = 1,
SS7_ASP_LM_T_WAIT_NOTIFY,
SS7_ASP_LM_T_WAIT_NOTIY_RKM,
SS7_ASP_LM_T_WAIT_RK_REG_RESP,
/* This must remain the last item: */
SS7_ASP_LM_TIMERS_LEN
};
extern const struct osmo_tdef ss7_asp_lm_timer_defaults[SS7_ASP_LM_TIMERS_LEN];
extern const struct value_string ss7_asp_lm_timer_names[];
static inline const char *ss7_asp_lm_timer_name(enum ss7_asp_lm_timer val)
{ return get_value_string(ss7_asp_lm_timer_names, val); }
extern struct osmo_fsm xua_default_lm_fsm;
extern const struct value_string m3ua_rkm_reg_status_vals[];
extern const struct value_string m3ua_rkm_dereg_status_vals[];
@ -69,9 +115,12 @@ extern const struct value_string m3ua_rkm_dereg_status_vals[];
int xua_as_transmit_msg(struct osmo_ss7_as *as, struct msgb *msg);
int xua_find_as_for_asp(struct osmo_ss7_as **as, const struct osmo_ss7_asp *asp,
const struct xua_msg_part *rctx_ie);
int ipa_tx_xua_as(struct osmo_ss7_as *as, struct xua_msg *xua);
int ipa_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg);
int ipa_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg, uint8_t sls);
struct osmo_ss7_as *ipa_find_as_for_asp(struct osmo_ss7_asp *asp);
int osmo_isup_party_parse(char *out_digits, const uint8_t *in,
unsigned int in_num_bytes, bool odd);

View File

@ -59,7 +59,7 @@ void xua_msg_free(struct xua_msg *msg)
}
int xua_msg_add_data(struct xua_msg *msg, uint16_t tag,
uint16_t len, uint8_t *dat)
uint16_t len, const uint8_t *dat)
{
struct xua_msg_part *part;
@ -283,13 +283,13 @@ int xua_msg_add_u32(struct xua_msg *xua, uint16_t iei, uint32_t val)
return xua_msg_add_data(xua, iei, sizeof(val_n), (uint8_t *) &val_n);
}
uint32_t xua_msg_part_get_u32(struct xua_msg_part *part)
uint32_t xua_msg_part_get_u32(const struct xua_msg_part *part)
{
OSMO_ASSERT(part->len >= 4);
return ntohl(*(uint32_t *)part->dat);
}
uint32_t xua_msg_get_u32(struct xua_msg *xua, uint16_t iei)
uint32_t xua_msg_get_u32(const struct xua_msg *xua, uint16_t iei)
{
struct xua_msg_part *part = xua_msg_find_tag(xua, iei);
if (!part)
@ -297,6 +297,47 @@ uint32_t xua_msg_get_u32(struct xua_msg *xua, uint16_t iei)
return xua_msg_part_get_u32(part);
}
/* get a U32 IE in host-byte-order, returning whether it exists (!= NULL) or not (== NULL) */
const uint32_t *xua_msg_get_u32p(const struct xua_msg *xua, uint16_t iei, uint32_t *out)
{
struct xua_msg_part *part = xua_msg_find_tag(xua, iei);
if (!part)
return NULL;
*out = xua_msg_part_get_u32(part);
return out;
}
const char *xua_msg_part_get_str(const struct xua_msg_part *part)
{
static char __thread buf[256];
if (part->len == 0)
return "";
/* RFC3868 3.9.4: Length of the INFO String parameter is from 0 to 255 octets */
if (part->len > 255)
return "<invalid-string-len>";
memcpy(buf, part->dat, part->len);
buf[part->len] = '\0';
return buf;
}
const char *xua_msg_get_str(const struct xua_msg *xua, uint16_t iei)
{
struct xua_msg_part *part = xua_msg_find_tag(xua, iei);
if (!part)
return NULL;
return xua_msg_part_get_str(part);
}
int xua_msg_get_len(const struct xua_msg *xua, uint16_t iei)
{
struct xua_msg_part *part = xua_msg_find_tag(xua, iei);
if (!part)
return -1;
return part->len;
}
void xua_part_add_gt(struct msgb *msg, const struct osmo_sccp_gt *gt)
{
uint16_t *len_ptr;
@ -376,9 +417,9 @@ int xua_msg_add_sccp_addr(struct xua_msg *xua, uint16_t iei, const struct osmo_s
msgb_t16l16vp_put_u32(tmp, SUA_IEI_SSN, addr->ssn);
}
if (addr->presence & OSMO_SCCP_ADDR_T_IPv4) {
msgb_t16l16vp_put_u32(tmp, SUA_IEI_IPv4, ntohl(addr->ip.v4.s_addr));
msgb_t16l16vp_put(tmp, SUA_IEI_IPv4, sizeof(addr->ip.v4), (const uint8_t *)&addr->ip.v4);
} else if (addr->presence & OSMO_SCCP_ADDR_T_IPv6) {
/* FIXME: IPv6 address */
msgb_t16l16vp_put(tmp, SUA_IEI_IPv6, sizeof(addr->ip.v6), (const uint8_t *)&addr->ip.v6);
}
rc = xua_msg_add_data(xua, iei, msgb_length(tmp), tmp->data);
msgb_free(tmp);
@ -478,37 +519,23 @@ int xua_dialect_check_all_mand_ies(const struct xua_dialect *dialect, struct xua
return 1;
}
static void append_to_buf(char *buf, bool *comma, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (!comma || *comma == true) {
strcat(buf, ",");
} else if (comma)
*comma = true;
vsprintf(buf+strlen(buf), fmt, ap);
va_end(ap);
}
char *xua_msg_dump(struct xua_msg *xua, const struct xua_dialect *dialect)
{
static char buf[1024];
struct osmo_strbuf sb = { .buf = buf, .len = sizeof(buf) };
struct xua_msg_part *part;
const struct xua_msg_class *xmc = NULL;
bool comma = false;
if (dialect)
xmc = dialect->class[xua->hdr.msg_class];
buf[0] = '\0';
append_to_buf(buf, &comma, "HDR=(%s,V=%u,LEN=%u)",
xua_hdr_dump(xua, dialect),
xua->hdr.version, xua->hdr.msg_length);
OSMO_STRBUF_PRINTF(sb, "HDR=(%s,V=%u,LEN=%u)", xua_hdr_dump(xua, dialect),
xua->hdr.version, ntohl(xua->hdr.msg_length));
llist_for_each_entry(part, &xua->headers, entry)
append_to_buf(buf, NULL, " PART(T=%s,L=%u,D=%s)",
xua_class_iei_name(xmc, part->tag), part->len,
osmo_hexdump_nospc(part->dat, part->len));
return buf;
OSMO_STRBUF_PRINTF(sb, ", PART(T=%s,L=%u,D=%s)",
xua_class_iei_name(xmc, part->tag), part->len,
osmo_hexdump_nospc(part->dat, part->len));
return sb.buf;
}

View File

@ -350,6 +350,12 @@ static int handle_rkey_dereg(struct osmo_ss7_asp *asp, uint32_t rctx,
return -1;
}
/* Reject if not dynamically allocated (OS#4239) */
if (!as->rkm_dyn_allocated) {
msgb_append_dereg_res(resp, M3UA_RKM_DEREG_ERR_NOT_REGD, 0);
return -1;
}
/* Reject if ASP is not even part of AS */
if (!osmo_ss7_as_has_asp(as, asp)) {
msgb_append_dereg_res(resp, M3UA_RKM_DEREG_ERR_INVAL_RCTX, 0);

104
src/xua_shared.c Normal file
View File

@ -0,0 +1,104 @@
/* Shared code between M3UA and SUA implementation */
/* (C) 2015-2021 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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 <stdint.h>
#include <unistd.h>
#include <string.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/logging.h>
#include <osmocom/sigtran/xua_msg.h>
#include <osmocom/sigtran/osmo_ss7.h>
#include <osmocom/sigtran/protocol/m3ua.h>
#include <osmocom/sigtran/protocol/sua.h>
#include "xua_internal.h"
/* if given ASP only has one AS, return that AS */
static struct osmo_ss7_as *find_single_as_for_asp(const struct osmo_ss7_asp *asp)
{
struct osmo_ss7_as *as, *as_found = NULL;
llist_for_each_entry(as, &asp->inst->as_list, list) {
if (!osmo_ss7_as_has_asp(as, asp))
continue;
/* check if we already had found another AS within this ASP -> not unique */
if (as_found)
return NULL;
as_found = as;
}
return as_found;
}
/* this is why we can use the M3UA constants below in a function shared between M3UA + SUA */
osmo_static_assert(M3UA_ERR_INVAL_ROUT_CTX == SUA_ERR_INVAL_ROUT_CTX, _err_rctx);
osmo_static_assert(M3UA_ERR_NO_CONFGD_AS_FOR_ASP == SUA_ERR_NO_CONFGD_AS_FOR_ASP, _err_as_for_asp);
/*! Find the AS for given ASP + optional routing context IE.
* if rctx_ie == NULL, we assume that this ASP is only part of a single AS;
* if rctx_ie is given, then we look-up the ASP based on the routing context,
* and verify that this ASP is part of it.
* \param[out] as caller-provided address-of-pointer to store the found AS
* \param[in] asp ASP for which we want to look-up the AS
* \param[in] rctx_ie routing context IE (may be NULL) to use for look-up
* \returns 0 in case of success; {M3UA,SUA}_ERR_* code in case of error. */
int xua_find_as_for_asp(struct osmo_ss7_as **as, const struct osmo_ss7_asp *asp,
const struct xua_msg_part *rctx_ie)
{
int log_ss = osmo_ss7_asp_get_log_subsys(asp);
*as = NULL;
if (rctx_ie) {
uint32_t rctx = xua_msg_part_get_u32(rctx_ie);
/* Use routing context IE to look up the AS for which the
* message was received. */
*as = osmo_ss7_as_find_by_rctx(asp->inst, rctx);
if (!*as) {
LOGPASP(asp, log_ss, LOGL_ERROR, "%s(): invalid routing context: %u\n",
__func__, rctx);
return M3UA_ERR_INVAL_ROUT_CTX;
}
/* Verify that this ASP is part of the AS. */
if (!osmo_ss7_as_has_asp(*as, asp)) {
LOGPASP(asp, log_ss, LOGL_ERROR,
"%s(): This Application Server Process is not part of the AS %s "
"resolved by routing context %u\n", __func__, (*as)->cfg.name, rctx);
return M3UA_ERR_NO_CONFGD_AS_FOR_ASP;
}
} else {
/* no explicit routing context; this only works if there is only one AS in the ASP */
*as = find_single_as_for_asp(asp);
if (!*as) {
LOGPASP(asp, log_ss, LOGL_ERROR,
"%s(): ASP sent M3UA without Routing Context IE but unable to uniquely "
"identify the AS for this message\n", __func__);
return M3UA_ERR_INVAL_ROUT_CTX;
}
}
return 0;
}

474
src/xua_snm.c Normal file
View File

@ -0,0 +1,474 @@
/* M3UA/SUA [S]SNM Handling */
/* (C) 2021 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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 <stdint.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/sigtran/osmo_ss7.h>
#include <osmocom/sigtran/protocol/m3ua.h>
#include <osmocom/sigtran/protocol/sua.h>
#include <osmocom/sigtran/protocol/mtp.h>
#include "xua_internal.h"
#include "sccp_internal.h"
/* we can share this code between M3UA and SUA as the below conditions are true */
osmo_static_assert(M3UA_SNM_DUNA == SUA_SNM_DUNA, _sa_duna);
osmo_static_assert(M3UA_SNM_DAVA == SUA_SNM_DAVA, _sa_dava);
osmo_static_assert(M3UA_SNM_DAUD == SUA_SNM_DAUD, _sa_dava);
osmo_static_assert(M3UA_IEI_AFFECTED_PC == SUA_IEI_AFFECTED_PC, _sa_aff_pc);
osmo_static_assert(M3UA_IEI_ROUTE_CTX == SUA_IEI_ROUTE_CTX, _sa_rctx);
osmo_static_assert(M3UA_IEI_INFO_STRING == SUA_IEI_INFO_STRING, _sa_inf_str);
static const char *format_affected_pcs_c(void *ctx, const struct osmo_ss7_instance *s7i,
const struct xua_msg_part *ie_aff_pc)
{
const uint32_t *aff_pc = (const uint32_t *) ie_aff_pc->dat;
unsigned int num_aff_pc = ie_aff_pc->len / sizeof(uint32_t);
char *out = talloc_strdup(ctx, "");
int i;
for (i = 0; i < num_aff_pc; i++) {
uint32_t _aff_pc = ntohl(aff_pc[i]);
uint32_t pc = _aff_pc & 0xffffff;
uint8_t mask = _aff_pc >> 24;
/* append point code + mask */
out = talloc_asprintf_append(out, "%s%s/%u, ", i == 0 ? "" : ", ",
osmo_ss7_pointcode_print(s7i, pc), mask);
}
return out;
}
/* obtain all routing contexts (in network byte order) that exist within the given ASP */
static unsigned int get_all_rctx_for_asp(uint32_t *rctx, unsigned int rctx_size,
struct osmo_ss7_asp *asp, struct osmo_ss7_as *excl_as)
{
unsigned int count = 0;
struct osmo_ss7_as *as;
llist_for_each_entry(as, &asp->inst->as_list, list) {
if (as == excl_as)
continue;
if (!osmo_ss7_as_has_asp(as, asp))
continue;
if (as->cfg.routing_key.context == 0)
continue;
if (count >= rctx_size)
break;
rctx[count] = htonl(as->cfg.routing_key.context);
count++;
}
return count;
}
static void xua_tx_snm_available(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
const uint32_t *aff_pc, unsigned int num_aff_pc,
const char *info_str, bool available)
{
switch (asp->cfg.proto) {
case OSMO_SS7_ASP_PROT_M3UA:
m3ua_tx_snm_available(asp, rctx, num_rctx, aff_pc, num_aff_pc, info_str, available);
break;
case OSMO_SS7_ASP_PROT_SUA:
sua_tx_snm_available(asp, rctx, num_rctx, aff_pc, num_aff_pc, NULL, NULL, info_str, available);
break;
default:
break;
}
}
static void xua_tx_upu(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
uint32_t dpc, uint16_t user, uint16_t cause, const char *info_str)
{
switch (asp->cfg.proto) {
case OSMO_SS7_ASP_PROT_M3UA:
m3ua_tx_dupu(asp, rctx, num_rctx, dpc, user, cause, info_str);
break;
case OSMO_SS7_ASP_PROT_SUA:
sua_tx_dupu(asp, rctx, num_rctx, dpc, user, cause, info_str);
break;
default:
break;
}
}
static void xua_tx_scon(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
const uint32_t *aff_pc, unsigned int num_aff_pc,
const uint32_t *concerned_dpc, const uint8_t *cong_level,
const char *info_string)
{
switch (asp->cfg.proto) {
case OSMO_SS7_ASP_PROT_M3UA:
m3ua_tx_snm_congestion(asp, rctx, num_rctx, aff_pc, num_aff_pc,
concerned_dpc, cong_level, info_string);
break;
case OSMO_SS7_ASP_PROT_SUA:
sua_tx_snm_congestion(asp, rctx, num_rctx, aff_pc, num_aff_pc, NULL,
cong_level ? *cong_level : 0, info_string);
break;
default:
break;
}
}
/* generate MTP-PAUSE / MTP-RESUME towards local SCCP users */
static void xua_snm_pc_available_to_sccp(struct osmo_sccp_instance *sccp,
const uint32_t *aff_pc, unsigned int num_aff_pc,
bool available)
{
int i;
for (i = 0; i < num_aff_pc; i++) {
uint32_t _aff_pc = ntohl(aff_pc[i]);
uint32_t pc = _aff_pc & 0xffffff;
uint8_t mask = _aff_pc >> 24;
if (!mask) {
if (available)
sccp_scmg_rx_mtp_resume(sccp, pc);
else
sccp_scmg_rx_mtp_pause(sccp, pc);
} else {
/* we have to send one MTP primitive for each individual point
* code within that mask */
uint32_t maskbits = (1 << mask) - 1;
uint32_t fullpc;
for (fullpc = (pc & ~maskbits); fullpc <= (pc | maskbits); fullpc++) {
if (available)
sccp_scmg_rx_mtp_resume(sccp, pc);
else
sccp_scmg_rx_mtp_pause(sccp, pc);
}
}
}
}
/* advertise availability of point codes (with masks) */
void xua_snm_pc_available(struct osmo_ss7_as *as, const uint32_t *aff_pc,
unsigned int num_aff_pc, const char *info_str, bool available)
{
struct osmo_ss7_instance *s7i = as->inst;
struct osmo_ss7_asp *asp;
uint32_t rctx[32];
unsigned int num_rctx;
/* inform local users via a MTP-{PAUSE, RESUME} primitive */
if (s7i->sccp)
xua_snm_pc_available_to_sccp(s7i->sccp, aff_pc, num_aff_pc, available);
/* inform remote ASPs via DUNA/DAVA */
llist_for_each_entry(asp, &s7i->asp_list, list) {
/* SSNM is only permitted for ASPs in ACTIVE state */
if (!osmo_ss7_asp_active(asp))
continue;
/* only send DAVA/DUNA if we locally are the SG and the remote is ASP */
if (asp->cfg.role != OSMO_SS7_ASP_ROLE_SG)
continue;
num_rctx = get_all_rctx_for_asp(rctx, ARRAY_SIZE(rctx), asp, as);
/* this can happen if the given ASP is only in the AS that reports the change,
* which shall be excluded */
if (num_rctx == 0)
continue;
xua_tx_snm_available(asp, rctx, num_rctx, aff_pc, num_aff_pc, info_str, available);
}
}
/* generate SS-PROHIBITED / SS-ALLOWED towards local SCCP users */
static void sua_snm_ssn_available_to_sccp(struct osmo_sccp_instance *sccp, uint32_t aff_pc,
uint32_t aff_ssn, uint32_t smi, bool available)
{
if (available)
sccp_scmg_rx_ssn_allowed(sccp, aff_pc, aff_ssn, smi);
else
sccp_scmg_rx_ssn_prohibited(sccp, aff_pc, aff_ssn, smi);
}
/* advertise availability of a single subsystem */
static void sua_snm_ssn_available(struct osmo_ss7_as *as, uint32_t aff_pc, uint32_t aff_ssn,
const uint32_t *smi, const char *info_str, bool available)
{
struct osmo_ss7_instance *s7i = as->inst;
struct osmo_ss7_asp *asp;
uint32_t rctx[32];
unsigned int num_rctx;
uint32_t _smi = smi ? *smi : 0; /* 0 == reserved/unknown in SUA */
if (s7i->sccp)
sua_snm_ssn_available_to_sccp(s7i->sccp, aff_pc, aff_ssn, _smi, available);
/* inform remote SUA ASPs via DUNA/DAVA */
llist_for_each_entry(asp, &s7i->asp_list, list) {
/* SSNM is only permitted for ASPs in ACTIVE state */
if (!osmo_ss7_asp_active(asp))
continue;
/* only send DAVA/DUNA if we locally are the SG and the remote is ASP */
if (asp->cfg.role != OSMO_SS7_ASP_ROLE_SG)
continue;
/* DUNA/DAVA for SSN only exists in SUA */
if (asp->cfg.proto != OSMO_SS7_ASP_PROT_SUA)
continue;
num_rctx = get_all_rctx_for_asp(rctx, ARRAY_SIZE(rctx), asp, as);
/* this can happen if the given ASP is only in the AS that reports the change,
* which shall be excluded */
if (num_rctx == 0)
continue;
sua_tx_snm_available(asp, rctx, num_rctx, &aff_pc, 1, &aff_ssn, smi, info_str, available);
}
}
static void xua_snm_upu(struct osmo_ss7_as *as, uint32_t dpc, uint16_t user, uint16_t cause,
const char *info_str)
{
struct osmo_ss7_instance *s7i = as->inst;
struct osmo_ss7_asp *asp;
uint32_t rctx[32];
unsigned int num_rctx;
/* Translate to MTP-STATUS.ind towards SCCP (will create N-PCSTATE.ind to SCU) */
if (s7i->sccp && user == MTP_SI_SCCP)
sccp_scmg_rx_mtp_status(s7i->sccp, dpc, cause);
/* inform remote ASPs via DUPU */
llist_for_each_entry(asp, &s7i->asp_list, list) {
/* SSNM is only permitted for ASPs in ACTIVE state */
if (!osmo_ss7_asp_active(asp))
continue;
/* only send DAVA/DUNA if we locally are the SG and the remote is ASP */
if (asp->cfg.role != OSMO_SS7_ASP_ROLE_SG)
continue;
num_rctx = get_all_rctx_for_asp(rctx, ARRAY_SIZE(rctx), asp, as);
/* this can happen if the given ASP is only in the AS that reports the change,
* which shall be excluded */
if (num_rctx == 0)
continue;
xua_tx_upu(asp, rctx, num_rctx, dpc, user, cause, info_str);
}
}
static void xua_snm_scon(struct osmo_ss7_as *as, const uint32_t *aff_pc, unsigned int num_aff_pc,
const uint32_t *concerned_dpc, const uint8_t *cong_level, const char *info_string)
{
struct osmo_ss7_instance *s7i = as->inst;
struct osmo_ss7_asp *asp;
uint32_t rctx[32];
unsigned int num_rctx;
/* TODO: How to translate to MTP and towards SCCP (create N-PCSTATE.ind to SCU) */
/* inform remote ASPs via SCON */
llist_for_each_entry(asp, &s7i->asp_list, list) {
/* SSNM is only permitted for ASPs in ACTIVE state */
if (!osmo_ss7_asp_active(asp))
continue;
/* only send SCON if we locally are the SG and the remote is ASP */
if (asp->cfg.role != OSMO_SS7_ASP_ROLE_SG)
continue;
num_rctx = get_all_rctx_for_asp(rctx, ARRAY_SIZE(rctx), asp, as);
/* this can happen if the given ASP is only in the AS that reports the change,
* which shall be excluded */
if (num_rctx == 0)
continue;
xua_tx_scon(asp, rctx, num_rctx, aff_pc, num_aff_pc, concerned_dpc, cong_level, info_string);
}
}
/* receive DAUD from ASP; pc is 'affected PC' IE with mask in network byte order! */
void xua_snm_rx_daud(struct osmo_ss7_asp *asp, struct xua_msg *xua)
{
struct xua_msg_part *ie_aff_pc = xua_msg_find_tag(xua, M3UA_IEI_AFFECTED_PC);
const char *info_str = xua_msg_get_str(xua, M3UA_IEI_INFO_STRING);
struct osmo_ss7_instance *s7i = asp->inst;
unsigned int num_aff_pc;
unsigned int num_rctx;
const uint32_t *aff_pc;
uint32_t rctx[32];
int log_ss = osmo_ss7_asp_get_log_subsys(asp);
int i;
OSMO_ASSERT(ie_aff_pc);
aff_pc = (const uint32_t *) ie_aff_pc->dat;
num_aff_pc = ie_aff_pc->len / sizeof(uint32_t);
num_rctx = get_all_rctx_for_asp(rctx, ARRAY_SIZE(rctx), asp, NULL);
LOGPASP(asp, log_ss, LOGL_INFO, "Rx DAUD(%s) for %s\n", info_str ? info_str : "",
format_affected_pcs_c(xua, asp->inst, ie_aff_pc));
/* iterate over list of point codes, generate DAVA/DUPU */
for (i = 0; i < num_aff_pc; i++) {
uint32_t _aff_pc = ntohl(aff_pc[i]);
uint32_t pc = _aff_pc & 0xffffff;
uint8_t mask = _aff_pc >> 24;
bool is_available = false;
if (mask == 0) {
/* one single point code */
/* FIXME: don't just check for a route; but also check if the route is "active" */
if (osmo_ss7_route_lookup(s7i, pc))
is_available = true;
xua_tx_snm_available(asp, rctx, num_rctx, &aff_pc[i], 1, "Response to DAUD",
is_available);
} else {
/* TODO: wildcard match */
LOGPASP(asp, log_ss, LOGL_NOTICE, "DAUD with wildcard match not supported yet\n");
}
}
}
/* an incoming xUA DUNA was received from a remote SG */
void xua_snm_rx_duna(struct osmo_ss7_asp *asp, struct osmo_ss7_as *as, struct xua_msg *xua)
{
struct xua_msg_part *ie_aff_pc = xua_msg_find_tag(xua, M3UA_IEI_AFFECTED_PC);
struct xua_msg_part *ie_ssn = xua_msg_find_tag(xua, SUA_IEI_SSN);
const char *info_str = xua_msg_get_str(xua, M3UA_IEI_INFO_STRING);
/* TODO: should our processing depend on the RCTX included? I somehow don't think so */
//struct xua_msg_part *ie_rctx = xua_msg_find_tag(xua, M3UA_IEI_ROUTE_CTX);
int log_ss = osmo_ss7_asp_get_log_subsys(asp);
OSMO_ASSERT(ie_aff_pc);
if (asp->cfg.role != OSMO_SS7_ASP_ROLE_ASP)
return;
LOGPASP(asp, log_ss, LOGL_NOTICE, "Rx DUNA(%s) for %s\n", info_str ? info_str : "",
format_affected_pcs_c(xua, asp->inst, ie_aff_pc));
if (asp->cfg.proto == OSMO_SS7_ASP_PROT_SUA && ie_ssn) {
/* when the SSN is included, DUNA corresponds to the SCCP N-STATE primitive */
uint32_t ssn = xua_msg_part_get_u32(ie_ssn);
const uint32_t *aff_pc = (const uint32_t *)ie_aff_pc->dat;
uint32_t pc, smi;
/* The Affected Point Code can only contain one point code when SSN is present */
if (ie_aff_pc->len/sizeof(uint32_t) != 1)
return;
pc = ntohl(aff_pc[0]) & 0xffffff;
sua_snm_ssn_available(as, pc, ssn, xua_msg_get_u32p(xua, SUA_IEI_SMI, &smi), info_str, false);
} else {
/* when the SSN is not included, DUNA corresponds to the SCCP N-PCSTATE primitive */
xua_snm_pc_available(as, (const uint32_t *)ie_aff_pc->dat,
ie_aff_pc->len / sizeof(uint32_t), info_str, false);
}
}
/* an incoming xUA DAVA was received from a remote SG */
void xua_snm_rx_dava(struct osmo_ss7_asp *asp, struct osmo_ss7_as *as, struct xua_msg *xua)
{
struct xua_msg_part *ie_aff_pc = xua_msg_find_tag(xua, M3UA_IEI_AFFECTED_PC);
struct xua_msg_part *ie_ssn = xua_msg_find_tag(xua, SUA_IEI_SSN);
const char *info_str = xua_msg_get_str(xua, M3UA_IEI_INFO_STRING);
/* TODO: should our processing depend on the RCTX included? I somehow don't think so */
//struct xua_msg_part *ie_rctx = xua_msg_find_tag(xua, M3UA_IEI_ROUTE_CTX);
int log_ss = osmo_ss7_asp_get_log_subsys(asp);
OSMO_ASSERT(ie_aff_pc);
if (asp->cfg.role != OSMO_SS7_ASP_ROLE_ASP)
return;
LOGPASP(asp, log_ss, LOGL_NOTICE, "Rx DAVA(%s) for %s\n", info_str ? info_str : "",
format_affected_pcs_c(xua, asp->inst, ie_aff_pc));
if (asp->cfg.proto == OSMO_SS7_ASP_PROT_SUA && ie_ssn) {
/* when the SSN is included, DAVA corresponds to the SCCP N-STATE primitive */
uint32_t ssn = xua_msg_part_get_u32(ie_ssn);
const uint32_t *aff_pc = (const uint32_t *)ie_aff_pc->dat;
uint32_t pc, smi;
/* The Affected Point Code can only contain one point code when SSN is present */
if (ie_aff_pc->len/sizeof(uint32_t) != 1)
return;
pc = ntohl(aff_pc[0]) & 0xffffff;
sua_snm_ssn_available(as, pc, ssn, xua_msg_get_u32p(xua, SUA_IEI_SMI, &smi), info_str, true);
} else {
/* when the SSN is not included, DAVA corresponds to the SCCP N-PCSTATE primitive */
xua_snm_pc_available(as, (const uint32_t *)ie_aff_pc->dat,
ie_aff_pc->len / sizeof(uint32_t), info_str, true);
}
}
/* an incoming SUA/M3UA DUPU was received from a remote SG */
void xua_snm_rx_dupu(struct osmo_ss7_asp *asp, struct osmo_ss7_as *as, struct xua_msg *xua)
{
uint32_t aff_pc = xua_msg_get_u32(xua, SUA_IEI_AFFECTED_PC);
const char *info_str = xua_msg_get_str(xua, SUA_IEI_INFO_STRING);
/* TODO: should our processing depend on the RCTX included? I somehow don't think so */
//struct xua_msg_part *ie_rctx = xua_msg_find_tag(xua, SUA_IEI_ROUTE_CTX);
int log_ss = osmo_ss7_asp_get_log_subsys(asp);
uint32_t cause_user;
uint16_t cause, user;
if (asp->cfg.role != OSMO_SS7_ASP_ROLE_ASP)
return;
switch (asp->cfg.proto) {
case OSMO_SS7_ASP_PROT_M3UA:
cause_user = xua_msg_get_u32(xua, M3UA_IEI_USER_CAUSE);
break;
case OSMO_SS7_ASP_PROT_SUA:
cause_user = xua_msg_get_u32(xua, SUA_IEI_USER_CAUSE);
break;
default:
return;
}
cause = cause_user >> 16;
user = cause_user & 0xffff;
LOGPASP(asp, log_ss, LOGL_NOTICE, "Rx DUPU(%s) for %s User %s, cause %u\n",
info_str ? info_str : "", osmo_ss7_pointcode_print(asp->inst, aff_pc),
get_value_string(mtp_si_vals, user), cause);
xua_snm_upu(as, aff_pc, user, cause, info_str);
}
/* an incoming SUA/M3UA SCON was received from a remote SG */
void xua_snm_rx_scon(struct osmo_ss7_asp *asp, struct osmo_ss7_as *as, struct xua_msg *xua)
{
struct xua_msg_part *ie_aff_pc = xua_msg_find_tag(xua, M3UA_IEI_AFFECTED_PC);
const char *info_str = xua_msg_get_str(xua, M3UA_IEI_INFO_STRING);
uint32_t _concerned_dpc, _cong_level;
const uint32_t *concerned_dpc = xua_msg_get_u32p(xua, M3UA_IEI_CONC_DEST, &_concerned_dpc);
const uint32_t *cong_level = xua_msg_get_u32p(xua, M3UA_IEI_CONG_IND, &_cong_level);
int log_ss = osmo_ss7_asp_get_log_subsys(asp);
OSMO_ASSERT(ie_aff_pc);
LOGPASP(asp, log_ss, LOGL_NOTICE, "RX SCON(%s) for %s level=%u\n", info_str ? info_str : "",
format_affected_pcs_c(xua, asp->inst, ie_aff_pc), cong_level ? *cong_level : 0);
xua_snm_scon(as, (const uint32_t *) ie_aff_pc->dat, ie_aff_pc->len / sizeof(uint32_t),
concerned_dpc, (const uint8_t *) cong_level, info_str);
}

View File

@ -1,5 +1,5 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS=-Wall -g $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMONETIF_CFLAGS) $(COVERAGE_FLAGS)
AM_CFLAGS=-Wall -g $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMONETIF_CFLAGS) $(COVERAGE_CFLAGS)
AM_LDFLAGS=$(COVERAGE_LDFLAGS)
bin_PROGRAMS = osmo-stp

View File

@ -32,6 +32,7 @@
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/application.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/fsm.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/stats.h>
@ -40,6 +41,7 @@
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/misc.h>
#include <osmocom/vty/cpu_sched_vty.h>
#include <osmocom/sigtran/osmo_ss7.h>
#include <osmocom/sigtran/sccp_sap.h>
@ -59,10 +61,10 @@ static const struct log_info log_info = {
};
static const char stp_copyright[] =
"Copyright (C) 2015-2017 by Harald Welte <laforge@gnumonks.org>\r\n"
"Contributions by Holger Freyther, Neels Hofmeyr\r\n"
"Copyright (C) 2015-2020 by Harald Welte <laforge@gnumonks.org>\r\n"
"Contributions by Holger Freyther, Neels Hofmeyr, Pau Espin, Vadim Yanitskiy\r\n"
"License GPLv2+: GNU GPL Version 2 or later <http://gnu.org/licenses/gpl-2.0.html>\r\n"
"This is free software: you are free ot change and redistribute it.\r\n"
"This is free software: you are free to change and redistribute it.\r\n"
"There is NO WARRANTY, to the extent permitted by law.\r\n\r\n"
"Free Software lives by contribution. If you use this, please contribute!\r\n";
@ -88,17 +90,49 @@ static void print_help(void)
printf(" -D --daemonize Fork the process into a background daemon\n");
printf(" -c --config-file filename The config file to use. Default: ./osmo-stp.cfg\n");
printf(" -V --version Print the version of OsmoSTP\n");
printf("\nVTY reference generation:\n");
printf(" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n");
printf(" --vty-ref-xml Generate the VTY reference XML output and exit.\n");
}
static void handle_long_options(const char *prog_name, const int long_option)
{
static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
switch (long_option) {
case 1:
vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
if (vty_ref_mode < 0) {
fprintf(stderr, "%s: Unknown VTY reference generation "
"mode '%s'\n", prog_name, optarg);
exit(2);
}
break;
case 2:
fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
exit(0);
default:
fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
exit(2);
}
}
static void handle_options(int argc, char **argv)
{
while (1) {
int option_index = 0, c;
static int long_option = 0;
static const struct option long_options[] = {
{ "help", 0, 0, 'h' },
{ "daemonize", 0, 0, 'D' },
{ "config-file", 1, 0, 'c' },
{ "version", 0, 0, 'V' },
{ "vty-ref-mode", 1, &long_option, 1 },
{ "vty-ref-xml", 0, &long_option, 2 },
{ NULL, 0, 0, 0 }
};
@ -107,6 +141,9 @@ static void handle_options(int argc, char **argv)
break;
switch (c) {
case 0:
handle_long_options(argv[0], long_option);
break;
case 'h':
print_help();
exit(0);
@ -134,11 +171,11 @@ static void handle_options(int argc, char **argv)
}
}
static void signal_handler(int signal)
static void signal_handler(int signum)
{
fprintf(stdout, "signal %u received\n", signal);
fprintf(stdout, "signal %u received\n", signum);
switch (signal) {
switch (signum) {
case SIGINT:
case SIGTERM:
/* FIXME: handle the signal somewhere else and gracefully shut down
@ -149,8 +186,17 @@ static void signal_handler(int signal)
break;
case SIGABRT:
osmo_generate_backtrace();
/* in case of abort, we want to obtain a talloc report
* and then return to the caller, who will abort the process */
/* in case of abort, we want to obtain a talloc report and
* then run default SIGABRT handler, who will generate coredump
* and abort the process. abort() should do this for us after we
* return, but program wouldn't exit if an external SIGABRT is
* received.
*/
talloc_report(tall_vty_ctx, stderr);
talloc_report_full(tall_stp_ctx, stderr);
signal(SIGABRT, SIG_DFL);
raise(SIGABRT);
break;
case SIGUSR1:
talloc_report(tall_vty_ctx, stderr);
talloc_report_full(tall_stp_ctx, stderr);
@ -167,6 +213,8 @@ int main(int argc, char **argv)
{
int rc;
srand(time(NULL));
tall_stp_ctx = talloc_named_const(NULL, 1, "osmo-stp");
msgb_talloc_ctx_init(tall_stp_ctx, 0);
osmo_init_logging2(tall_stp_ctx, &log_info);
@ -175,20 +223,21 @@ int main(int argc, char **argv)
vty_info.tall_ctx = tall_stp_ctx;
vty_init(&vty_info);
handle_options(argc, argv);
fputs(stp_copyright, stdout);
fputs("\n", stdout);
OSMO_ASSERT(osmo_ss7_init() == 0);
osmo_fsm_log_addr(false);
logging_vty_add_cmds();
osmo_stats_vty_add_cmds();
osmo_ss7_vty_init_sg(tall_stp_ctx);
osmo_sccp_vty_init();
osmo_cpu_sched_vty_init(tall_stp_ctx);
osmo_fsm_vty_add_cmds();
osmo_talloc_vty_add_cmds();
handle_options(argc, argv);
fputs(stp_copyright, stdout);
fputs("\n", stdout);
rc = vty_read_config_file(cmdline_config.config_file, NULL);
if (rc < 0) {
fprintf(stderr, "Failed to parse the config file '%s'\n",
@ -196,7 +245,7 @@ int main(int argc, char **argv)
exit(1);
}
rc = telnet_init_dynif(tall_stp_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_STP);
rc = telnet_init_default(tall_stp_ctx, NULL, OSMO_VTY_PORT_STP);
if (rc < 0) {
perror("Error binding VTY port\n");
exit(1);
@ -208,6 +257,7 @@ int main(int argc, char **argv)
signal(SIGUSR1, &signal_handler);
signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals();
rate_ctr_init(tall_stp_ctx);
if (cmdline_config.daemonize) {
rc = osmo_daemonize();

View File

@ -1,8 +1,9 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -Wall
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS)
AM_LDFLAGS = -no-install
EXTRA_DIST = m2ua_test.ok
noinst_PROGRAMS = m2ua_test
check_PROGRAMS = m2ua_test
m2ua_test_SOURCES = m2ua_test.c
m2ua_test_LDADD = $(top_builddir)/src/libxua.a $(LIBOSMOCORE_LIBS)

View File

@ -1,5 +1,8 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include $(LIBOSMOCORE_CFLAGS) -Wall
noinst_PROGRAMS = mtp_parse_test
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS)
AM_LDFLAGS = -no-install
check_PROGRAMS = mtp_parse_test
EXTRA_DIST = mtp_parse_test.ok

View File

@ -1,9 +1,10 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS)
AM_LDFLAGS = -no-install
EXTRA_DIST = sccp_test.ok
noinst_PROGRAMS = sccp_test
check_PROGRAMS = sccp_test
sccp_test_SOURCES = sccp_test.c
sccp_test_LDADD = \

View File

@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <stdio.h>

View File

@ -1,12 +1,12 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -Wall
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_LDFLAGS = -static
AM_LDFLAGS = -static -no-install
LDADD = $(top_builddir)/src/libosmo-sigtran.la \
$(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMONETIF_LIBS) $(LIBSCTP_LIBS)
EXTRA_DIST = ss7_test.ok ss7_test.err
noinst_PROGRAMS = ss7_test
check_PROGRAMS = ss7_test
ss7_test_SOURCES = ss7_test.c

View File

@ -248,7 +248,8 @@ static void test_as(void)
OSMO_ASSERT(osmo_ss7_as_find_by_rctx(s7i, 2342) == as);
OSMO_ASSERT(osmo_ss7_as_add_asp(as, "asp1") == -ENODEV);
asp = osmo_ss7_asp_find_or_create(s7i, "asp1", 0, M3UA_PORT, OSMO_SS7_ASP_PROT_M3UA);
asp = osmo_ss7_asp_find_or_create2(s7i, "asp1", 0, M3UA_PORT,
IPPROTO_SCTP, OSMO_SS7_ASP_PROT_M3UA);
OSMO_ASSERT(asp);
OSMO_ASSERT(osmo_ss7_as_has_asp(as, asp) == false);
@ -290,7 +291,7 @@ static void init_logging(void)
msgb_talloc_ctx_init(tall_ctx, 0);
osmo_init_logging2(tall_ctx, &log_info);
log_set_print_filename(osmo_stderr_target, 0);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
for (i = 0; i < ARRAY_SIZE(log_cats); i++)
log_set_category_filter(osmo_stderr_target, log_cats[i], 1, LOGL_DEBUG);

View File

@ -1,7 +1,7 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -Wall
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_LDFLAGS = -static
AM_LDFLAGS = -static -no-install
LDADD = $(top_builddir)/src/libosmo-sigtran.la \
$(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMONETIF_LIBS) $(LIBSCTP_LIBS)
@ -10,19 +10,21 @@ EXTRA_DIST = \
vty_test_runner.py \
$(NULL)
noinst_PROGRAMS = ss7_asp_vty_test
check_PROGRAMS = ss7_asp_vty_test
ss7_asp_vty_test_SOURCES = ss7_asp_vty_test.c
if ENABLE_EXT_TESTS
ext-tests:
ext-tests: $(top_builddir)/stp/osmo-stp
$(MAKE) vty-test
osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
else
ext-tests:
echo "Not running python-based external tests (determined at configure-time)"
endif
vty-python-test: $(BUILT_SOURCES)
vty-python-test: $(top_builddir)/stp/osmo-stp
$(srcdir)/vty_test_runner.py -w $(abs_top_builddir) -v
# To update the VTY script from current application behavior,

View File

@ -2,9 +2,16 @@ ss7_asp_vty_test> list
... !show cs7
show cs7 instance <0-15> users
show cs7 (sua|m3ua|ipa) [<0-65534>]
show cs7 (sua|m3ua|ipa) (sctp|tcp) [<0-65534>]
show cs7 config
show cs7 instance <0-15> asp
show cs7 instance <0-15> asp name ASP_NAME
show cs7 instance <0-15> asp-remaddr
show cs7 instance <0-15> asp-remaddr name ASP_NAME
show cs7 instance <0-15> asp-assoc-status
show cs7 instance <0-15> asp-assoc-status name ASP_NAME
show cs7 instance <0-15> as (active|all|m3ua|sua)
show cs7 instance <0-15> route
show cs7 instance <0-15> sccp addressbook
show cs7 instance <0-15> sccp users
show cs7 instance <0-15> sccp ssn <0-65535>
@ -17,9 +24,17 @@ ss7_asp_vty_test# list
... !show cs7
show cs7 instance <0-15> users
show cs7 (sua|m3ua|ipa) [<0-65534>]
show cs7 (sua|m3ua|ipa) (sctp|tcp) [<0-65534>]
show cs7 config
cs7 instance <0-15> asp NAME disconnect
show cs7 instance <0-15> asp
show cs7 instance <0-15> asp name ASP_NAME
show cs7 instance <0-15> asp-remaddr
show cs7 instance <0-15> asp-remaddr name ASP_NAME
show cs7 instance <0-15> asp-assoc-status
show cs7 instance <0-15> asp-assoc-status name ASP_NAME
show cs7 instance <0-15> as (active|all|m3ua|sua)
show cs7 instance <0-15> route
show cs7 instance <0-15> sccp addressbook
show cs7 instance <0-15> sccp users
show cs7 instance <0-15> sccp ssn <0-65535>
@ -40,16 +55,30 @@ ss7_asp_vty_test# show cs7 ?
config Currently running cs7 configuration
ss7_asp_vty_test# show cs7 m3ua ?
[<0-65534>] Port Number
[<0-65534>] Local Port Number
sctp SCTP (Stream Control Transmission Protocol)
tcp TCP (Transmission Control Protocol)
ss7_asp_vty_test# show cs7 m3ua 2905 ?
<cr>
ss7_asp_vty_test# show cs7 m3ua sctp ?
[<0-65534>] Local Port Number
ss7_asp_vty_test# show cs7 m3ua sctp 2905 ?
<cr>
ss7_asp_vty_test# show cs7 instance ?
<0-15> An instance of the SS7 stack
ss7_asp_vty_test# show cs7 instance 0 ?
users User Table
asp Application Server Process (ASP)
as Application Server (AS)
sccp Signalling Connection Control Part
users User Table
asp Application Server Process (ASP)
asp-remaddr Application Server Process (ASP) remote addresses information
asp-assoc-status Application Server Process (ASP) SCTP association status
as Application Server (AS)
route Routing Table
sccp Signalling Connection Control Part
ss7_asp_vty_test# show cs7 instance 0 as ?
active Display all active ASs
@ -80,12 +109,14 @@ ss7_asp_vty_test(config-cs7)# list
point-code delimiter (default|dash)
xua rkm routing-key-allocation (static-only|dynamic-permitted)
asp NAME <0-65535> <0-65535> (sua|m3ua|ipa)
asp NAME <0-65535> <0-65535> (sua|m3ua|ipa) (sctp|tcp)
no asp NAME
as NAME (sua|m3ua|ipa)
no as NAME
sccp-address NAME
no sccp-address NAME
sccp-timer (conn_est|ias|iar|rel|repeat_rel|int|guard|reset|reassembly) <1-999999>
sccp max-optional-data (<0-999999>|standard)
ss7_asp_vty_test(config-cs7)# ?
...
@ -98,6 +129,7 @@ ss7_asp_vty_test(config-cs7)# ?
as Configure an Application Server
sccp-address Create/Modify an SCCP addressbook entry
sccp-timer Configure SCCP timer values, see ITU-T Q.714
sccp Configure SCCP behavior
ss7_asp_vty_test(config-cs7)# description ?
TEXT Text until the end of the line
@ -130,19 +162,23 @@ ss7_asp_vty_test(config-cs7)# xua ?
ss7_asp_vty_test(config-cs7)# xua rkm ?
routing-key-allocation Routing Key Management Allocation Policy
ss7_asp_vty_test(config-cs7)# xua rkm routing-key-allocation ?
static-only Only static (pre-confgured) Routing Keys permitted
static-only Only static (pre-configured) Routing Keys permitted
dynamic-permitted Dynamically allocate Routing Keys for what ASPs request
ss7_asp_vty_test(config-cs7)# asp ?
NAME Name of ASP
ss7_asp_vty_test(config-cs7)# asp foo ?
<0-65535> Remote SCTP port number
<0-65535> Remote port number
ss7_asp_vty_test(config-cs7)# asp foo 0 ?
<0-65535> Local SCTP port number
<0-65535> Local port number
ss7_asp_vty_test(config-cs7)# asp foo 0 0 ?
sua SCCP User Adaptation
m3ua MTP3 User Adaptation
ipa IPA Multiplex (SCCP Lite)
ss7_asp_vty_test(config-cs7)# asp foo 0 0 m3ua ?
sctp SCTP (Stream Control Transmission Protocol)
tcp TCP (Transmission Control Protocol)
<cr>
ss7_asp_vty_test(config-cs7)# as ?
NAME Name of the Application Server
@ -215,32 +251,83 @@ ss7_asp_vty_test(config-cs7)# asp my-asp 12345 54321 m3ua
ss7_asp_vty_test(config-cs7-asp)# list
...
description .TEXT
remote-ip A.B.C.D
local-ip A.B.C.D
remote-ip (A.B.C.D|X:X::X:X) [primary]
no remote-ip (A.B.C.D|X:X::X:X)
local-ip (A.B.C.D|X:X::X:X) [primary]
no local-ip (A.B.C.D|X:X::X:X)
qos-class <0-255>
role (sg|asp|ipsp)
sctp-role (client|server)
transport-role (client|server)
sctp-param init (num-ostreams|max-instreams|max-attempts|timeout) <0-65535>
no sctp-param init (num-ostreams|max-instreams|max-attempts|timeout)
block
shutdown
...
ss7_asp_vty_test(config-cs7-asp)# ?
...
description Save human-readable description of the object
remote-ip Specify Remote IP Address of ASP
local-ip Specify Local IP Address from which to contact ASP
qos-class Specify QoS Class of ASP
role Specify the xUA role for this ASP
sctp-role Specify the SCTP role for this ASP
block Allows a SCTP Association with ASP, but doesn't let it become active
shutdown Terminates SCTP association; New associations will be rejected
description Save human-readable description of the object
remote-ip Specify Remote IP Address of ASP
no Negate a command or set its defaults
local-ip Specify Local IP Address from which to contact ASP
qos-class Specify QoS Class of ASP
role Specify the xUA role for this ASP
transport-role Specify the transport layer role for this ASP
sctp-param Configure SCTP parameters
block Allows a SCTP Association with ASP, but doesn't let it become active
shutdown Terminates SCTP association; New associations will be rejected
...
ss7_asp_vty_test(config-cs7-asp)# no ?
...
sctp-param Configure SCTP parameters
quirk Disable quirk to work around interop issues
...
ss7_asp_vty_test(config-cs7-asp)# remote-ip 127.0.0.200
ss7_asp_vty_test(config-cs7-asp)# local-ip 127.0.0.100
ss7_asp_vty_test(config-cs7-asp)# do show cs7 instance 0 asp
Effect Primary
ASP Name AS Name State Type Remote IP Addr:Rmt Port SCTP
------------ ------------ ------------- ---- ----------------------- ----------
my-asp ? uninitialized m3ua 127.0.0.200:12345
ASP Name AS Name State Type Role SCTP Role Local Addresses Remote Addresses
------------ ------------ ------------- ---- ---- --------- ----------------------- -----------------------
my-asp ? uninitialized m3ua sg server 127.0.0.100:54321 127.0.0.200:12345
ss7_asp_vty_test(config-cs7-asp)# remote-ip 127.0.0.201
ss7_asp_vty_test(config-cs7-asp)# local-ip 127.0.0.101
ss7_asp_vty_test(config-cs7-asp)# do show cs7 instance 0 asp
ASP Name AS Name State Type Role SCTP Role Local Addresses Remote Addresses
------------ ------------ ------------- ---- ---- --------- ----------------------- -----------------------
my-asp ? uninitialized m3ua sg server (127.0.0.100|127.0.0.101):54321 (127.0.0.200|127.0.0.201):12345
ss7_asp_vty_test(config-cs7-asp)# ! Mark as primary:
ss7_asp_vty_test(config-cs7-asp)# remote-ip 127.0.0.201 primary
ss7_asp_vty_test(config-cs7-asp)# ! 'local-ip 127.0.0.101 primary' cannot be tested here since output may be different based on sysctl available
ss7_asp_vty_test(config-cs7-asp)# local-ip 127.0.0.101
...
ss7_asp_vty_test(config-cs7-asp)# do show cs7 instance 0 asp
ASP Name AS Name State Type Role SCTP Role Local Addresses Remote Addresses
------------ ------------ ------------- ---- ---- --------- ----------------------- -----------------------
my-asp ? uninitialized m3ua sg server (127.0.0.100|127.0.0.101):54321 (127.0.0.200|127.0.0.201*):12345
ss7_asp_vty_test(config-cs7-asp)# show running-config
...
local-ip 127.0.0.100
local-ip 127.0.0.101
remote-ip 127.0.0.200
remote-ip 127.0.0.201 primary
...
end
ss7_asp_vty_test(config-cs7-asp)# ! Mark as non-primary:
ss7_asp_vty_test(config-cs7-asp)# remote-ip 127.0.0.201
ss7_asp_vty_test(config-cs7-asp)# local-ip 127.0.0.101
ss7_asp_vty_test(config-cs7-asp)# do show cs7 instance 0 asp
ASP Name AS Name State Type Role SCTP Role Local Addresses Remote Addresses
------------ ------------ ------------- ---- ---- --------- ----------------------- -----------------------
my-asp ? uninitialized m3ua sg server (127.0.0.100|127.0.0.101):54321 (127.0.0.200|127.0.0.201):12345
ss7_asp_vty_test(config-cs7-asp)# show running-config
...
local-ip 127.0.0.100
local-ip 127.0.0.101
remote-ip 127.0.0.200
remote-ip 127.0.0.201
...
end
ss7_asp_vty_test(config-cs7-asp)# exit
ss7_asp_vty_test(config-cs7)# as my-ass m3ua
@ -301,26 +388,23 @@ ss7_asp_vty_test(config-cs7-as)# asp my-asp
ss7_asp_vty_test(config-cs7-as)# routing-key 0 3.2.1
ss7_asp_vty_test(config-cs7-as)# do show cs7 instance 0 asp
Effect Primary
ASP Name AS Name State Type Remote IP Addr:Rmt Port SCTP
------------ ------------ ------------- ---- ----------------------- ----------
my-asp ? ASP_DOWN m3ua 127.0.0.200:12345
ASP Name AS Name State Type Role SCTP Role Local Addresses Remote Addresses
------------ ------------ ------------- ---- ---- --------- ----------------------- -----------------------
my-asp my-ass ASP_DOWN m3ua sg server (127.0.0.100|127.0.0.101):54321 (127.0.0.200|127.0.0.201):12345
ss7_asp_vty_test(config-cs7-as)# exit
ss7_asp_vty_test(config-cs7)# do show cs7 instance 0 asp
Effect Primary
ASP Name AS Name State Type Remote IP Addr:Rmt Port SCTP
------------ ------------ ------------- ---- ----------------------- ----------
my-asp ? ASP_DOWN m3ua 127.0.0.200:12345
ASP Name AS Name State Type Role SCTP Role Local Addresses Remote Addresses
------------ ------------ ------------- ---- ---- --------- ----------------------- -----------------------
my-asp my-ass ASP_DOWN m3ua sg server (127.0.0.100|127.0.0.101):54321 (127.0.0.200|127.0.0.201):12345
ss7_asp_vty_test(config-cs7)# exit
ss7_asp_vty_test(config)# do show cs7 instance 0 asp
Effect Primary
ASP Name AS Name State Type Remote IP Addr:Rmt Port SCTP
------------ ------------ ------------- ---- ----------------------- ----------
my-asp ? ASP_DOWN m3ua 127.0.0.200:12345
ASP Name AS Name State Type Role SCTP Role Local Addresses Remote Addresses
------------ ------------ ------------- ---- ---- --------- ----------------------- -----------------------
my-asp my-ass ASP_DOWN m3ua sg server (127.0.0.100|127.0.0.101):54321 (127.0.0.200|127.0.0.201):12345
ss7_asp_vty_test(config)# do show cs7 instance 0 as all
Routing Routing Key Cic Cic Traffic
@ -332,20 +416,24 @@ my-ass AS_DOWN 0 3.2.1
ss7_asp_vty_test(config)# show running-config
...
cs7 instance 1
sccp-address bar
routing-indicator PC
point-code 1.2.3
cs7 instance 0
asp my-asp 12345 54321 m3ua
local-ip 127.0.0.100
local-ip 127.0.0.101
remote-ip 127.0.0.200
remote-ip 127.0.0.201
role sg
sctp-role server
as my-ass m3ua
asp my-asp
routing-key 0 3.2.1
sccp-address foo
routing-indicator PC
point-code 1.2.3
cs7 instance 1
sccp-address bar
routing-indicator PC
point-code 1.2.3
end
ss7_asp_vty_test(config)# do show cs7 instance 0 users
@ -359,9 +447,8 @@ No ASP named 'unknown-asp' found
ss7_asp_vty_test(config-cs7)# no asp my-asp
ss7_asp_vty_test(config-cs7)# do show cs7 instance 0 asp
Effect Primary
ASP Name AS Name State Type Remote IP Addr:Rmt Port SCTP
------------ ------------ ------------- ---- ----------------------- ----------
ASP Name AS Name State Type Role SCTP Role Local Addresses Remote Addresses
------------ ------------ ------------- ---- ---- --------- ----------------------- -----------------------
ss7_asp_vty_test(config-cs7)# do show cs7 instance 0 as all
Routing Routing Key Cic Cic Traffic
@ -422,3 +509,43 @@ ss7_asp_vty_test(config-cs7)# sccp-timer ?
ss7_asp_vty_test(config-cs7)# sccp-timer conn_est ?
<1-999999> Timer value, in seconds
ss7_asp_vty_test(config-cs7)# sccp ?
max-optional-data Adjust the upper bound for the optional data length (the payload) for CR, CC, CREF and RLSD messages. For any Optional Data part larger than this value in octets, send CR, CC, CREF and RLSD messages without any payload, and send the data payload in a separate Data Form 1 message. ITU-T Q.713 sections 4.2 thru 4.5 define a limit of 130 bytes for the 'Data' parameter. This limit can be adjusted here. May be useful for interop with nonstandard SCCP peers.
ss7_asp_vty_test(config-cs7)# sccp max-optional-data ?
<0-999999> Set a non-standard maximum allowed number of bytes
standard Use the ITU-T Q.713 4.2 to 4.5 standard value of 130
ss7_asp_vty_test(config-cs7)# show running-config
... !sccp max-optional-data
ss7_asp_vty_test(config-cs7)# sccp max-optional-data 0
ss7_asp_vty_test(config-cs7)# show running-config
...
sccp max-optional-data 0
...
ss7_asp_vty_test(config-cs7)# sccp max-optional-data 123
ss7_asp_vty_test(config-cs7)# show running-config
...
sccp max-optional-data 123
...
ss7_asp_vty_test(config-cs7)# sccp max-optional-data 999999
ss7_asp_vty_test(config-cs7)# show running-config
...
cs7 instance 0
...
sccp max-optional-data 999999
...
cs7 instance 1
... !sccp max-optional-data
ss7_asp_vty_test(config-cs7)# sccp max-optional-data standard
ss7_asp_vty_test(config-cs7)# show running-config
... !sccp max-optional-data
ss7_asp_vty_test(config-cs7)# sccp max-optional-data 130
ss7_asp_vty_test(config-cs7)# show running-config
... !sccp max-optional-data

View File

@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#define _GNU_SOURCE
@ -45,7 +41,7 @@ void *root_ctx = NULL;
const struct log_info log_info = {
};
static void print_help()
static void print_help(void)
{
printf( "options:\n"
" -h --help this text\n"
@ -186,7 +182,7 @@ int main(int argc, char **argv)
}
}
rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42043);
rc = telnet_init_default(root_ctx, NULL, 42043);
if (rc < 0)
return 2;

View File

@ -30,6 +30,8 @@ from osmopy.osmo_ipa import IPA
# to be able to find $top_srcdir/doc/...
confpath = os.path.join(sys.path[0], '..')
TIMEOUT = 10
class TestVTYBase(unittest.TestCase):
def checkForEndAndExit(self):
@ -111,7 +113,9 @@ class TestVTYSTP(TestVTYBase):
# first check if STP is listening in required addresses:
found = False
for i in range(5):
if self.check_sctp_sock_local(['127.0.0.1', '127.0.0.2'], 2905):
if self.check_sctp_sock_local(['127.0.0.1', '127.0.0.2',
'0000:0000:0000:0000:0000:0000:0000:0001'],
2905):
found = True
break
else:
@ -122,14 +126,27 @@ class TestVTYSTP(TestVTYBase):
proto = socket.IPPROTO_SCTP
except AttributeError: # it seems to be not defined under python2?
proto = 132
# IPv4:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, proto)
s.bind(('127.0.0.3', 0))
s.settimeout(TIMEOUT)
try:
s.connect(('127.0.0.2',2905))
except socket.error as msg:
s.close()
self.assertTrue(False)
print("Connected to STP through SCTP")
self.fail("Failed to connect IPv4 socket: %s" % msg)
print("Connected to STP through SCTP (IPv4)")
s.close()
# IPv6:
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, proto)
s.bind(('::1', 0))
s.settimeout(TIMEOUT)
try:
s.connect(('::1',2905))
except socket.error as msg:
s.close()
self.fail("Failed to connect IPv6 socket: %s" % msg)
print("Connected to STP through SCTP (IPv6)")
s.close()
if __name__ == '__main__':

View File

@ -1,13 +1,13 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -Wall
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_LDFLAGS = -static
AM_LDFLAGS = -static -no-install
LDADD = $(top_builddir)/src/libosmo-sigtran.la \
$(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMONETIF_LIBS) $(LIBSCTP_LIBS)
EXTRA_DIST = xua_test.ok xua_test.err
noinst_HEADERS = sccp_test_data.h
noinst_PROGRAMS = xua_test
check_PROGRAMS = xua_test
xua_test_SOURCES = xua_test.c sccp_test_data.c

View File

@ -100,3 +100,56 @@ const uint8_t tcap_global_title[183] = {
0x01, 0x41, 0x84, 0x01, 0x04, 0x30, 0x03, 0x82,
0x01, 0x18, 0x00, 0x00, 0x00, 0x00
};
/* SCCP LUDT message containing a RANAP RESET */
const uint8_t ludt_ranap_reset[40] = {
0x13, 0x00, 0x0f, 0x07, 0x00, 0x0a, 0x00, 0x0d,
0x00, 0x00, 0x00, 0x04, 0x43, 0xc4, 0x04, 0x8e,
0x04, 0x43, 0xe9, 0x03, 0x8e, 0x11, 0x00, 0x00,
0x09, 0x00, 0x0d, 0x00, 0x00, 0x02, 0x00, 0x04,
0x40, 0x01, 0x42, 0x00, 0x03, 0x00, 0x01, 0x00
};
const uint8_t ludt_data300bytes[323] = {
0x13, 0x00, 0x0f, 0x07, 0x00, 0x0a, 0x00, 0x0d,
0x00, 0x00, 0x00, 0x04, 0x43, 0xc4, 0x04, 0x8e,
0x04, 0x43, 0xe9, 0x03, 0x8e, 0x2c, 0x01, 0xab, /* 0x2c, 0x01 (network order) = len = 300 */
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab,
};

View File

@ -12,3 +12,5 @@ extern const uint8_t bssmap_clear[13];
extern const uint8_t bssmap_released[14];
extern const uint8_t bssmap_release_complete[7];
extern const uint8_t tcap_global_title[183];
extern const uint8_t ludt_ranap_reset[40];
extern const uint8_t ludt_data300bytes[323];

View File

@ -369,9 +369,13 @@ struct sccp2sua_testcase {
#define PANDSIZ(x) { x, ARRAY_SIZE(x) }
#define PARTU32(x, data) { .tag = x, .len = 4, .dat = (uint8_t *) data }
#define PARTARR(x, data) { .tag = x, .len = ARRAY_SIZE(data), .dat = (uint8_t *) data }
/* GCC-4 errors with 'initializer element is not constant' if using XUA_HDR
* inside a const struct (OS#5004) */
#define _XUA_HDR(class, type) { .spare = 0, .msg_class = (class), .msg_type = (type) }
const uint32_t sua_proto_class0 = 0;
const uint32_t sua_proto_class2 = 2;
const uint32_t sua_hop_ctr_0x0f = 0x0f;
const uint32_t sua_loc_ref_bsc = 0x10203;
const uint32_t sua_loc_ref_msc = 0x00003;
const uint32_t sua_cause0 = 0x00003;
@ -384,7 +388,7 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
.name = "BSSMAP-RESET",
.sccp = PANDSIZ(bssmap_reset),
.sua = {
.hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
.hdr = _XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
.parts = {
PARTU32(SUA_IEI_PROTO_CLASS, &sua_proto_class0),
PARTARR(SUA_IEI_DEST_ADDR, sua_addr_ssn_bssmap),
@ -395,7 +399,7 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
.name = "BSSMAP-RESET-ACK",
.sccp = PANDSIZ(bssmap_reset_ack),
.sua = {
.hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
.hdr = _XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
.parts = {
PARTU32(SUA_IEI_PROTO_CLASS, &sua_proto_class0),
PARTARR(SUA_IEI_DEST_ADDR, sua_addr_ssn_bssmap_pc1),
@ -406,7 +410,7 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
.name = "BSSMAP-PAGING",
.sccp = PANDSIZ(bssmap_paging),
.sua = {
.hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
.hdr = _XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
.parts = {
PARTU32(SUA_IEI_PROTO_CLASS, &sua_proto_class0),
PARTARR(SUA_IEI_DEST_ADDR, sua_addr_ssn_bssmap_pc1),
@ -417,7 +421,7 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
.name = "BSSMAP-UDT",
.sccp = PANDSIZ(bssmap_udt),
.sua = {
.hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
.hdr = _XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
.parts = {
PARTU32(SUA_IEI_PROTO_CLASS, &sua_proto_class0),
PARTARR(SUA_IEI_DEST_ADDR, sua_addr_ssn_bssmap),
@ -428,7 +432,7 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
.name = "BSSMAP-CR",
.sccp = PANDSIZ(bssmap_cr),
.sua = {
.hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_CORE),
.hdr = _XUA_HDR(SUA_MSGC_CO, SUA_CO_CORE),
.parts = {
PARTU32(SUA_IEI_PROTO_CLASS, &sua_proto_class2),
PARTU32(SUA_IEI_SRC_REF, &sua_loc_ref_bsc),
@ -436,10 +440,14 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
},
},
}, {
/* This case expectedly prints "Input != re-encoded output!" because input has
* (struct sccp_connection_confirm).optional_start=1 pointing to SCCP_PNC_END_OF_OPTIONAL,
* while our (re-)encoder uses optional_start=0 which means entire msg is 1 byte
* lower (hence better). Both are valid. */
.name = "BSSMAP-CC",
.sccp = PANDSIZ(bssmap_cc),
.sua = {
.hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_COAK),
.hdr = _XUA_HDR(SUA_MSGC_CO, SUA_CO_COAK),
.parts = {
PARTU32(SUA_IEI_PROTO_CLASS, &sua_proto_class2),
PARTU32(SUA_IEI_SRC_REF, &sua_loc_ref_msc),
@ -450,7 +458,7 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
.name = "BSSMAP-DTAP",
.sccp = PANDSIZ(bssmap_dtap),
.sua = {
.hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_CODT),
.hdr = _XUA_HDR(SUA_MSGC_CO, SUA_CO_CODT),
.parts = {
PARTU32(SUA_IEI_SRC_REF, &sua_loc_ref_msc),
},
@ -459,7 +467,7 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
.name = "BSSMAP-CLEAR",
.sccp = PANDSIZ(bssmap_clear),
.sua = {
.hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_CODT),
.hdr = _XUA_HDR(SUA_MSGC_CO, SUA_CO_CODT),
.parts = {
PARTU32(SUA_IEI_SRC_REF, &sua_loc_ref_msc),
},
@ -468,7 +476,7 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
.name = "BSSMAP-RELEASED",
.sccp = PANDSIZ(bssmap_released),
.sua = {
.hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_RELRE),
.hdr = _XUA_HDR(SUA_MSGC_CO, SUA_CO_RELRE),
.parts = {
PARTU32(SUA_IEI_DEST_REF, &sua_loc_ref_msc),
PARTU32(SUA_IEI_SRC_REF, &sua_loc_ref_bsc),
@ -479,7 +487,7 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
.name = "BSSMAP-RELEASE_COMPLETE",
.sccp = PANDSIZ(bssmap_release_complete),
.sua = {
.hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_RELCO),
.hdr = _XUA_HDR(SUA_MSGC_CO, SUA_CO_RELCO),
.parts = {
PARTU32(SUA_IEI_DEST_REF, &sua_loc_ref_bsc),
PARTU32(SUA_IEI_SRC_REF, &sua_loc_ref_msc),
@ -489,17 +497,45 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
.name = "TCAP",
.sccp = PANDSIZ(tcap_global_title),
.sua = {
.hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
.hdr = _XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
.parts = {
},
},
}, {
/* This case expectedly prints "Input != re-encoded output!" because input is a
* LUDT with a small payload size fitting 1-byte length, while our (re-)encoder
* encodes it as UDT in this case because it's enough to encode and transmit the
* message. Both are valid. */
.name = "LUDT-RANAP_RELEASE",
.sccp = PANDSIZ(ludt_ranap_reset),
.sua = {
.hdr = _XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
.parts = {
PARTU32(SUA_IEI_PROTO_CLASS, &sua_proto_class0),
PARTU32(SUA_IEI_S7_HOP_CTR, &sua_hop_ctr_0x0f),
PARTARR(SUA_IEI_DEST_ADDR, sua_addr_ssn_bssmap),
PARTARR(SUA_IEI_SRC_ADDR, sua_addr_ssn_bssmap),
},
},
}, {
.name = "LUDT-data300bytes",
.sccp = PANDSIZ(ludt_data300bytes),
.sua = {
.hdr = _XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
.parts = {
PARTU32(SUA_IEI_PROTO_CLASS, &sua_proto_class0),
PARTU32(SUA_IEI_S7_HOP_CTR, &sua_hop_ctr_0x0f),
PARTARR(SUA_IEI_DEST_ADDR, sua_addr_ssn_bssmap),
PARTARR(SUA_IEI_SRC_ADDR, sua_addr_ssn_bssmap),
},
},
},
};
static void test_sccp2sua_case(const struct sccp2sua_testcase *tcase)
{
struct xua_msg *xua;
struct msgb *msg = msgb_alloc(300, "SCCP2SUA Test Input");
struct msgb *msg = msgb_alloc(500, "SCCP2SUA Test Input");
struct msgb *msg2;
printf("\n=> %s\n", tcase->name);
@ -569,7 +605,7 @@ static void test_rkm(void)
talloc_free(xua);
}
void test_sccp_addr_cmp()
static void test_sccp_addr_cmp(void)
{
int ai;
int bi;
@ -616,7 +652,9 @@ int main(int argc, char **argv)
stderr_target = log_target_create_stderr();
log_add_target(stderr_target);
log_set_use_color(stderr_target, 0);
log_set_print_filename(stderr_target, 0);
log_set_print_filename2(stderr_target, LOG_FILENAME_NONE);
log_set_print_category(stderr_target, 0);
log_set_print_category_hex(stderr_target, 0);
test_isup_parse();
test_sccp_addr_parser();

View File

@ -55,7 +55,8 @@ SCCP Input: [L2]> 02 01 02 03 00 00 03 02 01 00
Transcoding message SCCP -> XUA
Decoded SUA: HDR=(CO:COAK,V=0,LEN=0), PART(T=Protocol Class,L=4,D=00000002), PART(T=Destination Reference,L=4,D=00010203), PART(T=Source Reference,L=4,D=00000003)
Re-Encoding decoded SUA to SCCP
SCCP Output: [L2]> 02 01 02 03 00 00 03 02 01 00
SCCP Output: [L2]> 02 01 02 03 00 00 03 02 00
Input != re-encoded output!
=> BSSMAP-DTAP
SCCP Input: [L2]> 06 00 00 03 00 01 0f 01 00 0c 03 05 5c 08 11 81 33 66 02 13 45 f4
@ -91,6 +92,21 @@ Transcoding message SCCP -> XUA
Decoded SUA: HDR=(CL:CLDT,V=0,LEN=0), PART(T=Protocol Class,L=4,D=00000081), PART(T=Destination Address,L=32,D=0001000580010014000000040a00010453840900170000008003000800000007), PART(T=Source Address,L=32,D=0001000580010014000000040c00010444872000206500008003000800000006), PART(T=Data,L=154,D=6581974804260001984904510103df6c8188a181850201440201073080a780a08004012b30803012830110840107850791445776671697860120300682011884010400000000a306040142840105a306040151840105a306040131840105a309040112840105820102a309040111840105810101a306040114840100a30b0401418401043003830110a30b040141840104300382011800000000)
Re-Encoding decoded SUA to SCCP
SCCP Output: [L2]> 09 81 03 0d 18 0a 12 07 00 12 04 53 84 09 00 17 0b 12 06 00 12 04 44 87 20 00 20 65 9a 65 81 97 48 04 26 00 01 98 49 04 51 01 03 df 6c 81 88 a1 81 85 02 01 44 02 01 07 30 80 a7 80 a0 80 04 01 2b 30 80 30 12 83 01 10 84 01 07 85 07 91 44 57 76 67 16 97 86 01 20 30 06 82 01 18 84 01 04 00 00 00 00 a3 06 04 01 42 84 01 05 a3 06 04 01 51 84 01 05 a3 06 04 01 31 84 01 05 a3 09 04 01 12 84 01 05 82 01 02 a3 09 04 01 11 84 01 05 81 01 01 a3 06 04 01 14 84 01 00 a3 0b 04 01 41 84 01 04 30 03 83 01 10 a3 0b 04 01 41 84 01 04 30 03 82 01 18 00 00 00 00
=> LUDT-RANAP_RELEASE
SCCP Input: [L2]> 13 00 0f 07 00 0a 00 0d 00 00 00 04 43 c4 04 8e 04 43 e9 03 8e 11 00 00 09 00 0d 00 00 02 00 04 40 01 42 00 03 00 01 00
Transcoding message SCCP -> XUA
Decoded SUA: HDR=(CL:CLDT,V=0,LEN=0), PART(T=Protocol Class,L=4,D=00000000), PART(T=SS7 Hop Counter,L=4,D=0000000f), PART(T=Destination Address,L=20,D=0002000380020008000004c4800300080000008e), PART(T=Source Address,L=20,D=0002000380020008000003e9800300080000008e), PART(T=Data,L=17,D=0009000d00000200044001420003000100)
Re-Encoding decoded SUA to SCCP
SCCP Output: [L2]> 11 00 0f 04 08 0c 00 04 43 c4 04 8e 04 43 e9 03 8e 11 00 09 00 0d 00 00 02 00 04 40 01 42 00 03 00 01 00
Input != re-encoded output!
=> LUDT-data300bytes
SCCP Input: [L2]> 13 00 0f 07 00 0a 00 0d 00 00 00 04 43 c4 04 8e 04 43 e9 03 8e 2c 01 ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab
Transcoding message SCCP -> XUA
Decoded SUA: HDR=(CL:CLDT,V=0,LEN=0), PART(T=Protocol Class,L=4,D=00000000), PART(T=SS7 Hop Counter,L=4,D=0000000f), PART(T=Destination Address,L=20,D=0002000380020008000004c4800300080000008e), PART(T=Source Address,L=20,D=0002000380020008000003e9800300080000008e), PART(T=Data,L=300,D=abababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab)
Re-Encoding decoded SUA to SCCP
SCCP Output: [L2]> 13 00 0f 07 00 0a 00 0d 00 00 00 04 43 c4 04 8e 04 43 e9 03 8e 2c 01 ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab
Parsing M3UA Message
Parsing Nested M3UA Routing Key IE
Testing SCCP Address Encode/Decode