Skip to content

Commit 601cd8d

Browse files
Kudzai P MatizirofaKudzai P Matizirofa
Kudzai P Matizirofa
authored and
Kudzai P Matizirofa
committed
Re structured codebase and added code for handling interfaces
1 parent 6a5d149 commit 601cd8d

20 files changed

+229
-15
lines changed

__pycache__/firewall.cpython-311.pyc

2.11 KB
Binary file not shown.

__pycache__/main.cpython-311.pyc

4.07 KB
Binary file not shown.

__pycache__/packet.cpython-311.pyc

4.78 KB
Binary file not shown.

__pycache__/util.cpython-311.pyc

2 KB
Binary file not shown.

firewall.log

Whitespace-only changes.
4.11 KB
Binary file not shown.
1.92 KB
Binary file not shown.
4.79 KB
Binary file not shown.
1.52 KB
Binary file not shown.
1015 Bytes
Binary file not shown.
2.01 KB
Binary file not shown.

firewall/core.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import socket
2+
import time
3+
import threading
4+
from netaddr import IPNetwork
5+
import psutil
6+
import datetime
7+
import logging
8+
import ctypes, sys
9+
from firewall.firewall import check_packet
10+
from firewall.packet import ethernet_frame, ipv4_packet, tcp_packet, udp_packet
11+
from firewall.util import PROTOCOLS
12+
13+
#The software should run with admin permissions
14+
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)
15+
16+
logging.basicConfig(level=logging.INFO, filename="firewall.log", filemode="w")
17+
18+
# Create Send Socket for all the interfaces
19+
send_sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)
20+
send_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
21+
send_sock.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
22+
23+
def sendpacket(conn: socket.socket, payload, dst_ip):
24+
try:
25+
conn.sendto(payload, (dst_ip, 0))
26+
except PermissionError as broadcastError:
27+
print(broadcastError)
28+
pass
29+
except OSError as Error:
30+
print(Error)
31+
pass
32+
33+
34+
def bind_sockets(interface):
35+
# create a socket connection to listen for packets
36+
conn = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,socket.ntohs(0x0800))
37+
conn.bind((interface[0], 0))
38+
try:
39+
# continuously receive/listen for data
40+
while True:
41+
# send(bytes[, flags])
42+
raw_data, _ = conn.recvfrom(65536)
43+
"""
44+
Create and gather the frame details from an ethernet instance
45+
"""
46+
dest_mac, src_mac, eth_protocol, eth_data = ethernet_frame(raw_data) # gather the frame details
47+
src_port, dst_port = 0, 0
48+
49+
if eth_protocol == 8:
50+
s_addr, d_addr, protocol, ip_header = ipv4_packet(eth_data[14:34])
51+
logging.info(f"[{datetime.datetime.now()}] {interface[0]} ({d_addr}) > {PROTOCOLS[protocol]}")
52+
if protocol == 6:
53+
# Extract the TCP dst & src Ports
54+
src_port, dst_port = tcp_packet(eth_data[34:54])
55+
56+
elif protocol == 17:
57+
# Extract the UDP dst & src Ports
58+
src_port, dest_port, size, data = udp_packet(eth_data[34:42])
59+
60+
"""
61+
All traffic is denied by default and only routes specified
62+
in the rules list are allowed
63+
"""
64+
if check_packet(s_addr, d_addr, src_port, dst_port):
65+
sendpacket(send_sock, eth_data[14:], d_addr)
66+
else:
67+
logging.error(f"<FAILED ROUTE>[{datetime.datetime.now()}] {interface[0]} ({s_addr}, {d_addr}) > {PROTOCOLS[protocol]}")
68+
except KeyboardInterrupt:
69+
print("\n[END] STOPPED")
70+
return

firewall.py renamed to firewall/firewall.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import os
22
import csv
33

4-
from rule_list import get_all_rules
4+
from firewall.rule_list import get_all_rules
5+
56
"""
67
manages a collection of rule object.
78
"""
@@ -34,10 +35,5 @@ def check_packet(src_addr, dst_addr, src_port, dst_port):
3435
continue
3536
return False
3637
except Exception as e:
37-
print(f"[ERR] Error reading rules: {e}")
38+
print(f"[ERR] Error: {e}")
3839
return False
39-
40-
# print(check_packet("0.0.0.0", "127.0.0.0", 22, 22))
41-
# print(check_packet("192.168.2.100", "192.168.1.100", 22, 0))
42-
print(check_packet("192.168.2.100", "100", 80, 90))
43-
print(check_packet("192.168.3.100", "10.0.0.100", 0, 0))

