Skip to content

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.

Published inRYUSDN

35 Comments

  1. abhi abhi

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

      • abhi abhi

        yes it is working, thanks.

      • abhi abhi

        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))

  2. Anonymous Anonymous

    yes, all given above

  3. Nicolai Nicolai

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

  4. Nicolai Nicolai

    Why the value of links empty?

  5. Aiman Aiman

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

  6. Ben Ben

    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

  7. 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.

    • samuel samuel

      Yes, I also found this problem. It seems that only when ryu is started after starting the mininet, then the link data can be collected. I wonder what causes this problem?

  8. Ben Ben

    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

    • 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

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

  10. Anonymous Anonymous

    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

  11. 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.

  12. Ben Ben

    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

  13. Ben Ben

    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)

  14. 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.

  15. 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

  16. 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.

  17. Ben Ben

    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

  18. Anonymous Anonymous

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

    How to resolve this issue?

  19. Anonymous Anonymous

    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

  20. Edison de Queiroz Albuquerque Edison de Queiroz Albuquerque

    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.

  21. Edison de Queiroz Albuquerque Edison de Queiroz Albuquerque

    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.

  22. Khubaib Khubaib

    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?)

  23. sh sh

    Can I use the topology discovery in Ryu for wireless networks? How?

  24. HusaynS HusaynS

    How can I use topology discover module to identify legacy routers as well in my network. Im working on Hybrid SDN where I have to route traffic between two SDN switches connected to a router.

  25. I believe the topology discover uses LLDP, thus it should be able to identify other routers as well.

  26. Madiha Mateen Madiha Mateen

    I am using RYU controller for my link recovery use case. Ryu have Topology discovery module . My topology is fat tree. Topology discovery module is initiating but not getting the topology and its links. Ping between hosts is not successful. Any help on this would be appreciated.
    Thank You

  27. abdelhak barouk abdelhak barouk

    hello every one ,
    i am new in sdn and i have a project for vlans management ,i use java to do this
    but i am in a possition of how i can recuperate all topology’s links
    if somoene help me
    thnx

  28. Naim Naim

    hi sir when i run the shortestpath file give me such kind of error
    loading app shortestpath.py
    Traceback (most recent call last):
    File “/home/ubuntu/.local/lib/python3.6/site-packages/ryu/utils.py”, line 99, in import_module
    return _import_module_file(modname)
    File “/home/ubuntu/.local/lib/python3.6/site-packages/ryu/utils.py”, line 88, in _import_module_file
    return load_source(modname, abspath)
    File “/home/ubuntu/.local/lib/python3.6/site-packages/ryu/utils.py”, line 42, in load_source
    return loader.load_module(name)
    File “”, line 399, in _check_name_wrapper
    File “”, line 823, in load_module
    File “”, line 682, in load_module
    File “”, line 265, in _load_module_shim
    File “”, line 684, in _load
    File “”, line 665, in _load_unlocked
    File “”, line 674, in exec_module
    File “”, line 781, in get_code
    File “”, line 741, in source_to_code
    File “”, line 219, in _call_with_frames_removed
    File “/home/ubuntu/ryu/ryu/app/shortestpath.py”, line 152
    print “**********List of links”
    ^
    SyntaxError: Missing parentheses in call to ‘print’. Did you mean print(“**********List of links”)?

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
    File “/home/ubuntu/.local/bin/ryu-manager”, line 8, in
    sys.exit(main())
    File “/home/ubuntu/.local/lib/python3.6/site-packages/ryu/cmd/manager.py”, line 98, in main
    app_mgr.load_apps(app_lists)
    File “/home/ubuntu/.local/lib/python3.6/site-packages/ryu/base/app_manager.py”, line 415, in load_apps
    cls = self.load_app(app_cls_name)
    File “/home/ubuntu/.local/lib/python3.6/site-packages/ryu/base/app_manager.py”, line 392, in load_app
    mod = utils.import_module(name)
    File “/home/ubuntu/.local/lib/python3.6/site-packages/ryu/utils.py”, line 104, in import_module
    return importlib.import_module(modname)
    File “/usr/lib/python3.6/importlib/__init__.py”, line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
    File “”, line 994, in _gcd_import
    File “”, line 971, in _find_and_load
    File “”, line 941, in _find_and_load_unlocked
    File “”, line 219, in _call_with_frames_removed
    File “”, line 994, in _gcd_import
    File “”, line 971, in _find_and_load
    File “”, line 953, in _find_and_load_unlocked
    ModuleNotFoundError: No module named ‘shortestpath’
    help me how to handle this issue

  29. Fizz Fizz

    I need some help in my RYU application. I need to get the response time of a host co0nnected to my topology and use these parameters in a calculation in my code.

    Average response time request serviced by controller.
    response time of requests serviced by the switch.
    Maximum response time of controller packets.
    Minimum response time of controller.

    Can you please guide me…?

Leave a Reply to castroflaviojr Cancel reply

Your email address will not be published. Required fields are marked *