CHEN Yihui il y a 4 ans
commit
26a470e662
48 fichiers modifiés avec 1225 ajouts et 0 suppressions
  1. BIN
      img/1_2-3_2_rtt.png
  2. BIN
      img/1_2-3_2_th.png
  3. BIN
      img/1_3-1_2_rtt.png
  4. BIN
      img/1_3-1_2_th.png
  5. BIN
      img/2_2-1_2_rtt.png
  6. BIN
      img/2_2-1_2_th.png
  7. BIN
      img/2_2-4_2_rtt.png
  8. BIN
      img/2_2-4_2_th.png
  9. BIN
      img/2_3-1_2_rtt.png
  10. BIN
      img/2_3-1_2_th.png
  11. BIN
      img/3_2-1_2_rtt.png
  12. BIN
      img/3_2-1_2_th.png
  13. BIN
      img/3_3-1_2_rtt.png
  14. BIN
      img/3_3-1_2_th.png
  15. BIN
      img/3_3-1_3.png
  16. BIN
      img/3_3-1_3_rtt.png
  17. BIN
      img/3_3-1_3_th.png
  18. BIN
      img/4_2-1_2_rtt.png
  19. BIN
      img/4_2-1_2_th.png
  20. BIN
      img/4_3-1_2_rtt.png
  21. BIN
      img/4_3-1_2_th.png
  22. BIN
      img/4_3-2_3_rtt.png
  23. BIN
      img/4_3-2_3_th.png
  24. BIN
      img/arch-ecmp.png
  25. BIN
      img/arch.png
  26. BIN
      img/inter-cluster.png
  27. BIN
      img/many-to-one.png
  28. BIN
      img/many-to-one_trace.png
  29. BIN
      img/opt/2_2-4_2_rtt.png
  30. BIN
      img/opt/2_2-4_2_th.png
  31. BIN
      img/opt/3_3-1_2_rtt.png
  32. BIN
      img/opt/3_3-1_2_th.png
  33. BIN
      pcap/inter-cluster/inter-cluster-10-0.pcap
  34. BIN
      pcap/inter-cluster/inter-cluster-11-0.pcap
  35. BIN
      pcap/inter-cluster/inter-cluster-13-0.pcap
  36. BIN
      pcap/inter-cluster/inter-cluster-8-0.pcap
  37. BIN
      pcap/inter-cluster/inter-cluster_merged.pcap
  38. BIN
      pcap/many-to-one/Many-to-one-7-0.pcap
  39. BIN
      pcap/opt/inter-cluster/inter-cluster-11-0.pcap
  40. BIN
      pcap/opt/inter-cluster/inter-cluster-12-0.pcap
  41. BIN
      pcap/opt/inter-cluster/inter-cluster-14-0.pcap
  42. BIN
      pcap/opt/inter-cluster/inter-cluster-9-0.pcap
  43. BIN
      pcap/opt/many-to-one/inter-cluster-8-0.pcap
  44. 419 0
      report.md
  45. 204 0
      src/inter-cluster-opt.cc
  46. 199 0
      src/inter-cluster.cc
  47. 205 0
      src/many-to-one-opt.cc
  48. 198 0
      src/many-to-one.cc

BIN
img/1_2-3_2_rtt.png


BIN
img/1_2-3_2_th.png


BIN
img/1_3-1_2_rtt.png


BIN
img/1_3-1_2_th.png


BIN
img/2_2-1_2_rtt.png


BIN
img/2_2-1_2_th.png


BIN
img/2_2-4_2_rtt.png


BIN
img/2_2-4_2_th.png


BIN
img/2_3-1_2_rtt.png


BIN
img/2_3-1_2_th.png


BIN
img/3_2-1_2_rtt.png


BIN
img/3_2-1_2_th.png


BIN
img/3_3-1_2_rtt.png


BIN
img/3_3-1_2_th.png


BIN
img/3_3-1_3.png


BIN
img/3_3-1_3_rtt.png


BIN
img/3_3-1_3_th.png


BIN
img/4_2-1_2_rtt.png


BIN
img/4_2-1_2_th.png


BIN
img/4_3-1_2_rtt.png


BIN
img/4_3-1_2_th.png


BIN
img/4_3-2_3_rtt.png


BIN
img/4_3-2_3_th.png


BIN
img/arch-ecmp.png


BIN
img/arch.png


BIN
img/inter-cluster.png


BIN
img/many-to-one.png


BIN
img/many-to-one_trace.png


BIN
img/opt/2_2-4_2_rtt.png


BIN
img/opt/2_2-4_2_th.png


BIN
img/opt/3_3-1_2_rtt.png


BIN
img/opt/3_3-1_2_th.png


BIN
pcap/inter-cluster/inter-cluster-10-0.pcap


BIN
pcap/inter-cluster/inter-cluster-11-0.pcap


BIN
pcap/inter-cluster/inter-cluster-13-0.pcap


BIN
pcap/inter-cluster/inter-cluster-8-0.pcap


BIN
pcap/inter-cluster/inter-cluster_merged.pcap


BIN
pcap/many-to-one/Many-to-one-7-0.pcap


BIN
pcap/opt/inter-cluster/inter-cluster-11-0.pcap


BIN
pcap/opt/inter-cluster/inter-cluster-12-0.pcap


BIN
pcap/opt/inter-cluster/inter-cluster-14-0.pcap


BIN
pcap/opt/inter-cluster/inter-cluster-9-0.pcap


BIN
pcap/opt/many-to-one/inter-cluster-8-0.pcap


+ 419 - 0
report.md

