pox3

git clone git://git.codymlewis.com/pox3.git
Log | Files | Refs | README | LICENSE

commit 409c94526cb9f682099ceecf1046780f3a7ff5cd
parent dcb1f016d52e08fe2635e0297d14851a77ada365
Author: Cody Lewis <cody@codymlewis.com>
Date:   Tue, 14 Apr 2020 20:53:26 +1000

Improved readme and moved of_tutorial to samples

Diffstat:
MREADME.md | 8++++++--
Mpox3/core.py | 2+-
Dpox3/misc/of_tutorial.py | 149-------------------------------------------------------------------------------
Msamples/httopo.py | 18+++++++++---------
Msamples/mixed_switches.py | 24++++++++++++------------
Asamples/of_tutorial.py | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msamples/pretty_log.py | 16++++++++--------
Msamples/spanning_tree.py | 40++++++++++++++++++++--------------------
Msamples/topo.py | 18+++++++++---------
Msetup.py | 4++--
10 files changed, 216 insertions(+), 212 deletions(-)

diff --git a/README.md b/README.md @@ -24,10 +24,14 @@ pip3 install pox3 ## API -The following example runs with debug logging the of tutorial, it acts +The following example runs with debug logging, it acts like a simple hub ```python from pox3.boot import boot -boot(["log.level", "--DEBUG", "misc.of_tutorial"]) +boot(["log.level", "--DEBUG", "forwarding.hub"]) ``` + +To learn further I would recommend getting the `act_like_switch` method to work +in `samples/of_tutorial.py`, you will need to read the pox3 source code a bit +particularly the files in `pox3/lib/packet/` diff --git a/pox3/core.py b/pox3/core.py @@ -187,7 +187,7 @@ class POXCore (EventMixin): RereadConfiguration, ]) - version = (0, 1, 0) + version = (0, 1, 1) version_name = "auldridge" def __init__ (self, threaded_selecthub=True, epoll_selecthub=False, diff --git a/pox3/misc/of_tutorial.py b/pox3/misc/of_tutorial.py @@ -1,149 +0,0 @@ -# Copyright 2012 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. - -""" -This component is for use with the OpenFlow tutorial. - -It acts as a simple hub, but can be modified to act like an L2 -learning switch. - -It's roughly similar to the one Brandon Heller did for NOX. -""" - -from pox3.core import core -import pox3.openflow.libopenflow_01 as of - -log = core.getLogger() - - - -class Tutorial (object): - """ - A Tutorial object is created for each switch that connects. - A Connection object for that switch is passed to the __init__ function. - """ - def __init__ (self, connection): - # Keep track of the connection to the switch so that we can - # send it messages! - self.connection = connection - - # This binds our PacketIn event listener - connection.addListeners(self) - - # Use this table to keep track of which ethernet address is on - # which switch port (keys are MACs, values are ports). - self.mac_to_port = {} - - - def resend_packet (self, packet_in, out_port): - """ - Instructs the switch to resend a packet that it had sent to us. - "packet_in" is the ofp_packet_in object the switch had sent to the - controller due to a table-miss. - """ - msg = of.ofp_packet_out() - msg.data = packet_in - - # Add an action to send to the specified port - action = of.ofp_action_output(port = out_port) - msg.actions.append(action) - - # Send message to switch - self.connection.send(msg) - - - def act_like_hub (self, packet, packet_in): - """ - Implement hub-like behavior -- send all packets to all ports besides - the input port. - """ - - # We want to output to all ports -- we do that using the special - # OFPP_ALL port as the output port. (We could have also used - # OFPP_FLOOD.) - self.resend_packet(packet_in, of.OFPP_ALL) - - # Note that if we didn't get a valid buffer_id, a slightly better - # implementation would check that we got the full data before - # sending it (len(packet_in.data) should be == packet_in.total_len)). - - - def act_like_switch (self, packet, packet_in): - """ - Implement switch-like behavior. - """ - - """ # DELETE THIS LINE TO START WORKING ON THIS (AND THE ONE BELOW!) # - - # Here's some psuedocode to start you off implementing a learning - # switch. You'll need to rewrite it as real Python code. - - # Learn the port for the source MAC - self.mac_to_port ... <add or update entry> - - if the port associated with the destination MAC of the packet is known: - # Send packet out the associated port - self.resend_packet(packet_in, ...) - - # Once you have the above working, try pushing a flow entry - # instead of resending the packet (comment out the above and - # uncomment and complete the below.) - - log.debug("Installing flow...") - # Maybe the log statement should have source/destination/port? - - #msg = of.ofp_flow_mod() - # - ## Set fields to match received packet - #msg.match = of.ofp_match.from_packet(packet) - # - #< Set other fields of flow_mod (timeouts? buffer_id?) > - # - #< Add an output action, and send -- similar to resend_packet() > - - else: - # Flood the packet out everything but the input port - # This part looks familiar, right? - self.resend_packet(packet_in, of.OFPP_ALL) - - """ # DELETE THIS LINE TO START WORKING ON THIS # - - - def _handle_PacketIn (self, event): - """ - Handles packet in messages from the switch. - """ - - packet = event.parsed # This is the parsed packet data. - if not packet.parsed: - log.warning("Ignoring incomplete packet") - return - - packet_in = event.ofp # The actual ofp_packet_in message. - - # Comment out the following line and uncomment the one after - # when starting the exercise. - self.act_like_hub(packet, packet_in) - #self.act_like_switch(packet, packet_in) - - - -def launch (): - """ - Starts the component - """ - def start_switch (event): - log.debug("Controlling %s" % (event.connection,)) - Tutorial(event.connection) - core.openflow.addListenerByName("ConnectionUp", start_switch) diff --git a/samples/httopo.py b/samples/httopo.py @@ -4,7 +4,7 @@ # 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 +# 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, @@ -17,11 +17,11 @@ Fires up topology, discovery, and host_tracker """ def launch (): - import pox3.topology - pox3.topology.launch() - import pox3.openflow.discovery - pox3.openflow.discovery.launch() - import pox3.openflow.topology - pox3.openflow.topology.launch() - import pox3.host_tracker - pox3.host_tracker.launch() + import pox3.topology + pox3.topology.launch() + import pox3.openflow.discovery + pox3.openflow.discovery.launch() + import pox3.openflow.topology + pox3.openflow.topology.launch() + import pox3.host_tracker + pox3.host_tracker.launch() diff --git a/samples/mixed_switches.py b/samples/mixed_switches.py @@ -4,7 +4,7 @@ # 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 +# 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, @@ -17,12 +17,12 @@ A quick example of treating different datapaths differently. Although it's not currently particularly well supported, there's nothing to stop one from using different components with particular -switches. There are multiple ways to do this, but this component +switches. There are multiple ways to do this, but this component demonstrates a pretty straightforward one. When components are loaded from the commandline, their launch() -function is run. In many cases, this launch() function sets up -a listener for openflow.ConnectionUp events. When one is raised, +function is run. In many cases, this launch() function sets up +a listener for openflow.ConnectionUp events. When one is raised, the component handles it by setting up more event listeners on that connection. @@ -43,13 +43,13 @@ import pox3.forwarding.l2_learning as l2l log = core.getLogger() def _handle_ConnectionUp (event): - if event.dpid & 1 == 1: - log.info("Treating %s as l2_pairs", event.connection) - event.connection.addListenerByName("PacketIn", l2p._handle_PacketIn) - else: - log.info("Treating %s as l2_learning", event.connection) - l2l.LearningSwitch(event.connection, False) + if event.dpid & 1 == 1: + log.info("Treating %s as l2_pairs", event.connection) + event.connection.addListenerByName("PacketIn", l2p._handle_PacketIn) + else: + log.info("Treating %s as l2_learning", event.connection) + l2l.LearningSwitch(event.connection, False) def launch (): - core.openflow.addListenerByName("ConnectionUp", _handle_ConnectionUp) - log.info("Mixed switches demo running.") + core.openflow.addListenerByName("ConnectionUp", _handle_ConnectionUp) + log.info("Mixed switches demo running.") diff --git a/samples/of_tutorial.py b/samples/of_tutorial.py @@ -0,0 +1,149 @@ +# Copyright 2012 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. + +""" +This component is for use with the OpenFlow tutorial. + +It acts as a simple hub, but can be modified to act like an L2 +learning switch. + +It's roughly similar to the one Brandon Heller did for NOX. +""" + +from pox3.core import core +import pox3.openflow.libopenflow_01 as of + +log = core.getLogger() + + + +class Tutorial (object): + """ + A Tutorial object is created for each switch that connects. + A Connection object for that switch is passed to the __init__ function. + """ + def __init__ (self, connection): + # Keep track of the connection to the switch so that we can + # send it messages! + self.connection = connection + + # This binds our PacketIn event listener + connection.addListeners(self) + + # Use this table to keep track of which ethernet address is on + # which switch port (keys are MACs, values are ports). + self.mac_to_port = {} + + + def resend_packet (self, packet_in, out_port): + """ + Instructs the switch to resend a packet that it had sent to us. + "packet_in" is the ofp_packet_in object the switch had sent to the + controller due to a table-miss. + """ + msg = of.ofp_packet_out() + msg.data = packet_in + + # Add an action to send to the specified port + action = of.ofp_action_output(port = out_port) + msg.actions.append(action) + + # Send message to switch + self.connection.send(msg) + + + def act_like_hub (self, packet, packet_in): + """ + Implement hub-like behavior -- send all packets to all ports besides + the input port. + """ + + # We want to output to all ports -- we do that using the special + # OFPP_ALL port as the output port. (We could have also used + # OFPP_FLOOD.) + self.resend_packet(packet_in, of.OFPP_ALL) + + # Note that if we didn't get a valid buffer_id, a slightly better + # implementation would check that we got the full data before + # sending it (len(packet_in.data) should be == packet_in.total_len)). + + + def act_like_switch (self, packet, packet_in): + """ + Implement switch-like behavior. + """ + + """ # DELETE THIS LINE TO START WORKING ON THIS (AND THE ONE BELOW!) # + + # Here's some psuedocode to start you off implementing a learning + # switch. You'll need to rewrite it as real Python code. + + # Learn the port for the source MAC + self.mac_to_port ... <add or update entry> + + if the port associated with the destination MAC of the packet is known: + # Send packet out the associated port + self.resend_packet(packet_in, ...) + + # Once you have the above working, try pushing a flow entry + # instead of resending the packet (comment out the above and + # uncomment and complete the below.) + + log.debug("Installing flow...") + # Maybe the log statement should have source/destination/port? + + #msg = of.ofp_flow_mod() + # + ## Set fields to match received packet + #msg.match = of.ofp_match.from_packet(packet) + # + #< Set other fields of flow_mod (timeouts? buffer_id?) > + # + #< Add an output action, and send -- similar to resend_packet() > + + else: + # Flood the packet out everything but the input port + # This part looks familiar, right? + self.resend_packet(packet_in, of.OFPP_ALL) + + """ # DELETE THIS LINE TO START WORKING ON THIS # + + + def _handle_PacketIn (self, event): + """ + Handles packet in messages from the switch. + """ + + packet = event.parsed # This is the parsed packet data. + if not packet.parsed: + log.warning("Ignoring incomplete packet") + return + + packet_in = event.ofp # The actual ofp_packet_in message. + + # Comment out the following line and uncomment the one after + # when starting the exercise. + self.act_like_hub(packet, packet_in) + #self.act_like_switch(packet, packet_in) + + + +def launch (): + """ + Starts the component + """ + def start_switch (event): + log.debug("Controlling %s" % (event.connection,)) + Tutorial(event.connection) + core.openflow.addListenerByName("ConnectionUp", start_switch) diff --git a/samples/pretty_log.py b/samples/pretty_log.py @@ -4,7 +4,7 @@ # 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 +# 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, @@ -24,10 +24,10 @@ shortcut for that too. """ def launch (**kw): - import pox3.log.color - pox3.log.color.launch() - import pox3.log - pox3.log.launch(format="[@@@bold@@@level%(name)-23s@@@reset] " + - "@@@bold%(message)s@@@normal") - import pox3.log.level - pox3.log.level.launch(**kw) + import pox3.log.color + pox3.log.color.launch() + import pox3.log + pox3.log.launch(format="[@@@bold@@@level%(name)-23s@@@reset] " + + "@@@bold%(message)s@@@normal") + import pox3.log.level + pox3.log.level.launch(**kw) diff --git a/samples/spanning_tree.py b/samples/spanning_tree.py @@ -4,7 +4,7 @@ # 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 +# 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, @@ -18,24 +18,24 @@ works decently on topologies with loops. """ def launch (forwarding = "l2"): - import pox3.log.color - pox3.log.color.launch() - import pox3.log - pox3.log.launch(format="[@@@bold@@@level%(name)-22s@@@reset] " + - "@@@bold%(message)s@@@normal") - from pox3.core import core - import pox3.openflow.discovery - pox3.openflow.discovery.launch() + import pox3.log.color + pox3.log.color.launch() + import pox3.log + pox3.log.launch(format="[@@@bold@@@level%(name)-22s@@@reset] " + + "@@@bold%(message)s@@@normal") + from pox3.core import core + import pox3.openflow.discovery + pox3.openflow.discovery.launch() - core.getLogger("openflow.spanning_tree").setLevel("INFO") - if forwarding.lower() == "l3": - import pox3.forwarding.l3_learning as fw - elif forwarding.lower() == "l2_multi": - import pox3.forwarding.l2_multi as fw - else: - import pox3.forwarding.l2_learning as fw - core.getLogger().debug("Using forwarding: %s", fw.__name__) - fw.launch() + core.getLogger("openflow.spanning_tree").setLevel("INFO") + if forwarding.lower() == "l3": + import pox3.forwarding.l3_learning as fw + elif forwarding.lower() == "l2_multi": + import pox3.forwarding.l2_multi as fw + else: + import pox3.forwarding.l2_learning as fw + core.getLogger().debug("Using forwarding: %s", fw.__name__) + fw.launch() - import pox3.openflow.spanning_tree - pox3.openflow.spanning_tree.launch() + import pox3.openflow.spanning_tree + pox3.openflow.spanning_tree.launch() diff --git a/samples/topo.py b/samples/topo.py @@ -4,7 +4,7 @@ # 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 +# 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, @@ -17,11 +17,11 @@ Fires up topology, discovery, and a l2 learning switch controller """ def launch (): - import pox3.topology - pox3.topology.launch() - import pox3.openflow.discovery - pox3.openflow.discovery.launch() - import pox3.openflow.topology - pox3.openflow.topology.launch() - import pox3.forwarding.l2_learning - pox3.forwarding.l2_learning.launch() + import pox3.topology + pox3.topology.launch() + import pox3.openflow.discovery + pox3.openflow.discovery.launch() + import pox3.openflow.topology + pox3.openflow.topology.launch() + import pox3.forwarding.l2_learning + pox3.forwarding.l2_learning.launch() diff --git a/setup.py b/setup.py @@ -5,13 +5,13 @@ with open("README.md", "r") as fh: setuptools.setup( name="pox3", - version="0.1.0", + version="0.1.1", author="Cody Lewis", author_email="cody@codymlewis.com", description="A networking software library", long_description=LONG_DESCRIPTION, long_description_content_type="text/markdown", - url="https://gitlab.com/codymlewis/pox3", + url="https://github.com/codymlewis/pox3", packages=setuptools.find_packages(), classifiers=[ "Programming Language :: Python :: 3",