From 3ca423847929a6f9a9c0408153997346adbaa0b0 Mon Sep 17 00:00:00 2001 From: calincerchez Date: Fri, 13 Jul 2012 17:28:07 +0300 Subject: [PATCH] begin nas --- src/applications/nas/EMMEntity.cc | 497 ++++++++++++++++++++++++++ src/applications/nas/EMMEntity.h | 178 +++++++++ src/applications/nas/ESMEntity.cc | 146 ++++++++ src/applications/nas/ESMEntity.h | 74 ++++ src/applications/nas/NAS.cc | 335 +++++++++++++++++ src/applications/nas/NAS.h | 74 ++++ src/applications/nas/NAS.ned | 31 ++ src/applications/nas/NASMessage.msg | 124 +++++++ src/applications/nas/NASSerializer.cc | 247 +++++++++++++ src/applications/nas/NASSerializer.h | 40 +++ src/applications/nas/NASUtils.cc | 339 ++++++++++++++++++ src/applications/nas/NASUtils.h | 47 +++ src/networklayer/gtp/GTPControl.cc | 8 +- src/networklayer/gtp/GTPControl.h | 15 +- src/networklayer/gtp/GTPMessage.cc | 2 + src/networklayer/gtp/GTPMessage.h | 32 +- src/networklayer/gtp/GTPUser.ned | 4 +- src/networklayer/gtp/GTPUtils.cc | 64 +--- src/networklayer/gtp/GTPUtils.h | 45 ++- 19 files changed, 2204 insertions(+), 98 deletions(-) create mode 100644 src/applications/nas/EMMEntity.cc create mode 100644 src/applications/nas/EMMEntity.h create mode 100644 src/applications/nas/ESMEntity.cc create mode 100644 src/applications/nas/ESMEntity.h create mode 100644 src/applications/nas/NAS.cc create mode 100644 src/applications/nas/NAS.h create mode 100644 src/applications/nas/NAS.ned create mode 100644 src/applications/nas/NASMessage.msg create mode 100644 src/applications/nas/NASSerializer.cc create mode 100644 src/applications/nas/NASSerializer.h create mode 100644 src/applications/nas/NASUtils.cc create mode 100644 src/applications/nas/NASUtils.h diff --git a/src/applications/nas/EMMEntity.cc b/src/applications/nas/EMMEntity.cc new file mode 100644 index 0000000..f4f3b14 --- /dev/null +++ b/src/applications/nas/EMMEntity.cc @@ -0,0 +1,497 @@ +// +// Copyright (C) 2012 Calin Cerchez +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// 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. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +#include "EMMEntity.h" +#include "NAS.h" +#include "NASSerializer.h" +#include "LTEUtils.h" +#include "NASUtils.h" +#include "PhyControlInfo_m.h" + +EMMEntity::EMMEntity() { + init(); +} + +EMMEntity::EMMEntity(unsigned char appType) { + // TODO Auto-generated constructor stub + init(); + this->appType = appType; + nasKey = uniform(0, 6); + + if (appType == UE_APPL_TYPE) { + fsm = new cFSM("fsm-EMM-UE"); + fsm->setState(EMM_NULL); + take(fsm); + } else if (appType == MME_APPL_TYPE) { + fsm = new cFSM("fsm-EMM-MME"); + fsm->setState(EMM_DEREGISTERED_N); + take(fsm); + } +} + +EMMEntity::~EMMEntity() { + // TODO Auto-generated destructor stub + if (fsm != NULL) + dropAndDelete(fsm); + + if (imsi != NULL) + delete imsi; + + delete [] ueNetworkCapability; + + if (t3410 != NULL) { + if (t3410->getContextPointer() != NULL) + module->cancelEvent(t3410); + delete t3410; + } + if (t3402 != NULL) { + if (t3402->getContextPointer() != NULL) + module->cancelEvent(t3402); + delete t3402; + } + if (t3411 != NULL) { + if (t3411->getContextPointer() != NULL) + module->cancelEvent(t3411); + delete t3411; + } + if (t3450 != NULL) { + if (t3450->getContextPointer() != NULL) + module->cancelEvent(t3450); + delete t3450; + } +} + +void EMMEntity::init() { + attCounter = 0; + tracAreaUpdCounter = 0; + nasKey = 0; + appType = 0; + module = NULL; + epsUpdSt = -1; + fsm = NULL; + ueNetworkCapability = new char[3]; + ueNetworkCapability[0] = 0x02; + ueNetworkCapability[1] = 0x80; + ueNetworkCapability[2] = 0x40; + emmCause = -1; + t3402 = NULL; + t3410 = NULL; + t3411 = NULL; + t3450 = NULL; + imsi = NULL; + tmsi = 0; + peer = NULL; +} + +void EMMEntity::performStateTransition(EMMSublayerEvent event) { + + int oldState = fsm->getState(); + switch(oldState) { + case EMM_NULL: + switch(event) { + case SwitchOn: // switch on + FSM_Goto(*fsm, EMM_DEREGISTERED_PLMN_SEARCH); + break; + default: + EV << "NAS-EMM: Received unexpected event\n"; + break; + } + break; + case EMM_DEREGISTERED_PLMN_SEARCH: + switch(event) { + case NoUSIM: + FSM_Goto(*fsm, EMM_DEREGISTERED_NO_IMSI); + break; + case CellFoundPermittedPLMN: + FSM_Goto(*fsm, EMM_DEREGISTERED_NORMAL_SERVICE); + break; + default: + EV << "NAS-EMM: Received unexpected event\n"; + break; + } + break; + case EMM_DEREGISTERED_NORMAL_SERVICE: + switch(event) { + case AttachRequested: + FSM_Goto(*fsm, EMM_REGISTERED_INITIATED); + break; + default: + EV << "NAS-EMM: Received unexpected event\n"; + break; + } + break; + case EMM_DEREGISTERED_N: + switch(event) { + case AttachAccepted: { + if (tmsi == 0) { + tmsi = uniform(1, 1000); + FSM_Goto(*fsm, EMM_COMMON_PROCEDURE_INITIATED); + } + NASPlainMessage *msg = createAttachAccept(); + module->sendToS1AP(msg, ownerp->getEnbId(), ownerp->getMmeId()); + // else remain in this state + break; + } + case AttachRejected: { + NASPlainMessage *msg = createAttachReject(); + module->sendToS1AP(msg, ownerp->getEnbId(), ownerp->getMmeId()); + break; + } + case AttachCompleted: { + /* TODO stop T3450 */ + FSM_Goto(*fsm, EMM_REGISTERED_N); + break; + } + default: + EV << "NAS-EMM: Received unexpected event\n"; + break; + } + break; + case EMM_REGISTERED_INITIATED: + switch(event) { + case AttachAccepted: { + attCounter = 0; + tracAreaUpdCounter = 0; + module->cancelEvent(t3410); + epsUpdSt = EU1_UPDATED; + NASPlainMessage *msg = createAttachComplete(); + module->sendToRadio(msg, module->getChannelNumber()); + FSM_Goto(*fsm, EMM_REGISTERED_U); + break; + } + case AttachRejected: { + if (emmCause == EPSServNonEPSServNotAllowed) { + epsUpdSt = EU3_ROAMING_NOT_ALLOWED; + FSM_Goto(*fsm, EMM_DEREGISTERED_U); + } + break; + } + default: + EV << "NAS-EMM: Received unexpected event\n"; + break; + } + break; + case EMM_COMMON_PROCEDURE_INITIATED: + switch(event) { + case AttachCompleted: + /* TODO stop T3450 */ + FSM_Goto(*fsm, EMM_REGISTERED_N); + break; + default: + EV << "NAS-EMM: Received unexpected event\n"; + break; + } + break; + default: + EV << "NAS-EMM: Unknown state\n"; + break; + } + + if (oldState != fsm->getState()) + EV << "NAS-EMM: PSM-Transition: " << stateName(oldState) << " --> " << stateName(fsm->getState()) << " (event was: " << eventName(event) << ")\n"; + else + EV << "NAS-EMM: Staying in state: " << stateName(fsm->getState()) << " (event was: " << eventName(event) << ")\n"; + + stateEntered(); +} + +void EMMEntity::stateEntered() { + switch(fsm->getState()) { + case EMM_DEREGISTERED_NORMAL_SERVICE: { + attCounter = 0; + // send attach request with default pdn connection + NASPlainMessage *msg = createAttachRequest(); + module->sendToRadio(msg, module->getChannelNumber()); + // start t3410, reset t3402 and t3411 + startT3410(); + // go to EMM_REGISTERED_INITIATED + ownerp->setStatus(SUB_PENDING); + performStateTransition(AttachRequested); + break; + } + case EMM_DEREGISTERED_U: + case EMM_DEREGISTERED_N: + ownerp->setStatus(SUB_INACTIVE); + break; + case EMM_REGISTERED_NORMAL_SERVICE: + case EMM_REGISTERED_N: + ownerp->setStatus(SUB_ACTIVE); + break; + default: + break; + } +} + +NASPlainMessage *EMMEntity::createAttachRequest() { + NASPlainMessage *msg = new NASPlainMessage("Attach-Request"); + msg->setHdr(NASUtils().createHeader(0, PlainNASMessage, EMMMessage, 0, AttachRequest)); + msg->setIesArraySize(5); + + /* NAS key set identifier */ + msg->setIes(0, NASUtils().createIE(IE_V, IEType1, 0, nasKey)); + + /* EPS attach type */ + msg->setIes(1, NASUtils().createIE(IE_V, IEType1, 0, EPSAttach)); + + /* EPS mobile identity */ + msg->setIes(2, NASUtils().createEPSMobileIdIE(IE_LV, IMSI_ID, imsi)); + + /* UE network capability */ + msg->setIes(3, NASUtils().createIE(IE_LV, IEType4, 0, 3, ueNetworkCapability)); + + /* ESM message container */ + NASPlainMessage *pdnConnReq = peer->getDefPDNConnection()->createPDNConnectivityRequest(); + msg->encapsulate(pdnConnReq); + msg->setEncapPos(4); + + return msg; +} + +void EMMEntity::processAttachRequest(NASPlainMessage *msg) { + /* EPS mobile identity */ + char *id; + unsigned len = NASUtils().processEPSMobileIdIE(msg->getIes(2), id); + if (len == IMSI_CODED_SIZE) + imsi = id; + + /* ESM message container */ + NASPlainMessage *smsg = check_and_cast(msg->decapsulate()); + if (smsg->getHdr().getMsgType() == PDNConnectivityRequest) { + // initialize PDN connection for this UE + PDNConnection *conn = new PDNConnection(peer); + conn->processPDNConnectivityRequest(smsg); + if (fsm->getState() == EMM_DEREGISTERED_N) + peer->addPDNConnection(conn, true); + else + peer->addPDNConnection(conn, false); + } + delete msg; +} + +NASPlainMessage *EMMEntity::createAttachAccept() { + NASPlainMessage *msg = new NASPlainMessage("Attach-Accept"); + msg->setHdr(NASUtils().createHeader(0, PlainNASMessage, EMMMessage, 0, AttachAccept)); + msg->setIesArraySize(5); + + /* EPS attach result */ + msg->setIes(0, NASUtils().createIE(IE_V, IEType1, 0, 2)); // 2 = combined EPS/IMSI attach + + /* Spare half octet */ + msg->setIes(1, NASUtils().createIE(IE_V, IEType1, 0, 0)); + + /* T3412 value */ + // 111 - timer deactivated, 00000 - timer deactivated (for tracking area update) + msg->setIes(2, NASUtils().createIE(IE_V, IEType3, 0, 224)); + + /* TAI list */ + char *taiList = (char*)calloc(6, sizeof(char)); + taiList[0] = 0; // 00 - type list, 00000 number of elements + memcpy(taiList + 1, ownerp->getPlmnId(), PLMNID_CODED_SIZE); + memcpy(taiList + 1 + PLMNID_CODED_SIZE, ownerp->getTac(), TAC_CODED_SIZE); + msg->setIes(3, NASUtils().createIE(IE_LV, IEType4, 0, 6, taiList)); + + /* ESM message container */ + NASPlainMessage *actDefBearerReq = peer->getDefPDNConnection()->createActDefBearerRequest(); + msg->encapsulate(actDefBearerReq); + msg->setEncapPos(4); + + return msg; +} + +void EMMEntity::processAttachAccept(NASPlainMessage *msg) { + /* ESM message container */ + PDNConnection *conn = peer->getDefPDNConnection(); + NASPlainMessage *smsg = check_and_cast(msg->decapsulate()); + if (smsg->getHdr().getMsgType() == ActDefEPSBearerCtxtReq) + conn->processActDefBearerRequest(smsg); + + performStateTransition(AttachAccepted); + delete msg; +} + +NASPlainMessage *EMMEntity::createAttachReject() { + NASPlainMessage *msg = new NASPlainMessage("Attach-Reject"); + msg->setHdr(NASUtils().createHeader(0, PlainNASMessage, EMMMessage, 0, AttachReject)); + msg->setIesArraySize(1); + + /* EMM Cause */ + msg->setIes(0, NASUtils().createIE(IE_V, IEType3, 0, (unsigned char)emmCause)); + + /* ESM message container */ +// NASPlainMessage *actDefBearerReq = peer->getDefPDNConnection()->createActDefBearerRequest(); +// msg->encapsulate(actDefBearerReq); +// msg->setEncapPos(4); + + return msg; +} + +void EMMEntity::processAttachReject(NASPlainMessage *msg) { + /* EMM Cause */ + emmCause = msg->getIes(0).getValue(0); + + /* ESM message container */ +// PDNConnection *conn = peer->getDefPDNConnection(); +// NASPlainMessage *smsg = check_and_cast(msg->decapsulate()); +// if (smsg->getHdr().getMsgType() == ActDefEPSBearerCtxtReq) +// conn->processActDefBearerRequest(smsg); + + performStateTransition(AttachRejected); + delete msg; +} + + +NASPlainMessage *EMMEntity::createAttachComplete() { + NASPlainMessage *msg = new NASPlainMessage("Attach-Complete"); + msg->setHdr(NASUtils().createHeader(0, PlainNASMessage, EMMMessage, 0, AttachComplete)); + msg->setIesArraySize(1); + + /* ESM message container */ + NASPlainMessage *actDefBearerReq = peer->getDefPDNConnection()->createActDefBearerAccept(); + msg->encapsulate(actDefBearerReq); + msg->setEncapPos(0); + + return msg; +} + +void EMMEntity::processAttachComplete(NASPlainMessage *msg) { + /* ESM message container */ + PDNConnection *conn = peer->getDefPDNConnection(); + NASPlainMessage *smsg = check_and_cast(msg->decapsulate()); + if (smsg->getHdr().getMsgType() == ActDefEPSBearerCtxtAcc) + conn->processActDefBearerAccept(smsg); + + performStateTransition(AttachCompleted); + delete msg; +} + +const char *EMMEntity::stateName(int state) const { +#define CASE(x) case x: s=#x; break + const char *s = "unknown"; + switch (state) { + CASE(EMM_NULL); + CASE(EMM_DEREGISTERED_U); + CASE(EMM_DEREGISTERED_NORMAL_SERVICE); + CASE(EMM_DEREGISTERED_LIMITED_SERVICE); + CASE(EMM_DEREGISTERED_ATTEMPTING_TO_ATTACH); + CASE(EMM_DEREGISTERED_PLMN_SEARCH); + CASE(EMM_DEREGISTERED_NO_IMSI); + CASE(EMM_DEREGISTERED_ATTACH_NEEDED); + CASE(EMM_DEREGISTERED_NO_CELL_AVAILABLE); + CASE(EMM_REGISTERED_U); + CASE(EMM_REGISTERED_INITIATED); + CASE(EMM_REGISTERED_NORMAL_SERVICE); + CASE(EMM_REGISTERED_ATTEMPTING_TO_UPDATE); + CASE(EMM_REGISTERED_LIMITED_SERVICE); + CASE(EMM_REGISTERED_PLMN_SEARCH); + CASE(EMM_REGISTERED_UPDATE_NEEDED); + CASE(EMM_REGISTERED_ATTEMPTING_TO_UPDATE_MM); + CASE(EMM_REGISTERED_IMSI_DETACH_INITIATED); + CASE(EMM_DEREGISTERED_INITIATED_U); + CASE(EMM_TRACKING_AREA_UPDATING_INITIATED); + CASE(EMM_SERVICE_REQUEST_INITIATED); + CASE(EMM_DEREGISTERED_N); + CASE(EMM_COMMON_PROCEDURE_INITIATED); + CASE(EMM_REGISTERED_N); + CASE(EMM_DEREGISTERED_INITIATED_M); + } + return s; +#undef CASE +} + +const char *EMMEntity::statusName() const { +#define CASE(x) case x: s=#x; break + const char *s = "unknown"; + switch (epsUpdSt) { + CASE(EU1_UPDATED); + CASE(EU2_NOT_UPDATED); + CASE(EU3_ROAMING_NOT_ALLOWED); + } + return s; +#undef CASE +} + +const char *EMMEntity::eventName(int event) { +#define CASE(x) case x: s=#x; break + const char *s = "unknown"; + switch (event) { + CASE(EnableS1Mode); + CASE(NoUSIM); + CASE(CellFoundPermittedPLMN); + CASE(SwitchOn); + CASE(AttachRequested); + CASE(AttachAccepted); + CASE(AttachRejected); + CASE(AttachCompleted); + } + return s; +#undef CASE +} + +void EMMEntity::setModule(NAS *module) { + this->module = module; +} + +NAS *EMMEntity::getModule() { + return module; +} + +void EMMEntity::setOwner(Subscriber *ownerp) { + ownerp->setEmmEntity(this); + this->ownerp = ownerp; +} + +Subscriber *EMMEntity::getOwner() { + return ownerp; +} + +void EMMEntity::startT3410() { + if (t3410 == NULL) { + t3410 = new cMessage("T3410"); + t3410->setContextPointer(this); + } + module->scheduleAt(simTime() + T3410_TIMEOUT, t3410); + if (t3402 != NULL) + module->cancelEvent(t3402); + if (t3411 != NULL) + module->cancelEvent(t3411); +} + +void EMMEntity::setPeer(ESMEntity *peer) { + this->peer = peer; +} + +ESMEntity *EMMEntity::getPeer() { + return peer; +} + +std::string EMMEntity::info(int tabs) const { + std::stringstream out; + if (imsi != NULL) { + for (int i = 0; i < tabs; i++) out << "\t"; + out << "imsi:" << LTEUtils().toASCIIString(imsi, IMSI_CODED_SIZE) << "\n"; + } + if (epsUpdSt != -1) { + for (int i = 0; i < tabs; i++) out << "\t"; + out << "epsUpdSt:" << statusName() << "\n"; + } + if (fsm != NULL) { + for (int i = 0; i < tabs; i++) out << "\t"; + out << "emmSt:" << stateName(fsm->getState()) << "\n"; + } + + return out.str(); +} diff --git a/src/applications/nas/EMMEntity.h b/src/applications/nas/EMMEntity.h new file mode 100644 index 0000000..2e05223 --- /dev/null +++ b/src/applications/nas/EMMEntity.h @@ -0,0 +1,178 @@ +// +// Copyright (C) 2012 Calin Cerchez +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// 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. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +#ifndef EMMENTITY_H_ +#define EMMENTITY_H_ + +#include +#include "NASMessage_m.h" +#include "PDNConnection.h" + +#define T3410_TIMEOUT 20 + +class NAS; +class Subscriber; + +enum EMMSublayerState { + /* for UE */ + EMM_NULL = FSM_Steady(0), + EMM_DEREGISTERED_U = FSM_Steady(1), + EMM_DEREGISTERED_NORMAL_SERVICE = FSM_Steady(2), + EMM_DEREGISTERED_LIMITED_SERVICE = FSM_Steady(3), + EMM_DEREGISTERED_ATTEMPTING_TO_ATTACH = FSM_Steady(4), + EMM_DEREGISTERED_PLMN_SEARCH = FSM_Steady(5), + EMM_DEREGISTERED_NO_IMSI = FSM_Steady(6), + EMM_DEREGISTERED_ATTACH_NEEDED = FSM_Steady(7), + EMM_DEREGISTERED_NO_CELL_AVAILABLE = FSM_Steady(8), + EMM_REGISTERED_U = FSM_Steady(9), + EMM_REGISTERED_INITIATED = FSM_Steady(10), + EMM_REGISTERED_NORMAL_SERVICE = FSM_Steady(11), + EMM_REGISTERED_ATTEMPTING_TO_UPDATE = FSM_Steady(12), + EMM_REGISTERED_LIMITED_SERVICE = FSM_Steady(13), + EMM_REGISTERED_PLMN_SEARCH = FSM_Steady(14), + EMM_REGISTERED_UPDATE_NEEDED = FSM_Steady(15), + EMM_REGISTERED_NO_CELL_AVAILABLE = FSM_Steady(16), + EMM_REGISTERED_ATTEMPTING_TO_UPDATE_MM = FSM_Steady(17), + EMM_REGISTERED_IMSI_DETACH_INITIATED = FSM_Steady(18), + EMM_DEREGISTERED_INITIATED_U = FSM_Steady(19), + EMM_TRACKING_AREA_UPDATING_INITIATED = FSM_Steady(20), + EMM_SERVICE_REQUEST_INITIATED = FSM_Steady(21), + /* for MME */ + EMM_DEREGISTERED_N = FSM_Steady(22), + EMM_COMMON_PROCEDURE_INITIATED = FSM_Steady(23), + EMM_REGISTERED_N = FSM_Steady(24), + EMM_DEREGISTERED_INITIATED_M = FSM_Steady(25) +}; + +enum EMMSublayerEvent { + /* for UE */ + EnableS1Mode, + SwitchOn, + NoUSIM, + CellFoundPermittedPLMN, + AttachRequested, + /* for MME and UE */ + AttachAccepted, + AttachRejected, + /* for MME */ + AttachCompleted, +}; + +enum EPSUpdateStatus { + EU1_UPDATED, + EU2_NOT_UPDATED, + EU3_ROAMING_NOT_ALLOWED +}; + +/* + * Class for NAS EMM entity. + */ +class EMMEntity : public cPolymorphic { +private: + unsigned char attCounter; + unsigned char tracAreaUpdCounter; + + // used for message creating and processing + unsigned char appType; + unsigned char nasKey; + char emmCause; + char *imsi; + unsigned tmsi; + char *ueNetworkCapability; + + cFSM *fsm; + + char epsUpdSt; + + cMessage *t3402; + cMessage *t3410; + cMessage *t3411; + cMessage *t3450; + + NAS *module; + + Subscriber *ownerp; + + ESMEntity *peer; +public: + EMMEntity(); + EMMEntity(unsigned char appType); + virtual ~EMMEntity(); + + /* + * Method for initializing a EMM entity. Basically all parameters are set to + * their default values. + */ + void init(); + + /* + * Setter methods. + */ + void setOwner(Subscriber *ownerp); + void setModule(NAS *module); + void setImsi(char *imsi) { this->imsi = imsi; } + void setTmsi(unsigned tmsi) { this->tmsi = tmsi; } + void setPeer(ESMEntity *peer); + void setEmmCause(int emmCause) { this->emmCause = emmCause; } + + /* + * Getter methods. + */ + unsigned char getAppType() { return appType; } + NAS *getModule(); + Subscriber *getOwner(); + char *getImsi() { return imsi; } + unsigned getTmsi() { return tmsi; } + ESMEntity *getPeer(); + int getState() { return fsm->getState(); } + cFSM *getFSM() { return fsm; } + + /* + * Utility methods for state processing and printing. + */ + void performStateTransition(EMMSublayerEvent event); + void stateEntered(); + const char *stateName(int state) const; + const char *statusName() const; + const char *eventName(int event); + + + /* + * Methods for processing and creating of NAS messages, which are related to + * EMM entity. These messages are related to user equipment mobility. + */ + NASPlainMessage *createAttachRequest(); + NASPlainMessage *createAttachAccept(); + NASPlainMessage *createAttachReject(); + NASPlainMessage *createAttachComplete(); + void processAttachRequest(NASPlainMessage *msg); + void processAttachAccept(NASPlainMessage *msg); + void processAttachReject(NASPlainMessage *msg); + void processAttachComplete(NASPlainMessage *msg); + + /* + * Method for starting T3410 timer. This will cancel timers T3402 and T3411. + */ + void startT3410(); + + /* + * Method for printing information about EMM entity for a subscriber. + */ + std::string info(int tabs) const; +}; + +#endif /* EMMCONTEXT_H_ */ diff --git a/src/applications/nas/ESMEntity.cc b/src/applications/nas/ESMEntity.cc new file mode 100644 index 0000000..4e46fb1 --- /dev/null +++ b/src/applications/nas/ESMEntity.cc @@ -0,0 +1,146 @@ +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// 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. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +#include "ESMEntity.h" +#include "EMMEntity.h" +#include "NAS.h" +#include "NASUtils.h" +#include "DiameterApplication.h" +#include "GTPMessage_m.h" +#include "NASSerializer.h" + +ESMEntity::ESMEntity() { + init(); +} + +ESMEntity::ESMEntity(unsigned char appType) { + // TODO Auto-generated constructor stub + init(); + this->appType = appType; +} + +ESMEntity::~ESMEntity() { + // TODO Auto-generated destructor stub + delPDNConnection(0, conns.size()); +} + +void ESMEntity::init() { + ownerp = NULL; + appType = 0; + defConn = NULL; + connIds = 0; + bearerIds = 0; + peer = NULL; + module = NULL; +} + +void ESMEntity::setOwner(Subscriber *ownerp) { + ownerp->setEsmEntity(this); + this->ownerp = ownerp; +} + +Subscriber *ESMEntity::getOwner() { + return ownerp; +} + +void ESMEntity::addPDNConnection(PDNConnection *conn, bool def) { + conn->setOwner(this); + if (def == true) + defConn = conn; + conns.push_back(conn); +} + +void ESMEntity::delPDNConnection(unsigned start, unsigned end) { + PDNConnections::iterator first = conns.begin() + start; + PDNConnections::iterator last = conns.begin() + end; + PDNConnections::iterator i = first; + for (;i != last; ++i) { + if (*i == defConn) + defConn = NULL; + delete *i; + } + conns.erase(first, last); +} + +AVP *ESMEntity::createAPNConfigProfAVP() { + if (conns.size() > 0) { + std::vector apnConfigProfVec; + apnConfigProfVec.push_back(DiameterUtils().createUnsigned32AVP(AVP_ContextIdentifier, 1, 0, 0, TGPP, defConn->getId())); + apnConfigProfVec.push_back(DiameterUtils().createInteger32AVP(AVP_AllAPNConfigInclInd, 1, 0, 0, TGPP, All_APN_CONFIGURATIONS_INCLUDED)); + for (unsigned i = 0; i < conns.size(); i++) { + PDNConnection *conn = conns.at(i); + apnConfigProfVec.push_back(conn->createAPNConfigAVP()); + } + AVP *apnConfigProf = DiameterUtils().createGroupedAVP(AVP_APNConfigProfile, 1, 0, 0, TGPP, apnConfigProfVec); + DiameterUtils().deleteGroupedAVP(apnConfigProfVec); + return apnConfigProf; + } + return NULL; +} + +bool ESMEntity::processAPNConfigProfAVP(AVP *apnConfigProf) { + std::vector apnConfigProfVec = DiameterUtils().processGroupedAVP(apnConfigProf); + AVP *ctxtId = DiameterUtils().findAVP(AVP_ContextIdentifier, apnConfigProfVec); + if (ctxtId == NULL) { + EV << "DiameterS6a: Missing ContextIdentifier AVP.\n"; + return false; + } + unsigned defId = DiameterUtils().processUnsigned32AVP(ctxtId); + std::vector apnConfigs = DiameterUtils().findAVPs(AVP_APNConfig, apnConfigProfVec); + for (unsigned i = 0; i < apnConfigs.size(); i++) { + std::vector apnConfig = DiameterUtils().processGroupedAVP(apnConfigs[i]); + if (i < conns.size()) { + PDNConnection *conn = conns.at(i); + if (!conn->processAPNConfigAVP(apnConfig)) + return false; + if (conn->getId() == defId) + this->defConn = conn; + } else { + /* TODO */ + // add additional pdn connections received from HSS + } + } + DiameterUtils().deleteGroupedAVP(apnConfigProfVec); + return true; +} + +void ESMEntity::setPeer(EMMEntity *peer) { + this->peer = peer; +} + +EMMEntity *ESMEntity::getPeer() { + return peer; +} + +void ESMEntity::setModule(NAS *module) { + this->module = module; +} + +std::string ESMEntity::info(int tabs) const { + std::stringstream out; + if (conns.size() > 0) { + for (int i = 0; i < tabs; i++) out << "\t"; + out << "pdnConns:{\n"; + for (unsigned j = 0; j < conns.size(); j++) { + for (int i = 0; i < tabs + 1; i++) out << "\t"; + out << "pdnConn:{\n" << conns.at(j)->info(tabs + 2); + for (int i = 0; i < tabs + 1; i++) out << "\t"; + out << "}\n"; + } + for (int i = 0; i < tabs; i++) out << "\t"; + out << "}\n"; + } + return out.str(); +} diff --git a/src/applications/nas/ESMEntity.h b/src/applications/nas/ESMEntity.h new file mode 100644 index 0000000..b11344e --- /dev/null +++ b/src/applications/nas/ESMEntity.h @@ -0,0 +1,74 @@ +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// 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. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +#ifndef ESMENTITY_H_ +#define ESMENTITY_H_ + +#include +#include "PDNConnection.h" +#include "NASMessage_m.h" + +class Subscriber; +class EMMEntity; +class NAS; + +class ESMEntity { +private: + unsigned connIds; + unsigned char bearerIds; + unsigned char appType; + Subscriber *ownerp; + PDNConnection *defConn; + typedef std::vector PDNConnections; + PDNConnections conns; + NAS *module; + EMMEntity *peer; +public: + ESMEntity(); + ESMEntity(unsigned char appType); + virtual ~ESMEntity(); + + /* init */ + void init(); + + /* setter */ + void setOwner(Subscriber *owernp); + void setDefPDNConnection(PDNConnection *defConn) { this->defConn = defConn; } + void setPeer(EMMEntity *peer); + void setModule(NAS *module); + + /* getter */ + Subscriber *getOwner(); + unsigned char getAppType() { return appType; } + PDNConnection *getDefPDNConnection() { return defConn; } + EMMEntity *getPeer(); + unsigned size() { return conns.size(); } + + /* pdn connection */ + void addPDNConnection(PDNConnection *conn, bool def); + void delPDNConnection(unsigned start, unsigned end); + + /* gen bearer ids */ + unsigned char genBearerId() { return ++bearerIds; } + unsigned genPDNConnectionId() { return ++connIds; } + + /* diameter avp */ + AVP *createAPNConfigProfAVP(); + bool processAPNConfigProfAVP(AVP *apnConfigProf); + + std::string info(int tabs) const; +}; + +#endif /* ESMENTITY_H_ */ diff --git a/src/applications/nas/NAS.cc b/src/applications/nas/NAS.cc new file mode 100644 index 0000000..dfd2d0b --- /dev/null +++ b/src/applications/nas/NAS.cc @@ -0,0 +1,335 @@ +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// 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. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +#include "NAS.h" +#include "LTEUtils.h" +#include "PhyControlInfo_m.h" +#include "S1APControlInfo_m.h" +#include "S1APConstant.h" +#include "NASSerializer.h" +#include "PDNConnection.h" +#include "NASUtils.h" +#include "InterfaceTableAccess.h" +#include "IPv4InterfaceData.h" +#include "RoutingTableAccess.h" + +Define_Module(NAS) + +NAS::NAS() { + // TODO Auto-generated constructor stub + appType = RELAY_APPL_TYPE; +} + +NAS::~NAS() { + // TODO Auto-generated destructor stub +// if (sub != NULL) +// delete emm; +} + +void NAS::initialize(int stage) { + if (stage == 4) { + subT = SubscriberTableAccess().get(); + const char *fileName = par("configFile"); + if (fileName != NULL && (strcmp(fileName, ""))) + this->loadConfigFromXML(fileName); + ift=InterfaceTableAccess().get(); + rt=RoutingTableAccess().get(); + nb = NotificationBoardAccess().get(); + nb->subscribe(this, NF_SUB_AUTH_ACK); + nb->subscribe(this, NF_SUB_AUTH_NACK); + nb->subscribe(this, NF_SUB_PDN_ACK); + } +} + +void NAS::handleMessage(cMessage *msg) { + if (appType == RELAY_APPL_TYPE) { + if (msg->isSelfMessage()) { + msg->setName("Relay-Message"); +// NASRelay *relay = (NASRelay*)msg->getContextPointer(); +// relay->processTimer(msg); + } else if (msg->arrivedOn("radioIn")) { + EV << "NAS-RELAY: Received message from radio. Relaying message.\n"; + handleMessageFromRadio(msg); + } else { + EV << "NAS-RELAY: Received message from S1AP. Relaying message.\n"; + handleMessageFromS1AP(msg); + } + } else if (appType == MME_APPL_TYPE) { + msg->setName("MME-Message"); + if (msg->isSelfMessage()) { + + } else { + EV << "NAS-MME: Received message from S1AP. Processing message.\n"; + handleMessageFromS1AP(msg); + } + } else if (appType == UE_APPL_TYPE) { + msg->setName("UE-Message"); + if (msg->isSelfMessage()) { + + } else { + EV << "NAS-UE: Received message from radio. Processing message.\n"; + handleMessageFromRadio(msg); + } + } +} + +void NAS::handleMessageFromS1AP(cMessage *msg) { + S1APControlInfo *ctrl = dynamic_cast(msg->removeControlInfo()); + char *buf = (char*)calloc(ctrl->getValueArraySize(), sizeof(char)); + char *p = buf; + for (unsigned i = 0; i < ctrl->getValueArraySize(); i++) + buf[i] = ctrl->getValue(i); + Subscriber *sub = subT->findSubscriberForId(ctrl->getSubEnbId(), ctrl->getSubMmeId()); + if (sub == NULL) { + EV << "NAS: Unknown subscriber. Dropping message.\n"; + goto end; + } + { + NASPlainMessage *nmsg = new NASPlainMessage(); + NASHeader hdr = NASSerializer().parseHeader(p); + if (!hdr.getLength()) { + EV << "NAS: Message decoding error. Dropping message.\n"; + goto end; + } + p += hdr.getLength(); + nmsg->setHdr(hdr); + switch(hdr.getMsgType()) { + case AttachRequest: { + nmsg->setName("Attach-Request"); + if (!NASUtils().parseAttachRequest(nmsg, p)) { + EV << "NAS: Message processing error. Dropping message.\n"; + goto end; + } + sub->setMmeId(subT->genMmeId()); + sub->initEntities(appType); + EMMEntity *emm = sub->getEmmEntity(); + emm->setModule(this); + ESMEntity *esm = sub->getEsmEntity(); + esm->setModule(this); + emm->processAttachRequest(nmsg); + sub->setStatus(SUB_PENDING); + nb->fireChangeNotification(NF_SUB_NEEDS_AUTH, sub); + break; + } + case AttachAccept: { + nmsg->setName("Attach-Accept"); + if (!NASUtils().parseAttachAccept(nmsg, p)) { + EV << "NAS: Message parsing error. Dropping message.\n"; + goto end; + } + sub->setStatus(SUB_ACTIVE); + sendToRadio(nmsg, sub->getChannelNr()); + break; + } + case AttachReject: { + nmsg->setName("Attach-Reject"); + if (!NASUtils().parseAttachReject(nmsg, p)) { + EV << "NAS: Message parsing error. Dropping message.\n"; + goto end; + } + sub->setStatus(SUB_INACTIVE); + sendToRadio(nmsg, sub->getChannelNr()); + break; + } + case AttachComplete: { + nmsg->setName("Attach-Complete"); + if (!NASUtils().parseAttachComplete(nmsg, p)) { + EV << "NAS: Message parsing error. Dropping message.\n"; + goto end; + } + sub->getEmmEntity()->processAttachComplete(nmsg); + break; + } + default:; + } + } + end: + delete msg; + delete ctrl; +} + +void NAS::handleMessageFromRadio(cMessage *msg) { + NASPlainMessage *nmsg = check_and_cast(msg); + PhyControlInfo *ctrl = dynamic_cast(nmsg->removeControlInfo()); + Subscriber *sub = subT->findSubscriberForChannel(ctrl->getChannelNumber()); + if (appType == RELAY_APPL_TYPE) { + if (sub == NULL) { + sub = new Subscriber(); + sub->initEntities(appType); + // only dummy PDN connection to store the UE bearer contexts + PDNConnection *conn = new PDNConnection(); + conn->setOwner(sub->getEsmEntity()); + sub->getEsmEntity()->addPDNConnection(conn, true); + sub->setChannelNr(ctrl->getChannelNumber()); + sub->setEnbId(subT->genEnbId()); + sub->setMmeId(0); + sub->setStatus(SUB_PENDING); + subT->push_back(sub); + } + sendToS1AP(nmsg, sub->getEnbId(), sub->getMmeId()); + } else { + if (sub == NULL) { + delete nmsg; + return; + } +// NASUtils().printMessage(nmsg); + switch(nmsg->getHdr().getMsgType()) { + case AttachAccept: { + EMMEntity *emm = sub->getEmmEntity(); + ESMEntity *esm = sub->getEsmEntity(); + emm->processAttachAccept(nmsg); + InterfaceEntry *entry = ift->getInterfaceByName("UERadioInterface"); + if(entry) + { + entry->ipv4Data()->setIPAddress(esm->getDefPDNConnection()->getSubscriberAddress().get4()); + entry->setMACAddress(MACAddress::generateAutoAddress()); + entry->setMtu(1500); + IPRoute *route=new IPRoute(); + route->setInterface(entry); + rt->addRoute(route); + +// nb->fireChangeNotification(NF_INTERFACE_CONFIG_CHANGED, route); + } + break; + } + case AttachReject: { + EMMEntity *emm = sub->getEmmEntity(); + emm->processAttachReject(nmsg); + break; + } + default: + delete nmsg; + } + } + delete ctrl; +} + +void NAS::sendToS1AP(NASPlainMessage *nmsg, unsigned subEnbId, unsigned subMmeId) { + cMessage *msg = new cMessage(nmsg->getName()); + S1APControlInfo *ctrl = new S1APControlInfo(); + ctrl->setSubEnbId(subEnbId); + ctrl->setSubMmeId(subMmeId); + switch(nmsg->getHdr().getMsgType()) { + case AttachRequest: + ctrl->setProcId(id_initialUEMessage); + break; + case AttachAccept: + ctrl->setProcId(id_InitialContextSetup); + break; + case AttachReject: + ctrl->setProcId(id_downlinkNASTransport); + break; + case AttachComplete: + ctrl->setProcId(id_uplinkNASTransport); + break; + default:; + } + char *nasPdu; + unsigned nasPduLen = NASSerializer().serialize(nmsg, nasPdu); + ctrl->setValueArraySize(nasPduLen); + for (unsigned i = 0; i < ctrl->getValueArraySize(); i++) + ctrl->setValue(i, nasPdu[i]); + msg->setControlInfo(ctrl); + + delete nmsg; + send(msg, gate("s1apOut")); +} + +void NAS::sendToRadio(NASPlainMessage *nmsg, int channelNr) { + PhyControlInfo *ctrl = new PhyControlInfo(); + ctrl->setChannelNumber(channelNr); + ctrl->setBitrate(2e6); + nmsg->setControlInfo(ctrl); + send(nmsg, gate("radioOut")); +} + +void NAS::loadConfigFromXML(const char *filename) { + cXMLElement* config = ev.getXMLDocument(filename); + if (config == NULL) + error("NAS: Cannot read configuration from file: %s", filename); + + cXMLElement* nasNode = config->getElementByPath("NAS"); + if (nasNode == NULL) + error("NAS: No configuration for NAS"); + + if (!nasNode->getAttribute("appType")) + error("NAS: No for NAS"); + appType = atoi(nasNode->getAttribute("appType")); + if (appType == UE_APPL_TYPE) { + /* TODO */ + // no cell available + channelNumber = par("channelNumber"); + + Subscriber *sub = new Subscriber(); + sub->setChannelNr(channelNumber); + sub->initEntities(appType); + subT->push_back(sub); + loadESMConfigFromXML(*nasNode); + loadEMMConfigFromXML(*nasNode); + } else if (appType == RELAY_APPL_TYPE) { + + } else { + + } +} + +void NAS::loadEMMConfigFromXML(const cXMLElement &nasNode) { + EMMEntity *emm = subT->at(0)->getEmmEntity(); + emm->setModule(this); + + // go to EMM_DEREGISTERED_PLMN_SEARCH + emm->performStateTransition(SwitchOn); + + const char *imsi = nasNode.getAttribute("imsi"); + if (!imsi) { + emm->performStateTransition(NoUSIM); + return; + } + emm->setImsi(LTEUtils().toIMSI(imsi)); + // go to EMM_DEREGISTERED_NORMAL_SERVICE + emm->performStateTransition(CellFoundPermittedPLMN); + /* TODO */ + // limited-service + /* TODO */ + // manual network mode +} + +void NAS::loadESMConfigFromXML(const cXMLElement &nasNode) { + ESMEntity *esm = subT->at(0)->getEsmEntity(); + esm->setModule(this); + PDNConnection *conn = new PDNConnection(esm); + esm->addPDNConnection(conn, true); +} + +void NAS::receiveChangeNotification(int category, const cPolymorphic *details) { + Enter_Method_Silent(); + if (category == NF_SUB_AUTH_ACK) { + EV << "NAS: Received NF_SUB_AUTH_ACK notification. Processing notification.\n"; + Subscriber *sub = check_and_cast(details); + nb->fireChangeNotification(NF_SUB_NEEDS_PDN, sub->getDefaultPDNConn()); + } else if (category == NF_SUB_AUTH_NACK) { + EV << "NAS: Received NF_SUB_AUTH_NACK notification. Processing notification.\n"; + Subscriber *sub = check_and_cast(details); + EMMEntity *emm = sub->getEmmEntity(); + emm->performStateTransition(AttachRejected); + } else if (category == NF_SUB_PDN_ACK) { + EV << "NAS: Received NF_SUB_PDN_ACK notification. Processing notification.\n"; + PDNConnection *conn = check_and_cast(details); + EMMEntity *emm = conn->getOwner()->getPeer(); + if (emm->getState() == EMM_DEREGISTERED_N) { + emm->performStateTransition(AttachAccepted); + } + } +} diff --git a/src/applications/nas/NAS.h b/src/applications/nas/NAS.h new file mode 100644 index 0000000..e5cc3bd --- /dev/null +++ b/src/applications/nas/NAS.h @@ -0,0 +1,74 @@ +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// 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. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +#ifndef NAS_H_ +#define NAS_H_ + +#include +#include "NASMessage_m.h" +#include "SubscriberTableAccess.h" +#include "NotificationBoard.h" +#include "IInterfaceTable.h" +#include "RoutingTable.h" + +//#include "S1APConnectionTableAccess.h" +//#include "NASRelayTable.h" + +#define UE_APPL_TYPE 0 +#define MME_APPL_TYPE 1 +#define RELAY_APPL_TYPE 2 +#define SEND_RETRY_TIMER 2 + +class NAS : public cSimpleModule, public INotifiable { +private: + NotificationBoard *nb; // only for MME + unsigned char appType; + int channelNumber; + SubscriberTable *subT; + IInterfaceTable *ift; + IRoutingTable *rt; +public: + NAS(); + virtual ~NAS(); + + /* module */ + virtual int numInitStages() const { return 5; } + virtual void initialize(int stage); + void loadConfigFromXML(const char *filename); + void loadEMMConfigFromXML(const cXMLElement &nasNode); + void loadESMConfigFromXML(const cXMLElement &nasNode); + virtual void handleMessage(cMessage *msg); + + /* getter */ + int getChannelNumber() { return channelNumber; } + + /* message */ + void handleMessageFromS1AP(cMessage *msg); + void handleMessageFromRadio(cMessage *msg); + void sendToS1AP(NASPlainMessage *nmsg, unsigned subEnbId, unsigned subMmeId); + void sendToRadio(NASPlainMessage *nmsg, int channelNr); + + /* notification */ + virtual void receiveChangeNotification(int category, const cPolymorphic *details); + + /* timers */ + void dropTimer(cMessage *timer) { drop(timer); } + void takeTimer(cMessage *timer) { take(timer); } + +// S1APConnection *findConnection() { return s1apT->findConnection(); } + +}; + +#endif /* NAS_H_ */ diff --git a/src/applications/nas/NAS.ned b/src/applications/nas/NAS.ned new file mode 100644 index 0000000..60c2193 --- /dev/null +++ b/src/applications/nas/NAS.ned @@ -0,0 +1,31 @@ +// +// Copyright (C) 2012 Calin Cerchez +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// 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. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +package inet.applications.nas; + +simple NAS +{ + parameters: + @display("i=block/control"); + string configFile = default(""); + int channelNumber = default(0); + gates: + input radioIn; + output radioOut; + input s1apIn; + output s1apOut; +} \ No newline at end of file diff --git a/src/applications/nas/NASMessage.msg b/src/applications/nas/NASMessage.msg new file mode 100644 index 0000000..f91ddd2 --- /dev/null +++ b/src/applications/nas/NASMessage.msg @@ -0,0 +1,124 @@ +// +// Copyright (C) 2012 Calin Cerchez +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// 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. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +cplusplus {{ + #define NAS_ESM_HEADER_SIZE 3 + #define NAS_EMM_HEADER_SIZE 2 +}} + +enum InfoElemFormats +{ + IE_V = 1; + IE_LV = 2; + IE_LVE = 3; + IE_TV = 4; + IE_TLV = 5; + IE_TLVE = 6; +} + +enum InfoElemTypes +{ + IEType1 = 1; // TV or V with value length 1/2 + IEType2 = 2; // T with value length 0 + IEType3 = 3; // V or TV with value fixed length >= 1 + IEType4 = 4; // LV or TLV with value variable length < 255 + IEType6 = 6; // TLVE and LVE with value variable length > 255 +} + +enum ProtocolDiscriminator +{ + ESMMessage = 2; + EMMMessage = 7; +} + +enum SecurityHeaderTypes +{ + PlainNASMessage = 0; +} + +enum NASMessageTypes +{ + AttachRequest = 65; + AttachAccept = 66; + AttachComplete = 67; + AttachReject = 68; + ActDefEPSBearerCtxtReq = 193; + ActDefEPSBearerCtxtAcc = 194; + PDNConnectivityRequest = 208; +} + +enum RequestTypeValues +{ + InitialRequest = 1; + Handover = 2; + Unused_RequestType = 3; + Emergency = 4; +} + +enum EPSAttachTypes +{ + EPSAttach = 1; + CombinedAttach = 2; + EmergencyAttach = 6; + Reserved_EPSAttach = 7; +} + +enum EPSMobileIdentityTypes +{ + IMSI_ID = 1; + GUTI_ID = 6; + IMEI_ID = 3; +} + +enum NASInfoElemTypes +{ + EPSMobileId = 50; +} + +enum EMMCauses +{ + IMSIUnknownInHSS = 2; + IllegalUE = 3; + EPSServNotAllowed = 7; + EPSServNonEPSServNotAllowed = 8; +} + +class NASHeader +{ + unsigned char epsBearerId; // for ESM + unsigned char secHdrType; // for EMM + unsigned char protDiscr; + unsigned char procTransId; // only for ESM + unsigned char msgType; // 01------ for EMM + // 11------ for ESM + unsigned char length = 0; // only for serializing/parsing usage +} + +class NASInfoElem +{ + unsigned char format; + unsigned char ieType; + unsigned char type; + char value[]; // length = value array size +} + +packet NASPlainMessage +{ + NASHeader hdr; + NASInfoElem ies[]; + int encapPos; // position of encapsulated packet +} \ No newline at end of file diff --git a/src/applications/nas/NASSerializer.cc b/src/applications/nas/NASSerializer.cc new file mode 100644 index 0000000..9c187ec --- /dev/null +++ b/src/applications/nas/NASSerializer.cc @@ -0,0 +1,247 @@ +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// 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. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +/* See 3GPP TS 24007 chapter 11.2 */ + +#include "NASSerializer.h" +#include "NASUtils.h" +#include + +NASSerializer::NASSerializer() { + // TODO Auto-generated constructor stub + shift = 0; +} + +NASSerializer::~NASSerializer() { + // TODO Auto-generated destructor stub +} + +unsigned NASSerializer::calcLength(NASPlainMessage *msg) { + unsigned len = msg->getHdr().getMsgType() & 0x80 ? NAS_ESM_HEADER_SIZE : NAS_EMM_HEADER_SIZE; + for (unsigned i = 0; i < msg->getIesArraySize(); i++) { + NASInfoElem ie = msg->getIes(i); + switch(ie.getIeType()) { + case IEType1: + if (ie.getFormat() == IE_TV) + len++; + else if (ie.getFormat() == IE_V) { + len += (shift + 1) % 2; + shift = (shift + 1) % 2; + } + break; + case IEType2: + len++; + break; + case IEType3: + len += ie.getValueArraySize(); + if (ie.getFormat() == IE_TV) + len++; + break; + case IEType4: + len += ie.getValueArraySize() + 1; + if (ie.getFormat() == IE_TLV) + len++; + break; + case IEType6: + len += ie.getValueArraySize() + 2; + if (ie.getFormat() == IE_TLVE) + len++; + break; + default:; + } + } + return len; +} + +unsigned NASSerializer::serializeHeader(NASHeader hdr, char *p) { + + if (hdr.getMsgType() & 0x80) { // is ESM message + *((unsigned char*)(p++)) = (hdr.getEpsBearerId() << 4) | (hdr.getProtDiscr()); + *((unsigned char*)p++) = hdr.getProcTransId(); + } else { // for EMM message + *((unsigned char*)(p++)) = (hdr.getSecHdrType() << 4) | (hdr.getProtDiscr()); + } + *((unsigned char*)p++) = hdr.getMsgType(); + return hdr.getLength(); +} + +NASHeader NASSerializer::parseHeader(char *p) { + NASHeader hdr = NASHeader(); + hdr.setProtDiscr(*((unsigned char*)(p)) & 0x0f); + if (hdr.getProtDiscr() == ESMMessage) { // is ESM message + hdr.setEpsBearerId((*((unsigned char*)(p++)) >> 4) & 0x0f); + hdr.setLength(NAS_ESM_HEADER_SIZE); + hdr.setProcTransId(*((unsigned char*)p++)); + } else if (hdr.getProtDiscr() == EMMMessage) { // for EMM message + hdr.setSecHdrType((*((unsigned char*)(p++)) >> 4) & 0x0f); + hdr.setLength(NAS_EMM_HEADER_SIZE); + } + hdr.setMsgType(*((unsigned char*)p++)); + return hdr; +} + +unsigned NASSerializer::serializeIE(NASInfoElem ie, char *p) { + char *begin = p; + switch(ie.getIeType()) { + case IEType1: + if (ie.getFormat() == IE_TV) { + *((unsigned char*)(p++)) = (ie.getType() << 4) | (ie.getValue(0) & 0x0f); + } else if (ie.getFormat() == IE_V) { // shifting only for type 1 (after one IE type 1 follows another with type 1) + *((unsigned char*)(p)) += ((unsigned char)ie.getValue(0)) << (((shift + 1) % 2) * 4); + p += shift % 2; + shift = (shift + 1) % 2; + } + break; + case IEType2: + *((unsigned char*)(p++)) = ie.getType(); + break; + case IEType3: + if (ie.getFormat() == IE_TV) + *((unsigned char*)(p++)) = ie.getType(); + for (unsigned i = 0; i < ie.getValueArraySize(); i++) + *((char*)(p++)) = ie.getValue(i); + break; + case IEType4: + if (ie.getFormat() == IE_TLV) + *((unsigned char*)(p++)) = ie.getType(); + *((unsigned char*)(p++)) = ie.getValueArraySize(); + for (unsigned i = 0; i < ie.getValueArraySize(); i++) + *((char*)(p++)) = ie.getValue(i); + break; + case IEType6: + if (ie.getFormat() == IE_TLVE) + *((unsigned char*)(p++)) = ie.getType(); + *((unsigned short*)p) = ntohs(ie.getValueArraySize()); + p += 2; + for (unsigned i = 0; i < ie.getValueArraySize(); i++) + *((char*)(p++)) = ie.getValue(i); + break; + default:; + } + return p - begin; +} + +NASInfoElem NASSerializer::parseIE(char *p, unsigned char format, unsigned char ieType, unsigned short length) { + NASInfoElem ie = NASInfoElem(); + ie.setIeType(ieType); + ie.setFormat(format); + ie.setValueArraySize(length); + char *begin = p; + switch(ie.getIeType()) { + case IEType1: + ie.setValueArraySize(1); + if (ie.getFormat() == IE_TV) { + ie.setType((*((unsigned char*)(p)) >> 4) & 0x0f); + ie.setValue(0, *((unsigned char*)(p++)) & 0x0f); + } else if (ie.getFormat() == IE_V) { // shifting only for type 1 (after one IE type 1 follows another with type 1) + ie.setValue(0, *((unsigned char*)(p)) >> (((shift + 1) % 2) * 4) & 0x0f); + p += shift % 2; + shift = (shift + 1) % 2; + } + break; + case IEType2: + ie.setType(*((unsigned char*)(p++))); + break; + case IEType3: + if (ie.getFormat() == IE_TV) + ie.setType(*((unsigned char*)(p++))); + for (unsigned i = 0; i < ie.getValueArraySize(); i++) + ie.setValue(i, *((char*)(p++))); + break; + case IEType4: + if (ie.getFormat() == IE_TLV) + ie.setType(*((unsigned char*)(p++))); + ie.setValueArraySize(*((unsigned char*)(p++))); + for (unsigned i = 0; i < ie.getValueArraySize(); i++) + ie.setValue(i, *((char*)(p++))); + break; + case IEType6: + if (ie.getFormat() == IE_TLVE) + ie.setType(*((unsigned char*)(p++))); + ie.setValueArraySize(ntohs(*((unsigned short*)p))); + p += 2; + for (unsigned i = 0; i < ie.getValueArraySize(); i++) + ie.setValue(i, *((char*)(p++))); + break; + default:; + } + skip = p - begin; + return ie; +} + +unsigned NASSerializer::serialize(NASPlainMessage *msg, char *&buffer) { + NASPlainMessage *encMsg = NULL; + if (msg->getEncapsulatedPacket() != NULL) + encMsg = check_and_cast(msg->decapsulate()); + unsigned len = calcLength(msg); + int encPos = -1; + if (encMsg != NULL) { + len += calcLength(encMsg) + 2; + encPos = msg->getEncapPos(); + } + buffer = (char*)calloc(len, sizeof(char)); + char *p = buffer; + + p += serializeHeader(msg->getHdr(), p); + for (unsigned i = 0; i < msg->getIesArraySize(); i++) { + if ((i == (unsigned)encPos) && (encPos != -1)) { + char *esmCont; + unsigned esmContLen = NASSerializer().serialize(encMsg, esmCont); + NASInfoElem ie = NASUtils().createIE(IE_LVE, IEType6, 0, esmContLen, esmCont); + p += serializeIE(ie, p); + } + p += serializeIE(msg->getIes(i), p); + } + delete encMsg; + return len; +} + +//NASPlainMessage *NASSerializer::parse(char *buffer, unsigned len) { +// NASPlainMessage *msg = new NASPlainMessage(); +// char *p = buffer; +// NASHeader hdr = parseHeader(p); +// if (!hdr.getLength()) +// return NULL; +// p += hdr.getLength(); +// msg->setHdr(hdr); +// switch(hdr.getMsgType()) { +// case AttachRequest: +// msg->setIesArraySize(5); +// msg->setName("Attach-Request"); +// /* NAS key set identifier */ +// msg->setIes(0, NASUtils().createIE(IE_V, IEType1)); +// +// /* EPS attach type */ +// msg->setIes(1, NASUtils().createIE(IE_V, IEType1)); +// +// /* EPS mobile identity */ +// msg->setIes(2, NASUtils().createIE(IE_LV, IEType4)); +// +// /* UE network capability */ +// msg->setIes(3, NASUtils().createIE(IE_LV, IEType4)); +// +// /* ESM message container */ +// msg->setIes(4, NASUtils().createIE(IE_LVE, IEType6)); +// break; +// case PDNConnectivityRequest: +// break; +// default:; +// } +// for (unsigned i = 0; i < msg->getIesArraySize(); i++) { +// NASInfoElem ie = msg->getIes(i); +// p += parseIE(&ie, p); +// } +// return msg; +//} diff --git a/src/applications/nas/NASSerializer.h b/src/applications/nas/NASSerializer.h new file mode 100644 index 0000000..b8d1c32 --- /dev/null +++ b/src/applications/nas/NASSerializer.h @@ -0,0 +1,40 @@ +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// 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. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +#ifndef NASSERIALIZER_H_ +#define NASSERIALIZER_H_ + +#include "NASMessage_m.h" + +class NASSerializer { +private: + unsigned char shift; + unsigned serializeHeader(NASHeader hdr, char *p); + + unsigned serializeIE(NASInfoElem ie, char *p); + + unsigned calcLength(NASPlainMessage *msg); +public: + unsigned short skip; + NASSerializer(); + virtual ~NASSerializer(); + + unsigned serialize(NASPlainMessage *msg, char *&buffer); + NASHeader parseHeader(char *p); + NASInfoElem parseIE(char *p, unsigned char format, unsigned char ieType, unsigned short length = 0); +// NASPlainMessage *parse(char *buffer, unsigned len); +}; + +#endif /* NASSERIALIZER_H_ */ diff --git a/src/applications/nas/NASUtils.cc b/src/applications/nas/NASUtils.cc new file mode 100644 index 0000000..eae21a9 --- /dev/null +++ b/src/applications/nas/NASUtils.cc @@ -0,0 +1,339 @@ +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// 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. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +#include "NASUtils.h" +#include "NASSerializer.h" +#include "LTEUtils.h" + +NASUtils::NASUtils() { + // TODO Auto-generated constructor stub + +} + +NASUtils::~NASUtils() { + // TODO Auto-generated destructor stub +} + +NASHeader NASUtils::createHeader(unsigned char epsBearerId, unsigned char secHdrType, unsigned char protDiscr, unsigned char procTransId, unsigned char msgType) { + NASHeader hdr = NASHeader(); + hdr.setEpsBearerId(epsBearerId); + hdr.setSecHdrType(secHdrType); + hdr.setProtDiscr(protDiscr); + hdr.setProcTransId(procTransId); + hdr.setMsgType(msgType); + hdr.setLength(protDiscr == ESMMessage ? NAS_ESM_HEADER_SIZE : NAS_EMM_HEADER_SIZE); + return hdr; +} + + +NASInfoElem NASUtils::createIE(unsigned char format, unsigned char ieType, unsigned char type, char value) { + NASInfoElem ie = NASInfoElem(); + ie.setFormat(format); + ie.setIeType(ieType); + ie.setType(type); + ie.setValueArraySize(1); + ie.setValue(0, value); + return ie; +} + +NASInfoElem NASUtils::createIE(unsigned char format, unsigned char ieType, unsigned char type, unsigned short length, const char *value) { + NASInfoElem ie = NASInfoElem(); + ie.setFormat(format); + ie.setIeType(ieType); + ie.setType(type); + ie.setValueArraySize(length); + for (unsigned i = 0; i < length; i++) + ie.setValue(i, value[i]); + return ie; +} + +//NASInfoElem NASUtils::createIE(unsigned char format, unsigned char ieType, unsigned short length) { +// NASInfoElem ie = NASInfoElem(); +// ie.setFormat(format); +// ie.setIeType(ieType); +// ie.setValueArraySize(length); +// return ie; +//} + +NASInfoElem NASUtils::createEPSMobileIdIE(unsigned char format, unsigned char typeOfId, char *id) { + char *epsMobileId; + unsigned short length; + if (typeOfId == IMSI_ID) { + bool odd = ((id[1] & 0xf0) == 0xf0); + length = odd ? IMSI_CODED_SIZE : IMSI_CODED_SIZE + 1; + epsMobileId = (char*)calloc(length, sizeof(char)); + epsMobileId[0] = (odd ? 0x08 : 0) + typeOfId; + if (odd) { + epsMobileId[0] += ((unsigned char)id[0] << 4); + epsMobileId[1] = ((unsigned char)id[0] >> 4) + ((unsigned char)id[1] << 4); + memcpy(epsMobileId + 2, id + 2, IMSI_CODED_SIZE - 2); + } else { + unsigned j = 1; + unsigned l = 0; + unsigned k = 0; + for (unsigned i = 0; i < length; i += k % 2) { + epsMobileId[i] += (k % 2) ? ((unsigned char)(id[l] & ((j % 2) ? 0x0f : 0xf0)) >> 4) : ((unsigned char)(id[l] & ((j % 2) ? 0x0f : 0xf0)) << 4); + j++; + k++; + l += j % 2; + } + epsMobileId[l] += 0xf0; + } + } + return createIE(format, IEType4, EPSMobileId, length, epsMobileId); +} + +char *NASUtils::processIE(NASInfoElem ie) { + char *value = (char*)calloc(ie.getValueArraySize(), sizeof(char)); + for (unsigned i = 0; i < ie.getValueArraySize(); i++) + value[i] = ie.getValue(i); + return value; +} + +unsigned NASUtils::processEPSMobileIdIE(NASInfoElem ie, char *&id) { + char *value = processIE(ie); + unsigned char typeOfId = value[0] & 0x07; + bool odd = value[0] & 0x08; + unsigned short length; + if (typeOfId == IMSI_ID) { + length = IMSI_CODED_SIZE; + id = (char*)calloc(length, sizeof(char)); + if (odd) { + id[0] = ((value[0] >> 4) & 0x0f) + ((value[1] << 4) & 0xf0); + id[1] = ((value[1] >> 4) & 0x0f) + 0xf0; + memcpy(id + 2, value + 2, IMSI_CODED_SIZE - 2); + } else { + for (unsigned i = 0; i < length; i++) { + id[i] += ((value[i] >> 4) & 0x0f) + ((value[i + 1] << 4) & 0xf0); + } + } + } + return length; +} + +bool NASUtils::parseAttachRequest(NASPlainMessage *msg, char *buf) { + char *p = buf; + msg->setIesArraySize(5); + NASSerializer parser = NASSerializer(); + + /* NAS key set identifier */ + msg->setIes(0, parser.parseIE(p, IE_V, IEType1)); + p += parser.skip; + + /* EPS attach type */ + msg->setIes(1, parser.parseIE(p, IE_V, IEType1)); + p += parser.skip; + + /* EPS mobile identity */ + msg->setIes(2, parser.parseIE(p, IE_LV, IEType4)); + p += parser.skip; + + /* UE network capability */ + msg->setIes(3, parser.parseIE(p, IE_LV, IEType4)); + p += parser.skip; + + /* ESM message container */ + msg->setIes(4, parser.parseIE(p, IE_LVE, IEType6)); + p += parser.skip; + + buf = NASUtils().processIE(msg->getIes(4)); + p = buf; + NASPlainMessage *smsg = new NASPlainMessage(); + NASHeader hdr = NASSerializer().parseHeader(p); + if (!hdr.getLength()) { + EV << "NAS: Message decoding error. Dropping message.\n"; + return false; + } + p += hdr.getLength(); + smsg->setHdr(hdr); + + if (smsg->getHdr().getMsgType() == PDNConnectivityRequest) { + smsg->setName("PDN-Connectivity-Request"); + NASUtils().parsePDNConnectivityRequest(smsg, p); + msg->encapsulate(smsg); + } else { + delete smsg; + } + return true; +} + +void NASUtils::parsePDNConnectivityRequest(NASPlainMessage *msg, char *buf) { + char *p = buf; + msg->setIesArraySize(2); + NASSerializer parser = NASSerializer(); + + /* PDN type */ + msg->setIes(0, parser.parseIE(p, IE_V, IEType1)); + p += parser.skip; + + /* Request type */ + msg->setIes(1, parser.parseIE(p, IE_V, IEType1)); + p += parser.skip; +} + +bool NASUtils::parseAttachAccept(NASPlainMessage *msg, char *buf) { + char *p = buf; + msg->setIesArraySize(5); + NASSerializer parser = NASSerializer(); + + /* EPS attach result */ + msg->setIes(0, parser.parseIE(p, IE_V, IEType1)); + p += parser.skip; + + /* Spare half octet */ + msg->setIes(1, parser.parseIE(p, IE_V, IEType1)); + p += parser.skip; + + /* T3412 value */ + msg->setIes(2, parser.parseIE(p, IE_V, IEType3, 1)); + p += parser.skip; + + /* TAI list */ + msg->setIes(3, parser.parseIE(p, IE_LV, IEType4)); + p += parser.skip; + + /* ESM message container */ + msg->setIes(4, parser.parseIE(p, IE_LVE, IEType6)); + p += parser.skip; + + buf = NASUtils().processIE(msg->getIes(4)); + p = buf; + NASPlainMessage *smsg = new NASPlainMessage(); + NASHeader hdr = NASSerializer().parseHeader(p); + if (!hdr.getLength()) { + EV << "NAS: Message decoding error. Dropping message.\n"; + return false; + } + p += hdr.getLength(); + smsg->setHdr(hdr); + + if (smsg->getHdr().getMsgType() == ActDefEPSBearerCtxtReq) { + smsg->setName("Activate-Default-Bearer-Request"); + NASUtils().parseActDefBearerRequest(smsg, p); + msg->encapsulate(smsg); + } else { + delete smsg; + } + + return true; +} + +void NASUtils::parseActDefBearerRequest(NASPlainMessage *msg, char *buf) { + char *p = buf; + msg->setIesArraySize(3); + NASSerializer parser = NASSerializer(); + + /* EPS QoS */ + msg->setIes(0, parser.parseIE(p, IE_LV, IEType4)); + p += parser.skip; + + /* APN */ + msg->setIes(1, parser.parseIE(p, IE_LV, IEType4)); + p += parser.skip; + + /* PDN address */ + msg->setIes(2, parser.parseIE(p, IE_LV, IEType4)); +} + +bool NASUtils::parseAttachReject(NASPlainMessage *msg, char *buf) { + char *p = buf; + msg->setIesArraySize(1); + NASSerializer parser = NASSerializer(); + + /* T3412 value */ + msg->setIes(0, parser.parseIE(p, IE_V, IEType3, 1)); + p += parser.skip; + + /* ESM message container */ +// msg->setIes(0, parser.parseIE(p, IE_LVE, IEType6)); +// p += parser.skip; +// +// buf = NASUtils().processIE(msg->getIes(0)); +// p = buf; +// NASPlainMessage *smsg = new NASPlainMessage(); +// NASHeader hdr = NASSerializer().parseHeader(p); +// if (!hdr.getLength()) { +// EV << "NAS: Message decoding error. Dropping message.\n"; +// return false; +// } +// p += hdr.getLength(); +// smsg->setHdr(hdr); +// +// if (smsg->getHdr().getMsgType() == ActDefEPSBearerCtxtAcc) { +// smsg->setName("Activate-Default-Bearer-Request"); +// NASUtils().parseActDefBearerAccept(smsg, p); +// msg->encapsulate(smsg); +// } else { +// delete smsg; +// } + + return true; +} + +bool NASUtils::parseAttachComplete(NASPlainMessage *msg, char *buf) { + char *p = buf; + msg->setIesArraySize(1); + NASSerializer parser = NASSerializer(); + + /* ESM message container */ + msg->setIes(0, parser.parseIE(p, IE_LVE, IEType6)); + p += parser.skip; + + buf = NASUtils().processIE(msg->getIes(0)); + p = buf; + NASPlainMessage *smsg = new NASPlainMessage(); + NASHeader hdr = NASSerializer().parseHeader(p); + if (!hdr.getLength()) { + EV << "NAS: Message decoding error. Dropping message.\n"; + return false; + } + p += hdr.getLength(); + smsg->setHdr(hdr); + + if (smsg->getHdr().getMsgType() == ActDefEPSBearerCtxtAcc) { + smsg->setName("Activate-Default-Bearer-Request"); + NASUtils().parseActDefBearerAccept(smsg, p); + msg->encapsulate(smsg); + } else { + delete smsg; + } + + return true; +} + +void NASUtils::printMessage(NASPlainMessage *msg) { + NASHeader hdr = msg->getHdr(); + EV << "===================================================\n"; + EV << "NASMessage:\n"; + if ((hdr.getMsgType() & 0xc0) == 0xc0) + EV << "EPSBearerId: " << (unsigned)hdr.getEpsBearerId() << endl; + if ((hdr.getMsgType() & 0x80) == 0x80) + EV << "SecHdrType: " << (unsigned)hdr.getSecHdrType() << endl; + EV << "ProtDiscr: " << (unsigned)hdr.getProtDiscr() << endl; + if ((hdr.getMsgType() & 0xc0) == 0xc0) + EV << "ProcTransId: " << (unsigned)hdr.getProcTransId() << endl; + EV << "MsgType: " << (unsigned)hdr.getMsgType() << endl; + for (unsigned i = 0; i < msg->getIesArraySize(); i++) { + NASInfoElem ie = msg->getIes(i); + EV << "NASInfoElem nr. " << i << ": "; + char str[ie.getValueArraySize() * 3 + 1]; + int k = 0; + for (unsigned j = 0; j < ie.getValueArraySize(); j++, k += 3) + sprintf(&str[k], " %02x ", (unsigned char)ie.getValue(j)); + str[ie.getValueArraySize() * 3] = '\0'; + EV << str << endl; + } + EV << "===================================================\n"; +} diff --git a/src/applications/nas/NASUtils.h b/src/applications/nas/NASUtils.h new file mode 100644 index 0000000..2ac86bd --- /dev/null +++ b/src/applications/nas/NASUtils.h @@ -0,0 +1,47 @@ +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// 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. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +#ifndef NASUTILS_H_ +#define NASUTILS_H_ + +#include "NASMessage_m.h" + +class NASUtils { +public: + NASUtils(); + virtual ~NASUtils(); + + NASHeader createHeader(unsigned char epsBearerId, unsigned char secHdrType, unsigned char protDiscr, unsigned char procTransId, unsigned char msgType); + NASInfoElem createIE(unsigned char format, unsigned char ieType, unsigned char type, char value); + NASInfoElem createIE(unsigned char format, unsigned char ieType, unsigned char type, unsigned short length, const char *value); + NASInfoElem createEPSMobileIdIE(unsigned char format, unsigned char typeOfId, char *id); + +// NASInfoElem createIE(); // for parsing usage + + char *processIE(NASInfoElem ie); + unsigned processEPSMobileIdIE(NASInfoElem ie, char *&id); + + bool parseAttachRequest(NASPlainMessage *msg, char *buf); + bool parseAttachAccept(NASPlainMessage *msg, char *buf); + bool parseAttachReject(NASPlainMessage *msg, char *buf); + bool parseAttachComplete(NASPlainMessage *msg, char *buf); + void parsePDNConnectivityRequest(NASPlainMessage *msg, char *buf); + void parseActDefBearerRequest(NASPlainMessage *msg, char *buf); + void parseActDefBearerAccept(NASPlainMessage *msg, char *buf) {} + + void printMessage(NASPlainMessage *msg); +}; + +#endif /* NASUTILS_H_ */ diff --git a/src/networklayer/gtp/GTPControl.cc b/src/networklayer/gtp/GTPControl.cc index 9ffd4bf..2f05822 100644 --- a/src/networklayer/gtp/GTPControl.cc +++ b/src/networklayer/gtp/GTPControl.cc @@ -1,4 +1,6 @@ // +// Copyright (C) 2012 Calin Cerchez +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or @@ -32,12 +34,10 @@ GTPControl::~GTPControl() { } void GTPControl::initialize(int stage) { - GTP::initialize(stage); nb->subscribe(this, NF_SUB_NEEDS_PDN); nb->subscribe(this, NF_SUB_MODIF_TUNN); subT = SubscriberTableAccess().get(); - } void GTPControl::processMessage(GTPMessage *msg, GTPPath *path) { @@ -61,7 +61,6 @@ void GTPControl::processMessage(GTPMessage *msg, GTPPath *path) { } void GTPControl::receiveChangeNotification(int category, const cPolymorphic *details) { - Enter_Method_Silent(); if (category == NF_SUB_NEEDS_PDN) { EV << "GTPControl: Received NF_SUB_NEEDS_PDN notification. Processing notification.\n"; @@ -89,8 +88,6 @@ void GTPControl::receiveChangeNotification(int category, const cPolymorphic *det sub->setGTPProcedure(EUTRANInitAttachReq); te->sendCreateSessionRequest(); } -// for (unsigned i = 0; i < sub->conns.size(); i++) -// te->sendCreateSessionRequest(sub->conns.at(i)); } else if (category == NF_SUB_MODIF_TUNN) { EV << "GTPControl: Received NF_MODIF_TUNN notification. Processing notification.\n"; BearerContext *bearer = check_and_cast(details); @@ -99,6 +96,5 @@ void GTPControl::receiveChangeNotification(int category, const cPolymorphic *det if (sub->getEmmEntity()->getState() != EMM_REGISTERED_N) { s11Tunn->sendModifyBearerRequest(); } - } } diff --git a/src/networklayer/gtp/GTPControl.h b/src/networklayer/gtp/GTPControl.h index 94a751a..9bf1c05 100644 --- a/src/networklayer/gtp/GTPControl.h +++ b/src/networklayer/gtp/GTPControl.h @@ -1,4 +1,6 @@ // +// Copyright (C) 2012 Calin Cerchez +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or @@ -20,6 +22,10 @@ #include "GatewayTable.h" #include "GTP.h" +/* + * Class for GTP control protocol. This class inherits GTP generic module and adds + * GTP control functionality. + */ class GTPControl : public GTP { protected: GatewayTable *gT; @@ -33,12 +39,9 @@ public: virtual void initialize(int stage); void processMessage(GTPMessage *msg, GTPPath *path); - /* subscriber */ -// Subscriber *findSubscriberByIMSI(char *imsi) { return subT->findSubscriberByIMSI(imsi); } -// Subscriber *findSubscriberBySeqNr(unsigned seqNr) { return subT->findSubscriberBySeqNr(seqNr); } -// void push_back(Subscriber *sub) { subT->push_back(sub); } - - /* notification board */ + /* + * Notification methods. + */ virtual void receiveChangeNotification(int category, const cPolymorphic *details); }; diff --git a/src/networklayer/gtp/GTPMessage.cc b/src/networklayer/gtp/GTPMessage.cc index 1f2f832..03a74c7 100644 --- a/src/networklayer/gtp/GTPMessage.cc +++ b/src/networklayer/gtp/GTPMessage.cc @@ -1,4 +1,6 @@ // +// Copyright (C) 2012 Calin Cerchez +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or diff --git a/src/networklayer/gtp/GTPMessage.h b/src/networklayer/gtp/GTPMessage.h index 6621af5..c60cc2a 100644 --- a/src/networklayer/gtp/GTPMessage.h +++ b/src/networklayer/gtp/GTPMessage.h @@ -1,4 +1,6 @@ // +// Copyright (C) 2012 Calin Cerchez +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or @@ -19,6 +21,10 @@ #include "GTPMessage_m.h" #include "INETDefs.h" +/* + * Class for GTP message. This class inherits the message base class from .msg file + * and adds the vector with the GTP information elements. + */ class GTPMessage : public GTPMessage_Base { private: typedef std::vector GTPInfoElems; @@ -27,18 +33,42 @@ public: GTPMessage(const char *name=NULL, int kind=0) : GTPMessage_Base(name,kind) {} GTPMessage(const GTPMessage& other) : GTPMessage_Base(other.getName()) {operator=(other);} virtual ~GTPMessage(); + GTPMessage& operator=(const GTPMessage& other); virtual GTPMessage *dup() const {return new GTPMessage(*this);} + /* + * Methods overridden but not used. You should use instead pushIe. + */ virtual void setIesArraySize(unsigned int size); virtual void setIes(unsigned int k, const GTPInfoElemPtr& ies_var); + + /* + * Getter methods. + */ virtual unsigned int getIesArraySize() const; virtual GTPInfoElemPtr& getIes(unsigned int k); + /* + * Method for pushing GTP information element at the end of the vector. + */ void pushIe(GTPInfoElemPtr ie) { ies.push_back(ie); } -// void insertAvp(unsigned pos, AVPPtr avp); + + /* + * Method for printing the message contents. Currently it prints info + * only for the header. + */ void print(); + + /* + * Method for finding a GTP IE with a given GTP IE type and instance. It returns + * NULL, if the GTP IE is not found. + */ GTPInfoElemPtr findIe(unsigned char type, unsigned char instance); + + /* + * Method for finding a range of GTP IEs with a given GTP IE type and instance. + */ std::vector findIes(unsigned char type, unsigned char instance); }; diff --git a/src/networklayer/gtp/GTPUser.ned b/src/networklayer/gtp/GTPUser.ned index 63f5c1a..d0cc46d 100644 --- a/src/networklayer/gtp/GTPUser.ned +++ b/src/networklayer/gtp/GTPUser.ned @@ -25,6 +25,6 @@ simple GTPUser gates: input udpIn; output udpOut; - input radioIN; - output radioOUT; + input radioIn; + output radioOut; } \ No newline at end of file diff --git a/src/networklayer/gtp/GTPUtils.cc b/src/networklayer/gtp/GTPUtils.cc index 4bf4e79..7dd5477 100644 --- a/src/networklayer/gtp/GTPUtils.cc +++ b/src/networklayer/gtp/GTPUtils.cc @@ -1,4 +1,6 @@ // +// Copyright (C) 2012 Calin Cerchez +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or @@ -104,7 +106,6 @@ GTPInfoElem *GTPUtils::createCauseIE(unsigned short length, ie->setType(GTPV2_Cause); ie->setInstance(instance); ie->setValueArraySize(length); -// for (unsigned i = 0; i < length; i++) ie->setValue(i, value[i]); ie->setValue(0, cause); ie->setValue(1, ((pce << 2) & 0x04) | ((bce << 1) & 0x02) | (cs & 0x01)); if (length == GTPV2_CAUSE_IE_MAX_SIZE) { @@ -155,24 +156,6 @@ GTPInfoElem *GTPUtils::createFteidIE(unsigned char instance, unsigned teid, char return GTPUtils().createIE(GTPV2_F_TEID, len, instance, val); } -//GTPInfoElem *GTPUtils::createFteidIE(unsigned char instance, TunnelEndpoint *te) { -// GTPPath *path = te->getPath(); -// unsigned len = path->getLocalAddr().isIPv6() ? 21 : 9; -// char *val = (char*)calloc(len, sizeof(char)); -// char *p = val; -// -// *((char*)p) = (te->getPath()->getLocalAddr().isIPv6() ? 0x40 : 0x80) + path->getType(); -// p += 1; -// *((unsigned*)p) = ntohl(te->getLocalId()); -// p += 4; -// if (path->getLocalAddr().isIPv6()) { // not supported -// } else { -// *((unsigned*)p) = ntohl(path->getLocalAddr().get4().getInt()); -// p += 4; -// } -// return GTPUtils().createIE(GTPV2_F_TEID, len, instance, val); -//} - char *GTPUtils::processIE(GTPInfoElem *ie) { char *val = (char*)calloc(ie->getValueArraySize(), sizeof(char)); for (unsigned i = 0; i < ie->getValueArraySize(); i++) @@ -180,17 +163,6 @@ char *GTPUtils::processIE(GTPInfoElem *ie) { return val; } -//int GTPUtils::findIE(unsigned char type, unsigned char instance, GTPMessage *msg) { -// if (msg->getHeader()->getVersion() == 2) { -// for (unsigned i = 0; i < msg->getIesArraySize(); i++) { -// GTPInfoElem *ie = dynamic_cast(msg->getIes(i)); -// if ((ie->getType() == type) && (ie->getInstance() == instance)) -// return i; -// } -// } -// return -1; -//} - IPvXAddress GTPUtils::processAddrIE(GTPInfoElem *addr) { char *val = (char*)calloc(addr->getValueArraySize(), sizeof(char)); for (unsigned i = 0; i < addr->getValueArraySize(); i++) @@ -247,7 +219,8 @@ unsigned GTPUtils::getGTPv1InfoElemLen(unsigned char type) { switch(type) { case GTPV1_Recovery: return 1; case GTPV1_TEIDData1: return 4; - default:; + default: + break; } return 0; } @@ -266,32 +239,3 @@ std::vector GTPUtils::processGroupedIE(GTPInfoElem *ie) { } return group; } - -//void GTPUtils::printGTPMessage(GTPMessage *msg) { -// EV << "===================================================\n"; -// EV << "Version: " << (unsigned)msg->getHeader()->getVersion() << endl; -// EV << "Flags:\n"; -// if (msg->getHeader()->getVersion() == GTP_V1) { -// GTPv1Header *hdr = dynamic_cast(msg->getHeader()); -// EV << "\tProt. Type Flag: " << hdr->getPt() << endl; -// EV << "\tExt. Flag: " << hdr->getE() << endl; -// EV << "\tSeq. Nr. Flag: " << hdr->getS() << endl; -// EV << "\tNPDU Flag: " << hdr->getPn() << endl; -// EV << "Message Type: " << (unsigned)hdr->getType() << endl; -// EV << "TEID: " << hdr->getTeid() << endl; -// if (hdr->getE() || hdr->getE() || hdr->getS()) { -// EV << "Seq. Nr.: " << hdr->getSeqNr() << endl; -// EV << "NPDU Nr.: " << (unsigned)hdr->getNpduNr() << endl; -// EV << "Next Ext. Hdr. Type: " << (unsigned)hdr->getExtNextType() << endl; -// } -// } else if (msg->getHeader()->getVersion() == GTP_V2) { -// GTPv2Header *hdr = dynamic_cast(msg->getHeader()); -// EV << "\tPiggyback. Flag: " << hdr->getP() << endl; -// EV << "\tTEID Flag: " << hdr->getT() << endl; -// EV << "Message Type: " << (unsigned)hdr->getType() << endl; -// if (hdr->getT()) -// EV << "TEID: " << hdr->getTeid() << endl; -// EV << "Seq. Nr.: " << hdr->getSeqNr() << endl; -// } -// EV << "===================================================\n"; -//} diff --git a/src/networklayer/gtp/GTPUtils.h b/src/networklayer/gtp/GTPUtils.h index a08297a..67c11f1 100644 --- a/src/networklayer/gtp/GTPUtils.h +++ b/src/networklayer/gtp/GTPUtils.h @@ -1,4 +1,6 @@ // +// Copyright (C) 2012 Calin Cerchez +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or @@ -21,47 +23,44 @@ #include "TunnelEndpoint.h" #include "GTP.h" +/* + * GTP utility class. It is used for creating and processing GTP header and GTP + * information elements. + */ class GTPUtils { -// GTPMessageTemplate createTemplate(); public: GTPUtils(); virtual ~GTPUtils(); + /* + * Methods for GTP header and GTP information element creation. + */ GTPv2Header *createHeader(unsigned char type, bool p, bool t, unsigned int teid, unsigned int seqNr); GTPv1Header *createHeader(unsigned char type, bool pt, bool e, bool s, bool pn, unsigned int teid, unsigned int seqNr, unsigned char npduNr, unsigned char extNextType, std::vector exts); GTPInfoElem *createIE(unsigned char type, unsigned short length, unsigned char instance, char *value); GTPInfoElem *createIE(unsigned char type, unsigned char instance, std::string value); GTPInfoElem *createIE(unsigned char version, unsigned char type, unsigned char instance, char value); - char *processIE(GTPInfoElem *ie); - GTPInfoElem *createIntegerIE(unsigned char type, unsigned char instance, unsigned value); + GTPInfoElem *createCauseIE(unsigned short length, unsigned char instance, unsigned char cause, bool pce = false, bool bce = false, bool cs = false, unsigned char ieType = 0, unsigned char ieInstance = 0); + GTPInfoElem *createAddrIE(unsigned char type, unsigned char instance, IPvXAddress addr); + GTPInfoElem *createFteidIE(unsigned char instance, unsigned teid, char type, IPvXAddress addr); + GTPInfoElem *createGroupedIE(unsigned char type, unsigned char instance, unsigned length, std::vectories); - GTPInfoElem *createCauseIE(unsigned short length, - unsigned char instance, - unsigned char cause, - bool pce = false, - bool bce = false, - bool cs = false, - unsigned char ieType = 0, - unsigned char ieInstance = 0); + /* + * Methods for GTP header and GTP information element processing. They return the + * payload taken from the GTP information element (unsigned, integer, char etc + * values). + */ unsigned char processCauseIE(GTPInfoElem *cause); - - GTPInfoElem *createAddrIE(unsigned char type, unsigned char instance, IPvXAddress addr); + char *processIE(GTPInfoElem *ie); IPvXAddress processAddrIE(GTPInfoElem *addr); - - GTPInfoElem *createFteidIE(unsigned char instance, unsigned teid, char type, IPvXAddress addr); -// GTPInfoElem *createFteidIE(unsigned char instance, TunnelEndpoint *te); TunnelEndpoint *processFteidIE(GTPInfoElem *fteid); - - GTPInfoElem *createGroupedIE(unsigned char type, unsigned char instance, unsigned length, std::vectories); std::vector processGroupedIE(GTPInfoElem *ie); -// int findIE(unsigned char type, unsigned char instance, GTPMessage *msg); - + /* + * Method for returning the length of a GTPv1 information element based on its type. + */ unsigned getGTPv1InfoElemLen(unsigned char type); - -// void printGTPMessage(GTPMessage *msg); -// GTPInfoElem *checkAndReturnCause(GTPMessage *msg, unsigned mandatoryLeft, GTPInfoElemTemplate elems[GTP_IE_TYPE_MAX][GTP_IE_INSTANCE_MAX]); }; #endif /* GTPUTILS_H_ */ -- GitLab