Hi. Thank you for posting this.
Some comment on format/style.
- please add your signed-off-by.
- please split up into logically independent part.
e.g. the hunk to the simple_switch.py should be independent patch
simple_switch_12.py
spe.py and spe_config.py seems to be independent.
- Although I haven't looked the patch closely, there remains stale
commented out lines. Please remove them.
thanks,
On Wed, Jul 17, 2013 at 01:43:29PM +1200, Sam Russell wrote:
> ---
> ryu/app/simple_switch.py | 2 +-
> ryu/app/simple_switch_12.py | 142 +++++++++++++++++++++++
> ryu/app/spe.py | 265
> +++++++++++++++++++++++++++++++++++++++++++
> ryu/app/spe_config.py | 5 +
> 4 files changed, 413 insertions(+), 1 deletion(-)
> create mode 100644 ryu/app/simple_switch_12.py
> create mode 100644 ryu/app/spe.py
> create mode 100644 ryu/app/spe_config.py
>
> diff --git a/ryu/app/simple_switch.py b/ryu/app/simple_switch.py
> index 0397bc4..bbee3d6 100644
> --- a/ryu/app/simple_switch.py
> +++ b/ryu/app/simple_switch.py
> @@ -106,4 +106,4 @@ class SimpleSwitch(app_manager.RyuApp):
> elif reason == ofproto.OFPPR_MODIFY:
> self.logger.info("port modified %s", port_no)
> else:
> - self.logger.info("Illeagal port state %s %s", port_no, reason)
> + self.logger.info("Illegal port state %s %s", port_no, reason)
> diff --git a/ryu/app/simple_switch_12.py b/ryu/app/simple_switch_12.py
> new file mode 100644
> index 0000000..f50bd0f
> --- /dev/null
> +++ b/ryu/app/simple_switch_12.py
> @@ -0,0 +1,142 @@
> +# Copyright (C) 2011 Nippon Telegraph and Telephone Corporation.
> +# OpenFlow 1.2 upgrade by Sam Russell
> +#
> +# 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.
> +
> +import logging
> +import struct
> +
> +from ryu.base import app_manager
> +from ryu.controller import mac_to_port
> +from ryu.controller import ofp_event
> +from ryu.controller.handler import MAIN_DISPATCHER
> +from ryu.controller.handler import set_ev_cls
> +from ryu.ofproto import ofproto_v1_2, ofproto_v1_2_parser
> +from ryu.lib.mac import haddr_to_str
> +
> +
> +# TODO: we should split the handler into two parts, protocol
> +# independent and dependant parts.
> +
> +# TODO: can we use dpkt python library?
> +
> +# TODO: we need to move the followings to something like db
> +
> +
> +class SimpleSwitch(app_manager.RyuApp):
> + OFP_VERSIONS = [ofproto_v1_2.OFP_VERSION]
> +
> + def __init__(self, *args, **kwargs):
> + super(SimpleSwitch, self).__init__(*args, **kwargs)
> + self.mac_to_port = {}
> +
> + def add_flow(self, datapath, table_id, match, instructions,
> buffer_id=ofproto_v1_2.OFP_NO_BUFFER):
> + ofproto = datapath.ofproto
> +
> + #wildcards = ofproto_v1_2.OFPFW_ALL
> + #wildcards &= ~ofproto_v1_2.OFPFW_IN_PORT
> + #wildcards &= ~ofproto_v1_2.OFPFW_DL_DST
> + # old standard matching - deprecated from 1.2 onwards
> +
> + #match = datapath.ofproto_parser.OFPMatch(
> + # wildcards, in_port, 0, dst,
> + # 0, 0, 0, 0, 0, 0, 0, 0, 0)
> +
> + #datapath, cookie, cookie_mask, table_id, command,
> + # idle_timeout, hard_timeout, priority, buffer_id, out_port,
> + # out_group, flags, match, instructions)
> + mod = datapath.ofproto_parser.OFPFlowMod(
> + datapath=datapath, cookie=0, cookie_mask=0, table_id=table_id,
> + command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0,
> + priority=0x8000, buffer_id=buffer_id,
> + out_port=ofproto_v1_2.OFPP_ANY, out_group=ofproto_v1_2.OFPG_ANY,
> + flags=ofproto.OFPFF_SEND_FLOW_REM, match=match,
> instructions=instructions)
> + datapath.send_msg(mod)
> +
> + @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
> + def _packet_in_handler(self, ev):
> + msg = ev.msg
> + datapath = msg.datapath
> + ofproto = datapath.ofproto
> +
> + dst, src, _eth_type = struct.unpack_from('!6s6sH', buffer(msg.data),
> 0)
> +
> + dpid = datapath.id
> + self.mac_to_port.setdefault(dpid, {})
> +
> + match = msg.match
> + in_port = 0
> + #iterate through fields - parser should handle this
> + #packet in dpid 20015998343868 from 08:00:27:15:d4:53 to
> ff:ff:ff:ff:ff:ff log_port 0 phy_port 0
> + #Field MTInPort(header=2147483652,length=8,n_bytes=4,value=2)
> + #Field MTEthType(header=2147486210,length=6,n_bytes=2,value=2054)
> + #Field MTArpOp(header=2147494402,length=6,n_bytes=2,value=1)
> + #Field
> MTMetadata(header=2147484680,length=12,n_bytes=8,value=18446744073709551615L)
> + #Field
> MTArpSha(header=2147495942,length=10,n_bytes=6,value="\x08\x00'\x15\xd4S")
> + #Field
> MTEthDst(header=2147485190,length=10,n_bytes=6,value='\xff\xff\xff\xff\xff\xff')
> + #Field MTArpSpa(header=2147494916,length=8,n_bytes=4,value=167772161)
> + #Field
> MTArpTha(header=2147496454,length=10,n_bytes=6,value='\x00\x00\x00\x00\x00\x00')
> +
> + for o in match.fields:
> + if isinstance(o, ofproto_v1_2_parser.MTInPort):
> + in_port = o.value
> + break
> +
> + self.logger.info("packet in dpid %s from %s to %s log_port %s",
> + dpid, haddr_to_str(src), haddr_to_str(dst),
> + in_port)
> +
> +
> + # do we know the mac?
> + if src not in self.mac_to_port[dpid]:
> + # learn the mac address to avoid FLOOD next time.
> + self.mac_to_port[dpid][src] = in_port
> + # set a flow to table 0 to allow packets through to table 1
> + match = datapath.ofproto_parser.OFPMatch()
> + match.set_in_port(in_port)
> + match.set_dl_src(src)
> + instructions =
> [datapath.ofproto_parser.OFPInstructionGotoTable(1)]
> + self.add_flow(datapath, 0, match, instructions)
> +
> +
> + if dst in self.mac_to_port[dpid]:
> + out_port = self.mac_to_port[dpid][dst]
> + actions = [datapath.ofproto_parser.OFPActionOutput(out_port,
> 1500)]
> + match = datapath.ofproto_parser.OFPMatch()
> + match.set_dl_dst(dst)
> + instructions =
> [datapath.ofproto_parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
> actions)]
> + self.add_flow(datapath, 1, match, instructions,
> buffer_id=msg.buffer_id)
> + else:
> + out_port = ofproto_v1_2.OFPP_FLOOD
> + actions = [datapath.ofproto_parser.OFPActionOutput(out_port,
> 1500)]
> + out = datapath.ofproto_parser.OFPPacketOut(
> + datapath=datapath, buffer_id=msg.buffer_id, in_port=in_port,
> + actions=actions)
> + datapath.send_msg(out)
> +
> + @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER)
> + def _port_status_handler(self, ev):
> + msg = ev.msg
> + reason = msg.reason
> + port_no = msg.desc.port_no
> +
> + ofproto = msg.datapath.ofproto
> + if reason == ofproto.OFPPR_ADD:
> + self.logger.info("port added %s", port_no)
> + elif reason == ofproto.OFPPR_DELETE:
> + self.logger.info("port deleted %s", port_no)
> + elif reason == ofproto.OFPPR_MODIFY:
> + self.logger.info("port modified %s", port_no)
> + else:
> + self.logger.info("Illegal port state %s %s", port_no, reason)
> diff --git a/ryu/app/spe.py b/ryu/app/spe.py
> new file mode 100644
> index 0000000..e23b55b
> --- /dev/null
> +++ b/ryu/app/spe.py
> @@ -0,0 +1,265 @@
> +# Copyright (C) 2011 Nippon Telegraph and Telephone Corporation.
> +# OpenFlow 1.2 upgrade, modified for peering exchanges by Sam Russell 2013
> +#
> +# 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.
> +
> +import logging
> +import struct
> +
> +from ryu.base import app_manager
> +from ryu.controller import mac_to_port
> +from ryu.controller import ofp_event
> +from ryu.topology import event
> +from ryu.controller.handler import MAIN_DISPATCHER, DEAD_DISPATCHER
> +from ryu.controller.handler import set_ev_cls
> +from ryu.controller.handler import set_ev_handler
> +from ryu.ofproto import ofproto_v1_2, ofproto_v1_2_parser, ether
> +from ryu.lib.mac import haddr_to_str
> +from ryu.lib import ip
> +from ryu.lib.packet import packet
> +import spe_config
> +
> +
> +
> +# TODO: we should split the handler into two parts, protocol
> +# independent and dependant parts.
> +
> +# TODO: can we use dpkt python library?
> +
> +# TODO: we need to move the followings to something like db
> +
> +
> +class SPE(app_manager.RyuApp):
> + OFP_VERSIONS = [ofproto_v1_2.OFP_VERSION]
> +
> + def __init__(self, *args, **kwargs):
> + super(SPE, self).__init__(*args, **kwargs)
> + self.dps = {}
> + self.mac_to_port = {}
> +
> + def add_flow(self, datapath, table_id, match, instructions,
> priority=0x8000, buffer_id=ofproto_v1_2.OFP_NO_BUFFER):
> + ofproto = datapath.ofproto
> +
> + #wildcards = ofproto_v1_2.OFPFW_ALL
> + #wildcards &= ~ofproto_v1_2.OFPFW_IN_PORT
> + #wildcards &= ~ofproto_v1_2.OFPFW_DL_DST
> + # old standard matching - deprecated from 1.2 onwards
> +
> + #match = datapath.ofproto_parser.OFPMatch(
> + # wildcards, in_port, 0, dst,
> + # 0, 0, 0, 0, 0, 0, 0, 0, 0)
> +
> + #datapath, cookie, cookie_mask, table_id, command,
> + # idle_timeout, hard_timeout, priority, buffer_id, out_port,
> + # out_group, flags, match, instructions)
> + mod = datapath.ofproto_parser.OFPFlowMod(
> + datapath=datapath, cookie=0, cookie_mask=0, table_id=table_id,
> + command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0,
> + priority=0x8000, buffer_id=buffer_id,
> + out_port=ofproto_v1_2.OFPP_ANY, out_group=ofproto_v1_2.OFPG_ANY,
> + flags=ofproto.OFPFF_SEND_FLOW_REM, match=match,
> instructions=instructions)
> + datapath.send_msg(mod)
> +
> + def add_arp_reply_catcher(self, datapath, ipaddr, port, table_id=0):
> + match = datapath.ofproto_parser.OFPMatch()
> + match.set_dl_type(ether.ETH_TYPE_ARP)
> + match.set_arp_opcode(2)
> + if port:
> + match.set_in_port(port)
> + if ipaddr:
> + match.set_arp_spa(ipaddr)
> + ofproto = datapath.ofproto
> + instructions = [datapath.ofproto_parser.OFPInstructionGotoTable(2)]
> + self.add_flow(datapath, table_id, match, instructions,
> priority=0x8000)
> +
> + def add_arp_request_forwarder(self, datapath, ipaddr, port, table_id=0):
> + match = datapath.ofproto_parser.OFPMatch()
> + match.set_dl_type(ether.ETH_TYPE_ARP)
> + match.set_arp_opcode(1)
> + match.set_arp_tpa(ipaddr)
> + ofproto = datapath.ofproto
> + actions = [datapath.ofproto_parser.OFPActionOutput(port, 1500)]
> + instructions =
> [datapath.ofproto_parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
> actions)]
> + self.add_flow(datapath, table_id, match, instructions,
> priority=0x8000)
> +
> +
> + def init_flows(self, datapath):
> + ofproto = datapath.ofproto
> + for port, ipaddr in spe_config.ports.iteritems():
> + self.add_arp_reply_catcher(datapath,
> ipaddr=ip.ipv4_to_bin(ipaddr), port=port, table_id=1)
> + self.add_arp_request_forwarder(datapath,
> ipaddr=ip.ipv4_to_bin(ipaddr), port=port, table_id=1)
> +
> + # make a flow to send all ARP packets to table 1
> + match = datapath.ofproto_parser.OFPMatch()
> + match.set_dl_type(ether.ETH_TYPE_ARP)
> + instructions = [datapath.ofproto_parser.OFPInstructionGotoTable(1)]
> + self.add_flow(datapath, 0, match, instructions)
> +
> +
> + @set_ev_cls(ofp_event.EventOFPStateChange, [MAIN_DISPATCHER,
> DEAD_DISPATCHER])
> + def switch_enter_handler(self, ev):
> + dp = ev.datapath
> + if ev.state == MAIN_DISPATCHER:
> + self.logger.info("Switch entered: %s", dp.id)
> + self.init_flows(dp)
> +
> + elif ev.state == DEAD_DISPATCHER:
> + if dp.id is None:
> + return
> + self.logger.info("Switch left: %s", dp.id)
> +
> + @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
> + def _packet_in_handler(self, ev):
> + msg = ev.msg
> + datapath = msg.datapath
> + ofproto = datapath.ofproto
> +
> + dst, src, _eth_type = struct.unpack_from('!6s6sH', buffer(msg.data),
> 0)
> +
> + dpid = datapath.id
> + self.mac_to_port.setdefault(dpid, {})
> +
> + match = msg.match
> + in_port = 0
> + ethtype = 0
> + #iterate through fields - parser should handle this
> + #packet in dpid 20015998343868 from 08:00:27:15:d4:53 to
> ff:ff:ff:ff:ff:ff log_port 0 phy_port 0
> + #Field MTInPort(header=2147483652,length=8,n_bytes=4,value=2)
> + #Field MTEthType(header=2147486210,length=6,n_bytes=2,value=2054)
> + #Field MTArpOp(header=2147494402,length=6,n_bytes=2,value=1)
> + #Field
> MTMetadata(header=2147484680,length=12,n_bytes=8,value=18446744073709551615L)
> + #Field
> MTArpSha(header=2147495942,length=10,n_bytes=6,value="\x08\x00'\x15\xd4S")
> + #Field
> MTEthDst(header=2147485190,length=10,n_bytes=6,value='\xff\xff\xff\xff\xff\xff')
> + #Field MTArpSpa(header=2147494916,length=8,n_bytes=4,value=167772161)
> + #Field
> MTArpTha(header=2147496454,length=10,n_bytes=6,value='\x00\x00\x00\x00\x00\x00')
> +
> + # we should build a dictionary of MTXXXX class names to speed this up
> + for o in match.fields:
> + self.logger.info("Field %s", str(o))
> + if isinstance(o, ofproto_v1_2_parser.MTInPort):
> + in_port = o.value
> + break
> +
> + self.logger.info("packet in dpid %s from %s to %s log_port %s",
> + dpid, haddr_to_str(src), haddr_to_str(dst),
> + in_port)
> +
> + # parse packet
> +
> + pkt = packet.Packet(msg.data)
> + eth_pkt = pkt.next()
> + ethtype = eth_pkt.ethertype
> +
> + # if ARP (request) then flood and don't make a flow
> + if ethtype == ether.ETH_TYPE_ARP:
> + # if ARP reply then drop
> + arp_pkt = pkt.next()
> + if arp_pkt.opcode == 2:
> + # check config
> + if in_port not in spe_config.ports:
> + self.logger.info("Port %d not configured, trying to ARP
> as %s", in_port, ip.ipv4_to_str(arp_pkt.src_ip))
> + return
> + if ip.ipv4_to_bin(spe_config.ports[in_port]) !=
> arp_pkt.src_ip:
> + self.logger.info("Dropping spoofed ARP from port %d IP
> %s (expected IP %s)", in_port, ip.ipv4_to_str(arp_pkt.src_ip),
> spe_config.ports[in_port])
> + return
> + # don't flood here - we'll do it later
> + #out_port = ofproto_v1_2.OFPP_FLOOD
> + #actions = [datapath.ofproto_parser.OFPActionOutput(out_port,
> 1500)]
> + #out = datapath.ofproto_parser.OFPPacketOut(
> + # datapath=datapath, buffer_id=msg.buffer_id, in_port=in_port,
> + # actions=actions)
> + #datapath.send_msg(out)
> + # set ethtype to IP for the next part
> + ethtype=ether.ETH_TYPE_IP
> +
> + # do we know the mac?
> + #if src not in self.mac_to_port[dpid]:
> + # # learn the mac address to avoid FLOOD next time.
> + # self.mac_to_port[dpid][src] = in_port
> + # # set a flow to table 0 to allow packets through to table 1
> + # match = datapath.ofproto_parser.OFPMatch()
> + # match.set_in_port(in_port)
> + # match.set_dl_src(src)
> + # match.set_dl_type(ethtype)
> + # instructions =
> [datapath.ofproto_parser.OFPInstructionGotoTable(2)]
> + # self.add_flow(datapath, 0, match, instructions)
> + #
> + #
> + #if dst in self.mac_to_port[dpid]:
> + # out_port = self.mac_to_port[dpid][dst]
> + # match = datapath.ofproto_parser.OFPMatch()
> + # match.set_dl_dst(dst)
> + # actions = [datapath.ofproto_parser.OFPActionOutput(out_port,
> 1500)]
> + # instructions =
> [datapath.ofproto_parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
> actions)]
> + # self.add_flow(datapath, 2, match, instructions,
> buffer_id=msg.buffer_id)
> + #else:
> + # out_port = ofproto_v1_2.OFPP_FLOOD
> + # actions = [datapath.ofproto_parser.OFPActionOutput(out_port,
> 1500)]
> + # out = datapath.ofproto_parser.OFPPacketOut(
> + # datapath=datapath, buffer_id=msg.buffer_id, in_port=in_port,
> + # actions=actions)
> + # datapath.send_msg(out)
> +
> + # new way of handling this
> + # if packet got to us then we probably don't know the MAC
> + # so add table->2 flow to first table, and out action on table 2
> +
> + # learn the mac address to avoid FLOOD next time.
> + self.mac_to_port[dpid][src] = in_port
> + # set a flow to table 0 to allow packets through to table 1
> + match = datapath.ofproto_parser.OFPMatch()
> + match.set_in_port(in_port)
> + match.set_dl_src(src)
> + match.set_dl_type(ethtype)
> + instructions = [datapath.ofproto_parser.OFPInstructionGotoTable(2)]
> + self.add_flow(datapath, 0, match, instructions)
> +
> + match = datapath.ofproto_parser.OFPMatch()
> + match.set_dl_dst(src)
> + actions = [datapath.ofproto_parser.OFPActionOutput(in_port, 1500)]
> + instructions =
> [datapath.ofproto_parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
> actions)]
> + self.add_flow(datapath, 2, match, instructions)
> +
> + # then, just in case there's no out flow, we'll do that too
> +
> + if dst in self.mac_to_port[dpid]:
> + out_port = self.mac_to_port[dpid][dst]
> + match = datapath.ofproto_parser.OFPMatch()
> + match.set_dl_dst(dst)
> + actions = [datapath.ofproto_parser.OFPActionOutput(out_port,
> 1500)]
> + instructions =
> [datapath.ofproto_parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
> actions)]
> + self.add_flow(datapath, 2, match, instructions,
> buffer_id=msg.buffer_id)
> + else:
> + out_port = ofproto_v1_2.OFPP_FLOOD
> + actions = [datapath.ofproto_parser.OFPActionOutput(out_port,
> 1500)]
> + out = datapath.ofproto_parser.OFPPacketOut(
> + datapath=datapath, buffer_id=msg.buffer_id, in_port=in_port,
> + actions=actions)
> + datapath.send_msg(out)
> +
> + @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER)
> + def _port_status_handler(self, ev):
> + msg = ev.msg
> + reason = msg.reason
> + port_no = msg.desc.port_no
> +
> + ofproto = msg.datapath.ofproto
> + if reason == ofproto.OFPPR_ADD:
> + self.logger.info("port added %s", port_no)
> + elif reason == ofproto.OFPPR_DELETE:
> + self.logger.info("port deleted %s", port_no)
> + elif reason == ofproto.OFPPR_MODIFY:
> + self.logger.info("port modified %s", port_no)
> + else:
> + self.logger.info("Illegal port state %s %s", port_no, reason)
> diff --git a/ryu/app/spe_config.py b/ryu/app/spe_config.py
> new file mode 100644
> index 0000000..f0e8714
> --- /dev/null
> +++ b/ryu/app/spe_config.py
> @@ -0,0 +1,5 @@
> +ports = {
> + 1 : '10.1.1.1',
> + 2 : '10.1.1.2',
> + 3 : '10.1.1.3',
> + }
> \ No newline at end of file
> --
> 1.7.9.5
>
>
> ------------------------------------------------------------------------------
> See everything from the browser to the database with AppDynamics
> Get end-to-end visibility with application monitoring from AppDynamics
> Isolate bottlenecks and diagnose root cause in seconds.
> Start your free trial of AppDynamics Pro today!
> http://pubads.g.doubleclick.net/gampad/clk?id=48808831&iu=/4140/ostg.clktrk
> _______________________________________________
> Ryu-devel mailing list
> [email protected]
> https://lists.sourceforge.net/lists/listinfo/ryu-devel
>
--
yamahata
------------------------------------------------------------------------------
See everything from the browser to the database with AppDynamics
Get end-to-end visibility with application monitoring from AppDynamics
Isolate bottlenecks and diagnose root cause in seconds.
Start your free trial of AppDynamics Pro today!
http://pubads.g.doubleclick.net/gampad/clk?id=48808831&iu=/4140/ostg.clktrk
_______________________________________________
Ryu-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ryu-devel