Apuntes DAM
Volver al inicio

Plan de direccionamiento con subredes VLSM

Ejercicio de PythonDifícilUnos 70 minutos

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

  1. 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, escribe Red no válida: texto. Si la IP no es la dirección de red de ese prefijo (tiene bits de host a 1), escribe IP no es una dirección de red (sería RED/prefijo). En los dos casos no se escribe nada más.
  2. 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.
  3. 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).
  4. Para cada subred: Nombre (H hosts) → RED/p, y debajo, con dos espacios delante, máscara M · hosts útiles U y primera 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 direcciones y se sigue con la siguiente.
  5. Antes de las subredes, Red: RED/prefijo (T direcciones). Al final, Sin asignar: N de T direcciones, desde IP con la primera dirección libre, o Sin asignar: 0 de T direcciones si no queda ninguna.

Entrada

Línea 1: la red, IP/prefijo. Resto: una subred por línea, nombre hosts.

Datos de referencia

Tamaño de cada prefijo
PrefijoMáscaraDireccionesHosts útiles
/30255.255.255.25242
/29255.255.255.24886
/28255.255.255.2401614
/27255.255.255.2243230
/26255.255.255.1926462
/25255.255.255.128128126
/24255.255.255.0256254

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…

python
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.

🐍PythonPlan de direccionamiento con subredes VLSMDifícil

Ejemplo

Entrada (lo que se escribe por teclado)
192.168.10.0/24
Ventas 50
Administración 20
Almacén 10
Enlace router 2
Dirección 20
Salida esperada
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
⏳
Test oculto #3
⏳
Test oculto #4
⏳
Test oculto #5
0/5 tests pasados · pulsa un test para ver su entrada y su salida esperada

Solución explicada

Ver la solución completa
python
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 ipaddress y 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.

Dónde se explica