ErwinLi opened issue #1070: TCP Data Transfer Fails After Handover in specified DataRate range at nsnam / ns-3-dev

ErwinLi
2024-04-19T19:12:13Z

I am experiencing an issue in my ns-3 simulation setup where TCP data transfer fails after performing a handover of three UEs to another eNodeB. Each UE runs an OnOffApplication set with a data rate of 3-5 Mb/s, targeting a PacketSink application on a remote host via TCP. After the handover process, the remote host no longer receives any packets that are sent from the UEs. This issue does not occur when the data rates are set outside the 3-5 Mb/s range.

Expected Behavior:

The remote host should continue to receive packets after the UEs have handed over to the new eNodeB, regardless of the data rate setting.

Actual Behavior:

After the handover, the remote host ceases to receive any new packets from the UEs when the data rate is set within 3-5 Mb/s.

ns-3 version: ns-3.40/ns-3.41

The code with this problem:

# Network Structure
#
# +-------+ +----+ +------------------+
# | | |PGW |-------| Remote Host |
# | eNodeB|-------|SGW | | [UDP Server] |
# | | | | +------------------+
# +---+---+ +----+ |
# | | |
# | | |
# +-----+-------+ | +---------+----+
# | UE1 | +-----| P2P Internet |
# | [UDP Client]| +--------------+
# +-------------+
#
# +-------------+
# | UE2 |
# | [UDP Client]|
# +-------------+
#
# +-------------+
# | UE3 |
# | [UDP Client]|
# +-------------+

from ns import ns
import pandas as pd

port = 1234
send_port = 49153

tx_record_df = pd.DataFrame(columns=["uid", "size", "time", "step", "src", "dest", "seq_num"])
rx_record_df = pd.DataFrame(columns=["uid", "size", "time", "step", "src", "dest", "seq_num"])

def statistic_report(df1, df2):
"""
df1 for tx
df2 for rx
"""
# print(df1)
# print(df2)
sent_number = len(df1[df1["step"]==step])
print(f"sent {sent_number} packet in step {step}")
merged_df = pd.merge(df1, df2, on=['seq_num', 'src'], suffixes=('_tx', '_rx'), how='left', indicator=True)
# print(merged_df)
recv_number_in_step = len(merged_df[(merged_df['_merge'] == 'both') & (merged_df['step_tx'] == step)])
recv_number_total = len(df2)
receive_info = merged_df[merged_df['_merge'] == 'both']['step_tx'].value_counts()
for i in receive_info.index:
print(f"received {receive_info[i]} packet from step {i}")
# print(f"received {recv_number_in_step} packet sent in step {step}")
print(f"received {recv_number_total} packet in total")
try:
delivery_rate = recv_number_in_step/sent_number
except:
delivery_rate = 1
average_delay = (merged_df[merged_df['_merge'] == 'both']["time_rx"]-merged_df[merged_df['_merge'] == 'both']["time_tx"]).mean()
rate = merged_df["size_rx"].sum() / (merged_df["time_rx"].max()-merged_df["time_rx"].min())
print(f"delay: {average_delay} s\ndelivery rate: {delivery_rate}\nrate: {rate*8/1024/1024} Mbps")
df1.drop(merged_df[merged_df['_merge'] == 'both'].index, inplace=True)
df1.reset_index(drop=True, inplace=True)
df2.drop(df2.index, inplace=True)

step = 0

# Function used to make callback for Rx and Tx events of UDP
ns.cppyy.cppdef(
"""
#include<string>
using namespace ns3;
Callback<void, std::string, Ptr< const Packet >, const Address &, const Address &, const SeqTsSizeHeader &>
make_SeqTsSizeCallback(void(*func)(std::string, Ptr< const Packet >, const Address &, const Address &, const SeqTsSizeHeader &)){
return MakeCallback(func);
}
""")

def RxWithSeqTsSize_callback(contex, packet, address_src, address_dest, SeqTsSizeHeader):
address_src = ns.InetSocketAddress.ConvertFrom(address_src)
address_dest = ns.InetSocketAddress.ConvertFrom(address_dest)
# print(f"At {ns.Simulator.Now().GetSeconds():.5f}s ,{contex}, received {packet.__deref__().GetSize()} bytes data")
rx_record_df.loc[len(rx_record_df)] = [packet.GetUid(), SeqTsSizeHeader.GetSize(), ns.Simulator.Now().GetSeconds(), step, f"{address_src.GetIpv4()}:{address_src.GetPort()}", f"{address_dest.GetIpv4()}:{address_dest.GetPort()}", SeqTsSizeHeader.GetSeq()]

