Skip to content

Instantly share code, notes, and snippets.

@adammw
Created May 25, 2015 13:56
Show Gist options
  • Save adammw/b34424d62262645d7310 to your computer and use it in GitHub Desktop.
Save adammw/b34424d62262645d7310 to your computer and use it in GitHub Desktop.
# 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