@@ -0,0 +1,419 @@
+## Datacenter Network Simulation using ns3实验报告
+
+<p align=right>陈翊辉</p>
+
+<p align=right>SA19011116</p>
+
+[toc]
+
+### 网络结构和参数
+
+网络结构和参数如图1
+
+![arch](img/arch.png)
+
+<p align=center>图1 网络结构及参数图</p>
+
+#### inter-cluster
+
+![inter](img/inter-cluster.png)
+
+<p align=center>图2 inter-cluster连接示意图</p>
+
+#### many-to-one
+
+![many](img/many-to-one.png)
+
+<p align=center>图2 many-to-one连接示意图</p>
+
+### 目录组织说明
+
+* img:网络结构图,trace结果统计图
+* pcap:抓包文件
+* src:源码
+* report:报告
+
+```
+├── img
+│   ├── 1_2-3_2_rtt.png
+│   │   ...
+│   ├── many-to-one_trace.png
+│   └── opt
+│       ├── 2_2-4_2_rtt.png
+│       │   ...
+│       └── 3_3-1_2_th.png
+├── pcap
+│   ├── inter-cluster
+│   │   ├── inter-cluster_merged.pcap
+│   │   ├── inter-cluster-10-0.pcap
+│   │   ├── inter-cluster-11-0.pcap
+│   │   ├── inter-cluster-13-0.pcap
+│   │   └── inter-cluster-8-0.pcap
+│   ├── many-to-one
+│   │   └── Many-to-one-7-0.pcap
+│   └── opt
+│       ├── inter-cluster
+│       │   ├── inter-cluster-11-0.pcap
+│       │   ├── inter-cluster-12-0.pcap
+│       │   ├── inter-cluster-14-0.pcap
+│       │   └── inter-cluster-9-0.pcap
+│       └── many-to-one
+│           └── inter-cluster-8-0.pcap
+├── report.pdf
+└── src
+    ├── inter-cluster.cc
+    ├── inter-cluster-opt.cc
+    ├── many-to-one.cc
+    └── many-to-one-opt.cc
+
+```
+
+
+
+### 源码说明
+
+基本参照课上slides`ns3-Tutorial.pptx`的几个ppp和ethernets及tcp例子组合即可。
+
+代码使用Google Code Format规范,需要支持gnu std c++14编译器编译
+
+完整代码见`src/`
+
+首先定义各类节点数量,添加节点
+
+```c++
+const int CORE_SWITCH_NUM = 1;
+const int AGGREGATION_NUM = 2;
+const int TOR_NUM = 4;
+const int SERVER_NUM = 8;
+// ......
+
+// core_switch | aggregation_switch | tor_switch | server
+// 0           | 1 - 2              | 3 - 6      | 7 - 14
+all_nodes.Create(CORE_SWITCH_NUM + AGGREGATION_NUM + TOR_NUM + SERVER_NUM);
+```
+
+根据网络结构,将要用p2p连接的节点加入`p2p_nodes`
+
+```c++
+// p2p link
+p2p_nodes[i][j].Add(all_nodes.Get(i + CORE_THRESHOLD));
+p2p_nodes[i][j].Add(all_nodes.Get(j + AGGREGATION_THRESHOLD));
+```
+
+使用` PointToPointHelper`增加p2p网络
+
+```c++
+netdev_core_aggr[i * AGGREGATION_NUM + j] = p2p_helper[i][j].Install(p2p_nodes[i][j]);
+```
+
+(csma节点类似,略)
+
+用`Ipv4InterfaceContainer`分配3种ip地址
+
+```c++
+std::stringstream ss;
+ss << "10.0." << i + 1 << ".0";
+std::string base_addr;
+ss >> base_addr;
+addr.SetBase(base_addr.data(), "255.255.255.0");
+```
+
+安装网络应用`sink`和`client`,这里,使用`sink-client`对规定传输pattern
+
+pattern1: inter-cluster
+
+```c++
+  // sink - client settings idx: 1 - 8
+  // sink app nodes:   2, 4, 5, 7
+  //                   |  |  |  |
+  // client app nodes: 6, 8, 1, 3
+  const std::vector<std::pair<int, int>> sink_client_pairs = {
+      {2, 6}, {4, 8}, {5, 1}, {7, 3}};
+```
+
+pattern2: many-to-one
+
+```c++
+  // sink - client settings idx: 1 - 8
+  // sink app nodes:   1, 1, 1, 1, 1, 1, 1
+  //                   |  |  |  |  |  |  |
+  // client app nodes: 2, 3, 4, 5, 6, 7, 8
+  const std::vector<std::pair<int, int>> sink_client_pairs = {
+      {1, 2}, {1, 3}, {1, 4}, {1, 5}, {1, 6}, {1, 7}, {1, 8}};
+```
+
+这样两种pattern代码基本可以复用,只需修改`sink-client`对
+
+```c++
+PacketSinkHelper packetSinkHelper(
+  "ns3::TcpSocketFactory",
+  InetSocketAddress(Ipv4Address::GetAny(), 8080));
+OnOffHelper onOffHelper(
+  "ns3::TcpSocketFactory",
+  InetSocketAddress(
+  tor_server[sink_idx / 2].GetAddress(sink_idx % 2 + 1), 8080));
+```
+
+最后在sink端启用trace,csma混杂模式为`false`
+
+### 仿真结果及分析
+
+#### inter-cluster
+
+##### trace结果
+
+![2-6](img/3_3-1_3.png)
+
+<p align=center>图 2-6 节点 tcp抓包结果</p>
+
+以2-6的抓包为例,开始几个即ARP包用以确定对方mac地址,其后即两方来回tcp包,每个分包的长度有606,70等。
+
+##### 往返时间
+
+![2-6_rtt](img/3_3-1_3_rtt.png)
+
+<p align=center>图 2-6 节点间往返时间图</p>
+
+2-6之间的往返时间(rtt)很不稳定,较多序列(sequence)的往返时间很小(<10ms),但也有许多往返时间达到50多ms,100多ms,甚至少量达到了200多ms。
+
+##### 吞吐量与窗口大小
+
+![2-6th](img/3_3-1_3_th.png)
+
+<p align=center>图 2-6节点间吞吐量及分片长度图</p>
+
+2-6之间的吞吐量也非常不稳定,在50kbits/s到500kbits/s之间上下振荡。
+
+##### 总体结果
+
+| 测量           | 已捕获  | 已显示           |
+| -------------- | ------- | ---------------- |
+| 分组           | 4400    | 4400 (100.0%)    |
+| 时间跨度,s     | 61.285  | 61.285           |
+| 平均 pps       | 71.8    | 71.8             |
+| 平均分组大小,B | 413     | 413              |
+| 字节           | 1819396 | 1819396 (100.0%) |
+| 平均 字节/秒   | 29 k    | 29 k             |
+| 平均 比特/秒   | 237 k   | 237 k            |
+
+<p align=center>表 2-6节点间抓包结果</p>
+
+总体上说,2-6之间达到的平均速率为237kbtits,而从rtt和吞吐来看,比较不稳定。
+
+4-8,5-1,7-3之间的trace结果与2-6类似,往返时间和吞吐图见`img/`,总体结果如表
+
+|      | 平均分组大小,字节 | 字节    | 平均 比特/秒 |
+| ---- | ------------------ | ------- | ------------ |
+| 2-6  | 413                | 1819396 | 237 k        |
+| 4-8  | 414                | 1873482 | 244 k        |
+| 5-1  | 414                | 1858744 | 242 k        |
+| 7-3  | 414                | 1893156 | 247 k        |
+
+<p align=center>表 inter-cluster抓包总体结果</p>
+
+##### 网络瓶颈分析
+
+从理论上分析,在cluster间通信,如图2 many-to-one连接示意图,在core switch的链路上,每条链路平均分到1.5mbps的$\frac{1}{4}$,即大约375kbps;在aggregation switch到tor switch的链路上,每条链路平均分到1.0mbps的$\frac{1}{4}$,即大约250kbps,因而要full bisection使每台server达到1mpbs,瓶颈在aggregation switch到tor switch的链路上和core switch的链路上。从仿真结果来看,平均每对节点间的速率为243kbps左右,由此计算出a1和a2链路上速率为970kbps左右,以达到线路带宽上限,与理论符合较好。即aggregation switch链路上确实存在瓶颈。
+
+#### many-to-one
+
+##### trace结果
+
+![trace](img/many-to-one_trace.png)
+
+和inter-cluster类似,还是先arp找mac地址,之后tcp通信。
+
+| 测量           | 已捕获  | 已显示           |
+| -------------- | ------- | ---------------- |
+| 分组           | 20136   | 20136 (100.0%)   |
+| 时间跨度,s     | 67.446  | 67.446           |
+| 平均 pps       | 298.5   | 298.5            |
+| 平均分组大小,B | 408     | 408              |
+| 字节           | 8215218 | 8215218 (100.0%) |
+| 平均 字节/秒   | 121 k   | 121 k            |
+| 平均 比特/秒   | 974 k   | 974 k            |
+
+<p align=center>表 many-to-one trace结果统计表</p>
+
+从节点n1来看,速率基本达到了n1链路的1mbps。
+
+##### n2
+
+n2(10.0.1.3)和n1接在同一tor上,其往返时间和吞吐及窗口大小如下
+
+![2-1_th](img/1_3-1_2_rtt.png)
+
+<p align=center>图 2-1 节点间往返时间图</p>
+
+![2-1_th](img/1_3-1_2_th.png)
+
+<p align=center>图 2-1 节点间吞吐量和分片大小图</p>
+
+从上图可以看出,2-1节点间吞吐量相对稳定,在300-500kbps间;而往返时间开始时非常低(<20ms),之后逐渐升高,达到2000ms左右,仿真结束前回落到1200ms左右。
+
+##### n3 n4
+
+n3(10.0.2.2)和n4(10.0.2.3)和n1在同一cluster,以n3为例其往返时间和吞吐及窗口大小如下
+
+![3-1_rtt](img/2_2-1_2_rtt.png)
+
+<p align=center>图 3-1 节点间往返时间图</p>
+
+![3-1_th](img/2_2-1_2_th.png)
+
+<p align=center>图 3-1 节点间吞吐量和分片大小图</p>
+
+从上图来看,3-1节点间吞吐量非常不稳定,在0和400000bps之前,而往返时间在开始是极低,之后增长到2000ms以上,在仿真结束前又回到极低。4-1节点与3-1节点类似,见`img`,不在报告中包含。
+
+##### n5 n6 n7 n8
+
+n5 n6 n7 n8节点在另一个cluster,以n5(10.0.3.2)为例其往返时间和吞吐及窗口大小如下
+
+![5-1_rtt](img/3_2-1_2_rtt.png)
+
+<p align=center>图 5-1 节点间往返时间图</p>
+
+![5-1_th](img/3_2-1_2_th.png)
+
+<p align=center>图 5-1 节点间吞吐量和分片大小图</p>
+
+从上图来看,5-1节点间连接,在60s前几乎分不到带宽,往返时间在2000ms左右,在60s之后(n2 n3 n4都传输完了),分到几百kbps带宽,6,7,8节点结果见`img`,不在报告中包含。
+
+##### 总体结果
+
+| Address A | Address B | Packets | Bytes   | Bytes B → A | Duration |
+| --------- | --------- | ------- | ------- | ----------- | -------- |
+| 10.0.1.2  | 10.0.1.3  | 8984    | 3692696 | 3482972     | 61.99544 |
+| 10.0.1.2  | 10.0.2.3  | 2979    | 1236634 | 1166700     | 64.38073 |
+| 10.0.1.2  | 10.0.2.2  | 3267    | 1355098 | 1278444     | 64.37938 |
+| 10.0.1.2  | 10.0.4.2  | 3086    | 1235140 | 1165906     | 65.94691 |
+| 10.0.1.2  | 10.0.4.3  | 625     | 240002  | 226554      | 67.40779 |
+| 10.0.1.2  | 10.0.3.2  | 640     | 245136  | 231338      | 67.41691 |
+| 10.0.1.2  | 10.0.3.3  | 547     | 210000  | 198236      | 62.92476 |
+
+<p align=center>表 many-to-one trace结果统计表</p>
+
+从上表来看,离n1越近的吞吐越多。而5,6,7,8本来应该结果接近的,但其中7却获取了较多吞吐,这表明了网络可预测性不好,有较大波动和随机性。
+
+##### 网络瓶颈分析
+
+从理论上分析,若2-7节点都要达到1mbps速率,则n1节点需要7mbps,t1需要6mbps,t2需要2mbps,c1,a2需要4mbps,t3,t4需要2mbps。总的来说,瓶颈主要还是在core到aggregation之间。从仿真结果来看,cluster2总的吞吐都不高,也是受限于core到aggregation之间。
+
+### 优化方法
+
+从上面分析上看,网络瓶颈主要在core到aggregation及aggregation到tor之间。因而考虑增加一个core switch;并且参考胖树结构,增加连通度,增加aggregation到其他tor的连接;并且使用ecmp路由。
+
+![arch-ecmp](img/arch-ecmp.png)
+
+### 优化源码说明
+
+在之前代码基础上,只需做少量修改,具体见`src/`
+
+另外需要说明的是,需要注释掉ns-3里一个和ecmp有关的assert(或者编release版本)
+
+`ns-allinone-3.27/ns-3.27/src/internet/model/global-route-manager-impl.cc +316`
+
+```c++
+// NS_ASSERT_MSG (m_ecmpRootExits.size () <= 1, "Assumed there is at most one exit from the root to this vertex");
+```
+
+首先修改core switch 的数量
+
+```c++
+const int CORE_SWITCH_NUM = 2;
+const int AGGREGATION_NUM = 2;
+const int TOR_NUM = 4;
+const int SERVER_NUM = 8;
+```
+
+aggregation到tor的csma连接增加
+
+```c++
+for (int i = 0; i < AGGREGATION_NUM; ++i) {
+  csma_aggregation_nodes[i * 2].Add(all_nodes.Get(AGGREGATION_THRESHOLD + i));
+  csma_aggregation_nodes[i * 2].Add(all_nodes.Get(TOR_THRESHOLD));
+  csma_aggregation_nodes[i * 2].Add(all_nodes.Get(TOR_THRESHOLD + 1));
+
+  csma_aggregation_nodes[i * 2 + 1].Add(all_nodes.Get(AGGREGATION_THRESHOLD + i));
+  csma_aggregation_nodes[i * 2 + 1].Add(all_nodes.Get(TOR_THRESHOLD + 2));
+  csma_aggregation_nodes[i * 2 + 1].Add(all_nodes.Get(TOR_THRESHOLD + 3));
+}
+```
+
+使用ecmp路由
+
+```c++
+// Enable ECMP routing
+Config::SetDefault("ns3::Ipv4GlobalRouting::RandomEcmpRouting", BooleanValue(true));
+```
+
+### 优化结果
+
+优化的度量主要考虑往返时间,吞吐的大小和稳定性及各节点获得带宽的差距。
+
+#### inter-cluster
+
+##### 总体比较
+
+|      | 字节 - 优化前 | 平均 比特/秒 - 优化前 | 字节 - 优化后 | 平均 比特/秒 - 优化后 |
+| ---- | ------------- | --------------------- | ------------- | --------------------- |
+| 2-6  | 1819396       | 237 k                 | 3911322       | 496 k                 |
+| 4-8  | 1873482       | 244 k                 | 3425396       | 435 k                 |
+| 5-1  | 1858744       | 242 k                 | 3135438       | 401 k                 |
+| 7-3  | 1893156       | 247 k                 | 3551942       | 452 k                 |
+
+<p align=center>表 inter-cluster优化前后吞吐对比</p>
+
+从上表可见,优化后吞吐明显提升,瓶颈变为tor到server链路带宽。
+
+#####  往返时间
+
+![opt_inter_rtt](img/opt/2_2-4_2_rtt.png)
+
+<p align=center>图 优化后inter-cluster往返时间图</p>
+
+从上图来看,往返时间也有很大改善,大多数往返时间极端,最大也不超过100ms,极少数超过60ms。
+
+##### 吞吐及分片大小
+
+![opt-inter-th](img/opt/2_2-4_2_th.png)
+
+<p align=center>图 优化后inter-cluster吞吐和分片长度图</p>
+
+从上图来看,分片长度非常稳定,而吞吐除了大幅提高外也较优化前稳定。其他几对连接结果也类似。
+
+#### many-to-one
+
+##### 总体比较
+
+| Address A | Address B | Bytes   | Bytes-opt | Duration | Duration-opt | Bytes B → A | Bits/s B → A - opt |
+| --------- | --------- | ------- | --------- | -------- | ------------ | ----------- | ------------------ |
+| 10.0.1.2  | 10.0.1.3  | 3692696 | 3302192   | 61.99544 | 62.09534     | 3482972     | 401292.3           |
+| 10.0.1.2  | 10.0.2.2  | 1355098 | 1048420   | 64.37938 | 66.19298     | 1278444     | 119081.9           |
+| 10.0.1.2  | 10.0.2.3  | 1236634 | 360872    | 64.38073 | 68.13514     | 1166700     | 39450.31           |
+| 10.0.1.2  | 10.0.3.2  | 245136  | 905990    | 67.41691 | 64.15984     | 231338      | 105996.8           |
+| 10.0.1.2  | 10.0.3.3  | 210000  | 842802    | 62.92476 | 66.88895     | 198236      | 93787.21           |
+| 10.0.1.2  | 10.0.4.2  | 1235140 | 938414    | 65.94691 | 66.20609     | 1165906     | 106482.3           |
+| 10.0.1.2  | 10.0.4.3  | 240002  | 893610    | 67.40779 | 64.16249     | 226554      | 104477.5           |
+
+<p align=center>表 many-to-one优化前后总部比较</p>
+
+从上表来看,优化后,总的带宽没有变化,大约为1mbps(因为节点的连接是1mbps),但相对于优化前,另一个cluster的4个节点传输的字节数非常均匀,每个节点的传输过程时尝也比较均匀。较原来有较大提升。
+
+##### 往返时间
+
+![opt-mant-rtt](img/opt/3_3-1_2_rtt.png)
+
+从上图来看,在资源争夺阶段往返时间比优化前提高了,但也有较低的情况,原因是可以多条链路,有些链路通畅,而有些链路拥塞。而各个节点之前的差距比优化前减少了,也即提升了网络公平性。
+
+##### 吞吐
+
+![opt-mant-th](img/opt/3_3-1_2_th.png)
+
+<p align=center>图 many-to-one优化后吞吐和分片长度</p>
+
+从上图来看,优化后整个过程中吞吐都比较均匀,虽然有某些时刻降到0,但没有出现长时间获取不到资源,也没有出现优化前前期几乎争不到资源的情况,只能等别的节点传完再开始的情况。而分片大小也非常稳定。
+
+#### 总结
+
+使用了core switch和ecmp routing后,网络性能提升,特别是many-to-one这种情况,减少了网络的波动,增加了网络的公平性和可预测性。这比单纯提高带宽的效果要更好。并且增加core switch和链路数量后,网络的鲁棒性也提升了,即使某个核心或聚合交换机故障,网络也能连通。

