0%
Calcular el rango de direcciones de una red CIDR en Python

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.

I

InSkillCoach

· min

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.0 es la dirección de red
  • /24 significa 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.0 hasta 192.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:

  1. La notación CIDR combina una dirección IP y un prefijo para definir una red
  2. El prefijo determina cuántas direcciones IP hay disponibles en la red
  3. Python facilita los cálculos de red con su módulo ipaddress
  4. 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.

InSkillCoach

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+
2.0k
86

Comentarios

Los comentarios funcionan con GitHub Discussions

Inicia sesión con GitHub para participar en la discusión

¡Enlace copiado!