42 Commits

Author SHA1 Message Date
Pau Espin Pedrol
d5c0b73f00 Bump version: 0.3.0.93-65d3 → 0.4.0
Change-Id: I8a7e8830dba3193419e4854588a98d1572747344
2022-08-08 11:42:57 +02:00
Vadim Yanitskiy
65d3c505b7 libosmo-sbcap: add -no-undefined to libosmo_sbcap_la_LDFLAGS
Make sure that there is no undefined references in shared libraries.

Change-Id: Id08bbd5ba35ef3f507f60f31a0634f921ec1db65
2022-08-05 11:47:44 +00:00
Pau Espin Pedrol
13152304f7 cbsp: Store content of received Cell Id from Failed List
Change-Id: I10d2627cb5ef327fd57c22480a4e7d9a464930f0
2022-08-04 14:17:48 +02:00
Pau Espin Pedrol
40a6158e41 cbsp: Log storing of CellId info received from peers
This is already done similarly in Sbc-AP.

Change-Id: I81ad8d40c5e2b9fbc011db795993fb7f00e99692
2022-08-04 14:07:45 +02:00
Pau Espin Pedrol
d78f13c89a sbcap: Add Concurrent-Warning-Message-Indicator IE to Write-Replace-Req
3GPP TS 23.041 9.3.32 states that the IE is always present for non-ETWS
messages and never present in ETWS messages.

Related: OS#4945
Change-Id: I4a5ac56b7e6eeb85aee07ae2ddf10fa2afbbf4ef
2022-08-02 17:58:10 +02:00
Pau Espin Pedrol
28d3a53dc5 cbsp/sbcap: Fail if trying to Tx on non-connected (connecting) link
This way upper layers know right away that the message could not be
transmitted to that peer.

Change-Id: I1d2285d18ee064fd78191765e8cb833bf5ee08a4
2022-08-02 11:48:59 +02:00
Pau Espin Pedrol
caba7fc493 cbsp/sbcap: Set link fsm id with peer's name
Otherwise the id is left unset.

Change-Id: I8a6850293cbe01c66e4fb545d646d63743ddaecb
2022-08-02 11:48:59 +02:00
Pau Espin Pedrol
cb4e11f984 cbsp: Fix heap-use-after-free closing cli conn in connecting state
if conn is not in STREAM_CLI_STATE_CONNECTED state, it won't call disconnect_cb during
osmo_stream_cli_destroy(), hence the osmo-cbc pointers holding are not
nullified correctly.

"""
20220801174147247 DCBSP NOTICE ttcn3-bsc-server: Disconnected. (cbsp_link.c:101)
20220801174147247 DCBSP NOTICE ttcn3-bsc-server: Reconnecting... (cbsp_link.c:102)
20220801174147247 DLINP INFO [WAIT_RECONNECT] osmo_stream_cli_reconnect(): retrying in 5 seconds... (stream.c:287)
20220801174152235 DCBSP DEBUG CBSP-Link[0x612000002c20]{RESET_PENDING}: Timeout of T3 (fsm.c:317)
20220801174152236 DCBSP DEBUG CBSP-Link[0x612000002c20]{RESET_PENDING}: timer_cb requested termination (fsm.c:327)
20220801174152236 DCBSP DEBUG CBSP-Link[0x612000002c20]{RESET_PENDING}: Terminating (cause = OSMO_FSM_TERM_TIMEOUT) (fsm.c:332)
=================================================================
==17==ERROR: AddressSanitizer: heap-use-after-free on address 0x6180000024f0 at pc 0x7fbd28a05d01 bp 0x7ffe247352b0 sp 0x7ffe247352a8
READ of size 4 at 0x6180000024f0 thread T0
    #0 0x7fbd28a05d00 in osmo_stream_cli_close /tmp/libosmo-netif/src/stream.c:307
    #1 0x7fbd28a0a5b3 in osmo_stream_cli_destroy /tmp/libosmo-netif/src/stream.c:714
    #2 0x55c3534a0322 in cbc_cbsp_link_close /tmp/osmo-cbc/src/cbsp_link.c:356
    #3 0x55c3534a16e1 in cbsp_link_fsm_cleanup /tmp/osmo-cbc/src/cbsp_link_fsm.c:199
    #4 0x7fbd28bf5085 in _osmo_fsm_inst_term /tmp/libosmocore/src/fsm.c:947
    #5 0x7fbd28be6881 in fsm_tmr_cb /tmp/libosmocore/src/fsm.c:332
    #6 0x7fbd28bc70ab in osmo_timers_update /tmp/libosmocore/src/timer.c:269
    #7 0x7fbd28bcba5b in _osmo_select_main /tmp/libosmocore/src/select.c:394
    #8 0x7fbd28bcbb31 in osmo_select_main /tmp/libosmocore/src/select.c:438
    #9 0x55c35348bce8 in main /tmp/osmo-cbc/src/cbc_main.c:314
    #10 0x7fbd27a4cd09 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26d09)
    #11 0x55c353488ce9 in _start (/usr/local/bin/osmo-cbc+0x68ce9)
"""

Change-Id: Ic13578e958345207892465644b5e1f28537c032d
2022-08-01 20:36:49 +02:00
Pau Espin Pedrol
a76d15048d *_smscb_peer_fsm: Immediately NACK if Tx of msg failed
This makes the smscb_message_fsm receive immediate notice that this peer
failed to deliver the message, avoiding need to wait for 10 seconds
timeout.

Change-Id: I1f3911accb327f3378d65902d53d86fb298fd528
2022-08-01 10:55:21 +02:00
Pau Espin Pedrol
20705a0877 cbsp/sbcap: Fix memleak in error condition (tx without link)
Change-Id: I95f6ab4e642529afc583b0747450b83af1e8f32e
2022-08-01 10:54:02 +02:00
Pau Espin Pedrol
041ae3246d sbcap: Fix NULL pointer dereference
Related: Coverity CID#275347
Change-Id: I9ae1cad4ce4e3fe6cfd67ac6058fb7345a3dd49e
2022-08-01 10:49:12 +02:00
Pau Espin Pedrol
e86af042fd Propagate error to caller when tx cbsp/sbcap Write-Replace-Req fails
Change-Id: Ie40c8e41c297398bbec48f7bb2c60cfdc703fa5d
2022-07-29 17:55:33 +02:00
Pau Espin Pedrol
0b8a08b698 cbc_main: Enable logging of fsm timeouts
Change-Id: I6ffbfbc8eddb3efff0b6401223b9b5563cf83b51
2022-07-29 17:55:33 +02:00
Pau Espin Pedrol
d24c3dfba0 Split smscb_peer_fsm into CBSP and SBcAP specific FSMs
This further simplifies tracking of events and simplifies and clears
code on each of the FSMs.

Change-Id: I0fd00b60cdc6bc6a088be1336d849548ca89c847
2022-07-29 14:54:26 +02:00
Pau Espin Pedrol
c190939f0c Split event list for smscb_message_fsm and smscb_peer_fsm
It really doesn't make sense to share the event list betwen those for
several reasons.
First, because ech of those FSM relate to different things:
* smscb_message_fsm: Handling/scheduling of 1 smscb towards all peers
* smscb_peer_fsm: Handling/scheduling of 1 smscb towards 1 peer.

The former has higher level interface used by the REST
API, plus some mid level interface used by smscb_peer_fsm.
The later has a mid level interface used by smscb_message_fsm to
interact, and a lower level interface used by the SBcAP/CBSP links to
talk to.

Furthermore, this makes it a lot easier understanding which events are
sent from one to another FSM.

Change-Id: I909474d1ff4ec7ed20aff0981da47074397df6cb
2022-07-29 14:49:30 +02:00
Pau Espin Pedrol
cb99991127 Introduce logging category smscb
The smscb_message_fsm is totally independent of CBSP.

Change-Id: I6e686747ddde9540ff3f765ef461bfca812615ba
2022-07-29 14:49:30 +02:00
Pau Espin Pedrol
e6e878a2fc sbcap: Improve handling of WriteReplaceWarnResponse
If cause != accepted, submit a NACK to the upper layers.
In that case, in the upper layers we don't want to parse the PDU IE
"Unknown Tracking Area List" since it shouldn't be there. 3GPP TS
29.168 4.3.4.3.6 states:
"""
This IE shall only be included if the Cause IE indicates Message accepted, which means the MME will proceed with the
request for Tracking Areas that are known to the MME. The Cause IE indicating Tracking area not valid is used when
all Tracking Areas in the Request are invalid.
"""

Change-Id: I0a4d5bdbb6c4fd3870a4f4ebf226668b70fea06a
2022-07-29 14:49:30 +02:00
Pau Espin Pedrol
d8a537aca8 sbcap: Store reported failed TAIs from WriteReplaceResponse in cbc_message_peer
Change-Id: I37b08aa374c1097d2871bf69a7bb7893f32bccd3
2022-07-29 14:49:30 +02:00
Pau Espin Pedrol
4a9d22e5a0 sbcap: Request and handle Write Replace Warning Indication
Change-Id: I563e7d1c999f14b8197bb41e85b7bcf6262fe2a0
2022-07-29 14:49:30 +02:00
Pau Espin Pedrol
1429377005 Move ASN1C enc/dec logging to its own category
ASN1C encoding/decoding can get really verbose. Furthermore, it should
only be enabled under really specific conditions, so it makes sense to
have it under its own category.

Change-Id: Ia4cbae2395532d9b5b7b9177a7d0f31bf777d751
2022-07-29 14:49:30 +02:00
Pau Espin Pedrol
bc21b3ae81 Move cbc_cell_id2str() and make it public
It will be used later on to log whenever new cell ids are added to a
message_peer.

Change-Id: I74ccbbc810a2fa76fb2999a7588b3f67d4d21e03
2022-07-29 14:49:30 +02:00
Pau Espin Pedrol
ab8b8402a4 sbcap: Log info about messages received and trasmitted
Change-Id: Ifd1eca79bf4fac63de8066cd6a18004138d51d04
2022-07-29 14:49:26 +02:00
Pau Espin Pedrol
e416069cf6 sbcap: Fix typo in comment
Change-Id: I6d519cb80a90e2a7e80e4608c713996f94043f21
2022-07-27 13:42:17 +02:00
Pau Espin Pedrol
e67faef4ec Move sbcap_as_find_ie() to libsbcap
This function is generic and can be (will be) used in more places.

Change-Id: Ib0c7fa12a8f6faa4271e4132ca5a5c656e717400
2022-07-27 13:42:17 +02:00
Pau Espin Pedrol
745a2d6600 sbcap: Tx Error ind if Rx ProcedureCode is unknown
Change-Id: I3ee760b98d87f179917acbc369f56d72d0db571d
2022-07-27 13:42:17 +02:00
Pau Espin Pedrol
b093e6708d sbcap: Improve logging on non-implemented PWS Restart/Failure Ind
Change-Id: I0085ac40a7676d9c3626a9da38d02d3b1b2cf6b4
2022-07-27 13:42:17 +02:00
Pau Espin Pedrol
66625de21c sbcap: Improve logging of rx Error Indication
Change-Id: I63871a57f219a98f8191e6ee4787fc33edb21528
2022-07-27 13:42:06 +02:00
Pau Espin Pedrol
6db8c13fea sbcap: Drop unused events
Those events doesn't exist in SBc-AP.

Change-Id: Ie4843ed5debbd4282df487ada9596b4d75f4b46c
2022-07-26 14:33:47 +02:00
Pau Espin Pedrol
3f9d78e2a3 sbcap: Send Error Indication if decoding rx msg fails
Change-Id: I444290f3d697e7485e04eaa38acd8fc3623e0eab
2022-07-26 14:33:47 +02:00
Pau Espin Pedrol
692243baf5 sbcap: Fix typo in sbcap_cause_vals
Change-Id: I519a86e5292c39bf1ad502c587838e7b62a0e64f
2022-07-26 14:33:47 +02:00
Pau Espin Pedrol
9ce5d4bad7 Rename functions generating CBSP/SBc-AP Write-Replace request
Previous naming is misleading, as it seems to indicate the function is
used to generate any kind of message, which is wrong.

Change-Id: Ib0228c64bbf104accdbbebb6076004a6a1f44c6f
2022-07-26 14:33:43 +02:00
Pau Espin Pedrol
7beca6fda0 Rearrange cbc_message and cbsp message code
Move code from message_handling containing both code for generic
cbc_message and specific cbsp_message into thers files. The former are
moved to cbc_message.c and the later to cbsp_msg.c, similar to what's
already done for SBc-AP messages.

Change-Id: Ie4082e40f23170cfd6c573fbbb7beb8c6226b6e6
2022-07-26 10:01:05 +02:00
Pau Espin Pedrol
4dd20eec56 vty: Fix typo in vty output
Change-Id: I601f9ebcf17c5129edbce5b3ac7f0fd8294dd572
2022-07-26 08:04:06 +02:00
Pau Espin Pedrol
9144fd1fd8 sbcap_msg: Improve spec references for used IEs
Change-Id: I55749aa84415e0af240c3cc46f2b274e1d24e403
2022-07-26 08:03:33 +02:00
Pau Espin Pedrol
3c804efcc6 vty: Print created and expired times for each message
This also allows differentiating between expired and active
messages.

Change-Id: I9ee49b99546a44be706700c3db94d45210c47e54
2022-07-26 08:03:33 +02:00
Pau Espin Pedrol
5ae411980b vty: Add command to delete expired messages
Otherwise there's no way to remove expired messages, which keep filling
the expired list forever.

Change-Id: Ie7ed2d9ec8fc23cdc4cb007dce4150458085a6a3
2022-07-26 08:03:20 +02:00
Pau Espin Pedrol
93a588ba60 vty: Fix 'show message' not finding expired messages
Change-Id: I23523876f1df167e8afd730b2164f133c2776085
2022-07-25 16:27:26 +02:00
Pau Espin Pedrol
56d1ef3b52 cbc_vty: Fix missing indent char in dump_one_etws_msg
Change-Id: I8cdced4fa108dede8d5f5dba37dde9cb199198ab
2022-07-25 16:27:26 +02:00
Pau Espin Pedrol
3ef5020007 Support CBSP/TCP and SBc-AP/SCTP client mode
This patch adds support to set up a CBSP link so that osmo-cbc connects
as a TCP client to the BSC, which runs as a TCP server.
Similary, support to set up a SBc-AP link so that osmo-cbc connects
as a SCTP client to the MME, which runs as SCTP server.

A new "mode (client|server|disabled)" VTY command is added to use one
mother or the other, and also to disable the link and hence the peer
until it is set again. This is useful if the peer is under manintenance
for instance.

client sockets are created automatically when the "peer" vty node is
exited if the link is not yet created, hence creating it at startup or
if moved back from "disabled" mode. If client socket dies, it will
keep attempting reconnect every 5 seconds.

Related: OS#4945
Change-Id: I3ec54b615b41b56f7a9c64298e3fcaac37f4b60e
2022-07-25 16:27:26 +02:00
Pau Espin Pedrol
5044524f01 cosmetic: smscb_message_fsm.c: Fix typo in comment
Change-Id: I2eb15c2401efa9b07a51dbe71997da01aab30bba
2022-07-25 14:27:48 +02:00
Pau Espin Pedrol
3468e90ab3 sbcap: Fix encoding of Warning-Type
Change-Id: I9f6a0779c20ba86962aaaebc9b40ad162d2f2f3e
2022-07-25 14:27:48 +02:00
Pau Espin Pedrol
028b48b967 cbc-apitool: print usage instead of crashing if no subcmd passed
The "required" param was added in python 3.7.

Change-Id: Ia8d13beeb8da64644d1aaf36deeddda4de889a6c
2022-07-25 14:27:48 +02:00
38 changed files with 2512 additions and 1115 deletions

View File

@@ -117,7 +117,7 @@ def main(argv):
parser.add_argument("-p", "--port", help="TCP port to connect to", type=int, default=12345)
parser.add_argument("-v", "--verbose", help="increase output verbosity", action='count', default=0)
subparsers = parser.add_subparsers()
subparsers = parser.add_subparsers(required=True)
parser_c_cbs = subparsers.add_parser('create-cbs', help='Create a new CBS message')
parser_c_cbs.add_argument("--msg-id", type=int, help='Message Identifier', required=True)

103
debian/changelog vendored
View File

@@ -1,3 +1,106 @@
osmo-cbc (0.4.0) unstable; urgency=medium
[ Pau Espin Pedrol ]
* Configure libosmocore logging system as multithread
* Move peer to DELETED state before signalling parent FSM with DELETE_(N)ACK
* Fix typos in error messages
* main: remove newline char in perror() call
* cbsp_server: Exit process gracefully if binding socket fails
* Move header files to include/osmocom/cbc/ dir
* Improve error handling when forwarding ECBE msg to CBSP
* cbsp: Clean up conn closed paths
* rest_api: Fix cbs.data_user_len not set in 'payload_encoded'
* Add sbcap library
* Add unit tests for sbcap
* sbcap: Update asn1c skeleton files
* Add initial SBc-AP support to osmo-cbc
* doc: user manual: fix typo
* doc: Document SBc-AP support and config
* Fix printf format in 32bit arch
* vty: Fix call to OSMO_STRBUF_PRINTF
* sbcap: Update asn1c skeleton files
* Improve example osmo-cbc.cfg file
* sbcap_server.c: Remove unneeded goto
* sbcap_server: Log all sctp notif
* sbcap_server: Fix double-free of rx msg if conn is destroyed
* Split cbc_peer to its own .c and .h files
* cbc_peer: Close SBcAP conn when removing active peer
* Move function defined in rest_it_op.c to rest_it_op.h
* Move vty stuff to its own header cbc_vty.h
* Split cbc_message related code to its own .c and .h file
* Get rid of internal.h
* sbcap_server.h: Remove unused fields
* cbsp_server.c: Drop unused code
* Rename CBSP,SABP,SBcAP structs and APIs
* Rename {cbsp,sbcap}_server(_fsm).h s/server/link/
* Rename fields in cbc_{cbsp,sbcap}_mgr
* Use cbc_{cbsp,sbcap}_link_close when possible
* cbc_vty: Use value_string to define proto names used in vty
* cbc_vty: print correct protocol when writing config
* Refactor {cbsp,sbcap}_cbc_accept_cb
* cbc_vty: write peer config nodes at the end
* Make cbsp,sbcap mgr available in data model
* Fix cbc_vty_go_parent() not being called
* cbc_main: Fix setting default SBc-AP local address
* vty: Define peer proto during 'peer' node cmd
* Move struct cbc bring up code to its own file and functions
* Split cbsp/sbcap server socket creation from struct allocation
* cbc-apitool: Fix port stored in var as a string
* cbc-apitool: print usage instead of crashing if no subcmd passed
* sbcap: Fix encoding of Warning-Type
* cosmetic: smscb_message_fsm.c: Fix typo in comment
* Support CBSP/TCP and SBc-AP/SCTP client mode
* cbc_vty: Fix missing indent char in dump_one_etws_msg
* vty: Fix 'show message' not finding expired messages
* vty: Add command to delete expired messages
* vty: Print created and expired times for each message
* sbcap_msg: Improve spec references for used IEs
* vty: Fix typo in vty output
* Rearrange cbc_message and cbsp message code
* Rename functions generating CBSP/SBc-AP Write-Replace request
* sbcap: Fix typo in sbcap_cause_vals
* sbcap: Send Error Indication if decoding rx msg fails
* sbcap: Drop unused events
* sbcap: Improve logging of rx Error Indication
* sbcap: Improve logging on non-implemented PWS Restart/Failure Ind
* sbcap: Tx Error ind if Rx ProcedureCode is unknown
* Move sbcap_as_find_ie() to libsbcap
* sbcap: Fix typo in comment
* sbcap: Log info about messages received and trasmitted
* Move cbc_cell_id2str() and make it public
* Move ASN1C enc/dec logging to its own category
* sbcap: Request and handle Write Replace Warning Indication
* sbcap: Store reported failed TAIs from WriteReplaceResponse in cbc_message_peer
* sbcap: Improve handling of WriteReplaceWarnResponse
* Introduce logging category smscb
* Split event list for smscb_message_fsm and smscb_peer_fsm
* Split smscb_peer_fsm into CBSP and SBcAP specific FSMs
* cbc_main: Enable logging of fsm timeouts
* Propagate error to caller when tx cbsp/sbcap Write-Replace-Req fails
* sbcap: Fix NULL pointer dereference
* cbsp/sbcap: Fix memleak in error condition (tx without link)
* *_smscb_peer_fsm: Immediately NACK if Tx of msg failed
* cbsp: Fix heap-use-after-free closing cli conn in connecting state
* cbsp/sbcap: Set link fsm id with peer's name
* cbsp/sbcap: Fail if trying to Tx on non-connected (connecting) link
* sbcap: Add Concurrent-Warning-Message-Indicator IE to Write-Replace-Req
* cbsp: Log storing of CellId info received from peers
* cbsp: Store content of received Cell Id from Failed List
[ Oliver Smith ]
* gitreview: add new file
* checkpatch.conf: ignore sbcap generated files
* contrib/osmo-cbc.spec: add libosmo-sbcap0/-dev
* debian: add subpackages libosmo-sbcap0/-dev
* libosmo-sbcap.pc.in: new file
[ Vadim Yanitskiy ]
* tests/sbcap: fix wrong operator used in OSMO_ASSERT statement
* cbc_vty: use install_element[_ve](), not install_lib_element[_ve]()
* libosmo-sbcap: add -no-undefined to libosmo_sbcap_la_LDFLAGS
-- Pau Espin Pedrol <pespin@sysmocom.de> Mon, 08 Aug 2022 11:42:56 +0200
osmo-cbc (0.3.0) unstable; urgency=medium
[ Vadim Yanitskiy ]

View File

@@ -1,8 +1,10 @@
log stderr
logging level main notice
logging level rest notice
logging level smscb notice
logging level cbsp notice
logging level sbcap notice
logging level asn1c notice
logging level rest notice
cbc
unknown-peers reject
ecbe
@@ -16,9 +18,11 @@ cbc
local-ip ::1
local-port 29168
peer cbsp example-bsc
mode server
remote-ip 127.0.0.2
remote-port 48049
peer sbcap example-mme
mode client
remote-ip 127.0.0.2
remote-ip ::2
remote-port 29168

View File

@@ -5,6 +5,7 @@ noinst_HEADERS = \
cbc_vty.h \
cbsp_link.h \
cbsp_link_fsm.h \
cbsp_msg.h \
charset.h \
debug.h \
sbcap_msg.h \
@@ -12,4 +13,5 @@ noinst_HEADERS = \
sbcap_link_fsm.h \
rest_it_op.h \
smscb_message_fsm.h \
smscb_peer_fsm.h \
$(NULL)

View File

