# 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. Maven使用笔记(三)Maven的工作原理

    概述 Maven是一个项目管理工具,他包含了一个项目对象模型,一组标准集合,一个项目生命周期,一个依赖管理系统和用来运行定义生命周期阶段中插件目标的逻辑. Maven是基于约定优于配置的思想来管理代码 ...

  2. oracle 执行计划详解

    简介:     本文全面详细介绍oracle执行计划的相关的概念,访问数据的存取方法,表之间的连接等内容.     并有总结和概述,便于理解与记忆! +++ 目录 ---     一.相关的概念    ...

  3. Java运算符优先级(转)

    转自:http://www.cnblogs.com/gw811/archive/2012/10/13/2722752.html Java运算符优先级 序列号 符号 名称 结合性(与操作数) 目数 说明 ...

  4. MATLAB学习笔记(十一)——MATLAB图形用户界面设计

    (一)菜单设计 一.建立用户菜单 1.概况: 用户菜单一般含有一级菜单和二级菜单,乃至多级菜单.每一级菜单又包含多个菜单项.建立菜单可以使用uimenu函数. 2.uimenu函数调用: %建立一级菜 ...

  5. 【转】有赞的kylin方案

    http://tech.youzan.com/kylin-mondrian-saiku/

  6. 源码安装Memcached服务器及其2种PHP客户端

    本文所用源码包皆为当时最新stable稳定版. 安装memcached服务器 先安装libevent, 最新版为2.0.21 tar -zxvf libevent-2.0.21-stable.tar. ...

  7. [HTTP那些事] JSON数据

    随着Android的发展,各路大神的贡献,我们可用的轮子越来越多.比如HTTP请求框架,有自家的Volley,Square的okhttp, async-http-lib, 还有聚合版的xUtils以及 ...

  8. Unity中制作游戏的快照游戏支持玩家拍快照

    Unity中制作游戏的快照游戏支持玩家拍快照 有些游戏支持玩家“拍快照”,也就是将游戏的精彩瞬间以图片的形式记录下来的功能.这个功能比较有趣,而且以后的用途也会很广,为此本节打算介绍:截取矩形区域内游 ...

  9. 金子上的友情[XDU1011]

    Problem 1011 - 金子上的友情 Time Limit: 1000MS   Memory Limit: 65536KB   Difficulty: Total Submit: 336  Ac ...

  10. UVa 11181 条件概率

    题意:n个人选r个人,每个人被选中的概率为pi,问最后每个人被选中的概率是多少. sol:就是个简单的概率题,范围还特别小,深搜秒出...然而公式什么的很多还是需要注意的...     条件概率的公式 ...