mirror of
https://github.com/RangeNetworks/openbts.git
synced 2025-10-23 07:42:01 +00:00
sync from commercial 64a79ce7a18f7e3ef3fe5aeacf3b6629980d30b2
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,6 +7,7 @@
|
||||
*.la
|
||||
Makefile
|
||||
aclocal.m4
|
||||
autom4te.cache
|
||||
build-arch-stamp
|
||||
build-indep-stamp
|
||||
compile
|
||||
|
@@ -46,6 +46,7 @@
|
||||
#include <Peering.h>
|
||||
#include <GSMRadioResource.h>
|
||||
#include <NodeManager.h>
|
||||
#include <CBS.h>
|
||||
|
||||
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 <code> -- geograhical scope (0=immediate, 1=PLMN wide, 2=Location Area Wide, 3=Cell Wide)\n"
|
||||
" -code <number> -- Message code\n"
|
||||
" -un <number> -- Message update number. The gs + code + un make up the message serial number.\n"
|
||||
" -id <number> -- 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 <language> -- 2 character USC2 language specification as per from GSM 3.38 section 5, or - to reset.\n"
|
||||
" -row <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<string,string> 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 "<<cnt<<" CBS messages.\n";
|
||||
} else {
|
||||
os << "CBS message not found.\n";
|
||||
}
|
||||
} else if (subcmd == "add") {
|
||||
// add message
|
||||
if (CBSAddMessage(temp,errMsg)) {
|
||||
os << "CBS message successfully added.\n";
|
||||
} else {
|
||||
os << "CBS message not added:"<<errMsg<<"\n";
|
||||
}
|
||||
} else if (subcmd == "list" || subcmd == "") {
|
||||
// list messages.
|
||||
vector<CBMessage> msgs;
|
||||
CBSGetMessages(msgs,text);
|
||||
prettyTable_t tab;
|
||||
vector<string> 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<CBMessage>::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);
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
||||
|
Submodule CommonLibs updated: 3ad343b97b...11d8baa826
380
Control/CBS.cpp
Normal file
380
Control/CBS.cpp
Normal file
@@ -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 <GSMLogicalChannel.h>
|
||||
#include <GSMConfig.h>
|
||||
#include <GSMSMSCBL3Messages.h>
|
||||
#include <Reporting.h>
|
||||
#include <sqlite3.h>
|
||||
#include <sqlite3util.h>
|
||||
|
||||
// (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"<<LOGVAR(query);
|
||||
return 0;
|
||||
}
|
||||
int changes = sqlite3_changes(sCBSDB);
|
||||
return changes;
|
||||
}
|
||||
|
||||
|
||||
// The crackRowNames, crackCBMessageFromDB and CBSGetMessages must be kept matching.
|
||||
// These row names match crackCBMessageFromDB.
|
||||
static const char *crackRowNames = "GS,MESSAGE_CODE,UPDATE_NUMBER,MSGID,LANGUAGE_CODE,MESSAGE,SEND_COUNT,SEND_TIME,ROWID";
|
||||
|
||||
static void crackCBMessageFromDB(CBMessage &result, sqlite3_stmt* stmt)
|
||||
{
|
||||
result.setGS((CBMessage::GeographicalScope)sqlite3_column_int(stmt,0));
|
||||
result.setMessageCode((unsigned)sqlite3_column_int(stmt,1));
|
||||
result.setUpdateNumber((unsigned)sqlite3_column_int(stmt,2));
|
||||
result.setMessageId((unsigned)sqlite3_column_int(stmt,3));
|
||||
result.setLanguageCode((unsigned)sqlite3_column_int(stmt,4));
|
||||
result.setMessageText(string((const char*)sqlite3_column_text(stmt,5)));
|
||||
result.mSendCount = (unsigned)sqlite3_column_int(stmt,6);
|
||||
result.mSendTime = (unsigned)sqlite3_column_int(stmt,7);
|
||||
result.mRowId = (unsigned)sqlite3_column_int(stmt,8);
|
||||
}
|
||||
|
||||
// Return false on error; return true on success, which means we accessed the table - the result size is the number of entries.
|
||||
bool CBSGetMessages(vector<CBMessage> &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="<<rc;
|
||||
return false;
|
||||
}
|
||||
while (SQLITE_ROW == (rc=sqlite3_run_query(sCBSDB,stmt))) {
|
||||
CBMessage msg;
|
||||
crackCBMessageFromDB(msg, stmt);
|
||||
result.push_back(msg);
|
||||
LOG(DEBUG) <<LOGVAR(rc) <<LOGVAR(msg.cbtext());
|
||||
}
|
||||
sqlite3_finalize(stmt); // Finalize ASAP to unlock the database.
|
||||
LOG(DEBUG) <<"final"<<LOGVAR(rc);
|
||||
return true;
|
||||
}
|
||||
|
||||
int CBSClearMessages()
|
||||
{
|
||||
return cbsRunQuery("DELETE FROM SMSCB WHERE 1");
|
||||
}
|
||||
|
||||
static string strJoin(vector<string> &fields,string separator)
|
||||
{
|
||||
string result;
|
||||
int cnt = 0;
|
||||
for (vector<string>::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<string>*cols, vector<string>*vals, vector<string>*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<string>*cols, vector<string>*vals, vector<string>*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<string>*cols, vector<string>*vals, vector<string>*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<string> 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<CBMessage> existing;
|
||||
CBSGetMessages(existing,msg.mMessageText);
|
||||
for (vector<CBMessage>::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<string> 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<numPages; page++) {
|
||||
// Encode the mesage into pages.
|
||||
// We use UCS2 encoding for the message,
|
||||
// even though the input text is ASCII for now.
|
||||
char thisPage[82];
|
||||
unsigned dp = 0;
|
||||
int codingScheme;
|
||||
// (pat) If we want to implement languages we should support DCS of GSM 3.38 first, not UCS2.
|
||||
if (false && msg.mLanguageCode) {
|
||||
codingScheme = 0x11; // UCS2
|
||||
thisPage[dp++] = msg.mLanguageCode >> 8;
|
||||
thisPage[dp++] = msg.mLanguageCode & 0x0ff;
|
||||
while (dp<82 && mp<messageLen) {
|
||||
// UCS2 uses 16-bit characters.
|
||||
// (pat) Setting the high byte to 0 is just wrong - the user would want to put the 16-bit characters in the database,
|
||||
// that is the point of using UCS2.
|
||||
thisPage[dp++] = 0;
|
||||
thisPage[dp++] = msg.mMessageText[mp++];
|
||||
}
|
||||
while (dp<82) { thisPage[dp++] = 0; thisPage[dp++]='\r'; }
|
||||
} else {
|
||||
// 03.38 section 5
|
||||
codingScheme = 0x10; // 'default' codiing scheme
|
||||
int buf = 0;
|
||||
int shift = 0;
|
||||
// The spec (above) says to put this language stuff in, but it doesn't work on my samsung galaxy y. (dbrown)
|
||||
// encode7(languageCode >> 8, shift, dp, buf, thisPage);
|
||||
// encode7(languageCode & 0xFF, shift, dp, buf, thisPage);
|
||||
// encode7('\r', shift, dp, buf, thisPage);
|
||||
while (dp<81 && mp<messageLen) {
|
||||
encode7(msg.mMessageText[mp++], shift, dp, buf, thisPage);
|
||||
}
|
||||
while (dp<81) { encode7('\r', shift, dp, buf, thisPage); }
|
||||
thisPage[dp++] = buf;
|
||||
}
|
||||
// Format the page into an L3 message.
|
||||
GSM::L3SMSCBMessage message(
|
||||
GSM::L3SMSCBSerialNumber(msg.mGS,msg.mMessageCode,msg.mUpdateNumber),
|
||||
GSM::L3SMSCBMessageIdentifier(msg.mMessageId),
|
||||
GSM::L3SMSCBDataCodingScheme(codingScheme),
|
||||
GSM::L3SMSCBPageParameter(page+1,numPages),
|
||||
GSM::L3SMSCBContent(thisPage)
|
||||
);
|
||||
// Send it.
|
||||
LOG(DEBUG) << "sending L3 message page " << page+1 << ": " << message;
|
||||
CBCH->l2sendm(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
string CBMessage::cbtext()
|
||||
{
|
||||
ostringstream os;
|
||||
os <<LOGVARM(mGS)<<LOGVARM(mMessageCode)<<LOGVARM(mUpdateNumber)<<LOGVARM(mMessageId)<<LOGVAR(mMessageText);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
void CBMessage::cbtext(std::ostream &os)
|
||||
{
|
||||
os <<cbtext();
|
||||
}
|
||||
|
||||
|
||||
// (pat) The SMSCB name is misleading; this has nothing to do with SMS. It is Cell Broadcast Service.
|
||||
void* SMSCBSender(void*)
|
||||
{
|
||||
// Connect to the database.
|
||||
// Just keep trying until it connects.
|
||||
bool whine = true;
|
||||
while (!CBSConnectDatabase(whine)) { sleep(2); whine = false; }
|
||||
LOG(NOTICE) << "SMSCB service starting";
|
||||
|
||||
// Get a channel.
|
||||
GSM::CBCHLogicalChannel* CBCH = gBTS.getCBCH();
|
||||
|
||||
while (1) {
|
||||
// Get the next message ready to send.
|
||||
// (pat) The "ROWID" is not a column name, it is sql-lite magic to return the row number.
|
||||
int sqlresult;
|
||||
CBMessage msg;
|
||||
{
|
||||
string query = format("SELECT %s FROM SMSCB WHERE SEND_TIME==(SELECT min(SEND_TIME) FROM SMSCB)",crackRowNames);
|
||||
sqlite3_stmt *stmt;
|
||||
if (sqlite3_prepare_statement(sCBSDB,&stmt,query.c_str())) {
|
||||
LOG(ALERT) << "Cannot access SMSCB database: " << sqlite3_errmsg(sCBSDB);
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
// Send the message or sleep briefly.
|
||||
sqlresult = sqlite3_run_query(sCBSDB,stmt);
|
||||
if (sqlresult == SQLITE_ROW) {
|
||||
crackCBMessageFromDB(msg, stmt);
|
||||
}
|
||||
// Finalize ASAP to unlock the database.
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
LOG(DEBUG) <<LOGVAR(sqlresult) <<LOGVAR(msg.cbtext());
|
||||
|
||||
switch (sqlresult) {
|
||||
case SQLITE_ROW: {
|
||||
CBSSendMessage(msg,CBCH);
|
||||
// Update send count and send time in the database.
|
||||
char query[100];
|
||||
snprintf(query,100,"UPDATE SMSCB SET SEND_TIME = %u, SEND_COUNT = %u WHERE ROWID == %u",
|
||||
(unsigned)time(NULL), msg.mSendCount+1, (unsigned)msg.mRowId);
|
||||
if (!sqlite3_command(sCBSDB,query)) LOG(ALERT) << "SMSCB database timestamp update failed: " << sqlite3_errmsg(sCBSDB);
|
||||
continue;
|
||||
}
|
||||
case SQLITE_DONE:
|
||||
// Empty database.
|
||||
break;
|
||||
default:
|
||||
LOG(ALERT) << "SCSCB database failure: " << sqlite3_errmsg(sCBSDB);
|
||||
break;
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
// keep the compiler from whining
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}; // namespace
|
||||
|
||||
// vim: ts=4 sw=4
|
121
Control/CBS.h
Normal file
121
Control/CBS.h
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _SMSCB_H_
|
||||
#define _SMSCB_H_ 1
|
||||
#include <vector>
|
||||
#include <ostream>
|
||||
|
||||
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<CBMessage> &result, string text="");
|
||||
|
||||
}; // namespace
|
||||
|
||||
#endif
|
@@ -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) <<TCH<<" sending handover command";
|
||||
TCH->l3sendf(HandoverCommand);
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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<TranEntry> tranp = mmGetTran(i);
|
||||
|
@@ -45,6 +45,7 @@
|
||||
//#include <SIPDialog.h>
|
||||
#include <SIPExport.h>
|
||||
#include <Regexp.h>
|
||||
#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);
|
||||
}
|
||||
|
@@ -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.
|
||||
|
@@ -29,6 +29,7 @@
|
||||
#include <GSML3MMMessages.h>
|
||||
#include <SMSMessages.h>
|
||||
#include <GSMConfig.h>
|
||||
#include <RRLPServer.h>
|
||||
#include <Globals.h>
|
||||
#include <typeinfo>
|
||||
|
||||
@@ -261,6 +262,7 @@ static bool handleCommonMessages(const L3Message *l3msg, MMContext *mmchan, bool
|
||||
NewPagingResponseHandler(dynamic_cast<const L3PagingResponse*>(l3msg),mmchan);
|
||||
return true;
|
||||
case L3CASE_RR(L3RRMessage::ApplicationInformation):
|
||||
return true;
|
||||
case L3CASE_RR(L3RRMessage::RRStatus):
|
||||
handleStatus(l3msg,mmchan);
|
||||
return true;
|
||||
|
@@ -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<TranEntry> 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<TranEntry> 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<TranEntry> saver = this;
|
||||
{ ScopedLock lock(mL3RewriteLock,__FILE__,__LINE__);
|
||||
if (MachineBase *proc = currentProcedure()) {
|
||||
LOG(DEBUG) <<"sending l3msg to"<<LOGVAR(proc) <<LOGVAR(l3msg);
|
||||
result = handleMachineStatus(proc->dispatchL3Msg(l3msg));
|
||||
}
|
||||
}
|
||||
teProcInvoke(state,l3msg,NULL);
|
||||
#endif
|
||||
if (MachineBase *proc = currentProcedure()) {
|
||||
LOG(DEBUG) <<"sending l3msg to"<<LOGVAR(proc) <<LOGVAR(l3msg);
|
||||
return handleMachineStatus(proc->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"<<LOGVAR(proc) <<LOGVAR(frame);
|
||||
return handleMachineStatus(proc->dispatchFrame(frame,l3msg));
|
||||
bool result = false;
|
||||
RefCntPointer<TranEntry> saver = this;
|
||||
{
|
||||
ScopedLock lock(mL3RewriteLock,__FILE__,__LINE__);
|
||||
if (MachineBase *proc = currentProcedure()) {
|
||||
LOG(DEBUG) <<"sending frame to"<<LOGVAR(proc) <<LOGVAR(frame);
|
||||
result = handleMachineStatus(proc->dispatchFrame(frame,l3msg));
|
||||
} else {
|
||||
LOG(INFO) <<"Received message for transaction with no state machine. "<<this<<*frame; // Should never happen.
|
||||
}
|
||||
}
|
||||
LOG(ERR) <<"Received message for transaction with no state machine. "<<this<<*frame; // Should never happen.
|
||||
return false;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Return true if the message had a handler.
|
||||
// Caller responsible for deleting the sipmsg.
|
||||
bool TranEntry::lockAndInvokeSipMsg(const SIP::DialogMessage *sipmsg)
|
||||
{
|
||||
ScopedLock lock(mL3RewriteLock,__FILE__,__LINE__);
|
||||
#if ORIGINAL
|
||||
int state = mCurrentProcedure->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<TranEntry> 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<TranEntry> 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<CdrService*>(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);
|
||||
}
|
||||
}
|
||||
|
@@ -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<L3CDR> 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:
|
||||
|
||||
|
@@ -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
|
||||
|
0
Control/RRLPServer.cpp
Normal file
0
Control/RRLPServer.cpp
Normal file
0
Control/RRLPServer.h
Normal file
0
Control/RRLPServer.h
Normal file
@@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <list>
|
||||
|
||||
#include <Defines.h>
|
||||
#include "ControlCommon.h"
|
||||
#include "RadioResource.h"
|
||||
#include "L3CallControl.h"
|
||||
#include "L3MMLayer.h"
|
||||
|
||||
#include <GSMLogicalChannel.h>
|
||||
#include <GSMConfig.h>
|
||||
#include "../GPRS/GPRSExport.h"
|
||||
|
||||
#include <NeighborTable.h>
|
||||
#include <Peering.h>
|
||||
#include <SIPDialog.h>
|
||||
|
||||
#include <Reporting.h>
|
||||
#include <Logger.h>
|
||||
#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" <<LOGVAR(now) <<LOGVAR(chtype) <<LOGVAR(lur) <<LOGVAR(gprs)
|
||||
<<LOGVAR(when)<<LOGVAR(age)<<LOGVAR2("TE",timingError)<<LOGVAR(RSSI)<<LOGHEX(RA);
|
||||
}
|
||||
LOG(INFO) << "**Incoming Burst**"<<LOGVAR(lur)<<LOGVAR(gprs)
|
||||
<<LOGVAR(when)<<LOGVAR(age)<<LOGVAR2("TE",timingError)<<LOGVAR(RSSI)<<LOGHEX(RA);
|
||||
|
||||
//LOG(INFO) << "Incoming Burst: RA=0x" << hex << RA << dec
|
||||
// <<LOGVAR(when) <<LOGVAR(age)
|
||||
// << " delay=" << timingError <<LOGVAR(chtype);
|
||||
if (age>maxAge) {
|
||||
LOG(WARNING) << "ignoring RACH bust with age " << age;
|
||||
// FIXME -- What was supposed to be happening here?
|
||||
//gBTS.growT3122()/1000; // Hmmm...
|
||||
gBTS.growT3122(); // (pat) removed the /1000
|
||||
return;
|
||||
}
|
||||
|
||||
if (timingError>gConfig.getNum("GSM.MS.TA.Max")) {
|
||||
LOG(NOTICE) << "ignoring RACH burst with delay="<<timingError<<LOGVAR(chtype);
|
||||
return;
|
||||
}
|
||||
|
||||
if (chtype == PSingleBlock1PhaseType || chtype == PSingleBlock2PhaseType) {
|
||||
// This is a request for a GPRS TBF. It will get queued in the GPRS code
|
||||
// and handled when the GPRS MAC service loop gets around to it.
|
||||
// If GPRS is not enabled or is busy, it may just get dropped.
|
||||
GPRS::GPRSProcessRACH(RA,when,RSSI,timingError);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get an AGCH to send on.
|
||||
CCCHLogicalChannel *AGCH = gBTS.getAGCH();
|
||||
// Someone had better have created a least one AGCH.
|
||||
assert(AGCH);
|
||||
// Check AGCH load now.
|
||||
// (pat) The default value is 5, so about 1.25 second for a system
|
||||
// with a C0T0 beacon with only one CCCH.
|
||||
if ((int)AGCH->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
|
@@ -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 <list>
|
||||
#include <Interthread.h>
|
||||
#include "ControlCommon.h"
|
||||
#include <GSML3CommonElements.h>
|
||||
|
||||
|
||||
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<NewPagingEntry> 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
|
@@ -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 <GSMLogicalChannel.h>
|
||||
#include <GSMConfig.h>
|
||||
#include <GSMSMSCBL3Messages.h>
|
||||
#include <Reporting.h>
|
||||
#include <sqlite3.h>
|
||||
#include <sqlite3util.h>
|
||||
|
||||
// (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<numPages; page++) {
|
||||
// Encode the mesage into pages.
|
||||
// We use UCS2 encoding for the message,
|
||||
// even though the input text is ASCII for now.
|
||||
char thisPage[82];
|
||||
unsigned dp = 0;
|
||||
int codingScheme;
|
||||
if (false) { // in case anybody wants to make the encoding selectable
|
||||
codingScheme = 0x11; // UCS2
|
||||
thisPage[dp++] = languageCode >> 8;
|
||||
thisPage[dp++] = languageCode & 0x0ff;
|
||||
while (dp<82 && mp<messageLen) {
|
||||
thisPage[dp++] = 0;
|
||||
thisPage[dp++] = messageText[mp++];
|
||||
}
|
||||
while (dp<82) { thisPage[dp++] = 0; thisPage[dp++]='\r'; }
|
||||
} else {
|
||||
// 03.38 section 5
|
||||
codingScheme = 0x10; // 7'
|
||||
int buf = 0;
|
||||
int shift = 0;
|
||||
// The spec (above) says to put this language stuff in, but it doesn't work on my samsung galaxy y.
|
||||
// encode7(languageCode >> 8, shift, dp, buf, thisPage);
|
||||
// encode7(languageCode & 0xFF, shift, dp, buf, thisPage);
|
||||
// encode7('\r', shift, dp, buf, thisPage);
|
||||
while (dp<81 && mp<messageLen) {
|
||||
encode7(messageText[mp++], shift, dp, buf, thisPage);
|
||||
}
|
||||
while (dp<81) { encode7('\r', shift, dp, buf, thisPage); }
|
||||
thisPage[dp++] = buf;
|
||||
}
|
||||
// Format the page into an L3 message.
|
||||
GSM::L3SMSCBMessage message(
|
||||
GSM::L3SMSCBSerialNumber(GS,messageCode,updateNumber),
|
||||
GSM::L3SMSCBMessageIdentifier(messageID),
|
||||
GSM::L3SMSCBDataCodingScheme(codingScheme),
|
||||
GSM::L3SMSCBPageParameter(page+1,numPages),
|
||||
GSM::L3SMSCBContent(thisPage)
|
||||
);
|
||||
// Send it.
|
||||
LOG(DEBUG) << "sending L3 message page " << page+1 << ": " << message;
|
||||
CBCH->l2sendm(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
|
@@ -25,6 +25,7 @@
|
||||
#include <GSML3MMMessages.h>
|
||||
#include <Reporting.h>
|
||||
#include <Globals.h>
|
||||
#include <GSML3CommonElements.h>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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 \
|
||||
|
@@ -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());
|
||||
|
@@ -336,7 +336,9 @@ bool CCCHLogicalChannel::processRaches()
|
||||
// // Nothing available?
|
||||
// if (!LCH) {
|
||||
// failure:
|
||||
#if 0
|
||||
// return false;
|
||||
#endif
|
||||
// }
|
||||
|
||||
if (! rach->mChan) {
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -50,6 +50,11 @@ class SDCCHList : public std::vector<SDCCHLogicalChannel*> {};
|
||||
class TCHList : public std::vector<TCHFACCHLogicalChannel*> {};
|
||||
typedef std::vector<L2LogicalChannel*> 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
|
||||
|
||||
|
@@ -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) <<LOGVAR(fn) <<LOGVAR(count);
|
||||
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)))) {
|
||||
@@ -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);
|
||||
|
@@ -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())); }
|
||||
};
|
||||
|
@@ -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; }
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -472,6 +472,8 @@ public:
|
||||
mTN(0),mTSC(0),mHFlag(0),mARFCN(0),mMAIO(0),mHSN(0)
|
||||
{ }
|
||||
|
||||
bool initialized() { return mTypeAndOffset != TDMA_MISC; }
|
||||
|
||||
|
||||
size_t lengthV() const { return 3; }
|
||||
void writeV( L3Frame &dest, size_t &wp ) const;
|
||||
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -120,7 +120,9 @@ void L2LogicalChannelBase::startl1()
|
||||
void L2SAPMux::flushL3In()
|
||||
{
|
||||
while (L3Frame *l3f = mL3In.readNoBlock()) {
|
||||
LOG(ERR)<< "channel closure caused message to be discarded:"<<l3f<<" "<<descriptiveString();
|
||||
if (l3f->primitive() != L3_RELEASE_REQUEST && l3f->primitive() != L3_HARDRELEASE_REQUEST) {
|
||||
LOG(ERR)<< "channel closure caused message to be discarded:"<<l3f<<" "<<descriptiveString();
|
||||
}
|
||||
delete l3f;
|
||||
}
|
||||
}
|
||||
@@ -235,7 +237,10 @@ void L2SAPMux::sapWriteFromL3(const L3Frame& frame)
|
||||
{
|
||||
LOG(DEBUG) <<LOGVAR(frame);
|
||||
SAPI_t sap = frame.getSAPI();
|
||||
assert(sap == SAPI0 || sap == SAPI3 || sap == SAPI0Sacch || sap == SAPI3Sacch);
|
||||
if (!(sap == SAPI0 || sap == SAPI3 || sap == SAPI0Sacch || sap == SAPI3Sacch)) {
|
||||
devassert(0);
|
||||
sap = SAPI0; // This is a bug fix to avoid a crash.
|
||||
}
|
||||
unsigned sapi = SAP2SAPI(sap);
|
||||
devassert(mL2[sapi]);
|
||||
if (!mL2[sapi]) { return; } // Not initialized yet? Should never happen.
|
||||
@@ -265,6 +270,7 @@ void L2LogicalChannel::startNormalRelease()
|
||||
void L2LogicalChannel::l2sendf(const L3Frame& frame)
|
||||
{
|
||||
SAPI_t sap = frame.getSAPI();
|
||||
assert(sap == SAPI0 || sap == SAPI3 || sap == SAPI0Sacch || sap == SAPI3Sacch);
|
||||
WATCHINFO("l2sendf "<<channelDescription() <<LOGVAR(sap) <<LOGVAR(chtype()) <<" " <<frame);
|
||||
if (SAPIsSacch(sap)) { getSACCH()->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();
|
||||
|
@@ -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; }
|
||||
};
|
||||
|
||||
|
||||
|
@@ -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; i<len; i++) {
|
||||
writeField(wp,binary[i],8);
|
||||
}
|
||||
}
|
||||
// (pat) 9-8-2014 removed, unused.
|
||||
//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; i<len; i++) {
|
||||
// writeField(wp,binary[i],8);
|
||||
// }
|
||||
//}
|
||||
|
||||
unsigned L3Frame::MTI() const
|
||||
{
|
||||
|
@@ -716,8 +716,8 @@ class L3Frame : public BitVector { // (pat) This is in Layer3, common to UMTS a
|
||||
explicit L3Frame(Primitive wPrimitive) :BitVector((size_t)0),mPrimitive(wPrimitive),mSapi(SAPI0),mL2Length(0) { f3init(); }
|
||||
explicit L3Frame(SAPI_t wSapi, Primitive wPrimitive) :BitVector((size_t)0),mPrimitive(wPrimitive),mSapi(wSapi),mL2Length(0) { f3init(); }
|
||||
|
||||
explicit L3Frame(Primitive wPrimitive, size_t len)
|
||||
:BitVector(len),mPrimitive(wPrimitive),mSapi(SAPI0),mL2Length(len)
|
||||
explicit L3Frame(Primitive wPrimitive, size_t len, SAPI_t wSapi=SAPI0)
|
||||
:BitVector(len),mPrimitive(wPrimitive),mSapi(wSapi),mL2Length(len)
|
||||
{ f3init(); }
|
||||
|
||||
/** Put raw bits into the frame. */
|
||||
@@ -751,10 +751,12 @@ class L3Frame : public BitVector { // (pat) This is in Layer3, common to UMTS a
|
||||
explicit L3Frame(const L3Message& msg, Primitive wPrimitive=L3_DATA, SAPI_t sapi=SAPI0);
|
||||
|
||||
/** Get a frame from a hex string. */
|
||||
explicit L3Frame(const char*);
|
||||
explicit L3Frame(SAPI_t sapi, const char*);
|
||||
|
||||
/** Get a frame from raw binary. */
|
||||
explicit L3Frame(const char*, size_t len);
|
||||
// pat removed 9-8-2014 because it is unused. If you put it back in, add an explicit SAPI argument to make sure it is distguished
|
||||
// from the other constructors.
|
||||
//explicit L3Frame(const char*, size_t len);
|
||||
|
||||
/** Protocol Discriminator, GSM 04.08 10.2. */
|
||||
L3PD PD() const { return (L3PD)peekField(4,4); }
|
||||
|
@@ -21,7 +21,7 @@
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES)
|
||||
#AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES)
|
||||
#AM_CXXFLAGS = -O2 -g
|
||||
|
||||
noinst_LTLIBRARIES = libGSM.la
|
||||
|
@@ -21,11 +21,11 @@
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
AM_CXXFLAGS = -Wall -O3 -g -ldl -lpthread
|
||||
|
||||
noinst_LTLIBRARIES = libGSMShare.la
|
||||
|
||||
# AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
# AM_CXXFLAGS = -Wall -O3 -g -ldl -lpthread
|
||||
libGSMShare_la_CXXFLAGS = $(AM_CXXFLAGS) -O3
|
||||
libGSMShare_la_SOURCES = \
|
||||
L3Enums.cpp \
|
||||
AmrCoder.cpp \
|
||||
@@ -49,14 +49,10 @@ noinst_HEADERS = \
|
||||
GSM503Tables.h \
|
||||
A51.h
|
||||
|
||||
|
||||
# (pat 8-2013) I don't know how to properly include the CommonLibs directory using autofoo.
|
||||
# You are welcome to correct this reference.
|
||||
ViterbiTest_SOURCES = ViterbiTest.cpp
|
||||
ViterbiTest_LDADD = \
|
||||
$(GLOBALS_LA) \
|
||||
$(noinst_LTLIBRARIES) \
|
||||
../CommonLibs/libcommon.la \
|
||||
$(GSM_LA) \
|
||||
$(COMMON_LA) \
|
||||
$(SQLITE_LA)
|
||||
@@ -65,7 +61,6 @@ AMRTest_SOURCES = AMRTest.cpp
|
||||
AMRTest_LDADD = \
|
||||
$(GLOBALS_LA) \
|
||||
$(noinst_LTLIBRARIES) \
|
||||
../CommonLibs/libcommon.la \
|
||||
$(GSM_LA) \
|
||||
$(COMMON_LA) \
|
||||
$(SQLITE_LA)
|
||||
@@ -74,8 +69,6 @@ A51Test_SOURCES = A51Test.cpp
|
||||
A51Test_LDADD = \
|
||||
$(GLOBALS_LA) \
|
||||
$(noinst_LTLIBRARIES) \
|
||||
../CommonLibs/libcommon.la \
|
||||
$(GSM_LA) \
|
||||
$(COMMON_LA) \
|
||||
$(SQLITE_LA)
|
||||
|
||||
|
@@ -21,8 +21,8 @@
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
AM_CXXFLAGS = -Wall
|
||||
# AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
# AM_CXXFLAGS = -Wall
|
||||
|
||||
noinst_LTLIBRARIES = libglobals.la
|
||||
|
||||
|
11
Makefile.am
11
Makefile.am
@@ -21,13 +21,10 @@
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
DESTDIR :=
|
||||
DESTDIR ?=
|
||||
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES)
|
||||
AM_CXXFLAGS = -Wall -pthread -ldl
|
||||
|
||||
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread -ldl
|
||||
#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread -ldl
|
||||
# must be kept in sync with AC_CONFIG_MACRO_DIR in configure.ac
|
||||
ACLOCAL_AMFLAGS = -I config
|
||||
|
||||
# Order must be preserved
|
||||
_SUBDIRS = \
|
||||
@@ -36,7 +33,6 @@ _SUBDIRS = \
|
||||
Globals \
|
||||
SIP \
|
||||
GSM \
|
||||
GSMShare \
|
||||
SMS \
|
||||
Scanning \
|
||||
TRXManager \
|
||||
@@ -46,6 +42,7 @@ _SUBDIRS = \
|
||||
GPRS \
|
||||
SGSNGGSN \
|
||||
CLI \
|
||||
GSMShare \
|
||||
apps \
|
||||
doc
|
||||
|
||||
|
@@ -20,11 +20,12 @@
|
||||
#
|
||||
|
||||
GPROF_OPTIONS ?=
|
||||
CXXFLAGS := -g -O2 -DTIMESTAMP_ISO=`date +'"%Y-%m-%dT%H:%M:%S"'`
|
||||
CFLAGS := -g -O2 -DTIMESTAMP_ISO=`date +'"%Y-%m-%dT%H:%M:%S"'`
|
||||
AM_CXXFLAGS := -g -O2 -Wall -pthread -ldl -rdynamic -DTIMESTAMP_ISO=`date +'"%Y-%m-%dT%H:%M:%S"'`
|
||||
# AM_CFLAGS := -g -O2 -DTIMESTAMP_ISO=`date +'"%Y-%m-%dT%H:%M:%S"'`
|
||||
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES) $(OPENBTS_CPPFLAGS)
|
||||
AM_CXXFLAGS = -Wall -pthread -rdynamic -ldl $(OPENBTS_CXXFLAGS)
|
||||
# (oley) Each piece has its own set defined in their own */Makefile.am, overwriting these
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) ## $(USB_INCLUDES) $(WITH_INCLUDES) $(OPENBTS_CPPFLAGS)
|
||||
# AM_CXXFLAGS = -Wall -pthread -ldl -rdynamic ## $(OPENBTS_CXXFLAGS)
|
||||
|
||||
COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs
|
||||
CONTROL_INCLUDEDIR = $(top_srcdir)/Control
|
||||
|
Submodule NodeManager updated: 836ae03d86...7fce01fa89
@@ -22,11 +22,12 @@
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
AM_CXXFLAGS = -Wall -O3
|
||||
# AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
# AM_CXXFLAGS = -Wall -O3
|
||||
|
||||
noinst_LTLIBRARIES = libpeering.la
|
||||
|
||||
libpeering_la_CXXFLAGS = $(AM_CXXFLAGS) -O3
|
||||
libpeering_la_SOURCES = \
|
||||
NeighborTable.cpp \
|
||||
Peering.cpp
|
||||
|
@@ -88,6 +88,38 @@ string sockaddr2string(const struct sockaddr_in* peer, bool noempty)
|
||||
return format("%s:%d",addrString,(int)ntohs(peer->sin_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 "<<sendOrRecv <<LOGVAR2("peer",sockaddr2string(peer,true)) <<LOGVAR(message);
|
||||
// We used to watch everything except REQ NEI messages: if (strncmp(message,"REQ NEI",7))
|
||||
const char *eol = strchr(message,'\n');
|
||||
WATCHLEVEL(INFO," Peering "<<sendOrRecv <<LOGVAR2("peer",sockaddr2string(peer,true))
|
||||
<<LOGVAR2("message",string(message,eol?eol-message:strlen(message)))); // first line of message; they used to be long.
|
||||
} else {
|
||||
// At DEBUG level log all messages.
|
||||
LOG(DEBUG) << "Peering "<<sendOrRecv<<LOGVAR2("peer",sockaddr2string(peer,true)) <<LOGVAR(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void PeerMessageFIFOMap::addFIFO(unsigned transactionID)
|
||||
{
|
||||
PeerMessageFIFO* newFIFO = new PeerMessageFIFO;
|
||||
@@ -192,7 +224,8 @@ void PeerInterface::drive()
|
||||
}
|
||||
|
||||
mReadBuffer[numRead] = '\0';
|
||||
LOG(INFO) << "received " << mReadBuffer;
|
||||
//LOG(INFO) << "received " << mReadBuffer;
|
||||
LOG(DEBUG) << "received " << mReadBuffer;
|
||||
|
||||
process(mSocket.source(),mReadBuffer);
|
||||
}
|
||||
@@ -200,14 +233,15 @@ void PeerInterface::drive()
|
||||
|
||||
void PeerInterface::process(const struct sockaddr_in* peer, const char* message)
|
||||
{
|
||||
LOG(DEBUG) << message;
|
||||
// The REQ HANDOVER is only watched if it is the first.
|
||||
if (strstr(message,"HANDOVER") && ! strstr(message,"REQ HANDOVER")) WATCH(Utils::timestr() << " Peering recv:"<<message);
|
||||
logMessage("receive",peer,message);
|
||||
|
||||
// neighbor message?
|
||||
if (strncmp(message+3," NEIGHBOR_PARAMS",16)==0)
|
||||
return processNeighborParams(peer,message);
|
||||
|
||||
// must be handover related
|
||||
string peerString = sockaddr2string(peer, true);
|
||||
LOG(INFO) << "received from"<<LOGVAR2("peer",peerString) <<LOGVAR(message);
|
||||
|
||||
// Initial inbound handover request?
|
||||
if (strncmp(message,"REQ HANDOVER ",13)==0)
|
||||
@@ -446,7 +480,8 @@ void PeerInterface::processHandoverRequest(const struct sockaddr_in* peer, const
|
||||
if (!transaction) {
|
||||
WATCH(Utils::timestr() << " Peering recv:"<<message);
|
||||
|
||||
LOG(INFO) << "initial REQ HANDOVER for " << mobileID << " " << oldTransID;
|
||||
//LOG(INFO) << "initial REQ HANDOVER for " << mobileID << " " << oldTransID;
|
||||
LOG(DEBUG) << "initial REQ HANDOVER for " << mobileID << " " << oldTransID;
|
||||
|
||||
// Get a channel allocation.
|
||||
// For now, we are assuming a full-rate channel.
|
||||
@@ -662,16 +697,9 @@ void PeerInterface::processHandoverResponse(const struct sockaddr_in* peer, cons
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PeerInterface::sendMessage(const struct sockaddr_in* peer, const char *message)
|
||||
{
|
||||
LOG(DEBUG) << "sending message: " << message;
|
||||
const char *eol = strchr(message,'\n');
|
||||
if (!strstr(message,"REQ NEI")) {
|
||||
WATCH(Utils::timestr() << " Peering send:"<<string(message,eol?eol-message:strlen(message))); // first line of message.
|
||||
}
|
||||
logMessage("send",peer,message);
|
||||
ScopedLock lock(mLock);
|
||||
mSocket.send((const struct sockaddr*)peer,message);
|
||||
}
|
||||
@@ -729,13 +757,13 @@ void PeerInterface::sendHandoverFailure(const Control::HandoverEntry *hop, GSM::
|
||||
|
||||
bool PeerInterface::sendHandoverRequest(string peer, const RefCntPointer<TranEntry> 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) <<LOGVAR(peer) <<LOGVAR(msg);
|
||||
LOG(DEBUG) <<LOGVAR(peer) <<LOGVAR(msg);
|
||||
gPeerInterface.sendMessage(&peerAddr,msg.c_str());
|
||||
return true;
|
||||
|
@@ -21,8 +21,7 @@
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
|
||||
# AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
#AM_CXXFLAGS = -O2 -g
|
||||
|
||||
noinst_LTLIBRARIES = libSGSNGGSN.la
|
||||
|
@@ -265,8 +265,15 @@ static char *packettoa(char *result,unsigned char *packet, int len)
|
||||
sprintf(result,"proto=%s %d byte packet seq=%u ack=%u id=%u frag=%u from %s:%d to %s:%d",
|
||||
ip_proto_name(iph->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,
|
||||
|
@@ -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 \
|
||||
|
@@ -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
|
||||
|
@@ -389,6 +389,7 @@ struct SipCallbacks {
|
||||
}
|
||||
};
|
||||
|
||||
extern string OpenBTSUserAgent();
|
||||
|
||||
};
|
||||
#endif
|
||||
|
@@ -27,7 +27,6 @@
|
||||
#include <L3TermCause.h>
|
||||
#include <L3StateMachine.h>
|
||||
#include <GSML3MMElements.h> // for L3CMServiceType
|
||||
#include "config.h" // For VERSION
|
||||
#include <GSML3CCElements.h> // for L3Cause
|
||||
#include <algorithm>
|
||||
|
||||
@@ -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) <<LOGVAR(thing);
|
||||
return thing; // Hopefully, what is remaining is a phone number.
|
||||
}
|
||||
|
||||
@@ -1687,12 +1693,5 @@ std::ostream& operator<<(std::ostream& os, const DialogMessage*dmsg)
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const DialogMessage&dmsg) { os << &dmsg; return os; } // stupid language
|
||||
|
||||
string OpenBTSUserAgent()
|
||||
{
|
||||
static const char* userAgent1 = "OpenBTS " VERSION " Build Date " TIMESTAMP_ISO;
|
||||
string userAgent = string(userAgent1);
|
||||
return userAgent;
|
||||
}
|
||||
|
||||
|
||||
}; // namespace
|
||||
|
@@ -278,7 +278,6 @@ std::ostream& operator<<(std::ostream& os, const SipDialogRef&);
|
||||
std::ostream& operator<<(std::ostream& os, const SipDialog*);
|
||||
|
||||
extern SipDialog *gRegisterDialog;
|
||||
extern string OpenBTSUserAgent();
|
||||
|
||||
}; // namespace
|
||||
#endif
|
||||
|
@@ -463,8 +463,12 @@ void parseAuthenticate(string stuff, SipParamList ¶ms)
|
||||
|
||||
do {
|
||||
SipParam param;
|
||||
if (! parser.scanGenericParam(param)) break;
|
||||
params.push_back(param);
|
||||
// (pat 9-2014) sipauthserve incorrrectly puts extra commas in the Authorization string,
|
||||
// and this would reject it. This has not been a problem because we dont use that field, but lets fix it anyway.
|
||||
// formerly: if (! parser.scanGenericParam(param)) break;
|
||||
if (parser.scanGenericParam(param)) {
|
||||
params.push_back(param);
|
||||
}
|
||||
} while (parser.scanChar(','));
|
||||
} catch(SipParseError) {
|
||||
// error was already logged.
|
||||
|
@@ -21,8 +21,8 @@
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
AM_CXXFLAGS = -Wall
|
||||
# AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
# AM_CXXFLAGS = -Wall
|
||||
|
||||
noinst_LTLIBRARIES = libSMS.la
|
||||
|
||||
|
@@ -83,6 +83,7 @@ CPMessage * SMS::parseSMS( const GSM::L3Frame& frame )
|
||||
RPData*
|
||||
|
||||
*/
|
||||
#if UNUSED
|
||||
RPData *SMS::hex2rpdata(const char *hexstring)
|
||||
{
|
||||
RPData *rp_data = NULL;
|
||||
@@ -118,6 +119,7 @@ RPData *SMS::hex2rpdata(const char *hexstring)
|
||||
|
||||
return rp_data;
|
||||
}
|
||||
#endif
|
||||
|
||||
TLMessage *SMS::parseTPDU(const TLFrame& TPDU, bool directionUplink)
|
||||
{
|
||||
|
@@ -810,7 +810,7 @@ CPMessage * parseSMS( const GSM::L3Frame& frame );
|
||||
@param hexstring RPData encoded into hex-string.
|
||||
@return Pointer to parsed RPData or NULL on error.
|
||||
*/
|
||||
RPData *hex2rpdata(const char *hexstring);
|
||||
// unused: RPData *hex2rpdata(const char *hexstring);
|
||||
|
||||
/**
|
||||
Parse a TPDU.
|
||||
|
@@ -81,8 +81,8 @@ class RLFrame : public GSM::L3Frame
|
||||
:L3Frame(source), mPrimitive(wPrimitive)
|
||||
{ }
|
||||
#endif
|
||||
RLFrame(size_t bitsNeeded=0) :L3Frame(GSM::L3_DATA,bitsNeeded) { /*RLFrameInit();*/ }
|
||||
RLFrame(const BitVector2& source) :L3Frame(GSM::SAPIUndefined,source) { /*RLFrameInit();*/ }
|
||||
RLFrame(size_t bitsNeeded=0) :L3Frame(GSM::L3_DATA,bitsNeeded,GSM::SAPI3) { /*RLFrameInit();*/ }
|
||||
RLFrame(const BitVector2& source) :L3Frame(GSM::SAPI3,source) { /*RLFrameInit();*/ }
|
||||
void text(std::ostream& os) const;
|
||||
|
||||
#if UNUSED_PRIMITIVE
|
||||
@@ -112,8 +112,8 @@ class TLFrame : public GSM::L3Frame
|
||||
:L3Frame(source), mPrimitive(wPrimitive)
|
||||
{ }
|
||||
#endif
|
||||
TLFrame(size_t bitsNeeded=0) :L3Frame(GSM::L3_DATA,bitsNeeded) { /*TLFrameInit();*/ }
|
||||
TLFrame(const BitVector2& source) :L3Frame(GSM::SAPIUndefined,source) { /*TLFrameInit();*/ }
|
||||
TLFrame(size_t bitsNeeded=0) :L3Frame(GSM::L3_DATA,bitsNeeded,GSM::SAPI3) { /*TLFrameInit();*/ }
|
||||
TLFrame(const BitVector2& source) :L3Frame(GSM::SAPI3,source) { /*TLFrameInit();*/ }
|
||||
|
||||
#if UNUSED_PRIMITIVE
|
||||
SMSPrimitive primitive() const { return mPrimitive; }
|
||||
|
@@ -21,8 +21,8 @@
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
AM_CXXFLAGS = -Wall
|
||||
# AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
# AM_CXXFLAGS = -Wall
|
||||
|
||||
noinst_LTLIBRARIES = libscanning.la
|
||||
|
||||
|
@@ -25,8 +25,8 @@ EXTRA_DIST = \
|
||||
README.TRXManager \
|
||||
clockdump.sh
|
||||
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
AM_CXXFLAGS = -Wall
|
||||
# AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
# AM_CXXFLAGS = -Wall
|
||||
|
||||
noinst_LTLIBRARIES = libtrxmanager.la
|
||||
|
||||
|
@@ -24,8 +24,8 @@ include $(top_srcdir)/Makefile.common
|
||||
DESTDIR =
|
||||
|
||||
AM_CFLAGS = $(STD_DEFINES_AND_INCLUDES) -std=gnu99 -march=native
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
AM_CXXFLAGS = -ldl -lpthread
|
||||
# AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
# AM_CXXFLAGS = -ldl -lpthread
|
||||
|
||||
#UHD wins if both are defined
|
||||
if UHD
|
||||
|
@@ -21,10 +21,11 @@
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
DESTDIR :=
|
||||
DESTDIR =
|
||||
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES)
|
||||
AM_CXXFLAGS = -DOMNITHREAD_POSIX=1 -lusb-1.0 -ldl -lpthread
|
||||
# (oley) could not find a place where USB_INCLUDES or WITH_INCLUDES would be defined
|
||||
# AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES)
|
||||
# AM_CXXFLAGS = -DOMNITHREAD_POSIX=1 -lusb-1.0 -ldl -lpthread
|
||||
|
||||
EXTRA_DIST = \
|
||||
README \
|
||||
@@ -32,10 +33,11 @@ EXTRA_DIST = \
|
||||
|
||||
noinst_LTLIBRARIES = libtransceiver.la
|
||||
|
||||
libtransceiver_la_CXXFLAGS = $(AM_CXXFLAGS) -DOMNITHREAD_POSIX=1 -lusb-1.0
|
||||
libtransceiver_la_SOURCES = \
|
||||
rnrad1Core.cpp \
|
||||
fusb.cpp \
|
||||
rnrad1Rx.cpp \
|
||||
rnrad1Core.cpp \
|
||||
fusb.cpp \
|
||||
rnrad1Rx.cpp \
|
||||
rnrad1Tx.cpp \
|
||||
radioInterface.cpp \
|
||||
sigProcLib.cpp \
|
||||
@@ -56,17 +58,17 @@ noinst_PROGRAMS = \
|
||||
PowerScanner
|
||||
|
||||
noinst_HEADERS = \
|
||||
ad9862.h \
|
||||
bytesex.h \
|
||||
commands.h \
|
||||
fusb.h \
|
||||
fpga_regs.h \
|
||||
ids.h \
|
||||
i2c.h \
|
||||
interfaces.h \
|
||||
spi.h \
|
||||
rnrad1Core.h \
|
||||
rnrad1.h \
|
||||
ad9862.h \
|
||||
bytesex.h \
|
||||
commands.h \
|
||||
fusb.h \
|
||||
fpga_regs.h \
|
||||
ids.h \
|
||||
i2c.h \
|
||||
interfaces.h \
|
||||
spi.h \
|
||||
rnrad1Core.h \
|
||||
rnrad1.h \
|
||||
Complex.h \
|
||||
radioInterface.h \
|
||||
radioDevice.h \
|
||||
|
@@ -50,7 +50,7 @@ unsigned char* write_it(unsigned v, unsigned char *s) {
|
||||
}
|
||||
|
||||
|
||||
const double RAD1Device::LO_OFFSET = 6.0e6;
|
||||
const double RAD1Device::LO_OFFSET = 9.0e6;
|
||||
|
||||
const double RAD1Device::masterClockRate = (double) 52.0e6;
|
||||
|
||||
|
@@ -993,7 +993,7 @@ void Demodulator::driveDemod(bool wSingleARFCN)
|
||||
if (!wSingleARFCN) {
|
||||
while (!demodBurst) {
|
||||
RadioClock *radioClock = (mRadioInterface->getClock());
|
||||
radioClock->wait();
|
||||
//radioClock->wait();
|
||||
demodBurst = mDemodFIFO->get();
|
||||
}
|
||||
}
|
||||
|
@@ -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,
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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/"
|
||||
|
||||
|
@@ -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<TimeSlot> timeSlotList;
|
||||
|
||||
static int initTimeSlots() // Return how many slots used by beacon.
|
||||
@@ -957,6 +952,7 @@ vector<string> 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 ||
|
||||
|
@@ -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);
|
||||
|
4
apps/OpenBTSDo
Executable file
4
apps/OpenBTSDo
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
exec /OpenBTS/OpenBTSCLI -p 49300 -t 127.0.0.1 -d $*
|
||||
|
@@ -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.
|
||||
|
0
apps/OpenBTS.logrotate → apps/logrotated.OpenBTS
Executable file → Normal file
0
apps/OpenBTS.logrotate → apps/logrotated.OpenBTS
Executable file → Normal file
@@ -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
|
||||
|
84
configure.ac
84
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 <http://www.gnu.org/licenses/>.
|
||||
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
|
||||
: ${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.h>],
|
||||
[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
|
||||
|
4
debian/changelog
vendored
4
debian/changelog
vendored
@@ -1,5 +1,5 @@
|
||||
openbts (5.0) unstable; urgency=low
|
||||
openbts (5.0-master) unstable; urgency=low
|
||||
|
||||
* Test
|
||||
|
||||
-- Donald C. Kirker <donald.kirker@rangenetworks.com> Mon, 22 Apr 2013 00:11:00 -0700
|
||||
-- Range Packager <buildmanager@rangenetworks.com> Mon, 15 Sep 2014 17:42:42 -0700
|
||||
|
16
debian/postinst
vendored
16
debian/postinst
vendored
@@ -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
|
||||
|
24
debian/postinst.old
vendored
24
debian/postinst.old
vendored
@@ -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
|
||||
|
||||
|
||||
|
7
debian/prerm.old
vendored
7
debian/prerm.old
vendored
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# killall runloop.OpenBTS.sh &>/dev/null
|
||||
|
||||
|
||||
|
||||
|
30
package/deb-after-install.sh
Executable file
30
package/deb-after-install.sh
Executable file
@@ -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
|
||||
|
15
package/deb-before-install.sh
Executable file
15
package/deb-before-install.sh
Executable file
@@ -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
|
31
package/rpm-after-install.sh
Executable file
31
package/rpm-after-install.sh
Executable file
@@ -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
|
||||
|
15
package/rpm-before-install.sh
Executable file
15
package/rpm-before-install.sh
Executable file
@@ -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
|
Reference in New Issue
Block a user