sync from commercial 64a79ce7a18f7e3ef3fe5aeacf3b6629980d30b2

This commit is contained in:
Michael Iedema
2014-12-04 21:25:19 +01:00
parent eceec213a5
commit 3d59f52a3f
81 changed files with 1282 additions and 2269 deletions

1
.gitignore vendored
View File

@@ -7,6 +7,7 @@
*.la
Makefile
aclocal.m4
autom4te.cache
build-arch-stamp
build-indep-stamp
compile

View File

@@ -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);
}

View File

@@ -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

380
Control/CBS.cpp Normal file
View 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
View 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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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.

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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:

View File

@@ -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
View File

0
Control/RRLPServer.h Normal file
View File

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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 \

View File

@@ -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());

View File

@@ -336,7 +336,9 @@ bool CCCHLogicalChannel::processRaches()
// // Nothing available?
// if (!LCH) {
// failure:
#if 0
// return false;
#endif
// }
if (! rach->mChan) {

View File

@@ -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);
}
}
};

View File

@@ -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

View File

@@ -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);

View File

@@ -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())); }
};

View File

@@ -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; }

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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();

View File

@@ -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; }
};

View File

@@ -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
{

View File

@@ -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); }

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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,

View File

@@ -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 \

View File

@@ -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

View File

@@ -389,6 +389,7 @@ struct SipCallbacks {
}
};
extern string OpenBTSUserAgent();
};
#endif

View File

@@ -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

View File

@@ -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

View File

@@ -463,8 +463,12 @@ void parseAuthenticate(string stuff, SipParamList &params)
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.

View File

@@ -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

View File

@@ -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)
{

View File

@@ -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.

View File

@@ -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; }

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 \

View File

@@ -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;

View File

@@ -993,7 +993,7 @@ void Demodulator::driveDemod(bool wSingleARFCN)
if (!wSingleARFCN) {
while (!demodBurst) {
RadioClock *radioClock = (mRadioInterface->getClock());
radioClock->wait();
//radioClock->wait();
demodBurst = mDemodFIFO->get();
}
}

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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/"

View File

@@ -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 ||

View File

@@ -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
View File

@@ -0,0 +1,4 @@
#!/bin/bash
exec /OpenBTS/OpenBTSCLI -p 49300 -t 127.0.0.1 -d $*

View File

@@ -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
View File

View 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

View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -1,7 +0,0 @@
#!/bin/bash
# killall runloop.OpenBTS.sh &>/dev/null

1078
doxconfig

File diff suppressed because it is too large Load Diff

30
package/deb-after-install.sh Executable file
View 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
View 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
View 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
View 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