diff --git a/.gitignore b/.gitignore index 3c77505..f64f642 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ - +*.swo* *.pyc* *.txt~ *.py~ +*/__pycache__ +*/*/__pycache__ diff --git a/Directory/construct_flow.py b/Directory/construct_flow.py deleted file mode 100644 index ed571f6..0000000 --- a/Directory/construct_flow.py +++ /dev/null @@ -1,76 +0,0 @@ -#! usr/bin/env python - -from ryu.ofproto.ether import ETH_TYPE_IP, ETH_TYPE_ARP,ETH_TYPE_LLDP,ETH_TYPE_MPLS,ETH_TYPE_IPV6 -from ryu.ofproto.inet import IPPROTO_ICMP, IPPROTO_TCP, IPPROTO_UDP,IPPROTO_SCTP -from flow_addition import FlowAdd -import logging -class Construct(): - """ - Constructs Match object from supplied field. - The default value of all parameters is don't-care-match-all wildcard. - If no parameters are given, the returned match matches everything. - """ - - def __init__(self): - logging.info("Rule will be constructed") - - - def add_flow(self,datapath,actions, priority = 1000 ,in_port=None, in_phy_port=None, metadata=None, eth_dst=None, eth_src=None, eth_type=None, - vlan_vid=None, vlan_pcp=None, ip_dscp=None, ip_ecn=None, ip_proto=None, ipv4_src=None, ipv4_dst=None, - tcp_src=None, tcp_dst=None, udp_src=None, udp_dst=None, sctp_src=None, sctp_dst=None, icmpv4_type=None, - icmpv4_code=None, arp_op=None, arp_spa=None, arp_tpa=None, arp_sha=None, arp_tha=None, - ipv6_src=None, ipv6_dst=None, ipv6_flabel=None, icmpv6_type=None, icmpv6_code=None, - ipv6_nd_target=None, ipv6_nd_sll=None, ipv6_nd_tll=None, mpls_label=None, mpls_tc=None, mpls_bos=None, - pbb_isid=None, tunnel_id=None, ipv6_exthdr=None): - - assert (datapath is not None),"Datapath Object is Not set. " - assert (actions is not None),"Actions Object is Not set. " - - parser = datapath.ofproto_parser - - """ please check for actions that where it fits and what is its advantage """ - matchflow = FlowAdd() - match = parser.OFPMatch() - - if (eth_type is not None): - if (eth_type == ETH_TYPE_IP): - if (ip_proto is not None): - - # For ICMP flow rules. - if (ip_proto == IPPROTO_ICMP): - match = parser.OFPMatch(in_port = in_port, eth_type = eth_type, ip_proto= ip_proto, - icmpv4_type = icmpv4_type, ipv4_src = ipv4_src, ipv4_dst = ipv4_dst) - elif(ip_proto == IPPROTO_TCP): - match = parser.OFPMatch(in_port = in_port, eth_type = eth_type, ip_proto= ip_proto, - ipv4_src = ipv4_src, ipv4_dst = ipv4_dst, - tcp_src = tcp_src, tcp_dst = tcp_dst) - elif(ip_proto == IPPROTO_UDP): - match = parser.OFPMatch(in_port = in_port, eth_type = eth_type, ip_proto= ip_proto, - ipv4_src = ipv4_src, ipv4_dst = ipv4_dst, - udp_src = udp_src, udp_dst = udp_dst) - elif (ip_proto == IPPROTO_SCTP): - match = parser.OFPMatch(in_port = in_port, eth_type = eth_type, - eth_src=eth_src, eth_dst=eth_dst, - ip_proto= ip_proto) - else: - # default case - logging.info("Please check OFPMatch--> ip_proto parameter in order to continue.") - else: - logging.info("Please set OFPMatch--> ip_proto parameter in order to continue.") - - elif (eth_type == ETH_TYPE_ARP): - match = parser.OFPMatch(in_port = in_port, eth_type = eth_type,eth_src=eth_src,eth_dst=eth_dst) - elif (eth_type == ETH_TYPE_LLDP): - match = parser.OFPMatch(eth_type = eth_type) - elif (eth_type == ETH_TYPE_IPV6): - match = parser.OFPMatch(in_port = in_port, eth_type = eth_type,eth_src=eth_src,eth_dst=eth_dst) - elif (eth_type == ETH_TYPE_MPLS): - match = parser.OFPMatch(in_port = in_port, eth_type = eth_type,eth_src=eth_src,eth_dst=eth_dst) - else: - logging.info("Please set OFPMatch--> eth_type parameter in order to continue.") - - #Finally, add this match to flow table entry. - if match is not None: - matchflow.add_flow(datapath, priority, match, actions) - else: - logging.info("Sorry, no matching rule found or added.") diff --git a/Directory/flow_addition.py b/Directory/flow_addition.py deleted file mode 100644 index dc491a0..0000000 --- a/Directory/flow_addition.py +++ /dev/null @@ -1,23 +0,0 @@ -#! usr/bin/env python -import logging -class FlowAdd(): - """ - Default Function for constructing instructions. - Sends constructed message to connected Switch. - """ - - def __init__ (self): - logging.info("Flow table rules are sent to switch") - - - def add_flow(self,datapath, priority, match, actions): - ofproto = datapath.ofproto - parser = datapath.ofproto_parser - - inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, - actions)] - - mod = parser.OFPFlowMod(datapath=datapath, priority=priority, - match=match, instructions=inst, idle_timeout=1800) - datapath.send_msg(mod) - diff --git a/Directory/inefficient_stateful_firewall.py b/Directory/inefficient_stateful_firewall.py deleted file mode 100644 index 6176898..0000000 --- a/Directory/inefficient_stateful_firewall.py +++ /dev/null @@ -1,217 +0,0 @@ -# Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. -# -# 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. - -from ryu.base import app_manager -from ryu.controller import ofp_event, dpset -from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER,set_ev_cls -from ryu.ofproto import ofproto_v1_3, ofproto_v1_3_parser -from ryu.lib.packet import packet,ethernet,ipv4,udp,tcp,icmp -from ryu.ofproto.ether import ETH_TYPE_IP, ETH_TYPE_ARP,ETH_TYPE_LLDP,ETH_TYPE_MPLS,ETH_TYPE_IPV6 -from ryu.ofproto.inet import IPPROTO_ICMP, IPPROTO_TCP, IPPROTO_UDP,IPPROTO_SCTP -from parse_firewall_rules import parse_firewall -from switch_information import SwitchInfo -from packet_out import SendPacket -from construct_flow import Construct -from connection_tracking import TrackConnection -ICMP_PING = 8 -ICMP_PONG = 0 -TCP_SYN = 0x02 -TCP_SYN_ACK = 0x12 -TCP_BOGUS_FLAGS = 0x15 - -class InefficientFirewall(app_manager.RyuApp): - OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] - - inner_policy = {} - icmp_conn_track = {} - tcp_conn_track = {} - udp_conn_track = {} - sendpkt = SendPacket() - flow = Construct() - track = TrackConnection() - - def __init__(self, *args, **kwargs): - super(InefficientFirewall, self).__init__(*args, **kwargs) - self.mac_to_port = {} - parser = parse_firewall() - self.inner_policy = parser.parse() - self.logger.info("dict is ready") - - - @set_ev_cls(dpset.EventDP, dpset.DPSET_EV_DISPATCHER) - def handler_datapath(self, ev): - SwitchInfo(ev) - - - """ - Handles incoming packets. Decode them - and check for suitable Firewall Rules. - """ - @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) - def packet_in_handler(self, ev): - msg = ev.msg - datapath = msg.datapath - ofproto = datapath.ofproto - parser = datapath.ofproto_parser - in_port = msg.match['in_port'] - - try: - pkt = packet.Packet(msg.data) - eth = pkt.get_protocols(ethernet.ethernet)[0] - ethtype = eth.ethertype - - out_port = self.port_learn(datapath, eth, in_port) - action_fwd_to_out_port = [parser.OFPActionOutput(out_port)] - action_drop = [parser.OFPActionOutput(ofproto.OFPPC_NO_FWD)] - actions_default = action_fwd_to_out_port - - if(out_port != ofproto.OFPP_FLOOD) and (ethtype == ETH_TYPE_IP): - ipo = pkt.get_protocols(ipv4.ipv4)[0] - - #Check for ICMP - if(ipo.proto == IPPROTO_ICMP): - flag1 = 0 - icmpob = pkt.get_protocol(icmp.icmp) - #Check if this is PING or PONG - if ((icmpob.type==ICMP_PING) and self.inner_policy.has_key(ipo.src)): - temp = self.inner_policy.get(ipo.src) - for i in range(0,len(temp)): - if temp[i][0] == ipo.dst: - xyz = temp[i] - if((xyz[1]=='ICMP') and (xyz[5] == 'ALLOW')): - flag1 = 1 - actions_default = action_fwd_to_out_port - self.icmp_conn_track = self.track.conn_track_dict(self.icmp_conn_track,ipo.src, ipo.dst, "PING", "PONG", xyz[5],2) - break - - elif((icmpob.type == ICMP_PONG) and (self.icmp_conn_track.has_key(ipo.src))): - temp2 = self.icmp_conn_track.get(ipo.src) - for i in range(0,len(temp2)): - if temp2[i][0] == ipo.dst: - xyz = temp2[i] - if ((xyz[1]=='PONG') and (xyz[3] == 'ALLOW')): - flag1 = 1 - actions_default = action_fwd_to_out_port - break - - if (flag1 == 0): - actions_default = action_drop - - - #Check for TCP. - elif (ipo.proto == IPPROTO_TCP): - tcpo = pkt.get_protocol(tcp.tcp) - flag2 = 0 - # TCP SYN packet - if (((tcpo.bits & TCP_SYN) == TCP_SYN) & ((tcpo.bits & TCP_BOGUS_FLAGS) == 0x00)): - if self.inner_policy.has_key(ipo.src): - temp = self.inner_policy.get(ipo.src) - for i in range(0,len(temp)): - if ((temp[i][0] == ipo.dst) and (temp[i][1]=='TCP') and (int(temp[i][2]) == tcpo.src_port) and (int(temp[i][3]) == tcpo.dst_port) and (temp[i][5] == 'ALLOW')): - flag2 = 1 - actions_default = action_fwd_to_out_port - self.tcp_conn_track = self.track.conn_track_dict(self.tcp_conn_track,ipo.src, ipo.dst, tcpo.src_port, tcpo.dst_port , tcpo.seq,1) - break - - # TCP SYN ACK packet - elif((tcpo.bits & TCP_SYN_ACK) == TCP_SYN_ACK): - if self.tcp_conn_track.has_key(ipo.dst): - temp2 = self.tcp_conn_track.get(ipo.dst) - for i in range(0,len(temp2)): - if ((temp2[i][0] == ipo.src) and (int(temp2[i][1]) == tcpo.dst_port) and (int(temp2[i][2]) == tcpo.src_port)): - flag2 = 1 - actions_default = action_fwd_to_out_port - self.tcp_conn_track = self.track.conn_track_dict(self.tcp_conn_track,ipo.src, ipo.dst, tcpo.src_port, tcpo.dst_port, tcpo.seq,1) - break - - # All remaining TCP packets, like, ACK, PUSH, FIN etc. - else: - if self.tcp_conn_track.has_key(ipo.src): - temp3 = self.tcp_conn_track.get(ipo.src) - for i in range(0,len(temp3)): - if ((temp3[i][0] == ipo.dst) and (int(temp3[i][1]) == tcpo.src_port) and (int(temp3[i][2]) == tcpo.dst_port)): - flag2 = 1 - actions_default = action_fwd_to_out_port - break - - if (flag2 == 0): - actions_default = action_drop - - #Check for UDP - elif (ipo.proto == IPPROTO_UDP): - flag3 = 0 - udpo = pkt.get_protocol(udp.udp) - if self.inner_policy.has_key(ipo.src): - temp = self.inner_policy.get(ipo.src) - for i in range(0,len(temp)): - if temp[i][0] == ipo.dst: - xyz = temp[i] - if((xyz[1]=='UDP') and (int(xyz[2]) == udpo.src_port) and (int(xyz[3]) == udpo.dst_port) and (xyz[5] == 'ALLOW')): - flag3 = 1 - actions_default = action_fwd_to_out_port - self.udp_conn_track = self.track.conn_track_dict(self.udp_conn_track,ipo.src, ipo.dst, udpo.src_port,udpo.dst_port , xyz[5],1) - break - - if (flag3 == 0): - actions_default = action_drop - - else: - self.logger.info("Wrong IP protocol found") - actions_default = action_drop - - # Handling ARP Rules. - elif(ethtype == ETH_TYPE_ARP): - self.arp_handling(datapath, out_port, eth, in_port) - actions_default = action_fwd_to_out_port - else: - actions_default = action_drop - - except Exception as err: - self.logger.info("ERROR: %s" , err.message) - action_drop = [parser.OFPActionOutput(ofproto.OFPPC_NO_FWD)] - actions_default = action_drop - - finally: - self.sendpkt.send(datapath, msg, in_port, actions_default) - - - """ - Add ARP rules on flow tables - """ - def arp_handling(self,datapath, out_port, eth_obj, in_port): - if(out_port != datapath.ofproto.OFPP_FLOOD): - actions = [datapath.ofproto_parser.OFPActionOutput(out_port)] - self.flow.add_flow(datapath=datapath, actions=actions, priority=1000, in_port=in_port, - eth_type= ETH_TYPE_ARP, eth_src= eth_obj.src, eth_dst = eth_obj.dst) - - - """ - Learn Switch port associated with a MAC address here - """ - def port_learn(self, datapath, eth_obj, in_port): - try: - self.mac_to_port.setdefault(datapath.id, {'90:e2:ba:1c:55:54':1 , '90:e2:ba:1c:55:55':2}) - self.mac_to_port[datapath.id][eth_obj.src] = in_port - - if (eth_obj.ethertype == ETH_TYPE_IP) or (eth_obj.ethertype == ETH_TYPE_ARP): - if eth_obj.dst in self.mac_to_port[datapath.id]: - out_port = self.mac_to_port[datapath.id][eth_obj.dst] - else: - out_port = datapath.ofproto.OFPP_FLOOD - except Exception as err: - self.info(err.message) - out_port = datapath.ofproto.OFPP_FLOOD - finally: - return out_port diff --git a/Directory/inefficient_stateless_firewall.py b/Directory/inefficient_stateless_firewall.py deleted file mode 100644 index b8fbe81..0000000 --- a/Directory/inefficient_stateless_firewall.py +++ /dev/null @@ -1,193 +0,0 @@ -# Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. -# -# 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. - -from ryu.base import app_manager -from ryu.controller import ofp_event, dpset -from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER,set_ev_cls -from ryu.ofproto import ofproto_v1_3, ofproto_v1_3_parser -from ryu.lib.packet import packet,ethernet,ipv4,udp,tcp,icmp -from ryu.ofproto.ether import ETH_TYPE_IP, ETH_TYPE_ARP,ETH_TYPE_LLDP,ETH_TYPE_MPLS,ETH_TYPE_IPV6 -from ryu.ofproto.inet import IPPROTO_ICMP, IPPROTO_TCP, IPPROTO_UDP,IPPROTO_SCTP -from parse_firewall_rules import parse_firewall -from switch_information import SwitchInfo -from packet_out import SendPacket -from construct_flow import Construct -from connection_tracking import TrackConnection -ICMP_PING = 8 -ICMP_PONG = 0 -TCP_SYN = 0x02 -TCP_ACK = 0x10 -TCP_BOGUS_FLAGS = 0x15 - -class InefficientFirewall(app_manager.RyuApp): - OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] - - inner_policy = {} - sendpkt = SendPacket() - flow = Construct() - track = TrackConnection() - - def __init__(self, *args, **kwargs): - super(InefficientFirewall, self).__init__(*args, **kwargs) - self.mac_to_port = {} - parser = parse_firewall() - self.inner_policy = parser.parse() - self.logger.info("dict is ready") - - - @set_ev_cls(dpset.EventDP, dpset.DPSET_EV_DISPATCHER) - def handler_datapath(self, ev): - SwitchInfo(ev) - - - """ - Handles incoming packets. Decode them - and check for suitable Firewall Rules. - """ - @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) - def packet_in_handler(self, ev): - msg = ev.msg - datapath = msg.datapath - ofproto = datapath.ofproto - parser = datapath.ofproto_parser - in_port = msg.match['in_port'] - - try: - pkt = packet.Packet(msg.data) - eth = pkt.get_protocols(ethernet.ethernet)[0] - ethtype = eth.ethertype - - out_port = self.port_learn(datapath, eth, in_port) - action_fwd_to_out_port = [parser.OFPActionOutput(out_port)] - action_drop = [parser.OFPActionOutput(ofproto.OFPPC_NO_FWD)] - actions_default = action_fwd_to_out_port - - if(out_port != ofproto.OFPP_FLOOD) and (ethtype == ETH_TYPE_IP): - ipo = pkt.get_protocols(ipv4.ipv4)[0] - - #Check for ICMP - if(ipo.proto == IPPROTO_ICMP): - flag1 = 0 - icmpob = pkt.get_protocol(icmp.icmp) - #Check if this is PING or PONG - if ((icmpob.type==ICMP_PING) and self.inner_policy.has_key(ipo.src)): - temp = self.inner_policy.get(ipo.src) - for i in range(0,len(temp)): - if temp[i][0] == ipo.dst: - xyz = temp[i] - if((xyz[1]=='ICMP') and (xyz[4] == 'PING') and (xyz[5] == 'ALLOW')): - flag1 = 1 - actions_default = action_fwd_to_out_port - break - - elif((icmpob.type == ICMP_PONG) and (self.inner_policy.has_key(ipo.src))): - temp = self.inner_policy.get(ipo.src) - for i in range(0,len(temp)): - if temp[i][0] == ipo.dst: - xyz = temp[i] - if ((xyz[1]=='ICMP') and (xyz[4] == 'PONG') and (xyz[5] == 'ALLOW')): - flag1 = 1 - actions_default = action_fwd_to_out_port - break - - if (flag1 == 0): - actions_default = action_drop - - - #Check for TCP. - elif (ipo.proto == IPPROTO_TCP): - flag2 = 0 - tcpo = pkt.get_protocol(tcp.tcp) - # TCP packet with firewall rule - if self.inner_policy.has_key(ipo.src): - temp = self.inner_policy.get(ipo.src) - for i in range(0,len(temp)): - if ((temp[i][0] == ipo.dst) and (temp[i][1]=='TCP') and (int(temp[i][2]) == tcpo.src_port) and (int(temp[i][3]) == tcpo.dst_port) and (temp[i][4].upper() == 'ANY') and (temp[i][5] == 'ALLOW')): - flag2 = 1 - actions_default = action_fwd_to_out_port - break - elif (((tcpo.bits & TCP_ACK) == TCP_ACK) and (temp[i][0] == ipo.dst) and (temp[i][1]=='TCP') and (int(temp[i][2]) == tcpo.src_port) and (int(temp[i][3]) == tcpo.dst_port) and (temp[i][4].upper() == 'YES') and (temp[i][5] == 'ALLOW')): - flag2 = 1 - actions_default = action_fwd_to_out_port - break - - if (flag2 == 0): - actions_default = action_drop - - #Check for UDP - elif (ipo.proto == IPPROTO_UDP): - flag3 = 0 - udpo = pkt.get_protocol(udp.udp) - if self.inner_policy.has_key(ipo.src): - temp = self.inner_policy.get(ipo.src) - for i in range(0,len(temp)): - if temp[i][0] == ipo.dst: - xyz = temp[i] - if((xyz[1]=='UDP') and (int(xyz[2]) == udpo.src_port) and (int(xyz[3]) == udpo.dst_port) and (xyz[5] == 'ALLOW')): - flag3 = 1 - actions_default = action_fwd_to_out_port - break - - if (flag3 == 0): - actions_default = action_drop - - else: - self.logger.info("Wrong IP protocol found") - actions_default = action_drop - - # Handling ARP Rules. - elif(ethtype == ETH_TYPE_ARP): - self.arp_handling(datapath, out_port, eth, in_port) - actions_default = action_fwd_to_out_port - else: - actions_default = action_drop - - except Exception as err: - self.logger.info("ERROR: %s" , err.message) - action_drop = [parser.OFPActionOutput(ofproto.OFPPC_NO_FWD)] - actions_default = action_drop - - finally: - self.sendpkt.send(datapath, msg, in_port, actions_default) - - - """ - Add ARP rules on flow tables - """ - def arp_handling(self,datapath, out_port, eth_obj, in_port): - if(out_port != datapath.ofproto.OFPP_FLOOD): - actions = [datapath.ofproto_parser.OFPActionOutput(out_port)] - self.flow.add_flow(datapath=datapath, actions=actions, priority=1000, in_port=in_port, - eth_type= ETH_TYPE_ARP, eth_src= eth_obj.src, eth_dst = eth_obj.dst) - - - """ - Learn Switch port associated with a MAC address here - """ - def port_learn(self, datapath, eth_obj, in_port): - try: - self.mac_to_port.setdefault(datapath.id, {'90:e2:ba:1c:55:54':1 , '90:e2:ba:1c:55:55':2}) - self.mac_to_port[datapath.id][eth_obj.src] = in_port - - if (eth_obj.ethertype == ETH_TYPE_IP) or (eth_obj.ethertype == ETH_TYPE_ARP): - if eth_obj.dst in self.mac_to_port[datapath.id]: - out_port = self.mac_to_port[datapath.id][eth_obj.dst] - else: - out_port = datapath.ofproto.OFPP_FLOOD - except Exception as err: - self.info(err.message) - out_port = datapath.ofproto.OFPP_FLOOD - finally: - return out_port diff --git a/Directory/reset_flow_table.py b/Directory/reset_flow_table.py deleted file mode 100644 index 6c9b96b..0000000 --- a/Directory/reset_flow_table.py +++ /dev/null @@ -1,40 +0,0 @@ -#! usr/bin/env python -import logging -from ryu.ofproto.ether import ETH_TYPE_LLDP -from construct_flow import Construct - -class ResetSwitch(): - """ - Reset the switch. - Flush all flow table entries. - set up default behavior - """ - - def __init__(self,dp): - - self.__reset_switch(dp) - - - def __reset_switch(self,dp): - assert (dp is not None),"Datapath Object is Not set. " - - ofproto = dp.ofproto - parser = dp.ofproto_parser - flow_mod = dp.ofproto_parser.OFPFlowMod(dp,0,0,0, - ofproto.OFPFC_DELETE, - 0,0,1, - ofproto.OFPCML_NO_BUFFER, - ofproto.OFPP_ANY, - ofproto.OFPG_ANY, - ) - logging.info("deleting all flow table entries in the tables :" ) - dp.send_msg(flow_mod) - - const = Construct() - - actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, - ofproto.OFPCML_NO_BUFFER)] - const.add_flow(datapath = dp, actions = actions, priority=0) - - actions = [parser.OFPActionOutput(ofproto.OFPPC_NO_FWD)] - const.add_flow(datapath = dp, actions = actions, priority=10000, eth_type=ETH_TYPE_LLDP) diff --git a/Directory/secure_stateful_firewall.py b/Directory/secure_stateful_firewall.py deleted file mode 100644 index fe2a8cb..0000000 --- a/Directory/secure_stateful_firewall.py +++ /dev/null @@ -1,231 +0,0 @@ -# Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. -# -# 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. - -from ryu.base import app_manager -from ryu.controller import ofp_event, dpset -from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER,set_ev_cls -from ryu.ofproto import ofproto_v1_3, ofproto_v1_3_parser -from ryu.lib.packet import packet,ethernet,ipv4,udp,tcp,icmp -from ryu.ofproto.ether import ETH_TYPE_IP, ETH_TYPE_ARP,ETH_TYPE_LLDP,ETH_TYPE_MPLS,ETH_TYPE_IPV6 -from ryu.ofproto.inet import IPPROTO_ICMP, IPPROTO_TCP, IPPROTO_UDP,IPPROTO_SCTP -from parse_firewall_rules import parse_firewall -from switch_information import SwitchInfo -from packet_out import SendPacket -from construct_flow import Construct -from connection_tracking import TrackConnection -ICMP_PING = 8 -ICMP_PONG = 0 -TCP_SYN = 0x02 -TCP_SYN_ACK = 0x12 -TCP_BOGUS_FLAGS = 0x15 - -class SecureFirewall(app_manager.RyuApp): - OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] - - inner_policy = {} - icmp_conn_track = {} - tcp_conn_track = {} - udp_conn_track = {} - sendpkt = SendPacket() - flow = Construct() - track = TrackConnection() - - def __init__(self, *args, **kwargs): - super(SecureFirewall, self).__init__(*args, **kwargs) - self.mac_to_port = {} - parser = parse_firewall() - self.inner_policy = parser.parse() - self.logger.info("dict is ready") - - - @set_ev_cls(dpset.EventDP, dpset.DPSET_EV_DISPATCHER) - def handler_datapath(self, ev): - SwitchInfo(ev) - - - """ - Handles incoming packets. Decode them - and check for suitable Firewall Rules. - """ - @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) - def packet_in_handler(self, ev): - msg = ev.msg - datapath = msg.datapath - ofproto = datapath.ofproto - parser = datapath.ofproto_parser - in_port = msg.match['in_port'] - - try: - pkt = packet.Packet(msg.data) - eth = pkt.get_protocols(ethernet.ethernet)[0] - ethtype = eth.ethertype - - out_port = self.port_learn(datapath, eth, in_port) - action_fwd_to_out_port = [parser.OFPActionOutput(out_port)] - action_drop = [parser.OFPActionOutput(ofproto.OFPPC_NO_FWD)] - action_fwd_to_in_port = [parser.OFPActionOutput(in_port)] - actions_default = action_fwd_to_out_port - - if(out_port != ofproto.OFPP_FLOOD) and (ethtype == ETH_TYPE_IP): - ipo = pkt.get_protocols(ipv4.ipv4)[0] - - #Check for ICMP - if(ipo.proto == IPPROTO_ICMP): - icmpob = pkt.get_protocol(icmp.icmp) - flag1 = 0 - #Check if this is PING or PONG - if ((icmpob.type==ICMP_PING) and self.inner_policy.has_key(ipo.src)): - temp = self.inner_policy.get(ipo.src) - for i in range(0,len(temp)): - if temp[i][0] == ipo.dst: - xyz = temp[i] - if((xyz[1]=='ICMP') and (xyz[5] == 'ALLOW')): - flag1 = 1 - actions_default = action_fwd_to_out_port - self.icmp_conn_track = self.track.conn_track_dict(self.icmp_conn_track,ipo.src, ipo.dst, "PING", "PONG", xyz[5],2) - self.flow.add_flow(datapath=datapath, actions=actions_default, priority=1001, in_port = in_port, - eth_type = ETH_TYPE_IP, ip_proto = IPPROTO_ICMP, icmpv4_type= ICMP_PING, - ipv4_src = ipo.src, ipv4_dst= ipo.dst) - self.flow.add_flow(datapath=datapath, actions=action_fwd_to_in_port, priority=1000, in_port = out_port, - eth_type = ETH_TYPE_IP, ip_proto = IPPROTO_ICMP, icmpv4_type= ICMP_PONG, - ipv4_src = ipo.dst, ipv4_dst= ipo.src) - break - if (flag1 == 0): - actions_default = action_drop - self.flow.add_flow(datapath=datapath, actions=actions_default, priority=1001, in_port = in_port, - eth_type = ETH_TYPE_IP, ip_proto = IPPROTO_ICMP, icmpv4_type= icmpob.type, - ipv4_src = ipo.src, ipv4_dst= ipo.dst) - - #Check for TCP. - elif (ipo.proto == IPPROTO_TCP): - tcpo = pkt.get_protocol(tcp.tcp) - flag2 = 0 - # TCP SYN packet - if (((tcpo.bits & TCP_SYN) == TCP_SYN) & ((tcpo.bits & TCP_BOGUS_FLAGS) == 0x00)): - if self.inner_policy.has_key(ipo.src): - temp = self.inner_policy.get(ipo.src) - for i in range(0,len(temp)): - if ((temp[i][0] == ipo.dst) and (temp[i][1]=='TCP') and (int(temp[i][2]) == tcpo.src_port) and (int(temp[i][3]) == tcpo.dst_port) and (temp[i][5] == 'ALLOW')): - flag2= 1 - actions_default = action_fwd_to_out_port - self.tcp_conn_track = self.track.conn_track_dict(self.tcp_conn_track,ipo.src, ipo.dst, tcpo.src_port, tcpo.dst_port, tcpo.seq, 1) - self.flow.add_flow(datapath=datapath, actions=actions_default, priority=1001, in_port=in_port, - eth_type = ETH_TYPE_IP, ip_proto = IPPROTO_TCP, - ipv4_src = ipo.src, ipv4_dst = ipo.dst, - tcp_src = tcpo.src_port, tcp_dst = tcpo.dst_port) - break - - # TCP SYN ACK packet - elif((tcpo.bits & TCP_SYN_ACK) == TCP_SYN_ACK): - if self.tcp_conn_track.has_key(ipo.dst): - temp2 = self.tcp_conn_track.get(ipo.dst) - for i in range(0,len(temp2)): - if ((temp2[i][0] == ipo.src) and (int(temp2[i][1]) == tcpo.dst_port) and (int(temp2[i][2]) == tcpo.src_port)): - flag2 = 1 - actions_default = action_fwd_to_out_port - self.tcp_conn_track = self.track.conn_track_dict(self.tcp_conn_track,ipo.src, ipo.dst, tcpo.src_port, tcpo.dst_port, tcpo.seq, 1) - self.flow.add_flow(datapath=datapath, actions=actions_default, priority=1001, in_port=in_port, - eth_type = ETH_TYPE_IP, ip_proto = IPPROTO_TCP, - ipv4_src = ipo.src, ipv4_dst = ipo.dst, - tcp_src = tcpo.src_port, tcp_dst = tcpo.dst_port) - break - - # All remaining TCP packets, like, ACK, PUSH, FIN etc. - else: - if self.tcp_conn_track.has_key(ipo.src): - temp3 = self.tcp_conn_track.get(ipo.src) - for i in range(0,len(temp3)): - if ((temp3[i][0] == ipo.dst) and (int(temp3[i][1]) == tcpo.src_port) and (int(temp3[i][2]) == tcpo.dst_port)): - flag2 = 1 - actions_default = action_fwd_to_out_port - break - if (flag2 == 0): - actions_default = action_drop - - - #Check for UDP - elif (ipo.proto == IPPROTO_UDP): - udpo = pkt.get_protocol(udp.udp) - flag3 = 0 - if self.inner_policy.has_key(ipo.src): - temp = self.inner_policy.get(ipo.src) - for i in range(0,len(temp)): - if temp[i][0] == ipo.dst: - xyz = temp[i] - if((xyz[1]=='UDP') and (int(xyz[2]) == udpo.src_port) and (int(xyz[3]) == udpo.dst_port) and (xyz[5] == 'ALLOW')): - flag3 = 1 - actions_default = action_fwd_to_out_port - self.udp_conn_track = self.track.conn_track_dict(self.udp_conn_track,ipo.src, ipo.dst, udpo.src_port,udpo.dst_port , xyz[5],2) - self.flow.add_flow(datapath=datapath, actions=actions_default, priority=1001, in_port=in_port, - eth_type = ETH_TYPE_IP, ip_proto = IPPROTO_UDP, - ipv4_src = ipo.src, ipv4_dst = ipo.dst, - udp_src = udpo.src_port, udp_dst = udpo.dst_port) - - self.flow.add_flow(datapath=datapath, actions=action_fwd_to_in_port, priority=1000, in_port=out_port, - eth_type = ETH_TYPE_IP, ip_proto = IPPROTO_UDP, - ipv4_src = ipo.dst, ipv4_dst = ipo.src, - udp_src = udpo.dst_port, udp_dst = udpo.src_port) - break - if (flag3 == 0): - actions_default = action_drop - - else: - self.logger.info("Wrong IP protocol found") - actions_default = action_drop - - # Handling ARP Rules. - elif(ethtype == ETH_TYPE_ARP): - self.arp_handling(datapath, out_port, eth, in_port) - actions_default = action_fwd_to_out_port - else: - actions_default = action_drop - - except Exception as err: - self.logger.info("ERROR: %s" , err.message) - action_drop = [parser.OFPActionOutput(ofproto.OFPPC_NO_FWD)] - actions_default = action_drop - - finally: - self.sendpkt.send(datapath, msg, in_port, actions_default) - - - """ - Add ARP rules on flow tables - """ - def arp_handling(self,datapath, out_port, eth_obj, in_port): - if(out_port != datapath.ofproto.OFPP_FLOOD): - actions = [datapath.ofproto_parser.OFPActionOutput(out_port)] - self.flow.add_flow(datapath=datapath, actions=actions, priority=1000, in_port=in_port, - eth_type= ETH_TYPE_ARP, eth_src= eth_obj.src, eth_dst = eth_obj.dst) - - - """ - Learn Switch port associated with a MAC address here - """ - def port_learn(self, datapath, eth_obj, in_port): - try: - self.mac_to_port.setdefault(datapath.id, {'90:e2:ba:1c:55:54':1 , '90:e2:ba:1c:55:55':2}) - self.mac_to_port[datapath.id][eth_obj.src] = in_port - - if (eth_obj.ethertype == ETH_TYPE_IP) or (eth_obj.ethertype == ETH_TYPE_ARP): - if eth_obj.dst in self.mac_to_port[datapath.id]: - out_port = self.mac_to_port[datapath.id][eth_obj.dst] - else: - out_port = datapath.ofproto.OFPP_FLOOD - except Exception as err: - self.info(err.message) - out_port = datapath.ofproto.OFPP_FLOOD - finally: - return out_port diff --git a/Directory/secure_stateless_firewall.py b/Directory/secure_stateless_firewall.py deleted file mode 100644 index bc4227a..0000000 --- a/Directory/secure_stateless_firewall.py +++ /dev/null @@ -1,204 +0,0 @@ -# Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. -# -# 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. - -from ryu.base import app_manager -from ryu.controller import ofp_event, dpset -from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER,set_ev_cls -from ryu.ofproto import ofproto_v1_3, ofproto_v1_3_parser -from ryu.lib.packet import packet,ethernet,ipv4,udp,tcp,icmp -from ryu.ofproto.ether import ETH_TYPE_IP, ETH_TYPE_ARP,ETH_TYPE_LLDP,ETH_TYPE_MPLS,ETH_TYPE_IPV6 -from ryu.ofproto.inet import IPPROTO_ICMP, IPPROTO_TCP, IPPROTO_UDP,IPPROTO_SCTP -from parse_firewall_rules import parse_firewall -from switch_information import SwitchInfo -from packet_out import SendPacket -from construct_flow import Construct -from connection_tracking import TrackConnection -ICMP_PING = 8 -ICMP_PONG = 0 -TCP_SYN = 0x02 -TCP_ACK = 0x10 -TCP_BOGUS_FLAGS = 0x15 - -class SecureFirewall(app_manager.RyuApp): - OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] - - inner_policy = {} - sendpkt = SendPacket() - flow = Construct() - track = TrackConnection() - - def __init__(self, *args, **kwargs): - super(SecureFirewall, self).__init__(*args, **kwargs) - self.mac_to_port = {} - parser = parse_firewall() - self.inner_policy = parser.parse() - self.logger.info("dict is ready") - - - @set_ev_cls(dpset.EventDP, dpset.DPSET_EV_DISPATCHER) - def handler_datapath(self, ev): - SwitchInfo(ev) - - - """ - Handles incoming packets. Decode them - and check for suitable Firewall Rules. - """ - @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) - def packet_in_handler(self, ev): - msg = ev.msg - datapath = msg.datapath - ofproto = datapath.ofproto - parser = datapath.ofproto_parser - in_port = msg.match['in_port'] - - try: - pkt = packet.Packet(msg.data) - eth = pkt.get_protocols(ethernet.ethernet)[0] - ethtype = eth.ethertype - - out_port = self.port_learn(datapath, eth, in_port) - action_fwd_to_out_port = [parser.OFPActionOutput(out_port)] - action_drop = [parser.OFPActionOutput(ofproto.OFPPC_NO_FWD)] - action_fwd_to_in_port = [parser.OFPActionOutput(in_port)] - actions_default = action_fwd_to_out_port - - if(out_port != ofproto.OFPP_FLOOD) and (ethtype == ETH_TYPE_IP): - ipo = pkt.get_protocols(ipv4.ipv4)[0] - - #Check for ICMP - if(ipo.proto == IPPROTO_ICMP): - icmpob = pkt.get_protocol(icmp.icmp) - flag1 = 0 - #Check if this is PING or PONG - if ((icmpob.type==ICMP_PING) and self.inner_policy.has_key(ipo.src)): - temp = self.inner_policy.get(ipo.src) - for i in range(0,len(temp)): - if temp[i][0] == ipo.dst: - xyz = temp[i] - if((xyz[1]=='ICMP') and (xyz[4]=='PING') and (xyz[5] == 'ALLOW')): - flag1 = 1 - actions_default = action_fwd_to_out_port - self.flow.add_flow(datapath=datapath, actions=actions_default, priority=1001, in_port = in_port, - eth_type = ETH_TYPE_IP, ip_proto = IPPROTO_ICMP, icmpv4_type= ICMP_PING, - ipv4_src = ipo.src, ipv4_dst= ipo.dst) - self.flow.add_flow(datapath=datapath, actions=action_fwd_to_in_port, priority=1000, in_port = out_port, - eth_type = ETH_TYPE_IP, ip_proto = IPPROTO_ICMP, icmpv4_type= ICMP_PONG, - ipv4_src = ipo.dst, ipv4_dst= ipo.src) - break - if (flag1 == 0): - actions_default = action_drop - self.flow.add_flow(datapath=datapath, actions=actions_default, priority=1001, in_port = in_port, - eth_type = ETH_TYPE_IP, ip_proto = IPPROTO_ICMP, icmpv4_type= icmpob.type, - ipv4_src = ipo.src, ipv4_dst= ipo.dst) - - #Check for TCP. - elif (ipo.proto == IPPROTO_TCP): - tcpo = pkt.get_protocol(tcp.tcp) - flag2 = 0 - # TCP packet and Firewall Rule - if self.inner_policy.has_key(ipo.src): - temp = self.inner_policy.get(ipo.src) - for i in range(0,len(temp)): - if ((temp[i][0] == ipo.dst) and (temp[i][1]=='TCP') and (int(temp[i][2]) == tcpo.src_port) and (int(temp[i][3]) == tcpo.dst_port) and (temp[i][4].upper() == 'ANY') and (temp[i][5] == 'ALLOW')): - flag2 = 1 - actions_default = action_fwd_to_out_port - break - elif (((tcpo.bits & TCP_ACK) == TCP_ACK) and (temp[i][0] == ipo.dst) and (temp[i][1]=='TCP') and (int(temp[i][2]) == tcpo.src_port) and (int(temp[i][3]) == tcpo.dst_port) and (temp[i][4].upper() == 'YES') and (temp[i][5] == 'ALLOW')): - flag2 = 1 - actions_default = action_fwd_to_out_port - break - - if (flag2 != 0): - self.flow.add_flow(datapath=datapath, actions=actions_default, priority=1001, in_port=in_port, - eth_type = ETH_TYPE_IP, ip_proto = IPPROTO_TCP, - ipv4_src = ipo.src, ipv4_dst = ipo.dst, - tcp_src = tcpo.src_port, tcp_dst = tcpo.dst_port) - else: - actions_default = action_drop - - #Check for UDP - elif (ipo.proto == IPPROTO_UDP): - udpo = pkt.get_protocol(udp.udp) - flag3 = 0 - if self.inner_policy.has_key(ipo.src): - temp3 = self.inner_policy.get(ipo.src) - for i in range(0,len(temp3)): - if temp3[i][0] == ipo.dst: - xyz = temp3[i] - if((xyz[1]=='UDP') and (int(xyz[2]) == udpo.src_port) and (int(xyz[3]) == udpo.dst_port) and (xyz[5] == 'ALLOW')): - flag3 = 1 - actions_default = action_fwd_to_out_port - self.flow.add_flow(datapath=datapath, actions=actions_default, priority=1001, in_port=in_port, - eth_type = ETH_TYPE_IP, ip_proto = IPPROTO_UDP, - ipv4_src = ipo.src, ipv4_dst = ipo.dst, - udp_src = udpo.src_port, udp_dst = udpo.dst_port) - - self.flow.add_flow(datapath=datapath, actions=action_fwd_to_in_port, priority=1000, in_port=out_port, - eth_type = ETH_TYPE_IP, ip_proto = IPPROTO_UDP, - ipv4_src = ipo.dst, ipv4_dst = ipo.src, - udp_src = udpo.dst_port, udp_dst = udpo.src_port) - break - if (flag3 == 0): - actions_default = action_drop - - else: - self.logger.info("Wrong IP protocol found") - actions_default = action_drop - - # Handling ARP Rules. - elif(ethtype == ETH_TYPE_ARP): - self.arp_handling(datapath, out_port, eth, in_port) - actions_default = action_fwd_to_out_port - else: - actions_default = action_drop - - except Exception as err: - self.logger.info("ERROR: %s" , err.message) - action_drop = [parser.OFPActionOutput(ofproto.OFPPC_NO_FWD)] - actions_default = action_drop - - finally: - self.sendpkt.send(datapath, msg, in_port, actions_default) - - - """ - Add ARP rules on flow tables - """ - def arp_handling(self,datapath, out_port, eth_obj, in_port): - if(out_port != datapath.ofproto.OFPP_FLOOD): - actions = [datapath.ofproto_parser.OFPActionOutput(out_port)] - self.flow.add_flow(datapath=datapath, actions=actions, priority=1000, in_port=in_port, - eth_type= ETH_TYPE_ARP, eth_src= eth_obj.src, eth_dst = eth_obj.dst) - - - """ - Learn Switch port associated with a MAC address here - """ - def port_learn(self, datapath, eth_obj, in_port): - try: - self.mac_to_port.setdefault(datapath.id, {'90:e2:ba:1c:55:54':1 , '90:e2:ba:1c:55:55':2}) - self.mac_to_port[datapath.id][eth_obj.src] = in_port - - if (eth_obj.ethertype == ETH_TYPE_IP) or (eth_obj.ethertype == ETH_TYPE_ARP): - if eth_obj.dst in self.mac_to_port[datapath.id]: - out_port = self.mac_to_port[datapath.id][eth_obj.dst] - else: - out_port = datapath.ofproto.OFPP_FLOOD - except Exception as err: - self.info(err.message) - out_port = datapath.ofproto.OFPP_FLOOD - finally: - return out_port diff --git a/Directory/switch_information.py b/Directory/switch_information.py deleted file mode 100644 index eb1e03f..0000000 --- a/Directory/switch_information.py +++ /dev/null @@ -1,29 +0,0 @@ -#! usr/bin/env python -import logging -from reset_flow_table import ResetSwitch -class SwitchInfo(): - """ - This class handles switch connection and - disconnection information. Set the RYU Event - object and make the most out of it. - """ - - - def __init__(self,event): - switch = event.dp - if event.enter: - logging.info('datapath has joined') - logging.info(switch.ofproto) - logging.info(switch.ofproto_parser) - self.__switch_connected(switch) - else: - self.__switch_disconnected(switch) - - def __switch_connected(self, sw): - - logging.info("switch %s connected",sw.id) - ResetSwitch(sw) - - def __switch_disconnected(self, sw): - logging.info("switch %s disconnected",sw.id) - \ No newline at end of file diff --git a/2015-IDP-OpenFlow-Firewall.pdf b/Documents/2015-IDP-OpenFlow-Firewall.pdf similarity index 95% rename from 2015-IDP-OpenFlow-Firewall.pdf rename to Documents/2015-IDP-OpenFlow-Firewall.pdf index ae3e50f..1ec4fa4 100644 Binary files a/2015-IDP-OpenFlow-Firewall.pdf and b/Documents/2015-IDP-OpenFlow-Firewall.pdf differ diff --git a/Documents/Demo-SDN.txt b/Documents/Demo-SDN.txt new file mode 100644 index 0000000..72a17f4 --- /dev/null +++ b/Documents/Demo-SDN.txt @@ -0,0 +1,20 @@ +Kịch bản: +1. Ping theo database: +- Ping ICMP: + + Mở h1: xterm h1 -> ping 10.0.0.2 -> xem trên app có bị chặn. + -> ping 10.0.0.4 (ping trong cùng 1 switch xem nó cho phép truyền) + -> ping 10.0.0.15 (ping khác switch xem nó cho phép truyền) +- Ping TCP: + + Mở h2: xterm h2 -> hping3 -c 10 -k -s s_port -p dst_port -S 10.0.0.3 (ping TCP SYN)-> xem trên app có bị chặn. + -> ping TCP (SYN_ACK): hping3 -c 10 -k -s s_port -p dst_port -SA 10.0.0.3 -> xem trên app có bị chắn + + ping TCP cho phép truyền: hping3 -c 10 -k -s s_port -p dst_port -S 10.0.0.5 -> xem nó cho phép truyền trong cùng switch. + + hping3 -c 10 -k -s s_port -p dst_port -S 10.0.0.14 -> cho phép truyền khác switch. +- Ping UDP: tương ứn với kịch bản (TCP). +2. Thêm flow và check nó có bị chặn: +- Thêm flow ICMP từ h4 đến h10 (tcp port cho None). ping từ h4 đến h10 xem nó có bị chặn và có hiển thị trên app không. +- Thêm flow TCP từ h5 đến h9 (source_port: 1000, destination_port: 8080). hping3 từ h5 đến h9 xem nó có bị chặn và có hiển thị trên app không. -> hping3 từ h5 đến h9 với tcp_port khác (1000 và 1000) xem nó có cho phép truyền hay không. +- Thêm flow UDP từ h6 đến h8 (s_port: 8080, d_port: 1000). hping3 từ h6 đến h8 xem nó có bị chặn và có hiển thị trên app không. -> hping3 khác cổng (8080, 8080) xem nó cho phép truyền hay không. +3. Ping liên tục: +- Ping ICMP: mở h7 -> ping -f 10.0.0.10 -> xem trong vòng một vài giây xem nó có bị chặn và hiển thị trên app hay không -> và mininet: "sh ovs-ofctl dump-flows s1" để xem có flow drop hay không. -> ping lại từ h7 đến h10: h7 ping h10 (không flood) để xem nó có bị chặn hay không. +- Ping TCP: mở h4 -> hping3 đến h6. kịch bản tương tự như ping icmp. +- Ping UDP: mở h8 -> hping3 đến h12. kịch bản tương tự như ping icmp. diff --git a/Documents/Project's_Introduction.pdf b/Documents/Project's_Introduction.pdf new file mode 100644 index 0000000..c798c0a Binary files /dev/null and b/Documents/Project's_Introduction.pdf differ diff --git a/Documents/README.md b/Documents/README.md new file mode 100644 index 0000000..ff25c84 --- /dev/null +++ b/Documents/README.md @@ -0,0 +1,28 @@ +# Problem +* Why choice this topic. +* Advantages and disadvantages of traditional firewall and SDN firewall. +# Solve +* How does Firewall meet the advantages of SDN and how it solve disadvantage. +* What functions does Firewall have and compare for traditional firewall. +* Find out all the rules for the Firewall and deploy those instances. +* Use protocols :IP -> IPv4/IPv6, TCP/IP, UDP, ICMP +* Why block that host? What package will block and what doesn't block? +* Check for irregularities in flows. Different s\_ips have the same dst\_ip +* Check payload, if packages don't payload then this package is potential package attack to host/switch. +* There is no need to block all packets from a host, but to optimize it, block only files that are potentially malicious, or block only when the file has certain properties. +* Run real-time +# Works +1. Fix code -> Done +2. Find out all the rules for the Firewall and deploy those instances. + - Why block that host? What packets will block and what doesn't block? + - Corresponds to the rules. + - Check for irregularities in flows. Example: Diffrent s_ips have the same dst_ip, or One s_ips send packets to overnumber host. + - Check payload, if packets don't have payload then this packets is potential packets attack to host/switch. + - No need to block all packets from a host, justt block only files that are potential malicious, or block only when the file has certain properties. +3. Run real-time: + - Connect database: sql lite + - Connect api +# Result +# Conclusion +* Irrelevant: tracking package's payload use 'deep package inspection' and use AI for it + diff --git a/Documents/SDN-Firewall_And_Application_Control_Network.pdf b/Documents/SDN-Firewall_And_Application_Control_Network.pdf new file mode 100644 index 0000000..97cb035 Binary files /dev/null and b/Documents/SDN-Firewall_And_Application_Control_Network.pdf differ diff --git a/Documents/Works.txt b/Documents/Works.txt new file mode 100644 index 0000000..4d14bf1 --- /dev/null +++ b/Documents/Works.txt @@ -0,0 +1,10 @@ +1. Fix code -> Done +2. Find out all the rules for the Firewall and deploy those instances. + - Why block that host? What packets will block and what doesn't block? + - Check for irregularities in flows. Example: Diffrent s_ips have the same dst_ip, or One s_ips send packets to overnumber host. + - Check payload, if packets don't have payload then this packets is potential packets attack to host/switch. + - No need to block all packets from a host, justt block only files that are potential malicious, or block only when the file has certain properties. +3. Run real-time: + - Connect database: sql lite + - Connect api + diff --git a/Firewall/FirewallDrop.py b/Firewall/FirewallDrop.py new file mode 100644 index 0000000..819fd9a --- /dev/null +++ b/Firewall/FirewallDrop.py @@ -0,0 +1,393 @@ +from operator import attrgetter +from ryu.app import simple_switch_13 +from ryu.base import app_manager +from ryu.controller import ofp_event, dpset +from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER, set_ev_cls, DEAD_DISPATCHER +from ryu.ofproto import ofproto_v1_0, ofproto_v1_2, ofproto_v1_3 +from ryu.ofproto import ofproto_v1_0_parser, ofproto_v1_2_parser, ofproto_v1_3_parser +from ryu.lib import ofctl_v1_3 +from ryu.lib.packet import packet, ethernet, ipv4, udp, tcp, icmp +from ryu.ofproto.ether import ETH_TYPE_IP, ETH_TYPE_ARP, ETH_TYPE_LLDP, ETH_TYPE_MPLS, ETH_TYPE_IPV6 +from ryu.ofproto.inet import IPPROTO_ICMP, IPPROTO_TCP, IPPROTO_UDP, IPPROTO_SCTP +# custom lib +from ParseFirewallFromDB import ParseFirewallFromDB +from switch_information import SwitchInfo +from packet_out import SendPacket +from construct_flow import Construct +from connection_tracking import TrackConnection +from collections import defaultdict +from ryu.lib import hub +import time +from ryu.lib import dpid as dpid_lib + +ICMP_PING = 8 +ICMP_PONG = 0 +# hping3 -c 10 -k -s source_port -p destination_port -A dst_ip +TCP_SYN = 0x02 # -S +# hping3 -c 10 -k -s source_port -p destination_port -SA dst_ip +TCP_SYN_ACK = 0x12 # -SA +TCP_BOGUS_FLAGS = 0x15 #0x0F + +class SecureFirewall(app_manager.RyuApp): + OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION, + ofproto_v1_2.OFP_VERSION, + ofproto_v1_3.OFP_VERSION] + + database = ParseFirewallFromDB("dataset/firewall-drop.db") + inner_policy = {} + icmp_conn_track = {} + tcp_conn_track = {} + udp_conn_track = {} + sendpkt = SendPacket() + flow = Construct() + track = TrackConnection() + + def __init__(self, *args, **kwargs): + super(SecureFirewall, self).__init__(*args, **kwargs) + self.mac_to_port = {} + self.inner_policy = self.database.parse() + if self.inner_policy: + self.logger.info("Firewall rules parsed successfully from the database.") + else: + self.logger.error("Failed to parse firewall rules from the database.") + + # Get avenger value of stats on switch_information + self.total_packet = defaultdict(int) + self.datapaths = {} + self.monitor_thread = hub.spawn(self._monitor) + + self.logger.info("dict is ready") + # Get Stats + @set_ev_cls(ofp_event.EventOFPStateChange, + [MAIN_DISPATCHER, DEAD_DISPATCHER]) + def _state_change_handler(self, ev): + datapath = ev.datapath + if ev.state == MAIN_DISPATCHER: + if datapath.id not in self.datapaths: + self.logger.debug('register datapath: %016x', datapath.id) + self.datapaths[datapath.id] = datapath + elif ev.state == DEAD_DISPATCHER: + if datapath.id in self.datapaths: + self.logger.debug('unregister datapath: %016x', datapath.id) + del self.datapaths[datapath.id] + + def _monitor(self): + while True: + for dp in self.datapaths.values(): + dpid_str = dpid_lib.dpid_to_str(dp.id) + if dpid_str[:2] == '00': + self._request_stats(dp) + self.inner_policy = self.database.parse() + if self.inner_policy: + self.logger.info("Firewall rules parsed successfully from the database.") + else: + self.logger.error("Failed to parse firewall rules from the database.") + hub.sleep(5) + def _request_stats(self, datapath): + self.logger.debug('send stats request: %016x', datapath.id) + ofproto = datapath.ofproto + parser = datapath.ofproto_parser + + req = parser.OFPFlowStatsRequest(datapath) + datapath.send_msg(req) + @set_ev_cls( + [ ofp_event.EventOFPStatsReply, + ofp_event.EventOFPDescStatsReply, + ofp_event.EventOFPFlowStatsReply, + ofp_event.EventOFPAggregateStatsReply, + ofp_event.EventOFPTableStatsReply, + # ofp_event.EventOFPTableFeaturesStatsReply, + ofp_event.EventOFPPortStatsReply, + # ofp_event.EventOFPQueueStatsReply, + # ofp_event.EventOFPQueueDescStatsReply, + ofp_event.EventOFPMeterStatsReply, + ofp_event.EventOFPMeterFeaturesStatsReply, + ofp_event.EventOFPMeterConfigStatsReply, + ofp_event.EventOFPGroupStatsReply, + # ofp_event.EventOFPGroupFeaturesStatsReply, + ofp_event.EventOFPGroupDescStatsReply, + ofp_event.EventOFPPortDescStatsReply, + ], + MAIN_DISPATCHER, + ) + def _flow_stats_reply_handler(self, ev): + body = ev.msg.body + + datapath = ev.msg.datapath + ofproto = datapath.ofproto + parser = datapath.ofproto_parser + try: + for stat in sorted([flow for flow in body if flow.priority == 1001], + key=lambda flow: (flow.match.get('in_port', 0))): + + + self.total_packet[(stat.match['in_port'], stat.instructions[0].actions[0].port)] += 10 + print(stat.packet_count/self.total_packet[(stat.match['in_port'], stat.instructions[0].actions[0].port)]) + self.logger.info("Match: ") + self.logger.info(stat.match) + if(stat.packet_count / self.total_packet[(stat.match['in_port'], stat.instructions[0].actions[0].port)]) >= 3: + # Drop this packet here + actions = [] + inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)] + mod = parser.OFPFlowMod(datapath=datapath, priority=1002, match=stat.match, instructions=inst) + datapath.send_msg(mod) + + ipv4_src = stat.match.get('ipv4_src') + ipv4_dst = stat.match.get('ipv4_dst') + # in_port = stat.match.get('in_port') + # eth = stat.match.get('eth_type') + # ip_proto = stat.match.get('ip_proto') + self.logger.info("DROPED in --- in_port: {}, out_port: {}, IPv4_src: {}, IPv4_dst: {}".format(stat.match['in_port'], stat.instructions[0].actions[0].port, ipv4_src, ipv4_dst)) + + except Exception as err: + self.logger.info("ERROR in 140: %s", err) + + @set_ev_cls(dpset.EventDP, dpset.DPSET_EV_DISPATCHER) + def handler_datapath(self, ev): + SwitchInfo(ev) + + # Handles incoming packets. Decodes them and checks for suitable Firewall rules + @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) + def packet_in_handler(self, ev): + msg = ev.msg + datapath = msg.datapath + ofproto = datapath.ofproto + parser = datapath.ofproto_parser + in_port = msg.match['in_port'] + actions_default = [] + action_fwd_to_out_port = [] + + try: + pkt = packet.Packet(msg.data) + eth = pkt.get_protocols(ethernet.ethernet)[0] + ethtype = eth.ethertype + out_port = self.port_learn(datapath, eth, in_port) + action_drop = [parser.OFPActionOutput(ofproto.OFPPC_NO_FWD)] + action_fwd_to_out_port = [parser.OFPActionOutput(out_port)] + actions_default = action_fwd_to_out_port + + if(out_port != ofproto.OFPP_FLOOD) and (ethtype == ETH_TYPE_IP): + ipo = pkt.get_protocols(ipv4.ipv4)[0] + # check for ICMP + if ipo.proto == IPPROTO_ICMP: + icmpob = pkt.get_protocol(icmp.icmp) + flag1 = 0 + # Check if this is ICMP_PING + if ((icmpob.type==ICMP_PING) and (ipo.src in self.inner_policy)): + temp = self.inner_policy.get(ipo.src) + for i in range(0, len(temp)): + # print(temp[i][0]) + if temp[i][0] == ipo.dst: + xyz = temp[i] + # + # print(xyz[1->5]) + if(xyz[1] == "ICMP") and (xyz[5] == 'DROP'): #--- column in firewall rules + flag1 = 1 + actions_default = action_drop #drop + self.icmp_conn_track = self.track.conn_track_dict(self.icmp_conn_track,ipo.src, ipo.dst, "PING", "PONG", xyz[5],1) + self.logger.info("%s -> %s: Echo Request droped" % (ipo.src, ipo.dst)) + self.flow.add_flow(datapath=datapath, actions=actions_default, priority=1002, in_port=in_port, + eth_type=ETH_TYPE_IP, ip_proto=IPPROTO_ICMP, icmpv4_type=ICMP_PING, + ipv4_src=ipo.src, ipv4_dst = ipo.dst) + break + # Otherwise, ICMP_PONG + elif (icmpob.type == ICMP_PONG) and (ipo.dst in self.icmp_conn_track): + tmp = self.icmp_conn_track.get(ipo.dst) + for i in range(0, len(tmp)): + # + if tmp[i][0] == ipo.src: + xyz = tmp[i] + # + if(xyz[1] == 'PING') and (xyz[2] == 'PONG'): + print("ICMP track PING") + flag1 = 1 + actions_default = action_drop + self.flow.add_flow(datapath = datapath, actions=actions_default, + priority=1002, in_port=in_port, eth_type=ETH_TYPE_IP, + ip_proto = IPPROTO_ICMP, icmpv4_type=ICMP_PONG, + ipv4_src=ipo.src, ipv4_dst=ipo.dst) + self.icmp_conn_track = self.track.conn_track_dict(self.icmp_conn_track, ipo.src, + ipo.dst, "PONG", "PING", + xyz[3], 1) + self.logger.info("\n%s -> %s action = PING, state = BLOCKED \n" % (ipo.dst, ipo.src)) + # Time + # No match + if(flag1==0): + actions_default = action_fwd_to_out_port + self.flow.add_flow(datapath = datapath, actions=actions_default, + priority=1001, in_port=in_port, eth_type=ETH_TYPE_IP, + ip_proto = IPPROTO_ICMP, icmpv4_type=ICMP_PONG, + ipv4_src=ipo.src, ipv4_dst=ipo.dst) + + # Check for TCP + elif (ipo.proto == IPPROTO_TCP): + tcpo = pkt.get_protocol(tcp.tcp) + # tcp_payload = pkt.protocols[-1] + # print("--------") + # print("tcp.bits: ", tcpo.bits, " -- TCP_SYN: ", TCP_SYN, "-- &: ", (tcpo.bits & TCP_SYN)) + # print("--------") + flag2 = 0 + # TCP SYN packet + if ((tcpo.bits & TCP_SYN) == TCP_SYN) and ((tcpo.bits & TCP_BOGUS_FLAGS) == 0x00): + print("TCP_SYN!!") + if ipo.src in self.inner_policy: + temp = self.inner_policy.get(ipo.src) + for i in range(0, len(temp)): + # + # print("srcIP: ", ipo.src, " - dstIP: ", temp[i][0], " -- Protocol: ", temp[i][1], " -- srcPort: ", temp[i][2], " dstPort: ", temp[i][3]) + # print("srcIP: ", ipo.src, " - dstIP: ", ipo.dst, " -- Protocol: Nocheck", " -- srcPort: ", tcpo.src_port, " dstPort: ", tcpo.dst_port) + if((temp[i][0] == ipo.dst) and (temp[i][1] == 'TCP') and (int(temp[i][2]) == tcpo.src_port) and (int(temp[i][3]) == tcpo.dst_port) + and (temp[i][5] == 'DROP')): + flag2 = 1 + actions_default = action_drop + self.logger.info("%s -> %s : SYN DROPPED" % (ipo.src, ipo.dst)) + self.tcp_conn_track = self.track.conn_track_dict(self.tcp_conn_track, ipo.src, ipo.dst, tcpo.src_port, + tcpo.dst_port, tcpo.seq, 1) + self.flow.add_flow(datapath=datapath, actions=actions_default, priority = 1002, in_port=in_port, + eth_type = ETH_TYPE_IP, ip_proto= IPPROTO_TCP, + ipv4_src = ipo.src, ipv4_dst=ipo.dst, + tcp_src=tcpo.src_port, tcp_dst=tcpo.dst_port) + break + # TCP SYN ACK packet + elif (tcpo.bits & TCP_SYN_ACK) == TCP_SYN_ACK: + print("TCP_SYN_ACK!!") + if ipo.dst in self.tcp_conn_track: + temp2 = self.tcp_conn_track.get(ipo.dst) + for i in range(0, len(temp2)): + # + if (temp2[i][0] == ipo.src) and (int(temp2[i][1]) == tcpo.dst_port) and (int(temp2[i][2]) == tcpo.src_port): + # print("TCP SA track") + flag2 = 1 + actions_default = action_drop + self.logger.info("%s -> %s : SYN ACK DROPPED" % (ipo.src, ipo.dst)) + self.tcp_conn_track = self.track.conn_track_dict(self.tcp_conn_track, ipo.src, ipo.dst, tcpo.src_port, + tcpo.dst_port, tcpo.seq, 1) + self.flow.add_flow(datapath = datapath, actions=actions_default, priority = 1002, in_port = in_port, + eth_type = ETH_TYPE_IP, ip_proto = IPPROTO_TCP, ipv4_src = ipo.src, + ipv4_dst = ipo.dst, tcp_src=tcpo.src_port, tcp_dst=tcpo.dst_port) + self.logger.info("\n %s -> %s src_port=%s, dst_port=%s, state= BLOCKED\n" %(ipo.dst, ipo.src, temp2[i][1], temp2[i][2])) + break + # All remaining TCP packets + else: + if ipo.src in self.tcp_conn_track: + temp3 = self.tcp_conn_track.get(ipo.src) + for i in range(0, len(temp3)): + # + if (temp3[i][0] == ipo.dst) and (int(temp3[i][1]) == tcpo.src_port) and (int(temp3[i][2]) == tcpo.dst_port): + flag2 = 1 + actions_default = action_drop + self.logger.info("%s -> %s : TRANSMISSION DROPPED" % (ipo.src, ipo.dst)) + break + # No match + if flag2 == 0: + actions_default = action_fwd_to_out_port + self.flow.add_flow(datapath=datapath, actions=actions_default, priority = 1001, in_port=in_port, + eth_type = ETH_TYPE_IP, ip_proto= IPPROTO_TCP, + ipv4_src = ipo.src, ipv4_dst=ipo.dst, + tcp_src=tcpo.src_port, tcp_dst=tcpo.dst_port) + # self.logger.info("%s -> %s : ALLOWED" % (ipo.src, ipo.dst)) + + # Check for UDP + elif ipo.proto == IPPROTO_UDP: + flag3 = 0 + udpo = pkt.get_protocol(udp.udp) + # udp_payload = udpo.payload + # Check for tracked UDP + if ipo.dst in self.udp_conn_track: + tmp_tpl = self.udp_conn_track.get(ipo.dst) + tmp = list(tmp_tpl) + for i in range(0, len(tmp)): + # + if(tmp[i][0] == ipo.src): + xyz = tmp[i] + # + if (int(xyz[1]) == udpo.dst_port) and (int(xyz[2]) == udpo.src_port) and (xyz[3] == "DROPPED"): + flag3 = 1 + self.logger.info("%s -> %s : UDP PACKET DROP" % (ipo.src, ipo.dst)) + actions_default = action_drop + del tmp[i] + self.logger.info("\n%s -> %s src_port= %s, dst_port= %s, state= BLOCKED\n" %(ipo.src, ipo.dst, + udpo.src_port, udpo.dst_port)) + self.flow.add_flow(datapath=datapath, actions=actions_default, priority=1002, in_port = in_port, + eth_type = ETH_TYPE_IP, ip_proto = IPPROTO_UDP, + ipv4_src = ipo.src, ipv4_dst = ipo.dst, + udp_src = udpo.src_port, udp_dst = udpo.dst_port) + break + tmp_tpl = tuple(tmp) + if len(tmp_tpl) != 0: + self.udp_conn_track[ipo.dst] = tmp_tpl + else: + self.udp_conn_track.pop(ipo.dst, None) + # Check for first UDP packet + elif ipo.src in self.inner_policy: + temp = self.inner_policy.get(ipo.src) + for i in range(0, len(temp)): + # + if temp[i][0] == ipo.dst: + xyz = temp[i] + # + if (xyz[1] == 'UDP') and (int(xyz[2]) == udpo.src_port) and (int(xyz[3]) == udpo.dst_port) and (xyz[5] == 'DROP'): + flag3 = 1 + actions_default = action_drop + self.udp_conn_track = self.track.conn_track_dict(self.udp_conn_track, ipo.src, ipo.dst, udpo.src_port, + udpo.dst_port, "DROPPED", 1) + self.logger.info("%s -> %s : UDP PACKET BLOCKED" % (ipo.src, ipo.dst)) + self.logger.info("\n%s -> %s src_port= %s, dst_port= %s, state= DROPPED\n" %(ipo.src, ipo.dst, udpo.src_port, udpo.dst_port)) + self.flow.add_flow(datapath = datapath, actions = actions_default, priority = 1002, in_port = in_port, + eth_type =ETH_TYPE_IP, ip_proto=IPPROTO_UDP, + ipv4_src = ipo.src, ipv4_dst = ipo.dst, + udp_src = udpo.src_port, udp_dst = udpo.dst_port) + break + # No match + if flag3 == 0: + actions_default = action_fwd_to_out_port + # self.logger.info("%s -> %s : UDP ALLOW" % (ipo.src, ipo.dst)) + self.flow.add_flow(datapath = datapath, actions = actions_default, priority = 1001, + in_port = in_port, eth_type =ETH_TYPE_IP, ip_proto=IPPROTO_UDP, + ipv4_src = ipo.src, ipv4_dst = ipo.dst, + udp_src = udpo.src_port, udp_dst = udpo.dst_port) + + # If not ICMP, TCP or UDP then drop! + else: + self.logger.info("Wrong IP protoco found") + actions_default = action_drop + # Handling ARP rules + elif(ethtype == ETH_TYPE_ARP): + self.arp_handling(datapath, out_port, eth, in_port) + actions_default = action_fwd_to_out_port + # If packet is not IP or ARP then drop! + else: + actions_default = action_drop + + except Exception as err: + self.logger.info("ERROR in 425: %s", err) + action_drop = [parser.OFPActionOutput(ofproto.OFPPC_NO_FWD)] + actions_default = action_drop + finally: + self.sendpkt.send(datapath, msg, in_port, actions_default) + + # Add ARP rules on Flow Tables + def arp_handling(self, datapath, out_port, eth_obj, in_port): + if out_port != datapath.ofproto.OFPP_FLOOD: + actions = [datapath.ofproto_parser.OFPActionOutput(out_port)] + self.flow.add_flow(datapath=datapath, actions = actions, priority=1001, + in_port=in_port, eth_type=ETH_TYPE_ARP, eth_src=eth_obj.src,eth_dst=eth_obj.dst) + + def port_learn(self, datapath, eth_obj, in_port): + try: + self.mac_to_port.setdefault(datapath.id, {'00:00:00:00:00:01':1, '00:00:00:00:00:02':2, '00:00:00:00:00:03':3, '00:00:00:00:00:04':5, + '00:00:00:00:00:06':6,'00:00:00:00:00:07':7,'00:00:00:00:00:08':8,'00:00:00:00:00:09':9, + '00:00:00:00:00:10':10,'00:00:00:00:00:11':11,'00:00:00:00:00:12':12,'00:00:00:00:00:12':13, + '00:00:00:00:00:14':14,'00:00:00:00:00:15':15,}) + self.mac_to_port[datapath.id][eth_obj.src] = in_port + + if (eth_obj.ethertype == ETH_TYPE_IP) or (eth_obj.ethertype == ETH_TYPE_ARP): + if eth_obj.dst in self.mac_to_port[datapath.id]: + out_port = self.mac_to_port[datapath.id][eth_obj.dst] + return out_port + out_port = datapath.ofproto.OFPP_FLOOD + return out_port + except Exception as err: + self.info("Error in 435: ",err) + out_port = datapath.ofproto.OFPP_FLOOD + return out_port diff --git a/Firewall/ParseFirewallFromDB.py b/Firewall/ParseFirewallFromDB.py new file mode 100644 index 0000000..974ac3f --- /dev/null +++ b/Firewall/ParseFirewallFromDB.py @@ -0,0 +1,140 @@ +import sqlite3 +from connection_tracking import TrackConnection + +class ParseFirewallFromDB: + def __init__(self, db_file): + self.db_file = db_file + self.conn = sqlite3.connect(db_file) + self.cursor = self.conn.cursor() + def __del__(self): + self.conn.close() + + def parse(self): + firewall_dict = {} + + try: + self.cursor.execute("SELECT src_ip, dst_ip, protocol, src_port, dst_port, state, action FROM firewall_rules") + rows = self.cursor.fetchall() + + print("Firewall Rules:") + print("Source IP | Destination IP | Protocol | Source Port | Destination Port | State | Action") + for row in rows: + src_ip, dst_ip, protocol, src_port, dst_port, state, action = row + print(f"{src_ip} | {dst_ip} | {protocol} | {src_port} | {dst_port} | {state} | {action}") + + key = str(src_ip) + value = (dst_ip, protocol, src_port, dst_port, state, action) + + if key not in firewall_dict: + firewall_dict[key] = [value] + else: + firewall_dict[key].append(value) + # self.conn.close() + return firewall_dict + except sqlite3.Error as e: + print("Error in parse database: ", e) + return {} + + def insert_firewall_rules(self, firewall_rules): + for rule in firewall_rules: + self.cursor.execute(''' + INSERT INTO firewall_rules (src_ip, dst_ip, protocol, src_port, dst_port, state, action) + VALUES (?, ?, ?, ?, ?, ?, ?) + ''', rule) + self.conn.commit() + + # firewall_rules = [ + # ("10.0.0.3", "10.0.0.4", "ICMP", None, None, "PING", "DROP"), + # ("10.0.0.1", "10.0.0.2", "TCP", 1000, 8080, "NEW", "ALLOW"), + # ] +if __name__ == "__main__": + # db_file = "dataset/firewall-drop.db" + db_file = "dataset/firewall-drop.db" + parser = ParseFirewallFromDB(db_file) + inner_policy = parser.parse() + if inner_policy: + print("Successfully parsed database") + else: + print("Failed to parse database") + # parser.close_connection() + # check with code on (if) to see what it queyre + # check ICMP + # tcp_conn_track = {} + # track = TrackConnection() + # for src_ip in inner_policy: + # temp = inner_policy.get(src_ip) + # for i in range(0, len(temp)): + # # print(i, " - ip: ", temp[i][0]) + # xyz = temp[i] + # # print(i, " - xyz[1]=",xyz[1], " - xyz[5]=", xyz[5]) + # icmp_conn_track = track.conn_track_dict(icmp_conn_track,src_ip, temp[i][0], "PING", "PONG", xyz[5],1) + # for dst in icmp_conn_track: + # temp = icmp_conn_track.get(dst) + # print("dst_ip: ", dst) + # for i in range(0, len(temp)): + # print(i, " -ip: ", temp[i][0]) + # xyz = temp[iself] + # print("PING - [1]: ", xyz[1], " -- PONG - [2]: ", xyz[2]) + # print("") + # Check TCP + # for src_ip in inner_policy: + # temp = inner_policy.get(src_ip) + # print("srcIP: ", src_ip) + # for i in range(0, len(temp)): + # print(i, " - dstIP: ", temp[i][0], " -- Protocol: ",temp[i][1], " -- srcPort: ", temp[i][2], " -- dstPort: ", temp[i][3]) + # xyz = temp[i] + # print(i, " - xyz[1]=",xyz[1], " - xyz[5]=", xyz[5]) + # tcp_conn_track = track.conn_track_dict(tcp_conn_track,src_ip, temp[i][0], "PING", "PONG", xyz[5],1) + # print("") + # print("------------------------") + # print("--------------------New-------------") + # for dst_ip in tcp_conn_track: + # temp = tcp_conn_track.get(dst_ip) + # print("") + # print("dstIP: ", dst_ip) + # for i in range(0, len(temp)): + # print(i, " - srcIP: ", temp[i][0], " -- srcPort: ", temp[i][2], " -- dstPort: ", temp[i][1]) + # print("") + # print("---------------------") + # Check UDP + udp_conn_track = {} + # ipo = pkt.get_protocols(ipv4.ipv4)[0] + track = TrackConnection() + for src in inner_policy: + + temp = inner_policy.get(src) + for i in range(0, len(temp)): + print("ip_src: {}, temp: protocol[1] {}, src_port[2] {}, dst_port[3] {}, [4] {}, ip_dst[0] {}".format(src, temp[i][1],temp[i][2],temp[i][3],temp[i][4],temp[i][0])) + # if temp[i][0] == dst: + xyz = temp[i] + # + print("xzy: [1] {}, [2] {}, [3] {}, [4] {}, [5] {}".format(xyz[1],xyz[2],xyz[3],xyz[4],xyz[5])) + print("") + udp_conn_track = track.conn_track_dict(udp_conn_track, src, temp[i][0], temp[i][2], + temp[i][3], "DROP", 1) + print("------------------------") + # if (xyz[1] == 'UDP') and (int(xyz[2]) == udpo.src_port) and (int(xyz[3]) == udpo.dst_port) and (xyz[5] == 'DROP'): + print("===============================") + for dst in udp_conn_track: + tmp_tpl = udp_conn_track.get(dst) + tmp = list(tmp_tpl) + print("dst_ip: {}".format(dst)) + for i in range(0, len(tmp)): + # + # if(tmp[i][0] == ipo.src): + xyz = tmp[i] + # + print("xzy: src_ip[0] {}, [1] {}, [2] {}, [3] {}".format(xyz[0], xyz[1],xyz[2],xyz[3])) + # if (int(xyz[1]) == udpo.dst_port) and (int(xyz[2]) == udpo.src_port) and (xyz[3] == "UNREPLIED"): + # + # self.logger.info("%s -> %s : UDP PACKET ALLOWED" % (ipo.src, ipo.dst)) + # actions_default = action_fwd_to_out_port + # del tmp[i] + # self.logger.info("\n%s -> %s src_port= %s, dst_port= %s, state= ASSURED\n" %(ipo.src, ipo.dst, + # break + tmp_tpl = tuple(tmp) + if len(tmp_tpl) != 0: + udp_conn_track[dst] = tmp_tpl + else: + udp_conn_track.pop(dst, None) + diff --git a/Firewall/SQL/ParseFirewallFromDB.py b/Firewall/SQL/ParseFirewallFromDB.py new file mode 100644 index 0000000..a1a1b99 --- /dev/null +++ b/Firewall/SQL/ParseFirewallFromDB.py @@ -0,0 +1,49 @@ +import sqlite3 + +class ParseFirewallFromDB: + def __init__(self, db_file): + self.db_file = db_file + self.conn = sqlite3.connect(db_file) + self.cursor = self.conn.cursor() + + def parse(self): + firewall_dict = {} + + try: + self.cursor.execute("SELECT src_ip, dst_ip, protocol, src_port, dst_port, state, action FROM firewall_rules") + rows = self.cursor.fetchall() + + # In ra thông tin từ các hàng trong bảng + print("Firewall Rules:") + print("Source IP | Destination IP | Protocol | Source Port | Destination Port | State | Action") + for row in rows: + src_ip, dst_ip, protocol, src_port, dst_port, state, action = row + print(f"{src_ip} | {dst_ip} | {protocol} | {src_port} | {dst_port} | {state} | {action}") + key = str(src_ip) + value = (dst_ip, protocol, src_port, dst_port, state, action) + + if key not in firewall_dict: + firewall_dict[key] = [value] + else: + firewall_dict[key].append(value) + # self.conn.close() + return firewall_dict + except sqlite3.Error as e: + print("Error in parse database: ", e) + return {} + + def close_connection(self): + self.conn.close() + +if __name__ == "__main__": + db_file = "firewall-vTest.db" + parser = ParseFirewallFromDB(db_file) + inner_policy = parser.parse() + if inner_policy: + print("Successfully parsed database") + else: + print("Failed to parse database") + parser.close_connection() + # print("SQLite version: ",sqlite3.version, " -> sqlite3.version") + # print("Lib version sqlite3: ", sqlite3.sqlite_version, " -> sqlite3.sqlite_version") + diff --git a/Firewall/SQL/firewall-vTest.db b/Firewall/SQL/firewall-vTest.db new file mode 100644 index 0000000..41dc32c Binary files /dev/null and b/Firewall/SQL/firewall-vTest.db differ diff --git a/Firewall/SQL/firewall.csv b/Firewall/SQL/firewall.csv new file mode 100644 index 0000000..f002cb2 --- /dev/null +++ b/Firewall/SQL/firewall.csv @@ -0,0 +1,17 @@ +10.0.0.1,10.0.0.2,TCP,1000,8080,NEW,ALLOW +10.0.0.1,10.0.0.3,TCP,1000,8080,NEW,ALLOW +10.0.0.2,10.0.0.3,TCP,1000,1000,NEW,ALLOW +10.0.0.2,10.0.0.1,TCP,8080,1000,EST,ALLOW +10.0.0.2,10.0.0.3,TCP,1000,1000,EST,ALLOW +10.0.0.3,10.0.0.2,TCP,1000,1000,EST,ALLOW +10.0.0.3,10.0.0.2,TCP,1000,1000,NEW,ALLOW +10.0.0.3,10.0.0.1,TCP,8080,1000,EST,ALLOW +10.0.0.1,10.0.0.2,UDP,1000,8080,-,ALLOW +10.0.0.1,10.0.0.3,UDP,1000,8080,-,ALLOW +10.0.0.3,10.0.0.1,UDP,8080,1000,-,ALLOW +10.0.0.2,10.0.0.1,UDP,8080,1000,-,ALLOW +10.0.0.2,10.0.0.3,UDP,1000,1000,-,ALLOW +10.0.0.3,10.0.0.2,UDP,1000,1000,-,ALLOW +10.0.0.1,10.0.0.2,ICMP,-,-,PING,ALLOW +81.84.126.220,147.83.42.206,TCP,57253,1897,NEW,ALLOW +81.84.126.220,147.83.42.206,UDP,57253,1897,-,ALLOW \ No newline at end of file diff --git a/Firewall/SQL/firewall.db b/Firewall/SQL/firewall.db new file mode 100644 index 0000000..0722052 Binary files /dev/null and b/Firewall/SQL/firewall.db differ diff --git a/Firewall/SQL/firewallDB.txt b/Firewall/SQL/firewallDB.txt new file mode 100644 index 0000000..4caa76b --- /dev/null +++ b/Firewall/SQL/firewallDB.txt @@ -0,0 +1,10 @@ +CREATE TABLE firewall_rules ( + id INTEGER PRIMARY KEY, + src_ip TEXT, + dst_ip TEXT, + protocol TEXT, + src_port INTEGER, + dst_port INTEGER, + state TEXT, + action TEXT +); diff --git a/Firewall/SQL/firewallDrop.csv b/Firewall/SQL/firewallDrop.csv new file mode 100644 index 0000000..e3601dd --- /dev/null +++ b/Firewall/SQL/firewallDrop.csv @@ -0,0 +1,17 @@ +10.0.0.1,10.0.0.2,TCP,1000,8080,NEW,DROP +10.0.0.1,10.0.0.3,TCP,1000,8080,NEW,DROP +10.0.0.2,10.0.0.3,TCP,1000,1000,NEW,DROP +10.0.0.2,10.0.0.1,TCP,8080,1000,EST,DROP +10.0.0.2,10.0.0.3,TCP,1000,1000,EST,DROP +10.0.0.3,10.0.0.2,TCP,1000,1000,EST,DROP +10.0.0.3,10.0.0.2,TCP,1000,1000,NEW,DROP +10.0.0.3,10.0.0.1,TCP,8080,1000,EST,DROP +10.0.0.1,10.0.0.2,UDP,1000,8080,-,DROP +10.0.0.1,10.0.0.3,UDP,1000,8080,-,DROP +10.0.0.3,10.0.0.1,UDP,8080,1000,-,DROP +10.0.0.2,10.0.0.1,UDP,8080,1000,-,DROP +10.0.0.2,10.0.0.3,UDP,1000,1000,-,DROP +10.0.0.3,10.0.0.2,UDP,1000,1000,-,DROP +10.0.0.1,10.0.0.2,ICMP,-,-,PING,DROP +81.84.126.220,147.83.42.206,TCP,57253,1897,NEW,DROP +81.84.126.220,147.83.42.206,UDP,57253,1897,-,DROP diff --git a/Firewall/SQL/initDB.py b/Firewall/SQL/initDB.py new file mode 100644 index 0000000..bbb8910 --- /dev/null +++ b/Firewall/SQL/initDB.py @@ -0,0 +1,90 @@ +import sqlite3 + +class FirewallManager: + def __init__(self, db_file): + self.conn = sqlite3.connect(db_file) + self.cursor = self.conn.cursor() + + def create_table(self): + self.cursor.execute(''' + CREATE TABLE IF NOT EXISTS firewall_rules ( + id INTEGER PRIMARY KEY, + src_ip TEXT, + dst_ip TEXT, + protocol TEXT, + src_port INTEGER, + dst_port INTEGER, + state TEXT, + action TEXT + ) + ''') + self.conn.commit() + + def insert_firewall_rules(self, firewall_rules): + for rule in firewall_rules: + self.cursor.execute(''' + INSERT INTO firewall_rules (src_ip, dst_ip, protocol, src_port, dst_port, state, action) + VALUES (?, ?, ?, ?, ?, ?, ?) + ''', rule) + self.conn.commit() + + def insert_firewall_rules_from_file(self, file_path): + with open(file_path, 'r') as file: + for line in file: + parts = line.strip().split(',') + if len(parts) != 7: + continue + src_ip = parts[0] + dst_ip = parts[1] + protocol = parts[2] + src_port = int(parts[3]) if parts[3].isdigit() else None + dst_port = int(parts[4]) if parts[4].isdigit() else None + state = parts[5] if parts[5] != '-' else None + action = parts[6] + + # Insert into database + self.cursor.execute(''' + INSERT INTO firewall_rules (src_ip, dst_ip, protocol, src_port, dst_port, state, action) + VALUES (?, ?, ?, ?, ?, ?, ?) + ''', (src_ip, dst_ip, protocol, src_port, dst_port, state, action)) + self.conn.commit() + + + def close_connection(self): + self.conn.close() + +def readFiletoDatabase(db_file, file_path): + firewall_manager = FirewallManager(db_file) + firewall_manager.create_table() + firewall_manager.insert_firewall_rules_from_file(file_path) + firewall_manager.close_connection() + +if __name__ == "__main__": + db_file = "dataset/firewall-drop.db" + # readFiletoDatabase(db_file, "SQL/firewallDrop.csv") + firewall_rules = [ + ("10.0.0.1", "10.0.0.14", "ICMP", None, None, "PING", "DROP"), + # ("10.0.0.3", "10.0.0.4", "ICMP", None, None, "PING", "DROP"), + # ("10.0.0.2", "10.0.0.1", "ICMP", None, None, "PING", "ALLOW") + # ("10.0.0.1", "10.0.0.2", "TCP", 1000, 8080, "NEW", "ALLOW"), + # ("10.0.0.1", "10.0.0.3", "TCP", 1000, 8080, "NEW", "ALLOW"), + # ("10.0.0.2", "10.0.0.3", "TCP", 1000, 1000, "NEW", "ALLOW"), + # ("10.0.0.2", "10.0.0.1", "TCP", 8080, 1000, "EST", "ALLOW"), + # ("10.0.0.2", "10.0.0.3", "TCP", 1000, 1000, "EST", "ALLOW"), + # ("10.0.0.3", "10.0.0.2", "TCP", 1000, 1000, "EST", "ALLOW"), + # ("10.0.0.3", "10.0.0.2", "TCP", 1000, 1000, "NEW", "ALLOW"), + # ("10.0.0.3", "10.0.0.1", "TCP", 8080, 1000, "EST", "ALLOW"), + # ("10.0.0.1", "10.0.0.2", "UDP", 1000, 8080, None,"ALLOW"), + # ("10.0.0.1", "10.0.0.3", "UDP", 1000, 8080, None, "ALLOW"), + # ("10.0.0.3", "10.0.0.1", "UDP", 8080, 1000, None, "ALLOW"), + # ("10.0.0.3", "10.0.0.2", "UDP", 1000, 1000, None, "ALLOW"), + # ("10.0.0.2", "10.0.0.1", "UDP", 8080, 1000, None, "ALLOW"), + # ("10.0.0.2", "10.0.0.3", "UDP", 1000, 8080, None, "ALLOW"), + # ("10.0.0.1", "10.0.0.2", "ICMP", None, None, "PING", "ALLOW"), + # ("81.84.126.220", "147.83.42.206", "UDP", 57253, 1897, None, "ALLOW"), + # ("81.84.126.220", "147.83.42.206", "TCP", 57253, 1897, "NEW", "ALLOW"), + ] + firewall_manager = FirewallManager(db_file) + firewall_manager.create_table() + firewall_manager.insert_firewall_rules(firewall_rules) + firewall_manager.close_connection() diff --git a/Firewall/SQL/parse_firewall_rules_db.py b/Firewall/SQL/parse_firewall_rules_db.py new file mode 100644 index 0000000..5ae8906 --- /dev/null +++ b/Firewall/SQL/parse_firewall_rules_db.py @@ -0,0 +1,30 @@ +import sqlite3 + +class ParseFirewall: + + def parse(self): + # Connect to the SQLite database + conn = sqlite3.connect('firewall.db') + cursor = conn.cursor() + + # Read firewall rules from the database + cursor.execute("SELECT * FROM firewall_rules") + firewall_rules = cursor.fetchall() + + # Close the database connection + conn.close() + + # Process the firewall rules as needed + firewall_dict = {} + for rule in firewall_rules: + src_ip, dst_ip, protocol, src_port, dst_port, state, action = rule + if src_ip not in firewall_dict: + firewall_dict[src_ip] = [] + firewall_dict[src_ip].append((dst_ip, protocol, src_port, dst_port, state, action)) + + return firewall_dict + +if __name__ == "__main__": + parser = ParseFirewall() + firewall_rules = parser.parse() + print(firewall_rules) diff --git a/Directory/connection_tracking.py b/Firewall/connection_tracking.py similarity index 65% rename from Directory/connection_tracking.py rename to Firewall/connection_tracking.py index cf36cb3..990d4b5 100644 --- a/Directory/connection_tracking.py +++ b/Firewall/connection_tracking.py @@ -1,15 +1,10 @@ -#! usr/bin/env python import logging + class TrackConnection(): - """ - Firewall Connection Tracking. - Add "seen" flows to dictionary - and track them from incoming packets. - """ - + def __init__(self): - logging.info("Firewall Connection Tracking Dictionary.") - + logging.info("Stateful Firewall --> Connection Tracking can be possible...") + def conn_track_dict(self,dic,s_ip,d_ip,s_port,d_port,act,var): flag = 0 mydict = dic @@ -19,21 +14,19 @@ def conn_track_dict(self,dic,s_ip,d_ip,s_port,d_port,act,var): list1 = [d_ip,s_port,d_port,act] listobj = [] # For new flow initiators. - if(mydict.has_key(src_ip) is False): + if (src_ip in mydict) is False: key = src_ip tup = tuple(list1) listobj.append(tup) tup = tuple(listobj) mydict[key] = tup flag = 1 - - # Check if same entry is found in dictionary - elif (mydict.has_key(src_ip) is True): + else: for x in list(mydict[src_ip]): - if (list1 == list(x)): - flag = 1 + if list1 == list(x): + flag = 1 break - + # If unique entry, only then allow if(flag != 1): key = src_ip @@ -44,4 +37,3 @@ def conn_track_dict(self,dic,s_ip,d_ip,s_port,d_port,act,var): mydict[key] = tup return mydict - diff --git a/Firewall/construct_flow.py b/Firewall/construct_flow.py new file mode 100644 index 0000000..4f20654 --- /dev/null +++ b/Firewall/construct_flow.py @@ -0,0 +1,78 @@ +from ryu.ofproto.ether import ETH_TYPE_IP, ETH_TYPE_ARP, ETH_TYPE_LLDP, ETH_TYPE_MPLS, ETH_TYPE_IPV6 +from ryu.ofproto.inet import IPPROTO_ICMP, IPPROTO_TCP, IPPROTO_UDP, IPPROTO_SCTP +from flow_addition import FlowAdd +import logging + +class Construct(): + # Constructs match object from supplied field. + # The default valuuuuue of all paramenters is don't car match all wildcard. + # If no parameters are given, the returned match matches everything. + + def __init__(self): + logging.info("Rule will be constructed") + + def add_flow(self,datapath,actions, priority = 1000 ,in_port=None, in_phy_port=None, metadata=None, eth_dst=None, eth_src=None, eth_type=None, + vlan_vid=None, vlan_pcp=None, ip_dscp=None, ip_ecn=None, ip_proto=None, ipv4_src=None, ipv4_dst=None, + tcp_src=None, tcp_dst=None, udp_src=None, udp_dst=None, sctp_src=None, sctp_dst=None, icmpv4_type=None, + icmpv4_code=None, arp_op=None, arp_spa=None, arp_tpa=None, arp_sha=None, arp_tha=None, + ipv6_src=None, ipv6_dst=None, ipv6_flabel=None, icmpv6_type=None, icmpv6_code=None, + ipv6_nd_target=None, ipv6_nd_sll=None, ipv6_nd_tll=None, mpls_label=None, mpls_tc=None, mpls_bos=None, + pbb_isid=None, tunnel_id=None, ipv6_exthdr=None,idle_timeout=1800): + + assert (datapath is not None), "Datapath object is not set." + assert (actions is not None), "Actions object is not set." + + parser = datapath.ofproto_parser + + # Please check for actions that where it fits and what is its advantage + matchflow = FlowAdd() + match = parser.OFPMatch() + + if eth_type is not None: + logging.info("Eth_type is set") + if eth_type == ETH_TYPE_IP: + logging.info("IP object is set") + if ip_proto is not None: + logging.info("IP_Proto is set") + if (ip_proto == IPPROTO_ICMP): + logging.info("ICMP object type is set") + match = parser.OFPMatch(in_port = in_port, eth_type = eth_type, ip_proto=ip_proto, + icmpv4_type=icmpv4_type, ipv4_src=ipv4_src, ipv4_dst=ipv4_dst) + elif ip_proto == IPPROTO_TCP: + logging.info("TCP object is set") + match = parser.OFPMatch(in_port = in_port, eth_type = eth_type, ip_proto=ip_proto, + ipv4_src=ipv4_src, ipv4_dst=ipv4_dst, + tcp_src = tcp_src, tcp_dst = tcp_dst) + elif ip_proto == IPPROTO_UDP: + logging.info("UDP object is set") + match = parser.OFPMatch(in_port = in_port, eth_type = eth_type, ip_proto=ip_proto, + ipv4_src=ipv4_src, ipv4_dst=ipv4_dst, + udp_src = udp_src, udp_dst=udp_dst) + elif ip_proto == IPPROTO_SCTP: + logging.info("SCTP object is set") + match = parser.OFPMatch(in_port = in_port, eth_type = eth_type, + eth_src = eth_src, eth_dst=eth_dst, + ip_proto=ip_proto) + else: + logging.info("Please check OFPMatch -> ip_proto parameter in order to continue") + else: + logging.info("Please check OFPMatch -> ip_proto parameter in order to continue") + elif (eth_type == ETH_TYPE_ARP): + logging.info("ARP object is set") + match = parser.OFPMatch(in_port = in_port, eth_type = eth_type,eth_src=eth_src,eth_dst=eth_dst) + elif (eth_type == ETH_TYPE_LLDP): + logging.info("LLDP rule will be added") + match = parser.OFPMatch(eth_type = eth_type) + elif (eth_type == ETH_TYPE_IPV6): + logging.info("IPv6 object is set") + match = parser.OFPMatch(in_port = in_port, eth_type = eth_type,eth_src=eth_src,eth_dst=eth_dst) + elif (eth_type == ETH_TYPE_MPLS): + logging.info("MPLS object is set") + match = parser.OFPMatch(in_port = in_port, eth_type = eth_type,eth_src=eth_src,eth_dst=eth_dst) + else: + logging.info("Check eth_type!") + # Finally: add this match to flow table entry. + if match is not None: + matchflow.add_flow(datapath, priority, match, actions, idle_timeout) + else: + logging.info("No matching rule found or added") diff --git a/Firewall/ctrlapi.py b/Firewall/ctrlapi.py new file mode 100644 index 0000000..408877f --- /dev/null +++ b/Firewall/ctrlapi.py @@ -0,0 +1,622 @@ + +""" +This module receives all API requests +""" + +from curses.ascii import isdigit +import sys +import random +import logging + +from ryu.base import app_manager +from ryu.lib import ofctl_v1_3 +from ryu.ofproto import ofproto_v1_0, ofproto_v1_2, ofproto_v1_3 +from ryu.ofproto import ofproto_v1_0_parser, ofproto_v1_2_parser, ofproto_v1_3_parser +from ryu.topology.api import get_all_switch, get_all_link, get_all_host +from flowtracker import Tracker + + +PYTHON3 = sys.version_info > (3, 0) +logger = logging.getLogger("securefirewall") + + +class CtrlApi: + + MAGIC_COOKIE = 0x00007AB700000000 + + def __init__(self, app): + """Constructor""" + self.app = app + # dpset will be removed eventually + self.dpset = self.app.dpset + self.ofctl = ofctl_v1_3 + self.waiters = {} + self.rpc_clients = [] + self.tracker = Tracker() + + self.port_id = { + "IN_PORT": 0xFFFFFFF8, + "TABLE": 0xFFFFFFF9, + "NORMAL": 0xFFFFFFFA, + "FLOOD": 0xFFFFFFFB, + "ALL": 0xFFFFFFFC, + "CONTROLLER": 0xFFFFFFFD, + "LOCAL": 0xFFFFFFFE, + "ANY": 0xFFFFFFFF, + } + + self.reqfunction = { + "switchdesc": self.ofctl.get_desc_stats, + "portdesc": self.ofctl.get_port_desc, + "portstat": self.ofctl.get_port_stats, + "flowsumm": self.ofctl.get_aggregate_flow_stats, + "flowstat": self.ofctl.get_flow_stats, + "tablestat": self.ofctl.get_table_stats, + "tablefeature": self.ofctl.get_table_features, + "queueconfig": self.ofctl.get_queue_config, + "queuestat": self.ofctl.get_queue_stats, + "meterstat": self.ofctl.get_meter_stats, + "meterconfig": self.ofctl.get_meter_config, + "meterfeatures": self.ofctl.get_meter_features, + "groupdesc": self.ofctl.get_group_desc, + "groupstat": self.ofctl.get_group_stats, + "groupfeatures": self.ofctl.get_group_features, + "role": self.ofctl.get_role, + } + + # Get log file path + # handler = logger.handlers[0] + # self.logfile = handler.baseFilename + + logger.debug("Created Ctrl_Api") + + def get_tracker(self): + return self.tracker + + def get_waiters(self): + """Returns list of waiters""" + return self.waiters + + def get_actions(self, parser, action_set): + """TBD""" + actions = [] + a_dict = { + "SET_FIELD": (parser.OFPActionSetField, "field"), + "COPY_TTL_OUT": (parser.OFPActionCopyTtlOut, None), + "COPY_TTL_IN": (parser.OFPActionCopyTtlIn, None), + "POP_PBB": (parser.OFPActionPopPbb, None), + "PUSH_PBB": (parser.OFPActionPushPbb, "ethertype"), + "POP_MPLS": (parser.OFPActionPopMpls, "ethertype"), + "PUSH_MPLS": (parser.OFPActionPushMpls, "ethertype"), + "POP_VLAN": (parser.OFPActionPopVlan, None), + "PUSH_VLAN": (parser.OFPActionPushVlan, "ethertype"), + "DEC_MPLS_TTL": (parser.OFPActionDecMplsTtl, None), + "SET_MPLS_TTL": (parser.OFPActionSetMplsTtl, "mpls_ttl"), + "DEC_NW_TTL": (parser.OFPActionDecNwTtl, None), + "SET_NW_TTL": (parser.OFPActionSetNwTtl, "nw_ttl"), + "SET_QUEUE": (parser.OFPActionSetQueue, "queue_id"), + "GROUP": (parser.OFPActionGroup, "group_id"), + "OUTPUT": (parser.OFPActionOutput, "port"), + } + + for action in action_set: + key = list(action.keys())[0] # There should be only one key + value = action[key] + if key in a_dict: + found_action = a_dict[key][0] # the action + if a_dict[key][1]: # check if the action needs a value + kwargs = {} + if a_dict[key][1] == "field": + a_value = value.split("=") + val = 0 + if len(a_value) > 1: + x = a_value[1] + val = ( + int(x) + if x.isdigit() + else int(x, 16) + if x.startswith("0x") + else x + ) + kwargs = {a_value[0]: val} + elif a_dict[key][1] == "port": + a_value = value.upper() + val = ( + self.port_id[a_value] + if a_value in self.port_id + else int(a_value) + ) + kwargs = {a_dict[key][1]: val} + elif a_dict[key][1] == "ethertype": + ethertype = ( + int(value) + if value.isdigit() + else int(value, 16) + if value.startswith("0x") + else value + ) + kwargs = {a_dict[key][1]: ethertype} + else: + kwargs = {a_dict[key][1]: int(value)} + actions.append(found_action(**kwargs)) + else: + actions.append(found_action()) + print(actions) + else: + raise Exception("Action {} not supported!".format(key)) + return actions + + def _get_instructions(self, actions, ofproto, parser): + """TBD""" + # instructions + inst = [] + apply_actions = [] + write_actions = [] + + for item in actions: + # Python 2 has both types + if isinstance(item, str) or (not PYTHON3 and isinstance(item, unicode)): + if item.startswith("WRITE_METADATA"): + metadata = item.split(":")[1].split("/") + # expecting hex data + inst += [ + parser.OFPInstructionWriteMetadata( + int(metadata[0], 16), int(metadata[1], 16) + ) + ] + elif item.startswith("GOTO_TABLE"): + table_id = int(item.split(":")[1]) + inst += [parser.OFPInstructionGotoTable(table_id)] + elif item.startswith("METER"): + meter_id = int(item.split(":")[1]) + inst += [parser.OFPInstructionMeter(meter_id)] + elif item.startswith("CLEAR_ACTIONS"): + inst += [ + parser.OFPInstructionActions(ofproto.OFPIT_CLEAR_ACTIONS, []) + ] + else: # Apply Actions + action = item.split(":") + apply_actions += [{action[0]: action[1]}] + + elif isinstance(item, dict): # WRITE ACTIONS + wractions = item["WRITE_ACTIONS"] + for witem in wractions: + action = witem.split(":") + write_actions += [{action[0]: action[1]}] + + if apply_actions: + applyActions = self.get_actions(parser, apply_actions) + inst += [ + parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, applyActions) + ] + + if write_actions: + writeActions = self.get_actions(parser, write_actions) + inst += [ + parser.OFPInstructionActions(ofproto.OFPIT_WRITE_ACTIONS, writeActions) + ] + + return inst + + # def read_logs(self): + # items = [] + # with open(self.logfile, "r") as my_file: + # while True: + # line = my_file.readline() + # if not line: + # break + # lst = line.split("\t") + # items.append(lst) + # # items.append(line) + # return items + + # methods linked to web api + + def process_meter_upload(self, configlist): + """Sends meters to the switch to update meter tables.""" + switches = [str(t[0]) for t in self.get_switches()] + for swconfig in configlist: # for each + dpid = list(swconfig.keys())[0] + + if dpid not in switches: + break + + for flow in swconfig[dpid]: + flow["dpid"] = dpid + flow["operation"] = "add" + _ = self.process_meter_message(flow) + return "Meters added successfully!" + + def process_group_upload(self, configlist): + """Sends groups to the switch to update group tables.""" + switches = [str(t[0]) for t in self.get_switches()] + for swconfig in configlist: # for each + dpid = list(swconfig.keys())[0] + + if dpid not in switches: + break + + for flow in swconfig[dpid]: + flow["dpid"] = dpid + flow["operation"] = "add" + _ = self.process_group_message(flow) + return "Groups added successfully!" + + def process_flow_upload(self, configlist): + """Sends flows to the switch to update flow tables.""" + + # config_tree = {} + switches = [str(t[0]) for t in self.get_switches()] + for swconfig in configlist: # for each + dpid = list(swconfig.keys())[0] + if dpid not in switches: + break + for flow in swconfig[dpid]: + flow["dpid"] = dpid + flow["operation"] = "add" + _ = self.process_flow_message(flow) + return "Flows added successfully!" + + # @set_ev_cls(event.EventSwitchEnter) + + def get_topology_data(self): + """Get Topology Data""" + switch_list = get_all_switch(self.app) + switches = [switch.to_dict() for switch in switch_list] + links_list = get_all_link(self.app) + links = [link.to_dict() for link in links_list] + host_list = get_all_host(self.app) + + # To remove hosts that are not removed by controller + ports = [] + for switch in switch_list: + ports += switch.ports + port_macs = [p.hw_addr for p in ports] + n_host_list = [h for h in host_list if h.port.hw_addr in port_macs] + + hosts = [h.to_dict() for h in n_host_list] + return {"switches": switches, "links": links, "hosts": hosts} + + def delete_flow_list(self, flowlist): + """Delete a set of flows""" + for item in flowlist: + item["operation"] = "delst" + _ = self.process_flow_message(item) + + # if the flow was monitored + if item["cookie"] & self.MAGIC_COOKIE == self.MAGIC_COOKIE: + self.tracker.untrack(item["cookie"]) + return "Flows deleted successfully!" + + def monitor_flow_list(self, flowlist): + """Monitor a Flow""" + # Here we need to update the flow entries by adding a magic cookie + # and adding Apply Action: "OUTPUT:CONTROLLER" to the instructions + + for item in flowlist: + item["operation"] = "add" + item["cookie"] = self.MAGIC_COOKIE | random.randint(1, 0xFFFFFFFF) + item["priority"] += 1 + item["idle_timeout"] = 0 + item["hard_timeout"] = 0 + if "OUTPUT:CONTROLLER" not in item["actions"]: + item["actions"] += ["OUTPUT:CONTROLLER"] + _ = self.process_flow_message(item) + + return "Flows are monitored!" + + def rest_flow_monitoring(self, req): + """Reset Flow Monitoring""" + cookie = req["cookie"] + if cookie == "default": + self.tracker.reset(self.MAGIC_COOKIE) + else: + self.tracker.reset(int(cookie)) + + return "" + + def get_switches(self): + """Returns switch infor.""" + return self.dpset.get_all() + + def get_stats(self, req, dpid): + """Returns various stats""" + data_path = self.dpset.get(int(str(dpid), 0)) + if not data_path: + return + if req == "flows": + return self.ofctl.get_flow_stats(data_path, self.waiters) + elif req == "groups": + return { + "desc": self.ofctl.get_group_desc(data_path, self.waiters), + "stats": self.ofctl.get_group_stats(data_path, self.waiters), + } + elif req == "meters": + return { + "desc": self.ofctl.get_meter_config(data_path, self.waiters), + "stats": self.ofctl.get_meter_stats(data_path, self.waiters), + } + + def get_stats_request(self, request, dpid): + """Get stats using ryu's api""" + data_path = self.dpset.get(dpid) + func = self.reqfunction.get(request, None) + if data_path and func: + return func(data_path, self.waiters) + return None + + def process_flow_message(self, flow_entry): + """Process Flow Mod message""" + dpid = int(flow_entry.get("dpid", 0)) + data_path = self.dpset.get(dpid) + if not data_path: + return "Datapath does not exist!" + + ofproto = data_path.ofproto + parser = data_path.ofproto_parser + + command = { + "add": ofproto.OFPFC_ADD, + "mod": ofproto.OFPFC_MODIFY, + "modst": ofproto.OFPFC_MODIFY_STRICT, + "del": ofproto.OFPFC_DELETE, + "delst": ofproto.OFPFC_DELETE_STRICT, + } + + # Initialize arguments for the flow mod message + msg_kwargs = { + "datapath": data_path, + "command": command.get(flow_entry["operation"], ofproto.OFPFC_ADD), + "buffer_id": ofproto.OFP_NO_BUFFER, + } + + msg_kwargs["table_id"] = flow_entry.get("table_id", 0) + + # Match fields + mf = flow_entry.get("match", None) + # convert port names to numbers + if "in_port" in mf: + x = mf["in_port"] + mf["in_port"] = self.port_id[x] if x in self.port_id else x + # convert masks to tuples + for f in mf: + mask_pos = str(mf[f]).find("/") + if mask_pos >= 0: + parts = mf[f].split("/") + mf[f] = (parts[0], parts[1]) + if str(mf[f]).startswith("0x"): + mf[f] = int(mf[f], 16) + + msg_kwargs["match"] = parser.OFPMatch(**mf) if mf else None + + msg_kwargs["hard_timeout"] = flow_entry.get("hard_timeout", 0) + msg_kwargs["idle_timeout"] = flow_entry.get("idle_timeout", 0) + msg_kwargs["priority"] = flow_entry.get("priority", 0) + msg_kwargs["cookie"] = flow_entry.get("cookie", 0) + msg_kwargs["cookie_mask"] = flow_entry.get("cookie_mask", 0) + op = flow_entry.get("out_port", -1) # make it 0 + og = flow_entry.get("out_group", -1) + msg_kwargs["out_port"] = ofproto.OFPP_ANY if op <= 0 else op + msg_kwargs["out_group"] = ofproto.OFPG_ANY if og <= 0 else og + + # instructions + inst = [] + if "actions" in flow_entry: # Ryu's format + inst = self._get_instructions(flow_entry["actions"], ofproto, parser) + else: # FlowManager's format + # Goto meter + if ("meter_id" in flow_entry) and flow_entry["meter_id"]: + inst += [parser.OFPInstructionMeter(flow_entry["meter_id"])] + # Apply Actions + if ("apply" in flow_entry) and flow_entry["apply"]: + applyActions = self.get_actions(parser, flow_entry["apply"]) + inst += [ + parser.OFPInstructionActions( + ofproto.OFPIT_APPLY_ACTIONS, applyActions + ) + ] + # Clear Actions + if ("clearactions" in flow_entry) and flow_entry["clearactions"]: + inst += [parser.OFPInstructionActions(ofproto.OFPIT_CLEAR_ACTIONS, [])] + # Write Actions + if ("write" in flow_entry) and flow_entry["write"]: + # bc actions must be unique they are in dict + # from dict to list + toList = [{k: flow_entry["write"][k]} for k in flow_entry["write"]] + # print(toList) + writeActions = self.get_actions(parser, toList) + inst += [ + parser.OFPInstructionActions( + ofproto.OFPIT_WRITE_ACTIONS, writeActions + ) + ] + # Write Metadata + if ("metadata" in flow_entry) and flow_entry["metadata"]: + meta_mask = flow_entry.get("metadata_mask", 0) + inst += [ + parser.OFPInstructionWriteMetadata( + flow_entry["metadata"], meta_mask + ) + ] + # Goto Table Metadata + if ("goto" in flow_entry) and flow_entry["goto"]: + inst += [parser.OFPInstructionGotoTable(table_id=flow_entry["goto"])] + + msg_kwargs["instructions"] = inst + + # Flags + flags = 0 + flags += 0x01 if flow_entry.get("SEND_FLOW_REM", False) else 0 + flags += 0x02 if flow_entry.get("CHECK_OVERLAP", False) else 0 + flags += 0x04 if flow_entry.get("RESET_COUNTS", False) else 0 + flags += 0x08 if flow_entry.get("NO_PKT_COUNTS", False) else 0 + flags += 0x10 if flow_entry.get("NO_BYT_COUNTS", False) else 0 + + msg_kwargs["flags"] = flags + + # ryu/ryu/ofproto/ofproto_v1_3_parser.py + msg = parser.OFPFlowMod(**msg_kwargs) + try: + data_path.send_msg(msg) # ryu/ryu/controller/controller.py + except KeyError as err: + return "Unrecognized field " + err.__repr__() + except Exception as err: + print(msg) + return "Error " + err.__repr__() + + return "Message sent successfully." + + def process_group_message(self, d): + """Sends group form data to the switch to update group tables.""" + dpid = int(d.get("dpid", 0)) + data_path = self.dpset.get(dpid) + if not data_path: + return "Datapath does not exist!" + + ofproto = data_path.ofproto + parser = data_path.ofproto_parser + + command = { + "add": ofproto.OFPGC_ADD, + "mod": ofproto.OFPGC_MODIFY, + "del": ofproto.OFPGC_DELETE, + } + + cmd = command.get(d["operation"], ofproto.OFPGC_ADD) + + type_convert = { + "ALL": data_path.ofproto.OFPGT_ALL, + "SELECT": data_path.ofproto.OFPGT_SELECT, + "INDIRECT": data_path.ofproto.OFPGT_INDIRECT, + "FF": data_path.ofproto.OFPGT_FF, + } + + gtype = type_convert.get(d["type"]) + + group_id = d["group_id"] + + buckets = [] + for bucket in d["buckets"]: + weight = bucket.get("weight", 0) + watch_port = bucket.get("watch_port", ofproto.OFPP_ANY) + watch_group = bucket.get("watch_group", data_path.ofproto.OFPG_ANY) + actions = [] + if bucket["actions"]: + actions_list = [] + if isinstance(bucket["actions"][0], str) or ( + not PYTHON3 and isinstance(bucket["actions"][0], unicode) + ): + # Ryu's format + for i in bucket["actions"]: + x = i.split(":", 1) + y = ( + x[1].replace("{", "").replace("}", "").strip() + if len(x) > 1 + else "" + ) + y = y.replace(":", "=", 1) if x[0] == "SET_FIELD" else y + actions_list.append({x[0]: y}) + else: # FlowManager's format + actions_list = bucket["actions"] + actions = self.get_actions(parser, actions_list) + buckets.append( + data_path.ofproto_parser.OFPBucket( + weight, watch_port, watch_group, actions + ) + ) + + # print(dp, cmd, gtype, group_id, buckets) + group_mod = parser.OFPGroupMod(data_path, cmd, gtype, group_id, buckets) + + try: + data_path.send_msg(group_mod) # ryu/ryu/controller/controller.py + except KeyError as err: + return err.__repr__() + except Exception as err: + return err.__repr__() + + return "Message sent successfully." + + def process_meter_message(self, d): + """Sends meter form data to the switch to update meter table.""" + dpid = int(d.get("dpid", 0)) + data_path = self.dpset.get(dpid) + if not data_path: + return "Datapath does not exist!" + + ofproto = data_path.ofproto + parser = data_path.ofproto_parser + + command = { + "add": ofproto.OFPMC_ADD, + "mod": ofproto.OFPMC_MODIFY, + "del": ofproto.OFPMC_DELETE, + } + cmd = command.get(d["operation"], ofproto.OFPMC_ADD) + + meter_id = d["meter_id"] + + flags = 0 + bands = [] + if "flags" in d: # Ryu's format + print(d["flags"]) + for f in d["flags"]: + flags += 0x01 if f == "KBPS" else 0 + flags += 0x02 if f == "PKTPS" else 0 + flags += 0x04 if f == "BURST" else 0 + flags += 0x08 if f == "STATS" else 0 + + for band in d["bands"]: + if band["type"] == "DROP": + bands += [ + parser.OFPMeterBandDrop( + rate=band["rate"], burst_size=band["burst_size"] + ) + ] + elif band["type"] == "DSCP_REMARK": + bands += [ + parser.OFPMeterBandDscpRemark( + rate=band["rate"], + burst_size=band["burst_size"], + prec_level=band["prec_level"], + ) + ] + + else: # FlowManager's format + flags += 0x01 if d["OFPMF_KBPS"] else 0 + flags += 0x02 if d["OFPMF_PKTPS"] else 0 + flags += 0x04 if d["OFPMF_BURST"] else 0 + flags += 0x08 if d["OFPMF_STATS"] else 0 + + # Flags must have KBPS or PKTPS + flags = flags if (flags & 0x03) else (flags | 0x01) + + for band in d["bands"]: + # mtype = type_convert.get(band[0]) + if band[0] == "DROP": + bands += [parser.OFPMeterBandDrop(rate=band[1], burst_size=band[2])] + elif band[0] == "DSCP_REMARK": + bands += [ + parser.OFPMeterBandDscpRemark( + rate=band[1], burst_size=band[2], prec_level=band[3] + ) + ] + + meter_mod = parser.OFPMeterMod(data_path, cmd, flags, meter_id, bands) + try: + data_path.send_msg(meter_mod) + except KeyError as err: + return err.__repr__() + except Exception as err: + return err.__repr__() + + return "Message sent successfully." + + # def get_flow_stats(self, req, dpid): # unused + # flow = {} # no filters + # dp = self.dpset.get(int(str(dpid), 0)) + # return self.ofctl.get_flow_stats(dp, self.waiters, flow) + + +# This is is needed for get_topology_data() +app_manager.require_app("ryu.topology.switches", api_style=True) diff --git a/Firewall/customFirewallStateful.py b/Firewall/customFirewallStateful.py new file mode 100644 index 0000000..08d52d0 --- /dev/null +++ b/Firewall/customFirewallStateful.py @@ -0,0 +1,308 @@ +from ryu.base import app_manager +from ryu.controller import ofp_event, dpset +from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER, set_ev_cls +# from ryu.ofproto import ofproto_v1_3, ofproto_v1_3_parser +from ryu.ofproto import ofproto_v1_0, ofproto_v1_2, ofproto_v1_3 +from ryu.ofproto import ofproto_v1_0_parser, ofproto_v1_2_parser, ofproto_v1_3_parser +from ryu.lib.packet import packet, ethernet, ipv4, udp, tcp, icmp +from ryu.ofproto.ether import ETH_TYPE_IP, ETH_TYPE_ARP, ETH_TYPE_LLDP, ETH_TYPE_MPLS, ETH_TYPE_IPV6 +from ryu.ofproto.inet import IPPROTO_ICMP, IPPROTO_TCP, IPPROTO_UDP, IPPROTO_SCTP +# custom lib +# from parse_firewall_rules import parse_firewall +from ParseFirewallFromDB import ParseFirewallFromDB +from switch_information import SwitchInfo +from packet_out import SendPacket +from construct_flow import Construct +from connection_tracking import TrackConnection + +ICMP_PING = 8 +ICMP_PONG = 0 +# hping3 -c 10 -k -s source_port -p destination_port -A dst_ip +TCP_SYN = 0x02 # -S +# hping3 -c 10 -k -s source_port -p destination_port -SA dst_ip +TCP_SYN_ACK = 0x12 # -SA +TCP_BOGUS_FLAGS = 0x15 #0x0F + +class SecureFirewall(app_manager.RyuApp): + OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION, + ofproto_v1_2.OFP_VERSION, + ofproto_v1_3.OFP_VERSION] + + inner_policy = {} + icmp_conn_track = {} + tcp_conn_track = {} + udp_conn_track = {} + sendpkt = SendPacket() + flow = Construct() + track = TrackConnection() + + def __init__(self, *args, **kwargs): + super(SecureFirewall, self).__init__(*args, **kwargs) + self.mac_to_port = {} + # parser = parse_firewall() + parser = ParseFirewallFromDB("dataset/firewall-vTest.db") + self.inner_policy = parser.parse() + + if self.inner_policy: + self.logger.info("Firewall rules parsed successfully from the database.") + else: + self.logger.error("Failed to parse firewall rules from the database.") + self.logger.info("dict is ready") + + @set_ev_cls(dpset.EventDP, dpset.DPSET_EV_DISPATCHER) + def handler_datapath(self, ev): + SwitchInfo(ev) + + # Handles incoming packets. Decodes them and checks for suitable Firewall rules + @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) + def packet_in_handler(self, ev): + msg = ev.msg + datapath = msg.datapath + ofproto = datapath.ofproto + parser = datapath.ofproto_parser + in_port = msg.match['in_port'] + + actions_default = [] + action_fwd_to_out_port = [] + # out_port = ofproto.OFPP_FLOOD + + try: + pkt = packet.Packet(msg.data) + eth = pkt.get_protocols(ethernet.ethernet)[0] + ethtype = eth.ethertype + + out_port = self.port_learn(datapath, eth, in_port) + action_drop = [parser.OFPActionOutput(ofproto.OFPPC_NO_FWD)] + + # if out_port != ofproto.OFPP_FLOOD: + action_fwd_to_out_port = [parser.OFPActionOutput(out_port)] + # else: + # action_fwd_to_out_port = action_drop + actions_default = action_fwd_to_out_port + + if(out_port != ofproto.OFPP_FLOOD) and (ethtype == ETH_TYPE_IP): + # + ipo = pkt.get_protocols(ipv4.ipv4)[0] + + # check for ICMP + if ipo.proto == IPPROTO_ICMP: + icmpob = pkt.get_protocol(icmp.icmp) + flag1 = 0 + + # Check if this is ICMP_PING + if ((icmpob.type==ICMP_PING) and (ipo.src in self.inner_policy)): + temp = self.inner_policy.get(ipo.src) + for i in range(0, len(temp)): + # print(temp[i][0]) + if temp[i][0] == ipo.dst: + xyz = temp[i] + # + # print(xyz[1->5]) + if(xyz[1] == "ICMP") and (xyz[5] == 'ALLOW'): #--- column in firewall rules + flag1 = 1 + actions_default = action_fwd_to_out_port + self.icmp_conn_track = self.track.conn_track_dict(self.icmp_conn_track,ipo.src, ipo.dst, "PING", "PONG", xyz[5],1) + self.logger.info("%s -> %s: Echo Request allowd" % (ipo.src, ipo.dst)) + self.flow.add_flow(datapath=datapath, actions=actions_default, priority=1001, in_port=in_port, + eth_type=ETH_TYPE_IP, ip_proto=IPPROTO_ICMP, icmpv4_type=ICMP_PING, + ipv4_src=ipo.src, ipv4_dst = ipo.dst) + break + # Otherwise, ICMP_PONG + elif (icmpob.type == ICMP_PONG) and (ipo.dst in self.icmp_conn_track): + tmp = self.icmp_conn_track.get(ipo.dst) + for i in range(0, len(tmp)): + # + if tmp[i][0] == ipo.src: + xyz = tmp[i] + # + if(xyz[1] == 'PING') and (xyz[2] == 'PONG'): + flag1 = 1 + actions_default = action_fwd_to_out_port + self.flow.add_flow(datapath = datapath, actions=actions_default, + priority=1001, in_port=in_port, eth_type=ETH_TYPE_IP, + ip_proto = IPPROTO_ICMP, icmpv4_type=ICMP_PONG, + ipv4_src=ipo.src, ipv4_dst=ipo.dst) + self.icmp_conn_track = self.track.conn_track_dict(self.icmp_conn_track, ipo.src, + ipo.dst, "PONG", "PING", + xyz[3], 1) + self.logger.info("\n%s -> %s action = PING, state = ESTABLISHED \n" % (ipo.dst, ipo.src)) + + # No match + if(flag1==0): + actions_default = action_drop + self.flow.add_flow(datapath=datapath, actions=actions_default, priority=1001, + in_port=in_port, eth_type=ETH_TYPE_IP, ip_proto = IPPROTO_ICMP, + icmpv4_type = icmpob.type, ipv4_src = ipo.src, ipv4_dst = ipo.dst) + self.logger.info("%s -> %s : BLOCKED" %(ipo.src, ipo.dst)) + + # Check for TCP + elif (ipo.proto == IPPROTO_TCP): + tcpo = pkt.get_protocol(tcp.tcp) + print("--------") + print("tcp.bits: ", tcpo.bits, " -- TCP_SYN: ", TCP_SYN, "-- &: ", (tcpo.bits & TCP_SYN)) + print("--------") + flag2 = 0 + # TCP SYN packet + if ((tcpo.bits & TCP_SYN) == TCP_SYN) and ((tcpo.bits & TCP_BOGUS_FLAGS) == 0x00): + print("TCP_SYN!!") + if ipo.src in self.inner_policy: + temp = self.inner_policy.get(ipo.src) + for i in range(0, len(temp)): + # + print("srcIP: ", ipo.src, " - dstIP: ", temp[i][0], " -- Protocol: ", temp[i][1], " -- srcPort: ", temp[i][2], " dstPort: ", temp[i][3]) + print("srcIP: ", ipo.src, " - dstIP: ", ipo.dst, " -- Protocol: Nocheck", " -- srcPort: ", tcpo.src_port, " dstPort: ", tcpo.dst_port) + if((temp[i][0] == ipo.dst) and (temp[i][1] == 'TCP') and (int(temp[i][2]) == tcpo.src_port) and (int(temp[i][3]) == tcpo.dst_port) + and (temp[i][5] == 'ALLOW')): + flag2 = 1 + actions_default = action_fwd_to_out_port + self.logger.info("%s -> %s : SYN ALLOWED" % (ipo.src, ipo.dst)) + self.tcp_conn_track = self.track.conn_track_dict(self.tcp_conn_track, ipo.src, ipo.dst, tcpo.src_port, + tcpo.dst_port, tcpo.seq, 1) + self.flow.add_flow(datapath=datapath, actions=actions_default, priority = 1001, in_port=in_port, + eth_type = ETH_TYPE_IP, ip_proto= IPPROTO_TCP, + ipv4_src = ipo.src, ipv4_dst=ipo.dst, + tcp_src=tcpo.src_port, tcp_dst=tcpo.dst_port) + break + # TCP SYN ACK packet + elif (tcpo.bits & TCP_SYN_ACK) == TCP_SYN_ACK: + print("TCP_SYN_ACK!!") + if ipo.dst in self.tcp_conn_track: + temp2 = self.tcp_conn_track.get(ipo.dst) + for i in range(0, len(temp2)): + # + if (temp2[i][0] == ipo.src) and (int(temp[i][1]) == tcpo.dst_port) and (int(temp2[i][2]) == tcpo.src_port): + flag2 = 1 + actions_default = action_fwd_to_out_port + self.logger.info("%s -> %s : SYN ACK ALLOWED" % (ipo.src, ipo.dst)) + self.tcp_conn_track = self.track.conn_track_dict(self.tcp_conn_track, ipo.src, ipo.dst, tcpo.src_port, + tcpo.dst_port, tcpo.seq, 1) + self.flow.add_flow(datapath = datapath, actions=actions_default, priority = 1001, in_port = in_port, + eth_type = ETH_TYPE_IP, ip_proto = IPPROTO_TCP, ipv4_src = ipo.src, + ipv4_dst = ipo.dst, tcp_src=tcpo.src_port, tcp_dst=tcpo.dst_port) + self.logger.info("\n %s -> %s src_port=%s, dst_port=%s, state= ESTABLISHED\n" %(ipo.dst, ipo.src, temp2[i][1], temp2[i][2])) + break + # All remaining TCP packets + else: + if ipo.src in self.tcp_conn_track: + temp3 = self.tcp_conn_track.get(ipo.src) + for i in range(0, len(temp3)): + # + if (temp3[i][0] == ipo.dst) and (int(temp3[i][1]) == tcpo.src_port) and (int(temp3[i][2]) == tcpo.dst_port): + flag2 = 1 + actions_default = action_fwd_to_out_port + self.logger.info("%s -> %s : TRANSMISSION ALLOWED" % (ipo.src, ipo.dst)) + break + # No match + if flag2 == 0: + actions_default = action_drop + self.logger.info("%s -> %s : BLOCKED" % (ipo.src, ipo.dst)) + + # Check for UDP + elif ipo.proto == IPPROTO_UDP: + flag3 = 0 + udpo = pkt.get_protocol(udp.udp) + + # Check for tracked UDP + if ipo.dst in self.udp_conn_track: + tmp_tpl = self.udp_conn_track.get(ipo.dst) + tmp = list(tmp_tpl) + for i in range(0, len(tmp)): + # + if(tmp[i][0] == ipo.src): + xyz = tmp[i] + # + if (int(xyz[1]) == udpo.dst_port) and (int(xyz[2]) == udpo.src_port) and (xyz[3] == "UNREPLIED"): + flag3 = 1 + self.logger.info("%s -> %s : UDP PACKET ALLOWED" % (ipo.src, ipo.dst)) + actions_default = action_fwd_to_out_port + del tmp[i] + self.logger.info("\n%s -> %s src_port= %s, dst_port= %s, state= ASSURED\n" %(ipo.src, ipo.dst, + udpo.src_port, udpo.dst_port)) + self.flow.add_flow(datapath=datapath, actions=actions_default, priority=1001, in_port = in_port, + eth_type = ETH_TYPE_IP, ip_proto = IPPROTO_UDP, + ipv4_src = ipo.src, ipv4_dst = ipo.dst, + udp_src = udpo.src_port, udp_dst = udpo.dst_port) + break + tmp_tpl = tuple(tmp) + if len(tmp_tpl) != 0: + self.udp_conn_track[ipo.dst] = tmp_tpl + else: + self.udp_conn_track.pop(ipo.dst, None) + # Check for first UDP packet + elif ipo.src in self.inner_policy: + temp = self.inner_policy.get(ipo.src) + for i in range(0, len(temp)): + # + if temp[i][0] == ipo.dst: + xyz = temp[i] + # + if (xyz[1] == 'UDP') and (int(xyz[2]) == udpo.src_port) and (int(xyz[3]) == udpo.dst_port) and (xyz[5] == 'ALLOW'): + flag3 = 1 + actions_default = action_fwd_to_out_port + self.udp_conn_track = self.track.conn_track_dict(self.udp_conn_track, ipo.src, ipo.dst, udpo.src_port, + udpo.dst_port, "UNREPLIED", 1) + self.logger.info("%s -> %s : UDP PACKET ALLOWED" % (ipo.src, ipo.dst)) + self.logger.info("\n%s -> %s src_port= %s, dst_port= %s, state= UNREPLIED\n" %(ipo.src, ipo.dst, udpo.src_port, udpo.dst_port)) + self.flow.add_flow(datapath = datapath, actions = actions_default, priority = 1001, in_port = in_port, + eth_type =ETH_TYPE_IP, ip_proto=IPPROTO_UDP, + ipv4_src = ipo.src, ipv4_dst = ipo.dst, + udp_src = udpo.src_port, udp_dst = udpo.dst_port) + break + # No match + if flag3 == 0: + actions_default = action_drop + self.logger.info("%s -> %s : UDP BLOCKED" % (ipo.src, ipo.dst)) + self.flow.add_flow(datapath = datapath, actions = actions_default, priority = 1001, + in_port = in_port, eth_type =ETH_TYPE_IP, ip_proto=IPPROTO_UDP, + ipv4_src = ipo.src, ipv4_dst = ipo.dst, + udp_src = udpo.src_port, udp_dst = udpo.dst_port) + + # If not ICMP, TCP or UDP then drop! + else: + self.logger.info("Wrong IP protoco found") + actions_default = action_drop + # Handling ARP rules + elif(ethtype == ETH_TYPE_ARP): + self.arp_handling(datapath, out_port, eth, in_port) + actions_default = action_fwd_to_out_port + + # If packet is not IP or ARP then drop! + else: + actions_default = action_drop + + except Exception as err: + self.logger.info("ERROR: %s", err) + action_drop = [parser.OFPActionOutput(ofproto.OFPPC_NO_FWD)] + actions_default = action_drop + finally: + self.sendpkt.send(datapath, msg, in_port, actions_default) + + # Add ARP rules on Flow Tables + def arp_handling(self, datapath, out_port, eth_obj, in_port): + if out_port != datapath.ofproto.OFPP_FLOOD: + actions = [datapath.ofproto_parser.OFPActionOutput(out_port)] + self.flow.add_flow(datapath=datapath, actions = actions, priority=1000, + in_port=in_port, eth_type=ETH_TYPE_ARP, eth_src=eth_obj.src,eth_dst=eth_obj.dst) + + def port_learn(self, datapath, eth_obj, in_port): + try: + self.mac_to_port.setdefault(datapath.id, {'00:00:00:00:00:01':1, '00:00:00:00:00:02':2, '00:00:00:00:00:03':3, '00:00:00:00:00:04':5, + '00:00:00:00:00:06':6,'00:00:00:00:00:07':7,'00:00:00:00:00:08':8,'00:00:00:00:00:09':9, + '00:00:00:00:00:10':10,'00:00:00:00:00:11':11,'00:00:00:00:00:12':12,'00:00:00:00:00:12':13, + '00:00:00:00:00:14':14,'00:00:00:00:00:15':15,}) + self.mac_to_port[datapath.id][eth_obj.src] = in_port + # out_port = datapath.ofproto.OFPP_FLOOD + + if (eth_obj.ethertype == ETH_TYPE_IP) or (eth_obj.ethertype == ETH_TYPE_ARP): + if eth_obj.dst in self.mac_to_port[datapath.id]: + out_port = self.mac_to_port[datapath.id][eth_obj.dst] + return out_port + # else: + out_port = datapath.ofproto.OFPP_FLOOD + return out_port + except Exception as err: + self.info(err) + out_port = datapath.ofproto.OFPP_FLOOD + return out_port + # finally: + # return out_port diff --git a/Firewall/dataset/firewall-drop.db b/Firewall/dataset/firewall-drop.db new file mode 100644 index 0000000..7808d98 Binary files /dev/null and b/Firewall/dataset/firewall-drop.db differ diff --git a/Firewall/dataset/firewall-vTest.db b/Firewall/dataset/firewall-vTest.db new file mode 100644 index 0000000..705a3bc Binary files /dev/null and b/Firewall/dataset/firewall-vTest.db differ diff --git a/Firewall/dataset/firewall.txt b/Firewall/dataset/firewall.txt new file mode 100644 index 0000000..f002cb2 --- /dev/null +++ b/Firewall/dataset/firewall.txt @@ -0,0 +1,17 @@ +10.0.0.1,10.0.0.2,TCP,1000,8080,NEW,ALLOW +10.0.0.1,10.0.0.3,TCP,1000,8080,NEW,ALLOW +10.0.0.2,10.0.0.3,TCP,1000,1000,NEW,ALLOW +10.0.0.2,10.0.0.1,TCP,8080,1000,EST,ALLOW +10.0.0.2,10.0.0.3,TCP,1000,1000,EST,ALLOW +10.0.0.3,10.0.0.2,TCP,1000,1000,EST,ALLOW +10.0.0.3,10.0.0.2,TCP,1000,1000,NEW,ALLOW +10.0.0.3,10.0.0.1,TCP,8080,1000,EST,ALLOW +10.0.0.1,10.0.0.2,UDP,1000,8080,-,ALLOW +10.0.0.1,10.0.0.3,UDP,1000,8080,-,ALLOW +10.0.0.3,10.0.0.1,UDP,8080,1000,-,ALLOW +10.0.0.2,10.0.0.1,UDP,8080,1000,-,ALLOW +10.0.0.2,10.0.0.3,UDP,1000,1000,-,ALLOW +10.0.0.3,10.0.0.2,UDP,1000,1000,-,ALLOW +10.0.0.1,10.0.0.2,ICMP,-,-,PING,ALLOW +81.84.126.220,147.83.42.206,TCP,57253,1897,NEW,ALLOW +81.84.126.220,147.83.42.206,UDP,57253,1897,-,ALLOW \ No newline at end of file diff --git a/Firewall/flow_addition.py b/Firewall/flow_addition.py new file mode 100644 index 0000000..f5faec4 --- /dev/null +++ b/Firewall/flow_addition.py @@ -0,0 +1,19 @@ +import logging + +class FlowAdd(): + # default function for construcing instructions. + # Sends constructed message to connected Sswitch. + + def __init__(self): + logging.info("Flow table rules are sent to switch") + + def add_flow(self, datapath, priority, match, actions, idle_timeout=1800): + ofproto = datapath.ofproto + parser = datapath.ofproto_parser + + inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)] + + modify = parser.OFPFlowMod(datapath=datapath, priority = priority, match=match, + instructions = inst, idle_timeout = idle_timeout) + + datapath.send_msg(modify) diff --git a/Firewall/flowtracker.py b/Firewall/flowtracker.py new file mode 100644 index 0000000..20e7603 --- /dev/null +++ b/Firewall/flowtracker.py @@ -0,0 +1,102 @@ +from ryu.lib import pcaplib +from ryu.lib.packet import arp +from ryu.lib.packet import ethernet +from ryu.lib.packet import icmp +from ryu.lib.packet import ipv4 +from ryu.lib.packet import ipv6 +from ryu.lib.packet import packet +from ryu.lib.packet import packet_base +from ryu.lib.packet import tcp +from ryu.lib.packet import udp +from ryu.lib.packet import vlan +import json + +ETHERNET = ethernet.ethernet.__name__ +VLAN = vlan.vlan.__name__ +IPV4 = ipv4.ipv4.__name__ +IPV6 = ipv6.ipv6.__name__ +ARP = arp.arp.__name__ +ICMP = icmp.icmp.__name__ +TCP = tcp.tcp.__name__ +UDP = udp.udp.__name__ + + +# class Writer2(pcaplib.Writer): + +# def __init__(self, file_obj, snaplen=65535, network=1): +# super(Writer2, self).__init__( +# file_obj, snaplen=snaplen, network=network) + +# def write_pkt(self, buf, ts=None): +# super(Writer2, self).write_pkt(buf, ts=ts) +# self._f.flush() + + +class Tracker: + + all_stats = [] + + def untrack(self, id): + if id in self.existing_name(self.all_stats): + root = self.get_name(id, self.all_stats) + self.all_stats.remove(root) + + def reset(self, id): + if id in self.existing_name(self.all_stats): + root = self.get_name(id, self.all_stats) + root["children"] = [] + + def track(self, id, pkt): + # Find if a tree has been created for this ID, + # Otherwise, create a new one + if id in self.existing_name(self.all_stats): + root = self.get_name(id, self.all_stats) + else: + root = {"name": id, "children": []} + self.all_stats.append(root) + + # Get all protocols in a packet + header_list = [ + (p.protocol_name, p) + for p in pkt.protocols + if isinstance(p, packet_base.PacketBase) + ] + + for k in header_list: + name = self.getName(k, header_list) + if name in self.existing_name(root["children"]): + # If the protcol is found in the tree, make it root + # for the next protocol + root = self.get_name(name, root["children"]) + else: + # If the protcol is not found in the tree, create a new node + # and make it root for the next protocol + new_root = {"name": name, "children": []} + root["children"].append(new_root) + root = new_root + + c = root.setdefault("count", 0) + root["count"] = c + 1 + + return self.all_stats + + def getName(self, k, header_list): + if k[0] in [ETHERNET, IPV4, IPV6]: + s = k[1].src + d = k[1].dst + return "{} [{}, {}]".format(k[0], s, d) + if k[0] in [TCP, UDP]: + # s = k[1].src_port + d = k[1].dst_port + return "{} [{}]".format(k[0], d) + return k[0] + + def existing_name(self, lst): + for n in lst: + yield n["name"] + + def get_name(self, name, lst): + for n in lst: + if n.get("name", None) == name: + return n + return None diff --git a/Directory/packet_out.py b/Firewall/packet_out.py similarity index 74% rename from Directory/packet_out.py rename to Firewall/packet_out.py index 871d772..71bd4d0 100644 --- a/Directory/packet_out.py +++ b/Firewall/packet_out.py @@ -1,9 +1,8 @@ -#! usr/bin/env python import logging class SendPacket(): def __init__(self): - logging.info("Controller is configured to send received packets back to the switch") + logging.info("Controller is configured to handle packets coming from switch.") def send(self,datapath, msg, port, action): data = None @@ -13,4 +12,4 @@ def send(self,datapath, msg, port, action): out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, in_port=port, actions=action, data=data) - datapath.send_msg(out) \ No newline at end of file + datapath.send_msg(out) diff --git a/Directory/parse_firewall_rules.py b/Firewall/parse_firewall_rules.py similarity index 83% rename from Directory/parse_firewall_rules.py rename to Firewall/parse_firewall_rules.py index c8f958b..d153205 100644 --- a/Directory/parse_firewall_rules.py +++ b/Firewall/parse_firewall_rules.py @@ -1,4 +1,3 @@ -#! usr/bin/env python import copy class parse_firewall: @@ -15,7 +14,7 @@ def parse(self): list1.append(lines[i].split(',')) list2 = copy.deepcopy(list1) - if(firewall_dict.has_key(str(list2[i][0])) is False): + if((list2[i][0])) not in firewall_dict: key = str(list2[i][0]) list2[i].remove(key) tup = tuple(list2[i]) @@ -23,7 +22,7 @@ def parse(self): tup = tuple(listobj) firewall_dict[key] = tup - elif (firewall_dict.has_key(str(list2[i][0])) is True): + elif ((list2[i][0])) in firewall_dict: key = str(list2[i][0]) dst = firewall_dict[key] dst = list(dst) @@ -33,5 +32,5 @@ def parse(self): firewall_dict[key] = tup del listobj[:] - print len(firewall_dict.keys()) + print(len(firewall_dict.keys())) return firewall_dict diff --git a/Firewall/reset_flow_table.py b/Firewall/reset_flow_table.py new file mode 100644 index 0000000..c6853d2 --- /dev/null +++ b/Firewall/reset_flow_table.py @@ -0,0 +1,30 @@ +import logging +from ryu.ofproto.ether import ETH_TYPE_LLDP +from construct_flow import Construct + +class ResetSwitch(): + # Reset the switch. Flush all flow table entries and set up default behavior + + def __init__(self, dp): + self.__rest_switch(dp) + + def __rest_switch(self, dp): + assert (dp is not None), "Datapath Object is not set." + + ofproto = dp.ofproto + parser = dp.ofproto_parser + flow_mod = dp.ofproto_parser.OFPFlowMod(dp, 0,0,0, + ofproto.OFPFC_DELETE, 0,0,1, + ofproto.OFPCML_NO_BUFFER, + ofproto.OFPP_ANY, + ofproto.OFPG_ANY) + logging.info("Deleting all flow table entries...") + dp.send_msg(flow_mod) + + # Install table-miss flow entry + const = Construct() + actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, ofproto.OFPCML_NO_BUFFER)] + const.add_flow(datapath=dp, actions=actions, priority=0) + + actions = [parser.OFPActionOutput(ofproto.OFPPC_NO_FWD)] + const.add_flow(datapath = dp, actions=actions, priority=1000, eth_type = ETH_TYPE_LLDP) diff --git a/Firewall/statsSwitch.py b/Firewall/statsSwitch.py new file mode 100644 index 0000000..3746367 --- /dev/null +++ b/Firewall/statsSwitch.py @@ -0,0 +1,137 @@ +from operator import attrgetter + +from ryu.app import simple_switch_13 +from ryu.controller import ofp_event +from ryu.controller.handler import MAIN_DISPATCHER, DEAD_DISPATCHER +from ryu.controller.handler import set_ev_cls +from ryu.lib import hub +# import ryu.app.ofctl.api as api + + +class SimpleMonitor13(simple_switch_13.SimpleSwitch13): + + def __init__(self, *args, **kwargs): + super(SimpleMonitor13, self).__init__(*args, **kwargs) + self.datapaths = {} + self.monitor_thread = hub.spawn(self._monitor) + + @set_ev_cls(ofp_event.EventOFPStateChange, + [MAIN_DISPATCHER, DEAD_DISPATCHER]) + def _state_change_handler(self, ev): + datapath = ev.datapath + if ev.state == MAIN_DISPATCHER: + if datapath.id not in self.datapaths: + self.logger.debug('register datapath: %016x', datapath.id) + self.datapaths[datapath.id] = datapath + elif ev.state == DEAD_DISPATCHER: + if datapath.id in self.datapaths: + self.logger.debug('unregister datapath: %016x', datapath.id) + del self.datapaths[datapath.id] + + def _monitor(self): + while True: + for dp in self.datapaths.values(): + self._request_stats(dp) + hub.sleep(5) + + def _request_stats(self, datapath): + # print("REQUEST STATS") + self.logger.debug('send stats request: %016x', datapath.id) + ofproto = datapath.ofproto + parser = datapath.ofproto_parser + + req = parser.OFPFlowStatsRequest(datapath) + datapath.send_msg(req) + + req = parser.OFPPortStatsRequest(datapath, 0, ofproto.OFPP_ANY) + datapath.send_msg(req) + + @set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER) + def _flow_stats_reply_handler(self, ev): + body = ev.msg.body + print("**************************************") + print("**************************************") + # self.getFlows(ev.msg.datapath) + self.logger.info('datapath ' + 'in-port eth-dst eth_src ' + 'out-port packets bytes') + self.logger.info('---------------- ' + '-------- ----------------- ' + '-------- -------- --------') + for stat in sorted([flow for flow in body if flow.priority >= 1], + key=lambda flow: (flow.match.get('in_port', 0), + flow.match.get('eth_dst', ''), + flow.match.get('eth_src', ''))): + self.logger.info('%016x %4x %17s %17s %8x %8d %8d', + ev.msg.datapath.id, + stat.match['in_port'], stat.match['eth_dst'], stat.match['eth_src'], + stat.instructions[0].actions[0].port, + stat.packet_count, stat.byte_count) + + print("**************************************") + print("**************************************") + # body = ev.msg.body + # + # self.logger.info('datapath ' + # 'actions ' + # 'packets bytes') + # self.logger.info('---------------- ' + # '--------------------------------------------------- ' + # '-------- --------') + # for stat in sorted([flow for flow in body if flow.priority >= 1], + # key=lambda flow: flow.instructions[0].actions[0].port): + # self.logger.info('%016x %51s %8d %8d', + # ev.msg.datapath.id,stat.instructions[0].actions[0].port, + # stat.packet_count, stat.byte_count) + # if(int(stat.byte_count) > 2000000): + # #add a flow to slower hosts wire speed + # dp = ev.msg.datapath + # ofp = dp.ofproto + # parser = dp.ofproto_parser + # + # match = parser.OFPMatch(in_port=stat.match['in_port']) + # actions = [parser.OFPActionSetQueue(1)] + # inst = [parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS,actions),parser.OFPInstructionGotoTable(1)] + # mod = parser.OFPFlowMod(datapath=dp, priority=1001,match=match, instructions=inst) + # dp.send_msg(mod) + # if(int(stat.byte_count) > 3000000): + # #add a flow to drop the packet + # dp = ev.msg.datapath + # ofp = dp.ofproto + # parser = dp.ofproto_parser + # + # match = parser.OFPMatch(in_port=stat.match['in_port']) + # actions = [parser.OFPActionSetQueue(2)] + # inst = [parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS,actions),parser.OFPInstructionGotoTable(1)] + # mod = parser.OFPFlowMod(datapath=dp, priority=1002,match=match, instructions=inst) + # dp.send_msg(mod) + + # def ofdpaTableStatsRequest(self, datapath): + # parser = datapath.ofproto_parser + # return parser.OFPTableStatsRequest(datapath) + # + # def getFlows(self, datapath): + # """ + # Obtain a list of Flows loaded on the switch + # ` + # :return: A list of Flow Entires + # """ + # msg = self.ofdpaTableStatsRequest(datapath) + # reply = api.send_msg(ryuapp, msg, + # reply_cls=self.parser.OFPTableStatsReply, + # reply_multi=True) + # @set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER) + # def _port_stats_reply_handler(self, ev): + # body = ev.msg.body + # + # self.logger.info('datapath port ' + # 'rx-pkts rx-bytes rx-error ' + # 'tx-pkts tx-bytes tx-error') + # self.logger.info('---------------- -------- ' + # '-------- -------- -------- ' + # '-------- -------- --------') + # for stat in sorted(body, key=attrgetter('port_no')): + # self.logger.info('%016x %8x %8d %8d %8d %8d %8d %8d', + # ev.msg.datapath.id, stat.port_no, + # stat.rx_packets, stat.rx_bytes, stat.rx_errors, + # stat.tx_packets, stat.tx_bytes, stat.tx_errors) diff --git a/Firewall/switch_information.py b/Firewall/switch_information.py new file mode 100644 index 0000000..9044e68 --- /dev/null +++ b/Firewall/switch_information.py @@ -0,0 +1,21 @@ +import logging +from reset_flow_table import ResetSwitch + +class SwitchInfo(): + # Handles switch connection and disconnection information. + def __init__(self, event): + switch = event.dp + if event.enter: + logging.info("datapath has joined") + logging.info(switch.ofproto) + logging.info(switch.ofproto_parser) + self.__switch_connected(switch) + else: + self.__switch_disconnected(switch) + + def __switch_connected(self, sw): + logging.info("Switch %s has connected with OFP %s...", sw.id, sw.ofproto) + ResetSwitch(sw) + + def __switch_disconnected(self, sw): + logging.info("Switch %s has disconnected from OFP %s...", sw.id, sw.ofproto) diff --git a/Network/network.py b/Network/network.py new file mode 100644 index 0000000..e0d3f68 --- /dev/null +++ b/Network/network.py @@ -0,0 +1,65 @@ +""" +Three directly connected switches plus a host attached to each switch +with a remote RYU SDN Controller (c0): + _ _ _ _ _ c0_ _ _ _ _ _ + / | \ + / | \ + / | \ + / | \ + / | \ + ----s1--------------s2-----------------s3------ + / / | \ \ / / | \ \ / / | \ \ + h1 h2 h3 h4 h5 h6 h7 h8 h9 h10 h11 h12 h13 h14 h15 +""" + +from mininet.net import Mininet +from mininet.node import Controller, RemoteController +from mininet.cli import CLI +from mininet.log import setLogLevel, info + +# Ryu controller +ryu_ip = '127.0.0.1' +ryu_port = 6653 + +# Define remote RYU Controller +print ('Ryu IP Address: {}'.format( ryu_ip)) +print ('Ryu Port No: {}'.format(ryu_port)) + +def network(): + net = Mininet(topo=None, build=False) + # Add controller + info("Adding controller...\n") + net.addController('c0', controller=RemoteController, ip=ryu_ip,port=ryu_port) + + # Add host + info("Adding hosts...\n") + h1, h2, h3, h4, h5 = [net.addHost(h) for h in ('h1', 'h2', 'h3', 'h4', 'h5')] + h6, h7, h8, h9, h10 = [net.addHost(h) for h in ('h6', 'h7', 'h8', 'h9', 'h10')] + h11, h12, h13, h14, h15 = [net.addHost(h) for h in ('h11', 'h12', 'h13', 'h14', 'h15')] + # Add switches + info("Adding switches...\n") + s1, s2, s3 = [net.addSwitch(s) for s in ('s1', 's2', 's3')] + # Add links + info("Adding switch links...\n") + for sa, sb in [(s1, s2), (s2, s3)]: + net.addLink(sa, sb) + for h, s in [(h1, s1), (h2, s1), (h3, s1), (h4, s1), (h5, s1)]: + net.addLink(h, s) + for h, s in [(h6, s2), (h7, s2), (h8, s2), (h9, s2), (h10, s2)]: + net.addLink(h, s) + for h, s in [(h11, s3), (h12, s3), (h13, s3), (h14, s3), (h15, s3)]: + net.addLink(h, s) + + info("*** Starting network ***\n") + net.start() + info("*** Running CLI ***\n") + CLI(net) + + info("*** Stopping network ***\n") + net.stop() + +if __name__ == '__main__': + setLogLevel('info') + network() + +exit(0) diff --git a/README.md b/README.md index 3e9f3de..90951e3 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,147 @@ SDN_Firewall ============ +## My Topic +* Tutorial and result for this project: [SDN Firewall and Application control Network.](https://github.com/HODUCVU/SDN_Firewall/blob/main/Documents/SDN-Firewall_And_Application_Control_Network.pdf) + +* [Introduction from paper](/Documents/2015-IDP-OpenFlow-Firewall.pdf) +## Important files +``` +. +├── Documents +│   ├── SDN-Firewall_And_Application_Control_Network.pdf ********* +│   ├── 2015-IDP-OpenFlow-Firewall.pdf +│   ├── Project's_Introduction.pdf +│   ├── README.md +├── Firewall +│   ├── connection_tracking.py +│   ├── construct_flow.py +│   ├── customFirewallStateful.py +│   ├── dataset +│   │   ├── firewall-drop.db +│   │   └── firewall-vTest.db +│   ├── FirewallDrop.py *************** +│   ├── flow_addition.py +│   ├── packet_out.py +│   ├── ParseFirewallFromDB.py +│   ├── parse_firewall_rules.py +│   ├── reset_flow_table.py +│   ├── SQL +│   │   ├── firewallDB.txt +│   │   ├── firewallDrop.csv +│   │   ├── initDB.py +│   └── switch_information.py +├── Network +│   └── network.py +├── application +├── README.md +``` +### Network +* Structure Network +``` +Three directly connected switches plus a host attached to each switch +with a remote RYU SDN Controller (c0): + _ _ _ _ _ c0_ _ _ _ _ _ + / | \ + / | \ + / | \ + / | \ + / | \ + ----s1--------------s2-----------------s3------ + / / | \ \ / / | \ \ / / | \ \ + h1 h2 h3 h4 h5 h6 h7 h8 h9 h10 h11 h12 h13 h14 h15 +``` +* Implement +``` +> cd Network +> sudo python3 network.py + +mininet> net +h1 h1-eth0:s1-eth2 +h2 h2-eth0:s1-eth3 +h3 h3-eth0:s1-eth4 +h4 h4-eth0:s1-eth5 +h5 h5-eth0:s1-eth6 +h6 h6-eth0:s2-eth3 +h7 h7-eth0:s2-eth4 +h8 h8-eth0:s2-eth5 +h9 h9-eth0:s2-eth6 +h10 h10-eth0:s2-eth7 +h11 h11-eth0:s3-eth2 +h12 h12-eth0:s3-eth3 +h13 h13-eth0:s3-eth4 +h14 h14-eth0:s3-eth5 +h15 h15-eth0:s3-eth6 +s1 lo: s1-eth1:s2-eth1 s1-eth2:h1-eth0 s1-eth3:h2-eth0 s1-eth4:h3-eth0 s1-eth5:h4-eth0 s1-eth6:h5-eth0 +s2 lo: s2-eth1:s1-eth1 s2-eth2:s3-eth1 s2-eth3:h6-eth0 s2-eth4:h7-eth0 s2-eth5:h8-eth0 s2-eth6:h9-eth0 s2-eth7:h10-eth0 +s3 lo: s3-eth1:s2-eth2 s3-eth2:h11-eth0 s3-eth3:h12-eth0 s3-eth4:h13-eth0 s3-eth5:h14-eth0 s3-eth6:h15-eth0 +c0 +``` +## Firewall - run on python3.8 +### Database +* Step 1: Create database +``` +SDN_Firewall> cd Firewall/SQL +// Create database - it on Firewall/dataset/firewall-vTest.db already +SQL> python3 initDB.py +``` +* Step 2: Check database +``` +Firewall> python3 ParseFirewallFromDB.py +``` + +## Run check for Firewall +* Step 1: Create network +``` +> cd Network +> sudo python3 network.py +``` + + + +* Step 2: Open terminal for devices +``` +mininet> xterm c0 h1 h2 h3 <...> +``` +* Step 3: Check + * Controller + ``` + c0> cd Firewall + c0> ryu-manager FirewallDrop.py + ``` + * Ping with ICMP protocol + ``` + h1> ping -c2 10.0.0.2 + // Drop + h2> ping -c1 10.0.0.1 + // connect (don't have in table drop) + //and ... + // ping to other ip to check connect + ``` + * Ping with TCP protocol + ``` + h1 (SYN)> hping3 -c 10 -k -s 1000 -p 8080 -S 10.0.0.2 + h1 (SYN_ASK)> hping3 -c 10 -k -s 1000 -p 8080 -SA 10.0.0.2 + //-> DROP + h2 (SYN)> hping3 -c 10 -k -s 8080 -p 1000 -S 10.0.0.1 + h2 (SYN_ASK)> hping3 -c 10 -k -s 8080 -p 1000 -SA 10.0.0.1 + //-> DROP + //and ... + // ping to other port to check connect + ``` + * Ping with UDP protocol + ``` + h1> hping3 --udp -c 10 -k -s 1000 -p 8080 10.0.0.2 + h2> hping3 --udp -c 10 -k -s 8080 -p 1000 10.0.0.1 + //and ... + ``` + + + + + + +# Demo on Application +Video demo is pushed on [Youtube](https://www.youtube.com/watch?v=Y4_bdANML4c&fbclid=IwAR0CCl0YLMpUoshtkUvCrPExZ2ZvN3odxbjxDokLjhsl_V-wVNAzPE99YIA) \ +Click on video below: \ +[![Video Demo](http://img.youtube.com/vi/Y4_bdANML4c/0.jpg)](http://www.youtube.com/watch?v=Y4_bdANML4c) -SDN Firewall Project is about implementation of Firewall on SDN Controller by running bunch of Controller Application. - -This repository consists of few Ryu Applications which are just python scripts written for running firewall functionality on Ryu controller. You need to have Ryu and Mininet installed on your device in order to run these Ryu applications. The Ryu applications are capable of ICMP, TCP and UDP firewall rules. - -There are 2 types of application each of which having 2 version. Namely, - -* Inefficient Application -* Efficient Application - - -1) Inefficient Firewall: -It is named inefficient because it is **not** using Flow Tables functionality of Open vSwitches. It does not store any flow table entries extensively on the switch. Rather, it simply makes the switch to forward everything to the controller. Upon receiving each packet on controller, it takes necessary action (forwarding/discarding) to perform on the packet. - -2) Efficient Firewall: -This application uses Flow tables to store flow table entries on switch so that next time packets from that flow won't be forwarded to the controller. The switch will take decision based upon Flow table entries to forward to appropriate port or to reject the packet. Therefore, this applications are efficient in terms of packet exchange. - -The two versions of firewalls are : Stateful & Stateless. - -1) Stateful firewall: -In this type of firewall, the states of the packets that passed through controller, are remembered. These states are maintained in order to detect the flow of the packet exchange between two communicating parties. The next time when a packet is received by the controller, it will check the packet with the maintained states to match the packet with particular ongoing connection. This way, the firewall keeps track of the connections itself. - -2) Stateless Firewall: -This case implements traditional firewall which only checks each arriving packet. It does **not** maintain any states like Stateful Firewall. That is, it does not keep track of ongoing connection. Rather, it simply takes each packet as individuatl and tries to check the packet with the given firewall rules. Upon matching firewall rule, it carries out associated actions, like forwarding to particular port or discarding the packet. - - -With two types of applications and two versions of Firewall, this project consists of 4 different Ryu Firewall Applications. These are: - -* Inefficient Stateful Firewall -* Inefficient Stateless Firewall -* Efficient(named secure) Stateful Firewall -* Efficient(named secure) Stateless Firewall - -## What you have to do if you want to play around: - -1. Have mininet and ryu installed on your computer -2. Get this repo. -3. Create mininet topology for 3 nodes, 1 open vswich and 1 controller (Check Appendix of Report pdf or guidebook of Ryu for creating such topology) -4. Read two example txts that represents how you can define Firewall Rules for Stateful and Stateless firewalls. -5. Write down your rules inside Firewall.txt file -6. Run the appropriate Ryu application using ryu-manager on controller node. -7. For TCP case, use nc to listen tcp connection on one node and initiator on second node. This nodes should be on par with the firewall rules you have configured in Firewall.txt file. -8. Make some text trasnfer between your nodes. You will see the process on controller and switch. If you are running "Efficient" applications, you can see the flow table entries on switch that are created by the controller. - -For any queries about setup, please refer the Report file (2015-IDP-OpenFlow-Firewall.pdf) or drop me an email. - -P.S. Please bear the *worst* code quality. ;) \ No newline at end of file diff --git a/src/construct_flow.py b/src/construct_flow.py index 3549c9b..79bdca4 100644 --- a/src/construct_flow.py +++ b/src/construct_flow.py @@ -82,4 +82,4 @@ def add_flow(self,datapath,actions, priority = 1000 ,in_port=None, in_phy_port=N if match is not None: matchflow.add_flow(datapath, priority, match, actions,idle_timeout) else: - logging.info("Sorry, no matching rule found or added.") \ No newline at end of file + logging.info("Sorry, no matching rule found or added.") diff --git a/src/inefficient_stateful_firewall.py b/src/inefficient_stateful_firewall.py index 7f6ed84..39a4462 100644 --- a/src/inefficient_stateful_firewall.py +++ b/src/inefficient_stateful_firewall.py @@ -16,7 +16,9 @@ from ryu.base import app_manager from ryu.controller import ofp_event, dpset from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER,set_ev_cls -from ryu.ofproto import ofproto_v1_3, ofproto_v1_3_parser +# from ryu.ofproto import ofproto_v1_3, ofproto_v1_3_parser +from ryu.ofproto import ofproto_v1_0, ofproto_v1_2, ofproto_v1_3 +from ryu.ofproto import ofproto_v1_0_parser, ofproto_v1_2_parser, ofproto_v1_3_parser from ryu.lib.packet import packet,ethernet,ipv4,udp,tcp,icmp from ryu.ofproto.ether import ETH_TYPE_IP, ETH_TYPE_ARP,ETH_TYPE_LLDP,ETH_TYPE_MPLS,ETH_TYPE_IPV6 from ryu.ofproto.inet import IPPROTO_ICMP, IPPROTO_TCP, IPPROTO_UDP,IPPROTO_SCTP @@ -33,8 +35,10 @@ TCP_BOGUS_FLAGS = 0x15 class InefficientFirewall(app_manager.RyuApp): - OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] - + # OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] + OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION, + ofproto_v1_2.OFP_VERSION, + ofproto_v1_3.OFP_VERSION] inner_policy = {} icmp_conn_track = {} tcp_conn_track = {} @@ -84,10 +88,10 @@ def packet_in_handler(self, ev): # Check for ICMP if(ipo.proto == IPPROTO_ICMP): flag1 = 0 - icmpob = pkt.get_protocol(icmp.icmp) + icmpo = pkt.get_protocol(icmp.icmp) # Check if this is PING - if ((icmpob.type==ICMP_PING) and self.inner_policy.has_key(ipo.src)): + if ((icmpob.type==ICMP_PING) and (ipo.src in self.inner_policy)): temp = self.inner_policy.get(ipo.src) for i in range(0,len(temp)): if temp[i][0] == ipo.dst: @@ -100,7 +104,7 @@ def packet_in_handler(self, ev): break # Otherwise, PONG...! - elif((icmpob.type == ICMP_PONG) and (self.icmp_conn_track.has_key(ipo.src))): + elif((icmpob.type == ICMP_PONG) and (ipo.src in self.icmp_conn_track)): temp2 = self.icmp_conn_track.get(ipo.src) for i in range(0,len(temp2)): if temp2[i][0] == ipo.dst: @@ -125,7 +129,7 @@ def packet_in_handler(self, ev): # TCP SYN packet if (((tcpo.bits & TCP_SYN) == TCP_SYN) & ((tcpo.bits & TCP_BOGUS_FLAGS) == 0x00)): - if self.inner_policy.has_key(ipo.src): + if (ipo.src) in self.inner_policy: temp = self.inner_policy.get(ipo.src) for i in range(0,len(temp)): if ((temp[i][0] == ipo.dst) and (temp[i][1]=='TCP') and (int(temp[i][2]) == tcpo.src_port) and (int(temp[i][3]) == tcpo.dst_port) and (temp[i][5] == 'ALLOW')): @@ -137,7 +141,7 @@ def packet_in_handler(self, ev): # TCP SYN ACK packet elif((tcpo.bits & TCP_SYN_ACK) == TCP_SYN_ACK): - if self.tcp_conn_track.has_key(ipo.dst): + if (ipo.dst) in self.tcp_conn_track: temp2 = self.tcp_conn_track.get(ipo.dst) for i in range(0,len(temp2)): if ((temp2[i][0] == ipo.src) and (int(temp2[i][1]) == tcpo.dst_port) and (int(temp2[i][2]) == tcpo.src_port)): @@ -150,7 +154,7 @@ def packet_in_handler(self, ev): # All remaining TCP packets, like, ACK, PUSH, FIN etc. else: - if self.tcp_conn_track.has_key(ipo.src): + if ipo.src in self.tcp_conn_track: temp3 = self.tcp_conn_track.get(ipo.src) for i in range(0,len(temp3)): if ((temp3[i][0] == ipo.dst) and (int(temp3[i][1]) == tcpo.src_port) and (int(temp3[i][2]) == tcpo.dst_port)): @@ -171,7 +175,7 @@ def packet_in_handler(self, ev): udpo = pkt.get_protocol(udp.udp) # Check for tracked UDP - if self.udp_conn_track.has_key(ipo.dst): + if (ipo.dst) in self.udp_conn_track: tmp_tpl = self.udp_conn_track.get(ipo.dst) tmp = list(tmp_tpl) for i in range(0,len(tmp)): @@ -191,7 +195,7 @@ def packet_in_handler(self, ev): self.udp_conn_track.pop(ipo.dst,None) # Check for first UDP packet - elif self.inner_policy.has_key(ipo.src): + elif (ipo.src) in self.inner_policy: temp = self.inner_policy.get(ipo.src) for i in range(0,len(temp)): if temp[i][0] == ipo.dst: @@ -227,7 +231,7 @@ def packet_in_handler(self, ev): actions_default = action_drop except Exception as err: - self.logger.info("MYERROR: %s" , err.message) + self.logger.info("MYERROR: %s" , str(err)) action_drop = [parser.OFPActionOutput(ofproto.OFPPC_NO_FWD)] actions_default = action_drop @@ -262,4 +266,4 @@ def port_learn(self, datapath, eth_obj, in_port): self.info(err.message) out_port = datapath.ofproto.OFPP_FLOOD finally: - return out_port \ No newline at end of file + return out_port diff --git a/src/parse_firewall_rules.py b/src/parse_firewall_rules.py index c8f958b..63d2276 100644 --- a/src/parse_firewall_rules.py +++ b/src/parse_firewall_rules.py @@ -15,7 +15,7 @@ def parse(self): list1.append(lines[i].split(',')) list2 = copy.deepcopy(list1) - if(firewall_dict.has_key(str(list2[i][0])) is False): + if((list2[i][0])) not in firewall_dict: key = str(list2[i][0]) list2[i].remove(key) tup = tuple(list2[i]) @@ -23,7 +23,7 @@ def parse(self): tup = tuple(listobj) firewall_dict[key] = tup - elif (firewall_dict.has_key(str(list2[i][0])) is True): + elif ((list2[i][0])) in firewall_dict: key = str(list2[i][0]) dst = firewall_dict[key] dst = list(dst) @@ -33,5 +33,5 @@ def parse(self): firewall_dict[key] = tup del listobj[:] - print len(firewall_dict.keys()) + print(len(firewall_dict.keys())) return firewall_dict diff --git a/src/secure_stateful_firewall.py b/src/secure_stateful_firewall.py index 9dd916e..1e47480 100644 --- a/src/secure_stateful_firewall.py +++ b/src/secure_stateful_firewall.py @@ -291,4 +291,4 @@ def port_learn(self, datapath, eth_obj, in_port): self.info(err.message) out_port = datapath.ofproto.OFPP_FLOOD finally: - return out_port \ No newline at end of file + return out_port