@@ -16,6 +16,8 @@ enum cbc_cell_id_type {
CBC_CELL_ID_LAI,
CBC_CELL_ID_LAC,
CBC_CELL_ID_CI,
CBC_CELL_ID_ECGI,
CBC_CELL_ID_TAI,
};
struct cbc_cell_id {
@@ -27,6 +29,8 @@ struct cbc_cell_id {
struct osmo_location_area_id lai;
uint16_t lac;
uint16_t ci;
struct osmo_eutran_cell_global_id ecgi;
struct osmo_tracking_area_id tai;
} u;
/* only in failure list */
struct {
@@ -39,6 +43,8 @@ struct cbc_cell_id {
} num_compl;
};
const char *cbc_cell_id2str(const struct cbc_cell_id *cid);
/*********************************************************************************
* CBC itself
*********************************************************************************/

View File

@@ -99,9 +99,11 @@ struct cbc_message {
};
struct cbc_message *cbc_message_alloc(void *ctx, const struct cbc_message *cbcmsg);
void cbc_message_free(struct cbc_message *cbcmsg);
int cbc_message_new(const struct cbc_message *cbcmsg, struct rest_it_op *op);
void cbc_message_delete(struct cbc_message *cbcmsg, struct rest_it_op *op);
struct cbc_message *cbc_message_by_id(uint16_t message_id);
struct cbc_message *cbc_message_expired_by_id(uint16_t message_id);
int peer_new_cbc_message(struct cbc_peer *peer, struct cbc_message *cbcmsg);
int cbc_message_del_peer(struct cbc_message *cbcmsg, struct cbc_peer *peer);

View File

@@ -19,6 +19,16 @@ enum cbc_peer_protocol {
CBC_PEER_PROTO_SBcAP
};
enum cbc_peer_link_mode {
CBC_PEER_LINK_MODE_DISABLED = 0,
CBC_PEER_LINK_MODE_SERVER,
CBC_PEER_LINK_MODE_CLIENT,
};
extern const struct value_string cbc_peer_link_mode_names[];
static inline const char *cbc_peer_link_mode_name(enum cbc_peer_link_mode val)
{ return get_value_string(cbc_peer_link_mode_names, val); }
struct cbc_peer {
struct llist_head list; /* linked to cbc.peers */
const char *name;
@@ -34,6 +44,7 @@ struct cbc_peer {
struct cbc_sabp_link *sabp;
struct cbc_sbcap_link *sbcap;
} link;
enum cbc_peer_link_mode link_mode;
};
extern const struct value_string cbc_peer_proto_name[];
@@ -44,3 +55,4 @@ void cbc_peer_remove(struct cbc_peer *peer);
struct cbc_peer *cbc_peer_by_name(const char *name);
struct cbc_peer *cbc_peer_by_addr_proto(const char *remote_host, uint16_t remote_port,
enum cbc_peer_protocol proto);
int cbc_peer_apply_cfg_chg(struct cbc_peer *peer);

View File

@@ -31,19 +31,22 @@ int cbc_cbsp_mgr_open_srv(struct cbc_cbsp_mgr *mgr);
struct cbc_cbsp_link {
/* entry in osmo_cbsp_cbc.links */
struct llist_head list;
/* stream server connection for this link */
struct osmo_stream_srv *conn;
/* partially received CBSP message (rx completion pending) */
struct msgb *rx_msg;
struct osmo_fsm_inst *fi;
struct cbc_peer *peer;
bool is_client;
union {
struct osmo_stream_srv *srv_conn;
struct osmo_stream_cli *cli_conn;
void *conn; /* used when we just care about the pointer */
};
};
struct cbc_cbsp_link *cbc_cbsp_link_alloc(struct cbc_cbsp_mgr *cbc, struct cbc_peer *peer);
void cbc_cbsp_link_free(struct cbc_cbsp_link *link);
const char *cbc_cbsp_link_name(const struct cbc_cbsp_link *link);
void cbc_cbsp_link_tx(struct cbc_cbsp_link *link, struct osmo_cbsp_decoded *cbsp);
int cbc_cbsp_link_open_cli(struct cbc_cbsp_link *link);
int cbc_cbsp_link_tx(struct cbc_cbsp_link *link, struct osmo_cbsp_decoded *cbsp);
void cbc_cbsp_link_close(struct cbc_cbsp_link *link);
int cbc_cbsp_link_rx_cb(struct cbc_cbsp_link *link, struct osmo_cbsp_decoded *dec);

View File

@@ -0,0 +1,5 @@
#pragma once
#include <osmocom/gsm/cbsp.h>
struct cbc_message;
struct osmo_cbsp_decoded *cbsp_gen_write_replace_req(void *ctx, const struct cbc_message *cbcmsg);

View File

@@ -2,7 +2,9 @@
enum {
DMAIN,
DSMSCB,
DCBSP,
DSBcAP,
DASN1C,
DREST,
};

View File

@@ -33,15 +33,20 @@ int cbc_sbcap_mgr_open_srv(struct cbc_sbcap_mgr *mgr);
struct cbc_sbcap_link {
/* entry in osmo_sbcap_cbc.links */
struct llist_head list;
/* stream server connection for this link */
struct osmo_stream_srv *conn;
struct osmo_fsm_inst *fi;
struct cbc_peer *peer;
bool is_client;
union {
struct osmo_stream_srv *srv_conn;
struct osmo_stream_cli *cli_conn;
void *conn; /* used when we just care about the pointer */
};
};
struct cbc_sbcap_link *cbc_sbcap_link_alloc(struct cbc_sbcap_mgr *cbc, struct cbc_peer *peer);
void cbc_sbcap_link_free(struct cbc_sbcap_link *link);
const char *cbc_sbcap_link_name(const struct cbc_sbcap_link *link);
void cbc_sbcap_link_tx(struct cbc_sbcap_link *link, SBcAP_SBC_AP_PDU_t *pdu);
int cbc_sbcap_link_open_cli(struct cbc_sbcap_link *link);
int cbc_sbcap_link_tx(struct cbc_sbcap_link *link, SBcAP_SBC_AP_PDU_t *pdu);
void cbc_sbcap_link_close(struct cbc_sbcap_link *link);
int cbc_sbcap_link_rx_cb(struct cbc_sbcap_link *link, SBcAP_SBC_AP_PDU_t *pdu);

View File

@@ -6,10 +6,7 @@
extern struct osmo_fsm sbcap_link_fsm;
enum sbcap_link_event {
SBcAP_LINK_E_RX_RST_COMPL, /* reset complete received */
SBcAP_LINK_E_RX_RST_FAIL, /* reset failure received */
SBcAP_LINK_E_RX_KA_COMPL, /* keep-alive complete received */
SBcAP_LINK_E_RX_RESTART, /* restart received */
SBcAP_LINK_E_RX_RESTART, /* SBc-AP PWS Restart Ind received */
SBcAP_LINK_E_CMD_RESET, /* RESET command from CBC */
SBcAP_LINK_E_CMD_CLOSE, /* CLOSE command from CBC */
};

View File

@@ -1,11 +1,17 @@
#pragma once
#include <osmocom/core/linuxlist.h>
#include <osmocom/netif/stream.h>
#include <osmocom/sbcap/sbcap_common.h>
#include "cbc_data.h"
struct cbc_message;
typedef struct SBcAP_SBC_AP_PDU SBcAP_SBC_AP_PDU_t;
SBcAP_SBC_AP_PDU_t *cbcmsg_to_sbcap(void *ctx, const struct cbc_message *cbcmsg);
SBcAP_SBC_AP_PDU_t *sbcap_gen_write_replace_warning_req(void *ctx, const struct cbc_message *cbcmsg);
SBcAP_SBC_AP_PDU_t *sbcap_gen_stop_warning_req(void *ctx, const struct cbc_message *cbcmsg);
SBcAP_SBC_AP_PDU_t *sbcap_gen_error_ind(void *ctx, SBcAP_Cause_t cause, SBcAP_SBC_AP_PDU_t *rx_pdu);
void cci_from_sbcap_bcast_cell_id(struct cbc_cell_id *cci, const SBcAP_CellId_Broadcast_List_Item_t *it);
void cci_from_sbcap_tai(struct cbc_cell_id *cci, const SBcAP_TAI_t *tai);

View File

@@ -3,34 +3,28 @@
#include <stdint.h>
#include <osmocom/core/fsm.h>
enum smscb_fsm_event {
SMSCB_E_CHILD_DIED,
enum smscb_message_fsm_event {
SMSCB_MSG_E_CHILD_DIED,
/* create a message (from REST) */
SMSCB_E_CREATE,
SMSCB_MSG_E_CREATE,
/* replace a message (from REST) */
SMSCB_E_REPLACE,
SMSCB_MSG_E_REPLACE,
/* get status of a message (from REST) */
SMSCB_E_STATUS,
SMSCB_MSG_E_STATUS,
/* delete a message (from REST) */
SMSCB_E_DELETE,
/* CBSP peer confirms write */
SMSCB_E_CBSP_WRITE_ACK,
SMSCB_E_CBSP_WRITE_NACK,
/* CBSP peer confirms replace */
SMSCB_E_CBSP_REPLACE_ACK,
SMSCB_E_CBSP_REPLACE_NACK,
/* CBSP peer confirms delete */
SMSCB_E_CBSP_DELETE_ACK,
SMSCB_E_CBSP_DELETE_NACK,
/* CBSP peer confirms status query */
SMSCB_E_CBSP_STATUS_ACK,
SMSCB_E_CBSP_STATUS_NACK,
/* SBc-AP peer confirms write */
SMSCB_E_SBCAP_WRITE_ACK,
SMSCB_E_SBCAP_WRITE_NACK,
/* SBc-AP peer confirms delete */
SMSCB_E_SBCAP_DELETE_ACK,
SMSCB_E_SBCAP_DELETE_NACK,
SMSCB_MSG_E_DELETE,
/* peer confirms write */
SMSCB_MSG_E_WRITE_ACK,
SMSCB_MSG_E_WRITE_NACK,
/* peer confirms replace */
SMSCB_MSG_E_REPLACE_ACK,
SMSCB_MSG_E_REPLACE_NACK,
/* peer confirms delete */
SMSCB_MSG_E_DELETE_ACK,
SMSCB_MSG_E_DELETE_NACK,
/* peer confirms status query */
SMSCB_MSG_E_STATUS_ACK,
SMSCB_MSG_E_STATUS_NACK
};
enum smscb_fsm_state {
@@ -56,4 +50,4 @@ enum smscb_p_fsm_timer {
T_WAIT_DELETE_ACK,
};
extern const struct value_string smscb_fsm_event_names[];
extern const struct value_string smscb_message_fsm_event_names[];

View File

@@ -0,0 +1,40 @@
#pragma once
#include <stdint.h>
#include <osmocom/core/fsm.h>
enum smscb_peer_fsm_event {
/* create a message (from REST) */
SMSCB_PEER_E_CREATE,
/* replace a message (from REST) */
SMSCB_PEER_E_REPLACE,
/* get status of a message (from REST) */
SMSCB_PEER_E_STATUS,
/* delete a message (from REST) */
SMSCB_PEER_E_DELETE,
/* CBSP peer confirms write */
SMSCB_PEER_E_CBSP_WRITE_ACK,
SMSCB_PEER_E_CBSP_WRITE_NACK,
/* CBSP peer confirms replace */
SMSCB_PEER_E_CBSP_REPLACE_ACK,
SMSCB_PEER_E_CBSP_REPLACE_NACK,
/* CBSP peer confirms delete */
SMSCB_PEER_E_CBSP_DELETE_ACK,
SMSCB_PEER_E_CBSP_DELETE_NACK,
/* CBSP peer confirms status query */
SMSCB_PEER_E_CBSP_STATUS_ACK,
SMSCB_PEER_E_CBSP_STATUS_NACK,
/* SBc-AP peer confirms write */
SMSCB_PEER_E_SBCAP_WRITE_ACK,
SMSCB_PEER_E_SBCAP_WRITE_NACK,
/* SBc-AP peer confirms delete */
SMSCB_PEER_E_SBCAP_DELETE_ACK,
SMSCB_PEER_E_SBCAP_DELETE_NACK,
/* SBc-AP peer sends Write Replace Warning Indication to us */
SMSCB_PEER_E_SBCAP_WRITE_IND,
};
extern const struct value_string smscb_peer_fsm_event_names[];
extern struct osmo_fsm cbsp_smscb_peer_fsm;
extern struct osmo_fsm sbcap_smscb_peer_fsm;

View File

@@ -118,8 +118,8 @@
#include <osmocom/core/logging.h>
extern int _sbcap_DSBCAP;
#define SBC_AP_DEBUG(x, args ...) DEBUGP(_sbcap_DSBCAP, x, ## args)
extern int _sbcap_DASN1C;
#define SBC_AP_DEBUG(x, args ...) DEBUGP(_sbcap_DASN1C, x, ## args)
extern int asn1_xer_print;
@@ -137,12 +137,22 @@ void sbcap_pdu_free(SBcAP_SBC_AP_PDU_t *pdu);
struct msgb *sbcap_encode(SBcAP_SBC_AP_PDU_t *pdu);
SBcAP_SBC_AP_PDU_t *sbcap_decode(const struct msgb *msg);
const char *sbcap_procedure_code_str(SBcAP_ProcedureCode_t pc);
const char *sbcap_cause_str(SBcAP_Cause_t cause);
void sbcap_set_log_area(int log_area);
void sbcap_set_log_area(int log_area_sbcap, int log_area_asn1c);
SBcAP_ProcedureCode_t sbcap_pdu_get_procedure_code(const SBcAP_SBC_AP_PDU_t *pdu);
SBcAP_Criticality_t sbcap_pdu_get_criticality(const SBcAP_SBC_AP_PDU_t *pdu);
const char *sbcap_pdu_get_name(const SBcAP_SBC_AP_PDU_t *pdu);
void *sbcap_as_find_ie(void *void_list, SBcAP_ProtocolIE_ID_t ie_id);
SBcAP_Write_Replace_Warning_Request_IEs_t *sbcap_alloc_Write_Replace_Warning_Request_IE(
long id, SBcAP_Criticality_t criticality, SBcAP_Write_Replace_Warning_Request_IEs__value_PR present);
SBcAP_Stop_Warning_Request_IEs_t *sbcap_alloc_Stop_Warning_Request_IE(
long id, SBcAP_Criticality_t criticality, SBcAP_Stop_Warning_Request_IEs__value_PR present);
SBcAP_ErrorIndicationIEs_t *sbcap_alloc_Error_Indication_IE(
long id, SBcAP_Criticality_t criticality, SBcAP_Stop_Warning_Request_IEs__value_PR present);

View File

@@ -2,7 +2,7 @@
#include <osmocom/core/logging.h>
extern int _sbcap_DSBCAP;
#define SBC_AP_ASN_DEBUG(x, args ...) DEBUGP(_sbcap_DSBCAP, x "\n", ## args)
extern int _sbcap_DASN1C;
#define SBC_AP_ASN_DEBUG(x, args ...) DEBUGP(_sbcap_DASN1C, x "\n", ## args)
#define ASN_DEBUG SBC_AP_ASN_DEBUG

View File

@@ -17,15 +17,17 @@ osmo_cbc_SOURCES = \
cbc_vty.c \
cbsp_link.c \
cbsp_link_fsm.c \
cbsp_msg.c \
cbsp_smscb_peer_fsm.c \
rest_api.c \
charset.c \
message_handling.c \
rest_it_op.c \
sbcap_msg.c \
sbcap_link.c \
sbcap_link_fsm.c \
smscb_peer_fsm.c \
sbcap_smscb_peer_fsm.c \
smscb_message_fsm.c \
smscb_peer_fsm.c \
$(NULL)
osmo_cbc_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) \

View File

@@ -35,6 +35,46 @@
#include <osmocom/cbc/rest_it_op.h>
#include <osmocom/cbc/debug.h>
const char *cbc_cell_id2str(const struct cbc_cell_id *cid)
{
static char buf[256];
switch (cid->id_discr) {
case CBC_CELL_ID_NONE:
snprintf(buf, sizeof(buf), "NONE");
break;
case CBC_CELL_ID_BSS:
snprintf(buf, sizeof(buf), "BSS");
break;
case CBC_CELL_ID_CGI:
snprintf(buf, sizeof(buf), "CGI %s", osmo_cgi_name(&cid->u.cgi));
break;
case CBC_CELL_ID_LAC_CI:
snprintf(buf, sizeof(buf), "LAC %u CI %u", cid->u.lac_and_ci.lac, cid->u.lac_and_ci.ci);
break;
case CBC_CELL_ID_LAI:
snprintf(buf, sizeof(buf), "LAI %s", osmo_lai_name(&cid->u.lai));
break;
case CBC_CELL_ID_LAC:
snprintf(buf, sizeof(buf), "LAC %u", cid->u.lac);
break;
case CBC_CELL_ID_CI:
snprintf(buf, sizeof(buf), "CI %u", cid->u.ci);
break;
case CBC_CELL_ID_ECGI:
snprintf(buf, sizeof(buf), "ECGI %s-%05X-%02X", osmo_plmn_name(&cid->u.ecgi.plmn),
cid->u.ecgi.eci >> 8, cid->u.ecgi.eci & 0xff);
break;
case CBC_CELL_ID_TAI:
snprintf(buf, sizeof(buf), "TAI %s-%u", osmo_plmn_name(&cid->u.tai.plmn), cid->u.tai.tac);
break;
default:
snprintf(buf, sizeof(buf), "<invalid>");
break;
}
return buf;
}
struct cbc *cbc_alloc(void *ctx)
{
struct cbc *cbc;

View File

@@ -35,6 +35,7 @@
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/application.h>
#include <osmocom/core/fsm.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/stats.h>
@@ -44,9 +45,12 @@
#include <osmocom/vty/logging.h>
#include <osmocom/vty/misc.h>
#include <osmocom/sbcap/sbcap_common.h>
#include <osmocom/cbc/debug.h>
#include <osmocom/cbc/cbc_data.h>
#include <osmocom/cbc/cbc_vty.h>
#include <osmocom/cbc/cbc_peer.h>
static void *tall_cbc_ctx;
struct cbc *g_cbc;
@@ -59,6 +63,13 @@ static const struct log_info_cat log_info_cat[] = {
.enabled = 1,
.loglevel = LOGL_NOTICE,
},
[DSMSCB] = {
.name = "DSMSCB",
.description = "SMS Cell Broadcast handling",
.color = "\033[1;35m",
.enabled = 1,
.loglevel = LOGL_NOTICE,
},
[DCBSP] = {
.name = "DCBSP",
.description = "Cell Broadcast Service Protocol (CBC-BSC)",
@@ -73,6 +84,13 @@ static const struct log_info_cat log_info_cat[] = {
.enabled = 1,
.loglevel = LOGL_NOTICE,
},
[DASN1C] = {
.name = "DASN1C",
.description = "SBc-AP ASN1C enc/dec",
.color = "\033[1;34m",
.enabled = 1,
.loglevel = LOGL_NOTICE,
},
[DREST] = {
.name = "DREST",
.description = "REST interface",
@@ -99,6 +117,11 @@ static int cbc_vty_go_parent(struct vty *vty)
vty->node = CONFIG_NODE;
vty->index = NULL;
break;
case PEER_NODE:
cbc_peer_apply_cfg_chg((struct cbc_peer *)vty->index);
vty->node = CONFIG_NODE;
vty->index = NULL;
break;
default:
vty->node = CONFIG_NODE;
vty->index = NULL;
@@ -244,7 +267,9 @@ int main(int argc, char **argv)
msgb_talloc_ctx_init(tall_cbc_ctx, 0);
osmo_init_logging2(tall_cbc_ctx, &log_info);
log_enable_multithread();
sbcap_set_log_area(DSBcAP, DASN1C);
osmo_stats_init(tall_cbc_ctx);
osmo_fsm_log_timeouts(true);
vty_init(&vty_info);
g_cbc = cbc_alloc(tall_cbc_ctx);

View File

@@ -27,8 +27,146 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/cbc/cbsp_msg.h>
#include <osmocom/cbc/cbsp_link.h>
#include <osmocom/cbc/sbcap_msg.h>
#include <osmocom/cbc/sbcap_link.h>
#include <osmocom/cbc/cbc_message.h>
#include <osmocom/cbc/cbc_peer.h>
#include <osmocom/cbc/debug.h>
#include <osmocom/cbc/rest_it_op.h>
#include <osmocom/cbc/smscb_message_fsm.h>
/* determine if peer is within scope of cbc_msg */
static bool is_peer_in_scope(const struct cbc_peer *peer, const struct cbc_message *cbcmsg)
{
switch (cbcmsg->scope) {
case CBC_MSG_SCOPE_PLMN:
return true;
/* FIXME: differnt scopes */
default:
OSMO_ASSERT(0);
}
}
/* send given new message to given peer */
int peer_new_cbc_message(struct cbc_peer *peer, struct cbc_message *cbcmsg)
{
struct osmo_cbsp_decoded *cbsp;
SBcAP_SBC_AP_PDU_t *sbcap;
switch (peer->proto) {
case CBC_PEER_PROTO_CBSP:
/* skip peers without any current CBSP connection */
if (!peer->link.cbsp) {
LOGP(DCBSP, LOGL_NOTICE, "[%s] Tx CBSP: not connected\n",
peer->name);
return -ENOTCONN;
}
if (!(cbsp = cbsp_gen_write_replace_req(peer, cbcmsg))) {
LOGP(DCBSP, LOGL_ERROR, "[%s] Tx CBSP: msg gen failed\n",
peer->name);
return -EINVAL;
}
return cbc_cbsp_link_tx(peer->link.cbsp, cbsp);
case CBC_PEER_PROTO_SBcAP:
/* skip peers without any current SBc-AP connection */
if (!peer->link.sbcap) {
LOGP(DSBcAP, LOGL_NOTICE, "[%s] Tx SBc-AP: not connected\n",
peer->name);
return -ENOTCONN;
}
if (!(sbcap = sbcap_gen_write_replace_warning_req(peer, cbcmsg))) {
LOGP(DSBcAP, LOGL_ERROR, "[%s] Tx SBc-AP: msg gen failed\n",
peer->name);
return -EINVAL;
}
return cbc_sbcap_link_tx(peer->link.sbcap, sbcap);
case CBC_PEER_PROTO_SABP:
LOGP(DLGLOBAL, LOGL_ERROR, "Sending message to peer proto %s not implemented!\n",
get_value_string(cbc_peer_proto_name, peer->proto));
return -1;
default:
OSMO_ASSERT(0);
}
}
/* receive a new CBC message from the user (REST). Allocates new memory,
* a FSM, copies data from 'orig', routes to all peers and starts FSMs.
* Once the operation is complete (success, error, timeout) we must
* notify osmo_it_q of the completion */
int cbc_message_new(const struct cbc_message *orig, struct rest_it_op *op)
{
struct cbc_message *cbcmsg = cbc_message_alloc(g_cbc, orig);
struct cbc_peer *peer;
if (!cbcmsg) {
rest_it_op_set_http_result(op, 409, "Could not allocate");
rest_it_op_complete(op);
return -ENOMEM;
}
OSMO_ASSERT(llist_empty(&cbcmsg->peers));
/* iterate over all peers */
llist_for_each_entry(peer, &g_cbc->peers, list) {
struct cbc_message_peer *mp;
if (!is_peer_in_scope(peer, cbcmsg))
continue;
/* allocate new cbc_mesage_peer + related FSM */
mp = smscb_peer_fsm_alloc(peer, cbcmsg);
if (!mp) {
LOGP(DCBSP, LOGL_ERROR, "Cannot allocate cbc_message_peer\n");
continue;
}
}
/* kick off the state machine[s] */
if (osmo_fsm_inst_dispatch(cbcmsg->fi, SMSCB_MSG_E_CREATE, op) < 0) {
rest_it_op_set_http_result(op, 500, "Illegal FSM event");
rest_it_op_complete(op);
}
/* we continue in the FSM after the WRITE_ACK event was received */
return 0;
}
void cbc_message_delete(struct cbc_message *cbcmsg, struct rest_it_op *op)
{
if (osmo_fsm_inst_dispatch(cbcmsg->fi, SMSCB_MSG_E_DELETE, op) < 0) {
rest_it_op_set_http_result(op, 500, "Illegal FSM event");
rest_it_op_complete(op);
}
/* we continue in the FSM after the DELETE_ACK event was received */
}
struct cbc_message *cbc_message_by_id(uint16_t message_id)
{
struct cbc_message *cbc_msg;
llist_for_each_entry(cbc_msg, &g_cbc->messages, list) {
if (cbc_msg->msg.message_id == message_id)
return cbc_msg;
}
return NULL;
}
struct cbc_message *cbc_message_expired_by_id(uint16_t message_id)
{
struct cbc_message *cbc_msg;
llist_for_each_entry(cbc_msg, &g_cbc->expired_messages, list) {
if (cbc_msg->msg.message_id == message_id)
return cbc_msg;
}
return NULL;
}
/* remove a peer from the message */
int cbc_message_del_peer(struct cbc_message *cbcmsg, struct cbc_peer *peer)

View File

@@ -32,6 +32,7 @@
#include <osmocom/cbc/cbc_peer.h>
#include <osmocom/cbc/cbsp_link.h>
#include <osmocom/cbc/sbcap_link.h>
#include <osmocom/cbc/debug.h>
const struct value_string cbc_peer_proto_name[] = {
{ CBC_PEER_PROTO_CBSP, "CBSP" },
@@ -40,6 +41,14 @@ const struct value_string cbc_peer_proto_name[] = {
{ 0, NULL }
};
const struct value_string cbc_peer_link_mode_names[] = {
{ CBC_PEER_LINK_MODE_DISABLED, "disabled" },
{ CBC_PEER_LINK_MODE_SERVER, "server" },
{ CBC_PEER_LINK_MODE_CLIENT, "client" },
{}
};
/* create a new cbc_peer */
struct cbc_peer *cbc_peer_create(const char *name, enum cbc_peer_protocol proto)
{
@@ -120,3 +129,110 @@ struct cbc_peer *cbc_peer_by_addr_proto(const char *remote_host, uint16_t remote
}
return NULL;
}
static int cbc_peer_apply_cfg_chg_cbsp(struct cbc_peer *peer)
{
struct cbc_cbsp_link *link = peer->link.cbsp;
int rc = 0;
switch (peer->link_mode) {
case CBC_PEER_LINK_MODE_DISABLED:
if (link) {
LOGPCC(link, LOGL_NOTICE,
"link mode changed to 'disabled', closing active link\n");
cbc_cbsp_link_close(link);
}
/* Nothing to be done, cbc_cbsp_mgr->srv_link will refuse
* accepting() disabled peers. */
OSMO_ASSERT(!peer->link.cbsp);
break;
case CBC_PEER_LINK_MODE_SERVER:
if (link && link->is_client) {
LOGPCC(link, LOGL_NOTICE,
"link mode changed 'client' -> 'server', closing active link\n");
cbc_cbsp_link_close(link);
}
/* Nothing to be done, cbc_cbsp_mgr->srv_link will accept() and
* recreate the link */
OSMO_ASSERT(!peer->link.cbsp);
break;
case CBC_PEER_LINK_MODE_CLIENT:
if (link) {
if (link->is_client) {
/* nothing to be done, cli link already created */
break;
}
LOGPCC(link, LOGL_NOTICE,
"link mode changed 'server' -> 'client', closing active link\n");
cbc_cbsp_link_close(link);
}
OSMO_ASSERT(!peer->link.cbsp);
link = cbc_cbsp_link_alloc(g_cbc->cbsp.mgr, peer);
peer->link.cbsp = link;
rc = cbc_cbsp_link_open_cli(link);
break;
}
return rc;
}
static int cbc_peer_apply_cfg_chg_sbcap(struct cbc_peer *peer)
{
struct cbc_sbcap_link *link = peer->link.sbcap;
int rc = 0;
switch (peer->link_mode) {
case CBC_PEER_LINK_MODE_DISABLED:
if (link) {
LOGPSBCAPC(link, LOGL_NOTICE,
"link mode changed to 'disabled', closing active link\n");
cbc_sbcap_link_close(link);
}
/* Nothing to be done, cbc_sbcap_mgr->srv_link will refuse
* accepting() disabled peers. */
OSMO_ASSERT(!peer->link.sbcap);
break;
case CBC_PEER_LINK_MODE_SERVER:
if (link && link->is_client) {
LOGPSBCAPC(link, LOGL_NOTICE,
"link mode changed 'client' -> 'server', closing active link\n");
cbc_sbcap_link_close(link);
}
/* Nothing to be done, cbc_sbcap_mgr->srv_link will accept() and
* recreate the link */
OSMO_ASSERT(!peer->link.sbcap);
break;
case CBC_PEER_LINK_MODE_CLIENT:
if (link) {
if (link->is_client) {
/* nothing to be done, cli link already created */
break;
}
LOGPSBCAPC(link, LOGL_NOTICE,
"link mode changed 'server' -> 'client', closing active link\n");
cbc_sbcap_link_close(link);
}
OSMO_ASSERT(!peer->link.sbcap);
link = cbc_sbcap_link_alloc(g_cbc->sbcap.mgr, peer);
peer->link.sbcap = link;
rc = cbc_sbcap_link_open_cli(link);
break;
}
return rc;
}
int cbc_peer_apply_cfg_chg(struct cbc_peer *peer)
{
int rc = -ENOTSUP;
switch (peer->proto) {
case CBC_PEER_PROTO_CBSP:
rc = cbc_peer_apply_cfg_chg_cbsp(peer);
break;
case CBC_PEER_PROTO_SBcAP:
rc = cbc_peer_apply_cfg_chg_sbcap(peer);
break;
case CBC_PEER_PROTO_SABP:
break;
}
return rc;
}

View File

@@ -102,13 +102,26 @@ DEFUN(show_peers, show_peers_cmd,
static void dump_one_cbc_msg(struct vty *vty, const struct cbc_message *cbc_msg)
{
const struct smscb_message *smscb = &cbc_msg->msg;
char str_created[32], str_expired[32];
struct tm tm_created = {0};
struct tm tm_expired = {0};
OSMO_ASSERT(!smscb->is_etws);
vty_out(vty, "| %04X| %04X|%-20s|%-13s| %-4u|%c| %02x|%s",
localtime_r(&cbc_msg->time.created, &tm_created);
strftime(str_created, sizeof(str_created), "%Y-%m-%dT%H:%M:%SZ", &tm_created);
if (cbc_msg->time.expired > 0) {
localtime_r(&cbc_msg->time.expired, &tm_expired);
strftime(str_expired, sizeof(str_expired), "%Y-%m-%dT%H:%M:%SZ", &tm_expired);
} else {
OSMO_STRLCPY_ARRAY(str_expired, "active");
}
vty_out(vty, "| %04X| %04X|%-20s|%-13s| %-4u|%c| %02x|%-20s|%-20s|%s",
smscb->message_id, smscb->serial_nr, cbc_msg->cbe_name,
get_value_string(cbsp_category_names, cbc_msg->priority), cbc_msg->rep_period,
cbc_msg->extended_cbch ? 'E' : 'N', smscb->cbs.dcs,
str_created, str_expired,
VTY_NEWLINE);
}
@@ -119,9 +132,11 @@ DEFUN(show_messages_cbs, show_messages_cbs_cmd,
struct cbc_message *cbc_msg;
vty_out(vty,
"|MsgId|SerNo| CBE Name | Category |Period|E|DCS|%s", VTY_NEWLINE);
"|MsgId|SerNo| CBE Name | Category |Period|E|DCS| Created | Expired |%s",
VTY_NEWLINE);
vty_out(vty,
"|-----|-----|--------------------|-------------|------|-|---|%s", VTY_NEWLINE);
"|-----|-----|--------------------|-------------|------|-|---|--------------------|--------------------|%s",
VTY_NEWLINE);
llist_for_each_entry(cbc_msg, &g_cbc->messages, list) {
if (cbc_msg->msg.is_etws)
@@ -138,37 +153,25 @@ DEFUN(show_messages_cbs, show_messages_cbs_cmd,
return CMD_SUCCESS;
}
static const char *cbc_cell_id2str(const struct cbc_cell_id *cid)
DEFUN(delete_message_expired, delete_message_expired_cmd,
"delete message expired id <0-65535>",
"Delete message from the local expired list\n"
"Delete message from the local expired list\n"
"Delete message from the local expired list\n"
"Message ID\n" "Message ID\n")
{
static char buf[256];
switch (cid->id_discr) {
case CBC_CELL_ID_NONE:
snprintf(buf, sizeof(buf), "NONE");
break;
case CBC_CELL_ID_BSS:
snprintf(buf, sizeof(buf), "BSS");
break;
case CBC_CELL_ID_CGI:
snprintf(buf, sizeof(buf), "CGI %s", osmo_cgi_name(&cid->u.cgi));
break;
case CBC_CELL_ID_LAC_CI:
snprintf(buf, sizeof(buf), "LAC %u CI %u", cid->u.lac_and_ci.lac, cid->u.lac_and_ci.ci);
break;
case CBC_CELL_ID_LAI:
snprintf(buf, sizeof(buf), "LAI %s", osmo_lai_name(&cid->u.lai));
break;
case CBC_CELL_ID_LAC:
snprintf(buf, sizeof(buf), "LAC %u", cid->u.lac);
break;
case CBC_CELL_ID_CI:
snprintf(buf, sizeof(buf), "CI %u", cid->u.ci);
break;
default:
snprintf(buf, sizeof(buf), "<invalid>");
break;
struct cbc_message *cbc_msg;
uint16_t msgid = atoi(argv[0]);
cbc_msg = cbc_message_expired_by_id(msgid);
if (!cbc_msg) {
if (cbc_message_by_id(msgid))
vty_out(vty, "Message ID %s has not yet expired!%s", argv[0], VTY_NEWLINE);
else
vty_out(vty, "Unknown expired Message ID %s%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
return buf;
cbc_message_free(cbc_msg);
return CMD_SUCCESS;
}
static void dump_one_msg_peer(struct vty *vty, const struct cbc_message_peer *msg_peer, const char *pfx)
@@ -205,10 +208,13 @@ DEFUN(show_message_cbs, show_message_cbs_cmd,
const struct smscb_message *smscb;
struct cbc_message_peer *msg_peer;
char *timestr;
uint16_t msgid = atoi(argv[0]);
cbc_msg = cbc_message_by_id(atoi(argv[0]));
cbc_msg = cbc_message_by_id(msgid);
if (!cbc_msg)
cbc_msg = cbc_message_expired_by_id(msgid);
if (!cbc_msg) {
vty_out(vty, "Unknown Messsage ID %s%s", argv[0], VTY_NEWLINE);
vty_out(vty, "Unknown Message ID %s%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
smscb = &cbc_msg->msg;
@@ -251,13 +257,26 @@ DEFUN(show_message_cbs, show_message_cbs_cmd,
static void dump_one_etws_msg(struct vty *vty, const struct cbc_message *cbc_msg)
{
const struct smscb_message *smscb = &cbc_msg->msg;
char str_created[32], str_expired[32];
struct tm tm_created = {0};
struct tm tm_expired = {0};
OSMO_ASSERT(smscb->is_etws);
vty_out(vty, "| %04X| %04X|%-20s|%-13s| %-4u|%c| %04d|%s",
localtime_r(&cbc_msg->time.created, &tm_created);
strftime(str_created, sizeof(str_created), "%Y-%m-%dT%H:%M:%SZ", &tm_created);
if (cbc_msg->time.expired > 0) {
localtime_r(&cbc_msg->time.expired, &tm_expired);
strftime(str_expired, sizeof(str_expired), "%Y-%m-%dT%H:%M:%SZ", &tm_expired);
} else {
OSMO_STRLCPY_ARRAY(str_expired, "active");
}
vty_out(vty, "| %04X| %04X|%-20s|%-13s| %-4u|%c| %04d|%-20s|%-20s|%s",
smscb->message_id, smscb->serial_nr, cbc_msg->cbe_name,
get_value_string(cbsp_category_names, cbc_msg->priority), cbc_msg->rep_period,
cbc_msg->extended_cbch ? 'E' : 'N', smscb->etws.warning_type,
str_created, str_expired,
VTY_NEWLINE);
}
@@ -268,9 +287,11 @@ DEFUN(show_messages_etws, show_messages_etws_cmd,
struct cbc_message *cbc_msg;
vty_out(vty,
"|MsgId|SerNo| CBE Name | Category |Period|E|Warning Type|%s", VTY_NEWLINE);
"|MsgId|SerNo| CBE Name | Category |Period|E|Warning Type| Created | Expired |%s",
VTY_NEWLINE);
vty_out(vty,
"|-----|-----|--------------------|-------------|------|-|------------|%s", VTY_NEWLINE);
"|-----|-----|--------------------|-------------|------|-|------------|--------------------|--------------------|%s",
VTY_NEWLINE);
llist_for_each_entry(cbc_msg, &g_cbc->messages, list) {
if (!cbc_msg->msg.is_etws)
@@ -593,6 +614,19 @@ DEFUN_DEPRECATED(cfg_peer_proto, cfg_peer_proto_cmd,
return CMD_SUCCESS;
}
DEFUN_ATTR(cfg_peer_mode, cfg_peer_mode_cmd,
"mode (server|client|disabled)",
"Connect to peer as TCP(CBSP)/SCTP(SBc-AP) server or client\n"
"server: listen for inbound TCP (CBSP) / SCTP (SBc-AP) connections from a remote peer\n"
"client: establish outbound TCP (CBSP) / SCTP (SBc-AP) connection to a remote peer\n"
"Disable CBSP link\n",
CMD_ATTR_NODE_EXIT)
{
struct cbc_peer *peer = (struct cbc_peer *) vty->index;
peer->link_mode = get_string_value(cbc_peer_link_mode_names, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_peer_remote_port, cfg_peer_remote_port_cmd,
"remote-port <0-65535>",
"Configure remote (TCP) port of peer\n"
@@ -677,6 +711,7 @@ static void write_one_peer(struct vty *vty, struct cbc_peer *peer)
unsigned int i;
vty_out(vty, " peer %s %s%s", get_value_string(cbc_peer_proto_name_vty, peer->proto),
peer->name, VTY_NEWLINE);
vty_out(vty, " mode %s%s", cbc_peer_link_mode_name(peer->link_mode), VTY_NEWLINE);
if (peer->remote_port == -1)
vty_out(vty, " no remote-port%s", VTY_NEWLINE);
else
@@ -703,6 +738,7 @@ void cbc_vty_init(void)
install_element_ve(&show_message_cbs_cmd);
install_element_ve(&show_messages_cbs_cmd);
install_element_ve(&show_messages_etws_cmd);
install_element_ve(&delete_message_expired_cmd);
install_element(CONFIG_NODE, &cfg_cbc_cmd);
install_node(&cbc_node, config_write_cbc);
@@ -729,6 +765,7 @@ void cbc_vty_init(void)
install_element(CBC_NODE, &cfg_cbc_no_peer_cmd);
install_node(&peer_node, NULL);
install_element(PEER_NODE, &cfg_peer_proto_cmd);
install_element(PEER_NODE, &cfg_peer_mode_cmd);
install_element(PEER_NODE, &cfg_peer_remote_port_cmd);
install_element(PEER_NODE, &cfg_peer_no_remote_port_cmd);
install_element(PEER_NODE, &cfg_peer_remote_ip_cmd);

View File

@@ -41,18 +41,23 @@
struct cbc_cbsp_link *cbc_cbsp_link_alloc(struct cbc_cbsp_mgr *cbc, struct cbc_peer *peer)
{
struct cbc_cbsp_link *link;
char *name;
link = talloc_zero(cbc, struct cbc_cbsp_link);
OSMO_ASSERT(link);
link->peer = peer;
link->is_client = (peer->link_mode == CBC_PEER_LINK_MODE_CLIENT);
link->fi = osmo_fsm_inst_alloc(&cbsp_link_fsm, link, link, LOGL_DEBUG, NULL);
name = talloc_strdup(link, peer->name);
osmo_identifier_sanitize_buf(name, NULL, '_');
link->fi = osmo_fsm_inst_alloc(&cbsp_link_fsm, link, link, LOGL_DEBUG, name);
if (!link->fi) {
LOGPCC(link, LOGL_ERROR, "Unable to allocate FSM\n");
talloc_free(link);
return NULL;
}
talloc_free(name);
llist_add_tail(&link->list, &cbc->links);
return link;
@@ -70,18 +75,122 @@ void cbc_cbsp_link_free(struct cbc_cbsp_link *link)
const char *cbc_cbsp_link_name(const struct cbc_cbsp_link *link)
{
struct osmo_fd *ofd;
OSMO_ASSERT(link);
if (link->peer && link->peer->name) {
if (link->peer && link->peer->name)
return link->peer->name;
} else {
struct osmo_fd *ofd = osmo_stream_srv_get_ofd(link->conn);
return osmo_sock_get_name2(ofd->fd);
}
if (link->is_client)
ofd = osmo_stream_cli_get_ofd(link->cli_conn);
else
ofd = osmo_stream_srv_get_ofd(link->srv_conn);
return osmo_sock_get_name2(ofd->fd);
}
/*
* TCP client
*/
static int cbc_cbsp_link_cli_connect_cb(struct osmo_stream_cli *conn)
{
struct cbc_cbsp_link *link = osmo_stream_cli_get_data(conn);
LOGPCC(link, LOGL_NOTICE, "Connected\n");
osmo_fsm_inst_dispatch(link->fi, CBSP_LINK_E_CMD_RESET, NULL);
return 0;
}
static int cbc_cbsp_link_cli_disconnect_cb(struct osmo_stream_cli *conn)
{
struct cbc_cbsp_link *link = osmo_stream_cli_get_data(conn);
LOGPCC(link, LOGL_NOTICE, "Disconnected.\n");
LOGPCC(link, LOGL_NOTICE, "Reconnecting...\n");
osmo_stream_cli_reconnect(conn);
return 0;
}
static int cbc_cbsp_link_cli_read_cb(struct osmo_stream_cli *conn)
{
struct cbc_cbsp_link *link = osmo_stream_cli_get_data(conn);
struct osmo_fd *ofd = osmo_stream_cli_get_ofd(conn);
struct osmo_cbsp_decoded *decoded;
struct msgb *msg = NULL;
int rc;
LOGPCC(link, LOGL_DEBUG, "read_cb rx_msg=%p\n", link->rx_msg);
/* message de-segmentation */
rc = osmo_cbsp_recv_buffered(conn, ofd->fd, &msg, &link->rx_msg);
if (rc <= 0) {
if (rc == -EAGAIN || rc == -EINTR) {
/* more data needs to be read */
return 0;
} else if (rc == -EPIPE || rc == -ECONNRESET) {
/* lost connection with server */
} else if (rc == 0) {
/* connection closed with server */
}
/* destroy connection */
cbc_cbsp_link_close(link);
return -EBADF;
}
OSMO_ASSERT(msg);
LOGPCC(link, LOGL_DEBUG, "Received CBSP %s\n", msgb_hexdump(msg));
/* decode + dispatch message */
decoded = osmo_cbsp_decode(link, msg);
if (decoded) {
LOGPCC(link, LOGL_INFO, "Received CBSP %s\n",
get_value_string(cbsp_msg_type_names, decoded->msg_type));
g_cbc->cbsp.mgr->rx_cb(link, decoded);
} else {
LOGPCC(link, LOGL_ERROR, "Unable to decode %s\n", msgb_hexdump(msg));
}
msgb_free(msg);
return 0;
}
int cbc_cbsp_link_open_cli(struct cbc_cbsp_link *link)
{
struct osmo_stream_cli *conn;
struct cbc_peer *peer = link->peer;
int rc;
OSMO_ASSERT(link->is_client);
OSMO_ASSERT(peer->link_mode == CBC_PEER_LINK_MODE_CLIENT);
conn = osmo_stream_cli_create(link);
osmo_stream_cli_set_data(conn, link);
osmo_stream_cli_set_nodelay(conn, true);
osmo_stream_cli_set_reconnect_timeout(conn, 5);
osmo_stream_cli_set_proto(conn, IPPROTO_TCP);
osmo_stream_cli_set_connect_cb(conn, cbc_cbsp_link_cli_connect_cb);
osmo_stream_cli_set_disconnect_cb(conn, cbc_cbsp_link_cli_disconnect_cb);
osmo_stream_cli_set_read_cb(conn, cbc_cbsp_link_cli_read_cb);
rc = osmo_stream_cli_set_local_addrs(conn, (const char **)&g_cbc->config.cbsp.local_host, 1);
if (rc < 0)
goto free_ret;
/* We assign free local port for client links:
* osmo_stream_cli_set_local_port(conn, g_cbc->cbsp.local_port);
*/
OSMO_ASSERT(peer->num_remote_host > 0);
rc = osmo_stream_cli_set_addrs(conn, (const char **)peer->remote_host, peer->num_remote_host);
if (rc < 0)
goto free_ret;
osmo_stream_cli_set_port(conn, peer->remote_port);
rc = osmo_stream_cli_open(conn);
if (rc < 0)
goto free_ret;
link->cli_conn = conn;
return 0;
free_ret:
osmo_stream_cli_destroy(conn);
return rc;
}
/*
* TCP server
*/
/* data from BSC has arrived at CBC */
static int cbsp_cbc_read_cb(struct osmo_stream_srv *conn)
static int cbsp_cbc_srv_read_cb(struct osmo_stream_srv *conn)
{
struct osmo_stream_srv_link *srv_link = osmo_stream_srv_get_master(conn);
struct cbc_cbsp_link *link = osmo_stream_srv_get_data(conn);
@@ -125,7 +234,7 @@ static int cbsp_cbc_read_cb(struct osmo_stream_srv *conn)
}
/* connection from BSC to CBC has been closed */
static int cbsp_cbc_closed_cb(struct osmo_stream_srv *conn)
static int cbsp_cbc_srv_closed_cb(struct osmo_stream_srv *conn)
{
struct cbc_cbsp_link *link = osmo_stream_srv_get_data(conn);
LOGPCC(link, LOGL_NOTICE, "connection closed\n");
@@ -170,6 +279,23 @@ static int cbsp_cbc_accept_cb(struct osmo_stream_srv_link *srv_link, int fd)
peer = cbc_peer_create(NULL, CBC_PEER_PROTO_CBSP);
OSMO_ASSERT(peer);
peer->unknown_dynamic_peer = true;
} else { /* peer is known */
switch (peer->link_mode) {
case CBC_PEER_LINK_MODE_DISABLED:
LOGP(DCBSP, LOGL_NOTICE,
"Rejecting conn for disabled CBSP peer %s:%d\n",
remote_ip, remote_port);
close(fd);
return -1;
case CBC_PEER_LINK_MODE_CLIENT:
LOGP(DCBSP, LOGL_NOTICE,
"Rejecting conn for CBSP peer %s:%d configured as 'client'\n",
remote_ip, remote_port);
close(fd);
return -1;
default: /* MODE_SERVER */
break;
}
}
if (peer->link.cbsp) {
LOGPCC(peer->link.cbsp, LOGL_ERROR,
@@ -179,10 +305,11 @@ static int cbsp_cbc_accept_cb(struct osmo_stream_srv_link *srv_link, int fd)
}
link = cbc_cbsp_link_alloc(cbc, peer);
OSMO_ASSERT(link);
link->conn = osmo_stream_srv_create(srv_link, srv_link, fd,
cbsp_cbc_read_cb, cbsp_cbc_closed_cb,
link);
if (!link->conn) {
link->srv_conn = osmo_stream_srv_create(srv_link, srv_link, fd,
cbsp_cbc_srv_read_cb, cbsp_cbc_srv_closed_cb,
link);
if (!link->srv_conn) {
LOGPCC(link, LOGL_ERROR,
"Unable to create stream server for %s:%u\n",
remote_ip, remote_port);
@@ -195,14 +322,20 @@ static int cbsp_cbc_accept_cb(struct osmo_stream_srv_link *srv_link, int fd)
return 0;
}
void cbc_cbsp_link_tx(struct cbc_cbsp_link *link, struct osmo_cbsp_decoded *cbsp)
int cbc_cbsp_link_tx(struct cbc_cbsp_link *link, struct osmo_cbsp_decoded *cbsp)
{
struct msgb *msg;
if (!link) {
LOGP(DCBSP, LOGL_NOTICE, "Cannot transmit %s: no connection\n",
get_value_string(cbsp_msg_type_names, cbsp->msg_type));
return ;
talloc_free(cbsp);
return -ENOLINK;
} else if (link->is_client && !osmo_stream_cli_is_connected(link->cli_conn)) {
LOGPCC(link, LOGL_NOTICE, "Cannot transmit %s: reconnecting\n",
get_value_string(cbsp_msg_type_names, cbsp->msg_type));
talloc_free(cbsp);
return -ENOTCONN;
}
LOGPCC(link, LOGL_INFO, "Transmitting %s\n",
@@ -213,16 +346,32 @@ void cbc_cbsp_link_tx(struct cbc_cbsp_link *link, struct osmo_cbsp_decoded *cbsp
LOGPCC(link, LOGL_ERROR, "Failed to encode CBSP %s: %s\n",
get_value_string(cbsp_msg_type_names, cbsp->msg_type), osmo_cbsp_errstr);
talloc_free(cbsp);
return;
return -EINVAL;
}
talloc_free(cbsp);
osmo_stream_srv_send(link->conn, msg);
if (link->is_client)
osmo_stream_cli_send(link->cli_conn, msg);
else
osmo_stream_srv_send(link->srv_conn, msg);
return 0;
}
void cbc_cbsp_link_close(struct cbc_cbsp_link *link)
{
if (link->conn)
osmo_stream_srv_destroy(link->conn);
if (!link->conn)
return;
if (link->is_client) {
osmo_stream_cli_destroy(link->cli_conn);
if (link->peer)
link->peer->link.cbsp = NULL;
link->cli_conn = NULL;
if (link->fi)
osmo_fsm_inst_dispatch(link->fi, CBSP_LINK_E_CMD_CLOSE, NULL);
} else {
osmo_stream_srv_destroy(link->srv_conn);
/* Same as waht's done for cli is done for srv in closed_cb() */
}
}
/*

View File

@@ -27,7 +27,7 @@
#include <osmocom/cbc/cbsp_link.h>
#include <osmocom/cbc/cbsp_link_fsm.h>
#include <osmocom/cbc/debug.h>
#include <osmocom/cbc/smscb_message_fsm.h>
#include <osmocom/cbc/smscb_peer_fsm.h>
#define S(x) (1 << (x))
@@ -339,22 +339,22 @@ int cbc_cbsp_link_rx_cb(struct cbc_cbsp_link *link, struct osmo_cbsp_decoded *de
switch (dec->msg_type) {
case CBSP_MSGT_WRITE_REPLACE_COMPL:
if (dec->u.write_replace_compl.old_serial_nr)
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_E_CBSP_REPLACE_ACK, dec);
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_PEER_E_CBSP_REPLACE_ACK, dec);
else
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_E_CBSP_WRITE_ACK, dec);
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_PEER_E_CBSP_WRITE_ACK, dec);
case CBSP_MSGT_WRITE_REPLACE_FAIL:
if (dec->u.write_replace_fail.old_serial_nr)
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_E_CBSP_REPLACE_NACK, dec);
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_PEER_E_CBSP_REPLACE_NACK, dec);
else
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_E_CBSP_WRITE_NACK, dec);
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_PEER_E_CBSP_WRITE_NACK, dec);
case CBSP_MSGT_KILL_COMPL:
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_E_CBSP_DELETE_ACK, dec);
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_PEER_E_CBSP_DELETE_ACK, dec);
case CBSP_MSGT_KILL_FAIL:
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_E_CBSP_DELETE_NACK, dec);
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_PEER_E_CBSP_DELETE_NACK, dec);
case CBSP_MSGT_MSG_STATUS_QUERY_COMPL:
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_E_CBSP_STATUS_ACK, dec);
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_PEER_E_CBSP_STATUS_ACK, dec);
case CBSP_MSGT_MSG_STATUS_QUERY_FAIL:
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_E_CBSP_STATUS_NACK, dec);
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_PEER_E_CBSP_STATUS_NACK, dec);
default:
LOGPCC(link, LOGL_ERROR, "unknown message %s\n",
get_value_string(cbsp_msg_type_names, dec->msg_type));

110
src/cbsp_msg.c Normal file
View File

@@ -0,0 +1,110 @@
/* Osmocom CBC (Cell Broacast Centre) */
/* (C) 2019 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <errno.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/cbsp.h>
#include <osmocom/cbc/cbsp_msg.h>
#include <osmocom/cbc/cbc_message.h>
/* convert cbc_message to osmo_cbsp_cell_list */
static int cbcmsg_to_cbsp_cell_list(const void *ctx, struct osmo_cbsp_cell_list *list,
const struct cbc_message *cbcmsg)
{
struct osmo_cbsp_cell_ent *ent;
switch (cbcmsg->scope) {
case CBC_MSG_SCOPE_PLMN:
list->id_discr = CELL_IDENT_BSS;
ent = talloc_zero(ctx, struct osmo_cbsp_cell_ent);
if (!ent)
return -ENOMEM;
//ent->cell_id = ?
llist_add_tail(&ent->list, &list->list);
return 0;
default:
OSMO_ASSERT(0);
}
}
/* generate a CBSP WRITE-REPLACE from our internal representation */
struct osmo_cbsp_decoded *cbsp_gen_write_replace_req(void *ctx, const struct cbc_message *cbcmsg)
{
struct osmo_cbsp_write_replace *wrepl;
const struct smscb_message *smscb = &cbcmsg->msg;
struct osmo_cbsp_decoded *cbsp = osmo_cbsp_decoded_alloc(ctx, CBSP_MSGT_WRITE_REPLACE);
unsigned int i;
int rc;
if (!cbsp)
return NULL;
wrepl = &cbsp->u.write_replace;
wrepl->msg_id = smscb->message_id;
wrepl->new_serial_nr = smscb->serial_nr;
/* FIXME: old? */
/* Cell list */
rc = cbcmsg_to_cbsp_cell_list(cbcmsg, &wrepl->cell_list, cbcmsg);
if (rc < 0) {
talloc_free(cbsp);
return NULL;
}
if (!smscb->is_etws)
wrepl->is_cbs = true;
if (wrepl->is_cbs) {
if (cbcmsg->extended_cbch)
wrepl->u.cbs.channel_ind = CBSP_CHAN_IND_EXTENDED;
else
wrepl->u.cbs.channel_ind = CBSP_CHAN_IND_BASIC;
wrepl->u.cbs.category = cbcmsg->priority;
wrepl->u.cbs.rep_period = cbcmsg->rep_period;
wrepl->u.cbs.num_bcast_req = cbcmsg->num_bcast;
wrepl->u.cbs.dcs = smscb->cbs.dcs;
INIT_LLIST_HEAD(&wrepl->u.cbs.msg_content);
for (i = 0; i < smscb->cbs.num_pages; i++) {
struct osmo_cbsp_content *ce = talloc_zero(cbsp, struct osmo_cbsp_content);
if (i == smscb->cbs.num_pages - 1)
ce->user_len = smscb->cbs.data_user_len - (i * SMSCB_RAW_PAGE_LEN);
else
ce->user_len = SMSCB_RAW_PAGE_LEN;
memcpy(ce->data, smscb->cbs.data[i], SMSCB_RAW_PAGE_LEN);
llist_add_tail(&ce->list, &wrepl->u.cbs.msg_content);
}
} else {
wrepl->u.emergency.indicator = 1;
wrepl->u.emergency.warning_type = (smscb->etws.warning_type & 0x7f) << 9;
if (smscb->etws.user_alert)
wrepl->u.emergency.warning_type |= 0x0100;
if (smscb->etws.popup_on_display)
wrepl->u.emergency.warning_type |= 0x0080;
memcpy(wrepl->u.emergency.warning_sec_info, smscb->etws.warning_sec_info,
sizeof(wrepl->u.emergency.warning_sec_info));
if (cbcmsg->warning_period_sec == 0xffffffff)
wrepl->u.emergency.warning_period = 0;
else
wrepl->u.emergency.warning_period = cbcmsg->warning_period_sec;
}
return cbsp;
}

574
src/cbsp_smscb_peer_fsm.c Normal file
View File

@@ -0,0 +1,574 @@
/* SMSCB Peer FSM: Represents state of one SMSCB for one peer (BSC) */
/* This FSM exists per tuple of (message, bsc peer) */
/* (C) 2019 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/fsm.h>
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/gsm0808_utils.h>
#include <osmocom/gsm/cbsp.h>
#include <osmocom/sbcap/sbcap_common.h>
#include <osmocom/cbc/cbc_message.h>
#include <osmocom/cbc/cbc_peer.h>
#include <osmocom/cbc/cbsp_link.h>
#include <osmocom/cbc/sbcap_link.h>
#include <osmocom/cbc/sbcap_msg.h>
#include <osmocom/cbc/debug.h>
#include <osmocom/cbc/smscb_peer_fsm.h>
#include <osmocom/cbc/smscb_message_fsm.h>
#define S(x) (1 << (x))
/***********************************************************************
* Helper functions
***********************************************************************/
/* covert TS 08.08 Cell Identity value to CBC internal type */
static enum cbc_cell_id_type cci_discr_from_cell_id(enum CELL_IDENT id_discr)
{
switch (id_discr) {
case CELL_IDENT_NO_CELL:
return CBC_CELL_ID_NONE;
case CELL_IDENT_WHOLE_GLOBAL:
return CBC_CELL_ID_CGI;
case CELL_IDENT_LAC_AND_CI:
return CBC_CELL_ID_LAC_CI;
case CELL_IDENT_CI:
return CBC_CELL_ID_CI;
case CELL_IDENT_LAI:
return CBC_CELL_ID_LAI;
case CELL_IDENT_LAC:
return CBC_CELL_ID_LAC;
case CELL_IDENT_BSS:
return CBC_CELL_ID_BSS;
default:
return -1;
}
}
/* covert CBC internal type to TS 08.08 Cell Identity */
static enum CELL_IDENT cell_id_from_ccid_discr(enum cbc_cell_id_type in)
{
switch (in) {
case CBC_CELL_ID_NONE:
return CELL_IDENT_NO_CELL;
case CBC_CELL_ID_CGI:
return CELL_IDENT_WHOLE_GLOBAL;
case CBC_CELL_ID_LAC_CI:
return CELL_IDENT_LAC_AND_CI;
case CBC_CELL_ID_CI:
return CELL_IDENT_CI;
case CBC_CELL_ID_LAI:
return CELL_IDENT_LAI;
case CBC_CELL_ID_LAC:
return CELL_IDENT_LAC;
case CBC_CELL_ID_BSS:
return CELL_IDENT_BSS;
default:
return -1;
}
}
/* convert TS 08.08 Cell Identifier Union to CBC internal type */
static void cci_from_cbsp(struct cbc_cell_id *cci, enum CELL_IDENT id_discr,
const union gsm0808_cell_id_u *u)
{
cci->id_discr = cci_discr_from_cell_id(id_discr);
switch (id_discr) {
case CELL_IDENT_NO_CELL:
break;
case CELL_IDENT_WHOLE_GLOBAL:
cci->u.cgi = u->global;
break;
case CELL_IDENT_LAC_AND_CI:
cci->u.lac_and_ci = u->lac_and_ci;
break;
case CELL_IDENT_CI:
cci->u.ci = u->ci;
break;
case CELL_IDENT_LAI:
cci->u.lai = u->lai_and_lac;
break;
case CELL_IDENT_LAC:
cci->u.lac = u->lac;
break;
case CELL_IDENT_BSS:
break;
default:
break;
}
}
/* convert TS 08.08 Cell Identifier Union to CBC internal type */
static void cbsp_from_cci(union gsm0808_cell_id_u *u, const struct cbc_cell_id *cci)
{
switch (cci->id_discr) {
case CBC_CELL_ID_NONE:
break;
case CBC_CELL_ID_CGI:
u->global = cci->u.cgi;
printf("u->gobal: %s\n", osmo_hexdump((uint8_t *) &u->global, sizeof(u->global)));
break;
case CBC_CELL_ID_LAC_CI:
u->lac_and_ci = cci->u.lac_and_ci;
break;
case CBC_CELL_ID_CI:
u->ci = cci->u.ci;
break;
case CBC_CELL_ID_LAI:
u->lai_and_lac = cci->u.lai;
break;
case CBC_CELL_ID_LAC:
u->lac = cci->u.lac;
break;
case CBC_CELL_ID_BSS:
break;
default:
OSMO_ASSERT(0);
}
}
/* read a single osmo_cbsp_num_compl_ent and add it to cbc_message_peer */
static void cci_from_cbsp_compl_ent(struct cbc_message_peer *mp,
struct osmo_cbsp_num_compl_ent *ce, enum CELL_IDENT id_discr)
{
struct cbc_cell_id *cci;
cci = NULL; // FIXME: lookup
if (!cci) {
cci = talloc_zero(mp, struct cbc_cell_id);
if (!cci)
return;
llist_add_tail(&cci->list, &mp->num_compl_list);
}
cci_from_cbsp(cci, id_discr, &ce->cell_id);
cci->num_compl.num_compl += ce->num_compl;
cci->num_compl.num_bcast_info += ce->num_bcast_info;
LOGPFSML(mp->fi, LOGL_DEBUG, "Appending CellId %s to Broadcast Completed list\n",
cbc_cell_id2str(cci));
}
static void msg_peer_append_cbsp_compl(struct cbc_message_peer *mp,
struct osmo_cbsp_num_compl_list *nclist)
{
struct osmo_cbsp_num_compl_ent *ce;
llist_for_each_entry(ce, &nclist->list, list)
cci_from_cbsp_compl_ent(mp, ce, nclist->id_discr);
}
/* read a single osmo_cbsp_cell_ent and add it to cbc_message_peer */
static void cci_from_cbsp_cell_ent(struct cbc_message_peer *mp,
struct osmo_cbsp_cell_ent *ce, enum CELL_IDENT id_discr)
{
struct cbc_cell_id *cci;
cci = NULL; // FIXME: lookup
if (!cci) {
cci = talloc_zero(mp, struct cbc_cell_id);
if (!cci)
return;
llist_add_tail(&cci->list, &mp->cell_list);
}
cci_from_cbsp(cci, id_discr, &ce->cell_id);
LOGPFSML(mp->fi, LOGL_DEBUG, "Appending CellId %s to Cell list\n",
cbc_cell_id2str(cci));
}
static void msg_peer_append_cbsp_cell(struct cbc_message_peer *mp,
struct osmo_cbsp_cell_list *clist)
{
struct osmo_cbsp_cell_ent *ce;
llist_for_each_entry(ce, &clist->list, list)
cci_from_cbsp_cell_ent(mp, ce, clist->id_discr);
}
/* read a single osmo_cbsp_fail_ent and add it to cbc_message_peer */
static void cci_from_cbsp_fail_ent(struct cbc_message_peer *mp,
struct osmo_cbsp_fail_ent *fe)
{
struct cbc_cell_id *cci;
cci = NULL; // lookup */
if (!cci) {
cci = talloc_zero(mp, struct cbc_cell_id);
if (!cci)
return;
llist_add_tail(&cci->list, &mp->fail_list);
}
cci_from_cbsp(cci, fe->id_discr, &fe->cell_id);
cci->fail.cause = fe->cause;
LOGPFSML(mp->fi, LOGL_DEBUG, "Appending CellId %s (cause: %u) to Failed list\n",
cbc_cell_id2str(cci), cci->fail.cause);
}
static void msg_peer_append_cbsp_fail(struct cbc_message_peer *mp, struct llist_head *flist)
{
struct osmo_cbsp_fail_ent *fe;
llist_for_each_entry(fe, flist, list)
cci_from_cbsp_fail_ent(mp, fe);
}
/* append all cells from cbc_message_peer to given CBSP cell_list */
static void cbsp_append_cell_list(struct osmo_cbsp_cell_list *out, void *ctx,
const struct cbc_message_peer *mp)
{
struct cbc_cell_id *cci;
enum cbc_cell_id_type id_discr = CBC_CELL_ID_NONE;
llist_for_each_entry(cci, &mp->cell_list, list) {
struct osmo_cbsp_cell_ent *ent;
if (id_discr == CBC_CELL_ID_NONE)
id_discr = cci->id_discr;
else if (id_discr != cci->id_discr) {
LOGPFSML(mp->fi, LOGL_ERROR, "Cannot encode CBSP cell_list as not all "
"entries are of same type (%u != %u)\n", id_discr, cci->id_discr);
continue;
}
ent = talloc_zero(ctx, struct osmo_cbsp_cell_ent);
OSMO_ASSERT(ent);
cbsp_from_cci(&ent->cell_id, cci);
llist_add_tail(&ent->list, &out->list);
}
out->id_discr = cell_id_from_ccid_discr(id_discr);
}
/***********************************************************************
* actual FSM
***********************************************************************/
static void smscb_p_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
int rc;
switch (event) {
case SMSCB_PEER_E_CREATE:
/* send it to peer */
rc = peer_new_cbc_message(mp->peer, mp->cbcmsg);
osmo_fsm_inst_state_chg(fi, SMSCB_S_WAIT_WRITE_ACK, 10, T_WAIT_WRITE_ACK);
if (rc != 0) /* Immediately timeout the message */
fi->fsm->timer_cb(fi);
break;
default:
OSMO_ASSERT(0);
}
}
static void smscb_p_fsm_wait_write_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
struct osmo_cbsp_decoded *dec = NULL;
switch (event) {
case SMSCB_PEER_E_CBSP_WRITE_ACK:
dec = data;
msg_peer_append_cbsp_compl(mp, &dec->u.write_replace_compl.num_compl_list);
msg_peer_append_cbsp_cell(mp, &dec->u.write_replace_compl.cell_list);
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_MSG_E_WRITE_ACK, mp);
break;
case SMSCB_PEER_E_CBSP_WRITE_NACK:
dec = data;
msg_peer_append_cbsp_compl(mp, &dec->u.write_replace_fail.num_compl_list);
msg_peer_append_cbsp_cell(mp, &dec->u.write_replace_fail.cell_list);
msg_peer_append_cbsp_fail(mp, &dec->u.write_replace_fail.fail_list);
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_MSG_E_WRITE_NACK, mp);
break;
default:
OSMO_ASSERT(0);
}
}
static void smscb_p_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
struct osmo_cbsp_decoded *cbsp;
int rc;
switch (event) {
case SMSCB_PEER_E_REPLACE: /* send WRITE-REPLACE to BSC */
cbsp = osmo_cbsp_decoded_alloc(mp->peer, CBSP_MSGT_WRITE_REPLACE);
OSMO_ASSERT(cbsp);
cbsp->u.write_replace.msg_id = mp->cbcmsg->msg.message_id;
cbsp->u.write_replace.old_serial_nr = &mp->cbcmsg->msg.serial_nr;
//cbsp->u.write_replace.new_serial_nr
/* TODO: we assume that the replace will always affect all original cells */
cbsp_append_cell_list(&cbsp->u.write_replace.cell_list, cbsp, mp);
// TODO: ALL OTHER DATA
rc = cbc_cbsp_link_tx(mp->peer->link.cbsp, cbsp);
osmo_fsm_inst_state_chg(fi, SMSCB_S_WAIT_REPLACE_ACK, 10, T_WAIT_REPLACE_ACK);
if (rc != 0) /* Immediately timeout the message */
fi->fsm->timer_cb(fi);
break;
case SMSCB_PEER_E_STATUS: /* send MSG-STATUS-QUERY to BSC */
cbsp = osmo_cbsp_decoded_alloc(mp->peer, CBSP_MSGT_MSG_STATUS_QUERY);
OSMO_ASSERT(cbsp);
cbsp->u.msg_status_query.msg_id = mp->cbcmsg->msg.message_id;
cbsp->u.msg_status_query.old_serial_nr = mp->cbcmsg->msg.serial_nr;
cbsp_append_cell_list(&cbsp->u.msg_status_query.cell_list, cbsp, mp);
cbsp->u.msg_status_query.channel_ind = CBSP_CHAN_IND_BASIC;
rc = cbc_cbsp_link_tx(mp->peer->link.cbsp, cbsp);
osmo_fsm_inst_state_chg(fi, SMSCB_S_WAIT_STATUS_ACK, 10, T_WAIT_STATUS_ACK);
if (rc != 0) /* Immediately timeout the message */
fi->fsm->timer_cb(fi);
break;
default:
OSMO_ASSERT(0);
}
}
static void smscb_p_fsm_wait_status_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
struct osmo_cbsp_decoded *dec = NULL;
switch (event) {
case SMSCB_PEER_E_CBSP_STATUS_ACK:
dec = data;
msg_peer_append_cbsp_compl(mp, &dec->u.msg_status_query_compl.num_compl_list);
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_MSG_E_STATUS_ACK, mp);
break;
case SMSCB_PEER_E_CBSP_STATUS_NACK:
dec = data;
msg_peer_append_cbsp_compl(mp, &dec->u.msg_status_query_fail.num_compl_list);
msg_peer_append_cbsp_fail(mp, &dec->u.msg_status_query_fail.fail_list);
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_MSG_E_STATUS_NACK, mp);
break;
default:
OSMO_ASSERT(0);
}
}
static void smscb_p_fsm_wait_replace_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
struct osmo_cbsp_decoded *dec = NULL;
switch (event) {
case SMSCB_PEER_E_CBSP_REPLACE_ACK:
dec = data;
msg_peer_append_cbsp_compl(mp, &dec->u.write_replace_compl.num_compl_list);
msg_peer_append_cbsp_cell(mp, &dec->u.write_replace_compl.cell_list);
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_MSG_E_REPLACE_ACK, mp);
break;
case SMSCB_PEER_E_CBSP_REPLACE_NACK:
dec = data;
msg_peer_append_cbsp_compl(mp, &dec->u.write_replace_fail.num_compl_list);
msg_peer_append_cbsp_cell(mp, &dec->u.write_replace_fail.cell_list);
msg_peer_append_cbsp_fail(mp, &dec->u.write_replace_fail.fail_list);
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_MSG_E_REPLACE_NACK, mp);
break;
default:
OSMO_ASSERT(0);
}
}
static void smscb_p_fsm_wait_delete_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
struct osmo_cbsp_decoded *dec = NULL;
switch (event) {
case SMSCB_PEER_E_CBSP_DELETE_ACK:
dec = data;
/* append results */
msg_peer_append_cbsp_compl(mp, &dec->u.kill_compl.num_compl_list);
msg_peer_append_cbsp_cell(mp, &dec->u.kill_compl.cell_list);
osmo_fsm_inst_state_chg(fi, SMSCB_S_DELETED, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_MSG_E_DELETE_ACK, mp);
break;
case SMSCB_PEER_E_CBSP_DELETE_NACK:
dec = data;
/* append results */
msg_peer_append_cbsp_compl(mp, &dec->u.kill_fail.num_compl_list);
msg_peer_append_cbsp_cell(mp, &dec->u.kill_fail.cell_list);
msg_peer_append_cbsp_fail(mp, &dec->u.kill_fail.fail_list);
osmo_fsm_inst_state_chg(fi, SMSCB_S_DELETED, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_MSG_E_DELETE_NACK, mp);
break;
default:
OSMO_ASSERT(0);
}
}
static int smscb_p_fsm_timer_cb(struct osmo_fsm_inst *fi)
{
switch (fi->T) {
case T_WAIT_WRITE_ACK:
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_PEER_E_CBSP_WRITE_NACK, NULL);
break;
case T_WAIT_REPLACE_ACK:
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_MSG_E_REPLACE_NACK, NULL);
break;
case T_WAIT_STATUS_ACK:
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_MSG_E_STATUS_NACK, NULL);
break;
case T_WAIT_DELETE_ACK:
osmo_fsm_inst_state_chg(fi, SMSCB_S_DELETED, 0, 0);
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_PEER_E_CBSP_DELETE_NACK, NULL);
break;
default:
OSMO_ASSERT(0);
}
return 0;
}
static void smscb_p_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
struct osmo_cbsp_decoded *cbsp;
int rc;
switch (event) {
case SMSCB_PEER_E_DELETE: /* send KILL to BSC */
switch (fi->state) {
case SMSCB_S_DELETED:
case SMSCB_S_INIT:
LOGPFSML(fi, LOGL_ERROR, "Event %s not permitted\n",
osmo_fsm_event_name(fi->fsm, event));
return;
default:
break;
}
cbsp = osmo_cbsp_decoded_alloc(mp->peer, CBSP_MSGT_KILL);
OSMO_ASSERT(cbsp);
cbsp->u.kill.msg_id = mp->cbcmsg->msg.message_id;
cbsp->u.kill.old_serial_nr = mp->cbcmsg->msg.serial_nr;
/* TODO: we assume that the delete will always affect all original cells */
cbsp_append_cell_list(&cbsp->u.kill.cell_list, cbsp, mp);
if (!mp->cbcmsg->msg.is_etws) {
/* Channel Indication IE is only present in CBS, not in ETWS! */
cbsp->u.kill.channel_ind = talloc_zero(cbsp, enum cbsp_channel_ind);
OSMO_ASSERT(cbsp->u.kill.channel_ind);
*(cbsp->u.kill.channel_ind) = CBSP_CHAN_IND_BASIC;
}
rc = cbc_cbsp_link_tx(mp->peer->link.cbsp, cbsp);
osmo_fsm_inst_state_chg(fi, SMSCB_S_WAIT_DELETE_ACK, 10, T_WAIT_DELETE_ACK);
if (rc != 0) /* Immediately timeout the message */
fi->fsm->timer_cb(fi);
break;
default:
OSMO_ASSERT(0);
}
}
static void smscb_p_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
llist_del(&mp->list);
/* memory of mp is child of fi and hence automatically free'd */
}
static const struct osmo_fsm_state smscb_p_fsm_states[] = {
[SMSCB_S_INIT] = {
.name = "INIT",
.in_event_mask = S(SMSCB_PEER_E_CREATE),
.out_state_mask = S(SMSCB_S_WAIT_WRITE_ACK) |
S(SMSCB_S_ACTIVE),
.action = smscb_p_fsm_init,
},
[SMSCB_S_WAIT_WRITE_ACK] = {
.name = "WAIT_WRITE_ACK",
.in_event_mask = S(SMSCB_PEER_E_CBSP_WRITE_ACK) |
S(SMSCB_PEER_E_CBSP_WRITE_NACK),
.out_state_mask = S(SMSCB_S_ACTIVE) |
S(SMSCB_S_WAIT_DELETE_ACK),
.action = smscb_p_fsm_wait_write_ack,
},
[SMSCB_S_ACTIVE] = {
.name = "ACTIVE",
.in_event_mask = S(SMSCB_PEER_E_REPLACE) |
S(SMSCB_PEER_E_STATUS),
.out_state_mask = S(SMSCB_S_WAIT_REPLACE_ACK) |
S(SMSCB_S_WAIT_STATUS_ACK) |
S(SMSCB_S_WAIT_DELETE_ACK),
.action = smscb_p_fsm_active,
},
[SMSCB_S_WAIT_STATUS_ACK] = {
.name = "WAIT_STATUS_ACK",
.in_event_mask = S(SMSCB_PEER_E_CBSP_STATUS_ACK) |
S(SMSCB_PEER_E_CBSP_STATUS_NACK),
.out_state_mask = S(SMSCB_S_ACTIVE) |
S(SMSCB_S_WAIT_DELETE_ACK),
.action = smscb_p_fsm_wait_status_ack,
},
[SMSCB_S_WAIT_REPLACE_ACK] = {
.name = "WAIT_REPLACE_ACK",
.in_event_mask = S(SMSCB_PEER_E_CBSP_REPLACE_ACK) |
S(SMSCB_PEER_E_CBSP_REPLACE_NACK),
.out_state_mask = S(SMSCB_S_ACTIVE) |
S(SMSCB_S_WAIT_DELETE_ACK),
.action = smscb_p_fsm_wait_replace_ack,
},
[SMSCB_S_WAIT_DELETE_ACK] = {
.name = "WAIT_DELETE_ACK",
.in_event_mask = S(SMSCB_PEER_E_CBSP_DELETE_ACK) |
S(SMSCB_PEER_E_CBSP_DELETE_NACK),
.out_state_mask = S(SMSCB_S_DELETED),
.action = smscb_p_fsm_wait_delete_ack,
},
[SMSCB_S_DELETED] = {
.name = "DELETED",
},
};
struct osmo_fsm cbsp_smscb_peer_fsm = {
.name = "SMSCB-PEER-CBSP",
.states = smscb_p_fsm_states,
.num_states = ARRAY_SIZE(smscb_p_fsm_states),
.allstate_event_mask = S(SMSCB_PEER_E_DELETE),
.allstate_action = smscb_p_fsm_allstate,
.timer_cb = smscb_p_fsm_timer_cb,
.log_subsys = DCBSP,
.event_names = smscb_peer_fsm_event_names,
.cleanup = smscb_p_fsm_cleanup,
};
static __attribute__((constructor)) void on_dso_load_smscb_p_fsm(void)
{
OSMO_ASSERT(osmo_fsm_register(&cbsp_smscb_peer_fsm) == 0);
}

View File

@@ -1,237 +0,0 @@
/* Osmocom CBC (Cell Broacast Centre) */
/* (C) 2019 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <errno.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/cbsp.h>
#include <osmocom/cbc/cbc_data.h>
#include <osmocom/cbc/cbsp_link.h>
#include <osmocom/cbc/sbcap_link.h>
#include <osmocom/cbc/sbcap_msg.h>
#include <osmocom/cbc/rest_it_op.h>
#include <osmocom/cbc/debug.h>
#include <osmocom/cbc/cbc_peer.h>
#include <osmocom/cbc/smscb_message_fsm.h>
/* convert cbc_message to osmo_cbsp_cell_list */
static int cbcmsg_to_cbsp_cell_list(const void *ctx, struct osmo_cbsp_cell_list *list,
const struct cbc_message *cbcmsg)
{
struct osmo_cbsp_cell_ent *ent;
switch (cbcmsg->scope) {
case CBC_MSG_SCOPE_PLMN:
list->id_discr = CELL_IDENT_BSS;
ent = talloc_zero(ctx, struct osmo_cbsp_cell_ent);
if (!ent)
return -ENOMEM;
//ent->cell_id = ?
llist_add_tail(&ent->list, &list->list);
return 0;
default:
OSMO_ASSERT(0);
}
}
/* generate a CBSP WRITE-REPLACE from our internal representation */
struct osmo_cbsp_decoded *cbcmsg_to_cbsp(void *ctx, const struct cbc_message *cbcmsg)
{
struct osmo_cbsp_write_replace *wrepl;
const struct smscb_message *smscb = &cbcmsg->msg;
struct osmo_cbsp_decoded *cbsp = osmo_cbsp_decoded_alloc(ctx, CBSP_MSGT_WRITE_REPLACE);
unsigned int i;
int rc;
if (!cbsp)
return NULL;
wrepl = &cbsp->u.write_replace;
wrepl->msg_id = smscb->message_id;
wrepl->new_serial_nr = smscb->serial_nr;
/* FIXME: old? */
/* Cell list */
rc = cbcmsg_to_cbsp_cell_list(cbcmsg, &wrepl->cell_list, cbcmsg);
if (rc < 0) {
talloc_free(cbsp);
return NULL;
}
if (!smscb->is_etws)
wrepl->is_cbs = true;
if (wrepl->is_cbs) {
if (cbcmsg->extended_cbch)
wrepl->u.cbs.channel_ind = CBSP_CHAN_IND_EXTENDED;
else
wrepl->u.cbs.channel_ind = CBSP_CHAN_IND_BASIC;
wrepl->u.cbs.category = cbcmsg->priority;
wrepl->u.cbs.rep_period = cbcmsg->rep_period;
wrepl->u.cbs.num_bcast_req = cbcmsg->num_bcast;
wrepl->u.cbs.dcs = smscb->cbs.dcs;
INIT_LLIST_HEAD(&wrepl->u.cbs.msg_content);
for (i = 0; i < smscb->cbs.num_pages; i++) {
struct osmo_cbsp_content *ce = talloc_zero(cbsp, struct osmo_cbsp_content);
if (i == smscb->cbs.num_pages - 1)
ce->user_len = smscb->cbs.data_user_len - (i * SMSCB_RAW_PAGE_LEN);
else
ce->user_len = SMSCB_RAW_PAGE_LEN;
memcpy(ce->data, smscb->cbs.data[i], SMSCB_RAW_PAGE_LEN);
llist_add_tail(&ce->list, &wrepl->u.cbs.msg_content);
}
} else {
wrepl->u.emergency.indicator = 1;
wrepl->u.emergency.warning_type = (smscb->etws.warning_type & 0x7f) << 9;
if (smscb->etws.user_alert)
wrepl->u.emergency.warning_type |= 0x0100;
if (smscb->etws.popup_on_display)
wrepl->u.emergency.warning_type |= 0x0080;
memcpy(wrepl->u.emergency.warning_sec_info, smscb->etws.warning_sec_info,
sizeof(wrepl->u.emergency.warning_sec_info));
if (cbcmsg->warning_period_sec == 0xffffffff)
wrepl->u.emergency.warning_period = 0;
else
wrepl->u.emergency.warning_period = cbcmsg->warning_period_sec;
}
return cbsp;
}
/* determine if peer is within scope of cbc_msg */
static bool is_peer_in_scope(const struct cbc_peer *peer, const struct cbc_message *cbcmsg)
{
switch (cbcmsg->scope) {
case CBC_MSG_SCOPE_PLMN:
return true;
/* FIXME: differnt scopes */
default:
OSMO_ASSERT(0);
}
}
/* send given new message to given peer */
int peer_new_cbc_message(struct cbc_peer *peer, struct cbc_message *cbcmsg)
{
struct osmo_cbsp_decoded *cbsp;
SBcAP_SBC_AP_PDU_t *sbcap;
switch (peer->proto) {
case CBC_PEER_PROTO_CBSP:
/* skip peers without any current CBSP connection */
if (!peer->link.cbsp) {
LOGP(DCBSP, LOGL_NOTICE, "[%s] Tx CBSP: not connected\n",
peer->name);
return -ENOTCONN;
}
if (!(cbsp = cbcmsg_to_cbsp(peer, cbcmsg))) {
LOGP(DCBSP, LOGL_ERROR, "[%s] Tx CBSP: msg gen failed\n",
peer->name);
return -EINVAL;
}
cbc_cbsp_link_tx(peer->link.cbsp, cbsp);
break;
case CBC_PEER_PROTO_SBcAP:
/* skip peers without any current SBc-AP connection */
if (!peer->link.sbcap) {
LOGP(DSBcAP, LOGL_NOTICE, "[%s] Tx SBc-AP: not connected\n",
peer->name);
return -ENOTCONN;
}
if (!(sbcap = cbcmsg_to_sbcap(peer, cbcmsg))) {
LOGP(DSBcAP, LOGL_ERROR, "[%s] Tx SBc-AP: msg gen failed\n",
peer->name);
return -EINVAL;
}
cbc_sbcap_link_tx(peer->link.sbcap, sbcap);
break;
case CBC_PEER_PROTO_SABP:
LOGP(DLGLOBAL, LOGL_ERROR, "Sending message to peer proto %s not implemented!\n",
get_value_string(cbc_peer_proto_name, peer->proto));
return -1;
default:
OSMO_ASSERT(0);
}
return 0;
}
/* receive a new CBC message from the user (REST). Allocates new memory,
* a FSM, copies data from 'orig', routes to all peers and starts FSMs.
* Once the operation is complete (success, error, timeout) we must
* notify osmo_it_q of the completion */
int cbc_message_new(const struct cbc_message *orig, struct rest_it_op *op)
{
struct cbc_message *cbcmsg = cbc_message_alloc(g_cbc, orig);
struct cbc_peer *peer;
if (!cbcmsg) {
rest_it_op_set_http_result(op, 409, "Could not allocate");
rest_it_op_complete(op);
return -ENOMEM;
}
OSMO_ASSERT(llist_empty(&cbcmsg->peers));
/* iterate over all peers */
llist_for_each_entry(peer, &g_cbc->peers, list) {
struct cbc_message_peer *mp;
if (!is_peer_in_scope(peer, cbcmsg))
continue;
/* allocate new cbc_mesage_peer + related FSM */
mp = smscb_peer_fsm_alloc(peer, cbcmsg);
if (!mp) {
LOGP(DCBSP, LOGL_ERROR, "Cannot allocate cbc_message_peer\n");
continue;
}
}
/* kick off the state machine[s] */
if (osmo_fsm_inst_dispatch(cbcmsg->fi, SMSCB_E_CREATE, op) < 0) {
rest_it_op_set_http_result(op, 500, "Illegal FSM event");
rest_it_op_complete(op);
}
/* we continue in the FSM after the WRITE_ACK event was received */
return 0;
}
void cbc_message_delete(struct cbc_message *cbcmsg, struct rest_it_op *op)
{
if (osmo_fsm_inst_dispatch(cbcmsg->fi, SMSCB_E_DELETE, op) < 0) {
rest_it_op_set_http_result(op, 500, "Illegal FSM event");
rest_it_op_complete(op);
}
/* we continue in the FSM after the DELETE_ACK event was received */
}
struct cbc_message *cbc_message_by_id(uint16_t message_id)
{
struct cbc_message *cbc_msg;
llist_for_each_entry(cbc_msg, &g_cbc->messages, list) {
if (cbc_msg->msg.message_id == message_id)
return cbc_msg;
}
return NULL;
}

View File

@@ -376,7 +376,7 @@ libosmo_asn1_sbcap_la_LIBADD=$(ASN1C_LDADD)
sbcap_LIBVERSION=0:0:0
lib_LTLIBRARIES = libosmo-sbcap.la
libosmo_sbcap_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(sbcap_LIBVERSION)
libosmo_sbcap_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(sbcap_LIBVERSION) -no-undefined
libosmo_sbcap_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) \
$(ASN1C_LIBS) libosmo-asn1-sbcap.la
libosmo_sbcap_la_SOURCES = sbcap_common.c

View File

@@ -28,7 +28,25 @@
extern int asn1_xer_print;
int _sbcap_DSBCAP = 0;
int _sbcap_DASN1C = 0;
#define DSBCAP _sbcap_DSBCAP
#define DASN1C _sbcap_DASN1C
static const struct value_string sbcap_procedure_code_vals[] = {
{ SBcAP_ProcedureId_Write_Replace_Warning, "Write-Replace-Warning" },
{ SBcAP_ProcedureId_Stop_Warning, "Stop-Warning" },
{ SBcAP_ProcedureId_Error_Indication, "Error-Indication" },
{ SBcAP_ProcedureId_Write_Replace_Warning_Indication, "Write-Replace-Warning-Indication" },
{ SBcAP_ProcedureId_Stop_Warning_Indication, "Stop-Warning-Indication" },
{ SBcAP_ProcedureId_PWS_Restart_Indication, "PWS-Restart-Indication" },
{ SBcAP_ProcedureId_PWS_Failure_Indication, "PWS-Failure-Indication" },
{ 0, NULL }
};
const char *sbcap_procedure_code_str(SBcAP_ProcedureCode_t pc)
{
return get_value_string(sbcap_procedure_code_vals, pc);
}
static const struct value_string sbcap_cause_vals[] = {
{ SBcAP_Cause_message_accepted, "message accepted" },
@@ -36,7 +54,7 @@ static const struct value_string sbcap_cause_vals[] = {
{ SBcAP_Cause_parameter_value_invalid, "parameter value invalid" },
{ SBcAP_Cause_valid_message_not_identified, "valid message not identified" },
{ SBcAP_Cause_tracking_area_not_valid, "Tracking Area not valid" },
{ SBcAP_Cause_unrecognised_message, "unrecoznied message" },
{ SBcAP_Cause_unrecognised_message, "unrecognised message" },
{ SBcAP_Cause_missing_mandatory_element, "missing mandatory element" },
{ SBcAP_Cause_mME_capacity_exceeded, "MME capacity exceeded" },
{ SBcAP_Cause_mME_memory_exceeded, "MME memory exceeded" },
@@ -75,7 +93,8 @@ struct msgb *sbcap_encode(SBcAP_SBC_AP_PDU_t *pdu)
rval = aper_encode_to_buffer(&asn_DEF_SBcAP_SBC_AP_PDU, NULL, pdu,
msgb_data(msg), msgb_tailroom(msg));
if (rval.encoded < 0) {
LOGP(DSBCAP, LOGL_ERROR, "Error encoding type: %s\n",
LOGP(DSBCAP, LOGL_ERROR, "%s: Error encoding type: %s\n",
sbcap_pdu_get_name(pdu),
rval.failed_type->name);
msgb_free(msg);
return NULL;
@@ -96,6 +115,7 @@ SBcAP_SBC_AP_PDU_t *sbcap_decode(const struct msgb *msg)
LOGP(DSBCAP, LOGL_ERROR, "Error decoding code=%d\n", rval.code);
return NULL;
}
LOGP(DSBCAP, LOGL_DEBUG, "Decoded %s\n", sbcap_pdu_get_name(pdu));
return pdu;
}
@@ -111,9 +131,75 @@ void sbcap_pdu_free(SBcAP_SBC_AP_PDU_t *pdu)
ASN_STRUCT_FREE(asn_DEF_SBcAP_SBC_AP_PDU, pdu);
}
void sbcap_set_log_area(int log_area)
SBcAP_ProcedureCode_t sbcap_pdu_get_procedure_code(const SBcAP_SBC_AP_PDU_t *pdu)
{
_sbcap_DSBCAP = log_area;
switch (pdu->present) {
case SBcAP_SBC_AP_PDU_PR_initiatingMessage:
return pdu->choice.initiatingMessage.procedureCode;
case SBcAP_SBC_AP_PDU_PR_successfulOutcome:
return pdu->choice.successfulOutcome.procedureCode;
case SBcAP_SBC_AP_PDU_PR_unsuccessfulOutcome:
return pdu->choice.unsuccessfulOutcome.procedureCode;
case SBcAP_SBC_AP_PDU_PR_NOTHING:
default:
return -1;
}
}
SBcAP_Criticality_t sbcap_pdu_get_criticality(const SBcAP_SBC_AP_PDU_t *pdu)
{
switch (pdu->present) {
case SBcAP_SBC_AP_PDU_PR_initiatingMessage:
return pdu->choice.initiatingMessage.criticality;
case SBcAP_SBC_AP_PDU_PR_successfulOutcome:
return pdu->choice.successfulOutcome.criticality;
case SBcAP_SBC_AP_PDU_PR_unsuccessfulOutcome:
return pdu->choice.unsuccessfulOutcome.criticality;
case SBcAP_SBC_AP_PDU_PR_NOTHING:
default:
return -1;
}
}
const char *sbcap_pdu_get_name(const SBcAP_SBC_AP_PDU_t *pdu)
{
static char pdu_name[256] = "<unknown>";
struct osmo_strbuf sb = { .buf = pdu_name, .len = sizeof(pdu_name) };
SBcAP_ProcedureCode_t pc = sbcap_pdu_get_procedure_code(pdu);
OSMO_STRBUF_PRINTF(sb, "%s", sbcap_procedure_code_str(pc));
switch (pc) {
case SBcAP_ProcedureId_Write_Replace_Warning:
case SBcAP_ProcedureId_Stop_Warning:
OSMO_STRBUF_PRINTF(sb, "%s",
pdu->present == SBcAP_SBC_AP_PDU_PR_initiatingMessage
? "-Request" : "-Response");
break;
default:
break;
}
return pdu_name;
}
void *sbcap_as_find_ie(void *void_list, SBcAP_ProtocolIE_ID_t ie_id)
{
A_SEQUENCE_OF(SBcAP_ProtocolIE_ID_t) *li = (void *)void_list;
int i;
for (i = 0; i < li->count; i++) {
/* "SBcAP_ProtocolIE_ID_t id" is first element in all *_IEs struct */
SBcAP_ProtocolIE_ID_t *cur_ie_id = li->array[i];
if (*cur_ie_id == ie_id) {
return cur_ie_id;
}
}
return NULL;
}
void sbcap_set_log_area(int log_area_sbcap, int log_area_asn1c)
{
_sbcap_DSBCAP = log_area_sbcap;
_sbcap_DASN1C = log_area_asn1c;
}
SBcAP_Write_Replace_Warning_Request_IEs_t *sbcap_alloc_Write_Replace_Warning_Request_IE(
@@ -135,3 +221,13 @@ SBcAP_Stop_Warning_Request_IEs_t *sbcap_alloc_Stop_Warning_Request_IE(
ie->value.present = present;
return ie;
}
SBcAP_ErrorIndicationIEs_t *sbcap_alloc_Error_Indication_IE(
long id, SBcAP_Criticality_t criticality, SBcAP_Stop_Warning_Request_IEs__value_PR present)
{
SBcAP_ErrorIndicationIEs_t *ie = CALLOC(1, sizeof(*ie));
ie->id = id;
ie->criticality = criticality;
ie->value.present = present;
return ie;
}

View File

@@ -39,24 +39,30 @@
#include <osmocom/cbc/cbc_data.h>
#include <osmocom/cbc/sbcap_link.h>
#include <osmocom/cbc/sbcap_link_fsm.h>
#include <osmocom/cbc/sbcap_msg.h>
#include <osmocom/cbc/cbc_peer.h>
#include <osmocom/cbc/debug.h>
struct cbc_sbcap_link *cbc_sbcap_link_alloc(struct cbc_sbcap_mgr *cbc, struct cbc_peer *peer)
{
struct cbc_sbcap_link *link;
char *name;
link = talloc_zero(cbc, struct cbc_sbcap_link);
OSMO_ASSERT(link);
link->peer = peer;
link->is_client = (peer->link_mode == CBC_PEER_LINK_MODE_CLIENT);
link->fi = osmo_fsm_inst_alloc(&sbcap_link_fsm, link, link, LOGL_DEBUG, NULL);
name = talloc_strdup(link, peer->name);
osmo_identifier_sanitize_buf(name, NULL, '_');
link->fi = osmo_fsm_inst_alloc(&sbcap_link_fsm, link, link, LOGL_DEBUG, name);
if (!link->fi) {
LOGPSBCAPC(link, LOGL_ERROR, "Unable to allocate FSM\n");
talloc_free(link);
return NULL;
}
talloc_free(name);
llist_add_tail(&link->list, &cbc->links);
return link;
@@ -77,16 +83,152 @@ const char *cbc_sbcap_link_name(const struct cbc_sbcap_link *link)
struct osmo_fd *ofd;
OSMO_ASSERT(link);
if (link->peer && link->peer->name) {
if (link->peer && link->peer->name)
return link->peer->name;
}
ofd = osmo_stream_srv_get_ofd(link->conn);
if (link->is_client)
ofd = osmo_stream_cli_get_ofd(link->cli_conn);
else
ofd = osmo_stream_srv_get_ofd(link->srv_conn);
return osmo_sock_get_name2(ofd->fd);
}
/*
* SCTP client
*/
static int cbc_sbcap_link_cli_connect_cb(struct osmo_stream_cli *conn)
{
struct cbc_sbcap_link *link = osmo_stream_cli_get_data(conn);
LOGPSBCAPC(link, LOGL_NOTICE, "Connected\n");
osmo_fsm_inst_dispatch(link->fi, SBcAP_LINK_E_CMD_RESET, NULL);
return 0;
}
static int cbc_sbcap_link_cli_disconnect_cb(struct osmo_stream_cli *conn)
{
struct cbc_sbcap_link *link = osmo_stream_cli_get_data(conn);
LOGPSBCAPC(link, LOGL_NOTICE, "Disconnected.\n");
LOGPSBCAPC(link, LOGL_NOTICE, "Reconnecting...\n");
osmo_stream_cli_reconnect(conn);
return 0;
}
static int cbc_sbcap_link_cli_read_cb(struct osmo_stream_cli *conn)
{
struct cbc_sbcap_link *link = osmo_stream_cli_get_data(conn);
struct osmo_fd *ofd = osmo_stream_cli_get_ofd(conn);
SBcAP_SBC_AP_PDU_t *pdu;
struct msgb *msg = msgb_alloc_c(g_cbc, 1500, "SBcAP-rx");
struct sctp_sndrcvinfo sinfo;
int flags = 0;
int rc;
/* read SBc-AP message from socket and process it */
rc = sctp_recvmsg(ofd->fd, msgb_data(msg), msgb_tailroom(msg),
NULL, NULL, &sinfo, &flags);
LOGPSBCAPC(link, LOGL_DEBUG, "%s(): sctp_recvmsg() returned %d (flags=0x%x)\n",
__func__, rc, flags);
if (rc < 0) {
osmo_stream_cli_reconnect(conn);
goto out;
} else if (rc == 0) {
osmo_stream_cli_reconnect(conn);
} else {
msgb_put(msg, rc);
}
if (flags & MSG_NOTIFICATION) {
union sctp_notification *notif = (union sctp_notification *) msgb_data(msg);
LOGPSBCAPC(link, LOGL_DEBUG, "Rx sctp notif %s\n",
osmo_sctp_sn_type_str(notif->sn_header.sn_type));
switch (notif->sn_header.sn_type) {
case SCTP_SHUTDOWN_EVENT:
osmo_stream_cli_reconnect(conn);
break;
case SCTP_ASSOC_CHANGE:
LOGPSBCAPC(link, LOGL_DEBUG, "Rx sctp notif SCTP_ASSOC_CHANGE: %s\n",
osmo_sctp_assoc_chg_str(notif->sn_assoc_change.sac_state));
break;
default:
LOGPSBCAPC(link, LOGL_DEBUG, "Rx sctp notif %s (%u)\n",
osmo_sctp_sn_type_str(notif->sn_header.sn_type),
notif->sn_header.sn_type);
break;
}
rc = 0;
}
if (rc == 0)
goto out;
LOGPSBCAPC(link, LOGL_DEBUG, "Rx SBc-AP %s\n", msgb_hexdump(msg));
/* decode + dispatch message */
pdu = sbcap_decode(msg);
if (pdu) {
LOGPSBCAPC(link, LOGL_INFO, "Rx SBc-AP %s\n",
sbcap_pdu_get_name(pdu));
g_cbc->sbcap.mgr->rx_cb(link, pdu);
} else {
LOGPSBCAPC(link, LOGL_ERROR, "Unable to decode %s\n", msgb_hexdump(msg));
pdu = sbcap_gen_error_ind(link, SBcAP_Cause_unrecognised_message, NULL);
if (pdu) {
cbc_sbcap_link_tx(link, pdu);
} else {
LOGPSBCAPC(link, LOGL_ERROR,
"Tx SBc-AP Error-Indication: msg gen failed\n");
}
}
out:
msgb_free(msg);
return rc;
}
int cbc_sbcap_link_open_cli(struct cbc_sbcap_link *link)
{
struct osmo_stream_cli *conn;
struct cbc_peer *peer = link->peer;
int rc;
OSMO_ASSERT(link->is_client);
OSMO_ASSERT(peer->link_mode == CBC_PEER_LINK_MODE_CLIENT);
conn = osmo_stream_cli_create(link);
osmo_stream_cli_set_data(conn, link);
osmo_stream_cli_set_nodelay(conn, true);
osmo_stream_cli_set_reconnect_timeout(conn, 5);
osmo_stream_cli_set_proto(conn, IPPROTO_SCTP);
osmo_stream_cli_set_connect_cb(conn, cbc_sbcap_link_cli_connect_cb);
osmo_stream_cli_set_disconnect_cb(conn, cbc_sbcap_link_cli_disconnect_cb);
osmo_stream_cli_set_read_cb(conn, cbc_sbcap_link_cli_read_cb);
OSMO_ASSERT(g_cbc->config.sbcap.num_local_host > 0);
rc = osmo_stream_cli_set_local_addrs(conn, (const char **)&g_cbc->config.sbcap.local_host,
g_cbc->config.sbcap.num_local_host);
if (rc < 0)
goto free_ret;
/* We assign free local port for client links:
* osmo_stream_cli_set_local_port(conn, g_cbc->sbcap.local_port);
*/
OSMO_ASSERT(peer->num_remote_host > 0);
rc = osmo_stream_cli_set_addrs(conn, (const char **)peer->remote_host, peer->num_remote_host);
if (rc < 0)
goto free_ret;
osmo_stream_cli_set_port(conn, peer->remote_port);
rc = osmo_stream_cli_open(conn);
if (rc < 0)
goto free_ret;
link->cli_conn = conn;
return 0;
free_ret:
osmo_stream_cli_destroy(conn);
return rc;
}
/*
* SCTP server
*/
/* data from MME has arrived at CBC */
static int sbcap_cbc_read_cb(struct osmo_stream_srv *conn)
static int sbcap_cbc_srv_read_cb(struct osmo_stream_srv *conn)
{
struct osmo_stream_srv_link *srv_link = osmo_stream_srv_get_master(conn);
struct cbc_sbcap_link *link = osmo_stream_srv_get_data(conn);
@@ -136,13 +278,13 @@ static int sbcap_cbc_read_cb(struct osmo_stream_srv *conn)
if (rc == 0)
goto out;
LOGPSBCAPC(link, LOGL_DEBUG, "Received SBc-AP %s\n", msgb_hexdump(msg));
LOGPSBCAPC(link, LOGL_DEBUG, "Rx SBc-AP %s\n", msgb_hexdump(msg));
/* decode + dispatch message */
pdu = sbcap_decode(msg);
if (pdu) {
LOGPSBCAPC(link, LOGL_INFO, "Received SBc-AP %d\n",
pdu->present);
LOGPSBCAPC(link, LOGL_INFO, "Rx SBc-AP %s\n",
sbcap_pdu_get_name(pdu));
cbc->rx_cb(link, pdu);
} else {
LOGPSBCAPC(link, LOGL_ERROR, "Unable to decode %s\n", msgb_hexdump(msg));
@@ -153,7 +295,7 @@ out:
}
/* connection from MME to CBC has been closed */
static int sbcap_cbc_closed_cb(struct osmo_stream_srv *conn)
static int sbcap_cbc_srv_closed_cb(struct osmo_stream_srv *conn)
{
struct cbc_sbcap_link *link = osmo_stream_srv_get_data(conn);
LOGPSBCAPC(link, LOGL_NOTICE, "connection closed\n");
@@ -198,6 +340,23 @@ static int sbcap_cbc_accept_cb(struct osmo_stream_srv_link *srv_link, int fd)
peer = cbc_peer_create(NULL, CBC_PEER_PROTO_SBcAP);
OSMO_ASSERT(peer);
peer->unknown_dynamic_peer = true;
} else { /* peer is known */
switch (peer->link_mode) {
case CBC_PEER_LINK_MODE_DISABLED:
LOGP(DSBcAP, LOGL_NOTICE,
"Rejecting conn for disabled SBc-AP peer %s:%d\n",
remote_ip, remote_port);
close(fd);
return -1;
case CBC_PEER_LINK_MODE_CLIENT:
LOGP(DSBcAP, LOGL_NOTICE,
"Rejecting conn for SBc-AP peer %s:%d configured as 'client'\n",
remote_ip, remote_port);
close(fd);
return -1;
default: /* MODE_SERVER */
break;
}
}
if (peer->link.sbcap) {
LOGPSBCAPC(peer->link.sbcap, LOGL_ERROR,
@@ -208,10 +367,10 @@ static int sbcap_cbc_accept_cb(struct osmo_stream_srv_link *srv_link, int fd)
link = cbc_sbcap_link_alloc(cbc, peer);
OSMO_ASSERT(link);
link->conn = osmo_stream_srv_create(srv_link, srv_link, fd,
sbcap_cbc_read_cb, sbcap_cbc_closed_cb,
link);
if (!link->conn) {
link->srv_conn = osmo_stream_srv_create(srv_link, srv_link, fd,
sbcap_cbc_srv_read_cb, sbcap_cbc_srv_closed_cb,
link);
if (!link->srv_conn) {
LOGPSBCAPC(link, LOGL_ERROR,
"Unable to create stream server for %s:%u\n",
remote_ip, remote_port);
@@ -224,35 +383,64 @@ static int sbcap_cbc_accept_cb(struct osmo_stream_srv_link *srv_link, int fd)
return 0;
}
void cbc_sbcap_link_tx(struct cbc_sbcap_link *link, SBcAP_SBC_AP_PDU_t *pdu)
int cbc_sbcap_link_tx(struct cbc_sbcap_link *link, SBcAP_SBC_AP_PDU_t *pdu)
{
struct msgb *msg;
int rc = 0;
if (!pdu) {
LOGP(DSBcAP, LOGL_NOTICE, "Cannot transmit msg: no pdu\n");
return;
return -ENOMSG;
}
if (!link) {
LOGP(DSBcAP, LOGL_NOTICE, "Cannot transmit msg: no connection\n");
return;
LOGP(DSBcAP, LOGL_NOTICE, "Cannot transmit msg %s: no connection\n",
sbcap_pdu_get_name(pdu));
rc = -ENOLINK;
goto ret_free;
} else if (link->is_client && !osmo_stream_cli_is_connected(link->cli_conn)) {
LOGPSBCAPC(link, LOGL_NOTICE, "Cannot transmit msg %s: reconnecting\n",
sbcap_pdu_get_name(pdu));
rc = -ENOTCONN;
goto ret_free;
}
LOGPSBCAPC(link, LOGL_INFO, "Transmitting msg\n");
LOGPSBCAPC(link, LOGL_INFO, "Tx msg %s\n",
sbcap_pdu_get_name(pdu));
OSMO_ASSERT(link->conn);
msg = sbcap_encode(pdu);
if (!msg)
if (!msg) {
rc = -EINVAL;
goto ret_free;
LOGPSBCAPC(link, LOGL_DEBUG, "Encoded message: %s\n", msgb_hexdump(msg));
osmo_stream_srv_send(link->conn, msg);
}
LOGPSBCAPC(link, LOGL_DEBUG, "Encoded message %s: %s\n",
sbcap_pdu_get_name(pdu), msgb_hexdump(msg));
if (link->is_client)
osmo_stream_cli_send(link->cli_conn, msg);
else
osmo_stream_srv_send(link->srv_conn, msg);
ret_free:
sbcap_pdu_free(pdu);
return rc;
}
void cbc_sbcap_link_close(struct cbc_sbcap_link *link)
{
if (link->conn)
osmo_stream_srv_destroy(link->conn);
if (!link->conn)
return;
if (link->is_client) {
osmo_stream_cli_destroy(link->cli_conn);
osmo_stream_cli_destroy(link->cli_conn);
if (link->peer)
link->peer->link.sbcap = NULL;
link->cli_conn = NULL;
if (link->fi)
osmo_fsm_inst_dispatch(link->fi, SBcAP_LINK_E_CMD_CLOSE, NULL);
} else {
osmo_stream_srv_destroy(link->srv_conn);
/* Same as waht's done for cli is done for srv in closed_cb() */
}
}
/*

View File

@@ -28,9 +28,10 @@
#include <osmocom/cbc/cbc_message.h>
#include <osmocom/cbc/sbcap_link.h>
#include <osmocom/cbc/sbcap_link_fsm.h>
#include <osmocom/cbc/sbcap_msg.h>
#include <osmocom/cbc/debug.h>
#include <osmocom/cbc/cbc_peer.h>
#include <osmocom/cbc/smscb_message_fsm.h>
#include <osmocom/cbc/smscb_peer_fsm.h>
#define S(x) (1 << (x))
@@ -42,9 +43,6 @@ enum sbcap_link_state {
};
static const struct value_string sbcap_link_event_names[] = {
{ SBcAP_LINK_E_RX_RST_COMPL, "Rx Reset Complete" },
{ SBcAP_LINK_E_RX_RST_FAIL, "Rx Reset Failure" },
{ SBcAP_LINK_E_RX_KA_COMPL, "Rx Keep-Alive Complete" },
{ SBcAP_LINK_E_RX_RESTART, "Rx Restart" },
{ SBcAP_LINK_E_CMD_RESET, "RESET.cmd" },
{ SBcAP_LINK_E_CMD_CLOSE, "CLOSE.cmd" },
@@ -83,7 +81,7 @@ static void sbcap_link_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, vo
//pdu = data;
/* TODO: delete any CBS state we have for this peer */
/* TODO: re-send messages we have matching the scope of the peer */
LOGPSBCAPC(link, LOGL_NOTICE, "RESTART but re-sending not implemented yet\n");
LOGPSBCAPC(link, LOGL_NOTICE, "Rx PWS Restart Indication not implemented yet\n");
break;
default:
OSMO_ASSERT(0);
@@ -131,20 +129,6 @@ struct osmo_fsm sbcap_link_fsm = {
.cleanup = sbcap_link_fsm_cleanup,
};
static void *sbcap_as_find_ie(void *void_list, SBcAP_ProtocolIE_ID_t ie_id)
{
A_SEQUENCE_OF(SBcAP_ProtocolIE_ID_t) *li = (void *)void_list;
int i;
for (i = 0; i < li->count; i++) {
/* "SBcAP_ProtocolIE_ID_t id" is first element in all *_IEs struct */
SBcAP_ProtocolIE_ID_t *cur_ie_id = li->array[i];
if (*cur_ie_id == ie_id) {
return cur_ie_id;
}
}
return NULL;
}
static SBcAP_Message_Identifier_t *get_msg_id_ie(struct cbc_sbcap_link *link,
const SBcAP_SBC_AP_PDU_t *pdu)
{
@@ -209,17 +193,88 @@ static int get_msg_id(struct cbc_sbcap_link *link, const SBcAP_SBC_AP_PDU_t *pdu
if (!ie)
return -1;
if (ie->size != 2) {
LOGPSBCAPC(link, LOGL_ERROR, "get_msg_id wrong size %zu\n", ie->size);
LOGPSBCAPC(link, LOGL_ERROR, "get_msg_id(%s) wrong size %zu\n",
sbcap_pdu_get_name(pdu), ie->size);
return -1;
}
return osmo_load16be(ie->buf);
}
/* message was received from remote SBcAP peer (BSC) */
/* Rx Error Indication from peer */
static int cbc_sbcap_link_rx_error_ind(struct cbc_sbcap_link *link, SBcAP_SBC_AP_PDU_t *pdu)
{
A_SEQUENCE_OF(void) *as_pdu = NULL;
SBcAP_ErrorIndicationIEs_t *ie;
SBcAP_Criticality_Diagnostics_t *ie_diag = NULL;
long cause = -1;
long proc_code = -1;
long trigger_msg = -1;
long criticality = -1;
int i;
as_pdu = (void *)&pdu->choice.initiatingMessage.value.choice.Error_Indication.protocolIEs.list;
OSMO_ASSERT(as_pdu);
for (i = 0; i < as_pdu->count; i++) {
ie = (SBcAP_ErrorIndicationIEs_t *)(as_pdu->array[i]);
OSMO_ASSERT(ie);
switch (ie->id) {
case SBcAP_ErrorIndicationIEs__value_PR_Cause:
cause = ie->value.choice.Cause;
break;
case SBcAP_ErrorIndicationIEs__value_PR_Criticality_Diagnostics:
ie_diag = &ie->value.choice.Criticality_Diagnostics;
if (ie_diag->procedureCode)
proc_code = *ie_diag->procedureCode;
if (ie_diag->triggeringMessage)
trigger_msg = *ie_diag->triggeringMessage;
if (ie_diag->procedureCriticality)
criticality = *ie_diag->procedureCriticality;
break;
default:
continue;
}
}
LOGPSBCAPC(link, LOGL_ERROR, "Rx %s (cause=%ld, diagnostics=%d [proc_code=%ld, trigger_msg=%ld criticality=%ld])\n",
sbcap_pdu_get_name(pdu), cause, !!ie_diag, proc_code, trigger_msg, criticality);
return 0;
}
/* Rx Write Replace Warning Response from peer */
static int cbc_sbcap_link_rx_write_replace_warn_resp(struct cbc_sbcap_link *link,
struct cbc_message_peer *mp,
SBcAP_SBC_AP_PDU_t *pdu)
{
A_SEQUENCE_OF(void) *as_pdu;
SBcAP_Write_Replace_Warning_Response_IEs_t *ie;
SBcAP_SBC_AP_PDU_t *err_ind_pdu;
int ev = SMSCB_PEER_E_SBCAP_WRITE_ACK;
as_pdu = (void *)&pdu->choice.successfulOutcome.value.choice.Write_Replace_Warning_Response.protocolIEs.list;
/* static const long asn_VAL_19_SBcAP_id_Cause = 1; */
ie = sbcap_as_find_ie(as_pdu, 1);
if (ie) {
if (ie->value.choice.Cause != SBcAP_Cause_message_accepted)
ev = SMSCB_PEER_E_SBCAP_WRITE_NACK;
} else { /* This shouldn't happen, the IE is Mandatory... */
ev = SMSCB_PEER_E_SBCAP_WRITE_NACK;
err_ind_pdu = sbcap_gen_error_ind(link,
SBcAP_Cause_missing_mandatory_element, pdu);
if (err_ind_pdu)
cbc_sbcap_link_tx(link, err_ind_pdu);
}
return osmo_fsm_inst_dispatch(mp->fi, ev, pdu);
}
/* message was received from remote SBc-AP peer (MME) */
int cbc_sbcap_link_rx_cb(struct cbc_sbcap_link *link, SBcAP_SBC_AP_PDU_t *pdu)
{
struct cbc_message *smscb;
struct cbc_message_peer *mp;
SBcAP_SBC_AP_PDU_t *err_ind_pdu;
int msg_id;
/* messages without reference to a specific SMSCB message */
@@ -229,19 +284,31 @@ int cbc_sbcap_link_rx_cb(struct cbc_sbcap_link *link, SBcAP_SBC_AP_PDU_t *pdu)
case SBcAP_ProcedureId_Write_Replace_Warning:
case SBcAP_ProcedureId_Stop_Warning:
LOGPSBCAPC(link, LOGL_ERROR,
"SBcAP initiatingMessage procedure=%ld MME->CBC not expected\n",
pdu->choice.initiatingMessage.procedureCode);
"SBcAP %s MME->CBC not expected\n",
sbcap_pdu_get_name(pdu));
return -EINVAL;
case SBcAP_ProcedureId_PWS_Restart_Indication:
return osmo_fsm_inst_dispatch(link->fi, SBcAP_LINK_E_RX_RESTART, pdu);
case SBcAP_ProcedureId_Error_Indication:
return cbc_sbcap_link_rx_error_ind(link, pdu);
case SBcAP_ProcedureId_PWS_Failure_Indication:
LOGPSBCAPC(link, LOGL_NOTICE, "Rx %s not implemented yet\n",
sbcap_pdu_get_name(pdu));
return 0;
case SBcAP_ProcedureId_Stop_Warning_Indication:
case SBcAP_ProcedureId_Write_Replace_Warning_Indication:
break; /* Handle msg id below */
case SBcAP_ProcedureId_Error_Indication:
case SBcAP_ProcedureId_PWS_Failure_Indication:
default:
LOGPSBCAPC(link, LOGL_ERROR, "SBcAP initiatingMessage procedure=%ld not implemented?\n",
pdu->choice.initiatingMessage.procedureCode);
pdu->choice.initiatingMessage.procedureCode);
err_ind_pdu = sbcap_gen_error_ind(link, SBcAP_Cause_valid_message_not_identified, pdu);
if (!err_ind_pdu) {
LOGPSBCAPC(link, LOGL_ERROR,
"Tx SBc-AP Error-Indication: msg gen failed\n");
} else if (cbc_sbcap_link_tx(link, err_ind_pdu) < 0) {
LOGPSBCAPC(link, LOGL_ERROR,
"Tx SBc-AP Error-Indication failed\n");
}
return 0;
}
break;
@@ -276,8 +343,8 @@ int cbc_sbcap_link_rx_cb(struct cbc_sbcap_link *link, SBcAP_SBC_AP_PDU_t *pdu)
/* look-up smscb_message */
smscb = cbc_message_by_id(msg_id);
if (!smscb) {
LOGPSBCAPC(link, LOGL_ERROR, "Rx SBc-AP msg for unknown message-id 0x%04x\n",
msg_id);
LOGPSBCAPC(link, LOGL_ERROR, "Rx SBc-AP %s for unknown message-id 0x%04x\n",
sbcap_pdu_get_name(pdu), msg_id);
/* TODO: inform peer? */
return 0;
}
@@ -285,8 +352,8 @@ int cbc_sbcap_link_rx_cb(struct cbc_sbcap_link *link, SBcAP_SBC_AP_PDU_t *pdu)
/* look-up smscb_message_peer */
mp = cbc_message_peer_get(smscb, link->peer);
if (!mp) {
LOGPSBCAPC(link, LOGL_ERROR, "Rx SBc-AP msg for message-id 0x%04x without peer %s\n",
msg_id, link->peer->name);
LOGPSBCAPC(link, LOGL_ERROR, "Rx SBc-AP %s for message-id 0x%04x without peer %s\n",
sbcap_pdu_get_name(pdu), msg_id, link->peer->name);
/* TODO: inform peer? */
return 0;
}
@@ -295,6 +362,8 @@ int cbc_sbcap_link_rx_cb(struct cbc_sbcap_link *link, SBcAP_SBC_AP_PDU_t *pdu)
switch (pdu->present) {
case SBcAP_SBC_AP_PDU_PR_initiatingMessage:
switch (pdu->choice.initiatingMessage.procedureCode) {
case SBcAP_ProcedureId_Write_Replace_Warning_Indication:
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_PEER_E_SBCAP_WRITE_IND, pdu);
default:
break;
}
@@ -303,11 +372,11 @@ int cbc_sbcap_link_rx_cb(struct cbc_sbcap_link *link, SBcAP_SBC_AP_PDU_t *pdu)
switch (pdu->choice.successfulOutcome.procedureCode) {
case SBcAP_ProcedureId_Write_Replace_Warning:
//if (dec->u.write_replace_compl.old_serial_nr)
// return osmo_fsm_inst_dispatch(mp->fi, SMSCB_E_SBcAP_REPLACE_ACK, dec);
// return osmo_fsm_inst_dispatch(mp->fi, SMSCB_PEER_E_SBcAP_REPLACE_ACK, dec);
//else
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_E_SBCAP_WRITE_ACK, pdu);
return cbc_sbcap_link_rx_write_replace_warn_resp(link, mp, pdu);
case SBcAP_ProcedureId_Stop_Warning:
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_E_SBCAP_DELETE_ACK, pdu);
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_PEER_E_SBCAP_DELETE_ACK, pdu);
default:
break;
}
@@ -315,7 +384,7 @@ int cbc_sbcap_link_rx_cb(struct cbc_sbcap_link *link, SBcAP_SBC_AP_PDU_t *pdu)
case SBcAP_SBC_AP_PDU_PR_unsuccessfulOutcome:
switch (pdu->choice.unsuccessfulOutcome.procedureCode) {
case SBcAP_ProcedureId_Stop_Warning:
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_E_SBCAP_DELETE_NACK, pdu);
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_PEER_E_SBCAP_DELETE_NACK, pdu);
default:
break;
}

View File

@@ -35,10 +35,13 @@
#include <osmocom/cbc/sbcap_link.h>
#include <osmocom/cbc/debug.h>
/* 3GPP TS 36.413 9.2.1.53 */
/* 3GPP TS 36.413 9.2.1.53, 3GPP TS 23.041 9.3.35 */
#define SBCAP_WARN_MSG_CONTENTS_IE_MAX_LEN 9600
#if 0
/* Warning Area List
* 3GPP TS 36.413 9.2.1.46, 3GPP TS 23.041 9.3.30
*/
static void msgb_put_sbcap_cell_list(const struct cbc_message *cbcmsg, void *void_li)
{
static uint8_t ie_plm_id0[] = {0x05, 0xf5, 0x32};
@@ -78,13 +81,14 @@ static void msgb_put_sbcap_cell_list(const struct cbc_message *cbcmsg, void *voi
}
#endif
/* generate a SBc-AP WRITE-REPLACE WARNING REQUEST from our internal representation */
SBcAP_SBC_AP_PDU_t *cbcmsg_to_sbcap(void *ctx, const struct cbc_message *cbcmsg)
/* generate a SBc-AP WRITE-REPLACE WARNING REQUEST from our internal representation.
* 3GPP TS 36.413 9.1.13.1
*/
SBcAP_SBC_AP_PDU_t *sbcap_gen_write_replace_warning_req(void *ctx, const struct cbc_message *cbcmsg)
{
const struct smscb_message *smscb = &cbcmsg->msg;
SBcAP_SBC_AP_PDU_t *pdu;
SBcAP_Write_Replace_Warning_Request_IEs_t *ie;
uint16_t ie_warning_type;
unsigned int i;
uint8_t *ptr;
#if 0
@@ -101,7 +105,9 @@ SBcAP_SBC_AP_PDU_t *cbcmsg_to_sbcap(void *ctx, const struct cbc_message *cbcmsg)
A_SEQUENCE_OF(void) *as_pdu = (void *)&pdu->choice.initiatingMessage.value.choice.Write_Replace_Warning_Request.protocolIEs.list;
/* static const long asn_VAL_1_SBcAP_id_Message_Identifier = 5; */
/* Message Identifier:
* 3GPP TS 36.413 9.2.1.44, 3GPP TS 23.041 9.4.1.3.6
* static const long asn_VAL_1_SBcAP_id_Message_Identifier = 5; */
ie = sbcap_alloc_Write_Replace_Warning_Request_IE(5, SBcAP_Criticality_reject,
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Message_Identifier);
ie->value.choice.Message_Identifier.buf = MALLOC(sizeof(uint16_t));
@@ -110,7 +116,9 @@ SBcAP_SBC_AP_PDU_t *cbcmsg_to_sbcap(void *ctx, const struct cbc_message *cbcmsg)
osmo_store16be(smscb->message_id, ie->value.choice.Message_Identifier.buf);
ASN_SEQUENCE_ADD(as_pdu, ie);
/* static const long asn_VAL_2_SBcAP_id_Serial_Number = 11; */
/* Serial Number
* 3GPP TS 36.413 9.2.1.45, 3GPP TS 23.041 9.4.1.2.1
* static const long asn_VAL_2_SBcAP_id_Serial_Number = 11; */
ie = sbcap_alloc_Write_Replace_Warning_Request_IE(11, SBcAP_Criticality_reject,
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Serial_Number);
ie->value.choice.Serial_Number.buf = MALLOC(sizeof(uint16_t));
@@ -124,7 +132,9 @@ SBcAP_SBC_AP_PDU_t *cbcmsg_to_sbcap(void *ctx, const struct cbc_message *cbcmsg)
break; /* Nothing to be done :*/
#if 0
case CBC_MSG_SCOPE_EUTRAN_CGI:
/* static const long asn_VAL_25_SBcAP_id_Warning_Area_List = 15; */
/* Warning Area List
* 3GPP TS 36.413 9.2.1.46, 3GPP TS 23.041 9.3.30
* static const long asn_VAL_25_SBcAP_id_Warning_Area_List = 15; */
ie = sbcap_alloc_Write_Replace_Warning_Request_IE(15, SBcAP_Criticality_ignore,
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Warning_Area_List);
ASN_SEQUENCE_ADD(as_pdu, ie);
@@ -136,35 +146,39 @@ SBcAP_SBC_AP_PDU_t *cbcmsg_to_sbcap(void *ctx, const struct cbc_message *cbcmsg)
OSMO_ASSERT(0);
}
/* static const long asn_VAL_5_SBcAP_id_Repetition_Period = 10; */
/* Repetition Period
* 3GPP TS 36.413 9.2.1.48, 3GPP TS 23.041 9.3.8
* static const long asn_VAL_5_SBcAP_id_Repetition_Period = 10; */
ie = sbcap_alloc_Write_Replace_Warning_Request_IE(10, SBcAP_Criticality_reject,
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Repetition_Period);
ie->value.choice.Repetition_Period = cbcmsg->rep_period; /*seconds */
ASN_SEQUENCE_ADD(as_pdu, ie);
/* static const long asn_VAL_7_SBcAP_id_Number_of_Broadcasts_Requested = 7; */
/* Number of Broadcasts Requested
* 3GPP TS 36.413 9.2.1.49, 3GPP TS 23.041 9.3.9
* static const long asn_VAL_7_SBcAP_id_Number_of_Broadcasts_Requested = 7; */
ie = sbcap_alloc_Write_Replace_Warning_Request_IE(7, SBcAP_Criticality_reject,
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Number_of_Broadcasts_Requested);
ie->value.choice.Number_of_Broadcasts_Requested = cbcmsg->num_bcast;
ASN_SEQUENCE_ADD(as_pdu, ie);
if (smscb->is_etws) {
/* Warning Type, 3GPP TS 36.413 sec 9.2.1.50: */
ie_warning_type = smscb->etws.warning_type;
if (smscb->etws.user_alert)
ie_warning_type |= 0x0100;
if (smscb->etws.popup_on_display)
ie_warning_type |= 0x0080;
/* static const long asn_VAL_8_SBcAP_id_Warning_Type = 18; */
/* Warning Type
* 3GPP TS 36.413 sec 9.2.1.50, 3GPP TS 23.041 9.3.24
* static const long asn_VAL_8_SBcAP_id_Warning_Type = 18; */
ie = sbcap_alloc_Write_Replace_Warning_Request_IE(18, SBcAP_Criticality_ignore,
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Warning_Type);
ie->value.choice.Warning_Type.buf = MALLOC(sizeof(ie_warning_type));
ie->value.choice.Warning_Type.size = sizeof(ie_warning_type);
memcpy(ie->value.choice.Warning_Type.buf, &ie_warning_type, sizeof(ie_warning_type));
ie->value.choice.Warning_Type.buf = MALLOC(2);
ie->value.choice.Warning_Type.size = 2;
ie->value.choice.Warning_Type.buf[0] = ((smscb->etws.warning_type & 0x7f) << 1);
if (smscb->etws.user_alert)
ie->value.choice.Warning_Type.buf[0] |= 0x01;
ie->value.choice.Warning_Type.buf[1] = (smscb->etws.popup_on_display) ? 0x80 : 0x0;
ASN_SEQUENCE_ADD(as_pdu, ie);
/* Warning Security Information, 3GPP TS 36.413 sec 9.2.1.51: */
/*static const long asn_VAL_9_SBcAP_id_Warning_Security_Information = 17 */
/* Warning Security Information
* 3GPP TS 36.413 sec 9.2.1.51, 3GPP TS 23.041 9.3.25
* static const long asn_VAL_9_SBcAP_id_Warning_Security_Information = 17 */
ie = sbcap_alloc_Write_Replace_Warning_Request_IE(17, SBcAP_Criticality_ignore,
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Warning_Security_Information);
ie->value.choice.Warning_Security_Information.buf = MALLOC(sizeof(smscb->etws.warning_sec_info));
@@ -174,7 +188,9 @@ SBcAP_SBC_AP_PDU_t *cbcmsg_to_sbcap(void *ctx, const struct cbc_message *cbcmsg)
ASN_SEQUENCE_ADD(as_pdu, ie);
} else {
/* static const long asn_VAL_10_SBcAP_id_Data_Coding_Scheme = 3; */
/* Data Coding Scheme
* 3GPP TS 36.413 9.2.1.52, 3GPP TS 23.041 9.4.1.2.3
* static const long asn_VAL_10_SBcAP_id_Data_Coding_Scheme = 3; */
ie = sbcap_alloc_Write_Replace_Warning_Request_IE(3, SBcAP_Criticality_ignore,
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Data_Coding_Scheme);
ie->value.choice.Data_Coding_Scheme.buf = MALLOC(1);
@@ -183,8 +199,9 @@ SBcAP_SBC_AP_PDU_t *cbcmsg_to_sbcap(void *ctx, const struct cbc_message *cbcmsg)
*ie->value.choice.Data_Coding_Scheme.buf = smscb->cbs.dcs;
ASN_SEQUENCE_ADD(as_pdu, ie);
/* 3GPP TS 36.413 9.2.1.53 Warning Message Contents. Encoded as in S1AP. */
/* static const long asn_VAL_11_SBcAP_id_Warning_Message_Content = 16; */
/* Warning Message Contents
* 3GPP TS 36.413 9.2.1.53, 3GPP TS 23.041 9.3.35
* static const long asn_VAL_11_SBcAP_id_Warning_Message_Content = 16; */
ie = sbcap_alloc_Write_Replace_Warning_Request_IE(16, SBcAP_Criticality_ignore,
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Warning_Message_Content);
ie->value.choice.Warning_Message_Content.buf = MALLOC(1 + smscb->cbs.num_pages * (SMSCB_RAW_PAGE_LEN+1));
@@ -206,8 +223,24 @@ SBcAP_SBC_AP_PDU_t *cbcmsg_to_sbcap(void *ctx, const struct cbc_message *cbcmsg)
ptr++;
}
ASN_SEQUENCE_ADD(as_pdu, ie);
/* Concurrent Warning Message Indicator
* 3GPP TS 36.413 8.12.1.2 and 9.2.1.72, 3GPP TS 23.041 9.3.32
* static const long asn_VAL_13_SBcAP_id_Concurrent_Warning_Message_Indicator = 20; */
ie = sbcap_alloc_Write_Replace_Warning_Request_IE(20, SBcAP_Criticality_reject,
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Concurrent_Warning_Message_Indicator);
ie->value.choice.Concurrent_Warning_Message_Indicator = SBcAP_Concurrent_Warning_Message_Indicator_true;
ASN_SEQUENCE_ADD(as_pdu, ie);
}
/* Send Write-Replace-Warning-Indication
* 3GPP TS 36.413 4.3.4.3.5, 3GPP TS 23.041 9.3.39
* static const long asn_VAL_14_SBcAP_id_Send_Write_Replace_Warning_Indication = 24; */
ie = sbcap_alloc_Write_Replace_Warning_Request_IE(24, SBcAP_Criticality_ignore,
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Send_Write_Replace_Warning_Indication);
ie->value.choice.Send_Write_Replace_Warning_Indication = SBcAP_Send_Write_Replace_Warning_Indication_true;
ASN_SEQUENCE_ADD(as_pdu, ie);
return pdu;
}
@@ -231,7 +264,9 @@ SBcAP_SBC_AP_PDU_t *sbcap_gen_stop_warning_req(void *ctx, const struct cbc_messa
A_SEQUENCE_OF(void) *as_pdu = (void *)&pdu->choice.initiatingMessage.value.choice.Stop_Warning_Request.protocolIEs.list;
/* static const long asn_VAL_1_SBcAP_id_Message_Identifier = 5; */
/* Message Identifier:
* 3GPP TS 36.413 9.2.1.44, 3GPP TS 23.041 9.4.1.3.6
* static const long asn_VAL_1_SBcAP_id_Message_Identifier = 5; */
ie = sbcap_alloc_Stop_Warning_Request_IE(5, SBcAP_Criticality_reject,
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Message_Identifier);
ie->value.choice.Message_Identifier.buf = MALLOC(sizeof(uint16_t));
@@ -240,7 +275,9 @@ SBcAP_SBC_AP_PDU_t *sbcap_gen_stop_warning_req(void *ctx, const struct cbc_messa
osmo_store16be(smscb->message_id, ie->value.choice.Message_Identifier.buf);
ASN_SEQUENCE_ADD(as_pdu, ie);
/* static const long asn_VAL_2_SBcAP_id_Serial_Number = 11; */
/* Serial Number
* 3GPP TS 36.413 9.2.1.45, 3GPP TS 23.041 9.4.1.2.1
* static const long asn_VAL_2_SBcAP_id_Serial_Number = 11; */
ie = sbcap_alloc_Stop_Warning_Request_IE(11, SBcAP_Criticality_reject,
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Serial_Number);
ie->value.choice.Serial_Number.buf = MALLOC(sizeof(uint16_t));
@@ -254,7 +291,9 @@ SBcAP_SBC_AP_PDU_t *sbcap_gen_stop_warning_req(void *ctx, const struct cbc_messa
break; /* Nothing to be done :*/
#if 0
case CBC_MSG_SCOPE_EUTRAN_CGI:
/* static const long asn_VAL_25_SBcAP_id_Warning_Area_List = 15; */
/* Warning Area List
* 3GPP TS 36.413 9.2.1.46, 3GPP TS 23.041 9.3.30
* static const long asn_VAL_25_SBcAP_id_Warning_Area_List = 15; */
ie = sbcap_alloc_Stop_Warning_Request_IE(15, SBcAP_Criticality_ignore,
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Warning_Area_List);
ASN_SEQUENCE_ADD(as_pdu, ie);
@@ -268,3 +307,68 @@ SBcAP_SBC_AP_PDU_t *sbcap_gen_stop_warning_req(void *ctx, const struct cbc_messa
return pdu;
}
/* generate a SBc-AP ERROR INDICATION, 3GPP TS 29.168 4.3.4.2A.1.
* rx_pdu can be NULL.
*/
SBcAP_SBC_AP_PDU_t *sbcap_gen_error_ind(void *ctx, SBcAP_Cause_t cause, SBcAP_SBC_AP_PDU_t *rx_pdu)
{
SBcAP_SBC_AP_PDU_t *pdu;
SBcAP_ErrorIndicationIEs_t *ie;
pdu = sbcap_pdu_alloc();
if (!pdu)
return NULL;
pdu->present = SBcAP_SBC_AP_PDU_PR_initiatingMessage;
pdu->choice.initiatingMessage.procedureCode = SBcAP_ProcedureId_Error_Indication;
pdu->choice.initiatingMessage.criticality = SBcAP_Criticality_ignore;
pdu->choice.initiatingMessage.value.present = SBcAP_InitiatingMessage__value_PR_Error_Indication;
A_SEQUENCE_OF(void) *as_pdu = (void *)&pdu->choice.initiatingMessage.value.choice.Error_Indication.protocolIEs.list;
/* Cause, Optional:
* 3GPP TS 36.413 4.3.4.3.2
* static const long asn_VAL_19_SBcAP_id_Cause = 1; */
ie = sbcap_alloc_Error_Indication_IE(1, SBcAP_Criticality_ignore,
SBcAP_ErrorIndicationIEs__value_PR_Cause);
ie->value.choice.Cause = cause;
ASN_SEQUENCE_ADD(as_pdu, ie);
if (rx_pdu) {
SBcAP_Criticality_Diagnostics_t *diag_ie;
/* Criticality Diagnostics, Optional:
* 3GPP TS 36.413 4.3.4.3.3
* static const long asn_VAL_20_SBcAP_id_Criticality_Diagnostics = 2; */
ie = sbcap_alloc_Error_Indication_IE(1, SBcAP_Criticality_ignore,
SBcAP_ErrorIndicationIEs__value_PR_Criticality_Diagnostics);
diag_ie = &ie->value.choice.Criticality_Diagnostics;
diag_ie->procedureCode = MALLOC(sizeof(*diag_ie->procedureCode));
*diag_ie->procedureCode = sbcap_pdu_get_procedure_code(rx_pdu);
diag_ie->triggeringMessage = MALLOC(sizeof(*diag_ie->triggeringMessage));
*diag_ie->triggeringMessage = rx_pdu->present;
diag_ie->procedureCriticality = MALLOC(sizeof(*diag_ie->procedureCriticality));
*diag_ie->procedureCriticality = sbcap_pdu_get_criticality(rx_pdu);
ASN_SEQUENCE_ADD(as_pdu, ie);
}
return pdu;
}
static void cci_from_sbcap_ecgi(struct cbc_cell_id *cci, const SBcAP_EUTRAN_CGI_t *eCGI)
{
cci->id_discr = CBC_CELL_ID_ECGI;
cci->u.ecgi.eci = (osmo_load32be(&eCGI->cell_ID.buf[0]) >> 4);
osmo_plmn_from_bcd(eCGI->pLMNidentity.buf, &cci->u.ecgi.plmn);
}
/* Fill a cbc_cell_id from a SBcAP_CellId_Broadcast_List_Item */
void cci_from_sbcap_bcast_cell_id(struct cbc_cell_id *cci, const SBcAP_CellId_Broadcast_List_Item_t *it)
{
cci_from_sbcap_ecgi(cci, &it->eCGI);
}
/* Fill a cbc_cell_id from a SBcAP_TAI_t */
void cci_from_sbcap_tai(struct cbc_cell_id *cci, const SBcAP_TAI_t *tai)
{
cci->id_discr = CBC_CELL_ID_TAI;
cci->u.tai.tac = osmo_load16be(tai->tAC.buf);
osmo_plmn_from_bcd(tai->pLMNidentity.buf, &cci->u.tai.plmn);
}

373
src/sbcap_smscb_peer_fsm.c Normal file
View File

@@ -0,0 +1,373 @@
/* SMSCB Peer FSM: Represents state of one SMSCB for one peer (MME) */
/* This FSM exists per tuple of (message, mme peer) */
/* (C) 2019 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/fsm.h>
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/gsm0808_utils.h>
#include <osmocom/sbcap/sbcap_common.h>
#include <osmocom/cbc/cbc_message.h>
#include <osmocom/cbc/cbc_peer.h>
#include <osmocom/cbc/sbcap_link.h>
#include <osmocom/cbc/sbcap_msg.h>
#include <osmocom/cbc/debug.h>
#include <osmocom/cbc/smscb_peer_fsm.h>
#include <osmocom/cbc/smscb_message_fsm.h>
#define S(x) (1 << (x))
/***********************************************************************
* Helper functions
***********************************************************************/
/* append SBcAP cells to msg_peer compl list */
void msg_peer_append_compl_sbcap_bcast_area_list(struct cbc_message_peer *mp,
const SBcAP_Broadcast_Scheduled_Area_List_t *bcast)
{
SBcAP_CellId_Broadcast_List_t *cell_id_bscat = bcast->cellId_Broadcast_List;
A_SEQUENCE_OF(struct SBcAP_CellId_Broadcast_List_Item) *as_cell_id_bcast;
SBcAP_CellId_Broadcast_List_Item_t *it;
unsigned int i;
if (!cell_id_bscat)
return;
as_cell_id_bcast = (void *) &cell_id_bscat->list;
for (i = 0; i < as_cell_id_bcast->count; i++) {
it = (SBcAP_CellId_Broadcast_List_Item_t *)(as_cell_id_bcast->array[i]);
OSMO_ASSERT(it);
struct cbc_cell_id *cci = NULL; // FIXME: lookup
if (!cci) {
cci = talloc_zero(mp, struct cbc_cell_id);
if (!cci)
return;
llist_add_tail(&cci->list, &mp->num_compl_list);
}
cci_from_sbcap_bcast_cell_id(cci, it);
LOGPFSML(mp->fi, LOGL_DEBUG, "Appending CellId %s to Broadcast Completed list\n",
cbc_cell_id2str(cci));
cci->num_compl.num_compl += 1;
cci->num_compl.num_bcast_info += 1;
}
}
/* append SBcAP cells to msg_peer fail list */
void msg_peer_append_fail_sbcap_tai_list(struct cbc_message_peer *mp,
const SBcAP_List_of_TAIs_t *tais)
{
A_SEQUENCE_OF(List_of_TAIs__Member) *as_tais = (void *)&tais->list;
List_of_TAIs__Member *it;
unsigned int i;
for (i = 0; i < as_tais->count; i++) {
it = (List_of_TAIs__Member *)(as_tais->array[i]);
OSMO_ASSERT(it);
struct cbc_cell_id *cci = NULL; // FIXME: lookup
if (!cci) {
cci = talloc_zero(mp, struct cbc_cell_id);
if (!cci)
return;
llist_add_tail(&cci->list, &mp->fail_list);
}
cci_from_sbcap_tai(cci, &it->tai);
cci->fail.cause = SBcAP_Cause_tracking_area_not_valid;
LOGPFSML(mp->fi, LOGL_DEBUG, "Appending CellId %s (cause: %s) to Failed list\n",
cbc_cell_id2str(cci), sbcap_cause_str(cci->fail.cause));
}
}
/***********************************************************************
* actual FSM
***********************************************************************/
static void smscb_p_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
int rc;
switch (event) {
case SMSCB_PEER_E_CREATE:
/* send it to peer */
rc = peer_new_cbc_message(mp->peer, mp->cbcmsg);
osmo_fsm_inst_state_chg(fi, SMSCB_S_WAIT_WRITE_ACK, 10, T_WAIT_WRITE_ACK);
if (rc != 0) /* Immediately timeout the message */
fi->fsm->timer_cb(fi);
break;
default:
OSMO_ASSERT(0);
}
}
static void smscb_p_fsm_wait_write_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
SBcAP_SBC_AP_PDU_t *sbcap = NULL;
A_SEQUENCE_OF(void) *as_pdu;
SBcAP_Write_Replace_Warning_Response_IEs_t *ie;
switch (event) {
case SMSCB_PEER_E_SBCAP_WRITE_ACK:
sbcap = data;
OSMO_ASSERT(sbcap->present == SBcAP_SBC_AP_PDU_PR_successfulOutcome);
OSMO_ASSERT(sbcap->choice.successfulOutcome.procedureCode == SBcAP_ProcedureId_Write_Replace_Warning);
as_pdu = (void *)&sbcap->choice.successfulOutcome.value.choice.Write_Replace_Warning_Response.protocolIEs.list;
/* static const long asn_VAL_21_SBcAP_id_Unknown_Tracking_Area_List = 22; */
ie = sbcap_as_find_ie(as_pdu, 22);
if (ie) { /* IE is optional */
OSMO_ASSERT(ie->value.present == SBcAP_Write_Replace_Warning_Response_IEs__value_PR_List_of_TAIs);
msg_peer_append_fail_sbcap_tai_list(mp, &ie->value.choice.List_of_TAIs);
}
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_MSG_E_WRITE_ACK, mp);
break;
case SMSCB_PEER_E_SBCAP_WRITE_NACK:
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_MSG_E_WRITE_NACK, mp);
break;
default:
OSMO_ASSERT(0);
}
}
static void smscb_p_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
//struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
switch (event) {
case SMSCB_PEER_E_REPLACE: /* send WRITE-REPLACE to MME */
/* NOT IMPLEMENETED */
osmo_fsm_inst_state_chg(fi, SMSCB_S_WAIT_REPLACE_ACK, 10, T_WAIT_REPLACE_ACK);
break;
case SMSCB_PEER_E_STATUS:
/* NOT IMPLEMENETED */
osmo_fsm_inst_state_chg(fi, SMSCB_S_WAIT_STATUS_ACK, 10, T_WAIT_STATUS_ACK);
break;
default:
OSMO_ASSERT(0);
}
}
static void smscb_p_fsm_wait_status_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
//struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
switch (event) {
/* NOT IMPLEMENETED */
default:
OSMO_ASSERT(0);
}
}
static void smscb_p_fsm_wait_replace_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
//struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
switch (event) {
/* NOT IMPLEMENETED */
default:
OSMO_ASSERT(0);
}
}
static void smscb_p_fsm_wait_delete_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
switch (event) {
case SMSCB_PEER_E_SBCAP_DELETE_ACK:
//pdu = data;
osmo_fsm_inst_state_chg(fi, SMSCB_S_DELETED, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_MSG_E_DELETE_ACK, mp);
break;
case SMSCB_PEER_E_SBCAP_DELETE_NACK:
//pdu = data;
osmo_fsm_inst_state_chg(fi, SMSCB_S_DELETED, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_MSG_E_DELETE_NACK, mp);
break;
default:
OSMO_ASSERT(0);
}
}
static int smscb_p_fsm_timer_cb(struct osmo_fsm_inst *fi)
{
switch (fi->T) {
case T_WAIT_WRITE_ACK:
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_MSG_E_WRITE_NACK, NULL);
break;
case T_WAIT_REPLACE_ACK:
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_MSG_E_REPLACE_NACK, NULL);
break;
case T_WAIT_STATUS_ACK:
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_MSG_E_STATUS_NACK, NULL);
break;
case T_WAIT_DELETE_ACK:
osmo_fsm_inst_state_chg(fi, SMSCB_S_DELETED, 0, 0);
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_MSG_E_DELETE_NACK, NULL);
break;
default:
OSMO_ASSERT(0);
}
return 0;
}
static void smscb_p_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
SBcAP_SBC_AP_PDU_t *sbcap;
A_SEQUENCE_OF(void) *as_pdu;
SBcAP_Write_Replace_Warning_Indication_IEs_t *ie;
int rc;
switch (event) {
case SMSCB_PEER_E_DELETE: /* send Stop-Warning to MME */
switch (fi->state) {
case SMSCB_S_DELETED:
case SMSCB_S_INIT:
LOGPFSML(fi, LOGL_ERROR, "Event %s not permitted\n",
osmo_fsm_event_name(fi->fsm, event));
return;
default:
break;
}
if ((sbcap = sbcap_gen_stop_warning_req(mp->peer, mp->cbcmsg))) {
rc = cbc_sbcap_link_tx(mp->peer->link.sbcap, sbcap);
} else {
LOGP(DSBcAP, LOGL_ERROR,
"[%s] Tx SBc-AP Stop-Warning-Request: msg gen failed\n",
mp->peer->name);
rc = -1;
}
osmo_fsm_inst_state_chg(fi, SMSCB_S_WAIT_DELETE_ACK, 10, T_WAIT_DELETE_ACK);
if (rc != 0) /* Immediately timeout the message */
fi->fsm->timer_cb(fi);
break;
case SMSCB_PEER_E_SBCAP_WRITE_IND:
sbcap = (SBcAP_SBC_AP_PDU_t *)data;
OSMO_ASSERT(sbcap->present == SBcAP_SBC_AP_PDU_PR_initiatingMessage);
OSMO_ASSERT(sbcap->choice.initiatingMessage.procedureCode == SBcAP_ProcedureId_Write_Replace_Warning_Indication);
as_pdu = (void *)&sbcap->choice.initiatingMessage.value.choice.Write_Replace_Warning_Indication.protocolIEs.list;
/* static const long asn_VAL_36_SBcAP_id_Broadcast_Scheduled_Area_List = 23; */
ie = sbcap_as_find_ie(as_pdu, 23);
if (!ie)
return; /* IE is optional */
OSMO_ASSERT(ie->value.present == SBcAP_Write_Replace_Warning_Indication_IEs__value_PR_Broadcast_Scheduled_Area_List);
msg_peer_append_compl_sbcap_bcast_area_list(mp, &ie->value.choice.Broadcast_Scheduled_Area_List);
break;
default:
OSMO_ASSERT(0);
}
}
static void smscb_p_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
llist_del(&mp->list);
/* memory of mp is child of fi and hence automatically free'd */
}
static const struct osmo_fsm_state smscb_p_fsm_states[] = {
[SMSCB_S_INIT] = {
.name = "INIT",
.in_event_mask = S(SMSCB_PEER_E_CREATE),
.out_state_mask = S(SMSCB_S_WAIT_WRITE_ACK) |
S(SMSCB_S_ACTIVE),
.action = smscb_p_fsm_init,
},
[SMSCB_S_WAIT_WRITE_ACK] = {
.name = "WAIT_WRITE_ACK",
.in_event_mask = S(SMSCB_PEER_E_SBCAP_WRITE_ACK) |
S(SMSCB_PEER_E_SBCAP_WRITE_NACK),
.out_state_mask = S(SMSCB_S_ACTIVE) |
S(SMSCB_S_WAIT_DELETE_ACK),
.action = smscb_p_fsm_wait_write_ack,
},
[SMSCB_S_ACTIVE] = {
.name = "ACTIVE",
.in_event_mask = S(SMSCB_PEER_E_REPLACE) |
S(SMSCB_PEER_E_STATUS),
.out_state_mask = S(SMSCB_S_WAIT_REPLACE_ACK) |
S(SMSCB_S_WAIT_STATUS_ACK) |
S(SMSCB_S_WAIT_DELETE_ACK),
.action = smscb_p_fsm_active,
},
[SMSCB_S_WAIT_STATUS_ACK] = {
.name = "WAIT_STATUS_ACK",
.in_event_mask = 0 /* NOT IMPLEMENTED */,
.out_state_mask = S(SMSCB_S_ACTIVE) |
S(SMSCB_S_WAIT_DELETE_ACK),
.action = smscb_p_fsm_wait_status_ack,
},
[SMSCB_S_WAIT_REPLACE_ACK] = {
.name = "WAIT_REPLACE_ACK",
.in_event_mask = 0 /* NOT IMPLEMENTED */,
.out_state_mask = S(SMSCB_S_ACTIVE) |
S(SMSCB_S_WAIT_DELETE_ACK),
.action = smscb_p_fsm_wait_replace_ack,
},
[SMSCB_S_WAIT_DELETE_ACK] = {
.name = "WAIT_DELETE_ACK",
.in_event_mask = S(SMSCB_PEER_E_SBCAP_DELETE_ACK) |
S(SMSCB_PEER_E_SBCAP_DELETE_NACK),
.out_state_mask = S(SMSCB_S_DELETED),
.action = smscb_p_fsm_wait_delete_ack,
},
[SMSCB_S_DELETED] = {
.name = "DELETED",
},
};
struct osmo_fsm sbcap_smscb_peer_fsm = {
.name = "SMSCB-PEER-SBcAP",
.states = smscb_p_fsm_states,
.num_states = ARRAY_SIZE(smscb_p_fsm_states),
.allstate_event_mask = S(SMSCB_PEER_E_DELETE) |
S(SMSCB_PEER_E_SBCAP_WRITE_IND),
.allstate_action = smscb_p_fsm_allstate,
.timer_cb = smscb_p_fsm_timer_cb,
.log_subsys = DSBcAP,
.event_names = smscb_peer_fsm_event_names,
.cleanup = smscb_p_fsm_cleanup,
};
static __attribute__((constructor)) void on_dso_load_smscb_p_fsm(void)
{
OSMO_ASSERT(osmo_fsm_register(&sbcap_smscb_peer_fsm) == 0);
}

