├── _config.yml
├── images
├── torus.png
├── switches.png
├── acl_rules.png
├── vm_settings.png
├── acl_flow_view.png
├── acl_no_rules.png
├── onos_ui_login.png
├── onos_ui_topo.png
├── torus_intent.png
├── switches_and_hosts.png
├── vpls_intents_topo.png
├── vpls_intents_view.png
├── torus_intent_changed.png
└── vpls_intents_topo_changed.png
├── firewall
├── firewall-policies.csv
├── delete_firewall.py
└── firewall.py
└── README.md
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
--------------------------------------------------------------------------------
/images/torus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArnoTroch/ONOS-Tutorial/HEAD/images/torus.png
--------------------------------------------------------------------------------
/images/switches.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArnoTroch/ONOS-Tutorial/HEAD/images/switches.png
--------------------------------------------------------------------------------
/images/acl_rules.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArnoTroch/ONOS-Tutorial/HEAD/images/acl_rules.png
--------------------------------------------------------------------------------
/images/vm_settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArnoTroch/ONOS-Tutorial/HEAD/images/vm_settings.png
--------------------------------------------------------------------------------
/images/acl_flow_view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArnoTroch/ONOS-Tutorial/HEAD/images/acl_flow_view.png
--------------------------------------------------------------------------------
/images/acl_no_rules.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArnoTroch/ONOS-Tutorial/HEAD/images/acl_no_rules.png
--------------------------------------------------------------------------------
/images/onos_ui_login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArnoTroch/ONOS-Tutorial/HEAD/images/onos_ui_login.png
--------------------------------------------------------------------------------
/images/onos_ui_topo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArnoTroch/ONOS-Tutorial/HEAD/images/onos_ui_topo.png
--------------------------------------------------------------------------------
/images/torus_intent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArnoTroch/ONOS-Tutorial/HEAD/images/torus_intent.png
--------------------------------------------------------------------------------
/images/switches_and_hosts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArnoTroch/ONOS-Tutorial/HEAD/images/switches_and_hosts.png
--------------------------------------------------------------------------------
/images/vpls_intents_topo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArnoTroch/ONOS-Tutorial/HEAD/images/vpls_intents_topo.png
--------------------------------------------------------------------------------
/images/vpls_intents_view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArnoTroch/ONOS-Tutorial/HEAD/images/vpls_intents_view.png
--------------------------------------------------------------------------------
/images/torus_intent_changed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArnoTroch/ONOS-Tutorial/HEAD/images/torus_intent_changed.png
--------------------------------------------------------------------------------
/images/vpls_intents_topo_changed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArnoTroch/ONOS-Tutorial/HEAD/images/vpls_intents_topo_changed.png
--------------------------------------------------------------------------------
/firewall/firewall-policies.csv:
--------------------------------------------------------------------------------
1 | id,mac_0,mac_1
2 | 1,00:00:00:00:00:01,00:00:00:00:00:04
3 | 2,00:00:00:00:00:02,00:00:00:00:00:03
4 | 3,00:00:00:00:00:03,00:00:00:00:00:07
5 | 4,00:00:00:00:00:06,00:00:00:00:00:05
6 |
--------------------------------------------------------------------------------
/firewall/delete_firewall.py:
--------------------------------------------------------------------------------
1 | import requests
2 |
3 | # REST API url and headers
4 | host = "192.168.0.239"
5 | port = "8181"
6 | username = "karaf"
7 | password = "karaf"
8 | url = f"http://{host}:{port}/onos/v1/acl/rules"
9 |
10 | # remove all rules from the ACL using the REST API
11 | resp = requests.delete(url, auth=(username, password))
12 | print(resp.text)
13 |
--------------------------------------------------------------------------------
/firewall/firewall.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import csv
3 | import requests
4 |
5 | # REST API url and headers
6 | host = "192.168.0.239"
7 | port = "8181"
8 | username = "karaf"
9 | password = "karaf"
10 | url = f"http://{host}:{port}/onos/v1/acl/rules"
11 | headers = {'Content-type': 'application/json'}
12 |
13 | # read policy file
14 | policyFile = "firewall-policies.csv"
15 | firewall_rules = []
16 | with open(policyFile, 'r') as csvfile:
17 | rows = csv.reader(csvfile, delimiter=',')
18 | line_count = 0
19 | for row in rows:
20 | if line_count == 0:
21 | line_count += 1
22 | continue
23 | firewall_rules.append((row[1], row[2]))
24 | line_count += 1
25 |
26 | # put each firewall rule into the ACL using the REST API
27 | for rule in firewall_rules:
28 | resp = requests.post(
29 | url,
30 | json={
31 | "srcIp": "10.0.0.0/24",
32 | "srcMac": rule[0],
33 | "dstMac": rule[1]
34 | },
35 | auth=(username, password)
36 | )
37 | print(resp.text)
38 |
39 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Using ONOS as a robust SDN controller with Mininet
2 |
3 | ###### Arno Troch (arno.troch@student.uantwerpen.be)
4 |
5 | ### Table of Contents
6 |
7 | * [Setup Process](#setup-process)
8 | * [Installing a Mininet VM](#installing-a-mininet-vm)
9 | * [Installing ONOS](#installing-onos)
10 | * [Prerequisites](#prerequisites)
11 | * [Download and Run](#download-and-run)
12 | * [Introduction to ONOS](#introduction-to-onos)
13 | * [ONOS CLI and GUI](#onos-cli-and-gui)
14 | * [Accessing the CLI](#accessing-the-cli)
15 | * [Accessing the GUI](#accessing-the-gui)
16 | * [ONOS and Mininet](#onos-and-mininet)
17 | * [Example ONOS Use Cases](#example-onos-use-cases)
18 | * [L2 Firewall](#l2-firewall)
19 | * [Introduction to ACL](#introduction-to-acl)
20 | * [Firewall Implementation](#firewall-implementation)
21 | * [Virtual Private LAN Service (VPLS)](#virtual-private-lan-service-vpls)
22 | * [Intents](#intents)
23 | * [VPLS Implementation](#vpls-implementation)
24 |
25 | Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)
26 |
27 | #
28 |
29 | The Open Network Operating System, better known as ONOS, is an open-source operating system that provides robust SDN controllers to manage large networks. ONOS is specifically designed with high scalability, performance and availability in mind, and it provides lots of different APIs and abstractions for configuring and managing your networks.
30 |
31 | In this tutorial, we will cover how to set up ONOS on a virtual machine and how to use it in combination with Mininet. After that, we will also see a few examples where we will use some default applications that ONOS ships with.
32 |
33 |
34 | ## Setup Process
35 |
36 | ###### *Note: The only software requirement you will need for this tutorial is [VirtualBox](https://www.virtualbox.org).*
37 |
38 | ### Installing a Mininet VM
39 |
40 | For this tutorial, we will use one of the pre-packaged Mininet VMs available from the [Mininet Releases](https://github.com/mininet/mininet/releases/tag/2.3.0) page (in this tutorial, we used release 2.3.0). This VM runs Ubuntu 20.04.3 LTS, and it includes Mininet and all necessary tools and binaries for OpenFlow support, along with some tweaks to the kernel configuration to support larger networks.
41 |
42 | After downloading one of the OVA files, you can double-click on it to import it into VirtualBox.
43 | Once you see the Mininet VM listed in VirtualBox, go to Settings > Network and change the network adapter from NAT to Bridged Adapter. This will make it possible for your host OS to communicate with the VM through SSH (which will be explained in the next section).
44 |
45 |
46 |
47 | ### Installing ONOS
48 |
49 | The next step is to install ONOS. Start off by logging in on the Mininet VM (username: mininet, password: mininet). In this tutorial, we installed and used the newest available ONOS release version at the time, which was version 2.6.0 (Woodpecker).
50 |
51 | #### Prerequisites
52 |
53 | The [ONOS Requirements](https://wiki.onosproject.org/display/ONOS/Requirements) page tells us that our ONOS version requires Java 11.
54 | ```bash
55 | sudo apt update
56 | sudo apt install openjdk-11-jdk
57 | ```
58 |
59 | We will also set the `$JAVA_HOME` variable, which should give us better performance.
60 | ```bash
61 | sudo cat >> /etc/environment <
128 | If everything went well, you should be seeing the following window.
129 | ```
130 | mininet@mininet-vm:/opt$ sudo /opt/onos/bin/onos -l karaf
131 | Password authentication
132 | Password:
133 | Welcome to Open Network Operating System (ONOS)!
134 | ____ _ ______ ____
135 | / __ \/ |/ / __ \/ __/
136 | / /_/ / / /_/ /\ \
137 | \____/_/|_/\____/___/
138 |
139 | Documentation: wiki.onosproject.org
140 | Tutorials: tutorials.onosproject.org
141 | Mailing lists: lists.onosproject.org
142 |
143 | Come help out! Find out how at: contribute.onosproject.org
144 |
145 | Hit '' for a list of available commands
146 | and '[cmd] --help' for help on a specific command.
147 | Hit '' or type 'logout' to exit ONOS session.
148 |
149 | karaf@root >
150 | ```
151 |
152 | #### Accessing the GUI
153 |
154 | You can also access the GUI through a browser **on your host machine**. To do this, you can simply go to the URL `http://:8181/onos/ui`, where `` is the local IP address of your VM (The host is able to access the VM because of the network adapter that we put into "Bridged" mode). You can get the local IP of your guest OS as follows.
155 | ```
156 | mininet@mininet-vm:~$ ifconfig | grep inet
157 | inet 192.168.0.239 netmask 255.255.255.0 broadcast 192.168.0.255
158 | inet 127.0.0.1 netmask 255.0.0.0
159 | ```
160 |
161 | In this case, the local IP address of the system is `192.168.0.239`, so our URL would be `http://192.169.0.239:8181/onos/ui`. In your browser, you should now see the following screen.
162 |
163 | 
164 |
165 | From here, you can log in with one of the two default users, after which you should be redirected to the Topology View.
166 |
167 | 
168 |
169 | From here, you should normally be able to see an overview of the network topology that is currently connected to your controller(s), but since we haven't set up any network yet, so we don't see any devices.
170 |
171 | ## ONOS and Mininet
172 |
173 | Mininet can be used to create virtual network topologies, which can then be controlled by an SDN controller like ONOS. Start by creating a simple Mininet topology by running the following command in a new terminal window.
174 | ```bash
175 | sudo mn --topo tree,2,2 --mac --switch ovs,protocols=OpenFlow14 --controller remote,ip=192.168.0.239
176 | ```
177 |
178 | Let's quickly go over the different options we provided:
179 | * `--topo tree,2,2` creates a new tree topology with depth 2 and fanout 2
180 | * `--mac` sets the host MAC and IP addresses to small and readable values
181 | * `--switch ovs,protocols=OpenFlow14` tells Mininet to use Open vSwitches using OpenFlow version 14
182 | * `--controller remote,ip=192.168.0.239` says that the switches will be controlled by a remote controller located on IP address `192.168.0.239` (which is the local IP of the Mininet VM)
183 |
184 | Even though we have created a new topology, the network is still not showing up in the GUI. This is normal, since we haven't told ONOS yet that it should communicate with the switches using OpenFlow. We can do this by activating the pre-installed OpenFlow app through the ONOS CLI.
185 | ```
186 | karaf@root > app activate org.onosproject.openflow
187 | Activated org.onosproject.openflow
188 | ```
189 |
190 | Now, our small Mininet topology should be showing up in the GUI.
191 |
192 | 
193 |
194 | We can see that ONOS was able to discover the three switches that Mininet created. Pressing "h" should toggle the visibility of the host nodes, but ONOS hasn't discovered these hosts yet.
195 |
196 | ONOS will only discover the different hosts once these hosts try to send data between them. This can be done by running the `pingall` command in Mininet, which will make all the hosts exchange data between themselves. and ONOS would become aware of their existence. However, if you try to run `pingall` right now, you will see that it fails.
197 | ```
198 | mininet> pingall
199 | *** Ping: testing ping reachability
200 | h1 -> X X X
201 | h2 -> X X X
202 | h3 -> X X X
203 | h4 -> X X X
204 | *** Results: 100% dropped (0/12 received)
205 | ```
206 |
207 | The reason for this is that, even though the controller is able to talk to the switches through OpenFlow, the controller has no idea what to do with the packets that the switch is receiving from the different hosts. The way we solve this is by activating the *reactive forwarding* app from ONOS.
208 | ```
209 | karaf@root > app activate org.onosproject.fwd
210 | Activated org.onosproject.fwd
211 | ```
212 | This is a simple application that installs flows in response to every incoming packet arriving at the controller that didn't match a rule in the flow table on the switch
213 |
214 | This is a simple application that installs flows on the switches. Every packet that arrives at a switch and doesn't have a match in the flow table gets sent to the controller. There, the `fwd` application will create a flow rule for that kind of packet and install it in the switch. Now the `pingall` command works as it should.
215 | ```
216 | mininet> pingall
217 | *** Ping: testing ping reachability
218 | h1 -> h2 h3 h4
219 | h2 -> h1 h3 h4
220 | h3 -> h1 h2 h4
221 | h4 -> h1 h2 h3
222 | *** Results: 0% dropped (12/12 received)
223 | ```
224 |
225 | If you go back to the GUI and toggle the visibility of the hosts, you should be able to see the four different hosts created by Mininet.
226 |
227 | 
228 |
229 |
230 |
231 | Now that we have introduced the ONOS CLI and GUI, as well as how to use the controller in combination with Mininet, we can move on to some simple examples.
232 |
233 | ## Example ONOS Use Cases
234 |
235 | In this section, we will cover two use cases that could be useful on real networks. For the first use case, we will set up a layer 2 firewall to block traffic between certain hosts. In the second example, we will set up a Virtual Private LAN Service (VPLS).
236 |
237 | ### L2 Firewall
238 |
239 | #### Introduction to ACL
240 |
241 | We will be setting up a layer 2 firewall using the same Mininet topology as in the previous section. Luckily, we can easily set this up by taking advantage of a pre-installed ONOS application called `acl`. An ACL or Access Control List is a list containing rules that are used to filter network traffic. We will be creating rules to deny traffic between certain hosts. First, start by activating the application from the ONOS CLI.
242 | ```
243 | karaf@root > app activate org.onosproject.acl
244 | Activated org.onosproject.acl
245 | ```
246 |
247 | This application exposes a REST API that allows us to add and remove rules and is available on http://192.168.0.239:8181/onos/v1/acl. If we look at the [source code](https://github.com/opennetworkinglab/onos/blob/onos-2.6/apps/acl/src/main/java/org/onosproject/acl/AclWebResource.java) for this application, we can derive the following API endpoints:
248 |
249 | | Method | Route | Description |
250 | | ------- | ----------- | --------------------- |
251 | | GET | /rules | Get all ACL rules |
252 | | POST | /rules | Add a new ACL rule |
253 | | DELETE | /rules/{id} | Remove ACL rule |
254 | | DELETE | /rules | Remove all ACL rules |
255 |
256 | We can check to see if the REST API is active by requesting all current ACL rules. Since we haven't added any, we should get an empty response back, which is exactly what we get.
257 |
258 | 
259 |
260 | #### Firewall Implementation
261 |
262 | Now that we know how to change the Access Control List, we can start using it to block traffic. Since it is not very convenient to manually send POST requests to block certain connections, we have decided to write a Python script that reads a csv file with MAC address pairs and blocks the traffic between them by creating ACL rules with these MAC addresses. You can find the full example in the *firewall* directory. This directory also contains the `firewall-policies.csv` file, which contains the MAC address pairs for which we would like to block traffic:
263 |
264 | | id | mac_0 | mac_1 |
265 | | -- | ----------------- | ----------------- |
266 | | 1 | 00:00:00:00:00:01 | 00:00:00:00:00:04 |
267 | | 2 | 00:00:00:00:00:02 | 00:00:00:00:00:03 |
268 | | 3 | 00:00:00:00:00:03 | 00:00:00:00:00:07 |
269 | | 4 | 00:00:00:00:00:06 | 00:00:00:00:00:05 |
270 |
271 | We also have two Python files in that directory: `firewall.py` and `delete_firewall.py`. In `firewall.py`, we read all the MAC pairs from the csv file and send POST requests to the API to deny the traffic between them.
272 | ```python
273 | import sys
274 | import csv
275 | import requests
276 |
277 | # REST API url and headers
278 | host = "192.168.0.239"
279 | port = "8181"
280 | username = "karaf"
281 | password = "karaf"
282 | url = f"http://{host}:{port}/onos/v1/acl/rules"
283 | headers = {'Content-type': 'application/json'}
284 |
285 | # read policy file
286 | policyFile = "firewall-policies.csv"
287 | firewall_rules = []
288 | with open(policyFile, 'r') as csvfile:
289 | rows = csv.reader(csvfile, delimiter=',')
290 | line_count = 0
291 | for row in rows:
292 | if line_count == 0:
293 | line_count += 1
294 | continue
295 | firewall_rules.append((row[1], row[2]))
296 | line_count += 1
297 |
298 | # put each firewall rule into the ACL using the REST API
299 | for rule in firewall_rules:
300 | resp = requests.post(
301 | url,
302 | json={
303 | "srcIp": "10.0.0.0/24",
304 | "srcMac": rule[0],
305 | "dstMac": rule[1]
306 | },
307 | auth=(username, password)
308 | )
309 | print(resp.text)
310 |
311 | ```
312 |
313 | If you would like to remove all the rules again, you can use the `delete_firewall.py` file. This code is pretty self-explanatory: it will remove all ACL rules by sending a DELETE request to the API.
314 | ```python
315 | import requests
316 |
317 | # REST API url and headers
318 | host = "192.168.0.239"
319 | port = "8181"
320 | username = "karaf"
321 | password = "karaf"
322 | url = f"http://{host}:{port}/onos/v1/acl/rules"
323 |
324 | # remove all rules from the ACL using the REST API
325 | resp = requests.delete(url, auth=(username, password))
326 | print(resp.text)
327 |
328 | ```
329 |
330 | Once you run `firewall.py`, you can check whether the rules were properly added by sending another GET request. As you can see, there are a bunch of rules in the ACL now (one for every MAC pair).
331 |
332 | 
333 |
334 | Furthermore, running the `pingall` command in Mininet will now block certain connections:
335 | ```
336 | mininet> pingall
337 | *** Ping: testing ping reachability
338 | h1 -> h2 h3 X
339 | h2 -> h1 X h4
340 | h3 -> h1 X h4
341 | h4 -> X h2 h3
342 | *** Results: 33% dropped (8/12 received)
343 | ```
344 |
345 | We can even use the GUI to look at the Flow View for a certain switch, which shows the different flow rules installed in it. In switch 2, we can see that the `acl` app converted the ACL rules to flow rules, which can then be enforced by the switch.
346 |
347 | 
348 |
349 | We have now successfully set up a L2 firewall.
350 |
351 |
352 | ### Virtual Private LAN Service (VPLS)
353 | ###### *Note: make sure that, if you followed along with the previous firewall example, you clear your ACL rules using the `delete_firewall.py` file and deactivate the `acl` app (to prevent interference with this example)*
354 |
355 |
356 | In this section, we will look at a mora advanced use case for ONOS: we will set up a Virtual Private LAN service (VPLS). A VPLS is a multipoint-to-multipoint layer 2 Virtual Private Network (VPN) that allows geographically separated endpoints to share a L2 broadcast link, which makes it seem like the endpoints are on the same LAN.
357 |
358 | ONOS comes with a `vpls` application out of the box. Not only does this make it very easy to create, change and delete different VPLS networks whenever you want, it also allows the virtual LAN network to change dynamically over time. To understand how this works, we will first need to take a look at a more complex abstraction of ONOS called **intents**.
359 |
360 | #### Intents
361 | ONOS has a subsystem that is called the Intent Framework. Intents allow specifying network behaviour in form of policies, rather than mechanisms. In short, this means that we will tell the network *what* to do, not *how* it should do it. To understand this better, let's look at a practical example of intents in ONOS.
362 |
363 | Before we start, we will quickly set up ONOS and Mininet again. For ONOS, make sure you have the `org.onosproject.openflow` app activated. For Mininet, we will be using a larger and more complex topology called `torus`. You can create this topology as follows.
364 | ```bash
365 | sudo mn -c
366 | sudo mn --topo torus,3,3 --mac --switch ovs,protocols=OpenFlow14 --controller remote,ip=192.168.0.239
367 | ```
368 |
369 | The topology looks as follows in the Topology View.
370 |
371 | 
372 |
373 | Since the reactive forwarding app `org.onosproject.fwd` is deactivated right now, we are unable to ping between any of the hosts. Let's assume we only want a single connection between host 10.0.0.1 and host 10.0.0.6 (we will call these h1 and h6 respectively). We can enable such a connection by creating a *host-to-host intent* from the ONOS CLI.
374 | ```
375 | karaf@root > add-host-intent 00:00:00:00:00:01/None 00:00:00:00:00:06/None
376 | Host to Host intent submitted:
377 | HostToHostIntent{id=0x0, key=0x0, ...}
378 | ```
379 |
380 | This intent can be visualised from the GUI by going to the *Intents* view from the side menu, selecting the intent and clicking *Show selected intent on topology view*. The intent is shown by an orange dotted line.
381 |
382 | 
383 |
384 | The above picture shows what route the intent has chosen to connect hosts h1 and h6. However, what would happen if one of these orange links were to go down? Let's find out. From the Mininet console, disable the link between the first two switches.
385 | ```
386 | mininet> link s1x1 s2x1 down
387 | ```
388 |
389 | Now, let's see what happened from the Topology View.
390 |
391 | 
392 |
393 | As you can see, this example shows how powerful intents are: the intent has decided to choose a new route, since the old one was not working anymore.
394 |
395 | #### VPLS Implementation
396 | The `vpls` app that comes with ONOS makes use of these intents: instead of specifying exactly what routes to take, the app will use intents to describe how the network should behave. It will do this by defining all broadcast and unicast connections between all hosts that should be part of the VPLS network.
397 |
398 | Let's go through an example where we try to set up a VPLS connection between hosts 10.0.0.1, 10.0.0.2 and 10.0.0.6 (h1, h2 and h6 respectively).
399 |
400 | ###### *Note: For this demonstration, we disabled the `org.onosproject.gui2` app, which is the (default) second version of the GUI, and used version 1 called `org.onosproject.gui` instead. This is not necessary, but we did this because the first version allows us to show multiple intents at the same time in the Topology View.*
401 |
402 | Let's start by activating the necessary apps.
403 | ```
404 | karaf@root > app activate org.onosproject.vpls
405 | Activated org.onosproject.vpls
406 | ```
407 |
408 |
409 | To create a VPLS, we are required to define an interface for each host. We can also do this from the ONOS CLI.
410 | ```
411 | karaf@root > interface-add of:0000000000000101/1 h1
412 | Interface added
413 | karaf@root > interface-add of:0000000000000102/1 h2
414 | Interface added
415 | karaf@root > interface-add of:0000000000000203/1 h6
416 | Interface added
417 | ```
418 |
419 | Next, we create a new VPLS named *vpls1* and add all the interfaces to it.
420 | ```
421 | karaf@root > vpls create vpls1
422 | karaf@root > vpls add-if vpls1 h1
423 | karaf@root > vpls add-if vpls1 h2
424 | karaf@root > vpls add-if vpls1 h6
425 | ```
426 |
427 | Your VPLS should look like this now.
428 | ```
429 | karaf@root > vpls show
430 | ----------------
431 | VPLS name: vpls1
432 | Associated interfaces: [h1, h2, h6]
433 | Encapsulation: NONE
434 | State: ADDED
435 | ----------------
436 | ```
437 |
438 | If we go back to the GUI and look at the *Intents* view, we can see the new intents created by the `vpls` app.
439 |
440 | 
441 |
442 | As you can see, there are three `SinglePointToMultiPointIntent`s, which represent the overlay networks for broadcast traffic, and three `MultiPointToSinglePointIntent`s, which represent the links for unicast traffic.
443 |
444 | Using the first version of the GUI, we can visualise all the intents related to the virtual LAN network we just set up from the Topology View.
445 |
446 | 
447 |
448 | You should now be able to use the `ping` command between these three different hosts. Just like in the earlier intents example, let's "break" some links that are being used by this virtual network to see how the intent adapts.
449 | ```
450 | mininet> link s1x3 s2x3 down
451 | mininet> link s2x2 s2x3 down
452 | ```
453 |
454 | The intents are now using different routes.
455 |
456 | 
457 |
458 | Even though the virtual network has changed routes quite drastically, you can still `ping` between all hosts, as if nothing has changed.
459 |
--------------------------------------------------------------------------------