+ 204 - 0
src/inter-cluster-opt.cc

@@ -0,0 +1,204 @@
+#include <algorithm>
+#include <functional>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "ns3/applications-module.h"
+#include "ns3/core-module.h"
+#include "ns3/csma-module.h"
+#include "ns3/internet-module.h"
+#include "ns3/ipv4-global-routing-helper.h"
+#include "ns3/network-module.h"
+#include "ns3/point-to-point-module.h"
+#include "ns3/tcp-l4-protocol.h"
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE("InterCluster");
+
+// node num
+const int CORE_SWITCH_NUM = 2;
+const int AGGREGATION_NUM = 2;
+const int TOR_NUM = 4;
+const int SERVER_NUM = 8;
+
+const int CORE_THRESHOLD = 0;
+const int AGGREGATION_THRESHOLD = CORE_THRESHOLD + CORE_SWITCH_NUM;
+const int TOR_THRESHOLD = AGGREGATION_THRESHOLD + AGGREGATION_NUM;
+const int SERVER_THRESHOLD = TOR_THRESHOLD + TOR_NUM;
+
+int main(int argc, char* argv[]) {
+  LogComponentEnable("PacketSink", LOG_LEVEL_INFO);
+  LogComponentEnable("OnOffApplication", LOG_LEVEL_INFO);
+
+  // Enable ECMP routing
+  Config::SetDefault("ns3::Ipv4GlobalRouting::RandomEcmpRouting", BooleanValue(true));
+
+  // define all nodes
+  NodeContainer all_nodes;
+  // core_switch | aggregation_switch | tor_switch | server
+  // 0 - 1       | 2 - 3              | 4 - 7      | 8 - 15
+  all_nodes.Create(CORE_SWITCH_NUM + AGGREGATION_NUM + TOR_NUM + SERVER_NUM);
+
+  // netdevices
+  NetDeviceContainer netdev_container[CORE_SWITCH_NUM * AGGREGATION_NUM +
+                                      AGGREGATION_NUM + TOR_NUM];
+  NetDeviceContainer* netdev_core_aggr = netdev_container;
+  NetDeviceContainer* netdev_aggr_tor =
+      netdev_core_aggr + CORE_SWITCH_NUM * AGGREGATION_NUM;
+  NetDeviceContainer* netdev_tor_server = netdev_aggr_tor + AGGREGATION_NUM;
+
+  // p2p (core_switch - aggregation_switch)
+  NodeContainer p2p_nodes[CORE_SWITCH_NUM][AGGREGATION_NUM];
+  // p2p link
+  for (int i = 0; i < CORE_SWITCH_NUM; ++i) {
+    for (int j = 0; j < AGGREGATION_NUM; ++j) {
+      p2p_nodes[i][j].Add(all_nodes.Get(i + CORE_THRESHOLD));
+      p2p_nodes[i][j].Add(all_nodes.Get(j + AGGREGATION_THRESHOLD));
+    }
+  }
+  // p2p install
+  PointToPointHelper p2p_helper[CORE_SWITCH_NUM][AGGREGATION_NUM];
+  for (int i = 0; i < CORE_SWITCH_NUM; ++i) {
+    for (int j = 0; j < AGGREGATION_NUM; ++j) {
+      p2p_helper[i][j].SetDeviceAttribute("DataRate", StringValue("1.5Mbps"));
+      p2p_helper[i][j].SetChannelAttribute("Delay", StringValue("500ns"));
+      netdev_core_aggr[i * AGGREGATION_NUM + j] =
+          p2p_helper[i][j].Install(p2p_nodes[i][j]);
+    }
+  }
+
+  // csma
+  NodeContainer csma_nodes[AGGREGATION_NUM + TOR_NUM];
+
+  NodeContainer* csma_aggregation_nodes = csma_nodes;
+  NodeContainer* csma_tor_nodes = csma_aggregation_nodes + AGGREGATION_NUM;
+  // csma link (aggregation_switch - tor_switch)
+  for (int i = 0; i < AGGREGATION_NUM; ++i) {
+    csma_aggregation_nodes[i].Add(all_nodes.Get(AGGREGATION_THRESHOLD + i));
+    csma_aggregation_nodes[i].Add(all_nodes.Get(TOR_THRESHOLD));
+    csma_aggregation_nodes[i].Add(all_nodes.Get(TOR_THRESHOLD + 1));
+    csma_aggregation_nodes[i].Add(all_nodes.Get(TOR_THRESHOLD + 2));
+    csma_aggregation_nodes[i].Add(all_nodes.Get(TOR_THRESHOLD + 3));
+  }
+  // csma link (tor_switch - server)
+  for (int i = 0; i < TOR_NUM; ++i) {
+    csma_tor_nodes[i].Add(all_nodes.Get(TOR_THRESHOLD + i));
+    csma_tor_nodes[i].Add(all_nodes.Get(SERVER_THRESHOLD + i * 2));
+    csma_tor_nodes[i].Add(all_nodes.Get(SERVER_THRESHOLD + i * 2 + 1));
+  }
+  // csma install (aggregation_switch - tor_switch, tor_switch - server)
+  CsmaHelper csma_helper[AGGREGATION_NUM + TOR_NUM];
+  for (int i = 0; i < AGGREGATION_NUM + TOR_NUM; ++i) {
+    csma_helper[i].SetChannelAttribute("DataRate", StringValue("1.0Mbps"));
+    csma_helper[i].SetChannelAttribute("Delay", StringValue("500ns"));
+    netdev_aggr_tor[i] = csma_helper[i].Install(csma_nodes[i]);
+  }
+
+  // install internet stack for all nodes
+  InternetStackHelper stack;
+  stack.Install(all_nodes);
+
+  // assign ivp4 addresses
+  Ipv4InterfaceContainer ipv4_container[CORE_SWITCH_NUM * AGGREGATION_NUM +
+                                        AGGREGATION_NUM + TOR_NUM];
+  Ipv4InterfaceContainer* core_aggregation = ipv4_container;
+  Ipv4InterfaceContainer* aggregation_tor =
+      core_aggregation + CORE_SWITCH_NUM * AGGREGATION_NUM;
+  Ipv4InterfaceContainer* tor_server = aggregation_tor + AGGREGATION_NUM;
+
+  // assign tor - server
+  for (int i = 0; i < TOR_NUM; ++i) {
+    Ipv4AddressHelper addr;
+    std::stringstream ss;
+    ss << "10.0." << i + 1 << ".0";
+    std::string base_addr;
+    ss >> base_addr;
+    addr.SetBase(base_addr.data(), "255.255.255.0");
+    tor_server[i] = addr.Assign(netdev_tor_server[i]);
+  }
+  // assign aggregation - tor
+  for (int i = 0; i < AGGREGATION_NUM; ++i) {
+    Ipv4AddressHelper addr;
+    std::stringstream ss;
+    ss << "10." << i + 1 << ".1.0";
+    std::string base_addr;
+    ss >> base_addr;
+    addr.SetBase(base_addr.data(), "255.255.255.0");
+    aggregation_tor[i] = addr.Assign(netdev_aggr_tor[i]);
+  }
+  // assign core - aggregation
+  for (int i = 0; i < CORE_SWITCH_NUM * AGGREGATION_NUM; ++i) {
+    Ipv4AddressHelper addr;
+    std::stringstream ss;
+    ss << "192.168." << i + 1 << ".0";
+    std::string base_addr;
+    ss >> base_addr;
+    addr.SetBase(base_addr.data(), "255.255.255.0");
+    core_aggregation[i] = addr.Assign(netdev_core_aggr[i]);
+  }
+
+  // set tcp sink app
+  ApplicationContainer sinkOrClientApp[8];
+
+  // sink - client settings idx: 1 - 8
+  // sink app nodes:   2, 4, 5, 7
+  //                   |  |  |  |
+  // client app nodes: 6, 8, 1, 3
+  const std::vector<std::pair<int, int>> sink_client_pairs = {
+      {2, 6}, {4, 8}, {5, 1}, {7, 3}};
+
+  std::for_each(
+      sink_client_pairs.begin(), sink_client_pairs.end(),
+      [&](const std::pair<int, int>& pair) {
+        // c arrays start from 0 idx: 0 - 7
+        int sink_idx = pair.first - 1;
+        int client_idx = pair.second - 1;
+        // sink app setup
+        PacketSinkHelper packetSinkHelper(
+            "ns3::TcpSocketFactory",
+            InetSocketAddress(Ipv4Address::GetAny(), 8080 + sink_idx));
+        sinkOrClientApp[sink_idx] = packetSinkHelper.Install(
+            all_nodes.Get(SERVER_THRESHOLD + sink_idx));
+        sinkOrClientApp[sink_idx].Start(Seconds(0.0));
+        sinkOrClientApp[sink_idx].Stop(Seconds(60.0));
+
+        // client app setup
+        OnOffHelper onOffHelper(
+            "ns3::TcpSocketFactory",
+            InetSocketAddress(
+                tor_server[sink_idx / 2].GetAddress(sink_idx % 2 + 1),
+                8080 + sink_idx));
+        onOffHelper.SetAttribute(
+            "OnTime", StringValue("ns3::ConstantRandomVariable[Constant=50]"));
+        onOffHelper.SetAttribute(
+            "OffTime", StringValue("ns3::ConstantRandomVariable[Constant=0]"));
+        onOffHelper.SetAttribute("PacketSize", UintegerValue(4096));
+        sinkOrClientApp[client_idx] =
+            onOffHelper.Install(all_nodes.Get(SERVER_THRESHOLD + client_idx));
+        sinkOrClientApp[client_idx].Start(Seconds(0.0));
+        sinkOrClientApp[client_idx].Stop(Seconds(60.0));
+      });
+
+  // populate routing tables
+  Ipv4GlobalRoutingHelper::PopulateRoutingTables();
+
+  // enable pcap at sink nodes
+  std::for_each(sink_client_pairs.begin(), sink_client_pairs.end(),
+                [&](const std::pair<int, int>& pair) {
+                  // c arrays start from 0
+                  int sink_idx = pair.first - 1;
+                  csma_helper[AGGREGATION_NUM + sink_idx / 2].EnablePcap(
+                      "inter-cluster",
+                      netdev_tor_server[sink_idx / 2].Get(sink_idx % 2 + 1),
+                      false);
+                });
+  Simulator::Run();
+  Simulator::Destroy();
+
+  return 0;
+}

