Created
May 25, 2015 13:56
-
-
Save adammw/b34424d62262645d7310 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Copyright 2012 James McCauley | |
# Modified 2015 Adam Malcontenti-Wilson | |
# | |
# This file is part of POX. | |
# | |
# POX is free software: you can redistribute it and/or modify | |
# it under the terms of the GNU General Public License as published by | |
# the Free Software Foundation, either version 3 of the License, or | |
# (at your option) any later version. | |
# | |
# POX is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU General Public License for more details. | |
# | |
# You should have received a copy of the GNU General Public License | |
# along with POX. If not, see <http://www.gnu.org/licenses/>. | |
""" | |
L2 learning switch, with port mirroring functionality | |
Requires messenger.messenger library | |
To turn off mirroring: | |
echo '{"mirroring": {"monitor_ports": [], "out_port": 4}}' | nc localhost 7790 | |
To turn on mirroring of port 1 to port 4: | |
echo '{"mirroring": {"monitor_ports": [1], "out_port": 4}}' | nc localhost 7790 | |
""" | |
from pox.core import core | |
from pox.messenger.messenger import * | |
import pox.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 = {} | |
# Default mirroring | |
self.port_mirroring_monitor_ports = [1] | |
self.port_mirroring_out_port = 4 | |
# Listen for control messages | |
core.messenger.addListener(MessageReceived, self.messageReceived, weak=True) | |
def messageReceived(self, event, msg): | |
if msg and 'mirroring' in msg: | |
self.disableMirroring() | |
self.port_mirroring_monitor_ports = msg['mirroring']['monitor_ports'] | |
self.port_mirroring_out_port = msg['mirroring']['out_port'] | |
if len(self.port_mirroring_monitor_ports): | |
log.debug("Mirroing enabled: %s -> %s", repr(self.port_mirroring_monitor_ports), self.port_mirroring_out_port) | |
else: | |
log.debug("Mirroring disabled") | |
def disableMirroring(self): | |
log.debug("Deleting old mirroring flows...") | |
msg = of.ofp_flow_mod(command = of.OFPFC_DELETE, cookie = 99) | |
self.connection.send(msg) | |
def send_packet (self, buffer_id, raw_data, out_port, in_port): | |
""" | |
Sends a packet out of the specified switch port. | |
If buffer_id is a valid buffer on the switch, use that. Otherwise, | |
send the raw data in raw_data. | |
The "in_port" is the port number that packet arrived on. Use | |
OFPP_NONE if you're generating this packet. | |
""" | |
msg = of.ofp_packet_out() | |
msg.in_port = in_port | |
if buffer_id != -1 and buffer_id is not None: | |
# We got a buffer ID from the switch; use that | |
msg.buffer_id = buffer_id | |
else: | |
# No buffer ID from switch -- we got the raw data | |
if raw_data is None: | |
# No raw_data specified -- nothing to send! | |
return | |
msg.data = raw_data | |
# 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_switch (self, packet, packet_in): | |
""" | |
Implement switch-like behavior. | |
""" | |
src_mac = str(packet.src) | |
dst_mac = str(packet.dst) | |
log.debug("Packet received %s -> %s", src_mac, dst_mac) | |
# Learn the port for the source MAC | |
if src_mac != "ff:ff:ff:ff:ff:ff": | |
if not src_mac in self.mac_to_port: | |
log.debug("Learnt %s on port %s" % (src_mac, packet_in.in_port)) | |
self.mac_to_port[src_mac] = packet_in.in_port | |
elif self.mac_to_port[src_mac] != packet_in.in_port: | |
log.debug("Reassigned %s to port %s" % (src_mac, packet_in.in_port)) | |
self.mac_to_port[src_mac] = packet_in.in_port | |
if dst_mac in self.mac_to_port: | |
out_port = self.mac_to_port[dst_mac] | |
log.debug("Sending packet out port %s" % out_port) | |
# Send packet out the associated port | |
self.send_packet(packet_in.buffer_id, packet_in.data, | |
out_port, packet_in.in_port) | |
# Duplicate packets for mirroring, if enabled | |
log.debug("monitor ports: %s", repr(self.port_mirroring_monitor_ports)) | |
if packet_in.in_port in self.port_mirroring_monitor_ports or out_port in self.port_mirroring_monitor_ports: | |
log.debug("Duplicating packet out monitor port %s", self.port_mirroring_out_port) | |
self.send_packet(None, packet_in.data, self.port_mirroring_out_port, packet_in.in_port) | |
# Install flow for future packets | |
log.debug("Installing flow dst_mac = %s out_port = %s", dst_mac, out_port) | |
msg = of.ofp_flow_mod() | |
msg.idle_timeout = 60 | |
msg.priority = 128 | |
# Set fields to match received packet | |
#msg.match = of.ofp_match.from_packet(packet) | |
msg.match.dl_src = packet.src # ensures new srcs go through the control flow | |
msg.match.dl_dst = packet.dst | |
# Set action to output to out_port | |
msg.actions.append(of.ofp_action_output(port = out_port)) | |
if out_port in self.port_mirroring_monitor_ports: | |
msg.cookie = 99 | |
msg.actions.append(of.ofp_action_output(port = self.port_mirroring_out_port)) | |
# Send message to switch | |
self.connection.send(msg) | |
# Install mirroring flow | |
if packet_in.in_port in self.port_mirroring_monitor_ports: | |
log.debug("Installing mirroring flow") | |
msg = of.ofp_flow_mod() | |
msg.cookie = 99 | |
msg.priority = 256 | |
msg.idle_timeout = 60 | |
msg.match.in_port = packet_in.in_port | |
msg.match.dl_src = packet.src | |
msg.match.dl_dst = packet.dst | |
msg.actions.append(of.ofp_action_output(port = out_port)) | |
msg.actions.append(of.ofp_action_output(port = self.port_mirroring_out_port)) | |
self.connection.send(msg) | |
else: | |
# Flood the packet out everything but the input port | |
log.debug("Destination %s not in MAC table, flooding packet", dst_mac) | |
self.send_packet(packet_in.buffer_id, packet_in.data, | |
of.OFPP_FLOOD, packet_in.in_port) | |
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. | |
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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment