# Copyright 2012-2013 James McCauley
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License. """
A shortest-path forwarding application. This is a standalone L2 switch that learns ethernet addresses
across the entire network and picks short paths between them. You shouldn't really write an application this way -- you should
keep more state in the controller (that is, your flow tables),
and/or you should make your topology more static. However, this
does (mostly) work. :) Depends on openflow.discovery
Works with openflow.spanning_tree
""" from pox.core import core
import pox.openflow.libopenflow_01 as of
from pox.lib.revent import *
from pox.lib.recoco import Timer
from collections import defaultdict
from pox.openflow.discovery import Discovery
from pox.lib.util import dpid_to_str
import time log = core.getLogger() # Adjacency map. [sw1][sw2] -> port from sw1 to sw2
adjacency = defaultdict(lambda:defaultdict(lambda:None)) # Switches we know of. [dpid] -> Switch
switches = {} # ethaddr -> (switch, port)
mac_map = {} # [sw1][sw2] -> (distance, intermediate)
path_map = defaultdict(lambda:defaultdict(lambda:(None,None))) # Waiting path. (dpid,xid)->WaitingPath
waiting_paths = {} # Time to not flood in seconds
FLOOD_HOLDDOWN = 5 # Flow timeouts
FLOW_IDLE_TIMEOUT = 10
FLOW_HARD_TIMEOUT = 30 # How long is allowable to set up a path?
PATH_SETUP_TIME = 4 def _calc_paths ():
"""
Essentially Floyd-Warshall algorithm
""" def dump ():
for i in sws:
for j in sws:
a = path_map[i][j][0]
#a = adjacency[i][j]
if a is None: a = "*"
print a,
print sws = switches.values()
path_map.clear()
for k in sws:
for j,port in adjacency[k].iteritems():
if port is None: continue
path_map[k][j] = (1,None)
path_map[k][k] = (0,None) # distance, intermediate #dump() for k in sws:
for i in sws:
for j in sws:
if path_map[i][k][0] is not None:
if path_map[k][j][0] is not None:
# i -> k -> j exists
ikj_dist = path_map[i][k][0]+path_map[k][j][0]
if path_map[i][j][0] is None or ikj_dist < path_map[i][j][0]:
# i -> k -> j is better than existing
path_map[i][j] = (ikj_dist, k) #print "--------------------"
#dump() def _get_raw_path (src, dst):
"""
Get a raw path (just a list of nodes to traverse)
"""
if len(path_map) == 0: _calc_paths()
if src is dst:
# We're here!
return []
if path_map[src][dst][0] is None:
return None
intermediate = path_map[src][dst][1]
if intermediate is None:
# Directly connected
return []
return _get_raw_path(src, intermediate) + [intermediate] + \
_get_raw_path(intermediate, dst) def _check_path (p):
"""
Make sure that a path is actually a string of nodes with connected ports returns True if path is valid
"""
for a,b in zip(p[:-1],p[1:]):
if adjacency[a[0]][b[0]] != a[2]:
return False
if adjacency[b[0]][a[0]] != b[1]:
return False
return True def _get_path (src, dst, first_port, final_port):
"""
Gets a cooked path -- a list of (node,in_port,out_port)
"""
# Start with a raw path...
if src == dst:
path = [src]
else:
path = _get_raw_path(src, dst)
if path is None: return None
path = [src] + path + [dst] # Now add the ports
r = []
in_port = first_port
for s1,s2 in zip(path[:-1],path[1:]):
out_port = adjacency[s1][s2]
r.append((s1,in_port,out_port))
in_port = adjacency[s2][s1]
r.append((dst,in_port,final_port)) assert _check_path(r), "Illegal path!" return r class WaitingPath (object):
"""
A path which is waiting for its path to be established
"""
def __init__ (self, path, packet):
"""
xids is a sequence of (dpid,xid)
first_switch is the DPID where the packet came from
packet is something that can be sent in a packet_out
"""
self.expires_at = time.time() + PATH_SETUP_TIME
self.path = path
self.first_switch = path[0][0].dpid
self.xids = set()
self.packet = packet if len(waiting_paths) > 1000:
WaitingPath.expire_waiting_paths() def add_xid (self, dpid, xid):
self.xids.add((dpid,xid))
waiting_paths[(dpid,xid)] = self @property
def is_expired (self):
return time.time() >= self.expires_at def notify (self, event):
"""
Called when a barrier has been received
"""
self.xids.discard((event.dpid,event.xid))
if len(self.xids) == 0:
# Done!
if self.packet:
log.debug("Sending delayed packet out %s"
% (dpid_to_str(self.first_switch),))
msg = of.ofp_packet_out(data=self.packet,
action=of.ofp_action_output(port=of.OFPP_TABLE))
core.openflow.sendToDPID(self.first_switch, msg) core.l2_multi.raiseEvent(PathInstalled(self.path)) @staticmethod
def expire_waiting_paths ():
packets = set(waiting_paths.values())
killed = 0
for p in packets:
if p.is_expired:
killed += 1
for entry in p.xids:
waiting_paths.pop(entry, None)
if killed:
log.error("%i paths failed to install" % (killed,)) class PathInstalled (Event):
"""
Fired when a path is installed
"""
def __init__ (self, path):
Event.__init__(self)
self.path = path class Switch (EventMixin):
def __init__ (self):
self.connection = None
self.ports = None
self.dpid = None
self._listeners = None
self._connected_at = None def __repr__ (self):
return dpid_to_str(self.dpid) def _install (self, switch, in_port, out_port, match, buf = None):
msg = of.ofp_flow_mod()
msg.match = match
msg.match.in_port = in_port
msg.idle_timeout = FLOW_IDLE_TIMEOUT
msg.hard_timeout = FLOW_HARD_TIMEOUT
msg.actions.append(of.ofp_action_output(port = out_port))
msg.buffer_id = buf
switch.connection.send(msg) def _install_path (self, p, match, packet_in=None):
wp = WaitingPath(p, packet_in)
for sw,in_port,out_port in p:
self._install(sw, in_port, out_port, match)
msg = of.ofp_barrier_request()
sw.connection.send(msg)
wp.add_xid(sw.dpid,msg.xid) def install_path (self, dst_sw, last_port, match, event):
"""
Attempts to install a path between this switch and some destination
"""
p = _get_path(self, dst_sw, event.port, last_port)
if p is None:
log.warning("Can't get from %s to %s", match.dl_src, match.dl_dst) import pox.lib.packet as pkt if (match.dl_type == pkt.ethernet.IP_TYPE and
event.parsed.find('ipv4')):
# It's IP -- let's send a destination unreachable
log.debug("Dest unreachable (%s -> %s)",
match.dl_src, match.dl_dst) from pox.lib.addresses import EthAddr
e = pkt.ethernet()
e.src = EthAddr(dpid_to_str(self.dpid)) #FIXME: Hmm...
e.dst = match.dl_src
e.type = e.IP_TYPE
ipp = pkt.ipv4()
ipp.protocol = ipp.ICMP_PROTOCOL
ipp.srcip = match.nw_dst #FIXME: Ridiculous
ipp.dstip = match.nw_src
icmp = pkt.icmp()
icmp.type = pkt.ICMP.TYPE_DEST_UNREACH
icmp.code = pkt.ICMP.CODE_UNREACH_HOST
orig_ip = event.parsed.find('ipv4') d = orig_ip.pack()
d = d[:orig_ip.hl * 4 + 8]
import struct
d = struct.pack("!HH", 0,0) + d #FIXME: MTU
icmp.payload = d
ipp.payload = icmp
e.payload = ipp
msg = of.ofp_packet_out()
msg.actions.append(of.ofp_action_output(port = event.port))
msg.data = e.pack()
self.connection.send(msg) return log.debug("Installing path for %s -> %s %04x (%i hops)",
match.dl_src, match.dl_dst, match.dl_type, len(p)) # We have a path -- install it
self._install_path(p, match, event.ofp) # Now reverse it and install it backwards
# (we'll just assume that will work)
p = [(sw,out_port,in_port) for sw,in_port,out_port in p]
self._install_path(p, match.flip()) def _handle_PacketIn (self, event):
def flood ():
""" Floods the packet """
if self.is_holding_down:
log.warning("Not flooding -- holddown active")
msg = of.ofp_packet_out()
# OFPP_FLOOD is optional; some switches may need OFPP_ALL
msg.actions.append(of.ofp_action_output(port = of.OFPP_FLOOD))
msg.buffer_id = event.ofp.buffer_id
msg.in_port = event.port
self.connection.send(msg) def drop ():
# Kill the buffer
if event.ofp.buffer_id is not None:
msg = of.ofp_packet_out()
msg.buffer_id = event.ofp.buffer_id
event.ofp.buffer_id = None # Mark is dead
msg.in_port = event.port
self.connection.send(msg) packet = event.parsed loc = (self, event.port) # Place we saw this ethaddr
oldloc = mac_map.get(packet.src) # Place we last saw this ethaddr if packet.effective_ethertype == packet.LLDP_TYPE:
drop()
return if oldloc is None:
if packet.src.is_multicast == False:
mac_map[packet.src] = loc # Learn position for ethaddr
log.debug("Learned %s at %s.%i", packet.src, loc[0], loc[1])
elif oldloc != loc:
# ethaddr seen at different place!
if core.openflow_discovery.is_edge_port(loc[0].dpid, loc[1]):
# New place is another "plain" port (probably)
log.debug("%s moved from %s.%i to %s.%i?", packet.src,
dpid_to_str(oldloc[0].dpid), oldloc[1],
dpid_to_str( loc[0].dpid), loc[1])
if packet.src.is_multicast == False:
mac_map[packet.src] = loc # Learn position for ethaddr
log.debug("Learned %s at %s.%i", packet.src, loc[0], loc[1])
elif packet.dst.is_multicast == False:
# New place is a switch-to-switch port!
# Hopefully, this is a packet we're flooding because we didn't
# know the destination, and not because it's somehow not on a
# path that we expect it to be on.
# If spanning_tree is running, we might check that this port is
# on the spanning tree (it should be).
if packet.dst in mac_map:
# Unfortunately, we know the destination. It's possible that
# we learned it while it was in flight, but it's also possible
# that something has gone wrong.
log.warning("Packet from %s to known destination %s arrived "
"at %s.%i without flow", packet.src, packet.dst,
dpid_to_str(self.dpid), event.port) if packet.dst.is_multicast:
log.debug("Flood multicast from %s", packet.src)
flood()
else:
if packet.dst not in mac_map:
log.debug("%s unknown -- flooding" % (packet.dst,))
flood()
else:
dest = mac_map[packet.dst]
match = of.ofp_match.from_packet(packet)
self.install_path(dest[0], dest[1], match, event) def disconnect (self):
if self.connection is not None:
log.debug("Disconnect %s" % (self.connection,))
self.connection.removeListeners(self._listeners)
self.connection = None
self._listeners = None def connect (self, connection):
if self.dpid is None:
self.dpid = connection.dpid
assert self.dpid == connection.dpid
if self.ports is None:
self.ports = connection.features.ports
self.disconnect()
log.debug("Connect %s" % (connection,))
self.connection = connection
self._listeners = self.listenTo(connection)
self._connected_at = time.time() @property
def is_holding_down (self):
if self._connected_at is None: return True
if time.time() - self._connected_at > FLOOD_HOLDDOWN:
return False
return True def _handle_ConnectionDown (self, event):
self.disconnect() class l2_multi (EventMixin): _eventMixin_events = set([
PathInstalled,
]) def __init__ (self):
# Listen to dependencies
def startup ():
core.openflow.addListeners(self, priority=0)
core.openflow_discovery.addListeners(self)
core.call_when_ready(startup, ('openflow','openflow_discovery')) def _handle_LinkEvent (self, event):
def flip (link):
return Discovery.Link(link[2],link[3], link[0],link[1]) l = event.link
sw1 = switches[l.dpid1]
sw2 = switches[l.dpid2] # Invalidate all flows and path info.
# For link adds, this makes sure that if a new link leads to an
# improved path, we use it.
# For link removals, this makes sure that we don't use a
# path that may have been broken.
#NOTE: This could be radically improved! (e.g., not *ALL* paths break)
clear = of.ofp_flow_mod(command=of.OFPFC_DELETE)
for sw in switches.itervalues():
if sw.connection is None: continue
sw.connection.send(clear)
path_map.clear() if event.removed:
# This link no longer okay
if sw2 in adjacency[sw1]: del adjacency[sw1][sw2]
if sw1 in adjacency[sw2]: del adjacency[sw2][sw1] # But maybe there's another way to connect these...
for ll in core.openflow_discovery.adjacency:
if ll.dpid1 == l.dpid1 and ll.dpid2 == l.dpid2:
if flip(ll) in core.openflow_discovery.adjacency:
# Yup, link goes both ways
adjacency[sw1][sw2] = ll.port1
adjacency[sw2][sw1] = ll.port2
# Fixed -- new link chosen to connect these
break
else:
# If we already consider these nodes connected, we can
# ignore this link up.
# Otherwise, we might be interested...
if adjacency[sw1][sw2] is None:
# These previously weren't connected. If the link
# exists in both directions, we consider them connected now.
if flip(l) in core.openflow_discovery.adjacency:
# Yup, link goes both ways -- connected!
adjacency[sw1][sw2] = l.port1
adjacency[sw2][sw1] = l.port2 # If we have learned a MAC on this port which we now know to
# be connected to a switch, unlearn it.
bad_macs = set()
for mac,(sw,port) in mac_map.iteritems():
if sw is sw1 and port == l.port1: bad_macs.add(mac)
if sw is sw2 and port == l.port2: bad_macs.add(mac)
for mac in bad_macs:
log.debug("Unlearned %s", mac)
del mac_map[mac] def _handle_ConnectionUp (self, event):
sw = switches.get(event.dpid)
if sw is None:
# New switch
sw = Switch()
switches[event.dpid] = sw
sw.connect(event.connection)
else:
sw.connect(event.connection) def _handle_BarrierIn (self, event):
wp = waiting_paths.pop((event.dpid,event.xid), None)
if not wp:
#log.info("No waiting packet %s,%s", event.dpid, event.xid)
return
#log.debug("Notify waiting packet %s,%s", event.dpid, event.xid)
wp.notify(event) def launch ():
core.registerNew(l2_multi) timeout = min(max(PATH_SETUP_TIME, 5) * 2, 15)
Timer(timeout, WaitingPath.expire_waiting_paths, recurring=True)