+ 199 - 0
src/inter-cluster.cc

@@ -0,0 +1,199 @@
+#include <algorithm>
+#include <functional>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "ns3/applications-module.h"
+#include "ns3/core-module.h"
+#include "ns3/csma-module.h"
+#include "ns3/internet-module.h"
+#include "ns3/ipv4-global-routing-helper.h"
+#include "ns3/network-module.h"
+#include "ns3/point-to-point-module.h"
+#include "ns3/tcp-l4-protocol.h"
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE("InterCluster");
+
+// node num
+const int CORE_SWITCH_NUM = 1;
+const int AGGREGATION_NUM = 2;
+const int TOR_NUM = 4;
+const int SERVER_NUM = 8;
+
+const int CORE_THRESHOLD = 0;
+const int AGGREGATION_THRESHOLD = CORE_THRESHOLD + CORE_SWITCH_NUM;
+const int TOR_THRESHOLD = AGGREGATION_THRESHOLD + AGGREGATION_NUM;
+const int SERVER_THRESHOLD = TOR_THRESHOLD + TOR_NUM;
+
+int main(int argc, char* argv[]) {
+  LogComponentEnable("PacketSink", LOG_LEVEL_INFO);
+  LogComponentEnable("OnOffApplication", LOG_LEVEL_INFO);
+
+  // define all nodes
+  NodeContainer all_nodes;
+  // core_switch | aggregation_switch | tor_switch | server
+  // 0           | 1 - 2              | 3 - 6      | 7 - 14
+  all_nodes.Create(CORE_SWITCH_NUM + AGGREGATION_NUM + TOR_NUM + SERVER_NUM);
+
+  // netdevices
+  NetDeviceContainer netdev_container[CORE_SWITCH_NUM * AGGREGATION_NUM +
+                                      AGGREGATION_NUM + TOR_NUM];
+  NetDeviceContainer* netdev_core_aggr = netdev_container;
+  NetDeviceContainer* netdev_aggr_tor =
+      netdev_core_aggr + CORE_SWITCH_NUM * AGGREGATION_NUM;
+  NetDeviceContainer* netdev_tor_server = netdev_aggr_tor + AGGREGATION_NUM;
+
+  // p2p (core_switch - aggregation_switch)
+  NodeContainer p2p_nodes[CORE_SWITCH_NUM][AGGREGATION_NUM];
+  // p2p link
+  for (int i = 0; i < CORE_SWITCH_NUM; ++i) {
+    for (int j = 0; j < AGGREGATION_NUM; ++j) {
+      p2p_nodes[i][j].Add(all_nodes.Get(i + CORE_THRESHOLD));
+      p2p_nodes[i][j].Add(all_nodes.Get(j + AGGREGATION_THRESHOLD));
+    }
+  }
+  // p2p install
+  PointToPointHelper p2p_helper[CORE_SWITCH_NUM][AGGREGATION_NUM];
+  for (int i = 0; i < CORE_SWITCH_NUM; ++i) {
+    for (int j = 0; j < AGGREGATION_NUM; ++j) {
+      p2p_helper[i][j].SetDeviceAttribute("DataRate", StringValue("1.5Mbps"));
+      p2p_helper[i][j].SetChannelAttribute("Delay", StringValue("500ns"));
+      netdev_core_aggr[i * AGGREGATION_NUM + j] =
+          p2p_helper[i][j].Install(p2p_nodes[i][j]);
+    }
+  }
+
+  // csma
+  NodeContainer csma_nodes[AGGREGATION_NUM + TOR_NUM];
+
+  NodeContainer* csma_aggregation_nodes = csma_nodes;
+  NodeContainer* csma_tor_nodes = csma_aggregation_nodes + AGGREGATION_NUM;
+  // csma link (aggregation_switch - tor_switch)
+  for (int i = 0; i < AGGREGATION_NUM; ++i) {
+    csma_aggregation_nodes[i].Add(all_nodes.Get(AGGREGATION_THRESHOLD + i));
+    csma_aggregation_nodes[i].Add(all_nodes.Get(TOR_THRESHOLD + i * 2));
+    csma_aggregation_nodes[i].Add(all_nodes.Get(TOR_THRESHOLD + i * 2 + 1));
+  }
+  // csma link (tor_switch - server)
+  for (int i = 0; i < TOR_NUM; ++i) {
+    csma_tor_nodes[i].Add(all_nodes.Get(TOR_THRESHOLD + i));
+    csma_tor_nodes[i].Add(all_nodes.Get(SERVER_THRESHOLD + i * 2));
+    csma_tor_nodes[i].Add(all_nodes.Get(SERVER_THRESHOLD + i * 2 + 1));
+  }
+  // csma install (aggregation_switch - tor_switch, tor_switch - server)
+  CsmaHelper csma_helper[AGGREGATION_NUM + TOR_NUM];
+  for (int i = 0; i < AGGREGATION_NUM + TOR_NUM; ++i) {
+    csma_helper[i].SetChannelAttribute("DataRate", StringValue("1.0Mbps"));
+    csma_helper[i].SetChannelAttribute("Delay", StringValue("500ns"));
+    netdev_aggr_tor[i] = csma_helper[i].Install(csma_nodes[i]);
+  }
+
+  // install internet stack for all nodes
+  InternetStackHelper stack;
+  stack.Install(all_nodes);
+
+  // assign ivp4 addresses
+  Ipv4InterfaceContainer ipv4_container[CORE_SWITCH_NUM * AGGREGATION_NUM +
+                                        AGGREGATION_NUM + TOR_NUM];
+  Ipv4InterfaceContainer* core_aggregation = ipv4_container;
+  Ipv4InterfaceContainer* aggregation_tor =
+      core_aggregation + CORE_SWITCH_NUM * AGGREGATION_NUM;
+  Ipv4InterfaceContainer* tor_server = aggregation_tor + AGGREGATION_NUM;
+
+  // assign tor - server
+  for (int i = 0; i < TOR_NUM; ++i) {
+    Ipv4AddressHelper addr;
+    std::stringstream ss;
+    ss << "10.0." << i + 1 << ".0";
+    std::string base_addr;
+    ss >> base_addr;
+    addr.SetBase(base_addr.data(), "255.255.255.0");
+    tor_server[i] = addr.Assign(netdev_tor_server[i]);
+  }
+  // assign aggregation - tor
+  for (int i = 0; i < AGGREGATION_NUM; ++i) {
+    Ipv4AddressHelper addr;
+    std::stringstream ss;
+    ss << "10." << i + 1 << ".1.0";
+    std::string base_addr;
+    ss >> base_addr;
+    addr.SetBase(base_addr.data(), "255.255.255.0");
+    aggregation_tor[i] = addr.Assign(netdev_aggr_tor[i]);
+  }
+  // assign core - aggregation
+  for (int i = 0; i < CORE_SWITCH_NUM * AGGREGATION_NUM; ++i) {
+    Ipv4AddressHelper addr;
+    std::stringstream ss;
+    ss << "192.168." << i + 1 << ".0";
+    std::string base_addr;
+    ss >> base_addr;
+    addr.SetBase(base_addr.data(), "255.255.255.0");
+    core_aggregation[i] = addr.Assign(netdev_core_aggr[i]);
+  }
+
+  // set tcp sink app
+  ApplicationContainer sinkOrClientApp[8];
+
+  // sink - client settings idx: 1 - 8
+  // sink app nodes:   2, 4, 5, 7
+  //                   |  |  |  |
+  // client app nodes: 6, 8, 1, 3
+  const std::vector<std::pair<int, int>> sink_client_pairs = {
+      {2, 6}, {4, 8}, {5, 1}, {7, 3}};
+
+  std::for_each(
+      sink_client_pairs.begin(), sink_client_pairs.end(),
+      [&](const std::pair<int, int>& pair) {
+        // c arrays start from 0 idx: 0 - 7
+        int sink_idx = pair.first - 1;
+        int client_idx = pair.second - 1;
+        // sink app setup
+        PacketSinkHelper packetSinkHelper(
+            "ns3::TcpSocketFactory",
+            InetSocketAddress(Ipv4Address::GetAny(), 8080 + sink_idx));
+        sinkOrClientApp[sink_idx] = packetSinkHelper.Install(
+            all_nodes.Get(SERVER_THRESHOLD + sink_idx));
+        sinkOrClientApp[sink_idx].Start(Seconds(0.0));
+        sinkOrClientApp[sink_idx].Stop(Seconds(60.0));
+
+        // client app setup
+        OnOffHelper onOffHelper(
+            "ns3::TcpSocketFactory",
+            InetSocketAddress(
+                tor_server[sink_idx / 2].GetAddress(sink_idx % 2 + 1),
+                8080 + sink_idx));
+        onOffHelper.SetAttribute(
+            "OnTime", StringValue("ns3::ConstantRandomVariable[Constant=50]"));
+        onOffHelper.SetAttribute(
+            "OffTime", StringValue("ns3::ConstantRandomVariable[Constant=0]"));
+        onOffHelper.SetAttribute("PacketSize", UintegerValue(4096));
+        sinkOrClientApp[client_idx] =
+            onOffHelper.Install(all_nodes.Get(SERVER_THRESHOLD + client_idx));
+        sinkOrClientApp[client_idx].Start(Seconds(0.0));
+        sinkOrClientApp[client_idx].Stop(Seconds(60.0));
+      });
+
+  // populate routing tables
+  Ipv4GlobalRoutingHelper::PopulateRoutingTables();
+
+  // enable pcap at sink nodes
+  std::for_each(sink_client_pairs.begin(), sink_client_pairs.end(),
+                [&](const std::pair<int, int>& pair) {
+                  // c arrays start from 0
+                  int sink_idx = pair.first - 1;
+                  csma_helper[AGGREGATION_NUM + sink_idx / 2].EnablePcap(
+                      "inter-cluster",
+                      netdev_tor_server[sink_idx / 2].Get(sink_idx % 2 + 1),
+                      false);
+                });
+  Simulator::Run();
+  Simulator::Destroy();
+
+  return 0;
+}

