0%
Calculer la plage d'adresses d'un réseau CIDR en Python

Calculer la plage d'adresses d'un réseau CIDR en Python

Apprenez à déterminer le nombre d'adresses IP et la plage complète d'un réseau CIDR avec Python. Tutoriel détaillé utilisant le module ipaddress avec exemples pratiques.

I

InSkillCoach

· min

Calculer la plage d’adresses d’un réseau CIDR en Python

CIDR (Classless Inter-Domain Routing) est une méthode d’allocation d’adresses IP et de routage IP qui a supplanté le système précédent des classes d’adresses réseau. Comprendre comment calculer la plage d’adresses IP d’un réseau CIDR est essentiel pour la gestion de réseau, la sécurité informatique et l’administration système.

Dans ce guide, nous allons explorer comment utiliser Python pour déterminer rapidement et efficacement la plage d’adresses IP d’un réseau CIDR.

Qu’est-ce que la notation CIDR?

La notation CIDR se compose de deux parties:

  • Une adresse IP (IPv4 ou IPv6)
  • Un suffixe indiquant la taille du préfixe réseau (le masque de sous-réseau)

Par exemple, 192.168.1.0/24 indique:

  • 192.168.1.0 est l’adresse réseau
  • /24 signifie que les 24 premiers bits sont fixes (ce qui correspond à un masque de sous-réseau 255.255.255.0)
  • Ce réseau comprend toutes les adresses de 192.168.1.0 à 192.168.1.255

Utilisation du module ipaddress

Python inclut un module puissant appelé ipaddress dans sa bibliothèque standard (depuis Python 3.3), qui facilite grandement les calculs liés aux adresses IP et aux réseaux.

Installation

Aucune installation n’est nécessaire car le module ipaddress fait partie de la bibliothèque standard Python:

import ipaddress

Déterminer la plage d’adresses d’un réseau CIDR

Voici comment calculer la plage d’adresses d’un réseau CIDR:

import ipaddress

def get_cidr_range(cidr_notation):
    """
    Calcule la plage d'adresses IP d'un réseau CIDR.
    
    Args:
        cidr_notation (str): Notation CIDR (ex: '192.168.1.0/24')
        
    Returns:
        dict: Dictionnaire contenant les informations sur le réseau
    """
    try:
        network = ipaddress.ip_network(cidr_notation, strict=False)
        
        # Collecte des informations réseau
        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)}

# Exemples d'utilisation
cidr_examples = [
    '192.168.1.0/24',  # Réseau local classique (/24)
    '10.0.0.0/8',      # Réseau privé de classe A
    '172.16.0.0/12',   # Réseau privé de classe B
    '8.8.8.0/24',      # Réseau public (contenant Google DNS)
    '192.168.0.1/32',  # Une seule adresse IP
    '2001:db8::/32',   # Réseau 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
    
    # Affichage des informations réseau
    for key, value in network_info.items():
        print(f"- {key}: {value}")

Résultat pour un réseau 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

Compréhension du nombre d’adresses IP dans un réseau CIDR

Le nombre d’adresses IP dans un réseau CIDR peut être calculé avec la formule: 2^(32-préfixe) pour IPv4 et 2^(128-préfixe) pour IPv6.

Voici une fonction qui explique cette relation:

def explain_cidr_size(prefix_length, ip_version=4):
    """
    Explique la taille d'un réseau CIDR et calcule le nombre d'adresses IP.
    
    Args:
        prefix_length (int): Longueur du préfixe CIDR (ex: 24 dans /24)
        ip_version (int): Version IP (4 ou 6)
        
    Returns:
        dict: Explication et calculs
    """
    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:
        # Pour IPv4, on compte généralement les adresses utilisables
        # (en excluant l'adresse réseau et de broadcast)
        usable_addresses = max(0, num_addresses - 2) if prefix_length < 31 else num_addresses
    else:
        # Pour IPv6, on ne soustrait généralement pas d'adresses
        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

# Tableau comparatif de différents préfixes 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)
    
    # Calcul du masque de sous-réseau
    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}")

Résultat du tableau comparatif

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

Cas particuliers de CIDR

Réseaux /31 - Point à point (RFC 3021)

Le RFC 3021 a introduit l’utilisation du préfixe /31 pour les liaisons point à point, permettant d’économiser des adresses IP:

def analyze_point_to_point_network(cidr_notation):
    """
    Analyse un réseau point à point avec préfixe /31.
    
    Args:
        cidr_notation (str): Notation CIDR (doit être /31)
        
    Returns:
        dict: Information sur la liaison point à point
    """
    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'}
        
        # Dans un réseau /31, les deux adresses sont utilisables (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)}

# Exemple de liaison point à point
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}")

Réseaux /32 - Adresse unique

Pour les adresses individuelles (comme les interfaces de loopback):

def analyze_host_address(ip_address):
    """
    Analyse une adresse IP individuelle (préfixe /32).
    
    Args:
        ip_address (str): Adresse IP (avec ou sans /32)
        
    Returns:
        dict: Information sur l'adresse
    """
    try:
        # S'assurer que l'adresse a un préfixe /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)}

# Exemple d'adresse unique
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}")

Gestion des sous-réseaux (Subnetting)

La division d’un réseau en sous-réseaux est une tâche courante pour les administrateurs réseau. Python facilite cette opération:

