Topology Discovery with Ryu

How can be openflow used to do topology discovery? Why would you want to do that?

You might be interest in doing topology discovery on your topology for multiple reasons such as applying custom forwarding strategies from a centralized global view of the network. You may want something simple like running spanning tree or you may want to run shortest path forwarding. OpenFlow gives you the power to do that or even harder things.

SDN controllers can capitalize on the centralized view of the network and perform all kinds of operations such as pre-computing and installing backup paths.*

In this tutorial I’ll briefly describe the topology discovery module of the RYU controller and guide you through the development of a very naive application to print the information of the network.

The dependencies for this tutorial are:

  • Mininet 2.1 (If you haven’t installed it yet, follow this link or get the original installation instructions from this link)
  • Ryu (If you haven’t installed it yet, follow this link)
  • OpenVSwitch 2.0 (If you haven’t installed it yet, follow this link)

Copy the simple_switch.py application from RYU to a new file called topo_learner.py. We’ll start from that:

cd ryu/ryu/app/  
cp simple_switch.py topo_learner.py

Now, import the topology module adding this two lines:

from ryu.topology import event, switches
from ryu.topology.api import get_switch, get_link

Also add the following function at the end of the code.

@set_ev_cls(event.EventSwitchEnter)
    def get_topology_data(self, ev):
    switch_list = get_switch(self.topology_api_app, None)
    switches=[switch.dp.id for switch in switch_list]
    links_list = get_link(self.topology_api_app, None)
    links=[(link.src.dpid,link.dst.dpid,{'port':link.src.port_no}) for link in links_list]

The event EventSwitchEnter will trigger the activation of get_topology_data(). Next we call get_switch() to get the list of objects Switch, and get_link() to get the list of objects Link. This objects are defined in the topology.switches file. Then, we build a list with all the switches ([switches]) and next a list with all the links [(srcNode, dstNode, port)]. Notice that we also get the port from the source node that arrives at the destination node, as that information will be necessary later during the forwarding step.

Let’s go ahead and add a “print links” and “print switches” to get_topology_data() and see what happens.

Let’s test it:
Start ryu, then start mininet. ATTENTION: make sure you run ryu-manager sp.py --observe-links. The –observe-links option will turn on the topology RYU app. You should see something like this:

[1, 2, 3, 4]
[(2, 3, {'port': 3}), (3, 2, {'port': 2}), (3, 4, {'port': 3}), (2, 1, {'port': 2}), (4, 3, {'port': 2}), (1, 2, {'port': 2})]

If you forget to add the –observe-links option you will see something like this:

[1, 2, 3, 4]
[]

Now that topology discover is working we can move ahead and do lot’s of cool stuff with it, such as build a central view of the network using a library and then run shortest path forwarding, for example.

Advertisements

I'm a Network Engineer with software development experiences. MSCS from Georgia Tech. CCNA certified. ONF-SDN certified.

