void CCCHL2::writeHighSide(const GSM::L3Frame& l3)
{
	OBJLOG(DEEPDEBUG) <<"CCCHL2::writeHighSide " << l3;
	assert(mDownstream);
	//assert(l3.primitive()==UNIT_DATA);
	L2Header header(L2Length(l3.length()));
	mDownstream->writeHighSide(L2Frame(header,l3));
}
/**
	Read an L3Frame from SAP3.
	Throw exception on failure.  Will NOT return a NULL pointer.
*/
GSM::L3Frame* getFrameSMS(GSM::LogicalChannel *LCH, GSM::Primitive primitive=GSM::DATA)
{
	GSM::L3Frame *retVal = LCH->recv(LCH->N200()*LCH->T200(),3);
	if (!retVal) {
		LOG(NOTICE) << "channel read time out on " << *LCH << " SAP3";
		throw ChannelReadTimeout();
	}
	LOG(DEBUG) << "getFrameSMS on " << *LCH << " in frame " << *retVal;
	if (retVal->primitive() != primitive) {
		LOG(NOTICE) << "unexpected primitive on " << *LCH << ", expecting " << primitive << ", got " << *retVal;
		throw UnexpectedPrimitive();
	}
	if ((retVal->primitive() == GSM::DATA) && (retVal->PD() != GSM::L3SMSPD)) {
		LOG(NOTICE) << "unexpected (non-SMS) protocol on " << *LCH << " in frame " << *retVal;
		throw UnexpectedMessage();
	}
	return retVal;
}
Example #3
0
/**
	Read an L3Frame from SAP3.
	Throw exception on failure.  Will NOT return a NULL pointer.
*/
GSM::L3Frame* getFrameSMS(UMTS::DCCHLogicalChannel *LCH, GSM::Primitive primitive=GSM::DATA)
{
	// FIXME -- We need to determine a correct timeout value here.
	GSM::L3Frame *retVal = LCH->recv(20000,3);
	if (!retVal) {
		LOG(NOTICE) << "channel read time out on " << *LCH << " SAP3";
		throw ChannelReadTimeout();
	}
	LOG(DEBUG) << "getFrameSMS on " << *LCH << " in frame " << *retVal;
	if (retVal->primitive() != primitive) {
		LOG(NOTICE) << "unexpected primitive on " << *LCH << ", expecting " << primitive << ", got " << *retVal;
		throw UnexpectedPrimitive();
	}
	if ((retVal->primitive() == GSM::DATA) && (retVal->PD() != GSM::L3SMSPD)) {
		LOG(NOTICE) << "unexpected (non-SMS) protocol on " << *LCH << " in frame " << *retVal;
		throw UnexpectedMessage();
	}
	return retVal;
}
void Control::InCallMOSMSController(const CPData *cpData, TransactionEntry* transaction, GSM::SACCHLogicalChannel *LCH)
{
	LOG(INFO) << *cpData;

	// See GSM 04.11 Arrow Diagram A5 for the transaction
	// Step 1	MS->Network	CP-DATA containing RP-DATA
	// Step 2	Network->MS	CP-ACK
	// Step 3	Network->MS	CP-DATA containing RP-ACK
	// Step 4	MS->Network	CP-ACK

	// LAPDm operation, from GSM 04.11, Annex F:
	// """
	// Case C: Mobile originating short message transfer, parallel call. 
	// The mobile station will send a SABM command on the SACCH when a CM_SERV_ACC
	// message has been received from the network, allowing the short message
	// transfer to start. If no hand over occurs the link will stay up until the
	// MSC orders a explicit release, or the clearing procedure is invoked. If the
	// parallel call is cleared before the short message transfer is finalized, the
	// MSC will delay the clearing procedure toward the BSS, i.e. the channel
	// release procedure is delayed. 
	// """

	// Since there's a parallel call, we will assume correct provisioning.
	// And we know that CM and SABM are established.

	// Step 1 already happened in the SACCH service loop.
	// Just get the L3 TI and set the high bit since it originated in the MS.
	unsigned L3TI = cpData->TI() | 0x08;
	transaction->L3TI(L3TI);

	// Step 2
	// Respond with CP-ACK.
	// This just means that we got the message.
	LOG(INFO) << "sending CPAck";
	LCH->send(CPAck(L3TI),3);

	// Parse the message in CM and process RP part.
	// This is where we actually parse the message and send it out.
	// FIXME -- We need to set the message ref correctly,
	// even if the parsing fails.
	// The compiler gives a warning here.  Let it.  It will remind someone to fix it.
	unsigned ref;
	bool success = false;
	try {
		CPData data;
		data.parse(*cpData);
		LOG(INFO) << "CPData " << data;
		// Transfer out the RPDU -> TPDU -> delivery.
		ref = data.RPDU().reference();
		// This handler invokes higher-layer parsers, too.
		success = handleRPDU(transaction,data.RPDU());
	}
	catch (SMSReadError) {
		LOG(WARNING) << "SMS parsing failed (above L3)";
		// Cause 95, "semantically incorrect message".
		LCH->send(CPData(L3TI,RPError(95,ref)),3);
		throw UnexpectedMessage(transaction->ID());
	}
	catch (GSM::L3ReadError) {
		LOG(WARNING) << "SMS parsing failed (in L3)";
		throw UnsupportedMessage(transaction->ID());
	}

	// Step 3
	// Send CP-DATA containing RP-ACK and message reference.
	if (success) {
		LOG(INFO) << "sending RPAck in CPData";
		LCH->send(CPData(L3TI,RPAck(ref)),3);
	} else {
		LOG(INFO) << "sending RPError in CPData";
		// Cause 127 is "internetworking error, unspecified".
		// See GSM 04.11 Table 8.4.
		LCH->send(CPData(L3TI,RPError(127,ref)),3);
	}

	// Step 4
	// Get CP-ACK from the MS.
	GSM::L3Frame* CM = getFrameSMS(LCH);
	if (CM->MTI()!=CPMessage::ACK) {
		LOG(NOTICE) << "unexpected SMS CP message with MTI=" << CM->MTI() << " " << *CM;
		throw UnexpectedMessage(transaction->ID());
	}
	LOG(DEBUG) << "ack from MS: " << *CM;
	CPAck ack;
	ack.parse(*CM);
	LOG(INFO) << "CPAck " << ack;

	/* I had a hell of a time testing this with my B100
	   I know it went off, that's all. If things fail, look
	   here -kurtis */

	/* MOSMS RLLP request */
	if (gConfig.defines("Control.SMS.QueryRRLP")) {
		// Query for RRLP
		if (!sendRRLP(transaction->subscriber(), LCH)) {
			LOG(INFO) << "RRLP request failed";
		}
	}

	gTransactionTable.remove(transaction);
}
bool Control::deliverSMSToMS(const char *callingPartyDigits, const char* message, const char* contentType, unsigned L3TI, GSM::LogicalChannel *LCH)
{

	if (!LCH->multiframeMode(3)) {
		// Start ABM in SAP3.
		LCH->send(GSM::ESTABLISH,3);
		// Wait for SAP3 ABM to connect.
		// The next read on SAP3 should the ESTABLISH primitive.
		// This won't return NULL.  It will throw an exception if it fails.
		delete getFrameSMS(LCH,GSM::ESTABLISH);
	}

#if 0
	// HACK -- Check for "Easter Eggs"
	// TL-PID
	unsigned TLPID=0;
	if (strncmp(message,"#!TLPID",7)==0) sscanf(message,"#!TLPID%d",&TLPID);

	// Step 1
	// Send the first message.
	// CP-DATA, containing RP-DATA.
	unsigned reference = random() % 255;
	CPData deliver(L3TI,
		RPData(reference,
			RPAddress(gConfig.getStr("SMS.FakeSrcSMSC").c_str()),
			TLDeliver(callingPartyDigits,message,TLPID)));
#else
	// TODO: Read MIME Type from smqueue!!
	unsigned reference = random() % 255;
	RPData rp_data;

	if (strncmp(contentType,"text/plain",10)==0) {
		rp_data = RPData(reference,
			RPAddress(gConfig.getStr("SMS.FakeSrcSMSC").c_str()),
			TLDeliver(callingPartyDigits,message,0));
	} else if (strncmp(contentType,"application/vnd.3gpp.sms",24)==0) {
		BitVector RPDUbits(strlen(message)*4);
		if (!RPDUbits.unhex(message)) {
			LOG(WARNING) << "Hex string parsing failed (in incoming SIP MESSAGE)";
			throw UnexpectedMessage();
		}

		try {
			RLFrame RPDU(RPDUbits);
			LOG(DEBUG) << "SMS RPDU: " << RPDU;

			rp_data.parse(RPDU);
			LOG(DEBUG) << "SMS RP-DATA " << rp_data;
		}
		catch (SMSReadError) {
			LOG(WARNING) << "SMS parsing failed (above L3)";
			// Cause 95, "semantically incorrect message".
			LCH->send(CPData(L3TI,RPError(95,reference)),3);
			throw UnexpectedMessage();
		}
		catch (GSM::L3ReadError) {
			LOG(WARNING) << "SMS parsing failed (in L3)";
			// TODO:: send error back to the phone
			throw UnsupportedMessage();
		}
	} else {
		LOG(WARNING) << "Unsupported content type (in incoming SIP MESSAGE) -- type: " << contentType;
		throw UnexpectedMessage();
	}

	CPData deliver(L3TI,rp_data);

#endif

	// Start ABM in SAP3.
	//LCH->send(GSM::ESTABLISH,3);
	// Wait for SAP3 ABM to connect.
	// The next read on SAP3 should the ESTABLISH primitive.
	// This won't return NULL.  It will throw an exception if it fails.
	//delete getFrameSMS(LCH,GSM::ESTABLISH);

	LOG(INFO) << "sending " << deliver;
	LCH->send(deliver,3);

	// Step 2
	// Get the CP-ACK.
	// FIXME -- Check TI.
	LOG(DEBUG) << "MTSMS: waiting for CP-ACK";
	GSM::L3Frame *CM = getFrameSMS(LCH);
	LOG(DEBUG) << "MTSMS: ack from MS " << *CM;
	if (CM->MTI()!=CPMessage::ACK) {
		LOG(WARNING) << "MS rejected our RP-DATA with CP message with TI=" << CM->MTI();
		delete CM;
		throw UnexpectedMessage();
	}
	delete CM;

	// Step 3
	// Get CP-DATA containing RP-ACK and message reference.
	LOG(DEBUG) << "MTSMS: waiting for RP-ACK";
	CM = getFrameSMS(LCH);
	LOG(DEBUG) << "MTSMS: data from MS " << *CM;
	if (CM->MTI()!=CPMessage::DATA) {
		LOG(NOTICE) << "Unexpected SMS CP message with TI=" << CM->MTI();
		delete CM;
		throw UnexpectedMessage();
	}
	

	// FIXME -- Check L3 TI.

	// Parse to check for RP-ACK.
	CPData data;
	try {
		data.parse(*CM);
		LOG(DEBUG) << "CPData " << data;
	}
	catch (SMSReadError) {
		LOG(WARNING) << "SMS parsing failed (above L3)";
		// Cause 95, "semantically incorrect message".
		LCH->send(CPError(L3TI,95),3);
		throw UnexpectedMessage();
	}
	catch (GSM::L3ReadError) {
		LOG(WARNING) << "SMS parsing failed (in L3)";
		throw UnsupportedMessage();
	}
	delete CM;

	// FIXME -- Check SMS reference.

	bool success = true;
	if (data.RPDU().MTI()!=RPMessage::Ack) {
		LOG(WARNING) << "unexpected RPDU " << data.RPDU();
		success = false;
	}

	// Step 4
	// Send CP-ACK to the MS.
	LOG(INFO) << "MTSMS: sending CPAck";
	LCH->send(CPAck(L3TI),3);
	return success;
}
void Control::MOSMSController(const GSM::L3CMServiceRequest *req, GSM::LogicalChannel *LCH)
{
	assert(req);
	assert(req->serviceType().type() == GSM::L3CMServiceType::ShortMessage);
	assert(LCH);
	assert(LCH->type() != GSM::SACCHType);

	LOG(INFO) << "MOSMS, req " << *req;

	// If we got a TMSI, find the IMSI.
	// Note that this is a copy, not a reference.
	GSM::L3MobileIdentity mobileID = req->mobileID();
	resolveIMSI(mobileID,LCH);

	// Create a transaction record.
	TransactionEntry *transaction = new TransactionEntry(gConfig.getStr("SIP.Proxy.SMS").c_str(),mobileID,LCH);
	gTransactionTable.add(transaction);
	LOG(DEBUG) << "MOSMS: transaction: " << *transaction;

	// See GSM 04.11 Arrow Diagram A5 for the transaction
	// Step 1	MS->Network	CP-DATA containing RP-DATA
	// Step 2	Network->MS	CP-ACK
	// Step 3	Network->MS	CP-DATA containing RP-ACK
	// Step 4	MS->Network	CP-ACK

	// LAPDm operation, from GSM 04.11, Annex F:
	// """
	// Case A: Mobile originating short message transfer, no parallel call:
	// The mobile station side will initiate SAPI 3 establishment by a SABM command
	// on the DCCH after the cipher mode has been set. If no hand over occurs, the
	// SAPI 3 link will stay up until the last CP-ACK is received by the MSC, and
	// the clearing procedure is invoked.
	// """

	// FIXME: check provisioning

	if (gConfig.getNum("GSM.Authentication")||gConfig.getNum("GSM.Encryption")) {
		AuthenticationParameters authParams(mobileID);
		registerIMSI(authParams, LCH);
		authenticate(authParams, LCH);
	}

	// Let the phone know we're going ahead with the transaction.
	if (LCH->isDecrypting()) {
		LOG(INFO) << "Decryption ACTIVE for:" << mobileID << " CMServiceAccept NOT sent, because CipherModeCommand implies it.";
	}
	else {
		LOG(INFO) << "Decryption NOT active for: " << mobileID << " Sending CMServiceAccept";
		LCH->send(GSM::L3CMServiceAccept());
	}


	// Wait for SAP3 to connect.
	// The first read on SAP3 is the ESTABLISH primitive.
	delete getFrameSMS(LCH,GSM::ESTABLISH);

	// Step 1
	// Now get the first message.
	// Should be CP-DATA, containing RP-DATA.
	GSM::L3Frame *CM = getFrameSMS(LCH);
	LOG(DEBUG) << "data from MS " << *CM;
	if (CM->MTI()!=CPMessage::DATA) {
		LOG(NOTICE) << "unexpected SMS CP message with TI=" << CM->MTI();
		delete CM;
		throw UnexpectedMessage();
	}
	unsigned L3TI = CM->TI() | 0x08;
	transaction->L3TI(L3TI);

	// Step 2
	// Respond with CP-ACK.
	// This just means that we got the message.
	LOG(INFO) << "sending CPAck";
	LCH->send(CPAck(L3TI),3);

	// Parse the message in CM and process RP part.
	// This is where we actually parse the message and send it out.
	// FIXME -- We need to set the message ref correctly,
	// even if the parsing fails.
	// The compiler gives a warning here.  Let it.  It will remind someone to fix it.
	unsigned ref;
	bool success = false;
	try {
		CPData data;
		data.parse(*CM);
		LOG(INFO) << "CPData " << data;
		// Transfer out the RPDU -> TPDU -> delivery.
		ref = data.RPDU().reference();
		// This handler invokes higher-layer parsers, too.
		success = handleRPDU(transaction,data.RPDU());
	}
	catch (SMSReadError) {
		LOG(WARNING) << "SMS parsing failed (above L3)";
		// Cause 95, "semantically incorrect message".
		LCH->send(CPData(L3TI,RPError(95,ref)),3);
		delete CM;
		throw UnexpectedMessage();
	}
	catch (GSM::L3ReadError) {
		LOG(WARNING) << "SMS parsing failed (in L3)";
		delete CM;
		throw UnsupportedMessage();
	}
	delete CM;

	// Step 3
	// Send CP-DATA containing RP-ACK and message reference.
	if (success) {
		LOG(INFO) << "sending RPAck in CPData";
		LCH->send(CPData(L3TI,RPAck(ref)),3);
	} else {
		LOG(INFO) << "sending RPError in CPData";
		// Cause 127 is "internetworking error, unspecified".
		// See GSM 04.11 Table 8.4.
		LCH->send(CPData(L3TI,RPError(127,ref)),3);
	}

	// Step 4
	// Get CP-ACK from the MS.
	CM = getFrameSMS(LCH);
	if (CM->MTI()!=CPMessage::ACK) {
		LOG(NOTICE) << "unexpected SMS CP message with TI=" << CM->MTI();
		throw UnexpectedMessage();
	}
	LOG(DEBUG) << "ack from MS: " << *CM;
	CPAck ack;
	ack.parse(*CM);
	delete CM;
	LOG(INFO) << "CPAck " << ack;

	/* MOSMS RLLP request */
	if (gConfig.defines("Control.SMS.QueryRRLP")) {
		// Query for RRLP
		if (!sendRRLP(mobileID, LCH)) {
			LOG(INFO) << "RRLP request failed";
		}
	}

	// Done.
	LCH->send(GSM::L3ChannelRelease());
	gTransactionTable.remove(transaction);
	LOG(INFO) << "closing the Um channel";
}
Example #7
0
void Control::InCallMOSMSController(const CPData *cpData, TransactionEntry* transaction, UMTS::DCCHLogicalChannel *LCH)
{
	LOG(INFO) << *cpData;

	// FIXME -- We know this will be broken in UMTS.

	// Step 1 already happened in the SACCH service loop.
	// Just get the L3 TI and set the high bit since it originated in the MS.
	unsigned L3TI = cpData->TI() | 0x08;
	transaction->L3TI(L3TI);

	// Step 2
	// Respond with CP-ACK.
	// This just means that we got the message.
	LOG(INFO) << "sending CPAck";
	LCH->send(CPAck(L3TI),3);

	// Parse the message in CM and process RP part.
	// This is where we actually parse the message and send it out.
	// FIXME -- We need to set the message ref correctly,
	// even if the parsing fails.
	// The compiler gives a warning here.  Let it.  It will remind someone to fix it.
	unsigned ref;
	bool success = false;
	try {
		CPData data;
		data.parse(*cpData);
		LOG(INFO) << "CPData " << data;
		// Transfer out the RPDU -> TPDU -> delivery.
		ref = data.RPDU().reference();
		// This handler invokes higher-layer parsers, too.
		success = handleRPDU(transaction,data.RPDU());
	}
	catch (SMSReadError) {
		LOG(WARNING) << "SMS parsing failed (above L3)";
		// Cause 95, "semantically incorrect message".
		LCH->send(CPData(L3TI,RPError(95,ref)),3);
		throw UnexpectedMessage(transaction->ID());
	}
	catch (GSM::L3ReadError) {
		LOG(WARNING) << "SMS parsing failed (in L3)";
		throw UnsupportedMessage(transaction->ID());
	}

	// Step 3
	// Send CP-DATA containing RP-ACK and message reference.
	if (success) {
		LOG(INFO) << "sending RPAck in CPData";
		LCH->send(CPData(L3TI,RPAck(ref)),3);
	} else {
		LOG(INFO) << "sending RPError in CPData";
		// Cause 127 is "internetworking error, unspecified".
		// See GSM 04.11 Table 8.4.
		LCH->send(CPData(L3TI,RPError(127,ref)),3);
	}

	// Step 4
	// Get CP-ACK from the MS.
	GSM::L3Frame* CM = getFrameSMS(LCH);
	if (CM->MTI()!=CPMessage::ACK) {
		LOG(NOTICE) << "unexpected SMS CP message with MTI=" << CM->MTI() << " " << *CM;
		throw UnexpectedMessage(transaction->ID());
	}
	LOG(DEBUG) << "ack from MS: " << *CM;
	CPAck ack;
	ack.parse(*CM);
	LOG(INFO) << "CPAck " << ack;

	gTransactionTable.remove(transaction);
}