├── 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