849 lines
23 KiB
C++
849 lines
23 KiB
C++
/**@file Declarations for all externally-visible control-layer functions. */
|
|
/*
|
|
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
|
*
|
|
* This software is distributed under the terms of the GNU Affero Public License.
|
|
* See the COPYING file in the main directory for details.
|
|
*
|
|
* This use of this software may be subject to additional restrictions.
|
|
* See the LEGAL file in the main directory for details.
|
|
|
|
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/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef CONTROLCOMMON_H
|
|
#define CONTROLCOMMON_H
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <list>
|
|
|
|
#include <Logger.h>
|
|
#include <Interthread.h>
|
|
#include <Timeval.h>
|
|
|
|
|
|
#include <GSML3CommonElements.h>
|
|
#include <GSML3MMElements.h>
|
|
#include <GSML3CCElements.h>
|
|
#include <GSML3RRMessages.h>
|
|
#include <SIPEngine.h>
|
|
|
|
|
|
// Enough forward refs to prevent "kitchen sick" includes and circularity.
|
|
|
|
namespace GSM {
|
|
class Time;
|
|
class L3Message;
|
|
class GSMConfig;
|
|
class LogicalChannel;
|
|
class SDCCHLogicalChannel;
|
|
class CCCHLogicalChannel;
|
|
class TCHFACCHLogicalChannel;
|
|
class L3Cause;
|
|
class L3CMServiceRequest;
|
|
class L3LocationUpdatingRequest;
|
|
class L3IMSIDetachIndication;
|
|
class L3PagingResponse;
|
|
};
|
|
|
|
|
|
/**@namespace Control This namepace is for use by the control layer. */
|
|
namespace Control {
|
|
|
|
class TransactionEntry;
|
|
class TransactionTable;
|
|
|
|
/**@name Call control time-out values (in ms) from ITU-T Q.931 Table 9-1 and GSM 04.08 Table 11.4. */
|
|
//@{
|
|
#ifndef RACETEST
|
|
const unsigned T301ms=60000; ///< recv ALERT --> recv CONN
|
|
const unsigned T302ms=12000; ///< send SETUP ACK --> any progress
|
|
const unsigned T303ms=10000; ///< send SETUP --> recv CALL CONF or REL COMP
|
|
const unsigned T304ms=20000; ///< recv SETUP ACK --> any progress
|
|
const unsigned T305ms=30000; ///< send DISC --> recv REL or DISC
|
|
const unsigned T308ms=30000; ///< send REL --> rev REL or REL COMP
|
|
const unsigned T310ms=30000; ///< recv CALL CONF --> recv ALERT, CONN, or DISC
|
|
const unsigned T313ms=30000; ///< send CONNECT --> recv CONNECT ACK
|
|
#else
|
|
// These are reduced values to force testing of poor network behavior.
|
|
const unsigned T301ms=18000; ///< recv ALERT --> recv CONN
|
|
const unsigned T302ms=1200; ///< send SETUP ACK --> any progress
|
|
const unsigned T303ms=400; ///< send SETUP --> recv CALL CONF or REL COMP
|
|
const unsigned T304ms=2000; ///< recv SETUP ACK --> any progress
|
|
const unsigned T305ms=3000; ///< send DISC --> recv REL or DISC
|
|
const unsigned T308ms=3000; ///< send REL --> rev REL or REL COMP
|
|
const unsigned T310ms=3000; ///< recv CALL CONF --> recv ALERT, CONN, or DISC
|
|
const unsigned T313ms=3000; ///< send CONNECT --> recv CONNECT ACK
|
|
#endif
|
|
//@}
|
|
|
|
|
|
/**@name Common-use functions from the control layer. */
|
|
//@{
|
|
|
|
/**
|
|
Common-use function to block on a channel until a given primitive arrives.
|
|
Any payload is discarded.
|
|
@param LCH The logcial channel.
|
|
@param primitive The primitive to wait for.
|
|
@param timeout_ms The timeout in milliseconds.
|
|
@return True on success, false on timeout.
|
|
*/
|
|
bool waitForPrimitive(GSM::LogicalChannel *LCH,
|
|
GSM::Primitive primitive,
|
|
unsigned timeout_ms);
|
|
|
|
/**
|
|
Common-use function to block on a channel until a given primitive arrives.
|
|
Any payload is discarded. Block indefinitely, no timeout.
|
|
@param LCH The logcial channel.
|
|
@param primitive The primitive to wait for.
|
|
*/
|
|
void waitForPrimitive(GSM::LogicalChannel *LCH,
|
|
GSM::Primitive primitive);
|
|
|
|
/**
|
|
Get a message from a LogicalChannel.
|
|
Close the channel with abnormal release on timeout.
|
|
Caller must delete the returned pointer.
|
|
Throws ChannelReadTimeout, UnexpecedPrimitive or UnsupportedMessage on timeout.
|
|
@param LCH The channel to receive on.
|
|
@param SAPI The service access point.
|
|
@return Pointer to message.
|
|
*/
|
|
// FIXME -- This needs an adjustable timeout.
|
|
GSM::L3Message* getMessage(GSM::LogicalChannel* LCH, unsigned SAPI=0);
|
|
|
|
/**
|
|
Clear the state information associated with a TransactionEntry.
|
|
Removes the transaction data from both gSIPInterfaceMap and gTransactionTable.
|
|
@param transaction The transaction to clear.
|
|
*/
|
|
void clearTransactionHistory(TransactionEntry& transaction);
|
|
|
|
/**
|
|
Clear the state information associated with a TransactionEntry.
|
|
Removes the transaction data from both gSIPInterfaceMap and gTransactionTable.
|
|
@param transactionID The ID of the transaction.
|
|
*/
|
|
void clearTransactionHistory(unsigned transactionID);
|
|
|
|
//@}
|
|
|
|
|
|
|
|
/**@name Functions for mobility manangement operations. */
|
|
//@{
|
|
void CMServiceResponder(const GSM::L3CMServiceRequest* cmsrq, GSM::LogicalChannel* DCCH);
|
|
void IMSIDetachController(const GSM::L3IMSIDetachIndication* idi, GSM::LogicalChannel* DCCH);
|
|
void LocationUpdatingController(const GSM::L3LocationUpdatingRequest* lur, GSM::SDCCHLogicalChannel* SDCCH);
|
|
//@}
|
|
|
|
/**@name Functions for radio resource operations. */
|
|
//@{
|
|
/** Decode RACH bits and send an immediate assignment. */
|
|
void AccessGrantResponder(
|
|
unsigned requestReference, const GSM::Time& when,
|
|
float RSSI, float timingError);
|
|
/** Find and compelte the in-process transaction associated with a paging repsonse. */
|
|
void PagingResponseHandler(const GSM::L3PagingResponse*, GSM::LogicalChannel*);
|
|
/** Find and compelte the in-process transaction associated with a completed assignment. */
|
|
void AssignmentCompleteHandler(const GSM::L3AssignmentComplete*, GSM::TCHFACCHLogicalChannel*);
|
|
//@}
|
|
|
|
/**@name Functions for call control operations. */
|
|
//@{
|
|
/**@name MOC */
|
|
//@{
|
|
/** Run the MOC to the point of alerting, doing early assignment if needed. */
|
|
void MOCStarter(const GSM::L3CMServiceRequest*, GSM::LogicalChannel*);
|
|
/** Complete the MOC connection. */
|
|
void MOCController(TransactionEntry&, GSM::TCHFACCHLogicalChannel*);
|
|
/** Set up an emergency call, assuming very early assignment. */
|
|
void EmergencyCall(const GSM::L3CMServiceRequest*, GSM::LogicalChannel*);
|
|
//@}
|
|
/**@name MTC */
|
|
//@{
|
|
/** Run the MTC to the point of alerting, doing early assignment if needed. */
|
|
void MTCStarter(TransactionEntry&, GSM::LogicalChannel*);
|
|
/** Complete the MTC connection. */
|
|
void MTCController(TransactionEntry&, GSM::TCHFACCHLogicalChannel*);
|
|
//@}
|
|
/**@name Test Call */
|
|
//@{
|
|
/** Run the test call. */
|
|
void TestCall(TransactionEntry&, GSM::LogicalChannel*);
|
|
//@}
|
|
/**@name SMS */
|
|
//@{
|
|
|
|
/** MOSMS state machine. */
|
|
void MOSMSController(const GSM::L3CMServiceRequest *req,
|
|
GSM::LogicalChannel *LCH);
|
|
/**
|
|
Basic SMS delivery from an established CM.
|
|
On exit, SAP3 will be in ABM and LCH will still be open.
|
|
Throws exception for failures in connection layer or for parsing failure.
|
|
@return true on success in relay layer.
|
|
*/
|
|
bool deliverSMSToMS(const char *callingPartyDigits, const char* message, unsigned TI, GSM::LogicalChannel *LCH);
|
|
|
|
/** MTSMS */
|
|
void MTSMSController(TransactionEntry& transaction,
|
|
GSM::LogicalChannel *LCH);
|
|
//@}
|
|
|
|
/** Create a new transaction entry and start paging. */
|
|
void initiateMTTransaction(TransactionEntry& transaction,
|
|
GSM::ChannelType chanType, unsigned pageTime);
|
|
|
|
//@}
|
|
|
|
/**@name Dispatch controllers for specific channel types. */
|
|
//@{
|
|
void FACCHDispatcher(GSM::TCHFACCHLogicalChannel *TCHFACCH);
|
|
void SDCCHDispatcher(GSM::SDCCHLogicalChannel *SDCCH);
|
|
void DCCHDispatcher(GSM::LogicalChannel *DCCH);
|
|
//@}
|
|
|
|
|
|
|
|
/**
|
|
Resolve a mobile ID to an IMSI.
|
|
Returns TMSI, if it is already in the TMSITable.
|
|
@param sameLAI True if the mobileID is known to have come from this LAI.
|
|
@param mobID A mobile ID, that may be modified by the function.
|
|
@param SDCCH The Dm channel to the mobile.
|
|
@return A TMSI value from the TMSITable or zero if non found.
|
|
*/
|
|
unsigned resolveIMSI(bool sameLAI, GSM::L3MobileIdentity& mobID, GSM::LogicalChannel* LCH);
|
|
|
|
/**
|
|
Resolve a mobile ID to an IMSI.
|
|
@param mobID A mobile ID, that may be modified by the function.
|
|
@param SDCCH The Dm channel to the mobile.
|
|
*/
|
|
void resolveIMSI(GSM::L3MobileIdentity& mobID, GSM::LogicalChannel* LCH);
|
|
|
|
|
|
|
|
|
|
/**@ Paging mechanisms */
|
|
//@{
|
|
|
|
|
|
/** An entry in the paging list. */
|
|
class PagingEntry {
|
|
|
|
private:
|
|
|
|
GSM::L3MobileIdentity mID; ///< The mobile ID.
|
|
GSM::ChannelType mType; ///< The needed channel type.
|
|
unsigned mTransactionID; ///< The associated transaction ID.
|
|
Timeval mExpiration; ///< The expiration time for this entry.
|
|
|
|
public:
|
|
|
|
/**
|
|
Create a new entry, with current timestamp.
|
|
@param wID The ID to be paged.
|
|
@param wLife The number of milliseconds to keep paging.
|
|
*/
|
|
PagingEntry(const GSM::L3MobileIdentity& wID, GSM::ChannelType wType,
|
|
unsigned wTransactionID, unsigned wLife)
|
|
:mID(wID),mType(wType),mTransactionID(wTransactionID),mExpiration(wLife)
|
|
{}
|
|
|
|
/** Access the ID. */
|
|
const GSM::L3MobileIdentity& ID() const { return mID; }
|
|
|
|
/** Access the channel type needed. */
|
|
GSM::ChannelType type() const { return mType; }
|
|
|
|
unsigned transactionID() const { return mTransactionID; }
|
|
|
|
/** Renew the timer. */
|
|
void renew(unsigned wLife) { mExpiration = Timeval(wLife); }
|
|
|
|
/** Returns true if the entry is expired. */
|
|
bool expired() const { return mExpiration.passed(); }
|
|
|
|
};
|
|
|
|
typedef std::list<PagingEntry> PagingEntryList;
|
|
|
|
|
|
/**
|
|
The pager is a global object that generates paging messages on the CCCH.
|
|
To page a mobile, add the mobile ID to the pager.
|
|
The entry will be deleted automatically when it expires.
|
|
All pager operations are linear time.
|
|
Not much point in optimizing since the main operation is inherently linear.
|
|
*/
|
|
class Pager {
|
|
|
|
private:
|
|
|
|
PagingEntryList mPageIDs; ///< List of ID's to be paged.
|
|
Mutex mLock; ///< Lock for thread-safe access.
|
|
Signal mPageSignal; ///< signal to wake the paging loop
|
|
Thread mPagingThread; ///< Thread for the paging loop.
|
|
volatile bool mRunning;
|
|
|
|
public:
|
|
|
|
Pager()
|
|
:mRunning(false)
|
|
{}
|
|
|
|
/** Set the output FIFO and start the paging loop. */
|
|
void start();
|
|
|
|
/**
|
|
Add a mobile ID to the paging list.
|
|
@param addID The mobile ID to be paged.
|
|
@param chanType The channel type to be requested.
|
|
@param transaction The transaction record, which will be modified.
|
|
@param wLife The paging duration in ms, default based on SIP INVITE retry preiod, Timer A.
|
|
*/
|
|
void addID(
|
|
const GSM::L3MobileIdentity& addID,
|
|
GSM::ChannelType chanType,
|
|
TransactionEntry& transaction,
|
|
unsigned wLife=2*gConfig.getNum("SIP.Timer.A")
|
|
);
|
|
|
|
/**
|
|
Remove a mobile ID.
|
|
This is used to stop the paging when a phone responds.
|
|
@return The transaction ID associated with this entry.
|
|
*/
|
|
unsigned removeID(const GSM::L3MobileIdentity&);
|
|
|
|
private:
|
|
|
|
/**
|
|
Traverse the paging list, paging all IDs.
|
|
@return Number of IDs paged.
|
|
*/
|
|
unsigned pageAll();
|
|
|
|
/** A loop that repeatedly calls pageAll. */
|
|
void serviceLoop();
|
|
|
|
/** C-style adapter. */
|
|
friend void *PagerServiceLoopAdapter(Pager*);
|
|
|
|
public:
|
|
|
|
/** return size of PagingEntryList */
|
|
size_t pagingEntryListSize();
|
|
|
|
/** Dump the paging list to an ostream. */
|
|
void dump(std::ostream&) const;
|
|
};
|
|
|
|
|
|
void *PagerServiceLoopAdapter(Pager*);
|
|
|
|
|
|
//@} // paging mech
|
|
|
|
|
|
|
|
|
|
|
|
/**@name Transaction Table mechanisms. */
|
|
//@{
|
|
|
|
/**
|
|
A TransactionEntry object is used to maintain the state of a transaction
|
|
as it moves from channel to channel.
|
|
The object itself is not thread safe.
|
|
*/
|
|
class TransactionEntry {
|
|
|
|
public:
|
|
|
|
/** Call states based on GSM 04.08 5 and ITU-T Q.931 */
|
|
enum Q931CallState {
|
|
NullState,
|
|
Paging,
|
|
MOCInitiated,
|
|
MOCProceeding,
|
|
MTCConfirmed,
|
|
CallReceived,
|
|
CallPresent,
|
|
ConnectIndication,
|
|
Active,
|
|
DisconnectIndication,
|
|
ReleaseRequest,
|
|
SMSDelivering,
|
|
SMSSubmitting,
|
|
};
|
|
|
|
private:
|
|
|
|
unsigned mID; ///< the internal transaction ID, assigned by a TransactionTable
|
|
|
|
GSM::L3MobileIdentity mSubscriber; ///< some kind of subscriber ID, preferably IMSI
|
|
GSM::L3CMServiceType mService; ///< the associated service type
|
|
unsigned mTIFlag; ///< "0" for originating party ,"1" for terminating
|
|
unsigned mTIValue; ///< the L3 short transaction ID set by the MS
|
|
GSM::L3CalledPartyBCDNumber mCalled; ///< the associated called party number, if known
|
|
GSM::L3CallingPartyBCDNumber mCalling; ///< the associated calling party number, if known
|
|
|
|
SIP::SIPEngine mSIP; ///< the SIP IETF RFC-3621 protocol engine
|
|
Q931CallState mQ931State; ///< the GSM/ISDN/Q.931 call state
|
|
Timeval mStateTimer; ///< timestamp of last state change.
|
|
|
|
std::string mMessage; ///< text messaging payload
|
|
|
|
/**@name Timers from GSM and Q.931 (network side) */
|
|
//@{
|
|
// If you add a timer, remember to add it to
|
|
// the constructor, timerExpired and resetTimers methods.
|
|
GSM::Z100Timer mT301;
|
|
GSM::Z100Timer mT302;
|
|
GSM::Z100Timer mT303;
|
|
GSM::Z100Timer mT304;
|
|
GSM::Z100Timer mT305; ///< a "clearing timer"
|
|
GSM::Z100Timer mT308; ///< a "clearing timer"
|
|
GSM::Z100Timer mT310;
|
|
GSM::Z100Timer mT313;
|
|
GSM::Z100Timer mT3113; ///< the paging timer, NOT a Q.931 timer
|
|
GSM::Z100Timer mTR1M; ///< SMS RP-ACK timer, see GSM 04.11 6.2.1.2
|
|
//@}
|
|
|
|
public:
|
|
|
|
TransactionEntry();
|
|
|
|
/** This form is used for MTC or MT-SMS with TI set to 0. */
|
|
TransactionEntry(const GSM::L3MobileIdentity& wSubscriber,
|
|
const GSM::L3CMServiceType& wService,
|
|
const GSM::L3CallingPartyBCDNumber& wCalling,
|
|
const char *wMessage = NULL);
|
|
|
|
/** This form is used for MOC. */
|
|
TransactionEntry(const GSM::L3MobileIdentity& wSubscriber,
|
|
const GSM::L3CMServiceType& wService,
|
|
unsigned wTIValue,
|
|
const GSM::L3CalledPartyBCDNumber& wCalled);
|
|
|
|
/** Another MT form, with controlled TI. */
|
|
TransactionEntry(const GSM::L3MobileIdentity& wSubscriber,
|
|
const GSM::L3CMServiceType& wService,
|
|
unsigned wTIValue,
|
|
const GSM::L3CallingPartyBCDNumber& wCalling);
|
|
|
|
/**@name Accessors. */
|
|
//@{
|
|
unsigned TIValue() const { return mTIValue; }
|
|
unsigned TIFlag() const { return mTIFlag; }
|
|
void TI(unsigned wTIFlag, unsigned wTIValue)
|
|
{ mTIFlag=wTIFlag; mTIValue = wTIValue; }
|
|
|
|
const GSM::L3MobileIdentity& subscriber() const { return mSubscriber; }
|
|
|
|
const GSM::L3CMServiceType& service() const { return mService; }
|
|
|
|
const GSM::L3CalledPartyBCDNumber& called() const { return mCalled; }
|
|
|
|
const GSM::L3CallingPartyBCDNumber& calling() const { return mCalling; }
|
|
|
|
const char* message() const { return mMessage.data(); }
|
|
void message(const char *wMessage)
|
|
{
|
|
mMessage = wMessage;
|
|
}
|
|
|
|
unsigned ID() const { return mID; }
|
|
|
|
SIP::SIPEngine& SIP() { return mSIP; }
|
|
const SIP::SIPEngine& SIP() const { return mSIP; }
|
|
|
|
void Q931State(Q931CallState wState)
|
|
{
|
|
mStateTimer.now();
|
|
mQ931State=wState;
|
|
}
|
|
|
|
Q931CallState Q931State() const { return mQ931State; }
|
|
|
|
unsigned stateAge() const { return mStateTimer.elapsed(); }
|
|
|
|
/**@name Timer access. */
|
|
// TODO -- If we were clever, this would be a table.
|
|
//@{
|
|
GSM::Z100Timer& T301() { return mT301; }
|
|
GSM::Z100Timer& T302() { return mT302; }
|
|
GSM::Z100Timer& T303() { return mT303; }
|
|
GSM::Z100Timer& T304() { return mT304; }
|
|
GSM::Z100Timer& T305() { return mT305; }
|
|
GSM::Z100Timer& T308() { return mT308; }
|
|
GSM::Z100Timer& T310() { return mT310; }
|
|
GSM::Z100Timer& T313() { return mT313; }
|
|
GSM::Z100Timer& T3113() { return mT3113; }
|
|
GSM::Z100Timer& TR1M() { return mTR1M; }
|
|
//@}
|
|
//@}
|
|
|
|
/** Return true if clearing is in progress. */
|
|
bool clearing() const
|
|
{ return (mQ931State==ReleaseRequest) || (mQ931State==DisconnectIndication); }
|
|
|
|
/** Return true if any Q.931 timer is expired. */
|
|
bool timerExpired() const;
|
|
|
|
/** Reset all Q.931 timers. */
|
|
void resetTimers();
|
|
|
|
/** Returns true if the transaction is "dead". */
|
|
bool dead() const;
|
|
|
|
private:
|
|
|
|
friend class TransactionTable;
|
|
|
|
void ID(unsigned wID) { mID=wID; }
|
|
};
|
|
|
|
|
|
std::ostream& operator<<(std::ostream& os, const TransactionEntry&);
|
|
std::ostream& operator<<(std::ostream& os, TransactionEntry::Q931CallState);
|
|
|
|
|
|
/** A map of transactions keyed by ID. */
|
|
class TransactionMap : public std::map<unsigned,TransactionEntry> {};
|
|
|
|
/**
|
|
A table for tracking the states of active transactions.
|
|
Note that transaction table add and find operations
|
|
are pass-by-copy, not pass-by-reference.
|
|
*/
|
|
class TransactionTable {
|
|
|
|
private:
|
|
|
|
// FIXME -- We need to support log-time lookup by transaction ID _or_ IMSI.
|
|
// Right now, it's log-time for transaction ID and linear time for IMSI.
|
|
|
|
TransactionMap mTable;
|
|
mutable Mutex mLock;
|
|
unsigned mIDCounter;
|
|
|
|
public:
|
|
|
|
TransactionTable()
|
|
// This assumes the main application uses sdevrandom.
|
|
:mIDCounter(random())
|
|
{ }
|
|
|
|
/**
|
|
Return a new ID for use in the table.
|
|
*/
|
|
unsigned newID();
|
|
|
|
/**
|
|
Insert a new entry into the table.
|
|
@param value The entry to copy into the table.
|
|
*/
|
|
void add(const TransactionEntry& value);
|
|
|
|
/**
|
|
Update a transaction in the table.
|
|
Uses the ID previously assigned to the TransactionEntry by add().
|
|
@param value The new TransactionEntry to be copied in.
|
|
*/
|
|
void update(const TransactionEntry& value);
|
|
|
|
/**
|
|
Find an entry and make a copy.
|
|
(Removes entry if it was dead.)
|
|
@param wID The transaction ID to search.
|
|
@param target A place to put the copy.
|
|
@return True if successful.
|
|
*/
|
|
bool find(unsigned wID, TransactionEntry& target);
|
|
|
|
/**
|
|
Remove an entry from the table.
|
|
@param wID The transaction ID to search.
|
|
@return True if the ID was really in the table.
|
|
*/
|
|
bool remove(unsigned wID);
|
|
|
|
/**
|
|
Find an entry by its mobile ID.
|
|
Also clears dead entries during search.
|
|
@param mobileID The mobile at to search for.
|
|
@param target A TransactionEntry to accept the found record.
|
|
@return true is the mobile ID was found.
|
|
*/
|
|
bool find(const GSM::L3MobileIdentity& mobileID, TransactionEntry& target);
|
|
|
|
/**
|
|
Remove "dead" entries from the table.
|
|
A "dead" entry is a transaction that is no longer active.
|
|
*/
|
|
void clearDeadEntries();
|
|
|
|
/**@Access to raw map. */
|
|
//@{
|
|
TransactionMap::const_iterator begin() { clearDeadEntries(); return mTable.begin(); }
|
|
TransactionMap::const_iterator end() const { return mTable.end(); }
|
|
//@}
|
|
|
|
size_t size();
|
|
|
|
/** Write entries as text to a stream. */
|
|
void dump(std::ostream&) const;
|
|
};
|
|
|
|
//@} // Transaction Table
|
|
|
|
|
|
|
|
|
|
/**@ TMSI mechanisms */
|
|
//@{
|
|
|
|
class TMSIRecord {
|
|
|
|
private:
|
|
|
|
std::string mIMSI;
|
|
std::string mIMEI;
|
|
Timeval mCreated; ///< Time when this TMSI was created.
|
|
mutable Timeval mTouched; ///< Time when this TMSI was last accessed.
|
|
|
|
public:
|
|
|
|
TMSIRecord() {}
|
|
|
|
TMSIRecord(const char* wIMSI, const char* wIMEI = NULL):
|
|
mIMSI(wIMSI), mIMEI(wIMEI!=NULL?wIMEI:"?")
|
|
{ }
|
|
|
|
const char* IMSI() const { return mIMSI.c_str(); }
|
|
const char* IMEI() const { return mIMEI.c_str(); }
|
|
void IMEI(const std::string &imei) { mIMEI = imei; }
|
|
void touch() const { mTouched.now(); }
|
|
|
|
/** Record age in seconds. */
|
|
unsigned age() const { return mCreated.elapsed()/1000; }
|
|
|
|
/** Time since last access in seconds. */
|
|
unsigned touched() const { return mTouched.elapsed()/1000; }
|
|
|
|
void save(unsigned TMSI, FILE*) const;
|
|
|
|
/**
|
|
Load a TMSI record from a file.
|
|
@return TMSI or 0 on read failure.
|
|
*/
|
|
unsigned load(FILE*);
|
|
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream&, const TMSIRecord&);
|
|
|
|
typedef std::map<unsigned,TMSIRecord> TMSIMap;
|
|
|
|
class TMSITable {
|
|
|
|
private:
|
|
|
|
TMSIMap mMap; ///< IMSI/TMSI mapping
|
|
unsigned mCounter; ///< a counter to generate new TMSIs
|
|
unsigned mClear; ///< next TMSI to be cleared from the table
|
|
mutable Mutex mLock; ///< concurrency control
|
|
|
|
|
|
public:
|
|
|
|
TMSITable()
|
|
:mCounter(time(NULL)),
|
|
mClear(mCounter)
|
|
{}
|
|
|
|
/**
|
|
Create a new entry in the table.
|
|
@param IMSI The IMSI to create an entry for.
|
|
@return The assigned TMSI.
|
|
*/
|
|
unsigned assign(const char* IMSI, const char* IMEI = NULL);
|
|
|
|
/**
|
|
Set IMEI for a selected TMSI.
|
|
@param TMSI The TMSI to set IMEI for
|
|
@param IMEI The IMEI to set.
|
|
@return true if the TMSI exists and we've set IMEI for it.
|
|
*/
|
|
bool setIMEI(unsigned TMSI, const std::string& IMEI);
|
|
|
|
/**
|
|
Find an IMSI in the table.
|
|
This is a log-time operation.
|
|
@param TMSI The TMSI to find.
|
|
@return Pointer to c-string IMSI or NULL.
|
|
*/
|
|
const char* IMSI(unsigned TMSI) const;
|
|
|
|
|
|
/**
|
|
Find an entry in the table.
|
|
This is a log-time operation.
|
|
@param TMSI The TMSI to find.
|
|
@param target A TMSI record to catch the result.
|
|
@return true if the TMSI was found.
|
|
*/
|
|
bool find(unsigned TMSI, TMSIRecord& target);
|
|
|
|
/**
|
|
Find a TMSI in the table.
|
|
This is a linear-time operation.
|
|
@param IMSI The IMSI to mach.
|
|
@return A TMSI value or zero on failure.
|
|
*/
|
|
unsigned TMSI(const char* IMSI) const;
|
|
|
|
/**
|
|
Update the record's timestamp.
|
|
*/
|
|
void touch(unsigned TMSI) const;
|
|
|
|
/**
|
|
Remove an entry from the table.
|
|
@param TMSI The TMSI to remove.
|
|
*/
|
|
void erase(unsigned TMSI);
|
|
|
|
/** Write entries as text to a stream. */
|
|
void dump(std::ostream&) const;
|
|
|
|
/** Save the table to a file. */
|
|
void save(const char* filename) const;
|
|
|
|
/** Load the table from a file. */
|
|
void load(const char*filename);
|
|
|
|
/** Clear the table completely. */
|
|
void clear() { mMap.clear(); }
|
|
|
|
size_t size() const;
|
|
|
|
// FIXME -- These are not thread safe and should be removed.
|
|
TMSIMap::const_iterator begin() const { return mMap.begin(); }
|
|
TMSIMap::const_iterator end() const { return mMap.end(); }
|
|
|
|
private:
|
|
|
|
/** Erase entries, oldest first, to limit the table size. */
|
|
void purge();
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**@name Control-layer exceptions. */
|
|
//@{
|
|
|
|
/**
|
|
A control layer excpection includes a pointer to a transaction.
|
|
The transaction might require some clean-up action, depending on the exception.
|
|
*/
|
|
class ControlLayerException {
|
|
|
|
private:
|
|
|
|
unsigned mTransactionID;
|
|
|
|
public:
|
|
|
|
ControlLayerException(unsigned wTransactionID=0)
|
|
:mTransactionID(wTransactionID)
|
|
{}
|
|
|
|
unsigned transactionID() { return mTransactionID; }
|
|
};
|
|
|
|
/** Thrown when the control layer gets the wrong message */
|
|
class UnexpectedMessage : public ControlLayerException {
|
|
public:
|
|
UnexpectedMessage(unsigned wTransactionID=0)
|
|
:ControlLayerException(wTransactionID)
|
|
{}
|
|
};
|
|
|
|
/** Thrown when recvL3 returns NULL */
|
|
class ChannelReadTimeout : public ControlLayerException {
|
|
public:
|
|
ChannelReadTimeout(unsigned wTransactionID=0)
|
|
:ControlLayerException(wTransactionID)
|
|
{}
|
|
};
|
|
|
|
/** Thrown when L3 can't parse an incoming message */
|
|
class UnsupportedMessage : public ControlLayerException {
|
|
public:
|
|
UnsupportedMessage(unsigned wTransactionID=0)
|
|
:ControlLayerException(wTransactionID)
|
|
{}
|
|
};
|
|
|
|
/** Thrown when the control layer gets the wrong primitive */
|
|
class UnexpectedPrimitive : public ControlLayerException {
|
|
public:
|
|
UnexpectedPrimitive(unsigned wTransactionID=0)
|
|
:ControlLayerException(wTransactionID)
|
|
{}
|
|
};
|
|
|
|
/** Thrown when a T3xx expires */
|
|
class Q931TimerExpired : public ControlLayerException {
|
|
public:
|
|
Q931TimerExpired(unsigned wTransactionID=0)
|
|
:ControlLayerException(wTransactionID)
|
|
{}
|
|
};
|
|
|
|
|
|
//@}
|
|
|
|
|
|
} //Control
|
|
|
|
|
|
|
|
/**@addtogroup Globals */
|
|
//@{
|
|
/** A single global transaction table in the global namespace. */
|
|
extern Control::TransactionTable gTransactionTable;
|
|
/** A single global TMSI table in the global namespace. */
|
|
extern Control::TMSITable gTMSITable;
|
|
//@}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
// vim: ts=4 sw=4
|