Python Scripting for Network Checks

Use Python for efficient network checks like ping sweeps, retries, logging, and Slack/email alerts.

Krit Karnjanakrajang

Krit Karnjanakrajang

|
Krit Karnjanakrajang

Krit Karnjanakrajang

Founder

A visionary leader with a passion for technology, He expertise in Business Analyst and AI integration helps the team deliver exceptional results on time, he leads the team towards transformative results.

Learn Python Scripting for Network Checks

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.

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:

  1. Create a new directory for your scripts (if you don’t have one yet):

    mkdir network_scripts cd network_scripts
  2. Create a virtual environment using the built-in venv module:

    python3 -m venv venv
  3. 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:
  1. Shebang Line: #!/usr/bin/env python3 ensures that the script runs with Python 3 when it’s executed directly (on Unix-like systems).

  2. 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.

  3. ping_host Function:

    • Uses subprocess.check_output to call the ping 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 return False.

  4. 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 and unreachable_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 to 3.

  • 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 or raw-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 system ping 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

  1. 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.

  2. Extensibility: Add features such as retries, result parsing, and notifications. Over time, you can integrate data storage (SQLite, MySQL, or even cloud-based solutions).

  3. 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?

Latest Posts

See all posts