Q2NS dev
ns-3 module
Loading...
Searching...
No Matches
q2ns-classical-network-builder.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-classical-network-builder.h"
11
12#include "ns3/ipv4-static-routing-helper.h"
13#include "ns3/ipv6-static-routing-helper.h"
14#include "ns3/ripng-helper.h"
15
16#include <sstream>
17
18using namespace ns3;
19
20
22
30 m_links.push_back(link);
31}
33 m_lans.push_back(lan);
34}
39 m_defDelay = d;
40}
41void ClassicalNetworkBuilder::AttachNode(std::string name, Ptr<Node> node) {
42 m_nodes[name] = node;
43}
44
48
49 NodeContainer all;
50 for (auto& kv : m_nodes)
51 all.Add(kv.second);
52
53 if (m_routing == Routing::Ripng && v6) {
54 InternetStackHelper stack;
55 stack.SetIpv4StackInstall(v4);
56 stack.SetIpv6StackInstall(v6);
57
58 Ipv6ListRoutingHelper list6;
59 RipNgHelper rip6;
60 // Optional: faster convergence, lower periodic updates if you want:
61 // rip6.Set("LinkDown", BooleanValue(false));
62
63 // priority 0 = highest
64 list6.Add(rip6, 0);
65 // We can still add static/v6 as a backup with lower priority if desired:
66 // Ipv6StaticRoutingHelper v6static; list6.Add(v6static, 10);
67
68 // Build a combined InternetStack with the custom v6 list routing
69 InternetStackHelper custom;
70 custom.SetIpv4StackInstall(v4);
71 custom.SetIpv6StackInstall(v6);
72 custom.SetRoutingHelper(list6);
73 custom.Install(all);
74 return;
75 }
76
77 // Default (Global/StaticShortestPaths/None): vanilla stack
78 InternetStackHelper stack;
79 stack.SetIpv4StackInstall(v4);
80 stack.SetIpv6StackInstall(v6);
81 stack.Install(all);
82}
83
84
86 NS_ASSERT(!m_nodes.empty());
88
89 NetworkHandle handle;
92
93 // Initialize node entries
94 for (auto& kv : m_nodes)
95 handle.nodes[kv.first].node = kv.second;
96
97 BuildLinks(handle);
98 BuildLans(handle);
99 AssignAddresses(handle);
100
101 // Optional routing
102 if (m_routing == Routing::Global) {
103 if (handle.hasIpv4)
104 Ipv4GlobalRoutingHelper::PopulateRoutingTables();
105 // v6: on-link ND only; multi-hop v6 won't work in this mode
107 InstallStaticShortestPaths(handle); // v4+v6 static (kept)
108 } else if (m_routing == Routing::Ripng) {
109 // nothing to call: RIPng learns routes during sim time
110 }
111
112
113 return handle;
114}
115
117 PointToPointHelper p2p;
118 p2p.SetDeviceAttribute("DataRate", StringValue(m_defRate));
119 p2p.SetChannelAttribute("Delay", StringValue(m_defDelay));
120
121 Ipv4AddressHelper v4h;
122 v4h.SetBase("10.1.0.0", "255.255.0.0"); // /16: ~65k addresses
123 Ipv6AddressHelper v6h;
124 v6h.SetBase(Ipv6Address("2001:db8::"), Ipv6Prefix(64));
125
126 for (const auto& l : m_links) {
127 Ptr<Node> A = m_nodes.at(l.nodeA);
128 Ptr<Node> B = m_nodes.at(l.nodeB);
129 NetDeviceContainer devs = p2p.Install(A, B);
130
131 auto& aEntry = handle.nodes[l.nodeA];
132 auto& bEntry = handle.nodes[l.nodeB];
133 aEntry.devices.push_back(devs.Get(0));
134 bEntry.devices.push_back(devs.Get(1));
135 aEntry.devBySegment[l.id] = devs.Get(0);
136 bEntry.devBySegment[l.id] = devs.Get(1);
137
138 Ipv4InterfaceContainer if4;
139 Ipv6InterfaceContainer if6;
140
141 if (handle.hasIpv4) {
142 v4h.NewNetwork();
143 if4 = v4h.Assign(devs);
144 aEntry.v4Addrs.push_back(if4.GetAddress(0));
145 bEntry.v4Addrs.push_back(if4.GetAddress(1));
146 }
147 if (handle.hasIpv6) {
148 v6h.NewNetwork();
149 if6 = v6h.Assign(devs);
150 if6.SetForwarding(0, true);
151 if6.SetForwarding(1, true);
152 if (m_routing == Routing::Global) {
153 if6.SetDefaultRouteInAllNodes(0);
154 if6.SetDefaultRouteInAllNodes(1);
155 }
156 aEntry.v6Addrs.push_back(if6.GetAddress(0, 1));
157 bEntry.v6Addrs.push_back(if6.GetAddress(1, 1));
158 }
159
160 // segIf cache
161 auto& ai = aEntry.segIf[l.id];
162 auto& bi = bEntry.segIf[l.id];
163 if (handle.hasIpv4) {
164 ai.ifIndexV4 = A->GetObject<Ipv4>()->GetInterfaceForDevice(devs.Get(0));
165 bi.ifIndexV4 = B->GetObject<Ipv4>()->GetInterfaceForDevice(devs.Get(1));
166 ai.v4 = if4.GetAddress(0);
167 bi.v4 = if4.GetAddress(1);
168 }
169 if (handle.hasIpv6) {
170 ai.ifIndexV6 = A->GetObject<Ipv6>()->GetInterfaceForDevice(devs.Get(0));
171 bi.ifIndexV6 = B->GetObject<Ipv6>()->GetInterfaceForDevice(devs.Get(1));
172 ai.v6 = if6.GetAddress(0, 1); // global
173 bi.v6 = if6.GetAddress(1, 1); // global
174 ai.v6ll = if6.GetAddress(0, 0); // link-local
175 bi.v6ll = if6.GetAddress(1, 0); // link-local
176 }
177 }
178}
179
181 for (const auto& lan : m_lans) {
182 CsmaHelper csma;
183 csma.SetChannelAttribute("DataRate", StringValue(lan.rate.empty() ? m_defRate : lan.rate));
184 csma.SetChannelAttribute("Delay", StringValue(lan.delay.empty() ? m_defDelay : lan.delay));
185
186 NodeContainer members;
187 for (auto& name : lan.members)
188 members.Add(m_nodes.at(name));
189
190 NetDeviceContainer devs = csma.Install(members);
191
192 Ipv4AddressHelper v4h;
193 v4h.SetBase("10.1.0.0", "255.255.0.0"); // /16: ~65k addresses
194 Ipv6AddressHelper v6h;
195 v6h.SetBase(Ipv6Address("2001:db8:1::"), Ipv6Prefix(64));
196
197 Ipv4InterfaceContainer if4;
198 Ipv6InterfaceContainer if6;
199
200 if (handle.hasIpv4) {
201 if4 = v4h.Assign(devs);
202 for (uint32_t i = 0; i < members.GetN(); ++i) {
203 auto name = lan.members[i];
204 handle.nodes[name].devices.push_back(devs.Get(i));
205 handle.nodes[name].v4Addrs.push_back(if4.GetAddress(i));
206 handle.nodes[name].ifaceIndex.push_back(
207 members.Get(i)->GetObject<Ipv4>()->GetInterfaceForDevice(devs.Get(i)));
208 handle.nodes[name].devBySegment[lan.id] = devs.Get(i);
209
210 auto& info4 = handle.nodes[name].segIf[lan.id];
211 info4.ifIndexV4 = members.Get(i)->GetObject<Ipv4>()->GetInterfaceForDevice(devs.Get(i));
212 info4.v4 = if4.GetAddress(i);
213 }
214 }
215 if (handle.hasIpv6) {
216 if6 = v6h.Assign(devs);
217 for (uint32_t i = 0; i < members.GetN(); ++i) {
218 auto name = lan.members[i];
219 if6.SetForwarding(i, true);
220 if (m_routing == Routing::Global) {
221 if6.SetDefaultRouteInAllNodes(i);
222 }
223 handle.nodes[name].devices.push_back(devs.Get(i));
224 handle.nodes[name].v6Addrs.push_back(if6.GetAddress(i, 1));
225 handle.nodes[name].ifaceIndex.push_back(
226 members.Get(i)->GetObject<Ipv6>()->GetInterfaceForDevice(devs.Get(i)));
227 handle.nodes[name].devBySegment[lan.id] = devs.Get(i);
228
229 auto& info6 = handle.nodes[name].segIf[lan.id];
230 info6.ifIndexV6 = members.Get(i)->GetObject<Ipv6>()->GetInterfaceForDevice(devs.Get(i));
231 info6.v6 = if6.GetAddress(i, 1); // global
232 info6.v6ll = if6.GetAddress(i, 0); // link-local
233 }
234 }
235 }
236}
237
239 // Build segment -> members map
240 std::map<std::string, std::vector<std::string>> segMembers;
241 for (const auto& [name, entry] : h.nodes)
242 for (const auto& kv : entry.segIf)
243 segMembers[kv.first].push_back(name);
244
245 // Build adjacency (node-name graph)
246 std::map<std::string, std::vector<std::string>> adj;
247 for (const auto& [seg, members] : segMembers) {
248 if (members.size() < 2)
249 continue;
250 if (members.size() == 2) {
251 adj[members[0]].push_back(members[1]);
252 adj[members[1]].push_back(members[0]);
253 } else {
254 for (size_t i = 0; i < members.size(); ++i)
255 for (size_t j = i + 1; j < members.size(); ++j) {
256 adj[members[i]].push_back(members[j]);
257 adj[members[j]].push_back(members[i]);
258 }
259 }
260 }
261
262 Ipv4StaticRoutingHelper v4h;
263 Ipv6StaticRoutingHelper v6h;
264
265 for (const auto& [srcName, srcEntry] : h.nodes) {
266 // BFS from src
267 std::unordered_map<std::string, std::string> parent;
268 std::queue<std::string> q;
269 parent[srcName] = "";
270 q.push(srcName);
271
272 while (!q.empty()) {
273 std::string u = q.front();
274 q.pop();
275 for (const auto& v : adj[u]) {
276 if (!parent.count(v)) {
277 parent[v] = u;
278 q.push(v);
279 }
280 }
281 }
282
283 auto firstHop = [&](const std::string& dst) -> std::string {
284 if (dst == srcName)
285 return srcName;
286 if (!parent.count(dst))
287 return {};
288 std::string cur = dst, prev = parent.at(cur);
289 while (!prev.empty() && prev != srcName) {
290 cur = prev;
291 prev = parent.at(cur);
292 }
293 return prev.empty() ? std::string() : cur;
294 };
295
296 // Program host routes to every other node
297 for (const auto& [dstName, dstEntry] : h.nodes) {
298 if (dstName == srcName)
299 continue;
300 std::string nhName = firstHop(dstName);
301 if (nhName.empty())
302 continue;
303
304 // Find shared segment between src and next-hop
305 std::string segId;
306 for (const auto& kv : srcEntry.segIf) {
307 if (h.nodes.at(nhName).segIf.count(kv.first)) {
308 segId = kv.first;
309 break;
310 }
311 }
312 if (segId.empty())
313 continue;
314
315 const auto& meSeg = srcEntry.segIf.at(segId);
316 const auto& nhSeg = h.nodes.at(nhName).segIf.at(segId);
317
318 if (h.hasIpv4 && !dstEntry.v4Addrs.empty() && meSeg.ifIndexV4 != UINT32_MAX &&
319 nhSeg.ifIndexV4 != UINT32_MAX) {
320 auto me4 = srcEntry.node->GetObject<Ipv4>();
321 auto rt = v4h.GetStaticRouting(me4);
322 rt->AddHostRouteTo(dstEntry.v4Addrs.front(), nhSeg.v4, meSeg.ifIndexV4);
323 }
324
325 if (h.hasIpv6 && !dstEntry.v6Addrs.empty() && meSeg.ifIndexV6 != UINT32_MAX &&
326 nhSeg.ifIndexV6 != UINT32_MAX) {
327 auto me6 = srcEntry.node->GetObject<Ipv6>();
328 auto rt6 = v6h.GetStaticRouting(me6);
329 rt6->AddHostRouteTo(dstEntry.v6Addrs.front(), nhSeg.v6ll, meSeg.ifIndexV6);
330 }
331 }
332 }
333}
334
335
336
338 // Placeholder for per-node address book, if you want to flatten prefixes later.
339}
void InstallStaticShortestPaths(NetworkHandle &handle)
std::map< std::string, Ptr< Node > > m_nodes
void AttachNode(std::string name, Ptr< Node > node)