Files
openbts-2.8/GSM/GSML3RRElements.cpp
Ivan Kluchnikov b6f4309fb0 Added and fixed GSM L3 MM and RR messages and elements, which are necessary for authentication and ciphering implementation.
1. Fixed L3MobileStationClassmark3 element parsing.
2. Added parsing for CKSN and L3RejectCause elements.
3. Added KC variable to L3MobileIdentity element.
4. Added CKSN element to L3PagingResponse, L3LocationUpdatingRequest, L3CMServiceRequest, L3CMReestablishmentRequest and L3PagingResponse messages.
5. Added GSM L3 RR messages: L3CipheringModeCommand, L3CipheringModeComplete.
6. Added GSM L3 RR elements: L3CipheringSettings and L3CipheringResponse.
2013-01-11 22:17:42 +04:00

738 lines
17 KiB
C++

/**@file
@brief Radio Resource messages, GSM 04.08 9.1.
*/
/*
* Copyright 2008, 2009 Free Software Foundation, Inc.
* Copyright 2010 Kestrel Signal Processing, 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 <iterator> // for L3APDUData::text
#include "GSML3RRElements.h"
#include <Logger.h>
using namespace std;
using namespace GSM;
void L3CellOptionsBCCH::writeV(L3Frame& dest, size_t &wp) const
{
dest.writeField(wp,0,1);
dest.writeField(wp,mPWRC,1);
dest.writeField(wp,mDTX,2);
dest.writeField(wp,mRADIO_LINK_TIMEOUT,4);
}
void L3CellOptionsBCCH::text(ostream& os) const
{
os << "PWRC=" << mPWRC;
os << " DTX=" << mDTX;
os << " RADIO_LINK_TIMEOUT=" << mRADIO_LINK_TIMEOUT;
}
void L3CellOptionsSACCH::writeV(L3Frame& dest, size_t &wp) const
{
dest.writeField(wp,(mDTX>>2)&0x01,1);
dest.writeField(wp,mPWRC,1);
dest.writeField(wp,mDTX&0x03,2);
dest.writeField(wp,mRADIO_LINK_TIMEOUT,4);
}
void L3CellOptionsSACCH::text(ostream& os) const
{
os << "PWRC=" << mPWRC;
os << " DTX=" << mDTX;
os << " RADIO_LINK_TIMEOUT=" << mRADIO_LINK_TIMEOUT;
}
void L3CellSelectionParameters::writeV(L3Frame& dest, size_t &wp) const
{
dest.writeField(wp,mCELL_RESELECT_HYSTERESIS,3);
dest.writeField(wp,mMS_TXPWR_MAX_CCH,5);
dest.writeField(wp,mACS,1);
dest.writeField(wp,mNECI,1);
dest.writeField(wp,mRXLEV_ACCESS_MIN,6);
}
void L3CellSelectionParameters::text(ostream& os) const
{
os << "CELL-RESELECT-HYSTERESIS=" << mCELL_RESELECT_HYSTERESIS;
os << " MS-TXPWR-MAX-CCH=" << mMS_TXPWR_MAX_CCH;
os << " ACS=" << mACS;
os << " NECI=" << mNECI;
os << " RXLEV-ACCESS-MIN=" << mRXLEV_ACCESS_MIN;
}
void L3CipheringSettings::writeV(L3Frame& dest, size_t &wp) const
{
dest.writeField(wp,mAlgorithmIdentifier,3);
dest.writeField(wp,mSC,1);
}
void L3CipheringSettings::text(ostream& os) const
{
os << "AlgorithmIdentifier=" << mAlgorithmIdentifier;
os << " SC=" << mSC;
}
void L3CipheringResponse::writeV(L3Frame& dest, size_t &wp) const
{
dest.writeField(wp,0,3); // Spare Bits 0 0 0
dest.writeField(wp,mCR,1);
}
void L3CipheringResponse::text(ostream& os) const
{
os << "CR=" << mCR;
}
void L3ControlChannelDescription::writeV(L3Frame& dest, size_t &wp) const
{
dest.writeField(wp,0,1);
dest.writeField(wp,mATT,1);
dest.writeField(wp,mBS_AG_BLKS_RES,3);
dest.writeField(wp,mCCCH_CONF,3);
dest.writeField(wp,0,5);
dest.writeField(wp,mBS_PA_MFRMS,3);
dest.writeField(wp,mT3212,8);
}
void L3ControlChannelDescription::text(ostream& os) const
{
os << "ATT=" << mATT;
os << " BS_AG_BLKS_RES=" << mBS_AG_BLKS_RES;
os << " CCCH_CONF=" << mCCCH_CONF;
os << " BS_PA_MFRMS=" << mBS_PA_MFRMS;
os << " T3212=" << mT3212;
}
bool L3FrequencyList::contains(unsigned wARFCN) const
{
for (unsigned i=0; i<mARFCNs.size(); i++) {
if (mARFCNs[i]==wARFCN) return true;
}
return false;
}
unsigned L3FrequencyList::base() const
{
if (mARFCNs.size()==0) return 0;
unsigned retVal = mARFCNs[0];
for (unsigned i=1; i<mARFCNs.size(); i++) {
unsigned thisVal = mARFCNs[i];
if (thisVal<retVal) retVal=thisVal;
}
return retVal;
}
unsigned L3FrequencyList::spread() const
{
if (mARFCNs.size()==0) return 0;
unsigned max = mARFCNs[0];
for (unsigned i=0; i<mARFCNs.size(); i++) {
if (mARFCNs[i]>max) max=mARFCNs[i];
}
return max - base();
}
void L3FrequencyList::writeV(L3Frame& dest, size_t &wp) const
{
// If this were used as Frequency List, it had to be coded
// as the variable bit map format, GSM 04.08 10.5.2.13.7.
// But it is used as Cell Channel Description and is coded
// as the variable bit map format, GSM 04.08 10.5.2.1b.7.
// Difference is in abscence of Length field.
// The header occupies first 7 most significant bits of
// the first V-part octet and should be 1000111b=0x47 for
// the variable length bitmap.
dest.writeField(wp,0x47,7);
// base ARFCN
unsigned baseARFCN = base();
dest.writeField(wp,baseARFCN,10);
// bit map
unsigned delta = spread();
unsigned numBits = 8*lengthV() - 17;
if (numBits<delta) { LOG(ALERT) << "L3FrequencyList cannot encode full ARFCN set"; }
for (unsigned i=0; i<numBits; i++) {
unsigned thisARFCN = baseARFCN + 1 + i;
if (contains(thisARFCN)) dest.writeField(wp,1,1);
else dest.writeField(wp,0,1);
}
}
void L3FrequencyList::text(ostream& os) const
{
int size = mARFCNs.size();
for (int i=0; i<size; i++) {
os << mARFCNs[i] << " ";
}
}
void L3CellChannelDescription::writeV(L3Frame& dest, size_t& wp) const
{
dest.fillField(wp,0,3);
L3FrequencyList::writeV(dest,wp);
}
void L3NeighborCellsDescription::writeV(L3Frame& dest, size_t& wp) const
{
dest.fillField(wp,0,3);
L3FrequencyList::writeV(dest,wp);
}
void L3NeighborCellsDescription::text(ostream& os) const
{
os << "EXT-IND=0 BA-IND=0 ";
os << " ARFCNs=(";
L3FrequencyList::text(os);
os << ")";
}
void L3NCCPermitted::writeV(L3Frame& dest, size_t &wp) const
{
dest.writeField(wp,mPermitted,8);
}
void L3NCCPermitted::text(ostream& os) const
{
os << hex << "0x" << mPermitted << dec;
}
void L3RACHControlParameters::writeV(L3Frame& dest, size_t &wp) const
{
// GMS 04.08 10.5.2.29
dest.writeField(wp, mMaxRetrans, 2);
dest.writeField(wp, mTxInteger, 4);
dest.writeField(wp, mCellBarAccess, 1);
dest.writeField(wp, mRE, 1);
dest.writeField(wp, mAC, 16);
}
void L3RACHControlParameters::text(ostream& os) const
{
os << "maxRetrans=" << mMaxRetrans;
os << " txInteger=" << mTxInteger;
os << " cellBarAccess=" << mCellBarAccess;
os << " RE=" << mRE;
os << hex << " AC=0x" << mAC << dec;
}
void L3PageMode::writeV(L3Frame& dest, size_t &wp) const
{
// PageMode is 1/2 octet. Spare[3:2], PM[1:0]
dest.writeField(wp, 0x00, 2);
dest.writeField(wp, mPageMode, 2);
}
void L3PageMode::parseV( const L3Frame &src, size_t &rp)
{
// Read out spare bits.
rp += 2;
// Read out PageMode.
mPageMode = src.readField(rp, 2);
}
void L3PageMode::text(ostream& os) const
{
os << mPageMode;
}
void L3DedicatedModeOrTBF::writeV( L3Frame& dest, size_t &wp )const
{
// 1/2 Octet.
dest.writeField(wp, 0, 1);
dest.writeField(wp, mTMA, 1);
dest.writeField(wp, mDownlink, 1);
dest.writeField(wp, mDMOrTBF, 1);
}
void L3DedicatedModeOrTBF::text(ostream& os) const
{
os << "TMA=" << mTMA;
os << " Downlink=" << mDownlink;
os << " DMOrTBF=" << mDMOrTBF;
}
void L3ChannelDescription::writeV( L3Frame &dest, size_t &wp ) const
{
// GSM 04.08 10.5.2.5
// Channel Description Format (non-hopping)
// 7 6 5 4 3 2 1 0
// [ TSC ][ H=0 ][ SPARE(0,0)][ ARFCN[9:8] ] Octet 3
// [ ARFCN[7:0] ] Octet 4 H=0
//
// HACK -- Hard code for non-hopping.
assert(mHFlag==0);
dest.writeField(wp,mTypeAndOffset,5);
dest.writeField(wp,mTN,3);
dest.writeField(wp,mTSC,3);
dest.writeField(wp,0,3); // H=0 + 2 spares
dest.writeField(wp,mARFCN,10);
}
void L3ChannelDescription::parseV(const L3Frame& src, size_t &rp)
{
// GSM 04.08 10.5.2.5
mTypeAndOffset = (TypeAndOffset)src.readField(rp,5);
mTN = src.readField(rp,3);
mTSC = src.readField(rp,3);
mHFlag = src.readField(rp,1);
if (mHFlag) {
mMAIO = src.readField(rp,6);
mHSN = src.readField(rp,6);
} else {
rp += 2; // skip 2 spare bits
mARFCN = src.readField(rp,10);
}
}
void L3ChannelDescription::text(std::ostream& os) const
{
os << "typeAndOffset=" << mTypeAndOffset;
os << " TN=" << mTN;
os << " TSC=" << mTSC;
os << " ARFCN=" << mARFCN;
}
void L3RequestReference::writeV( L3Frame &dest, size_t &wp ) const
{
// Request Reference Format.
// 7 6 5 4 3 2 1 0
// [ RequestReference [7:0] ] Octet 2
// [ T1[4:0] ][ T3[5:3] ] Octet 3
// [ T3[2:0] ][ T2[4:0] ] Octet 4
dest.writeField(wp, mRA, 8);
dest.writeField(wp, mT1p, 5);
dest.writeField(wp, mT3, 6);
dest.writeField(wp, mT2, 5);
}
void L3RequestReference::text(ostream& os) const
{
os << "RA=" << mRA;
os << " T1'=" << mT1p;
os << " T2=" << mT2;
os << " T3=" << mT3;
}
void L3TimingAdvance::writeV( L3Frame &dest, size_t &wp ) const
{
dest.writeField(wp, 0x00, 2);
dest.writeField(wp, mTimingAdvance, 6);
}
void L3TimingAdvance::text(ostream& os) const
{
os << mTimingAdvance;
}
void L3RRCause::writeV( L3Frame &dest, size_t &wp ) const
{
dest.writeField(wp, mCauseValue, 8);
}
void L3RRCause::parseV( const L3Frame &src, size_t &rp )
{
mCauseValue = src.readField(rp, 8);
}
void L3RRCause::text(ostream& os) const
{
os << "0x" << hex << mCauseValue << dec;
}
void L3PowerCommand::writeV( L3Frame &dest, size_t &wp )const
{
dest.writeField(wp, 0, 3);
dest.writeField(wp, mCommand, 5);
}
void L3PowerCommand::text(ostream& os) const
{
os << mCommand;
}
void L3ChannelMode::writeV( L3Frame& dest, size_t &wp) const
{
dest.writeField(wp, mMode, 8);
}
void L3ChannelMode::parseV(const L3Frame& src, size_t& rp)
{
mMode = (Mode)src.readField(rp,8);
}
ostream& GSM::operator<<(ostream& os, L3ChannelMode::Mode mode)
{
switch (mode) {
case L3ChannelMode::SignallingOnly: os << "signalling"; break;
case L3ChannelMode::SpeechV1: os << "speech1"; break;
case L3ChannelMode::SpeechV2: os << "speech2"; break;
case L3ChannelMode::SpeechV3: os << "speech3"; break;
default: os << "?" << (int)mode << "?";
}
return os;
}
void L3ChannelMode::text(ostream& os) const
{
os << mMode;
}
// Application Information IEs
// APDU ID
void L3APDUID::writeV( L3Frame& dest, size_t &wp) const
{
// APDU ID is 1/2 octet. Protocol Identifier [3:0]
dest.writeField(wp,mProtocolIdentifier,4);
}
void L3APDUID::parseV( const L3Frame &src, size_t &rp)
{
// Read out Protocol Identifier.
mProtocolIdentifier = src.readField(rp, 4);
}
void L3APDUID::text(ostream& os) const
{
os << mProtocolIdentifier;
}
// APDU Flags
void L3APDUFlags::writeV( L3Frame& dest, size_t &wp) const
{
// APDU Flags is 1/2 octet. Protocol Identifier [3:0]
dest.writeField(wp,0,1); // spare
dest.writeField(wp,mCR,1); // C/R
dest.writeField(wp,mFirstSegment,1);
dest.writeField(wp,mLastSegment,1);
}
void L3APDUFlags::parseV( const L3Frame &src, size_t &rp)
{
// Read out Protocol Identifier.
rp += 1; // skip spare
mCR = src.readField(rp, 1);
mFirstSegment = src.readField(rp, 1);
mLastSegment = src.readField(rp, 1);
}
void L3APDUFlags::text(ostream& os) const
{
os << mCR << "," << mFirstSegment << "," << mLastSegment;
}
// APDU Data
L3APDUData::~L3APDUData()
{
}
L3APDUData::L3APDUData()
:L3ProtocolElement()
{
}
L3APDUData::L3APDUData(BitVector data)
:L3ProtocolElement()
,mData(data)
{
}
void L3APDUData::writeV( L3Frame& dest, size_t &wp) const
{
// we only need to write the data part
// TODO - single line please. copy / memcpy, anything better then a for loop
LOG(DEBUG) << "L3APDUData: writeV " << mData.size() << " bits";
mData.copyToSegment(dest, wp);
wp += mData.size() / 8;
}
void L3APDUData::parseV( const L3Frame& src, size_t &rp, size_t expectedLength )
{
LOG(DEBUG) << "L3APDUData: parseV " << expectedLength << " bytes";
mData.resize(expectedLength*8);
src.copyToSegment(mData, rp, expectedLength*8); // expectedLength is bytes, not bits
//for ( size_t i = 0 ; i < expectedLength ; ++i)
// mData[i] = src.readField(rp, 8);
}
void L3APDUData::text(ostream& os) const
{
// TODO - use the following two lines (get rid of the "char / char*" error)
//std::ostream_iterator<std::string> output( os, "" );
//std::copy( mData.begin(), mData.end(), output );
for (size_t i = 0 ; i < mData.size() ; ++i) {
os << (mData[i] ? "1" : "0");
}
}
void L3MeasurementResults::parseV(const L3Frame& frame, size_t &rp)
{
// GSM 04.08 10.5.2.20
mBA_USED = frame.readField(rp,1);
mDTX_USED = frame.readField(rp,1);
mRXLEV_FULL_SERVING_CELL = frame.readField(rp,6);
rp++; // spare
mMEAS_VALID = frame.readField(rp,1);
mRXLEV_SUB_SERVING_CELL = frame.readField(rp,6);
rp++; // spare
mRXQUAL_FULL_SERVING_CELL = frame.readField(rp,3);
mRXQUAL_SUB_SERVING_CELL = frame.readField(rp,3);
mNO_NCELL = frame.readField(rp,3);
for (unsigned i=0; i<6; i++) {
mRXLEV_NCELL[i] = frame.readField(rp,6);
mBCCH_FREQ_NCELL[i] = frame.readField(rp,5);
mBSIC_NCELL[i] = frame.readField(rp,6);
}
}
void L3MeasurementResults::text(ostream& os) const
{
// GSM 04.08 10.5.2.20
os << "BA_USED=" << mBA_USED;
os << " DTX_USED=" << mDTX_USED;
os << " MEAS_VALID=" << mMEAS_VALID;
// Note that the value of the MEAS-VALID bit is reversed
// from what you might expect.
if (mMEAS_VALID) return;
os << " RXLEV_FULL_SERVING_CELL=" << mRXLEV_FULL_SERVING_CELL;
os << " RXLEV_SUB_SERVING_CELL=" << mRXLEV_SUB_SERVING_CELL;
os << " RXQUAL_FULL_SERVING_CELL=" << mRXQUAL_FULL_SERVING_CELL;
os << " RXQUAL_SUB_SERVING_CELL=" << mRXQUAL_SUB_SERVING_CELL;
os << " NO_NCELL=" << mNO_NCELL;
// no measurements?
if (mNO_NCELL==0) return;
// no neighbor list?
if (mNO_NCELL==7) return;
for (unsigned i=0; i<mNO_NCELL; i++) {
os << " RXLEV_NCELL" << i+1 << "=" << mRXLEV_NCELL[i];
os << " BCCH_FREQ_NCELL" << i+1 << "=" << mBCCH_FREQ_NCELL[i];
os << " BSIC_NCELL" << i+1 << "=" << mBSIC_NCELL[i];
}
}
unsigned L3MeasurementResults::RXLEV_NCELL(unsigned * target) const
{
for (unsigned i=0; i<mNO_NCELL; i++) target[i] = mRXLEV_NCELL[i];
return mNO_NCELL;
}
unsigned L3MeasurementResults::BCCH_FREQ_NCELL(unsigned * target) const
{
for (unsigned i=0; i<mNO_NCELL; i++) target[i] = mBCCH_FREQ_NCELL[i];
return mNO_NCELL;
}
unsigned L3MeasurementResults::BSIC_NCELL(unsigned * target) const
{
for (unsigned i=0; i<mNO_NCELL; i++) target[i] = mBSIC_NCELL[i];
return mNO_NCELL;
}
int L3MeasurementResults::decodeLevToDBm(unsigned lev) const
{
// See GSM 05.08 8.1.4.
// SCALE is 0 for anything but the ENHANCED MEASUREMENT REPORT message.
return -111 + lev;
}
float L3MeasurementResults::decodeQualToBER(unsigned qual) const
{
// See GSM 05.08 8.2.4.
// Convert lowest value as "0" instead of 0.14%.
static const float vals[] = {0.0, 0.28, 0.57, 1.13, 2.26, 4.53, 9.05, 18.10};
assert(qual<8);
return 0.01*vals[qual];
}
L3SI3RestOctets::L3SI3RestOctets()
:L3RestOctets(),
mHaveSelectionParameters(false),
mCBQ(0),mCELL_RESELECT_OFFSET(0),
mTEMPORARY_OFFSET(0),
mPENALTY_TIME(0)
{
// See GSM 04.08 10.5.2.34 and 05.08 9 Table 1.
if (!gConfig.defines("GSM.SI3RO")) return;
// Optional Cell Selection Parameters.
// CELL_BAR_QUALIFY. 1 bit. Default value is 0.
if (gConfig.defines("GSM.SI3RO.CBQ")) {
mCBQ = gConfig.getNum("GSM.SI3RO.CBQ");
mHaveSelectionParameters = true;
}
// CELL_RESELECT_OFFSET. 6 bits. Default value is 0.
// C2 offset in 2 dB steps
if (gConfig.defines("GSM.SI3RO.CRO")) {
mCELL_RESELECT_OFFSET = gConfig.getNum("GSM.SI3RO.CRO");
mHaveSelectionParameters = true;
}
// Another offset to C2 in 10 dB steps, applied during penalty time.
// 3 bits. // Default is 0 dB but "7" means "infinity".
if (gConfig.defines("GSM.SI3RO.TEMPORARY_OFFSET")) {
mTEMPORARY_OFFSET = gConfig.getNum("GSM.SI3RO.TEMPORARY_OFFSET");
mHaveSelectionParameters = true;
}
// The time for which the temporary offset is applied, 20*(n+1).
if (gConfig.defines("GSM.SI3RO.PENALTY_TIME")) {
mPENALTY_TIME = gConfig.getNum("GSM.SI3RO.PENALTY_TIME");
mHaveSelectionParameters = true;
}
}
size_t L3SI3RestOctets::lengthV() const
{
size_t sumBits = 0;
if (mHaveSelectionParameters) sumBits += 1 + 1+6+3+5;
size_t octets = sumBits/8;
if (sumBits%8) octets += 1;
return octets;
}
void L3SI3RestOctets::writeV(L3Frame& dest, size_t &wp) const
{
if (mHaveSelectionParameters) {
dest.writeH(wp);
dest.writeField(wp,mCBQ,1);
dest.writeField(wp,mCELL_RESELECT_OFFSET,6);
dest.writeField(wp,mTEMPORARY_OFFSET,3);
dest.writeField(wp,mPENALTY_TIME,5);
}
}
void L3SI3RestOctets::text(ostream& os) const
{
if (mHaveSelectionParameters) {
os << "CBQ=" << mCBQ;
os << " CELL_RESELECT_OFFSET=" << mCELL_RESELECT_OFFSET;
os << " TEMPORARY_OFFSET=" << mTEMPORARY_OFFSET;
os << " PANALTY_TIME=" << mPENALTY_TIME;
}
}
// vim: ts=4 sw=4