可以看到,通过Floyd算法计算两个终端之间的最短路径。

pox目录中的交换机mac地址学习模块 l2_multi源码的更多相关文章

  1. 【小菜学网络】交换机与MAC地址学习

    上一小节介绍了 集线器 ,一种工作于物理层的简单网络设备.由于集线器采用广播的方式中继.转发物理信号,传输效率受到极大制约. 精准转发 为了解决集线器工作效率低下的尴尬,我们需要设计一种更高级的网络设 ...

  2. Mininet实验 MAC地址学习

    实验目的 了解交换机的MAC地址学习过程. 了解交换机对已知单播.未知单播和广播帧的转发方式. 实验原理 MAC(media access control,介质访问控制)地址是识别LAN节点的标识.M ...

  3. mac地址学习笔记

    MAC(Media Access Control或者Medium Access Control)地址, 意译为媒体访问控制,或称为物理地址.硬件地址,用来定义网络设备的位置. 在OSI模型中,第三层网 ...

  4. [转]OpenTK学习笔记(1)-源码、官网地址

    OpenTK源码下载地址:https://github.com/opentk/opentk OpenTK使用Nuget安装命令:OpenTK:Install-Package OpenTK -Versi ...

  5. MQTT再学习 -- MQTT 客户端源码分析

    MQTT 源码分析,搜索了一下发现网络上讲的很少,多是逍遥子的那几篇. 参看:逍遥子_mosquitto源码分析系列 参看:MQTT libmosquitto源码分析 参看:Mosquitto学习笔记 ...

  6. MAC 下编译 ANDROID P 源码 提示 internal error: Could not find a supported mac sdk: ["10.10" "10.11" "10.12" "10.13"]

    MAC 下编译 ANDROID P 源码出现下面的问题: ninja: no work to do. [21/21] out/soong/.bootstrap/bin/soong_build out/ ...

  7. Hadoop学习笔记(9) ——源码初窥

    Hadoop学习笔记(9) ——源码初窥 之前我们把Hadoop算是入了门,下载的源码,写了HelloWorld,简要分析了其编程要点,然后也编了个较复杂的示例.接下来其实就有两条路可走了,一条是继续 ...

  8. c# winform 中的 工具栏自动隐藏 splitter用法 带源码

    c# winform 中的 工具栏自动隐藏 splitter用法 带源码 代码下载地址 http://download.csdn.net/detail/simadi/7649313

  9. Android中Canvas绘图基础详解(附源码下载) (转)

    Android中Canvas绘图基础详解(附源码下载) 原文链接  http://blog.csdn.net/iispring/article/details/49770651   AndroidCa ...

