/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Jaume Nin <jnin@cttc.es>
* Nicola Baldo <nbaldo@cttc.es>
*/
#include <ns3/epc-helper.h>
#include <ns3/log.h>
#include <ns3/inet-socket-address.h>
#include <ns3/mac48-address.h>
#include <ns3/epc-gtpu-tunnel-endpoint.h>
#include <ns3/eps-bearer.h>
#include <ns3/ipv4-address.h>
#include <ns3/internet-stack-helper.h>
#include <ns3/point-to-point-helper.h>
#include <ns3/packet-socket-helper.h>
#include <ns3/packet-socket-address.h>
#include <ns3/epc-enb-application.h>
#include <ns3/epc-sgw-pgw-application.h>
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("EpcHelper");
NS_OBJECT_ENSURE_REGISTERED (EpcHelper);
EpcHelper::EpcHelper ()
: m_gtpuUdpPort (2152) // fixed by the standard
{
NS_LOG_FUNCTION (this);
// since we use point-to-point links for all S1-U links,
// we use a /30 subnet which can hold exactly two addresses
// (remember that net broadcast and null address are not valid)
m_s1uIpv4AddressHelper.SetBase ("10.0.0.0", "255.255.255.252");
// we use a /8 net for all UEs
m_ueAddressHelper.SetBase ("7.0.0.0", "255.0.0.0");
// create SgwPgwNode
m_sgwPgw = CreateObject<Node> ();
InternetStackHelper internet;
internet.Install (m_sgwPgw);
// create S1-U socket
Ptr<Socket> sgwPgwS1uSocket = Socket::CreateSocket (m_sgwPgw, TypeId::LookupByName ("ns3::UdpSocketFactory"));
int retval = sgwPgwS1uSocket->Bind (InetSocketAddress (Ipv4Address::GetAny (), m_gtpuUdpPort));
NS_ASSERT (retval == 0);
// create TUN device implementing tunneling of user data over GTP-U/UDP/IP
m_tunDevice = CreateObject<VirtualNetDevice> ();
// allow jumbo packets
m_tunDevice->SetAttribute ("Mtu", UintegerValue (30000));
// yes we need this
m_tunDevice->SetAddress (Mac48Address::Allocate ());
m_sgwPgw->AddDevice (m_tunDevice);
NetDeviceContainer tunDeviceContainer;
tunDeviceContainer.Add (m_tunDevice);
// the TUN device is on the same subnet as the UEs, so when a packet
// addressed to an UE arrives at the intenet to the WAN interface of
// the PGW it will be forwarded to the TUN device.
Ipv4InterfaceContainer tunDeviceIpv4IfContainer = m_ueAddressHelper.Assign (tunDeviceContainer);
// create EpcSgwPgwApplication
m_sgwPgwApp = CreateObject<EpcSgwPgwApplication> (m_tunDevice, sgwPgwS1uSocket);
m_sgwPgw->AddApplication (m_sgwPgwApp);
// connect SgwPgwApplication and virtual net device for tunneling
m_tunDevice->SetSendCallback (MakeCallback (&EpcSgwPgwApplication::RecvFromTunDevice, m_sgwPgwApp));
}
EpcHelper::~EpcHelper ()
{
NS_LOG_FUNCTION (this);
}
TypeId
EpcHelper::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::EpcHelper")
.SetParent<Object> ()
.AddConstructor<EpcHelper> ()
.AddAttribute ("S1uLinkDataRate",
"The data rate to be used for the next S1-U link to be created",
DataRateValue (DataRate ("10Gb/s")),
MakeDataRateAccessor (&EpcHelper::m_s1uLinkDataRate),
MakeDataRateChecker ())
.AddAttribute ("S1uLinkDelay",
"The delay to be used for the next S1-U link to be created",
TimeValue (Seconds (0)),
MakeTimeAccessor (&EpcHelper::m_s1uLinkDelay),
MakeTimeChecker ())
.AddAttribute ("S1uLinkMtu",
"The MTU of the next S1-U link to be created. Note that, because of the additional GTP/UDP/IP tunneling overhead, you need a MTU larger than the end-to-end MTU that you want to support.",
UintegerValue (2000),
MakeUintegerAccessor (&EpcHelper::m_s1uLinkMtu),
MakeUintegerChecker<uint16_t> ())
;
return tid;
}
void
EpcHelper::DoDispose ()
{
m_tunDevice->SetSendCallback (MakeNullCallback<bool, Ptr<Packet>, const Address&, const Address&, uint16_t> ());
m_tunDevice = 0;
m_sgwPgwApp = 0;
m_sgwPgw->Dispose ();
}
void
EpcHelper::AddEnb (Ptr<Node> enb, Ptr<NetDevice> lteEnbNetDevice)
{
NS_LOG_FUNCTION (this << enb << lteEnbNetDevice);
NS_ASSERT (enb == lteEnbNetDevice->GetNode ());
// add an IPv4 stack to the previously created eNB
InternetStackHelper internet;
internet.Install (enb);
NS_LOG_LOGIC ("number of Ipv4 ifaces of the eNB after node creation: " << enb->GetObject<Ipv4> ()->GetNInterfaces ());
// create a point to point link between the new eNB and the SGW with
// the corresponding new NetDevices on each side
NodeContainer enbSgwNodes;
enbSgwNodes.Add (m_sgwPgw);
enbSgwNodes.Add (enb);
PointToPointHelper p2ph;
p2ph.SetDeviceAttribute ("DataRate", DataRateValue (m_s1uLinkDataRate));
p2ph.SetDeviceAttribute ("Mtu", UintegerValue (m_s1uLinkMtu));
p2ph.SetChannelAttribute ("Delay", TimeValue (m_s1uLinkDelay));
NetDeviceContainer enbSgwDevices = p2ph.Install (enb, m_sgwPgw);
NS_LOG_LOGIC ("number of Ipv4 ifaces of the eNB after installing p2p dev: " << enb->GetObject<Ipv4> ()->GetNInterfaces ());
Ptr<NetDevice> enbDev = enbSgwDevices.Get (0);
Ptr<NetDevice> sgwDev = enbSgwDevices.Get (1);
m_s1uIpv4AddressHelper.NewNetwork ();
Ipv4InterfaceContainer enbSgwIpIfaces = m_s1uIpv4AddressHelper.Assign (enbSgwDevices);
NS_LOG_LOGIC ("number of Ipv4 ifaces of the eNB after assigning Ipv4 addr to S1 dev: " << enb->GetObject<Ipv4> ()->GetNInterfaces ());
Ipv4Address enbAddress = enbSgwIpIfaces.GetAddress (0);
Ipv4Address sgwAddress = enbSgwIpIfaces.GetAddress (1);
// create S1-U socket for the ENB
Ptr<Socket> enbS1uSocket = Socket::CreateSocket (enb, TypeId::LookupByName ("ns3::UdpSocketFactory"));
int retval = enbS1uSocket->Bind (InetSocketAddress (enbAddress, m_gtpuUdpPort));
NS_ASSERT (retval == 0);
// give PacketSocket powers to the eNB
//PacketSocketHelper packetSocket;
//packetSocket.Install (enb);
// create LTE socket for the ENB
Ptr<Socket> enbLteSocket = Socket::CreateSocket (enb, TypeId::LookupByName ("ns3::PacketSocketFactory"));
PacketSocketAddress enbLteSocketBindAddress;
enbLteSocketBindAddress.SetSingleDevice (lteEnbNetDevice->GetIfIndex ());
enbLteSocketBindAddress.SetProtocol (Ipv4L3Protocol::PROT_NUMBER);
retval = enbLteSocket->Bind (enbLteSocketBindAddress);
NS_ASSERT (retval == 0);
PacketSocketAddress enbLteSocketConnectAddress;
enbLteSocketConnectAddress.SetPhysicalAddress (Mac48Address::GetBroadcast ());
enbLteSocketConnectAddress.SetSingleDevice (lteEnbNetDevice->GetIfIndex ());
enbLteSocketConnectAddress.SetProtocol (Ipv4L3Protocol::PROT_NUMBER);
retval = enbLteSocket->Connect (enbLteSocketConnectAddress);
NS_ASSERT (retval == 0);
NS_LOG_INFO ("create EpcEnbApplication");
Ptr<EpcEnbApplication> enbApp = CreateObject<EpcEnbApplication> (enbLteSocket, enbS1uSocket, sgwAddress);
enb->AddApplication (enbApp);
NS_ASSERT (enb->GetNApplications () == 1);
NS_ASSERT_MSG (enb->GetApplication (0)->GetObject<EpcEnbApplication> () != 0, "cannot retrieve EpcEnbApplication");
NS_LOG_LOGIC ("enb: " << enb << ", enb->GetApplication (0): " << enb->GetApplication (0));
}
void
EpcHelper::ActivateEpsBearer (Ptr<NetDevice> ueLteDevice, Ptr<NetDevice> enbLteDevice, Ptr<EpcTft> tft, uint16_t rnti, uint8_t lcid)
{
Ptr<Node> ueNode = ueLteDevice->GetNode ();
Ptr<Ipv4> ueIpv4 = ueNode->GetObject<Ipv4> ();
int32_t interface = ueIpv4->GetInterfaceForDevice (ueLteDevice);
NS_ASSERT (interface >= 0);
NS_ASSERT (ueIpv4->GetNAddresses (interface) == 1);
Ipv4Address ueAddr = ueIpv4->GetAddress (interface, 0).GetLocal ();
NS_LOG_LOGIC (" UE IP address: " << ueAddr);
// NOTE: unlike ueLteDevice, enbLteDevice is NOT an Ipv4 enabled
// device. In fact we are interested in the S1 device of the eNB.
// We find it by relying on the assumption that the S1 device is the
// only Ipv4 enabled device of the eNB besides the localhost interface.
Ptr<Node> enbNode = enbLteDevice->GetNode ();
NS_ASSERT (enbNode != 0);
Ptr<Ipv4> enbIpv4 = enbNode->GetObject<Ipv4> ();
NS_LOG_LOGIC ("number of Ipv4 ifaces of the eNB: " << enbIpv4->GetNInterfaces ());
// two ifaces total: loopback + the S1-U interface
NS_ASSERT (enbIpv4->GetNInterfaces () == 2);
NS_ASSERT (ueIpv4->GetNAddresses (1) == 1);
// iface index 0 is loopback, index 1 is the S1-U interface
Ipv4Address enbAddr = enbIpv4->GetAddress (1, 0).GetLocal ();
NS_LOG_LOGIC (" ENB IP address: " << enbAddr);
// setup S1 bearer at EpcSgwPgwApplication
uint32_t teid = m_sgwPgwApp->ActivateS1Bearer (ueAddr, enbAddr, tft);
// setup S1 bearer at EpcEnbApplication
NS_LOG_LOGIC ("enb: " << enbNode << ", enb->GetApplication (0): " << enbNode->GetApplication (0));
NS_ASSERT (enbNode->GetNApplications () == 1);
Ptr<Application> app = enbNode->GetApplication (0);
NS_ASSERT (app != 0);
Ptr<EpcEnbApplication> epcEnbApp = app->GetObject<EpcEnbApplication> ();
NS_ASSERT (epcEnbApp != 0);
epcEnbApp->ErabSetupRequest (teid, rnti, lcid);
}
Ptr<Node>
EpcHelper::GetPgwNode ()
{
return m_sgwPgw;
}
Ipv4InterfaceContainer
EpcHelper::AssignUeIpv4Address (NetDeviceContainer ueDevices)
{
return m_ueAddressHelper.Assign (ueDevices);
}
Ipv4Address
EpcHelper::GetUeDefaultGatewayAddress ()
{
// return the address of the tun device
return m_sgwPgw->GetObject<Ipv4> ()->GetAddress (1, 0).GetLocal ();
}
} // namespace ns3