def TxWithSeqTsSize_callback(contex, packet, address_src, address_dest, SeqTsSizeHeader):
address_src = ns.InetSocketAddress.ConvertFrom(address_src)
address_dest = ns.InetSocketAddress.ConvertFrom(address_dest)
# print(f"At {ns.Simulator.Now().GetSeconds():.5f}s ,{contex}, sent {packet.__deref__().GetSize()} bytes data")
tx_record_df.loc[len(tx_record_df)] = [packet.__deref__().GetUid(), SeqTsSizeHeader.GetSize(), ns.Simulator.Now().GetSeconds(), step, f"{address_src.GetIpv4()}:{address_src.GetPort()}", f"{address_dest.GetIpv4()}:{address_dest.GetPort()}", SeqTsSizeHeader.GetSeq()]

#
ns.Config.SetDefault("ns3::LteSpectrumPhy::CtrlErrorModelEnabled", ns.BooleanValue(0))
ns.Config.SetDefault("ns3::LteSpectrumPhy::DataErrorModelEnabled", ns.BooleanValue(0))

# Initialise LTE Helper
lte_helper = ns.CreateObject("LteHelper")

# Initialise EPC Helper and install it to LTE Help
epc_helper = ns.PointToPointEpcHelper()
lte_helper.SetEpcHelper(epc_helper)

# Initialise P2P link Helper
p2p_helper = ns.PointToPointHelper()

# Get PGW
pgw = epc_helper.GetPgwNode()

# ---------- initialise nodes ---------- #

# Set container for eNodeB
enb_nodes = ns.NodeContainer()
enb_nodes.Create(2)

# Set container for UE
ue_nodes = ns.NodeContainer()
ue_nodes.Create(3)

# Set remote host
remote_nodes = ns.NodeContainer()
remote_nodes.Create(1)

# ---------- Set mobility ---------- #

# Set mobility
mobilityhelper = ns.MobilityHelper()
mobilityhelper.SetMobilityModel("ns3::ConstantPositionMobilityModel")
mobilityhelper.Install(enb_nodes)
mobilityhelper.Install(ue_nodes)

# ---------- Initialise network ---------- #

# Install device to Node
lte_helper.SetEnbDeviceAttribute("UlBandwidth", ns.UintegerValue(50))
# lte_helper.SetEpcHelper(epc_helper)
enb_devices1 = lte_helper.InstallEnbDevice(enb_nodes.Get(0))
lte_helper.SetEnbDeviceAttribute("UlBandwidth", ns.UintegerValue(100))
# lte_helper.SetEpcHelper(epc_helper)
enb_devices2 = lte_helper.InstallEnbDevice(enb_nodes.Get(1))
ue_devices = lte_helper.InstallUeDevice(ue_nodes)

# Get devices from devices container
LteEnbNetDevices_list = []
LteEnbNetDevices_list.append(enb_devices1.Get(0).GetObject["LteEnbNetDevice"]())
LteEnbNetDevices_list.append(enb_devices2.Get(0).GetObject["LteEnbNetDevice"]())

# Install network stack to UE and remote host
internet = ns.InternetStackHelper()
internet.Install(ue_nodes)
internet.Install(remote_nodes)
ue_ip_interface = epc_helper.AssignUeIpv4Address(ue_devices)

# Install network stack and X2 interface to eNodeBs
internet.Install(enb_nodes)
lte_helper.AddX2Interface(enb_nodes)

# Install p2p link between PGW and remote host
p2p_helper.SetDeviceAttribute("DataRate", ns.DataRateValue(ns.DataRate("100Gb/s")))
p2p_helper.SetDeviceAttribute("Mtu", ns.UintegerValue(1500))
# p2p_helper.SetChannelAttribute("Delay", ns.TimeValue(ns.MilliSeconds(10)))
p2p_link_device = p2p_helper.Install(pgw, remote_nodes.Get(0))

# Assign IP to PGW and remote host
ipv4_helper = ns.Ipv4AddressHelper()
ipv4_helper.SetBase("1.0.0.0", "255.0.0.0")
p2p_ip_interface = ipv4_helper.Assign(p2p_link_device)

# Create Routing
ipv4_routing_helper = ns.Ipv4StaticRoutingHelper()
remote_routing = ipv4_routing_helper.GetStaticRouting(remote_nodes.Get(0).GetObject["Ipv4"]())
remote_routing.AddNetworkRouteTo(ns.Ipv4Address("7.0.0.0"), ns.Ipv4Mask("255.0.0.0"), 1)

