Hello developers
I implement host discovery functions and api
In class Host, it contains:
1. port: which port it connected
2. ipv4: it ipv4 address
3. ipv6: it ipv6 address
4. mac: mac address
Last time(http://sourceforge.net/p/ryu/mailman/message/34194496/) I
implement it in one packet in handler.
Now I split two packet in handler for lldp and host discovery.
Also, I ignore some logical ports and switch-to-switch ports.
Please check patch file for more details
Thanks.
--
Yi Tseng (a.k.a Takeshi)
Taiwan National Chiao Tung University
Department of Computer Science
W2CNLab
From d5bf34982b69c276897388333c65cc2a94d876ec Mon Sep 17 00:00:00 2001
From: Takeshi <a86487...@gmail.com>
Date: Thu, 16 Jul 2015 19:24:33 +0800
Subject: [PATCH 1/2] add host discovery functions
Signed-off-by: Takeshi <a86487...@gmail.com>
---
ryu/topology/api.py | 8 +++
ryu/topology/event.py | 23 ++++++++
ryu/topology/switches.py | 139 ++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 167 insertions(+), 3 deletions(-)
diff --git a/ryu/topology/api.py b/ryu/topology/api.py
index 7485a8e..cd72b84 100644
--- a/ryu/topology/api.py
+++ b/ryu/topology/api.py
@@ -35,4 +35,12 @@ def get_all_link(app):
return get_link(app)
+def get_host(app, dpid=None):
+ rep = app.send_request(event.EventHostRequest(dpid))
+ return rep.hosts
+
+
+def get_all_host(app):
+ return get_host(app)
+
app_manager.require_app('ryu.topology.switches', api_style=True)
diff --git a/ryu/topology/event.py b/ryu/topology/event.py
index bd87ab0..83881c0 100644
--- a/ryu/topology/event.py
+++ b/ryu/topology/event.py
@@ -126,3 +126,26 @@ class EventLinkReply(event.EventReplyBase):
def __str__(self):
return 'EventLinkReply<dst=%s, dpid=%s, links=%s>' % \
(self.dst, self.dpid, len(self.links))
+
+
+class EventHostRequest(event.EventRequestBase):
+ # if dpid is None, replay all hosts
+ def __init__(self, dpid=None):
+ super(EventHostRequest, self).__init__()
+ self.dst = 'switches'
+ self.dpid = dpid
+
+ def __str__(self):
+ return 'EventHostRequest<src=%s, dpid=%s>' % \
+ (self.src, self.dpid)
+
+
+class EventHostReply(event.EventReplyBase):
+ def __init__(self, dst, dpid, hosts):
+ super(EventHostReply, self).__init__(dst)
+ self.dpid = dpid
+ self.hosts = hosts
+
+ def __str__(self):
+ return 'EventHostReply<dst=%s, dpid=%s, hosts=%s>' % \
+ (self.dst, self.dpid, len(self.hosts))
diff --git a/ryu/topology/switches.py b/ryu/topology/switches.py
index ba956d1..31b4b0c 100644
--- a/ryu/topology/switches.py
+++ b/ryu/topology/switches.py
@@ -29,10 +29,12 @@ from ryu.lib import addrconv, hub
from ryu.lib.mac import DONTCARE_STR
from ryu.lib.dpid import dpid_to_str, str_to_dpid
from ryu.lib.port_no import port_no_to_str
-from ryu.lib.packet import packet, ethernet, lldp
+from ryu.lib.packet import packet, ethernet
+from ryu.lib.packet import lldp, ether_types
+from ryu.lib.packet import arp, ipv4, ipv6
from ryu.ofproto.ether import ETH_TYPE_LLDP
-from ryu.ofproto import ofproto_v1_0
from ryu.ofproto import nx_match
+from ryu.ofproto import ofproto_v1_0
from ryu.ofproto import ofproto_v1_2
from ryu.ofproto import ofproto_v1_3
from ryu.ofproto import ofproto_v1_4
@@ -158,6 +160,68 @@ class Link(object):
return 'Link: %s to %s' % (self.src, self.dst)
+class Host(object):
+ # This is data class passed by EventHostXXX
+ def __init__(self, mac, port):
+ super(Host, self).__init__()
+ self.port = port
+ self.mac = mac
+ self.ipv4 = []
+ self.ipv6 = []
+
+ def to_dict(self):
+ d = {'mac': self.mac,
+ 'ipv4': self.ipv4,
+ 'ipv6': self.ipv6,
+ 'port': self.port.to_dict()}
+ return d
+
+ def __eq__(self, host):
+ return self.mac == host.mac and self.port == host.port
+
+ def __str__(self):
+ msg = 'Host<mac=%s, port=%s,' % (self.mac, str(self.port))
+ msg += ','.join(self.ipv4)
+ msg += ','.join(self.ipv6)
+ msg += '>'
+ return msg
+
+
+class HostState(dict):
+ # mac address -> Host class
+ def __init__(self):
+ super(HostState, self).__init__()
+
+ def add(self, host):
+ mac = host.mac
+ self.setdefault(mac, host)
+
+ def update_ip(self, host, ip_v4=None, ip_v6=None):
+ mac = host.mac
+ host = None
+ if mac in self:
+ host = self[mac]
+
+ if not host:
+ return
+
+ if ip_v4 != None and ip_v4 not in host.ipv4:
+ host.ipv4.append(ip_v4)
+
+ if ip_v6 != None and ip_v6 not in host.ipv6:
+ host.ipv6.append(ip_v6)
+
+ def get_by_dpid(self, dpid):
+ result = []
+
+ for mac in self:
+ host = self[mac]
+ if host.port.dpid == dpid:
+ result.append(host)
+
+ return result
+
+
class PortState(dict):
# dict: int port_no -> OFPPort port
# OFPPort is defined in ryu.ofproto.ofproto_v1_X_parser
@@ -451,6 +515,7 @@ class Switches(app_manager.RyuApp):
self.port_state = {} # datapath_id => ports
self.ports = PortDataState() # Port class -> PortData class
self.links = LinkState() # Link class -> timestamp
+ self.hosts = HostState() # mac address -> Host class list
self.is_active = True
self.link_discovery = self.CONF.observe_links
@@ -518,6 +583,13 @@ class Switches(app_manager.RyuApp):
self.send_event_to_observers(event.EventLinkDelete(rev_link))
self.ports.move_front(dst)
+ def _is_edge_port(self, port):
+ for link in self.links:
+ if port == link.src or port == link.dst:
+ return False
+
+ return True
+
@set_ev_cls(ofp_event.EventOFPStateChange,
[MAIN_DISPATCHER, DEAD_DISPATCHER])
def state_change_handler(self, ev):
@@ -680,7 +752,7 @@ class Switches(app_manager.RyuApp):
dp.ofproto.OFP_VERSION)
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
- def packet_in_handler(self, ev):
+ def lldp_packet_in_handler(self, ev):
if not self.link_discovery:
return
@@ -738,6 +810,54 @@ class Switches(app_manager.RyuApp):
if self.explicit_drop:
self._drop_packet(msg)
+ @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
+ def host_discovery_packet_in_handler(self, ev):
+ msg = ev.msg
+ pkt = packet.Packet(msg.data)
+ eth = pkt.get_protocols(ethernet.ethernet)[0]
+
+ # ignore lldp packet
+ if eth.ethertype == ETH_TYPE_LLDP:
+ return
+
+ datapath = msg.datapath
+ dpid = datapath.id
+ port_no = -1
+
+ if msg.datapath.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION:
+ port_no = msg.in_port
+ else:
+ port_no = msg.match['in_port']
+
+ port = self._get_port(dpid, port_no)
+
+ # can't find this port(ex: logic port)
+ if not port:
+ return
+ # ignore switch-to-switch port
+ if not self._is_edge_port(port):
+ return
+
+ host_mac = eth.src
+ host = Host(host_mac, port)
+
+ # arp packet, update both location and ip
+ if eth.ethertype == ether_types.ETH_TYPE_ARP:
+ self.hosts.add(host)
+ arp_pkt = pkt.get_protocols(arp.arp)[0]
+ self.hosts.update_ip(host, ip_v4=arp_pkt.src_ip)
+
+ # ipv4 packet, update ip only
+ elif eth.ethertype == ether_types.ETH_TYPE_IP:
+ ipv4_pkt = pkt.get_protocols(ipv4.ipv4)[0]
+ self.hosts.update_ip(host, ip_v4=ipv4_pkt.src)
+
+ # ipv6 packet, update ip only
+ elif eth.ethertype == ether_types.ETH_TYPE_IPV6:
+ # TODO: need to handle NDP
+ ipv6_pkt = pkt.get_protocols(ipv6.ipv6)[0]
+ self.hosts.update_ip(host, ip_v6=ipv6_pkt.src)
+
def send_lldp_packet(self, port):
try:
port_data = self.ports.lldp_sent(port)
@@ -862,3 +982,16 @@ class Switches(app_manager.RyuApp):
links = [link for link in self.links if link.src.dpid == dpid]
rep = event.EventLinkReply(req.src, dpid, links)
self.reply_to_request(req, rep)
+
+ @set_ev_cls(event.EventHostRequest)
+ def host_request_handler(self, req):
+ dpid = req.dpid
+ hosts = []
+ if dpid is None:
+ for mac in self.hosts:
+ hosts.append(self.hosts[mac])
+ else:
+ hosts = self.hosts.get_by_dpid(dpid)
+
+ rep = event.EventHostReply(req.src, dpid, hosts)
+ self.reply_to_request(req, rep)
--
2.3.2 (Apple Git-55)
------------------------------------------------------------------------------
Don't Limit Your Business. Reach for the Cloud.
GigeNET's Cloud Solutions provide you with the tools and support that
you need to offload your IT needs and focus on growing your business.
Configured For All Businesses. Start Your Cloud Today.
https://www.gigenetcloud.com/
_______________________________________________
Ryu-devel mailing list
Ryu-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/ryu-devel