随机推荐

  1. 自定义viewgroup实现ArcMenu

    最终效果如下 实现思路 通过效果图,会有几个问题: a.动画效果如何实现 可以看出动画是从顶点外外发射的,可能有人说,那还不简单,默认元素都在定点位置,然后TraslateAnimation就好了:这 ...

  2. iOS的I/O操作

    一般而言,处理文件时都要经历以下四个步骤: 1.创建文件 2.打开文件,以便在后面的I/O操作中引用该文件 3.对打开的文件执行I/O操作(读取.写入.更新) 4.关闭文件 iOS中,对文件常见的处理 ...

  3. 数据库优化和SQL操作的相关题目

    SQL操作 1.有一个数据库表peope,表有字段name,age,address三个属性(注:没有主键).现在如果表中有重复的数据,请删去重复只留下其中的一条.重复的定义就是两条记录的name,ag ...

  4. C# Window Form解决播放amr格式音乐问题

    最近搞一个项目,需要获取微信端语音文件,下载之后发现是AMR格式的录音文件,这下把我搞晕了,C#中的4种播放模式不支持播放AMR,想到都觉得头痛,如何是好?最后找到的方案,其实也简单:windows ...

  5. Java Hour 26 Execution

    有句名言,叫做10000小时成为某一个领域的专家.姑且不辩论这句话是否正确,让我们到达10000小时的时候再回头来看吧. 26 Hours. Java 虚拟机启动的时候加载一个指定的类然后调用该类中的 ...

  6. C# 从入门到精通

    int i=null; int? i=null; 我们用一个问号 来指定这个值类型是可空的 属性是只读的

  7. android模拟器使用PC串口调试

    android模拟器使用PC串口调试1.模拟器可以使用PC的串口  启动模拟器并加载PC串口 命令如下:  运行 emulator @模拟器名称 -qemu -serial COM12.查看串口是否被 ...

  8. C/C++知识点

    1 cout<<endl;什么意思? 就是回车的意思~ 相当于C语言里面的printf("\n"); 2 cin>> 键盘输入 例子:double  r=1 ...

  9. Spring AOP 详解

    AOP使用场景 AOP用来封装横切关注点,具体可以在下面的场景中使用: Authentication 权限 Caching 缓存 Context passing 内容传递 Error handling ...

  10. PDA手持终端集成一体打印 二次开发

    PDA手持终端集成一体打印 二次开发支持 VS2008或VS2005开发工具 c#或C++开发语言 Mobile6.5,支持GSM通话,GPRS,EDGE网络;内置wifi,蓝牙,gps商场单品管理小 ...