void Control::MTCStarter(TransactionEntry& transaction, LogicalChannel *LCH) { assert(LCH); LOG(INFO) << "MTC on " << LCH->type() << " transaction: "<< transaction; // Determine if very early assigment already happened. bool veryEarly = false; if (LCH->type()==FACCHType) veryEarly=true; // Allocate a TCH for the call. TCHFACCHLogicalChannel *TCH = NULL; if (!veryEarly) { TCH = allocateTCH(dynamic_cast<SDCCHLogicalChannel*>(LCH)); // It's OK to just return on failure; allocateTCH cleaned up already. // The orphaned transaction will be cleared automatically later. if (TCH==NULL) return; } // Get transaction identifiers. // This transaction was created by the SIPInterface when it // processed the INVITE that started this call. if (!veryEarly) TCH->transactionID(transaction.ID()); LCH->transactionID(transaction.ID()); unsigned L3TI = transaction.TIValue(); assert(transaction.TIFlag()==1); // GSM 04.08 5.2.2.1 LOG(INFO) << "sending GSM Setup to call " << transaction.calling(); LCH->send(L3Setup(0,L3TI,L3CallingPartyBCDNumber(transaction.calling()))); transaction.T303().set(); transaction.Q931State(TransactionEntry::CallPresent); gTransactionTable.update(transaction); // Wait for Call Confirmed message. LOG(DEBUG) << "wait for GSM Call Confirmed"; while (transaction.Q931State()!=TransactionEntry::MTCConfirmed) { if (transaction.SIP().MTCSendTrying()==SIP::Fail) { LOG(NOTICE) << "call failed on SIP side"; LCH->send(RELEASE); // Cause 0x03 is "no route to destination" return abortCall(transaction,LCH,L3Cause(0x03)); } // FIXME -- What's the proper timeout here? // It's the SIP TRYING timeout, whatever that is. if (updateGSMSignalling(transaction,LCH,1000)) { LOG(INFO) << "Release from GSM side"; LCH->send(RELEASE); return; } // Check for SIP cancel, too. if (transaction.SIP().MTCWaitForACK()==SIP::Fail) { LOG(NOTICE) << "call failed on SIP side"; LCH->send(RELEASE); // Cause 0x10 is "normal clearing" return abortCall(transaction,LCH,L3Cause(0x10)); } } // The transaction is moving to the MTCController. // Once this update happens, don't change the transaction object again in this function. gTransactionTable.update(transaction); LOG(DEBUG) << "transaction: " << transaction; if (veryEarly) { // For very early assignment, we need a mode change. static const L3ChannelMode mode(L3ChannelMode::SpeechV1); LCH->send(L3ChannelModeModify(LCH->channelDescription(),mode)); L3Message* msg_ack = getMessage(LCH); 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,LCH,L3Cause(0x06)); MTCController(transaction,dynamic_cast<TCHFACCHLogicalChannel*>(LCH)); } else { // For late assignment, send the TCH assignment now. // This dispatcher on the next channel will continue the transaction. assignTCHF(transaction,dynamic_cast<SDCCHLogicalChannel*>(LCH),TCH); } }
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); }
/** This function starts MOC on the SDCCH to the point of TCH assignment. @param req The CM Service Request that started all of this. @param LCH The logical used to initiate call setup. */ void Control::MOCStarter(const L3CMServiceRequest* req, LogicalChannel *LCH) { assert(LCH); assert(req); LOG(INFO) << *req; // Determine if very early assignment already happened. bool veryEarly = (LCH->type()==FACCHType); // If we got a TMSI, find the IMSI. // Note that this is a copy, not a reference. L3MobileIdentity mobileIdentity = req->mobileIdentity(); resolveIMSI(mobileIdentity,LCH); // FIXME -- At this point, verify the that subscriber has access to this service. // If the subscriber isn't authorized, send a CM Service Reject with // cause code, 0x41, "requested service option not subscribed", // followed by a Channel Release with cause code 0x6f, "unspecified". // Otherwise, proceed to the next section of code. // For now, we are assuming that the phone won't make a call if it didn't // get registered. // Allocate a TCH for the call, if we don't have it already. TCHFACCHLogicalChannel *TCH = NULL; if (!veryEarly) { TCH = allocateTCH(dynamic_cast<SDCCHLogicalChannel*>(LCH)); // It's OK to just return on failure; allocateTCH cleaned up already, // and the SIP side and transaction record don't exist yet. if (TCH==NULL) return; } // Let the phone know we're going ahead with the transaction. LOG(DEBUG) << "sending CMServiceAccept"; LCH->send(L3CMServiceAccept()); // Get the Setup message. // GSM 04.08 5.2.1.2 const L3Setup *setup = NULL; L3Message* msg_setup = NULL; while (msg_setup = getMessage(LCH)) { setup = dynamic_cast<const L3Setup*>(msg_setup); if (!setup) { L3GPRSSuspensionRequest *r = dynamic_cast<L3GPRSSuspensionRequest*>(msg_setup); if (!r) { if (msg_setup) { LOG(WARN) << "Unexpected message " << *msg_setup; delete msg_setup; } throw UnexpectedMessage(); } else { LOG(INFO) << "Ignored L3 RR GPRS Suspension Request."; if (msg_setup) delete msg_setup; continue; } } break; } LOG(INFO) << *setup; // Pull out the L3 short transaction information now. // See GSM 04.07 11.2.3.1.3. unsigned L3TI = setup->TIValue(); if (!setup->haveCalledPartyBCDNumber()) { // FIXME -- This is quick-and-dirty, not following GSM 04.08 5. LOG(WARN) << "MOC setup with no number"; // Cause 0x60 "Invalid mandatory information" LCH->send(L3ReleaseComplete(1,L3TI,L3Cause(0x60))); LCH->send(L3ChannelRelease()); // The SIP side and transaction record don't exist yet. // So we're done. delete msg_setup; return; } LOG(DEBUG) << "SIP start engine"; // Get the users sip_uri by pulling out the IMSI. const char *IMSI = mobileIdentity.digits(); // Pull out Number user is trying to call and use as the sip_uri. const char *bcd_digits = setup->calledPartyBCDNumber().digits(); // 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, setup->calledPartyBCDNumber()); assert(transaction.TIFlag()==0); transaction.SIP().User(IMSI); transaction.Q931State(TransactionEntry::MOCInitiated); LCH->transactionID(transaction.ID()); if (!veryEarly) TCH->transactionID(transaction.ID()); LOG(DEBUG) << "transaction: " << transaction; gTransactionTable.add(transaction); // At this point, we have enough information start the SIP call setup. // We also have a SIP side and a transaction that will need to be // cleaned up on abort or clearing. // Now start a call by contacting asterisk. // Engine methods will return their current state. // The remote party will start ringing soon. LOG(DEBUG) << "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(); // Once we can start SIP call setup, send Call Proceeding. LOG(DEBUG) << "Sending Call Proceeding"; LCH->send(L3CallProceeding(1,L3TI)); transaction.Q931State(TransactionEntry::MOCProceeding); gTransactionTable.update(transaction); // Finally done with the Setup message. delete msg_setup; // The transaction is moving on to the MOCController. // If we need a TCH assignment, we do it here. gTransactionTable.update(transaction); LOG(DEBUG) << "transaction: " << transaction; if (veryEarly) { // For very early assignment, we need a mode change. static const L3ChannelMode mode(L3ChannelMode::SpeechV1); LCH->send(L3ChannelModeModify(LCH->channelDescription(),mode)); L3Message *msg_ack = getMessage(LCH); const L3ChannelModeModifyAcknowledge *ack = dynamic_cast<L3ChannelModeModifyAcknowledge*>(msg_ack); if (!ack) { if (msg_ack) { LOG(WARN) << "Unexpected message " << *msg_ack; delete msg_setup; } throw UnexpectedMessage(transaction.ID()); } // Cause 0x06 is "channel unacceptable" bool modeOK = (ack->mode()==mode); delete msg_ack; if (!modeOK) return abortCall(transaction,LCH,L3Cause(0x06)); MOCController(transaction,dynamic_cast<TCHFACCHLogicalChannel*>(LCH)); } else { // For late assignment, send the TCH assignment now. // This dispatcher on the next channel will continue the transaction. assignTCHF(transaction,dynamic_cast<SDCCHLogicalChannel*>(LCH),TCH); } }