+ 205 - 0
src/many-to-one-opt.cc

@@ -0,0 +1,205 @@
+#include <algorithm>
+#include <functional>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "ns3/applications-module.h"
+#include "ns3/core-module.h"
+#include "ns3/csma-module.h"
+#include "ns3/internet-module.h"
+#include "ns3/ipv4-global-routing-helper.h"
+#include "ns3/network-module.h"
+#include "ns3/point-to-point-module.h"
+#include "ns3/tcp-l4-protocol.h"
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE("ManyToOne");
+
+// node num
+const int CORE_SWITCH_NUM = 2;
+const int AGGREGATION_NUM = 2;
+const int TOR_NUM = 4;
+const int SERVER_NUM = 8;
+
+const int CORE_THRESHOLD = 0;
+const int AGGREGATION_THRESHOLD = CORE_THRESHOLD + CORE_SWITCH_NUM;
+const int TOR_THRESHOLD = AGGREGATION_THRESHOLD + AGGREGATION_NUM;
+const int SERVER_THRESHOLD = TOR_THRESHOLD + TOR_NUM;
+
+int main(int argc, char* argv[]) {
+  LogComponentEnable("PacketSink", LOG_LEVEL_INFO);
+  LogComponentEnable("OnOffApplication", LOG_LEVEL_INFO);
+
+  // Enable ECMP routing
+  Config::SetDefault("ns3::Ipv4GlobalRouting::RandomEcmpRouting",
+                     BooleanValue(true));
+
+  // define all nodes
+  NodeContainer all_nodes;
+  // core_switch | aggregation_switch | tor_switch | server
+  // 0 - 1       | 2 - 3              | 4 - 7      | 8 - 15
+  all_nodes.Create(CORE_SWITCH_NUM + AGGREGATION_NUM + TOR_NUM + SERVER_NUM);
+
+  // netdevices
+  NetDeviceContainer netdev_container[CORE_SWITCH_NUM * AGGREGATION_NUM +
+                                      AGGREGATION_NUM + TOR_NUM];
+  NetDeviceContainer* netdev_core_aggr = netdev_container;
+  NetDeviceContainer* netdev_aggr_tor =
+      netdev_core_aggr + CORE_SWITCH_NUM * AGGREGATION_NUM;
+  NetDeviceContainer* netdev_tor_server = netdev_aggr_tor + AGGREGATION_NUM;
+
+  // p2p (core_switch - aggregation_switch)
+  NodeContainer p2p_nodes[CORE_SWITCH_NUM][AGGREGATION_NUM];
+  // p2p link
+  for (int i = 0; i < CORE_SWITCH_NUM; ++i) {
+    for (int j = 0; j < AGGREGATION_NUM; ++j) {
+      p2p_nodes[i][j].Add(all_nodes.Get(i + CORE_THRESHOLD));
+      p2p_nodes[i][j].Add(all_nodes.Get(j + AGGREGATION_THRESHOLD));
+    }
+  }
+  // p2p install
+  PointToPointHelper p2p_helper[CORE_SWITCH_NUM][AGGREGATION_NUM];
+  for (int i = 0; i < CORE_SWITCH_NUM; ++i) {
+    for (int j = 0; j < AGGREGATION_NUM; ++j) {
+      p2p_helper[i][j].SetDeviceAttribute("DataRate", StringValue("1.5Mbps"));
+      p2p_helper[i][j].SetChannelAttribute("Delay", StringValue("500ns"));
+      netdev_core_aggr[i * AGGREGATION_NUM + j] =
+          p2p_helper[i][j].Install(p2p_nodes[i][j]);
+    }
+  }
+
+  // csma
+  NodeContainer csma_nodes[AGGREGATION_NUM + TOR_NUM];
+
+  NodeContainer* csma_aggregation_nodes = csma_nodes;
+  NodeContainer* csma_tor_nodes = csma_aggregation_nodes + AGGREGATION_NUM;
+  // csma link (aggregation_switch - tor_switch)
+  for (int i = 0; i < AGGREGATION_NUM; ++i) {
+    csma_aggregation_nodes[i].Add(all_nodes.Get(AGGREGATION_THRESHOLD + i));
+    csma_aggregation_nodes[i].Add(all_nodes.Get(TOR_THRESHOLD));
+    csma_aggregation_nodes[i].Add(all_nodes.Get(TOR_THRESHOLD + 1));
+    csma_aggregation_nodes[i].Add(all_nodes.Get(TOR_THRESHOLD + 2));
+    csma_aggregation_nodes[i].Add(all_nodes.Get(TOR_THRESHOLD + 3));
+  }
+  // csma link (tor_switch - server)
+  for (int i = 0; i < TOR_NUM; ++i) {
+    csma_tor_nodes[i].Add(all_nodes.Get(TOR_THRESHOLD + i));
+    csma_tor_nodes[i].Add(all_nodes.Get(SERVER_THRESHOLD + i * 2));
+    csma_tor_nodes[i].Add(all_nodes.Get(SERVER_THRESHOLD + i * 2 + 1));
+  }
+  // csma install (aggregation_switch - tor_switch, tor_switch - server)
+  CsmaHelper csma_helper[AGGREGATION_NUM + TOR_NUM];
+  for (int i = 0; i < AGGREGATION_NUM + TOR_NUM; ++i) {
+    csma_helper[i].SetChannelAttribute("DataRate", StringValue("1.0Mbps"));
+    csma_helper[i].SetChannelAttribute("Delay", StringValue("500ns"));
+    netdev_aggr_tor[i] = csma_helper[i].Install(csma_nodes[i]);
+  }
+
+  // install internet stack for all nodes
+  InternetStackHelper stack;
+  stack.Install(all_nodes);
+
+  // assign ivp4 addresses
+  Ipv4InterfaceContainer ipv4_container[CORE_SWITCH_NUM * AGGREGATION_NUM +
+                                        AGGREGATION_NUM + TOR_NUM];
+  Ipv4InterfaceContainer* core_aggregation = ipv4_container;
+  Ipv4InterfaceContainer* aggregation_tor =
+      core_aggregation + CORE_SWITCH_NUM * AGGREGATION_NUM;
+  Ipv4InterfaceContainer* tor_server = aggregation_tor + AGGREGATION_NUM;
+
+  // assign tor - server
+  for (int i = 0; i < TOR_NUM; ++i) {
+    Ipv4AddressHelper addr;
+    std::stringstream ss;
+    ss << "10.0." << i + 1 << ".0";
+    std::string base_addr;
+    ss >> base_addr;
+    addr.SetBase(base_addr.data(), "255.255.255.0");
+    tor_server[i] = addr.Assign(netdev_tor_server[i]);
+  }
+  // assign aggregation - tor
+  for (int i = 0; i < AGGREGATION_NUM; ++i) {
+    Ipv4AddressHelper addr;
+    std::stringstream ss;
+    ss << "10." << i + 1 << ".1.0";
+    std::string base_addr;
+    ss >> base_addr;
+    addr.SetBase(base_addr.data(), "255.255.255.0");
+    aggregation_tor[i] = addr.Assign(netdev_aggr_tor[i]);
+  }
+  // assign core - aggregation
+  for (int i = 0; i < CORE_SWITCH_NUM * AGGREGATION_NUM; ++i) {
+    Ipv4AddressHelper addr;
+    std::stringstream ss;
+    ss << "192.168." << i + 1 << ".0";
+    std::string base_addr;
+    ss >> base_addr;
+    addr.SetBase(base_addr.data(), "255.255.255.0");
+    core_aggregation[i] = addr.Assign(netdev_core_aggr[i]);
+  }
+
+  // set tcp sink app
+  ApplicationContainer sinkOrClientApp[8];
+
+  // sink - client settings idx: 1 - 8
+  // sink app nodes:   1, 1, 1, 1, 1, 1, 1
+  //                   |  |  |  |  |  |  |
+  // client app nodes: 2, 3, 4, 5, 6, 7, 8
+  const std::vector<std::pair<int, int>> sink_client_pairs = {
+      {1, 2}, {1, 3}, {1, 4}, {1, 5}, {1, 6}, {1, 7}, {1, 8}};
+
+  std::for_each(
+      sink_client_pairs.begin(), sink_client_pairs.end(),
+      [&](const std::pair<int, int>& pair) {
+        // c arrays start from 0 idx: 0 - 7
+        int sink_idx = pair.first - 1;
+        int client_idx = pair.second - 1;
+        // sink app setup
+        PacketSinkHelper packetSinkHelper(
+            "ns3::TcpSocketFactory",
+            InetSocketAddress(Ipv4Address::GetAny(), 8080 + client_idx));
+        sinkOrClientApp[sink_idx] = packetSinkHelper.Install(
+            all_nodes.Get(SERVER_THRESHOLD + sink_idx));
+        sinkOrClientApp[sink_idx].Start(Seconds(0.0));
+        sinkOrClientApp[sink_idx].Stop(Seconds(60.0));
+
+        // client app setup
+        OnOffHelper onOffHelper(
+            "ns3::TcpSocketFactory",
+            InetSocketAddress(
+                tor_server[sink_idx / 2].GetAddress(sink_idx % 2 + 1),
+                8080 + client_idx));
+        onOffHelper.SetAttribute(
+            "OnTime", StringValue("ns3::ConstantRandomVariable[Constant=50]"));
+        onOffHelper.SetAttribute(
+            "OffTime", StringValue("ns3::ConstantRandomVariable[Constant=0]"));
+        onOffHelper.SetAttribute("PacketSize", UintegerValue(4096));
+        sinkOrClientApp[client_idx] =
+            onOffHelper.Install(all_nodes.Get(SERVER_THRESHOLD + client_idx));
+        sinkOrClientApp[client_idx].Start(Seconds(0.0));
+        sinkOrClientApp[client_idx].Stop(Seconds(60.0));
+      });
+
+  // populate routing tables
+  Ipv4GlobalRoutingHelper::PopulateRoutingTables();
+
+  // enable pcap at sink nodes
+  std::for_each(sink_client_pairs.begin(), sink_client_pairs.end(),
+                [&](const std::pair<int, int>& pair) {
+                  // c arrays start from 0
+                  int sink_idx = pair.first - 1;
+                  csma_helper[AGGREGATION_NUM + sink_idx / 2].EnablePcap(
+                      "inter-cluster",
+                      netdev_tor_server[sink_idx / 2].Get(sink_idx % 2 + 1),
+                      false);
+                });
+  Simulator::Run();
+  Simulator::Destroy();
+
+  return 0;
+}

