Plan de direccionamiento con subredes VLSM
Reparte una red IPv4 entre los departamentos de una empresa con máscaras de longitud variable: cada subred con el prefijo justo para sus hosts, su red, primera y última dirección útil y difusión, y el espacio que queda libre. Operaciones de bits con enteros, validación de IP y ordenación.
- Operadores de bits (&, <<, >>)
- Direcciones IP como enteros
- Potencias de 2
- sorted con key
- Validación de la entrada
- Funciones pequeñas
Enunciado
Una empresa tiene asignada la red 192.168.10.0/24 y quiere dividirla entre sus departamentos. Con subredes del mismo tamaño se desperdician direcciones (el enlace entre dos routers no necesita 62 direcciones), así que se usa VLSM: cada subred recibe la máscara más ajustada a los equipos que va a tener.
El método que se enseña (y el que sigue este programa) es asignar primero las subredes más grandes: así cada bloque empieza en una dirección múltiplo de su tamaño y las subredes nunca se solapan. De cada subred hay que dar la dirección de red, la máscara, la primera y la última dirección que se pueden asignar a equipos y la de difusión.
Hacerlo a mano es lento y es fácil equivocarse con la última dirección o la de difusión. Una dirección IP no es más que un número de 32 bits escrito en cuatro bytes: con ese número, las cuentas son sumas y operaciones de bits.
Qué tiene que hacer el programa
- La primera línea es la red,
IP/prefijo, con el prefijo entre 8 y 30. Si no tiene ese formato o la IP no es válida, escribeRed no válida: texto. Si la IP no es la dirección de red de ese prefijo (tiene bits de host a 1), escribeIP no es una dirección de red (sería RED/prefijo). En los dos casos no se escribe nada más. - Cada línea siguiente es una subred: un nombre (puede tener espacios) y el número de hosts que necesita, un entero mayor que 0. Una línea con otro formato escribe
Aviso: línea N ignorada. - Las subredes se asignan de mayor a menor número de hosts (si empatan, por orden de la entrada), una detrás de otra desde el principio de la red. Cada una recibe el prefijo más largo que tenga sitio para sus hosts más la dirección de red y la de difusión, y como mucho /30 (un enlace de 2 hosts es un /30).
- Para cada subred:
Nombre (H hosts) → RED/p, y debajo, con dos espacios delante,máscara M · hosts útiles Uyprimera A · última B · difusión D. Si no cabe en lo que queda de la red,Nombre (H hosts) → no cabe: necesita un /p y quedan N direccionesy se sigue con la siguiente. - Antes de las subredes,
Red: RED/prefijo (T direcciones). Al final,Sin asignar: N de T direcciones, desde IPcon la primera dirección libre, oSin asignar: 0 de T direccionessi no queda ninguna.
Entrada
Línea 1: la red, IP/prefijo. Resto: una subred por línea, nombre hosts.
Datos de referencia
| Prefijo | Máscara | Direcciones | Hosts útiles |
|---|---|---|---|
| /30 | 255.255.255.252 | 4 | 2 |
| /29 | 255.255.255.248 | 8 | 6 |
| /28 | 255.255.255.240 | 16 | 14 |
| /27 | 255.255.255.224 | 32 | 30 |
| /26 | 255.255.255.192 | 64 | 62 |
| /25 | 255.255.255.128 | 128 | 126 |
| /24 | 255.255.255.0 | 256 | 254 |
Ejemplos de ejecución
Tu programa debe escribir exactamente esta salida para estas entradas. Las pruebas del editor incluyen estos ejemplos y otros casos ocultos.
Una oficina con cinco subredes
Entrada
192.168.10.0/24 Ventas 50 Administración 20 Almacén 10 Enlace router 2 Dirección 20
Salida por consola
Red: 192.168.10.0/24 (256 direcciones) Ventas (50 hosts) → 192.168.10.0/26 máscara 255.255.255.192 · hosts útiles 62 primera 192.168.10.1 · última 192.168.10.62 · difusión 192.168.10.63 Administración (20 hosts) → 192.168.10.64/27 máscara 255.255.255.224 · hosts útiles 30 primera 192.168.10.65 · última 192.168.10.94 · difusión 192.168.10.95 Dirección (20 hosts) → 192.168.10.96/27 máscara 255.255.255.224 · hosts útiles 30 primera 192.168.10.97 · última 192.168.10.126 · difusión 192.168.10.127 Almacén (10 hosts) → 192.168.10.128/28 máscara 255.255.255.240 · hosts útiles 14 primera 192.168.10.129 · última 192.168.10.142 · difusión 192.168.10.143 Enlace router (2 hosts) → 192.168.10.144/30 máscara 255.255.255.252 · hosts útiles 2 primera 192.168.10.145 · última 192.168.10.146 · difusión 192.168.10.147 Sin asignar: 108 de 256 direcciones, desde 192.168.10.148
Una IP que no es de red
Entrada
10.0.0.5/24 A 10
Salida por consola
10.0.0.5 no es una dirección de red (sería 10.0.0.0/24)
Guía paso a paso
Intenta resolverlo por tu cuenta y abre un paso solo cuando te atasques: cada uno te acerca a la solución sin dártela entera.
1. La IP como número
Cada byte vale 256 veces más que el siguiente: recorre los cuatro y acumula n = n * 256 + byte. Para volver al texto, desplaza y quédate con 8 bits: (n >> 24) & 255, (n >> 16) & 255…
def a_ip(n):
return ".".join(str((n >> d) & 255) for d in (24, 16, 8, 0))2. La máscara y la dirección de red
La máscara de un prefijo p son p unos seguidos de ceros: (0xFFFFFFFF << (32 - p)) & 0xFFFFFFFF (el & recorta lo que se sale de 32 bits, porque los enteros de Python no tienen límite). La dirección de red es ip & mascara: si no coincide con la IP, la IP tenía bits de host.
3. El prefijo de cada subred
Empieza en /30 y ve agrandando el bloque (restando 1 al prefijo) mientras 2 ** (32 - p) - 2 sea menor que los hosts que necesitas.
4. Asignar en orden
Ordena las subredes por hosts de mayor a menor con sorted(..., key=lambda s: -s[1]) (es estable: los empates mantienen el orden de la entrada). Lleva en una variable la siguiente dirección libre y súmale el tamaño de cada bloque que asignes.
5. Las direcciones de cada subred
Con la red R y el tamaño T del bloque: la primera útil es R + 1, la última R + T − 2 y la difusión R + T − 1. Comprueba antes que R + T no se pase del final de la red.
Resuélvelo aquí
El editor trae el esqueleto del programa. Pulsa «Ejecutar» para comprobarlo con los ejemplos y con 3 casos ocultos que buscan los errores típicos.
Ejemplo
192.168.10.0/24 Ventas 50 Administración 20 Almacén 10 Enlace router 2 Dirección 20
Red: 192.168.10.0/24 (256 direcciones) Ventas (50 hosts) → 192.168.10.0/26 máscara 255.255.255.192 · hosts útiles 62 primera 192.168.10.1 · última 192.168.10.62 · difusión 192.168.10.63 Administración (20 hosts) → 192.168.10.64/27 máscara 255.255.255.224 · hosts útiles 30 primera 192.168.10.65 · última 192.168.10.94 · difusión 192.168.10.95 Dirección (20 hosts) → 192.168.10.96/27 máscara 255.255.255.224 · hosts útiles 30 primera 192.168.10.97 · última 192.168.10.126 · difusión 192.168.10.127 Almacén (10 hosts) → 192.168.10.128/28 máscara 255.255.255.240 · hosts útiles 14 primera 192.168.10.129 · última 192.168.10.142 · difusión 192.168.10.143 Enlace router (2 hosts) → 192.168.10.144/30 máscara 255.255.255.252 · hosts útiles 2 primera 192.168.10.145 · última 192.168.10.146 · difusión 192.168.10.147 Sin asignar: 108 de 256 direcciones, desde 192.168.10.148
Solución explicada
Ver la solución completa
1import sys
2
3
4def a_numero(ip):
5 """«192.168.10.0» → entero de 32 bits; None si no es una IPv4."""
6 partes = ip.split(".")
7 if len(partes) != 4 or not all(p.isdigit() and int(p) <= 255 for p in partes):
8 return None
9 n = 0
10 for p in partes:
11 n = n * 256 + int(p)
12 return n
13
14
15def a_ip(n):
16 return ".".join(str((n >> desplazamiento) & 255) for desplazamiento in (24, 16, 8, 0))
17
18
19def mascara(prefijo):
20 return (0xFFFFFFFF << (32 - prefijo)) & 0xFFFFFFFF
21
22
23def prefijo_para(hosts):
24 """El prefijo más largo cuyo bloque tiene sitio para «hosts» (sin contar la red y la difusión), como mucho /30."""
25 prefijo = 30
26 while 2 ** (32 - prefijo) - 2 < hosts:
27 prefijo -= 1
28 return prefijo
29
30
31def main():
32 lineas = sys.stdin.read().split("\n")
33 texto = lineas[0].strip()
34 if texto.count("/") != 1 or a_numero(texto.split("/")[0]) is None or not texto.split("/")[1].isdigit() \
35 or not 8 <= int(texto.split("/")[1]) <= 30:
36 print(f"Red no válida: {texto}")
37 return
38 ip, prefijo = texto.split("/")
39 prefijo = int(prefijo)
40 base = a_numero(ip)
41 if base & mascara(prefijo) != base:
42 print(f"{ip} no es una dirección de red (sería {a_ip(base & mascara(prefijo))}/{prefijo})")
43 return
44 total = 2 ** (32 - prefijo)
45
46 subredes = []
47 for num, linea in enumerate(lineas[1:], start=2):
48 partes = linea.split()
49 if not partes:
50 continue
51 if len(partes) < 2 or not partes[-1].isdigit() or int(partes[-1]) == 0:
52 print(f"Aviso: línea {num} ignorada")
53 continue
54 subredes.append((" ".join(partes[:-1]), int(partes[-1])))
55
56 print(f"Red: {a_ip(base)}/{prefijo} ({total} direcciones)")
57 # VLSM: de la más grande a la más pequeña, así cada bloque queda alineado con su tamaño
58 siguiente = base
59 for nombre, hosts in sorted(subredes, key=lambda s: -s[1]):
60 p = prefijo_para(hosts)
61 tamano = 2 ** (32 - p)
62 if p < prefijo or siguiente + tamano > base + total:
63 print(f"{nombre} ({hosts} hosts) → no cabe: necesita un /{p} y quedan {base + total - siguiente} direcciones")
64 continue
65 print(f"{nombre} ({hosts} hosts) → {a_ip(siguiente)}/{p}")
66 print(f" máscara {a_ip(mascara(p))} · hosts útiles {tamano - 2}")
67 print(f" primera {a_ip(siguiente + 1)} · última {a_ip(siguiente + tamano - 2)} · difusión {a_ip(siguiente + tamano - 1)}")
68 siguiente += tamano
69
70 libres = base + total - siguiente
71 if libres:
72 print(f"Sin asignar: {libres} de {total} direcciones, desde {a_ip(siguiente)}")
73 else:
74 print(f"Sin asignar: 0 de {total} direcciones")
75
76
77main()Tratar las direcciones como enteros de 32 bits convierte todo el subnetting en aritmética: la máscara es un desplazamiento, la red un & y las direcciones de cada subred, sumas. La conversión a texto solo ocurre al escribir.
Asignar de mayor a menor garantiza la alineación: después de bloques de 64, 32 y 16, la siguiente dirección libre es siempre múltiplo del tamaño del bloque que toca, que es exactamente lo que exige una dirección de red.
La validación tiene dos niveles: que el texto sea una IP con prefijo, y que la IP sea de verdad una dirección de red. Es un error frecuente escribir 192.168.10.5/24 queriendo decir la red 192.168.10.0/24.
El módulo ipaddress de Python hace todo esto (ip_network, subnets, broadcast_address). Escribirlo a mano enseña lo que hay debajo; en un programa real conviene usar la librería.
Para ir más allá
- Resuelve el mismo problema con el módulo
ipaddressy comprueba que da lo mismo. - Muestra también el rango en binario de cada subred, marcando dónde acaba la parte de red.
- Añade un modo inverso: dada una IP y su máscara, di a qué subred del plan pertenece.