mirror of
https://github.com/fairwaves/openbts-2.8.git
synced 2025-10-23 07:42:01 +00:00
Added support for performance-reporting counters. and Patch 4588 in private: For some reason, ReportingTest won't build on all systems. Since it is not part of the actuall application, I am commenting it out from the Makefile.am for now. git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@4627 19bc5d8c-e614-43d4-8b26-e1612bc8e597
296 lines
8.1 KiB
C++
296 lines
8.1 KiB
C++
/**@file RRLPServer */
|
|
/*
|
|
* Copyright 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
|
|
* Copyright 2011 Range Networks, Inc.
|
|
*
|
|
* This software is distributed under the terms of the GNU Affero Public License.
|
|
* See the COPYING file in the main directory for details.
|
|
*
|
|
* This use of this software may be subject to additional restrictions.
|
|
* See the LEGAL file in the main directory for details.
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
#include <Timeval.h>
|
|
|
|
using namespace std;
|
|
|
|
#include "ControlCommon.h"
|
|
#include "RRLPServer.h"
|
|
|
|
#include <GSMLogicalChannel.h>
|
|
#include <GSML3MMMessages.h>
|
|
|
|
#include <SubscriberRegistry.h>
|
|
|
|
#include <Logger.h>
|
|
#include <Reporting.h>
|
|
|
|
using namespace GSM;
|
|
using namespace Control;
|
|
|
|
void clean(char *line)
|
|
{
|
|
char *p = line + strlen(line) - 1;
|
|
while (p > line && *p <= ' ') *p-- = 0;
|
|
}
|
|
|
|
string getConfig()
|
|
{
|
|
const char *configs[] = {
|
|
"GSM.RRLP.ACCURACY",
|
|
"GSM.RRLP.RESPONSETIME",
|
|
"GSM.RRLP.ALMANAC.URL",
|
|
"GSM.RRLP.EPHEMERIS.URL",
|
|
"GSM.RRLP.ALMANAC.REFRESH.TIME",
|
|
"GSM.RRLP.EPHEMERIS.REFRESH.TIME",
|
|
"GSM.RRLP.SEED.LATITUDE",
|
|
"GSM.RRLP.SEED.LONGITUDE",
|
|
"GSM.RRLP.SEED.ALTITUDE",
|
|
"GSM.RRLP.EPHEMERIS.ASSIST.COUNT",
|
|
"GSM.RRLP.ALMANAC.ASSIST.PRESENT",
|
|
0
|
|
};
|
|
string config = "";
|
|
for (const char **p = configs; *p; p++) {
|
|
string configValue = gConfig.getStr(*p, "");
|
|
if (configValue.length() == 0) return "";
|
|
config.append("&");
|
|
config.append(*p);
|
|
config.append("=");
|
|
if (0 == strcmp((*p) + strlen(*p) - 3, "URL")) {
|
|
// TODO - better to have urlencode and decode, but then I'd have to look for them
|
|
char buf[3];
|
|
buf[2] = 0;
|
|
for (const char *q = configValue.c_str(); *q; q++) {
|
|
sprintf(buf, "%02x", *q);
|
|
config.append(buf);
|
|
}
|
|
} else {
|
|
config.append(configValue);
|
|
}
|
|
}
|
|
return config;
|
|
}
|
|
|
|
RRLPServer::RRLPServer(L3MobileIdentity wMobileID, LogicalChannel *wDCCH)
|
|
{
|
|
trouble = false;
|
|
url = gConfig.getStr("GSM.RRLP.SERVER.URL", "");
|
|
if (url.length() == 0) {
|
|
LOG(INFO) << "RRLP not configured";
|
|
trouble = true;
|
|
return;
|
|
}
|
|
mobileID = wMobileID;
|
|
DCCH = wDCCH;
|
|
// name of subscriber
|
|
name = string("IMSI") + mobileID.digits();
|
|
//if IMEI tagging enabled, check if this IMEI (which is updated elsewhere) has RRLP disabled
|
|
//otherwise just go on
|
|
if (gConfig.defines("Control.LUR.QueryIMEI")){
|
|
//check supported bit
|
|
string supported= gSubscriberRegistry.imsiGet(name, "RRLPSupported");
|
|
if(supported.empty() || supported == "0"){
|
|
LOG(INFO) << "RRLP not supported for " << name;
|
|
trouble = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool RRLPServer::assist()
|
|
{
|
|
if (trouble) return false;
|
|
query = "query=assist";
|
|
return transact();
|
|
}
|
|
|
|
bool RRLPServer::locate()
|
|
{
|
|
if (trouble) return false;
|
|
query = "query=loc";
|
|
return transact();
|
|
}
|
|
|
|
bool RRLPServer::transact()
|
|
{
|
|
vector<string> apdus;
|
|
while (true) {
|
|
// bounce off server
|
|
string esc = "'";
|
|
string config = getConfig();
|
|
if (config.length() == 0) return false;
|
|
string cmd = "wget -qO- " + esc + url + "?" + query + config + esc;
|
|
LOG(INFO) << "*************** " << cmd;
|
|
FILE *result = popen(cmd.c_str(), "r");
|
|
if (!result) {
|
|
LOG(CRIT) << "popen call \"" << cmd << "\" failed";
|
|
return NULL;
|
|
}
|
|
// build map of responses, and list of apdus
|
|
map<string,string> response;
|
|
size_t nbytes = 1500;
|
|
char *line = (char*)malloc(nbytes+1);
|
|
while (!feof(result)) {
|
|
if (!fgets(line, nbytes, result)) break;
|
|
clean(line);
|
|
LOG(INFO) << "server return: " << line;
|
|
char *p = strchr(line, '=');
|
|
if (!p) continue;
|
|
string lhs = string(line, 0, p-line);
|
|
string rhs = string(line, p+1-line, string::npos);
|
|
if (lhs == "apdu") {
|
|
apdus.push_back(rhs);
|
|
} else {
|
|
response[lhs] = rhs;
|
|
}
|
|
}
|
|
free(line);
|
|
pclose(result);
|
|
|
|
// quit if error
|
|
if (response.find("error") != response.end()) {
|
|
LOG(INFO) << "error from server: " << response["error"];
|
|
return false;
|
|
}
|
|
|
|
// quit if ack from assist unless there are more apdu messages
|
|
if (response.find("ack") != response.end()) {
|
|
LOG(INFO) << "ack from mobile, decoded by server";
|
|
if (apdus.size() == 0) {
|
|
return true;
|
|
} else {
|
|
LOG(INFO) << "more apdu messages";
|
|
}
|
|
}
|
|
|
|
// quit if location decoded
|
|
if (response.find("latitude") != response.end() &&
|
|
response.find("longitude") != response.end() &&
|
|
response.find("positionError") != response.end()) {
|
|
if (!gSubscriberRegistry.RRLPUpdate(name, response["latitude"],
|
|
response["longitude"],
|
|
response["positionError"])){
|
|
LOG(INFO) << "SR update problem";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// bounce off mobile
|
|
if (apdus.size() == 0) {
|
|
LOG(INFO) << "missing apdu for mobile";
|
|
return false;
|
|
}
|
|
string apdu = apdus[0];
|
|
apdus.erase(apdus.begin());
|
|
BitVector bv(apdu.size()*4);
|
|
if (!bv.unhex(apdu.c_str())) {
|
|
LOG(INFO) << "BitVector::unhex problem";
|
|
return false;
|
|
}
|
|
|
|
DCCH->send(L3ApplicationInformation(bv));
|
|
// Receive an L3 frame with a timeout. Timeout loc req response time max + 2 seconds.
|
|
L3Frame* resp = DCCH->recv(130000);
|
|
if (!resp) {
|
|
return false;
|
|
}
|
|
LOG(INFO) << "RRLPQuery returned " << *resp;
|
|
if (resp->primitive() != DATA) {
|
|
LOG(INFO) << "didn't receive data";
|
|
switch (resp->primitive()) {
|
|
case ESTABLISH: LOG(INFO) << "channel establihsment"; break;
|
|
case RELEASE: LOG(INFO) << "normal channel release"; break;
|
|
case DATA: LOG(INFO) << "multiframe data transfer"; break;
|
|
case UNIT_DATA: LOG(INFO) << "datagram-type data transfer"; break;
|
|
case ERROR: LOG(INFO) << "channel error"; break;
|
|
case HARDRELEASE: LOG(INFO) << "forced release after an assignment"; break;
|
|
default: LOG(INFO) << "unrecognized primitive response"; break;
|
|
}
|
|
delete resp;
|
|
return false;
|
|
}
|
|
const unsigned PD_RR = 6;
|
|
LOG(INFO) << "resp.pd = " << resp->PD();
|
|
if (resp->PD() != PD_RR) {
|
|
LOG(INFO) << "didn't receive an RR message";
|
|
delete resp;
|
|
return false;
|
|
}
|
|
const unsigned MTI_RR_STATUS = 18;
|
|
if (resp->MTI() == MTI_RR_STATUS) {
|
|
int cause = resp->peekField(16, 8);
|
|
delete resp;
|
|
switch (cause) {
|
|
case 97:
|
|
LOG(INFO) << "MS says: message not implemented";
|
|
//Rejection code only useful if we're gathering IMEIs
|
|
if (gConfig.defines("Control.LUR.QueryIMEI")){
|
|
// flag unsupported in SR so we don't waste time on it again
|
|
if (gSubscriberRegistry.imsiSet(name, "RRLPSupported", "0")) {
|
|
LOG(INFO) << "SR update problem";
|
|
}
|
|
}
|
|
return false;
|
|
case 98:
|
|
LOG(INFO) << "MS says: message type not compatible with protocol state";
|
|
return false;
|
|
default:
|
|
LOG(INFO) << "unknown RR_STATUS response, cause = " << cause;
|
|
return false;
|
|
}
|
|
}
|
|
const unsigned MTI_RR_APDU = 56;
|
|
if (resp->MTI() != MTI_RR_APDU) {
|
|
LOG(INFO) << "received unexpected RR Message " << resp->MTI();
|
|
delete resp;
|
|
return false;
|
|
}
|
|
|
|
// looks like a good APDU
|
|
BitVector *bv2 = (BitVector*)resp;
|
|
BitVector bv3 = bv2->tail(32);
|
|
ostringstream os;
|
|
bv3.hex(os);
|
|
apdu = os.str();
|
|
delete resp;
|
|
|
|
// next query for server
|
|
query = "query=apdu&apdu=" + apdu;
|
|
}
|
|
// not reached
|
|
}
|
|
|
|
bool sendRRLP(GSM::L3MobileIdentity mobileID, GSM::LogicalChannel *LCH)
|
|
{
|
|
// Query for RRLP
|
|
RRLPServer wRRLPServer(mobileID, LCH);
|
|
if (!wRRLPServer.locate()) {
|
|
LOG(INFO) << "locate problem";
|
|
if (!wRRLPServer.assist()) {
|
|
LOG(INFO) << "assist problem";
|
|
// Failure of assist will almost invariably lead to failure in locate.
|
|
// Don't even try. It causes too much confusion.
|
|
return false;
|
|
}
|
|
if (!wRRLPServer.locate()) {
|
|
LOG(INFO) << "locate problem";
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|