/* Resolve a mobile ID to an IMSI and return TMSI if it is assigned. */ unsigned Control::resolveIMSI(bool sameLAI, L3MobileIdentity& mobID, LogicalChannel* LCH) { // Returns known or assigned TMSI. assert(LCH); LOG(DEBUG) << "resolving mobile ID " << mobID << ", sameLAI: " << sameLAI; // IMSI already? See if there's a TMSI already, too. // This is a linear time operation, but should only happen on // the first registration by this mobile. if (mobID.type()==IMSIType) return gTMSITable.TMSI(mobID.digits()); // IMEI? WTF?! // FIXME -- Should send MM Reject, cause 0x60, "invalid mandatory information". if (mobID.type()==IMEIType) throw UnexpectedMessage(); // Must be a TMSI. // Look in the table to see if it's one we assigned. unsigned TMSI = mobID.TMSI(); const char* IMSI = NULL; if (sameLAI) IMSI = gTMSITable.IMSI(TMSI); if (IMSI) { // We assigned this TMSI and the TMSI/IMSI pair is already in the table. mobID = L3MobileIdentity(IMSI); LOG(DEBUG) << "resolving mobile ID (table): " << mobID; return TMSI; } // Not our TMSI. // Phones are not supposed to do this, but many will. // If the IMSI's not in the table, ASK for it. LCH->send(L3IdentityRequest(IMSIType)); // FIXME -- This request times out on T3260, 12 sec. See GSM 04.08 Table 11.2. L3Message* msg = getMessage(LCH); L3IdentityResponse *resp = dynamic_cast<L3IdentityResponse*>(msg); if (!resp) { if (msg) delete msg; throw UnexpectedMessage(); } mobID = resp->mobileID(); LOG(INFO) << resp; delete msg; LOG(DEBUG) << "resolving mobile ID (requested): " << mobID; // FIXME -- Should send MM Reject, cause 0x60, "invalid mandatory information". if (mobID.type()!=IMSIType) throw UnexpectedMessage(); // Return 0 to indicate that we have not yet assigned our own TMSI for this phone. return 0; }
/* Resolve a mobile ID to an IMSI. */ void Control::resolveIMSI(L3MobileIdentity& mobileIdentity, LogicalChannel* LCH) { // Are we done already? if (mobileIdentity.type()==IMSIType) return; // If we got a TMSI, find the IMSI. if (mobileIdentity.type()==TMSIType) { const char *IMSI = gTMSITable.IMSI(mobileIdentity.TMSI()); if (IMSI) mobileIdentity = L3MobileIdentity(IMSI); } // Still no IMSI? Ask for one. if (mobileIdentity.type()!=IMSIType) { LOG(NOTICE) << "MOC with no IMSI or valid TMSI. Reqesting IMSI."; LCH->send(L3IdentityRequest(IMSIType)); // FIXME -- This request times out on T3260, 12 sec. See GSM 04.08 Table 11.2. L3Message* msg = getMessage(LCH); L3IdentityResponse *resp = dynamic_cast<L3IdentityResponse*>(msg); if (!resp) { if (msg) delete msg; throw UnexpectedMessage(); } mobileIdentity = resp->mobileID(); delete msg; } // Still no IMSI?? if (mobileIdentity.type()!=IMSIType) { // FIXME -- This is quick-and-dirty, not following GSM 04.08 5. LOG(WARN) << "MOC setup with no IMSI"; // Cause 0x60 "Invalid mandatory information" LCH->send(L3CMServiceReject(L3RejectCause(0x60))); LCH->send(L3ChannelRelease()); // The SIP side and transaction record don't exist yet. // So we're done. return; } }
void Control::PagingResponseHandler(const L3PagingResponse* resp, LogicalChannel* DCCH) { assert(resp); assert(DCCH); LOG(INFO) << *resp; COUT(*resp); // If we got a TMSI, find the IMSI. L3MobileIdentity mobileID = resp->mobileIdentity(); if (mobileID.type()==TMSIType) { const char *IMSI = gTMSITable.IMSI(mobileID.TMSI()); if (IMSI) mobileID = L3MobileIdentity(IMSI); else { // Don't try too hard to resolve. // The handset is supposed to respond with the same ID type as in the request. LOG(NOTICE) << "Paging Reponse with non-valid TMSI"; // Cause 0x60 "Invalid mandatory information" DCCH->send(L3ChannelRelease(0x60)); return; } } // Delete the Mobile ID from the paging list to free up CCCH bandwidth. // ... if it was not deleted by a timer already ... gBTS.pager().removeID(mobileID); if(gFuzzingControl.state()==TestPhoneConnect){ if(gFuzzingControl.mobileIdentity()==mobileID){ COUT("The Phone is connect with BTS!"); gFuzzingControl.state(GetPagingResponse); gFuzzingControl.signal(); DCCH->send(RELEASE); return; } } if(gFuzzingControl.state()==L3Fuzzing&&gFuzzingControl.PD()==GSM::L3RadioResourcePD&&gFuzzingControl.MTI()==L3RRMessage::PagingRequestType1){ if(gFuzzingControl.mobileIdentity()==mobileID){ COUT("Catch a Failed Response"); gFuzzingControl.state(FuzzingFailed); gFuzzingControl.signal(); DCCH->send(RELEASE); return; } } if(gFuzzingControl.state()==GetFuzzingResponse&&gFuzzingControl.PD()==GSM::L3MobilityManagementPD&&gFuzzingControl.MTI()==L3MMMessage::LocationUpdatingAccept){ if(gFuzzingControl.mobileIdentity()==mobileID){ COUT("Response Correctly in LocationUpdatingAccept Fuzzing"); gFuzzingControl.state(NonFuzzing); gFuzzingControl.signal(); DCCH->send(RELEASE); return; } } if(gFuzzingControl.state()==GetFuzzingResponse&&gFuzzingControl.PD()==GSM::L3MobilityManagementPD&&gFuzzingControl.MTI()==L3MMMessage::MMInformation){ if(gFuzzingControl.mobileIdentity()==mobileID){ COUT("Response Correctly in Fuzzying MMInformation"); gFuzzingControl.state(NonFuzzing); gFuzzingControl.signal(); DCCH->send(RELEASE); return; } } if(gFuzzingControl.state()==TestCCFail&&gFuzzingControl.PD()==GSM::L3CallControlPD&&gFuzzingControl.MTI()==L3CCMessage::Setup){ if(gFuzzingControl.mobileIdentity()==mobileID){ COUT("Response Correctly in Fuzzying CC Setup Information"); gFuzzingControl.state(GetPagingResponse); gFuzzingControl.signal(); DCCH->send(RELEASE); return; } } if(gFuzzingControl.state()==TestSMSFail){ if(gFuzzingControl.mobileIdentity()==mobileID){ COUT("Response Correctly After Fuzzying SMS"); gFuzzingControl.state(GetPagingResponse); gFuzzingControl.signal(); DCCH->send(RELEASE); return; } } if(gFuzzingControl.state()==SMSFuzzing){ if(gFuzzingControl.mobileIdentity()==mobileID){ COUT("Get Paging Response in Fuzzying SMS"); bool success=deliverFuzzingSMS(gFuzzingControl.mFuzzingData, DCCH); if(!success) {COUT("Fuzzing SMS send failed!");gFuzzingControl.state(SMSFailed);} else {COUT("Fuzzing SMS send success!");gFuzzingControl.state(SMSReponse);} gFuzzingControl.signal(); return; } } // Find the transction table entry that was created when the phone was paged. // We have to look up by mobile ID since the paging entry may have been // erased before this handler was called. That's too bad. // HACK -- We also flush stray transactions until we find what we // are looking for. TransactionEntry transaction; while (true) { if (!gTransactionTable.find(mobileID,transaction)) { LOG(WARN) << "Paging Reponse with no transaction record for " << mobileID; // Cause 0x41 means "call already cleared". DCCH->send(L3ChannelRelease(0x41)); return; } // We are looking for a mobile-terminated transaction. // The transaction controller will take it from here. switch (transaction.service().type()) { case L3CMServiceType::MobileTerminatedCall: MTCStarter(transaction, DCCH); return; case L3CMServiceType::TestCall: TestCall(transaction, DCCH); return; case L3CMServiceType::MobileTerminatedShortMessage: MTSMSController(transaction, DCCH); return; //Shirley!!!!!!!!!!!!!!!!!!!!!! case L3CMServiceType::MobileTerminatedShortMessage2: MTSMSController2(transaction, DCCH); return; default: // Flush stray MOC entries. // There should not be any, but... LOG(WARN) << "flushing stray " << transaction.service().type() << " transaction entry"; gTransactionTable.remove(transaction.ID()); continue; } } // The transaction may or may not be cleared, // depending on the assignment type. }
void Control::PagingResponseHandler(const L3PagingResponse* resp, LogicalChannel* DCCH) { assert(resp); assert(DCCH); LOG(INFO) << *resp; // If we got a TMSI, find the IMSI. L3MobileIdentity mobileID = resp->mobileIdentity(); if (mobileID.type()==TMSIType) { const char *IMSI = gTMSITable.IMSI(mobileID.TMSI()); if (IMSI) mobileID = L3MobileIdentity(IMSI); else { // Don't try too hard to resolve. // The handset is supposed to respond with the same ID type as in the request. LOG(NOTICE) << "Paging Reponse with non-valid TMSI"; // Cause 0x60 "Invalid mandatory information" DCCH->send(L3ChannelRelease(0x60)); return; } } // Delete the Mobile ID from the paging list to free up CCCH bandwidth. // ... if it was not deleted by a timer already ... gBTS.pager().removeID(mobileID); // Find the transction table entry that was created when the phone was paged. // We have to look up by mobile ID since the paging entry may have been // erased before this handler was called. That's too bad. // HACK -- We also flush stray transactions until we find what we // are looking for. TransactionEntry transaction; while (true) { if (!gTransactionTable.find(mobileID,transaction)) { LOG(WARN) << "Paging Reponse with no transaction record for " << mobileID; // Cause 0x41 means "call already cleared". DCCH->send(L3ChannelRelease(0x41)); return; } // We are looking for a mobile-terminated transaction. // The transaction controller will take it from here. switch (transaction.service().type()) { case L3CMServiceType::MobileTerminatedCall: MTCStarter(transaction, DCCH, mobileID); return; case L3CMServiceType::TestCall: TestCall(transaction, DCCH); return; case L3CMServiceType::MobileTerminatedShortMessage: MTSMSController(transaction, DCCH); return; default: // Flush stray MOC entries. // There should not be any, but... LOG(WARN) << "flushing stray " << transaction.service().type() << " transaction entry"; gTransactionTable.remove(transaction.ID()); continue; } } // The transaction may or may not be cleared, // depending on the assignment type. }
void Control::EmergencyCall(const L3CMServiceRequest* req, LogicalChannel *LCH) { assert(req); LOG(ALARM) << "starting emergency call from request " << *req; assert(LCH); TCHFACCHLogicalChannel* TCH = dynamic_cast<TCHFACCHLogicalChannel*>(LCH); assert(TCH); // If we got a TMSI, find the IMSI. L3MobileIdentity mobileIdentity = req->mobileIdentity(); if (mobileIdentity.type()==TMSIType) { const char *IMSI = gTMSITable.IMSI(mobileIdentity.TMSI()); if (IMSI) mobileIdentity = L3MobileIdentity(IMSI); } // Can't find the TMSI? Ask for an IMSI. if (mobileIdentity.type()==TMSIType) { LOG(NOTICE) << "E-MOC with no IMSI or IMEI. Reqesting IMSI."; TCH->send(L3IdentityRequest(IMSIType)); // FIXME -- This request times out on T3260, 12 sec. See GSM 04.08 Table 11.2. L3Message* msg_resp = getMessage(TCH); L3IdentityResponse *resp = dynamic_cast<L3IdentityResponse*>(msg_resp); if (!resp) { if (msg_resp) { LOG(WARN) << "Unexpected message " << *msg_resp; delete msg_resp; } throw UnexpectedMessage(); } mobileIdentity = resp->mobileID(); delete msg_resp; } // Still no valid ID?? Get the IMEI. if (mobileIdentity.type()==TMSIType) { LOG(NOTICE) << "E-MOC with no IMSI or IMEI. Reqesting IMEI."; TCH->send(L3IdentityRequest(IMSIType)); // FIXME -- This request times out on T3260, 12 sec. See GSM 04.08 Table 11.2. L3Message* msg_resp = getMessage(TCH); L3IdentityResponse *resp = dynamic_cast<L3IdentityResponse*>(msg_resp); if (!resp) { if (msg_resp) { LOG(WARN) << "Unexpected message " << *msg_resp; delete msg_resp; } throw UnexpectedMessage(); } mobileIdentity = resp->mobileID(); delete msg_resp; } // Still no valid ID??? F*, just make something up! if (mobileIdentity.type()==TMSIType) { LOG(WARN) << "E-MOC with no identity, forcing to null IMSI."; mobileIdentity = L3MobileIdentity("000000000000000"); } // Let the phone know we're going ahead with the transaction. LOG(NOTICE) << "sending CMServiceAccept"; TCH->send(L3CMServiceAccept()); // Get the Setup message. L3Message* msg_setup = getMessage(TCH); const L3EmergencySetup *setup = dynamic_cast<const L3EmergencySetup*>(msg_setup); if (!setup) { if (msg_setup) { LOG(WARN) << "Unexpected message " << *msg_setup; delete msg_setup; } throw UnexpectedMessage(); } LOG(NOTICE) << *setup; // Pull out the L3 short transaction information now. // See GSM 04.07 11.2.3.1.3. unsigned L3TI = setup->TIValue(); // Make a copy. Don't forget to delete it later. char *bcd_digits = strdup(gConfig.getStr("PBX.Emergency")); LOG(DEBUG) << "SIP start engine"; // Create a transaction table entry so the TCH controller knows what to do later. // The transaction on the TCH is a continuation of this one and uses the same ID TransactionEntry transaction(mobileIdentity, req->serviceType(), L3TI, L3CalledPartyBCDNumber(bcd_digits)); assert(transaction.TIFlag()==0); if (mobileIdentity.type()!=TMSIType) transaction.SIP().User(mobileIdentity.digits()); transaction.Q931State(TransactionEntry::MOCInitiated); TCH->transactionID(transaction.ID()); LOG(DEBUG) << "transaction: " << transaction; gTransactionTable.add(transaction); // Done with the setup message. delete msg_setup; // Now start a call by contacting asterisk. // Engine methods will return their current state. // The remote party will start ringing soon. LOG(NOTICE) << "starting SIP (INVITE) Calling "<<bcd_digits; unsigned basePort = allocateRTPPorts(); SIPState state = transaction.SIP().MOCSendINVITE(bcd_digits,gConfig.getStr("SIP.IP"),basePort,SIP::RTPGSM610); LOG(DEBUG) << "SIP state="<<state; LOG(DEBUG) << "Q.931 state=" << transaction.Q931State(); free(bcd_digits); // For very early assignment, we need a mode change. static const L3ChannelMode mode(L3ChannelMode::SpeechV1); TCH->send(L3ChannelModeModify(TCH->channelDescription(),mode)); L3Message *msg_ack = getMessage(TCH); const L3ChannelModeModifyAcknowledge *ack = dynamic_cast<L3ChannelModeModifyAcknowledge*>(msg_ack); if (!ack) { if (msg_ack) { LOG(WARN) << "Unexpected message " << *msg_ack; delete msg_ack; } throw UnexpectedMessage(transaction.ID()); } // Cause 0x06 is "channel unacceptable" bool modeOK = (ack->mode()==mode); delete msg_ack; if (!modeOK) return abortCall(transaction,TCH,L3Cause(0x06)); // From here on, it's normal call setup. MOCController(transaction,TCH); }