Calcular o intervalo de endereços de uma rede CIDR em Python
Aprenda a determinar o número de endereços IP e o intervalo completo de uma rede CIDR com Python. Tutorial detalhado usando o módulo ipaddress, com exemplos práticos.
InSkillCoach
Calcular o intervalo de endereços de uma rede CIDR em Python
CIDR (Classless Inter-Domain Routing) é um método de alocação de endereços IP e de roteamento IP que substituiu o sistema anterior de classes de endereços de rede. Entender como calcular o intervalo de endereços IP de uma rede CIDR é essencial para a gestão de redes, a segurança da informação e a administração de sistemas.
Neste guia, vamos explorar como usar Python para determinar de forma rápida e eficiente o intervalo de endereços IP de uma rede CIDR.
O que é a notação CIDR?
A notação CIDR é composta por duas partes:
- Um endereço IP (IPv4 ou IPv6)
- Um sufixo que indica o tamanho do prefixo de rede (a máscara de sub-rede)
Por exemplo, 192.168.1.0/24 indica:
192.168.1.0é o endereço de rede/24significa que os 24 primeiros bits são fixos (o que corresponde a uma máscara de sub-rede 255.255.255.0)- Essa rede compreende todos os endereços de
192.168.1.0a192.168.1.255
Usando o módulo ipaddress
O Python inclui um poderoso módulo chamado ipaddress em sua biblioteca padrão (desde o Python 3.3), que facilita bastante os cálculos relacionados a endereços IP e redes.
Instalação
Nenhuma instalação é necessária, pois o módulo ipaddress faz parte da biblioteca padrão do Python:
import ipaddress
Determinar o intervalo de endereços de uma rede CIDR
Veja como calcular o intervalo de endereços de uma rede CIDR:
import ipaddress
def get_cidr_range(cidr_notation):
"""
Calcula o intervalo de endereços IP de uma rede CIDR.
Args:
cidr_notation (str): Notação CIDR (ex: '192.168.1.0/24')
Returns:
dict: Dicionário contendo as informações sobre a rede
"""
try:
network = ipaddress.ip_network(cidr_notation, strict=False)
# Coleta das informações de rede
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)}
# Exemplos de uso
cidr_examples = [
'192.168.1.0/24', # Rede local clássica (/24)
'10.0.0.0/8', # Rede privada de classe A
'172.16.0.0/12', # Rede privada de classe B
'8.8.8.0/24', # Rede pública (contendo o DNS do Google)
'192.168.0.1/32', # Um único endereço IP
'2001:db8::/32', # Rede 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
# Exibição das informações de rede
for key, value in network_info.items():
print(f"- {key}: {value}")
Resultado para uma rede 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
Entendendo o número de endereços IP em uma rede CIDR
O número de endereços IP em uma rede CIDR pode ser calculado com a fórmula: 2^(32-prefixo) para IPv4 e 2^(128-prefixo) para IPv6.
Aqui está uma função que explica essa relação:
def explain_cidr_size(prefix_length, ip_version=4):
"""
Explica o tamanho de uma rede CIDR e calcula o número de endereços IP.
Args:
prefix_length (int): Comprimento do prefixo CIDR (ex: 24 em /24)
ip_version (int): Versão do IP (4 ou 6)
Returns:
dict: Explicação e 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:
# Em IPv4, geralmente contamos os endereços utilizáveis
# (excluindo o endereço de rede e o de broadcast)
usable_addresses = max(0, num_addresses - 2) if prefix_length < 31 else num_addresses
else:
# Em IPv6, geralmente não se subtrai nenhum endereço
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
# Tabela comparativa de diferentes prefixos 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 da máscara de sub-rede
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 da tabela 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 - Ponto a ponto (RFC 3021)
A RFC 3021 introduziu o uso do prefixo /31 para enlaces ponto a ponto, permitindo economizar endereços IP:
def analyze_point_to_point_network(cidr_notation):
"""
Analisa uma rede ponto a ponto com prefixo /31.
Args:
cidr_notation (str): Notação CIDR (deve ser /31)
Returns:
dict: Informações sobre o enlace ponto a ponto
"""
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'}
# Em uma rede /31, os dois endereços são utilizáveis (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)}
# Exemplo de enlace ponto a ponto
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 - Endereço único
Para endereços individuais (como as interfaces de loopback):
def analyze_host_address(ip_address):
"""
Analisa um endereço IP individual (prefixo /32).
Args:
ip_address (str): Endereço IP (com ou sem /32)
Returns:
dict: Informações sobre o endereço
"""
try:
# Garantir que o endereço tenha um prefixo /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)}
# Exemplo de endereço único
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}")
Gerenciamento de sub-redes (Subnetting)
Dividir uma rede em sub-redes é uma tarefa comum para administradores de rede. O Python facilita essa operação:
def create_subnets(cidr_notation, new_prefix_length):
"""
Divide uma rede CIDR em sub-redes.
Args:
cidr_notation (str): Notação CIDR da rede pai
new_prefix_length (int): Comprimento do prefixo para as sub-redes
Returns:
list: Lista das sub-redes
"""
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 o número de sub-redes exibidas para evitar uma saída muito extensa
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)}
# Exemplo de divisão em sub-redes
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}")
Aplicação prática: planejamento de rede
Aqui está um exemplo de aplicação prática para planejar uma rede com várias sub-redes que têm necessidades específicas em termos de número de hosts:
def plan_network(base_network, requirements):
"""
Planeja uma rede alocando sub-redes de acordo com requisitos específicos.
Args:
base_network (str): Rede base em notação CIDR
requirements (dict): Dicionário com o nome da sub-rede e o número de hosts necessários
Returns:
dict: Plano de sub-redes alocadas
"""
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 o prefixo necessário para o 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}"}
# Obter a próxima sub-rede disponível
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])
}
# Atualizar a rede 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)}
# Exemplo de planejamento de rede corporativa
base_network = '10.0.0.0/22' # Bloco /22 (1022 endereços utilizáveis)
# Requisitos para diferentes departamentos
department_requirements = {
'Bureau': 500, # Escritório precisa de 500 hosts
'Serveurs': 100, # Servidores precisam de 100 hosts
'Invités': 200, # Rede de convidados precisa de 200 hosts
'IoT': 50, # Dispositivos IoT precisam de 50 endereços
'Management': 30 # Rede de gerenciamento precisa de 30 endereços
}
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}")
Compatibilidade com IPv6
Todas as funções acima também funcionam com IPv6, embora alguns conceitos, como o endereço de broadcast, não existam em IPv6:
def get_ipv6_cidr_range(cidr_notation):
"""
Calcula a faixa de endereços de uma rede IPv6 CIDR.
Args:
cidr_notation (str): Notação CIDR IPv6 (ex.: '2001:db8::/64')
Returns:
dict: Informações sobre a rede 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 a exibição a alguns endereços no IPv6
# (caso contrário, teríamos milhões ou bilhões de entradas)
hosts_iter = network.hosts()
first_host = next(hosts_iter)
# No IPv6, mostramos apenas o primeiro e o último endereço
# pois geralmente há um número muito grande deles
sample_hosts = [str(first_host)]
# Para redes /64 e maiores, calcular o último endereço é muito custoso
# então o construímos manualmente
if network.prefixlen <= 64:
# Para um prefixo ≤ 64, calcular explicitamente o último endereço
last_host_str = str(network.network_address + (1 << (128 - network.prefixlen)) - 1)
sample_note = "Trop d'adresses pour énumérer"
else:
# Para um prefixo > 64, podemos enumerar os hosts
# Avançar no iterador para encontrar o último endereço
last_host = None
count = 0
limit = 1000 # Limitar a iteração para prefixos maiores
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)}
# Exemplos de redes IPv6
ipv6_examples = [
'2001:db8::/32', # Bloco de documentação
'2001:db8::/48', # Alocação típica para um site
'2001:db8::/64', # Rede típica para um 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}")
Conclusão
O gerenciamento de redes CIDR é uma habilidade fundamental para profissionais de TI. O módulo ipaddress do Python oferece uma maneira elegante e poderosa de trabalhar com endereços IP e redes.
Estes são os principais pontos a lembrar:
- A notação CIDR combina um endereço IP e um prefixo para definir uma rede
- O prefixo determina quantos endereços IP estão disponíveis na rede
- O Python facilita os cálculos de rede com o módulo
ipaddress - Os conceitos se aplicam tanto ao IPv4 quanto ao IPv6, com algumas diferenças
Esses conhecimentos são essenciais para:
- Planejar e projetar redes corporativas
- Entender a segmentação de rede
- Configurar equipamentos de rede
- Desenvolver aplicações de monitoramento ou de automação de rede
- Melhorar a segurança da rede por meio de uma segmentação adequada
Para aprofundar seus conhecimentos, consulte a documentação oficial do módulo ipaddress e as RFCs associadas aos padrões da Internet.
Sobre InSkillCoach
Especialista em treinamento e tecnologias
Coach especializado em tecnologias avançadas e IA, apoiado pela GNeurone Inc.
Certificações:
- AWS Certified Solutions Architect – Professional
- Certificações Google Cloud
- Microsoft Certified: DevOps Engineer Expert
- Certified Kubernetes Administrator (CKA)
- CompTIA Security+
Comentários
Os comentários são alimentados pelo GitHub Discussions
Conecte-se com o GitHub para participar da discussão