A standalone Linux network scanner with an interactive terminal interface, written in Python.
PyNetScan discovers devices, scans TCP ports, optionally probes UDP services, identifies hostnames and manufacturers, collects basic service information, and displays the results in a searchable TUI.
- Single standalone Python file
- Interactive terminal user interface
- Automatic local subnet detection
- Custom CIDR network scanning
- ARP discovery on directly connected networks
- ICMP and TCP fallback discovery
- Routed-network scanning
- Quick, Standard, Deep, Full, Discovery, and Custom scan profiles
- Custom TCP port lists and ranges
- Optional response-based UDP probes
- Concurrent host and port scanning
- Live scan progress, elapsed time, rate, and estimated completion
- Scan cancellation with partial results retained
- Reverse DNS, NetBIOS, mDNS, and SSDP discovery
- MAC address manufacturer lookup
- Basic operating-system estimation
- Estimated device-type classification
- Basic service and banner detection
- Security review indicators for potentially risky services
- Search, filtering, and sorting
- Individual-device rescanning
- Device aliases
- Full-subnet refresh and change comparison
- CSV and JSON exports
- Large-scan safety warnings
PyNetScan does not require a traditional installation. Download the single netscan.py file, make it executable, and run it.
wget https://raw.githubusercontent.com/flattery103/PyNetscan/main/netscan.py
chmod +x netscan.py
./netscan.pyYou can also run it through Python:
python3 netscan.pyPyNetScan is designed for Linux and requires:
- Python 3
- The Linux
ipcommand - A terminal with curses support
Most Linux distributions already include these components.
mDNS discovery uses the optional Python zeroconf module.
Install it with:
python3 -m pip install --user zeroconfPyNetScan will continue to work without zeroconf, but mDNS names and services will not be collected.
PyNetScan can run without root privileges.
Without root access, it falls back to ICMP and TCP-based discovery when raw ARP or raw ICMP sockets are unavailable.
For the best discovery results on a directly connected network, run it with sudo:
sudo ./netscan.pyRoot privileges improve ARP and ICMP discovery but are not required for normal TCP scanning.
Run PyNetScan without arguments to open the scan-profile menu:
./netscan.pyPyNetScan attempts to detect the local subnet automatically.
You can specify a network manually:
./netscan.py --network 192.168.1.0/24Find responsive devices without performing a general TCP port scan.
./netscan.py --profile discoverScan a smaller set of commonly used TCP services.
./netscan.py --profile quickScan a broad set of common TCP services. This is the recommended general-purpose profile.
./netscan.py --profile standardScan TCP ports 1 through 1024, selected higher-value ports, and collect basic service and banner information.
./netscan.py --profile deepScan all TCP ports from 1 through 65535.
./netscan.py --profile fullThe older --all-ports option is also supported:
./netscan.py --all-portsFull scans can take a considerable amount of time and generate substantial network traffic.
Scan a custom list of TCP ports or port ranges:
./netscan.py --ports 22,80,443,3389Ranges are supported:
./netscan.py --ports 1-1024,3389,8000-8100You can also explicitly select the Custom profile:
./netscan.py --profile custom --ports 22,80,443,8000-8100UDP scanning is optional and separate from TCP scanning.
sudo ./netscan.py \
--profile standard \
--udp-ports 53,123,137,161,1900UDP results are listed as responded, not definitively open.
A UDP service may be open but remain silent. A missing response can also indicate packet filtering. PyNetScan therefore does not label silent UDP ports as closed or open.
Scan the automatically detected subnet with the Standard profile:
./netscan.py --profile standardScan a specific network:
./netscan.py \
--network 192.168.50.0/24 \
--profile standardRun a fast scan:
./netscan.py \
--network 192.168.50.0/24 \
--profile quickRun a Deep scan with banner detection:
sudo ./netscan.py \
--network 192.168.50.0/24 \
--profile deepEnable banner detection with the Standard profile:
./netscan.py \
--profile standard \
--bannersScan selected TCP and UDP ports:
sudo ./netscan.py \
--network 192.168.50.0/24 \
--ports 22,53,80,443,445,3389 \
--udp-ports 53,123,161Use a specific network interface:
sudo ./netscan.py \
--network 192.168.50.0/24 \
--interface ens18Run the Standard profile without displaying the startup menu:
./netscan.py --no-menuAutomatically export JSON after the scan:
./netscan.py \
--profile standard \
--jsonSpecify an export filename:
./netscan.py \
--profile standard \
--output office-network-scanusage: netscan.py [-h] [-n NETWORK]
[--profile {discover,quick,standard,deep,full,custom}]
[-a] [-p PORTS] [--udp-ports UDP_PORTS]
[--interface INTERFACE] [--timeout TIMEOUT]
[--concurrency CONCURRENCY]
[--port-concurrency PORT_CONCURRENCY]
[--no-dns] [--no-mdns] [--no-ssdp]
[--banners] [--output OUTPUT] [--json]
[--force] [--no-menu] [--update-oui]
[--no-oui] [--version]
PyNetScan - standalone Linux network scanner with a TUI
options:
-h, --help
Show the help message and exit.
-n, --network NETWORK
Subnet in CIDR notation, such as 192.168.1.0/24.
--profile {discover,quick,standard,deep,full,custom}
Select a scan profile and skip the startup profile menu.
-a, --all-ports
Scan all TCP ports from 1 through 65535. Equivalent to
--profile full.
-p, --ports PORTS
Custom TCP ports or ranges, such as
22,80,443,8000-8100.
--udp-ports UDP_PORTS
Optional response-based UDP probes, such as
53,123,137,161,1900.
--interface INTERFACE
Network interface to use, such as ens18.
--timeout TIMEOUT
TCP and UDP timeout in seconds. Default: 0.5.
--concurrency CONCURRENCY
Maximum number of hosts processed concurrently.
Default: 200.
--port-concurrency PORT_CONCURRENCY
Maximum number of concurrent TCP connection attempts.
Default: 800.
--no-dns
Disable reverse DNS lookups.
--no-mdns
Disable mDNS discovery.
--no-ssdp
Disable SSDP discovery.
--banners
Enable basic service and banner detection.
--output OUTPUT
Preferred export filename or base path.
--json
Automatically export JSON after scanning.
--force
Skip large-scan confirmation.
--no-menu
Use the Standard profile without opening the startup menu.
--update-oui
Force an update of the IEEE manufacturer database.
--no-oui
Disable MAC address manufacturer lookups.
--version
Display the PyNetScan version and exit.
| Key | Action |
|---|---|
| Up / Down | Select a device |
| Page Up / Page Down | Move through the device list |
| Enter | Open details for the selected device |
/ |
Search the current results |
x |
Clear the current search |
f |
Cycle through result filters |
o |
Cycle through sort modes |
r |
Refresh the entire subnet |
s |
Rescan only the selected device |
a |
Add, change, or remove a device alias |
e |
Export results to CSV |
j |
Export results to JSON |
? |
Open the help screen |
q |
Quit |
| Key | Action |
|---|---|
| Up / Down | Scroll through details |
| Page Up / Page Down | Scroll one page |
| Home / End | Jump to the beginning or end |
p |
Ping the selected device |
s |
Rescan the selected device |
a |
Add, change, or remove its alias |
b |
Return to the device list |
q |
Quit |
Press q to cancel the active scan.
PyNetScan keeps the partial results gathered before cancellation so they can still be reviewed or exported.
Search can match:
- IP address
- Device name or alias
- MAC address
- Manufacturer
- Operating-system guess
- Device type
- Open port
Press f to cycle through these filters:
- All devices
- Devices with open or responding ports
- Devices with services requiring review
- Changed devices
- New devices
- Offline devices
Press o to cycle through available sort modes, including:
- IP address
- Device name
- Manufacturer
- Operating-system guess
- Number of detected ports
- Change status
Press r to scan the subnet again.
PyNetScan compares the new scan with the previous results and identifies:
- New devices
- Returning devices
- Devices that no longer respond
- Newly detected TCP ports
- TCP ports that are no longer detected
- Newly responding UDP ports
- UDP ports that stopped responding
- Changed names, MAC addresses, or device information
Result symbols include:
+ New device
* Changed device
- Offline device
! Service may require security review
Press a while a device is selected to add or change its alias.
Aliases are stored in:
~/.config/netscan/aliases.json
Entering a blank alias removes the saved alias.
PyNetScan uses the IEEE OUI database to identify device manufacturers from MAC addresses.
The local cache is stored in:
~/.cache/netscan/oui.json
The database is refreshed automatically when it becomes outdated.
Force an update with:
./netscan.py --update-ouiDisable manufacturer lookups with:
./netscan.py --no-ouiIf the database cannot be downloaded, PyNetScan continues scanning without manufacturer information.
Press e to export CSV or j to export JSON.
By default, PyNetScan creates timestamped filenames such as:
pynetscan_192.168.1.0-24_2026-07-16_143500.csv
pynetscan_192.168.1.0-24_2026-07-16_143500.json
CSV exports include fields such as:
- IP address
- Status and detected changes
- Device name and type
- MAC address and manufacturer
- Operating-system guess
- Discovery method
- TCP ports
- Responding UDP ports
- Security review items
- Service banners
- mDNS information
- SSDP information
JSON exports also include scan settings, timestamps, route information, warnings, and structured device details.
Use --output to choose a filename or base path:
./netscan.py \
--profile standard \
--output scans/office-networkThis produces the applicable extension:
scans/office-network.csv
scans/office-network.json
PyNetScan warns before starting scans that involve unusually large networks or very high numbers of connection attempts.
A Full scan of an entire /24 network can involve more than 16 million TCP connection attempts and may take a considerable amount of time.
Use --force only when you understand the size and impact of the requested scan:
sudo ./netscan.py \
--network 192.168.1.0/24 \
--profile full \
--forceConsider scanning a smaller address range or a targeted list of ports whenever possible.
PyNetScan provides useful network inventory information, but several results are estimates.
Operating-system identification is primarily based on the TTL from an ICMP response.
Firewalls, routers, virtualization, and customized network stacks can make these guesses inaccurate.
Devices discovered only through TCP are reported as having an unknown operating system unless a real ICMP TTL was received.
Device types are estimated using available information such as:
- Open ports
- Manufacturer
- Hostname
- mDNS services
- SSDP information
- Service banners
These classifications should be treated as likely device types rather than definitive identification.
PyNetScan highlights services that may deserve review, such as:
- Telnet
- FTP
- SMB
- RDP
- VNC
- Database listeners
- Redis
- MongoDB
- Elasticsearch
- Docker API
A review indicator does not prove that a vulnerability exists. It means the detected service may warrant additional inspection or access-control verification.
UDP results only show ports that returned a recognizable response. Silent UDP ports are not marked open or closed.
Only scan networks and systems that you own or have explicit permission to test.
Network scanning can trigger monitoring systems, generate significant traffic, or violate organizational policies when performed without authorization.
PyNetScan is licensed under the GNU General Public License version 3.
See the LICENSE file for the complete license text.