OpenBTS-UMTS/UMTS/AsnHelper.cpp

270 lines
8.8 KiB
C++

/*
* OpenBTS provides an open source alternative to legacy telco protocols and
* traditionally complex, proprietary hardware systems.
*
* Copyright 2014 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero General
* Public License version 3. See the COPYING and NOTICE files in the main
* directory for licensing information.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
*/
#include "AsnHelper.h"
//#include "ByteVector.h" included from AsnHelper.h
#include "SgsnBase.h" // For the layer 3 logging facility.
#include <ctype.h>
#include <Configuration.h>
#include "URRC.h"
extern ConfigurationTable gConfig; // Where is this thing?
// (pat) This variable controls asn debug statements.
// It is used in the ASN/makefile to over-ride the default ASN debugger.
extern "C" {
int rn_asn_debug = 1;
};
namespace ASN {
//#include "ENUMERATED.h" included from AsnHelper.h
//#include "BIT_STRING.h" included from AsnHelper.h
#include "Digit.h"
#include "asn_SEQUENCE_OF.h"
};
namespace UMTS {
// Return true on success, false on failure.
// Make the ByteVector large enough to hold the expected encoded message,
// and the ByteVector size will be shrink wrapped around the result before return.
// If the descr is specified, an error message is printed on failure before return.
bool uperEncodeToBV(ASN::asn_TYPE_descriptor_t *td,void *sptr, ByteVector &result, const std::string descr)
{
rn_asn_debug = gConfig.getNum("UMTS.Debug.ASN");
ASN::asn_enc_rval_t rval = uper_encode_to_buffer(td,sptr,result.begin(),result.allocSize());
if (rval.encoded < 0) {
LOG(ALERT) << "ASN encoder failed encoding '"<<descr<<"' into buf bytesize="<<result.size();
return false;
}
// (pat) rval.encoded is number of bits, despite documentation
// at asn_enc_rval_t that claims it is in bytes.
result.setSizeBits(rval.encoded);
return true;
}
// Decode an Asn message and return whatever kind of message pops out.
void *uperDecodeFromByteV(ASN::asn_TYPE_descriptor_t *asnType,ByteVector &bv)
{
void *result = NULL;
ASN::asn_dec_rval_s rval = uper_decode_complete(NULL, // optional stack size
asnType, &result,
bv.begin(), bv.size() // per buffer size is in bytes.
);
//0, // No skip bits
//0); // No unused bits. (There may be but we dont tell per.)
if (rval.code != ASN::RC_OK) {
// What else should we say about this?
LOG(ERR) << "incoming message could not be decoded.";
return 0;
}
if (rval.consumed != bv.size()) {
LOG(INFO) << "incoming message consumed only" << rval.consumed << " of " << bv.size() << " bytes.";
}
return result;
}
// Same as uperDecodeFromByteV but work on a BitVector
void *uperDecodeFromBitV(ASN::asn_TYPE_descriptor_t *asnType,BitVector &in)
{
ByteVector bv(in);
return uperDecodeFromByteV(asnType,bv);
}
// Set the ASN BIT_STRING_t to an allocated buffer of the proper size.
// User must determine the proper size for the ASN message being used.
void setAsnBIT_STRING(ASN::BIT_STRING_t *result,uint8_t *buf, unsigned numBits)
{
result->buf = buf;
result->size = (numBits+7)/8;
result->bits_unused = (numBits%8) ? (8-(numBits%8)) : 0;
}
ASN::BIT_STRING_t allocAsnBIT_STRING(unsigned numBits)
{
ASN::BIT_STRING_t result;
setAsnBIT_STRING(&result,(uint8_t*)calloc(1,(7+numBits)/8),numBits);
return result;
}
/** Copy a string of ASCII digits into an ASN.1 SEQUENCE OF DIGIT. */
void setASN1SeqOfDigits(void *seq, const char* digit)
{
ASN::asn_sequence_empty(seq);
while (*digit != '\0') {
ASN::Digit_t* d = (ASN::Digit_t*)calloc(1,sizeof(ASN::Digit_t));
assert(d);
*d = *digit++ - '0';
int ret = ASN::ASN_SEQUENCE_ADD(seq,d);
assert(ret==0);
}
}
// Convert an integral value to an ASN ENUMERATED struct.
// Note: if you use this in an assignment, it will not free the previous value, if any.
// Example:
// ENUMERATED_t someAsnEnumeratedValue; // In asn somewhere.
// someAsnEnumeratedValue = toAsnEnumerated(3); // In our code.
ASN::ENUMERATED_t toAsnEnumerated(unsigned value)
{
ASN::ENUMERATED_t result;
memset(&result,0,sizeof(result));
asn_long2INTEGER(&result,value);
return result;
}
long asnEnum2long(ASN::ENUMERATED_t &thing)
{
long result;
if (asn_INTEGER2long(&thing,&result)) {
// We should not get this; it indicates a drastic encoding error in the UE.
// If we do get it, will have to add arguments to figure out where it happened.
LOG(ERR) << "failure converting asn ENUMERATED type to long";
}
return result;
}
// The argument must be A_SEQUENCE_OF(Digit_t) or equivalent.
// Digit_t is typedefed to long.
typedef A_SEQUENCE_OF(long) asn_sequence_of_long;
AsnSeqOfDigit2BV::AsnSeqOfDigit2BV(void*arg)
: ByteVector(((asn_sequence_of_long*)arg)->count)
{
asn_sequence_of_long *list = (asn_sequence_of_long*)arg;
int cnt = size();
uint8_t *bytes = begin();
for (int i = 0; i < cnt; i++) {
// The Digit_t is typedefed to long, and the longs are allocated.
// (It couldnt be any more wasteful; pretty amusing that this is to compress the structures.)
bytes[i] = (uint8_t) *(list->array[i]);
}
}
//=============== Functions for AsnEnumMap ====================================
// Call back for ASN print function.
static int mycb(const void *buf, size_t size, void *application_specific_key)
{
// Format of the value is "enumvalue (actualvalue)", eg: "5 (dat20)"
const char *cp = (const char*)buf;
cp = strchr(cp,'(');
while (*cp != 0 && !isdigit(*cp)) { cp++; }
long *presult = (long*) application_specific_key;
*presult = atoi(cp);
//printf("mycp buffer=%s %ld\n",(const char*)buf,*presult);
return 0;
}
void AsnEnumMap::asnLoadEnumeratedValues(ASN::asn_TYPE_descriptor_t &asnp, unsigned maxEnumValue)
{
mNumValues = (1+maxEnumValue);
mActualValues = (long*) malloc(mNumValues*sizeof(long));
// Note: n is a long because that is what is expected.
for (long n = 0; n <= (long)maxEnumValue; n++) {
ASN::ENUMERATED_t foo;
memset(&foo,0,sizeof(foo));
ASN::asn_long2INTEGER(&foo, n);
// In the original asn description, the actual values are just strings,
// not the actual integral values that we need, but the actual
// value is (almost) always included in the string.
// The asn compiler does not provide any native way to get the enum strings out,
// but we can use the print_struct which does a call-back to us with the info.
asnp.print_struct(&asnp,(const void*) &foo,0,mycb,(void*)&mActualValues[n]);
}
}
void AsnEnumMap::dump()
{
for (unsigned enumValue = 0; enumValue < mNumValues; enumValue++) {
printf("%u=>%ld ",enumValue,mActualValues[enumValue]);
if (enumValue && enumValue % 8 == 0) printf("\n");
}
printf("\n");
}
// Return the asn enum value for an actual value.
// This is slow, but only happens at setup.
// Return "close" value if none match.
int AsnEnumMap::findEnum(long actual)
{
unsigned closeindex = 0;
long closediff = 0x7fffffff;
for (unsigned enumValue = 0; enumValue < mNumValues; enumValue++) {
if (actual == mActualValues[enumValue]) return enumValue;
int diff = mActualValues[enumValue] - actual;
if (diff < 0) diff = - diff;
if (diff < closediff) { closediff = diff; closeindex = enumValue; }
}
//std::cout << "warning: enum value " << actual << " does not match, using: " << mActualValues[closeindex] <<"\n";
return closeindex;
}
// Call back for ASN print function.
static int mycb2(const void *buf, size_t size, void *application_specific_key)
{
const char *cp = (const char*)buf; // Gotta love c++.
// Format of the value is "enumvalue (actualvalue)", eg: "5 (dat20)"
std::ostringstream *ssp = (std::ostringstream*)application_specific_key;
ssp->write(cp,size);
return 0;
}
// Like asn_fprint but return the result in a C++ string.
std::string asn2string(ASN::asn_TYPE_descriptor_t *asnp, const void *struct_ptr)
{
std::ostringstream ss;
asnp->print_struct(asnp,struct_ptr,0,mycb2,(void*)&ss);
return ss.str();
}
void asnLogMsg(unsigned rbid, ASN::asn_TYPE_descriptor_t *asnp, const void *struct_ptr,
const char *comment,
UEInfo *uep, // Or NULL if none.
uint32_t urnti) // If uep is NULL, put this in the log instead.
{
int debug = gConfig.getNum("UMTS.Debug.Messages");
if (debug || IS_LOG_LEVEL(INFO)) {
// This C++ IO paradigm is so crappy.
std::string readable = asn2string(asnp,struct_ptr);
std::string id = uep ? uep->ueid() : format(" urnti=0x%x",urnti);
_LOG(INFO) << (comment?comment:"") <<id<<LOGVAR(rbid) <<" "<< readable.c_str();
if (debug && comment) {
MGLOG(comment <<id<<LOGVAR(rbid));
LOGWATCH(comment <<id<<LOGVAR(rbid));
}
}
}
//void AsnBitString::finish(ASN::BIT_STRING_t *ptr)
//{
// if (ptr->bits_unused) setSizeBits(ptr->size*8 - ptr->bits_unused);
//}
//
//AsnBitString::AsnBitString(ASN::BIT_STRING_t *ptr) :
// ByteVector(ptr->buf,ptr->size)
//{
// finish(ptr);
//}
//
//AsnBitString::AsnBitString(ASN::BIT_STRING_t &ref) :
// ByteVector(ref.buf,ref.size)
//{
// finish(ptr);
//}
}; // namespace UMTS