Contents
- A computer with reasonable specs and free PCI-E slot (I used an old Dell PowerEdge R210 II)
- 4-port Ethernet card (I used IOCrest SI-PEX24042 for ~US$45)
- Internet connection (to download software packages)
- Ubuntu 18.0.4 OS
- RYU
- Snort
PART I – Building the base system
In this step, you will install the 4-port Ethernet card and the Ubuntu 18.0.4 operation system.Install the Ethernet card
Turn off the PC, unplug the power, then follow the manufacturer instructions to install the Ethernet card.Install the OS
Follow the instructions here to install Ubuntu 18.0.4. Caution: This will wipe-out the existing data on the machine’s hard drive, so be sure you have a backup.Verify the Installation
Once the installation is complete, login and open a terminal. Your computer should have four new Ethernet interfaces in addition to any original interfaces. Verify the status of interfaces and take note of their names:ovs@ovs-machine:~$ ip link 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000 link/ether d0:67:e5:f1:03:ea brd ff:ff:ff:ff:ff:ff 3: enp3s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN mode DEFAULT group default qlen 1000 link/ether 00:e0:4c:68:21:c1 brd ff:ff:ff:ff:ff:ff 4: enp4s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN mode DEFAULT group default qlen 1000 link/ether 00:e0:4c:68:21:c2 brd ff:ff:ff:ff:ff:ff 5: enp5s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN mode DEFAULT group default qlen 1000 link/ether 00:e0:4c:68:21:c3 brd ff:ff:ff:ff:ff:ff 6: eno2: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN mode DEFAULT group default qlen 1000 link/ether d0:67:e5:f1:03:eb brd ff:ff:ff:ff:ff:ff 7: enp6s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN mode DEFAULT group default qlen 1000 link/ether 00:e0:4c:68:21:c4 brd ff:ff:ff:ff:ff:ffNotice that all interfaces are UP. You should also confirm that you have connectivity to the Internet by pinging know external destination (try 8.8.8.8).
PART II – Create OpenFlow Switch
Here you will create an OpenFlow Switch using Open vSwitch (ovs). Form the terminal, use apt-get to install the ovs:ovs@ovs-machine:~$ sudo apt-get update ovs@ovs-machine:~$ sudo apt-get install openvswitch-switchIf the installation is successful, you should be able check the OVS version:
ovs@ovs-machine:~$ sudo ovs-vsctl show d60627a8-fdb0-4fae-902f-eb839fc388c3 ovs_version: "2.9.0"Create a bridge and add the four Ethernet ports to it:
ovs@ovs-machine:~$ sudo ovs-vsctl add-br br0 ovs@ovs-machine:~sudo ovs-vsctl set bridge br0 stp_enable=true ovs@ovs-machine:~$ sudo ovs-vsctl add-port br0 enp3s0 -- set interface enp3s0 ofport_request=1 ovs@ovs-machine:~$ sudo ovs-vsctl add-port br0 enp4s0 -- set interface enp4s0 ofport_request=2 ovs@ovs-machine:~$ sudo ovs-vsctl add-port br0 enp5s0 -- set interface enp5s0 ofport_request=3 ovs@ovs-machine:~$ sudo ovs-vsctl add-port br0 enp6s0 -- set interface enp6s0 ofport_request=4The ofport_request let you set the interface port number; otherwise, the OVS will select one. Run the show command again and see the changes:
ovs@ovs-machine:~$ sudo ovs-vsctl show d60627a8-fdb0-4fae-902f-eb839fc388c3 Bridge "br0" Port "enp3s0" Interface "enp3s0" Port "enp4s0" Interface "enp4s0" Port "br0" Interface "br0" type: internal Port "enp5s0" Interface "enp5s0" Port "enp6s0" Interface "enp6s0" ovs_version: "2.9.0"Use the ip command again and note the change in the output:
ovs@ovs-machine:~$ ip link 1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: enp3s0: mtu 1500 qdisc fq_codel master ovs-system state DOWN mode DEFAULT group default qlen 1000 link/ether 00:e0:4c:68:21:c1 brd ff:ff:ff:ff:ff:ff 3: enp4s0: mtu 1500 qdisc fq_codel master ovs-system state DOWN mode DEFAULT group default qlen 1000 link/ether 00:e0:4c:68:21:c2 brd ff:ff:ff:ff:ff:ff 4: eno1: mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000 link/ether d0:67:e5:f1:03:ea brd ff:ff:ff:ff:ff:ff 5: enp5s0: mtu 1500 qdisc fq_codel master ovs-system state DOWN mode DEFAULT group default qlen 1000 link/ether 00:e0:4c:68:21:c3 brd ff:ff:ff:ff:ff:ff 6: eno2: mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether d0:67:e5:f1:03:eb brd ff:ff:ff:ff:ff:ff 7: enp6s0: mtu 1500 qdisc fq_codel master ovs-system state DOWN mode DEFAULT group default qlen 1000 link/ether 00:e0:4c:68:21:c4 brd ff:ff:ff:ff:ff:ff 8: ovs-system: mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether ee:a0:00:e1:b8:11 brd ff:ff:ff:ff:ff:ff 9: br0: mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/ether 00:e0:4c:68:21:c1 brd ff:ff:ff:ff:ff:ffThe br0 is now an interface and the other interfaces belong to ovs-system.
Connecting to the Controller
At this point, the switch will be working as a standalone switch (without controller). Connect the hosts Host 1 and Host 2 to the switch interfaces. If they are configured with IP addresses from the same subnet (say 192.168.1.0/24), they will be able to connect. Note: If the hosts cannot ping each other, make sure the interfaces are UP. If not, use the command:ovs@ovs-machine:~$ sudo ip link set upThe switch needs to connect to a controller to be able to exchange OpenFlow packets and populate its flow tables. The OVS can connect to the controller in out-of-band mode, which requires a dedicated interface, or using in-band connection mode. This latter, which is the default, requires only one network for OpenFlow and the rest of the data traffic. The switch needs to have an interface configured with an IP address to communicate with the controller. Rather than using one of the physical interfaces, we will use the br0 interface, which is an internal interface of the bridge. The IP address can be anything you choose.
ovs@ovs-machine:~$ sudo ip address add 10.0.10.10/24 dev br0 ovs@ovs-machine:~$ sudo ip link set br0 upNow, tell the switch where to find the controller and what to do if it cannot find it:
ovs@ovs-machine:~$ sudo ovs-vsctl set-controller br0 tcp:10.0.10.10:6633 \ -- set-fail-mode br0 standaloneThen verify:
ovs@ovs-machine:~$ sudo ovs-vsctl show d60627a8-fdb0-4fae-902f-eb839fc388c3 Bridge "br0" Controller "tcp:10.0.10.10:6633" fail_mode: standalone Port "enp6s0" Interface "enp6s0" Port "br0" Interface "br0" type: internal Port "enp3s0" Interface "enp3s0" Port "enp4s0" Interface "enp4s0" Port "enp5s0" Interface "enp5s0" ovs_version: "2.9.0"The ping between the two hosts continues because the switch is still working in standalone mode.
PART III – Install the Ryu Controller
It is time to install the Ryu controller. Ryu is a component-based software defined networking framework. Ryu supports various protocols for managing network devices, such as OpenFlow, Netconf, and OF-config and integrates well with Snort.Install Ryu
The easiest way to install Ryu is using apt-get (you can also use pip3):ovs@ovs-machine:~$ sudo apt-get python3-ryuCheck if the installation is successful by running the ryu-manager:
ovs@ovs-machine:~$ ryu-manager --version ryu-manager 4.15Optionally, you can install FlowManager. The FlowManager is an application that provides a user-friendly interface to modify and monitor the flow tables in the network.
ovs@ovs-machine:~$ sudo apt-get git ovs@ovs-machine:~$ git clone https://github.com/martimy/flowmanagerIn another terminal start Ryu using the following command. The controller will run two application: the first application implements a simple L2 switch, and the second application is the FlowManager, which provide more information about the OVS.
ovs@ovs-machine:~$ ryu-manager --verbose ryu.app.simple_switch_13 flowmanager/flowmanager.py loading app ryu.app.simple_switch_13 loading app ryu.controller.ofp_handler instantiating app ryu.app.simple_switch_13 of SimpleSwitch13 instantiating app ryu.controller.ofp_handler of OFPHandler BRICK SimpleSwitch13 CONSUMES EventOFPPacketIn CONSUMES EventOFPSwitchFeatures BRICK ofp_event PROVIDES EventOFPPacketIn TO {'SimpleSwitch13': {'main'}} PROVIDES EventOFPSwitchFeatures TO {'SimpleSwitch13': {'config'}} CONSUMES EventOFPEchoReply CONSUMES EventOFPEchoRequest CONSUMES EventOFPErrorMsg CONSUMES EventOFPHello CONSUMES EventOFPPortDescStatsReply CONSUMES EventOFPPortStatus CONSUMES EventOFPSwitchFeatures connected socket: address:('10.0.10.10', 52636) hello ev <ryu.controller.ofp_event.EventOFPHello object at 0x7efcb802af98> move onto config mode EVENT ofp_event->SimpleSwitch13 EventOFPSwitchFeatures switch features ev version=0x4,msg_type=0x6,msg_len=0x20,xid=0xc5e4f13d,OFPSwitchFeatures(auxiliary_id=0,capabilities=79,datapath_id=963354567105,n_buffers=0,n_tables=254) move onto main mode …The output above shows that the controller connected to the switch successfully. Using a browser, enter the URL http://localhost:8080/home/ to access the FlowManager. You should see something similar to the screenshot below. In the homepage you should see the switch’s ID and other information as well as the port descriptions and statistics. The Flow Tables page shows the flow entries created by Simple_Switch_13 app to forward packets between Host 1 and Host 2 (BTW, ping should still be going between the hosts by now.)
Make the Interface Configuration Permanent
The OVS configuration you created earlier will remain in effect even if you reboot the server, but the IP address of the br0 interface will not. Also, the next time the machine reboots, the four switch interfaces will remain in DOWN state until activated manually. Ubuntu 18.0.4 replaces the old file /etc/network/interfaces with Netplan utility for easy networking configuration. Netplan uses a YAML to describe network interfaces and their configuration. To make the network configuration permanent, edit the following .yaml file to include the configuration shown below:$ sudo nano /etc/netplan/01-network-manager-all.yaml<yaml file goes here>
network: version: 2 renderer: networkd ethernets: eno1: dhcp4: yes dhcp6: yes br0: dhcp4: no dhcp6: no addresses: [10.0.10.10/24]Notice that: 1) you need to change the renderer from NetworkManager, which allows using GUI to configure the interfaces, to networkd that relies on the .yaml file for the configuration; 2) you need to make sure that the Internet-facing interface still receives a DHCP address; and 3) the br0 interface has a static IP address. To activate the configuration:
ovs@ovs-machine:~$ sudo netplan applyFinally, you need to activate the four interfaces upon reboot. You can do this up adding ip commands to the /etc/rc.local file. This file does not exist by default in Ubuntu 18.0.4, so you must add it manually (this suggests that there may be a better way to do this, so I’ll keep looking.)
$ ovs@ovs-machine:~$ sudo nano /etc/rc.local
#!/bin/bash ip link set enp3s0 up ip link set enp4s0 up ip link set enp5s0 up ip link set enp6s0 up exit 0Make the file executable:
$ ovs@ovs-machine:~$ sudo chmod +x /etc/rc.localNow the interface will be up after reboot.
Run Ryu as a service
Here we add a system service to run Ryu and its applications when automatically when the server boots. Create a file run_controller.sh in your home directory (remember to change the home directory wherever applicable):ovs@ovs-machine:~$ sudo nano run_conroller.sh
#!/bin/bash ryu-manager ryu.app.simple_switch_13 /home/ovs/flowmanager/flowmaanger.pyMake it executable:
ovs@ovs-machine:~$ sudo chmod +x run_controller.shCreate a file /etc/systemd/system/ryu.service:
ovs@ovs-machine:~$ sudo /etc/systemd/system/ryu.service:
[Unit] Description=RYU Controller [Service] User=ovs TimeoutStartSec=0 WorkingDirectory=/home/ovs ExecStart=/home/ovs/run_controller.sh Restart=always RestartSec=5 [Install] WantedBy=multi-user.targetEnable the service and start it (if ryu is already running, you should stop it first):
ovs@ovs-machine:~$ sudo systemctl enable /etc/systemd/system/ryu.service ovs@ovs-machine:~$ sudo systemctl start ryu.serviceor
ovs@ovs-machine:~$ sudo service ryu startVerify that the service is active and running
ovs@ovs-machine:~$ sudo service ryu statusNow it is a good time to reboot the server and verify that everything works correctly.
PART IV – Integrating Snort
Snort is an open source network intrusion prevention system, capable of performing real-time traffic analysis and packet logging on IP networks. Snort can be linked to Ryu in two ways as shown in here. In our case, we will follow Option #1. Once Snort is linked to Ryu, it can send alerts to the controller, which may respond to by modifying the switch flow tables.Creating Mirror Interface
Snort requires an interface from which it receives packets for analysis. This interface is normally connected to a switch port that work in mirror mode. In our server, we will create an internal OVS port that serve this purpose. Later, the L2 switch application will be modified slightly to copy all traffic passing through the switch to this mirror port. Add an Internal port to the switchovs@ovs-machine:~$ sudo ovs-vsctl add-port br0 snort0 \ -- set interface snort0 type=internal \ -- set interface snort0 ofport_request=5Verify:
ovs@ovs-machine:~$ sudo ovs-vsctl showAnd bring it up:
ovs@ovs-machine:~$ sudo ip link set snort0 upThe FlowManager will also show the new port as port #5. Before you conclude this step, you should add the above command to the /etc/rc.local file to ensure that the interface is brought up when the server reboots.
#!/bin/bash ip link set enp3s0 up ip link set enp4s0 up ip link set enp5s0 up ip link set enp6s0 up ip link set snort0 up exit 0
Installing Snort
Install Snort using apt-get:ovs@ovs-machine:~$ sudo apt-get install snort*Snort will present you with a series of configuration screens:
- Screen #1 will ask for an interface name. I find this interface irrelevant, so just use your server built-in interface name.
- Screen #2 will ask for the network prefix. Use the subnet that used for Host 1 and Host 2, say 192.168.1.0/24
- Screen #3 will ask for the sniffing interface name. Enter snort0.
ovs@ovs-machine:~$ snort –versionSnort will run as a service in the background, but for now, we want to launch it manually, so we will change the configuration:
ovs@ovs-machine:~$ sudo dpkg-reconfigure snortThe first screen will ask about the start mode. Change that to Manual. Keep the answers for the remaining screen to default unless you want to modify the interface name and/or the network range.
Link to Ryu
For this final step, we need to launch a modified L2 switch app that imports Ryu snortlib and displays alert messages received from Snort to the standard output. The app is included with the Ryu installation, but you can also get it from Github. Before running this app, make a copy of the file to your home directory and edit the following line to include the correct port number for snort0 interface (mine is 5)def init__(self, *args, *kwargs): super(SimpleSwitchSnort, self).__init(args, **kwargs) self.snort = kwargs['snortlib'] self.snort_port = 5 self.mac_to_port = {}Edit the file run_controller.sh to use simple_switch_snort.py.
#!/bin/bash ryu-manager ~/simple_switch_snort.py ~/flowmanager/flowmaanger.pyStop the ryu service if it is running and run the ryu-manager manually:
ovs@ovs-machine:~$ sudo service ryu stop ovs@ovs-machine:~$ ./run_controllerThe FlowManager will show that the flows added include additional OUTPUT action that copies packets to port 5. In another terminal launch Snort manually:
ovs@ovs-machine:~$ sudo snort -i snort0 -A unsock -l /tmp -c /etc/snort/snort.confWatch the alert messages sent from Snort to the Ryu’s app being displayed on the terminal running the ryu-manager. You can also ping between Host 1 and Host 2 to generate alerts related to ICMP Echo.
Wrapping up
Congratulation! You have managed to build an OpenFlow switch with integrated Ryu controller and Snort IDS/IPS. Here are a couple of notes to conclude this post: We have disabled ryu and snort services temporarily to be able to launch them manually and see the alert messages. So, remember to enable and restart both services.ovs@ovs-machine:~$ sudo service ryu --full-restart ovs@ovs-machine:~$ sudo service ryu status ● ryu.service - RYU Controller Loaded: loaded (/etc/systemd/system/ryu.service; enabled; vendor preset: enab Active: active (running) since Thu 2018-09-13 10:53:29 ADT; 6s ago ...Re-configure Snort to change the start mode to ‘boot’. The service will restart upon existing the configuration.
ovs@ovs-machine:~$ sudo dpkg-reconfigure snort ... ovs@ovs-machine:~$ service ryu status ● ryu.service - RYU Controller Loaded: loaded (/etc/systemd/system/ryu.service; enabled; vendor preset: enab Active: active (running) since Thu 2018-09-13 11:56:12 ADT; 1min 12s ago ...The next time you boot the service, both Ryu and Snort will be active and connected but you won’t be able to see the alert messages. What you can do is modify the app simple_switch_snort.py to do something with the alert message other than printing them. Perhaps, you can send them to a log file or make the app react to the messages by modifying the flow tables (to drop suspect packets, for example). This is the true benefit of connecting Snort to Ryu.