dgsm Proxy Cache design WIP
Change-Id: Ifa322e84fadd3b04943c8c7024c0e2de4935bed0
This commit is contained in:
parent
b3c1726fb9
commit
1971b6731a
|
@ -186,6 +186,7 @@ AC_OUTPUT(
|
|||
Makefile
|
||||
doc/Makefile
|
||||
doc/examples/Makefile
|
||||
doc/sequence_charts/Makefile
|
||||
src/Makefile
|
||||
src/gsupclient/Makefile
|
||||
src/mslookup/Makefile
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
SUBDIRS = \
|
||||
examples \
|
||||
manuals \
|
||||
sequence_charts \
|
||||
$(NULL)
|
||||
|
|
|
@ -4,13 +4,18 @@ EXTRA_DIST = example_subscriber_add_update_delete.vty \
|
|||
osmohlr-usermanual.adoc \
|
||||
osmohlr-usermanual-docinfo.xml \
|
||||
osmohlr-vty-reference.xml \
|
||||
chapters/proxy_cache_attach.msc \
|
||||
chapters/proxy_cache_more_tuples.msc \
|
||||
chapters/proxy_cache_periodic_lu.msc \
|
||||
chapters/proxy_cache_tuple_cache_dry.msc \
|
||||
chapters/proxy_cache_umts_aka_resync.msc \
|
||||
regen_doc.sh \
|
||||
chapters \
|
||||
vty
|
||||
|
||||
if BUILD_MANUALS
|
||||
ASCIIDOC = osmohlr-usermanual.adoc
|
||||
ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc $(srcdir)/*.vty $(srcdir)/*.ctrl
|
||||
ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc $(srcdir)/*.vty $(srcdir)/*.ctrl $(srcdir)/chapters/*.msc
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
|
||||
|
||||
VTY_REFERENCE = osmohlr-vty-reference.xml
|
||||
|
|
|
@ -0,0 +1,333 @@
|
|||
== Distributed GSM / GSUP Proxy Cache: Remedy Temporary Link Failure to Home HLR
|
||||
|
||||
The aim of the Proxy Cache is to still provide service to roaming subscribers even if the GSUP link to the home HLR is
|
||||
temporarily down or unresponsive.
|
||||
|
||||
If a subscriber from a remote site is currently roaming at this local site, and the link to the subscriber's home HLR
|
||||
has succeeded before, the GSUP proxy cache can try to bridge the time of temporary link failure to that home HLR.
|
||||
|
||||
Tasks to take over from an unreachable home HLR:
|
||||
|
||||
- Cache and send auth tuples on Send Auth Info Request.
|
||||
- Acknowledge periodic Location Updating.
|
||||
- ...?
|
||||
|
||||
=== Design Considerations
|
||||
|
||||
==== Authentication
|
||||
|
||||
The most critical role of the home HLR is providing the Authentication and Key Agreement (AKA) tuples. If the home HLR
|
||||
is not reachable, the lack of fresh authentication challenges would normally cause the subscriber to be rejected. To
|
||||
avoid that, a proxying HLR needs to be able to provide AKA tuples on behalf of the home HLR.
|
||||
|
||||
In short, the strategy of the D-GSM proxy cache is:
|
||||
|
||||
- Try to keep a certain number of unused full UMTS AKA tuples in the proxy cache at all times.
|
||||
- When the MSC requests more tuples, dispense some from the cache, and fill it back up later on, as soon as a good link
|
||||
is available.
|
||||
- When the tuple cache in the proxy HLR runs dry, 3G RAN becomes unusable. But 2G RAN may fall back to GSM AKA, if the
|
||||
proxy HLR configuration permits it: resend previously used GSM AKA auth tuples to the MSC, omitting UMTS AKA items
|
||||
from the Send Auth Info Result, to force the MSC to send a GSM AKA challenge on 2G.
|
||||
|
||||
The remaining part of this section provides detailed reasoning for this strategy.
|
||||
|
||||
The aim is to attach a subscriber without immediate access to the authentication key data.
|
||||
|
||||
Completely switching off authentication would be an option on GERAN (2G), but this would mean complete lack of
|
||||
encryption on the air interface, and is not recommended. On 3G and later, authentication is always mandatory.
|
||||
|
||||
The key data is known only to the USIM and the home HLR. The HLR generates distinct authentication tuples, each
|
||||
containing a cryptographic challenge (RAND, AUTN) and its expected response (SRES, XRES). The MSC consumes one tuple
|
||||
per authentication: it sends the challenge to the subscriber, and compares the response received.
|
||||
|
||||
The proxy could generate fresh tuples if the cryptographic key data (Ki,K,OP/OPC) from the home HLR was shared with the
|
||||
proxy HLR. Distributed GSM does not do this, because:
|
||||
|
||||
- The key data is cryptographically very valuable. If it were leaked, any and all authentication challenges would be
|
||||
fully compromised.
|
||||
|
||||
- In D-GSM, each home site shall retain exclusive authority over the user data. It should not be necessary to share the
|
||||
secret keys with any remote site.
|
||||
|
||||
So, how about resending already used auth tuples to the MSC when no fresh ones are available? Resending identical
|
||||
authentication challenges makes the system vulnerable to relatively trivial replay-attacks, but this may be an
|
||||
acceptable fallback in situations of failing links, if it means being able to provide reliable roaming.
|
||||
|
||||
But, even if a proxying HLR is willing to compromise cryptographic security to improve service, this can only work with
|
||||
GSM AKA:
|
||||
|
||||
- In GSM AKA (so-called 2G auth), tuples may be re-used any amount of times without a strict need to generate more
|
||||
authentication challenges. The SIM will merely calculate the (same) SRES response again, and authentication will
|
||||
succeed. It is bad security to do so, but it is a choice the core network is free to make.
|
||||
|
||||
- UMTS AKA (Milenage or so-called 3G auth, but also used on 2G GERAN) adds mutual authentication, i.e. the core network
|
||||
must prove that it is authentic. Specifically to thwart replay-attacks that would spoof a core network, UMTS AKA
|
||||
contains an ongoing sequence number (SQN) that is woven into the authentication challenge. An SQN may skip forward by
|
||||
a certain number of counts, but it can never move backwards. If a USIM detects a stale SQN, it will request an
|
||||
authentication re-synchronisation (by passing AUTS in an Authentication Failure message), after which a freshly
|
||||
generated UMTS AKA challenge is strictly required -- not possible with an unresponsive home HLR.
|
||||
|
||||
Post-R99 (1999) 2G GERAN networks are capable of UMTS AKA, so, not only 3G, but also the vast majority of 2G networks
|
||||
today use UMTS AKA -- and so does Osmocom, typically. Hence it is desirable to fully support UMTS AKA in D-GSM.
|
||||
|
||||
[options="header"]
|
||||
|===
|
||||
| RAN | authentication is... 2+| available AKA types
|
||||
| GERAN (2G) | optional | GSM AKA | UMTS AKA
|
||||
| UTRAN (3G) | mandatory | - | UMTS AKA
|
||||
|===
|
||||
|
||||
UMTS AKA will not allow re-sending previously used authentication tuples. But a UMTS capable SIM will fall back to GSM
|
||||
AKA if the network sent only a GSM AKA challenge. If the proxy HLR sends only GSM AKA tuples, then the MSC will request
|
||||
GSM authentication, and re-sending old tuples is again possible. However, a USIM will only fall back to GSM AKA if the
|
||||
phone is attaching on a 2G network. For 3G RAN and later, UMTS AKA is mandatory. So, as soon as a site uses 3G or newer
|
||||
RAN technology, there is simply no way to resend previously used authentication tuples.
|
||||
|
||||
The only way to have unused UMTS AKA tuples in the proxy HLR is to already have them stored from an earlier time. The
|
||||
idea is to request more auth tuples in advance whenever the link is available, and cache them in the proxy. When the MSC
|
||||
uses up some tuples from the proxy HLR, the proxy cache can fill up again in its own time, by requesting more tuples
|
||||
from the home HLR at a time of good link. Then, the next time the subscriber needs immediate action, it does not matter
|
||||
whether the home HLR is directly reachable or not.
|
||||
|
||||
In an aside, since OsmoMSC already caches a number of authentication tuples, one option would be to implement this in
|
||||
OsmoMSC, and not in the proxy HLR: the MSC could request new tuples long before its tuple cache runs dry. However, the
|
||||
OsmoMSC VLR state is volatile, and a power cycle of the system would lose the tuple cache; if the home HLR is
|
||||
unreachable at the same time of the power cycle, roaming service would be interrupted. The proxy cache in the HLR is
|
||||
persistent, so roaming can continue immediately after a power cycle, even if the home HLR link is down.
|
||||
|
||||
==== Location Updating
|
||||
|
||||
Any attached subscriber periodically repeats a Location Updating procedure, typically every 15 minutes. If a home HLR is
|
||||
unreachable at the time of the periodic Location Updating, a roaming subscriber would assume that it is detached from
|
||||
the network, even though the local site it is roaming at is still fully operational.
|
||||
|
||||
The aim of D-GSM is to keep subscribers attached even if the remote home HLR is temporarily unreachable. The simplest
|
||||
way to achieve that is by directly responding with a Update Location Result to the MSC.
|
||||
|
||||
In addition to accepting an Update Location, a proxy HLR should also start an Insert Subscriber Data procedure, as a
|
||||
home HLR would do. For a periodic Location Updating, the MSC should already know all of the information that an Insert
|
||||
Subscriber Data would convey (i.e. the MSISDN), and there would be no strict need to resend this data. But if a
|
||||
subscriber quickly detaches and re-attaches (e.g. the device rebooted), the MSC has discarded the subscriber info from
|
||||
the VLR, and hence the proxy HLR should also always perform an Insert Subscriber Data. (On the GSUP wire, a periodic LU
|
||||
is indistinguishable from an IMSI-Attach LU.)
|
||||
|
||||
Furthermore, the longer the proxy HLR's cache keeps a roaming subscriber's data after an IMSI Detach, the longer it is
|
||||
possible for the subscriber to immediately re-attach despite the home HLR being temporarily unreachable.
|
||||
|
||||
If a subscriber has carried out a GSUP Update Location with the proxy HLR while the home HLR was unreachable, it is not
|
||||
strictly necessary to repeat that Update Location message to the home HLR later. The home HLR does keep a timestamped
|
||||
record of an Update Location from a proxy HLR if seen, but that has no visible effect on serving the subscriber:
|
||||
|
||||
- If the home HLR still thinks that the subscriber is currently attached at the home site, it will respond to mslookup
|
||||
requests. But the actual site the subscriber is roaming at will have a younger age, and its mslookup responses will
|
||||
win.
|
||||
|
||||
- If the home HLR has no record of the subscriber being attached recently, or has a record of being attached at another
|
||||
remote site, it does not respond to mslookup requests for that subscriber. If it records the new proxy LU, it still
|
||||
does not respond to mslookup requests since the subscriber is attached remotely, i.e. there is no difference.
|
||||
|
||||
It is thinkable to always handle an Update Location in the proxy HLR, and never even attempt to involve the home HLR in
|
||||
case the proxy cache already has data for a given subscriber, but then the proxy HLR would never notice a changed MSISDN
|
||||
or authorization status for this subscriber. It is best practice to involve the home HLR whenever possible.
|
||||
|
||||
==== IMSI Detach
|
||||
|
||||
If a GSUP client reports a detaching IMSI when the home HLR is not reachable, simply respond with an ack.
|
||||
|
||||
It is not required to signal the home HLR with a detach once the link is back up. A home HLR anyway flags a remotely
|
||||
roaming subscriber as attached-at-a-proxy, and there is literally no difference between telling a home HLR about a
|
||||
detach or not.
|
||||
|
||||
(TODO: is there even a GSUP message that a VLR should send on IMSI Detach? see OS#4374)
|
||||
|
||||
[[proxy_cache_umts_aka_resync]]
|
||||
==== UMTS AKA Resync
|
||||
|
||||
When the SQN between USIM and AUC (subscriber and home HLR) have diverged, the Send Authentication Info Request from the
|
||||
MSC contains an AUTS IE. This means that a resynchronization between USIM and AUC (the home HLR) is necessary. All of
|
||||
the UMTS AKA tuples in the proxy cache are now unusable, and the home HLR must respond with fresh tuples after doing a
|
||||
resync. This also means that either the home HLR must be reachable immediately, or GSM AKA fallback must be allowed for
|
||||
the subscriber to remain in roaming service.
|
||||
|
||||
In short:
|
||||
|
||||
- A UMTS AKA resync is handled similarly to the attaching of a so far unknown subscriber.
|
||||
- With the exception that previous GSM AKA tuples may be available to try a fallback to re-using older tuples.
|
||||
|
||||
Needless to say that avoiding the need for UMTS AKA resynchronization is an important aspect of D-GSM's resilience
|
||||
against unreliable links.
|
||||
|
||||
In UMTS AKA, there is not one single SQN, but there are a number SQN slots, called IND slots or IND buckets. The IND
|
||||
bitlen configured on the USIM determines the amount of slots available. The IND bitlen is usually 5, i.e. 2^5^ = 32
|
||||
slots. Monotonously rising SQN are only strictly enforced within each slot, so that each site should maintain a
|
||||
different IND slot. OsmoHLR determines distinct IND slots based on the IPA unit name. As soon as more than 16 sites
|
||||
(with an MSC and SGSN each) are maintained, IND slots may be shared between distinct sites, and administrative care
|
||||
should be taken to choose wisely which sites share the same slots: those that least share a common user group.
|
||||
|
||||
On 2G RAN, it may be possible to fall back to GSM AKA after a UMTS AKA resync request.
|
||||
TODO: test this
|
||||
|
||||
Either way, the AUTS that was received from the MSC definitely needs to find its way to the home HLR, and, ideally, the
|
||||
immediately returned auth tuples from the home HLR should be used to attach the subscriber.
|
||||
|
||||
=== CS and PS
|
||||
|
||||
Each subscriber may have multiple HLR subscriptions from distinct CN Domain VLRs at any time: Circuit Switched (MSC) and
|
||||
Packet Switched (SGSN) attach separately and perform Update Location Requests that are completely orthogonal, as far as
|
||||
the HLR is concerned.
|
||||
|
||||
Particularly the UMTS AKA tuples, which use distinct IND slots per VLR, need to be cached separately per CN Domain.
|
||||
|
||||
Hence it is not enough to maintain one cache per subscriber. A separate auth tuple cache and Mobility Management state
|
||||
has to be kept for each VLR that is requesting roaming service for a given subscriber.
|
||||
|
||||
=== Intercepting GSUP Conversations
|
||||
|
||||
Taking over GSUP conversations in the proxy HLR is not as trivial as it may sound. Here are potential problems and how
|
||||
to fix them.
|
||||
|
||||
[[proxy_cache_gsup_mm_messages]]
|
||||
==== Which GSUP Conversations to Intercept
|
||||
|
||||
For the purpose of providing highly available roaming despite unreliable links to the home HLR, it suffices to intercept
|
||||
Mobility Management (MM) related GSUP messages, only:
|
||||
|
||||
- Send Auth Info Request / Result
|
||||
- Update Location Request / Result
|
||||
- Insert Subscriber Data Request / Result
|
||||
- PurgeMS Request / Result (?)
|
||||
|
||||
An interesting feature would be to also intercept specific USSD requests, like returning the own MSISDN or IMSI more
|
||||
reliably, or handling services that only make sense when served by the local site. At the time of writing, this is seen
|
||||
as a future extension of D-GSM and not considered for implementation.
|
||||
|
||||
==== Determining Whether a Home HLR is Responsive
|
||||
|
||||
Normally, all GSUP messages are merely routed via the proxy HLR and are handled by the home HLR. The idea is that the
|
||||
proxy HLR jumps in and saves a GSUP conversation when the home HLR is not answering properly.
|
||||
|
||||
The simplest method to decide whether a home HLR is currently connected would be to look at the GSUP client state.
|
||||
However, a local flag that indicates an established GSUP connection does not necessarily mean a reliable link.
|
||||
There are keep-alive messages on the GSUP/IPA link, and a lost connection should reflect in the client state, so that a
|
||||
lost GSUP link definitely indicates an unresponsive home HLR. But for various reasons (e.g. packet loss), the link might
|
||||
look intact, but still a given GSUP message fails to get a response from the home HLR.
|
||||
|
||||
A more resilient method to decide whether a home HLR is responsive is to keep track of every MM related GSUP
|
||||
conversation for each subscriber, and to jump in and take over the GSUP conversation as soon as the response is taking
|
||||
too long to arrive. However, choosing an inadequate timeout period would either mean responding after the MSC has
|
||||
already timed out (too slow), or completely cutting off all responses from a high-latency home HLR (too fast).
|
||||
|
||||
Also, if the proxy HLR has already responded to the MSC, but a slow home HLR's response arrives shortly after,
|
||||
forwarding this late message to the MSC on top of the earlier response to the same request would confuse the GSUP
|
||||
conversation.
|
||||
|
||||
So, the proxy HLR just jumping into the GSUP conversation when a specific delay has passed is fairly complex and error
|
||||
prone. A better idea is to always intercept MM related GSUP conversations:
|
||||
|
||||
[[proxy_cache_gsup_conversations]]
|
||||
==== Solution: Distinct GSUP Conversations
|
||||
|
||||
A solution that avoids all of the above problems is to *always* take over *all* MM related conversations (see
|
||||
<<proxy_cache_gsup_mm_messages>>), as soon as the proxy has sufficient data to service them by itself; at the same time,
|
||||
the proxy HLR should also relay the same requests to the home HLR, and acknowledge its responses, after the fact.
|
||||
|
||||
If the proxy cache already has a complete record of a subscriber, the proxy HLR can always directly accept an Update
|
||||
Location Request, including an Insert Subscriber Data. A prompt response ensures that the MSC does not timeout its GSUP
|
||||
request, and reduces waiting time for the subscriber.
|
||||
|
||||
To ensure that the proxy HLR's data on the subscriber doesn't become stale and diverge from the home HLR, the proxy
|
||||
asynchronously also forwards an Update Location Request to the home HLR. In most normal cases, there will be no
|
||||
surprises, and the home HLR will continue with an Insert Subscriber Data Request containing already known data, and an
|
||||
Update Location Result accepting the LU.
|
||||
|
||||
If the home HLR does not respond, the proxy HLR ignores that fact -- the home HLR is not reachable, and the aim is to
|
||||
continue to service the subscriber for the time being.
|
||||
|
||||
But, should the home HLR's Insert Subscriber Data Request send different data than the proxy cache sees on record, the
|
||||
proxy HLR can trigger another Insert Subscriber Data Request to the MSC, to correct the stale data sent before.
|
||||
|
||||
Similarly, if the home HLR rejects the Update Location Request completely, the proxy HLR can tell the MSC to detach the
|
||||
subscriber with a Cancel Location Request message, as soon as it notices the rejection.
|
||||
|
||||
Note that a UMTS AKA resynchronization request invalidates the entire auth tuple cache and needs to either be sent to
|
||||
the home HLR immediately, if available, or the AUTS from the USIM must later reach the home HLR to obtain fresh UMTS AKA
|
||||
tuples for the cache. See <<proxy_cache_umts_aka_resync>>.
|
||||
|
||||
=== Message Sequences
|
||||
|
||||
==== Normal Roaming Attach
|
||||
|
||||
On first attaching via a proxy HLR, when there is no proxy state for the subscriber yet, the home HLR must be reachable.
|
||||
|
||||
The normal procedure takes place without modification, except that he proxy HLR keeps a copy of the first auth tuples it
|
||||
forwards from the home HLR back to the MSC (marked as used) (1). This is to have auth tuples available for resending
|
||||
already used tuples in a fallback to GSM AKA, in case this is enabled in the proxy HLR config.
|
||||
|
||||
After the Location Updating has completed successfully, the proxy HLR fills up its auth tuple cache by additional Send
|
||||
Auth Info Requests (2). As soon as unused auth tuples become available, the proxy HLR can discard already used tuples
|
||||
from (1).
|
||||
|
||||
.Normal attaching of a subscriber that is roaming here
|
||||
["mscgen"]
|
||||
----
|
||||
include::proxy_cache_attach.msc[]
|
||||
----
|
||||
|
||||
==== MSC Requests More Auth Tuples
|
||||
|
||||
As soon as the MSC has run out of fresh auth tuples, it will ask the HLR proxy for more. Without proxy caching, this
|
||||
request would be directly forwarded to the home HLR. Instead, the proxy HLR finds unused auth tuples in the cache and
|
||||
directly sends those (3). Even if there is a reliable link, the home HLR is not contacted at this point.
|
||||
|
||||
Directly after completing the Send Auth Info Result, the proxy HLR finds that less tuples than requested by the D-GSM
|
||||
configuration are cached, and asks the home HLR for more tuples, to fill up the cache (4). If there currently is no
|
||||
reliable link, this will fail, and the proxy HLR will retry periodically (5) / upon GSUP reconnect.
|
||||
|
||||
.When the MSC has used up all of its auth tuples, but the proxy HLR still has unused auth tuples in the cache
|
||||
["mscgen"]
|
||||
----
|
||||
include::proxy_cache_more_tuples.msc[]
|
||||
----
|
||||
|
||||
==== Running Out of Auth Tuples
|
||||
|
||||
When all fresh tuples from the proxy HLR have been used up, and the home HLR remains unreachable, the proxy HLR normally
|
||||
fails and rejects the subscriber (default configuration).
|
||||
|
||||
If explicitly enabled in the configuration, the proxy HLR will attempt to fall back to GSM AKA and resend already spent
|
||||
tuples, deliberately omitting UMTS AKA parts (6).
|
||||
|
||||
Note that an attempt to refill the tuple cache in the proxy HLR always happens asynchronously. If there are no tuples,
|
||||
that means the link to the home HLR is currently broken, and there is no point in trying to contact it now. Tuples will
|
||||
be obtained as soon as the link is established again.
|
||||
|
||||
.When the MSC has used up all of its auth tuples and the proxy HLR tuple cache is dry
|
||||
["mscgen"]
|
||||
----
|
||||
include::proxy_cache_tuple_cache_dry.msc[]
|
||||
----
|
||||
|
||||
==== Periodic Location Updating
|
||||
|
||||
Each subscriber performs periodic Location Updating to ensure that it is not implicitly detached from the network. When
|
||||
the proxy HLR already has a proxy cache for this subscriber, all information to complete the periodic Location Updating
|
||||
is already known in the proxy HLR. If the link to the home HLR is unresponsive, the proxy HLR mimicks the Insert
|
||||
Subscriber Data Request that the home HLR would normally send, using the cached MSISDN, and then sends the Update
|
||||
Location Result. The subscriber remains attached without a responsive link to the home HLR being required.
|
||||
|
||||
.Periodic Location Updating when the MSC still has unused auth tuples
|
||||
["mscgen"]
|
||||
----
|
||||
include::proxy_cache_periodic_lu.msc[]
|
||||
----
|
||||
|
||||
==== UMTS AKA Resync
|
||||
|
||||
The AUTS from a UMTS AKA resync needs to reach the home HLR sooner or later, and a resync renders all UMTS AKA tuples in
|
||||
the cache stale.
|
||||
|
||||
.Cached tuples become unusable from a UMTS AKA resynchronisation request from the USIM.
|
||||
["mscgen"]
|
||||
----
|
||||
include::proxy_cache_umts_aka_resync.msc[]
|
||||
----
|
|
@ -0,0 +1,33 @@
|
|||
msc {
|
||||
hscale="2";
|
||||
ms[label="MS,BSS"],__msc[label="MSC"],hlr[label="HLR proxy"],home[label="Home HLR"];
|
||||
|
||||
ms => __msc [label="Location Updating Request (IMSI Attach)"];
|
||||
__msc => hlr [label="Send Auth Info Request"];
|
||||
hlr abox hlr [label="No proxy cache data available for this subscriber"];
|
||||
hlr rbox home [label="mslookup finds the home HLR"];
|
||||
hlr => home [label="Send Auth Info Request"];
|
||||
hlr <= home [label="Send Auth Info Result\nwith 5 auth tuples"];
|
||||
hlr rbox hlr [label="(1) Keep a copy of the auth tuples"];
|
||||
__msc <= hlr [label="Send Auth Info Result"];
|
||||
__msc rbox __msc [label="MSC stores 5 auth tuples,\nuses the first one now,\nand keeps the rest for later requests"];
|
||||
ms rbox __msc [label="Authentication"];
|
||||
__msc => hlr [label="Update Location Request"];
|
||||
hlr => home [label="Update Location Request"];
|
||||
hlr <= home [label="Insert Subscriber Data Request\n(with subscriber's MSISDN)"];
|
||||
hlr rbox hlr [label="proxy HLR caches the MSISDN"];
|
||||
__msc <= hlr [label="Insert Subscriber Data Request"];
|
||||
__msc => hlr [label="Insert Subscriber Data Result"];
|
||||
hlr => home [label="Insert Subscriber Data Result"];
|
||||
hlr <= home [label="Update Location Result"];
|
||||
__msc <= hlr [label="Update Location Result"];
|
||||
ms <= __msc [label="Location Updating Accept"];
|
||||
hlr abox hlr [label="After successful Update Location, check the cache"];
|
||||
hlr rbox hlr [label="(2) Ask for more auth tuples to cache\n(amount of tuples configurable)"];
|
||||
hlr => home [label="Send Auth Info Request"];
|
||||
hlr <= home [label="Send Auth Info Result"];
|
||||
hlr rbox hlr [label="store 5 more tuples"];
|
||||
hlr => home [label="Send Auth Info Request"];
|
||||
hlr <= home [label="Send Auth Info Result"];
|
||||
hlr rbox hlr [label="store yet 5 more tuples"];
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
msc {
|
||||
hscale="2";
|
||||
ms[label="MS,BSS"],__msc[label="MSC"],hlr[label="HLR proxy"],home[label="Home HLR"];
|
||||
|
||||
ms => __msc [label="CM Service Request / Paging Response"];
|
||||
__msc => hlr [label="Send Auth Info Request"];
|
||||
hlr rbox hlr [label="Use already set up proxy path"];
|
||||
hlr abox hlr [label="there still are unsent auth tuples\nin the cache"];
|
||||
hlr rbox hlr [label="(3) Send cached, fresh tuples"];
|
||||
__msc <= hlr [label="Send Auth Info Result\ncontaining auth tuples\nfrom the proxy cache"];
|
||||
ms rbox __msc [label="Authentication"];
|
||||
ms rbox __msc [label="Continue the CM Service / Paging action"];
|
||||
hlr abox hlr [label="Note that there are no/few unused tuples in the cache, fill up again"];
|
||||
hlr rbox hlr [label="(4) Ask for more auth tuples to cache"];
|
||||
hlr => home [label="Send Auth Info Request"];
|
||||
--- [label="If the home HLR link is not working"];
|
||||
hlr abox hlr [label="no link\nor\nresponse timeout"];
|
||||
hlr rbox hlr [label="(5) Set up a timer to retry SAI\n(a few minutes?)"];
|
||||
hlr abox hlr [label="Timer triggers"];
|
||||
hlr => home [label="Send Auth Info Request"];
|
||||
--- [label="If the home HLR link is functional"];
|
||||
hlr <= home [label="Send Auth Info Result"];
|
||||
hlr rbox hlr [label="store 5 more tuples"];
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
msc {
|
||||
hscale="2";
|
||||
ms[label="MS,BSS"],__msc[label="MSC"],hlr[label="HLR proxy"],home[label="Home HLR"];
|
||||
|
||||
ms => __msc [label="Location Updating Request (Periodic)"];
|
||||
ms rbox __msc [label="Authentication,\nusing the next of 5 auth tuples the MSC has stored"];
|
||||
__msc => hlr [label="Update Location Request"];
|
||||
hlr rbox hlr [label="Use already set up proxy path"];
|
||||
hlr abox hlr [label="(8) proxy cache already has all information to answer"];
|
||||
__msc <= hlr [label="Insert Subscriber Data Request"];
|
||||
__msc => hlr [label="Insert Subscriber Data Result"];
|
||||
__msc <= hlr [label="Update Location Result"];
|
||||
ms <= __msc [label="Location Updating Accept"];
|
||||
hlr rbox hlr [label="(9) Verify Update Location with home HLR"];
|
||||
|||;
|
||||
--- [label="if the home HLR has no changes and accepts"];
|
||||
hlr => home [label="Update Location Request"];
|
||||
hlr <= home [label="Insert Subscriber Data Request"];
|
||||
hlr => home [label="Insert Subscriber Data Result"];
|
||||
hlr abox hlr [label="Notice identical MSISDN"];
|
||||
hlr <= home [label="Update Location Result"];
|
||||
|||;
|
||||
--- [label="if the home HLR is unreachable"];
|
||||
hlr => home [label="Update Location Request"];
|
||||
hlr abox hlr [label="no link\nor\nresponse timeout"];
|
||||
hlr rbox hlr [label="Don't care, carry on"];
|
||||
|||;
|
||||
--- [label="if the home HLR has a modified MSISDN, and accepts"];
|
||||
hlr => home [label="Update Location Request"];
|
||||
hlr <= home [label="Insert Subscriber Data Request"];
|
||||
hlr => home [label="Insert Subscriber Data Result"];
|
||||
hlr abox hlr [label="Notice changed MSISDN"];
|
||||
__msc <= hlr [label="Insert Subscriber Data Request"];
|
||||
__msc => hlr [label="Insert Subscriber Data Result"];
|
||||
hlr <= home [label="Update Location Result"];
|
||||
|||;
|
||||
--- [label="if the home HLR rejects"];
|
||||
hlr => home [label="Update Location Request"];
|
||||
hlr <= home [label="Update Location Error"];
|
||||
__msc <= hlr [label="Cancel Location Request"];
|
||||
__msc => hlr [label="Cancel Location Result"];
|
||||
hlr rbox hlr [label="Clear subscriber cache"];
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
msc {
|
||||
hscale="2";
|
||||
ms[label="MS,BSS"],__msc[label="MSC"],hlr[label="HLR proxy"],home[label="Home HLR"];
|
||||
|
||||
ms => __msc [label="CM Service Request / Paging Response"];
|
||||
__msc => hlr [label="Send Auth Info Request"];
|
||||
hlr rbox hlr [label="Use already set up proxy path"];
|
||||
hlr abox hlr [label="All cached auth tuples have been sent to the MSC before"];
|
||||
--- [label="If no GSM AKA fallback is allowed"];
|
||||
__msc <= hlr [label="Send Auth Info Error"];
|
||||
ms rbox __msc [label="Detach"];
|
||||
--- [label="If GSM AKA fallback is allowed"];
|
||||
hlr rbox hlr [label="(6) Resend only GSM AKA tuples, already sent earlier"];
|
||||
__msc <= hlr [label="Send Auth Info Result\ncontaining GSM AKA auth tuples\nfrom the proxy cache"];
|
||||
ms rbox __msc [label="2G: Authentication\n3G: Detach"];
|
||||
ms rbox __msc [label="Continue the CM Service / Paging action"];
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
msc {
|
||||
hscale="2";
|
||||
ms[label="MS,BSS"],__msc[label="MSC"],hlr[label="HLR proxy"],home[label="Home HLR"];
|
||||
|
||||
ms => __msc [label="CM Service Request / Paging Response"];
|
||||
__msc => hlr [label="Send Auth Info Request"];
|
||||
hlr abox hlr [label="there still are unsent auth tuples\nin the cache"];
|
||||
hlr rbox hlr [label="Send cached, fresh tuples"];
|
||||
__msc <= hlr [label="Send Auth Info Result\ncontaining auth tuples\nfrom the proxy cache"];
|
||||
ms <= __msc [label="Authentication Request"];
|
||||
ms => __msc [label="USIM requests UMTS AKA resync\nAuth Failure with AUTS"];
|
||||
__msc => hlr [label="Send Auth Info Request\ncontaining AUTS IE"];
|
||||
hlr rbox hlr [label="Mark all UMTS AKA tuples as stale"];
|
||||
--- [label="If the home HLR responds in time"];
|
||||
hlr => home [label="Send Auth Info Request"];
|
||||
hlr <= home [label="Send Auth Info Result\nwith 5 auth tuples"];
|
||||
hlr rbox hlr [label="clear tuple cache, store new tuples"];
|
||||
__msc <= hlr [label="Send Auth Info Result"];
|
||||
ms rbox __msc [label="Authentication"];
|
||||
hlr rbox hlr [label="fill up the tuple cache as necessary"];
|
||||
hlr => home [label="Send Auth Info Request"];
|
||||
hlr <= home [label="Send Auth Info Result"];
|
||||
...;
|
||||
--- [label="If the home HLR is unreachable"];
|
||||
hlr => home [label="Send Auth Info Request"];
|
||||
hlr abox hlr [label="no link\nor\nresponse timeout"];
|
||||
hlr rbox hlr [label="Store the AUTS received earlier,\nand set up a timer to retry SAI\n(a few minutes?)"];
|
||||
--- [label="If no GSM AKA fallback is allowed"];
|
||||
__msc <= hlr [label="Send Auth Info Error"];
|
||||
ms rbox __msc [label="Detach"];
|
||||
--- [label="If GSM AKA fallback is allowed"];
|
||||
hlr rbox hlr [label="Resend only GSM AKA tuples, already sent earlier"];
|
||||
__msc <= hlr [label="Send Auth Info Result\ncontaining GSM AKA auth tuples\nfrom the proxy cache"];
|
||||
ms rbox __msc [label="2G: Authentication\n3G: Detach"];
|
||||
---;
|
||||
hlr abox hlr [label="AUTS timer triggers"];
|
||||
hlr => home [label="Send Auth Info Request"];
|
||||
hlr <= home [label="Send Auth Info Result"];
|
||||
hlr rbox hlr [label="clear tuple cache, store new tuples"];
|
||||
hlr rbox hlr [label="continue to fill up the cache as necessary"];
|
||||
}
|
|
@ -26,6 +26,8 @@ include::./common/chapters/control_if.adoc[]
|
|||
|
||||
include::{srcdir}/chapters/dgsm.adoc[]
|
||||
|
||||
include::{srcdir}/chapters/proxy_cache.adoc[]
|
||||
|
||||
include::./common/chapters/gsup.adoc[]
|
||||
|
||||
include::./common/chapters/port_numbers.adoc[]
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
all:
|
||||
echo "built only on manual invocation, needs mscgen and dot (graphviz) programs: invoke 'make charts'"
|
||||
|
||||
charts: msc dot
|
||||
|
||||
EXTRA_DIST = \
|
||||
proxy_cache.dot \
|
||||
proxy_cache__mm_fsm.dot \
|
||||
proxy_cache__to_home_hlr_fsm.dot \
|
||||
$(NULL)
|
||||
|
||||
CLEANFILES = \
|
||||
proxy_cache.png \
|
||||
proxy_cache__mm_fsm.png \
|
||||
proxy_cache__to_home_hlr_fsm.png \
|
||||
$(NULL)
|
||||
|
||||
msc: \
|
||||
$(NULL)
|
||||
|
||||
dot: \
|
||||
$(builddir)/proxy_cache.png \
|
||||
$(builddir)/proxy_cache__mm_fsm.png \
|
||||
$(builddir)/proxy_cache__to_home_hlr_fsm.png \
|
||||
$(NULL)
|
||||
|
||||
$(builddir)/%.png: %.msc
|
||||
mscgen -T png -o $@ $<
|
||||
|
||||
$(builddir)/%.msc: $(srcdir)/%.ladder
|
||||
@which ladder_to_msc.py || (echo 'PLEASE POINT YOUR $$PATH AT libosmocore/contrib/ladder_to_msc.py' && false)
|
||||
ladder_to_msc.py -i $< -o $@
|
||||
|
||||
$(builddir)/%.png: $(srcdir)/%.dot
|
||||
dot -Tpng $< > $@
|
||||
|
||||
.PHONY: poll
|
||||
poll:
|
||||
while true; do $(MAKE) msc dot; sleep 1; done
|
|
@ -0,0 +1,19 @@
|
|||
digraph G {
|
||||
rankdir=LR
|
||||
labelloc=t;
|
||||
|
||||
msc [label="MS/BSS/MSC"]
|
||||
subgraph cluster_proxy {
|
||||
label="HLR Proxy"
|
||||
proxy_mm [label="Proxy Mobility Management FSM",shape=box3d]
|
||||
proxy_home [label="Proxy to Home HLR FSM",shape=box3d]
|
||||
proxy [label="GSUP Proxy"]
|
||||
proxy_mm -> proxy_home [constraint=false,dir=both,label="(FSM events)"]
|
||||
}
|
||||
hlr [label="Home HLR"]
|
||||
|
||||
msc -> proxy_mm [dir=both,label="MM related GSUP\n (immediate response\n if possible)"]
|
||||
proxy_home -> hlr [dir=both,label="MM related GSUP\n (delayed)"]
|
||||
|
||||
msc -> proxy -> hlr [dir=both,label="non-MM GSUP",style=dashed]
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
digraph G {
|
||||
rankdir=TB
|
||||
labelloc=t; label="HLR Proxy MM FSM"
|
||||
|
||||
top,top2,top3[shape=invtriangle,label="(1)"]
|
||||
|
||||
top -> READY
|
||||
|
||||
new [label="proxy_cache_subscr_new()\n/ proxy_cache_subscr_from_db()",shape=box]
|
||||
READY [style=bold]
|
||||
HIBERNATE [shape=note,label="Hibernate\n (keep in DB)"]
|
||||
CLEAR [shape=box,label="Clear DB entry\n (discard completely)"]
|
||||
WAIT_AUTH_TUPLES [style=bold]
|
||||
WAIT_SUBSCR_DATA [style=bold]
|
||||
WAIT_GSUP_ISD_RESULT [style=bold]
|
||||
|
||||
home_fsm [label="Proxy to Home HLR FSM",shape=box3d]
|
||||
{rank=same;READY,home_fsm}
|
||||
|
||||
|
||||
new -> {READY,home_fsm}
|
||||
|
||||
READY -> {event_lu_req,event_auth_info_req} [arrowhead=none]
|
||||
|
||||
event_auth_info_req [shape=rarrow,label="Rx GSUP\nSend Auth Info Request\nfrom MSC"]
|
||||
event_auth_info_req -> junction_send_auth_info_req
|
||||
junction_send_auth_info_req [shape=diamond,label="Unused\nauth tuples\navailable?"]
|
||||
junction_send_auth_info_req -> action_send_auth_info [label="yes"]
|
||||
junction_send_auth_info_req -> emit_need_tuples [label="no"]
|
||||
emit_need_tuples [shape=lpromoter,label="emit\n HOME_EV_CHECK_TUPLES\n to Home FSM"]
|
||||
emit_need_tuples->WAIT_AUTH_TUPLES
|
||||
WAIT_AUTH_TUPLES -> rx_ev_rx_auth_tuples [arrowhead=none]
|
||||
rx_ev_rx_auth_tuples [shape=rpromoter,label="receive\n MM_EV_RX_AUTH_TUPLES"]
|
||||
rx_ev_rx_auth_tuples -> action_send_auth_info
|
||||
action_send_auth_info [shape=larrow,label="Tx GSUP\nSend Auth Info Result\nwith fresh auth tuples\n to MSC"]
|
||||
action_send_auth_info -> emit_check_tuples
|
||||
emit_check_tuples [shape=lpromoter,label="emit\n HOME_EV_CHECK_TUPLES\n to Home FSM"]
|
||||
emit_check_tuples -> top2
|
||||
WAIT_AUTH_TUPLES -> junction_check_auth_fallback [label="Timeout",style=dashed]
|
||||
junction_check_auth_fallback -> action_do_auth_fallback [label="yes",style=dashed]
|
||||
action_do_auth_fallback [shape=larrow,label="Tx GSUP\nSend Auth Info Result\nwith recycled auth tuple\n(GSM AKA only)"]
|
||||
junction_check_auth_fallback [shape=diamond,label="Re-usable\nauth tuples\navailable?"]
|
||||
junction_check_auth_fallback -> action_fail_auth [label="no",style=dashed]
|
||||
action_fail_auth [shape=larrow,label="Tx GSUP\nSend Auth Info Error\npending re-connection to\nthe home HLR"]
|
||||
{action_do_auth_fallback,action_fail_auth} -> top2 [style=dashed]
|
||||
|
||||
event_lu_req [shape=rarrow,label="Rx GSUP\nUpdate Location Request\nfrom MSC"]
|
||||
event_lu_req -> emit_lu_req
|
||||
emit_lu_req [shape=lpromoter,label="emit\n HOME_EV_CONFIRM_LU"];
|
||||
emit_lu_req -> junction_check_subscriber_data
|
||||
junction_check_subscriber_data [shape=diamond,label="Subscriber\nData\nknown?"]
|
||||
junction_check_subscriber_data -> WAIT_SUBSCR_DATA [label=no]
|
||||
WAIT_SUBSCR_DATA -> rx_ev_subscr_data [arrowhead=none]
|
||||
rx_ev_subscr_data [shape=rpromoter,label="receive\n MM_EV_RX_SUBSCR_DATA"];
|
||||
rx_ev_subscr_data -> action_subscr_data_req
|
||||
junction_check_subscriber_data -> action_subscr_data_req [label="yes"]
|
||||
action_subscr_data_req [shape=larrow,label="Tx GSUP\n Insert Subscriber Data\n Request to MSC"]
|
||||
action_subscr_data_req -> WAIT_GSUP_ISD_RESULT
|
||||
WAIT_GSUP_ISD_RESULT -> tx_gsup_isd_res [arrowhead=none]
|
||||
tx_gsup_isd_res [shape=rarrow,label="Rx GSUP\n Insert Subscriber Data Result\nfrom MSC"]
|
||||
tx_gsup_isd_res -> top3
|
||||
|
||||
{WAIT_GSUP_ISD_RESULT,WAIT_SUBSCR_DATA} -> action_lu_reject [label="Timeout",style=dashed]
|
||||
action_lu_reject [shape=larrow,label="Tx GSUP\nUpdate Location Error\nto MSC\npending reconnect of home HLR"]
|
||||
action_lu_reject -> top3 [style=dashed]
|
||||
|
||||
READY -> HIBERNATE [label="Timeout"]
|
||||
READY -> rx_ev_subscr_invalid [arrowhead=none]
|
||||
rx_ev_subscr_invalid[shape=rpromoter,label="receive\n MM_EV_SUBSCR_INVALID"]
|
||||
rx_ev_subscr_invalid -> tx_purge_req
|
||||
tx_purge_req [shape=larrow,label="Tx GSUP\nPurge MS Request"]
|
||||
tx_purge_req -> note_purge [style=dotted]
|
||||
note_purge [shape=note,label="Don't care about\nPurge MS Result"]
|
||||
tx_purge_req -> CLEAR
|
||||
{CLEAR,HIBERNATE} -> TERM
|
||||
TERM[shape=octagon][style=bold]
|
||||
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
digraph G {
|
||||
rankdir=TB
|
||||
labelloc=t; label="HLR Proxy to Home HLR FSM"
|
||||
|
||||
top,to_top1,to_top2,to_top3,to_top4,to_top5[shape=invtriangle,label="(A)"]
|
||||
top->junction_resolve_home_hlr
|
||||
|
||||
at_clear,to_clear1,to_clear2 [shape=invtriangle,label="(X)"]
|
||||
|
||||
mm_fsm [shape=box3d,label="Proxy MM FSM"]
|
||||
mm_fsm -> junction_resolve_home_hlr [style=invisible,arrowhead=none]
|
||||
|
||||
WAIT_HOME_HLR_RESOLVED [style=bold]
|
||||
WAIT_UPDATE_LOCATION_RESULT [style=bold]
|
||||
WAIT_SEND_AUTH_INFO_RESULT [style=bold]
|
||||
IDLE [style=bold]
|
||||
CLEAR [style=bold]
|
||||
|
||||
new [label="proxy_cache_subscr_new()\n/ proxy_cache_subscr_from_db()",shape=box]
|
||||
{
|
||||
rank=same;
|
||||
junction_resolve_home_hlr [shape=diamond,label="Home HLR\n known?"]
|
||||
junction_confirm_home_hlr [shape=diamond,label="mslookup\n for home HLR\n very old?"]
|
||||
junction_update_location [shape=diamond,label="Did MM FSM emit another\n HOME_EV_CONFIRM_LU?"]
|
||||
junction_auth_info [shape=diamond,label="Auth tuple\ncache full of\nfresh tuples?"]
|
||||
}
|
||||
|
||||
new -> {junction_resolve_home_hlr, mm_fsm}
|
||||
|
||||
junction_resolve_home_hlr -> junction_confirm_home_hlr [label="known"]
|
||||
junction_resolve_home_hlr -> action_mslookup [label="wat"]
|
||||
action_mslookup [shape=larrow,label="start mslookup"]
|
||||
action_mslookup -> WAIT_HOME_HLR_RESOLVED
|
||||
WAIT_HOME_HLR_RESOLVED -> rx_mslookup_res [arrowhead=none]
|
||||
rx_mslookup_res [shape=rarrow,label="home HLR\n resolved"]
|
||||
rx_mslookup_res -> to_top1
|
||||
WAIT_HOME_HLR_RESOLVED -> CLEAR [style=dashed,label=Timeout]
|
||||
|
||||
junction_update_location -> action_lu_req [label="need data\n/\nneed to confirm LU"]
|
||||
action_lu_req [shape=larrow,label="Tx GSUP Update Location Req\nto home HLR"]
|
||||
action_lu_req -> WAIT_UPDATE_LOCATION_RESULT
|
||||
WAIT_UPDATE_LOCATION_RESULT->rx_isd [arrowhead=none]
|
||||
rx_isd [shape=rarrow,label="Rx GSUP\n Insert Subscriber\n Data Request\nfrom home HLR"]
|
||||
rx_isd -> emit_rx_subscr_data
|
||||
emit_rx_subscr_data [shape=lpromoter,label="emit\n MM_EV_RX_SUBSCR_DATA"]
|
||||
emit_rx_subscr_data->action_tx_isd_res
|
||||
action_tx_isd_res -> WAIT_UPDATE_LOCATION_RESULT
|
||||
action_tx_isd_res [shape=larrow,label="Tx GSUP Insert Subscriber Data Result\nto home HLR"]
|
||||
WAIT_UPDATE_LOCATION_RESULT -> rx_lu_res [arrowhead=none]
|
||||
rx_lu_res [shape=rarrow,label="Rx GSUP Update\n Location Result\nfrom home HLR"]
|
||||
rx_lu_res -> to_top2
|
||||
junction_update_location -> junction_auth_info [label="data known,\nLU confirmed"]
|
||||
WAIT_UPDATE_LOCATION_RESULT->junction_lu_failed [label="Timeout "]
|
||||
junction_lu_failed [shape=diamond,label="has home HLR\never confirmed\na LU before?"];
|
||||
junction_lu_failed -> to_clear2 [label="never\nconfirmed",style=dashed]
|
||||
junction_lu_failed -> note_lu_failed [label="has\nconfirmed\nbefore"]
|
||||
note_lu_failed [shape=note,label="Home HLR is\ntemporarily unreachable,\n just carry on."]
|
||||
note_lu_failed -> to_top3
|
||||
|
||||
junction_confirm_home_hlr -> action_mslookup_confirm [label="very old"]
|
||||
junction_confirm_home_hlr -> junction_update_location [label="fair enough"]
|
||||
action_mslookup_confirm [shape=larrow,label="start mslookup"]
|
||||
note_mslookup_confirm [shape=note,label="Result evaluated in IDLE state.\nIf no mslookup result comes\n back, the home HLR is\ntemporarily unreachable:\ncontinue anyway.\nThis is sanity checking for\na *modified* home HLR"]
|
||||
action_mslookup_confirm -> note_mslookup_confirm [arrowhead=none,style=dotted]
|
||||
action_mslookup_confirm -> junction_update_location
|
||||
|
||||
junction_auth_info -> action_sai_req [label="need more tuples"]
|
||||
junction_auth_info -> set_idle_timeout [label="cache is fresh"]
|
||||
action_sai_req [shape=larrow,label="Tx GSUP\n Send Auth Info Request\n to home HLR"]
|
||||
action_sai_req -> WAIT_SEND_AUTH_INFO_RESULT
|
||||
WAIT_SEND_AUTH_INFO_RESULT->rx_sai_res[arrowhead=none]
|
||||
rx_sai_res[shape=rarrow,label="Rx GSUP\n Send Auth Info\n Result\nfrom home HLR"]
|
||||
rx_sai_res -> emit_rx_tuples
|
||||
emit_rx_tuples [shape=lpromoter,label="emit\n MM_EV_RX_AUTH_TUPLES"]
|
||||
emit_rx_tuples -> to_top4
|
||||
WAIT_SEND_AUTH_INFO_RESULT->set_idle_timeout[label="Timeout",style=dashed]
|
||||
set_idle_timeout [shape=diamond,label="Select IDLE\n timeout:\nmin(\nretry tuples,\n confirm mslookup)"]
|
||||
set_idle_timeout -> IDLE
|
||||
|
||||
IDLE -> rx_mslookup_confirm_res [arrowhead=none]
|
||||
rx_mslookup_confirm_res [shape=rarrow,label="Rx mslookup result"]
|
||||
rx_mslookup_confirm_res -> junction_compare_home_hlr
|
||||
junction_compare_home_hlr [shape=diamond,label="mslookup result\n matches home HLR\n on record?"]
|
||||
junction_compare_home_hlr -> set_idle_timeout [label="matches"]
|
||||
junction_compare_home_hlr -> to_clear1 [label="mismatch",style=dashed]
|
||||
|
||||
IDLE -> to_top5 [label="Timeout"]
|
||||
IDLE -> {rx_ev_check_tuples,rx_ev_confirm_lu} [arrowhead=none]
|
||||
rx_ev_check_tuples [shape=rpromoter,label="receive\n HOME_EV_CHECK_TUPLES"]
|
||||
rx_ev_confirm_lu [shape=rpromoter,label="receive\n HOME_EV_CONFIRM_LU"]
|
||||
{rx_ev_check_tuples,rx_ev_confirm_lu} -> to_top5
|
||||
|
||||
at_clear -> CLEAR
|
||||
CLEAR -> emit_subscr_invalid -> TERM
|
||||
emit_subscr_invalid [shape=lpromoter,label="emit\n MM_EV_SUBSCR_INVALID"]
|
||||
TERM[shape=octagon][style=bold]
|
||||
}
|
|
@ -14,6 +14,8 @@ noinst_HEADERS = \
|
|||
mslookup_server.h \
|
||||
mslookup_server_mdns.h \
|
||||
proxy.h \
|
||||
proxy_mm.h \
|
||||
proxy_db.h \
|
||||
rand.h \
|
||||
remote_hlr.h \
|
||||
timestamp.h \
|
||||
|
|
|
@ -20,8 +20,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <time.h>
|
||||
#include <osmocom/gsm/protocol/gsm_23_003.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/gsm/protocol/gsm_23_003.h>
|
||||
#include <osmocom/gsm/gsup.h>
|
||||
#include <osmocom/gsupclient/gsup_peer_id.h>
|
||||
#include <osmocom/hlr/timestamp.h>
|
||||
|
||||
|
@ -65,6 +68,8 @@ struct proxy_subscr {
|
|||
char msisdn[GSM23003_MSISDN_MAX_DIGITS+1];
|
||||
struct osmo_sockaddr_str remote_hlr_addr;
|
||||
struct proxy_subscr_domain_state cs, ps;
|
||||
struct osmo_fsm_inst *mm_fi;
|
||||
struct osmo_fsm_inst *to_home_fi;
|
||||
};
|
||||
|
||||
void proxy_init(struct osmo_gsup_server *gsup_server_to_vlr);
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/* Copyright 2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* Author: Neels Hofmeyr <neels@hofmeyr.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* If a subscriber from a remote site has successfully attached at this local site, and the link to the subscriber's
|
||||
* home HLR has succeeded, this will try to bridge the time of temporary link failure to that home HLR.
|
||||
* Tasks to take over from the unreachable home HLR:
|
||||
* - Resend known auth tuples on OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST.
|
||||
* - ...?
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/* Data stored per subscriber */
|
||||
struct proxy_broken_link_cache {
|
||||
struct osmo_auth_vector auth_vectors[OSMO_GSUP_MAX_NUM_AUTH_INFO];
|
||||
size_t num_auth_vectors;
|
||||
|
||||
timestamp_t last_update;
|
||||
};
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/hlr/proxy.h>
|
||||
|
||||
enum proxy_mm_fsm_event {
|
||||
PROXY_MM_EV_SUBSCR_INVALID,
|
||||
PROXY_MM_EV_RX_GSUP_LU,
|
||||
PROXY_MM_EV_RX_GSUP_SAI,
|
||||
PROXY_MM_EV_RX_SUBSCR_DATA,
|
||||
PROXY_MM_EV_RX_GSUP_ISD_RESULT,
|
||||
PROXY_MM_EV_RX_AUTH_TUPLES,
|
||||
};
|
||||
|
||||
enum proxy_to_home_fsm_event {
|
||||
PROXY_TO_HOME_EV_HOME_HLR_RESOLVED,
|
||||
PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ,
|
||||
PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT,
|
||||
PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT,
|
||||
PROXY_TO_HOME_EV_CHECK_TUPLES,
|
||||
PROXY_TO_HOME_EV_CONFIRM_LU,
|
||||
};
|
||||
|
||||
extern struct llist_head proxy_mm_list;
|
||||
|
||||
struct proxy_mm_auth_cache {
|
||||
struct llist_head entry;
|
||||
uint64_t db_id;
|
||||
struct osmo_auth_vector auth_vectors[OSMO_GSUP_MAX_NUM_AUTH_INFO];
|
||||
size_t num_auth_vectors;
|
||||
unsigned int sent_to_vlr_count;
|
||||
};
|
||||
|
||||
struct proxy_mm {
|
||||
struct llist_head entry;
|
||||
struct osmo_gsup_peer_id vlr_name;
|
||||
char imsi[GSM23003_IMSI_MAX_DIGITS+1];
|
||||
bool is_ps;
|
||||
struct osmo_fsm_inst *mm_fi;
|
||||
struct osmo_fsm_inst *to_home_fi;
|
||||
struct llist_head auth_cache;
|
||||
};
|
||||
|
||||
struct proxy_mm *proxy_mm_alloc(const struct osmo_gsup_peer_id *vlr_name,
|
||||
bool is_ps,
|
||||
const char *imsi);
|
||||
|
||||
void proxy_mm_add_auth_vectors(struct proxy_mm *proxy_mm,
|
||||
const struct osmo_auth_vector *auth_vectors, size_t num_auth_vectors);
|
||||
struct proxy_mm_auth_cache *proxy_mm_get_auth_vectors(struct proxy_mm *proxy_mm);
|
||||
void proxy_mm_use_auth_vectors(struct proxy_mm *proxy_mm, struct proxy_mm_auth_cache *ac);
|
||||
void proxy_mm_discard_auth_vectors(struct proxy_mm *proxy_mm, struct proxy_mm_auth_cache *ac);
|
||||
|
||||
bool proxy_mm_subscriber_data_known(const struct proxy_mm *proxy_mm);
|
21
sql/hlr.sql
21
sql/hlr.sql
|
@ -87,6 +87,27 @@ CREATE TABLE ind (
|
|||
UNIQUE (vlr)
|
||||
);
|
||||
|
||||
CREATE TABLE proxy_auth_cache {
|
||||
id INTEGER PRIMARY KEY,
|
||||
vlr TEXT NOT NULL,
|
||||
imsi TEXT NOT NULL,
|
||||
sent_count INTEGER
|
||||
};
|
||||
|
||||
CREATE TABLE proxy_auth_cache_vector {
|
||||
-- belongs to this proxy_auth_cache entry
|
||||
proxy_auth_cache_id INTEGER,
|
||||
rand VARCHAR(32),
|
||||
autn VARCHAR(32),
|
||||
ck VARCHAR(32),
|
||||
ik VARCHAR(32),
|
||||
res VARCHAR(32),
|
||||
kc[8] VARCHAR(16),
|
||||
sres[4] VARCHAR(8),
|
||||
-- enum osmo_sub_auth_type bitmask
|
||||
auth_types INTEGER
|
||||
};
|
||||
|
||||
CREATE UNIQUE INDEX idx_subscr_imsi ON subscriber (imsi);
|
||||
|
||||
-- Set HLR database schema version number
|
||||
|
|
|
@ -54,6 +54,9 @@ osmo_hlr_SOURCES = \
|
|||
gsup_send.c \
|
||||
hlr_ussd.c \
|
||||
proxy.c \
|
||||
proxy_mm.c \
|
||||
proxy_to_home.c \
|
||||
proxy_db.c \
|
||||
dgsm.c \
|
||||
remote_hlr.c \
|
||||
lu_fsm.c \
|
||||
|
|
12
src/proxy.c
12
src/proxy.c
|
@ -403,6 +403,18 @@ static int proxy_acknowledge_gsup_from_remote_hlr(struct proxy *proxy, const str
|
|||
);
|
||||
break;
|
||||
|
||||
case OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT:
|
||||
/* Remember the auth tuples: if the remote HLR becomes unreachable for an intermediate period, we can
|
||||
* still re-use this auth information a number of times and keep the subscriber attached (on this
|
||||
* roaming/proxy HLR). */
|
||||
#if 0
|
||||
for (i = 0; i < gsup->num_auth_vectors; i++)
|
||||
proxy_subscr_new.auth_vectors[i] = gsup->auth_vectors[i];
|
||||
proxy_subscr_new.num_auth_vectors = gsup->num_auth_vectors;
|
||||
rc = proxy_subscr_create_or_update(proxy, &proxy_subscr_new);
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,417 @@
|
|||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
#include <osmocom/gsm/apn.h>
|
||||
#include <osmocom/hlr/hlr.h>
|
||||
#include <osmocom/hlr/proxy_mm.h>
|
||||
#include <osmocom/hlr/proxy_db.h>
|
||||
|
||||
enum proxy_mm_fsm_state {
|
||||
PROXY_MM_ST_READY,
|
||||
PROXY_MM_ST_WAIT_SUBSCR_DATA,
|
||||
PROXY_MM_ST_WAIT_GSUP_ISD_RESULT,
|
||||
PROXY_MM_ST_WAIT_AUTH_TUPLES,
|
||||
};
|
||||
|
||||
static const struct value_string proxy_mm_fsm_event_names[] = {
|
||||
OSMO_VALUE_STRING(PROXY_MM_EV_SUBSCR_INVALID),
|
||||
OSMO_VALUE_STRING(PROXY_MM_EV_RX_GSUP_LU),
|
||||
OSMO_VALUE_STRING(PROXY_MM_EV_RX_GSUP_SAI),
|
||||
OSMO_VALUE_STRING(PROXY_MM_EV_RX_SUBSCR_DATA),
|
||||
OSMO_VALUE_STRING(PROXY_MM_EV_RX_GSUP_ISD_RESULT),
|
||||
OSMO_VALUE_STRING(PROXY_MM_EV_RX_AUTH_TUPLES),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct osmo_fsm proxy_mm_fsm;
|
||||
static struct osmo_fsm proxy_to_home_fsm;
|
||||
|
||||
struct osmo_tdef proxy_mm_tdefs[] = {
|
||||
// FIXME
|
||||
{ .T=-1, .default_val=5, .desc="proxy_mm ready timeout" },
|
||||
{ .T=-2, .default_val=5, .desc="proxy_mm wait_subscr_data timeout" },
|
||||
{ .T=-3, .default_val=5, .desc="proxy_mm wait_gsup_isd_result timeout" },
|
||||
{ .T=-4, .default_val=5, .desc="proxy_mm wait_auth_tuples timeout" },
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_tdef_state_timeout proxy_mm_fsm_timeouts[32] = {
|
||||
// FIXME
|
||||
[PROXY_MM_ST_READY] = { .T=-1 },
|
||||
[PROXY_MM_ST_WAIT_SUBSCR_DATA] = { .T=-2 },
|
||||
[PROXY_MM_ST_WAIT_GSUP_ISD_RESULT] = { .T=-3 },
|
||||
[PROXY_MM_ST_WAIT_AUTH_TUPLES] = { .T=-4 },
|
||||
};
|
||||
|
||||
#define proxy_mm_fsm_state_chg(state) \
|
||||
osmo_tdef_fsm_inst_state_chg(mm_fi, state, \
|
||||
proxy_mm_fsm_timeouts, \
|
||||
proxy_mm_tdefs, \
|
||||
5)
|
||||
|
||||
LLIST_HEAD(proxy_mm_list);
|
||||
|
||||
struct proxy_mm *proxy_mm_alloc(const struct osmo_gsup_peer_id *vlr_name,
|
||||
bool is_ps,
|
||||
const char *imsi)
|
||||
{
|
||||
struct proxy_mm *proxy_mm;
|
||||
|
||||
struct osmo_fsm_inst *mm_fi = osmo_fsm_inst_alloc(&proxy_mm_fsm, g_hlr, NULL, LOGL_DEBUG, imsi);
|
||||
OSMO_ASSERT(mm_fi);
|
||||
|
||||
proxy_mm = talloc(mm_fi, struct proxy_mm);
|
||||
OSMO_ASSERT(proxy_mm);
|
||||
mm_fi->priv = proxy_mm;
|
||||
*proxy_mm = (struct proxy_mm){
|
||||
.mm_fi = mm_fi,
|
||||
.is_ps = is_ps,
|
||||
};
|
||||
OSMO_STRLCPY_ARRAY(proxy_mm->imsi, imsi);
|
||||
INIT_LLIST_HEAD(&proxy_mm->auth_cache);
|
||||
|
||||
llist_add(&proxy_mm->entry, &proxy_mm_list);
|
||||
|
||||
proxy_mm->to_home_fi = osmo_fsm_inst_alloc_child(&proxy_to_home_fsm, mm_fi, PROXY_MM_EV_SUBSCR_INVALID);
|
||||
proxy_mm->to_home_fi->priv = proxy_mm;
|
||||
|
||||
/* Do a state change to activate timeout */
|
||||
proxy_mm_fsm_state_chg(PROXY_MM_ST_READY);
|
||||
|
||||
return proxy_mm;
|
||||
}
|
||||
|
||||
void proxy_mm_add_auth_vectors(struct proxy_mm *proxy_mm,
|
||||
const struct osmo_auth_vector *auth_vectors, size_t num_auth_vectors)
|
||||
{
|
||||
struct proxy_mm_auth_cache *ac = talloc_zero(proxy_mm, struct proxy_mm_auth_cache);
|
||||
int i;
|
||||
OSMO_ASSERT(ac);
|
||||
ac->num_auth_vectors = num_auth_vectors;
|
||||
for (i = 0; i < num_auth_vectors; i++)
|
||||
ac->auth_vectors[i] = auth_vectors[i];
|
||||
if (proxy_db_add_auth_vectors(&proxy_mm->vlr_name, ac)) {
|
||||
talloc_free(ac);
|
||||
return;
|
||||
}
|
||||
llist_add(&ac->entry, &proxy_mm->auth_cache);
|
||||
}
|
||||
|
||||
struct proxy_mm_auth_cache *proxy_mm_get_auth_vectors(struct proxy_mm *proxy_mm)
|
||||
{
|
||||
struct proxy_mm_auth_cache *i;
|
||||
struct proxy_mm_auth_cache *ac = NULL;
|
||||
|
||||
llist_for_each_entry(i, &proxy_mm->auth_cache, entry) {
|
||||
if (!ac || i->sent_to_vlr_count < ac->sent_to_vlr_count) {
|
||||
ac = i;
|
||||
}
|
||||
}
|
||||
|
||||
/* ac now points to (one of) the least used auth cache entries (or NULL if none). */
|
||||
return ac;
|
||||
}
|
||||
|
||||
void proxy_mm_discard_auth_vectors(struct proxy_mm *proxy_mm, struct proxy_mm_auth_cache *ac)
|
||||
{
|
||||
proxy_db_drop_auth_vectors(ac->db_id);
|
||||
llist_del(&ac->entry);
|
||||
talloc_free(ac);
|
||||
}
|
||||
|
||||
/* Mark given auth cache entries as sent to the VLR and clean up if necessary. */
|
||||
void proxy_mm_use_auth_vectors(struct proxy_mm *proxy_mm, struct proxy_mm_auth_cache *ac)
|
||||
{
|
||||
struct proxy_mm_auth_cache *i, *i_next;
|
||||
bool found_fresh_ac = false;
|
||||
|
||||
/* The aim is to keep at least one set of already used auth tuples in the cache. If there are still fresh ones,
|
||||
* all used auth vectors can be discarded. If there are no fresh ones left, keep only this last set. */
|
||||
|
||||
llist_for_each_entry_safe(i, i_next, &proxy_mm->auth_cache, entry) {
|
||||
if (i == ac)
|
||||
continue;
|
||||
if (i->sent_to_vlr_count) {
|
||||
/* An auth entry other than this freshly used one, which has been used before.
|
||||
* No need to keep it. */
|
||||
proxy_mm_discard_auth_vectors(proxy_mm, i);
|
||||
continue;
|
||||
}
|
||||
if (!i->sent_to_vlr_count)
|
||||
found_fresh_ac = true;
|
||||
}
|
||||
|
||||
if (found_fresh_ac) {
|
||||
/* There are still other, fresh auth vectors. */
|
||||
proxy_mm_discard_auth_vectors(proxy_mm, ac);
|
||||
} else {
|
||||
/* else, only this ac remains in the list */
|
||||
ac->sent_to_vlr_count++;
|
||||
proxy_db_auth_vectors_update_sent_count(ac);
|
||||
}
|
||||
}
|
||||
|
||||
static void proxy_mm_ready_action(struct osmo_fsm_inst *mm_fi, uint32_t event, void *data)
|
||||
{
|
||||
struct proxy *proxy = g_hlr->gs->proxy;
|
||||
struct proxy_mm *proxy_mm = mm_fi->priv;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case PROXY_MM_EV_SUBSCR_INVALID:
|
||||
/* Home HLR has responded and rejected a Location Updating, or no home HLR could be found. The
|
||||
* subscriber is invalid, remove it from the cache. */
|
||||
proxy_subscr_del(proxy, proxy_mm->imsi);
|
||||
osmo_fsm_inst_term(mm_fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
break;
|
||||
|
||||
case PROXY_MM_EV_RX_GSUP_LU:
|
||||
/* The MSC asks for a LU. If we don't know details about this subscriber, then we'll have to wait for the
|
||||
* home HLR to respond. If we already know details about the subscriber, we respond immediately (with
|
||||
* Insert Subscriber Data and accept the LU), but also ask the home HLR to confirm the LU later. */
|
||||
osmo_fsm_inst_dispatch(proxy_mm->to_home_fi, PROXY_TO_HOME_EV_CONFIRM_LU, NULL);
|
||||
|
||||
if (proxy_mm_subscriber_data_known(proxy_mm))
|
||||
proxy_mm_fsm_state_chg(PROXY_MM_ST_WAIT_GSUP_ISD_RESULT);
|
||||
else
|
||||
proxy_mm_fsm_state_chg(PROXY_MM_ST_WAIT_SUBSCR_DATA);
|
||||
break;
|
||||
|
||||
case PROXY_MM_EV_RX_GSUP_SAI:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static int proxy_mm_ready_timeout(struct osmo_fsm_inst *mm_fi)
|
||||
{
|
||||
/* Return 1 to terminate FSM instance, 0 to keep running */
|
||||
return 1;
|
||||
}
|
||||
|
||||
void proxy_mm_wait_subscr_data_onenter(struct osmo_fsm_inst *mm_fi, uint32_t prev_state)
|
||||
{
|
||||
//struct proxy_mm *proxy_mm = mm_fi->priv;
|
||||
// FIXME
|
||||
}
|
||||
|
||||
static void proxy_mm_wait_subscr_data_action(struct osmo_fsm_inst *mm_fi, uint32_t event, void *data)
|
||||
{
|
||||
//struct proxy_mm *proxy_mm = mm_fi->priv;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case PROXY_MM_EV_RX_SUBSCR_DATA:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static int proxy_mm_wait_subscr_data_timeout(struct osmo_fsm_inst *mm_fi)
|
||||
{
|
||||
/* Return 1 to terminate FSM instance, 0 to keep running */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void proxy_mm_lu_error(struct osmo_fsm_inst *mm_fi)
|
||||
{
|
||||
osmo_gsup_req_respond_err(req, GMM_CAUSE_ROAMING_NOTALLOWED,
|
||||
"LU does not accept GSUP rx");
|
||||
|
||||
}
|
||||
|
||||
void proxy_mm_wait_gsup_isd_result_onenter(struct osmo_fsm_inst *mm_fi, uint32_t prev_state)
|
||||
{
|
||||
struct proxy_mm *proxy_mm = mm_fi->priv;
|
||||
struct proxy_subscr proxy_subscr;
|
||||
struct osmo_gsup_message isd_req;
|
||||
|
||||
uint8_t msisdn_enc[OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN];
|
||||
uint8_t apn[APN_MAXLEN];
|
||||
|
||||
isd_req.message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST;
|
||||
|
||||
if (proxy_subscr_get_by_imsi(&proxy_subscr, g_hlr->gs->proxy, proxy_mm->imsi)) {
|
||||
LOGPFSML(mm_fi, LOGL_ERROR,
|
||||
"Proxy: trying to send cached Subscriber Data, but there is no proxy entry\n");
|
||||
proxy_mm_lu_error(mm_fi);
|
||||
return;
|
||||
}
|
||||
|
||||
if (proxy_subscr.msisdn[0] == '\0') {
|
||||
LOGPFSML(mm_fi, LOGL_ERROR,
|
||||
"Proxy: trying to send cached Subscriber Data, but subscriber has no MSISDN in proxy cache\n");
|
||||
proxy_mm_lu_error(mm_fi);
|
||||
return;
|
||||
}
|
||||
|
||||
if (osmo_gsup_create_insert_subscriber_data_msg(&isd_req, proxy_mm->imsi,
|
||||
proxy_subscr->msisdn, msisdn_enc, sizeof(msisdn_enc),
|
||||
apn, sizeof(apn),
|
||||
proxy_mm->is_ps? OSMO_GSUP_CN_DOMAIN_PS : OSMO_GSUP_CN_DOMAIN_CS)) {
|
||||
LOGPFSML(mm_fi, LOGL_ERROR, "Proxy: failed to send cached Subscriber Data\n");
|
||||
proxy_mm_lu_error(mm_fi);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void proxy_mm_wait_gsup_isd_result_action(struct osmo_fsm_inst *mm_fi, uint32_t event, void *data)
|
||||
{
|
||||
//struct proxy_mm *proxy_mm = mm_fi->priv;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case PROXY_MM_EV_RX_GSUP_ISD_RESULT:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static int proxy_mm_wait_gsup_isd_result_timeout(struct osmo_fsm_inst *mm_fi)
|
||||
{
|
||||
/* Return 1 to terminate FSM instance, 0 to keep running */
|
||||
return 1;
|
||||
}
|
||||
|
||||
void proxy_mm_wait_auth_tuples_onenter(struct osmo_fsm_inst *mm_fi, uint32_t prev_state)
|
||||
{
|
||||
//struct proxy_mm *proxy_mm = mm_fi->priv;
|
||||
// FIXME
|
||||
}
|
||||
|
||||
static void proxy_mm_wait_auth_tuples_action(struct osmo_fsm_inst *mm_fi, uint32_t event, void *data)
|
||||
{
|
||||
//struct proxy_mm *proxy_mm = mm_fi->priv;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case PROXY_MM_EV_RX_AUTH_TUPLES:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static int proxy_mm_wait_auth_tuples_timeout(struct osmo_fsm_inst *mm_fi)
|
||||
{
|
||||
/* Return 1 to terminate FSM instance, 0 to keep running */
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
static const struct osmo_fsm_state proxy_mm_fsm_states[] = {
|
||||
[PROXY_MM_ST_READY] = {
|
||||
.name = "ready",
|
||||
.in_event_mask = 0
|
||||
| S(PROXY_MM_EV_SUBSCR_INVALID)
|
||||
| S(PROXY_MM_EV_RX_GSUP_LU)
|
||||
| S(PROXY_MM_EV_RX_GSUP_SAI)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(PROXY_MM_ST_READY)
|
||||
| S(PROXY_MM_ST_WAIT_SUBSCR_DATA)
|
||||
| S(PROXY_MM_ST_WAIT_GSUP_ISD_RESULT)
|
||||
| S(PROXY_MM_ST_WAIT_AUTH_TUPLES)
|
||||
,
|
||||
.action = proxy_mm_ready_action,
|
||||
},
|
||||
[PROXY_MM_ST_WAIT_SUBSCR_DATA] = {
|
||||
.name = "wait_subscr_data",
|
||||
.in_event_mask = 0
|
||||
| S(PROXY_MM_EV_RX_SUBSCR_DATA)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(PROXY_MM_ST_WAIT_GSUP_ISD_RESULT)
|
||||
| S(PROXY_MM_ST_READY)
|
||||
,
|
||||
.onenter = proxy_mm_wait_subscr_data_onenter,
|
||||
.action = proxy_mm_wait_subscr_data_action,
|
||||
},
|
||||
[PROXY_MM_ST_WAIT_GSUP_ISD_RESULT] = {
|
||||
.name = "wait_gsup_isd_result",
|
||||
.in_event_mask = 0
|
||||
| S(PROXY_MM_EV_RX_GSUP_ISD_RESULT)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(PROXY_MM_ST_READY)
|
||||
,
|
||||
.onenter = proxy_mm_wait_gsup_isd_result_onenter,
|
||||
.action = proxy_mm_wait_gsup_isd_result_action,
|
||||
},
|
||||
[PROXY_MM_ST_WAIT_AUTH_TUPLES] = {
|
||||
.name = "wait_auth_tuples",
|
||||
.in_event_mask = 0
|
||||
| S(PROXY_MM_EV_RX_AUTH_TUPLES)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(PROXY_MM_ST_READY)
|
||||
,
|
||||
.onenter = proxy_mm_wait_auth_tuples_onenter,
|
||||
.action = proxy_mm_wait_auth_tuples_action,
|
||||
},
|
||||
};
|
||||
|
||||
static int proxy_mm_fsm_timer_cb(struct osmo_fsm_inst *mm_fi)
|
||||
{
|
||||
//struct proxy_mm *proxy_mm = mm_fi->priv;
|
||||
switch (mm_fi->state) {
|
||||
|
||||
case PROXY_MM_ST_READY:
|
||||
return proxy_mm_ready_timeout(mm_fi);
|
||||
|
||||
case PROXY_MM_ST_WAIT_SUBSCR_DATA:
|
||||
return proxy_mm_wait_subscr_data_timeout(mm_fi);
|
||||
|
||||
case PROXY_MM_ST_WAIT_GSUP_ISD_RESULT:
|
||||
return proxy_mm_wait_gsup_isd_result_timeout(mm_fi);
|
||||
|
||||
case PROXY_MM_ST_WAIT_AUTH_TUPLES:
|
||||
return proxy_mm_wait_auth_tuples_timeout(mm_fi);
|
||||
|
||||
default:
|
||||
/* Return 1 to terminate FSM instance, 0 to keep running */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void proxy_mm_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
|
||||
{
|
||||
struct proxy_mm *proxy_mm = fi->priv;
|
||||
llist_del(&proxy_mm->entry);
|
||||
}
|
||||
|
||||
static struct osmo_fsm proxy_mm_fsm = {
|
||||
.name = "proxy_mm",
|
||||
.states = proxy_mm_fsm_states,
|
||||
.num_states = ARRAY_SIZE(proxy_mm_fsm_states),
|
||||
.log_subsys = DLGLOBAL, // FIXME
|
||||
.event_names = proxy_mm_fsm_event_names,
|
||||
.timer_cb = proxy_mm_fsm_timer_cb,
|
||||
.cleanup = proxy_mm_fsm_cleanup,
|
||||
};
|
||||
|
||||
static __attribute__((constructor)) void proxy_mm_fsm_register(void)
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&proxy_mm_fsm) == 0);
|
||||
}
|
||||
|
||||
bool proxy_mm_subscriber_data_known(const struct proxy_mm *proxy_mm)
|
||||
{
|
||||
struct proxy_subscr proxy_subscr;
|
||||
if (proxy_subscr_get_by_imsi(&proxy_subscr, g_hlr->gs->proxy, proxy_mm->imsi))
|
||||
return false;
|
||||
return proxy_subscr.msisdn[0] != '\0';
|
||||
}
|
|
@ -0,0 +1,439 @@
|
|||
|
||||
#include <osmocom/hlr/proxy_mm.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
|
||||
enum proxy_to_home_fsm_state {
|
||||
PROXY_TO_HOME_ST_WAIT_HOME_HLR_RESOLVED,
|
||||
PROXY_TO_HOME_ST_WAIT_UPDATE_LOCATION_RESULT,
|
||||
PROXY_TO_HOME_ST_WAIT_SEND_AUTH_INFO_RESULT,
|
||||
PROXY_TO_HOME_ST_IDLE,
|
||||
PROXY_TO_HOME_ST_CLEAR,
|
||||
};
|
||||
|
||||
static const struct value_string proxy_to_home_fsm_event_names[] = {
|
||||
OSMO_VALUE_STRING(PROXY_TO_HOME_EV_HOME_HLR_RESOLVED),
|
||||
OSMO_VALUE_STRING(PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ),
|
||||
OSMO_VALUE_STRING(PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT),
|
||||
OSMO_VALUE_STRING(PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT),
|
||||
OSMO_VALUE_STRING(PROXY_TO_HOME_EV_CHECK_TUPLES),
|
||||
OSMO_VALUE_STRING(PROXY_TO_HOME_EV_CONFIRM_LU),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct osmo_fsm proxy_to_home_fsm;
|
||||
|
||||
struct osmo_tdef proxy_to_home_tdefs[] = {
|
||||
// FIXME
|
||||
{ .T=-1, .default_val=5, .desc="proxy_to_home wait_home_hlr_resolved timeout" },
|
||||
{ .T=-2, .default_val=5, .desc="proxy_to_home wait_update_location_result timeout" },
|
||||
{ .T=-3, .default_val=5, .desc="proxy_to_home wait_send_auth_info_result timeout" },
|
||||
{ .T=-4, .default_val=5, .desc="proxy_to_home idle timeout" },
|
||||
{ .T=-5, .default_val=5, .desc="proxy_to_home clear timeout" },
|
||||
{}
|
||||
};
|
||||
|
||||
#if 0
|
||||
static const struct osmo_tdef_state_timeout proxy_to_home_fsm_timeouts[32] = {
|
||||
// FIXME
|
||||
[PROXY_TO_HOME_ST_WAIT_HOME_HLR_RESOLVED] = { .T=-1 },
|
||||
[PROXY_TO_HOME_ST_WAIT_UPDATE_LOCATION_RESULT] = { .T=-2 },
|
||||
[PROXY_TO_HOME_ST_WAIT_SEND_AUTH_INFO_RESULT] = { .T=-3 },
|
||||
[PROXY_TO_HOME_ST_IDLE] = { .T=-4 },
|
||||
[PROXY_TO_HOME_ST_CLEAR] = { .T=-5 },
|
||||
};
|
||||
#endif
|
||||
|
||||
#define proxy_to_home_fsm_state_chg(state) \
|
||||
osmo_tdef_fsm_inst_state_chg(fi, state, \
|
||||
proxy_to_home_fsm_timeouts, \
|
||||
proxy_to_home_tdefs, \
|
||||
5)
|
||||
|
||||
void proxy_to_home_wait_home_hlr_resolved_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
//struct proxy_mm *proxy_mm = fi->priv;
|
||||
// FIXME
|
||||
}
|
||||
|
||||
static void proxy_to_home_wait_home_hlr_resolved_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
//struct proxy_mm *proxy_mm = fi->priv;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case PROXY_TO_HOME_EV_HOME_HLR_RESOLVED:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_CHECK_TUPLES:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_CONFIRM_LU:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static int proxy_to_home_wait_home_hlr_resolved_timeout(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
/* Return 1 to terminate FSM instance, 0 to keep running */
|
||||
return 1;
|
||||
}
|
||||
|
||||
void proxy_to_home_wait_update_location_result_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
//struct proxy_mm *proxy_mm = fi->priv;
|
||||
// FIXME
|
||||
}
|
||||
|
||||
static void proxy_to_home_wait_update_location_result_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
//struct proxy_mm *proxy_mm = fi->priv;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case PROXY_TO_HOME_EV_HOME_HLR_RESOLVED:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_CHECK_TUPLES:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_CONFIRM_LU:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static int proxy_to_home_wait_update_location_result_timeout(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
/* Return 1 to terminate FSM instance, 0 to keep running */
|
||||
return 1;
|
||||
}
|
||||
|
||||
void proxy_to_home_wait_send_auth_info_result_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
//struct proxy_mm *proxy_mm = fi->priv;
|
||||
// FIXME
|
||||
}
|
||||
|
||||
static void proxy_to_home_wait_send_auth_info_result_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
//struct proxy_mm *proxy_mm = fi->priv;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case PROXY_TO_HOME_EV_HOME_HLR_RESOLVED:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_CHECK_TUPLES:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_CONFIRM_LU:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static int proxy_to_home_wait_send_auth_info_result_timeout(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
/* Return 1 to terminate FSM instance, 0 to keep running */
|
||||
return 1;
|
||||
}
|
||||
|
||||
void proxy_to_home_idle_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
//struct proxy_mm *proxy_mm = fi->priv;
|
||||
// FIXME
|
||||
}
|
||||
|
||||
static void proxy_to_home_idle_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
//struct proxy_mm *proxy_mm = fi->priv;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case PROXY_TO_HOME_EV_HOME_HLR_RESOLVED:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_CHECK_TUPLES:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_CONFIRM_LU:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static int proxy_to_home_idle_timeout(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
/* Return 1 to terminate FSM instance, 0 to keep running */
|
||||
return 1;
|
||||
}
|
||||
|
||||
void proxy_to_home_clear_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
//struct proxy_mm *proxy_mm = fi->priv;
|
||||
// FIXME
|
||||
}
|
||||
|
||||
static void proxy_to_home_clear_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
//struct proxy_mm *proxy_mm = fi->priv;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case PROXY_TO_HOME_EV_HOME_HLR_RESOLVED:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_CHECK_TUPLES:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case PROXY_TO_HOME_EV_CONFIRM_LU:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static int proxy_to_home_clear_timeout(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
/* Return 1 to terminate FSM instance, 0 to keep running */
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
static const struct osmo_fsm_state proxy_to_home_fsm_states[] = {
|
||||
[PROXY_TO_HOME_ST_WAIT_HOME_HLR_RESOLVED] = {
|
||||
.name = "wait_home_hlr_resolved",
|
||||
.in_event_mask = 0
|
||||
| S(PROXY_TO_HOME_EV_HOME_HLR_RESOLVED)
|
||||
| S(PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ)
|
||||
| S(PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT)
|
||||
| S(PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT)
|
||||
| S(PROXY_TO_HOME_EV_CHECK_TUPLES)
|
||||
| S(PROXY_TO_HOME_EV_CONFIRM_LU)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(PROXY_TO_HOME_ST_WAIT_HOME_HLR_RESOLVED)
|
||||
| S(PROXY_TO_HOME_ST_WAIT_UPDATE_LOCATION_RESULT)
|
||||
| S(PROXY_TO_HOME_ST_WAIT_SEND_AUTH_INFO_RESULT)
|
||||
| S(PROXY_TO_HOME_ST_IDLE)
|
||||
| S(PROXY_TO_HOME_ST_CLEAR)
|
||||
,
|
||||
.onenter = proxy_to_home_wait_home_hlr_resolved_onenter,
|
||||
.action = proxy_to_home_wait_home_hlr_resolved_action,
|
||||
},
|
||||
[PROXY_TO_HOME_ST_WAIT_UPDATE_LOCATION_RESULT] = {
|
||||
.name = "wait_update_location_result",
|
||||
.in_event_mask = 0
|
||||
| S(PROXY_TO_HOME_EV_HOME_HLR_RESOLVED)
|
||||
| S(PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ)
|
||||
| S(PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT)
|
||||
| S(PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT)
|
||||
| S(PROXY_TO_HOME_EV_CHECK_TUPLES)
|
||||
| S(PROXY_TO_HOME_EV_CONFIRM_LU)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(PROXY_TO_HOME_ST_WAIT_HOME_HLR_RESOLVED)
|
||||
| S(PROXY_TO_HOME_ST_WAIT_UPDATE_LOCATION_RESULT)
|
||||
| S(PROXY_TO_HOME_ST_WAIT_SEND_AUTH_INFO_RESULT)
|
||||
| S(PROXY_TO_HOME_ST_IDLE)
|
||||
| S(PROXY_TO_HOME_ST_CLEAR)
|
||||
,
|
||||
.onenter = proxy_to_home_wait_update_location_result_onenter,
|
||||
.action = proxy_to_home_wait_update_location_result_action,
|
||||
},
|
||||
[PROXY_TO_HOME_ST_WAIT_SEND_AUTH_INFO_RESULT] = {
|
||||
.name = "wait_send_auth_info_result",
|
||||
.in_event_mask = 0
|
||||
| S(PROXY_TO_HOME_EV_HOME_HLR_RESOLVED)
|
||||
| S(PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ)
|
||||
| S(PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT)
|
||||
| S(PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT)
|
||||
| S(PROXY_TO_HOME_EV_CHECK_TUPLES)
|
||||
| S(PROXY_TO_HOME_EV_CONFIRM_LU)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(PROXY_TO_HOME_ST_WAIT_HOME_HLR_RESOLVED)
|
||||
| S(PROXY_TO_HOME_ST_WAIT_UPDATE_LOCATION_RESULT)
|
||||
| S(PROXY_TO_HOME_ST_WAIT_SEND_AUTH_INFO_RESULT)
|
||||
| S(PROXY_TO_HOME_ST_IDLE)
|
||||
| S(PROXY_TO_HOME_ST_CLEAR)
|
||||
,
|
||||
.onenter = proxy_to_home_wait_send_auth_info_result_onenter,
|
||||
.action = proxy_to_home_wait_send_auth_info_result_action,
|
||||
},
|
||||
[PROXY_TO_HOME_ST_IDLE] = {
|
||||
.name = "idle",
|
||||
.in_event_mask = 0
|
||||
| S(PROXY_TO_HOME_EV_HOME_HLR_RESOLVED)
|
||||
| S(PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ)
|
||||
| S(PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT)
|
||||
| S(PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT)
|
||||
| S(PROXY_TO_HOME_EV_CHECK_TUPLES)
|
||||
| S(PROXY_TO_HOME_EV_CONFIRM_LU)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(PROXY_TO_HOME_ST_WAIT_HOME_HLR_RESOLVED)
|
||||
| S(PROXY_TO_HOME_ST_WAIT_UPDATE_LOCATION_RESULT)
|
||||
| S(PROXY_TO_HOME_ST_WAIT_SEND_AUTH_INFO_RESULT)
|
||||
| S(PROXY_TO_HOME_ST_IDLE)
|
||||
| S(PROXY_TO_HOME_ST_CLEAR)
|
||||
,
|
||||
.onenter = proxy_to_home_idle_onenter,
|
||||
.action = proxy_to_home_idle_action,
|
||||
},
|
||||
[PROXY_TO_HOME_ST_CLEAR] = {
|
||||
.name = "clear",
|
||||
.in_event_mask = 0
|
||||
| S(PROXY_TO_HOME_EV_HOME_HLR_RESOLVED)
|
||||
| S(PROXY_TO_HOME_EV_RX_INSERT_SUBSCRIBER_DATA_REQ)
|
||||
| S(PROXY_TO_HOME_EV_RX_UPDATE_LOCATION_RESULT)
|
||||
| S(PROXY_TO_HOME_EV_RX_SEND_AUTH_INFO_RESULT)
|
||||
| S(PROXY_TO_HOME_EV_CHECK_TUPLES)
|
||||
| S(PROXY_TO_HOME_EV_CONFIRM_LU)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(PROXY_TO_HOME_ST_WAIT_HOME_HLR_RESOLVED)
|
||||
| S(PROXY_TO_HOME_ST_WAIT_UPDATE_LOCATION_RESULT)
|
||||
| S(PROXY_TO_HOME_ST_WAIT_SEND_AUTH_INFO_RESULT)
|
||||
| S(PROXY_TO_HOME_ST_IDLE)
|
||||
| S(PROXY_TO_HOME_ST_CLEAR)
|
||||
,
|
||||
.onenter = proxy_to_home_clear_onenter,
|
||||
.action = proxy_to_home_clear_action,
|
||||
},
|
||||
};
|
||||
|
||||
static int proxy_to_home_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
//struct proxy_mm *proxy_mm = fi->priv;
|
||||
switch (fi->state) {
|
||||
|
||||
case PROXY_TO_HOME_ST_WAIT_HOME_HLR_RESOLVED:
|
||||
return proxy_to_home_wait_home_hlr_resolved_timeout(fi);
|
||||
|
||||
case PROXY_TO_HOME_ST_WAIT_UPDATE_LOCATION_RESULT:
|
||||
return proxy_to_home_wait_update_location_result_timeout(fi);
|
||||
|
||||
case PROXY_TO_HOME_ST_WAIT_SEND_AUTH_INFO_RESULT:
|
||||
return proxy_to_home_wait_send_auth_info_result_timeout(fi);
|
||||
|
||||
case PROXY_TO_HOME_ST_IDLE:
|
||||
return proxy_to_home_idle_timeout(fi);
|
||||
|
||||
case PROXY_TO_HOME_ST_CLEAR:
|
||||
return proxy_to_home_clear_timeout(fi);
|
||||
|
||||
default:
|
||||
/* Return 1 to terminate FSM instance, 0 to keep running */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void proxy_to_home_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
|
||||
{
|
||||
//struct proxy_mm *proxy_mm = fi->priv;
|
||||
// FIXME
|
||||
}
|
||||
|
||||
static struct osmo_fsm proxy_to_home_fsm = {
|
||||
.name = "proxy_to_home",
|
||||
.states = proxy_to_home_fsm_states,
|
||||
.num_states = ARRAY_SIZE(proxy_to_home_fsm_states),
|
||||
.log_subsys = DLGLOBAL, // FIXME
|
||||
.event_names = proxy_to_home_fsm_event_names,
|
||||
.timer_cb = proxy_to_home_fsm_timer_cb,
|
||||
.cleanup = proxy_to_home_fsm_cleanup,
|
||||
};
|
||||
|
||||
static __attribute__((constructor)) void proxy_to_home_fsm_register(void)
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&proxy_to_home_fsm) == 0);
|
||||
}
|
Loading…
Reference in New Issue