mirror of
https://github.com/RangeNetworks/openbts.git
synced 2025-10-23 07:42:01 +00:00
631 lines
23 KiB
C++
631 lines
23 KiB
C++
/*
|
|
* Copyright 2011, 2014 Range Networks, Inc.
|
|
*
|
|
* This software is distributed under multiple licenses;
|
|
* see the COPYING file in the main directory for licensing
|
|
* information for this specific distribution.
|
|
*
|
|
* This use of this software may be subject to additional restrictions.
|
|
* See the LEGAL file in the main directory for details.
|
|
|
|
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.
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <poll.h>
|
|
#include "LLC.h"
|
|
#define GGSN_IMPLEMENTATION 1
|
|
#include "SgsnBase.h"
|
|
#include "Sgsn.h"
|
|
#include "Ggsn.h"
|
|
#include "miniggsn.h"
|
|
#include <GSMConfig.h>
|
|
//#include "MSInfo.h" // To dump MSInfo
|
|
#include "GPRSL3Messages.h"
|
|
#define CASENAME(x) case x: return #x;
|
|
|
|
// GSM04.08 (24.008 better) 9.5.1 describes Activate PDP Context Request Message.
|
|
// The incoming IP address is in pdp->eua.v
|
|
// GSM04.08 10.5.6.4 describes the Packed Data Protocol Address.
|
|
// If second byte (the length) is 2 and type is IP, DHCP assigns an IP address.
|
|
// We dont support anything else.
|
|
// We are just going to ignore the incoming address, and assign it one from our range.
|
|
// We are permitted to change IPv6 to use IPv4, but since we ignore it all, doesnt matter.
|
|
// GSM04.08 10.5.6.4 describes Packet Data Protocol Configuration Options.
|
|
// This is there solely to support optional PPP tunneling all the way from
|
|
// TE (Terminal Equipment) attached to the MS, through the SGSN and GGSN, and out
|
|
// to some internet endpoint elsewhere. Special support is needed both to establish
|
|
// the PPP connection and to service it, since it is its own protocol and packets
|
|
// for PPP connection types need to be passed through the GGSN.
|
|
// We will not support PPP now.
|
|
|
|
namespace SGSN {
|
|
Ggsn gGgsn;
|
|
|
|
static uint32_t mg_dns[2] = {0,0};
|
|
|
|
const char *SmCause::name(unsigned mt, bool ornull)
|
|
{
|
|
switch (mt) {
|
|
CASENAME(Operator_Determined_Barring)
|
|
CASENAME(MBMS_bearer_capabilities_insufficient_for_the_service)
|
|
CASENAME(LLC_or_SNDCP_failure)
|
|
CASENAME(Insufficient_resources)
|
|
CASENAME(Missing_or_unknown_APN)
|
|
CASENAME(Unknown_PDP_address_or_PDP_type)
|
|
CASENAME(User_authentication_failed)
|
|
CASENAME(Activation_rejected_by_GGSN_Serving_GW_or_PDN_GW)
|
|
CASENAME(Activation_rejected_unspecified)
|
|
CASENAME(Service_option_not_supported)
|
|
CASENAME(Requested_service_option_not_subscribed)
|
|
CASENAME(Service_option_temporarily_out_of_order)
|
|
CASENAME(NSAPI_already_used)
|
|
CASENAME(Regular_deactivation)
|
|
CASENAME(QoS_not_accepted)
|
|
CASENAME(Network_failure)
|
|
CASENAME(Reactivation_required)
|
|
CASENAME(Feature_not_supported)
|
|
CASENAME(Semantic_error_in_the_TFT_operation)
|
|
CASENAME(Syntactical_error_in_the_TFT_operation)
|
|
CASENAME(Unknown_PDP_context)
|
|
CASENAME(Semantic_errors_in_packet_filter)
|
|
CASENAME(Syntactical_errors_in_packet_filter)
|
|
CASENAME(PDP_context_without_TFT_already_activated)
|
|
CASENAME(Multicast_group_membership_timeout)
|
|
CASENAME(Activation_rejected_BCM_violation)
|
|
CASENAME(PDP_type_IPv4_only_allowed)
|
|
CASENAME(PDP_type_IPv6_only_allowed)
|
|
CASENAME(Single_address_bearers_only_allowed)
|
|
CASENAME(Collision_with_network_initiated_request)
|
|
CASENAME(Invalid_transaction_identifier_value)
|
|
CASENAME(Semantically_incorrect_message)
|
|
CASENAME(Invalid_mandatory_information)
|
|
CASENAME(Message_type_nonexistent_or_not_implemented)
|
|
CASENAME(Message_type_not_compatible_with_the_protocol_state)
|
|
CASENAME(Information_element_nonexistent_or_not_implemented)
|
|
CASENAME(Conditional_IE_error)
|
|
CASENAME(Message_not_compatible_with_the_protocol_state)
|
|
CASENAME(Protocol_error_unspecified)
|
|
CASENAME(APN_restriction_value_incompatible_with_active_PDP_context)
|
|
case 0:
|
|
return ornull ? 0 : "SmCause type 0";
|
|
default:
|
|
return ornull ? 0 : "SmCause type unrecognized";
|
|
}
|
|
}
|
|
|
|
static void sethighpri()
|
|
{
|
|
pthread_t me = pthread_self();
|
|
int policy; struct sched_param sp;
|
|
pthread_getschedparam(me,&policy,&sp);
|
|
SGSNLOG("service loop"<<LOGVAR(policy)<<LOGVAR2("priority",sp.sched_priority));
|
|
policy = SCHED_FIFO; // Gives this thread higher priority.
|
|
pthread_setschedparam(me,policy,&sp);
|
|
}
|
|
|
|
|
|
void *miniGgsnReadServiceLoop(void *arg)
|
|
{
|
|
Ggsn *ggsn = (Ggsn*)arg;
|
|
sethighpri();
|
|
while (ggsn->active() && !gBTS.btsShutdown()) {
|
|
struct pollfd fds[1];
|
|
fds[0].fd = tun_fd;
|
|
fds[0].events = POLLIN;
|
|
fds[0].revents = 0; // being cautious
|
|
// We time out occassionally to check if the user wants to shut the sgsn down.
|
|
if (-1 == poll(fds,1,ggsn->mStopTimeout)) {
|
|
SGSNERROR("ggsn: poll failure, errno="<<strerror(errno));
|
|
continue;
|
|
}
|
|
if (fds[0].revents & POLLIN) {
|
|
miniggsn_handle_read();
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void *miniGgsnWriteServiceLoop(void *arg)
|
|
{
|
|
sethighpri();
|
|
Ggsn *ggsn = (Ggsn*)arg;
|
|
while (ggsn->active() && !gBTS.btsShutdown()) {
|
|
// 8-6-2012 This interthreadqueue is clumping things up. Try taking out the timeout.
|
|
//PdpPdu *npdu = ggsn->mTxQ.read(ggsn->mStopTimeout);
|
|
PdpPdu *npdu = ggsn->mTxQ.read();
|
|
if (npdu) {
|
|
miniggsn_snd_npdu_by_mgc(npdu->mgp, npdu->mpdu.begin(), npdu->mpdu.size());
|
|
delete npdu;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void addShellRequest(const char *wCmd,GmmInfo*gmm,PdpContext *pdp)
|
|
{
|
|
ShellRequest *req = new ShellRequest();
|
|
req->msrCommand = wCmd;
|
|
req->msrArg1 = gmm->mImsi.hexstr();
|
|
if (pdp && pdp->mgp) {
|
|
char ipaddr[40], nsapi[10];
|
|
ip_ntoa(pdp->mgp->mg_ip, ipaddr);
|
|
req->msrArg2 = ipaddr;
|
|
sprintf(nsapi,"%d",pdp->mNSapi);
|
|
req->msrArg3 = nsapi;
|
|
}
|
|
gGgsn.mShellQ.write(req);
|
|
}
|
|
|
|
void addShellRequest(const char *wCmd,const char *arg1)
|
|
{
|
|
ShellRequest *req = new ShellRequest();
|
|
req->msrCommand = wCmd;
|
|
req->msrArg1 = arg1;
|
|
gGgsn.mShellQ.write(req);
|
|
}
|
|
|
|
// Lurk on the mShellQ and execute a shell script to process requests found.
|
|
void *miniGgsnShellServiceLoop(void *arg)
|
|
{
|
|
Ggsn *ggsn = (Ggsn*)arg;
|
|
std::string shname = gConfig.getStr(SQL_SHELLSCRIPT);
|
|
while (ggsn->active() && !gBTS.btsShutdown()) {
|
|
ShellRequest *req = ggsn->mShellQ.read(ggsn->mStopTimeout);
|
|
if (! req) continue;
|
|
// (pat added 3-2014) Dont try to exec a shell script that does not exist; prevents a warning.
|
|
if (0 != access(shname.c_str(),R_OK)) continue;
|
|
runcmd("/bin/sh","sh",shname.c_str(), req->msrCommand.c_str(), req->msrArg1.c_str(),
|
|
req->msrArg2.c_str(),req->msrArg3.c_str());
|
|
delete req;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Return true on success
|
|
bool Ggsn::start()
|
|
{
|
|
if (gGgsn.mActive) { return false; }
|
|
if (!miniggsn_init()) { return false; }
|
|
gGgsn.mGgsnRecvThread.start(miniGgsnReadServiceLoop,&gGgsn);
|
|
gGgsn.mGgsnSendThread.start(miniGgsnWriteServiceLoop,&gGgsn);
|
|
if (gConfig.getStr(SQL_SHELLSCRIPT).size() > 1) {
|
|
gGgsn.mGgsnShellThread.start(miniGgsnShellServiceLoop,&gGgsn);
|
|
gGgsn.mShellThreadActive = true;
|
|
}
|
|
gGgsn.mActive = true;
|
|
//time_t now; time(&now);
|
|
//char timebuf[30];
|
|
//ctime_r(&now,timebuf);
|
|
//addShellRequest("Start",timebuf);
|
|
addShellRequest("Start","");
|
|
return true;
|
|
}
|
|
|
|
void Ggsn::stop()
|
|
{
|
|
if (!gGgsn.mActive) {return;}
|
|
gGgsn.mGgsnRecvThread.join();
|
|
gGgsn.mGgsnSendThread.join();
|
|
if (gGgsn.mShellThreadActive) {
|
|
gGgsn.mGgsnShellThread.join();
|
|
gGgsn.mShellThreadActive = false;
|
|
}
|
|
gGgsn.mActive = false;
|
|
}
|
|
|
|
// Call this to update the PCO for retransmission to the MS.
|
|
// We cannot just make up a PCO ahead of time and then send it out to
|
|
// all the MS for two reasons:
|
|
// o The incoming options have uniquifying transaction identifiers that are
|
|
// different each time, so we must modify the incoming pcoReq each time while
|
|
// carefully preserving those modifiers.
|
|
// o There are two major formats for the PCO options - see below.
|
|
static void setPco(ByteVector &resultpco, ByteVector &pcoReq)
|
|
{
|
|
if (mg_dns[0] == 0) { ip_finddns(mg_dns); }
|
|
|
|
// GSM 24.008 10.5.6.3: Procotol Configuration Options.
|
|
// These are the "negotiated" address params wrapped in PPP,
|
|
// except they are not very negotiated; we are going to cram them
|
|
// into the MS and it can accept them or die.
|
|
// The first byte is the header, followed by any number of:
|
|
// protocol id (2 octets)
|
|
// length of contents (1 octet)
|
|
// I have seen this supplied by the MS in two different ways:
|
|
// - as two separate 0x8021 requests, each with a single option request
|
|
// (Blackberry)
|
|
// - as a single 0x8021 request with two option requests for the two DNS servers.
|
|
// (Samsung phone)
|
|
resultpco.clone(pcoReq);
|
|
unsigned char *pc = resultpco.begin();
|
|
unsigned char *end = pc + resultpco.size();
|
|
__attribute__((unused)) const char *protname = "";
|
|
if (*pc++ != 0x80) {
|
|
MGERROR("SGSN: Unrecognized PCO Config Protocol: %d\n",pc[-1]);
|
|
} else while (pc < end) {
|
|
unsigned proto = (pc[0] << 8) + pc[1];
|
|
pc += 2;
|
|
unsigned ipcplen = *pc++;
|
|
if (proto == 0x8021) { // IP Control Protocol.
|
|
// IPCP looks like this:
|
|
// 1 byte: command: 1 => configure-request
|
|
// 1 byte: transaction uniquifying identifier.
|
|
// 2 bytes: total length of ipcp (even though length above was a byte)
|
|
// Followed by IPCP options, where each option is:
|
|
// 1 byte: option code
|
|
// 1 byte: total option length N (should be 6)
|
|
// N bytes: data
|
|
if (pc[0] == 1) { // command = configure-request
|
|
// pc[1] is the uniquifying identifier, leave it alone.
|
|
// pc[2]&pc[3] are another length.
|
|
// Followed by one or more 6 byte option consisting of:
|
|
// pc[4]: IPCP option code
|
|
// pc[5]: length (6)
|
|
// pc[6-9]: data
|
|
// Note: the 4 byte header length is included in the option_len
|
|
unsigned ipcp_len = pc[3]; // pc[2] better be 0.
|
|
unsigned char *op;
|
|
for (op = &pc[4]; op < pc + ipcp_len; op += op[1]) {
|
|
const char *what = "";
|
|
switch (op[0]) {
|
|
case 0x81: // primary dns.
|
|
pc[0] = 2; // IPCP command = ACK
|
|
if (op[1] < 6) {
|
|
bad_ipcp_opt_len:
|
|
MGERROR("SGSN: Invalid PCO IPCP Config Option Length: opt=0x%x len=%d\n",
|
|
op[0], op[1]);
|
|
goto next_protocol;
|
|
}
|
|
memcpy(&op[2], &mg_dns[0], 4); // addr in network order.
|
|
break;
|
|
case 0x83: // secondary dns
|
|
pc[0] = 2; // IPCP command = ACK
|
|
if (op[1] < 6) { goto bad_ipcp_opt_len; }
|
|
memcpy(&op[2], &mg_dns[mg_dns[1] ? 1 : 0], 4); // addr in network order.
|
|
break;
|
|
case 2:
|
|
what = "IP Compression Protocol"; goto bad_ipcp;
|
|
case 0x82:
|
|
what = "primary NBNS [NetBios Name Service]"; goto bad_ipcp;
|
|
case 0x84:
|
|
what = "secondary NBNS [NetBios Name Service]"; goto bad_ipcp;
|
|
default: bad_ipcp:
|
|
// It would be nice to send an SMS message that the phone is set up improperly.
|
|
MGWARN("SGSN: warning: ignoring PDP Context activation IPCP option %d %s\n",pc[4],what);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else if (proto == 0xc021) { // LCP:
|
|
protname = "(LCP [Link Control Protocol] for PPP)";
|
|
goto unsupported_protocol;
|
|
} else if (proto == 0xc223) { // CHAP:
|
|
protname = "(CHAP [Challenge-Handshake Authentication Protocol] for PPP)";
|
|
goto unsupported_protocol;
|
|
} else if (proto == 0xc023) { // PAP: Password authentication protocol.
|
|
protname = "(PAP [Password Authentication Protocol] for PPP)";
|
|
goto unsupported_protocol;
|
|
} else {
|
|
// If we see any of these options are non-empty, the MS may be configured to use PPP.
|
|
// It is hopeless; user must reconfigure the MS.
|
|
unsupported_protocol:
|
|
if (ipcplen) {
|
|
// 6-12: Remove this message for the 3.0 release because we get
|
|
// lots of bogus complaints about PAP.
|
|
//MGWARN("SGSN: warning: ignoring PDP Context activation sub-protocol 0x%x %s; MS may require reconfiguration.\n",proto,protname);
|
|
}
|
|
}
|
|
next_protocol:
|
|
pc += ipcplen;
|
|
}
|
|
}
|
|
|
|
static void setIpAddr(ByteVector &result,mg_con_t *mgp)
|
|
{
|
|
// This is the IP address, only IPv4 supported.
|
|
// If the MS asks for IPv6, it is supposed to accept IPv4 anyway.
|
|
result = ByteVector(6);// IPv4 address + 2 byte header.
|
|
// 3GPP 24.008 10.5.6.4
|
|
result.setByte(0,0x01); // IETF allocated address, which is all we support.
|
|
result.setByte(1,0x21); // IPv4.
|
|
// This is a special case - we cannot use setUIint32 because it converts to network order,
|
|
// but the ip address is already in network order, which is what 3GPP requires, so just copy it.
|
|
memcpy(result.begin()+2, &mgp->mg_ip, 4);
|
|
}
|
|
|
|
// TODO: We have to set the transaction identifier for the PdpContext?
|
|
//void sendPdpDeactivateAll(SgsnInfo *si, unsigned cause) //SmCause::Cause cause)
|
|
void sendPdpDeactivateAll(SgsnInfo *si, SmCause::Cause cause)
|
|
{
|
|
// TODO: what should transactionId be?
|
|
// This is a downlink command, and we are supposed to allocate a new transaction id for each one,
|
|
// but since this is the only one we ever send, maybe 0 is ok.
|
|
int transactionId = 0;
|
|
L3SmMsgDeactivatePdpContextRequest deact(transactionId,(SmCause::Cause)cause,true);
|
|
//si->getLlcEntity(LlcSapi::GPRSMM)->lleWriteHighSide(deact);
|
|
si->sgsnWriteHighSideMsg(deact);
|
|
}
|
|
|
|
//void sendSmStatus(SgsnInfo *si,SmCause::Cause cause)
|
|
//{
|
|
// L3SmMsgSmStatus smstat(cause);
|
|
// LlcDlFrame bv(1000);
|
|
// smstat.smwrite(bv);
|
|
// SGSNLOG("Sending "<<smstat.str() <<" "<<si);
|
|
// si->getLlcEntity(LlcSapi::GPRSMM)
|
|
// ->lleWriteHighSide(bv,false,"pdp context reject");
|
|
//}
|
|
|
|
static void sendPdpContextReject(SgsnInfo *si,SmCause::Cause cause,unsigned ti)
|
|
{
|
|
L3SmMsgActivatePdpContextReject pdpRej(ti,cause);
|
|
si->sgsnWriteHighSideMsg(pdpRej);
|
|
}
|
|
|
|
void sendPdpContextAccept(SgsnInfo *si, PdpContext *pdp)
|
|
{
|
|
L3SmMsgActivatePdpContextAccept pdpa(pdp->mTransactionId);
|
|
pdpa.mLlcSapi = pdp->mLlcSapi;
|
|
|
|
// On the blackberry, the qosReq is 3 bytes, but it will fail is you just return that.
|
|
// Must return the whole shebang.
|
|
SmQoS resultQoS(12); // The full 12 byte QoS works.
|
|
resultQoS.defaultPS(pdp->mRabStatus.mRateDownlink,pdp->mRabStatus.mRateUplink);
|
|
pdpa.mQoS = resultQoS;
|
|
|
|
pdpa.mRadioPriority = 2; // 2 is a medium priority. Why do we pass this to the MS at all?
|
|
setPco(pdpa.mPco,pdp->mPcoReq);
|
|
setIpAddr(pdpa.mPdpAddress,pdp->mgp);
|
|
// No Packet Flow Identifier - not implemented.
|
|
// No SM cause, unless "the network accepts the requested PDN connectivity with restrictions."
|
|
SGSNLOG("Sending "<<pdpa.str() <<" "<<si); //done by sgsnWriteHighSideMsg
|
|
|
|
// Send L3 Activate PDP Context Accept it on its way.
|
|
si->sgsnWriteHighSideMsg(pdpa);
|
|
addShellRequest("PdpActivate",si->getGmm(),pdp);
|
|
}
|
|
|
|
// TODO: All the messages below should include the phone tlli.
|
|
static void handleActivatePdpContextRequest(SgsnInfo *si, L3SmMsgActivatePdpContextRequest &pdpr)
|
|
{
|
|
unsigned ti = pdpr.mTransactionId;
|
|
const char *name = "Activate Pdp Context Request:";
|
|
// For UMTS, the MS that does not support GPRS is supposed to set the LlcSapi
|
|
// to "LLC-non-assigned" which is value 0, and then the Network is supposed to send the same one back.
|
|
// So just dont bother to validate llc sapi at all in UMTS, since we dont use it for anything.
|
|
#if RN_UMTS == 0
|
|
if (! LlcEngine::isValidDataSapi(pdpr.mLlcSapi)) {
|
|
SGSNWARN(name<<"invalid llc sapi:"<<pdpr.mLlcSapi);
|
|
sendPdpContextReject(si,SmCause::Invalid_mandatory_information,ti);
|
|
return;
|
|
}
|
|
#endif
|
|
if (pdpr.mNSapi < 5 || pdpr.mNSapi > 15) {
|
|
SGSNWARN(name<<"invalid ns sapi:"<<pdpr.mNSapi);
|
|
sendPdpContextReject(si,SmCause::Invalid_mandatory_information,ti);
|
|
return;
|
|
}
|
|
// After the BTS is power cycled, the MS may attempt to re-establish the connection by
|
|
// sending ActivatePdpContextRequest first thing. We want to force it to re-attach,
|
|
// but the Blackberry, for one, does not follow the spec on this procedure and
|
|
// generally ignores the 'implicity_detached' status message.
|
|
// Not too surprising, because unlike us, power-cycling their SGSN for testing is
|
|
// probably not that easy for the MS manufacturers.
|
|
if (!si->isRegistered()) {
|
|
sendPdpContextReject(si,SmCause::Message_not_compatible_with_the_protocol_state,ti);
|
|
// Send a GMM message too, just to be safe.
|
|
sendImplicitlyDetached(si);
|
|
return;
|
|
}
|
|
GmmInfo *gmm = si->getGmm();
|
|
assert(gmm); // if isRegistered is true then mGmmp is set by definition.
|
|
|
|
PdpContext *pdp = gmm->getPdp(pdpr.mNSapi);
|
|
bool duplicateRequest = false;
|
|
if (pdp) {
|
|
if (pdp->mTransactionId == pdpr.mTransactionId) {
|
|
// Another request for the same pdp.
|
|
duplicateRequest = true;
|
|
if (pdp->mNSapi != (int) pdpr.mNSapi) {
|
|
// TODO: We should punt at this point.
|
|
SGSNWARN(name<<"duplicate request with different ns sapi:"<<pdpr.mNSapi);
|
|
}
|
|
} else {
|
|
SGSNWARN(name<<"ns sapi already in use:"<<pdpr.mNSapi);
|
|
sendPdpContextReject(si,SmCause::NSAPI_already_used,ti);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (duplicateRequest) {
|
|
SGSNLOG("Duplicate PdpContextRequest");
|
|
} else {
|
|
// Allocate an IP address.
|
|
mg_con_t *mgp = mg_con_find_free(gmm->mPTmsi,pdpr.mNSapi);
|
|
if (mgp == NULL) {
|
|
SGSNERROR(name<<"out of ip addresses");
|
|
sendPdpContextReject(si,SmCause::Insufficient_resources,ti);
|
|
return;
|
|
}
|
|
|
|
// Okey dokey. Allocate and hook up.
|
|
//pdp = allocPdp(pdpr.mNSapi,pdpr.mLlcSapi,mgp);
|
|
//LlcEntityUserData *userdatalle = si->getLlcEntityUserData(pdpr.mLlcSapi);
|
|
//sndcp = new Sndcp(pdpr.mNSapi,pdpr.mLlcSapi,userdatalle);
|
|
|
|
pdp = new PdpContext(gmm,mgp,pdpr.mNSapi,pdpr.mLlcSapi);
|
|
gmm->connectPdp(pdp,mgp);
|
|
|
|
//mgp->mg_pdp = pdp;
|
|
//sndcp->setPdp(pdp);
|
|
|
|
#if RN_UMTS
|
|
// For UMTS the PDP context creation is two-part.
|
|
// At this point we must allocate the RB [Radio Bearer] for the UE
|
|
// and we must wait for the acknowledgement message from the UE
|
|
// before we can send the final ActivatePdpContextAccept message.
|
|
// Like this:
|
|
// L3 ActivatePdpContextRequest, NSAPI=n (n=5..15)
|
|
// MS ---------------------------------> Network
|
|
// RRC RadioBearerSetup, RbId=n
|
|
// MS <--------------------------------- Network
|
|
// RRC RadioBearerSetupComplete
|
|
// MS ---------------------------------> Network
|
|
// L3 ActivatePdpContextAccept NSAPI=n
|
|
// MS <--------------------------------- Network
|
|
pdp->mRabStatus = SgsnAdapter::allocateRabForPdp(si->mMsHandle,pdpr.mNSapi,pdpr.mQoS);
|
|
switch (pdp->mRabStatus.mStatus) {
|
|
case RabStatus::RabFailure:
|
|
SGSNERROR(name<<"Rab Allocation Failure:"<<SmCause::name(pdp->mRabStatus.mFailCode));
|
|
sendPdpContextReject(si,pdp->mRabStatus.mFailCode,ti);
|
|
return;
|
|
case RabStatus::RabPending:
|
|
pdp->mUmtsStatePending = true;
|
|
pdp->mPendingPdpr = pdpr;
|
|
return;
|
|
case RabStatus::RabAllocated:
|
|
// The Rab was allocated previously by this UE.
|
|
// Fall through to resend the accept message.
|
|
break;
|
|
default: assert(0);
|
|
}
|
|
#else
|
|
// It is gprs. The link is already allocated.
|
|
// We ignore the QoS request; what a joke - it barely goes low enough to specify the GPRS bandwidth.
|
|
// For now, just use the base throughput of the GPRS link which is 2.5KBytes/sec less overhead.
|
|
// TODO: set the uplink/downlink rates from the multi-slot class of the MS.
|
|
pdp->mRabStatus.mStatus = RabStatus::RabAllocated;
|
|
pdp->mRabStatus.mRateUplink = pdp->mRabStatus.mRateDownlink = 2;
|
|
#endif
|
|
}
|
|
|
|
// This must be set each time, because it has uniquifying ids in it:
|
|
pdp->update(pdpr);
|
|
|
|
sendPdpContextAccept(si,pdp);
|
|
|
|
/*****
|
|
// Create the accept message. 3GPP 24.008 9.5.2.
|
|
//L3SmMsgActivatePdpContextAccept pdpa(pdpr.mTransactionId);
|
|
L3SmMsgActivatePdpContextAccept pdpa(pdp->mTransactionId);
|
|
// Note: we send back the llc sapi was sent to us, even if it is invalid.
|
|
pdpa.mLlcSapi = pdpr.mLlcSapi;
|
|
// Using the qos params from the sender did not work.
|
|
// Send our own default qos params:
|
|
setQoS(pdpa.mQoS,pdpr.mQoS);
|
|
pdpa.mRadioPriority = 2; // 2 is a medium priority. Why do we pass this to the MS at all?
|
|
setPco(pdpa.mPco,pdpr.mPco);
|
|
setIpAddr(pdpa.mPdpAddress,pdp->mgp);
|
|
// No Packet Flow Identifier - not implemented.
|
|
// No SM cause, unless "the network accepts the requested PDN connectivity with restrictions."
|
|
SGSNLOG("Sending "<<pdpa.str() <<" "<<si);
|
|
|
|
// Send L3 Activate PDP Context Accept it on its way.
|
|
si->sgsnWriteHighSideMsg(pdpa);
|
|
****/
|
|
}
|
|
|
|
void handleDeactivatePdpContextRequest(SgsnInfo *si, L3SmMsgDeactivatePdpContextRequest &deact)
|
|
{
|
|
unsigned nsapi;
|
|
unsigned nsapiMask = 0; // Mask of nsapi that were freed.
|
|
if (deact.mTearDownIndicator) {
|
|
nsapiMask = si->freePdpAll(false);
|
|
} else {
|
|
// Look for the pdp with this transaction identifier.
|
|
bool found = false;
|
|
for (nsapi = 0; nsapi < 16; nsapi++) { // 0-4 are unused, but be safe.
|
|
PdpContext *pdp;
|
|
if ((pdp = si->getPdp(nsapi))) {
|
|
if (pdp->mTransactionId == deact.mTransactionId) {
|
|
addShellRequest("PdpDeactivate",si->getGmm(),pdp);
|
|
si->freePdp(nsapi);
|
|
nsapiMask = 1<<nsapi;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!found) {
|
|
SGSNWARN("PdpContextDeactivate: pdp context not found for ti="<<deact.mTransactionId);
|
|
// and send what??
|
|
}
|
|
}
|
|
// Send L3 Activate PDP Context Accept on its way.
|
|
L3SmMsgDeactivatePdpContextAccept deactaccept(deact.mTransactionId);
|
|
//gmmlle->lleWriteHighSide(deactaccept);
|
|
si->sgsnWriteHighSideMsg(deactaccept);
|
|
|
|
// Deactivate the RABs. Only does something in UMTS, a no-op in GPRS.
|
|
// 23.060 9.3.4.1 shows the RABs being deactivated after sending
|
|
// the DeactivatePdpContextAccept message, so we will comply,
|
|
// although it probably does not matter. These are sent in acknowledged mode
|
|
// so it is conceivable that they will not arrive at the UE in order.
|
|
if (nsapiMask) { si->deactivateRabs(nsapiMask); }
|
|
}
|
|
|
|
|
|
// The gmmlle is the lle of the GMM entity, the one to which we should return a message.
|
|
void Ggsn::handleL3SmMsg(SgsnInfo *si,L3GprsFrame &frame1)
|
|
{
|
|
L3SmFrame frame(frame1);
|
|
unsigned mt = frame.getMsgType(); // message type
|
|
//SGSNLOG("CRACKING SM MSG TYPE "<<mt);
|
|
switch (mt) {
|
|
case L3SmMsg::ActivatePDPContextRequest: {
|
|
L3SmMsgActivatePdpContextRequest pdpr(frame);
|
|
SGSNLOG("Received "<<pdpr.str() <<si);
|
|
handleActivatePdpContextRequest(si,pdpr);
|
|
break;
|
|
}
|
|
case L3SmMsg::SMStatus: {
|
|
L3SmMsgSmStatus stmsg(frame);
|
|
SGSNLOG("Received SmStatus: "<<stmsg.str()<<si);
|
|
break;
|
|
}
|
|
//case L3SmMsg::ActivatePDPContextAccept:
|
|
//case L3SmMsg::ActivatePDPContextReject:
|
|
//case RequestPDPContextActivation:
|
|
//case RequestPDPContextActivationReject:
|
|
|
|
case L3SmMsg::DeactivatePDPContextRequest: {
|
|
//SGSNLOG("Incoming Deactivate pdp, frame="<<frame<< " bv="<<(ByteVector)frame);
|
|
L3SmMsgDeactivatePdpContextRequest deact(frame);
|
|
SGSNLOG("Received DeactivatePdpContextRequest: "<<deact.str());
|
|
handleDeactivatePdpContextRequest(si,deact);
|
|
break;
|
|
}
|
|
|
|
//DeactivatePDPContextAccept:
|
|
|
|
//ModifyPDPContextRequest = 0x48, // network to MS direction.
|
|
//ModifyPDPContextAccept = 0x49, // MS to netowrk direction.
|
|
//ModifyPDPContextRequestMS = 0x4a, // MS to network direction.
|
|
//ModifyPDPContextAcceptMS = 0x4b, // network to MS direction.
|
|
//ModifyPDPContextReject = 0x4c,
|
|
//ActivateSecondaryPDPContextRequest = 0x4d,
|
|
//ActivateSecondaryPDPContextAccept = 0x4e,
|
|
//ActivateSecondaryPDPContextReject = 0x4f,
|
|
|
|
|
|
// From GSM 24.008:
|
|
//ActivateMBMSContextRequest = 0x56,
|
|
//ActivateMBMSContextAccept = 0x57,
|
|
//ActivateMBMSContextReject = 0x58,
|
|
//RequestMBMSContextActivation = 0x59,
|
|
//RequestMBMSContextActivationReject = 0x5a,
|
|
|
|
//RequestSecondaryPDPContextActivation = 0x5b,
|
|
//RequestSecondaryPDPContextActivationReject = 0x5c,
|
|
//Notification = 0x5d,
|
|
default:
|
|
SGSNWARN("Ignoring GPRS SM message type "<<mt<<" " <<L3SmMsg::name(mt));
|
|
break;
|
|
}
|
|
}
|
|
|
|
}; // namespace
|