Calcular el rango de direcciones de una red CIDR en Python
Aprende a determinar el número de direcciones IP y el rango completo de una red CIDR con Python. Tutorial detallado que utiliza el módulo ipaddress con ejemplos prácticos.
InSkillCoach
Calcular el rango de direcciones de una red CIDR en Python
CIDR (Classless Inter-Domain Routing) es un método de asignación de direcciones IP y de enrutamiento IP que ha sustituido al sistema anterior de clases de direcciones de red. Comprender cómo calcular el rango de direcciones IP de una red CIDR es esencial para la gestión de redes, la seguridad informática y la administración de sistemas.
En esta guía exploraremos cómo utilizar Python para determinar de forma rápida y eficiente el rango de direcciones IP de una red CIDR.
¿Qué es la notación CIDR?
La notación CIDR se compone de dos partes:
- Una dirección IP (IPv4 o IPv6)
- Un sufijo que indica el tamaño del prefijo de red (la máscara de subred)
Por ejemplo, 192.168.1.0/24 indica:
192.168.1.0es la dirección de red/24significa que los 24 primeros bits son fijos (lo que corresponde a una máscara de subred 255.255.255.0)- Esta red comprende todas las direcciones desde
192.168.1.0hasta192.168.1.255
Uso del módulo ipaddress
Python incluye un potente módulo llamado ipaddress en su biblioteca estándar (desde Python 3.3), que facilita enormemente los cálculos relacionados con direcciones IP y redes.
Instalación
No es necesaria ninguna instalación, ya que el módulo ipaddress forma parte de la biblioteca estándar de Python:
import ipaddress
Determinar el rango de direcciones de una red CIDR
Así se calcula el rango de direcciones de una red CIDR:
import ipaddress
def get_cidr_range(cidr_notation):
"""
Calcula el rango de direcciones IP de una red CIDR.
Args:
cidr_notation (str): Notación CIDR (ej: '192.168.1.0/24')
Returns:
dict: Diccionario con la información sobre la red
"""
try:
network = ipaddress.ip_network(cidr_notation, strict=False)
# Recopilación de la información de red
network_info = {
'network_address': str(network.network_address),
'broadcast_address': str(network.broadcast_address) if hasattr(network, 'broadcast_address') else None,
'netmask': str(network.netmask),
'prefix_length': network.prefixlen,
'num_addresses': network.num_addresses,
'first_usable_address': str(next(network.hosts())) if network.num_addresses > 1 else None,
'last_usable_address': str(list(network.hosts())[-1]) if network.num_addresses > 1 else None,
'address_range': f"{next(network.hosts())} - {list(network.hosts())[-1]}" if network.num_addresses > 1 else None,
'is_private': network.is_private,
'version': 'IPv4' if network.version == 4 else 'IPv6'
}
return network_info
except ValueError as e:
return {'error': str(e)}
# Ejemplos de uso
cidr_examples = [
'192.168.1.0/24', # Red local clásica (/24)
'10.0.0.0/8', # Red privada de clase A
'172.16.0.0/12', # Red privada de clase B
'8.8.8.0/24', # Red pública (que contiene Google DNS)
'192.168.0.1/32', # Una sola dirección IP
'2001:db8::/32', # Red IPv6
]
for cidr in cidr_examples:
print(f"\nInformations pour le réseau {cidr}:")
network_info = get_cidr_range(cidr)
if 'error' in network_info:
print(f"Erreur: {network_info['error']}")
continue
# Visualización de la información de red
for key, value in network_info.items():
print(f"- {key}: {value}")
Resultado para una red IPv4 (/24)
Informations pour le réseau 192.168.1.0/24:
- network_address: 192.168.1.0
- broadcast_address: 192.168.1.255
- netmask: 255.255.255.0
- prefix_length: 24
- num_addresses: 256
- first_usable_address: 192.168.1.1
- last_usable_address: 192.168.1.254
- address_range: 192.168.1.1 - 192.168.1.254
- is_private: True
- version: IPv4
Comprender el número de direcciones IP en una red CIDR
El número de direcciones IP en una red CIDR se puede calcular con la fórmula: 2^(32-prefijo) para IPv4 y 2^(128-prefijo) para IPv6.
Esta es una función que explica esta relación:
def explain_cidr_size(prefix_length, ip_version=4):
"""
Explica el tamaño de una red CIDR y calcula el número de direcciones IP.
Args:
prefix_length (int): Longitud del prefijo CIDR (ej: 24 en /24)
ip_version (int): Versión IP (4 o 6)
Returns:
dict: Explicación y cálculos
"""
if ip_version == 4:
total_bits = 32
max_prefix = 32
else: # IPv6
total_bits = 128
max_prefix = 128
if prefix_length < 0 or prefix_length > max_prefix:
return {'error': f'Préfixe invalide pour IPv{ip_version}'}
host_bits = total_bits - prefix_length
num_addresses = 2 ** host_bits
if ip_version == 4:
# Para IPv4, se cuentan generalmente las direcciones utilizables
# (excluyendo la dirección de red y la de broadcast)
usable_addresses = max(0, num_addresses - 2) if prefix_length < 31 else num_addresses
else:
# Para IPv6, generalmente no se resta ninguna dirección
usable_addresses = num_addresses
explanation = {
'prefix_length': prefix_length,
'ip_version': f'IPv{ip_version}',
'total_bits': total_bits,
'network_bits': prefix_length,
'host_bits': host_bits,
'formula': f'2^{host_bits}',
'total_addresses': num_addresses,
'usable_addresses': usable_addresses
}
return explanation
# Tabla comparativa de diferentes prefijos CIDR
print("Tableau comparatif des tailles de réseaux CIDR pour IPv4:")
print("-" * 80)
print(f"{'Préfixe':<10}{'Masque':<15}{'Bits hôte':<15}{'Nombre d\'adresses':<20}{'Adresses utilisables':<20}")
print("-" * 80)
for prefix in [8, 16, 20, 24, 27, 28, 29, 30, 31, 32]:
info = explain_cidr_size(prefix)
# Cálculo de la máscara de subred
if prefix == 32:
mask = "255.255.255.255"
else:
mask_int = (0xffffffff >> (32 - prefix)) << (32 - prefix)
mask = '.'.join([str((mask_int >> i) & 0xff) for i in [24, 16, 8, 0]])
print(f"/{prefix:<9}{mask:<15}{info['host_bits']:<15}{info['total_addresses']:<20}{info['usable_addresses']:<20}")
Resultado de la tabla comparativa
Tableau comparatif des tailles de réseaux CIDR pour IPv4:
--------------------------------------------------------------------------------
Préfixe Masque Bits hôte Nombre d'adresses Adresses utilisables
--------------------------------------------------------------------------------
/8 255.0.0.0 24 16777216 16777214
/16 255.255.0.0 16 65536 65534
/20 255.255.240.0 12 4096 4094
/24 255.255.255.0 8 256 254
/27 255.255.255.224 5 32 30
/28 255.255.255.240 4 16 14
/29 255.255.255.248 3 8 6
/30 255.255.255.252 2 4 2
/31 255.255.255.254 1 2 2
/32 255.255.255.255 0 1 1
Casos particulares de CIDR
Redes /31 - Punto a punto (RFC 3021)
La RFC 3021 introdujo el uso del prefijo /31 para los enlaces punto a punto, lo que permite ahorrar direcciones IP:
def analyze_point_to_point_network(cidr_notation):
"""
Analiza una red punto a punto con prefijo /31.
Args:
cidr_notation (str): Notación CIDR (debe ser /31)
Returns:
dict: Información sobre el enlace punto a punto
"""
try:
network = ipaddress.ip_network(cidr_notation, strict=False)
if network.prefixlen != 31:
return {'error': 'Cette fonction est conçue pour les réseaux /31 uniquement'}
# En una red /31, las dos direcciones son utilizables (RFC 3021)
addresses = list(network.hosts())
if len(addresses) != 2:
return {'error': 'Nombre d\'adresses incorrect pour un réseau /31'}
return {
'network': str(network),
'device_a': str(addresses[0]),
'device_b': str(addresses[1]),
'note': 'Configuration point à point (RFC 3021)'
}
except ValueError as e:
return {'error': str(e)}
# Ejemplo de enlace punto a punto
p2p_example = '192.168.1.0/31'
p2p_info = analyze_point_to_point_network(p2p_example)
print(f"\nLiaison point à point {p2p_example}:")
for key, value in p2p_info.items():
print(f"- {key}: {value}")
Redes /32 - Dirección única
Para direcciones individuales (como las interfaces de loopback):
def analyze_host_address(ip_address):
"""
Analiza una dirección IP individual (prefijo /32).
Args:
ip_address (str): Dirección IP (con o sin /32)
Returns:
dict: Información sobre la dirección
"""
try:
# Asegurarse de que la dirección tenga un prefijo /32
if '/' not in ip_address:
ip_address = f"{ip_address}/32"
host = ipaddress.ip_interface(ip_address)
if host.network.prefixlen != 32:
return {'error': 'Cette fonction est conçue pour les adresses /32 uniquement'}
return {
'address': str(host.ip),
'prefix': host.network.prefixlen,
'network': str(host.network),
'is_private': host.ip.is_private,
'note': 'Adresse unique (souvent utilisée pour les interfaces loopback)'
}
except ValueError as e:
return {'error': str(e)}
# Ejemplo de dirección única
host_example = '10.0.0.1/32'
host_info = analyze_host_address(host_example)
print(f"\nAdresse unique {host_example}:")
for key, value in host_info.items():
print(f"- {key}: {value}")
Gestión de subredes (Subnetting)
La división de una red en subredes es una tarea habitual para los administradores de red. Python facilita esta operación:
def create_subnets(cidr_notation, new_prefix_length):
"""
Divide una red CIDR en subredes.
Args:
cidr_notation (str): Notación CIDR de la red padre
new_prefix_length (int): Longitud del prefijo para las subredes
Returns:
list: Lista de subredes
"""
try:
network = ipaddress.ip_network(cidr_notation, strict=False)
if new_prefix_length <= network.prefixlen:
return {'error': 'Le nouveau préfixe doit être plus grand que le préfixe du réseau parent'}
subnets = list(network.subnets(new_prefix=new_prefix_length))
# Limitar el número de subredes mostradas para evitar una salida demasiado voluminosa
display_limit = 10
total_subnets = len(subnets)
result = {
'parent_network': str(network),
'parent_prefix': network.prefixlen,
'new_prefix': new_prefix_length,
'total_subnets': total_subnets,
'subnets_sample': [str(subnet) for subnet in subnets[:display_limit]]
}
if total_subnets > display_limit:
result['note'] = f"Affichage limité aux {display_limit} premiers sous-réseaux sur un total de {total_subnets}"
return result
except ValueError as e:
return {'error': str(e)}
# Ejemplo de división en subredes
subnet_example = '192.168.0.0/24'
new_prefix = 26
subnet_info = create_subnets(subnet_example, new_prefix)
print(f"\nDivision de {subnet_example} en sous-réseaux /{new_prefix}:")
for key, value in subnet_info.items():
if key == 'subnets_sample':
print(f"- Exemples de sous-réseaux:")
for i, subnet in enumerate(value, 1):
print(f" {i}. {subnet}")
else:
print(f"- {key}: {value}")
Aplicación práctica: planificación de red
Este es un ejemplo de aplicación práctica para planificar una red con varias subredes que tienen necesidades específicas en cuanto al número de hosts:
def plan_network(base_network, requirements):
"""
Planifica una red asignando subredes según requisitos específicos.
Args:
base_network (str): Red base en notación CIDR
requirements (dict): Diccionario con el nombre de la subred y el número de hosts requeridos
Returns:
dict: Plan de subredes asignadas
"""
try:
network = ipaddress.ip_network(base_network, strict=False)
sorted_requirements = sorted(requirements.items(), key=lambda x: x[1], reverse=True)
allocated_subnets = {}
next_network = network
for name, hosts_needed in sorted_requirements:
# Calcular el prefijo necesario para el número de hosts
# Fórmula: 2^(32-prefix) - 2 ≥ hosts_needed (para IPv4)
prefix = 32
while prefix > 0 and (2 ** (32 - prefix) - 2 < hosts_needed):
prefix -= 1
if prefix <= network.prefixlen:
return {'error': f"Impossible d'allouer suffisamment d'adresses pour {name}"}
# Obtener la siguiente subred disponible
try:
subnet = next(next_network.subnets(new_prefix=prefix))
allocated_subnets[name] = {
'subnet': str(subnet),
'prefix': prefix,
'addresses_total': subnet.num_addresses,
'addresses_usable': subnet.num_addresses - 2,
'network_address': str(subnet.network_address),
'broadcast_address': str(subnet.broadcast_address),
'first_usable': str(next(subnet.hosts())),
'last_usable': str(list(subnet.hosts())[-1])
}
# Actualizar la red restante
remaining_networks = list(next_network.address_exclude(subnet))
if remaining_networks:
next_network = remaining_networks[0]
else:
break
except StopIteration:
return {'error': f"Espace d'adressage épuisé, impossible d'allouer pour {name}"}
return {
'base_network': str(network),
'allocated_subnets': allocated_subnets
}
except ValueError as e:
return {'error': str(e)}
# Ejemplo de planificación de una red de empresa
base_network = '10.0.0.0/22' # Bloque /22 (1022 direcciones utilizables)
# Requisitos para distintos departamentos
department_requirements = {
'Bureau': 500, # Oficina necesita 500 hosts
'Serveurs': 100, # Servidores necesita 100 hosts
'Invités': 200, # La red de invitados necesita 200 hosts
'IoT': 50, # Los dispositivos IoT necesitan 50 direcciones
'Management': 30 # La red de gestión necesita 30 direcciones
}
network_plan = plan_network(base_network, department_requirements)
print(f"\nPlan de réseau pour {base_network}:")
if 'error' in network_plan:
print(f"Erreur: {network_plan['error']}")
else:
print(f"Réseau de base: {network_plan['base_network']}")
print("\nAllocation des sous-réseaux:")
for dept, subnet_info in network_plan['allocated_subnets'].items():
print(f"\n{dept}:")
for key, value in subnet_info.items():
print(f"- {key}: {value}")
Compatibilidad con IPv6
Todas las funciones anteriores también funcionan con IPv6, aunque algunos conceptos, como la dirección de difusión (broadcast), no existen en IPv6:
def get_ipv6_cidr_range(cidr_notation):
"""
Calcula el rango de direcciones de una red IPv6 CIDR.
Args:
cidr_notation (str): Notación CIDR IPv6 (p. ej.: '2001:db8::/64')
Returns:
dict: Información sobre la red IPv6
"""
try:
network = ipaddress.ip_network(cidr_notation, strict=False)
if network.version != 6:
return {'error': 'Cette fonction est conçue pour les réseaux IPv6 uniquement'}
# Limitar la salida a unas pocas direcciones en IPv6
# (de lo contrario tendríamos millones o miles de millones de entradas)
hosts_iter = network.hosts()
first_host = next(hosts_iter)
# En IPv6 solo se muestran la primera y la última dirección
# porque normalmente hay un número enorme de ellas
sample_hosts = [str(first_host)]
# Para redes /64 y mayores, calcular la última dirección es muy costoso
# por lo que la construimos manualmente
if network.prefixlen <= 64:
# Para un prefijo ≤ 64, calcular explícitamente la última dirección
last_host_str = str(network.network_address + (1 << (128 - network.prefixlen)) - 1)
sample_note = "Trop d'adresses pour énumérer"
else:
# Para un prefijo > 64, podemos enumerar los hosts
# Avanzar por el iterador para encontrar la última dirección
last_host = None
count = 0
limit = 1000 # Limitar la iteración para los prefijos más grandes
for host in hosts_iter:
last_host = host
count += 1
if count >= limit:
break
last_host_str = str(last_host) if last_host else "..."
sample_note = f"Échantillon limité à {count+1} adresses" if count < limit else f"Échantillon limité aux premières et dernières adresses"
sample_hosts.append(last_host_str)
return {
'network_address': str(network.network_address),
'prefix_length': network.prefixlen,
'num_addresses': network.num_addresses,
'address_sample': sample_hosts,
'sample_note': sample_note,
'is_private': network.is_private,
'is_global': network.is_global,
'version': 'IPv6'
}
except ValueError as e:
return {'error': str(e)}
# Ejemplos de redes IPv6
ipv6_examples = [
'2001:db8::/32', # Bloque de documentación
'2001:db8::/48', # Asignación típica para un sitio
'2001:db8::/64', # Red típica para un segmento
'fe80::/64', # Link-local
'fd00::/8' # Unique Local Address (ULA)
]
for cidr in ipv6_examples:
print(f"\nInformations pour le réseau IPv6 {cidr}:")
network_info = get_ipv6_cidr_range(cidr)
if 'error' in network_info:
print(f"Erreur: {network_info['error']}")
continue
for key, value in network_info.items():
print(f"- {key}: {value}")
Conclusión
La gestión de redes CIDR es una competencia fundamental para los profesionales de la informática. El módulo ipaddress de Python ofrece una forma elegante y potente de trabajar con direcciones IP y redes.
Estos son los puntos clave que hay que recordar:
- La notación CIDR combina una dirección IP y un prefijo para definir una red
- El prefijo determina cuántas direcciones IP hay disponibles en la red
- Python facilita los cálculos de red con su módulo
ipaddress - Los conceptos se aplican tanto a IPv4 como a IPv6, con algunas diferencias
Estos conocimientos son esenciales para:
- Planificar y diseñar redes empresariales
- Comprender la segmentación de red
- Configurar equipos de red
- Desarrollar aplicaciones de monitorización o de automatización de redes
- Mejorar la seguridad de red mediante una segmentación adecuada
Para profundizar en estos conocimientos, consulta la documentación oficial del módulo ipaddress y las RFC asociadas a los estándares de Internet.
Acerca de InSkillCoach
Experto en formación y tecnologías
Coach especializado en tecnologías avanzadas e IA, impulsado por GNeurone Inc.
Certificaciones:
- AWS Certified Solutions Architect – Professional
- Certificaciones de Google Cloud
- Microsoft Certified: DevOps Engineer Expert
- Certified Kubernetes Administrator (CKA)
- CompTIA Security+
Comentarios
Los comentarios funcionan con GitHub Discussions
Inicia sesión con GitHub para participar en la discusión