Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

L3Firewall.py

from pox.core import core
import pox.openflow.libopenflow_01 as of
from pox.lib.revent import *
from pox.lib.util import dpidToStr
from pox.lib.addresses import EthAddr
from collections import namedtuple
import os
''' New imports here ... '''
import csv
import argparse
from pox.lib.packet.ethernet import ethernet, ETHER_BROADCAST
from pox.lib.addresses import IPAddr
import pox.lib.packet as pkt
from pox.lib.packet.arp import arp
from pox.lib.packet.ipv4 import ipv4
from pox.lib.packet.icmp import icmp

# Import time to track duration of time that ddos flood is occuring for
import time

log = core.getLogger()
priority = 60000

l2config = "l2firewall.config"
l3config = "l3firewall.config"

# Track connections to detect ddos attack
connection_counter = {}
BLOCK_THRESHOLD = 10 # connections
BLOCK_TIME_WINDOW = 3 # seconds


class Firewall (EventMixin):

	def __init__ (self,l2config,l3config):
		self.listenTo(core.openflow)
		self.disbaled_MAC_pair = [] # Store a tuple of MAC pair which will be installed into the flow table of each switch.
		self.fwconfig = list()

		'''
		Read the CSV file
		'''
		if l2config == "":
			l2config="l2firewall.config"
			
		if l3config == "":
			l3config="l3firewall.config" 
		with open(l2config, 'rb') as rules:
			csvreader = csv.DictReader(rules) # Map into a dictionary
			for line in csvreader:
				# Read MAC address. Convert string to Ethernet address using the EthAddr() function.
                                if line['mac_0'] != 'any':
				    mac_0 = EthAddr(line['mac_0'])
                                else:
                                    mac_0 = None

                                if line['mac_1'] != 'any':
        				mac_1 = EthAddr(line['mac_1'])
                                else:
                                    mac_1 = None
				# Append to the array storing all MAC pair.
				self.disbaled_MAC_pair.append((mac_0,mac_1))

		with open(l3config) as csvfile:
			log.debug("[*] Reading l3config file during initialization!")
			#self.rules = csv.DictReader(csvfile)
			self.rules = [row for row in csv.DictReader(csvfile)]
			if not self.rules:
				log.debug("[*] No rules found")
			for row in self.rules:
				log.debug("[*] Saving individual rule parameters in rule dict !")
				s_ip = row['src_ip']
				d_ip = row['dst_ip']
				s_port = row['src_port']
				d_port = row['dst_port']
				print "src_ip, dst_ip, src_port, dst_port", s_ip,d_ip,s_port,d_port
			log.debug("[*] Finished updating l3config file !")

		log.debug("[*] Enabling Firewall Module")

	def replyToARP(self, packet, match, event):
		log.debug("[*] Entering replyToARP")
		r = arp()
		r.opcode = arp.REPLY
		r.hwdst = match.dl_src
		r.protosrc = match.nw_dst
		r.protodst = match.nw_src
		r.hwsrc = match.dl_dst
		e = ethernet(type=packet.ARP_TYPE, src = r.hwsrc, dst=r.hwdst)
		e.set_payload(r)
		msg = of.ofp_packet_out()
		msg.data = e.pack()
		msg.actions.append(of.ofp_action_output(port=of.OFPP_IN_PORT))
		msg.in_port = event.port
		event.connection.send(msg)
		log.debug("[*] Exiting replyToARP")

	def allowOther(self,event):
		log.debug("[*] Entered allowOther")
		msg = of.ofp_flow_mod()
		match = of.ofp_match()
		action = of.ofp_action_output(port = of.OFPP_NORMAL)
		msg.actions.append(action)
		event.connection.send(msg)
		log.debug("[*] Exiting allowOther")

	def installFlow(self, event, offset, srcmac, dstmac, srcip, dstip, sport, dport, nwproto):
		log.debug("[*] Entered installFlow")
		msg = of.ofp_flow_mod()
		match = of.ofp_match()
		if(srcip != None):
			match.nw_src = IPAddr(srcip)
		if(dstip != None):
			match.nw_dst = IPAddr(dstip)	
		match.nw_proto = int(nwproto)
		match.dl_src = srcmac
		match.dl_dst = dstmac
		match.tp_src = sport
		match.tp_dst = dport
		match.dl_type = pkt.ethernet.IP_TYPE
		msg.match = match
		msg.hard_timeout = 0
		msg.idle_timeout = 200
		msg.priority = priority + offset		
		event.connection.send(msg)
		log.debug("[*] Exiting installFlow")

	# ************* My mods start here *******************
	# Create function to block offending MAC address
	def blockMAC(self, event, mac_address):
		# Add a flow rule to block a MAC address permanently
		log.debug("[*] Entered blockMAC")
		msg = of.ofp_flow_mod()
		match = of.ofp_match()
		match.dl_src = EthAddr(mac_address)
		match.dl_type = pkt.ethernet.IP_TYPE
		msg.match = match
		msg.priority = 65535 # high priority
		msg.action = []
		event.connection.send(msg)
		log.debug("[*] Permanently blocked the MAC address %s" % mac_address)
		log.debug("[*] Exiting blockMAC")
	# ************* My mods end here ********************

	def replyToIP(self, packet, match, event, fwconfig):
		log.debug("[*] Entered replyToIP")		
		srcmac = str(match.dl_src)
		dstmac = str(match.dl_src)
		sport = str(match.tp_src)
		dport = str(match.tp_dst)
		nwproto = str(match.nw_proto)

		# ************* DDOS Mods start here *******************
		log.debug("[*] Entered DDOS in replyToIP")
		current_time = time.time()

		# Create a new entry to track the new connection for possible offense
		if srcmac not in connection_counter:
			connection_counter[srcmac] = []
		connection_counter[srcmac].append(current_time)

		# Clean up connections that don't meet offensive threshold		
		connection_counter[srcmac] = \
			[item for item in connection_counter[srcmac] if (current_time - item) <= BLOCK_TIME_WINDOW]

		# If the number of connections from the same MAC address exceeds the offense threshold, block.
		if len(connection_counter[srcmac]) > BLOCK_THRESHOLD:
			log.debug("[*] Blocking MAC address %s for DDOS " % srcmac)
			self.blockMAC(event, srcmac)
			return
		# ************* DDOS Mods end here *******************

		log.debug("[*] Processing rules in replyToIP")

		if not self.rules:
			log.debug("[*] Rules are empty")
		else:
			log.debug("[*] Rules are not empty")	
			for row in self.rules:
				log.debug("[**] in replyToIP for loop...")
				prio = row['priority']
				srcmac = row['src_mac']
				dstmac = row['dst_mac']
				s_ip = row['src_ip']
				d_ip = row['dst_ip']
				s_port = row['src_port']
				d_port = row['dst_port']
				nw_proto = row['nw_proto']
				
				log.debug("[**] You are in original code block ...")
				srcmac1 = EthAddr(srcmac) if srcmac != 'any' else None
				dstmac1 = EthAddr(dstmac) if dstmac != 'any' else None
				s_ip1 = s_ip if s_ip != 'any' else None
				d_ip1 = d_ip if d_ip != 'any' else None
				s_port1 = int(s_port) if s_port != 'any' else None
				d_port1 = int(d_port) if d_port != 'any' else None
				prio1 = int(prio) if prio != None else priority
				if nw_proto == "tcp":
					nw_proto1 = pkt.ipv4.TCP_PROTOCOL
				elif nw_proto == "icmp":
					nw_proto1 = pkt.ipv4.ICMP_PROTOCOL
					s_port1 = None
					d_port1 = None
				elif nw_proto == "udp":
					nw_proto1 = pkt.ipv4.UDP_PROTOCOL
				else:
					log.debug("PROTOCOL field is mandatory, Choose between ICMP, TCP, UDP")
				print (prio1,s_ip1, d_ip1, s_port1, d_port1,nw_proto1)
				self.installFlow(event,prio1, srcmac1, dstmac1, s_ip1, d_ip1, s_port1, d_port1, nw_proto1)
		#log.debug("[*] Allowing other events")		
		self.allowOther(event)
		log.debug("[*] Exiting replyToIP")


	def _handle_ConnectionUp (self, event):
		log.debug("[*] Entered _handle_ConnectionUp")		
		''' Add your logic here ... '''

		'''
		Iterate through the disbaled_MAC_pair array, and for each
		pair we install a rule in each OpenFlow switch
		'''
		self.connection = event.connection

		for (source, destination) in self.disbaled_MAC_pair:

			print source,destination
			message = of.ofp_flow_mod() # OpenFlow massage. Instructs a switch to install a flow
			match = of.ofp_match() # Create a match
			match.dl_src = source # Source address

			match.dl_dst = destination # Destination address
			message.priority = 65535 # Set priority (between 0 and 65535)
			message.match = match			
			event.connection.send(message) # Send instruction to the switch

		log.debug("[*] Firewall rules installed on %s", dpidToStr(event.dpid))
		log.debug("[*] Exiting _handle_ConnectionUp")


	def _handle_PacketIn(self, event):

		log.debug("[*] Entered _handle_PacketIn")
		packet = event.parsed
		match = of.ofp_match.from_packet(packet)

		if(match.dl_type == packet.ARP_TYPE and match.nw_proto == arp.REQUEST):

		  self.replyToARP(packet, match, event)

		if(match.dl_type == packet.IP_TYPE):
		  ip_packet = packet.payload
		  print "Ip_packet.protocol = ", ip_packet.protocol
		  if ip_packet.protocol == ip_packet.TCP_PROTOCOL:
			log.debug("[*] TCP it is !")
   
		  self.replyToIP(packet, match, event, self.rules)
		  self.allowOther(event)
		log.debug("[*] Exiting _handle_PacketIn")


def launch (l2config="l2firewall.config",l3config="l3firewall.config"):
	'''
	Starting the Firewall module
	'''
	parser = argparse.ArgumentParser()
	parser.add_argument('--l2config', action='store', dest='l2config',
					help='Layer 2 config file', default='l2firewall.config')
	parser.add_argument('--l3config', action='store', dest='l3config',
					help='Layer 3 config file', default='l3firewall.config')
	core.registerNew(Firewall,l2config,l3config)