for i in range(ue_nodes.GetN()):
ue_node = ue_nodes.Get(i)
ue_routing = ipv4_routing_helper.GetStaticRouting(ue_node.GetObject["Ipv4"]())
ue_routing.SetDefaultRoute(epc_helper.GetUeDefaultGatewayAddress(), 1)

# Attach UEs to eNodeB
lte_helper.Attach(ue_devices)

# ---------- Install TCP server ---------- #

# Install Sink app to remote host
tcp_sink = ns.PacketSinkHelper("ns3::TcpSocketFactory", ns.InetSocketAddress(ns.Ipv4Address.GetAny(), port).ConvertTo())
tcp_sink.SetAttribute("EnableSeqTsSizeHeader", ns.BooleanValue(1))
tcp_sink_apps = tcp_sink.Install(remote_nodes.Get(0))
tcp_sink_apps.Start(ns.Seconds(0.1))
tcp_sink_apps.Stop(ns.Seconds(5.0))

# ---------- Install TCP clients ---------- #

# Install TCP client to UEs
for i in range(ue_nodes.GetN()):
tcp_client = ns.OnOffHelper("ns3::TcpSocketFactory", ns.InetSocketAddress(p2p_ip_interface.GetAddress(1), port).ConvertTo())
tcp_client.SetAttribute("OnTime", ns.StringValue("ns3::ConstantRandomVariable[Constant=1]"))
tcp_client.SetAttribute("OffTime", ns.StringValue("ns3::ConstantRandomVariable[Constant=0]"))
tcp_client.SetAttribute("DataRate", ns.StringValue("3Mb/s"));
tcp_client.SetAttribute("PacketSize", ns.UintegerValue(512))
tcp_client.SetAttribute("EnableSeqTsSizeHeader", ns.BooleanValue(1))
# if not set Local attr, the source ip when using UDP will be 0.0.0.0
ue_adress = ns.InetSocketAddress(ue_ip_interface.GetAddress(i), send_port)
tcp_client.SetAttribute("Local", ns.AddressValue(ue_adress.ConvertTo()))
tcp_client_apps = tcp_client.Install(ue_nodes.Get(i))
tcp_client_apps.Start(ns.Seconds(2))
tcp_client_apps.Stop(ns.Seconds(2.5))

# ---------- Enable UDP Traces ---------- #

# Enable OnOff Tx Traces
TxWithSeqTsSize_callback_cpp = ns.cppyy.gbl.make_SeqTsSizeCallback(TxWithSeqTsSize_callback)
ns.Config.Connect("/NodeList/*/ApplicationList/*/$ns3::OnOffApplication/TxWithSeqTsSize", TxWithSeqTsSize_callback_cpp)

# Enable Sink Rx Traces
RxWithSeqTsSize_callback_cpp = ns.cppyy.gbl.make_SeqTsSizeCallback(RxWithSeqTsSize_callback)
ns.Config.Connect("/NodeList/*/ApplicationList/*/$ns3::PacketSink/RxWithSeqTsSize", RxWithSeqTsSize_callback_cpp)

# Eable log
# ns.LogComponentEnable("OnOffApplication", ns.LOG_LEVEL_INFO)
# ns.LogComponentEnable("PacketSink", ns.LOG_LEVEL_INFO)

# ---------- Get configurations ---------- #

print("\n-------------------- eNodeB configurations --------------------\n")
enb_node = enb_nodes.Get(0)
enb_node_id = enb_node.GetId()
print(f"eNodeB Node ID: {enb_node_id}")

print(f"eNodeB downlink Bandwidth:{LteEnbNetDevices_list[0].GetDlBandwidth()}")
print(f"eNodeB downlink Earfcn:{LteEnbNetDevices_list[0].GetDlEarfcn()}")
print(f"eNodeB uplink Bandwidth:{LteEnbNetDevices_list[0].GetUlBandwidth()}")
print(f"eNodeB uplink Earfcn:{LteEnbNetDevices_list[0].GetUlEarfcn()}")

# ---------- Run Simulation ---------- #

# Simulate RL Gym environment, so set two steps here

# Run simulation for 0.05s
print("\n-------------------- Step0 2.0s-2.0.5s --------------------\n")
ns.Simulator.Stop(ns.Seconds(2.05))
ns.Simulator.Run()

# Calculate delay
statistic_report(tx_record_df,rx_record_df)
step+=1