View File

@@ -35,20 +35,38 @@
#include <osmocom/cbc/debug.h>
#include <osmocom/cbc/rest_it_op.h>
#include <osmocom/cbc/smscb_message_fsm.h>
#include <osmocom/cbc/smscb_peer_fsm.h>
#define S(x) (1 << (x))
const struct value_string smscb_message_fsm_event_names[] = {
{ SMSCB_MSG_E_CHILD_DIED, "CHILD_DIED" },
{ SMSCB_MSG_E_CREATE, "CREATE" },
{ SMSCB_MSG_E_REPLACE, "REPLACE" },
{ SMSCB_MSG_E_STATUS, "STATUS" },
{ SMSCB_MSG_E_DELETE, "DELETE" },
{ SMSCB_MSG_E_WRITE_ACK, "WRITE_ACK" },
{ SMSCB_MSG_E_WRITE_NACK, "WRITE_NACK" },
{ SMSCB_MSG_E_REPLACE_ACK, "REPLACE_ACK" },
{ SMSCB_MSG_E_REPLACE_NACK, "REPLACE_NACK" },
{ SMSCB_MSG_E_DELETE_ACK, "DELETE_ACK" },
{ SMSCB_MSG_E_DELETE_NACK, "DELETE_NACK" },
{ SMSCB_MSG_E_STATUS_ACK, "STATUS_ACK" },
{ SMSCB_MSG_E_STATUS_NACK, "STATUS_NACK" },
{ 0, NULL }
};
static void smscb_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct cbc_message *cbcmsg = fi->priv;
switch (event) {
case SMSCB_E_CREATE:
case SMSCB_MSG_E_CREATE:
OSMO_ASSERT(!cbcmsg->it_op);
cbcmsg->it_op = data;
osmo_fsm_inst_state_chg(fi, SMSCB_S_WAIT_WRITE_ACK, 15, T_WAIT_WRITE_ACK);
/* forward this event to all child FSMs (i.e. all smscb_message_peer) */
osmo_fsm_inst_broadcast_children(fi, SMSCB_E_CREATE, NULL);
osmo_fsm_inst_broadcast_children(fi, SMSCB_PEER_E_CREATE, NULL);
break;
default:
OSMO_ASSERT(0);
@@ -61,14 +79,12 @@ static void smscb_fsm_wait_write_ack(struct osmo_fsm_inst *fi, uint32_t event, v
struct osmo_fsm_inst *peer_fi;
switch (event) {
case SMSCB_E_CBSP_WRITE_ACK:
case SMSCB_E_CBSP_WRITE_NACK:
case SMSCB_E_SBCAP_WRITE_ACK:
case SMSCB_E_SBCAP_WRITE_NACK:
case SMSCB_MSG_E_WRITE_ACK:
case SMSCB_MSG_E_WRITE_NACK:
/* check if any per-peer children have not yet received the ACK or
* timed out */
llist_for_each_entry(peer_fi, &fi->proc.children, proc.child) {
if (peer_fi->state == SMSCB_S_WAIT_WRITE_ACK)
if (peer_fi->state != SMSCB_S_ACTIVE)
return;
}
rest_it_op_set_http_result(cbcmsg->it_op, 201, "Created"); // FIXME: error cases
@@ -92,26 +108,26 @@ static void smscb_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *dat
struct cbc_message *cbcmsg = fi->priv;
switch (event) {
case SMSCB_E_REPLACE:
case SMSCB_MSG_E_REPLACE:
OSMO_ASSERT(!cbcmsg->it_op);
cbcmsg->it_op = data;
osmo_fsm_inst_state_chg(fi, SMSCB_S_WAIT_REPLACE_ACK, 15, T_WAIT_REPLACE_ACK);
/* forward this event to all child FSMs (i.e. all smscb_message_peer) */
osmo_fsm_inst_broadcast_children(fi, SMSCB_E_REPLACE, data);
osmo_fsm_inst_broadcast_children(fi, SMSCB_PEER_E_REPLACE, data);
break;
case SMSCB_E_STATUS:
case SMSCB_MSG_E_STATUS:
OSMO_ASSERT(!cbcmsg->it_op);
cbcmsg->it_op = data;
osmo_fsm_inst_state_chg(fi, SMSCB_S_WAIT_STATUS_ACK, 15, T_WAIT_STATUS_ACK);
/* forward this event to all child FSMs (i.e. all smscb_message_peer) */
osmo_fsm_inst_broadcast_children(fi, SMSCB_E_STATUS, data);
osmo_fsm_inst_broadcast_children(fi, SMSCB_PEER_E_STATUS, data);
break;
case SMSCB_E_DELETE:
case SMSCB_MSG_E_DELETE:
OSMO_ASSERT(!cbcmsg->it_op);
cbcmsg->it_op = data;
osmo_fsm_inst_state_chg(fi, SMSCB_S_WAIT_DELETE_ACK, 15, T_WAIT_DELETE_ACK);
/* forward this event to all child FSMs (i.e. all smscb_message_peer) */
osmo_fsm_inst_broadcast_children(fi, SMSCB_E_DELETE, data);
osmo_fsm_inst_broadcast_children(fi, SMSCB_PEER_E_DELETE, data);
break;
default:
OSMO_ASSERT(0);
@@ -124,8 +140,8 @@ static void smscb_fsm_wait_replace_ack(struct osmo_fsm_inst *fi, uint32_t event,
struct osmo_fsm_inst *peer_fi;
switch (event) {
case SMSCB_E_CBSP_REPLACE_ACK:
case SMSCB_E_CBSP_REPLACE_NACK:
case SMSCB_MSG_E_REPLACE_ACK:
case SMSCB_MSG_E_REPLACE_NACK:
llist_for_each_entry(peer_fi, &fi->proc.children, proc.child) {
if (peer_fi->state == SMSCB_S_WAIT_REPLACE_ACK)
return;
@@ -152,8 +168,8 @@ static void smscb_fsm_wait_status_ack(struct osmo_fsm_inst *fi, uint32_t event,
struct osmo_fsm_inst *peer_fi;
switch (event) {
case SMSCB_E_CBSP_STATUS_ACK:
case SMSCB_E_CBSP_STATUS_NACK:
case SMSCB_MSG_E_STATUS_ACK:
case SMSCB_MSG_E_STATUS_NACK:
llist_for_each_entry(peer_fi, &fi->proc.children, proc.child) {
if (peer_fi->state == SMSCB_S_WAIT_STATUS_ACK)
return;
@@ -180,10 +196,8 @@ static void smscb_fsm_wait_delete_ack(struct osmo_fsm_inst *fi, uint32_t event,
struct osmo_fsm_inst *peer_fi;
switch (event) {
case SMSCB_E_CBSP_DELETE_ACK:
case SMSCB_E_CBSP_DELETE_NACK:
case SMSCB_E_SBCAP_DELETE_ACK:
case SMSCB_E_SBCAP_DELETE_NACK:
case SMSCB_MSG_E_DELETE_ACK:
case SMSCB_MSG_E_DELETE_NACK:
llist_for_each_entry(peer_fi, &fi->proc.children, proc.child) {
if (peer_fi->state != SMSCB_S_DELETED)
return;
@@ -223,25 +237,23 @@ static void smscb_fsm_deleted_onenter(struct osmo_fsm_inst *fi, uint32_t old_sta
static struct osmo_fsm_state smscb_fsm_states[] = {
[SMSCB_S_INIT] = {
.name = "INIT",
.in_event_mask = S(SMSCB_E_CREATE),
.in_event_mask = S(SMSCB_MSG_E_CREATE),
.out_state_mask = S(SMSCB_S_WAIT_WRITE_ACK),
.action = smscb_fsm_init,
},
[SMSCB_S_WAIT_WRITE_ACK] = {
.name = "WAIT_WRITE_ACK",
.in_event_mask = S(SMSCB_E_CBSP_WRITE_ACK) |
S(SMSCB_E_CBSP_WRITE_NACK) |
S(SMSCB_E_SBCAP_WRITE_ACK) |
S(SMSCB_E_SBCAP_WRITE_NACK),
.in_event_mask = S(SMSCB_MSG_E_WRITE_ACK) |
S(SMSCB_MSG_E_WRITE_NACK),
.out_state_mask = S(SMSCB_S_ACTIVE),
.action = smscb_fsm_wait_write_ack,
.onleave = smscb_fsm_wait_write_ack_onleave,
},
[SMSCB_S_ACTIVE] = {
.name = "ACTIVE",
.in_event_mask = S(SMSCB_E_REPLACE) |
S(SMSCB_E_STATUS) |
S(SMSCB_E_DELETE),
.in_event_mask = S(SMSCB_MSG_E_REPLACE) |
S(SMSCB_MSG_E_STATUS) |
S(SMSCB_MSG_E_DELETE),
.out_state_mask = S(SMSCB_S_ACTIVE) |
S(SMSCB_S_WAIT_REPLACE_ACK) |
S(SMSCB_S_WAIT_STATUS_ACK) |
@@ -250,26 +262,24 @@ static struct osmo_fsm_state smscb_fsm_states[] = {
},
[SMSCB_S_WAIT_REPLACE_ACK] = {
.name = "WAIT_REPLACE_ACK",
.in_event_mask = S(SMSCB_E_CBSP_REPLACE_ACK) |
S(SMSCB_E_CBSP_REPLACE_NACK),
.in_event_mask = S(SMSCB_MSG_E_REPLACE_ACK) |
S(SMSCB_MSG_E_REPLACE_NACK),
.out_state_mask = S(SMSCB_S_ACTIVE),
.action = smscb_fsm_wait_replace_ack,
.onleave = smscb_fsm_wait_replace_ack_onleave,
},
[SMSCB_S_WAIT_STATUS_ACK] = {
.name = "WAIT_STATUS_ACK",
.in_event_mask = S(SMSCB_E_CBSP_STATUS_ACK) |
S(SMSCB_E_CBSP_STATUS_NACK),
.in_event_mask = S(SMSCB_MSG_E_STATUS_ACK) |
S(SMSCB_MSG_E_STATUS_NACK),
.out_state_mask = S(SMSCB_S_ACTIVE),
.action = smscb_fsm_wait_status_ack,
.onleave = smscb_fsm_wait_status_ack_onleave,
},
[SMSCB_S_WAIT_DELETE_ACK] = {
.name = "WAIT_DELETE_ACK",
.in_event_mask = S(SMSCB_E_CBSP_DELETE_ACK) |
S(SMSCB_E_CBSP_DELETE_NACK) |
S(SMSCB_E_SBCAP_DELETE_ACK) |
S(SMSCB_E_SBCAP_DELETE_NACK),
.in_event_mask = S(SMSCB_MSG_E_DELETE_ACK) |
S(SMSCB_MSG_E_DELETE_NACK),
.out_state_mask = S(SMSCB_S_DELETED),
.action = smscb_fsm_wait_delete_ack,
.onleave = smscb_fsm_wait_delete_ack_onleave,
@@ -287,19 +297,19 @@ static int smscb_fsm_timer_cb(struct osmo_fsm_inst *fi)
{
switch (fi->T) {
case T_WAIT_WRITE_ACK:
/* onexit will take care of notifying the user */
/* onleave will take care of notifying the user */
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
break;
case T_WAIT_REPLACE_ACK:
/* onexit will take care of notifying the user */
/* onleave will take care of notifying the user */
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
break;
case T_WAIT_STATUS_ACK:
/* onexit will take care of notifying the user */
/* onleave will take care of notifying the user */
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
break;
case T_WAIT_DELETE_ACK:
/* onexit will take care of notifying the user */
/* onleave will take care of notifying the user */
osmo_fsm_inst_state_chg(fi, SMSCB_S_DELETED, 0, 0);
break;
default:
@@ -311,7 +321,7 @@ static int smscb_fsm_timer_cb(struct osmo_fsm_inst *fi)
static void smscb_fsm_allstate(struct osmo_fsm_inst *Fi, uint32_t event, void *data)
{
switch (event) {
case SMSCB_E_CHILD_DIED:
case SMSCB_MSG_E_CHILD_DIED:
break;
default:
OSMO_ASSERT(0);
@@ -332,11 +342,11 @@ static struct osmo_fsm smscb_fsm = {
.name = "SMSCB",
.states = smscb_fsm_states,
.num_states = ARRAY_SIZE(smscb_fsm_states),
.allstate_event_mask = S(SMSCB_E_CHILD_DIED),
.allstate_event_mask = S(SMSCB_MSG_E_CHILD_DIED),
.allstate_action = smscb_fsm_allstate,
.timer_cb = smscb_fsm_timer_cb,
.log_subsys = DCBSP,
.event_names = smscb_fsm_event_names,
.log_subsys = DSMSCB,
.event_names = smscb_message_fsm_event_names,
.cleanup= smscb_fsm_cleanup,
};
@@ -349,7 +359,7 @@ struct cbc_message *cbc_message_alloc(void *ctx, const struct cbc_message *orig_
char idbuf[32];
if (cbc_message_by_id(orig_msg->msg.message_id)) {
LOGP(DCBSP, LOGL_ERROR, "Cannot create message_id %u (already exists)\n",
LOGP(DSMSCB, LOGL_ERROR, "Cannot create message_id %u (already exists)\n",
orig_msg->msg.message_id);
return NULL;
}
@@ -357,13 +367,13 @@ struct cbc_message *cbc_message_alloc(void *ctx, const struct cbc_message *orig_
snprintf(idbuf, sizeof(idbuf), "%s-%u", orig_msg->cbe_name, orig_msg->msg.message_id);
fi = osmo_fsm_inst_alloc(&smscb_fsm, ctx, NULL, LOGL_INFO, idbuf);
if (!fi) {
LOGP(DCBSP, LOGL_ERROR, "Cannot allocate cbc_message fsm\n");
LOGP(DSMSCB, LOGL_ERROR, "Cannot allocate cbc_message fsm\n");
return NULL;
}
smscb = talloc(fi, struct cbc_message);
if (!smscb) {
LOGP(DCBSP, LOGL_ERROR, "Cannot allocate cbc_message\n");
LOGP(DSMSCB, LOGL_ERROR, "Cannot allocate cbc_message\n");
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
return NULL;
}
@@ -384,6 +394,11 @@ struct cbc_message *cbc_message_alloc(void *ctx, const struct cbc_message *orig_
return smscb;
}
void cbc_message_free(struct cbc_message *cbcmsg)
{
osmo_fsm_inst_term(cbcmsg->fi, OSMO_FSM_TERM_REGULAR, NULL);
}
__attribute__((constructor)) void smscb_fsm_constructor(void)
{
OSMO_ASSERT(osmo_fsm_register(&smscb_fsm) == 0);

View File

@@ -27,645 +27,54 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/fsm.h>
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/gsm0808_utils.h>
#include <osmocom/gsm/cbsp.h>
#include <osmocom/sbcap/sbcap_common.h>
#include <osmocom/cbc/cbc_message.h>
#include <osmocom/cbc/cbc_peer.h>
#include <osmocom/cbc/cbsp_link.h>
#include <osmocom/cbc/sbcap_link.h>
#include <osmocom/cbc/sbcap_msg.h>
#include <osmocom/cbc/debug.h>
#include <osmocom/cbc/smscb_peer_fsm.h>
#include <osmocom/cbc/smscb_message_fsm.h>
#define S(x) (1 << (x))
const struct value_string smscb_fsm_event_names[] = {
{ SMSCB_E_CHILD_DIED, "CHILD_DIED" },
{ SMSCB_E_CREATE, "CREATE" },
{ SMSCB_E_REPLACE, "REPLACE" },
{ SMSCB_E_STATUS, "STATUS" },
{ SMSCB_E_DELETE, "DELETE" },
{ SMSCB_E_CBSP_WRITE_ACK, "CBSP_WRITE_ACK" },
{ SMSCB_E_CBSP_WRITE_NACK, "CBSP_WRITE_NACK" },
{ SMSCB_E_CBSP_REPLACE_ACK, "CBSP_REPLACE_ACK" },
{ SMSCB_E_CBSP_REPLACE_NACK, "CBSP_REPLACE_NACK" },
{ SMSCB_E_CBSP_DELETE_ACK, "CBSP_DELETE_ACK" },
{ SMSCB_E_CBSP_DELETE_NACK, "CBSP_DELETE_NACK" },
{ SMSCB_E_CBSP_STATUS_ACK, "CBSP_STATUS_ACK" },
{ SMSCB_E_CBSP_STATUS_NACK, "CBSP_STATUS_NACK" },
{ SMSCB_E_SBCAP_WRITE_ACK, "SBcAP_WRITE_ACK" },
{ SMSCB_E_SBCAP_WRITE_NACK, "SBcAP_WRITE_NACK" },
{ SMSCB_E_SBCAP_DELETE_ACK, "SBcAP_DELETE_ACK" },
{ SMSCB_E_SBCAP_DELETE_NACK, "SBcAP_DELETE_NACK" },
const struct value_string smscb_peer_fsm_event_names[] = {
{ SMSCB_PEER_E_CREATE, "CREATE" },
{ SMSCB_PEER_E_REPLACE, "REPLACE" },
{ SMSCB_PEER_E_STATUS, "STATUS" },
{ SMSCB_PEER_E_DELETE, "DELETE" },
{ SMSCB_PEER_E_CBSP_WRITE_ACK, "CBSP_WRITE_ACK" },
{ SMSCB_PEER_E_CBSP_WRITE_NACK, "CBSP_WRITE_NACK" },
{ SMSCB_PEER_E_CBSP_REPLACE_ACK, "CBSP_REPLACE_ACK" },
{ SMSCB_PEER_E_CBSP_REPLACE_NACK, "CBSP_REPLACE_NACK" },
{ SMSCB_PEER_E_CBSP_DELETE_ACK, "CBSP_DELETE_ACK" },
{ SMSCB_PEER_E_CBSP_DELETE_NACK, "CBSP_DELETE_NACK" },
{ SMSCB_PEER_E_CBSP_STATUS_ACK, "CBSP_STATUS_ACK" },
{ SMSCB_PEER_E_CBSP_STATUS_NACK, "CBSP_STATUS_NACK" },
{ SMSCB_PEER_E_SBCAP_WRITE_ACK, "SBcAP_WRITE_ACK" },
{ SMSCB_PEER_E_SBCAP_WRITE_NACK, "SBcAP_WRITE_NACK" },
{ SMSCB_PEER_E_SBCAP_DELETE_ACK, "SBcAP_DELETE_ACK" },
{ SMSCB_PEER_E_SBCAP_DELETE_NACK, "SBcAP_DELETE_NACK" },
{ SMSCB_PEER_E_SBCAP_WRITE_IND, "SBcAP_WRITE_IND" },
{ 0, NULL }
};
#if 0
static const struct value_string smscb_p_fsm_timer_names[] = {
OSMO_VALUE_STRING(T_WAIT_WRITE_ACK),
OSMO_VALUE_STRING(T_WAIT_REPLACE_ACK),
OSMO_VALUE_STRING(T_WAIT_DELETE_ACK),
{ 0, NULL }
};
#endif
/***********************************************************************
* Helper functions
***********************************************************************/
/* covert TS 08.08 Cell Identity value to CBC internal type */
static enum cbc_cell_id_type cci_discr_from_cell_id(enum CELL_IDENT id_discr)
{
switch (id_discr) {
case CELL_IDENT_NO_CELL:
return CBC_CELL_ID_NONE;
case CELL_IDENT_WHOLE_GLOBAL:
return CBC_CELL_ID_CGI;
case CELL_IDENT_LAC_AND_CI:
return CBC_CELL_ID_LAC_CI;
case CELL_IDENT_CI:
return CBC_CELL_ID_CI;
case CELL_IDENT_LAI:
return CBC_CELL_ID_LAI;
case CELL_IDENT_LAC:
return CBC_CELL_ID_LAC;
case CELL_IDENT_BSS:
return CBC_CELL_ID_BSS;
default:
return -1;
}
}
/* covert CBC internal type to TS 08.08 Cell Identity */
static enum CELL_IDENT cell_id_from_ccid_discr(enum cbc_cell_id_type in)
{
switch (in) {
case CBC_CELL_ID_NONE:
return CELL_IDENT_NO_CELL;
case CBC_CELL_ID_CGI:
return CELL_IDENT_WHOLE_GLOBAL;
case CBC_CELL_ID_LAC_CI:
return CELL_IDENT_LAC_AND_CI;
case CBC_CELL_ID_CI:
return CELL_IDENT_CI;
case CBC_CELL_ID_LAI:
return CELL_IDENT_LAI;
case CBC_CELL_ID_LAC:
return CELL_IDENT_LAC;
case CBC_CELL_ID_BSS:
return CELL_IDENT_BSS;
default:
return -1;
}
}
/* convert TS 08.08 Cell Identifier Union to CBC internal type */
static void cci_from_cbsp(struct cbc_cell_id *cci, enum CELL_IDENT id_discr,
const union gsm0808_cell_id_u *u)
{
cci->id_discr = cci_discr_from_cell_id(id_discr);
switch (id_discr) {
case CELL_IDENT_NO_CELL:
break;
case CELL_IDENT_WHOLE_GLOBAL:
cci->u.cgi = u->global;
break;
case CELL_IDENT_LAC_AND_CI:
cci->u.lac_and_ci = u->lac_and_ci;
break;
case CELL_IDENT_CI:
cci->u.ci = u->ci;
break;
case CELL_IDENT_LAI:
cci->u.lai = u->lai_and_lac;
break;
case CELL_IDENT_LAC:
cci->u.lac = u->lac;
break;
case CELL_IDENT_BSS:
break;
default:
break;
}
}
/* convert TS 08.08 Cell Identifier Union to CBC internal type */
static void cbsp_from_cci(union gsm0808_cell_id_u *u, const struct cbc_cell_id *cci)
{
switch (cci->id_discr) {
case CBC_CELL_ID_NONE:
break;
case CBC_CELL_ID_CGI:
u->global = cci->u.cgi;
printf("u->gobal: %s\n", osmo_hexdump((uint8_t *) &u->global, sizeof(u->global)));
break;
case CBC_CELL_ID_LAC_CI:
u->lac_and_ci = cci->u.lac_and_ci;
break;
case CBC_CELL_ID_CI:
u->ci = cci->u.ci;
break;
case CBC_CELL_ID_LAI:
u->lai_and_lac = cci->u.lai;
break;
case CBC_CELL_ID_LAC:
u->lac = cci->u.lac;
break;
case CBC_CELL_ID_BSS:
break;
default:
OSMO_ASSERT(0);
}
}
/* read a single osmo_cbsp_num_compl_ent and add it to cbc_message_peer */
static void cci_from_cbsp_compl_ent(struct cbc_message_peer *mp,
struct osmo_cbsp_num_compl_ent *ce, enum CELL_IDENT id_discr)
{
struct cbc_cell_id *cci;
cci = NULL; // FIXME: lookup
if (!cci) {
cci = talloc_zero(mp, struct cbc_cell_id);
if (!cci)
return;
llist_add_tail(&cci->list, &mp->num_compl_list);
}
cci_from_cbsp(cci, id_discr, &ce->cell_id);
cci->num_compl.num_compl += ce->num_compl;
cci->num_compl.num_bcast_info += ce->num_bcast_info;
}
static void msg_peer_append_cbsp_compl(struct cbc_message_peer *mp,
struct osmo_cbsp_num_compl_list *nclist)
{
struct osmo_cbsp_num_compl_ent *ce;
llist_for_each_entry(ce, &nclist->list, list)
cci_from_cbsp_compl_ent(mp, ce, nclist->id_discr);
}
/* read a single osmo_cbsp_cell_ent and add it to cbc_message_peer */
static void cci_from_cbsp_cell_ent(struct cbc_message_peer *mp,
struct osmo_cbsp_cell_ent *ce, enum CELL_IDENT id_discr)
{
struct cbc_cell_id *cci;
cci = NULL; // FIXME: lookup
if (!cci) {
cci = talloc_zero(mp, struct cbc_cell_id);
if (!cci)
return;
llist_add_tail(&cci->list, &mp->cell_list);
}
cci_from_cbsp(cci, id_discr, &ce->cell_id);
}
static void msg_peer_append_cbsp_cell(struct cbc_message_peer *mp,
struct osmo_cbsp_cell_list *clist)
{
struct osmo_cbsp_cell_ent *ce;
llist_for_each_entry(ce, &clist->list, list)
cci_from_cbsp_cell_ent(mp, ce, clist->id_discr);
}
/* read a single osmo_cbsp_fail_ent and add it to cbc_message_peer */
static void cci_from_cbsp_fail_ent(struct cbc_message_peer *mp,
struct osmo_cbsp_fail_ent *fe)
{
struct cbc_cell_id *cci;
cci = NULL; // lookup */
if (!cci) {
cci = talloc_zero(mp, struct cbc_cell_id);
if (!cci)
return;
llist_add_tail(&cci->list, &mp->fail_list);
}
cci->id_discr = cci_discr_from_cell_id(fe->id_discr);
cci->fail.cause = fe->cause;
}
static void msg_peer_append_cbsp_fail(struct cbc_message_peer *mp, struct llist_head *flist)
{
struct osmo_cbsp_fail_ent *fe;
llist_for_each_entry(fe, flist, list)
cci_from_cbsp_fail_ent(mp, fe);
}
/* append all cells from cbc_message_peer to given CBSP cell_list */
static void cbsp_append_cell_list(struct osmo_cbsp_cell_list *out, void *ctx,
const struct cbc_message_peer *mp)
{
struct cbc_cell_id *cci;
enum cbc_cell_id_type id_discr = CBC_CELL_ID_NONE;
llist_for_each_entry(cci, &mp->cell_list, list) {
struct osmo_cbsp_cell_ent *ent;
if (id_discr == CBC_CELL_ID_NONE)
id_discr = cci->id_discr;
else if (id_discr != cci->id_discr) {
LOGPFSML(mp->fi, LOGL_ERROR, "Cannot encode CBSP cell_list as not all "
"entries are of same type (%u != %u)\n", id_discr, cci->id_discr);
continue;
}
ent = talloc_zero(ctx, struct osmo_cbsp_cell_ent);
OSMO_ASSERT(ent);
cbsp_from_cci(&ent->cell_id, cci);
llist_add_tail(&ent->list, &out->list);
}
out->id_discr = cell_id_from_ccid_discr(id_discr);
}
/***********************************************************************
* actual FSM
***********************************************************************/
static void smscb_p_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
int rc;
switch (event) {
case SMSCB_E_CREATE:
/* send it to peer */
rc = peer_new_cbc_message(mp->peer, mp->cbcmsg);
if (rc == 0) {
/* wait for peers' response */
osmo_fsm_inst_state_chg(fi, SMSCB_S_WAIT_WRITE_ACK, 10,
T_WAIT_WRITE_ACK);
}
break;
default:
OSMO_ASSERT(0);
}
}
static void smscb_p_fsm_wait_write_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
struct osmo_cbsp_decoded *dec = NULL;
//SBcAP_SBC_AP_PDU_t *pdu = NULL;
switch (event) {
case SMSCB_E_CBSP_WRITE_ACK:
dec = data;
msg_peer_append_cbsp_compl(mp, &dec->u.write_replace_compl.num_compl_list);
msg_peer_append_cbsp_cell(mp, &dec->u.write_replace_compl.cell_list);
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_CBSP_WRITE_ACK, mp);
break;
case SMSCB_E_CBSP_WRITE_NACK:
dec = data;
msg_peer_append_cbsp_compl(mp, &dec->u.write_replace_fail.num_compl_list);
msg_peer_append_cbsp_cell(mp, &dec->u.write_replace_fail.cell_list);
msg_peer_append_cbsp_fail(mp, &dec->u.write_replace_fail.fail_list);
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_CBSP_WRITE_NACK, mp);
break;
case SMSCB_E_SBCAP_WRITE_ACK:
//pdu = data;
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_SBCAP_WRITE_ACK, mp);
break;
case SMSCB_E_SBCAP_WRITE_NACK:
//pdu = data;
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_SBCAP_WRITE_NACK, mp);
break;
default:
OSMO_ASSERT(0);
}
}
static void smscb_p_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
struct osmo_cbsp_decoded *cbsp;
switch (event) {
case SMSCB_E_REPLACE: /* send WRITE-REPLACE to BSC */
cbsp = osmo_cbsp_decoded_alloc(mp->peer, CBSP_MSGT_WRITE_REPLACE);
OSMO_ASSERT(cbsp);
cbsp->u.write_replace.msg_id = mp->cbcmsg->msg.message_id;
cbsp->u.write_replace.old_serial_nr = &mp->cbcmsg->msg.serial_nr;
//cbsp->u.write_replace.new_serial_nr
/* TODO: we assume that the replace will always affect all original cells */
cbsp_append_cell_list(&cbsp->u.write_replace.cell_list, cbsp, mp);
// TODO: ALL OTHER DATA
cbc_cbsp_link_tx(mp->peer->link.cbsp, cbsp);
osmo_fsm_inst_state_chg(fi, SMSCB_S_WAIT_REPLACE_ACK, 10, T_WAIT_REPLACE_ACK);
break;
case SMSCB_E_STATUS: /* send MSG-STATUS-QUERY to BSC */
cbsp = osmo_cbsp_decoded_alloc(mp->peer, CBSP_MSGT_MSG_STATUS_QUERY);
OSMO_ASSERT(cbsp);
cbsp->u.msg_status_query.msg_id = mp->cbcmsg->msg.message_id;
cbsp->u.msg_status_query.old_serial_nr = mp->cbcmsg->msg.serial_nr;
cbsp_append_cell_list(&cbsp->u.msg_status_query.cell_list, cbsp, mp);
cbsp->u.msg_status_query.channel_ind = CBSP_CHAN_IND_BASIC;
cbc_cbsp_link_tx(mp->peer->link.cbsp, cbsp);
osmo_fsm_inst_state_chg(fi, SMSCB_S_WAIT_STATUS_ACK, 10, T_WAIT_STATUS_ACK);
break;
default:
OSMO_ASSERT(0);
}
}
static void smscb_p_fsm_wait_status_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
struct osmo_cbsp_decoded *dec = NULL;
switch (event) {
case SMSCB_E_CBSP_STATUS_ACK:
dec = data;
msg_peer_append_cbsp_compl(mp, &dec->u.msg_status_query_compl.num_compl_list);
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_CBSP_STATUS_ACK, mp);
break;
case SMSCB_E_CBSP_STATUS_NACK:
dec = data;
msg_peer_append_cbsp_compl(mp, &dec->u.msg_status_query_fail.num_compl_list);
msg_peer_append_cbsp_fail(mp, &dec->u.msg_status_query_fail.fail_list);
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_CBSP_STATUS_NACK, mp);
break;
default:
OSMO_ASSERT(0);
}
}
static void smscb_p_fsm_wait_replace_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
struct osmo_cbsp_decoded *dec = NULL;
switch (event) {
case SMSCB_E_CBSP_REPLACE_ACK:
dec = data;
msg_peer_append_cbsp_compl(mp, &dec->u.write_replace_compl.num_compl_list);
msg_peer_append_cbsp_cell(mp, &dec->u.write_replace_compl.cell_list);
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_CBSP_REPLACE_ACK, mp);
break;
case SMSCB_E_CBSP_REPLACE_NACK:
dec = data;
msg_peer_append_cbsp_compl(mp, &dec->u.write_replace_fail.num_compl_list);
msg_peer_append_cbsp_cell(mp, &dec->u.write_replace_fail.cell_list);
msg_peer_append_cbsp_fail(mp, &dec->u.write_replace_fail.fail_list);
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_CBSP_REPLACE_NACK, mp);
break;
default:
OSMO_ASSERT(0);
}
}
static void smscb_p_fsm_wait_delete_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
struct osmo_cbsp_decoded *dec = NULL;
//SBcAP_SBC_AP_PDU_t *pdu = NULL;
switch (event) {
case SMSCB_E_CBSP_DELETE_ACK:
dec = data;
/* append results */
msg_peer_append_cbsp_compl(mp, &dec->u.kill_compl.num_compl_list);
msg_peer_append_cbsp_cell(mp, &dec->u.kill_compl.cell_list);
osmo_fsm_inst_state_chg(fi, SMSCB_S_DELETED, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_CBSP_DELETE_ACK, mp);
break;
case SMSCB_E_CBSP_DELETE_NACK:
dec = data;
/* append results */
msg_peer_append_cbsp_compl(mp, &dec->u.kill_fail.num_compl_list);
msg_peer_append_cbsp_cell(mp, &dec->u.kill_fail.cell_list);
msg_peer_append_cbsp_fail(mp, &dec->u.kill_fail.fail_list);
osmo_fsm_inst_state_chg(fi, SMSCB_S_DELETED, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_CBSP_DELETE_NACK, mp);
break;
case SMSCB_E_SBCAP_DELETE_ACK:
//pdu = data;
osmo_fsm_inst_state_chg(fi, SMSCB_S_DELETED, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_SBCAP_DELETE_ACK, mp);
break;
case SMSCB_E_SBCAP_DELETE_NACK:
//pdu = data;
osmo_fsm_inst_state_chg(fi, SMSCB_S_DELETED, 0, 0);
/* Signal parent fsm about completion */
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_SBCAP_DELETE_NACK, mp);
break;
default:
OSMO_ASSERT(0);
}
}
static int smscb_p_fsm_timer_cb(struct osmo_fsm_inst *fi)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *)fi->priv;
int ev;
switch (fi->T) {
case T_WAIT_WRITE_ACK:
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
switch (mp->peer->proto) {
case CBC_PEER_PROTO_CBSP:
ev = SMSCB_E_CBSP_WRITE_NACK;
break;
case CBC_PEER_PROTO_SBcAP:
ev = SMSCB_E_SBCAP_WRITE_NACK;
break;
default:
OSMO_ASSERT(0);
}
osmo_fsm_inst_dispatch(fi->proc.parent, ev, NULL);
break;
case T_WAIT_REPLACE_ACK:
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_CBSP_REPLACE_NACK, NULL);
break;
case T_WAIT_STATUS_ACK:
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_CBSP_STATUS_NACK, NULL);
break;
case T_WAIT_DELETE_ACK:
osmo_fsm_inst_state_chg(fi, SMSCB_S_DELETED, 0, 0);
switch (mp->peer->proto) {
case CBC_PEER_PROTO_CBSP:
ev = SMSCB_E_CBSP_DELETE_NACK;
break;
case CBC_PEER_PROTO_SBcAP:
ev = SMSCB_E_SBCAP_DELETE_NACK;
break;
default:
OSMO_ASSERT(0);
}
osmo_fsm_inst_dispatch(fi->proc.parent, ev, NULL);
break;
default:
OSMO_ASSERT(0);
}
return 0;
}
static void smscb_p_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
struct osmo_cbsp_decoded *cbsp;
SBcAP_SBC_AP_PDU_t *sbcap;
switch (event) {
case SMSCB_E_DELETE: /* send KILL to BSC */
switch (fi->state) {
case SMSCB_S_DELETED:
case SMSCB_S_INIT:
LOGPFSML(fi, LOGL_ERROR, "Event %s not permitted\n",
osmo_fsm_event_name(fi->fsm, event));
return;
default:
break;
}
switch (mp->peer->proto) {
case CBC_PEER_PROTO_CBSP:
cbsp = osmo_cbsp_decoded_alloc(mp->peer, CBSP_MSGT_KILL);
OSMO_ASSERT(cbsp);
cbsp->u.kill.msg_id = mp->cbcmsg->msg.message_id;
cbsp->u.kill.old_serial_nr = mp->cbcmsg->msg.serial_nr;
/* TODO: we assume that the delete will always affect all original cells */
cbsp_append_cell_list(&cbsp->u.kill.cell_list, cbsp, mp);
if (!mp->cbcmsg->msg.is_etws) {
/* Channel Indication IE is only present in CBS, not in ETWS! */
cbsp->u.kill.channel_ind = talloc_zero(cbsp, enum cbsp_channel_ind);
OSMO_ASSERT(cbsp->u.kill.channel_ind);
*(cbsp->u.kill.channel_ind) = CBSP_CHAN_IND_BASIC;
}
cbc_cbsp_link_tx(mp->peer->link.cbsp, cbsp);
break;
case CBC_PEER_PROTO_SBcAP:
if ((sbcap = sbcap_gen_stop_warning_req(mp->peer, mp->cbcmsg))) {
cbc_sbcap_link_tx(mp->peer->link.sbcap, sbcap);
} else {
LOGP(DSBcAP, LOGL_ERROR,
"[%s] Tx SBc-AP Stop-Warning-Request: msg gen failed\n",
mp->peer->name);
}
break;
case CBC_PEER_PROTO_SABP:
default:
osmo_panic("SMSCB_E_DELETE not implemented for proto %u", mp->peer->proto);
}
osmo_fsm_inst_state_chg(fi, SMSCB_S_WAIT_DELETE_ACK, 10, T_WAIT_DELETE_ACK);
break;
default:
OSMO_ASSERT(0);
}
}
static void smscb_p_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
llist_del(&mp->list);
/* memory of mp is child of fi and hence automatically free'd */
}
static const struct osmo_fsm_state smscb_p_fsm_states[] = {
[SMSCB_S_INIT] = {
.name = "INIT",
.in_event_mask = S(SMSCB_E_CREATE),
.out_state_mask = S(SMSCB_S_WAIT_WRITE_ACK),
.action = smscb_p_fsm_init,
},
[SMSCB_S_WAIT_WRITE_ACK] = {
.name = "WAIT_WRITE_ACK",
.in_event_mask = S(SMSCB_E_CBSP_WRITE_ACK) |
S(SMSCB_E_CBSP_WRITE_NACK) |
S(SMSCB_E_SBCAP_WRITE_ACK) |
S(SMSCB_E_SBCAP_WRITE_NACK),
.out_state_mask = S(SMSCB_S_ACTIVE) |
S(SMSCB_S_WAIT_DELETE_ACK),
.action = smscb_p_fsm_wait_write_ack,
},
[SMSCB_S_ACTIVE] = {
.name = "ACTIVE",
.in_event_mask = S(SMSCB_E_REPLACE) |
S(SMSCB_E_STATUS),
.out_state_mask = S(SMSCB_S_WAIT_REPLACE_ACK) |
S(SMSCB_S_WAIT_STATUS_ACK) |
S(SMSCB_S_WAIT_DELETE_ACK),
.action = smscb_p_fsm_active,
},
[SMSCB_S_WAIT_STATUS_ACK] = {
.name = "WAIT_STATUS_ACK",
.in_event_mask = S(SMSCB_E_CBSP_STATUS_ACK) |
S(SMSCB_E_CBSP_STATUS_NACK),
.out_state_mask = S(SMSCB_S_ACTIVE) |
S(SMSCB_S_WAIT_DELETE_ACK),
.action = smscb_p_fsm_wait_status_ack,
},
[SMSCB_S_WAIT_REPLACE_ACK] = {
.name = "WAIT_REPLACE_ACK",
.in_event_mask = S(SMSCB_E_CBSP_REPLACE_ACK) |
S(SMSCB_E_CBSP_REPLACE_NACK),
.out_state_mask = S(SMSCB_S_ACTIVE) |
S(SMSCB_S_WAIT_DELETE_ACK),
.action = smscb_p_fsm_wait_replace_ack,
},
[SMSCB_S_WAIT_DELETE_ACK] = {
.name = "WAIT_DELETE_ACK",
.in_event_mask = S(SMSCB_E_CBSP_DELETE_ACK) |
S(SMSCB_E_CBSP_DELETE_NACK) |
S(SMSCB_E_SBCAP_DELETE_ACK) |
S(SMSCB_E_SBCAP_DELETE_NACK),
.out_state_mask = S(SMSCB_S_DELETED),
.action = smscb_p_fsm_wait_delete_ack,
},
[SMSCB_S_DELETED] = {
.name = "DELETED",
},
};
struct osmo_fsm smscb_p_fsm = {
.name = "SMSCB-PEER",
.states = smscb_p_fsm_states,
.num_states = ARRAY_SIZE(smscb_p_fsm_states),
.allstate_event_mask = S(SMSCB_E_DELETE),
.allstate_action = smscb_p_fsm_allstate,
.timer_cb = smscb_p_fsm_timer_cb,
.log_subsys = DCBSP,
.event_names = smscb_fsm_event_names,
.cleanup = smscb_p_fsm_cleanup,
};
static __attribute__((constructor)) void on_dso_load_smscb_p_fsm(void)
{
OSMO_ASSERT(osmo_fsm_register(&smscb_p_fsm) == 0);
}
struct cbc_message_peer *smscb_peer_fsm_alloc(struct cbc_peer *peer, struct cbc_message *cbcmsg)
{
struct cbc_message_peer *mp;
struct osmo_fsm_inst *fi;
struct osmo_fsm *fsm_def;
fi = osmo_fsm_inst_alloc_child(&smscb_p_fsm, cbcmsg->fi, SMSCB_E_CHILD_DIED);
switch (peer->proto) {
case CBC_PEER_PROTO_CBSP:
fsm_def = &cbsp_smscb_peer_fsm;
break;
case CBC_PEER_PROTO_SBcAP:
fsm_def = &sbcap_smscb_peer_fsm;
break;
case CBC_PEER_PROTO_SABP:
default:
osmo_panic("smscb_peer FSM not implemented for proto %u", peer->proto);
}
fi = osmo_fsm_inst_alloc_child(fsm_def, cbcmsg->fi, SMSCB_MSG_E_CHILD_DIED);
if (!fi)
return NULL;
/* include the peer name in the ID of the child FSM */

View File

@@ -14,7 +14,7 @@ void test_asn1c_enc(void)
struct msgb *msg;
SBcAP_SBC_AP_PDU_t *pdu;
SBcAP_Write_Replace_Warning_Request_IEs_t *ie;
uint16_t ie_warning_type = 0x01;
uint8_t ie_warning_type[2] = {(0x01 << 1) | 0x01, 0x80};
uint8_t ie_dcs = 2;
uint8_t ie_warning_sec_info[50] = {0x30, 0x40, 0x12, 0x23, 0x45};
uint8_t ie_warning_msg_content[SBCAP_WARN_MSG_CONTENTS_IE_MAX_LEN] = {0x30, 0x40, 0x12, 0x23, 0x45};
@@ -68,8 +68,6 @@ void test_asn1c_enc(void)
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Warning_Type);
ie->value.choice.Warning_Type.buf = MALLOC(sizeof(ie_warning_type));
ie->value.choice.Warning_Type.size = sizeof(ie_warning_type);
ie_warning_type |= 0x0100;
ie_warning_type |= 0x0080;
memcpy(ie->value.choice.Warning_Type.buf, &ie_warning_type, sizeof(ie_warning_type));
ASN_SEQUENCE_ADD(as_pdu, ie);
@@ -204,8 +202,8 @@ SBc Application Part
static const struct log_info_cat log_categories[] = {
[0] = {
.name = "DSBcAP",
.description = "SBc Application Part (CBC-MME)",
.name = "DMAIN",
.description = "main category",
.color = "\033[1;32m",
.enabled = 1,
.loglevel = LOGL_DEBUG,
@@ -222,7 +220,7 @@ int main(int argc, char **argv)
void *ctx = talloc_named_const(NULL, 0, "mgcp_test");
void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0);
osmo_init_logging2(ctx, &log_info);
sbcap_set_log_area(0);
sbcap_set_log_area(0, 0);
log_set_log_level(osmo_stderr_target, LOGL_DEBUG);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 0);

View File

@@ -1,5 +1,5 @@
==== test_asn1c_enc ====
Encoded message: 00 00 00 76 00 00 08 00 05 00 02 ab 01 00 0b 00 02 ab cd 00 0a 00 02 00 1e 00 07 00 02 00 59 00 12 40 02 81 01 00 11 40 32 30 40 12 23 45 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 40 01 02 00 10 40 16 00 13 30 40 12 23 45 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Encoded message: 00 00 00 76 00 00 08 00 05 00 02 ab 01 00 0b 00 02 ab cd 00 0a 00 02 00 1e 00 07 00 02 00 59 00 12 40 02 03 80 00 11 40 32 30 40 12 23 45 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 40 01 02 00 10 40 16 00 13 30 40 12 23 45 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
==== test_asn1c_dec ====
Decoding message: 20 00 00 14 00 00 03 00 05 00 02 00 2b 00 0b 00 02 41 70 00 01 00 01 00
Decoded message successfully