Extract Subnet Addresses with Python
Extracting and analyzing the IP addresses in a subnet are essential tasks for network administrators, security specialists, and developers of monitoring tools. In this guide, we will explore how to use Python to efficiently extract all the addresses in a subnet, filter them by various criteria, and analyze them for a range of practical applications.
Why extract subnet addresses?
Extracting the IP addresses of a subnet is useful for:
- Network inventory: Catalog all available addresses
- Security auditing: Check which addresses are active or in use
- Capacity planning: Assess subnet utilization
- Network monitoring: Systematically monitor all addresses
- Device configuration: Prepare configuration scripts for network devices
The ipaddress module to the rescue
Python provides a powerful module called ipaddress that greatly simplifies working with IP addresses and subnets. Let’s import it to get started:
import ipaddress
Extracting all addresses in a subnet
Basic technique
The simplest way to extract all the addresses in a subnet is to iterate over the network object:
def extract_all_addresses(cidr):
"""Extrait toutes les adresses IP d'un sous-réseau donné en notation CIDR."""
network = ipaddress.ip_network(cidr)
return list(network.hosts())
# Exemple pour un petit sous-réseau
subnet = "192.168.1.0/29"
addresses = extract_all_addresses(subnet)
print(f"Le sous-réseau {subnet} contient {len(addresses)} adresses utilisables:")
for addr in addresses:
print(f" - {addr}")
Output:
Le sous-réseau 192.168.1.0/29 contient 6 adresses utilisables:
- 192.168.1.1
- 192.168.1.2
- 192.168.1.3
- 192.168.1.4
- 192.168.1.5
- 192.168.1.6
Optimization for large subnets
For large subnets, it’s better to avoid storing all the addresses in memory at once. Let’s use generators:
def address_generator(cidr):
"""Générateur qui produit les adresses IP d'un sous-réseau sans les charger toutes en mémoire."""
network = ipaddress.ip_network(cidr)
for host in network.hosts():
yield host
# Utilisation pour un sous-réseau plus grand
subnet = "10.0.0.0/24" # 254 adresses utilisables
for i, addr in enumerate(address_generator(subnet)):
if i < 5: # Afficher seulement les 5 premières
print(f"Adresse {i+1}: {addr}")
elif i == 5:
print("...")
Extracting subsets of addresses
Addresses at the beginning and end of the subnet
def get_subnet_boundaries(cidr, count=5):
"""Retourne les premières et dernières adresses d'un sous-réseau."""
network = ipaddress.ip_network(cidr)
hosts = list(network.hosts())
if len(hosts) <= count * 2:
return hosts
return hosts[:count] + hosts[-count:]
# Exemple
subnet = "172.16.0.0/20"
boundaries = get_subnet_boundaries(subnet)
print(f"Premières adresses de {subnet}:")
for addr in boundaries[:5]:
print(f" - {addr}")
print(f"Dernières adresses de {subnet}:")
for addr in boundaries[-5:]:
print(f" - {addr}")
Extracting addresses by range
def extract_address_range(cidr, start_idx, end_idx):
"""Extrait une plage spécifique d'adresses d'un sous-réseau."""
network = ipaddress.ip_network(cidr)
hosts = list(network.hosts())
if start_idx >= len(hosts) or end_idx > len(hosts):
raise ValueError("Indices hors limites")
return hosts[start_idx:end_idx]
# Exemple: obtenir les adresses 10 à 20 d'un sous-réseau
subnet = "192.168.2.0/24"
middle_range = extract_address_range(subnet, 10, 20)
print(f"Adresses 10 à 20 de {subnet}:")
for addr in middle_range:
print(f" - {addr}")
Analyzing and filtering addresses
Filtering by address pattern
def filter_by_pattern(cidr, pattern):
"""Filtre les adresses d'un sous-réseau selon un motif."""
network = ipaddress.ip_network(cidr)
pattern_str = str(pattern)
result = []
for addr in network.hosts():
if pattern_str in str(addr):
result.append(addr)
return result
# Exemple: trouver toutes les adresses contenant "100"
subnet = "192.168.0.0/22"
matching_addrs = filter_by_pattern(subnet, "100")
print(f"Adresses contenant '100' dans {subnet}:")
for addr in matching_addrs:
print(f" - {addr}")
Identifying even and odd addresses
def categorize_addresses(cidr):
"""Catégorise les adresses en paires et impaires."""
network = ipaddress.ip_network(cidr)
even = []
odd = []
for host in network.hosts():
if int(host) % 2 == 0:
even.append(host)
else:
odd.append(host)
return {
'even': even,
'odd': odd
}
# Exemple
subnet = "192.168.1.16/28"
categories = categorize_addresses(subnet)
print(f"Adresses paires de {subnet}: {len(categories['even'])}")
print(f"Adresses impaires de {subnet}: {len(categories['odd'])}")
Practical applications
Scanning a subnet for active hosts
import subprocess
import concurrent.futures
import platform
def ping(ip):
"""Vérifie si une adresse IP répond au ping."""
param = "-n" if platform.system().lower() == "windows" else "-c"
command = ["ping", param, "1", str(ip)]
return subprocess.call(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0
def scan_subnet(cidr, max_workers=10):
"""Scanne un sous-réseau pour trouver les hôtes actifs."""
network = ipaddress.ip_network(cidr)
active_hosts = []
with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
# Créer un dictionnaire de futures
future_to_ip = {executor.submit(ping, ip): ip for ip in network.hosts()}
for future in concurrent.futures.as_completed(future_to_ip):
ip = future_to_ip[future]
try:
if future.result():
active_hosts.append(ip)
print(f"Hôte actif trouvé: {ip}")
except Exception as exc:
print(f"{ip} a généré une exception: {exc}")
return active_hosts
# Exemple d'utilisation (attention: peut prendre du temps selon la taille du sous-réseau)
# active_hosts = scan_subnet("192.168.1.0/24")
# print(f"Total des hôtes actifs: {len(active_hosts)}")
Generating configuration files for network devices
def generate_dhcp_config(cidr, domain="example.com"):
"""Génère une configuration DHCP pour un sous-réseau."""
network = ipaddress.ip_network(cidr)
config = [
f"subnet {network.network_address} netmask {network.netmask} {{",
f" option domain-name \"{domain}\";",
f" option domain-name-servers 8.8.8.8, 8.8.4.4;",
f" option routers {next(network.hosts())};",
f" option broadcast-address {network.broadcast_address};",
f" default-lease-time 600;",
f" max-lease-time 7200;",
"}",
"",
"# Réservations statiques"
]
# Ajouter quelques réservations d'exemple
for i, host in enumerate(list(network.hosts())[:5]):
config.append(f"host device{i+1} {{")
config.append(f" hardware ethernet 00:11:22:33:44:{i+1:02x};")
config.append(f" fixed-address {host};")
config.append("}")
return "\n".join(config)
# Exemple
subnet = "192.168.5.0/24"
dhcp_config = generate_dhcp_config(subnet)
print("Configuration DHCP générée:")
print(dhcp_config)
Handling IPv6
The ipaddress module works just as well with IPv6 as with IPv4:
def compare_ipv4_ipv6_extraction(ipv4_cidr, ipv6_cidr, count=5):
"""Compare l'extraction d'adresses IPv4 et IPv6."""
ipv4_net = ipaddress.ip_network(ipv4_cidr)
ipv6_net = ipaddress.ip_network(ipv6_cidr)
ipv4_hosts = list(ipv4_net.hosts())[:count]
ipv6_hosts = list(ipv6_net.hosts())[:count]
print(f"Premiers {count} hôtes IPv4 dans {ipv4_cidr}:")
for host in ipv4_hosts:
print(f" - {host}")
print(f"\nPremiers {count} hôtes IPv6 dans {ipv6_cidr}:")
for host in ipv6_hosts:
print(f" - {host}")
print(f"\nTaille totale IPv4: {ipv4_net.num_addresses} adresses")
print(f"Taille totale IPv6: {ipv6_net.num_addresses} adresses")
# Exemple
compare_ipv4_ipv6_extraction("192.168.1.0/24", "2001:db8::/120")
Performance optimization
For very large subnets (such as /16 or larger in IPv4, or most IPv6 subnets), full enumeration can be inefficient or impossible. Here are a few optimized approaches:
def sample_large_subnet(cidr, sample_size=1000):
"""Échantillonne un grand sous-réseau de manière statistique."""
network = ipaddress.ip_network(cidr)
total_addresses = network.num_addresses
# Si le réseau a des adresses de broadcast/réseau, les exclure du calcul
if network.version == 4:
host_addresses = total_addresses - 2
else: # IPv6 n'a pas ces restrictions
host_addresses = total_addresses
if host_addresses <= sample_size:
return list(network.hosts())
# Calculer des points d'échantillonnage statistiques
step = host_addresses // sample_size
result = []
current = int(network.network_address) + (1 if network.version == 4 else 0)
for _ in range(sample_size):
addr = ipaddress.ip_address(current)
if addr in network:
result.append(addr)
current += step
return result
# Exemple avec un grand sous-réseau
large_subnet = "10.0.0.0/16" # 65,534 adresses utilisables
samples = sample_large_subnet(large_subnet, 10)
print(f"Échantillon de 10 adresses du sous-réseau {large_subnet}:")
for addr in samples:
print(f" - {addr}")
Key takeaways
- The
ipaddressmodule is essential for working efficiently with IP addresses in Python - For small subnets, full enumeration is simple and efficient
- For large subnets, favor generators and sampling
- Extraction techniques can be adapted for both IPv4 and IPv6
- Practical applications include network scanning, device configuration, and address inventory
Conclusion
Extracting IP addresses from a subnet is a fundamental skill for any network administrator or developer working with network applications. With its ipaddress module, Python provides powerful tools to automate these tasks and integrate them into larger network management, monitoring, or security systems.
By mastering these techniques, you can build robust solutions for managing your network infrastructure, whether you’re dealing with small local networks or large enterprise deployments.
About InSkillCoach
Training and technology expert
Coach specializing in advanced technologies and AI, backed by GNeurone Inc.
Certifications:
- AWS Certified Solutions Architect – Professional
- Google Cloud certifications
- Microsoft Certified: DevOps Engineer Expert
- Certified Kubernetes Administrator (CKA)
- CompTIA Security+
Comments
Comments are powered by GitHub Discussions
Sign in with GitHub to join the discussion