print("\n-------------------- handover --------------------\n New eNodeB:")

# handover
lte_helper.HandoverRequest(ns.Seconds(0), ue_devices.Get(0), LteEnbNetDevices_list[0], LteEnbNetDevices_list[1])
lte_helper.HandoverRequest(ns.Seconds(0), ue_devices.Get(1), LteEnbNetDevices_list[0], LteEnbNetDevices_list[1])
lte_helper.HandoverRequest(ns.Seconds(0), ue_devices.Get(2), LteEnbNetDevices_list[0], LteEnbNetDevices_list[1])

print(f"eNodeB downlink Bandwidth:{LteEnbNetDevices_list[1].GetDlBandwidth()}")
print(f"eNodeB downlink Earfcn:{LteEnbNetDevices_list[1].GetDlEarfcn()}")
print(f"eNodeB uplink Bandwidth:{LteEnbNetDevices_list[1].GetUlBandwidth()}")
print(f"eNodeB uplink Earfcn:{LteEnbNetDevices_list[1].GetUlEarfcn()}")

# Run simulation for 0.05s
print("\n-------------------- Step1 2.05s-2.1s --------------------\n")
ns.Simulator.Stop(ns.Seconds(0.05))
ns.Simulator.Run()

# Calculate delay
statistic_report(tx_record_df,rx_record_df)
step+=1

# Run simulation for 0.05s
print("\n-------------------- Step2 2.1s-2.15s --------------------\n")
ns.Simulator.Stop(ns.Seconds(0.05))
ns.Simulator.Run()

# Calculate delay
statistic_report(tx_record_df,rx_record_df)
step+=1

for i in range(7):
print(f"\n-------------------- Step{step} {2+(step)*0.05}s-{2+(step+1)*0.05}s --------------------\n")
ns.Simulator.Stop(ns.Seconds(0.05))
ns.Simulator.Run()

# Calculate delay
statistic_report(tx_record_df,rx_record_df)
step+=1

ns.Simulator.Destroy()

output when Data rate is 3Mb/s:

-------------------- eNodeB configurations --------------------

eNodeB Node ID: 3
eNodeB downlink Bandwidth:25
eNodeB downlink Earfcn:100
eNodeB uplink Bandwidth:50
eNodeB uplink Earfcn:18100

-------------------- Step0 2.0s-2.0.5s --------------------

sent 73 packet in step 0
received 62 packet from step 0
received 62 packet in total
delay: 0.006228405096774197 s
delivery rate: 0.8493150684931506
rate: 10.529703600935873 Mbps

-------------------- handover --------------------
New eNodeB:
eNodeB downlink Bandwidth:25
eNodeB downlink Earfcn:100
eNodeB uplink Bandwidth:100
eNodeB uplink Earfcn:18100

-------------------- Step1 2.05s-2.1s --------------------

sent 110 packet in step 1
received 2 packet from step 0
received 2 packet in total
delay: 0.005575169000000102 s
delivery rate: 0.0
rate: 16108.247417331442 Mbps

-------------------- Step2 2.1s-2.15s --------------------

sent 110 packet in step 2
received 0 packet in total
delay: nan s
delivery rate: 0.0
rate: nan Mbps

-------------------- Step3 2.15s-2.2s --------------------

sent 110 packet in step 3
received 0 packet in total
delay: nan s
delivery rate: 0.0
rate: nan Mbps

-------------------- Step4 2.2s-2.25s --------------------

sent 110 packet in step 4
received 0 packet in total
delay: nan s
delivery rate: 0.0
rate: nan Mbps

-------------------- Step5 2.25s-2.3s --------------------

sent 110 packet in step 5
received 0 packet in total
delay: nan s
delivery rate: 0.0
rate: nan Mbps

-------------------- Step6 2.3s-2.35s --------------------

sent 109 packet in step 6
received 0 packet in total
delay: nan s
delivery rate: 0.0
rate: nan Mbps

-------------------- Step7 2.35s-2.4s --------------------

sent 103 packet in step 7
received 0 packet in total
delay: nan s
delivery rate: 0.0
rate: nan Mbps

-------------------- Step8 2.4s-2.45s --------------------

sent 0 packet in step 8
received 0 packet in total
delay: nan s
delivery rate: 1
rate: nan Mbps

-------------------- Step9 2.45s-2.5s --------------------

sent 0 packet in step 9
received 0 packet in total
delay: nan s
delivery rate: 1
rate: nan Mbps