l2_multi.py
# 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)
l2_multi.py的更多相关文章
- python调用py中rar的路径问题。
1.python调用py,在py中的os.getcwd()获取的不是py的路径,可以通过os.path.split(os.path.realpath(__file__))[0]来获取py的路径. 2. ...
- Python导入其他文件中的.py文件 即模块
import sys sys.path.append("路径") import .py文件
- import renumber.py in pymol
cp renumber.py /usr/local/lib/python2.7/dist-packages/pymol import renumber or run /path/to/renumber ...
- python gettitle.py
#!/usr/bin/env python # coding=utf-8 import threading import requests import Queue import sys import ...
- 解决 odoo.py: error: option --addons-path: The addons-path 'local-addons/' does not seem to a be a valid Addons Directory!
情况说明 odoo源文件路径-/odoo-dev/odoo/: 我的模块插件路径 ~/odoo-dev/local-addons/my-module 在my-module中创建了__init__.py ...
- caffe机器学习自带图片分类器classify.py实现输出预测结果的概率及caffe的web_demo例子运行实例
caffe机器学习环境搭建及python接口编译参见我的上一篇博客:机器学习caffe环境搭建--redhat7.1和caffe的python接口编译 1.运行caffe图片分类器python接口 还 ...
- 【转】Windows下使用libsvm中的grid.py和easy.py进行参数调优
libsvm中有进行参数调优的工具grid.py和easy.py可以使用,这些工具可以帮助我们选择更好的参数,减少自己参数选优带来的烦扰. 所需工具:libsvm.gnuplot 本机环境:Windo ...
- MySqlNDB使用自带的ndb_setup.py安装集群
在用Mysql做集群时,使用Mysql的NDB版本更易于集群的扩展,稳定和数据的实时性. 我们可以使用Mysql自带的工具进行集群安装与管理:ndb_setup.py.位于Mysql的安装目录bin下 ...
- 将做好的py文件打包成模块,供别人安装调用
现在要将写完的3个py文件,打包. 步骤: 1.新建一个文件夹setup(名字随便取),在setup文件夹下,再新建一个文件夹financeapi. 2.将上面4个py文件拷贝至financeapi文 ...
随机推荐
- HTTP协议超级详解
HTTP协议简介 超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式.协作式和超媒体信息系统的应用层协议.HTTP是万维网的数据通信的基础. ...
- c# 接口使用
- sed进阶教程
寻址规则 常规寻址 如果没有指定地址,那么命令将应用于每一行. 如果只有一个地址,那么命令应用于与这个地址匹配的任意行. 如果指定了由逗号分隔的两个地址,那么命令应用于匹配第一个地址(不包括第一个地址 ...
- springboot中的小技巧
1.Thymeleaf 在时使用Thymeleaf的时候不用设置html标签:在默认的Thymeleaf的页面中,对于.html的要求比较严格,少写或者多写标签,都会被报错,而在springboot ...
- PS批量制作获奖证书并导出PNG
其实方法和"使用PS批量制作视频字幕"的方法类似.区别在于制作视频字幕时导出成psd格式就可以直接导入Premiere中使用了,而获奖证书考虑到打印设备有无PS的不确定性,可能需要 ...
- GPT分区格式
1. GPT定义 全局唯一标识分区表(GUID partition table, 缩写:GPT)是一个实体硬盘的分区表的结构布局的标准.它是可扩展固件接口(UEFI)标准的一部分,被用于替代BIOS系 ...
- 基于Java+Selenium的WebUI自动化测试框架(六)---浏览器初始化
本篇我们来讨论,如何写一个浏览器初始化的类.在写之前,先思考一下,我们需要一个什么样的初始化? 先来看看使用原生的Java + selenium是怎么做的.(以firefox为例) System.se ...
- python中set(集合),深浅拷贝以及一些补充知识点
1.set集合 特点:无序,不重复,元素必须可哈希(不可变) 作用:去重复 本身是可变的数据类型.有增删改查操作.{集合的增删改查操作应用较少,这里不做详细介绍了(这里的增有一个方法update注意这 ...
- 1260:【例9.4】拦截导弹(Noip1999)
题目来源:http://ybt.ssoier.cn:8088/problem_show.php?pid=1260 1260:[例9.4]拦截导弹(Noip1999) 时间限制: 1000 ms ...
- PYTHON WEB开发学习路线
两年大数据广告项目测试:项目覆盖几千万用户前景还不错:只是在工作中感觉测试太无力,最近准备辞职转web开发:同时会离开成都,(/(ㄒoㄒ)/~~待了6年,要离开喜欢的城市,喜欢的女孩很难受) ps:列 ...