Posted in RYU, SDN
27 comments on “Topology Discovery with Ryu
  1. abhi says:

    i have done exactly the same thing, but it is showing
    AttributeError: ‘SimpleSwitch’ object has no attribute ‘topology_api_app’

    Like

  2. Make sure you are importing the right modules.

    Like

  3. Anonymous says:

    yes, all given above

    Like

  4. abhi says:

    yes it is working, thanks.

    Like

  5. abhi says:

    hey form your code i got an idea, what i am doing is storing the information of new links created in a text file but i don,t know how to proceed further, can you help me, this is the code.

    def dictify network(self, src, dst):
    G=nx.graph()
    fi=open(“/ryu/app/links.txt”,’r’)
    p1=re.compile(“dpid=\d+”)
    for line in fi:
    m1=re.findall(p1,line)
    G.add_node(m1[0])
    G.add_node(m1[1])
    G.add_edge(m1[0],m1[1],weight=random.randomrange(100,201))
    return nx.shortest_path(G,”dpid=”+str(src),”dpid=”+str(dst))

    Like

  6. Nicolai says:

    Sorry, i have an error object has no attribute ‘topology_api_app’ , how to fix it? previous answer has not helped

    Like

  7. Nicolai says:

    Why the value of links empty?

    Like

  8. Aiman says:

    Thanks a lot, I need to simulate precomouting and installing backup paths , Any help with this would be very very appreciated. thanks again

    Like

  9. Ben says:

    Hi,
    I have the same problem as Nicolai.. The link list is always empty. I have tried many differennt things, but could not get it running correctly.

    What I tried was:

    1) starting the app with different position of –observe-links
    2) using the Zodiac FX and Raspberry PIs to set up a topology
    3) using the OpenVswitch for simulated switch and topology
    4) adding the described lines from above to the simple_switch_13.py code and using the example provided by the author above (https://github.com/castroflavio/ryu/blob/master/ryu/app/shortestpath.py)

    All this results in a given datapathID for the switch and an empty links list.. You can find my problems described with more details here: http://forums.northboundnetworks.com/index.php?topic=322.0

    Any help is very appreciated!

    Thanks,
    Ben

    Like

  10. Hi Ben and Nicolai. I’m troubleshooting this application again. I find that for whatever reason, now the application works better when starting ryu after starting mininet.

    Like

  11. Ben says:

    Hi,

    Thanks for checking again! In my current setup (topology discrovery of a ring topology with 4 zodiac fx openflow switches) the above code works fine and I usually start Ryu after

    What would have really saved much time in my troubleshouting: Mention in your tutorial, that a link is only a connection between switches and NOT a connection between a host and a switch. I was trying to get link info from Ryu with a topology that unfortunately had no links in it.. (I guess it is a networking beginner fault.)

    PS: Is there a way to get the information from a link, that detemines which port on a switch is connected to which (other) switch. So if there is a link between switch A and switch B, I want to know which port on switch A is connected switch B. This is very important for routing and there has to be a way, but I dont see it at the moment.

    Cheers,
    Ben

    Like

  12. There’s definitely a way.

    Otherwise I wouldn’t be able to do routing. Take a look at my shortest path code/post. I don’t remember now how is it. But I’ll get back to you.

    Can I ask you something. Is the zodiac FX hardware assisted OF forwarding? Or is all of it in software?

    Flavio Castro

    Like

  13. Paul says:

    The Zodiac FX is a hardware device but all OpenFlow processing is done in software.

    Like

  14. Anonymous says:

    Hi Flavio Castro,

    I also debugged your code for shortestpath https://github.com/castroflavio/ryu/blob/master/ryu/app/shortestpath.py (because I am interested in your routing strategy).

    Seems that line 115 (next=path[path.index(dpid)+1]) does not work properly, because the bounds of the path list are not checked before.
    I tried this:

    # …

    path_length = nx.shortest_path_length(self.net,src,dst)

    if path_length != 0:
    next=path[path.index(dpid)+1]
    out_port=self.net[dpid][next][‘port’]

    # …

    and sent ARP, IGMP and UDP packets, which did not work, as the out_port is never be written, because the calculated path is either 00:00:00:00:00:00 (path_length = 0), or the dst was not known. Maybe an end-to-end connection for example with TCP would work.

    I dont understand this line (which I think is the critical part of the routing, as it extracts the port info for the link):

    out_port=self.net[dpid][next][‘port’]

    Also, this line only works for OpenFlow 1.0. I need it for OpenFlow 1.3. Do you know how the equivalent could look like?

    Cheers,
    Ben

    Like

  15. Thanks for your comments.

    I have not tested this for OF 1.3.

    Regarding line 115, what is the topology you are trying to run when that fails? Is that for OF 1.3 or 1.0?

    the net data structure is a dictionary organized in the following way.
    { switch1: {
    neighbor1: {
    ‘port’: port }
    }}

    You can obtain all the links for given switch by reading its neighbors, the port represents the output port that connects the switch to the neighbor.

    What does it mean to have a path length 0 for you?

    If a destination is unknown, then we will flood the packet.

    Like

  16. Ben says:

    Hi,

    thanks for your answer!

    I tried your OpenFlow 1.0 app with OpenFlow 1.3 switches. This is the resulting error:

    Error in the datapath 000070b3d56cd425 from (‘10.234.2.72’, 49197)
    hub: uncaught exception: Traceback (most recent call last):
    File “/usr/local/lib/python2.7/dist-packages/ryu/lib/hub.py”, line 54, in _launch
    return func(*args, **kwargs)
    File “/usr/local/lib/python2.7/dist-packages/ryu/controller/controller.py”, line 447, in datapath_connection_factory
    datapath.serve()
    File “/usr/local/lib/python2.7/dist-packages/ryu/controller/controller.py”, line 365, in serve
    self._recv_loop()
    File “/usr/local/lib/python2.7/dist-packages/ryu/controller/controller.py”, line 129, in deactivate
    method(self)
    File “/usr/local/lib/python2.7/dist-packages/ryu/controller/controller.py”, line 269, in _recv_loop
    handler(ev)
    File “/usr/local/lib/python2.7/dist-packages/ryu/controller/ofp_handler.py”, line 205, in switch_features_handler
    datapath.ports = msg.ports
    AttributeError: ‘OFPSwitchFeatures’ object has no attribute ‘ports’

    Thank you for clarifying. So you are kind of mixing the “learning switch”
    approach (learn src; if dst known forward to port otherwise flood) with the
    topology information you receive from Ryu’s topology API and you use the topo API
    only for calculating the shortest path. Am I right?

    Your approach will not work for me (I think), as I am interested in directly
    forwarding UDP traffic (unidirectional, multicast), so I will never be able to
    “learn” the path to my dst, as the dst will never be the src for another packet
    (it could be, but this is not sure).. I had a look at the LLDP packets that are
    automatically sent by the ryu topology api, when starting a controller with the
    –observe-links option and it seems, that this packets clearly provide the port
    info for a link.. I’m wondering why this info is not available through the Ryu
    topology API. Do I really have to parse the LLDP packets manually? 😦 (Ryu also
    parses them for the link info)

    I think path length 0 means that src == dst = true. if this is the case, your
    path is empty, so this line (115)

    next=path[path.index(dpid)+1]

    is likely to fail, or is python preventing this? (Im pretty new to python..)

    PS: To make things worse, my topology is a ring topology of 4 3-port switches
    (zodiac fx with 2 links and 1 host (raspberry pi) respectively) 😉

    Best regards,
    Ben

    Like

  17. Ben says:

    PSPS: When I change the version of your app to OF1.3 (by changing OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION] to OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]) the error looks like this:

    ProjectController: Exception occurred during handler processing. Backtrace from offending handler [_packet_in_handler] servicing event [EventOFPPacketIn] follows.
    Traceback (most recent call last):
    File “/usr/local/lib/python2.7/dist-packages/ryu/base/app_manager.py”, line 290, in _event_loop
    handler(ev)
    File “/home/ben/masterthesis/ryu_code/castro_shortest_path_example_original.py”, line 76, in _packet_in_handler
    self.net.add_edge(dpid,src,{‘port’:msg.in_port})
    AttributeError: ‘OFPPacketIn’ object has no attribute ‘in_port’

    The error in the last post was with OF1.0 (app)

    Like

  18. Okay.

    My first suggestion is not to develop the application using the zodiacFX. If you develop the app using software switches you end up isolating hardware bugs.

    After the application works, then you move to the hardware.

    Would you elaborate better on your use case? If you are not learning, then you need to pre-configure the network. In other words, you have to tell the SDN controller where each network is connected.

    Like

  19. Hi,

    I’m currently doing my masterthesis with the topic “Evaluation of SDN Methods for Distributed Industrial Automation Systems”. I am especially interested in UDP multicast traffic, which is directly routed with the topology information (ring network) without flooding or learning. Therefore I use IGMP (for host discovery and multicast group membership management) and LLDP (for link discovery). No other traffic will be forwarded in my small experimental setup, which contains 4 openflow enabled switches and 4 hosts. My topology is static, but it is dynamically discovered and the controller can also handle topology changes.

    I started to parse the LLDP manually, because Ryu’s topology information on links is not sufficient for my use case, as it does not contain explicit port information.

    Regards,
    Ben

    Like

  20. I wouldn’t parse LLDP manually.

    I can get the port information just fine. I’d recommend spend more time experimenting with RYU and figure out how to get the OF information.

    Like

  21. Ben says:

    Thanks for the advice!

    I had a look at the topology discovery API again. I thought the link passed by an ‘EventLinkxxx’ event contains src and dst of type ryu.topology.switches.Switch, but it actually does not contain Switches, but ports (ryu.topology.switches.Port), that are connected by the link. I think I was a little confused 😉

    Cheers,
    Ben

    Like

  22. Anonymous says:

    I am also facing the same issue:
    AttributeError: ‘SimpleSwitch’ object has no attribute ‘topology_api_app’

    How to resolve this issue?

    Like

  23. Anonymous says:

    I got this error: AttributeError: ‘SimpleSwitch’ object has no attribute ‘topology_api_app’

    I believe in the init you need to add
    self.topology_api_app = self

    Like

  24. Edison de Queiroz Albuquerque says:

    iside __init__ I wrote “self.topology_api_app = self”. this belongs to shortestpath.py but is missing in “topo_learner.py. After I did it, the program ran, but it is still missing the links.
    I’ll work on that. If I find the answer I’ll share.

    Like

  25. Edison de Queiroz Albuquerque says:

    the first test I made was with mininet and a single switch.
    the I use a network of 3 switches, tree topology, and the links appeared.
    I was expecting to see links from switch to host, but the links are only from switch to switch.
    Hope this helps somebody.

    Like

  26. Khubaib says:

    It doesn’t work for OpenFlow1.5

    You get data on the switches, but not the links. Anyone know how to do it (I had to make some changes to ryu/topology/switches.py to incorporate v1.5, perhaps more are required?)

    Like

Please leave any feedback

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Network Heresy

Tales of the network reformation

How to Do Great Research

Grad school survival advice from Nick Feamster and Alex Gray

n40lab

A blog about cloud, virtualization, sdn and centos

%d bloggers like this: