Monday 10 June 2013

OpenFlow Switch on Raspberry Pi Part 4: Ryu config to make LINC a L2 switch

This is part 4 in the series of building an OpenFlow switch on the Raspberry Pi.  In  part 3 of this OpenFlow series we had built our switch, installed the Ryu controller and got the switch to connect to the controller.

Before carry on it's worth expanding a bit more on the Ryu controller. It's an OpenSource controller developed by NTT in Japan and released under the commercially friendly Apache2 licence.

When we started the controller in the last post it was in fact useless. The controller is a little unusual since it is in-fact a framework for control. It effectively allows you to build network APIs (Application Programmer Interfaces). You need to write your own business logic to define what you want the network to do. This business logic is defined in fairly short python programmes.  I'm not a python programmer and have never learnt - I will have to get my 15 year old son to teach me how to programme in python!

You can think of these python scripts as apps - potentially there could be an app store for the network equivalent to iTunes.

Fortunately we have some ready made python scripts available which we can use with ryu and at least do some useful things.

Before we shutdown ryu and get it to use one of these scripts let's see what state LINC is in to confirm that the ryu controller has in-fact done nothing!

The LINC switch uses Erlang syntax so the commands may seem complex and unfamiliar.

 The command to view the forwarding table on LINC is:

(linc@raspberrypi)1> ets:tab2list(linc:lookup(0, flow_table_0)).

The response is:
[]

An empty table. The switch doesn't know what to do with any packets that arrive since the forwarding table is empty.

OK lets shutdown ryu by doing CTRL-C.

We now need to use one of the python scripts which came with LINC. There are also scripts which came with ryu but they don't all work with LINC.

You can either use the script at:

https://github.com/FlowForwarding/LINC-Switch/blob/master/scripts/ryu/l2_switch_v1_3.py

Copy and paste this into a file called l2_switch_v1_3.py on the computer where your controller is running or copy the script in the LINC installation directory.  This python switch allows LINC to function as a layer 2 switch.  It could alternatively be an app to turn it into a firewall or whatever network device you dream up!
LINC-Switch/scripts/ryu/l2_switch_v13.py 

We now need to restart ryu with the python script

$  LINC-Switch/scripts/ryu/l2_switch_v13.py

In my case ryu now reports:

installing new source mac received from port 4
installing new source mac received from port 4
installing new source mac received from port 4

Let's now look at LINC. In the console we have

10:08:40.542 [info] Created port: {port,4,[{queues_status,disabled},{queues,[]},{config,{port_configuration,undefined,up,false,false,false}},{features,{features,undefined,'100Mb-FD',true,copper,unsupported}},{queues,[]},{interface,"eth0"}]}
10:08:40.780 [info] Created port: {port,3,[{queues_status,disabled},{queues,[]},{config,{port_configuration,undefined,up,false,false,false}},{features,{features,undefined,'100Mb-FD',true,copper,unsupported}},{queues,[]},{interface,"wlan0"}]}
10:08:41.020 [info] Created port: {port,2,[{queues_status,disabled},{queues,[]},{config,{port_configuration,undefined,up,false,false,false}},{features,{features,undefined,'100Mb-FD',true,copper,unsupported}},{queues,[]},{interface,"eth2"}]}
10:08:41.289 [info] Created port: {port,1,[{queues_status,disabled},{queues,[]},{config,{port_configuration,undefined,up,false,false,false}},{features,{features,undefined,'100Mb-FD',true,copper,unsupported}},{queues,[]},{interface,"eth1"}]}
10:08:41.307 [info] Connected to controller 192.168.1.4:6633/0 using OFP v4