+ 198 - 0
src/many-to-one.cc

@@ -0,0 +1,198 @@
+#include <algorithm>
+#include <functional>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "ns3/applications-module.h"
+#include "ns3/core-module.h"
+#include "ns3/csma-module.h"
+#include "ns3/internet-module.h"
+#include "ns3/ipv4-global-routing-helper.h"
+#include "ns3/network-module.h"
+#include "ns3/point-to-point-module.h"
+#include "ns3/tcp-l4-protocol.h"
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE("ManyToOne");
+
+// node num
+const int CORE_SWITCH_NUM = 1;
+const int AGGREGATION_NUM = 2;
+const int TOR_NUM = 4;
+const int SERVER_NUM = 8;
+
+const int CORE_THRESHOLD = 0;
+const int AGGREGATION_THRESHOLD = CORE_THRESHOLD + CORE_SWITCH_NUM;
+const int TOR_THRESHOLD = AGGREGATION_THRESHOLD + AGGREGATION_NUM;
+const int SERVER_THRESHOLD = TOR_THRESHOLD + TOR_NUM;
+
+int main(int argc, char* argv[]) {
+  LogComponentEnable("PacketSink", LOG_LEVEL_INFO);
+  LogComponentEnable("OnOffApplication", LOG_LEVEL_INFO);
+
+  // define all nodes
+  NodeContainer all_nodes;
+  // core_switch | aggregation_switch | tor_switch | server
+  // 0           | 1 - 2              | 3 - 6      | 7 - 14
+  all_nodes.Create(CORE_SWITCH_NUM + AGGREGATION_NUM + TOR_NUM + SERVER_NUM);
+
+  // netdevices
+  NetDeviceContainer netdev_container[CORE_SWITCH_NUM * AGGREGATION_NUM +
+                                      AGGREGATION_NUM + TOR_NUM];
+  NetDeviceContainer* netdev_core_aggr = netdev_container;
+  NetDeviceContainer* netdev_aggr_tor =
+      netdev_core_aggr + CORE_SWITCH_NUM * AGGREGATION_NUM;
+  NetDeviceContainer* netdev_tor_server = netdev_aggr_tor + AGGREGATION_NUM;
+
+  // p2p (core_switch - aggregation_switch)
+  NodeContainer p2p_nodes[CORE_SWITCH_NUM][AGGREGATION_NUM];
+  // p2p link
+  for (int i = 0; i < CORE_SWITCH_NUM; ++i) {
+    for (int j = 0; j < AGGREGATION_NUM; ++j) {
+      p2p_nodes[i][j].Add(all_nodes.Get(i + CORE_THRESHOLD));
+      p2p_nodes[i][j].Add(all_nodes.Get(j + AGGREGATION_THRESHOLD));
+    }
+  }
+  // p2p install
+  PointToPointHelper p2p_helper[CORE_SWITCH_NUM][AGGREGATION_NUM];
+  for (int i = 0; i < CORE_SWITCH_NUM; ++i) {
+    for (int j = 0; j < AGGREGATION_NUM; ++j) {
+      p2p_helper[i][j].SetDeviceAttribute("DataRate", StringValue("1.5Mbps"));
+      p2p_helper[i][j].SetChannelAttribute("Delay", StringValue("500ns"));
+      netdev_core_aggr[i * AGGREGATION_NUM + j] =
+          p2p_helper[i][j].Install(p2p_nodes[i][j]);
+    }
+  }
+
+  // csma
+  NodeContainer csma_nodes[AGGREGATION_NUM + TOR_NUM];
+
+  NodeContainer* csma_aggregation_nodes = csma_nodes;
+  NodeContainer* csma_tor_nodes = csma_aggregation_nodes + AGGREGATION_NUM;
+  // csma link (aggregation_switch - tor_switch)
+  for (int i = 0; i < AGGREGATION_NUM; ++i) {
+    csma_aggregation_nodes[i].Add(all_nodes.Get(AGGREGATION_THRESHOLD + i));
+    csma_aggregation_nodes[i].Add(all_nodes.Get(TOR_THRESHOLD + i * 2));
+    csma_aggregation_nodes[i].Add(all_nodes.Get(TOR_THRESHOLD + i * 2 + 1));
+  }
+  // csma link (tor_switch - server)
+  for (int i = 0; i < TOR_NUM; ++i) {
+    csma_tor_nodes[i].Add(all_nodes.Get(TOR_THRESHOLD + i));
+    csma_tor_nodes[i].Add(all_nodes.Get(SERVER_THRESHOLD + i * 2));
+    csma_tor_nodes[i].Add(all_nodes.Get(SERVER_THRESHOLD + i * 2 + 1));
+  }
+  // csma install (aggregation_switch - tor_switch, tor_switch - server)
+  CsmaHelper csma_helper[AGGREGATION_NUM + TOR_NUM];
+  for (int i = 0; i < AGGREGATION_NUM + TOR_NUM; ++i) {
+    csma_helper[i].SetChannelAttribute("DataRate", StringValue("1.0Mbps"));
+    csma_helper[i].SetChannelAttribute("Delay", StringValue("500ns"));
+    netdev_aggr_tor[i] = csma_helper[i].Install(csma_nodes[i]);
+  }
+
+  // install internet stack for all nodes
+  InternetStackHelper stack;
+  stack.Install(all_nodes);
+
+  // assign ivp4 addresses
+  Ipv4InterfaceContainer ipv4_container[CORE_SWITCH_NUM * AGGREGATION_NUM +
+                                        AGGREGATION_NUM + TOR_NUM];
+  Ipv4InterfaceContainer* core_aggregation = ipv4_container;
+  Ipv4InterfaceContainer* aggregation_tor =
+      core_aggregation + CORE_SWITCH_NUM * AGGREGATION_NUM;
+  Ipv4InterfaceContainer* tor_server = aggregation_tor + AGGREGATION_NUM;
+
+  // assign tor - server
+  for (int i = 0; i < TOR_NUM; ++i) {
+    Ipv4AddressHelper addr;
+    std::stringstream ss;
+    ss << "10.0." << i + 1 << ".0";
+    std::string base_addr;
+    ss >> base_addr;
+    addr.SetBase(base_addr.data(), "255.255.255.0");
+    tor_server[i] = addr.Assign(netdev_tor_server[i]);
+  }
+  // assign aggregation - tor
+  for (int i = 0; i < AGGREGATION_NUM; ++i) {
+    Ipv4AddressHelper addr;
+    std::stringstream ss;
+    ss << "10." << i + 1 << ".1.0";
+    std::string base_addr;
+    ss >> base_addr;
+    addr.SetBase(base_addr.data(), "255.255.255.0");
+    aggregation_tor[i] = addr.Assign(netdev_aggr_tor[i]);
+  }
+  // assign core - aggregation
+  for (int i = 0; i < CORE_SWITCH_NUM * AGGREGATION_NUM; ++i) {
+    Ipv4AddressHelper addr;
+    std::stringstream ss;
+    ss << "192.168." << i + 1 << ".0";
+    std::string base_addr;
+    ss >> base_addr;
+    addr.SetBase(base_addr.data(), "255.255.255.0");
+    core_aggregation[i] = addr.Assign(netdev_core_aggr[i]);
+  }
+
+  // set tcp sink app
+  ApplicationContainer sinkOrClientApp[8];
+
+  // sink - client settings idx: 1 - 8
+  // sink app nodes:   1, 1, 1, 1, 1, 1, 1
+  //                   |  |  |  |  |  |  |
+  // client app nodes: 2, 3, 4, 5, 6, 7, 8
+  const std::vector<std::pair<int, int>> sink_client_pairs = {
+      {1, 2}, {1, 3}, {1, 4}, {1, 5}, {1, 6}, {1, 7}, {1, 8}};
+
+  std::for_each(
+      sink_client_pairs.begin(), sink_client_pairs.end(),
+      [&](const std::pair<int, int>& pair) {
+        // c arrays start from 0 idx: 0 - 7
+        int sink_idx = pair.first - 1;
+        int client_idx = pair.second - 1;
+        // sink app setup
+        PacketSinkHelper packetSinkHelper(
+            "ns3::TcpSocketFactory",
+            InetSocketAddress(Ipv4Address::GetAny(), 8080 + client_idx));
+        sinkOrClientApp[sink_idx] = packetSinkHelper.Install(
+            all_nodes.Get(SERVER_THRESHOLD + sink_idx));
+        sinkOrClientApp[sink_idx].Start(Seconds(0.0));
+        sinkOrClientApp[sink_idx].Stop(Seconds(60.0));
+
+        // client app setup
+        OnOffHelper onOffHelper(
+            "ns3::TcpSocketFactory",
+            InetSocketAddress(
+                tor_server[sink_idx / 2].GetAddress(sink_idx % 2 + 1), 8080 + client_idx));
+        onOffHelper.SetAttribute(
+            "OnTime", StringValue("ns3::ConstantRandomVariable[Constant=50]"));
+        onOffHelper.SetAttribute(
+            "OffTime", StringValue("ns3::ConstantRandomVariable[Constant=0]"));
+        onOffHelper.SetAttribute("PacketSize", UintegerValue(4096));
+        sinkOrClientApp[client_idx] =
+            onOffHelper.Install(all_nodes.Get(SERVER_THRESHOLD + client_idx));
+        sinkOrClientApp[client_idx].Start(Seconds(0.0));
+        sinkOrClientApp[client_idx].Stop(Seconds(60.0));
+      });
+
+  // populate routing tables
+  Ipv4GlobalRoutingHelper::PopulateRoutingTables();
+
+  // enable pcap at sink nodes
+  std::for_each(sink_client_pairs.begin(), sink_client_pairs.end(),
+                [&](const std::pair<int, int>& pair) {
+                  // c arrays start from 0
+                  int sink_idx = pair.first - 1;
+                  csma_helper[AGGREGATION_NUM + sink_idx / 2].EnablePcap(
+                      "Many-to-one",
+                      netdev_tor_server[sink_idx / 2].Get(sink_idx % 2 + 1),
+                      false);
+                });
+  Simulator::Run();
+  Simulator::Destroy();
+
+  return 0;
+}