Python Scripting for Network Checks
Python scripts for performing network checks—specifically focusing on ping sweeps and handling network-related tasks. The overall style is modeled on how many penetration-test or security-oriented instructors break down topics into digestible parts. By the end of this tutorial, you should have a solid foundation for automating network checks with Python, understand the concepts behind them, and know how to extend them in various ways.
1. Setting Up a Python Environment
1.1 Installing Python on Linux
Most modern Linux distributions (such as Ubuntu, Debian, Fedora, Kali Linux, etc.) include Python preinstalled—often Python 3.x versions. If you want to verify your Python installation, open a terminal and type:
python3 --version
This command should display your Python version (e.g., 3.8.x, 3.9.x, or later). If you do not have Python 3 installed, you can install it with your distribution’s package manager. For example, on Debian/Ubuntu-based systems:
sudo apt-get update
sudo apt-get install python3
On Fedora or CentOS:
sudo dnf install python3
On Kali Linux, Python 3 is typically preinstalled. Just verify it as above.
Why Python 3?
Python 3 is the current standard for most modern scripts and frameworks, including pen-testing tools. Python 2 has been deprecated, so you’ll find better support and libraries for Python 3.
1.2 Virtual Environments (Optional but Recommended)
A virtual environment allows you to isolate project-specific dependencies, libraries, and even versions of Python packages, preventing conflicts with system-wide installations or other projects. To create and activate a virtual environment:
-
Create a new directory for your scripts (if you don’t have one yet):
mkdir network_scripts cd network_scripts
-
Create a virtual environment using the built-in
venv
module:python3 -m venv venv
-
Activate the virtual environment:
-
On Linux/macOS:
source venv/bin/activate
-
On Windows:
.\venv\Scripts\activate
-
Once activated, you’ll see a (venv)
prefix in your terminal prompt. Any Python packages you install now will stay local to this environment.
2. Writing a Simple Ping Script
2.1 Overview of the subprocess
Module
A common technique in Python network scripting is to use the subprocess
module to invoke system commands. For a basic ping check, you can run the system’s ping
command through Python, capture its output, and parse it.
Although there are Python libraries that mimic ping
(for instance, using ICMP packets directly via raw sockets), using system commands is straightforward and often sufficient for simpler tasks. But be aware that raw-socket-based solutions might require elevated privileges (e.g., sudo
) on some systems.
2.2 Basic Ping Script Structure
Below is a minimal example of a Python script that pings a single IP address. Let’s call it ping_script.py
.
#!/usr/bin/env python3
import subprocess
def ping_host(host):
"""Ping a host on Linux-based systems."""
try:
# '-c 1' sends one ping packet, '-W 1' sets a 1-second timeout
output = subprocess.check_output(
["ping", "-c", "1", "-W", "1", host],
stderr=subprocess.STDOUT # Capture standard output and errors
)
return True, output.decode()
except subprocess.CalledProcessError as e:
return False, e.output.decode()
def main():
host = "8.8.8.8" # Google Public DNS
success, output = ping_host(host)
if success:
print(f"[+] {host} is reachable!")
else:
print(f"[-] {host} is unreachable.")
print("Output:")
print(output)
if __name__ == "__main__":
main()
Explanation:
-
Shebang Line:
#!/usr/bin/env python3
ensures that the script runs with Python 3 when it’s executed directly (on Unix-like systems). -
Importing subprocess: This is the built-in Python module that allows us to spawn new processes, connect to their input/output/error pipes, and obtain their return codes.
-
ping_host Function:
-
Uses
subprocess.check_output
to call theping
command with a single packet (-c 1
) and a 1-second timeout (-W 1
). -
If the command returns successfully, it decodes the output from bytes to string.
-
If the command fails, it raises a
CalledProcessError
. We catch that error, parse its output, and returnFalse
.
-
-
Main Function:
-
Pings the Google DNS server (
8.8.8.8
). -
Prints a success or failure message.
-
Finally prints the entire output of the ping command to the terminal.
-
2.3 Looping Through a List of IP Addresses and Capturing Responses
Let’s modify our script to iterate over multiple IP addresses. Suppose we have a list of IPs in a file named hosts.txt
. Each line in hosts.txt
contains one IP address.
#!/usr/bin/env python3
import subprocess
def ping_host(host):
try:
output = subprocess.check_output(
["ping", "-c", "1", "-W", "1", host],
stderr=subprocess.STDOUT
)
return True, output.decode()
except subprocess.CalledProcessError as e:
return False, e.output.decode()
def main():
hosts_file = "hosts.txt" # Path to your file with IP addresses
reachable_hosts = []
unreachable_hosts = []
with open(hosts_file, "r") as f:
hosts = [line.strip() for line in f if line.strip()]
for host in hosts:
success, output = ping_host(host)
if success:
reachable_hosts.append(host)
print(f"[+] {host} is reachable!")
else:
unreachable_hosts.append(host)
print(f"[-] {host} is unreachable.")
# Logging output to a file:
with open("ping_results.log", "w") as log_file:
log_file.write("Reachable Hosts:\n")
for h in reachable_hosts:
log_file.write(f"{h}\n")
log_file.write("\nUnreachable Hosts:\n")
for h in unreachable_hosts:
log_file.write(f"{h}\n")
if __name__ == "__main__":
main()
Highlights:
-
We open the file
hosts.txt
and read each line as a potential host (stripping whitespace). -
We maintain two lists:
reachable_hosts
andunreachable_hosts
. -
We write our results to a
ping_results.log
file, separating reachable and unreachable hosts.
2.4 Logging Output to a Database (Optional)
Instead of a log file, you might want to store results in a more structured place, like a SQLite database. This is useful when building up a large-scale scanning framework. An example snippet:
import sqlite3
def store_results_to_db(reachable_hosts, unreachable_hosts):
# Connect to database (creates it if it doesn't exist)
conn = sqlite3.connect("network_results.db")
cursor = conn.cursor()
# Create table if not exists
cursor.execute("""
CREATE TABLE IF NOT EXISTS ping_results (
id INTEGER PRIMARY KEY AUTOINCREMENT,
host TEXT NOT NULL,
status TEXT NOT NULL
)
""")
for host in reachable_hosts:
cursor.execute("INSERT INTO ping_results (host, status) VALUES (?, ?)",
(host, 'reachable'))
for host in unreachable_hosts:
cursor.execute("INSERT INTO ping_results (host, status) VALUES (?, ?)",
(host, 'unreachable'))
conn.commit()
conn.close()
You can call store_results_to_db()
at the end of the script to record your results. It will create a table named ping_results
(if it doesn’t already exist) and store each host’s status for later queries or analysis.
3. Enhancements and Error Handling
3.1 Timeouts and Retries for Unreachable Hosts
In real-world network scenarios, sometimes a host may fail to respond on the first attempt but respond upon a subsequent try. You can implement a simple retry mechanism by looping a few times before marking the host as unreachable.
def ping_host_with_retry(host, retries=3, timeout=1):
cmd = ["ping", "-c", "1", "-W", str(timeout), host]
for i in range(retries):
try:
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
return True, output.decode()
except subprocess.CalledProcessError as e:
# Attempt again
pass
return False, f"Host {host} did not respond after {retries} attempts"
-
We have a
retries
parameter that defaults to3
. -
We keep sending a single ping each time. If the command still fails after the specified number of retries, we return
False
. -
This approach helps mitigate issues with network congestion or transient drops.
3.2 Parsing Ping Results for Statistics (Latency, Packet Loss)
Often you need to know more than whether a host is reachable. Latency (round-trip time) or packet-loss data can be important. The raw output of ping
typically includes lines like:
--- 8.8.8.8 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 20.386/20.386/20.386/0.000 ms
Here’s how you might parse out the average latency:
import re
def parse_ping_output(output):
# This regex attempts to capture the min, avg, max, and mdev from the rtt line
match = re.search(r"rtt min/avg/max/mdev = ([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+)", output)
if match:
min_rtt, avg_rtt, max_rtt, mdev_rtt = match.groups()
return {
"min_rtt": float(min_rtt),
"avg_rtt": float(avg_rtt),
"max_rtt": float(max_rtt),
"mdev_rtt": float(mdev_rtt)
}
else:
return None
Then, integrate this function:
success, output = ping_host_with_retry(host)
if success:
stats = parse_ping_output(output)
if stats:
print(f"[+] {host} is reachable with avg latency: {stats['avg_rtt']} ms")
else:
print(f"[+] {host} is reachable (but no RTT stats parsed).")
3.3 Integrating Email or Slack Notifications
It’s often helpful to get immediate notifications about network changes or failures. Many DevOps or secops environments use Slack or email for automated alerts. Python supports sending email via the smtplib
module, and Slack can be integrated via their API or through a webhook URL.
Email Example (SMTP)
import smtplib
from email.mime.text import MIMEText
def send_email_notification(subject, body, sender, recipients):
msg = MIMEText(body)
msg["Subject"] = subject
msg["From"] = sender
msg["To"] = ", ".join(recipients)
# For local SMTP server or adjust for your email provider
with smtplib.SMTP("localhost") as server:
server.sendmail(sender, recipients, msg.as_string())
# Usage:
if not success:
send_email_notification(
subject="Host Down!",
body=f"The host {host} appears to be unreachable.",
sender="[email protected]",
recipients=["[email protected]"]
)
Slack Webhook Example
Slack provides a unique webhook URL where you can POST
a JSON payload containing your message. You can use Python’s requests
module:
import requests
import json
def send_slack_notification(webhook_url, message):
payload = {"text": message}
headers = {"Content-Type": "application/json"}
requests.post(webhook_url, data=json.dumps(payload), headers=headers)
# Usage:
if not success:
send_slack_notification(
"https://hooks.slack.com/services/...",
f"Host {host} is unreachable!"
)
Suggested Learning Path
1. Practice Python Scripting
-
Automate repetitive tasks: If you find yourself running the same commands in a terminal many times—like scanning networks, doing repeated ping sweeps—automate them with Python. Build scripts that iterate over large lists of targets or subnets.
-
Focus on building robust error handling: The network environment can be unpredictable. Host discovery might be blocked by firewalls, or have partial packet loss. Make your scripts resilient to these realities.
2. Experiment with Raw Sockets (Advanced)
-
Explore libraries like
scapy
orraw-ping
, or craft your own raw ICMP packets. This approach gives you deeper insight into how protocols function, and can circumvent certain limitations of the systemping
command. -
Be aware that this often requires privileged (root) access on many systems.
3. Incorporate Other Protocol Checks
-
After you master ping sweeps, move on to scripts that check ports (TCP/UDP scanning), service banners, or enumerations (e.g., SMB shares, HTTP endpoints).
-
Tools like
nmap
are standard in penetration testing, but building your own Python-based scanners helps you learn fundamentals and can be integrated into custom workflows.
4. Logging, Monitoring, and Reporting
-
Develop a habit of storing all script outputs in a structured format: text files, CSV, or databases. This facilitates further analysis or correlation with other data sources.
-
Integrate a logging library (like the built-in
logging
module) to handle different verbosity levels (INFO, WARNING, ERROR, etc.). It’s much more flexible than simply printing messages.
5. Build on Pentesting & Security Knowledge
-
As you improve in scripting, blend your scripts with other tools used in pentesting, e.g., analyzing results from
nmap
, searching for vulnerabilities with Python-based scanners. -
Learn about OSINT techniques, so you can automate data gathering from external sources.
6. Version Control and Collaboration
- Place your scripts in a Git repository, even if it’s for personal use. This helps you track changes, revert to stable versions, and collaborate with peers.
7. Continuous Learning
-
Python has extensive libraries for everything from cryptographic functions to web frameworks. Expanding your repertoire of libraries will enable you to build more complex scripts, from vulnerability checks to elaborate data analysis or automation pipelines.
-
Follow security communities (forums, local meetups, or online groups) to learn about advanced topics and best practices.
Putting It All Together
Below is a more complete example that combines multiple concepts: using a retry strategy, parsing ping results for latency, and logging to both a file and Slack. While slightly longer, it captures many of the recommended best practices for a functional, real-world script in a pentester’s toolkit:
#!/usr/bin/env python3
import subprocess
import re
import requests
import json
import logging
# Configure logging
logging.basicConfig(
filename="ping_sweeps.log",
filemode="a",
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s"
)
def ping_host_with_retry(host, retries=3, timeout=1):
cmd = ["ping", "-c", "1", "-W", str(timeout), host]
for i in range(retries):
try:
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
return True, output.decode()
except subprocess.CalledProcessError:
pass
return False, ""
def parse_ping_output(output):
match = re.search(r"rtt min/avg/max/mdev = ([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+)", output)
if match:
min_rtt, avg_rtt, max_rtt, mdev_rtt = match.groups()
return {
"min_rtt": float(min_rtt),
"avg_rtt": float(avg_rtt),
"max_rtt": float(max_rtt),
"mdev_rtt": float(mdev_rtt)
}
else:
return None
def send_slack_notification(webhook_url, message):
payload = {"text": message}
headers = {"Content-Type": "application/json"}
try:
response = requests.post(webhook_url, data=json.dumps(payload), headers=headers)
response.raise_for_status() # Raises an exception if the POST is unsuccessful
except Exception as e:
logging.error(f"Error sending Slack notification: {e}")
def main():
hosts_file = "hosts.txt"
slack_webhook_url = "https://hooks.slack.com/services/..."
reachable_hosts = []
unreachable_hosts = []
# Read hosts
with open(hosts_file, "r") as f:
hosts = [line.strip() for line in f if line.strip()]
for host in hosts:
success, output = ping_host_with_retry(host, retries=3, timeout=1)
if success:
stats = parse_ping_output(output)
if stats:
logging.info(f"{host} is reachable (avg latency: {stats['avg_rtt']} ms)")
print(f"[+] {host} reachable - avg latency: {stats['avg_rtt']} ms")
else:
logging.info(f"{host} is reachable (no RTT data parsed).")
print(f"[+] {host} is reachable, no RTT data parsed.")
reachable_hosts.append(host)
else:
logging.warning(f"{host} is unreachable after retries.")
print(f"[-] {host} unreachable.")
unreachable_hosts.append(host)
# Send Slack notification for unreachable hosts
if unreachable_hosts:
msg = "The following hosts are unreachable:\n" + "\n".join(unreachable_hosts)
send_slack_notification(slack_webhook_url, msg)
if __name__ == "__main__":
main()
Key Takeaways
-
Solid Foundations: Start with a simple Python script that calls
ping
for a single host, expand to multiple hosts, add logging, and handle errors gracefully. -
Extensibility: Add features such as retries, result parsing, and notifications. Over time, you can integrate data storage (SQLite, MySQL, or even cloud-based solutions).
-
Security Perspective: As a pentester, automating tasks like network sweeps with Python can save enormous amounts of time and help you uncover interesting patterns or vulnerabilities faster.
Conclusion
With consistent practice and iterative improvements, your Python scripts will become invaluable tools in both daily IT operations and pentesting engagements. By starting with something as straightforward as a ping sweep and layering on advanced features—like robust error handling, retries, structured logging, latency analysis, and alerts—you’ll find yourself not just performing checks but truly monitoring and understanding network behavior.
From here, branch out into new areas: custom port scanners, service detection, vulnerability scanning scripts, or even orchestration of existing tools like nmap
. Each script you write cements your knowledge of how networks, protocols, and systems interact.
Happy scripting, and may your pings always return “reachable!”
Test Your Knowledge
What would be the best reason to use SQLite instead of plain .log files in this context?
In a network with firewalls, ICMP requests are often blocked. What’s the effect on your ping script?
What is the primary reason to parse the ping output for average latency (avg_rtt)?
Which part of the code sends a Slack alert?
What is the advantage of using a with open(...) as f: statement in Python?