[{flow_entry,{0,#Ref<0.0.0.897>},
             0,
             {ofp_match,[]},
             <<0,0,0,0,0,0,0,0>>,
             [],
             {1370,599721,348915},
             {infinity,0,0},
             {infinity,0,0},
             [{ofp_instruction_write_actions,4,
                                             [{ofp_action_output,16,controller,65535}]}]}]


(linc@raspberrypi)5> ets:tab2list(linc:lookup(0, flow_table_0)).

We now have something in the flow table

[{flow_entry,{0,#Ref<0.0.0.1215>},
             0,
             {ofp_match,[]},
             <<0,0,0,0,0,0,0,0>>,
             [],
             {1370,600106,318986},
             {infinity,0,0},
             {infinity,0,0},
             [{ofp_instruction_write_actions,4,
                                             [{ofp_action_output,16,controller,65535}]}]},
 {flow_entry,{123,#Ref<0.0.0.1219>},
             123,
             {ofp_match,[{ofp_field,openflow_basic,in_port,false,
                                    <<0,0,0,4>>,
                                    undefined},
                         {ofp_field,openflow_basic,eth_src,false,
                                    <<192,63,14,164,241,229>>,
                                    undefined}]},
             <<0,0,0,0,0,0,0,0>>,
             [],
             {1370,600110,573684},
             {infinity,0,0},
             {infinity,0,0},
             [{ofp_instruction_goto_table,6,1}]},
 {flow_entry,{123,#Ref<0.0.0.1223>},
             123,
             {ofp_match,[{ofp_field,openflow_basic,in_port,false,
                                    <<0,0,0,4>>,
                                    undefined},
                         {ofp_field,openflow_basic,eth_src,false,
                                    <<0,31,208,172,255,21>>,
                                    undefined}]},
             <<0,0,0,0,0,0,0,0>>,
             [],
             {1370,600110,817008},
             {infinity,0,0},
             {infinity,0,0},
             [{ofp_instruction_goto_table,6,1}]},
 {flow_entry,{123,#Ref<0.0.0.1254>},
             123,
             {ofp_match,[{ofp_field,openflow_basic,in_port,false,
                                    <<0,0,0,4>>,
                                    undefined},
                         {ofp_field,openflow_basic,eth_src,false,
                                    <<116,240,109,81,22,170>>,
                                    undefined}]},
             <<0,0,0,0,0,0,0,0>>,
             [],
             {1370,600112,591632},
             {infinity,0,0},
             {infinity,0,0},
             [{ofp_instruction_goto_table,6,1}]}]

Yours will probably look different. In the next post we'll do a little test scenario and controlled traffic.


Thursday 6 June 2013

OpenFlow Switch on Raspberry Pi Part 3: Ryu OpenFlow controller connected to LINC switch

Back to the cheapest OpenFlow switch in the world based on the Raspberry Pi.

So far we've built our OpenFlow switch, installed Erlang and installed the LINC OpenFlow switch.

The switch isn't much use without a controller.  There are a number of OpenFlow switches out there. Beacon, Flashlight, Ryu and more.

Beacon is rather old so doesn't work with LINC which supports the latest protocols. For my OpenFlow lab I'm going to use the NTT ryu controller.

Ryu is written in python so should work on your platform. It's even possible to run it on the Raspberry Pi but for now I'm running it on an OpenSuse machine.

The Ryu webpage is  http://osrg.github.io/ryu/  and the download page takes you to Ryu's github page at https://github.com/osrg/ryu

If you have an up-to-date python installation you may be able to install Ryu with the simple command

pip install ryu
If you're like me you're installation will need some work to make it install!

I didn't have pip (the python package installer) so I had to install "distribute" first

$ wget http://python-distribute.org/distribute_setup.py
$ python distribute_setup.py

Then I had to install pip

$ wget http://pypi.python.org/packages/source/p/pip/pip-0.7.2.tar.gz
$ tar xzf pip-0.7.2.tar.gz
$ cd pip-0.7.2
$  python setup.py install

Then I installed ryu

$ pip install ryu

You may be lucky and it runs.
$ pip install ryu

I was not so lucky.  I was missing the gevent python library

$  pip install gevent

However it wouldn't install so I had to install the python-devel RPM next
I then installed a bunch of missing python libraries

$ pip install gevent
$ pip install webob
$ pip install routes

Finally gevent worked !
$ pip install gevent
$ ryu-manager
loading app ryu.controller.ofp_handler
instantiating app ryu.controller.ofp_handler

Yes a running OpenFlow controller !!
Although LINC supports auto controller configuration using OF Config, we'll set it up manually.

Now we have a running controller on a machine, we need to find the IP address of the machine. On Linux
the command is /sbin/ifconfig -a

My address is 192.168.1.4

On windows machines you need a cmd prompt and type ipconfig /all to find the IP address

We now need to edit the LINC config file to point it at the controller.
To edit the file on the Pi

pi@raspberrypi ~/LINC-Switch $ sudo vi rel/linc/releases/1.0/sys.config

      %% Configuration of the controllers switch will connect to. Ideally
       %% this list should be empty and assignement should be handled by an
       %% OF-Config client.
       %% Default OFP controller port is 6633.
       {controllers,
        [
         {"Switch0-DefaultController", "192.168.1.4", 6633, tcp}
        ]},

       %% Configure ports available to the switch when using the
       %% userspace backend according to your system setup.
       %% * Under Linux all TAP interfaces must be set up beforehand
       %%   as persistent.
       %% * Under MacOSX TAP interfaces are created during node
       %%   startup (which requires setting an ip parameter).

 This line is commented out with a %% by default so the %% needs to be removed and the correct IP address inserted.

OK time to let the switch connect to the controller!

pi@raspberrypi ~/LINC-Switch $ sudo rel/linc/bin/linc console
Exec: /home/pi/LINC-Switch/rel/linc/erts-5.9.3.1/bin/erlexec -boot /home/pi/LINC-Switch/rel/linc/releases/1.0/linc -mode embedded -config /home/pi/LINC-Switch/rel/linc/releases/1.0/sys.config -args_file /home/pi/LINC-Switch/rel/linc/releases/1.0/vm.args -- console
Root: /home/pi/LINC-Switch/rel/linc

Erlang R15B03 (erts-5.9.3.1) [source] [async-threads:0] [kernel-poll:false]

20:52:02.873 [info] Application lager started on node linc@raspberrypi
20:52:02.883 [info] Application ssh started on node linc@raspberrypi
20:52:02.912 [info] Application enetconf started on node linc@raspberrypi
20:52:02.927 [info] Application linc started on node linc@raspberrypi
Eshell V5.9.3.1  (abort with ^G)
(linc@raspberrypi)1>
(linc@raspberrypi)1> 20:52:03.230 [info] Created port: {port,4,[{queues_status,disabled},{queues,[]},{config,{port_configuration,undefined,up,false,false,false}},{features,{features,undefined,'100Mb-FD',true,copper,unsupported}},{queues,[]},{interface,"eth0"}]}
20:52:03.481 [info] Created port: {port,3,[{queues_status,disabled},{queues,[]},{config,{port_configuration,undefined,up,false,false,false}},{features,{features,undefined,'100Mb-FD',true,copper,unsupported}},{queues,[]},{interface,"wlan0"}]}
20:52:03.736 [info] Created port: {port,2,[{queues_status,disabled},{queues,[]},{config,{port_configuration,undefined,up,false,false,false}},{features,{features,undefined,'100Mb-FD',true,copper,unsupported}},{queues,[]},{interface,"eth2"}]}
20:52:03.974 [info] Created port: {port,1,[{queues_status,disabled},{queues,[]},{config,{port_configuration,undefined,up,false,false,false}},{features,{features,undefined,'100Mb-FD',true,copper,unsupported}},{queues,[]},{interface,"eth1"}]}
20:52:04.020 [info] Connected to controller 192.168.1.4:6633/0 using OFP v4

(linc@raspberrypi)1>

Success!

In my next post in this series we'll start doing some useful experiments.

OpenFlow and WiFi

My last post answered a question that came up about the latency between an OpenFlow switch and the OpenFlow Controller. I've also been asked about OpenFlow and the relevance for WiFi.

OpenFlow often talks about ethernet packets so the question was "Can we extend this idea to WLAN systems such as wifi?"

Of course!

WiFi is of course ethernet based.  It's basically just a different layer 1 technology (physical interface) compared to the physical interface of wired cable. Supporting OpenFlow for WiFi is simple - at least conceptually.

Before delving into WiFi I though it would be interesting to look at the wider OpenFlow landscape beyond ethernet.  OpenFlow is attracting a lot of interest in traditional telecom area of optical networking where there is no Ethernet at all. It is viewed as a possible mechanism to dynamically provision optical pipes on the fly. There is a technology already defined in the optical networking space for pipe provisioning -
G.ASON (automatically switched optical network). It has has some traction but is not universally adopted therefore many vendors are looking at OpenFlow as possible next generation mechanism for optical provisioning.

So back to WiFi. Because WiFi is ethernet packet based, OpenFlow can be used today but why would you want to use OpenFlow in a WiFi environment?

If you are building a public WiFi HotSpot today, you'll need some registration service. Often people have a landing page where they can register to gain access. Usually what happens is the person connecting to the HotSpot opens a browser and starts to connect to say Google. The person is not authenticated so there needs to be a box to intercept the connection to Google and redirect them to the landing page instead.  Many corporate users may have their internal proxy set and the box doing the intercept may fail to redirect these users to the landing page.  If the draconian security department at the corporate hasn’t given the user permission to change the web site proxy address then they are stuck!

An OpenFlow enabled WiFi Access Point would however  not require the additional complexity of box to intercept the connections - it can do it itself.

The customer's device sends some packets and the OpenFlow device can look to see if this person is authenticated and simply forward the packets to a specific address if they are not.  The OpenFlow architecture would do this by default. The packet would arrive at the OpenFlow switch and let's say it's been configured to use the MAC address lookup. If the MAC address is unknown to the switch, the packet would be referred to the controller.  The controller can then do a MAC look-up to see if this user is already a subscriber - this could easily be done via LDAP for example. If the customer was unknown they could be redirected to a registration or landing page and if they were a subscriber they would simply be connected to the Internet service without any effort on their part.

In fact it would be possible to provide a seamless service for customers. A vastly better experience for WiFi than is currently in place.

So if the customer's MAC address is already known then the OpenFlow WiFi access device could even authenticate the user solely on MAC address and direct them to their service without all the hassle of signing in.

Monday 3 June 2013

OpenFlow and latency

One of the questions that came up about OpenFlow last week was about latency.  It was a good question and exposes the lack of clarity about the role between the switch and the controller.

The switch is the data plane. It's role is to move data packets from A to B
The controller makes decisions for the switch whether to move specific packets from A to B or whether they should go from A to C or even block them.

In order for the controller to make this decision, the switch consults with the controller when a new packet arrives. Now this is where the confusion arose. The switch only refers the first packet it doesn't know how to handle. The controller then communicates a rule and the switch continues to use that rule until either it expires or is told to use a new rule. Subsequent packets in the flow do not need the controller to be involved. The switch does the work.

If the switch referred every packet to the controller then:
  1. The bandwidth pipe to the controller would need to be huge since all the traffic would be copied to the controller
  2. The latency would increase by at least the round trip time to the controller
  3. The controller would need higher availability,  scale massively and have more real-time performance than the switch
 The first packet referred to the controller does indeed increase the latency by at least the round trip to the controller but subsequent packets do not. Once the switch knows how to handle subsequent packets the controller does not need to be involved.

The end result is the pipes to the controller do not need to be big.  It needs to be big enough to handle the packets for how many new flows per second there are x the span of control of the controller. If you have an environment where there are lots of long duration flows (eg video serving) there will be les work for the controller to do compared to say web surfing which tends to be lots of short duration packet exchanges.

There is little information to help network planners dimension the link to the controller so this is one area where there is an opportunity for academics to do research and potentially for tools to be developed.  It is likely that the tools would be closed loop taking the traffic profiles and extrapolating for dimensioning.