firewall/packet.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import random
2+
import time
3+
import struct
4+
import socket
5+
import binascii
6+
7+
"""
8+
Represents a network packet with source and destination IP addresses
9+
"""
10+
11+
### Methods to handle protocols
12+
def mac_addr(mac_add_bytes):
13+
# convert the bytes to string
14+
return ':'.join(map('{:02x}'.format, mac_add_bytes)).upper()
15+
16+
17+
def ethernet_frame(data):
18+
dest_mac, src_mac, eth_protocol = struct.unpack('!6s6sH', data[:14])
19+
return mac_addr(dest_mac), mac_addr(src_mac), socket.htons(eth_protocol), data
20+
21+
22+
def ipv4_packet(ip_header):
23+
iph = struct.unpack('!BBHHHBBH4s4s', ip_header[:20])
24+
25+
version_ihl = iph[0]
26+
version = version_ihl >> 4
27+
28+
ih_len = (version_ihl & 0xF) * 4
29+
ttl = iph[5]
30+
protocol = iph[6]
31+
32+
s_addr = socket.inet_ntoa(iph[8])
33+
d_addr = socket.inet_ntoa(iph[9])
34+
35+
return s_addr, d_addr, protocol, ip_header[ih_len:]
36+
37+
38+
def icmp_packet(data):
39+
icmp_type, code, checksum = struct.unpack('!BBH', data[:4])
40+
if icmp_type == 0:
41+
type_status = "0 (Echo Reply)"
42+
elif icmp_type == 8:
43+
type_status = "8 (Echo Request)"
44+
return (icmp_type, type_status), code, checksum, data[4:]
45+
46+
47+
def tcp_packet(packet_buffer):
48+
tcp_raw_data = struct.unpack("!2s2s4s4s2s2s2s2s", packet_buffer)
49+
src_port = binary_to_ascii(tcp_raw_data[0])
50+
dst_port = binary_to_ascii(tcp_raw_data[1])
51+
52+
return int(src_port, 16), int(dst_port, 16)
53+
54+
def binary_to_ascii(binary_data):
55+
return binascii.hexlify(binary_data).decode("utf-8")
56+
57+
def udp_packet(data):
58+
src_port, dest_port, size = struct.unpack('!HH2xH', data)
59+
return src_port, dest_port, size, data[8:]
60+
61+
62+
def arp_packet(arp_header):
63+
arph = struct.unpack("!2s2s1s1s2s6s4s6s4s", arp_header)
64+
65+
# convert bytes to hex and then decode it as string
66+
protocol_size = binascii.hexlify(arph[3]).decode('utf-8')
67+
protocol_type = binascii.hexlify(arph[1]).decode('utf-8')
68+
69+
# get the mac address bytes and convert it to a string
70+
src_mac = mac_addr(arph[5])
71+
dst_mac = mac_addr(arph[7])
72+
73+
# get the ip bytes and convert it to an IP string
74+
src_ip = socket.inet_ntoa(arph[6])
75+
dst_ip = socket.inet_ntoa(arph[8])
76+
77+
return src_ip, dst_ip, src_mac, dst_mac, protocol_type, protocol_size
78+
79+
80+
def ipv6_packet(data):
81+
ipv6_first_word, payload_legth, protocol, hoplimit = struct.unpack(">IHBB", data[0:8])
82+
src_ip = socket.inet_ntop(socket.AF_INET6, data[8:24])
83+
dst_ip = socket.inet_ntop(socket.AF_INET6, data[24:40])
84+
85+
version = ipv6_first_word >> 28
86+
traffic_class = int(ipv6_first_word >> 16) & 4095
87+
flow_label = int(ipv6_first_word) & 65535
88+
89+
return src_ip, dst_ip, protocol, data[40:]
90+
File renamed without changes.

rule_list.py renamed to firewall/rule_list.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from rule import Rule
1+
from firewall.rule import Rule
22

33
__rules = [
44
Rule(False, "192.168.8.100", "0", "224.0.0.251", "0").get_rule(),

firewall/util.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import json, psutil
2+
3+
PROTOCOLS = {
4+
1: "ICMP",
5+
6: "TCP",
6+
17: "UDP"
7+
}
8+
9+
10+
def compare_rules(rule1, rule2):
11+
if str(rule1).strip() == str(rule2).strip():
12+
return True
13+
return False
14+
15+
def get_interfaces():
16+
addrs = psutil.net_if_addrs()
17+
try:
18+
interfaces = {}
19+
for key in addrs.keys():
20+
if key == "lo":
21+
continue
22+
else:
23+
interface_ip = addrs[key][0].broadcast.split(".")[0:3]
24+
interface_ip.append("0")
25+
interface_ip = ".".join(interface_ip)
26+
interfaces[key] = {"network":interface_ip,"ip":addrs[key][0].address, "netmask": addrs[key][0].netmask}
27+
return interfaces
28+
except AttributeError:
29+
return interfaces
30+
except Exception as e:
31+
print(f"Error getting interfaces : {e}")
32+
exit()
33+
34+
def pprint(string):
35+
print(json.dumps(string, indent=2))

main.py

Lines changed: 0 additions & 4 deletions
This file was deleted.

packet.py

Lines changed: 0 additions & 3 deletions
This file was deleted.

run.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import threading
2+
import time
3+
from firewall.core import bind_sockets
4+
from firewall.firewall import check_packet
5+
from firewall.util import get_interfaces
6+
7+
8+
##Test
9+
# print(check_packet("192.168.2.100", "100", 80, 90))
10+
# print(check_packet("192.168.3.100", "10.0.0.100", 0, 0))
11+
12+
interfaces = get_interfaces()
13+
14+
if len(interfaces.items()) < 4:
15+
print("Not enough interfaces")
16+
exit()
17+
18+
for key, val in interfaces.items():
19+
tr = threading.Thread(target=bind_sockets,args=([key, val],), name=key)
20+
tr.setDaemon(True)
21+
tr.start()
22+
print("FIREWALL IS RUNNING ")
23+
24+
try:
25+
while True:
26+
for _ in range(10):
27+
time.sleep(.2)
28+
except KeyboardInterrupt:
29+
print("\nEXITING FIREWALL")
30+
exit(1)

0 commit comments

Comments
 (0)