def create_subnets(cidr_notation, new_prefix_length):
    """
    Divise un réseau CIDR en sous-réseaux.
    
    Args:
        cidr_notation (str): Notation CIDR du réseau parent
        new_prefix_length (int): Longueur du préfixe pour les sous-réseaux
        
    Returns:
        list: Liste des sous-réseaux
    """
    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))
        
        # Limiter le nombre de sous-réseaux affichés pour éviter une sortie trop volumineuse
        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)}

# Exemple de division en sous-réseaux
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}")

Application pratique : Planification de réseau

Voici un exemple d’application pratique pour planifier un réseau avec plusieurs sous-réseaux ayant des besoins spécifiques en termes de nombre d’hôtes:

def plan_network(base_network, requirements):
    """
    Planifie un réseau en allouant des sous-réseaux selon des exigences spécifiques.
    
    Args:
        base_network (str): Réseau de base en notation CIDR
        requirements (dict): Dictionnaire avec nom de sous-réseau et nombre d'hôtes requis
        
    Returns:
        dict: Plan de sous-réseaux alloués
    """
    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:
            # Calculer le préfixe requis pour le nombre d'hôtes
            # Formule: 2^(32-prefix) - 2 ≥ hosts_needed (pour 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}"}
            
            # Obtenir le prochain sous-réseau 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])
                }
                
                # Mettre à jour le réseau restant
                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)}

# Exemple de planification de réseau d'entreprise
base_network = '10.0.0.0/22'  # Bloc /22 (1022 adresses utilisables)

# Exigences pour différents départements
department_requirements = {
    'Bureau': 500,         # Bureau a besoin de 500 hôtes
    'Serveurs': 100,       # Serveurs a besoin de 100 hôtes
    'Invités': 200,        # Réseau invités a besoin de 200 hôtes
    'IoT': 50,             # Appareils IoT ont besoin de 50 adresses
    'Management': 30       # Réseau de gestion a besoin de 30 adresses
}

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}")

Compatibilité avec IPv6

Toutes les fonctions ci-dessus fonctionnent également avec IPv6, bien que certains concepts comme l’adresse de diffusion (broadcast) n’existent pas en IPv6:

def get_ipv6_cidr_range(cidr_notation):
    """
    Calcule la plage d'adresses pour un réseau IPv6 CIDR.
    
    Args:
        cidr_notation (str): Notation CIDR IPv6 (ex: '2001:db8::/64')
        
    Returns:
        dict: Informations sur le réseau 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'}
        
        # Limiter l'affichage à quelques adresses pour IPv6
        # (sinon nous aurions des millions ou des milliards d'entrées)
        hosts_iter = network.hosts()
        first_host = next(hosts_iter)
        
        # Pour IPv6, on ne montre que les premières et dernières adresses
        # car il y en a généralement un très grand nombre
        sample_hosts = [str(first_host)]
        
        # Pour les réseaux /64 et plus grands, calculer la dernière adresse est très coûteux
        # donc nous la construisons manuellement
        if network.prefixlen <= 64:
            # Pour un préfixe ≤ 64, calculer explicitement la dernière adresse
            last_host_str = str(network.network_address + (1 << (128 - network.prefixlen)) - 1)
            sample_note = "Trop d'adresses pour énumérer"
        else:
            # Pour un préfixe > 64, nous pouvons énumérer les hôtes
            # Avancer dans l'itérateur pour trouver la dernière adresse
            last_host = None
            count = 0
            limit = 1000  # Limiter l'itération pour les préfixes plus grands
            
            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)}

# Exemples de réseaux IPv6
ipv6_examples = [
    '2001:db8::/32',   # Bloc de documentation
    '2001:db8::/48',   # Allocation typique pour un site
    '2001:db8::/64',   # Réseau typique pour un segment
    '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}")

Conclusion

La gestion des réseaux CIDR est une compétence fondamentale pour les professionnels de l’informatique. Le module ipaddress de Python offre une manière élégante et puissante de travailler avec des adresses IP et des réseaux.

Voici les points clés à retenir:

  1. La notation CIDR combine une adresse IP et un préfixe pour définir un réseau
  2. Le préfixe détermine combien d’adresses IP sont disponibles dans le réseau
  3. Python facilite les calculs réseau avec son module ipaddress
  4. Les concepts s’appliquent à la fois à IPv4 et IPv6, avec quelques différences

Ces connaissances sont essentielles pour:

  • Planifier et concevoir des réseaux d’entreprise
  • Comprendre la segmentation réseau
  • Configurer des équipements réseau
  • Développer des applications de surveillance ou d’automatisation réseau
  • Améliorer la sécurité réseau par une segmentation appropriée

Pour approfondir vos connaissances, consultez la documentation officielle du module ipaddress et les RFC associés aux standards Internet.

import EmailSubscribe from ’../../../components/EmailSubscribe.astro’;

InSkillCoach

À propos de InSkillCoach

Expert en formation et technologies

Coach spécialisé dans les technologies avancées et l'IA, porté par GNeurone Inc.

Certifications:

  • AWS Certified Solutions Architect – Professional
  • Certifications Google Cloud
  • Microsoft Certified: DevOps Engineer Expert
  • Certified Kubernetes Administrator (CKA)
  • CompTIA Security+
1.6k
347

Commentaires

Les commentaires sont alimentés par GitHub Discussions

Connectez-vous avec GitHub pour participer à la discussion

Lien copié !