Q2NS dev
ns-3 module
Loading...
Searching...
No Matches
q2ns-teleportation-helper.cc
Go to the documentation of this file.
1/*-----------------------------------------------------------------------------
2 * Q2NS - Quantum Network Simulator
3 * Copyright (c) 2026 quantuminternet.it
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *---------------------------------------------------------------------------*/
9
10#include "ns3/q2ns-teleportation-helper.h"
11#include "ns3/q2ns-teleportation-app.h"
12
13#include "ns3/q2ns-netcontroller.h"
14#include "ns3/q2ns-qchannel.h"
15#include "ns3/q2ns-qnode.h"
16
17#include "ns3/data-rate.h"
18#include "ns3/double.h"
19#include "ns3/internet-stack-helper.h"
20#include "ns3/ipv4-address-helper.h"
21#include "ns3/ipv4-global-routing-helper.h"
22#include "ns3/ipv4.h"
23#include "ns3/log.h"
24#include "ns3/names.h"
25#include "ns3/node-container.h"
26#include "ns3/point-to-point-helper.h"
27#include "ns3/pointer.h"
28#include "ns3/queue-size.h"
29#include "ns3/simulator.h"
30#include "ns3/string.h"
31#include "ns3/uinteger.h"
32
33#include <sstream>
34#include <stdexcept>
35#include <unordered_set>
36
37namespace q2ns {
38
39NS_LOG_COMPONENT_DEFINE("TeleportationHelper");
40
42 m_backend = std::move(backend);
43 return *this;
44}
45
47 m_defaultQueue = std::move(max);
48 return *this;
49}
50
52 m_portBase = base;
53 return *this;
54}
55
60
62TeleportationHelper::SetTeleportState(const std::shared_ptr<q2ns::QState>& tpl) {
63 m_teleportState = tpl;
64 return *this;
65}
66
68 NS_LOG_INFO("TeleportationHelper::Install start");
69
70 if (!m_nc) {
71 NS_LOG_ERROR("TeleportationHelper: NetController not set; call SetNetController(...) first.\n");
72 throw std::runtime_error("TeleportationHelper requires NetController");
73 }
74
75 if (!m_teleportState) {
76 NS_LOG_ERROR(
77 "TeleportationHelper: Teleport state not set; call SetTeleportState(...) first.\n");
78 throw std::runtime_error("TeleportationHelper requires TeleportState");
79 }
80
81
82 // 1) Nodes + Names
83 BuildNodes(spec);
84
85 // 2) Internet stack on all nodes
86 ns3::InternetStackHelper internet;
87 for (auto& kv : m_name2node) {
88 internet.Install(kv.second);
89 }
90
91 // 3) Classical links (p2p), addressing, queues, background traffic
92 BuildClassical(spec);
93
94 // 4) Quantum links
95 BuildQuantum(spec);
96
97 // 5) Populate routing for multi-hop classical control & background
98 ns3::Ipv4GlobalRoutingHelper::PopulateRoutingTables();
99
100 // 6) Install per-session TeleportationApp instances
101 InstallSessionApps(spec);
102
103 NS_LOG_INFO("TeleportationHelper::Install done");
104}
105
106uint16_t TeleportationHelper::AllocatePort(uint64_t sessionId) const {
107 uint16_t p = static_cast<uint16_t>(m_portBase + (sessionId % 10000ull));
108 if (p == 0)
109 p = m_portBase + 1;
110 return p;
111}
112
114 NS_LOG_INFO("Building " << spec.nodes.size() << " nodes");
115
116 for (const auto& n : spec.nodes) {
117 if (m_name2node.count(n.name)) {
118 NS_LOG_WARN("Duplicate node id '" << n.name << "'; skipping duplicate");
119 continue;
120 }
121
122 // Create QNode through NetController (wires it to the shared registry)
123 auto qnode = m_nc->CreateNode(); // Ptr<QNode>
124 auto node = ns3::StaticCast<ns3::Node>(qnode); // Store as Ptr<Node> for Internet helpers
125 m_name2node.emplace(n.name, node);
126 ns3::Names::Add(n.name, node);
127 }
128}
129
130static ns3::Ptr<ns3::Ipv4> GetIpv4(ns3::Ptr<ns3::Node> node) {
131 return node->GetObject<ns3::Ipv4>();
132}
133
134static ns3::Ipv4Address GetFirstNonLoopback(ns3::Ptr<ns3::Node> node) {
135 auto ipv4 = GetIpv4(node);
136 if (!ipv4)
137 return ns3::Ipv4Address::GetAny();
138
139 for (uint32_t i = 0; i < ipv4->GetNInterfaces(); ++i) {
140 for (uint32_t j = 0; j < ipv4->GetNAddresses(i); ++j) {
141 auto ifaddr = ipv4->GetAddress(i, j);
142 auto addr = ifaddr.GetLocal();
143 if (addr != ns3::Ipv4Address("127.0.0.1") && addr != ns3::Ipv4Address::GetZero()) {
144 return addr;
145 }
146 }
147 }
148 return ns3::Ipv4Address::GetAny(); // fallback; routing should still work by dest
149}
150
152 NS_LOG_INFO("Building " << spec.classicalEdges.size() << " classical links");
153
154 // We'll hand out /30 subnets per link: 10.0.X.Y/30
155 ns3::Ipv4AddressHelper ip;
156 uint32_t netId = 0; // increment per link
157
158 for (const auto& e : spec.classicalEdges) {
159 auto itU = m_name2node.find(e.u);
160 auto itV = m_name2node.find(e.v);
161 if (itU == m_name2node.end() || itV == m_name2node.end()) {
162 NS_LOG_ERROR("Classical edge refers to unknown nodes: " << e.u << " - " << e.v);
163 continue;
164 }
165
166 ns3::NodeContainer pair(itU->second, itV->second);
167
168 // Point-to-point with per-edge DataRate and Delay (distance * delayNsPerKm)
169 ns3::PointToPointHelper p2p;
170 std::ostringstream dr;
171 dr << e.mbps << "Mbps";
172 p2p.SetDeviceAttribute("DataRate", ns3::StringValue(dr.str()));
173
174 double delayNs = e.delayNsPerKm * e.distanceKm;
175 std::ostringstream dl;
176 dl << delayNs << "ns";
177 p2p.SetChannelAttribute("Delay", ns3::StringValue(dl.str()));
178
179 // TxQueue MaxSize (DropTail) -- per link; falls back to helper default
180 std::string qmax = e.queueMax.empty() ? m_defaultQueue : e.queueMax;
181 p2p.SetQueue("ns3::DropTailQueue<Packet>", "MaxSize",
182 ns3::QueueSizeValue(ns3::QueueSize(qmax)));
183
184 auto devices = p2p.Install(pair);
185
186 // Address this link with its own /30
187 // 10.(netId/256).(netId%256).0/30
188 uint8_t a = static_cast<uint8_t>((netId >> 8) & 0xFF);
189 uint8_t b = static_cast<uint8_t>(netId & 0xFF);
190 std::ostringstream base;
191 base << "10." << unsigned(a) << "." << unsigned(b) << ".0";
192 ip.SetBase(ns3::Ipv4Address(base.str().c_str()), "255.255.255.252");
193 ip.Assign(devices);
194
195 ++netId;
196 }
197}
198
199
200
202 NS_LOG_INFO("Quantum edges: " << spec.quantumEdges.size());
203 for (const auto& e : spec.quantumEdges) {
204 auto itU = m_name2node.find(e.u);
205 auto itV = m_name2node.find(e.v);
206 if (itU == m_name2node.end() || itV == m_name2node.end()) {
207 NS_LOG_WARN("Quantum edge refers to unknown nodes: " << e.u << " - " << e.v << "\n");
208 continue;
209 }
210 auto a = ns3::DynamicCast<q2ns::QNode>(itU->second);
211 auto b = ns3::DynamicCast<q2ns::QNode>(itV->second);
212 if (!a || !b) {
213 NS_LOG_ERROR("Quantum edge endpoints are not QNodes: " << e.u << " - " << e.v << "\n");
214 continue;
215 }
216 const double delayNs = e.delayNsPerKm * e.distanceKm;
217 ns3::Time oneWay = ns3::NanoSeconds(static_cast<uint64_t>(std::max(0.0, delayNs)));
218 auto ch = m_nc->InstallQuantumLink(a, b);
219 ch->SetAttribute("Delay", ns3::TimeValue(oneWay));
220 }
221}
222
223
225 NS_LOG_INFO("Installing " << spec.sessions.size() << " teleportation sessions");
226
227 // Track used (dstNode, port) to warn on user-specified conflicts
228 std::unordered_set<uint64_t> usedPortKey;
229 auto keyOf = [&](ns3::Ptr<ns3::Node> n, uint16_t p) -> uint64_t {
230 return (static_cast<uint64_t>(n->GetId()) << 32) | static_cast<uint64_t>(p);
231 };
232
233 for (const auto& s : spec.sessions) {
234 if (!m_name2node.count(s.src) || !m_name2node.count(s.dst)) {
235 NS_LOG_ERROR("Session refers to unknown nodes: " << s.src << " -> " << s.dst);
236 continue;
237 }
238 auto srcNode = m_name2node.at(s.src);
239 auto dstNode = m_name2node.at(s.dst);
240 auto dstQ = ns3::DynamicCast<q2ns::QNode>(dstNode);
241
242 // Classical address of Bob (dst) -- choose first non-loopback; routing handles path
243 ns3::Ipv4Address dstAddr = GetFirstNonLoopback(dstNode);
244 if (dstAddr == ns3::Ipv4Address::GetAny()) {
245 NS_LOG_WARN("Destination node " << s.dst << " has no IPv4 address yet");
246 }
247
248 // Control port per session (auto if 0)
249 uint16_t ctrlPort = s.ctrlPort ? s.ctrlPort : AllocatePort(s.sessionId);
250 if (s.ctrlPort) {
251 uint64_t k = keyOf(dstNode, ctrlPort);
252 if (usedPortKey.count(k)) {
253 NS_LOG_WARN("Two sessions target dst=" << s.dst << " with the same CtrlPort=" << ctrlPort
254 << " -- PacketSink bind conflict likely.");
255 } else {
256 usedPortKey.insert(k);
257 }
258 }
259
260 // Protocol preference: per-session override if provided, else try to infer from first edge
261 std::string proto = s.protocol;
262 if (proto.empty()) {
263 // Fallback: use "udp" unless a matching classical edge was declared "tcp"
264 proto = "udp";
265 for (const auto& e : spec.classicalEdges) {
266 if ((e.u == s.src && e.v == s.dst) || (e.u == s.dst && e.v == s.src)) {
267 proto = e.protocol;
268 break;
269 }
270 }
271 }
272
273 // Backend for both apps
274 std::string backend = spec.backend.empty() ? m_backend : spec.backend;
275
276 // Session-unique qubit tag on Bob
277 std::ostringstream tag;
278 tag << "teleport_target_s" << s.sessionId;
279
280 // Source (Alice)
281 {
282 auto app = ns3::CreateObject<TeleportationApp>();
283 srcNode->AddApplication(app);
284 app->SetAttribute("Role", ns3::StringValue("source"));
285 app->SetAttribute("Peer", ns3::Ipv4AddressValue(dstAddr));
286 app->SetAttribute("CtrlPort", ns3::UintegerValue(ctrlPort));
287 app->SetAttribute("ClassicalProtocol", ns3::StringValue(proto));
288 app->SetAttribute("Backend", ns3::StringValue(backend));
289 app->SetAttribute("TargetQubitTag", ns3::StringValue(tag.str()));
290 app->SetAttribute("SessionId", ns3::UintegerValue(s.sessionId));
291 app->SetAttribute("SessionStartTime", ns3::TimeValue(ns3::Seconds(s.start_s)));
292 if (dstQ) {
293 app->SetAttribute("PeerQNode", ns3::PointerValue(dstQ));
294 }
295 app->SetNetController(m_nc); // NetController so the app can schedule sends / resolve states
296 if (m_teleportState) {
297 app->SetTeleportState(m_teleportState);
298 }
299 app->SetStartTime(ns3::Seconds(0.0)); // app schedules its own SessionStart
300 app->SetStopTime(ns3::Seconds(1e9)); // effectively forever unless user stops earlier
301 }
302
303 // Sink (Bob)
304 {
305 auto app = ns3::CreateObject<TeleportationApp>();
306 dstNode->AddApplication(app);
307 app->SetAttribute("Role", ns3::StringValue("sink"));
308 app->SetAttribute("Peer",
309 ns3::Ipv4AddressValue(ns3::Ipv4Address::GetAny())); // unused on sink
310 app->SetAttribute("CtrlPort", ns3::UintegerValue(ctrlPort));
311 app->SetAttribute("ClassicalProtocol", ns3::StringValue(proto));
312 app->SetAttribute("Backend", ns3::StringValue(backend));
313 app->SetAttribute("TargetQubitTag", ns3::StringValue(tag.str()));
314 app->SetAttribute("SessionId", ns3::UintegerValue(s.sessionId));
315 app->SetAttribute("SessionStartTime", ns3::TimeValue(ns3::Seconds(s.start_s)));
316 app->SetNetController(m_nc);
317 if (m_teleportState) {
318 app->SetTeleportState(m_teleportState);
319 }
320 app->SetStartTime(ns3::Seconds(0.0));
321 app->SetStopTime(ns3::Seconds(1e9));
322 }
323 }
324}
325
326} // namespace q2ns
Main user-facing facade for creating and configuring a quantum network.
ns3::Ptr< QNode > CreateNode(const std::string &label="")
Create a QNode with an optional human-readable label.
ns3::Ptr< QChannel > InstallQuantumLink(ns3::Ptr< QNode > a, ns3::Ptr< QNode > b)
Install a duplex quantum link between two nodes.
TeleportationHelper & SetNetController(NetController *nc)
void BuildClassical(const TopologySpec &spec)
TeleportationHelper & UseBackend(std::string backend)
TeleportationHelper & SetDefaultQueue(std::string max)
void InstallSessionApps(const TopologySpec &spec)
TeleportationHelper & SetTeleportState(const std::shared_ptr< q2ns::QState > &tpl)
void BuildQuantum(const TopologySpec &spec)
void Install(const TopologySpec &spec)
TeleportationHelper & SetPortBase(uint16_t base)
uint16_t AllocatePort(uint64_t sessionId) const
std::unordered_map< std::string, ns3::Ptr< ns3::Node > > m_name2node
std::shared_ptr< q2ns::QState > m_teleportState
void BuildNodes(const TopologySpec &spec)
static ns3::Ipv4Address GetFirstNonLoopback(ns3::Ptr< ns3::Node > node)
static ns3::Ptr< ns3::Ipv4 > GetIpv4(ns3::Ptr< ns3::Node > node)
std::vector< LinkSpec > quantumEdges
std::vector< NodeSpec > nodes
std::vector< SessionSpec > sessions
std::vector< LinkSpec > classicalEdges