├── README.md ├── cmd ├── kill.sh ├── start.sh └── update.sh ├── config ├── ned │ ├── Gridnet.ned │ ├── Gridnet.txt │ ├── Nsfnet.ned │ └── Nsfnet.txt └── omnetpp.ini ├── environment.yml ├── modules ├── __init__.py ├── gym_env │ ├── __init__.py │ └── omnet_env.py ├── inet │ ├── Makefile │ ├── ipv4 │ │ ├── Ipv4.cc │ │ ├── Ipv4.h │ │ ├── Ipv4.ned │ │ ├── pfrpTable.cc │ │ └── pfrpTable.h │ └── udpapp │ │ ├── UdpPfrpApp.cc │ │ ├── UdpPfrpApp.h │ │ └── UdpPfrpApp.ned └── omnetpp │ └── cdataratechannel.cc └── utils ├── get_ned.py ├── get_osfp_ned.py └── get_rip_ned.py /README.md: -------------------------------------------------------------------------------- 1 |

RL4NeT++: A Packet-Level Network Simulation Framework for DRL-Based Routing Algorithms

2 |

Intelligent Sensing and Computing Research Center / School of Artificial Intelligence / Beijing University of Posts and Telecommunications

3 | 4 | 5 | 6 | ## Table of Contents 7 | 8 | - [Table of Contents](#table-of-contents) 9 | - [Directory Structure](#directory-structure) 10 | - [Environment Setup](#environment-setup) 11 | - [Conda Environment Setup](#conda-environment-setup) 12 | - [OMNeT++ Installation](#omnet-installation) 13 | - [ZMQ Installation](#zmq-installation) 14 | - [INET Installation](#inet-installation) 15 | - [Usage Instructions](#usage-instructions) 16 | - [Modify Simulation Mode](#modify-simulation-mode) 17 | - [Run Simulation Using Python](#run-simulation-using-python) 18 | - [Interact with OMNeT++ Process](#interact-with-omnet-process) 19 | - [Example Python File](#example-python-file) 20 | - [Contributors](#contributors) 21 | - [Contact](#contact) 22 | 23 | ## Directory Structure 24 | 25 | ``` 26 | ├── README.md 27 | ├── environment.yml // conda environment setup file 28 | ├── cmd // scripts for controlling simulation environment execution and simulation processes 29 | ├── kill.sh // Script for terminating running simulation processes 30 | ├── start.sh // Script for starting a simulation process 31 | └── update.sh // Script for updating the simulation environment and restart it 32 | ├── config // Configuration files for the simulation environment 33 | ├── omnetpp.ini // OMNeT++ initialization file 34 | └── ned // Network topologies and initial probability tables 35 | ├── modules // Components of the simulation environment 36 | ├── init.py 37 | ├── gym_env // Communication module connecting Python and OMNeT++ 38 | ├── omnetpp // Addon files to OMNeT++ 39 | └── inet // Addon files to INET 40 | └── utils // Tools for users' convenience 41 | ├── get_ned.py // Convert topology files from GML files to NED file 42 | ├── get_ospf_ned.py // Replace routers in the topology information with OSPF 43 | └── get_rip_ned.py // Replace routers in the topology information with RIP 44 | ``` 45 | 46 | ## Environment Setup 47 | 48 | In the current directory, download the RL4Net++ source code: 49 | 50 | ```bash 51 | git clone https://github.com/bupt-ipcr/RL4NeTpp.git 52 | ``` 53 | 54 | ### Conda Environment Setup 55 | 56 | In RL4NeTpp folder, install the `conda` environment named "pfrp" using the `environment.yml` file: 57 | 58 | ``` 59 | cd RL4Netpp 60 | conda env create -f environment.yml 61 | ``` 62 | 63 | ### OMNeT++ Installation 64 | 65 | Afterward, download the omnetpp 5.6.1 installation package from the official website: 66 | 67 | ```shell 68 | cd ../ 69 | wget https://github.com/omnetpp/omnetpp/releases/download/omnetpp-5.6.1/omnetpp-5.6.1-src-linux.tgz 70 | ``` 71 | 72 | Extract it to the current directory: 73 | 74 | ```shell 75 | tar zxvf omnetpp-5.6.1-src-linux.tgz -C ./ 76 | ``` 77 | 78 | Navigate to the "omnetpp-5.6.1" folder and make the following changes in the `configure.user` file: 79 | 80 | ```shell 81 | WITH_TKENV=no # Used to enable a graphical interface based on Tcl/Tk (not needed here) 82 | WITH_QTENV=no # Used to enable a graphical interface based on Qt 83 | WITH_OSG=no # Used to enable OpenSceneGraph in the Qt-based graphical interface 84 | WITH_OSGEARTH=no # Same as above, used to enable osgEarth (requires enabling the previous option) 85 | ``` 86 | 87 | Then, replace `RL4Net++/modules/omnetpp/cdataratechannel.cc` with `omnetpp-5.6.1/src/sim/cdataratechannel.cc` in the current directory and run the following commands to set up the environment variables: 88 | 89 | ```shell 90 | cp -f RL4Netpp/modules/omnetpp/cdataratechannel.cc omnetpp-5.6.1/src/sim/ 91 | cd omnetpp-5.6.1/ 92 | . setenv 93 | ``` 94 | 95 | Next, install the necessary OSG libraries: 96 | 97 | ```shell 98 | apt install software-properties-common 99 | add-apt-repository ppa:ubuntugis/ppa 100 | apt update 101 | apt install openscenegraph-plugin-osgearth libosgearth-dev 102 | ``` 103 | 104 | Install other dependencies like bison, build-essential, gcc, g++… 105 | 106 | ```shell 107 | apt install build-essential gcc g++ bison flex perl tcl-dev tk-dev blt libxml2-dev zlib1g-dev doxygen graphviz openmpi-bin libopenmpi-dev libpcap-dev default-jre 108 | ``` 109 | 110 | Configure and build OMNeT++: 111 | 112 | ```shell 113 | ./configure 114 | make 115 | ``` 116 | 117 | After compilation, test if OMNeT++ is working properly: 118 | 119 | ```shell 120 | cd samples/dyna/ 121 | ./dyna 122 | ``` 123 | 124 | If it runs successfully, the installation is complete. 125 | 126 | ### ZMQ Installation 127 | 128 | Return to the current directory and prepare to install the sodium library, a dependency of zmq: 129 | 130 | ```shell 131 | cd ../../../ 132 | git clone https://github.com/jedisct1/libsodium.git 133 | cd libsodium/ 134 | ``` 135 | 136 | Configure, compile, and install the library: 137 | 138 | ```shell 139 | apt install autoconf 140 | ./autogen.sh -s 141 | ./configure 142 | make -j32 143 | sudo make install 144 | ``` 145 | 146 | Then, write the compiled dynamic library files to `/usr/local/include/`: 147 | 148 | ```shell 149 | ldconfig 150 | ``` 151 | 152 | Install ZMQ: 153 | 154 | ```shell 155 | apt install libzmq3-dev 156 | ``` 157 | 158 | ### INET Installation 159 | 160 | Return to the current directory and install INET version 4.2.1: 161 | 162 | ```shell 163 | cd ../ 164 | wget https://github.com/inet-framework/inet/releases/download/v4.2.1/inet-4.2.1-src.tgz 165 | tar zxvf inet-4.2.1-src.tgz -C ./ 166 | ``` 167 | 168 | After extraction, replace modified files in INET: 169 | 170 | ```shell 171 | cp -f RL4Netpp/modules/inet/Makefile inet4/ 172 | cp -r -f ./modules/inet/ipv4/* inet4/src/inet/networklayer/ipv4/ 173 | cp -r -f ./modules/inet/udpapp/* inet4/src/inet/applications/udpapp/ 174 | ``` 175 | 176 | Compile INET: 177 | 178 | ```shell 179 | cd inet4/ 180 | make makefiles 181 | make -j32 182 | . setenv 183 | ``` 184 | 185 | The installation is now complete. 186 | 187 | ## Usage Instructions 188 | 189 | ### Modify Simulation Mode 190 | 191 | - **Traditional Routing Algorithms**: Change `simMode` in `config/omnetpp.ini` file to 0. 192 | - **Single-Agent Reinforcement Learning Algorithm(SADRL)**: Change `simMode` to 1. 193 | - **Multi-Agent Reinforcement Learning Algorithm(MADRL)**: Change `simMode` to 2. 194 | 195 | In `config/omnetpp.ini` , update the network topology information that needs to be simulated in `network` and `routingFileName` parameters. Other parameters can be adjusted according to simulation requirements. 196 | 197 | ### Run Simulation Using Python 198 | 199 | Next, write Python files for running and interacting with OMNeT++. At the beginning of the Python code file, import the `OmnetEnv` class: 200 | 201 | ```python 202 | from modules.gym_env.omnet_env import OmnetEnv 203 | ``` 204 | 205 | Before the main code, initialize `OmnetEnv` based on Gym: 206 | 207 | ```python 208 | env = OmnetEnv() 209 | env = env.unwrapped 210 | ``` 211 | 212 | If you are using SADRL, add the following statement: 213 | 214 | ```python 215 | env.is_multi_agent = False 216 | ``` 217 | 218 | For MADRL, add: 219 | 220 | ```python 221 | env.is_multi_agent = True 222 | ``` 223 | 224 | Before each episode training starts, call `env.reset()` to start and reset the network simulation environment. 225 | 226 | ### Interact with OMNeT++ Process 227 | 228 | Use the `env.get_obs()` function to obtain three parameters sent back from OMNeT++: 229 | 230 | - **The first parameter:** String type, with values "s" or "r", representing whether the information returned is state or reward. 231 | - **The second parameter:** Integer type, representing the step number. 232 | - **The third parameter:** String type, containing the message body. 233 | - If the first parameter is "s" the third parameter contains link load information during the network simulation. 234 | - If the first parameter is "r" the third parameter contains delay and packet loss rate information for the current step. 235 | 236 | If `env.get_obs()` returns state information, input it into the DRL algorithm, and the agent will output a set of action values. Convert these action values to a string and use `env.make_action(action_str)` to send the action string to OMNeT++. 237 | 238 | If `env.get_obs()` returns reward information, immediately call `env.reward_rcvd()` after obtaining the information to inform OMNeT++ that the reward has been received. 239 | 240 | After completing an episode of training in the reinforcement learning algorithm, use `env.close()` to close the simulation environment. 241 | 242 | ## Example Python File 243 | 244 | Here's an example for a simple SADRL algorithm: 245 | 246 | ```python 247 | from modules.gym_env.omnet_env import OmnetEnv 248 | import ... 249 | 250 | env = OmnetEnv() 251 | env = env.unwrapped 252 | env.is_multi_agent = False 253 | 254 | agent = ... 255 | 256 | while current_episode < MAX_EPISODE: 257 | env.reset() 258 | ... 259 | while current_step < MAX_STEP_PER_EPISODE: 260 | s_or_r, step, msg = env.get_obs() 261 | if s_or_r == "s": 262 | ... 263 | state = torch.FloatTensor(msg) 264 | action = agent.make_action(state) 265 | ... 266 | action_str = "".join( 267 | f"{str(torch.sigmoid(a).tolist()[0][j])}," 268 | for j in range(action_dim) 269 | ) 270 | action_str = action_str[:-1] 271 | env.make_action(action_str) 272 | elif s_or_r == "r": 273 | delay = float(msg.split(",")[0]) 274 | loss_rate = float(msg.split(",")[1]) 275 | ... 276 | env.reward_rcvd() 277 | if env: 278 | env.close() 279 | ``` 280 | 281 | ## Contributors 282 | 283 | - [Jiawei Chen](https://github.com/Chen-J-W) 284 | - [Yang Xiao](https://github.com/gnayoaix) 285 | - [Guocheng Lin](https://github.com/lgc0208) 286 | - [Jun Liu](https://github.com/liujunlj) 287 | 288 | ## Contact 289 | 290 | Guocheng Lin ([linguocheng@bupt.edu.cn](mailto:linguocheng@bupt.edu.cn)), Beijing University of Posts and Telecommunications, China 291 | -------------------------------------------------------------------------------- /cmd/kill.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ### 3 | # @Author : CHEN Jiawei 4 | # @Date : 2023-10-18 20:28:25 5 | # @LastEditors : LIN Guocheng 6 | # @LastEditTime : 2023-10-18 20:58:15 7 | # @FilePath : /home/lgc/test/RL4Net++/cmd/kill.sh 8 | # @Description : Kill all RL4Net++ related processes. 9 | ### 10 | 11 | killall python 12 | killall inet 13 | killall opp_run_release -------------------------------------------------------------------------------- /cmd/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ### 3 | # @Author : CHEN Jiawei 4 | # @Date : 2023-10-18 20:28:25 5 | # @LastEditors : LIN Guocheng 6 | # @LastEditTime : 2023-10-18 21:03:59 7 | # @FilePath : /home/lgc/test/RL4Net++/cmd/start.sh 8 | # @Description : Start omnetpp, inet and conda environment before simulation. 9 | ### 10 | 11 | # replace the following paths with your omnetpp and inet path 12 | omnetpp_path="../omnetpp-5.6.1" 13 | inet_path="../inet4.5" 14 | 15 | current_path=$(pwd) 16 | 17 | # replace with your conda environment name 18 | conda_name="pfrp" 19 | 20 | # start omnetpp and inet environment 21 | cd $omnetpp_path 22 | . setenv 23 | cd $current_path 24 | 25 | cd $inet_path 26 | . setenv 27 | cd $current_path 28 | 29 | # start conda environment 30 | conda activate $conda_name -------------------------------------------------------------------------------- /cmd/update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ### 3 | # @Author : CHEN Jiawei 4 | # @Date : 2023-10-18 20:28:25 5 | # @LastEditors : LIN Guocheng 6 | # @LastEditTime : 2023-10-19 17:05:04 7 | # @FilePath : /home/lgc/test/RL4Net++/cmd/update.sh 8 | # @Description : Update edited modules to the path of omnetpp and inet, including file copying and compilation. 9 | ### 10 | 11 | # replace the following paths with your omnetpp and inet path 12 | omnetpp_path="../omnetpp-5.6.1" 13 | inet_path="../inet4" 14 | 15 | current_path=$(pwd) 16 | 17 | # copy edited module files 18 | cp -r ./modules/omnetpp/* $omnetpp_path/src/sim/ 19 | cp -r ./modules/inet/ipv4/* $inet_path/src/inet/networklayer/ipv4/ 20 | cp -r ./modules/inet/udpapp/* $inet_path/src/inet/applications/udpapp/ 21 | 22 | # compile omnetpp and inet 23 | cd $omnetpp_path 24 | make -j32 25 | . setenv 26 | cd $current_path 27 | 28 | cd $inet_path 29 | make -j32 30 | . setenv 31 | cd $current_path 32 | -------------------------------------------------------------------------------- /config/ned/Gridnet.ned: -------------------------------------------------------------------------------- 1 | import inet.networklayer.configurator.ipv4.Ipv4NetworkConfigurator; 2 | import inet.node.inet.StandardHost; 3 | import inet.node.inet.Router; 4 | import ned.DatarateChannel; 5 | import inet.node.ethernet.Eth1G; 6 | 7 | network Gridnet 8 | { 9 | parameters: 10 | @display("p=10,10;b=712,152"); 11 | types: 12 | channel C extends DatarateChannel 13 | { 14 | delay = 0.002s; 15 | datarate = 1Mbps; 16 | } 17 | submodules: 18 | H0: StandardHost { 19 | parameters: 20 | forwarding = true; 21 | @display("p=250,150;i=device/laptop"); 22 | gates: 23 | ethg[]; 24 | } 25 | 26 | H1: StandardHost { 27 | parameters: 28 | forwarding = true; 29 | @display("p=250,150;i=device/laptop"); 30 | gates: 31 | ethg[]; 32 | } 33 | 34 | H2: StandardHost { 35 | parameters: 36 | forwarding = true; 37 | @display("p=250,150;i=device/laptop"); 38 | gates: 39 | ethg[]; 40 | } 41 | 42 | H3: StandardHost { 43 | parameters: 44 | forwarding = true; 45 | @display("p=250,150;i=device/laptop"); 46 | gates: 47 | ethg[]; 48 | } 49 | 50 | H4: StandardHost { 51 | parameters: 52 | forwarding = true; 53 | @display("p=250,150;i=device/laptop"); 54 | gates: 55 | ethg[]; 56 | } 57 | 58 | H5: StandardHost { 59 | parameters: 60 | forwarding = true; 61 | @display("p=250,150;i=device/laptop"); 62 | gates: 63 | ethg[]; 64 | } 65 | 66 | H6: StandardHost { 67 | parameters: 68 | forwarding = true; 69 | @display("p=250,150;i=device/laptop"); 70 | gates: 71 | ethg[]; 72 | } 73 | 74 | H7: StandardHost { 75 | parameters: 76 | forwarding = true; 77 | @display("p=250,150;i=device/laptop"); 78 | gates: 79 | ethg[]; 80 | } 81 | 82 | H8: StandardHost { 83 | parameters: 84 | forwarding = true; 85 | @display("p=250,150;i=device/laptop"); 86 | gates: 87 | ethg[]; 88 | } 89 | 90 | configurator: Ipv4NetworkConfigurator { 91 | parameters: 92 | addDefaultRoutes = false; 93 | @display("p=100,100;is=s"); 94 | } 95 | R0: Router { 96 | parameters: 97 | hasOspf = true; 98 | // hasRip = true; 99 | @display("p=250,200"); 100 | } 101 | 102 | R1: Router { 103 | parameters: 104 | hasOspf = true; 105 | // hasRip = true; 106 | @display("p=250,200"); 107 | } 108 | 109 | R2: Router { 110 | parameters: 111 | hasOspf = true; 112 | // hasRip = true; 113 | @display("p=250,200"); 114 | } 115 | 116 | R3: Router { 117 | parameters: 118 | hasOspf = true; 119 | // hasRip = true; 120 | @display("p=250,200"); 121 | } 122 | 123 | R4: Router { 124 | parameters: 125 | hasOspf = true; 126 | // hasRip = true; 127 | @display("p=250,200"); 128 | } 129 | 130 | R5: Router { 131 | parameters: 132 | hasOspf = true; 133 | // hasRip = true; 134 | @display("p=250,200"); 135 | } 136 | 137 | R6: Router { 138 | parameters: 139 | hasOspf = true; 140 | // hasRip = true; 141 | @display("p=250,200"); 142 | } 143 | 144 | R7: Router { 145 | parameters: 146 | hasOspf = true; 147 | // hasRip = true; 148 | @display("p=250,200"); 149 | } 150 | 151 | R8: Router { 152 | parameters: 153 | hasOspf = true; 154 | // hasRip = true; 155 | @display("p=250,200"); 156 | } 157 | 158 | connections: 159 | R0.pppg++ <--> C <--> R2.pppg++; 160 | R0.pppg++ <--> C <--> R3.pppg++; 161 | R0.pppg++ <--> C <--> R7.pppg++; 162 | R0.pppg++ <--> C <--> R8.pppg++; 163 | 164 | R1.pppg++ <--> C <--> R2.pppg++; 165 | R1.pppg++ <--> C <--> R4.pppg++; 166 | R1.pppg++ <--> C <--> R5.pppg++; 167 | R1.pppg++ <--> C <--> R6.pppg++; 168 | R1.pppg++ <--> C <--> R7.pppg++; 169 | 170 | R2.pppg++ <--> C <--> R3.pppg++; 171 | R2.pppg++ <--> C <--> R8.pppg++; 172 | 173 | R3.pppg++ <--> C <--> R4.pppg++; 174 | R3.pppg++ <--> C <--> R8.pppg++; 175 | 176 | R4.pppg++ <--> C <--> R5.pppg++; 177 | R4.pppg++ <--> C <--> R6.pppg++; 178 | R4.pppg++ <--> C <--> R7.pppg++; 179 | 180 | R5.pppg++ <--> C <--> R6.pppg++; 181 | R5.pppg++ <--> C <--> R7.pppg++; 182 | 183 | R6.pppg++ <--> C <--> R7.pppg++; 184 | R6.pppg++ <--> C <--> R8.pppg++; 185 | 186 | 187 | R0.ethg++ <--> Eth1G <--> H0.ethg++; 188 | R1.ethg++ <--> Eth1G <--> H1.ethg++; 189 | R2.ethg++ <--> Eth1G <--> H2.ethg++; 190 | R3.ethg++ <--> Eth1G <--> H3.ethg++; 191 | R4.ethg++ <--> Eth1G <--> H4.ethg++; 192 | R5.ethg++ <--> Eth1G <--> H5.ethg++; 193 | R6.ethg++ <--> Eth1G <--> H6.ethg++; 194 | R7.ethg++ <--> Eth1G <--> H7.ethg++; 195 | R8.ethg++ <--> Eth1G <--> H8.ethg++; 196 | } -------------------------------------------------------------------------------- /config/ned/Gridnet.txt: -------------------------------------------------------------------------------- 1 | 0,0,25,25,0,0,0,25,25,0,0,20,0,20,20,20,20,0,25,25,0,25,0,0,0,0,25,25,0,25,0,25,0,0,0,25,0,20,0,20,0,20,20,20,0,0,25,0,0,25,0,25,25,0,0,20,0,0,20,20,0,20,20,20,20,0,0,20,20,20,0,0,25,0,25,25,0,0,25,0,0 -------------------------------------------------------------------------------- /config/ned/Nsfnet.ned: -------------------------------------------------------------------------------- 1 | import inet.networklayer.configurator.ipv4.Ipv4NetworkConfigurator; 2 | import inet.node.inet.StandardHost; 3 | import inet.node.inet.Router; 4 | import ned.DatarateChannel; 5 | import inet.node.ethernet.Eth1G; 6 | 7 | network Nsfnet 8 | { 9 | parameters: 10 | @display("p=10,10;b=712,152"); 11 | types: 12 | channel C extends DatarateChannel 13 | { 14 | delay = 0.002s; 15 | datarate = 1Mbps; 16 | } 17 | submodules: 18 | H0: StandardHost { 19 | parameters: 20 | forwarding = true; 21 | @display("p=250,150;i=device/laptop"); 22 | gates: 23 | ethg[]; 24 | } 25 | 26 | H1: StandardHost { 27 | parameters: 28 | forwarding = true; 29 | @display("p=250,150;i=device/laptop"); 30 | gates: 31 | ethg[]; 32 | } 33 | 34 | H2: StandardHost { 35 | parameters: 36 | forwarding = true; 37 | @display("p=250,150;i=device/laptop"); 38 | gates: 39 | ethg[]; 40 | } 41 | 42 | H3: StandardHost { 43 | parameters: 44 | forwarding = true; 45 | @display("p=250,150;i=device/laptop"); 46 | gates: 47 | ethg[]; 48 | } 49 | 50 | H4: StandardHost { 51 | parameters: 52 | forwarding = true; 53 | @display("p=250,150;i=device/laptop"); 54 | gates: 55 | ethg[]; 56 | } 57 | 58 | H5: StandardHost { 59 | parameters: 60 | forwarding = true; 61 | @display("p=250,150;i=device/laptop"); 62 | gates: 63 | ethg[]; 64 | } 65 | 66 | H6: StandardHost { 67 | parameters: 68 | forwarding = true; 69 | @display("p=250,150;i=device/laptop"); 70 | gates: 71 | ethg[]; 72 | } 73 | 74 | H7: StandardHost { 75 | parameters: 76 | forwarding = true; 77 | @display("p=250,150;i=device/laptop"); 78 | gates: 79 | ethg[]; 80 | } 81 | 82 | H8: StandardHost { 83 | parameters: 84 | forwarding = true; 85 | @display("p=250,150;i=device/laptop"); 86 | gates: 87 | ethg[]; 88 | } 89 | 90 | H9: StandardHost { 91 | parameters: 92 | forwarding = true; 93 | @display("p=250,150;i=device/laptop"); 94 | gates: 95 | ethg[]; 96 | } 97 | 98 | H10: StandardHost { 99 | parameters: 100 | forwarding = true; 101 | @display("p=250,150;i=device/laptop"); 102 | gates: 103 | ethg[]; 104 | } 105 | 106 | H11: StandardHost { 107 | parameters: 108 | forwarding = true; 109 | @display("p=250,150;i=device/laptop"); 110 | gates: 111 | ethg[]; 112 | } 113 | 114 | H12: StandardHost { 115 | parameters: 116 | forwarding = true; 117 | @display("p=250,150;i=device/laptop"); 118 | gates: 119 | ethg[]; 120 | } 121 | 122 | configurator: Ipv4NetworkConfigurator { 123 | parameters: 124 | addDefaultRoutes = false; 125 | @display("p=100,100;is=s"); 126 | } 127 | R0: Router { 128 | parameters: 129 | hasOspf = true; 130 | // hasRip = true; 131 | @display("p=250,200"); 132 | } 133 | 134 | R1: Router { 135 | parameters: 136 | hasOspf = true; 137 | // hasRip = true; 138 | @display("p=250,200"); 139 | } 140 | 141 | R2: Router { 142 | parameters: 143 | hasOspf = true; 144 | // hasRip = true; 145 | @display("p=250,200"); 146 | } 147 | 148 | R3: Router { 149 | parameters: 150 | hasOspf = true; 151 | // hasRip = true; 152 | @display("p=250,200"); 153 | } 154 | 155 | R4: Router { 156 | parameters: 157 | hasOspf = true; 158 | // hasRip = true; 159 | @display("p=250,200"); 160 | } 161 | 162 | R5: Router { 163 | parameters: 164 | hasOspf = true; 165 | // hasRip = true; 166 | @display("p=250,200"); 167 | } 168 | 169 | R6: Router { 170 | parameters: 171 | hasOspf = true; 172 | // hasRip = true; 173 | @display("p=250,200"); 174 | } 175 | 176 | R7: Router { 177 | parameters: 178 | hasOspf = true; 179 | // hasRip = true; 180 | @display("p=250,200"); 181 | } 182 | 183 | R8: Router { 184 | parameters: 185 | hasOspf = true; 186 | // hasRip = true; 187 | @display("p=250,200"); 188 | } 189 | 190 | R9: Router { 191 | parameters: 192 | hasOspf = true; 193 | // hasRip = true; 194 | @display("p=250,200"); 195 | } 196 | 197 | R10: Router { 198 | parameters: 199 | hasOspf = true; 200 | // hasRip = true; 201 | @display("p=250,200"); 202 | } 203 | 204 | R11: Router { 205 | parameters: 206 | hasOspf = true; 207 | // hasRip = true; 208 | @display("p=250,200"); 209 | } 210 | 211 | R12: Router { 212 | parameters: 213 | hasOspf = true; 214 | // hasRip = true; 215 | @display("p=250,200"); 216 | } 217 | 218 | connections: 219 | R0.pppg++ <--> C <--> R2.pppg++; 220 | R0.pppg++ <--> C <--> R7.pppg++; 221 | R0.pppg++ <--> C <--> R11.pppg++; 222 | 223 | R1.pppg++ <--> C <--> R2.pppg++; 224 | R1.pppg++ <--> C <--> R4.pppg++; 225 | 226 | 227 | R3.pppg++ <--> C <--> R12.pppg++; 228 | 229 | R4.pppg++ <--> C <--> R12.pppg++; 230 | 231 | R5.pppg++ <--> C <--> R6.pppg++; 232 | R5.pppg++ <--> C <--> R9.pppg++; 233 | 234 | R6.pppg++ <--> C <--> R7.pppg++; 235 | R6.pppg++ <--> C <--> R12.pppg++; 236 | 237 | 238 | R8.pppg++ <--> C <--> R9.pppg++; 239 | 240 | R9.pppg++ <--> C <--> R11.pppg++; 241 | 242 | R10.pppg++ <--> C <--> R11.pppg++; 243 | 244 | R11.pppg++ <--> C <--> R12.pppg++; 245 | 246 | 247 | R0.ethg++ <--> Eth1G <--> H0.ethg++; 248 | R1.ethg++ <--> Eth1G <--> H1.ethg++; 249 | R2.ethg++ <--> Eth1G <--> H2.ethg++; 250 | R3.ethg++ <--> Eth1G <--> H3.ethg++; 251 | R4.ethg++ <--> Eth1G <--> H4.ethg++; 252 | R5.ethg++ <--> Eth1G <--> H5.ethg++; 253 | R6.ethg++ <--> Eth1G <--> H6.ethg++; 254 | R7.ethg++ <--> Eth1G <--> H7.ethg++; 255 | R8.ethg++ <--> Eth1G <--> H8.ethg++; 256 | R9.ethg++ <--> Eth1G <--> H9.ethg++; 257 | R10.ethg++ <--> Eth1G <--> H10.ethg++; 258 | R11.ethg++ <--> Eth1G <--> H11.ethg++; 259 | R12.ethg++ <--> Eth1G <--> H12.ethg++; 260 | } -------------------------------------------------------------------------------- /config/ned/Nsfnet.txt: -------------------------------------------------------------------------------- 1 | 0,0,33,0,0,0,0,33,0,0,0,33,0,0,0,50,0,50,0,0,0,0,0,0,0,0,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,50,0,0,0,0,0,0,0,0,0,0,50,0,0,0,0,0,0,50,0,0,50,0,0,0,0,0,0,0,0,33,0,33,0,0,0,0,33,50,0,0,0,0,0,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,33,0,0,33,0,0,33,0,0,0,0,0,0,0,0,0,0,0,0,100,0,25,0,0,0,0,0,0,0,0,25,25,0,25,0,0,0,25,25,0,25,0,0,0,0,25,0 -------------------------------------------------------------------------------- /config/omnetpp.ini: -------------------------------------------------------------------------------- 1 | [General] 2 | # define the path for ned files and name of network 3 | ned-path = ned 4 | network = Gridnet 5 | 6 | # define network random seeds 7 | num-rngs = 1 8 | seed-0-mt = 5000 9 | 10 | # close the output of the omnetpp intermediate process simulation information 11 | **.statistic-recording = false 12 | **.scalar-recording = false 13 | **.vector-recording = false 14 | 15 | **.H*.numApps = 1 16 | **.app[0].typename = "UdpPfrpApp" 17 | **.app[0].destPort = 1234 18 | **.app[0].messageLength = 128 # unit: bytes 19 | 20 | # enter flow rate for every host 21 | **.app[0].flowRate = 0.2 22 | 23 | # simMode: 0 - traditional algorithm such as OSPF and RIP 24 | # simMode: 1 - DRL for single-agent 25 | # simMode: 2 - DRL for multi-agent 26 | **.app[0].simMode = 0 27 | **.app[0].startTime = 0s 28 | **.app[0].nodeNum = 9 29 | 30 | # enter the path of your initial routing probability file 31 | **.app[0].routingFileName = "ned/Gridnet.txt" 32 | 33 | **.app[0].localPort = 1234 34 | **.app[0].timeToLive = 200 35 | 36 | # discard the packet when the packet delivery time exceeds survivalTime 37 | **.app[0].survivalTime = 2.0 38 | **.ipv4.ip.survivalTime = 2.0 39 | 40 | # duration of each step, unit: s 41 | **.app[0].stepTime = 2 42 | 43 | # the number of steps in the omnetpp simulation 44 | # needs to be at least 3 more than the number of steps used for training 45 | # in order to start cold 46 | **.app[0].totalStep = 60 47 | 48 | **.app[0].zmqPort = 5555 49 | 50 | **.arp.cacheTimeout = 1s -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: pfrp 2 | dependencies: 3 | - _libgcc_mutex=0.1=main 4 | - ca-certificates=2021.4.13=h06a4308_1 5 | - certifi=2020.12.5=py37h06a4308_0 6 | - ld_impl_linux-64=2.33.1=h53a641e_7 7 | - libffi=3.3=he6710b0_2 8 | - libgcc-ng=9.1.0=hdf63c60_0 9 | - libstdcxx-ng=9.1.0=hdf63c60_0 10 | - ncurses=6.2=he6710b0_1 11 | - openssl=1.1.1k=h27cfd23_0 12 | - pip=21.0.1=py37h06a4308_0 13 | - python=3.7.10=hdb3f193_0 14 | - readline=8.1=h27cfd23_0 15 | - setuptools=52.0.0=py37h06a4308_0 16 | - sqlite=3.35.4=hdfb4753_0 17 | - tk=8.6.10=hbc83047_0 18 | - wheel=0.36.2=pyhd3eb1b0_0 19 | - xz=5.2.5=h7b6447c_0 20 | - zlib=1.2.11=h7b6447c_3 21 | - pip: 22 | - absl-py==0.12.0 23 | - cachetools==4.2.2 24 | - chardet==4.0.0 25 | - cloudpickle==1.6.0 26 | - cycler==0.10.0 27 | - google-auth==1.30.1 28 | - google-auth-oauthlib==0.4.4 29 | - grpcio==1.38.0 30 | - gym==0.18.3 31 | - idna==2.10 32 | - importlib-metadata==4.2.0 33 | - kiwisolver==1.3.1 34 | - markdown==3.3.4 35 | - matplotlib==3.4.1 36 | - numpy==1.20.2 37 | - oauthlib==3.1.0 38 | - pandas==1.2.4 39 | - pillow==8.2.0 40 | - protobuf==3.17.1 41 | - pyasn1==0.4.8 42 | - pyasn1-modules==0.2.8 43 | - pyglet==1.5.15 44 | - pyparsing==2.4.7 45 | - python-dateutil==2.8.1 46 | - pytz==2021.1 47 | - pyzmq==22.0.3 48 | - requests==2.25.1 49 | - requests-oauthlib==1.3.0 50 | - rl4net==0.2.6 51 | - rsa==4.7.2 52 | - scipy==1.6.3 53 | - six==1.15.0 54 | - tensorboard==2.5.0 55 | - tensorboard-data-server==0.6.1 56 | - tensorboard-plugin-wit==1.8.0 57 | - torch==1.8.1 58 | - typing-extensions==3.10.0.0 59 | - urllib3==1.26.5 60 | - werkzeug==2.0.1 61 | - zipp==3.4.1 62 | - zmq==0.0.0 63 | prefix: /root/anaconda3/envs/pfrp 64 | 65 | -------------------------------------------------------------------------------- /modules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bupt-ipcr/RL4NeTpp/b989a65d016b190ae297179eee2654f53b784741/modules/__init__.py -------------------------------------------------------------------------------- /modules/gym_env/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author : CHEN Jiawei 3 | Date : 2023-10-19 10:02:20 4 | LastEditors : LIN Guocheng 5 | LastEditTime : 2023-10-19 10:40:42 6 | FilePath : RL4Net++/modules/gym_env/__init__.py 7 | Description : Register OmnetEnv Environment. 8 | """ 9 | 10 | from gym.envs.registration import register 11 | 12 | register( 13 | id="OMNET-v0", 14 | entry_point="env.omnet_env:OmnetEnv", 15 | ) 16 | -------------------------------------------------------------------------------- /modules/gym_env/omnet_env.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author : CHEN Jiawei 3 | Date : 2023-10-18 20:28:25 4 | LastEditors : LIN Guocheng 5 | LastEditTime : 2023-10-19 10:38:45 6 | FilePath : RL4Net++/modules/gym_env/omnet_env.py 7 | Description : A gym-based implementation, which provides the connection between python and omnetpp. 8 | """ 9 | 10 | import contextlib 11 | import os 12 | import signal 13 | 14 | import gym 15 | import zmq 16 | 17 | 18 | class OmnetEnv(gym.Env): 19 | def __init__(self): 20 | self.sim_pid = None 21 | self.sim_proc = None 22 | self.port = 5555 23 | self.context = zmq.Context() 24 | self.socket = self.context.socket(zmq.REP) 25 | self.is_multi_agent = True 26 | 27 | def start_zmq_socket(self): 28 | """start zmq socket""" 29 | with contextlib.suppress(Exception): 30 | self.socket.bind(f"tcp://*:{str(self.port)}") 31 | 32 | def reset(self): 33 | """reset omnetpp environment""" 34 | print("env resetting ...") 35 | self.close() 36 | self.start_zmq_socket() 37 | self.start_sim() 38 | 39 | def close(self): 40 | """close all inet and omnetpp process""" 41 | with contextlib.suppress(Exception): 42 | os.system("killall inet") 43 | os.system("killall opp_run_release") 44 | 45 | if self.sim_pid: 46 | os.killpg(self.sim_pid, signal.SIGUSR1) 47 | self.sim_proc = None 48 | self.sim_pid = None 49 | 50 | def start_sim(self): 51 | cmd = "nohup inet > out.inet &" 52 | os.system(cmd) 53 | 54 | def get_obs(self): 55 | """get current state or reward 56 | 57 | Returns: 58 | string: Flag for state or reward. "s" means state, while "r" means reward. 59 | int : Step for the current message. 60 | list : State list or reward value 61 | """ 62 | request = self.socket.recv() 63 | req = str(request).split("@@") 64 | s_or_r = req[0][2:] 65 | step = int(req[1]) 66 | if not self.is_multi_agent and s_or_r == "s": 67 | msg = [float(state) for state in req[2][:-1].split(",")] 68 | else: 69 | msg = req[2][:-1] 70 | 71 | return s_or_r, step, msg 72 | 73 | def get_states(self, state_str): 74 | """convert state messages to state matrix 75 | 76 | Args: 77 | state_str (string): state message 78 | 79 | Returns: 80 | list: state matrix 81 | """ 82 | return [float(state) for state in state_str.split(",")] 83 | 84 | def get_rewards(self, reward_str): 85 | """convert reward messages to reward variable 86 | 87 | Args: 88 | reward_str (string): reward messages 89 | 90 | Returns: 91 | float: value of reward 92 | """ 93 | return float(reward_str) 94 | 95 | def get_done(self): 96 | """get whether the environment finished 97 | 98 | Returns: 99 | bool: whether the environment finished 100 | """ 101 | return False 102 | 103 | def get_info(self): 104 | """get some extra information 105 | 106 | Returns: 107 | dictionary: extra information if need 108 | """ 109 | return {} 110 | 111 | def step(self, action): 112 | """make action and get state or reward from omnetpp 113 | 114 | Args: 115 | action (List): List of forwarding probability 116 | 117 | Returns: 118 | list: next state 119 | list: latency and loss rate 120 | bool: whether the episode done 121 | dictionary: some extra information 122 | """ 123 | self.make_action(list(action)) 124 | s_, r = self.get_obs() 125 | done = self.get_done() 126 | info = self.get_info() 127 | return s_, r, done, info 128 | 129 | def make_action(self, action): 130 | """make action in omnetpp 131 | 132 | Args: 133 | action (string): forwarding probability of all links 134 | """ 135 | self.socket.send_string(action) 136 | 137 | def render(self): 138 | pass 139 | 140 | def end_epsode(self): 141 | """inform omnetpp of the end of the current episode""" 142 | self.socket.send_string("end episode") 143 | 144 | def reward_rcvd(self): 145 | """inform omnetpp that python has already gotten the reward""" 146 | self.socket.send_string("reward received") 147 | -------------------------------------------------------------------------------- /modules/inet/Makefile: -------------------------------------------------------------------------------- 1 | FEATURETOOL = opp_featuretool 2 | FEATURES_H = src/inet/features.h 3 | 4 | 5 | .PHONY: all clean cleanall makefiles makefiles-so makefiles-lib makefiles-exe checkmakefiles doxy doc submodule-init 6 | 7 | all: checkmakefiles $(FEATURES_H) 8 | @cd src && $(MAKE) 9 | 10 | clean: checkmakefiles 11 | @cd src && $(MAKE) clean 12 | 13 | cleanall: checkmakefiles 14 | @cd src && $(MAKE) MODE=release clean 15 | @cd src && $(MAKE) MODE=debug clean 16 | @rm -f src/Makefile $(FEATURES_H) 17 | 18 | MAKEMAKE_OPTIONS := -f --deep -o INET -O out -pINET -lzmq -I. 19 | 20 | makefiles: makefiles-so 21 | 22 | makefiles-so: $(FEATURES_H) 23 | @FEATURE_OPTIONS=$$($(FEATURETOOL) options -f -l) && cd src && opp_makemake --make-so $(MAKEMAKE_OPTIONS) $$FEATURE_OPTIONS 24 | 25 | makefiles-lib: $(FEATURES_H) 26 | @FEATURE_OPTIONS=$$($(FEATURETOOL) options -f -l) && cd src && opp_makemake --make-lib $(MAKEMAKE_OPTIONS) $$FEATURE_OPTIONS 27 | 28 | makefiles-exe: $(FEATURES_H) 29 | @FEATURE_OPTIONS=$$($(FEATURETOOL) options -f -l) && cd src && opp_makemake $(MAKEMAKE_OPTIONS) $$FEATURE_OPTIONS 30 | 31 | checkmakefiles: 32 | @if [ ! -f src/Makefile ]; then \ 33 | echo; \ 34 | echo '========================================================================'; \ 35 | echo 'src/Makefile does not exist. Please use "make makefiles" to generate it!'; \ 36 | echo '========================================================================'; \ 37 | echo; \ 38 | exit 1; \ 39 | fi 40 | 41 | # generate an include file that contains all the WITH_FEATURE macros according to the current enablement of features 42 | $(FEATURES_H): $(wildcard .oppfeaturestate) .oppfeatures 43 | @$(FEATURETOOL) defines >$(FEATURES_H) 44 | 45 | doc: 46 | @cd doc/src && $(MAKE) 47 | @doxygen doxy.cfg 48 | 49 | ddoc: 50 | @cd doc/src && ./docker-make html && echo "===> file:$$(pwd)/_build/html/index.html" 51 | 52 | -------------------------------------------------------------------------------- /modules/inet/ipv4/Ipv4.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2004 Andras Varga 3 | // Copyright (C) 2014 OpenSim Ltd. 4 | // 5 | // This program is free software; you can redistribute it and/or 6 | // modify it under the terms of the GNU Lesser General Public License 7 | // as published by the Free Software Foundation; either version 2 8 | // of the License, or (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU Lesser General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU Lesser General Public License 16 | // along with this program; if not, see . 17 | // 18 | 19 | #include "inet/networklayer/ipv4/Ipv4.h" 20 | #include "inet/applications/common/SocketTag_m.h" 21 | #include "inet/common/INETUtils.h" 22 | #include "inet/common/IProtocolRegistrationListener.h" 23 | #include "inet/common/LayeredProtocolBase.h" 24 | #include "inet/common/ModuleAccess.h" 25 | #include "inet/common/ProtocolTag_m.h" 26 | #include "inet/common/checksum/TcpIpChecksum.h" 27 | #include "inet/common/lifecycle/ModuleOperations.h" 28 | #include "inet/common/lifecycle/NodeStatus.h" 29 | #include "inet/common/packet/Message.h" 30 | #include "inet/linklayer/common/InterfaceTag_m.h" 31 | #include "inet/linklayer/common/MacAddressTag_m.h" 32 | #include "inet/networklayer/arp/ipv4/ArpPacket_m.h" 33 | #include "inet/networklayer/common/DscpTag_m.h" 34 | #include "inet/networklayer/common/EcnTag_m.h" 35 | #include "inet/networklayer/common/FragmentationTag_m.h" 36 | #include "inet/networklayer/common/HopLimitTag_m.h" 37 | #include "inet/networklayer/common/L3AddressResolver.h" 38 | #include "inet/networklayer/common/L3AddressTag_m.h" 39 | #include "inet/networklayer/common/L3Tools.h" 40 | #include "inet/networklayer/common/MulticastTag_m.h" 41 | #include "inet/networklayer/common/NextHopAddressTag_m.h" 42 | #include "inet/networklayer/common/TosTag_m.h" 43 | #include "inet/networklayer/contract/IArp.h" 44 | #include "inet/networklayer/contract/IInterfaceTable.h" 45 | #include "inet/networklayer/contract/ipv4/Ipv4SocketCommand_m.h" 46 | #include "inet/networklayer/ipv4/IIpv4RoutingTable.h" 47 | #include "inet/networklayer/ipv4/IcmpHeader_m.h" 48 | #include "inet/networklayer/ipv4/Ipv4Header_m.h" 49 | #include "inet/networklayer/ipv4/Ipv4InterfaceData.h" 50 | #include "inet/networklayer/ipv4/Ipv4OptionsTag_m.h" 51 | #include "inet/networklayer/ipv4/Ipv4RoutingTable.h" 52 | #include "inet/networklayer/ipv4/pfrpTable.h" 53 | #include 54 | #include 55 | #include 56 | 57 | namespace inet { 58 | 59 | Define_Module(Ipv4); 60 | 61 | // TODO TRANSLATE 62 | // a multicast cimek eseten hianyoznak bizonyos NetFilter hook-ok 63 | // a local interface-k hasznalata eseten szinten hianyozhatnak bizonyos NetFilter hook-ok 64 | 65 | Ipv4::Ipv4() { 66 | } 67 | 68 | Ipv4::~Ipv4() { 69 | for (auto it : socketIdToSocketDescriptor) 70 | delete it.second; 71 | flush(); 72 | } 73 | 74 | void Ipv4::initialize(int stage) { 75 | OperationalBase::initialize(stage); 76 | 77 | if (stage == INITSTAGE_LOCAL) { 78 | ift = getModuleFromPar(par("interfaceTableModule"), this); 79 | rt = getModuleFromPar(par("routingTableModule"), this); 80 | arp = getModuleFromPar(par("arpModule"), this); 81 | icmp = getModuleFromPar(par("icmpModule"), this); 82 | 83 | transportInGateBaseId = gateBaseId("transportIn"); 84 | 85 | const char *crcModeString = par("crcMode"); 86 | crcMode = parseCrcMode(crcModeString, false); 87 | 88 | defaultTimeToLive = par("timeToLive"); 89 | defaultMCTimeToLive = par("multicastTimeToLive"); 90 | fragmentTimeoutTime = par("fragmentTimeout"); 91 | limitedBroadcast = par("limitedBroadcast"); 92 | directBroadcastInterfaces = par("directBroadcastInterfaces").stdstringValue(); 93 | 94 | directBroadcastInterfaceMatcher.setPattern(directBroadcastInterfaces.c_str(), false, true, false); 95 | 96 | curFragmentId = 0; 97 | lastCheckTime = 0; 98 | 99 | numMulticast = numLocalDeliver = numDropped = numUnroutable = numForwarded = 0; 100 | 101 | // NetFilter: 102 | hooks.clear(); 103 | queuedDatagramsForHooks.clear(); 104 | 105 | pendingPackets.clear(); 106 | cModule *arpModule = check_and_cast(arp); 107 | arpModule->subscribe(IArp::arpResolutionCompletedSignal, this); 108 | arpModule->subscribe(IArp::arpResolutionFailedSignal, this); 109 | 110 | registerService(Protocol::ipv4, gate("transportIn"), gate("queueIn")); 111 | registerProtocol(Protocol::ipv4, gate("queueOut"), gate("transportOut")); 112 | 113 | survivalTime = par("survivalTime"); 114 | 115 | WATCH(numMulticast); 116 | WATCH(numLocalDeliver); 117 | WATCH(numDropped); 118 | WATCH(numUnroutable); 119 | WATCH(numForwarded); 120 | WATCH_MAP(pendingPackets); 121 | WATCH_MAP(socketIdToSocketDescriptor); 122 | } 123 | } 124 | 125 | void Ipv4::handleRegisterService(const Protocol &protocol, cGate *out, ServicePrimitive servicePrimitive) { 126 | Enter_Method("handleRegisterService"); 127 | } 128 | 129 | void Ipv4::handleRegisterProtocol(const Protocol &protocol, cGate *in, ServicePrimitive servicePrimitive) { 130 | Enter_Method("handleRegisterProtocol"); 131 | if (in->isName("transportIn")) 132 | upperProtocols.insert(&protocol); 133 | } 134 | 135 | void Ipv4::refreshDisplay() const { 136 | OperationalBase::refreshDisplay(); 137 | 138 | char buf[80] = ""; 139 | if (numForwarded > 0) 140 | sprintf(buf + strlen(buf), "fwd:%d ", numForwarded); 141 | if (numLocalDeliver > 0) 142 | sprintf(buf + strlen(buf), "up:%d ", numLocalDeliver); 143 | if (numMulticast > 0) 144 | sprintf(buf + strlen(buf), "mcast:%d ", numMulticast); 145 | if (numDropped > 0) 146 | sprintf(buf + strlen(buf), "DROP:%d ", numDropped); 147 | if (numUnroutable > 0) 148 | sprintf(buf + strlen(buf), "UNROUTABLE:%d ", numUnroutable); 149 | getDisplayString().setTagArg("t", 0, buf); 150 | } 151 | 152 | void Ipv4::handleRequest(Request *request) { 153 | auto ctrl = request->getControlInfo(); 154 | if (ctrl == nullptr) 155 | throw cRuntimeError("Request '%s' arrived without controlinfo", request->getName()); 156 | else if (Ipv4SocketBindCommand *command = dynamic_cast(ctrl)) { 157 | int socketId = request->getTag()->getSocketId(); 158 | SocketDescriptor *descriptor = new SocketDescriptor(socketId, command->getProtocol()->getId(), command->getLocalAddress()); 159 | socketIdToSocketDescriptor[socketId] = descriptor; 160 | delete request; 161 | } else if (Ipv4SocketConnectCommand *command = dynamic_cast(ctrl)) { 162 | int socketId = request->getTag()->getSocketId(); 163 | if (socketIdToSocketDescriptor.find(socketId) == socketIdToSocketDescriptor.end()) 164 | throw cRuntimeError("Ipv4Socket: should use bind() before connect()"); 165 | socketIdToSocketDescriptor[socketId]->remoteAddress = command->getRemoteAddress(); 166 | delete request; 167 | } else if (dynamic_cast(ctrl) != nullptr) { 168 | int socketId = request->getTag()->getSocketId(); 169 | auto it = socketIdToSocketDescriptor.find(socketId); 170 | if (it != socketIdToSocketDescriptor.end()) { 171 | delete it->second; 172 | socketIdToSocketDescriptor.erase(it); 173 | auto indication = new Indication("closed", IPv4_I_SOCKET_CLOSED); 174 | auto ctrl = new Ipv4SocketClosedIndication(); 175 | indication->setControlInfo(ctrl); 176 | indication->addTag()->setSocketId(socketId); 177 | send(indication, "transportOut"); 178 | } 179 | delete request; 180 | } else if (dynamic_cast(ctrl) != nullptr) { 181 | int socketId = request->getTag()->getSocketId(); 182 | auto it = socketIdToSocketDescriptor.find(socketId); 183 | if (it != socketIdToSocketDescriptor.end()) { 184 | delete it->second; 185 | socketIdToSocketDescriptor.erase(it); 186 | } 187 | delete request; 188 | } else 189 | throw cRuntimeError("Unknown command: '%s' with %s", request->getName(), ctrl->getClassName()); 190 | } 191 | 192 | void Ipv4::handleMessageWhenUp(cMessage *msg) { 193 | if (msg->arrivedOn("transportIn")) { // TODO packet->getArrivalGate()->getBaseId() == transportInGateBaseId 194 | if (auto request = dynamic_cast(msg)) 195 | handleRequest(request); 196 | else 197 | handlePacketFromHL(check_and_cast(msg)); 198 | } else if (msg->arrivedOn("queueIn")) { // from network 199 | handleIncomingDatagram(check_and_cast(msg)); 200 | } else 201 | throw cRuntimeError("message arrived on unknown gate '%s'", msg->getArrivalGate()->getName()); 202 | } 203 | 204 | bool Ipv4::verifyCrc(const Ptr &ipv4Header) { 205 | switch (ipv4Header->getCrcMode()) { 206 | case CRC_DECLARED_CORRECT: { 207 | // if the CRC mode is declared to be correct, then the check passes if and only if the chunk is correct 208 | return ipv4Header->isCorrect(); 209 | } 210 | case CRC_DECLARED_INCORRECT: 211 | // if the CRC mode is declared to be incorrect, then the check fails 212 | return false; 213 | case CRC_COMPUTED: { 214 | if (ipv4Header->isCorrect()) { 215 | // compute the CRC, the check passes if the result is 0xFFFF (includes the received CRC) and the chunks are correct 216 | MemoryOutputStream ipv4HeaderStream; 217 | Chunk::serialize(ipv4HeaderStream, ipv4Header); 218 | uint16_t computedCrc = TcpIpChecksum::checksum(ipv4HeaderStream.getData()); 219 | return computedCrc == 0; 220 | } else { 221 | return false; 222 | } 223 | } 224 | default: 225 | throw cRuntimeError("Unknown CRC mode"); 226 | } 227 | } 228 | 229 | const InterfaceEntry *Ipv4::getSourceInterface(Packet *packet) { 230 | auto tag = packet->findTag(); 231 | return tag != nullptr ? ift->getInterfaceById(tag->getInterfaceId()) : nullptr; 232 | } 233 | 234 | const InterfaceEntry *Ipv4::getDestInterface(Packet *packet) { 235 | auto tag = packet->findTag(); 236 | return tag != nullptr ? ift->getInterfaceById(tag->getInterfaceId()) : nullptr; 237 | } 238 | 239 | Ipv4Address Ipv4::getNextHop(Packet *packet) { 240 | auto tag = packet->findTag(); 241 | return tag != nullptr ? tag->getNextHopAddress().toIpv4() : Ipv4Address::UNSPECIFIED_ADDRESS; 242 | } 243 | 244 | void Ipv4::handleIncomingDatagram(Packet *packet) { 245 | ASSERT(packet); 246 | int interfaceId = packet->getTag()->getInterfaceId(); 247 | emit(packetReceivedFromLowerSignal, packet); 248 | 249 | // 250 | // "Prerouting" 251 | // 252 | 253 | const auto &ipv4Header = packet->peekAtFront(); 254 | packet->addTagIfAbsent()->setProtocol(&Protocol::ipv4); 255 | packet->addTagIfAbsent()->setNetworkProtocolHeader(ipv4Header); 256 | 257 | if (!verifyCrc(ipv4Header)) { 258 | EV_WARN << "CRC error found, drop packet\n"; 259 | PacketDropDetails details; 260 | details.setReason(INCORRECTLY_RECEIVED); 261 | emit(packetDroppedSignal, packet, &details); 262 | delete packet; 263 | return; 264 | } 265 | 266 | if (ipv4Header->getTotalLengthField() > packet->getDataLength()) { 267 | EV_WARN << "length error found, sending ICMP_PARAMETER_PROBLEM\n"; 268 | sendIcmpError(packet, interfaceId, ICMP_PARAMETER_PROBLEM, 0); 269 | return; 270 | } 271 | 272 | // remove lower layer paddings: 273 | if (ipv4Header->getTotalLengthField() < packet->getDataLength()) { 274 | packet->setBackOffset(packet->getFrontOffset() + ipv4Header->getTotalLengthField()); 275 | } 276 | 277 | // check for header biterror 278 | if (packet->hasBitError()) { 279 | // probability of bit error in header = size of header / size of total message 280 | // (ignore bit error if in payload) 281 | double relativeHeaderLength = B(ipv4Header->getHeaderLength()).get() / (double)B(ipv4Header->getChunkLength()).get(); 282 | if (dblrand() <= relativeHeaderLength) { 283 | EV_WARN << "bit error found, sending ICMP_PARAMETER_PROBLEM\n"; 284 | sendIcmpError(packet, interfaceId, ICMP_PARAMETER_PROBLEM, 0); 285 | return; 286 | } 287 | } 288 | 289 | EV_DETAIL << "Received datagram `" << ipv4Header->getName() << "' with dest=" << ipv4Header->getDestAddress() << "\n"; 290 | 291 | if (datagramPreRoutingHook(packet) == INetfilter::IHook::ACCEPT) 292 | preroutingFinish(packet); 293 | } 294 | 295 | Packet *Ipv4::prepareForForwarding(Packet *packet) const { 296 | const auto &ipv4Header = removeNetworkProtocolHeader(packet); 297 | ipv4Header->setTimeToLive(ipv4Header->getTimeToLive() - 1); 298 | insertNetworkProtocolHeader(packet, Protocol::ipv4, ipv4Header); 299 | return packet; 300 | } 301 | 302 | void Ipv4::preroutingFinish(Packet *packet) { 303 | const InterfaceEntry *fromIE = ift->getInterfaceById(packet->getTag()->getInterfaceId()); 304 | Ipv4Address nextHopAddr = getNextHop(packet); 305 | Ipv4Address un(Ipv4Address::UNSPECIFIED_ADDRESS); 306 | const auto &ipv4Header = packet->peekAtFront(); 307 | ASSERT(ipv4Header); 308 | Ipv4Address destAddr = ipv4Header->getDestAddress(); 309 | 310 | // route packet 311 | 312 | if (fromIE->isLoopback()) { 313 | reassembleAndDeliver(packet); 314 | } else if (destAddr.isMulticast()) { 315 | // check for local delivery 316 | // Note: multicast routers will receive IGMP datagrams even if their interface is not joined to the group 317 | if (fromIE->getProtocolData()->isMemberOfMulticastGroup(destAddr) || 318 | (rt->isMulticastForwardingEnabled() && ipv4Header->getProtocolId() == IP_PROT_IGMP)) 319 | reassembleAndDeliver(packet->dup()); 320 | else 321 | EV_WARN << "Skip local delivery of multicast datagram (input interface not in multicast group)\n"; 322 | 323 | // don't forward if IP forwarding is off, or if dest address is link-scope 324 | if (!rt->isMulticastForwardingEnabled()) { 325 | EV_WARN << "Skip forwarding of multicast datagram (forwarding disabled)\n"; 326 | delete packet; 327 | } else if (destAddr.isLinkLocalMulticast()) { 328 | EV_WARN << "Skip forwarding of multicast datagram (packet is link-local)\n"; 329 | delete packet; 330 | } else if (ipv4Header->getTimeToLive() <= 1) { // TTL before decrement 331 | EV_WARN << "Skip forwarding of multicast datagram (TTL reached 0)\n"; 332 | delete packet; 333 | } else 334 | forwardMulticastPacket(prepareForForwarding(packet)); 335 | } else { 336 | const InterfaceEntry *broadcastIE = nullptr; 337 | // check for local delivery; we must accept also packets coming from the interfaces that 338 | // do not yet have an IP address assigned. This happens during DHCP requests. 339 | if (rt->isLocalAddress(destAddr) || fromIE->getProtocolData()->getIPAddress().isUnspecified()) { 340 | reassembleAndDeliver(packet); 341 | } else if (destAddr.isLimitedBroadcastAddress() || (broadcastIE = rt->findInterfaceByLocalBroadcastAddress(destAddr))) { 342 | // broadcast datagram on the target subnet if we are a router 343 | if (broadcastIE && fromIE != broadcastIE && rt->isForwardingEnabled()) { 344 | if (directBroadcastInterfaceMatcher.matches(broadcastIE->getInterfaceName()) || 345 | directBroadcastInterfaceMatcher.matches(broadcastIE->getInterfaceFullPath().c_str())) { 346 | auto packetCopy = prepareForForwarding(packet->dup()); 347 | packetCopy->addTagIfAbsent()->setInterfaceId(broadcastIE->getInterfaceId()); 348 | packetCopy->addTagIfAbsent()->setNextHopAddress(Ipv4Address::ALLONES_ADDRESS); 349 | fragmentPostRouting(packetCopy); 350 | } else 351 | EV_INFO << "Forwarding of direct broadcast packets is disabled on interface " << broadcastIE->getInterfaceName() << std::endl; 352 | } 353 | 354 | EV_INFO << "Broadcast received\n"; 355 | reassembleAndDeliver(packet); 356 | } else if (!rt->isForwardingEnabled()) { 357 | EV_WARN << "forwarding off, dropping packet\n"; 358 | numDropped++; 359 | PacketDropDetails details; 360 | details.setReason(FORWARDING_DISABLED); 361 | emit(packetDroppedSignal, packet, &details); 362 | delete packet; 363 | } else { 364 | packet->addTagIfAbsent()->setNextHopAddress(nextHopAddr); 365 | routeUnicastPacket(prepareForForwarding(packet)); 366 | } 367 | } 368 | } 369 | 370 | void Ipv4::handlePacketFromHL(Packet *packet) { 371 | emit(packetReceivedFromUpperSignal, packet); 372 | 373 | // if no interface exists, do not send datagram 374 | if (ift->getNumInterfaces() == 0) { 375 | EV_ERROR << "No interfaces exist, dropping packet\n"; 376 | numDropped++; 377 | PacketDropDetails details; 378 | details.setReason(NO_INTERFACE_FOUND); 379 | emit(packetDroppedSignal, packet, &details); 380 | delete packet; 381 | return; 382 | } 383 | 384 | // encapsulate 385 | encapsulate(packet); 386 | 387 | // TODO: 388 | L3Address nextHopAddr(Ipv4Address::UNSPECIFIED_ADDRESS); 389 | if (datagramLocalOutHook(packet) == INetfilter::IHook::ACCEPT) 390 | datagramLocalOut(packet); 391 | } 392 | 393 | void Ipv4::datagramLocalOut(Packet *packet) { 394 | const InterfaceEntry *destIE = getDestInterface(packet); 395 | Ipv4Address requestedNextHopAddress = getNextHop(packet); 396 | 397 | const auto &ipv4Header = packet->peekAtFront(); 398 | bool multicastLoop = false; 399 | MulticastReq *mcr = packet->findTag(); 400 | if (mcr != nullptr) { 401 | multicastLoop = mcr->getMulticastLoop(); 402 | } 403 | 404 | // send 405 | Ipv4Address destAddr = ipv4Header->getDestAddress(); 406 | 407 | if (ipv4Header->getDestAddress().isMulticast()) { 408 | destIE = determineOutgoingInterfaceForMulticastDatagram(ipv4Header, destIE); 409 | packet->addTagIfAbsent()->setInterfaceId(destIE ? destIE->getInterfaceId() : -1); 410 | 411 | // loop back a copy 412 | if (multicastLoop && (!destIE || !destIE->isLoopback())) { 413 | const InterfaceEntry *loopbackIF = ift->findFirstLoopbackInterface(); 414 | if (loopbackIF) { 415 | auto packetCopy = packet->dup(); 416 | packetCopy->addTagIfAbsent()->setInterfaceId(loopbackIF->getInterfaceId()); 417 | packetCopy->addTagIfAbsent()->setNextHopAddress(destAddr); 418 | fragmentPostRouting(packetCopy); 419 | } 420 | } 421 | 422 | if (destIE) { 423 | numMulticast++; 424 | packet->addTagIfAbsent()->setInterfaceId(destIE->getInterfaceId()); // FIXME KLUDGE is it needed? 425 | packet->addTagIfAbsent()->setNextHopAddress(destAddr); 426 | fragmentPostRouting(packet); 427 | } else { 428 | numUnroutable++; 429 | PacketDropDetails details; 430 | details.setReason(NO_INTERFACE_FOUND); 431 | emit(packetDroppedSignal, packet, &details); 432 | delete packet; 433 | } 434 | } else { // unicast and broadcast 435 | // check for local delivery 436 | if (rt->isLocalAddress(destAddr)) { 437 | if (destIE && !destIE->isLoopback()) { 438 | EV_DETAIL << "datagram destination address is local, ignoring destination interface specified in the control info\n"; 439 | destIE = nullptr; 440 | packet->addTagIfAbsent()->setInterfaceId(-1); 441 | } 442 | if (!destIE) { 443 | destIE = ift->findFirstLoopbackInterface(); 444 | packet->addTagIfAbsent()->setInterfaceId(destIE ? destIE->getInterfaceId() : -1); 445 | } 446 | ASSERT(destIE); 447 | packet->addTagIfAbsent()->setNextHopAddress(destAddr); 448 | routeUnicastPacket(packet); 449 | } else if (destAddr.isLimitedBroadcastAddress() || rt->isLocalBroadcastAddress(destAddr)) 450 | routeLocalBroadcastPacket(packet); 451 | else { 452 | packet->addTagIfAbsent()->setNextHopAddress(requestedNextHopAddress); 453 | routeUnicastPacket(packet); 454 | } 455 | } 456 | } 457 | 458 | /* Choose the outgoing interface for the muticast datagram: 459 | * 1. use the interface specified by MULTICAST_IF socket option (received in the control info) 460 | * 2. lookup the destination address in the routing table 461 | * 3. if no route, choose the interface according to the source address 462 | * 4. or if the source address is unspecified, choose the first MULTICAST interface 463 | */ 464 | const InterfaceEntry *Ipv4::determineOutgoingInterfaceForMulticastDatagram(const Ptr &ipv4Header, const InterfaceEntry *multicastIFOption) { 465 | const InterfaceEntry *ie = nullptr; 466 | if (multicastIFOption) { 467 | ie = multicastIFOption; 468 | EV_DETAIL << "multicast packet routed by socket option via output interface " << ie->getInterfaceName() << "\n"; 469 | } 470 | if (!ie) { 471 | Ipv4Route *route = rt->findBestMatchingRoute(ipv4Header->getDestAddress()); 472 | if (route) 473 | ie = route->getInterface(); 474 | if (ie) 475 | EV_DETAIL << "multicast packet routed by routing table via output interface " << ie->getInterfaceName() << "\n"; 476 | } 477 | if (!ie) { 478 | ie = rt->getInterfaceByAddress(ipv4Header->getSrcAddress()); 479 | if (ie) 480 | EV_DETAIL << "multicast packet routed by source address via output interface " << ie->getInterfaceName() << "\n"; 481 | } 482 | if (!ie) { 483 | ie = ift->findFirstMulticastInterface(); 484 | if (ie) 485 | EV_DETAIL << "multicast packet routed via the first multicast interface " << ie->getInterfaceName() << "\n"; 486 | } 487 | return ie; 488 | } 489 | 490 | void Ipv4::routeUnicastPacket(Packet *packet) { 491 | const InterfaceEntry *fromIE = getSourceInterface(packet); 492 | const InterfaceEntry *destIE = getDestInterface(packet); 493 | Ipv4Address nextHopAddress = getNextHop(packet); 494 | 495 | const auto &ipv4Header = packet->peekAtFront(); 496 | Ipv4Address destAddr = ipv4Header->getDestAddress(); 497 | 498 | // if output port was explicitly requested, use that, otherwise use Ipv4 routing 499 | if (destIE) { 500 | EV_DETAIL << "using manually specified output interface " << destIE->getInterfaceName() << "\n"; 501 | // and nextHopAddr remains unspecified 502 | if (!nextHopAddress.isUnspecified()) { 503 | // do nothing, next hop address already specified 504 | } 505 | // special case ICMP reply 506 | else if (destIE->isBroadcast()) { 507 | // if the interface is broadcast we must search the next hop 508 | const Ipv4Route *re = rt->findBestMatchingRoute(destAddr); 509 | if (re && re->getInterface() == destIE) { 510 | packet->addTagIfAbsent()->setNextHopAddress(re->getGateway()); 511 | } 512 | } 513 | } else { 514 | if (strstr(packet->getFullName(), "pfrp")) { // pfrp 515 | string modulePath = getParentModule()->getFullPath(); 516 | const char *pktName = packet->getFullName(); 517 | pair p = pfrpTable::getInstance()->getRoute(modulePath, pktName, int(packet->getBitLength())); 518 | if (strstr(packet->getFullName(), "pfrpma")) { 519 | pfrpTable::getInstance()->countPktInNode(modulePath, pktName); 520 | } 521 | string s = p.first; 522 | InterfaceEntry *ie = ift->getInterface(p.second); 523 | L3Address next; 524 | const char *da = s.c_str(); 525 | cStringTokenizer tokenizer(da); 526 | const char *token = tokenizer.nextToken(); 527 | L3AddressResolver().tryResolve(token, next); 528 | 529 | destIE = ie; 530 | 531 | packet->addTagIfAbsent()->setInterfaceId(destIE->getInterfaceId()); 532 | packet->addTagIfAbsent()->setNextHopAddress(next.toIpv4()); 533 | } else { // no pfrp 534 | const Ipv4Route *re = rt->findBestMatchingRoute(destAddr); 535 | if (re) { 536 | destIE = re->getInterface(); 537 | packet->addTagIfAbsent()->setInterfaceId(destIE->getInterfaceId()); 538 | packet->addTagIfAbsent()->setNextHopAddress(re->getGateway()); 539 | } 540 | } 541 | } 542 | 543 | if (!destIE) { // no route found 544 | numUnroutable++; 545 | PacketDropDetails details; 546 | details.setReason(NO_ROUTE_FOUND); 547 | emit(packetDroppedSignal, packet, &details); 548 | sendIcmpError(packet, fromIE ? fromIE->getInterfaceId() : -1, ICMP_DESTINATION_UNREACHABLE, 0); 549 | } else { // fragment and send 550 | if (fromIE != nullptr) { 551 | if (datagramForwardHook(packet) != INetfilter::IHook::ACCEPT) 552 | return; 553 | } 554 | 555 | routeUnicastPacketFinish(packet); 556 | } 557 | } 558 | 559 | void Ipv4::routeUnicastPacketFinish(Packet *packet) { 560 | 561 | EV_INFO << "output interface = " << getDestInterface(packet)->getInterfaceName() << ", next hop address = " << getNextHop(packet) << "\n"; 562 | numForwarded++; 563 | fragmentPostRouting(packet); 564 | } 565 | 566 | void Ipv4::routeLocalBroadcastPacket(Packet *packet) { 567 | auto interfaceReq = packet->findTag(); 568 | const InterfaceEntry *destIE = interfaceReq != nullptr ? ift->getInterfaceById(interfaceReq->getInterfaceId()) : nullptr; 569 | // The destination address is 255.255.255.255 or local subnet broadcast address. 570 | // We always use 255.255.255.255 as nextHopAddress, because it is recognized by ARP, 571 | // and mapped to the broadcast MAC address. 572 | if (destIE != nullptr) { 573 | packet->addTagIfAbsent()->setInterfaceId(destIE->getInterfaceId()); // FIXME KLUDGE is it needed? 574 | packet->addTagIfAbsent()->setNextHopAddress(Ipv4Address::ALLONES_ADDRESS); 575 | fragmentPostRouting(packet); 576 | } else if (limitedBroadcast) { 577 | auto destAddr = packet->peekAtFront()->getDestAddress(); 578 | // forward to each matching interfaces including loopback 579 | for (int i = 0; i < ift->getNumInterfaces(); i++) { 580 | const InterfaceEntry *ie = ift->getInterface(i); 581 | if (!destAddr.isLimitedBroadcastAddress()) { 582 | Ipv4Address interfaceAddr = ie->getProtocolData()->getIPAddress(); 583 | Ipv4Address broadcastAddr = interfaceAddr.makeBroadcastAddress(ie->getProtocolData()->getNetmask()); 584 | if (destAddr != broadcastAddr) 585 | continue; 586 | } 587 | auto packetCopy = packet->dup(); 588 | packetCopy->addTagIfAbsent()->setInterfaceId(ie->getInterfaceId()); 589 | packetCopy->addTagIfAbsent()->setNextHopAddress(Ipv4Address::ALLONES_ADDRESS); 590 | fragmentPostRouting(packetCopy); 591 | } 592 | delete packet; 593 | } else { 594 | numDropped++; 595 | PacketDropDetails details; 596 | details.setReason(NO_INTERFACE_FOUND); 597 | emit(packetDroppedSignal, packet, &details); 598 | delete packet; 599 | } 600 | } 601 | 602 | const InterfaceEntry *Ipv4::getShortestPathInterfaceToSource(const Ptr &ipv4Header) const { 603 | return rt->getInterfaceForDestAddr(ipv4Header->getSrcAddress()); 604 | } 605 | 606 | void Ipv4::forwardMulticastPacket(Packet *packet) { 607 | const InterfaceEntry *fromIE = ift->getInterfaceById(packet->getTag()->getInterfaceId()); 608 | const auto &ipv4Header = packet->peekAtFront(); 609 | const Ipv4Address &srcAddr = ipv4Header->getSrcAddress(); 610 | const Ipv4Address &destAddr = ipv4Header->getDestAddress(); 611 | ASSERT(destAddr.isMulticast()); 612 | ASSERT(!destAddr.isLinkLocalMulticast()); 613 | 614 | numMulticast++; 615 | 616 | const Ipv4MulticastRoute *route = rt->findBestMatchingMulticastRoute(srcAddr, destAddr); 617 | if (!route) { 618 | EV_WARN << "Multicast route does not exist, try to add.\n"; 619 | // TODO: no need to emit fromIE when tags will be used in place of control infos 620 | emit(ipv4NewMulticastSignal, ipv4Header.get(), const_cast(fromIE)); 621 | 622 | // read new record 623 | route = rt->findBestMatchingMulticastRoute(srcAddr, destAddr); 624 | 625 | if (!route) { 626 | numUnroutable++; 627 | PacketDropDetails details; 628 | details.setReason(NO_ROUTE_FOUND); 629 | emit(packetDroppedSignal, packet, &details); 630 | delete packet; 631 | return; 632 | } 633 | } 634 | 635 | if (route->getInInterface() && fromIE != route->getInInterface()->getInterface()) { 636 | // TODO: no need to emit fromIE when tags will be used in place of control infos 637 | emit(ipv4DataOnNonrpfSignal, ipv4Header.get(), const_cast(fromIE)); 638 | numDropped++; 639 | PacketDropDetails details; 640 | emit(packetDroppedSignal, packet, &details); 641 | delete packet; 642 | } 643 | // backward compatible: no parent means shortest path interface to source (RPB routing) 644 | else if (!route->getInInterface() && fromIE != getShortestPathInterfaceToSource(ipv4Header)) { 645 | numDropped++; 646 | PacketDropDetails details; 647 | emit(packetDroppedSignal, packet, &details); 648 | delete packet; 649 | } else { 650 | // TODO: no need to emit fromIE when tags will be used in place of control infos 651 | emit(ipv4DataOnRpfSignal, ipv4Header.get(), const_cast(fromIE)); // forwarding hook 652 | 653 | numForwarded++; 654 | // copy original datagram for multiple destinations 655 | for (unsigned int i = 0; i < route->getNumOutInterfaces(); i++) { 656 | Ipv4MulticastRoute::OutInterface *outInterface = route->getOutInterface(i); 657 | const InterfaceEntry *destIE = outInterface->getInterface(); 658 | if (destIE != fromIE && outInterface->isEnabled()) { 659 | int ttlThreshold = destIE->getProtocolData()->getMulticastTtlThreshold(); 660 | if (ipv4Header->getTimeToLive() <= ttlThreshold) 661 | EV_WARN << "Not forwarding to " << destIE->getInterfaceName() << " (ttl threshold reached)\n"; 662 | else if (outInterface->isLeaf() && !destIE->getProtocolData()->hasMulticastListener(destAddr)) 663 | EV_WARN << "Not forwarding to " << destIE->getInterfaceName() << " (no listeners)\n"; 664 | else { 665 | EV_DETAIL << "Forwarding to " << destIE->getInterfaceName() << "\n"; 666 | auto packetCopy = packet->dup(); 667 | packetCopy->addTagIfAbsent()->setInterfaceId(destIE->getInterfaceId()); 668 | packetCopy->addTagIfAbsent()->setNextHopAddress(destAddr); 669 | fragmentPostRouting(packetCopy); 670 | } 671 | } 672 | } 673 | 674 | // TODO: no need to emit fromIE when tags will be use, d in place of control infos 675 | emit(ipv4MdataRegisterSignal, packet, const_cast(fromIE)); // postRouting hook 676 | 677 | // only copies sent, delete original packet 678 | delete packet; 679 | } 680 | } 681 | 682 | void Ipv4::reassembleAndDeliver(Packet *packet) { 683 | EV_INFO << "Delivering " << packet << " locally.\n"; 684 | 685 | const auto &ipv4Header = packet->peekAtFront(); 686 | if (ipv4Header->getSrcAddress().isUnspecified()) 687 | EV_WARN << "Received datagram '" << packet->getName() << "' without source address filled in\n"; 688 | 689 | // reassemble the packet (if fragmented) 690 | if (ipv4Header->getFragmentOffset() != 0 || ipv4Header->getMoreFragments()) { 691 | EV_DETAIL << "Datagram fragment: offset=" << ipv4Header->getFragmentOffset() 692 | << ", MORE=" << (ipv4Header->getMoreFragments() ? "true" : "false") << ".\n"; 693 | 694 | // erase timed out fragments in fragmentation buffer; check every 10 seconds max 695 | if (simTime() >= lastCheckTime + 10) { 696 | lastCheckTime = simTime(); 697 | fragbuf.purgeStaleFragments(icmp, simTime() - fragmentTimeoutTime); 698 | } 699 | 700 | packet = fragbuf.addFragment(packet, simTime()); 701 | if (!packet) { 702 | EV_DETAIL << "No complete datagram yet.\n"; 703 | return; 704 | } 705 | if (packet->peekAtFront()->getCrcMode() == CRC_COMPUTED) { 706 | auto ipv4Header = removeNetworkProtocolHeader(packet); 707 | setComputedCrc(ipv4Header); 708 | insertNetworkProtocolHeader(packet, Protocol::ipv4, ipv4Header); 709 | } 710 | EV_DETAIL << "This fragment completes the datagram.\n"; 711 | } 712 | 713 | if (datagramLocalInHook(packet) == INetfilter::IHook::ACCEPT) 714 | reassembleAndDeliverFinish(packet); 715 | } 716 | 717 | void Ipv4::reassembleAndDeliverFinish(Packet *packet) { 718 | auto ipv4HeaderPosition = packet->getFrontOffset(); 719 | const auto &ipv4Header = packet->peekAtFront(); 720 | const Protocol *protocol = ipv4Header->getProtocol(); 721 | auto remoteAddress(ipv4Header->getSrcAddress()); 722 | auto localAddress(ipv4Header->getDestAddress()); 723 | decapsulate(packet); 724 | bool hasSocket = false; 725 | for (const auto &elem : socketIdToSocketDescriptor) { 726 | if (elem.second->protocolId == protocol->getId() && (elem.second->localAddress.isUnspecified() || elem.second->localAddress == localAddress) && (elem.second->remoteAddress.isUnspecified() || elem.second->remoteAddress == remoteAddress)) { 727 | auto *packetCopy = packet->dup(); 728 | packetCopy->setKind(IPv4_I_DATA); 729 | packetCopy->addTagIfAbsent()->setSocketId(elem.second->socketId); 730 | EV_INFO << "Passing up to socket " << elem.second->socketId << "\n"; 731 | emit(packetSentToUpperSignal, packetCopy); 732 | send(packetCopy, "transportOut"); 733 | hasSocket = true; 734 | } 735 | } 736 | if (upperProtocols.find(protocol) != upperProtocols.end()) { 737 | EV_INFO << "Passing up to protocol " << *protocol << "\n"; 738 | emit(packetSentToUpperSignal, packet); 739 | send(packet, "transportOut"); 740 | numLocalDeliver++; 741 | } else if (hasSocket) { 742 | delete packet; 743 | } else { 744 | EV_ERROR << "Transport protocol '" << protocol->getName() << "' not connected, discarding packet\n"; 745 | packet->setFrontOffset(ipv4HeaderPosition); 746 | const InterfaceEntry *fromIE = getSourceInterface(packet); 747 | sendIcmpError(packet, fromIE ? fromIE->getInterfaceId() : -1, ICMP_DESTINATION_UNREACHABLE, ICMP_DU_PROTOCOL_UNREACHABLE); 748 | } 749 | } 750 | 751 | void Ipv4::decapsulate(Packet *packet) { 752 | // decapsulate transport packet 753 | const auto &ipv4Header = packet->popAtFront(); 754 | 755 | // create and fill in control info 756 | packet->addTagIfAbsent()->setDifferentiatedServicesCodePoint(ipv4Header->getDscp()); 757 | packet->addTagIfAbsent()->setExplicitCongestionNotification(ipv4Header->getEcn()); 758 | packet->addTagIfAbsent()->setTos(ipv4Header->getTypeOfService()); 759 | 760 | // original Ipv4 datagram might be needed in upper layers to send back ICMP error message 761 | 762 | auto transportProtocol = ProtocolGroup::ipprotocol.getProtocol(ipv4Header->getProtocolId()); 763 | packet->addTagIfAbsent()->setProtocol(transportProtocol); 764 | packet->addTagIfAbsent()->setProtocol(transportProtocol); 765 | auto l3AddressInd = packet->addTagIfAbsent(); 766 | l3AddressInd->setSrcAddress(ipv4Header->getSrcAddress()); 767 | l3AddressInd->setDestAddress(ipv4Header->getDestAddress()); 768 | packet->addTagIfAbsent()->setHopLimit(ipv4Header->getTimeToLive()); 769 | } 770 | 771 | void Ipv4::fragmentPostRouting(Packet *packet) { 772 | const InterfaceEntry *destIE = ift->getInterfaceById(packet->getTag()->getInterfaceId()); 773 | // fill in source address 774 | if (packet->peekAtFront()->getSrcAddress().isUnspecified()) { 775 | auto ipv4Header = removeNetworkProtocolHeader(packet); 776 | ipv4Header->setSrcAddress(destIE->getProtocolData()->getIPAddress()); 777 | insertNetworkProtocolHeader(packet, Protocol::ipv4, ipv4Header); 778 | } 779 | if (datagramPostRoutingHook(packet) == INetfilter::IHook::ACCEPT) { 780 | fragmentAndSend(packet); 781 | } 782 | } 783 | 784 | void Ipv4::setComputedCrc(Ptr &ipv4Header) { 785 | ASSERT(crcMode == CRC_COMPUTED); 786 | ipv4Header->setCrc(0); 787 | MemoryOutputStream ipv4HeaderStream; 788 | Chunk::serialize(ipv4HeaderStream, ipv4Header); 789 | // compute the CRC 790 | uint16_t crc = TcpIpChecksum::checksum(ipv4HeaderStream.getData()); 791 | ipv4Header->setCrc(crc); 792 | } 793 | 794 | void Ipv4::insertCrc(const Ptr &ipv4Header) { 795 | CrcMode crcMode = ipv4Header->getCrcMode(); 796 | switch (crcMode) { 797 | case CRC_DECLARED_CORRECT: 798 | // if the CRC mode is declared to be correct, then set the CRC to an easily recognizable value 799 | ipv4Header->setCrc(0xC00D); 800 | break; 801 | case CRC_DECLARED_INCORRECT: 802 | // if the CRC mode is declared to be incorrect, then set the CRC to an easily recognizable value 803 | ipv4Header->setCrc(0xBAAD); 804 | break; 805 | case CRC_COMPUTED: { 806 | // if the CRC mode is computed, then compute the CRC and set it 807 | // this computation is delayed after the routing decision, see INetfilter hook 808 | ipv4Header->setCrc(0x0000); // make sure that the CRC is 0 in the Udp header before computing the CRC 809 | MemoryOutputStream ipv4HeaderStream; 810 | Chunk::serialize(ipv4HeaderStream, ipv4Header); 811 | // compute the CRC 812 | uint16_t crc = TcpIpChecksum::checksum(ipv4HeaderStream.getData()); 813 | ipv4Header->setCrc(crc); 814 | break; 815 | } 816 | default: 817 | throw cRuntimeError("Unknown CRC mode: %d", (int)crcMode); 818 | } 819 | } 820 | 821 | void Ipv4::fragmentAndSend(Packet *packet) { 822 | const InterfaceEntry *destIE = ift->getInterfaceById(packet->getTag()->getInterfaceId()); 823 | Ipv4Address nextHopAddr = getNextHop(packet); 824 | if (nextHopAddr.isUnspecified()) { 825 | nextHopAddr = packet->peekAtFront()->getDestAddress(); 826 | packet->addTagIfAbsent()->setNextHopAddress(nextHopAddr); 827 | } 828 | 829 | const auto &ipv4Header = packet->peekAtFront(); 830 | 831 | const char *pktName = packet->getFullName(); 832 | 833 | // hop counter check 834 | if (ipv4Header->getTimeToLive() <= 0) { 835 | // drop datagram, destruction responsibility in ICMP 836 | PacketDropDetails details; 837 | details.setReason(HOP_LIMIT_REACHED); 838 | emit(packetDroppedSignal, packet, &details); 839 | EV_WARN << "datagram TTL reached zero, sending ICMP_TIME_EXCEEDED\n"; 840 | sendIcmpError(packet, -1 /*TODO*/, ICMP_TIME_EXCEEDED, 0); 841 | numDropped++; 842 | return; 843 | } 844 | 845 | // pfrp survivalTime 846 | if (simTime().dbl() - packet->getCreationTime().dbl() >= survivalTime) { 847 | // drop datagram, destruction responsibility in ICMP 848 | PacketDropDetails details; 849 | details.setReason(HOP_LIMIT_REACHED); 850 | emit(packetDroppedSignal, packet, &details); 851 | EV_WARN << "datagram TTL reached zero, sending ICMP_TIME_EXCEEDED\n"; 852 | 853 | sendIcmpError(packet, -1 /*TODO*/, ICMP_TIME_EXCEEDED, 0); 854 | numDropped++; 855 | return; 856 | } 857 | 858 | int mtu = destIE->getMtu(); 859 | 860 | // send datagram straight out if it doesn't require fragmentation (note: mtu==0 means infinite mtu) 861 | if (mtu == 0 || packet->getByteLength() <= mtu) { 862 | if (crcMode == CRC_COMPUTED) { 863 | auto ipv4Header = removeNetworkProtocolHeader(packet); 864 | setComputedCrc(ipv4Header); 865 | insertNetworkProtocolHeader(packet, Protocol::ipv4, ipv4Header); 866 | } 867 | sendDatagramToOutput(packet); 868 | return; 869 | } 870 | 871 | // if "don't fragment" bit is set, throw datagram away and send ICMP error message 872 | if (ipv4Header->getDontFragment()) { 873 | PacketDropDetails details; 874 | emit(packetDroppedSignal, packet, &details); 875 | EV_WARN << "datagram larger than MTU and don't fragment bit set, sending ICMP_DESTINATION_UNREACHABLE\n"; 876 | sendIcmpError(packet, -1 /*TODO*/, ICMP_DESTINATION_UNREACHABLE, 877 | ICMP_DU_FRAGMENTATION_NEEDED); 878 | numDropped++; 879 | return; 880 | } 881 | 882 | // FIXME some IP options should not be copied into each fragment, check their COPY bit 883 | int headerLength = B(ipv4Header->getHeaderLength()).get(); 884 | int payloadLength = B(packet->getDataLength()).get() - headerLength; 885 | int fragmentLength = ((mtu - headerLength) / 8) * 8; // payload only (without header) 886 | int offsetBase = ipv4Header->getFragmentOffset(); 887 | if (fragmentLength <= 0) 888 | throw cRuntimeError("Cannot fragment datagram: MTU=%d too small for header size (%d bytes)", mtu, headerLength); // exception and not ICMP because this is likely a simulation configuration error, not something one wants to simulate 889 | 890 | int noOfFragments = (payloadLength + fragmentLength - 1) / fragmentLength; 891 | EV_DETAIL << "Breaking datagram into " << noOfFragments << " fragments\n"; 892 | 893 | // create and send fragments 894 | std::string fragMsgName = packet->getName(); 895 | fragMsgName += "-frag-"; 896 | 897 | int offset = 0; 898 | while (offset < payloadLength) { 899 | bool lastFragment = (offset + fragmentLength >= payloadLength); 900 | // length equal to fragmentLength, except for last fragment; 901 | int thisFragmentLength = lastFragment ? payloadLength - offset : fragmentLength; 902 | 903 | std::string curFragName = fragMsgName + std::to_string(offset); 904 | if (lastFragment) 905 | curFragName += "-last"; 906 | Packet *fragment = new Packet(curFragName.c_str()); // TODO add offset or index to fragment name 907 | 908 | // copy Tags from packet to fragment 909 | fragment->copyTags(*packet); 910 | 911 | ASSERT(fragment->getByteLength() == 0); 912 | auto fraghdr = staticPtrCast(ipv4Header->dupShared()); 913 | const auto &fragData = packet->peekDataAt(B(headerLength + offset), B(thisFragmentLength)); 914 | ASSERT(fragData->getChunkLength() == B(thisFragmentLength)); 915 | fragment->insertAtBack(fragData); 916 | 917 | // "more fragments" bit is unchanged in the last fragment, otherwise true 918 | if (!lastFragment) 919 | fraghdr->setMoreFragments(true); 920 | 921 | fraghdr->setFragmentOffset(offsetBase + offset); 922 | fraghdr->setTotalLengthField(B(headerLength + thisFragmentLength)); 923 | if (crcMode == CRC_COMPUTED) 924 | setComputedCrc(fraghdr); 925 | 926 | fragment->insertAtFront(fraghdr); 927 | ASSERT(fragment->getByteLength() == headerLength + thisFragmentLength); 928 | sendDatagramToOutput(fragment); 929 | offset += thisFragmentLength; 930 | } 931 | 932 | delete packet; 933 | } 934 | 935 | void Ipv4::encapsulate(Packet *transportPacket) { 936 | const auto &ipv4Header = makeShared(); 937 | 938 | auto l3AddressReq = transportPacket->removeTag(); 939 | Ipv4Address src = l3AddressReq->getSrcAddress().toIpv4(); 940 | bool nonLocalSrcAddress = l3AddressReq->getNonLocalSrcAddress(); 941 | Ipv4Address dest = l3AddressReq->getDestAddress().toIpv4(); 942 | delete l3AddressReq; 943 | 944 | ipv4Header->setProtocolId((IpProtocolId)ProtocolGroup::ipprotocol.getProtocolNumber(transportPacket->getTag()->getProtocol())); 945 | 946 | auto hopLimitReq = transportPacket->removeTagIfPresent(); 947 | short ttl = (hopLimitReq != nullptr) ? hopLimitReq->getHopLimit() : -1; 948 | delete hopLimitReq; 949 | bool dontFragment = false; 950 | if (auto dontFragmentReq = transportPacket->removeTagIfPresent()) { 951 | dontFragment = dontFragmentReq->getDontFragment(); 952 | delete dontFragmentReq; 953 | } 954 | 955 | // set source and destination address 956 | ipv4Header->setDestAddress(dest); 957 | 958 | // when source address was given, use it; otherwise it'll get the address 959 | // of the outgoing interface after routing 960 | if (!src.isUnspecified()) { 961 | if (!nonLocalSrcAddress && rt->getInterfaceByAddress(src) == nullptr) 962 | // if interface parameter does not match existing interface, do not send datagram 963 | throw cRuntimeError("Wrong source address %s in (%s)%s: no interface with such address", 964 | src.str().c_str(), transportPacket->getClassName(), transportPacket->getFullName()); 965 | 966 | ipv4Header->setSrcAddress(src); 967 | } 968 | 969 | // set other fields 970 | if (TosReq *tosReq = transportPacket->removeTagIfPresent()) { 971 | ipv4Header->setTypeOfService(tosReq->getTos()); 972 | delete tosReq; 973 | if (transportPacket->findTag()) 974 | throw cRuntimeError("TosReq and DscpReq found together"); 975 | if (transportPacket->findTag()) 976 | throw cRuntimeError("TosReq and EcnReq found together"); 977 | } 978 | if (DscpReq *dscpReq = transportPacket->removeTagIfPresent()) { 979 | ipv4Header->setDscp(dscpReq->getDifferentiatedServicesCodePoint()); 980 | delete dscpReq; 981 | } 982 | if (EcnReq *ecnReq = transportPacket->removeTagIfPresent()) { 983 | ipv4Header->setEcn(ecnReq->getExplicitCongestionNotification()); 984 | delete ecnReq; 985 | } 986 | 987 | ipv4Header->setIdentification(curFragmentId++); 988 | ipv4Header->setMoreFragments(false); 989 | ipv4Header->setDontFragment(dontFragment); 990 | ipv4Header->setFragmentOffset(0); 991 | 992 | if (ttl != -1) { 993 | ASSERT(ttl > 0); 994 | } else if (ipv4Header->getDestAddress().isLinkLocalMulticast()) 995 | ttl = 1; 996 | else if (ipv4Header->getDestAddress().isMulticast()) 997 | ttl = defaultMCTimeToLive; 998 | else 999 | ttl = defaultTimeToLive; 1000 | ipv4Header->setTimeToLive(ttl); 1001 | 1002 | if (Ipv4OptionsReq *optReq = transportPacket->removeTagIfPresent()) { 1003 | for (size_t i = 0; i < optReq->getOptionArraySize(); i++) { 1004 | auto opt = optReq->dropOption(i); 1005 | ipv4Header->addOption(opt); 1006 | ipv4Header->addChunkLength(B(opt->getLength())); 1007 | } 1008 | delete optReq; 1009 | } 1010 | 1011 | ASSERT(ipv4Header->getChunkLength() <= IPv4_MAX_HEADER_LENGTH); 1012 | ipv4Header->setHeaderLength(ipv4Header->getChunkLength()); 1013 | ipv4Header->setTotalLengthField(ipv4Header->getChunkLength() + transportPacket->getDataLength()); 1014 | ipv4Header->setCrcMode(crcMode); 1015 | ipv4Header->setCrc(0); 1016 | switch (crcMode) { 1017 | case CRC_DECLARED_CORRECT: 1018 | // if the CRC mode is declared to be correct, then set the CRC to an easily recognizable value 1019 | ipv4Header->setCrc(0xC00D); 1020 | break; 1021 | case CRC_DECLARED_INCORRECT: 1022 | // if the CRC mode is declared to be incorrect, then set the CRC to an easily recognizable value 1023 | ipv4Header->setCrc(0xBAAD); 1024 | break; 1025 | case CRC_COMPUTED: { 1026 | ipv4Header->setCrc(0); 1027 | // crc will be calculated in fragmentAndSend() 1028 | break; 1029 | } 1030 | default: 1031 | throw cRuntimeError("Unknown CRC mode"); 1032 | } 1033 | insertNetworkProtocolHeader(transportPacket, Protocol::ipv4, ipv4Header); 1034 | // setting Ipv4 options is currently not supported 1035 | } 1036 | 1037 | void Ipv4::sendDatagramToOutput(Packet *packet) { 1038 | const InterfaceEntry *ie = ift->getInterfaceById(packet->getTag()->getInterfaceId()); 1039 | auto nextHopAddressReq = packet->removeTag(); 1040 | Ipv4Address nextHopAddr = nextHopAddressReq->getNextHopAddress().toIpv4(); 1041 | delete nextHopAddressReq; 1042 | if (!ie->isBroadcast() || ie->getMacAddress().isUnspecified()) // we can't do ARP 1043 | sendPacketToNIC(packet); 1044 | else { 1045 | MacAddress nextHopMacAddr = resolveNextHopMacAddress(packet, nextHopAddr, ie); 1046 | if (nextHopMacAddr.isUnspecified()) { 1047 | EV_INFO << "Pending " << packet << " to ARP resolution.\n"; 1048 | pendingPackets[nextHopAddr].insert(packet); 1049 | } else { 1050 | ASSERT2(pendingPackets.find(nextHopAddr) == pendingPackets.end(), "Ipv4-ARP error: nextHopAddr found in ARP table, but Ipv4 queue for nextHopAddr not empty"); 1051 | packet->addTagIfAbsent()->setDestAddress(nextHopMacAddr); 1052 | sendPacketToNIC(packet); 1053 | } 1054 | } 1055 | } 1056 | 1057 | void Ipv4::arpResolutionCompleted(IArp::Notification *entry) { 1058 | if (entry->l3Address.getType() != L3Address::IPv4) 1059 | return; 1060 | auto it = pendingPackets.find(entry->l3Address.toIpv4()); 1061 | if (it != pendingPackets.end()) { 1062 | cPacketQueue &packetQueue = it->second; 1063 | EV << "ARP resolution completed for " << entry->l3Address << ". Sending " << packetQueue.getLength() 1064 | << " waiting packets from the queue\n"; 1065 | 1066 | while (!packetQueue.isEmpty()) { 1067 | Packet *packet = check_and_cast(packetQueue.pop()); 1068 | EV << "Sending out queued packet " << packet << "\n"; 1069 | packet->addTagIfAbsent()->setInterfaceId(entry->ie->getInterfaceId()); 1070 | packet->addTagIfAbsent()->setDestAddress(entry->macAddress); 1071 | sendPacketToNIC(packet); 1072 | } 1073 | pendingPackets.erase(it); 1074 | } 1075 | } 1076 | 1077 | void Ipv4::arpResolutionTimedOut(IArp::Notification *entry) { 1078 | if (entry->l3Address.getType() != L3Address::IPv4) 1079 | return; 1080 | auto it = pendingPackets.find(entry->l3Address.toIpv4()); 1081 | if (it != pendingPackets.end()) { 1082 | cPacketQueue &packetQueue = it->second; 1083 | EV << "ARP resolution failed for " << entry->l3Address << ", dropping " << packetQueue.getLength() << " packets\n"; 1084 | for (int i = 0; i < packetQueue.getLength(); i++) { 1085 | auto packet = packetQueue.get(i); 1086 | PacketDropDetails details; 1087 | details.setReason(ADDRESS_RESOLUTION_FAILED); 1088 | emit(packetDroppedSignal, packet, &details); 1089 | } 1090 | packetQueue.clear(); 1091 | pendingPackets.erase(it); 1092 | } 1093 | } 1094 | 1095 | MacAddress Ipv4::resolveNextHopMacAddress(cPacket *packet, Ipv4Address nextHopAddr, const InterfaceEntry *destIE) { 1096 | if (nextHopAddr.isLimitedBroadcastAddress() || nextHopAddr == destIE->getProtocolData()->getNetworkBroadcastAddress()) { 1097 | EV_DETAIL << "destination address is broadcast, sending packet to broadcast MAC address\n"; 1098 | return MacAddress::BROADCAST_ADDRESS; 1099 | } 1100 | 1101 | if (nextHopAddr.isMulticast()) { 1102 | MacAddress macAddr = MacAddress::makeMulticastAddress(nextHopAddr); 1103 | EV_DETAIL << "destination address is multicast, sending packet to MAC address " << macAddr << "\n"; 1104 | return macAddr; 1105 | } 1106 | 1107 | return arp->resolveL3Address(nextHopAddr, destIE); 1108 | } 1109 | 1110 | void Ipv4::sendPacketToNIC(Packet *packet) { 1111 | EV_INFO << "Sending " << packet << " to output interface = " << ift->getInterfaceById(packet->getTag()->getInterfaceId())->getInterfaceName() << ".\n"; 1112 | packet->addTagIfAbsent()->setProtocol(&Protocol::ipv4); 1113 | packet->addTagIfAbsent()->setProtocol(&Protocol::ipv4); 1114 | delete packet->removeTagIfPresent(); 1115 | ASSERT(packet->findTag() != nullptr); 1116 | send(packet, "queueOut"); 1117 | } 1118 | 1119 | // NetFilter: 1120 | 1121 | void Ipv4::registerHook(int priority, INetfilter::IHook *hook) { 1122 | Enter_Method("registerHook()"); 1123 | NetfilterBase::registerHook(priority, hook); 1124 | } 1125 | 1126 | void Ipv4::unregisterHook(INetfilter::IHook *hook) { 1127 | Enter_Method("unregisterHook()"); 1128 | NetfilterBase::unregisterHook(hook); 1129 | } 1130 | 1131 | void Ipv4::dropQueuedDatagram(const Packet *packet) { 1132 | Enter_Method("dropQueuedDatagram()"); 1133 | for (auto iter = queuedDatagramsForHooks.begin(); iter != queuedDatagramsForHooks.end(); iter++) { 1134 | if (iter->packet == packet) { 1135 | delete packet; 1136 | queuedDatagramsForHooks.erase(iter); 1137 | return; 1138 | } 1139 | } 1140 | } 1141 | 1142 | void Ipv4::reinjectQueuedDatagram(const Packet *packet) { 1143 | Enter_Method("reinjectDatagram()"); 1144 | for (auto iter = queuedDatagramsForHooks.begin(); iter != queuedDatagramsForHooks.end(); iter++) { 1145 | if (iter->packet == packet) { 1146 | auto *qPacket = iter->packet; 1147 | take(qPacket); 1148 | switch (iter->hookType) { 1149 | case INetfilter::IHook::LOCALOUT: 1150 | datagramLocalOut(qPacket); 1151 | break; 1152 | 1153 | case INetfilter::IHook::PREROUTING: 1154 | preroutingFinish(qPacket); 1155 | break; 1156 | 1157 | case INetfilter::IHook::POSTROUTING: 1158 | fragmentAndSend(qPacket); 1159 | break; 1160 | 1161 | case INetfilter::IHook::LOCALIN: 1162 | reassembleAndDeliverFinish(qPacket); 1163 | break; 1164 | 1165 | case INetfilter::IHook::FORWARD: 1166 | routeUnicastPacketFinish(qPacket); 1167 | break; 1168 | 1169 | default: 1170 | throw cRuntimeError("Unknown hook ID: %d", (int)(iter->hookType)); 1171 | break; 1172 | } 1173 | queuedDatagramsForHooks.erase(iter); 1174 | return; 1175 | } 1176 | } 1177 | } 1178 | 1179 | INetfilter::IHook::Result Ipv4::datagramPreRoutingHook(Packet *packet) { 1180 | for (auto &elem : hooks) { 1181 | IHook::Result r = elem.second->datagramPreRoutingHook(packet); 1182 | switch (r) { 1183 | case INetfilter::IHook::ACCEPT: 1184 | break; // continue iteration 1185 | 1186 | case INetfilter::IHook::DROP: 1187 | delete packet; 1188 | return r; 1189 | 1190 | case INetfilter::IHook::QUEUE: 1191 | queuedDatagramsForHooks.push_back(QueuedDatagramForHook(packet, INetfilter::IHook::PREROUTING)); 1192 | return r; 1193 | 1194 | case INetfilter::IHook::STOLEN: 1195 | return r; 1196 | 1197 | default: 1198 | throw cRuntimeError("Unknown Hook::Result value: %d", (int)r); 1199 | } 1200 | } 1201 | return INetfilter::IHook::ACCEPT; 1202 | } 1203 | 1204 | INetfilter::IHook::Result Ipv4::datagramForwardHook(Packet *packet) { 1205 | for (auto &elem : hooks) { 1206 | IHook::Result r = elem.second->datagramForwardHook(packet); 1207 | switch (r) { 1208 | case INetfilter::IHook::ACCEPT: 1209 | break; // continue iteration 1210 | 1211 | case INetfilter::IHook::DROP: 1212 | delete packet; 1213 | return r; 1214 | 1215 | case INetfilter::IHook::QUEUE: 1216 | queuedDatagramsForHooks.push_back(QueuedDatagramForHook(packet, INetfilter::IHook::FORWARD)); 1217 | return r; 1218 | 1219 | case INetfilter::IHook::STOLEN: 1220 | return r; 1221 | 1222 | default: 1223 | throw cRuntimeError("Unknown Hook::Result value: %d", (int)r); 1224 | } 1225 | } 1226 | return INetfilter::IHook::ACCEPT; 1227 | } 1228 | 1229 | INetfilter::IHook::Result Ipv4::datagramPostRoutingHook(Packet *packet) { 1230 | for (auto &elem : hooks) { 1231 | IHook::Result r = elem.second->datagramPostRoutingHook(packet); 1232 | switch (r) { 1233 | case INetfilter::IHook::ACCEPT: 1234 | break; // continue iteration 1235 | 1236 | case INetfilter::IHook::DROP: 1237 | delete packet; 1238 | return r; 1239 | 1240 | case INetfilter::IHook::QUEUE: 1241 | queuedDatagramsForHooks.push_back(QueuedDatagramForHook(packet, INetfilter::IHook::POSTROUTING)); 1242 | return r; 1243 | 1244 | case INetfilter::IHook::STOLEN: 1245 | return r; 1246 | 1247 | default: 1248 | throw cRuntimeError("Unknown Hook::Result value: %d", (int)r); 1249 | } 1250 | } 1251 | return INetfilter::IHook::ACCEPT; 1252 | } 1253 | 1254 | void Ipv4::handleStartOperation(LifecycleOperation *operation) { 1255 | start(); 1256 | } 1257 | 1258 | void Ipv4::handleStopOperation(LifecycleOperation *operation) { 1259 | // TODO: stop should send and wait pending packets 1260 | stop(); 1261 | } 1262 | 1263 | void Ipv4::handleCrashOperation(LifecycleOperation *operation) { 1264 | stop(); 1265 | } 1266 | 1267 | void Ipv4::start() { 1268 | } 1269 | 1270 | void Ipv4::stop() { 1271 | flush(); 1272 | for (auto it : socketIdToSocketDescriptor) 1273 | delete it.second; 1274 | socketIdToSocketDescriptor.clear(); 1275 | } 1276 | 1277 | void Ipv4::flush() { 1278 | EV_DEBUG << "Ipv4::flush(): pending packets:\n"; 1279 | for (auto &elem : pendingPackets) { 1280 | EV_DEBUG << "Ipv4::flush(): " << elem.first << ": " << elem.second.str() << endl; 1281 | elem.second.clear(); 1282 | } 1283 | pendingPackets.clear(); 1284 | 1285 | EV_DEBUG << "Ipv4::flush(): packets in hooks: " << queuedDatagramsForHooks.size() << endl; 1286 | for (auto &elem : queuedDatagramsForHooks) { 1287 | delete elem.packet; 1288 | } 1289 | queuedDatagramsForHooks.clear(); 1290 | 1291 | fragbuf.flush(); 1292 | } 1293 | 1294 | INetfilter::IHook::Result Ipv4::datagramLocalInHook(Packet *packet) { 1295 | for (auto &elem : hooks) { 1296 | IHook::Result r = elem.second->datagramLocalInHook(packet); 1297 | switch (r) { 1298 | case INetfilter::IHook::ACCEPT: 1299 | break; // continue iteration 1300 | 1301 | case INetfilter::IHook::DROP: 1302 | delete packet; 1303 | return r; 1304 | 1305 | case INetfilter::IHook::QUEUE: { 1306 | if (packet->getOwner() != this) 1307 | throw cRuntimeError("Model error: netfilter hook changed the owner of queued datagram '%s'", packet->getFullName()); 1308 | queuedDatagramsForHooks.push_back(QueuedDatagramForHook(packet, INetfilter::IHook::LOCALIN)); 1309 | return r; 1310 | } 1311 | 1312 | case INetfilter::IHook::STOLEN: 1313 | return r; 1314 | 1315 | default: 1316 | throw cRuntimeError("Unknown Hook::Result value: %d", (int)r); 1317 | } 1318 | } 1319 | return INetfilter::IHook::ACCEPT; 1320 | } 1321 | 1322 | INetfilter::IHook::Result Ipv4::datagramLocalOutHook(Packet *packet) { 1323 | for (auto &elem : hooks) { 1324 | IHook::Result r = elem.second->datagramLocalOutHook(packet); 1325 | switch (r) { 1326 | case INetfilter::IHook::ACCEPT: 1327 | break; // continue iteration 1328 | 1329 | case INetfilter::IHook::DROP: 1330 | delete packet; 1331 | return r; 1332 | 1333 | case INetfilter::IHook::QUEUE: 1334 | queuedDatagramsForHooks.push_back(QueuedDatagramForHook(packet, INetfilter::IHook::LOCALOUT)); 1335 | return r; 1336 | 1337 | case INetfilter::IHook::STOLEN: 1338 | return r; 1339 | 1340 | default: 1341 | throw cRuntimeError("Unknown Hook::Result value: %d", (int)r); 1342 | } 1343 | } 1344 | return INetfilter::IHook::ACCEPT; 1345 | } 1346 | 1347 | void Ipv4::receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details) { 1348 | Enter_Method_Silent(); 1349 | 1350 | if (signalID == IArp::arpResolutionCompletedSignal) { 1351 | arpResolutionCompleted(check_and_cast(obj)); 1352 | } 1353 | if (signalID == IArp::arpResolutionFailedSignal) { 1354 | arpResolutionTimedOut(check_and_cast(obj)); 1355 | } 1356 | } 1357 | 1358 | void Ipv4::sendIcmpError(Packet *origPacket, int inputInterfaceId, IcmpType type, IcmpCode code) { 1359 | icmp->sendErrorMessage(origPacket, inputInterfaceId, type, code); 1360 | } 1361 | 1362 | } // namespace inet 1363 | -------------------------------------------------------------------------------- /modules/inet/ipv4/Ipv4.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2004 Andras Varga 3 | // Copyright (C) 2014 OpenSim Ltd. 4 | // 5 | // This program is free software; you can redistribute it and/or 6 | // modify it under the terms of the GNU Lesser General Public License 7 | // as published by the Free Software Foundation; either version 2 8 | // of the License, or (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU Lesser General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU Lesser General Public License 16 | // along with this program; if not, see . 17 | // 18 | 19 | #ifndef __INET_IPV4_H 20 | #define __INET_IPV4_H 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "inet/common/INETDefs.h" 27 | #include "inet/common/IProtocolRegistrationListener.h" 28 | #include "inet/common/lifecycle/ModuleOperations.h" 29 | #include "inet/common/lifecycle/OperationalBase.h" 30 | #include "inet/common/packet/Message.h" 31 | #include "inet/networklayer/contract/IArp.h" 32 | #include "inet/networklayer/contract/INetfilter.h" 33 | #include "inet/networklayer/contract/INetworkProtocol.h" 34 | #include "inet/networklayer/ipv4/Icmp.h" 35 | #include "inet/networklayer/ipv4/Ipv4FragBuf.h" 36 | #include "inet/networklayer/ipv4/Ipv4Header_m.h" 37 | 38 | namespace inet { 39 | 40 | class ArpPacket; 41 | class IcmpHeader; 42 | class IInterfaceTable; 43 | class IIpv4RoutingTable; 44 | // class IIpv4PfrpRoutingTable; 45 | 46 | /** 47 | * Implements the Ipv4 protocol. 48 | */ 49 | class INET_API Ipv4 : public OperationalBase, public NetfilterBase, public INetworkProtocol, public IProtocolRegistrationListener, public cListener { 50 | public: 51 | double survivalTime; 52 | /** 53 | * Represents an Ipv4Header, queued by a Hook 54 | */ 55 | class QueuedDatagramForHook { 56 | public: 57 | QueuedDatagramForHook(Packet *packet, IHook::Type hookType) : packet(packet), hookType(hookType) {} 58 | virtual ~QueuedDatagramForHook() {} 59 | 60 | Packet *packet = nullptr; 61 | const IHook::Type hookType = static_cast(-1); 62 | }; 63 | typedef std::map PendingPackets; 64 | 65 | struct SocketDescriptor { 66 | int socketId = -1; 67 | int protocolId = -1; 68 | Ipv4Address localAddress; 69 | Ipv4Address remoteAddress; 70 | 71 | SocketDescriptor(int socketId, int protocolId, Ipv4Address localAddress) 72 | : socketId(socketId), protocolId(protocolId), localAddress(localAddress) {} 73 | }; 74 | 75 | protected: 76 | IIpv4RoutingTable *rt = nullptr; 77 | // IIpv4PfrpRoutingTable *prt = nullptr; 78 | IInterfaceTable *ift = nullptr; 79 | IArp *arp = nullptr; 80 | Icmp *icmp = nullptr; 81 | int transportInGateBaseId = -1; 82 | 83 | // config 84 | CrcMode crcMode = CRC_MODE_UNDEFINED; 85 | int defaultTimeToLive = -1; 86 | int defaultMCTimeToLive = -1; 87 | simtime_t fragmentTimeoutTime; 88 | bool limitedBroadcast = false; 89 | std::string directBroadcastInterfaces = ""; 90 | 91 | cPatternMatcher directBroadcastInterfaceMatcher; 92 | 93 | // working vars 94 | uint16_t curFragmentId = -1; // counter, used to assign unique fragmentIds to datagrams 95 | Ipv4FragBuf fragbuf; // fragmentation reassembly buffer 96 | simtime_t lastCheckTime; // when fragbuf was last checked for state fragments 97 | std::set upperProtocols; // where to send packets after decapsulation 98 | std::map socketIdToSocketDescriptor; 99 | 100 | // ARP related 101 | PendingPackets pendingPackets; // map indexed with IPv4Address for outbound packets waiting for ARP resolution 102 | 103 | // statistics 104 | int numMulticast = 0; 105 | int numLocalDeliver = 0; 106 | int numDropped = 0; // forwarding off, no outgoing interface, too large but "don't fragment" is set, TTL exceeded, etc 107 | int numUnroutable = 0; 108 | int numForwarded = 0; 109 | 110 | // hooks 111 | typedef std::list DatagramQueueForHooks; 112 | DatagramQueueForHooks queuedDatagramsForHooks; 113 | 114 | protected: 115 | // utility: look up interface from getArrivalGate() 116 | virtual const InterfaceEntry *getSourceInterface(Packet *packet); 117 | virtual const InterfaceEntry *getDestInterface(Packet *packet); 118 | virtual Ipv4Address getNextHop(Packet *packet); 119 | 120 | // utility: look up route to the source of the datagram and return its interface 121 | virtual const InterfaceEntry *getShortestPathInterfaceToSource(const Ptr &ipv4Header) const; 122 | 123 | // utility: show current statistics above the icon 124 | virtual void refreshDisplay() const override; 125 | 126 | // utility: processing requested ARP resolution completed 127 | void arpResolutionCompleted(IArp::Notification *entry); 128 | 129 | // utility: processing requested ARP resolution timed out 130 | void arpResolutionTimedOut(IArp::Notification *entry); 131 | 132 | // utility: verifying CRC 133 | bool verifyCrc(const Ptr &ipv4Header); 134 | 135 | // utility: calculate and set CRC 136 | void setComputedCrc(Ptr &ipv4Header); 137 | 138 | public: 139 | static void insertCrc(const Ptr &ipv4Header); 140 | 141 | protected: 142 | /** 143 | * Encapsulate packet coming from higher layers into Ipv4Header, using 144 | * the given control info. Override if you subclassed controlInfo and/or 145 | * want to add options etc to the datagram. 146 | */ 147 | virtual void encapsulate(Packet *packet); 148 | 149 | /** 150 | * Handle Ipv4Header messages arriving from lower layer. 151 | * Decrements TTL, then invokes routePacket(). 152 | */ 153 | virtual void handleIncomingDatagram(Packet *packet); 154 | 155 | // called after PREROUTING Hook (used for reinject, too) 156 | virtual void preroutingFinish(Packet *packet); 157 | 158 | /** 159 | * Handle messages (typically packets to be send in Ipv4) from transport or ICMP. 160 | * Invokes encapsulate(), then routePacket(). 161 | */ 162 | virtual void handlePacketFromHL(Packet *packet); 163 | 164 | /** 165 | * Routes and sends datagram received from higher layers. 166 | * Invokes datagramLocalOutHook(), then routePacket(). 167 | */ 168 | virtual void datagramLocalOut(Packet *packet); 169 | 170 | /** 171 | * Performs unicast routing. Based on the routing decision, it sends the 172 | * datagram through the outgoing interface. 173 | */ 174 | virtual void routeUnicastPacket(Packet *packet); 175 | 176 | // called after FORWARD Hook (used for reinject, too) 177 | void routeUnicastPacketFinish(Packet *packet); 178 | 179 | /** 180 | * Broadcasts the datagram on the specified interface. 181 | * When destIE is nullptr, the datagram is broadcasted on each interface. 182 | */ 183 | virtual void routeLocalBroadcastPacket(Packet *packet); 184 | 185 | /** 186 | * Determines the output interface for the given multicast datagram. 187 | */ 188 | virtual const InterfaceEntry *determineOutgoingInterfaceForMulticastDatagram(const Ptr &ipv4Header, const InterfaceEntry *multicastIFOption); 189 | 190 | /** 191 | * Forwards packets to all multicast destinations, using fragmentAndSend(). 192 | */ 193 | virtual void forwardMulticastPacket(Packet *packet); 194 | 195 | /** 196 | * Perform reassembly of fragmented datagrams, then send them up to the 197 | * higher layers using sendToHL(). 198 | */ 199 | virtual void reassembleAndDeliver(Packet *packet); 200 | 201 | // called after LOCAL_IN Hook (used for reinject, too) 202 | virtual void reassembleAndDeliverFinish(Packet *packet); 203 | 204 | /** 205 | * Decapsulate packet. 206 | */ 207 | virtual void decapsulate(Packet *packet); 208 | 209 | /** 210 | * Call PostRouting Hook and continue with fragmentAndSend() if accepted 211 | */ 212 | virtual void fragmentPostRouting(Packet *datagram); 213 | 214 | /** 215 | * Fragment packet if needed, then send it to the selected interface using 216 | * sendDatagramToOutput(). 217 | */ 218 | virtual void fragmentAndSend(Packet *packet); 219 | 220 | /** 221 | * Send datagram on the given interface. 222 | */ 223 | virtual void sendDatagramToOutput(Packet *packet); 224 | 225 | virtual MacAddress resolveNextHopMacAddress(cPacket *packet, Ipv4Address nextHopAddr, const InterfaceEntry *destIE); 226 | 227 | virtual void sendPacketToNIC(Packet *packet); 228 | 229 | virtual void sendIcmpError(Packet *packet, int inputInterfaceId, IcmpType type, IcmpCode code); 230 | 231 | virtual Packet *prepareForForwarding(Packet *packet) const; 232 | 233 | public: 234 | Ipv4(); 235 | virtual ~Ipv4(); 236 | 237 | virtual void handleRegisterService(const Protocol &protocol, cGate *out, ServicePrimitive servicePrimitive) override; 238 | virtual void handleRegisterProtocol(const Protocol &protocol, cGate *in, ServicePrimitive servicePrimitive) override; 239 | 240 | protected: 241 | virtual int numInitStages() const override { return NUM_INIT_STAGES; } 242 | virtual void initialize(int stage) override; 243 | virtual void handleMessageWhenUp(cMessage *msg) override; 244 | 245 | void handleRequest(Request *request); 246 | 247 | // NetFilter functions: 248 | 249 | protected: 250 | /** 251 | * called before a packet arriving from the network is routed 252 | */ 253 | IHook::Result datagramPreRoutingHook(Packet *datagram); 254 | 255 | /** 256 | * called before a packet arriving from the network is delivered via the network 257 | */ 258 | IHook::Result datagramForwardHook(Packet *datagram); 259 | 260 | /** 261 | * called before a packet is delivered via the network 262 | */ 263 | IHook::Result datagramPostRoutingHook(Packet *datagram); 264 | 265 | /** 266 | * called before a packet arriving from the network is delivered locally 267 | */ 268 | IHook::Result datagramLocalInHook(Packet *datagram); 269 | 270 | /** 271 | * called before a packet arriving locally is delivered 272 | */ 273 | IHook::Result datagramLocalOutHook(Packet *datagram); 274 | 275 | public: 276 | /** 277 | * registers a Hook to be executed during datagram processing 278 | */ 279 | virtual void registerHook(int priority, IHook *hook) override; 280 | 281 | /** 282 | * unregisters a Hook to be executed during datagram processing 283 | */ 284 | virtual void unregisterHook(IHook *hook) override; 285 | 286 | /** 287 | * drop a previously queued datagram 288 | */ 289 | virtual void dropQueuedDatagram(const Packet *datagram) override; 290 | 291 | /** 292 | * re-injects a previously queued datagram 293 | */ 294 | virtual void reinjectQueuedDatagram(const Packet *datagram) override; 295 | 296 | /** 297 | * ILifecycle methods 298 | */ 299 | virtual bool isInitializeStage(int stage) override { return stage == INITSTAGE_NETWORK_LAYER; } 300 | virtual bool isModuleStartStage(int stage) override { return stage == ModuleStartOperation::STAGE_NETWORK_LAYER; } 301 | virtual bool isModuleStopStage(int stage) override { return stage == ModuleStopOperation::STAGE_NETWORK_LAYER; } 302 | virtual void handleStartOperation(LifecycleOperation *operation) override; 303 | virtual void handleStopOperation(LifecycleOperation *operation) override; 304 | virtual void handleCrashOperation(LifecycleOperation *operation) override; 305 | 306 | /// cListener method 307 | virtual void receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details) override; 308 | 309 | protected: 310 | virtual void start(); 311 | virtual void stop(); 312 | virtual void flush(); 313 | }; 314 | 315 | } // namespace inet 316 | 317 | #endif // ifndef __INET_IPV4_H 318 | -------------------------------------------------------------------------------- /modules/inet/ipv4/Ipv4.ned: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2004 Andras Varga 3 | // 4 | // This program is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Lesser General Public License 6 | // as published by the Free Software Foundation; either version 2 7 | // of the License, or (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with this program; if not, see . 16 | // 17 | 18 | package inet.networklayer.ipv4; 19 | 20 | moduleinterface IIpv4 21 | { 22 | gates: 23 | input transportIn @labels(Ipv4ControlInfo/down,TcpHeader,UdpHeader,SctpHeader); 24 | output transportOut @labels(Ipv4ControlInfo/up,TcpHeader,UdpHeader,SctpHeader); 25 | input queueIn @labels(Ipv4Header,ArpPacket,Ieee802Ctrl); 26 | output queueOut @labels(Ipv4Header,ArpPacket,Ieee802Ctrl); 27 | } 28 | 29 | // 30 | // Implements the IPv4 protocol. The protocol header is represented 31 | // by the ~Ipv4Header message class. 32 | // 33 | // Interfacing with higher layer protocols 34 | // 35 | // To send a packet over IPv4 from a higher layer protocol, the module should 36 | // fill in an ~L3AddressReq object, attach it to the packet with the Packets's 37 | // addTag() method, then send the packet to the ~Ipv4 module. 38 | // 39 | // When ~Ipv4 sends up a packet to a higher layer protocol, it will also attach 40 | // an ~L3AddressInd to the packet, with the source and destination IPv4 addresses 41 | // of the IPv4 datagram in which the packet arrived. 42 | // 43 | // ~Ipv4 can serve several higher-layer protocols. The higher layer protocols 44 | // should call registerProtocol with their gate towards the ~Ipv4 module, 45 | // for fill up the protocol-to-gateindex map. When delivering packets to them, 46 | // the output gate is determined from the Protocol in the IPv4 header. 47 | // 48 | // Routing and interfacing with lower layers 49 | // 50 | // The routing table is stored in the module ~Ipv4RoutingTable. When a datagram 51 | // needs to be routed, ~Ipv4 queries ~Ipv4RoutingTable for the output interface 52 | // (or "port") and next hop address of the packet. This is done by directly 53 | // calling C++ methods (such as findBestMatchingRoute(destAddress)) of ~Ipv4RoutingTable. 54 | // No message exchange with ~Ipv4RoutingTable takes place. 55 | // 56 | // A routed datagram will be sent to the queueOut, which is expected to be 57 | // connected to ~INetworkInterface modules. 58 | // 59 | // Routing protocol implementations (e.g. OSPF and ISIS) can also query 60 | // and manipulate the route table by calling ~Ipv4RoutingTable's methods in C++. 61 | // 62 | // Working with Arp 63 | // 64 | // Ipv4 module subscribe to arpResolutionCompleted and arpResolutionFailed signals on Arp module. 65 | // The ~Arp module accessed via arpOut gate, should not insert any module between ~Ipv4 and ~Arp. 66 | // Before Ipv4 module send down a packet to lower layer, ask MacAddress of next hop from Arp via 67 | // method call. If MacAddress unspecified, then start address resolution via Arp method call and 68 | // insert packet to a queue specified by next hop addr. 69 | // When received a arpResolutionCompleted, then send packets from queue of next hop addr. 70 | // When received a arpResolutionFailed, then drop packets from queue of next hop addr. 71 | // When Ipv4 module received an ARP packet from Lower Layer on some queueIn gate, 72 | // then send out this packet on arpOut gate. When received a packet on arpIn gate, 73 | // then send out this packet on the specified queueOut gate. 74 | // 75 | // Performance model, QoS 76 | // 77 | // In the current form, ~Ipv4 contains a FIFO which queues up Ipv4 datagrams; 78 | // datagrams are processed in order. The processing time is determined by the 79 | // procDelay module parameter. 80 | // 81 | // The current performance model comes from the QueueBase C++ base class. 82 | // If you need a more sophisticated performance model, you may change the 83 | // module implementation (the Ipv4 class), and: (1) override the startService() 84 | // method which determines processing time for a packet, or (2) use a 85 | // different base class. 86 | // 87 | // @see ~Ipv4RoutingTable, ~Arp 88 | // 89 | // @author Andras Varga 90 | // 91 | simple Ipv4 like IIpv4 92 | { 93 | parameters: 94 | string interfaceTableModule; // The path to the InterfaceTable module 95 | string routingTableModule; 96 | string arpModule; 97 | string icmpModule; 98 | string crcMode @enum("declared", "computed") = default("declared"); 99 | double procDelay @unit(s) = default(0s); 100 | int timeToLive = default(32); 101 | double survivalTime; 102 | int multicastTimeToLive = default(32); 103 | double fragmentTimeout @unit(s) = default(60s); 104 | bool limitedBroadcast = default(false); // send out limited broadcast packets comming from higher layer 105 | string directBroadcastInterfaces = default(""); // list of interfaces that direct broadcast is enabled (by default direct broadcast is disabled on all interfaces) 106 | @display("i=block/routing"); 107 | @signal[packetSentToUpper](type=cPacket); 108 | @signal[packetReceivedFromUpper](type=cPacket); 109 | @signal[packetSentToLower](type=cPacket); 110 | @signal[packetReceivedFromLower](type=cPacket); 111 | @signal[packetDropped](type=cPacket); 112 | @signal[ipv4NewMulticast](type=inet::Ipv4Header); 113 | @signal[ipv4DataOnNonrpf](type=inet::Ipv4Header); 114 | @signal[ipv4DataOnRpf](type=inet::Ipv4Header); 115 | @signal[ipv4MdataRegister](type=inet::Packet); 116 | @statistic[packetDropAddressResolutionFailed](title="packet drop: address resolution failed"; source=packetDropReasonIsAddressResolutionFailed(packetDropped); record=count,sum(packetBytes),vector(packetBytes); interpolationmode=none); 117 | @statistic[packetDropHopLimitReached](title="packet drop: hop limit reached"; source=packetDropReasonIsHopLimitReached(packetDropped); record=count,sum(packetBytes),vector(packetBytes); interpolationmode=none); 118 | @statistic[packetDropForwardingDisabled](title="packet drop: forwarding disabled"; source=packetDropReasonIsForwardingDisabled(packetDropped); record=count,sum(packetBytes),vector(packetBytes); interpolationmode=none); 119 | @statistic[packetDropNoInterfaceFound](title="packet drop: no interface found"; source=packetDropReasonIsNoInterfaceFound(packetDropped); record=count,sum(packetBytes),vector(packetBytes); interpolationmode=none); 120 | @statistic[packetDropNoRouteFound](title="packet drop: no route found"; source=packetDropReasonIsNoRouteFound(packetDropped); record=count,sum(packetBytes),vector(packetBytes); interpolationmode=none); 121 | @statistic[packetDropUndefined](title="packet drop: undefined"; source=packetDropReasonIsUndefined(packetDropped); record=count,sum(packetBytes),vector(packetBytes); interpolationmode=none); 122 | gates: 123 | input transportIn @labels(Ipv4ControlInfo/down,TcpHeader,UdpHeader,SctpHeader); 124 | output transportOut @labels(Ipv4ControlInfo/up,TcpHeader,UdpHeader,SctpHeader); 125 | input queueIn @labels(Ipv4Header,ArpPacket,Ieee802Ctrl); 126 | output queueOut @labels(Ipv4Header,ArpPacket,Ieee802Ctrl); 127 | } 128 | 129 | -------------------------------------------------------------------------------- /modules/inet/ipv4/pfrpTable.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author : CHEN Jiawei 3 | * @Date : 2023-10-18 20:28:25 4 | * @LastEditors : LIN Guocheng 5 | * @LastEditTime : 2023-10-19 15:23:38 6 | * @FilePath : /home/lgc/test/RL4Net++/modules/ipv4/pfrpTable.cc 7 | * @Description : Probabilistic routing is implemented so that when a packet arrives at the router the next hop 8 | * is selected for forwarding based on the probabilistic routing table. 9 | */ 10 | #include "pfrpTable.h" 11 | 12 | pfrpTable *pfrpTable::pTable = NULL; 13 | 14 | pfrpTable::pfrpTable() 15 | { 16 | } 17 | 18 | pfrpTable::~pfrpTable() 19 | { 20 | } 21 | 22 | /** 23 | * @description: Get unique static instance of probabilistic routing table. 24 | * @return {*pfrpTable} probabilistic routing table 25 | */ 26 | pfrpTable *pfrpTable::getInstance() 27 | { 28 | if (!pTable) 29 | { 30 | throw "pTable is not initiated"; 31 | } 32 | return pTable; 33 | } 34 | 35 | /** 36 | * @description: Initialize probabilistic routing table. 37 | * @param {int} num node num in network topology 38 | * @param {char} *file file path to initial probabilistic routing table 39 | * @param {int} port communication port for ZMQ 40 | * @param {double} survivalTime_v survival time, set in omnetpp.ini 41 | * @param {int} totalStep_v total step, set in omnetpp.ini 42 | * @param {int} simMode_v simulation mode, set in omnetpp.ini 43 | * @return {*pfrpTable} Probabilistic routing table for completion of initialization. 44 | */ 45 | pfrpTable *pfrpTable::initTable(int num, const char *file, int port, double survivalTime_v, int totalStep_v, int simMode_v) 46 | { 47 | if (!pTable) 48 | { 49 | pTable = new pfrpTable(); 50 | pTable->setNodeNum(num); 51 | pTable->setRoutingFileName(file); 52 | pTable->setVals(port, survivalTime_v, totalStep_v, simMode_v); 53 | pTable->initiate(); 54 | } 55 | return pTable; 56 | } 57 | 58 | /** 59 | * @description: Initialize the intermediate variables needed to complete the probabilistic routing. 60 | * @return {*} None 61 | */ 62 | void pfrpTable::initiate() 63 | { 64 | string connectAddr = "tcp://localhost:" + to_string(zmqPort); 65 | zmq_connect((void *)zmqSocket, connectAddr.c_str()); 66 | 67 | topo = (int **)malloc(nodeNum * sizeof(int *)); 68 | memset(topo, 0, nodeNum); 69 | int *tmp = (int *)malloc(nodeNum * nodeNum * sizeof(int)); 70 | memset(tmp, 0, nodeNum * nodeNum); 71 | for (int i = 0; i < nodeNum; i++) 72 | { 73 | topo[i] = tmp + nodeNum * i; 74 | } 75 | 76 | allProb = (int **)malloc(nodeNum * sizeof(int *)); 77 | memset(allProb, 0, nodeNum * sizeof(int *)); 78 | tmp = (int *)malloc(nodeNum * nodeNum * sizeof(int)); 79 | memset(tmp, 0, nodeNum * nodeNum * sizeof(int)); 80 | for (int i = 0; i < nodeNum; i++) 81 | { 82 | allProb[i] = tmp + nodeNum * i; 83 | } 84 | allProb = getProb(routingFileName, allProb); 85 | 86 | pkct = (int **)malloc(nodeNum * sizeof(int *)); 87 | memset(pkct, 0, nodeNum * sizeof(int *)); 88 | tmp = (int *)malloc(nodeNum * nodeNum * sizeof(int)); 89 | memset(tmp, 0, nodeNum * nodeNum * sizeof(int)); 90 | for (int i = 0; i < nodeNum; i++) 91 | { 92 | pkct[i] = tmp + nodeNum * i; 93 | } 94 | 95 | stepIsEnd = (bool *)malloc(totalStep * sizeof(bool)); 96 | memset(stepIsEnd, false, totalStep * sizeof(bool)); 97 | 98 | stepFinished = (bool *)malloc(totalStep * sizeof(bool)); 99 | memset(stepFinished, false, totalStep * sizeof(bool)); 100 | 101 | stepEndTime = (double *)malloc(totalStep * sizeof(double)); 102 | memset(stepEndTime, 0, totalStep * sizeof(double)); 103 | 104 | stepEndRecordCount = (int *)malloc(totalStep * sizeof(int)); 105 | memset(stepEndRecordCount, 0, totalStep * sizeof(int)); 106 | 107 | updateProbCount = (int *)malloc(totalStep * sizeof(int)); 108 | memset(updateProbCount, 0, totalStep * sizeof(int)); 109 | 110 | pkNumOfStep = (int *)malloc(totalStep * sizeof(int)); 111 | memset(pkNumOfStep, 0, totalStep * sizeof(int)); 112 | 113 | ttlDrop = (int *)malloc(totalStep * sizeof(int)); 114 | memset(ttlDrop, 0, totalStep * sizeof(int)); 115 | 116 | stDrop = (int *)malloc(totalStep * sizeof(int)); 117 | memset(stDrop, 0, totalStep * sizeof(int)); 118 | 119 | for (int i = 0; i < nodeNum; i++) 120 | for (int j = i; j < nodeNum; j++) 121 | if (topo[i][j] >= 0) 122 | edgeNum++; 123 | 124 | // multi-agent DRL 125 | if (simMode == 2) 126 | { 127 | 128 | for (int i = 0; i < totalStep; i++) 129 | { 130 | vector *> nvec; 131 | for (int j = 0; j < nodeNum; j++) 132 | { 133 | unordered_set *nuset = new unordered_set; 134 | nvec.push_back(nuset); 135 | } 136 | pktInNode.push_back(nvec); 137 | 138 | unordered_map *numap = new unordered_map; 139 | pktDelay.push_back(numap); 140 | } 141 | 142 | stepPktNum = (int *)malloc(totalStep * sizeof(int)); 143 | memset(stepPktNum, 0, totalStep * sizeof(int)); 144 | } 145 | } 146 | 147 | /** 148 | * @description: Assign the number of nodes in the network topology to nodeNum. 149 | * @param {int} num the number of nodes in the network topology 150 | * @return {*} None 151 | */ 152 | void pfrpTable::setNodeNum(int num) 153 | { 154 | nodeNum = num; 155 | } 156 | 157 | /** 158 | * @description: Get the number of nodes in the network topology from nodeNum. 159 | * @return {int} the number of nodes in the network topology 160 | */ 161 | int pfrpTable::getNodeNum() 162 | { 163 | return nodeNum; 164 | } 165 | 166 | /** 167 | * @description: Convert the initial probabilistic routing table into a probability matrix. 168 | * @param {string} fileName file path to initial probabilistic routing table 169 | * @param {int} **Prob initial probabilistic routing matrix 170 | * @return {**pfrpTable} updated probabilitstic routing matrix 171 | */ 172 | int **pfrpTable::getProb(string fileName, int **Prob) 173 | { 174 | ifstream myfile(fileName); 175 | 176 | for (int i = 0; i < nodeNum; i++) 177 | { 178 | for (int j = 0; j < nodeNum; j++) 179 | { 180 | string od_prob; 181 | getline(myfile, od_prob, ','); 182 | Prob[i][j] = atoi(od_prob.c_str()); 183 | } 184 | } 185 | 186 | int edgeCount = 0; 187 | for (int i = 0; i < nodeNum; i++) 188 | { 189 | for (int j = i; j < nodeNum; j++) 190 | { 191 | 192 | if (Prob[i][j]) 193 | { 194 | topo[i][j] = edgeCount * 2; 195 | topo[j][i] = edgeCount * 2 + 1; 196 | edgeCount++; 197 | } 198 | else 199 | { 200 | topo[i][j] = -1; 201 | topo[j][i] = -1; 202 | } 203 | } 204 | } 205 | myfile.close(); 206 | return Prob; 207 | } 208 | 209 | /*** 210 | * @description: Select next hop for current packet based on probabilistic routing table. 211 | * @param {int} nodeId ID of current node 212 | * @param {int} dstNode ID of destination of current packet 213 | * @return {int} ID of the next-hop node that can be selected 214 | */ 215 | int pfrpTable::getNextNode(int nodeId, int dstNode) 216 | { 217 | if (allProb[nodeId][dstNode]) 218 | { 219 | return dstNode; 220 | } 221 | else 222 | { 223 | vector candidateNodes; 224 | vector candidateProbs; 225 | int probSum = 0; 226 | for (int i = 0; i < nodeNum; i++) 227 | { 228 | // if the point with id i is adjacent to the current point and up to the dstnode node 229 | if (allProb[nodeId][i]) 230 | { 231 | candidateNodes.push_back(i); 232 | candidateProbs.push_back(allProb[nodeId][i]); 233 | probSum += allProb[nodeId][i]; 234 | } 235 | } 236 | int randProb = int(rand() % probSum); 237 | 238 | for (int i = 0; i < candidateNodes.size(); i++) 239 | { 240 | int curProb = 0; 241 | for (int j = 0; j <= i; j++) 242 | curProb += candidateProbs[j]; 243 | if (randProb <= curProb) 244 | return candidateNodes[i]; 245 | } 246 | } 247 | } 248 | 249 | /** 250 | * @description: Get the ID of the output gate forwarded by the current node to the next node. 251 | * @param {int} nodeId ID of current node 252 | * @param {int} nextNode ID of destination of current packet 253 | * @return {int} ID of the output gate 254 | */ 255 | int pfrpTable::getGateId(int nodeId, int nextNode) 256 | { 257 | 258 | int gateId = 0; 259 | 260 | // links in the environment are bi-directional links 261 | for (int i = 0; i < nodeNum; i++) 262 | { 263 | if (allProb[nodeId][i]) 264 | { 265 | if (i == nextNode) 266 | break; 267 | else 268 | gateId++; 269 | } 270 | } 271 | // The first bit of ift has a lo0, so +1. 272 | gateId++; 273 | return gateId; 274 | } 275 | 276 | /** 277 | * @description: Get next hop and the ID of the output gate forwarded by the current node to the next node. 278 | * @param {string} path routing path 279 | * @param {char} *pkName name of the packet 280 | * @param {int} pkByte size of the packet, unit: Byte 281 | * @return {pair} R or H + destination Node ID, ID of the output gate 282 | */ 283 | pair pfrpTable::getRoute(string path, const char *pkName, int pkByte) 284 | { 285 | 286 | if (!pTable) 287 | { 288 | throw "getRoute"; 289 | } 290 | 291 | char pathCpy[50] = {0}; 292 | strncpy(pathCpy, path.c_str(), 49); 293 | char *locPtr = NULL; 294 | char *networkName = strtok_r(pathCpy, ".", &locPtr); 295 | char *thisNodeName = strtok_r(NULL, ".", &locPtr); 296 | int thisNodeId = atoi(thisNodeName + 1); 297 | 298 | // a pkName example: H0-H5-pfrp-4387-0 299 | char pkNameCpy[50] = {0}; 300 | strncpy(pkNameCpy, pkName, 49); 301 | char *locPtr_1 = NULL; 302 | char *srcName = strtok_r(pkNameCpy, "-", &locPtr_1); 303 | char *dstName = strtok_r(NULL, "-", &locPtr_1); 304 | int dstNodeId = atoi(dstName + 1); 305 | 306 | pair p; 307 | 308 | if (thisNodeName[0] == 'H') 309 | { 310 | // first hop: from host to router 311 | p.first = "R" + to_string(thisNodeId); 312 | p.second = 1; 313 | return p; 314 | } 315 | 316 | if (thisNodeId == dstNodeId) 317 | { 318 | // final hop: from router to host 319 | p.first = "H" + to_string(dstNodeId); 320 | int gid = 0; 321 | for (int i = 0; i < nodeNum; i++) 322 | { 323 | if (allProb[dstNodeId][i]) 324 | { 325 | gid++; 326 | } 327 | } 328 | // Ift has a lo0 in the first place so it should be +1, 329 | // router pointing to host is the last interface so it should be +1 again 330 | // counting from 0 so it should be -1 331 | p.second = gid + 1; 332 | return p; 333 | } 334 | 335 | int nextNode = getNextNode(thisNodeId, dstNodeId); 336 | p.first = "R" + to_string(nextNode); 337 | p.second = getGateId(thisNodeId, nextNode); 338 | countPkct(thisNodeId, nextNode, pkByte); 339 | 340 | return p; 341 | } 342 | 343 | /** 344 | * @description: show the details of routing table and network topology 345 | * @return {*} None 346 | */ 347 | void pfrpTable::showInfo() 348 | { 349 | cout << endl; 350 | cout << "allProb:" << endl; 351 | for (int ii = 0; ii < nodeNum; ii++) 352 | { 353 | for (int jj = 0; jj < nodeNum; jj++) 354 | { 355 | cout << allProb[ii][jj] << " "; 356 | } 357 | cout << endl; 358 | } 359 | 360 | cout << endl; 361 | cout << "topo:" << endl; 362 | for (int ii = 0; ii < nodeNum; ii++) 363 | { 364 | for (int jj = 0; jj < nodeNum; jj++) 365 | { 366 | cout << topo[ii][jj] << " "; 367 | } 368 | cout << endl; 369 | } 370 | 371 | cout << endl; 372 | cout << "edgeNum: " << edgeNum << endl; 373 | cout << endl; 374 | cout << "totalStep: " << totalStep << endl; 375 | cout << endl; 376 | } 377 | 378 | /** 379 | * @description: Assign the file path of the initial probabilistic routing table to routingFileName. 380 | * @param {char} *file the file path of the initial probabilistic routing table 381 | * @return {*} None 382 | */ 383 | void pfrpTable::setRoutingFileName(const char *file) 384 | { 385 | string s(file); 386 | routingFileName = s; 387 | } 388 | 389 | /** 390 | * @description: Calculate the sum of the packet sizes of all the packets sent from the source node to 391 | * the destination node that reach the destination node. 392 | * @param {int} src source node from which the packet is sent 393 | * @param {int} dst destination node of the packet 394 | * @param {int} pkByte size of the packet, unit: Byte 395 | * @return {*} None 396 | */ 397 | void pfrpTable::countPkct(int src, int dst, int pkByte) 398 | { 399 | pkct[src][dst] += pkByte; 400 | } 401 | 402 | /** 403 | * @description: Read and save variables in omnetpp.ini 404 | * @param {int} port port of ZMQ communication 405 | * @param {double} survivalTime_v survival time in omnetpp.ini 406 | * @param {int} totalStep_v total step in omnetpp.ini 407 | * @param {int} simMode_v simulation mode in omnetpp.ini 408 | * @return {*} 409 | */ 410 | void pfrpTable::setVals(int port, double survivalTime_v, int totalStep_v, int simMode_v) 411 | { 412 | zmqPort = port; 413 | survivalTime = survivalTime_v; 414 | totalStep = totalStep_v; 415 | simMode = simMode_v; 416 | } 417 | 418 | /** 419 | * @description: Clear packets from the network topology. 420 | * @return {*} None 421 | */ 422 | void pfrpTable::cleanPkctAndTps() 423 | { 424 | 425 | for (int i = 0; i < nodeNum; i++) 426 | for (int j = 0; j < nodeNum; j++) 427 | { 428 | pkct[i][j] = 0; 429 | } 430 | } 431 | 432 | /** 433 | * @description: Counts the transmission delay of all packets sent in each step. 434 | * @param {int} step step for which all packets have been sent and received 435 | * @param {double} delay delay statistics in the step 436 | * @param {double} currentTime current timestamp in omnetpp environment 437 | * @return {*} None 438 | */ 439 | void pfrpTable::setDelayWithStep(int step, double delay, double currentTime) 440 | { 441 | if (!pTable) 442 | { 443 | throw "setDelayWithStep"; 444 | } 445 | if (delayWithStep.size() < (step + 1)) 446 | { 447 | vector delayVec; 448 | delayVec.push_back(delay); 449 | delayWithStep.push_back(delayVec); 450 | } 451 | else 452 | { 453 | delayWithStep[step].push_back(delay); 454 | 455 | if (stepIsEnd[step] && (!stepFinished[step])) 456 | { 457 | if (delayWithStep[step].size() == pkNumOfStep[step]) 458 | { 459 | endStep(step); 460 | delayWithStep[step].clear(); 461 | } 462 | } 463 | 464 | for (int formerStep = 0; formerStep <= step; formerStep++) 465 | { 466 | double timePast = currentTime - stepEndTime[formerStep]; 467 | if (stepIsEnd[formerStep] && (timePast >= survivalTime) && (!stepFinished[formerStep])) 468 | { 469 | endStep(formerStep); 470 | delayWithStep[formerStep].clear(); 471 | } 472 | } 473 | } 474 | } 475 | 476 | /** 477 | * @description: Send the state of the current step to python 478 | * then update the probabilistic routing table based on the information returned by python 479 | * @param {int} step the current step that the status message to be sent 480 | * @return {*} None 481 | */ 482 | void pfrpTable::updateProb(int step) 483 | { 484 | // send state message and receive reward message 485 | if (!pTable) 486 | { 487 | throw "updateProb"; 488 | } 489 | 490 | updateProbCount[step]++; 491 | if (updateProbCount[step] == nodeNum) 492 | { 493 | // the last node to go into next update step 494 | string stateStr; 495 | stateStr += "s@@" + to_string(step) + "@@"; 496 | for (int i = 0; i < nodeNum; i++) 497 | for (int j = 0; j < nodeNum; j++) 498 | { 499 | if (i == nodeNum - 1 && j == nodeNum - 1) 500 | stateStr += to_string(double(pkct[i][j]) / 1024 / 1024); 501 | else 502 | stateStr += to_string(double(pkct[i][j]) / 1024 / 1024) + ","; 503 | } 504 | cleanPkctAndTps(); 505 | 506 | const char *reqData = stateStr.c_str(); 507 | const size_t reqLen = strlen(reqData); 508 | zmq::message_t request{reqLen}; 509 | memcpy(request.data(), reqData, reqLen); 510 | zmqSocket.send(request); 511 | 512 | zmq::message_t reply; 513 | zmqSocket.recv(&reply); 514 | char *buffer = new char[reply.size() + 1]; 515 | memset(buffer, 0, reply.size() + 1); 516 | memcpy(buffer, reply.data(), reply.size()); 517 | 518 | // single-agent DRL 519 | if (simMode == 1) 520 | { 521 | // Update for single-agent DRL 522 | // Get the link weights for twice the number of network links and assemble them into a probability matrix on the omnetpp side. 523 | char *od_prob; 524 | double *weights = new double[edgeNum * 2]; 525 | for (int i = 0; i < edgeNum * 2; i++) 526 | { 527 | if (i == 0) 528 | { 529 | od_prob = strtok(buffer, ","); 530 | } 531 | else 532 | { 533 | od_prob = strtok(NULL, ","); 534 | } 535 | weights[i] = atof(od_prob); 536 | } 537 | 538 | for (int i = 0; i < nodeNum; i++) 539 | { 540 | double totalWeight = 0.0; 541 | for (int j = 0; j < nodeNum; j++) 542 | { 543 | if (topo[i][j] >= 0) 544 | { 545 | totalWeight += weights[topo[i][j]]; 546 | } 547 | } 548 | for (int j = 0; j < nodeNum; j++) 549 | { 550 | if (topo[i][j] >= 0) 551 | { 552 | int prob = (int)(weights[topo[i][j]] / totalWeight * 100); 553 | if (prob < 1) 554 | { 555 | prob = 1; 556 | } 557 | allProb[i][j] = prob; 558 | } 559 | } 560 | } 561 | delete weights; 562 | } 563 | // multi-agent 564 | else if (simMode == 2) 565 | { 566 | // The probability matrix is calculated on the python side and passed directly to the receiver. 567 | char *newProb; 568 | for (int i = 0; i < nodeNum; i++) 569 | { 570 | for (int j = 0; j < nodeNum; j++) 571 | { 572 | if (i == 0 && j == 0) 573 | { 574 | newProb = strtok(buffer, ","); 575 | } 576 | else 577 | { 578 | newProb = strtok(NULL, ","); 579 | } 580 | allProb[i][j] = atoi(newProb); 581 | } 582 | } 583 | } 584 | sendId = 0; // reset packer ID at the start of next step 585 | showInfo(); 586 | delete buffer; 587 | } 588 | } 589 | 590 | /** 591 | * @description: Record the total number of packets sent under the current step. 592 | * @param {int} pkNum the number of packets sent under the current step 593 | * @param {int} stepNum current step to be recorded 594 | * @return {*} None 595 | */ 596 | void pfrpTable::pkNumRecord(int pkNum, int stepNum) 597 | { 598 | pkNumOfStep[stepNum] += pkNum; 599 | } 600 | 601 | /** 602 | * @description: Finish the current step, 603 | * calculate the average latency and packet loss for the network as a whole, 604 | * and send the reward to python 605 | * @param {int} step the step to be finished 606 | * @return {*} None 607 | */ 608 | void pfrpTable::endStep(int step) 609 | { 610 | if (firstTime) 611 | { 612 | firstTime = false; 613 | } 614 | else 615 | { 616 | double sum = 0.0; 617 | for (int i = 0; i < delayWithStep[step].size(); i++) 618 | { 619 | sum += delayWithStep[step][i]; 620 | } 621 | double avgDelay = sum / delayWithStep[step].size(); 622 | 623 | double lossRate = 1.0 - (double)(delayWithStep[step].size()) / (double)(pkNumOfStep[step]); 624 | string reward = to_string(avgDelay) + "," + to_string(lossRate); 625 | string reqStr = "r@@" + to_string(step) + "@@" + reward; 626 | 627 | cout << "---- reward ---- " << reqStr << endl; 628 | 629 | const char *reqData = reqStr.c_str(); 630 | const size_t reqLen = strlen(reqData); 631 | zmq::message_t request{reqLen}; 632 | memcpy(request.data(), reqData, reqLen); 633 | zmqSocket.send(request); 634 | 635 | zmq::message_t reply; 636 | zmqSocket.recv(&reply); 637 | } 638 | stepFinished[step] = true; 639 | } 640 | 641 | /** 642 | * @description: the function is called at the end of each node's step, 643 | * and the accumulator counts until all nodes have ended their step. 644 | * @param {int} step the corresponding step when the function wakes up 645 | * @param {double} endTime the end time of the current step 646 | * @return {*} None 647 | */ 648 | void pfrpTable::stepEndRecord(int step, double endTime) 649 | { 650 | if (!pTable) 651 | { 652 | throw "stepEndRecord"; 653 | } 654 | 655 | stepEndRecordCount[step]++; 656 | if (stepEndRecordCount[step] == nodeNum) 657 | { 658 | stepIsEnd[step] = true; 659 | stepEndTime[step] = endTime; 660 | } 661 | } 662 | 663 | /** 664 | * @description: Count the number of packets passing through the current node. 665 | * @param {string} path routing path for arriving packets 666 | * @param {char} *pkName name of the arriving packet 667 | * @return {*} None 668 | */ 669 | void pfrpTable::countPktInNode(string path, const char *pkName) 670 | { 671 | char pathCpy[50] = {0}; 672 | strncpy(pathCpy, path.c_str(), 49); 673 | char *locPtr = NULL; 674 | char *networkName = strtok_r(pathCpy, ".", &locPtr); 675 | char *thisNodeName = strtok_r(NULL, ".", &locPtr); 676 | 677 | // only passed routers are counted 678 | if (thisNodeName[0] == 'H') 679 | { 680 | return; 681 | } 682 | 683 | int thisNodeId = atoi(thisNodeName + 1); 684 | 685 | // an example of pkName: H0-H5-pfrp-4387-0 686 | char pkNameCpy[50] = {0}; 687 | strncpy(pkNameCpy, pkName, 49); 688 | char *locPtr_1 = NULL; 689 | char *srcName = strtok_r(pkNameCpy, "-", &locPtr_1); 690 | char *dstName = strtok_r(NULL, "-", &locPtr_1); 691 | char *ProtName = strtok_r(NULL, "-", &locPtr_1); 692 | char *pktIdName = strtok_r(NULL, "-", &locPtr_1); 693 | char *stepNumName = strtok_r(NULL, "-", &locPtr_1); 694 | 695 | int pktId = atoi(pktIdName); 696 | int stepNum = atoi(stepNumName); 697 | (*pktInNode[stepNum][thisNodeId]).insert(pktId); 698 | } 699 | 700 | /** 701 | * @description: Record the delay of the packet ID pktID in the corresponding step. 702 | * @param {int} step step of the packet to be counted 703 | * @param {int} pktId ID of the packet to be counted 704 | * @param {double} delay delay of the packet to be counted 705 | * @return {*} None 706 | */ 707 | void pfrpTable::countPktDelay(int step, int pktId, double delay) 708 | { 709 | (*pktDelay[step])[pktId] = delay; 710 | } 711 | 712 | /** 713 | * @description: Determine if the corresponding step is finished based on the current omnetpp timestamp. 714 | * @param {int} step step to be judged 715 | * @param {double} currentTime the current omnetpp timestamp 716 | * @return {*} None 717 | */ 718 | void pfrpTable::stepOverJudge(int step, double currentTime) 719 | { 720 | stepPktNum[step]++; 721 | if (stepIsEnd[step] && (!stepFinished[step])) 722 | { 723 | 724 | if (stepPktNum[step] == pkNumOfStep[step]) 725 | { 726 | endStepMulti(step); 727 | } 728 | } 729 | 730 | for (int formerStep = 0; formerStep <= step; formerStep++) 731 | { 732 | double timePast = currentTime - stepEndTime[formerStep]; 733 | if (stepIsEnd[formerStep] && (timePast >= survivalTime) && (!stepFinished[formerStep])) 734 | { 735 | endStepMulti(formerStep); 736 | } 737 | } 738 | } 739 | 740 | /** 741 | * @description: End the current step under multi-agent DRL 742 | * @param {int} step the step to be finished 743 | * @return {*} None 744 | */ 745 | void pfrpTable::endStepMulti(int step) 746 | { 747 | if (firstTime) 748 | { 749 | firstTime = false; 750 | } 751 | else 752 | { 753 | vector pktPass(nodeNum, 0); 754 | vector pktArrive(nodeNum, 0); 755 | vector> delays; 756 | for (int i = 0; i < nodeNum; i++) 757 | { 758 | vector nvec; 759 | for (int pktId : (*pktInNode[step][i])) 760 | { 761 | if ((*pktDelay[step]).count(pktId)) 762 | { 763 | nvec.push_back((*pktDelay[step])[pktId]); 764 | pktArrive[i]++; 765 | } 766 | pktPass[i]++; 767 | } 768 | 769 | delays.push_back(nvec); 770 | } 771 | 772 | string reqStr = "r@@" + to_string(step) + "@@"; 773 | for (int i = 0; i < nodeNum; i++) 774 | { 775 | double sum = 0.0; 776 | for (int j = 0; j < delays[i].size(); j++) 777 | { 778 | sum += delays[i][j]; 779 | } 780 | double avgDelay = (delays[i].size() == 0) ? 0.0 : (sum / delays[i].size()); 781 | 782 | double lossRate = 1.0 - (double)(pktArrive[i]) / (double)(pktPass[i]); 783 | 784 | reqStr += to_string(avgDelay) + "," + to_string(lossRate) + "/"; 785 | } 786 | reqStr.pop_back(); // remove final '/' 787 | 788 | cout << endl; 789 | cout << " -------- reward -------- " << endl; 790 | cout << endl 791 | << endl 792 | << reqStr << endl 793 | << endl; 794 | 795 | for (int i = 0; i < nodeNum; i++) 796 | { 797 | pktInNode[step][i]->clear(); 798 | } 799 | pktDelay[step]->clear(); 800 | 801 | const char *reqData = reqStr.c_str(); 802 | const size_t reqLen = strlen(reqData); 803 | zmq::message_t request{reqLen}; 804 | memcpy(request.data(), reqData, reqLen); 805 | zmqSocket.send(request); 806 | 807 | zmq::message_t reply; 808 | zmqSocket.recv(&reply); 809 | } 810 | stepFinished[step] = true; 811 | } 812 | 813 | /** 814 | * @description: Get the id of the sending gate. 815 | * @return {*} None 816 | */ 817 | int pfrpTable::getSendId() 818 | { 819 | return sendId++; 820 | } 821 | -------------------------------------------------------------------------------- /modules/inet/ipv4/pfrpTable.h: -------------------------------------------------------------------------------- 1 | /*** 2 | * @Author : CHEN Jiawei 3 | * @Date : 2023-10-18 20:28:25 4 | * @LastEditors : LIN Guocheng 5 | * @LastEditTime : 2023-10-19 15:21:29 6 | * @FilePath : /home/lgc/test/RL4Net++/modules/ipv4/pfrpTable.h 7 | * @Description : This module stores the forwarding probability of the whole network, 8 | * and also acts as a statistics module for the whole network, 9 | * exchanging data with the py side via zmq communication. 10 | * Currently there is no way to use it as a local variable, 11 | * only a globally unique static object. Multi-intelligence environment 12 | * can be extended to one object per node. 13 | */ 14 | 15 | #include "string.h" 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | using namespace std; 28 | using namespace omnetpp; 29 | 30 | class pfrpTable 31 | { 32 | public: 33 | // Get unique static instance of probabilistic routing table. 34 | static pfrpTable *getInstance(); 35 | 36 | // Initialize probabilistic routing table. 37 | static pfrpTable *initTable(int num, const char *file, int port, double survivalTime_v, int totalStep_v, int simMode_v); 38 | 39 | public: 40 | // Initialize the intermediate variables needed to complete the probabilistic routing. 41 | void initiate(); 42 | 43 | // Get next hop and the ID of the output gate forwarded by the current node to the next node. 44 | pair getRoute(string path, const char *pkName, int pkByte); 45 | 46 | // Counts the transmission delay of all packets sent in each step. 47 | void setDelayWithStep(int step, double delay, double currentTime); 48 | 49 | /** 50 | * Finish the current step, calculate the average latency and packet loss for the network as a whole, 51 | * and send the reward to python. 52 | */ 53 | void endStep(int step); 54 | 55 | /** 56 | * Send the state of the current step to python then update the probabilistic routing table 57 | * based on the information returned by python 58 | */ 59 | void updateProb(int step); 60 | 61 | // Record the total number of packets sent under the current step. 62 | void pkNumRecord(int pkNum, int stepNum); 63 | 64 | /** 65 | * The function is called at the end of each node's step, 66 | * and the accumulator counts until all nodes have ended their step. 67 | */ 68 | void stepEndRecord(int step, double endTime); 69 | 70 | // Assign the number of nodes in the network topology to nodeNum. 71 | void setNodeNum(int num); 72 | 73 | // Get the number of nodes in the network topology from nodeNum. 74 | int getNodeNum(); 75 | 76 | // Select next hop for current packet based on probabilistic routing table. 77 | int getNextNode(int nodeId, int dstNode); 78 | 79 | // Convert the initial probabilistic routing table into a probability matrix. 80 | int **getProb(string fileName, int **Prob); 81 | 82 | // Get the ID of the output gate forwarded by the current node to the next node. 83 | int getGateId(int nodeId, int nextNode); 84 | 85 | // show the details of routing table and network topology 86 | void showInfo(); 87 | 88 | // Assign the file path of the initial probabilistic routing table to routingFileName. 89 | void setRoutingFileName(const char *file); 90 | 91 | /** 92 | * Calculate the sum of the packet sizes of all the packets sent from the source node 93 | * to the destination node that reach the destination node. 94 | */ 95 | void countPkct(int src, int dst, int pkByte); 96 | 97 | // Read and save variables in omnetpp.ini 98 | void setVals(int port, double survivalTime_v, int totalStep_v, int simMode_v); 99 | 100 | // Clear packets from the network topology. 101 | void cleanPkctAndTps(); 102 | 103 | // Count the number of packets passing through the current node. 104 | void countPktInNode(string path, const char *pkName); 105 | 106 | // Record the delay of the packet ID pktID in the corresponding step. 107 | void countPktDelay(int step, int pktId, double delay); 108 | 109 | // Determine if the corresponding step is finished based on the current omnetpp timestamp. 110 | void stepOverJudge(int step, double currentTime); 111 | 112 | // End the current step under multi-agent DRL 113 | void endStepMulti(int step); 114 | 115 | // Get the id of the sending gate. 116 | int getSendId(); 117 | 118 | private: 119 | static pfrpTable *pTable; 120 | 121 | private: 122 | pfrpTable(); 123 | virtual ~pfrpTable(); 124 | int **allProb; // Stores the forwarding probability between all nodes, which is updated every step. 125 | /** 126 | * Store the network topology, denoted as -1 between nodes with no links, 127 | * and as the corresponding link number between nodes with links present 128 | */ 129 | int **topo; 130 | int nodeNum = 0; 131 | string routingFileName; // Name of the file used to initialize the forwarding probability matrix. 132 | int **pkct = NULL; // Counts the amount of traffic that passes through each link in one step of time, in bytes. 133 | bool firstTime = true; // Used to discard the data of the zeroth step. 134 | int edgeNum = 0; 135 | vector> delayWithStep; 136 | int *pkNumOfStep; 137 | bool *stepIsEnd; 138 | double *stepEndTime; 139 | bool *stepFinished; 140 | int *updateProbCount; 141 | int *stepEndRecordCount; 142 | double survivalTime; 143 | int totalStep = 0; 144 | int *ttlDrop; 145 | int *stDrop; 146 | 147 | vector *>> pktInNode; 148 | vector *> pktDelay; 149 | 150 | int *stepPktNum; // This is used to store how many pakcets each step receives. 151 | int sendId = 0; // ID used to identify the package, each package has a unique ID 152 | int simMode; 153 | int zmqPort; 154 | zmq::context_t zmqContext{1}; 155 | zmq::socket_t zmqSocket{zmqContext, ZMQ_REQ}; 156 | }; -------------------------------------------------------------------------------- /modules/inet/udpapp/UdpPfrpApp.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2000 Institut fuer Telematik, Universitaet Karlsruhe 3 | // Copyright (C) 2004,2011 Andras Varga 4 | // 5 | // This program is free software; you can redistribute it and/or 6 | // modify it under the terms of the GNU Lesser General Public License 7 | // as published by the Free Software Foundation; either version 2 8 | // of the License, or (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU Lesser General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU Lesser General Public License 16 | // along with this program; if not, see . 17 | // 18 | 19 | #include "inet/applications/udpapp/UdpPfrpApp.h" 20 | #include "inet/applications/base/ApplicationPacket_m.h" 21 | #include "inet/common/ModuleAccess.h" 22 | #include "inet/common/TagBase_m.h" 23 | #include "inet/common/TimeTag_m.h" 24 | #include "inet/common/lifecycle/ModuleOperations.h" 25 | #include "inet/common/packet/Packet.h" 26 | #include "inet/networklayer/common/FragmentationTag_m.h" 27 | #include "inet/networklayer/common/L3AddressResolver.h" 28 | #include "inet/networklayer/ipv4/pfrpTable.h" 29 | #include "inet/transportlayer/contract/udp/UdpControlInfo_m.h" 30 | #include "unistd.h" 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | using namespace std; 37 | using namespace omnetpp; 38 | namespace inet 39 | { 40 | 41 | Define_Module(UdpPfrpApp); 42 | 43 | UdpPfrpApp::~UdpPfrpApp() 44 | { 45 | cancelAndDelete(selfMsg); 46 | } 47 | 48 | void UdpPfrpApp::initialize(int stage) 49 | { 50 | ApplicationBase::initialize(stage); 51 | 52 | if (stage == INITSTAGE_LOCAL) 53 | { 54 | numSent = 0; 55 | numReceived = 0; 56 | WATCH(numSent); 57 | WATCH(numReceived); 58 | 59 | nodeNum = par("nodeNum"); 60 | stepTime = par("stepTime"); 61 | zmqPort = par("zmqPort"); 62 | routingFileName = par("routingFileName"); 63 | messageLength = par("messageLength"); 64 | flowRate = par("flowRate"); 65 | sendInterval = 1 / (flowRate * 1024 * 1024 / 8 / messageLength); 66 | survivalTime = par("survivalTime"); 67 | totalStep = par("totalStep"); 68 | simMode = par("simMode"); 69 | if (simMode == 0) 70 | { 71 | routingProtocol = "other"; 72 | } 73 | else if (simMode == 1) 74 | { 75 | routingProtocol = "pfrpsa"; 76 | } 77 | else if (simMode == 2) 78 | { 79 | routingProtocol = "pfrpma"; 80 | } 81 | 82 | pfrpTable::initTable(nodeNum, routingFileName, zmqPort, survivalTime, totalStep, simMode); 83 | 84 | localPort = par("localPort"); 85 | destPort = par("destPort"); 86 | startTime = par("startTime"); 87 | stopTime = par("stopTime"); 88 | dontFragment = par("dontFragment"); 89 | if (stopTime >= SIMTIME_ZERO && stopTime < startTime) 90 | throw cRuntimeError("Invalid startTime/stopTime parameters"); 91 | selfMsg = new cMessage("sendTimer"); 92 | randDst = getDstNode(); 93 | } 94 | } 95 | 96 | /** 97 | * @description: Randomly select a node that is not connected to the source node as the destination node. 98 | * @return {int} ID of destination node 99 | */ 100 | int UdpPfrpApp::getDstNode() 101 | { 102 | // get the current time in microseconds 103 | auto now = std::chrono::system_clock::now(); 104 | auto now_us = std::chrono::time_point_cast(now); 105 | auto value = now_us.time_since_epoch(); 106 | long micros = value.count(); 107 | // using microseconds as random number seeds 108 | std::mt19937 gen(micros); 109 | // get source node 110 | string sender = getParentModule()->getFullName(); 111 | int senderNode = atoi(sender.c_str() + 1); 112 | int dst = senderNode; 113 | while (dst == senderNode) 114 | { 115 | std::uniform_int_distribution dist(0, nodeNum - 1); 116 | dst = dist(gen); 117 | } 118 | return dst; 119 | } 120 | 121 | void UdpPfrpApp::finish() 122 | { 123 | ApplicationBase::finish(); 124 | } 125 | 126 | void UdpPfrpApp::setSocketOptions() 127 | { 128 | int timeToLive = par("timeToLive"); 129 | if (timeToLive != -1) 130 | socket.setTimeToLive(timeToLive); 131 | 132 | int dscp = par("dscp"); 133 | if (dscp != -1) 134 | socket.setDscp(dscp); 135 | 136 | int tos = par("tos"); 137 | if (tos != -1) 138 | socket.setTos(tos); 139 | 140 | const char *multicastInterface = par("multicastInterface"); 141 | if (multicastInterface[0]) 142 | { 143 | IInterfaceTable *ift = getModuleFromPar(par("interfaceTableModule"), this); 144 | InterfaceEntry *ie = ift->findInterfaceByName(multicastInterface); 145 | if (!ie) 146 | throw cRuntimeError("Wrong multicastInterface setting: no interface named \"%s\"", multicastInterface); 147 | socket.setMulticastOutputInterface(ie->getInterfaceId()); 148 | } 149 | 150 | bool receiveBroadcast = par("receiveBroadcast"); 151 | if (receiveBroadcast) 152 | socket.setBroadcast(true); 153 | 154 | bool joinLocalMulticastGroups = par("joinLocalMulticastGroups"); 155 | if (joinLocalMulticastGroups) 156 | { 157 | MulticastGroupList mgl = getModuleFromPar(par("interfaceTableModule"), this)->collectMulticastGroups(); 158 | socket.joinLocalMulticastGroups(mgl); 159 | } 160 | socket.setCallback(this); 161 | } 162 | 163 | void UdpPfrpApp::sendPacket() 164 | { 165 | string sender = getParentModule()->getFullName(); // current node(source node) 166 | int senderNode = atoi(sender.c_str() + 1); 167 | 168 | string destName = "H" + to_string(randDst); 169 | // The name of the packet, in the format of 170 | // "{source node}-{host node}-{routing protocol}-{packet number}-{current step}", such as H0-H5-pfrp-4387-0 171 | int sendId = pfrpTable::getInstance()->getSendId(); 172 | string pkName = sender + "-" + destName + "-" + routingProtocol + "-" + to_string(sendId) + "-" + to_string(stepNum); 173 | Packet *packet = new Packet(pkName.c_str()); 174 | sendPacketId++; 175 | 176 | if (dontFragment) 177 | packet->addTag()->setDontFragment(true); 178 | const auto &payload = makeShared(); 179 | 180 | payload->setChunkLength(B((int)messageLength)); 181 | payload->setSequenceNumber(numSent); 182 | payload->addTag()->setCreationTime(simTime()); 183 | packet->insertAtBack(payload); 184 | 185 | L3Address destAddr; 186 | const char *dn = destName.c_str(); 187 | cStringTokenizer tokenizer(dn); 188 | const char *token = tokenizer.nextToken(); 189 | L3AddressResolver().tryResolve(token, destAddr); 190 | emit(packetSentSignal, packet); 191 | 192 | socket.sendTo(packet, destAddr, destPort); // send packet 193 | numSent++; 194 | } 195 | 196 | void UdpPfrpApp::processStart() 197 | { 198 | socket.setOutputGate(gate("socketOut")); 199 | const char *localAddress = par("localAddress"); 200 | socket.bind(*localAddress ? L3AddressResolver().resolve(localAddress) : L3Address(), localPort); 201 | setSocketOptions(); 202 | 203 | selfMsg->setKind(SEND); 204 | processSend(); 205 | } 206 | 207 | void UdpPfrpApp::processSend() 208 | { 209 | // No more packets are sent when the step count is exceeded 210 | if (stepNum <= totalStep) 211 | { 212 | if (timerStep == 0) 213 | { 214 | timerStep = simTime(); 215 | } 216 | simtime_t timeC = simTime() - timerStep; 217 | if (timeC > stepTime) 218 | { 219 | cout << "-------- step " << stepNum << " Node " << getParentModule()->getFullName() << " --------" << endl; 220 | pfrpTable::getInstance()->pkNumRecord(sendPacketId, stepNum); 221 | pfrpTable::getInstance()->stepEndRecord(stepNum, simTime().dbl()); 222 | 223 | if (simMode == 0) 224 | { 225 | // Traditional algorithms, do not need to update the probabilistic routing table, 226 | // only need to clean up the remnants of state statistics 227 | pfrpTable::getInstance()->cleanPkctAndTps(); 228 | } 229 | else if (simMode == 1 || simMode == 2) 230 | { 231 | // DRL algorithms, need to update forwarding probability 232 | pfrpTable::getInstance()->updateProb(stepNum); 233 | } 234 | 235 | stepNum++; 236 | sendPacketId = 0; 237 | timerStep = simTime(); 238 | } 239 | sendPacket(); 240 | 241 | cMersenneTwister *mt; 242 | // Traffic generation method: limit upper and lower bounds, evenly distributed 243 | cUniform un = cUniform(mt, 0.9 * sendInterval, 1.1 * sendInterval); 244 | double interval = un.draw(); 245 | simtime_t d = simTime() + interval; 246 | if (stopTime < SIMTIME_ZERO || d < stopTime) 247 | { 248 | selfMsg->setKind(SEND); 249 | scheduleAt(d, selfMsg); 250 | } 251 | else 252 | { 253 | selfMsg->setKind(STOP); 254 | scheduleAt(stopTime, selfMsg); 255 | } 256 | } 257 | } 258 | 259 | void UdpPfrpApp::processStop() 260 | { 261 | socket.close(); 262 | } 263 | 264 | void UdpPfrpApp::handleMessageWhenUp(cMessage *msg) 265 | { 266 | if (msg->isSelfMessage()) 267 | { 268 | ASSERT(msg == selfMsg); 269 | switch (selfMsg->getKind()) 270 | { 271 | case START: 272 | processStart(); 273 | break; 274 | 275 | case SEND: 276 | processSend(); 277 | break; 278 | 279 | case STOP: 280 | processStop(); 281 | break; 282 | 283 | default: 284 | throw cRuntimeError("Invalid kind %d in self message", (int)selfMsg->getKind()); 285 | } 286 | } 287 | else 288 | { 289 | if (strstr(msg->getFullName(), routingProtocol.c_str())) 290 | { 291 | char msgName[50] = {0}; 292 | strncpy(msgName, msg->getFullName(), 49); 293 | 294 | char *locPtr = NULL; 295 | char *srcNode = strtok_r(msgName, "-", &locPtr); 296 | char *dstNode = strtok_r(NULL, "-", &locPtr); 297 | char *thisNode = (char *)getParentModule()->getName(); 298 | 299 | string thisN = thisNode; 300 | string dstN = dstNode; 301 | if (thisN == dstN) 302 | { 303 | socket.processMessage(msg); 304 | } 305 | } 306 | } 307 | } 308 | 309 | void UdpPfrpApp::socketDataArrived(UdpSocket *socket, Packet *packet) 310 | { 311 | // process incoming packet 312 | processPacket(packet); 313 | } 314 | 315 | void UdpPfrpApp::socketErrorArrived(UdpSocket *socket, Indication *indication) 316 | { 317 | EV_WARN << "Ignoring UDP error report " << indication->getName() << endl; 318 | 319 | delete indication; 320 | } 321 | 322 | void UdpPfrpApp::socketClosed(UdpSocket *socket) 323 | { 324 | if (operationalState == State::STOPPING_OPERATION) 325 | startActiveOperationExtraTimeOrFinish(par("stopOperationExtraTime")); 326 | } 327 | 328 | void UdpPfrpApp::refreshDisplay() const 329 | { 330 | ApplicationBase::refreshDisplay(); 331 | 332 | char buf[100]; 333 | sprintf(buf, "rcvd: %d pks\nsent: %d pks", numReceived, numSent); 334 | getDisplayString().setTagArg("t", 0, buf); 335 | } 336 | 337 | void UdpPfrpApp::processPacket(Packet *pk) 338 | { 339 | emit(packetReceivedSignal, pk); 340 | 341 | char pkName[50] = {0}; 342 | strncpy(pkName, pk->getFullName(), 49); 343 | 344 | char *locPtr = NULL; 345 | char *pksrc = strtok_r(pkName, "-", &locPtr) + 1; 346 | char *pkdst = strtok_r(NULL, "-", &locPtr) + 1; 347 | char *pfrpStr = strtok_r(NULL, "-", &locPtr); 348 | char *pktIdName = strtok_r(NULL, "-", &locPtr); 349 | char *stepNumStr = strtok_r(NULL, "-", &locPtr); 350 | int pktId = atoi(pktIdName); 351 | int pkStep = atoi(stepNumStr); 352 | 353 | // multi-agent DRL 354 | if (simMode == 2) 355 | { 356 | pfrpTable::getInstance()->countPktDelay(pkStep, pktId, (simTime() - pk->getCreationTime()).dbl()); 357 | pfrpTable::getInstance()->stepOverJudge(pkStep, simTime().dbl()); 358 | } 359 | else 360 | { 361 | pfrpTable::getInstance()->setDelayWithStep(pkStep, (simTime() - pk->getCreationTime()).dbl(), simTime().dbl()); 362 | } 363 | numReceived++; 364 | delete pk; 365 | } 366 | void UdpPfrpApp::handleStartOperation(LifecycleOperation *operation) 367 | { 368 | simtime_t start = std::max(startTime, simTime()); 369 | if ((stopTime < SIMTIME_ZERO) || (start < stopTime) || (start == stopTime && startTime == stopTime)) 370 | { 371 | selfMsg->setKind(START); 372 | scheduleAt(start, selfMsg); 373 | } 374 | } 375 | 376 | void UdpPfrpApp::handleStopOperation(LifecycleOperation *operation) 377 | { 378 | cancelEvent(selfMsg); 379 | socket.close(); 380 | delayActiveOperationFinish(par("stopOperationTimeout")); 381 | } 382 | 383 | void UdpPfrpApp::handleCrashOperation(LifecycleOperation *operation) 384 | { 385 | cancelEvent(selfMsg); 386 | socket.destroy(); // TODO in real operating systems, program crash detected by OS and OS closes sockets of crashed programs. 387 | } 388 | 389 | } // namespace inet 390 | -------------------------------------------------------------------------------- /modules/inet/udpapp/UdpPfrpApp.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2000 Institut fuer Telematik, Universitaet Karlsruhe 3 | // Copyright (C) 2004,2011 Andras Varga 4 | // 5 | // This program is free software; you can redistribute it and/or 6 | // modify it under the terms of the GNU Lesser General Public License 7 | // as published by the Free Software Foundation; either version 2 8 | // of the License, or (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU Lesser General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU Lesser General Public License 16 | // along with this program; if not, see . 17 | // 18 | 19 | #ifndef __INET_UDPPFRPAPP_H 20 | #define __INET_UDPPFRPAPP_H 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "inet/common/INETDefs.h" 33 | #include 34 | 35 | #include "inet/applications/base/ApplicationBase.h" 36 | #include "inet/transportlayer/contract/udp/UdpSocket.h" 37 | 38 | using namespace std; 39 | 40 | namespace inet { 41 | 42 | /** 43 | * UDP application. See NED for more info. 44 | */ 45 | class INET_API UdpPfrpApp : public ApplicationBase, public UdpSocket::ICallback { 46 | protected: 47 | enum SelfMsgKinds { START = 1, 48 | SEND, 49 | STOP }; 50 | 51 | // parameters 52 | std::vector destAddressStr; 53 | int localPort = -1, destPort = -1; 54 | simtime_t startTime; 55 | simtime_t stopTime; 56 | bool dontFragment = false; 57 | const char *packetName = nullptr; 58 | 59 | int nodeNum; 60 | int sendPacketId = 0; 61 | const char *routingFileName; 62 | string routingProtocol; 63 | double dv; 64 | double tpv; 65 | double nuv; 66 | int stepTime; 67 | int zmqPort; 68 | int messageLength; 69 | double flowRate; 70 | double sendInterval; 71 | int ttl; 72 | int totalStep; 73 | double survivalTime; 74 | int simMode; 75 | int fixedDst; 76 | 77 | UdpSocket socket; 78 | cMessage *selfMsg = nullptr; 79 | 80 | // statistics 81 | int numSent = 0; 82 | int numReceived = 0; 83 | 84 | protected: 85 | virtual int numInitStages() const override { return NUM_INIT_STAGES; } 86 | virtual void initialize(int stage) override; 87 | virtual void handleMessageWhenUp(cMessage *msg) override; 88 | virtual void finish() override; 89 | virtual void refreshDisplay() const override; 90 | 91 | // chooses random destination address 92 | virtual void sendPacket(); 93 | virtual void processPacket(Packet *msg); 94 | virtual void setSocketOptions(); 95 | 96 | virtual void processStart(); 97 | virtual void processSend(); 98 | virtual void processStop(); 99 | 100 | virtual void handleStartOperation(LifecycleOperation *operation) override; 101 | virtual void handleStopOperation(LifecycleOperation *operation) override; 102 | virtual void handleCrashOperation(LifecycleOperation *operation) override; 103 | 104 | virtual void socketDataArrived(UdpSocket *socket, Packet *packet) override; 105 | virtual void socketErrorArrived(UdpSocket *socket, Indication *indication) override; 106 | virtual void socketClosed(UdpSocket *socket) override; 107 | 108 | int getDstNode(); 109 | 110 | public: 111 | UdpPfrpApp() {} 112 | ~UdpPfrpApp(); 113 | simtime_t oTime = 0; 114 | simtime_t timerStep = 0; 115 | int stepNum = 0; 116 | }; 117 | 118 | } // namespace inet 119 | 120 | #endif // ifndef __INET_UDPPFRPAPP_H 121 | -------------------------------------------------------------------------------- /modules/inet/udpapp/UdpPfrpApp.ned: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2004 Andras Varga 3 | // Copyright (C) 2000 Institut fuer Telematik, Universitaet Karlsruhe 4 | // 5 | // This program is free software; you can redistribute it and/or 6 | // modify it under the terms of the GNU Lesser General Public License 7 | // as published by the Free Software Foundation; either version 2 8 | // of the License, or (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU Lesser General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU Lesser General Public License 16 | // along with this program; if not, see . 17 | // 18 | 19 | package inet.applications.udpapp; 20 | 21 | import inet.applications.contract.IApp; 22 | 23 | // 24 | // Sends UDP packets to the given IP address at the given interval. 25 | // Compatible with both ~Ipv4 and ~Ipv6. 26 | // 27 | // The sending interval can be a constant or a random value (e.g. exponential(1)). 28 | // If the destAddresses parameter contains more than one address, one 29 | // of them is randomly chosen for each packet. An address may be given in the 30 | // dotted decimal notation, or with the module name. (The L3AddressResolver 31 | // class is used to resolve the address.) To disable the model, set 32 | // destAddresses to "". 33 | // 34 | // Received packets are discarded. 35 | // 36 | // The peer can be a ~UdpSink, another ~UdpBasicApp (it handles received packets 37 | // like ~UdpSink), or a ~UdpEchoApp. When used with ~UdpEchoApp, the rcvdPkLifetime 38 | // statistic will contain the round-trip times. 39 | // 40 | simple UdpPfrpApp like IApp 41 | { 42 | parameters: 43 | int nodeNum; 44 | //int stopPacketNum; 45 | int stepTime; 46 | int zmqPort; 47 | int totalStep; 48 | double survivalTime; 49 | string routingFileName; 50 | string interfaceTableModule; // The path to the InterfaceTable module 51 | int simMode; 52 | int localPort = default(-1); // local port (-1: use ephemeral port) 53 | string localAddress = default(""); 54 | int destPort; 55 | double flowRate; // unit Mbits/s 56 | int messageLength; // unit bytes 57 | double startTime @unit(s) = default(this.sendInterval); // application start time (start of the first packet) 58 | double stopTime @unit(s) = default(-1s); // time of finishing sending, -1s means forever 59 | int timeToLive = default(-1); // if not -1, set the TTL (IPv4) or Hop Limit (IPv6) field of sent packets to this value 60 | bool dontFragment = default(false); // if true, asks IP to not fragment the message during routing 61 | int dscp = default(-1); // if not -1, set the DSCP field (on IPv4/IPv6) of sent packets to this value 62 | int tos = default(-1); // if not -1, set the Type Of Service (IPv4) / Traffic Class (IPv6) field of sent packets to this value 63 | string multicastInterface = default(""); // if not empty, set the multicast output interface option on the socket (interface name expected) 64 | bool receiveBroadcast = default(false); // if true, makes the socket receive broadcast packets 65 | bool joinLocalMulticastGroups = default(false); // if true, makes the socket receive packets from all multicast groups set on local interfaces 66 | @display("i=block/app"); 67 | @lifecycleSupport; 68 | double stopOperationExtraTime @unit(s) = default(-1s); // extra time after lifecycle stop operation finished 69 | double stopOperationTimeout @unit(s) = default(2s); // timeout value for lifecycle stop operation 70 | @signal[packetSent](type=inet::Packet); 71 | @signal[packetReceived](type=inet::Packet); 72 | @statistic[packetReceived](title="packets received"; source=packetReceived; record=count,"sum(packetBytes)","vector(packetBytes)"; interpolationmode=none); 73 | @statistic[throughput](title="throughput"; unit=bps; source="throughput(packetReceived)"; record=vector); 74 | @statistic[packetSent](title="packets sent"; source=packetSent; record=count,"sum(packetBytes)","vector(packetBytes)"; interpolationmode=none); 75 | @statistic[rcvdPkLifetime](title="received packet lifetime"; source="dataAge(packetReceived)"; unit=s; record=stats,vector; interpolationmode=none); 76 | @statistic[rcvdPkSeqNo](title="received packet sequence number"; source="appPkSeqNo(packetReceived)"; record=vector; interpolationmode=none); 77 | gates: 78 | input socketIn @labels(UdpControlInfo/up); 79 | output socketOut @labels(UdpControlInfo/down); 80 | } 81 | 82 | -------------------------------------------------------------------------------- /modules/omnetpp/cdataratechannel.cc: -------------------------------------------------------------------------------- 1 | //======================================================================== 2 | // CDATARATECHANNEL.CC - part of 3 | // 4 | // OMNeT++/OMNEST 5 | // Discrete System Simulation in C++ 6 | // 7 | //======================================================================== 8 | 9 | /*--------------------------------------------------------------* 10 | Copyright (C) 1992-2017 Andras Varga 11 | Copyright (C) 2006-2017 OpenSim Ltd. 12 | 13 | This file is distributed WITHOUT ANY WARRANTY. See the file 14 | `license' for details on this and other legal matters. 15 | *--------------------------------------------------------------*/ 16 | 17 | #include "omnetpp/cdataratechannel.h" 18 | #include "omnetpp/cenvir.h" 19 | #include "omnetpp/cexception.h" 20 | #include "omnetpp/cgate.h" 21 | #include "omnetpp/cmodule.h" 22 | #include "omnetpp/cpacket.h" 23 | #include "omnetpp/csimulation.h" 24 | #include "omnetpp/ctimestampedvalue.h" 25 | #include "omnetpp/distrib.h" 26 | #include "omnetpp/globals.h" 27 | 28 | #ifdef WITH_PARSIM 29 | #include "omnetpp/ccommbuffer.h" 30 | #endif 31 | 32 | namespace omnetpp { 33 | 34 | using std::ostream; 35 | 36 | Register_Class(cDatarateChannel); 37 | 38 | simsignal_t cDatarateChannel::channelBusySignal; 39 | simsignal_t cDatarateChannel::messageSentSignal; 40 | simsignal_t cDatarateChannel::messageDiscardedSignal; 41 | 42 | cDatarateChannel::cDatarateChannel(const char *name) : cChannel(name) { 43 | txFinishTime = -1; 44 | delay = 0; 45 | datarate = 0; 46 | ber = 0; 47 | per = 0; 48 | } 49 | 50 | cDatarateChannel::~cDatarateChannel() { 51 | } 52 | 53 | void cDatarateChannel::finish() { 54 | if (txFinishTime != -1 && mayHaveListeners(channelBusySignal)) { 55 | cTimestampedValue tmp(txFinishTime, 0L); 56 | emit(channelBusySignal, &tmp); 57 | } 58 | } 59 | 60 | cDatarateChannel *cDatarateChannel::create(const char *name) { 61 | return dynamic_cast(cChannelType::getDatarateChannelType()->create(name)); 62 | } 63 | 64 | std::string cDatarateChannel::str() const { 65 | return cChannel::str(); 66 | } 67 | 68 | void cDatarateChannel::initialize() { 69 | channelBusySignal = registerSignal("channelBusy"); 70 | messageSentSignal = registerSignal("messageSent"); 71 | messageDiscardedSignal = registerSignal("messageDiscarded"); 72 | 73 | emit(channelBusySignal, 0); 74 | } 75 | 76 | void cDatarateChannel::rereadPars() { 77 | delay = par("delay"); 78 | datarate = par("datarate"); 79 | ber = par("ber"); 80 | per = par("per"); 81 | 82 | if (delay < SIMTIME_ZERO) 83 | throw cRuntimeError(this, "Negative delay %s", SIMTIME_STR(delay)); 84 | if (datarate < 0) 85 | throw cRuntimeError(this, "Negative datarate %g", datarate); 86 | if (ber < 0 || ber > 1) 87 | throw cRuntimeError(this, "Wrong bit error rate %g", ber); 88 | if (per < 0 || per > 1) 89 | throw cRuntimeError(this, "Wrong packet error rate %g", per); 90 | 91 | setFlag(FL_ISDISABLED, par("disabled")); 92 | setFlag(FL_DELAY_NONZERO, delay != SIMTIME_ZERO); 93 | setFlag(FL_DATARATE_NONZERO, datarate != 0); 94 | setFlag(FL_BER_NONZERO, ber != 0); 95 | setFlag(FL_PER_NONZERO, per != 0); 96 | } 97 | 98 | void cDatarateChannel::handleParameterChange(const char *) { 99 | rereadPars(); 100 | } 101 | 102 | void cDatarateChannel::setDelay(double d) { 103 | par("delay").setDoubleValue(d); 104 | } 105 | 106 | void cDatarateChannel::setDatarate(double d) { 107 | par("datarate").setDoubleValue(d); 108 | } 109 | 110 | void cDatarateChannel::setBitErrorRate(double d) { 111 | par("ber").setDoubleValue(d); 112 | } 113 | 114 | void cDatarateChannel::setPacketErrorRate(double d) { 115 | par("per").setDoubleValue(d); 116 | } 117 | 118 | void cDatarateChannel::setDisabled(bool d) { 119 | par("disabled").setBoolValue(d); 120 | } 121 | 122 | simtime_t cDatarateChannel::calculateDuration(cMessage *msg) const { 123 | if ((flags & FL_DATARATE_NONZERO) && msg->isPacket()) 124 | return ((cPacket *)msg)->getBitLength() / datarate; 125 | else 126 | return SIMTIME_ZERO; 127 | } 128 | 129 | void cDatarateChannel::processMessage(cMessage *msg, simtime_t t, result_t &result) { 130 | // if channel is disabled, signal that message should be deleted 131 | if (flags & FL_ISDISABLED) { 132 | result.discard = false; 133 | cTimestampedValue tmp(t, msg); 134 | emit(messageDiscardedSignal, &tmp); 135 | return; 136 | } 137 | 138 | if (txFinishTime != -1 && mayHaveListeners(channelBusySignal)) { 139 | cTimestampedValue tmp(txFinishTime, 0L); 140 | emit(channelBusySignal, &tmp); 141 | } 142 | 143 | // datarate modeling 144 | if ((flags & FL_DATARATE_NONZERO) && msg->isPacket()) { 145 | cPacket *pkt = (cPacket *)msg; 146 | simtime_t duration = pkt->getBitLength() / datarate; 147 | result.duration = duration; 148 | txFinishTime = t + duration; 149 | } else { 150 | txFinishTime = t; 151 | } 152 | 153 | // propagation delay modeling 154 | result.delay = delay; 155 | 156 | // bit error modeling 157 | if ((flags & (FL_BER_NONZERO | FL_PER_NONZERO)) && msg->isPacket()) { 158 | cPacket *pkt = (cPacket *)msg; 159 | if (flags & FL_BER_NONZERO) 160 | if (dblrand() < 1.0 - pow(1.0 - ber, (double)pkt->getBitLength())) 161 | pkt->setBitError(true); 162 | 163 | if (flags & FL_PER_NONZERO) 164 | if (dblrand() < per) 165 | pkt->setBitError(true); 166 | } 167 | 168 | // emit signals 169 | if (mayHaveListeners(messageSentSignal)) { 170 | MessageSentSignalValue tmp(t, msg, &result); 171 | emit(messageSentSignal, &tmp); 172 | } 173 | if (mayHaveListeners(channelBusySignal)) { 174 | cTimestampedValue tmp(t, 1L); 175 | emit(channelBusySignal, &tmp); 176 | } 177 | } 178 | 179 | void cDatarateChannel::forceTransmissionFinishTime(simtime_t t) { 180 | // TODO record this into the eventlog so that it can be visualized in the sequence chart 181 | txFinishTime = t; 182 | } 183 | 184 | } // namespace omnetpp 185 | -------------------------------------------------------------------------------- /utils/get_ned.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author : CHEN Jiawei 3 | Date : 2023-10-19 09:51:55 4 | LastEditors : LIN Guocheng 5 | LastEditTime : 2023-10-19 16:19:34 6 | FilePath : /home/lgc/test/RL4Net++/utils/get_ned.py 7 | Description : Convert a gml file to a network topology file for OMNeT++ simulation based on the URL of the gml file. 8 | """ 9 | 10 | import requests 11 | import re 12 | 13 | 14 | def get_omnetpp_ned(gml_url: str) -> None: 15 | """Reads gml file information from a given URL. 16 | 17 | Args: 18 | gml_url (str): URL of the gml file 19 | """ 20 | if not gml_url: 21 | print("\033[0;31m Error! gml_url is None! \033[0m") 22 | return 23 | topo_name = re.search(r"\/(\w+)\.gml", gml_url)[1] 24 | gml_info = requests.get(gml_url).content.decode() 25 | node_info_strs = re.findall(r"node \[.*?\]", gml_info, re.S) 26 | node_num = len(node_info_strs) 27 | edge_info_str = re.findall(r"edge \[.*?\]", gml_info, re.S) 28 | edge_info = [ 29 | { 30 | value[0]: value[1] 31 | for value in re.findall(r"\s+([^\s]{2,}?)\s+([^\[\]]+?)\n", e) 32 | } 33 | for e in edge_info_str 34 | ] 35 | edges = [] 36 | bothway_edges = [] 37 | for info in edge_info: 38 | edges.append((int(info["source"]), int(info["target"]))) 39 | bothway_edges.extend( 40 | ( 41 | (int(info["source"]), int(info["target"])), 42 | (int(info["target"]), int(info["source"])), 43 | ) 44 | ) 45 | edges = sorted(edges, key=lambda x: (x[0], x[1])) 46 | bothway_edges = sorted(bothway_edges, key=lambda x: (x[0], x[1])) 47 | edges = [ 48 | [edge for edge in edges if edge[0] == i] for i in range(int(edges[-1][0]) + 1) 49 | ] 50 | bothway_edges = [ 51 | [bothway_edge for bothway_edge in bothway_edges if bothway_edge[0] == i] 52 | for i in range(int(bothway_edges[-1][0]) + 1) 53 | ] 54 | print( 55 | f"\033[0;33m Topo: {topo_name} has {node_num} nodes and {len(edge_info)} edges. \033[0m" 56 | ) 57 | write_ned(topo_name, node_num, edges) 58 | write_init_prob_file(topo_name, node_num, bothway_edges) 59 | print( 60 | f"\033[0;32m Congratulations! config/ned/{topo_name}.ned and config/ned/{topo_name}.txt were successfully generated! \033[0m" 61 | ) 62 | 63 | 64 | def write_ned( 65 | topo_name: str, 66 | node_num: int, 67 | edges: list, 68 | bandwidth: float = 1, 69 | link_delay: float = 0.002, 70 | ) -> None: 71 | """Converts the read information into a .ned file. 72 | 73 | Args: 74 | topo_name (str): name of network topology 75 | node_num (int): number of router nodes 76 | edges (list): network topology link information, where each element represents a bi-directional link 77 | bandwidth (float, optional): bandwidth of the network. Defaults to 1. 78 | link_delay (float, optional): delay of network link. Defaults to 0.002. 79 | """ 80 | 81 | with open(f"config/ned/{topo_name}.ned", "w") as f: 82 | print("\033[0;33m Starting Generating config/ned/" + topo_name + ".ned \033[0m") 83 | f.write("import inet.networklayer.configurator.ipv4.Ipv4NetworkConfigurator;\n") 84 | f.write("import inet.node.inet.StandardHost;\n") 85 | f.write("import inet.node.inet.Router;\n") 86 | f.write("import ned.DatarateChannel;\n") 87 | f.write("import inet.node.ethernet.Eth1G;\n\n") 88 | f.write(f"network {topo_name}\n") 89 | f.write("{\n") 90 | f.write(" parameters:\n") 91 | f.write(' @display("p=10,10;b=712,152");\n') 92 | f.write(" types:\n") 93 | f.write(" channel C extends DatarateChannel\n") 94 | f.write(" {\n") 95 | f.write(f" delay = {link_delay}s;\n") 96 | f.write(f" datarate = {bandwidth}Mbps;\n") 97 | f.write(" }\n") 98 | f.write(" submodules:\n") 99 | for node_index in range(node_num): 100 | f.write(f" H{node_index}" + ": StandardHost {\n") 101 | f.write(" parameters:\n") 102 | f.write(" forwarding = true;\n") 103 | f.write(' @display("p=250,150;i=device/laptop");\n') 104 | f.write(" gates:\n") 105 | f.write(" ethg[];\n") 106 | f.write(" }\n\n") 107 | f.write(" configurator: Ipv4NetworkConfigurator {\n") 108 | f.write(" parameters:\n") 109 | f.write(" addDefaultRoutes = false;\n") 110 | f.write(' @display("p=100,100;is=s");\n') 111 | f.write(" }\n") 112 | for node_index in range(node_num): 113 | f.write(f" R{node_index}" + ": Router {\n") 114 | f.write(" parameters:\n") 115 | f.write(" hasOspf = true;\n") 116 | f.write(" // hasRip = true;\n") 117 | f.write(' @display("p=250,200");\n') 118 | f.write(" }\n\n") 119 | f.write(" connections:\n") 120 | for edge_ in edges: 121 | for edge in edge_: 122 | f.write( 123 | f" R{edge[0]}.pppg++ <--> C <--> R{edge[1]}.pppg++;\n" 124 | ) 125 | f.write("\n") 126 | f.write("\n") 127 | for node_index in range(node_num): 128 | f.write( 129 | f" R{node_index}.ethg++ <--> Eth1G <--> H{node_index}.ethg++;\n" 130 | ) 131 | f.write("}") 132 | print( 133 | "\033[0;32m config/ned/" 134 | + topo_name 135 | + ".ned was successfully generated! \033[0m" 136 | ) 137 | 138 | 139 | def write_init_prob_file(topo_name: str, node_num: int, bothway_edges: list) -> None: 140 | """Write the initial probability .txt file for the network, 141 | at which point the forwarding probabilities for each link are equal. 142 | 143 | Args: 144 | topo_name (str): name of network topology 145 | node_num (int): number of router nodes 146 | bothway_edges (list): network topology link information, where each element represents a unidirectional link 147 | """ 148 | init_prob = sum( 149 | ( 150 | [ 151 | 100 // len(edge) if i in [t[1] for t in edge] else 0 152 | for i in range(node_num) 153 | ] 154 | for edge in bothway_edges 155 | ), 156 | [], 157 | ) 158 | with open(f"config/ned/{topo_name}.txt", "w") as f: 159 | print("\033[0;33m Starting Generating config/ned/" + topo_name + ".txt \033[0m") 160 | f.write(",".join(str(i) for i in init_prob)) 161 | print(f"\033[0;32m {topo_name}.txt was successfully generated! \033[0m") 162 | 163 | 164 | if __name__ == "__main__": 165 | gml_url = "http://topology-zoo.org/files/Gridnet.gml" 166 | get_omnetpp_ned(gml_url) 167 | -------------------------------------------------------------------------------- /utils/get_osfp_ned.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author : CHEN Jiawei 3 | Date : 2023-10-19 09:51:55 4 | LastEditors : LIN Guocheng 5 | LastEditTime : 2023-10-19 15:59:34 6 | FilePath : /home/lgc/test/RL4Net++/utils/get_osfp_ned.py 7 | Description : Change hasRip in the ned file to hasOspf. 8 | """ 9 | 10 | 11 | def change_ned_to_rip(ned_file_path: str) -> None: 12 | """Change hasRip in the ned file to hasOspf. 13 | 14 | Args: 15 | ned_file_path (str): path of .ned file 16 | """ 17 | if not ned_file_path: 18 | print("\033[0;31m Error! gml_url is None! \033[0m") 19 | return 20 | with open(ned_file_path, "r") as ned_file: 21 | context = ned_file.read() 22 | context = context.replace("hasRip = true;", "hasOspf = true;") 23 | with open(ned_file_path, "w") as ned_file: 24 | ned_file.write(context) 25 | 26 | 27 | if __name__ == "__main__": 28 | change_ned_to_rip("config/ned/Sprint.ned") 29 | -------------------------------------------------------------------------------- /utils/get_rip_ned.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author : CHEN Jiawei 3 | Date : 2023-10-19 09:51:55 4 | LastEditors : LIN Guocheng 5 | LastEditTime : 2023-10-19 16:00:35 6 | FilePath : /home/lgc/test/RL4Net++/utils/get_rip_ned.py 7 | Description : Change hasOspf in the ned file to hasRip. 8 | """ 9 | 10 | 11 | def change_ned_to_rip(ned_file_path: str) -> None: 12 | """Change hasOspf in the ned file to hasRip. 13 | 14 | Args: 15 | ned_file_path (str): path of .ned file 16 | """ 17 | if not ned_file_path: 18 | print("\033[0;31m Error! gml_url is None! \033[0m") 19 | return 20 | with open(ned_file_path, "r") as ned_file: 21 | context = ned_file.read() 22 | context = context.replace("hasOspf = true;", "hasRip = true;") 23 | with open(ned_file_path, "w") as ned_file: 24 | ned_file.write(context) 25 | 26 | 27 | if __name__ == "__main__": 28 | change_ned_to_rip("config/ned/Abilene.ned") 29 | --------------------------------------------------------------------------------