summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorTakeshi <a86487817@gmail.com>2015-07-16 19:24:33 +0800
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2015-07-18 15:28:46 +0900
commitaf63e5f51954fe1f080c1804940affce9a5d13cb (patch)
tree9db0cd577c878e5399d4e8b6fd6b2c824371a7f3
parentb5c4b9ae519ac1c1bef4087ba602c602da4a4e95 (diff)
add host discovery functions
Signed-off-by: Takeshi <a86487817@gmail.com> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r--ryu/topology/api.py8
-rw-r--r--ryu/topology/event.py23
-rw-r--r--ryu/topology/switches.py139
3 files changed, 167 insertions, 3 deletions
diff --git a/ryu/topology/api.py b/ryu/topology/api.py
index 7485a8e6..cd72b84b 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 bd87ab00..83881c00 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 ba956d15..31b4b0c8 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)