Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,36 @@ This firewall uses a **threshold-based detection approach**:
python3 test_attacks.py 127.0.0.1
```

## 🏃 Running on Windows

This program uses `netsh` to manage firewall rules, which requires administrative privileges.

1. **Open as Administrator:** Right-click on **Command Prompt** or **Windows PowerShell** and select **"Run as administrator"**.

2. **Navigate to Folder:** Change to the project directory:
```sh
cd path\to\simple_firewall
```

3. **(Optional) Activate Virtual Environment:** If you use a virtual environment:
```sh
.\venv\Scripts\activate
```

4. **Install Requirements:** Install the main requirements and the Windows-specific ones:
```sh
pip install -r requirements.txt
pip install -r requirements-windows.txt
```

5. **Run the Program:**
```sh
python main.py -i "Your Interface Name"

# Example:
python main.py -i "Ethernet"
```

## Usage

### Basic Commands
Expand Down
8 changes: 8 additions & 0 deletions requirements-windows.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# This file lists Python packages required *only* for running
# this project on Windows, which are not in the main requirements.txt.

# psutil (used for Windows-specific interface detection) is
# already listed in the main requirements.txt as a core
# dependency for all platforms.

# Future Windows-only dependencies can be added here.
34 changes: 27 additions & 7 deletions src/firewall/blocking.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def _unblock_ip_macos(self, ip: str) -> bool:
return result.returncode == 0

def _block_ip_windows(self, ip: str) -> bool:
"""Block IP using Windows Firewall"""
"""Block IP using Windows Firewall (netsh)"""
rule_name = f"SimpleFirewall_Block_{ip.replace('.', '_')}"
cmd = [
'netsh', 'advfirewall', 'firewall', 'add', 'rule',
Expand All @@ -166,18 +166,38 @@ def _block_ip_windows(self, ip: str) -> bool:
'action=block',
f'remoteip={ip}'
]
result = subprocess.run(cmd, capture_output=True, text=True)
return result.returncode == 0

try:
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
self.logger.debug(f"netsh add rule stdout: {result.stdout.strip()}")
return True
else:
self.logger.error(f"netsh add rule failed: rc={result.returncode} stdout={result.stdout.strip()} stderr={result.stderr.strip()}")
return False
except Exception as e:
self.logger.error(f"Exception when running netsh add rule: {e}")
return False

def _unblock_ip_windows(self, ip: str) -> bool:
"""Unblock IP using Windows Firewall"""
"""Unblock IP using Windows Firewall (netsh)"""
rule_name = f"SimpleFirewall_Block_{ip.replace('.', '_')}"
cmd = [
'netsh', 'advfirewall', 'firewall', 'delete', 'rule',
f'name={rule_name}'
]
result = subprocess.run(cmd, capture_output=True, text=True)
return result.returncode == 0
try:
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
self.logger.debug(f"netsh delete rule stdout: {result.stdout.strip()}")
return True
else:
# Sometimes netsh returns 1 when rule not found; log and return False
self.logger.error(f"netsh delete rule failed: rc={result.returncode} stdout={result.stdout.strip()} stderr={result.stderr.strip()}")
return False
except Exception as e:
self.logger.error(f"Exception when running netsh delete rule: {e}")
return False


def get_blocked_ips(self) -> Dict[str, str]:
"""Get currently blocked IPs with their block times"""
Expand Down
103 changes: 65 additions & 38 deletions src/network/interface.py
Original file line number Diff line number Diff line change
@@ -1,88 +1,115 @@
# src/network/interface.py
"""Network interface detection and management"""

import netifaces
import platform
from typing import Optional, List

# Use psutil on Windows to get friendly interface names
try:
import psutil
except Exception:
psutil = None


class NetworkInterface:
"""Handles network interface detection and management"""

def __init__(self, interface: str = None):
self._platform = platform.system().lower()
self.interface = interface or self._get_default_interface()

def _get_default_interface(self) -> str:
"""Get the default network interface"""
"""Get the default network interface (Windows-friendly names when available)"""
try:
interfaces = netifaces.interfaces()

# Filter out loopback and virtual interfaces
physical_interfaces = [
iface for iface in interfaces
if not iface.startswith(('lo', 'docker', 'veth', 'br-'))
]

# Prefer ethernet interfaces, then wireless
for iface in physical_interfaces:
if iface.startswith(('eth', 'en')):
return iface

for iface in physical_interfaces:
if iface.startswith(('wl', 'wlan')):
return iface

# Fallback to first available interface
return physical_interfaces[0] if physical_interfaces else 'eth0'

# On Windows prefer psutil names (friendly names)
if self._platform == 'windows' and psutil is not None:
# psutil returns a mapping of friendly names
names = [name for name in psutil.net_if_addrs().keys()
if not name.startswith(('Loopback', 'loopback', 'vEthernet'))]
# Prefer "Ethernet" or "Wi-Fi" heuristically
for n in names:
if n.lower().startswith('ether') or 'ethernet' in n.lower():
return n
for n in names:
if 'wi' in n.lower() or 'wifi' in n.lower() or 'wi-fi' in n.lower():
return n
return names[0] if names else 'Ethernet'
else:
interfaces = netifaces.interfaces()

# Filter out loopback and virtual interfaces
physical_interfaces = [
iface for iface in interfaces
if not iface.startswith(('lo', 'docker', 'veth', 'br-'))
]

# Prefer ethernet interfaces, then wireless
for iface in physical_interfaces:
if iface.startswith(('eth', 'en')):
return iface

for iface in physical_interfaces:
if iface.startswith(('wl', 'wlan')):
return iface

# Fallback to first available interface
return physical_interfaces[0] if physical_interfaces else 'eth0'

except Exception:
return 'eth0'
return ''

def get_interface_info(self) -> dict:
"""Get information about the current interface"""
try:
addrs = netifaces.ifaddresses(self.interface)
addrs = netifaces.ifaddresses(self.interface) if self.interface else {}
info = {
'name': self.interface,
'ipv4': [],
'ipv6': [],
'mac': None
}

# Get IPv4 addresses
if netifaces.AF_INET in addrs:
if addrs and netifaces.AF_INET in addrs:
for addr in addrs[netifaces.AF_INET]:
info['ipv4'].append({
'addr': addr.get('addr'),
'netmask': addr.get('netmask'),
'broadcast': addr.get('broadcast')
})

# Get IPv6 addresses
if netifaces.AF_INET6 in addrs:
if addrs and netifaces.AF_INET6 in addrs:
for addr in addrs[netifaces.AF_INET6]:
info['ipv6'].append({
'addr': addr.get('addr'),
'netmask': addr.get('netmask')
})

# Get MAC address
if netifaces.AF_LINK in addrs:
if addrs and netifaces.AF_LINK in addrs:
info['mac'] = addrs[netifaces.AF_LINK][0].get('addr')

return info

except Exception as e:
return {'name': self.interface, 'error': str(e)}

def list_all_interfaces(self) -> List[str]:
"""List all available network interfaces"""
"""List all available network interfaces (friendly names preferred on Windows)"""
try:
if self._platform == 'windows' and psutil is not None:
return list(psutil.net_if_addrs().keys())
return netifaces.interfaces()
except Exception:
return []

def is_interface_up(self) -> bool:
"""Check if the interface is up and running"""
try:
if self._platform == 'windows' and psutil is not None:
return self.interface in psutil.net_if_stats()
return self.interface in netifaces.interfaces()
except Exception:
return False
return False
Loading