From 3d59f52a3f0c2782b9169d998f8f539a22597cdf Mon Sep 17 00:00:00 2001 From: Michael Iedema Date: Thu, 4 Dec 2014 21:25:19 +0100 Subject: [PATCH] sync from commercial 64a79ce7a18f7e3ef3fe5aeacf3b6629980d30b2 --- .gitignore | 1 + CLI/CLICommands.cpp | 152 ++- CLI/Makefile.am | 4 +- CommonLibs | 2 +- Control/CBS.cpp | 380 ++++++ Control/CBS.h | 121 ++ Control/L3Handover.cpp | 3 +- Control/L3LogicalChannel.cpp | 2 +- Control/L3MMLayer.cpp | 6 +- Control/L3MobilityManagement.cpp | 14 +- Control/L3SMSControl.cpp | 2 - Control/L3StateMachine.cpp | 2 + Control/L3TranEntry.cpp | 142 ++- Control/L3TranEntry.h | 6 +- Control/Makefile.am | 12 +- Control/RRLPServer.cpp | 0 Control/RRLPServer.h | 0 Control/RadioResource.cpp | 424 ------- Control/RadioResource.h | 177 --- Control/SMSCB.cpp | 205 ---- Control/TMSITable.cpp | 10 +- GPRS/Makefile.am | 7 +- GPRS/TBF.cpp | 2 +- GSM/GSMCCCH.cpp | 2 + GSM/GSMConfig.cpp | 37 +- GSM/GSMConfig.h | 14 +- GSM/GSML1FEC.cpp | 21 +- GSM/GSML1FEC.h | 13 +- GSM/GSML3CommonElements.h | 20 + GSM/GSML3Message.h | 1 + GSM/GSML3RRElements.h | 7 +- GSM/GSML3RRMessages.cpp | 11 +- GSM/GSMLogicalChannel.cpp | 34 +- GSM/GSMLogicalChannel.h | 21 +- GSM/GSMTransfer.cpp | 27 +- GSM/GSMTransfer.h | 10 +- GSM/Makefile.am | 2 +- GSMShare/Makefile.am | 17 +- Globals/Makefile.am | 4 +- Makefile.am | 11 +- Makefile.common | 9 +- NodeManager | 2 +- Peering/Makefile.am | 5 +- Peering/Peering.cpp | 58 +- SGSNGGSN/Makefile.am | 3 +- SGSNGGSN/miniggsn.cpp | 11 +- SIP/Makefile.am | 5 +- SIP/SIPBase.cpp | 22 +- SIP/SIPBase.h | 1 + SIP/SIPDialog.cpp | 19 +- SIP/SIPDialog.h | 1 - SIP/SIPParse.cpp | 8 +- SMS/Makefile.am | 4 +- SMS/SMSMessages.cpp | 2 + SMS/SMSMessages.h | 2 +- SMS/SMSTransfer.h | 8 +- Scanning/Makefile.am | 4 +- TRXManager/Makefile.am | 4 +- Transceiver52M/Makefile.am | 4 +- TransceiverRAD1/Makefile.am | 36 +- TransceiverRAD1/RAD1Device.cpp | 2 +- TransceiverRAD1/Transceiver.cpp | 2 +- TransceiverRAD1/runTransceiver.cpp | 2 +- apps/GetConfigurationKeys.cpp | 16 +- apps/Makefile.am | 34 +- apps/OpenBTS.cpp | 6 +- apps/OpenBTSCLI.cpp | 7 - apps/OpenBTSDo | 4 + apps/README.DatabaseCreation | 14 - .../{OpenBTS.logrotate => logrotated.OpenBTS} | 0 apps/openbts.conf | 24 + configure.ac | 86 +- debian/changelog | 4 +- debian/postinst | 16 + debian/postinst.old | 24 - debian/prerm.old | 7 - doxconfig | 1078 ----------------- package/deb-after-install.sh | 30 + package/deb-before-install.sh | 15 + package/rpm-after-install.sh | 31 + package/rpm-before-install.sh | 15 + 81 files changed, 1282 insertions(+), 2269 deletions(-) create mode 100644 Control/CBS.cpp create mode 100644 Control/CBS.h create mode 100644 Control/RRLPServer.cpp create mode 100644 Control/RRLPServer.h delete mode 100644 Control/RadioResource.cpp delete mode 100644 Control/RadioResource.h delete mode 100644 Control/SMSCB.cpp create mode 100755 apps/OpenBTSDo delete mode 100644 apps/README.DatabaseCreation rename apps/{OpenBTS.logrotate => logrotated.OpenBTS} (100%) mode change 100755 => 100644 delete mode 100755 debian/postinst.old delete mode 100755 debian/prerm.old delete mode 100644 doxconfig create mode 100755 package/deb-after-install.sh create mode 100755 package/deb-before-install.sh create mode 100755 package/rpm-after-install.sh create mode 100755 package/rpm-before-install.sh diff --git a/.gitignore b/.gitignore index b2c4f0c..bbd51de 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ *.la Makefile aclocal.m4 +autom4te.cache build-arch-stamp build-indep-stamp compile diff --git a/CLI/CLICommands.cpp b/CLI/CLICommands.cpp index 2724d68..74ab1db 100644 --- a/CLI/CLICommands.cpp +++ b/CLI/CLICommands.cpp @@ -46,6 +46,7 @@ #include #include #include +#include std::string getARFCNsString(unsigned band); @@ -282,6 +283,139 @@ static CLIStatus tmsis(int argc, char** argv, ostream& os) return SUCCESS; } +static const char *cbsHelp = "-- List or add Cell Broadcast Service messages.\n" + " Syntax: cbs [list | add | delete | clear] [-options] [\"message\"]\n" + " Options correspond to message parameters in GSM 3.41 9.3.2.\n" + " -gs -- geograhical scope (0=immediate, 1=PLMN wide, 2=Location Area Wide, 3=Cell Wide)\n" + " -code -- Message code\n" + " -un -- Message update number. The gs + code + un make up the message serial number.\n" + " -id -- Message Identifier (which is really the message type)\n" + // (pat) We dont support lanugages yet. There are three different ways to specify them so this simple idea is not good enough; + // There is an interface to the language below, but it is not hooked up in the CBS code. + //" -lang -- 2 character USC2 language specification as per from GSM 3.38 section 5, or - to reset.\n" + " -row -- OpenBTS row number of message in database; used only to delete a specific message.\n" + " -l -- list: longer listing.\n" + " -tab -- list: separate list output with tabs instead of spaces.\n" + ; +static CLIStatus cbscmd(int argc, char** argv, ostream& os) +{ + if (gConfig.getStr("Control.SMSCB.Table").size() == 0) { + os << "Warning: CBS service is currently disabled by Control.SMSCB.Table option"; + } + string text, subcmd; + if (argc > 1 && argv[1][0] != '-') { + subcmd = string(argv[1]); + argc--; argv++; + } + map options = cliParse(argc,argv,os,"-l -tab -gs: -code: -un: -id: -lang: -row:"); + //for (int i = 0; i < argc; i++) { printf("argv[%d]=%s\n",i,argv[i]); } + + if (subcmd == "" && argc) { // allow options before or after subcmd. + subcmd = string(argv[0]); + argc--; argv++; + } + if (argc) { + text = string(argv[0]); + argc--; argv++; + } + if (argc) return BAD_NUM_ARGS; + + + // Load up a CBMessage from the command line arguments. + CBMessage temp; // Message template. + { + string gs = options["-gs"]; + string code = options["-code"]; + string un = options["-un"]; + string id = options["-id"]; + string lang = options["-lang"]; + string row = options["-row"]; + + if (gs.size()) { + if (! temp.setGS(atoi(gs.c_str()))) { + os << "invalid -gs value; "; + return BAD_VALUE; + } + } + if (code.size()) { temp.setMessageCode(atoi(code.c_str())); } + if (un.size()) { temp.setUpdateNumber(atoi(un.c_str())); } + if (id.size()) { temp.setMessageId(atoi(id.c_str())); } + if (row.size()) { temp.setRow(atoi(row.c_str())); } + if (lang.size()) { + if (! temp.setLanguage(lang)) { + os << "invalid -lang value; "; + return BAD_VALUE; + } + } + if (text.size()) { temp.setMessageText(text); } + } + bool tabSeparated = !!options.count("-tab"); + bool verbose = !!options.count("-l"); + string errMsg; + + + // Do something. + if (subcmd == "clear") { + // clear (delete) all messages. + if (argc) return BAD_NUM_ARGS; + CBSClearMessages(); + } else if (subcmd == "delete") { + // delete matching messages + int cnt = CBSDeleteMessage(temp); + if (cnt) { + os << "Deleted "< msgs; + CBSGetMessages(msgs,text); + prettyTable_t tab; + vector vh; + if (verbose) { + tab.push_back(stringSplit(vh,"row gs msg update msg send send text")); vh.clear(); + tab.push_back(stringSplit(vh,"_ _ code number id count age ")); vh.clear(); + tab.push_back(stringSplit(vh,"--- -- ---- ------ ---- ----- ---- ----")); vh.clear(); + } else { + tab.push_back(stringSplit(vh,"row text")); vh.clear(); + tab.push_back(stringSplit(vh,"--- ----")); vh.clear(); + } + time_t now = time(NULL); + for (vector::iterator it = msgs.begin(); it != msgs.end(); it++) { + CBMessage &msg = *it; + vh.clear(); + vh.push_back(format("%d",(unsigned)msg.mRowId)); + if (verbose) { + vh.push_back(format("%u",(unsigned)msg.mGS)); + vh.push_back(format("%u",(unsigned)msg.mMessageCode)); + vh.push_back(format("%u",(unsigned)msg.mUpdateNumber)); + vh.push_back(format("%u",(unsigned)msg.mMessageId)); + // No language support yet, so dont show it. + //string lang = msg.getLanguage(); + //vh.push_back(lang.size() ? lang : string("-")); + vh.push_back(format("%u",(unsigned)msg.mSendCount)); + vh.push_back(format("%d",msg.mSendTime?(int)(now - msg.mSendTime) : -1)); + } + vh.push_back(msg.mMessageText); + tab.push_back(vh); + //msg.cbtext(os); + //os << "\n"; + } + printPrettyTable(tab,os,tabSeparated); + } else { + return BAD_VALUE; + } + return SUCCESS; +} + int isIMSI(const char *imsi) { if (!imsi) @@ -309,7 +443,7 @@ static CLIStatus sendsimple(int argc, char** argv, ostream& os) const char *txtBuf = rest.c_str(); if (!isIMSI(IMSI)) { - os << "Invalid IMSI. Enter 15 digits only."; + os << "Invalid IMSI. Enter 15 digits only. "; return BAD_VALUE; } @@ -903,16 +1037,8 @@ static CLIStatus page(int argc, char **argv, ostream& os) return SUCCESS; } -static GSM::ChannelType translateChannelType(string channelName) -{ - if (0==strcasecmp(channelName.c_str(),"sdcch")) { - return GSM::SDCCHType; - } else if (0==strcasecmp(channelName.c_str(),"tch")) { - return GSM::TCHFType; - } else { - throw CLIParseError(format("invalid channel -type argument: %s",channelName)); - } -} + + // Return the Transaction Id or zero is an invalid value. @@ -1304,6 +1430,8 @@ static CLIStatus chans(int argc, char **argv, ostream& os) #endif + + const char *powerHelp = "[atten] -- report or set current output power attentuation."; static bool powerCheckAttenValidity(const char *configOptionName,int atten, ostream&os) @@ -1620,6 +1748,8 @@ void Parser::addCommands() addCommand("stats", stats,"[patt] OR clear -- print all, or selected, performance counters, OR clear all counters."); addCommand("handover", handover,handoverHelp); addCommand("memstat", memStat, "-- internal testing command: print memory use stats."); + addCommand("cbs", cbscmd, cbsHelp); + addCommand("power", powerCommand, powerHelp); } diff --git a/CLI/Makefile.am b/CLI/Makefile.am index cbbd19b..14f0c4a 100644 --- a/CLI/Makefile.am +++ b/CLI/Makefile.am @@ -24,8 +24,8 @@ include $(top_srcdir)/Makefile.common EXTRA_DIST = \ README.CLI -AM_CXXFLAGS = -Wall -AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) +# AM_CXXFLAGS = -Wall +# AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) noinst_LTLIBRARIES = libcli.la diff --git a/CommonLibs b/CommonLibs index 3ad343b..11d8baa 160000 --- a/CommonLibs +++ b/CommonLibs @@ -1 +1 @@ -Subproject commit 3ad343b97b137743283194a2d55622c26c6d4800 +Subproject commit 11d8baa82662c9c862aa0fe8a5c585c2eb9cfb89 diff --git a/Control/CBS.cpp b/Control/CBS.cpp new file mode 100644 index 0000000..3e02c44 --- /dev/null +++ b/Control/CBS.cpp @@ -0,0 +1,380 @@ +/**@file SMSCB Control (L3), GSM 03.41. */ +/* +* Copyright 2010 Kestrel Signal Processing, Inc. +* Copyright 2014 Range Networks, Inc. +* +* This software is distributed under multiple licenses; +* see the COPYING file in the main directory for licensing +* information for this specific distribution. +* +* This use of this software may be subject to additional restrictions. +* See the LEGAL file in the main directory for details. + + 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. + +*/ + +#define LOG_GROUP LogGroup::Control + +#include "ControlCommon.h" +#include "CBS.h" +#include +#include +#include +#include +#include +#include + +// (pat) See GSM 03.41. SMSCB is a broadcast message service on a dedicated broadcast channel and unrelated to anything else. +// Broadcast messages are completely unacknowledged. They are repeated perpetually. + +namespace Control { + +static sqlite3 *sCBSDB = NULL; + + +static const char* createSMSCBTable = { + "CREATE TABLE IF NOT EXISTS SMSCB (" + "GS INTEGER NOT NULL, " + "MESSAGE_CODE INTEGER NOT NULL, " + "UPDATE_NUMBER INTEGER NOT NULL, " + "MSGID INTEGER NOT NULL, " + "LANGUAGE_CODE INTEGER NOT NULL, " // (pat) A 2 character string encoded as a 2 byte integer. + "MESSAGE TEXT NOT NULL, " + "SEND_TIME INTEGER DEFAULT 0, " + "SEND_COUNT INTEGER DEFAULT 0" + ")" + +}; + + +static sqlite3* CBSConnectDatabase(bool whine) +{ + string path = gConfig.getStr("Control.SMSCB.Table"); + if (path.length() == 0) { return NULL; } + + if (sCBSDB) { return sCBSDB; } + + int rc = sqlite3_open(path.c_str(),&sCBSDB); + if (rc) { + if (whine) LOG(EMERG) << "Cannot open Cell Broadcast Service database on path " << path << ": " << sqlite3_errmsg(sCBSDB); + sqlite3_close(sCBSDB); + sCBSDB = NULL; + return NULL; + } + if (!sqlite3_command(sCBSDB,createSMSCBTable)) { + if (whine) LOG(EMERG) << "Cannot create Cell Broadcast Service table"; + return NULL; + } + // Set high-concurrency WAL mode. + if (!sqlite3_command(sCBSDB,enableWAL)) { + if (whine) LOG(EMERG) << "Cannot enable WAL mode on database at " << path << ", error message: " << sqlite3_errmsg(sCBSDB); + } + return sCBSDB; +} + + +static int cbsRunQuery(string query) +{ + if (!CBSConnectDatabase(true)) { return 0; } + LOG(DEBUG) << LOGVAR(query); + if (! sqlite_command(sCBSDB,query.c_str())) { + LOG(INFO) << "CBS SQL query failed"< &result, string text) +{ + if (!CBSConnectDatabase(true)) { return false; } + result.clear(); + sqlite3_stmt *stmt = NULL; + string query = format("SELECT %s FROM SMSCB ",crackRowNames); + if (text.size()) { + query += format("WHERE MESSAGE=='%s'",text); + } + int rc; + if ((rc = sqlite3_prepare_statement(sCBSDB,&stmt,query.c_str()))) { + LOG(DEBUG) << "sqlite3_prepare_statement failed code="< &fields,string separator) +{ + string result; + int cnt = 0; + for (vector::iterator it = fields.begin(); it != fields.end(); it++, cnt++) { + if (cnt) result.append(separator); + result.append(*it); + } + return result; +} + +static void update1field(const char *col, unsigned uval, vector*cols, vector*vals, vector*both) +{ + if (cols) { cols->push_back(col); } + if (vals) { vals->push_back(format("%u",uval)); } + if (both) { both->push_back(format("%s=%u",col,uval)); } +} + +static void update1field(const char *col, string sval, vector*cols, vector*vals, vector*both) +{ + if (cols) { cols->push_back(col); } + if (vals) { vals->push_back(format("'%s'",sval)); } + if (both) { both->push_back(format("%s='%s'",col,sval)); } +} + +// The all flag is for INSERT which must update all the DB fields with the "NOT NULL" option. Oops. +static void CBMessage2SQLFields(CBMessage &msg, vector*cols, vector*vals, vector*both, bool all) +{ + if (all || msg.mGS_change) { update1field("GS",msg.mGS,cols,vals,both); } + if (all || msg.mMessageCode_change) { update1field("MESSAGE_CODE",msg.mMessageCode,cols,vals,both); } + if (all || msg.mUpdateNumber_change) { update1field("UPDATE_NUMBER",msg.mUpdateNumber,cols,vals,both); } + if (all || msg.mMessageId_change) { update1field("MSGID",msg.mMessageId,cols,vals,both); } + if (all || msg.mLanguageCode_change) { update1field("LANGUAGE_CODE",msg.mLanguageCode,cols,vals,both); } + //if (all || msg.mLanguage_change) { update1field("LANGUAGE_CODE",msg.getLanguageCode(),cols,vals,both); } + if (all || msg.mMessageText.size()) { update1field("MESSAGE",msg.mMessageText,cols,vals,both); } + // ROWID is a synthetic field. + if (msg.mRowId_change) { update1field("ROWID",msg.mRowId,cols,vals,both); } +} + +// Deletes all messages that match msg, which must have at least one field set. +int CBSDeleteMessage(CBMessage &msg) +{ + vector fields; + CBMessage2SQLFields(msg,NULL,NULL,&fields,false); + if (fields.size()) { + string query = format("DELETE FROM SMSCB WHERE %s",strJoin(fields,",")); + return cbsRunQuery(query); + } else { + return 0; // If the CBMessage contained no fields, dont delete all messages, just return 0. + } +} + + +int CBSAddMessage(CBMessage &msg, string &errorMsg) +{ + if (msg.mMessageText.size() == 0) { + errorMsg = string("Attempt to add message with no text"); + return 0; + } + if (!CBSConnectDatabase(true)) { + errorMsg = string("could not write to database"); + return 0; + } + + + // Does the message exist already? + vector existing; + CBSGetMessages(existing,msg.mMessageText); + for (vector::iterator it = existing.begin(); it != existing.end(); it++) { + if (msg.match(*it)) { + errorMsg = string("Attempt to add duplicate message"); + return 0; + } + } + + // TODO: If the message matches an existing we should increment the update_number. + + // like this: INSERT OR REPLACE INTO SMSCB (GS,MESSAGE_CODE,UPDATE_NUMBER,MSGID,LANGUAGE_CODE,MESSAGE) VALUES (0,0,0,0,0,'whatever') + // Cannot use REPLACE: REPLACE only works if INSERT would generate a constraint conflict, ie, duplicate UNIQ field. + vector cols, vals; + // We must update all the fields with the history "NOT NULL" value in the database. Oops. + CBMessage2SQLFields(msg, &cols, &vals, NULL,true); + string query = format("INSERT INTO SMSCB (%s) VALUES (%s)",strJoin(cols,","),strJoin(vals,",")); + return cbsRunQuery(query); +} + + +static void encode7(char mc, int &shift, unsigned int &dp, int &buf, char *thisPage) +{ + buf |= (mc & 0x7F) << shift--; + if (shift < 0) { + shift = 7; + } else { + thisPage[dp++] = buf & 0xFF; + buf = buf >> 8; + } +} + + +// (pat 8-2014) I added the CBMessage class and added CLI cbscmd to manipulate the database, +// but I did not change the basic encoding and transmit logic below nor did I enable the language option. +static void CBSSendMessage(CBMessage &msg, GSM::CBCHLogicalChannel* CBCH) +{ + // Figure out how many pages to send. + const unsigned maxLen = 40*15; + unsigned messageLen = msg.mMessageText.length(); + if (messageLen>maxLen) { + LOG(ALERT) << "SMSCB message ID " << msg.mMessageId << " to long; truncating to " << maxLen << " char."; + messageLen = maxLen; + } + unsigned numPages = messageLen / 40; + if (messageLen % 40) numPages++; + unsigned mp = 0; + + LOG(INFO) << "sending message ID=" << msg.mMessageId << " code=" << msg.mMessageCode << " in " << numPages << " pages: " << msg.mMessageText; + + // Break into pages and send each page. + for (unsigned page=0; page> 8; + thisPage[dp++] = msg.mLanguageCode & 0x0ff; + while (dp<82 && mp> 8, shift, dp, buf, thisPage); + // encode7(languageCode & 0xFF, shift, dp, buf, thisPage); + // encode7('\r', shift, dp, buf, thisPage); + while (dp<81 && mpl2sendm(message); + } +} + + +string CBMessage::cbtext() +{ + ostringstream os; + os < +#include + +namespace Control { + + +// Short Message Cell Broadcast Service +// GSM 3.41 9.3.2 +// See also WEA = Wireless Emergency Alert services on the internet. +// See discussion on wiki ticket #1281. +// Note that USSD is another way to show messages directly on a handset. +struct CBMessage { + // The handset displays the message if it has not seen it before within the specified Geographical Scope. + enum GeographicalScope { + GS_Immediate = 0, + GS_PLMN = 1, + GS_LocationArea = 2, + GS_Cell = 3, + }; + GeographicalScope mGS; // Geographical Scope: + Bool_z mGS_change; + bool setGS(int gs) { // Return false if out of range. + if (gs < 0 || gs > 3) { mGS = GS_Immediate; return false; } + mGS = (GeographicalScope) gs; + mGS_change = true; + return true; + } + + UInt_z mMessageCode; // Unique id for message. + Bool_z mMessageCode_change; + bool setMessageCode(unsigned mc) { mMessageCode = mc; mMessageCode_change = true; return true; } + + UInt_z mUpdateNumber; // Another unique id for message. + Bool_z mUpdateNumber_change; + bool setUpdateNumber(unsigned un) { mUpdateNumber = un; mUpdateNumber_change = true; return true; } + + // The message id identifies the message type (bad name.) + // In particular, values above 0x3e7 (where did they get that?) are reserved. + // The only interesting one is 0x3e9 GPS Assistance data. + UInt_z mMessageId; // Yet another unique id for the message. + Bool_z mMessageId_change; + bool setMessageId(unsigned id) { mMessageId = id; mMessageId_change = true; return true; } + + // Language code is not implemented. + // This should be Data Coding Scheme, not 'language code'. + + // See GSM 3.38 section 5. + // (pat) The historical SMSCB database has an unsigned "language_code" entry that is not implemented. + // But there are many different ways to specify and represent the lanugage: + // 1. Using the Data Coding Scheme as per GSM 3.38 section 5; + // 2. By preceding the default encoding with 2 characters; + // 3. Using UCS2, which uses 16 bit characters. + // The character encoding in the message can use 7-bit, 8-bit, 16-bit, or 7-bit+extension-characters. + // Unfortunately, DCS == 0 is a valid value (german), so I'm not even sure how we should store the language code, + // but probably need both the DCS and the language code, which would require a CBS database update, which would + // mean we would have to check for and handle older revs of the database, so I am punting this. + // So here is some code to interface to the SMSCB database, but it doesnt do anything. + UInt_z mLanguageCode; // 2 ascii chars encoded as a short word. + Bool_z mLanguageCode_change; + bool setLanguageCode(unsigned lc) { mLanguageCode = lc; mLanguageCode_change = true; return true; } + bool setLanguage(string lc) { + if (lc.size() == 0 || lc == "-") { + return setLanguageCode(0); + } else if (lc.size() == 2) { + return setLanguageCode(((unsigned)lc[1] << 8) | lc[0]); + } else { + return false; + } + } + string getLanguage() { + // If mLanguageCode is 0 it will return "" + char buf[3]; buf[0] = mLanguageCode & 0x7f; buf[1] = (mLanguageCode>>8)&0x7f; buf[2] = 0; + return string(buf); + } + + string mMessageText; // The message itself. + bool setMessageText(string mt) { mMessageText = mt; return true; } + + bool match(CBMessage &other) { + return mGS == other.mGS && mMessageCode == other.mMessageCode && mUpdateNumber == other.mUpdateNumber && + mMessageId == other.mMessageId && mLanguageCode == other.mLanguageCode && mMessageText == other.mMessageText; + } + + // These are OpenBTS specific fields: + UInt_z mSendCount; + time_t mSendTime; + UInt_z mRowId; // Identifies this message in our database. + Bool_z mRowId_change; + bool setRow(int row) { mRowId = row; mRowId_change = true; return true; } + + CBMessage() : mGS(GS_Immediate), mSendTime(0) {} + + string cbtext(); + void cbtext(std::ostream &os); +}; + +int CBSClearMessages(); +int CBSDeleteMessage(CBMessage &msg); +int CBSAddMessage(CBMessage &msg, string &errMsg); +bool CBSGetMessages(vector &result, string text=""); + +}; // namespace + +#endif diff --git a/Control/L3Handover.cpp b/Control/L3Handover.cpp index 6fdf3d0..7e685c9 100644 --- a/Control/L3Handover.cpp +++ b/Control/L3Handover.cpp @@ -182,6 +182,7 @@ static BestNeighbor HandoverDecision(const L3MeasurementResults* measurements, S } LOG(DEBUG) << LOGVAR(penalty); + // This uses RLXLEV_DL.History to comute the neighbor RXLEV BestNeighbor bestn = chp->neighborFindBest(penalty); if (! bestn.mValid) { return NoHandover(bestn); } @@ -305,7 +306,7 @@ bool outboundHandoverTransfer(TranEntry* transaction, L3LogicalChannel *TCH) // default TA value defined in GSM 5.10 6.6, which is 0. // We use a 0 value in OutboundPowerCmd which is max power. HandoverEntry *hop = transaction->getHandoverEntry(true); - L3Frame HandoverCommand(hop->mHexEncodedL3HandoverCommand.c_str()); + L3Frame HandoverCommand(SAPI0, hop->mHexEncodedL3HandoverCommand.c_str()); LOG(INFO) <l3sendf(HandoverCommand); diff --git a/Control/L3LogicalChannel.cpp b/Control/L3LogicalChannel.cpp index 750536c..e7260c1 100644 --- a/Control/L3LogicalChannel.cpp +++ b/Control/L3LogicalChannel.cpp @@ -328,7 +328,7 @@ void L3LogicalChannel::chanLost() void L3LogicalChannel::chanRelease(Primitive prim,TermCause cause) { OBJLOG(DEBUG) << prim; - chanFreeContext(cause); + //chanFreeContext(cause); switch (prim) { case L3_HARDRELEASE_REQUEST: chanSetState(L3LogicalChannel::chRequestHardRelease); diff --git a/Control/L3MMLayer.cpp b/Control/L3MMLayer.cpp index cd19666..40e0c4c 100644 --- a/Control/L3MMLayer.cpp +++ b/Control/L3MMLayer.cpp @@ -273,7 +273,11 @@ bool MMContext::mmCheckSipMsgs() { // Update: We cannot hold the global lock while invoking state machines because they can block. // As an interim measure, just dont lock this and hope for the best. - //ScopedLock lock(gMMLock,__FILE__,__LINE__); // I think this is unnecessary, but be safe. + // (pat) Update 9-19-2014: Formerly, when this global lock was in place we got the "blocked more than one second at..." + // messages during the stress test. Ticket #1905 reports a crash that looks like using a transaction here while + // it is being deleted. Since I have separated layer2 from layer3 with InterthreadQueues since the last test, + // I am re-enabling this global lock to attempt to fix the crash. + ScopedLock lock(gMMLock,__FILE__,__LINE__); // I think this is unnecessary, but be safe. bool result = false; for (unsigned i = TE_first; i < TE_num; i++) { RefCntPointer tranp = mmGetTran(i); diff --git a/Control/L3MobilityManagement.cpp b/Control/L3MobilityManagement.cpp index 982ecfc..dd66c64 100644 --- a/Control/L3MobilityManagement.cpp +++ b/Control/L3MobilityManagement.cpp @@ -45,6 +45,7 @@ //#include #include #include +#include "RRLPServer.h" using namespace GSM; @@ -253,7 +254,6 @@ MMSharedData *LUBase::ludata() const } - // TODO: Reject cause should be determined in a more central location, probably sipauthserve. // We may want different reject codes based on the IMSI or MSISDN of the MS, or on the CM service being requested (CC, SMS, USSD, GPRS), // although this code here is used only by LUR. @@ -806,11 +806,14 @@ MachineStatus LUAuthentication::machineRunState(int state, const GSM::L3Message* const L3MobileStationClassmark2& classmark = resp->classmark(); // We are storing the A5Bits for later use by CC, which is probably unnecessary because // it is included in the CC message. - int A5Bits = (classmark.A5_1()<<2) + (classmark.A5_2()<<1) + classmark.A5_3(); + int A5Bits = classmark.getA5Bits(); ludata()->store.setClassmark(A5Bits,classmark.powerClass()); //gTMSITable.classmark(getImsiCh(),classmark); // This one is going away; we'll update once later. if (gConfig.getBool("GSM.Cipher.Encrypt")) { + // (pat) 9-2014 hack: GSML1FEC gets the Kc directly out of the tmsi table so we need to flush to the physical table + // before sending the ciphering mode command. + gTMSITable.tmsiTabUpdate(getImsi(),&ludata()->store); //int encryptionAlgorithm = gTMSITable.getPreferredA5Algorithm(getImsi().c_str()); int encryptionAlgorithm = getPreferredA5Algorithm(A5Bits); if (!encryptionAlgorithm) { @@ -820,6 +823,8 @@ MachineStatus LUAuthentication::machineRunState(int state, const GSM::L3Message* channel()->l3sendm(GSM::L3CipheringModeCommand( GSM::L3CipheringModeSetting(true, encryptionAlgorithm), GSM::L3CipheringModeResponse(false))); + // We are now waiting for the cihering mode comlete command... + return MachineStatusOK; // The TMMCancel timer is running. } else { LOG(DEBUG) << "no ki: NOT sending Ciphering Mode Command on " << *channel() << " for " << getImsiName(); } @@ -827,6 +832,11 @@ MachineStatus LUAuthentication::machineRunState(int state, const GSM::L3Message* return callMachStart(new LUFinish(tran())); } + case L3CASE_RR(CipheringModeComplete): { + // The fact the message arrived means success. We hope. Even if that were not true, we should proceed anyway. + return callMachStart(new LUFinish(tran())); + } + default: return unexpectedState(state,l3msg); } diff --git a/Control/L3SMSControl.cpp b/Control/L3SMSControl.cpp index 9e4a7bd..06caeb5 100644 --- a/Control/L3SMSControl.cpp +++ b/Control/L3SMSControl.cpp @@ -520,8 +520,6 @@ MachineStatus MTSMSMachine::machineRunState1(int state,const L3Frame*frame,const // SABM was started by us and handset simultaneously, so we just dont bother with making ESTABLISH_CONFIRM separate. case L3CASE_PRIMITIVE(L3_ESTABLISH_INDICATION): case L3CASE_PRIMITIVE(L3_ESTABLISH_CONFIRM): { - //step1: - // Step 1 // Send the first message. // CP-DATA, containing RP-DATA. diff --git a/Control/L3StateMachine.cpp b/Control/L3StateMachine.cpp index 530801c..b0c8e20 100644 --- a/Control/L3StateMachine.cpp +++ b/Control/L3StateMachine.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -261,6 +262,7 @@ static bool handleCommonMessages(const L3Message *l3msg, MMContext *mmchan, bool NewPagingResponseHandler(dynamic_cast(l3msg),mmchan); return true; case L3CASE_RR(L3RRMessage::ApplicationInformation): + return true; case L3CASE_RR(L3RRMessage::RRStatus): handleStatus(l3msg,mmchan); return true; diff --git a/Control/L3TranEntry.cpp b/Control/L3TranEntry.cpp index d550b35..300bb4a 100644 --- a/Control/L3TranEntry.cpp +++ b/Control/L3TranEntry.cpp @@ -742,7 +742,7 @@ L3Cause::AnyCause TranEntry::terminationRequested() // This is run in BS1 to create the handover string to send to BS2. // The string must contain everything about the SIP side of the session. // Everything needed to be known about the radio side of the session was transferred as an L3 HandoverCommand. -string TranEntry::handoverString(string peer) const +string TranEntry::handoverString(string peer,string cause) const { // This string is a set of key-value pairs. // It needs to carry all of the information of the GSM Abis Handover Request message, @@ -761,6 +761,7 @@ string TranEntry::handoverString(string peer) const os << " L3TI=" << mL3TI; if (mCalled.digits()[0]) os << " called=" << mCalled.digits(); if (mCalling.digits()[0]) os << " calling=" << mCalling.digits(); + if (cause.size()) os << " cause=" << cause; const SipBase *sip = Unconst(this)->getDialog(); os << " REFER=" << sip->dsHandoverMessage(peer); @@ -1402,25 +1403,33 @@ bool TranEntry::handleMachineStatus(MachineStatus status) // If no proc is specified here, assume that teSetProcedure was called previously and start the currentProcedure. bool TranEntry::lockAndStart(MachineBase *wProc) { - ScopedLock lock(mL3RewriteLock,__FILE__,__LINE__); - if (wProc) { - teSetProcedure(wProc,false); - devassert(wProc == currentProcedure()); - } else { - wProc = currentProcedure(); - devassert(wProc); // Someone set the currentProcedure before calling this method. + bool result = false; + RefCntPointer saver = this; + { ScopedLock lock(mL3RewriteLock,__FILE__,__LINE__); + if (wProc) { + teSetProcedure(wProc,false); + devassert(wProc == currentProcedure()); + } else { + wProc = currentProcedure(); + devassert(wProc); // Someone set the currentProcedure before calling this method. + } + result = handleMachineStatus(wProc->callMachStart(wProc)); } - return handleMachineStatus(wProc->callMachStart(wProc)); + return result; } // Start a procedure by passing it this L3 message: bool TranEntry::lockAndStart(MachineBase *wProc, GSM::L3Message *l3msg) { - ScopedLock lock(mL3RewriteLock,__FILE__,__LINE__); - teSetProcedure(wProc,false); - devassert(wProc == currentProcedure()); - return handleMachineStatus(wProc->dispatchL3Msg(l3msg)); + bool result = false; + RefCntPointer saver = this; + { ScopedLock lock(mL3RewriteLock,__FILE__,__LINE__); + teSetProcedure(wProc,false); + devassert(wProc == currentProcedure()); + result = handleMachineStatus(wProc->dispatchL3Msg(l3msg)); + } + return result; } #if UNUSED @@ -1433,21 +1442,15 @@ bool TranEntry::lockAndStart(MachineBase *wProc, GSM::L3Message *l3msg) bool TranEntry::lockAndInvokeL3Msg(const GSM::L3Message *l3msg /*, const L3LogicalChannel *lch*/) { LOG(DEBUG); - ScopedLock lock(mL3RewriteLock,__FILE__,__LINE__); -#if ORIGINAL - // Look up the message in the Procedure message table. - int state = mCurrentProcedure->findMsgMap(l3msg->PD(),l3msg->MTI()); - if (state == -1) { - // If state is -1, message is not mapped and is ignored by us. - return false; // Message was not wanted by this Procedure. + bool result = false; + RefCntPointer saver = this; + { ScopedLock lock(mL3RewriteLock,__FILE__,__LINE__); + if (MachineBase *proc = currentProcedure()) { + LOG(DEBUG) <<"sending l3msg to"<dispatchL3Msg(l3msg)); + } } - teProcInvoke(state,l3msg,NULL); -#endif - if (MachineBase *proc = currentProcedure()) { - LOG(DEBUG) <<"sending l3msg to"<dispatchL3Msg(l3msg)); - } - return false; + return result; } #endif @@ -1455,29 +1458,32 @@ bool TranEntry::lockAndInvokeL3Msg(const GSM::L3Message *l3msg /*, const L3Logic bool TranEntry::lockAndInvokeFrame(const L3Frame *frame, const L3Message *l3msg) { LOG(DEBUG) << l3msg; - ScopedLock lock(mL3RewriteLock,__FILE__,__LINE__); - if (MachineBase *proc = currentProcedure()) { - LOG(DEBUG) <<"sending frame to"<dispatchFrame(frame,l3msg)); + bool result = false; + RefCntPointer saver = this; + { + ScopedLock lock(mL3RewriteLock,__FILE__,__LINE__); + if (MachineBase *proc = currentProcedure()) { + LOG(DEBUG) <<"sending frame to"<dispatchFrame(frame,l3msg)); + } else { + LOG(INFO) <<"Received message for transaction with no state machine. "<mSipHandlerState; - if (state >= 0) { return teProcInvoke(state,NULL,sipmsg); } - return MachineStatusUnexpectedMessage; -#endif - if (MachineBase *proc = currentProcedure()) { - return handleMachineStatus(proc->dispatchSipDialogMsg(sipmsg)); + bool result = false; + RefCntPointer saver = this; + { ScopedLock lock(mL3RewriteLock,__FILE__,__LINE__); + if (MachineBase *proc = currentProcedure()) { + result = handleMachineStatus(proc->dispatchSipDialogMsg(sipmsg)); + } } - return false; + return result; } bool TranEntry::lockAndInvokeSipMsgs() @@ -1497,12 +1503,18 @@ bool TranEntry::lockAndInvokeSipMsgs() bool TranEntry::lockAndInvokeTimeout(L3Timer *timer) { + // handleMachineStatus may unlink the transaction; this reference prevents the transaction + // from being deleted until this routine exists. Without this, the ScopedLock tries to reference the deleted transaction. + bool result = false; + RefCntPointer saver = this; + LOG(DEBUG) << LOGVAR2("timer",timer->tName()) << this; - ScopedLock lock(mL3RewriteLock,__FILE__,__LINE__); - if (MachineBase *proc = currentProcedure()) { - return handleMachineStatus(proc->dispatchTimeout(timer)); + { ScopedLock lock(mL3RewriteLock,__FILE__,__LINE__); + if (MachineBase *proc = currentProcedure()) { + result = handleMachineStatus(proc->dispatchTimeout(timer)); + } } - return false; + return result; } void TranEntry::terminateHook() @@ -1592,9 +1604,16 @@ void TranEntry::teRemove(TermCause cause) // dialog->dialogCancel(cause, l3Cause); // Does nothing if dialog not yet started. //} - if (L3CDR *cdr = this->createCDR(false,cause)) { - gCdrService.cdrServiceStart(); - gCdrService.cdrAdd(cdr); + // (pat 9-29-2014) Harry was seeing intermittent crashes in the code below. + // Crash was inside sqlite when calling gConfig.getStr() from cdrServiceStart(), which would appear to be a memory corruption + // in something immediately preceding, but unknown what it could be. + // I cannot find any problems, but this is the only new addition, so to be safe, we will completely avoid this code + // unless specifically enabled by setting Control.CDR.Dirname. + if (gConfig.getStr("Control.CDR.Dirname").size()) { + if (L3CDR *cdr = this->createCDR(true,cause)) { + gCdrService.cdrServiceStart(); + gCdrService.cdrAdd(cdr); + } } // It is important to make this transaction no longer point at the dialog, because the dialog @@ -1724,12 +1743,17 @@ L3CDR *TranEntry::createCDR(bool makeCMR, TermCause cause) // If true, make a CM goto labelmtc; break; + case L3CMServiceType::LocationUpdateRequest: + if (! makeCMR) { goto labelIgnore; } + cdr.cdrType = "LUR"; + cdr.cdrFromImsi = this->subscriber().mImsi; + break; + case L3CMServiceType::UndefinedType: case L3CMServiceType::SupplementaryService: case L3CMServiceType::VoiceCallGroup: case L3CMServiceType::VoiceBroadcast: case L3CMServiceType::LocationService: - case L3CMServiceType::LocationUpdateRequest: case L3CMServiceType::HandoverCall: // The handover code sets the type to MobileOriginatedCall or MobileTerminatedCall so we should not see this. break; @@ -1816,7 +1840,7 @@ void L3CDR::cdrWriteEntry(FILE *pf) fprintf(pf,"%lu,%lu,",cdrConnectTime,cdrDuration); fprintf(pf,"%d,",cdrMessageSize); fprintf(pf,"%s,%s,",cdrToHandover.c_str(),cdrFromHandover.c_str()); // handover to, from - fprintf(pf,"%c",(cdrCause.mtcInstigator == TermCause::SideLocal) ? 'L' : 'R'); // termination side + fprintf(pf,"%c,",(cdrCause.mtcInstigator == TermCause::SideLocal) ? 'L' : 'R'); // termination side fprintf(pf,"%d",cdrCause.tcGetValue()); // termination cause fprintf(pf,"\n"); fflush(pf); // Write to disk in case OpenBTS crashes. @@ -1833,11 +1857,13 @@ void CdrService::cdrOpenFile() return; } if (mpf) { fclose(mpf); mpf = NULL; } + string dirname = gConfig.getStr("Control.CDR.Dirname"); + if (0 == dirname.size()) { return; } // Disabled if the Control.CDR.Dirname is empty. + cdrCurrentDay = tm.tm_yday; string date = format("%04d-%02d-%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); - const char *dirname = "/var/log/OpenBTS"; // TODO: make this a config option. - mkdir(dirname,0777); // Doesnt hurt to do this even if unnecessary. + mkdir(dirname.c_str(),0777); // Doesnt hurt to do this even if unnecessary. string btsid = gConfig.getStr("SIP.Local.IP"); // Default bts id to the local IP address. string filename = format("%s/OpenBTS_%s_%s.cdr",dirname,btsid,date); @@ -1851,6 +1877,7 @@ void*CdrService::cdrServiceLoop(void*arg) CdrService *self = static_cast(arg); while (!gBTS.btsShutdown()) { L3CDR *cdr = self->mCdrQueue.read(); // Blocking read. + if (!cdr) { continue; } // paranoid, but maybe happens during shutdown. self->cdrOpenFile(); // We may have to open a new file if the date changed. if (self->mpf) { cdr->cdrWriteEntry(self->mpf); } delete cdr; @@ -1860,8 +1887,13 @@ void*CdrService::cdrServiceLoop(void*arg) void CdrService::cdrServiceStart() { - if (!cdrServiceRunning) { - cdrServiceRunning = true; + bool startme = false; + { ScopedLock lock(cdrLock); // This is probably paranoid overkill. + if (!cdrServiceRunning) { + startme = cdrServiceRunning = true; + } + } + if (startme) { cdrServiceThread.start(cdrServiceLoop,this); } } diff --git a/Control/L3TranEntry.h b/Control/L3TranEntry.h index 05ae744..e392000 100644 --- a/Control/L3TranEntry.h +++ b/Control/L3TranEntry.h @@ -219,7 +219,7 @@ class TranEntryProtected // Call Data Record, created from a TranEntry. // The information we dont know is left empty. struct L3CDR { - string cdrType; // MOC, MTC, Emergency + string cdrType; // MOC, MTC, Emergency. TranEntryId cdrTid; string cdrToImsi, cdrFromImsi; string cdrToNumber, cdrFromNumber; @@ -237,6 +237,7 @@ struct L3CDR { // pat added 6-2014. // This is sent L3CDR records. It writes them to a file. class CdrService { + Mutex cdrLock; InterthreadQueue mCdrQueue; Thread cdrServiceThread; FILE *mpf; @@ -306,6 +307,7 @@ class TranEntry : public MemCheckTranEntry, public RefCntBase, public TranEntryP std::string mContentType; ///< text message payload content type //@} + TermCause mFinalDisposition; // How transaction ended. @@ -492,7 +494,7 @@ class TranEntry : public MemCheckTranEntry, public RefCntBase, public TranEntryP string text() const; /** Genrate an encoded string for handovers. */ - std::string handoverString(string peer) const; + std::string handoverString(string peer,string cause) const; private: diff --git a/Control/Makefile.am b/Control/Makefile.am index b58ac64..9acb1a3 100644 --- a/Control/Makefile.am +++ b/Control/Makefile.am @@ -22,8 +22,8 @@ include $(top_srcdir)/Makefile.common -AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) -AM_CXXFLAGS = -Wall +# AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) +# AM_CXXFLAGS = -Wall EXTRA_DIST = README.Control @@ -47,7 +47,8 @@ libcontrol_la_SOURCES = \ TMSITable.cpp \ ControlTransfer.cpp \ DCCHDispatch.cpp \ - SMSCB.cpp + CBS.cpp \ + RRLPServer.cpp # TODO - move CollectMSInfo.cpp and RRLPQueryController.cpp to RRLP directory. @@ -68,4 +69,7 @@ noinst_HEADERS = \ L3Utils.h \ ControlCommon.h \ ControlTransfer.h \ - TMSITable.h + TMSITable.h \ + TMSITable.h \ + CBS.h \ + RRLPServer.h diff --git a/Control/RRLPServer.cpp b/Control/RRLPServer.cpp new file mode 100644 index 0000000..e69de29 diff --git a/Control/RRLPServer.h b/Control/RRLPServer.h new file mode 100644 index 0000000..e69de29 diff --git a/Control/RadioResource.cpp b/Control/RadioResource.cpp deleted file mode 100644 index 5817166..0000000 --- a/Control/RadioResource.cpp +++ /dev/null @@ -1,424 +0,0 @@ -/**@file GSM Radio Resource procedures, GSM 04.18 and GSM 04.08. */ - -/* -* Copyright 2008, 2009, 2010 Free Software Foundation, Inc. -* Copyright 2010 Kestrel Signal Processing, Inc. -* Copyright 2011, 2012, 2014 Range Networks, Inc. -* - - 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. - -* This software is distributed under multiple licenses; -* see the COPYING file in the main directory for licensing -* information for this specific distribuion. -* -* This use of this software may be subject to additional restrictions. -* See the LEGAL file in the main directory for details. -*/ - - -#define LOG_GROUP LogGroup::Control - - -#include -#include -#include - -#include -#include "ControlCommon.h" -#include "RadioResource.h" -#include "L3CallControl.h" -#include "L3MMLayer.h" - -#include -#include -#include "../GPRS/GPRSExport.h" - -#include -#include -#include - -#include -#include -#undef WARNING - - - - -using namespace std; -using namespace GSM; -using namespace Control; - - - - -/** - Determine the channel type needed. - This is based on GSM 04.08 9.1.8, Table 9.3 and 9.3a. - The following is assumed about the global BTS capabilities: - - We do not support call reestablishment. - - We do not support GPRS. - @param RA The request reference from the channel request message. - @return channel type code, undefined if not a supported service -*/ -static -ChannelType decodeChannelNeeded(unsigned RA) -{ - // This code is based on GSM 04.08 Table 9.9. section 9.1.8 - - unsigned RA4 = RA>>4; - unsigned RA5 = RA>>5; - - - // We use VEA for all emergency calls, regardless of configuration. - if (RA5 == 0x05) return TCHFType; // emergency call - - // Answer to paging, Table 9.9a. - // We don't support TCH/H, so it's wither SDCCH or TCH/F. - // The spec allows for "SDCCH-only" MS. We won't support that here. - // FIXME -- So we probably should not use "any channel" in the paging indications. - if (RA5 == 0x04) return TCHFType; // any channel or any TCH. - if (RA4 == 0x01) return SDCCHType; // SDCCH - if (RA4 == 0x02) return TCHFType; // TCH/F - if (RA4 == 0x03) return TCHFType; // TCH/F - if ((RA&0xf8) == 0x78 && RA != 0x7f) return PSingleBlock1PhaseType; - if ((RA&0xf8) == 0x70) return PSingleBlock2PhaseType; - - int NECI = gConfig.getNum("GSM.CellSelection.NECI"); - if (NECI==0) { - if (RA5 == 0x07) return SDCCHType; // MOC or SDCCH procedures - if (RA5 == 0x00) return SDCCHType; // location updating - } else { - if (gConfig.getBool("Control.VEA")) { - // Very Early Assignment - if (RA5 == 0x07) return TCHFType; // MOC for TCH/F - if (RA4 == 0x04) return TCHFType; // MOC, TCH/H sufficient - } else { - // Early Assignment - if (RA5 == 0x07) return SDCCHType; // MOC for TCH/F - if (RA4 == 0x04) return SDCCHType; // MOC, TCH/H sufficient - } - if (RA4 == 0x00) return SDCCHType; // location updating - if (RA4 == 0x01) return SDCCHType; // other procedures on SDCCH - } - - // Anything else falls through to here. - // We are still ignoring data calls, LMU. - return UndefinedCHType; -} - - -/** Return true if RA indicates LUR. */ -static -bool requestingLUR(unsigned RA) -{ - int NECI = gConfig.getNum("GSM.CellSelection.NECI"); - if (NECI==0) return ((RA>>5) == 0x00); - else return ((RA>>4) == 0x00); -} - - - - - - -/** Decode RACH bits and send an immediate assignment; may block waiting for a channel for an SOS call. */ -static -void AccessGrantResponder( - unsigned RA, const GSM::Time& when, - float RSSI, float timingError) -{ - // RR Establishment. - // Immediate Assignment procedure, "Answer from the Network" - // GSM 04.08 3.3.1.1.3. - // Given a request reference, try to allocate a channel - // and send the assignment to the handset on the CCCH. - // This GSM's version of medium access control. - // Papa Legba, open that door... - // The RA bits are defined in 44.018 9.1.8 - - gReports.incr("OpenBTS.GSM.RR.RACH.TA.All",(int)(timingError)); - gReports.incr("OpenBTS.GSM.RR.RACH.RA.All",RA); - - // Are we holding off new allocations? - if (gBTS.hold()) { - LOG(NOTICE) << "ignoring RACH due to BTS hold-off"; - return; - } - - // Check "when" against current clock to see if we're too late. - // Calculate maximum number of frames of delay. - // See GSM 04.08 3.3.1.1.2 for the logic here. - static const unsigned txInteger = gConfig.getNum("GSM.RACH.TxInteger"); - static const int maxAge = GSM::RACHSpreadSlots[txInteger] + GSM::RACHWaitSParam[txInteger]; - // Check burst age. - int age = gBTS.time() - when; - ChannelType chtype = decodeChannelNeeded(RA); - int lur = requestingLUR(RA); - int gprs = (chtype == PSingleBlock1PhaseType) || (chtype == PSingleBlock2PhaseType); - - // This is for debugging. - if (GPRS::GPRSDebug && gprs) { - Time now = gBTS.time(); - LOG(NOTICE) << "RACH" <gConfig.getNum("GSM.MS.TA.Max")) { - LOG(NOTICE) << "ignoring RACH burst with delay="<load()>gConfig.getNum("GSM.CCCH.AGCH.QMax")) { - LOG(CRIT) << "AGCH congestion"; - return; - } - - // Check for location update. - // This gives LUR a lower priority than other services. - // (pat): LUR = Location Update Request Message - if (requestingLUR(RA)) { - // Don't answer this LUR if it will not leave enough channels open for other operations. - if ((int)gBTS.SDCCHAvailable()<=gConfig.getNum("GSM.Channels.SDCCHReserve")) { - // (pat) Dont print this alarming message or tell the handsets to go away until we have - // been up long enough for the channels to clear their initial recyclable state. - // See L1Decoder::recyclable() - unsigned startupdelay = (max(T3101ms,max(T3109ms,T3111ms)) +999)/ 1000; - if (gBTS.uptime() <= (time_t)startupdelay) { return; } - unsigned waitTime = gBTS.growT3122()/1000; - LOG(CRIT) << "LUR congestion, RA=" << RA << " T3122=" << waitTime; - const L3ImmediateAssignmentReject reject(L3RequestReference(RA,when),waitTime); - LOG(DEBUG) << "LUR rejection, sending " << reject; - AGCH->l2sendm(reject); - return; - } - } - - // Allocate the channel according to the needed type indicated by RA. - // The returned channel is already open and ready for the transaction. - L2LogicalChannel *LCH = NULL; - switch (chtype) { - case TCHFType: LCH = gBTS.getTCH(); break; - case SDCCHType: LCH = gBTS.getSDCCH(); break; -#if 0 - // GSM04.08 sec 3.5.2.1.2 - case PSingleBlock1PhaseType: - case PSingleBlock2PhaseType: - { - L3RRMessage *msg = GPRS::GPRSProcessRACH(chtype, - L3RequestReference(RA,when), - RSSI,timingError,AGCH); - if (msg) { - AGCH->l2sendm(*msg); - delete msg; - } - return; - } -#endif - // If we don't support the service, assign to an SDCCH and we can reject it in L3. - case UndefinedCHType: - LOG(NOTICE) << "RACH burst for unsupported service RA=" << RA; - LCH = gBTS.getSDCCH(); - break; - // We should never be here. - default: assert(0); - } - - - // Nothing available? - if (!LCH) { - // Rejection, GSM 04.08 3.3.1.1.3.2. - { - unsigned waitTime = gBTS.growT3122()/1000; - // TODO: If all channels are statically allocated for gprs, dont throw an alert. - LOG(CRIT) << "congestion, RA=" << RA << " T3122=" << waitTime; - const L3ImmediateAssignmentReject reject(L3RequestReference(RA,when),waitTime); - LOG(DEBUG) << "rejection, sending " << reject; - AGCH->l2sendm(reject); - } - return; - } - - // (pat) gprs todo: Notify GPRS that the MS is getting a voice channel. - // It may imply abandonment of packet contexts, if the phone does not - // support DTM (Dual Transfer Mode.) There may be other housekeeping - // for DTM phones; haven't looked into it. - - // Set the channel physical parameters from the RACH burst. - LCH->setPhy(RSSI,timingError,gBTS.clock().systime(when.FN())); - gReports.incr("OpenBTS.GSM.RR.RACH.TA.Accepted",(int)(timingError)); - - // Assignment, GSM 04.08 3.3.1.1.3.1. - // Create the ImmediateAssignment message. - // Woot!! We got a channel! Thanks to Legba! - int initialTA = (int)(timingError + 0.5F); - if (initialTA<0) initialTA=0; - if (initialTA>62) initialTA=62; - const L3ImmediateAssignment assign( - L3RequestReference(RA,when), - LCH->channelDescription(), - L3TimingAdvance(initialTA) - ); - LOG(INFO) << "sending L3ImmediateAssignment " << assign; - // (pat) This call appears to block. - // (david) Not anymore. It got fixed in the trunk while you were working on GPRS. - // (doug) Adding in a delay to make sure SI5/6 get out before IA. - // (pat) I believe this sleep is necessary partly because of a delay in starting the SI5/SI6 service loop; - // see SACCHLogicalChannel::serviceLoop() which sleeps when the channel is inactive. - // I reduced the delays in both places. - //sleepFrames(20); - sleepFrames(4); - AGCH->l2sendm(assign); - - // On successful allocation, shrink T3122. - gBTS.shrinkT3122(); -} - - - -void* Control::AccessGrantServiceLoop(void*) -{ - while (true) { - ChannelRequestRecord *req = gBTS.nextChannelRequest(); - if (!req) continue; - AccessGrantResponder( - req->RA(), req->frame(), - req->RSSI(), req->timingError() - ); - delete req; - } - return NULL; -} - - - -GSM::ChannelType NewPagingEntry::getChanType() -{ - if (mWantCS && gConfig.getBool("Control.VEA")) { - // Very early assignment. - return GSM::TCHFType; - } else { - return GSM::SDCCHType; - } -} - -// This does a lot of mallocing. -L3MobileIdentity NewPagingEntry::getMobileId() -{ - if (mTmsi.valid()) { - // page by tmsi - return L3MobileIdentity(mTmsi.value()); - } else { - // page by imsi - return L3MobileIdentity(mImsi.c_str()); - } -} - -static unsigned newPageAll() -{ - LOG(DEBUG); - NewPagingList_t pages; - gMMLayer.mmGetPages(pages,true); // Blocks until there are some. - - LOG(INFO) << "paging " << pages.size() << " mobile(s)"; - - // Page remaining entries, two at a time if possible. - // These PCH send operations are non-blocking. - for (NewPagingList_t::iterator lp = pages.begin(); lp != pages.end(); ) { - // FIXME -- This completely ignores the paging groups. - // HACK -- So we send every page twice. - // That will probably mean a different Pager for each subchannel. - // See GSM 04.08 10.5.2.11 and GSM 05.02 6.5.2. - const L3MobileIdentity& id1 = lp->getMobileId(); - ChannelType type1 = lp->getChanType(); - ++lp; - if (lp==pages.end()) { - // Just one ID left? - LOG(DEBUG) << "paging " << id1; - gBTS.getPCH(0)->l2sendm(L3PagingRequestType1(id1,type1)); - gBTS.getPCH(0)->l2sendm(L3PagingRequestType1(id1,type1)); - break; - } - // Page by pairs when possible. - const L3MobileIdentity& id2 = lp->getMobileId(); - ChannelType type2 = lp->getChanType(); - ++lp; - LOG(DEBUG) << "paging " << id1 << " and " << id2; - gBTS.getPCH(0)->l2sendm(L3PagingRequestType1(id1,type1,id2,type2)); - gBTS.getPCH(0)->l2sendm(L3PagingRequestType1(id1,type1,id2,type2)); - } - - return pages.size(); -} - - -size_t Pager::pagingEntryListSize() -{ - NewPagingList_t pages; - gMMLayer.mmGetPages(pages,false); - return pages.size(); -} - -void Pager::start() -{ - if (mRunning) return; - mRunning=true; - mPagingThread.start((void* (*)(void*))PagerServiceLoopAdapter, (void*)this); -} - - - -void* Control::PagerServiceLoopAdapter(Pager *pager) -{ - pager->serviceLoop(); - return NULL; -} - -void Pager::serviceLoop() -{ - while (mRunning) { - // Wait for pending activity to clear the channel. - // This wait is what causes PCH to have lower priority than AGCH. - // getPCH returns the minimum load PCH. Each PCH sends one paging message per 51-multiframe. - if (unsigned load = gBTS.getPCH()->load()) { - LOG(DEBUG) << "Pager waiting with load " << load; - sleepFrames(51); // There could be multiple paging channels, in which case this is too long. - continue; - } - newPageAll(); - } -} - - - -// vim: ts=4 sw=4 diff --git a/Control/RadioResource.h b/Control/RadioResource.h deleted file mode 100644 index a08b0df..0000000 --- a/Control/RadioResource.h +++ /dev/null @@ -1,177 +0,0 @@ -/**@file GSM Radio Resource procedures, GSM 04.18 and GSM 04.08. */ -/* -* Copyright 2008, 2009, 2010 Free Software Foundation, Inc. -* Copyright 2010 Kestrel Signal Processing, Inc. -* Copyright 2011 Range Networks, Inc. -* -* This software is distributed under multiple licenses; -* see the COPYING file in the main directory for licensing -* information for this specific distribuion. -* -* This use of this software may be subject to additional restrictions. -* See the LEGAL file in the main directory for details. - - 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. - -*/ - -#ifndef RADIORESOURCE_H -#define RADIORESOURCE_H - -#include -#include -#include "ControlCommon.h" -#include - - -namespace GSM { -class Time; -class TCHFACCHLogicalChannel; -class SACCHLogicalChannel; -class L3PagingResponse; -class L3AssignmentComplete; -class L3HandoverComplete; -class L3HandoverAccess; -class L3MeasurementResults; -}; - -namespace Control { - -class TransactionEntry; - - - - -/** - Determine whether or not to initiate a handover. - @param measurements The measurement results from the SACCH. - @param SACCH The SACCH in question. -*/ -void HandoverDetermination(const GSM::L3MeasurementResults &measurements, float myRxLev, GSM::SACCHLogicalChannel* SACCH); - - -/** Find and complete the in-process transaction associated with a paging repsonse. */ -void PagingResponseHandler(const GSM::L3PagingResponse*, L3LogicalChannel*); - -/** Find and compelte the in-process transaction associated with a completed assignment. */ -void AssignmentCompleteHandler(const GSM::L3AssignmentComplete*, GSM::TCHFACCHLogicalChannel*); - -/** Save handover parameters from L1 in the proper transaction record. */ -bool SaveHandoverAccess(unsigned handoverReference, float RSSI, float timingError, const GSM::Time& timestamp); - -/** Process the handover access; returns when the transaction is cleared. */ -void ProcessHandoverAccess(L3LogicalChannel *chan); -bool outboundHandoverTransfer(TranEntry* transaction, L3LogicalChannel *TCH); - -/**@ Access Grant mechanisms */ -//@{ - - -/** Decode RACH bits and send an immediate assignment; may block waiting for a channel. */ -//void AccessGrantResponder( -// unsigned requestReference, const GSM::Time& when, -// float RSSI, float timingError); - - -/** This record carries all of the parameters associated with a RACH burst. */ -class ChannelRequestRecord { - - private: - - unsigned mRA; ///< request reference - GSM::Time mFrame; ///< receive timestamp - float mRSSI; ///< dB wrt full scale - float mTimingError; ///< correlator timing error in symbol periods - - public: - - ChannelRequestRecord( - unsigned wRA, const GSM::Time& wFrame, - float wRSSI, float wTimingError) - :mRA(wRA), mFrame(wFrame), - mRSSI(wRSSI), mTimingError(wTimingError) - { } - - unsigned RA() const { return mRA; } - const GSM::Time& frame() const { return mFrame; } - float RSSI() const { return mRSSI; } - float timingError() const { return mTimingError; } - -}; - - -/** A thread to process contents of the channel request queue. */ -void* AccessGrantServiceLoop(void*); - - -//@} - -/**@ Paging mechanisms */ -//@{ - -/** An entry in the paging list. */ - -struct NewPagingEntry { - bool mWantCS; // true for CS service requested, false for anything else, which is SMS. - std::string mImsi; // Always provided. - TMSI_t mTmsi; // Provided if known and we are sure it has been assigned to the MS. - // Such a clever language. - NewPagingEntry(bool wWantCS, std::string &wImsi, TMSI_t &wTmsi) : mWantCS(wWantCS), mImsi(wImsi), mTmsi(wTmsi) {} - - GSM::ChannelType getChanType(); - GSM::L3MobileIdentity getMobileId(); -}; -typedef std::vector NewPagingList_t; - - - -/** - 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: - - 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(); - - private: - - /** A loop that repeatedly calls pageAll. */ - void serviceLoop(); - - /** C-style adapter. */ - friend void *PagerServiceLoopAdapter(Pager*); - -public: - - /** return size of PagingEntryList */ - size_t pagingEntryListSize(); -}; - - -void *PagerServiceLoopAdapter(Pager*); - - - -//@} // paging mech - -} - - -#endif diff --git a/Control/SMSCB.cpp b/Control/SMSCB.cpp deleted file mode 100644 index 81916a8..0000000 --- a/Control/SMSCB.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/**@file SMSCB Control (L3), GSM 03.41. */ -/* -* Copyright 2010 Kestrel Signal Processing, Inc. -* Copyright 2014 Range Networks, Inc. -* -* This software is distributed under multiple licenses; -* see the COPYING file in the main directory for licensing -* information for this specific distribution. -* -* This use of this software may be subject to additional restrictions. -* See the LEGAL file in the main directory for details. - - 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. - -*/ - -#define LOG_GROUP LogGroup::Control - -#include "ControlCommon.h" -#include -#include -#include -#include -#include -#include - -// (pat) See GSM 03.41. SMSCB is a broadcast message service on a dedicated broadcast channel and unrelated to anything else. -// Broadcast messages are completely unacknowledged. They are repeated perpetually. - - -static const char* createSMSCBTable = { - "CREATE TABLE IF NOT EXISTS SMSCB (" - "GS INTEGER NOT NULL, " - "MESSAGE_CODE INTEGER NOT NULL, " - "UPDATE_NUMBER INTEGER NOT NULL, " - "MSGID INTEGER NOT NULL, " - "LANGUAGE_CODE INTEGER NOT NULL, " - "MESSAGE TEXT NOT NULL, " - "SEND_TIME INTEGER DEFAULT 0, " - "SEND_COUNT INTEGER DEFAULT 0" - ")" -}; - - - -sqlite3* SMSCBConnectDatabase(const char* path, sqlite3 **DB) -{ - int rc = sqlite3_open(path,DB); - if (rc) { - LOG(EMERG) << "Cannot open SMSCB database on path " << path << ": " << sqlite3_errmsg(*DB); - sqlite3_close(*DB); - *DB = NULL; - return NULL; - } - if (!sqlite3_command(*DB,createSMSCBTable)) { - LOG(EMERG) << "Cannot create SMSCB table"; - return NULL; - } - // Set high-concurrency WAL mode. - if (!sqlite3_command(*DB,enableWAL)) { - LOG(EMERG) << "Cannot enable WAL mode on database at " << path << ", error message: " << sqlite3_errmsg(*DB); - } - return *DB; -} - - -void encode7(char mc, int &shift, unsigned int &dp, int &buf, char *thisPage) -{ - buf |= (mc & 0x7F) << shift--; - if (shift < 0) { - shift = 7; - } else { - thisPage[dp++] = buf & 0xFF; - buf = buf >> 8; - } -} - - -void SMSCBSendMessage(sqlite3* DB, sqlite3_stmt* stmt, GSM::CBCHLogicalChannel* CBCH) -{ - // Get the message parameters. - // These column numbers need to line up with the argeuments to the SELEECT. - unsigned GS = (unsigned)sqlite3_column_int(stmt,0); - unsigned messageCode = (unsigned)sqlite3_column_int(stmt,1); - unsigned updateNumber = (unsigned)sqlite3_column_int(stmt,2); - unsigned messageID = (unsigned)sqlite3_column_int(stmt,3); - char* messageText = strdup((const char*)sqlite3_column_text(stmt,4)); - unsigned languageCode = (unsigned)sqlite3_column_int(stmt,5); - unsigned sendCount = (unsigned)sqlite3_column_int(stmt,6); - unsigned rowid = (unsigned)sqlite3_column_int(stmt,7); - // Done with the database entry. - // Finalize ASAP to unlock the database. - sqlite3_finalize(stmt); - - // Figure out how many pages to send. - const unsigned maxLen = 40*15; - unsigned messageLen = strlen((const char*)messageText); - if (messageLen>maxLen) { - LOG(ALERT) << "SMSCB message ID " << messageID << " to long; truncating to " << maxLen << " char."; - messageLen = maxLen; - } - unsigned numPages = messageLen / 40; - if (messageLen % 40) numPages++; - unsigned mp = 0; - - LOG(INFO) << "sending message ID=" << messageID << " code=" << messageCode << " in " << numPages << " pages: " << messageText; - - // Break into pages and send each page. - for (unsigned page=0; page> 8; - thisPage[dp++] = languageCode & 0x0ff; - while (dp<82 && mp> 8, shift, dp, buf, thisPage); - // encode7(languageCode & 0xFF, shift, dp, buf, thisPage); - // encode7('\r', shift, dp, buf, thisPage); - while (dp<81 && mpl2sendm(message); - } - free(messageText); - - // Update send count and send time in the database. - char query[100]; - sprintf(query,"UPDATE SMSCB SET SEND_TIME = %u, SEND_COUNT = %u WHERE ROWID == %u", - (unsigned)time(NULL), sendCount+1, rowid); - if (!sqlite3_command(DB,query)) LOG(ALERT) << "timestamp update failed: " << sqlite3_errmsg(DB); -} - - - - - - -void* Control::SMSCBSender(void*) -{ - // Connect to the database. - // Just keep trying until it connects. - sqlite3 *DB; - while (!SMSCBConnectDatabase(gConfig.getStr("Control.SMSCB.Table").c_str(),&DB)) { sleep(1); } - LOG(NOTICE) << "SMSCB service starting"; - - // Get a channel. - GSM::CBCHLogicalChannel* CBCH = gBTS.getCBCH(); - - while (1) { - // Get the next message ready to send. - const char* query = - "SELECT" - " GS,MESSAGE_CODE,UPDATE_NUMBER,MSGID,MESSAGE,LANGUAGE_CODE,SEND_COUNT,ROWID" - " FROM SMSCB" - " WHERE SEND_TIME==(SELECT min(SEND_TIME) FROM SMSCB)"; - sqlite3_stmt *stmt; - if (sqlite3_prepare_statement(DB,&stmt,query)) { - LOG(ALERT) << "Cannot access SMSCB database: " << sqlite3_errmsg(DB); - sleep(1); - continue; - } - // Send the message or sleep briefly. - int result = sqlite3_run_query(DB,stmt); - if (result==SQLITE_ROW) SMSCBSendMessage(DB,stmt,CBCH); - else sleep(1); - // Log errors. - if ((result!=SQLITE_ROW) && (result!=SQLITE_DONE)) - LOG(ALERT) << "SCSCB database failure: " << sqlite3_errmsg(DB); - } - // keep the compiler from whining - return NULL; -} - - -// vim: ts=4 sw=4 diff --git a/Control/TMSITable.cpp b/Control/TMSITable.cpp index 10a56c4..5080e53 100644 --- a/Control/TMSITable.cpp +++ b/Control/TMSITable.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -907,7 +908,7 @@ void TMSITable::setIMEI(string IMSI, string IMEI) bool TMSITable::classmark(const char* IMSI, const GSM::L3MobileStationClassmark2& classmark) { - int A5Bits = (classmark.A5_1()<<2) + (classmark.A5_2()<<1) + classmark.A5_3(); + int A5Bits = classmark.getA5Bits(); char query[100]; snprintf(query,100, "UPDATE TMSI_TABLE SET A5_SUPPORT=%u,ACCESSED=%u,POWER_CLASS=%u " @@ -918,11 +919,12 @@ bool TMSITable::classmark(const char* IMSI, const GSM::L3MobileStationClassmark2 } +// Return 0 for no encryption, or the algorithm number, ie, 1 means A5_1, 2 means A5_2, etc. unsigned getPreferredA5Algorithm(unsigned A5Bits) { - if (A5Bits&1) return 3; - // if (A5Bits&2) return 2; not supported - if (A5Bits&4) return 1; + if (A5Bits & GSM::EncryptionAlgorithm::Bit5_3) return 3; + // if (A5Bits & GSM::EncryptionAlgorithm::Bit5_2) return 2; // not supported + if (A5Bits & GSM::EncryptionAlgorithm::Bit5_1) return 1; return 0; } diff --git a/GPRS/Makefile.am b/GPRS/Makefile.am index 14fb5e2..7dd684c 100644 --- a/GPRS/Makefile.am +++ b/GPRS/Makefile.am @@ -21,14 +21,11 @@ include $(top_srcdir)/Makefile.common -AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) - -#AM_CXXFLAGS = -O0 -g -CXXFLAGS = -g -O2 +# AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) +# CXXFLAGS = -g -O2 noinst_LTLIBRARIES = libGPRS.la - libGPRS_la_SOURCES = \ MSInfo.cpp \ RLCEngine.cpp \ diff --git a/GPRS/TBF.cpp b/GPRS/TBF.cpp index 330138b..4a2154c 100644 --- a/GPRS/TBF.cpp +++ b/GPRS/TBF.cpp @@ -492,7 +492,7 @@ L3ImmediateAssignment *gprsPageCcchStart( void sendAssignmentCcch( PDCHL1FEC *pacch, // The PACCH channel where the MS will be directed to send its answer. TBF *tbf, // The TBF that wants to initiate a downlink transfer to the MS. - std::ostream *os) // not supported in C_RELEASE. + std::ostream *os) { MSInfo *ms = tbf->mtMS; string imsi = ms->sgsnFindImsiByHandle(ms->msGetHandle()); diff --git a/GSM/GSMCCCH.cpp b/GSM/GSMCCCH.cpp index a394f6a..9cda9db 100644 --- a/GSM/GSMCCCH.cpp +++ b/GSM/GSMCCCH.cpp @@ -336,7 +336,9 @@ bool CCCHLogicalChannel::processRaches() // // Nothing available? // if (!LCH) { // failure: +#if 0 // return false; +#endif // } if (! rach->mChan) { diff --git a/GSM/GSMConfig.cpp b/GSM/GSMConfig.cpp index 461a593..135b60b 100644 --- a/GSM/GSMConfig.cpp +++ b/GSM/GSMConfig.cpp @@ -38,6 +38,10 @@ extern void PagerStart(); int GSM::gNumC7s, GSM::gNumC1s; // Number of C7 and C1 timelots created. static bool testStart = false; // Set this to try testing without the channels started. +bool GSM::isCBSEnabled() { + return gConfig.getStr("Control.SMSCB.Table").length() != 0; +} + GSMConfig::GSMConfig() :mCBCH(NULL), @@ -536,6 +540,24 @@ void GSMConfig::createCombinationI(TransceiverManager& TRX, unsigned CN, unsigne class Beacon {}; +static Thread CBCHControlThread; +static bool isCBSRunning = false; +void GSMConfig::createCBCH(ARFCNManager *radio, TypeAndOffset type, int CN, int TN) +{ + LOG(INFO) << "creating CBCH for SMSCB"; + // CBCH is always SDCCH 2 (third one) but may be on any timeslot, depending on beacon type. + CBCHLogicalChannel *CBCH = new CBCHLogicalChannel(CN,TN,type == SDCCH_4_2 ? gSDCCH4[2] : gSDCCH8[2]); + CBCH->downstream(radio); + CBCH->cbchOpen(); + gBTS.addCBCH(CBCH); + if (!isCBSRunning) { // As of 8-2014, this is only called once so this test is irrelevant. + isCBSRunning = true; + CBCHControlThread.start((void*(*)(void*))Control::SMSCBSender,NULL); + } + mCBCHDescription = L3ChannelDescription(type,TN,gConfig.getNum("GSM.Identity.BSIC.BCC"),gConfig.getNum("GSM.Radio.C0")+CN); + regenerateBeacon(); // To update SI4 with the new CBCH timeslot. +} + // Combination-5 beacon has 3 x CCCH + 4 x SDCCH. // There can be only one Combination 5 beacon, always on timeslot 0. class BeaconC5 : public Beacon { @@ -544,8 +566,6 @@ class BeaconC5 : public Beacon { BCCHL1FEC BCCH; RACHL1FEC *RACH; CCCHLogicalChannel *CCCH; - CBCHLogicalChannel *CBCH; - Thread CBCHControlThread; SDCCHLogicalChannel *C0T0SDCCH[4]; Thread C0T0SDCCHControlThread[4]; @@ -573,8 +593,9 @@ class BeaconC5 : public Beacon { C0T0SDCCH[2] = new SDCCHLogicalChannel(0,0,gSDCCH_4_2); C0T0SDCCH[3] = new SDCCHLogicalChannel(0,0,gSDCCH_4_3); - // Subchannel 2 used for CBCH if SMSCB enabled. - bool SMSCB = (gConfig.getStr("Control.SMSCB.Table").length() != 0); + // GSM 5.02 6.4.1: Subchannel 2 used for CBCH if CBS enabled. + // (pat) CBCH location is advertised in L3SystemInformationType4. + bool SMSCB = isCBSEnabled(); for (int i=0; i<4; i++) { if (SMSCB && (i==2)) continue; C0T0SDCCH[i]->downstream(radio); @@ -584,14 +605,8 @@ class BeaconC5 : public Beacon { gBTS.addSDCCH(C0T0SDCCH[i]); } // Install CBCH if used. - // It writes on SDCCH4. if (SMSCB) { - LOG(INFO) << "creating CBCH for SMSCB"; - CBCH = new CBCHLogicalChannel(gSDCCH_4_2); - CBCH->downstream(radio); - CBCH->lcopen(); - gBTS.addCBCH(CBCH); - CBCHControlThread.start((void*(*)(void*))Control::SMSCBSender,NULL); + gBTS.createCBCH(radio,SDCCH_4_2,0,0); } } }; diff --git a/GSM/GSMConfig.h b/GSM/GSMConfig.h index 0e941b4..6bb5a54 100644 --- a/GSM/GSMConfig.h +++ b/GSM/GSMConfig.h @@ -50,6 +50,11 @@ class SDCCHList : public std::vector {}; class TCHList : public std::vector {}; typedef std::vector L2ChanList; +struct TimeSlot { + int mCN, mTN; + TimeSlot(int wCN,int wTN) : mCN(wCN), mTN(wTN) {} +}; + /** This object carries the top-level GSM air interface configuration. It serves as a central clearinghouse to get access to everything else in the GSM code. @@ -218,11 +223,10 @@ class GSMConfig { /**@ Manage the CBCH. */ //@{ - + L3ChannelDescription mCBCHDescription; /** The add method is not mutex protected and should only be used during initialization. */ - void addCBCH(CBCHLogicalChannel *wCBCH) - { assert(mCBCH==NULL); mCBCH=wCBCH; } - + void addCBCH(CBCHLogicalChannel *wCBCH) { assert(mCBCH==NULL); mCBCH=wCBCH; } + void createCBCH(ARFCNManager *radio, TypeAndOffset type, int CN, int TN); CBCHLogicalChannel* getCBCH() { return mCBCH; } //@} @@ -284,6 +288,8 @@ class GSMConfig { extern int gNumC7s, gNumC1s; +extern bool isCBSEnabled(); + }; // GSM diff --git a/GSM/GSML1FEC.cpp b/GSM/GSML1FEC.cpp index 2491d4c..250dc6d 100644 --- a/GSM/GSML1FEC.cpp +++ b/GSM/GSML1FEC.cpp @@ -397,6 +397,8 @@ bool imsi2kc(string wIMSI, unsigned char *wKc) for (size_t p = 0; p < kc.length(); p += 2) { *dst++ = (unhex(kc[p]) << 4) | (unhex(kc[p+1])); } + // Dont leave this message in production code: + LOG(DEBUG) << format("decrypt maybe imsi=%s Kc=%s",wIMSI.c_str(),kc.c_str()); return true; } @@ -409,6 +411,7 @@ bool L1Decoder::decrypt_maybe(string wIMSI, int wA5Alg) if (!imsi2kc(wIMSI, mKc)) return false; mEncrypted = ENCRYPT_MAYBE; mEncryptionAlgorithm = wA5Alg; + LOG(DEBUG) << format("decrypt maybe imsi=%s algorithm=%d",wIMSI.c_str(),mEncryptionAlgorithm); return true; } @@ -817,10 +820,13 @@ void XCCHL1Decoder::decrypt() int t2 = fn % 26; int t3 = fn % 51; int count = (t1<<11) | (t3<<5) | t2; + LOG(DEBUG) <> (j%8)))) { @@ -1186,8 +1192,10 @@ void L1Encoder::transmit(BitVector2 *mI, BitVector2 *mE, const int *qbits) int count = (t1<<11) | (t3<<5) | t2; if (mEncryptionAlgorithm == 1) { A51_GSM(kc, 64, count, block1, block2); - } else { + } else if (mEncryptionAlgorithm == 3) { A53_GSM(kc, 64, count, block1, block2); + } else { + devassert(0); } for (int i = 0; i < 114; i++) { int b = p ? (random() & 0xFFFFFF) < p : 0; @@ -1672,8 +1680,10 @@ void TCHFACCHL1Decoder::decrypt(int B) int count = (t1<<11) | (t3<<5) | t2; if (mEncryptionAlgorithm == 1) { A51_GSM(mKc, 64, count, block1, block2); - } else { + } else if (mEncryptionAlgorithm == 3) { A53_GSM(mKc, 64, count, block1, block2); + } else { + devassert(0); } for (int j = 0; j < 114; j++) { if ((block2[j/8] & (0x80 >> (j%8)))) { @@ -2355,8 +2365,10 @@ void TCHFACCHL1Encoder::dispatch() int count = (t1<<11) | (t3<<5) | t2; if (mEncryptionAlgorithm == 1) { A51_GSM(kc, 64, count, block1, block2); - } else { + } else if (mEncryptionAlgorithm == 3) { A53_GSM(kc, 64, count, block1, block2); + } else { + devassert(0); } for (int i = 0; i < 114; i++) { int b = p ? (random() & 0xFFFFFF) < p : 0; @@ -2674,6 +2686,7 @@ void SACCHL1Encoder::sendFrame(const L2Frame& frame) void CBCHL1Encoder::sendFrame(const L2Frame& frame) { + OBJLOG(DEBUG); // Sync to (FN/51)%8==0 at the start of a new block. if (frame.peekField(4,4)==0) { mNextWriteTime.rollForward(mMapping.frameMapping(0),51*8); diff --git a/GSM/GSML1FEC.h b/GSM/GSML1FEC.h index c9b4053..28783b8 100644 --- a/GSM/GSML1FEC.h +++ b/GSM/GSML1FEC.h @@ -140,7 +140,7 @@ class L1Encoder { public: EncryptionType mEncrypted; - int mEncryptionAlgorithm; + int mEncryptionAlgorithm; // Algorithm number, ie 1 means A5_1, 2 means A5_2, etc. /** The basic encoder constructor. @@ -332,7 +332,7 @@ class L1Decoder { //ViterbiR2O4 mVCoder; ///< nearly all GSM channels use the same convolutional code EncryptionType mEncrypted; - int mEncryptionAlgorithm; + int mEncryptionAlgorithm; // Algorithm number, ie 1 means A5_1, 2 means A5_2, etc. unsigned char mKc[8]; int mFN[8]; @@ -1482,9 +1482,8 @@ class CBCHL1Encoder : public XCCHL1Encoder { public: - CBCHL1Encoder(const TDMAMapping& wMapping, - L1FEC* wParent) - :XCCHL1Encoder(0,0,wMapping,wParent) + CBCHL1Encoder(int wCN, int wTN, const TDMAMapping& wMapping, L1FEC* wParent) + :XCCHL1Encoder(wCN,wTN,wMapping,wParent) {} /** Override sendFrame to meet sync requirements of GSM 05.02 6.5.4. */ @@ -1523,10 +1522,10 @@ class CBCHL1FEC : public L1FEC { public: - CBCHL1FEC(const MappingPair& wMapping) + CBCHL1FEC(int wCN, int wTN, const MappingPair& wMapping) :L1FEC() { - mEncoder = new CBCHL1Encoder(wMapping.downlink(),this); + mEncoder = new CBCHL1Encoder(wCN,wTN,wMapping.downlink(),this); } //string debugId() const { static string id; return id.size() ? id : (id=format("CBCHL1FEC %s ",descriptiveString())); } }; diff --git a/GSM/GSML3CommonElements.h b/GSM/GSML3CommonElements.h index 8353da0..85b474e 100644 --- a/GSM/GSML3CommonElements.h +++ b/GSM/GSML3CommonElements.h @@ -180,6 +180,16 @@ class L3MobileStationClassmark1 : public L3ProtocolElement { }; + +// (pat) Added 9-2014. Encoding for encryption algorithms. +struct EncryptionAlgorithm { + // These bits define a mask of supported encryption algorithms. + enum { + Bit5_1 = 1, + Bit5_2 = 2, + Bit5_3 = 4 + }; +}; /** Mobile Station Classmark 2, GSM 04.08 10.5.1.5 */ @@ -213,9 +223,19 @@ class L3MobileStationClassmark2 : public L3ProtocolElement { void text(std::ostream&) const; // These return true if the encryption type is supported. + private: bool A5_1() const { return mA5_1==0; } bool A5_2() const { return mA5_2!=0; } bool A5_3() const { return mA5_3!=0; } + public: + // (pat) Pre-9-2014 encoding: int getA5Bits() const { return (A5_1()<<2) + (A5_2()<<1) + A5_3(); } + int getA5Bits() const { + int result = 0; + if (A5_1()) { result |= EncryptionAlgorithm::Bit5_1; } + if (A5_2()) { result |= EncryptionAlgorithm::Bit5_2; } + if (A5_3()) { result |= EncryptionAlgorithm::Bit5_3; } + return result; + } // Returns the power class, based on power capability encoding. int powerClass() const { return mRFPowerCapability+1; } diff --git a/GSM/GSML3Message.h b/GSM/GSML3Message.h index dc063ff..caea01d 100644 --- a/GSM/GSML3Message.h +++ b/GSM/GSML3Message.h @@ -93,6 +93,7 @@ class L3Message { /** Generate an L3Frame for this message. The caller is responsible for deleting the memory. + (pat) TODO: This is called only from RRLPServer, and apparently unnecessarily. Get rid of this. */ L3Frame* frame(GSM::Primitive prim=L3_DATA) const; diff --git a/GSM/GSML3RRElements.h b/GSM/GSML3RRElements.h index ab9ff91..c3ab3b7 100644 --- a/GSM/GSML3RRElements.h +++ b/GSM/GSML3RRElements.h @@ -471,6 +471,8 @@ public: :mTypeAndOffset(TDMA_MISC), mTN(0),mTSC(0),mHFlag(0),mARFCN(0),mMAIO(0),mHSN(0) { } + + bool initialized() { return mTypeAndOffset != TDMA_MISC; } size_t lengthV() const { return 3; } @@ -964,14 +966,15 @@ class L3CipheringModeSetting : public L3ProtocolElement protected: bool mCiphering; - int mAlgorithm; // algorithm is A5/mAlgorithm + int mAlgorithm; // algorithm is A5/mAlgorithm, 1 means A5_1, which is encoded in the IE as 0. public: + // Pass in the algorithm number, ie 1 means A5_1, 2 means A5_2, etc. L3CipheringModeSetting(bool wCiphering, int wAlgorithm) :mCiphering(wCiphering), mAlgorithm(wAlgorithm) { - // assert(wAlgorithm >= 1 && wAlgorithm <= 7); + // devassert(wAlgorithm >= 1 && wAlgorithm <= 7); } size_t lengthV() const; diff --git a/GSM/GSML3RRMessages.cpp b/GSM/GSML3RRMessages.cpp index 91fc90c..b3c0ea8 100644 --- a/GSM/GSML3RRMessages.cpp +++ b/GSM/GSML3RRMessages.cpp @@ -421,9 +421,14 @@ void L3SIType4RestOctets::writeText(std::ostream& os) const L3SystemInformationType4::L3SystemInformationType4() :L3RRMessageRO(), - mHaveCBCH(gConfig.getStr("Control.SMSCB.Table").length() != 0), - mCBCHChannelDescription(SDCCH_4_2,0,gConfig.getNum("GSM.Identity.BSIC.BCC"),gConfig.getNum("GSM.Radio.C0")) -{ } + mHaveCBCH(false) +{ + // Dont advertise CBCH unless and until we have a valid channel description for it. + if (isCBSEnabled() && gBTS.mCBCHDescription.initialized()) { + mHaveCBCH = true; + mCBCHChannelDescription = gBTS.mCBCHDescription; + } +} size_t L3SystemInformationType4::l2BodyLength() const diff --git a/GSM/GSMLogicalChannel.cpp b/GSM/GSMLogicalChannel.cpp index de6a686..5b42169 100644 --- a/GSM/GSMLogicalChannel.cpp +++ b/GSM/GSMLogicalChannel.cpp @@ -120,7 +120,9 @@ void L2LogicalChannelBase::startl1() void L2SAPMux::flushL3In() { while (L3Frame *l3f = mL3In.readNoBlock()) { - LOG(ERR)<< "channel closure caused message to be discarded:"<primitive() != L3_RELEASE_REQUEST && l3f->primitive() != L3_HARDRELEASE_REQUEST) { + LOG(ERR)<< "channel closure caused message to be discarded:"<l2sendf(frame); return; } switch (frame.primitive()) { @@ -901,12 +907,12 @@ TCHFACCHLogicalChannel::TCHFACCHLogicalChannel( -CBCHLogicalChannel::CBCHLogicalChannel(const CompleteMapping& wMapping) +CBCHLogicalChannel::CBCHLogicalChannel(int wCN, int wTN, const CompleteMapping& wMapping) { - mL1 = new CBCHL1FEC(wMapping.LCH()); + mL1 = new CBCHL1FEC(wCN, wTN, wMapping.LCH()); L2DL *sap0 = new CBCHL2; sapInit(sap0,NULL); - mSACCH = new SACCHLogicalChannel(0,0,wMapping.SACCH(),this); + mSACCH = new SACCHLogicalChannel(wCN,wTN,wMapping.SACCH(),this); connect(mL1); } @@ -918,6 +924,24 @@ void CBCHLogicalChannel::l2sendm(const L3SMSCBMessage& msg) l2sendf(frame); } +void CBCHLogicalChannel::l2sendf(const L3Frame& frame) +{ + if (mL2[0]) { // Should always be set, but protects against a race during startup. + mL2[0]->l2dlWriteHighSide(frame); + } +} + +void CBCHLogicalChannel::cbchOpen() +{ + if (mL1) mL1->l1init(); // (pat) L1FEC::l1init() + sapStart(); // startl1(); + devassert(mSACCH); + if (mSACCH) { + mSACCH->sacchInit(); + mSACCH->sapStart(); + } +} + ostream& operator<<(ostream& os, const L2LogicalChannelBase& chan) { os << chan.descriptiveString(); diff --git a/GSM/GSMLogicalChannel.h b/GSM/GSMLogicalChannel.h index 32d85de..b2a0813 100644 --- a/GSM/GSMLogicalChannel.h +++ b/GSM/GSMLogicalChannel.h @@ -610,25 +610,28 @@ class TCHFACCHLogicalChannel : public L2LogicalChannel { See GSM 04.12 3.3.1. */ // (pat) This uses one SDCCH slot, so it has a SACCH that doesnt do anything. +// It must be based on L2LogicalChannel because it has a SACCH, but all the relevant methods +// in L2LogicalChannel are overwritten so messages pass straight through to class CBCHLogicalChannel : public L2LogicalChannel { - - protected: - - /* - The CBCH should be written be a single thread. - The input interface is *not* multi-thread safe. - */ + // The CBCH should be written be a single thread. + // The input interface is *not* multi-thread safe. public: - CBCHLogicalChannel(const CompleteMapping& wMapping); + CBCHLogicalChannel(int wCN, int wTN, const CompleteMapping& wMapping); void l2sendm(const L3SMSCBMessage& msg); + void l2sendm(const L3Message&) { devassert(0); } // No other messages supported. - void l2sendm(const L3Message&) { devassert(0); } + void l2sendf(const L3Frame& frame); + + void cbchOpen(); ChannelType chtype() const { return CBCHType; } + // unneeded: void writeToL1(const L2Frame& frame) { mL1->writeHighSide(frame); } + void writeLowSide(const L2Frame&) { assert(0); } // There is no uplink. + L3Frame * l2recv(unsigned, unsigned) { devassert(0); return NULL; } }; diff --git a/GSM/GSMTransfer.cpp b/GSM/GSMTransfer.cpp index ec58400..9f4f935 100644 --- a/GSM/GSMTransfer.cpp +++ b/GSM/GSMTransfer.cpp @@ -596,8 +596,8 @@ L3Frame::L3Frame(const L3Message& msg, Primitive wPrimitive, SAPI_t wSapi) -L3Frame::L3Frame(const char* hexString) - :mPrimitive(L3_DATA),mSapi(SAPIUndefined) +L3Frame::L3Frame(SAPI_t sapi,const char* hexString) + :mPrimitive(L3_DATA),mSapi(sapi) { f3init(); size_t len = strlen(hexString); @@ -613,17 +613,18 @@ L3Frame::L3Frame(const char* hexString) } -L3Frame::L3Frame(const char* binary, size_t len) - :mPrimitive(L3_DATA),mSapi(SAPIUndefined) -{ - f3init(); - mL2Length = len; - resize(len*8); - size_t wp=0; - for (size_t i=0; isin_port)); } + +// Return a pointer to arg1, or an empty string. Remember it is a pointer into the message so it is not terminated. +static const char *getPeeringMsgArg1(const char *message) +{ + const char *arg = strchr(message,' '); + if (arg) { + while (*arg == ' ') { arg++; } + return arg; + } else { + return message + strlen(message); // If no argument, return pointer to end of string, not a NULL pointer. + } +} + +// (pat 9-2014) Print out peering messages. We triage the messages depending on the log level. +// INFO level - Handover messages only. +// DEBUG level - all messages. +static void logMessage(const char*sendOrRecv, const struct sockaddr_in* peer, const char *message) +{ + const char *arg1 = getPeeringMsgArg1(message); + if (0 == strncmp(arg1,"HANDOVER",8)) { + LOG(INFO) << "Peering "< tran, string cause) { - string msg = string("REQ HANDOVER ") + tran->handoverString(peer); - if (cause.size()) msg += " cause=" + cause; + string msg = string("REQ HANDOVER ") + tran->handoverString(peer,cause); struct sockaddr_in peerAddr; if (!resolveAddress(&peerAddr,peer.c_str())) { LOG(ALERT) << "cannot resolve peer address " << peer; return false; } + //LOG(INFO) <protocol), len, tcph->seq, tcph->ack_seq, iph->id, iph->frag_off, - ip_ntoa(iph->saddr,nbuf1),tcph->source, - ip_ntoa(iph->daddr,nbuf2),tcph->dest); + ip_ntoa(iph->saddr,nbuf1),ntohs(tcph->source), + ip_ntoa(iph->daddr,nbuf2),ntohs(tcph->dest)); + } else if (verbose && iph->protocol == IPPROTO_UDP) { + struct udphdr *udph = (struct udphdr*) (packet + 4 * iph->ihl); + sprintf(result,"proto=%s %d byte packet id=%u frag=%u from %s:%d to %s:%d", + ip_proto_name(iph->protocol), len, + iph->id, iph->frag_off, + ip_ntoa(iph->saddr,nbuf1),ntohs(udph->source), + ip_ntoa(iph->daddr,nbuf2),ntohs(udph->dest)); } else { sprintf(result,"proto=%s %d byte packet from %s to %s", ip_proto_name(iph->protocol), len, diff --git a/SIP/Makefile.am b/SIP/Makefile.am index 8ce56e2..ac5b948 100644 --- a/SIP/Makefile.am +++ b/SIP/Makefile.am @@ -22,11 +22,12 @@ include $(top_srcdir)/Makefile.common # Previously: -I$(ORTP_INCLUDEDIR) $(ORTP_CPPFLAGS) -AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) -AM_CXXFLAGS = -Wall -Wextra +# AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) +# AM_CXXFLAGS = -Wall -Wextra noinst_LTLIBRARIES = libSIP.la +libSIP_la_CXXFLAGS = $(AM_CXXFLAGS) -Wextra libSIP_la_SOURCES = \ SIPRtp.cpp \ SIPParse.cpp \ diff --git a/SIP/SIPBase.cpp b/SIP/SIPBase.cpp index 51c35b8..f39b4d3 100644 --- a/SIP/SIPBase.cpp +++ b/SIP/SIPBase.cpp @@ -38,6 +38,7 @@ #include "SIP2Interface.h" #include "SIPUtility.h" #include "SIPRtp.h" +#include "config.h" // For VERSION #undef WARNING @@ -124,6 +125,15 @@ bool SipBaseProtected::sipIsStuck() const } } +static unsigned nextInviteCSeqNum() // formerly: random()%600; +{ + static unsigned seqNum = 0; + if (seqNum == 0) { + seqNum = random() & 0xfffff; // Any old number will do. + } + return ++seqNum; +} + DialogStateVars::DialogStateVars() { // generate a tag now. @@ -133,7 +143,9 @@ DialogStateVars::DialogStateVars() //mLocalTag=make_tag(); // set our CSeq in case we need one - mLocalCSeq = gSipInterface.nextInviteCSeqNum(); // formerly: random()%600; + // (pat 8-1-2014) We do not want to contaminate class SipBase with an external reference to gSipInterface + //mLocalCSeq = gSipInterface.nextInviteCSeqNum(); // formerly: random()%600; + mLocalCSeq = nextInviteCSeqNum(); // formerly: random()%600; } string DialogStateVars::dsToString() const @@ -494,5 +506,13 @@ void SipCallbacks::writePrivateHeaders(SipMessage *msg, const L3LogicalChannel * if (writePrivateHeaders_callback) { writePrivateHeaders_callback(msg,l3chan); } } +string OpenBTSUserAgent() +{ + // FIXME: This is broken... + static const char* userAgent1 = "OpenBTS " VERSION " Build Date " TIMESTAMP_ISO; + string userAgent = string(userAgent1); + return userAgent; +} + }; // vim: ts=4 sw=4 diff --git a/SIP/SIPBase.h b/SIP/SIPBase.h index 70c6c87..ffef742 100644 --- a/SIP/SIPBase.h +++ b/SIP/SIPBase.h @@ -389,6 +389,7 @@ struct SipCallbacks { } }; +extern string OpenBTSUserAgent(); }; #endif diff --git a/SIP/SIPDialog.cpp b/SIP/SIPDialog.cpp index d7b4463..0dc16ea 100644 --- a/SIP/SIPDialog.cpp +++ b/SIP/SIPDialog.cpp @@ -27,7 +27,6 @@ #include #include #include // for L3CMServiceType -#include "config.h" // For VERSION #include // for L3Cause #include @@ -623,7 +622,12 @@ SipMessage *SipDialog::makeRegisterMsg(DialogType wMethod, const L3LogicalChanne msg->msmAuthorizationValue = format("Digest realm=\"%s\", username=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\", algorithm=MD5, qop=\"auth\" ", realm.c_str(), authUsername.c_str(), RAND.c_str(), authUri.c_str(), response.c_str()); } else { - msg->msmAuthorizationValue = format("Digest, nonce=%s, uri=%s, response=%s",RAND.c_str(),msid.mImsi.c_str(),SRES); + // (pat 9-2014) These fields are all supposed to be quoted. It was a mistake not to quote them. + // The above code that quotes the fields was used for kazoo, so the unquoted strings are only use for sipauthserve. + // Also remove the extraneous leading comma. + // RFC3261 says the fields are quoted. + //msg->msmAuthorizationValue = format("Digest, nonce=%s, uri=%s, response=%s",RAND.c_str(),msid.mImsi.c_str(),SRES); + msg->msmAuthorizationValue = format("Digest nonce=\"%s\", uri=\"%s\", response=\"%s\"",RAND.c_str(),msid.mImsi.c_str(),SRES); } } } else if (wMethod == SIPDTUnregister ) { @@ -943,7 +947,8 @@ static bool isPhoneNumber(string thing) static string removeUriFluff(string thing) { if (unsigned first = thing.find('<') != string::npos) { // Remove the angle brackets. - thing = thing.substr(first,thing.find_last_of('>')); + thing = thing.substr(first); // chop off initial angle bracket. + thing = thing.substr(0,thing.find_last_of('>')); // chop off trailing angle bracket, and anything following. } thing = thing.substr(0,thing.find_last_of('@')); // Chop off the ip address, if any. const char *str = thing.c_str(); @@ -953,6 +958,7 @@ static string removeUriFluff(string thing) } else if (0 == strncasecmp(str,"sips:",5)) { // secure not supported, but always hopeful.... thing = thing.substr(5); } + LOG(DEBUG) <getClock()); - radioClock->wait(); + //radioClock->wait(); demodBurst = mDemodFIFO->get(); } } diff --git a/TransceiverRAD1/runTransceiver.cpp b/TransceiverRAD1/runTransceiver.cpp index d1fb123..9fd40ed 100644 --- a/TransceiverRAD1/runTransceiver.cpp +++ b/TransceiverRAD1/runTransceiver.cpp @@ -234,7 +234,7 @@ ConfigurationKeyMap getConfigurationKeys() map[tmp->getName()] = *tmp; delete tmp; - // (pat 4-2014) Added this to fix problems with "radio dropouts" and the solution was to change this to larger number (5). + // (pat 4-2014) Added this after having problems with "radio dropouts" and the solution was to change this to larger number (5). { ConfigurationKey tmp("TRX.LatencyBumpUp","1", "time measured in GSM timeslots", ConfigurationKey::DEVELOPER, diff --git a/apps/GetConfigurationKeys.cpp b/apps/GetConfigurationKeys.cpp index 15eaa23..c547df1 100644 --- a/apps/GetConfigurationKeys.cpp +++ b/apps/GetConfigurationKeys.cpp @@ -724,13 +724,14 @@ ConfigurationKeyMap getConfigurationKeys() map[tmp.getName()] = tmp; } + // It is CBS [Cell Broadcast Service]. { ConfigurationKey tmp("Control.SMSCB.Table","", "", ConfigurationKey::CUSTOMERWARN, ConfigurationKey::FILEPATH_OPT,// audited "", - true, - "File path for SMSCB scheduling database. " + true, // static because the channel is started in GSMConfig at init time. + "File path for CBS [Cell Broadcast Service] scheduling database. " "By default, this feature is disabled. " "To enable, specify a file path for the database e.g. /var/run/SMSCB.db. " "To disable again, execute \"unconfig Control.SMSCB.Table\"." @@ -3175,6 +3176,17 @@ ConfigurationKeyMap getConfigurationKeys() map[tmp.getName()] = tmp; } + { ConfigurationKey tmp("Control.CDR.Dirname","", // (pat) Added 7-2014. + "filename", + ConfigurationKey::DEVELOPER, + ConfigurationKey::FILEPATH_OPT, + "", + true, + "If set, CDR (Call Data Records) are placed in this directory. A good choice is /var/log/OpenBTS. " + ); + map[tmp.getName()] = tmp; + } + return map; } diff --git a/apps/Makefile.am b/apps/Makefile.am index 8ee8167..ba141bb 100644 --- a/apps/Makefile.am +++ b/apps/Makefile.am @@ -23,7 +23,6 @@ include $(top_srcdir)/Makefile.common DESTDIR := -# These variables are defined in ../Makefile.common # AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) # AM_CXXFLAGS = -Wall -ldl -pthread @@ -31,9 +30,8 @@ noinst_PROGRAMS = \ JSONEventsClient \ OpenBTS \ OpenBTSCLI - -OpenBTS_SOURCES = OpenBTS.cpp GetConfigurationKeys.cpp -OpenBTS_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) +noinst_HEADERS = \ + OpenBTSConfig.h ourlibs = \ $(CLI_LA) \ @@ -51,34 +49,40 @@ ourlibs = \ $(NODEMANAGER_LA) \ $(ORTP_LIBS) +OpenBTS_SOURCES = OpenBTS.cpp GetConfigurationKeys.cpp +OpenBTS_LDADD = $(ourlibs) -lcrypto -lssl -ldl -lortp -la53 -lcoredumper +OpenBTS_LDFLAGS = $(GPROF_OPTIONS) -rdynamic + clilibs= \ $(GLOBALS_LA) -OpenBTS_LDADD = $(ourlibs) -lcrypto -lssl -ldl -lortp -la53 -lcoredumper -OpenBTS_LDFLAGS = $(GPROF_OPTIONS) -rdynamic -noinst_HEADERS = \ - OpenBTSConfig.h - OpenBTSCLI_SOURCES = OpenBTSCLI.cpp OpenBTSCLI_LDADD = $(clilibs) -lreadline -OpenBTSCLI_CXXFLAGS = -DBUILD_CLI +OpenBTSCLI_CXXFLAGS = $(AM_CXXFLAGS) -DBUILD_CLI JSONEventsClient_SOURCES = JSONEventsClient.cpp -JSONEventsClient_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) JSONEventsClient_LDADD = $(NODEMANAGER_LA) EXTRA_DIST = \ + CLI \ OpenBTS.example.sql \ - setUpFiles.sh \ openbts.conf \ + iptables.rules \ + logrotated.OpenBTS \ + openbtsconfig \ + OpenBTSDo \ + rsyslogd.OpenBTS.conf \ generateConfigTable.sh \ exportConfigTable.sh \ - importConfigTable.sh + importConfigTable.sh \ + generateTeX.sh install: OpenBTS OpenBTSCLI mkdir -p "$(DESTDIR)/OpenBTS/" install OpenBTS "$(DESTDIR)/OpenBTS/" install OpenBTSCLI "$(DESTDIR)/OpenBTS/" + install OpenBTSDo "$(DESTDIR)/OpenBTS/" + chmod +x "$(DESTDIR)/OpenBTS/OpenBTSDo" install .gdbinit "$(DESTDIR)/OpenBTS/" mkdir -p "$(DESTDIR)/etc/init/" install openbts.conf "$(DESTDIR)/etc/init/" @@ -88,6 +92,8 @@ install: OpenBTS OpenBTSCLI mkdir -p "$(DESTDIR)/etc/rsyslog.d/" mkdir -p "$(DESTDIR)/etc/logrotate.d/" install rsyslogd.OpenBTS.conf "$(DESTDIR)/etc/rsyslog.d/OpenBTS.conf" - install OpenBTS.logrotate "$(DESTDIR)/etc/logrotate.d/OpenBTS" + install logrotated.OpenBTS "$(DESTDIR)/etc/logrotate.d/OpenBTS" mkdir -p "$(DESTDIR)/home/openbts/" + install CLI "$(DESTDIR)/home/openbts/" install openbtsconfig "$(DESTDIR)/home/openbts/" + diff --git a/apps/OpenBTS.cpp b/apps/OpenBTS.cpp index 9c866bb..87853dc 100644 --- a/apps/OpenBTS.cpp +++ b/apps/OpenBTS.cpp @@ -495,11 +495,6 @@ void processArgs(int argc, char *argv[]) } } -struct TimeSlot { - int mCN, mTN; - TimeSlot(int wCN,int wTN) : mCN(wCN), mTN(wTN) {} -}; - std::deque timeSlotList; static int initTimeSlots() // Return how many slots used by beacon. @@ -957,6 +952,7 @@ vector configurationCrossCheck(const string& key) { warning.str(std::string()); } + // SIP.Local.IP cannot be 127.0.0.1 when any of the SIP.Proxy.* settings are non-localhost } else if (key.compare("SIP.Local.IP") == 0 || key.compare("SIP.Proxy.Registration") == 0 || key.compare("SIP.Proxy.SMS") == 0 || diff --git a/apps/OpenBTSCLI.cpp b/apps/OpenBTSCLI.cpp index c3f46ce..54f2619 100644 --- a/apps/OpenBTSCLI.cpp +++ b/apps/OpenBTSCLI.cpp @@ -220,13 +220,6 @@ int main(int argc, char *argv[]) } } - // Note that this only works if we are communicating across localhost. - // TODO: Fix this so we can push the file across the socket if done - // remotely. - // Don't do this if running the single line configuration methods, we - // will assume running from an external script is mostly a testing - // thing, not a deployment thing for an actual base station. - if (sCommand.c_str()[0] == '\0') { banner(); printf("Connecting to %s:%d...\n", target, port); diff --git a/apps/OpenBTSDo b/apps/OpenBTSDo new file mode 100755 index 0000000..f65edcf --- /dev/null +++ b/apps/OpenBTSDo @@ -0,0 +1,4 @@ +#!/bin/bash + +exec /OpenBTS/OpenBTSCLI -p 49300 -t 127.0.0.1 -d $* + diff --git a/apps/README.DatabaseCreation b/apps/README.DatabaseCreation deleted file mode 100644 index 90d3639..0000000 --- a/apps/README.DatabaseCreation +++ /dev/null @@ -1,14 +0,0 @@ -OpenBTS requires a configuration database, OpenBTS.db. - -In an intitial installation, OpenBTS.db is created from OpenBTS.example.sql. - -To do that: - -sh> sqlite3 OpenBTS.db -sqlite3> .read OpenBTS.example.sql -sqlite3> .quit - -Done! - -BTW: If OpenBTS.db already exists, you will need to delete that file before doing this procedure. - diff --git a/apps/OpenBTS.logrotate b/apps/logrotated.OpenBTS old mode 100755 new mode 100644 similarity index 100% rename from apps/OpenBTS.logrotate rename to apps/logrotated.OpenBTS diff --git a/apps/openbts.conf b/apps/openbts.conf index 3c13517..c37544d 100644 --- a/apps/openbts.conf +++ b/apps/openbts.conf @@ -14,6 +14,30 @@ script exec ./OpenBTS end script +pre-start script + # Since Ubuntu clears /var/run on reboot, create this before we try to start + if [ ! -e /var/run/OpenBTS ]; then + mkdir /var/run/OpenBTS + fi + + if [ ! -e /var/run/rrlp ]; then + mkdir /var/run/rrlp + chmod 777 /var/run/rrlp + fi + + # place for CRD data + if [ ! -e /var/lib/OpenBTS ]; then + mkdir /var/lib/OpenBTS + fi + +# make sure permissions are set up correctly +if [ -d /var/lib/asterisk/sqlite3dir ]; then + chown -R asterisk:www-data /var/lib/asterisk/sqlite3dir + chmod 775 /var/lib/asterisk/sqlite3dir + chmod 664 /var/lib/asterisk/sqlite3dir/sqlite3* +fi +end script + post-stop script if pgrep transceiver; then killall transceiver; fi end script diff --git a/configure.ac b/configure.ac index 36e36f2..417c8e3 100644 --- a/configure.ac +++ b/configure.ac @@ -19,41 +19,45 @@ dnl You should have received a copy of the GNU General Public License dnl along with this program. If not, see . dnl -AC_INIT(openbts,5.0.0-prealpha) -AC_PREREQ(2.57) -AC_CONFIG_SRCDIR([config/Makefile.am]) -AC_CONFIG_AUX_DIR([.]) -AC_CONFIG_MACRO_DIR([config]) -AM_CONFIG_HEADER(config.h) +AC_PREREQ([2.57]) +# must be AC_INIT([openbts], [5.0-master]) +AC_INIT(openbts,5.0-master) AC_CANONICAL_BUILD AC_CANONICAL_HOST AC_CANONICAL_TARGET -AM_INIT_AUTOMAKE +AC_CONFIG_AUX_DIR([.]) +AM_INIT_AUTOMAKE([-Wno-portability]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_MACRO_DIR([config]) +AC_CONFIG_SRCDIR([apps/OpenBTS.cpp]) # was AC_CONFIG_SRCDIR([config/Makefile.am]) dnl Checks for programs. -AM_PROG_AS -AC_PROG_CXX +: ${CXXFLAGS=""} ## remove auto setting of CXXFLAGS = -g -O2 +AC_PROG_CXX +AC_PROG_AWK +AC_PROG_CC +AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET -AC_PROG_INSTALL -AC_PATH_PROG([RM_PROG], [rm]) -#AC_PROG_AWK -#AC_PROG_CC -#AC_PROG_CPP -#AC_PROG_RANLIB +AM_PROG_AS ## GNU radio +AC_PATH_PROG([RM_PROG], [rm]) ## GNU radio +# (oley) uncomment if any libraries are being generated +# AC_PROG_RANLIB + +# AC_LIBTOOL_WIN32_DLL +# AC_PROG_LIBTOOL +LT_INIT -AC_LIBTOOL_WIN32_DLL AC_ENABLE_SHARED dnl do build shared libraries AC_DISABLE_STATIC dnl don't build static libraries -AC_PROG_LIBTOOL dnl Checks for header files. AC_HEADER_STDC dnl This is required for GnuRadio includes to understand endianess correctly: AC_CHECK_HEADERS([byteswap.h]) -#AC_CHECK_HEADERS([arpa/inet.h fcntl.h inttypes.h limits.h malloc.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h string.h sys/file.h sys/ioctl.h sys/mount.h sys/param.h sys/socket.h sys/time.h syslog.h unistd.h utime.h]) +# AC_CHECK_HEADERS([arpa/inet.h fcntl.h inttypes.h limits.h malloc.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h string.h sys/file.h sys/ioctl.h sys/mount.h sys/param.h sys/socket.h sys/time.h syslog.h unistd.h utime.h]) dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST @@ -106,8 +110,7 @@ fi AC_CHECK_LIB(zmq, zmq_init, ,[AC_MSG_ERROR([Cannot link with -lzmq. Install the libzmq3-dev package manually or run $ sudo ./NodeManager/install_libzmq.sh])]) AC_MSG_CHECKING([whether libzmq installation works]) AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], - [zmq_init(1);]) - ], + [zmq_init(1);])], [AC_MSG_RESULT([yes])], [AC_MSG_ERROR([no. Install the libzmq3-dev package manually or run $ sudo ./NodeManager/install_libzmq.sh])]) @@ -124,47 +127,13 @@ AS_IF([gcc -I=/usr/local/include -o config/test_ortp_version config/test_ortp_ve AC_MSG_RESULT([>= 0.22.0]) ], [AC_MSG_RESULT([< 0.22.0])]) -dnl Check for other misc libs -#AC_CHECK_LIB([c], [main]) -#AC_CHECK_LIB([dl], [main]) -#AC_CHECK_LIB([pthread], [main]) -#AC_CHECK_LIB([readline], [main]) -#AC_CHECK_LIB([sqlite3], [main]) -#AC_CHECK_LIB([usrp], [main]) -dnl Checks for typedefs, structures, and compiler characteristics. -#AC_HEADER_STDBOOL -#AC_TYPE_UID_T -#AC_C_INLINE -#AC_TYPE_INT16_T -#AC_TYPE_INT32_T -#AC_TYPE_INT64_T -#AC_TYPE_INT8_T -#AC_TYPE_MODE_T -#AC_TYPE_OFF_T -#AC_TYPE_PID_T -#AC_TYPE_SIZE_T -#AC_TYPE_SSIZE_T -#AC_CHECK_MEMBERS([struct stat.st_blksize]) -#AC_TYPE_UINT16_T -#AC_TYPE_UINT32_T -#AC_TYPE_UINT64_T -#AC_TYPE_UINT8_T +dnl Check for other misc libs +# AC_CHECK_LIB([sqlite3], [main]) dnl Check for glibc-specific network functions -AC_CHECK_FUNC(gethostbyname_r, [AC_DEFINE(HAVE_GETHOSTBYNAME_R, 1, Define if libc implements gethostbyname_r)]) -AC_CHECK_FUNC(gethostbyname2_r, [AC_DEFINE(HAVE_GETHOSTBYNAME2_R, 1, Define if libc implements gethostbyname2_r)]) - -dnl Checks for library functions. -#AC_FUNC_ALLOCA -#AC_FUNC_ERROR_AT_LINE -#AC_FUNC_FORK -#AC_FUNC_MALLOC -#AC_FUNC_MMAP -#AC_FUNC_REALLOC -#AC_FUNC_STRERROR_R -#AC_FUNC_STRTOD -#AC_CHECK_FUNCS([bzero clock_gettime dup2 fdatasync floor gethostbyname getpagesize gettimeofday inet_ntoa localtime_r memchr memmove memset mkdir munmap pow regcomp rint select setenv socket sqrt strcasecmp strchr strdup strerror strncasecmp strpbrk strrchr strstr strtol strtoul sysinfo utime]) +AC_CHECK_FUNC(gethostbyname_r, [AC_DEFINE(HAVE_GETHOSTBYNAME_R, 1, [Define if libc implements gethostbyname_r])]) +AC_CHECK_FUNC(gethostbyname2_r, [AC_DEFINE(HAVE_GETHOSTBYNAME2_R, 1, [Define if libc implements gethostbyname2_r])]) dnl Output files AC_CONFIG_FILES([\ @@ -189,5 +158,4 @@ AC_CONFIG_FILES([\ NodeManager/Makefile \ Scanning/Makefile \ ]) - AC_OUTPUT diff --git a/debian/changelog b/debian/changelog index b33e404..3c2ba63 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,5 @@ -openbts (5.0) unstable; urgency=low +openbts (5.0-master) unstable; urgency=low * Test - -- Donald C. Kirker Mon, 22 Apr 2013 00:11:00 -0700 + -- Range Packager Mon, 15 Sep 2014 17:42:42 -0700 diff --git a/debian/postinst b/debian/postinst index c5e39bb..262f202 100755 --- a/debian/postinst +++ b/debian/postinst @@ -23,6 +23,22 @@ configure() DATE=$(date --rfc-3339='date') CONFIG_BACKUP=/etc/OpenBTS/OpenBTS.dump-$DATE +# add user openbts +if ! getent passwd openbts > /dev/null ; then + echo 'Adding system user for OpenBTS' 1>&2 + adduser --system --group --quiet \ + --home /home/openbts \ + --no-create-home --disabled-login \ + --gecos "OpenBTS daemon" \ + openbts +fi + +# add user openbts to some groups +for group in sudo asterisk www-data; do + if groups openbts | grep -w -q -v $group; then + adduser openbts $group + fi +done if [ ! -e $CONFIG_BACKUP ]; then sqlite3 /etc/OpenBTS/OpenBTS.db ".dump" > $CONFIG_BACKUP diff --git a/debian/postinst.old b/debian/postinst.old deleted file mode 100755 index 32bce3d..0000000 --- a/debian/postinst.old +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -INSTALL_DIR=/OpenBTS -DATE=$(date --rfc-3339='date') -BACKUP_DIR=$INSTALL_DIR/backup_$DATE -CONFIG_BACKUP=/etc/OpenBTS/OpenBTS.dump-$DATE - -if [ ! -e $BACKUP_DIR ]; then - mkdir $BACKUP_DIR/ - - mv $INSTALL_DIR/OpenBTS $BACKUP_DIR/ - mv $INSTALL_DIR/transceiver $BACKUP_DIR/ -fi - - - -if [ ! -e $CONFIG_BACKUP ]; then - sqlite3 /etc/OpenBTS/OpenBTS.db ".dump" > $CONFIG_BACKUP -fi - -sqlite3 /etc/OpenBTS/OpenBTS.db ".read /etc/OpenBTS/OpenBTS_3.0.0.sql" &>/dev/null - - - diff --git a/debian/prerm.old b/debian/prerm.old deleted file mode 100755 index 7e60d4a..0000000 --- a/debian/prerm.old +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -# killall runloop.OpenBTS.sh &>/dev/null - - - - diff --git a/doxconfig b/doxconfig deleted file mode 100644 index 396db64..0000000 --- a/doxconfig +++ /dev/null @@ -1,1078 +0,0 @@ -# Doxyfile 1.3.4 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project -# -# All text after a hash (#) is considered a comment and will be ignored -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. - -PROJECT_NAME = OpenBTS - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = 2.7 - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = dox - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, -# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en -# (Japanese with English messages), Korean, Norwegian, Polish, Portuguese, -# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. - -OUTPUT_LANGUAGE = English - -# This tag can be used to specify the encoding used in the generated output. -# The encoding is not always determined by the language that is chosen, -# but also whether or not the output is meant for Windows or non-Windows users. -# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES -# forces the Windows encoding (this is the default for the Windows binary), -# whereas setting the tag to NO uses a Unix-style encoding (the default for -# all platforms other than Windows). - -USE_WINDOWS_ENCODING = NO - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited -# members of a class in the documentation of that class as if those members were -# ordinary class members. Constructors, destructors and assignment operators of -# the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = NO - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. It is allowed to use relative paths in the argument list. - -STRIP_FROM_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like the Qt-style comments (thus requiring an -# explict @brief command for a brief description. - -JAVADOC_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the DETAILS_AT_TOP tag is set to YES then Doxygen -# will output the detailed description near the top, like JavaDoc. -# If set to NO, the detailed description appears after the member -# documentation. - -DETAILS_AT_TOP = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# reimplements. - -INHERIT_DOCS = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 4 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources -# only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources -# only. Doxygen will then generate output that is more tailored for Java. -# For instance, namespaces will be presented as packages, qualified scopes -# will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = YES - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = YES - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = YES - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = YES - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# users are advised to set this option to NO. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = YES - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp -# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc - -FILE_PATTERNS = - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories -# that are symbolic links (a Unix filesystem feature) are excluded from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. - -EXCLUDE_PATTERNS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. - -INPUT_FILTER = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. - -SOURCE_BROWSER = YES - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES (the default) -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = YES - -# If the REFERENCES_RELATION tag is set to YES (the default) -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = YES - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = YES - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet - -HTML_STYLESHEET = - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output dir. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - -# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be -# generated containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, -# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are -# probably better off using the HTML help feature. - -GENERATE_TREEVIEW = YES - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = YES - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = a4wide - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = NO - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = NO - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimised for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assigments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. This is useful -# if you want to understand what is going on. On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_PREDEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse the -# parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::addtions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or -# super classes. Setting the tag to NO turns the diagrams off. Note that this -# option is superceded by the HAVE_DOT option below. This is only a fallback. It is -# recommended to install and use dot, since it yields more powerful graphs. - -CLASS_DIAGRAMS = YES - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = NO - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similiar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will -# generate a call dependency graph for every global function or class method. -# Note that enabling this option will significantly increase the time of a run. -# So in most cases it will be better to enable call graphs for selected -# functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif -# If left blank png will be used. - -DOT_IMAGE_FORMAT = png - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found on the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width -# (in pixels) of the graphs generated by dot. If a graph becomes larger than -# this value, doxygen will try to truncate the graph, so that it fits within -# the specified constraint. Beware that most browsers cannot cope with very -# large images. - -MAX_DOT_GRAPH_WIDTH = 1024 - -# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height -# (in pixels) of the graphs generated by dot. If a graph becomes larger than -# this value, doxygen will try to truncate the graph, so that it fits within -# the specified constraint. Beware that most browsers cannot cope with very -# large images. - -MAX_DOT_GRAPH_HEIGHT = 1024 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes that -# lay further from the root node will be omitted. Note that setting this option to -# 1 or 2 may greatly reduce the computation time needed for large code bases. Also -# note that a graph may be further truncated if the graph's image dimensions are -# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). -# If 0 is used for the depth value (the default), the graph is not depth-constrained. - -MAX_DOT_GRAPH_DEPTH = 0 - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES - -#--------------------------------------------------------------------------- -# Configuration::addtions related to the search engine -#--------------------------------------------------------------------------- - -# The SEARCHENGINE tag specifies whether or not a search engine should be -# used. If set to NO the values of all tags below this one will be ignored. - -SEARCHENGINE = NO diff --git a/package/deb-after-install.sh b/package/deb-after-install.sh new file mode 100755 index 0000000..7dc4adc --- /dev/null +++ b/package/deb-after-install.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# copy of debian/postinst +DATE=$(date --rfc-3339='date') +CONFIG_BACKUP=/etc/OpenBTS/OpenBTS.dump-$DATE + +# add user openbts +if ! getent passwd openbts > /dev/null ; then + echo 'Adding system user for OpenBTS' 1>&2 + adduser --system --group --quiet \ + --home /home/openbts \ + --gecos "OpenBTS daemon" \ + openbts +fi + +# add user openbts to some groups +for group in sudo www-data; do + if groups openbts | grep -w -q -v $group; then + adduser openbts $group + fi +done + +if [ ! -e $CONFIG_BACKUP ]; then + sqlite3 /etc/OpenBTS/OpenBTS.db ".dump" > $CONFIG_BACKUP +fi + +sqlite3 /etc/OpenBTS/OpenBTS.db ".read /etc/OpenBTS/OpenBTS.example.sql" > /dev/null 2>&1 + +chown openbts:openbts /home/openbts/openbtsconfig + diff --git a/package/deb-before-install.sh b/package/deb-before-install.sh new file mode 100755 index 0000000..2b3c4b8 --- /dev/null +++ b/package/deb-before-install.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# copy of debian/preinst +INSTALL_DIR=/OpenBTS +DATE=$(date --rfc-3339='date') +BACKUP_DIR=$INSTALL_DIR/backup_$DATE + +if [ -f $INSTALL_DIR/OpenBTS -a -f $INSTALL_DIR/transceiver ]; then + if [ ! -e $BACKUP_DIR ]; then + mkdir -p $BACKUP_DIR/ + + mv $INSTALL_DIR/OpenBTS $BACKUP_DIR/ + mv $INSTALL_DIR/transceiver $BACKUP_DIR/ + fi +fi diff --git a/package/rpm-after-install.sh b/package/rpm-after-install.sh new file mode 100755 index 0000000..0616cdd --- /dev/null +++ b/package/rpm-after-install.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# copy of debian/postinst +DATE=$(date --rfc-3339='date') +CONFIG_BACKUP=/etc/OpenBTS/OpenBTS.dump-$DATE + +# add user openbts +if ! getent passwd openbts > /dev/null ; then + echo 'Adding system user for OpenBTS' 1>&2 + adduser --system --group \ + --home /home/openbts \ + --user-group \ + --comment "OpenBTS daemon" \ + openbts +fi + +# add user openbts to some groups +for group in sudo httpd; do + if egrep -i "^$group" /etc/group; then + adduser -G $group openbts + fi +done + +if [ ! -e $CONFIG_BACKUP ]; then + sqlite3 /etc/OpenBTS/OpenBTS.db ".dump" > $CONFIG_BACKUP +fi + +sqlite3 /etc/OpenBTS/OpenBTS.db ".read /etc/OpenBTS/OpenBTS.example.sql" > /dev/null 2>&1 + +chown openbts:openbts /home/openbts/openbtsconfig + diff --git a/package/rpm-before-install.sh b/package/rpm-before-install.sh new file mode 100755 index 0000000..2b3c4b8 --- /dev/null +++ b/package/rpm-before-install.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# copy of debian/preinst +INSTALL_DIR=/OpenBTS +DATE=$(date --rfc-3339='date') +BACKUP_DIR=$INSTALL_DIR/backup_$DATE + +if [ -f $INSTALL_DIR/OpenBTS -a -f $INSTALL_DIR/transceiver ]; then + if [ ! -e $BACKUP_DIR ]; then + mkdir -p $BACKUP_DIR/ + + mv $INSTALL_DIR/OpenBTS $BACKUP_DIR/ + mv $INSTALL_DIR/transceiver $BACKUP_DIR/ + fi +fi