Apuntes · Capítulo 3Redes locales e IP

Enlace, Ethernet y VLANs

Enlace, Ethernet y VLANs

En la semana 2 escribieron programas que conversan por la red: un connect en un extremo, un accept en el otro, y los bytes llegan. Desde ahí, la red parece un tubo. Este capítulo abre el tubo por el tramo más corto: el salto entre dos equipos vecinos. Antes de que un paquete cruce el mundo tiene que cruzar un cable, y ese cable tiene problemas propios. Los bits se dañan, varios equipos pueden querer hablar a la vez, y el host que envía debe averiguar la dirección física de su vecino antes de mandarle algo.

Bajamos a la capa de enlace (link layer) y, muy brevemente, a la capa física que tiene debajo. Vamos a seguir un orden de abajo hacia arriba, porque cada tema resuelve un problema que deja el anterior:

  1. La capa física mueve bits, pero con errores.
  2. Por eso hace falta agruparlos en frames y detectar los errores: paridad, checksum y CRC.
  3. Si el medio es compartido, hay que decidir quién transmite: acceso múltiple.
  4. Ethernet junta todo lo anterior y agrega direcciones MAC.
  5. Para usar esas direcciones hay que encontrarlas: ARP.
  6. Un switch conecta varios enlaces y aprende solo por dónde está cada MAC.
  7. Si los switches forman un ciclo, un solo broadcast tumba la red: spanning tree.
  8. Y si una sola red física debe comportarse como varias, usamos VLANs.

Casi todo lo vamos a ver funcionando. En el laboratorio arman un switch con un bridge de Linux, capturan ARP, miran cómo aprende el switch, separan la red en VLANs y encuentran la etiqueta 802.1Q dentro de un frame.

Ruta de lectura.

Antes de clase (obligatorio, unos 15 a 20 minutos). Sesión A: Dos tiempos que no hay que confundir, Qué hace la capa de enlace, y de Detección de errores, la introducción, Paridad y Checksum. Sesión B: la introducción de Acceso múltiple con Particionar el canal, Ethernet y, de ARP, Cómo funciona.

En clase. Sesión A: La capa física en lo esencial, CRC, Cuál detecta qué y Por qué el enlace usa CRC y el transporte un checksum. Sesión B: Acceso aleatorio: ALOHA, Acceso aleatorio: CSMA, CSMA/CD en la Ethernet original, Un intercambio real, Switches, Spanning tree y VLANs.

Después de clase. Repasen con el Resumen y los Ejercicios, y preparen El laboratorio de esta semana. Opcional: Tomar turnos, Los bytes de ARP, ARP a mano, en C y Un paquete a través de VLANs, en tres capturas. Para profundizar, las lecturas del libro y las gratuitas de la semana:

Este capítulo acompaña a Kurose y Ross, Computer Networking: A Top-Down Approach (9.ª ed.). Para la Sesión A, las secciones 1.2.2 Physical Media, 6.1 Introduction to the Link Layer y 6.2 Error-Detection and -Correction Techniques, junto con CS168, Links. Para la Sesión B, las secciones 6.3 Multiple Access Links and Protocols y 6.4 Switched Local Area Networks, junto con CS168, Ethernet, Layer 2 Routing y ARP. Complementaria: RFC 826, ARP. El código del capítulo está en code/week-03 y se prueba con make test dentro del contenedor del curso. Las instrucciones del entorno están en labs/README.md.

La capa física en lo esencial

La capa física mueve bits de un lado a otro de un enlace (link), que es lo que une directamente a dos o más nodos. No sabe qué significan esos bits ni dónde empieza un mensaje. Solo convierte bits en una señal, la pone en un medio de transmisión (transmission medium) y, al otro lado, convierte la señal de nuevo en bits. No vamos a estudiar señales en detalle, pero sí necesitamos entender qué limita a cada medio, porque de ahí salen las decisiones de las capas de arriba.

Medios guiados y no guiados

Un medio es guiado si la señal viaja dentro de algo sólido: un cable de cobre o una fibra. Es no guiado si se propaga por el aire o el vacío.

Medio Señal Alcance y velocidad típicos Qué lo limita
Par trenzado de cobre Eléctrica 1 a 10 Gb/s, hasta 100 m Atenuación e interferencia
Cable coaxial Eléctrica Hasta varios Gb/s compartidos, en redes de cable Atenuación; el medio se comparte entre vecinos
Fibra multimodo Luz 10 a 100 Gb/s, de 100 m a unos cientos de metros Dispersión: los modos de luz llegan en momentos distintos
Fibra monomodo Luz 100 Gb/s o más, decenas de km sin amplificar Atenuación, muy baja; dispersión
Radio local (Wi-Fi) Ondas de radio Cientos de Mb/s a varios Gb/s, decenas de metros Paredes, distancia, interferencia, medio compartido
Radio celular Ondas de radio Decenas de Mb/s a más de 1 Gb/s, cientos de metros a km Distancia, espectro disponible, usuarios por celda
Satélite Ondas de radio Decenas a cientos de Mb/s Retardo de propagación, clima, cielo despejado

Las cifras son órdenes de magnitud, no especificaciones: cambian con cada generación de equipos. Lo que conviene recordar son las causas.

  • Atenuación. La señal se debilita con la distancia. En cobre se pierde mucho más que en fibra: por eso el par trenzado se queda en 100 m y una fibra monomodo llega a decenas de kilómetros sin amplificar.
  • Interferencia. Otras señales se suman a la nuestra. El par trenzado está trenzado justamente para que el ruido externo afecte por igual a los dos hilos y se cancele al restarlos. La luz en una fibra no sufre interferencia electromagnética.
  • Medio compartido. En el cobre de una Ethernet moderna cada enlace es de dos equipos. En radio, todos los que están cerca comparten el mismo aire. Esto crea el problema de acceso múltiple de la Sesión B.

Idea clave. El medio decide la tasa de errores. En una fibra los errores son rarísimos; en un enlace de radio son el día a día. Por eso Ethernet solo detecta errores, mientras Wi-Fi además retransmite en la capa de enlace.

Ancho de banda, modulación y codificación

La palabra bandwidth (ancho de banda) tiene dos significados que conviene separar:

  • En señales, es el rango de frecuencias que usa un enlace, y se mide en hertz. Un canal Wi-Fi de 20 MHz tiene 20 MHz de ancho de banda.
  • En redes, se usa para la tasa de bits, en bits por segundo. Es lo que decimos al hablar de “un enlace de 1 Gb/s”.

Los dos están relacionados. Modulación es la forma de representar bits cambiando una propiedad de la señal: su amplitud, su frecuencia o su fase. Un símbolo es uno de los estados que la señal puede tomar durante un instante. Si hay más estados posibles, cada símbolo lleva más bits: con 2 estados lleva 1 bit, con 16 lleva 4, y con 256 (lo que se llama 256-QAM) lleva 8. Pero los estados quedan más cerca entre sí y el ruido los confunde con mayor facilidad. Por eso un celular cambia de modulación según la calidad de la señal.

Shannon puso un límite a todo esto. La capacidad máxima de un canal, sin importar cuán ingeniosa sea la modulación, es

C = B · log2(1 + S/N)

donde B es el ancho de banda en hertz y S/N es la relación entre la potencia de la señal y la del ruido. Por ejemplo, un canal de 20 MHz con una relación señal a ruido de 30 dB (S/N = 1000, porque 30 dB = 10^(30/10)) permite como mucho 20 × 10^6 × log2(1001) ≈ 199 Mb/s. Para subir esa cifra hay dos caminos: más espectro o mejor señal. Por eso los operadores pagan tanto por el espectro.

La codificación agrega reglas sobre qué bits se ponen en el medio. Sirve para que el receptor pueda mantener su reloj sincronizado (necesita ver cambios en la señal) y, en algunos casos, para corregir errores. Tiene un costo: la tasa en el medio es mayor que la tasa útil. La Ethernet de 10 Gb/s por fibra, por ejemplo, usa la codificación 64b/66b: agrega 2 bits por cada 64, de modo que en la fibra hay 10,3125 Gb/s para entregar 10 Gb/s útiles.

En la práctica. Cuando un enlace “de 1 Gb/s” entrega unos 950 Mb/s en una medición con TCP, no hay nada malo. Cada frame de 1500 bytes de datos ocupa 1538 bytes en el cable y, de esos 1500, TCP e IP se llevan 40. Quedan 1460/1538 ≈ 95 % del enlace. Sacaremos esas cuentas con el formato de Ethernet en la Sesión B.

Dos tiempos que no hay que confundir

De la semana 1 ya conocen estos dos retardos. Los repetimos porque el acceso múltiple depende de la relación entre ellos.

  • Tiempo de transmisión: lo que tarda en salir el frame completo. Es L / R, con L en bits y R en bits por segundo. 1500 bytes a 1 Gb/s son 12 µs.
  • Tiempo de propagación: lo que tarda un bit en llegar al otro lado. Es d / v, con v ≈ 2 × 10^8 m/s en cobre y fibra. 100 m son 0,5 µs.

La velocidad del enlace no cambia la propagación: un enlace más rápido pone los bits más juntos, pero no los hace viajar más rápido.

Qué hace la capa de enlace

La capa física nos deja una corriente de bits con errores entre dos equipos. La capa de enlace convierte eso en algo más útil: una forma de entregar un paquete completo al vecino. Su unidad de datos es el frame (también se le dice trama). Un frame encapsula un datagrama de la capa de red, igual que un sobre contiene una carta.

Sus servicios son estos, y no todas las tecnologías dan todos:

  • Framing. Indicar dónde empieza y termina cada frame, y agregar un header con direcciones y un campo para detectar errores.
  • Acceso al medio. Decidir quién puede transmitir si el medio es compartido.
  • Entrega entre vecinos. Hacer llegar el frame al otro extremo de este enlace, nada más. Lo que pasa después ya es responsabilidad de otro enlace.
  • Detección de errores. Descartar frames dañados en lugar de entregarlos.
  • Entrega confiable, opcional. Retransmitir los frames perdidos. Wi-Fi lo hace, porque pierde mucho. Ethernet no: en un cable los errores son tan raros que el costo no se justifica y se deja a TCP.

La idea importante es el alcance. IP entrega de un extremo al otro de Internet; el enlace entrega solo hasta el siguiente equipo. Un datagrama que viaja de Lima a Tokio cruza decenas de enlaces distintos, quizá Wi-Fi, Ethernet, fibra de larga distancia. En cada uno viaja dentro de un frame distinto, con sus propias direcciones, y esos frames nacen y mueren en cada salto. Lo veremos con una captura real al final del capítulo.

Idea clave. La capa de enlace trabaja de un salto a la vez. Cada router desenvuelve el frame que llega, mira el datagrama y lo envuelve en un frame nuevo para el siguiente enlace.

Cómo marcar dónde empieza un frame

El receptor recibe bits: ¿cómo sabe dónde empieza un frame? Hay tres técnicas clásicas.

  • Contar. El header dice cuántos bytes vienen. Es simple, pero si ese campo se daña se pierde el rumbo y no se sabe dónde buscar el siguiente frame.
  • Marcas especiales. Un byte reservado indica el inicio y el fin. Si ese byte aparece entre los datos, se le antepone un byte de escape y el receptor lo quita. Es lo que se llama byte stuffing. PPP funciona así.
  • Violar la codificación. Usar una señal que no representa ningún dato válido.

Ethernet usa un preámbulo: 7 bytes de 10101010, que permiten sincronizar el reloj del receptor, y un byte de inicio, 10101011, que marca el comienzo. Entre frames tiene que quedar un silencio mínimo, y la señal misma indica cuándo termina el frame.

Dónde vive

La mayor parte de la capa de enlace está en la tarjeta de red (network interface card, NIC): un chip que calcula el CRC, arma y revisa frames y copia los datos a la memoria del host. Solo una parte corre en el driver del sistema operativo. Por eso la capa de enlace es la frontera entre software y hardware, y por eso se pueden usar algoritmos como el CRC, que son baratos en hardware. En el curso no tenemos NIC físicas: cada nodo tiene interfaces virtuales (veth) y todo lo hace el kernel. Ese detalle va a explicar algunas cosas de las capturas.

Detección de errores

La detección de errores (error detection) responde a una pregunta: ¿llegó el frame tal como salió? El modelo es el siguiente. El emisor tiene D bits de datos y calcula con ellos unos bits redundantes, EDC (error-detection code). Envía los dos juntos. El receptor recibe D' y EDC', que pueden haber cambiado, y comprueba si coinciden.

emisor:    D  →  calcula EDC  →  envía D, EDC
                                     │  enlace con errores
receptor:                            ▼
           recibe D', EDC'  →  ¿EDC' coincide con el EDC de D'?

Conviene leer bien lo que garantiza. Si la comprobación falla, hubo un error. Si la comprobación pasa, probablemente no lo hubo: siempre hay patrones de error que no se notan. Un buen método hace que esos patrones sean muy improbables, y el costo se paga en bits extra y en cálculo.

También importa el tipo de error. En muchos enlaces los errores no llegan aislados sino en ráfagas (bursts): un golpe de ruido daña varios bits seguidos. Un método que resiste errores sueltos pero no ráfagas sirve de poco.

Paridad

Es el método más simple. Con paridad par, se agrega un bit de modo que la cantidad total de unos sea par.

datos:     1 0 1 1 0 0 1   (cuatro unos)
paridad:   0
enviado:   1 0 1 1 0 0 1 0

El receptor cuenta los unos. Si salen impares, hubo un error. Pero si cambian dos bits, la cuenta sigue siendo par y nadie se entera: la paridad detecta solo una cantidad impar de errores. Si los errores llegan en ráfagas, una ráfaga larga tiene cerca del 50 % de probabilidad de pasar inadvertida.

La paridad bidimensional ordena los bits en una matriz y agrega un bit de paridad por fila y otro por columna.

             paridad de fila
1 0 1 0  |  0                       1 0 1 0  |  0
1 1 1 1  |  0     se daña un bit    1 1 0 1  |  0   ← esta fila falla
0 1 1 0  |  0     ──────────────►   0 1 1 0  |  0
─────────                           ─────────
0 0 1 1  |  0                       0 0 1 1  |  0
                                        ▲ esta columna falla

Si se daña un solo bit, fallan una fila y una columna, y su cruce dice cuál es. Así que no solo se detecta el error: se corrige. Esa capacidad de reparar sin retransmitir se llama corrección de errores hacia adelante (forward error correction, FEC). Vale la pena en enlaces donde retransmitir es muy lento (un satélite) o imposible (una transmisión a miles de receptores). Este esquema también detecta cualquier error de 2 o de 3 bits, aunque no puede corregirlos. Con 4 bits en las esquinas de un rectángulo, todas las filas y columnas siguen pares y el error pasa.

Checksum

Un checksum trata los datos como una secuencia de números y los suma. El checksum de Internet, que usan IPv4, UDP y TCP, divide los datos en palabras de 16 bits, las suma con acarreo circular (lo que se pasa de 16 bits se vuelve a sumar abajo) y envía el complemento a uno del resultado. Por ejemplo, con dos palabras:

  0x4500
+ 0x0054
--------
  0x4554    complemento: 0xBAAB, que es lo que se envía

El receptor suma todo, checksum incluido, y debe obtener 0xFFFF. Se calcula con unas pocas líneas de software, que recorren los datos una sola vez. Es barato. Pero es débil, como veremos: como la suma es conmutativa, si dos palabras se intercambian el resultado es el mismo. Volveremos a usarlo al ver transporte.

CRC

El CRC (cyclic redundancy check) es el método que usan Ethernet y Wi-Fi. Parece más complicado que un checksum, pero es solo una división.

Definición. Emisor y receptor acuerdan un generador G de r + 1 bits, cuyo primer bit es 1. Para los datos D, el emisor elige r bits R de modo que la cadena D seguida de R sea divisible exactamente entre G. El receptor divide lo que recibe entre G. Si el residuo no es cero, hubo un error.

Aritmética módulo 2. Toda la cuenta se hace sin acarreos: sumar y restar son lo mismo, y los dos son XOR.

  1011        1011
+ 0101      - 0101
------      ------
  1110        1110

Dividir se hace igual que a mano en la escuela, pero con XOR en vez de resta. El generador “cabe” en cualquier grupo que empiece con 1.

Por qué el residuo sirve. Agregar r ceros a D es multiplicar por 2^r. La cadena que se envía es T = D·2^r XOR R. Queremos que T sea múltiplo de G, así que R tiene que ser el residuo de dividir D·2^r entre G. Como restar y sumar son lo mismo, al “sumarle” el residuo lo estamos quitando, y la división queda exacta.

Ejemplo resuelto. Datos D = 1101011 y generador G = 1001. Como G tiene 4 bits, r = 3. Se agregan tres ceros a los datos y se divide. El programa crc.c muestra cada paso: la línea de arriba es el estado actual y la de abajo es el generador, alineado con el primer 1 que queda.

$ build/crc 1101011 1001
  1101011000
  1001  (XOR en la posición 0)
  0100011000
   1001  (XOR en la posición 1)
  0000111000
      1001  (XOR en la posición 4)
  0000011100
       1001  (XOR en la posición 5)
  0000001110
        1001  (XOR en la posición 6)
  0000000111
CRC (r = 3 bits): 111
se transmite: 1101011111

Los últimos tres bits, 111, son el CRC. El receptor recibe 1101011111, lo divide tal cual, sin agregar nada, y obtiene residuo cero.

$ build/crc -c 1101011111 1001
  1101011111
  1001  (XOR en la posición 0)
  0100011111
   1001  (XOR en la posición 1)
  0000111111
      1001  (XOR en la posición 4)
  0000011011
       1001  (XOR en la posición 5)
  0000001001
        1001  (XOR en la posición 6)
  0000000000
residuo: 000
sin errores detectados

Si un bit cambia en el camino, el residuo deja de ser cero:

$ build/crc -c 1101011011 1001
  ...
residuo: 100
ERROR detectado

Para usarlo ustedes, compilen con make dentro de code/week-03. Conviene que hagan la cuenta a mano con otros datos y comparen con el programa.

Qué errores detecta. Lo más claro es verlo con polinomios. Una cadena de bits es un polinomio con coeficientes 0 o 1: 1001 es x^3 + 1. Si lo enviado es T y el error es un patrón E (con un 1 en cada bit dañado), el receptor recibe T XOR E. Como T es múltiplo de G, el residuo solo depende de E:

Un error pasa inadvertido si y solo si E es múltiplo de G.

De ahí salen las garantías. Un CRC de r bits, con un generador que empieza y termina en 1:

  • Detecta siempre cualquier ráfaga de hasta r bits, y por lo tanto cualquier error de un solo bit. Una ráfaga de largo b ≤ r es x^k por un polinomio de grado b - 1 < r. Como G termina en 1, no comparte factores con x^k: para ser múltiplo de G, tendría que serlo el otro factor. Y eso es imposible, porque su grado es menor que el de G.
  • Detecta cualquier cantidad impar de errores, si G tiene a x + 1 como factor. Es el caso de 1001, porque x^3 + 1 = (x + 1)(x^2 + x + 1).
  • Si los bits se dañan al azar, pasa inadvertida una fracción cercana a 1/2^r de los patrones posibles.

Las pruebas de test_crc.py revisan estas afirmaciones con G = 1001 en todos los patrones de error posibles para la palabra de 10 bits del ejemplo.

1001 sirve para aprender, pero es un mal generador. El error 1001, dos bits a distancia 3, es múltiplo de sí mismo y por lo tanto no se detecta. Aquí lo vemos, con ese patrón desplazado tres posiciones sobre la palabra del ejemplo:

$ build/crc -c 1100010111 1001
  ...
residuo: 000
sin errores detectados

Los bits 3 y 6 están dañados y el receptor cree que todo está bien. Elegir un buen generador es un problema matemático serio. Por eso nadie los inventa: se usan los de los estándares.

CRC-32. Ethernet usa un CRC de 32 bits. Su generador es el polinomio de grado 32 cuyo valor es 0x04C11DB7 (sin contar el bit más alto). Detecta cualquier ráfaga de hasta 32 bits, y solo deja pasar del orden de 1 de cada 2^32 (unos 4 mil millones) de los patrones al azar. Se implementa en hardware con un registro de desplazamiento y unas compuertas XOR, sin costo apreciable por bit.

Cuál detecta qué

Para comparar, el programa detect.py daña frames aleatorios de 64 bytes de distintas formas y cuenta cuántos daños no detecta cada método, usando el zlib.crc32 de Python para el CRC-32.

$ python3 detect.py
20000 intentos por fila, frames de 64 bytes. Daños NO detectados:
daño                        paridad  checksum   crc32
1 bit                             0         0       0
2 bits                        20000       616       0
ráfaga de 16 bits              9957         0       0
ráfaga de 32 bits             10033         1       0
ráfaga de 100 bits             9958         1       0
intercambio de palabras       20000     20000       0

La semilla es fija, así que el resultado se repite. Cada fila tiene su historia:

  • 2 bits. La paridad falla el 100 % de las veces. El checksum falla en 616 de 20 000, cerca de 1/32: pasa cuando los dos bits caen en la misma posición de dos palabras de 16 bits (probabilidad 1/16) y cambian en sentidos opuestos, uno de 0 a 1 y el otro de 1 a 0 (probabilidad 1/2). Así la suma no cambia.
  • Ráfagas. La paridad falla cerca de la mitad. El checksum casi siempre detecta.
  • Intercambio de palabras. Dos palabras de 16 bits cambian de lugar. Ni la paridad ni el checksum lo notan, porque la suma no depende del orden. Este daño no es teórico: una memoria o un driver con errores puede causarlo.

Que el CRC-32 marque 0 en toda la columna no significa que sea infalible. Con 20 000 intentos y una probabilidad de 1 en 4 mil millones, lo esperable es cero.

Por qué el enlace usa CRC y el transporte un checksum

Si el CRC es mucho mejor, ¿por qué TCP y UDP siguen con un checksum más débil?

  • Dónde se calcula. En el enlace, el CRC lo calcula un chip, y hacerlo en hardware es casi gratis. El transporte se diseñó para correr en software, en computadoras de los años 70 y 80, donde un CRC bit por bit salía caro y una suma salía barata.
  • Qué protege cada uno. El CRC protege un enlace, y se recalcula en cada salto. Un error que ocurra dentro de un router (al copiar en memoria, por ejemplo) no lo ve ningún CRC, porque el router recalcula uno nuevo y correcto sobre datos ya dañados. El checksum de transporte, en cambio, va de extremo a extremo. Es el argumento end-to-end de la semana 2: algunas garantías solo se pueden dar en los extremos.
  • Los errores que le llegan. Como los enlaces ya filtraron casi todos los errores de transmisión, el checksum solo es una segunda red, y no necesita ser excelente.

Los dos juntos cubren cosas distintas: el CRC detecta muy bien los errores del cable, y el checksum detecta, aunque no tan bien, lo que pasa entre los extremos.

Error común. Creer que un frame con CRC correcto garantiza datos correctos para la aplicación. El CRC solo dice que este enlace no dañó el frame. Si la aplicación necesita certeza, tiene que verificar por su cuenta, por ejemplo con un hash del archivo.

Acceso múltiple

Empezamos la Sesión B con el problema de los enlaces de broadcast: varios nodos comparten un solo medio y lo que uno transmite lo reciben todos. Es lo que ocurre en el aire de Wi-Fi, en el cable coaxial de una red de cable y en la Ethernet original. Si dos nodos transmiten a la vez, las señales se mezclan y nadie entiende nada: hay una colisión (collision).

Un protocolo de acceso múltiple (multiple access) es el conjunto de reglas para decidir quién transmite. Y no hay un canal aparte para coordinarse: las reglas usan el mismo medio. Lo ideal, para un canal de R bits por segundo, sería esto:

  1. Si un solo nodo quiere transmitir, lo hace a R.
  2. Si quieren M nodos, cada uno obtiene en promedio R/M.
  3. Sin un coordinador central que pueda fallar.
  4. Simple y barato.

Hay tres familias, que se diferencian en cómo reparten el canal.

Particionar el canal

Se divide el canal en pedazos fijos y cada nodo recibe el suyo.

  • TDM (time-division multiplexing). El tiempo se parte en ranuras, y cada nodo tiene las suyas, por turnos fijos.
  • FDM (frequency-division multiplexing). Cada nodo usa una banda de frecuencias distinta.
  • CDMA (code-division multiple access). Todos usan a la vez la misma banda, con códigos que permiten separarlos.

No hay colisiones y el reparto es justo. Pero fallan con el requisito 1: un nodo solo, en un canal con 10 ranuras, usa una ranura y deja las otras nueve vacías, y llega a R/10 aunque nadie más quiera transmitir. Es muy buena opción con mucha carga constante y mala con tráfico en ráfagas, que es lo habitual en redes de datos. Es el mismo argumento de la semana 1 entre conmutación de circuitos y de paquetes.

Acceso aleatorio: ALOHA

Aquí no hay reparto. Cada nodo transmite a toda velocidad, y si hay colisión, espera un tiempo aleatorio y vuelve a intentar. La aleatoriedad es lo que evita que los mismos dos nodos vuelvan a chocar para siempre.

ALOHA nació en la Universidad de Hawái, alrededor de 1970, para unir por radio terminales repartidos por las islas. Su versión ranurada divide el tiempo en ranuras del tamaño de un frame, y las transmisiones empiezan solo al inicio de una ranura. Si hubo colisión, en cada ranura siguiente el nodo reintenta con probabilidad p.

¿Qué fracción del canal se aprovecha? Supongamos N nodos activos, y que cada uno transmite en una ranura con probabilidad p. Una ranura se aprovecha si exactamente uno transmite. La probabilidad es

S = N · p · (1 - p)^(N-1)

Derivando se ve que el máximo está en p = 1/N. Sea G = N·p la carga ofrecida: los intentos de transmisión por ranura. Con N grande, (1 - 1/N)^(N-1) tiende a 1/e, y queda:

S = G · e^(-G)     máximo en G = 1:   S = 1/e ≈ 0.368

En el mejor caso se aprovecha el 37 % del canal. El resto son ranuras vacías (otro 37 %) y colisiones.

En ALOHA puro no hay ranuras: se transmite apenas hay algo que enviar. Eso duplica el período de riesgo. Un frame que empieza en t choca con cualquier otro que haya empezado en el tiempo de frame anterior, y también con cualquiera que empiece durante su propia transmisión. Por eso:

S = G · e^(-2G)    máximo en G = 1/2:   S = 1/(2e) ≈ 0.184

Podemos revisar estas fórmulas con una simulación. aloha.c simula 50 nodos en el caso ranurado (400 000 ranuras) y, en el caso puro, 400 000 intentos en instantes al azar. Las dos columnas de sim vienen de la simulación; las otras dos, de la fórmula.

$ build/aloha
   G | ranurado sim  G*e^-G | puro sim  G*e^-2G
0.10 |        0.091   0.090 |    0.082    0.082
0.25 |        0.196   0.195 |    0.152    0.152
0.50 |        0.304   0.303 |    0.184    0.184
0.75 |        0.358   0.354 |    0.168    0.167
1.00 |        0.372   0.368 |    0.135    0.135
1.50 |        0.336   0.335 |    0.074    0.075
2.00 |        0.271   0.271 |    0.036    0.037
3.00 |        0.145   0.149 |    0.007    0.007

Las columnas de sim usan un generador con semilla fija, y por eso el resultado es igual en cualquier máquina. Las diferencias en el caso ranurado se deben a que la fórmula G·e^(-G) supone N infinito, y simulamos 50 nodos.

Gráfico de la fracción aprovechada del canal, S, según la carga ofrecida G, de 0 a 3, con los puntos simulados y las curvas de las fórmulas. ALOHA ranurado sube hasta 0,368 en G = 1 y baja a 0,145 en G = 3. ALOHA puro sube hasta 0,184 en G = 1/2 y baja hasta casi cero en G = 3. A la derecha de cada máximo, más carga da menos throughputGráfico de la fracción aprovechada del canal, S, según la carga ofrecida G, de 0 a 3, con los puntos simulados y las curvas de las fórmulas. ALOHA ranurado sube hasta 0,368 en G = 1 y baja a 0,145 en G = 3. ALOHA puro sube hasta 0,184 en G = 1/2 y baja hasta casi cero en G = 3. A la derecha de cada máximo, más carga da menos throughput
La simulación cae sobre las fórmulas. Pasado el máximo, cada intento de más solo agrega colisiones: el canal no se queda en su mejor punto, sino que colapsa.

Lo más importante de la tabla es qué pasa a la derecha del máximo: si se ofrece más carga, se obtiene menos. Con G = 3, casi todo son colisiones. Una red de acceso aleatorio sobrecargada no se queda en su máximo, sino que colapsa. Volveremos a ver esta forma en control de congestión.

Acceso aleatorio: CSMA

ALOHA tiene un defecto de mala educación: transmite aunque otro esté hablando. CSMA (carrier sense multiple access) agrega dos reglas que aplicamos en una conversación:

  • Escuchar antes de hablar. Si el canal está ocupado, se espera.
  • Dejar de hablar si otro empezó (detección de colisión, CD). Si uno detecta otra señal mientras transmite, corta.

Si todos escuchan antes de hablar, ¿por qué sigue habiendo colisiones? Por el retardo de propagación. Si A empieza a transmitir, B, a cierta distancia, sigue oyendo silencio hasta que le llegue la señal, y puede empezar también. Cuanto mayor sea el retardo de propagación respecto al tiempo de transmisión, peor.

tiempo   A                                    B
  │      │██  A empieza                       │
  │      │███                                 │   B oye silencio y empieza
  │      │████                             ███│
  │      │█████▓▓                       ▓▓████│   las señales se cruzan
  ▼      │██▓▓▓▓▓▓                     ▓▓▓▓▓██│   A y B detectan la colisión

CSMA/CD en la Ethernet original

CSMA/CD es el protocolo de la Ethernet clásica, con un cable compartido. Una tarjeta de red hace esto:

  1. Toma un frame para enviar. Si el canal está libre, empieza. Si está ocupado, espera a que se libere y entonces empieza.
  2. Mientras transmite, escucha. Si termina sin detectar nada, el frame salió bien.
  3. Si detecta otra señal, deja de transmitir y envía una breve señal de interferencia (jam), para que todos noten la colisión.
  4. Espera un tiempo aleatorio, según el retroceso exponencial binario (binary exponential backoff), y vuelve al paso 1.

El retroceso se calcula así. Después de la colisión número n de un frame, se elige K al azar en {0, 1, …, 2^m - 1}, con m = min(n, 10), y se espera K × 512 tiempos de bit. Tras 16 intentos, se descarta el frame.

Colisión Valores posibles de K Espera máxima a 10 Mb/s
1.ª 0, 1 51,2 µs
2.ª 0 a 3 153,6 µs
3.ª 0 a 7 358,4 µs
10.ª en adelante 0 a 1023 52,4 ms

La idea es elegante: nadie necesita saber cuántos nodos están compitiendo. Con pocas colisiones se reintenta rápido. Si los choques siguen, el rango crece, y cada intento se separa más, hasta que uno gana.

¿Por qué hay un tamaño mínimo de frame? Para que el emisor sepa que hubo colisión, tiene que seguir transmitiendo cuando la señal del otro extremo vuelva. En el peor caso, eso tarda dos tiempos de propagación. La Ethernet de 10 Mb/s fijó 512 bits (64 bytes), que tardan 51,2 µs en salir: un frame más corto podría terminar antes de que el emisor oiga la colisión, y creería que todo salió bien. A 2 × 10^8 m/s, 51,2 µs de ida y vuelta dan unos 5 km de ida, y los retardos de los repetidores reducían ese máximo a unos 2.5 km. Esa es la razón de los 64 bytes, que siguen en el estándar.

Eficiencia. Si a = d_prop / d_trans, la fracción aprovechada de CSMA/CD es aproximadamente 1 / (1 + 5a). Con frames de 1500 bytes a 10 Mb/s (d_trans = 1200 µs) y d_prop = 10 µs, da 96 %. Es mucho mejor que ALOHA, porque una colisión se interrumpe pronto y no se pierde el tiempo de un frame entero. Ahora vemos por qué los dos tiempos importan: cuando a tiende a 0, las colisiones casi no cuestan.

En la práctica. Wi-Fi también es acceso aleatorio, pero con CSMA/CA, de collision avoidance. Un equipo de radio no puede oír a otros mientras transmite, porque su propia señal es mucho más fuerte. Por eso no detecta colisiones: intenta evitarlas y usa acuses de recibo. Lo estudiaremos en la semana 14.

Tomar turnos

La tercera familia intenta lo mejor de las otras dos.

  • Polling. Un nodo maestro invita a los demás, por turnos, a transmitir. Elimina las colisiones, pero el maestro es un punto único de falla y las invitaciones consumen tiempo.
  • Token passing. Un mensaje especial, el token, pasa de nodo en nodo. Solo quien tiene el token transmite. No hay maestro, pero un token perdido obliga a un procedimiento de recuperación.
Familia Ejemplos Solo un nodo activo Muchos nodos activos Colisiones
Particionar TDM, FDM, CDMA Mal: obtiene R/N Bien: reparto justo No
Acceso aleatorio ALOHA, CSMA/CD, CSMA/CA Bien: obtiene R Peor Sí
Tomar turnos Polling, token passing Casi R Bien No

Las redes reales mezclan estas ideas. Una red de cable (DOCSIS), por ejemplo, reparte el coaxial por frecuencias, usa ranuras de tiempo asignadas por el equipo del proveedor y permite pedir esas ranuras con acceso aleatorio.

¿Y hoy en Ethernet?

La Ethernet con un cable compartido ya no existe en la práctica. Hoy cada host tiene un cable propio hasta un switch, con un par de hilos o de fibras para cada dirección (full-duplex). Nadie comparte nada: no puede haber colisiones. CSMA/CD simplemente no se usa, aunque el formato de frame siga siendo el de 1980. Solo sobrevive como modo opcional para equipos que trabajan en half-duplex, y las velocidades de 10 Gb/s en adelante ya ni siquiera lo incluyen.

Idea clave. Un switch resolvió el acceso múltiple por eliminación: convirtió un medio compartido en muchos enlaces punto a punto. El problema sigue vivo en el aire (Wi-Fi, 5G), donde compartir el medio es inevitable.

Ethernet

Ethernet nació en 1973 en Xerox PARC, con Bob Metcalfe y David Boggs, y sigue dominando las redes cableadas. Pasó de 2,94 Mb/s a 800 Gb/s, de un cable coaxial compartido a fibra y a switches. Casi todo cambió, salvo una cosa: el formato del frame. Esa es la razón de que sobreviviera: cada velocidad nueva sigue hablando con las capas de arriba de la misma manera.

El formato del frame

bytes:   8          6        6        2        46 a 1500       4
      ┌──────────┬────────┬────────┬───────┬───────────────┬─────┐
      │preámbulo │destino │ origen │ Ether │     datos     │ FCS │
      │  y SFD   │  MAC   │  MAC   │ Type  │               │ CRC │
      └──────────┴────────┴────────┴───────┴───────────────┴─────┘
                 └───────── lo que se ve en una captura ──────┘
  • Preámbulo y SFD (8 bytes). Sincronizan al receptor. La tarjeta de red los quita.
  • Dirección de destino y de origen (6 bytes cada una). Direcciones MAC.
  • EtherType (2 bytes). Dice a qué protocolo de arriba entregar los datos: 0x0800 IPv4, 0x0806 ARP, 0x86DD IPv6. Es la multiplexación de la capa de enlace, igual que los puertos en el transporte.
  • Datos (46 a 1500 bytes). El datagrama. Los 1500 son la MTU clásica de Ethernet. Si hay menos de 46, se rellena con ceros hasta llegar a 46 (padding).
  • FCS, frame check sequence (4 bytes). El CRC-32, calculado sobre las direcciones, el EtherType y los datos.

Sin contar el preámbulo, un frame mide entre 64 y 1518 bytes. Es el mínimo que venía de CSMA/CD.

El servicio que da Ethernet es sin conexión y no confiable. No hay handshake antes de enviar. El receptor no devuelve ningún ACK: si un frame llega dañado, lo descarta sin avisar. Si los datos eran de TCP, TCP notará el hueco y retransmitirá.

Tampoco hay un campo TTL: un frame que da vueltas en un ciclo no muere nunca. Eso nos va a dar un buen problema en un rato.

En la práctica. Con el formato podemos calcular lo que queda para los datos. Por cada frame de 1500 bytes de datos, en el cable hay 8 de preámbulo, 14 de direcciones y EtherType, 4 de FCS y 12 de silencio obligatorio entre frames: 1538 en total. En un enlace de 1 Gb/s, la tasa máxima de datos de IP es 1500/1538 × 1 Gb/s ≈ 975 Mb/s.

Las variantes de Ethernet tienen nombres como 1000BASE-T o 10GBASE-LR: la velocidad (1000 Mb/s, 10 Gb/s), BASE por banda base y el medio (T par trenzado; LR, fibra monomodo de largo alcance). Para las capas de arriba, todas son iguales.

Direcciones MAC

Una dirección MAC tiene 48 bits y se escribe en hexadecimal: 02:00:00:00:00:01. Identifica una interfaz, no un equipo: una laptop con Wi-Fi y cable tiene dos.

Los fabricantes compran bloques: los primeros 24 bits (el OUI) identifican al fabricante, y los otros 24 los asigna él, de modo que en principio cada tarjeta sale de fábrica con una MAC única. Dos bits del primer byte tienen un significado especial:

  • El bit menos significativo (I/G) indica una dirección de grupo. Si vale 1, el frame va a varios receptores. La dirección de broadcast ff:ff:ff:ff:ff:ff, todos unos, es un caso particular.
  • El siguiente bit (U/L) indica una dirección administrada localmente, si vale 1: alguien la puso a mano, y no viene de un fabricante.

En el laboratorio les dimos a los hosts direcciones como 02:00:00:00:00:01. Ese 02 tiene el bit U/L en 1 y el I/G en 0: es una dirección unicast administrada localmente. Usamos direcciones fijas para que las salidas de estos apuntes coincidan con las suyas.

Una tarjeta de red normalmente acepta los frames dirigidos a su MAC, al broadcast y a sus grupos, y descarta el resto sin molestar al sistema operativo. tcpdump la pone en modo promiscuo para ver todo lo que llega.

MAC frente a IP

Si cada interfaz ya tiene una dirección, ¿para qué dos? Porque hacen trabajos distintos.

Dirección MAC Dirección IP
Capa Enlace Red
Tamaño 48 bits 32 bits (IPv4), 128 (IPv6)
Estructura Plana: el valor no dice dónde está Jerárquica: el prefijo dice en qué red está
Alcance Un enlace o una red local Toda la Internet
Quién la asigna El fabricante El administrador de la red, o DHCP
Cambia al mudarse No Sí
Analogía DNI: identifica, sin decir dónde vive Dirección postal: dice cómo llegar

La MAC es plana: 02:00:00:00:00:01 no da ninguna pista sobre su ubicación. Por eso un switch tiene que aprender por dónde está cada una, con una entrada por cada MAC. Eso funciona para miles de direcciones, pero no para los miles de millones de Internet. La IP es jerárquica, así que un router puede resolver con una sola entrada a toda una red.

Hay otra razón. Si solo existiera IP, una tarjeta de red tendría que interrumpir al sistema operativo por cada frame que pasa, solo para saber si es para ella. Y si solo existiera MAC, no podríamos cambiar de red sin cambiar de identidad, y la capa de red quedaría atada a una tecnología. Con dos niveles, IP funciona sobre cualquier enlace.

ARP

Ya tenemos el problema planteado. Un host quiere enviar un datagrama a 10.203.0.2, que está en su misma subred. Para armar el frame necesita la MAC del destino, y solo sabe su IP. ARP (Address Resolution Protocol, RFC 826) resuelve esa traducción: dada una IP de la red local, ¿qué MAC le corresponde?

Cómo funciona

  1. El host A revisa su tabla ARP (una cache). Si tiene la IP, usa esa MAC y listo.
  2. Si no, envía una consulta: un frame a la dirección de broadcast con el mensaje “¿quién tiene 10.203.0.2? Avísenle a 10.203.0.1”.
  3. Todos los nodos de la red local reciben la consulta. Solo el owner de esa IP responde.
  4. La respuesta va en unicast, directo a quien preguntó: “10.203.0.2 está en 02:00:00:00:00:02”.
  5. A guarda el dato en su tabla por un tiempo y envía el datagrama que tenía en espera.

¿Por qué se pregunta por broadcast y se responde por unicast? La pregunta se hace a gritos porque A no sabe a quién dirigirla: justo eso es lo que está averiguando. La respuesta no necesita gritar, porque la pregunta ya traía la MAC de A. Molestar a todos una segunda vez no serviría de nada.

ARP no necesita configuración: una tabla ARP se llena sola. Tampoco está exactamente en una capa: sus mensajes van dentro de un frame de Ethernet, con EtherType 0x0806, pero contienen direcciones IP. Se suele poner en la frontera entre red y enlace.

Error común. Creer que ARP pasa por routers o que funciona con cualquier IP. ARP solo llega hasta donde llega un broadcast, es decir, hasta el borde de la red local. Si un host necesita la MAC de una IP en otra subred, se equivoca de pregunta. Ya vamos a ver qué pregunta correcta hace.

Un intercambio real

Vamos a verlo. La topología del laboratorio tiene un switch sw1 y cuatro hosts, con direcciones de 10.203.0.1 a 10.203.0.4.

   h1             h2              h3              h4
10.203.0.1     10.203.0.2      10.203.0.3      10.203.0.4
…:00:01        …:00:02         …:00:03         …:00:04
    \ eth1        | eth2          | eth3         / eth4
     \            |               |             /
      └───────────┴──────  sw1  ──┴────────────┘
                       (bridge br0)

Se levanta con make lan, dentro de code/week-03, y se entra a cada nodo con docker exec. En adelante escribimos h1$ comando para abreviar docker exec clab-w03-lan-h1 comando. Si el comando lleva tuberías, en la terminal hay que envolverlo: docker exec clab-w03-lan-h1 sh -c '...'.

Al empezar, la tabla ARP de h1 está vacía en eth1, la interfaz de la red de datos. Cada nodo tiene además eth0, la red de administración de Containerlab, que no es parte del laboratorio.

h1$ ip -br addr show eth1
eth1@if3476      UP             10.203.0.1/24
h1$ ip neigh show dev eth1

Dejamos tcpdump escuchando en h1 y hacemos ping a h2. Los tcpdump con & se dejan en otra terminal, o se lanzan con docker exec -d, y se detienen con Ctrl+C o con pkill tcpdump. Al leer el archivo, la opción -e muestra las MAC y -t oculta la hora, para que las líneas quepan.

h1$ tcpdump -U -n -i eth1 -w /tmp/arp.pcap "arp or icmp" &
h1$ ping -c 2 10.203.0.2
PING 10.203.0.2 (10.203.0.2): 56 data bytes
64 bytes from 10.203.0.2: seq=0 ttl=64 time=0.193 ms
64 bytes from 10.203.0.2: seq=1 ttl=64 time=0.186 ms

--- 10.203.0.2 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.186/0.189/0.193 ms
h1$ tcpdump -t -e -n -r /tmp/arp.pcap
02:00:00:00:00:01 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 10.203.0.2 tell 10.203.0.1, length 28
02:00:00:00:00:02 > 02:00:00:00:00:01, ethertype ARP (0x0806), length 42: Reply 10.203.0.2 is-at 02:00:00:00:00:02, length 28
02:00:00:00:00:01 > 02:00:00:00:00:02, ethertype IPv4 (0x0800), length 98: 10.203.0.1 > 10.203.0.2: ICMP echo request, id 83, seq 0, length 64
02:00:00:00:00:02 > 02:00:00:00:00:01, ethertype IPv4 (0x0800), length 98: 10.203.0.2 > 10.203.0.1: ICMP echo reply, id 83, seq 0, length 64
02:00:00:00:00:01 > 02:00:00:00:00:02, ethertype IPv4 (0x0800), length 98: 10.203.0.1 > 10.203.0.2: ICMP echo request, id 83, seq 1, length 64
02:00:00:00:00:02 > 02:00:00:00:00:01, ethertype IPv4 (0x0800), length 98: 10.203.0.2 > 10.203.0.1: ICMP echo reply, id 83, seq 1, length 64

El orden es exactamente lo que dijimos. Primero la consulta, a ff:ff:ff:ff:ff:ff. Luego la respuesta, en unicast. Recién entonces sale el primer ping. El segundo ping ya no necesita ARP. Y las dos tablas quedan llenas.

h1$ ip neigh show dev eth1
10.203.0.2 lladdr 02:00:00:00:00:02 REACHABLE
h2$ ip neigh show dev eth1
10.203.0.1 lladdr 02:00:00:00:00:01 DELAY

h2 nunca hizo una consulta y aun así ya conoce a h1. No fue magia: la consulta traía la IP y la MAC de quien preguntaba, y el RFC 826 le permite aprovecharlas si va a responder. Es un ahorro razonable, porque probablemente h2 tenga que contestarle pronto.

Los estados son del kernel de Linux. REACHABLE significa que la entrada se confirmó hace poco. Tras unos 30 s sin confirmación pasa a STALE, y la próxima vez que se use, a DELAY y luego a PROBE, hasta comprobar que el vecino sigue ahí. h2 aparece en DELAY porque la entrada salió de una consulta, sin confirmar, y ya la usó para responder. Una tabla ARP no es permanente: si un equipo cambia de MAC, el resto se entera pronto.

Los bytes de ARP

Veamos el mismo frame byte por byte.

h1$ tcpdump -t -e -n -xx -r /tmp/arp.pcap arp
02:00:00:00:00:01 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 10.203.0.2 tell 10.203.0.1, length 28
	0x0000:  ffff ffff ffff 0200 0000 0001 0806 0001
	0x0010:  0800 0604 0001 0200 0000 0001 0acb 0001
	0x0020:  0000 0000 0000 0acb 0002
Bytes Valor Significado
0–5 ff:ff:ff:ff:ff:ff Destino del frame: broadcast
6–11 02:00:00:00:00:01 Origen del frame: h1
12–13 0x0806 EtherType: ARP
14–15 0x0001 Tipo de hardware: Ethernet
16–17 0x0800 Tipo de protocolo: IPv4
18, 19 6, 4 Largo de una MAC y de una IPv4
20–21 0x0001 Operación: 1 consulta, 2 respuesta
22–27 02:00:00:00:00:01 MAC de quien pregunta
28–31 0a cb 00 01 IP de quien pregunta: 10.203.0.1
32–37 00:00:00:00:00:00 MAC buscada: todavía se desconoce
38–41 0a cb 00 02 IP buscada: 10.203.0.2

ARP se diseñó para cualquier combinación de enlace y red. De ahí los campos de tipo y de largo, aunque hoy casi siempre valen lo mismo.

Error común. Ver length 42 y pensar que contradice el mínimo de 64 bytes. La captura no ve todo. El mínimo de 64 bytes incluye el relleno y los 4 bytes del FCS, y esos los pone la tarjeta de red. Aquí no hay tarjeta: entre dos interfaces veth, el kernel pasa el frame sin relleno y sin FCS. En un cable real, este frame saldría con 18 bytes de relleno y 4 de FCS: 42 + 18 + 4 = 64. Tampoco es raro ver mtu 9500 en las salidas: Containerlab pone esa MTU en sus enlaces. Los 1500 bytes son el valor de la Ethernet clásica.

ARP a mano, en C

El programa arp_probe.c hace a mano lo que el kernel hace antes de enviar un paquete a un vecino. Abre un socket AF_PACKET, en el que lo que se envía ya es un frame completo, y arma los 42 bytes campo por campo. Como dice la guía de estilo, no se copia una struct a la red: cada campo se escribe en su posición y con su orden de bytes.

memset(frame, 0xff, 6);                 /* destino: broadcast */
memcpy(frame + 6, my_mac, 6);           /* origen */
put_u16(frame + 12, ETHERTYPE_ARP);     /* EtherType */

uint8_t *arp = frame + ETH_HEADER_LEN;
put_u16(arp + 0, 1);                    /* tipo de hardware: Ethernet */
put_u16(arp + 2, ETHERTYPE_IP);         /* tipo de protocolo: IPv4 */
arp[4] = 6;                             /* largo de una dirección de hardware */
arp[5] = 4;                             /* largo de una dirección de protocolo */
put_u16(arp + 6, ARP_OP_REQUEST);
memcpy(arp + 8, my_mac, 6);             /* quién pregunta */
memcpy(arp + 14, my_ip, 4);
memset(arp + 18, 0, 6);                 /* MAC buscada: todavía no se conoce */
memcpy(arp + 24, target_ip, 4);

Se compila con enlace estático, porque corre dentro de los nodos, que no tienen las bibliotecas del contenedor del curso, y queda montado en ellos como /usr/local/bin/arp_probe.

h1$ arp_probe eth1 10.203.0.4
frame enviado (42 bytes):
ff ff ff ff ff ff 02 00 00 00 00 01 08 06
00 01 08 00 06 04 00 01 02 00 00 00 00 01
0a cb 00 01 00 00 00 00 00 00 0a cb 00 04
respuesta en 0.047 ms, la MAC del vecino es 02:00:00:00:00:04
frame recibido (42 bytes):
02 00 00 00 00 01 02 00 00 00 00 04 08 06
00 01 08 00 06 04 00 02 02 00 00 00 00 04
0a cb 00 04 02 00 00 00 00 01 0a cb 00 01
h1$ arp_probe eth1 10.203.0.99
frame enviado (42 bytes):
ff ff ff ff ff ff 02 00 00 00 00 01 08 06
00 01 08 00 06 04 00 01 02 00 00 00 00 01
0a cb 00 01 00 00 00 00 00 00 0a cb 00 63
sin respuesta en 1000 ms

Los bytes coinciden con los de la tabla. El tiempo depende de la máquina. Y fíjense en la línea final: si la IP no existe, nadie contesta y ARP no devuelve ningún error. El que pregunta solo sabe que nadie respondió. Un ping a esa IP mostraría el mismo silencio.

En la práctica. ARP no tiene autenticación. Cualquier nodo de la red local puede responder “esa IP soy yo” y quedarse con el tráfico de otro. Esa es la base del ataque de ARP spoofing, que retomaremos en la semana 15. Es una de las razones para separar redes con VLANs.

Hacia otra subred: se busca al gateway

Ahora, la pregunta importante. Un host con la IP 10.203.110.1/24 quiere enviar a 10.203.120.2, que está en otra subred. ¿De quién busca la MAC?

No de 10.203.120.2. Esa consulta nunca recibiría respuesta: el destino no está en esta red, y los broadcasts no cruzan routers. El host decide así:

  1. Compara el destino con su propia subred, usando su máscara. No coincide.
  2. Mira su tabla de rutas. La ruta que corresponde tiene un gateway: el router de salida, digamos 10.203.110.254.
  3. Busca con ARP la MAC del gateway.
  4. Arma un frame con destino a la MAC del router y dentro pone un datagrama con destino a la IP final.

Lo veremos en una captura real al final, en la sección de VLANs. Allí, ip neigh de h1 no muestra a 10.203.120.2: muestra solo a su gateway, aunque acaba de intercambiar paquetes con 10.203.120.2.

Idea clave. La dirección IP dice a dónde va el paquete al final. La dirección MAC dice a qué vecino se lo paso ahora. Por eso la IP de destino se mantiene de extremo a extremo y la MAC de destino cambia en cada salto.

Switches

Con un solo cable compartido, todos oyen todo. Con un switch, cada host tiene su propio enlace, y el switch lleva cada frame solo por donde hace falta. Es la pieza central de una red local moderna.

Antes: hubs

Un hub era una caja con varios puertos que trabajaba en la capa física. Cada bit que entraba por un puerto salía por todos los demás. No entendía frames, no tenía tabla y no podía guardar nada. Eléctricamente, un hub era un cable compartido con más conectores: dos hosts que transmitían a la vez colisionaban. Hoy solo interesan como historia, porque ayudan a entender qué aportó un switch.

Qué hace un switch

Un switch recibe un frame completo, mira la MAC de destino y lo reenvía solo por el puerto que corresponde. Tiene una cola por puerto, de modo que varios frames pueden ir hacia un mismo destino sin colisionar: esperan. Hace forwarding (decidir por qué puerto sale) y filtering (decidir que un frame no sale por ninguno). Para eso usa su forwarding table (también llamada FDB, MAC table o tabla de conmutación). Cada entrada dice:

(dirección MAC, puerto, tiempo desde la última vez que se vio)

¿Quién llena esa tabla? Nadie: el switch aprende solo, mirando las direcciones de origen. Un frame que entra por el puerto 3 con origen X prueba que X está por el puerto 3. Eso es todo el truco. El algoritmo completo es corto:

Cuando llega un frame por el puerto p, con origen S y destino D:

  aprender:  anotar que S está por el puerto p, con tiempo 0
  buscar D en la tabla:
    si D es broadcast o no está:  enviarlo por todos los puertos, menos p  (flooding)
    si D está por el puerto p:    descartarlo                              (filtering)
    si D está por otro puerto q:  enviarlo solo por q                      (forwarding)

  periódicamente: borrar las entradas que no se han visto hace mucho tiempo

Por esto un switch no necesita configuración: se conecta y funciona. Un host tampoco necesita saber que existe. Es transparente: no cambia los frames y los hosts no se dirigen a él. El borrado de entradas viejas permite que la red se adapte si un host se muda a otro puerto. En un bridge de Linux, el tiempo es de 300 s: ageing_time 30000 en la salida de ip -d link, en centésimas de segundo.

Aprendiendo en vivo

El bridge de Linux hace exactamente esto: es un switch por software. En la topología del laboratorio, sw1 tiene br0, con los cuatro puertos dentro. Al inicio, su tabla no tiene ninguna entrada aprendida. bridge fdb show también lista entradas permanent (las MAC de sus propios puertos), y por eso las filtramos.

sw1$ bridge fdb show br br0 | grep -v permanent

Antes de seguir, hagan una predicción. h1 hace ping a h2. La consulta ARP es un broadcast, así que debe salir por todos los puertos. La respuesta, que va a h1, se envía cuando el switch ya vio a h1, de modo que solo sale por eth1. ¿Qué debe ver h3? Solo la consulta. Lo comprobamos dejando un tcpdump en h3 durante el mismo ping de la sección anterior:

h3$ tcpdump -U -t -e -n -l -i eth1 "arp or icmp"
02:00:00:00:00:01 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 10.203.0.2 tell 10.203.0.1, length 28

Exactamente eso: una línea, de seis frames que hubo en total. Tampoco quedó registrado en su tabla ARP, porque el mensaje no era para él. Y la tabla del switch ya aprendió:

h3$ ip neigh show dev eth1
sw1$ bridge fdb show br br0 | grep -v permanent
02:00:00:00:00:01 dev eth1 master br0
02:00:00:00:00:02 dev eth2 master br0

h1 y h2 están ahí. h3 y h4 no, porque todavía no han transmitido nada. Un switch no conoce a un host que no ha hablado.

Animación de los seis frames de un ping de h1 a h2 a través de sw1, con la tabla vacía. La consulta ARP de h1 entra por eth1, el switch anota a …:01 en eth1 y la envía por eth2, eth3 y eth4. La respuesta de h2 entra por eth2, el switch anota a …:02 en eth2 y la envía solo por eth1. Los dos ping y sus respuestas salen cada uno por un solo puerto. Al final la tabla tiene dos entradas y h3 vio un solo frameAnimación de los seis frames de un ping de h1 a h2 a través de sw1, con la tabla vacía. La consulta ARP de h1 entra por eth1, el switch anota a …:01 en eth1 y la envía por eth2, eth3 y eth4. La respuesta de h2 entra por eth2, el switch anota a …:02 en eth2 y la envía solo por eth1. Los dos ping y sus respuestas salen cada uno por un solo puerto. Al final la tabla tiene dos entradas y h3 vio un solo frame
El switch aprende de las direcciones de origen, sin ninguna configuración. El flooding se usa solo mientras hace falta, y por eso h3 ve uno de los seis frames.

Flooding de un destino desconocido

El flooding no es solo para broadcasts. Si un frame va en unicast a una MAC que no está en la tabla, tampoco queda otra opción. Para verlo, vaciamos la tabla del switch, pero no la tabla ARP de h1. Así, h1 enviará directamente en unicast a una MAC que el switch ha olvidado.

sw1$ ip link set br0 type bridge fdb_flush
h3$ tcpdump -U -t -e -n -l -i eth1 icmp &
h1$ ping -c 3 10.203.0.2
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.040/0.051/0.070 ms
h3$ (salida de tcpdump)
02:00:00:00:00:01 > 02:00:00:00:00:02, ethertype IPv4 (0x0800), length 98: 10.203.0.1 > 10.203.0.2: ICMP echo request, id 119, seq 0, length 64

Entre h1 y h2 hubo seis frames, y h3 vio uno solo: el primer echo request, que iba dirigido a la MAC de h2. Lo recibió porque el switch no sabía dónde estaba h2. La respuesta de h2 le enseñó al switch su ubicación, y h3 no volvió a ver nada. Su tarjeta habría descartado ese frame en un uso normal; lo vemos porque tcpdump la pone en modo promiscuo.

Si no aprendiera: un hub

Un bridge de Linux con ageing_time 0 olvida cada dirección al instante y hace flooding de todo. Es la forma más cercana a un hub que podemos tener aquí. (No es un hub de verdad: guarda y reenvía frames y no tiene colisiones.)

sw1$ ip link set br0 type bridge ageing_time 0
h3$ tcpdump -U -t -e -n -l -i eth1 icmp &
h1$ ping -c 2 10.203.0.2
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.046/0.050/0.054 ms
h3$ (salida de tcpdump)
02:00:00:00:00:01 > 02:00:00:00:00:02, ethertype IPv4 (0x0800), length 98: 10.203.0.1 > 10.203.0.2: ICMP echo request, id 127, seq 0, length 64
02:00:00:00:00:02 > 02:00:00:00:00:01, ethertype IPv4 (0x0800), length 98: 10.203.0.2 > 10.203.0.1: ICMP echo reply, id 127, seq 0, length 64
02:00:00:00:00:01 > 02:00:00:00:00:02, ethertype IPv4 (0x0800), length 98: 10.203.0.1 > 10.203.0.2: ICMP echo request, id 127, seq 1, length 64
02:00:00:00:00:02 > 02:00:00:00:00:01, ethertype IPv4 (0x0800), length 98: 10.203.0.2 > 10.203.0.1: ICMP echo reply, id 127, seq 1, length 64
sw1$ ip link set br0 type bridge ageing_time 30000

Ahora h3 ve toda la conversación ajena. Con un switch normal, un host no ve el tráfico unicast de los demás, y esto mejora tanto el rendimiento como, en cierta medida, la privacidad.

Filtering: el destino está por donde llegó

¿Qué hace un switch si el destino está en el mismo puerto por donde llegó el frame? Lo descarta: ese frame ya llegó, o va a llegar, sin ayuda. Esto se da cuando hay otro switch de por medio. En la topología de dos switches de la sección de VLANs (antes de configurar las VLANs), h1 y h2 están en sw1, y h3 y h4 en sw2. Si sw2 ya aprendió que h2 está por su puerto hacia sw1, y sw1 por alguna razón hace flooding de un frame para h2, entonces el frame llega a sw2 por el mismo puerto donde está h2. Debe descartarlo, y h4 no debe ver nada. Si sw2 no conoce a h2, hace flooding y h4 sí lo ve.

(sw2 ya conoce a h2 por eth3, y se vacía solo la tabla de sw1)
h1$ ping -c 1 -W 1 10.203.110.2 | tail -2
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.072/0.072/0.072 ms
h4$ wc -l < /tmp/f.txt
0

(se vacían las tablas de sw1 y de sw2)
h1$ ping -c 1 -W 1 10.203.110.2 | tail -2
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.073/0.073/0.073 ms
h4$ wc -l < /tmp/f.txt
1
h4$ cut -c1-100 /tmp/f.txt
06:40:15.181990 02:00:00:00:00:01 > 02:00:00:00:00:02, ethertype IPv4 (0x0800), length 98: 10.203.11

/tmp/f.txt guarda la salida de un tcpdump que h4 deja corriendo con el filtro icmp. Llegó el frame solo cuando sw2 no conocía a h2.

La prueba test_2_same_port_destination_is_filtered de test_campus.py repite este experimento. Para que los dos estén en la misma subred, le agrega por un momento a h2 la dirección 10.203.110.2.

Dominios de colisión y de broadcast

Dos conceptos para ordenar lo anterior.

  • Un dominio de colisión es el conjunto de nodos cuyas transmisiones pueden chocar entre sí. Un hub junta a todos en uno solo. Un switch full-duplex deja a cada enlace en el suyo, sin colisiones posibles.
  • Un dominio de broadcast es el conjunto de nodos a los que llega un broadcast. Un switch no lo separa: hace flooding de los broadcasts. Un router sí lo separa, y una VLAN también.
Equipo Capa Separa colisión Separa broadcast Configuración Reenvía según
Hub Física No No Ninguna Nada: repite bits
Switch Enlace Sí No Ninguna MAC de destino
Router Red Sí Sí Direcciones y routing IP de destino

Switches frente a routers

Los dos son equipos de almacenar y reenviar, pero se ubican en capas distintas.

Switch Router
Tabla Aprendida sola, MAC por MAC Rutas por prefijo, de un protocolo de routing
Escala Miles de MAC: toda dirección es una entrada Toda Internet, porque agrupa por prefijo
Topología Solo un árbol, sin ciclos Cualquiera, y usa caminos alternativos
Broadcast Hace flooding Lo corta
Modifica el frame No Sí: frame nuevo, y baja el TTL
Configurar Casi nada Bastante

En la práctica. Un switch es la solución simple para una red de unos cientos de hosts. Una red muy grande no puede ser un solo dominio de broadcast: los broadcasts de ARP consumirían a todos, y la tabla crecería sin control. Por eso una red grande se divide en subredes unidas por routers. Y por eso existen equipos que hacen las dos cosas, que se llaman switches de capa 3.

Spanning tree

Ahora que sabemos cómo funciona un switch, conectemos varios. Por redundancia, tiene todo el sentido hacer un ciclo: si un cable se corta, queda otro camino. Pero el algoritmo de aprendizaje supone que no hay ciclos, y si los hay, el resultado es catastrófico.

El problema: la tormenta de broadcast

Un broadcast llega a un switch, que hace flooding por todos los demás puertos, y cada copia llega al siguiente switch, que hace lo mismo. En un ciclo, las copias vuelven a su origen y siguen dando vueltas. Y Ethernet no tiene TTL: ningún frame muere por sí solo. Si el ciclo está bien conectado, además, se multiplican. Es una tormenta de broadcast.

Tenemos una topología para verlo: tres switches en triángulo, con h1 en sw1 y h2 en sw2. Al levantarse, el ciclo está abierto (eth3 de sw1 está apagada).

                 h1
                  │ eth1
                 sw1
           eth2 /   \ eth3 (apagada al inicio)
               /     \
         eth3 /       \ eth2
            sw2 ───── sw3
             │  eth2 eth1
        eth1 │
             h2

Le agregamos un retardo de 5 ms a cada enlace entre switches. Es una decisión de seguridad para sus laptops. En una primera prueba, con retardo en un solo sentido de cada enlace, un solo switch usó más de la mitad de un procesador y h2 recibió más de un millón de frames por segundo. Con 5 ms en los dos sentidos circulan pocos cientos por segundo, y las cifras que siguen dependen de eso y de su máquina: en un enlace real, una tormenta llega hasta saturarlo.

Se levanta con make loop. Esperen unos 10 segundos, para que pasen los mensajes que aparecen al iniciar. Vamos a contar los frames que recibe h2 en /sys/class/net/eth1/statistics/rx_packets. Con el ciclo abierto, todo está tranquilo, y al cerrar el ciclo tampoco pasa nada todavía: nadie ha enviado un broadcast.

h1$ ping -c 2 -W 1 10.203.0.2
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 12.720/20.054/27.388 ms

ciclo abierto, 5 s sin tráfico: 8 -> 9
sw1$ ip link set eth3 up
ciclo cerrado, 5 s sin tráfico: 9 -> 9

Un ciclo no hace daño mientras nadie transmita. Ahora vaciamos las tablas ARP de h1 y h2, y h1 hace un solo ping, que provoca una sola consulta ARP.

h1$ ping -c 1 -W 1 10.203.0.2
1 packets transmitted, 0 packets received, 100% packet loss
rx de h2 = 208
rx de h2 = 308
rx de h2 = 411
rx de h2 = 509
rx de h2 = 613

Los contadores se tomaron a intervalos de un segundo, sin que nadie enviara nada más. La tasa no baja: unos 100 frames por segundo, para siempre. El ping falló, y conviene preguntarse por qué, si la red está llena de copias. Veamos qué recibe h2:

h2$ tcpdump -U -t -e -n -l -i eth1
02:00:00:00:00:01 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 10.203.0.2
02:00:00:00:00:02 > 02:00:00:00:00:01, ethertype ARP (0x0806), length 42: Reply 10.203.0.2 is-at 02:
02:00:00:00:00:01 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 10.203.0.2
02:00:00:00:00:02 > 02:00:00:00:00:01, ethertype ARP (0x0806), length 42: Reply 10.203.0.2 is-at 02:
02:00:00:00:00:01 > 02:00:00:00:00:02, ethertype IPv4 (0x0800), length 98: 10.203.0.1 > 10.203.0.2:
02:00:00:00:00:02 > 02:00:00:00:00:01, ethertype IPv4 (0x0800), length 98: 10.203.0.2 > 10.203.0.1:
02:00:00:00:00:01 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 10.203.0.2

(Cortamos las líneas a 100 columnas.) Son la misma consulta una y otra vez, con su respuesta. Y el problema es peor que el flooding. Miren la tabla de sw1:

sw1$ bridge fdb show br br0 | grep 02:00
02:00:00:00:00:02 dev eth2 master br0
02:00:00:00:00:01 dev eth3 master br0

h1 está conectado a eth1 de sw1, pero sw1 cree que está por eth3. La copia que dio la vuelta llegó por eth3 con origen h1, y el switch aprendió, de buena fe, algo falso. Las tablas quedan envenenadas: el tráfico unicast hacia h1 se va por el lado equivocado, y por eso casi ningún ping vuelve:

h1$ ping -c 5 -W 1 10.203.0.2
5 packets transmitted, 1 packets received, 80% packet loss
round-trip min/avg/max = 10.487/10.487/10.487 ms

La tormenta no para por sí sola. Solo se acaba si se abre el ciclo:

sw1$ ip link set eth3 down
ciclo abierto otra vez, 3 s: 1369 -> 1369
Animación de la tormenta en el triángulo de sw1, sw2 y sw3, con h1 en sw1 y h2 en sw2. Con el ciclo cerrado y sin tráfico, h2 sigue con 9 frames. h1 envía una consulta ARP, y sw1 la reenvía por eth2 y eth3. Las dos copias dan vueltas en sentidos opuestos, y cada vez que pasan por sw2 le dejan un frame a h2. El contador sube 208, 308, 411, 509 y 613, una lectura por segundo. La tabla de sw1 pasa a decir que h1 está por eth3, y de 5 ping vuelve 1. Al apagar eth3 de sw1, las copias desaparecen y el contador se queda en 1369Animación de la tormenta en el triángulo de sw1, sw2 y sw3, con h1 en sw1 y h2 en sw2. Con el ciclo cerrado y sin tráfico, h2 sigue con 9 frames. h1 envía una consulta ARP, y sw1 la reenvía por eth2 y eth3. Las dos copias dan vueltas en sentidos opuestos, y cada vez que pasan por sw2 le dejan un frame a h2. El contador sube 208, 308, 411, 509 y 613, una lectura por segundo. La tabla de sw1 pasa a decir que h1 está por eth3, y de 5 ping vuelve 1. Al apagar eth3 de sw1, las copias desaparecen y el contador se queda en 1369
Una sola consulta basta, porque ningún frame muere por sí solo. Las copias no solo ocupan el enlace: también le enseñan al switch una ubicación falsa.

Error común. Pensar que más enlaces entre switches siempre dan más confiabilidad. Sin spanning tree, un cable de más puede tumbar una red: basta que alguien conecte dos puertos de un switch con un mismo cable. Es una de las fallas clásicas en una oficina.

La solución: desactivar enlaces, sin quitarlos

Si el algoritmo necesita un árbol, démosle un árbol. El Spanning Tree Protocol (STP, IEEE 802.1D), diseñado por Radia Perlman en 1985, hace que los switches apaguen por sí solos los puertos justos para que no queden ciclos, conservando la conexión entre todos. Si un enlace falla, reactivan alguno. Los puertos apagados son el repuesto.

Los switches se envían mensajes llamados BPDU (bridge protocol data units), en frames a una dirección multicast especial. Con ellos, de forma distribuida, hacen tres cosas:

  1. Eligen una raíz. Cada switch tiene un identificador: una prioridad configurable seguida de su MAC. Gana el menor, con la prioridad como criterio principal.
  2. Cada switch que no es raíz elige su puerto raíz: el de menor costo total hacia la raíz. Un empate se resuelve a favor del vecino con menor identificador.
  3. Cada enlace elige un puerto designado: el del extremo más cercano a la raíz. Un empate también lo resuelve el menor identificador.

Todo puerto que no sea raíz ni designado se bloquea: no reenvía tráfico ni aprende, aunque sigue escuchando BPDU.

Apliquemos el algoritmo a nuestro triángulo antes de ejecutarlo. Pusimos prioridades fijas: 4096 para sw1, 8192 para sw2 y 12288 para sw3.

  • Raíz: sw1, la de menor prioridad.
  • Puertos raíz: sw2:eth3 y sw3:eth2, los directos a sw1 (costo 2 cada uno, contra 4 por el otro lado).
  • Enlaces con sw1: sus dos puertos son designados, porque es la raíz.
  • Enlace sw2–sw3: los dos extremos están a costo 2. Empate, y gana el menor identificador: sw2. Por tanto sw2:eth2 es designado, y sw3:eth1 se bloquea.

Activamos STP en los tres, volvemos a cerrar el ciclo y le damos unos segundos para converger. Cada identificador aparece en hexadecimal: 1000 es 4096, 2000 es 8192 y 3000 es 12288.

sw1$ ip link set br0 type bridge stp_state 1      (también en sw2 y sw3)
sw1$ ip link set eth3 up

sw1$ bridge link
3585: eth2@if3584:  mtu 9500 master br0 state forwardin
3582: eth3@if3583:  mtu 9500 master br0 state forwardin
3586: eth1@if3587:  mtu 9500 master br0 state forwardin
bridge_id 1000.AA:C1:AB:5D:BD:C7 designated_root 1000.AA:C1:AB:5D:BD:C7 root_port 0 root_path_cost 0

sw2$ bridge link
3584: eth3@if3585:  mtu 9500 master br0 state forwardin
3580: eth2@if3581:  mtu 9500 master br0 state forwardin
3588: eth1@if3589:  mtu 9500 master br0 state forwardin
bridge_id 2000.AA:C1:AB:0F:A2:F9 designated_root 1000.AA:C1:AB:5D:BD:C7 root_port 3 root_path_cost 2

sw3$ bridge link
3581: eth1@if3580:  mtu 9500 master br0 state blocking
3583: eth2@if3582:  mtu 9500 master br0 state forwardin
bridge_id 3000.AA:C1:AB:A1:D1:2D designated_root 1000.AA:C1:AB:5D:BD:C7 root_port 2 root_path_cost 2

(Las líneas están cortadas a 55 columnas, y la última de cada bloque viene de ip -d link show br0.) Las MAC cambian en cada despliegue, porque son las que Containerlab asigna al azar a los puertos de los switches. Lo que predijimos sale tal cual: sw3:eth1 está en blocking, y los tres coinciden en quién es la raíz. Un ping ya no provoca una tormenta: h2 recibe solo un puñado de frames.

h1$ ping -c 2 -W 1 10.203.0.2
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 13.940/21.060/28.181 ms
con stp: 1373 -> 1377

Los BPDU se pueden ver. La raíz los envía cada hello-time (2 s) y cada switch los retransmite hacia abajo:

sw3$ tcpdump -t -n -l -v -c 1 -i eth2 stp
STP 802.1d, Config, Flags [Topology change], bridge-id 1000.aa:c1:ab:5d:bd:c7.8003, length 35
	message-age 0.00s, max-age 20.00s, hello-time 2.00s, forwarding-delay 2.00s
	root-id 1000.aa:c1:ab:5d:bd:c7, root-pathcost 0

Cuando un enlace falla

Cortemos el enlace sw1–sw2. Eso deja a h2 aislado, salvo por el enlace sw2–sw3, que está bloqueado. Medimos cuánto tarda en volver.

sw1$ ip link set eth2 down
t=0 s  sw3:eth1 state blocking
t=21 s  sw3:eth1 state listening
t=23 s  sw3:eth1 state learning
t=25 s  sw3:eth1 state forwarding
t=25 s  ping OK

Casi medio minuto sin red, y tiene una explicación exacta. El STP clásico es lento a propósito, y sus temporizadores aparecen en el BPDU de arriba:

  • max-age (20 s). sw3 guarda la información de la raíz por su puerto bloqueado y la considera válida hasta que caduque. sw2 le avisa enseguida que perdió su camino, pero sw3 ignora esos BPDU por ser peores que lo que ya tiene. Espera los 20 s.
  • forwarding-delay (2 s aquí, 15 s por defecto). Antes de reenviar, un puerto pasa por listening, sin aprender ni reenviar, y luego por learning, aprendiendo sin reenviar. Cada etapa dura este tiempo. Su función es dar tiempo a que todos los switches se enteren: si un puerto empezara a reenviar de inmediato, se podría formar un ciclo durante la transición.

Total: 20 + 2 + 2 = 24 s, con el margen de nuestro segundero. Con los valores por defecto, que usan 15 s por etapa, serían 50 s. La medición depende de cuándo vence cada temporizador, pero no de la velocidad de la máquina.

Dos triángulos y una línea de tiempo. Antes del corte, sw1 es la raíz, los enlaces hacia sw2 y sw3 están activos y el enlace sw2-sw3 está bloqueado en sw3:eth1. Después del corte de sw1-sw2, el enlace sw2-sw3 pasa a formar parte del árbol, con sw3:eth1 en forwarding. Abajo, la línea de tiempo medida de sw3:eth1: blocking hasta los 21 s, listening hasta los 23, learning hasta los 25 y luego forwarding, con el ping de vuelta. Los primeros 20 s son el max-ageDos triángulos y una línea de tiempo. Antes del corte, sw1 es la raíz, los enlaces hacia sw2 y sw3 están activos y el enlace sw2-sw3 está bloqueado en sw3:eth1. Después del corte de sw1-sw2, el enlace sw2-sw3 pasa a formar parte del árbol, con sw3:eth1 en forwarding. Abajo, la línea de tiempo medida de sw3:eth1: blocking hasta los 21 s, listening hasta los 23, learning hasta los 25 y luego forwarding, con el ping de vuelta. Los primeros 20 s son el max-age
Casi todo el corte es espera: 20 s hasta que caduca lo que sw3 sabe de la raíz, y dos etapas de seguridad antes de reenviar.

En la práctica. Un corte de 30 a 50 s era inaceptable, y por eso se creó RSTP (802.1w), que negocia con sus vecinos y converge normalmente en segundos, o en menos. El bridge de Linux dentro del kernel, que es lo que usamos, solo tiene el STP clásico; RSTP en Linux requiere un programa aparte. En un data center moderno ni siquiera se usa uno ni otro: se prefiere routing de capa 3 con varios caminos activos a la vez, como veremos en la semana 14. STP desperdicia los enlaces que bloquea.

Idea clave. STP no quita el ciclo del cable: quita el ciclo del tráfico. Todo reenvío entre switches sigue un árbol y los enlaces restantes quedan de repuesto. Esto contrasta con un router, que tiene TTL y puede usar cualquier topología.

VLANs

Tenemos un switch con cuatro hosts. Imaginen que en una empresa pertenecen a dos áreas, y sus datos no deberían mezclarse. Los broadcasts de una no tienen por qué molestar a la otra. Podríamos comprar dos switches, pero las áreas cambian, y entonces habría que mover cables. Además, si en cada piso hay gente de las dos áreas, harían falta dos cables entre pisos.

Una VLAN (virtual LAN) es una red lógica separada dentro de una misma red física. El switch trata cada VLAN como si fuera un switch aparte: un frame que entra por un puerto de una VLAN solo sale por puertos de esa misma VLAN. Cada VLAN es su propio dominio de broadcast.

Puertos access y trunk

Un puerto de un switch con VLANs puede ser de dos tipos.

  • Access. Pertenece a una sola VLAN y se conecta a un host. El host no sabe nada de VLANs: sus frames salen y llegan como siempre, sin ninguna marca. El switch recuerda a qué VLAN pertenece el puerto.
  • Trunk. Lleva varias VLANs por un solo enlace, y conecta switches entre sí o un switch con un router. Aquí hay que indicar a qué VLAN pertenece cada frame.

Para ello existe la etiqueta 802.1Q. Es un campo de 4 bytes que se inserta en el frame después de las direcciones.

frame sin etiqueta:
┌─────────┬────────┬───────────┬───────────┬─────┐
│ destino │ origen │ EtherType │   datos   │ FCS │
└─────────┴────────┴───────────┴───────────┴─────┘

frame con etiqueta:
┌─────────┬────────┬────────┬─────┬───────────┬───────────┬─────┐
│ destino │ origen │ 0x8100 │ TCI │ EtherType │   datos   │ FCS │
└─────────┴────────┴────────┴─────┴───────────┴───────────┴─────┘
                    2 bytes  2 bytes

TCI (16 bits):   PCP (3 bits)  │  DEI (1 bit)  │  VID (12 bits)
                 prioridad        descartable     número de VLAN
  • 0x8100 ocupa el lugar donde iría el EtherType. Así, un equipo que no entiende VLANs ve un protocolo que no conoce y deja el resto en paz.
  • VID es el número de VLAN. Con 12 bits hay 4096 valores. El 0 y el 4095 están reservados, y quedan 4094 VLANs.
  • PCP es una prioridad de calidad de servicio y DEI marca si el frame se puede descartar primero.

Como el frame cambió, el FCS se recalcula al poner o al quitar la etiqueta. El tamaño máximo sube de 1518 a 1522 bytes.

Un switch trabaja así. Un frame que entra por un puerto access recibe por dentro la VLAN de ese puerto. Si sale por un puerto access de la misma VLAN, sale sin marca. Si sale por un trunk, se le pone la etiqueta. Al entrar por un trunk se lee la etiqueta. Y la forwarding table es por pareja (VLAN, MAC): el aprendizaje de cada VLAN es independiente.

Configurar VLANs en un bridge de Linux

Volvemos a la topología de un switch, con h1 y h2 en la VLAN 110, y h3 y h4 en la 120. Los cuatro siguen en la misma subred 10.203.0.0/24: es a propósito, para ver qué hace la VLAN por sí sola. El script es vlan/lan_sw1.sh:

ip link set br0 type bridge vlan_filtering 1

for port in eth1 eth2; do
  bridge vlan add dev $port vid 110 pvid untagged
  bridge vlan del dev $port vid 1
done

for port in eth3 eth4; do
  bridge vlan add dev $port vid 120 pvid untagged
  bridge vlan del dev $port vid 1
done
  • vlan_filtering 1 activa las VLANs. Sin esto, el bridge las ignora.
  • vid 110 permite la VLAN 110 en el puerto.
  • pvid (port VLAN ID) indica que un frame sin etiqueta que entra por este puerto pertenece a esa VLAN.
  • untagged indica que al salir por este puerto se le quita la etiqueta.

Un puerto con pvid untagged es, en resumen, un puerto access. Se borra la VLAN 1 porque Linux la pone en todo puerto por defecto. Se aplica sin copiar el archivo dentro del nodo, desde code/week-03:

$ docker exec -i clab-w03-lan-sw1 sh -e < vlan/lan_sw1.sh
sw1$ bridge vlan show
port              vlan-id
eth1              110 PVID Egress Untagged
eth2              110 PVID Egress Untagged
eth3              120 PVID Egress Untagged
eth4              120 PVID Egress Untagged
br0               1 PVID Egress Untagged

La fila de br0 se refiere al puerto interno del propio bridge, que no usamos: el switch no tiene IP.

Vaciamos las tablas, y ya podemos probar. Dentro de cada VLAN, todo funciona. Entre VLANs, no:

h1$ ping -c 2 -W 1 10.203.0.2 | tail -2
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.082/0.148/0.214 ms
h1$ ping -c 2 -W 1 10.203.0.3 | tail -2
--- 10.203.0.3 ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss
h3$ ping -c 2 -W 1 10.203.0.4 | tail -2
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.119/0.158/0.197 ms
h1$ ip neigh show dev eth1
10.203.0.2 lladdr 02:00:00:00:00:02 REACHABLE
10.203.0.3 FAILED
sw1$ bridge fdb show br br0 | grep -v permanent
02:00:00:00:00:01 dev eth1 vlan 110 master br0
02:00:00:00:00:02 dev eth2 vlan 110 master br0
02:00:00:00:00:03 dev eth3 vlan 120 master br0
02:00:00:00:00:04 dev eth4 vlan 120 master br0

h1 y h3 están en la misma subred IP y en el mismo switch físico, y no pueden hablar. La consulta ARP de h1 no llega a h3, porque es un broadcast de la VLAN 110 y solo sale por sus puertos: el estado queda en FAILED. Cada entrada de la tabla del switch ahora tiene una VLAN.

Esto también limita lo que un equipo le puede hacer a otro. Desde otra VLAN no se ven los broadcasts ni se puede responder un ARP falso. Por eso las VLANs se usan también para separar cámaras o impresoras del resto de la red. Pero no son un firewall: en cuanto se conectan con un router, pasa todo lo que el router deje pasar.

Pasar de una VLAN a otra: hace falta un router

Error común. Creer que una VLAN, por sí sola, da comunicación entre redes. Una VLAN hace exactamente lo contrario: separa. Dos VLANs son dos redes distintas, tan separadas como si estuvieran en switches sin conexión. Para que un paquete pase de una a otra, hace falta un router, y cada VLAN necesita su propia subred IP.

Si un router tuviera una interfaz física por VLAN, necesitaríamos muchos cables. Una forma ingeniosa de ahorrarlos es usar un solo cable en modo trunk hasta el router, y crear en él una subinterfaz por VLAN. A cada subinterfaz se le asigna una IP, que es el gateway de esa VLAN. Esto se conoce como router-on-a-stick.

Para este paso usamos otra topología (make campus): dos switches, un trunk entre ellos y un router colgado de sw2. Hay una VLAN que se reparte entre los dos switches, para que el trunk tenga un trabajo real.

                                     r1  eth1.110 10.203.110.254
                                     │   eth1.120 10.203.120.254
                                     │ eth1
                                     │ trunk (110 y 120)
                                     │ eth4
  h1 ──eth1── sw1 ──eth3══════eth3── sw2 ──eth1── h3
  h2 ──eth2──┘       trunk (110 y 120)  └──eth2── h4

  h1  10.203.110.1   VLAN 110          h3  10.203.110.3   VLAN 110
  h2  10.203.120.2   VLAN 120          h4  10.203.120.4   VLAN 120

Cada host ya tiene su dirección y su gateway:

h1$ ip route
default via 10.203.110.254 dev eth1
10.203.110.0/24 dev eth1 proto kernel scope link src 10.203.110.1
172.90.3.64/26 dev eth0 proto kernel scope link src 172.90.3.72

Antes de configurar nada, no hay router todavía: la consulta ARP por el gateway se queda sin respuesta.

h1$ ping -c 1 -W 1 10.203.120.2 | tail -2
--- 10.203.120.2 ping statistics ---
1 packets transmitted, 0 packets received, 100% packet loss
h1$ ip neigh show dev eth1
10.203.110.254 INCOMPLETE

Un trunk es un puerto en el que se permiten varias VLANs, sin pvid ni untagged. Sus frames salen con etiqueta. Si por ese puerto entra un frame sin etiqueta, se descarta, porque ya no tiene VLAN por defecto. La configuración de sw2 es vlan/campus_sw2.sh:

ip link set br0 type bridge vlan_filtering 1

bridge vlan add dev eth1 vid 110 pvid untagged
bridge vlan del dev eth1 vid 1
bridge vlan add dev eth2 vid 120 pvid untagged
bridge vlan del dev eth2 vid 1

for port in eth3 eth4; do
  bridge vlan add dev $port vid 110
  bridge vlan add dev $port vid 120
  bridge vlan del dev $port vid 1
done

Y en el router, vlan/campus_r1.sh crea una subinterfaz por VLAN sobre el trunk. El nombre eth1.110 es solo una convención; lo que manda es type vlan id 110.

ip link add link eth1 name eth1.110 type vlan id 110
ip link add link eth1 name eth1.120 type vlan id 120
ip addr add 10.203.110.254/24 dev eth1.110
ip addr add 10.203.120.254/24 dev eth1.120
ip link set eth1.110 up
ip link set eth1.120 up

Lo que envía eth1.110 sale por eth1 con la etiqueta 110. Lo que llega a eth1 con la etiqueta 110 aparece en eth1.110 sin ella. Para el resto del router, son dos interfaces comunes. Aplicamos los scripts:

$ docker exec -i clab-w03-campus-sw1 sh -e < vlan/campus_sw1.sh
$ docker exec -i clab-w03-campus-sw2 sh -e < vlan/campus_sw2.sh
$ docker exec -i clab-w03-campus-r1  sh -e < vlan/campus_r1.sh
sw2$ bridge vlan show
port              vlan-id
eth1              110 PVID Egress Untagged
eth2              120 PVID Egress Untagged
eth3              110
                  120
eth4              110
                  120
br0               1 PVID Egress Untagged
r1$ ip -br addr show | grep eth1
eth1.110@eth1    UP             10.203.110.254/24
eth1.120@eth1    UP             10.203.120.254/24
eth1@if3549      UP
r1$ vtysh -c 'show ip route connected' | tail -4
IPv4 unicast VRF default:
C>* 10.203.110.0/24 is directly connected, eth1.110, weight 1, 00:00:01
C>* 10.203.120.0/24 is directly connected, eth1.120, weight 1, 00:00:01
C>* 172.90.3.64/26 is directly connected, eth0, weight 1, 00:00:10

El router tiene una ruta directa a cada subred, sin ningún protocolo de routing. En los trunks, las VLANs permitidas aparecen sin PVID ni Untagged. No hizo falta activar el forwarding de IP en r1: sysctl net.ipv4.ip_forward ya vale 1 en los nodos del laboratorio.

Un paquete a través de VLANs, en tres capturas

Vaciamos las tablas de los hosts y de los switches, y hacemos un ping de h1 (VLAN 110) a h2 (VLAN 120), mientras capturamos en varios sitios.

h1$ ping -c 1 -W 1 10.203.120.2
PING 10.203.120.2 (10.203.120.2): 56 data bytes
64 bytes from 10.203.120.2: seq=0 ttl=63 time=0.455 ms

--- 10.203.120.2 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.455/0.455/0.455 ms

Lo que ve h1. Su frame va hacia el router, y sin etiqueta: h1 no sabe nada de VLANs.

h1$ tcpdump -t -e -n -r /tmp/c.pcap
02:00:00:00:00:01 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 10.203.110.254 tell 10.203.110.1, length 28
02:00:00:00:00:fe > 02:00:00:00:00:01, ethertype ARP (0x0806), length 42: Reply 10.203.110.254 is-at 02:00:00:00:00:fe, length 28
02:00:00:00:00:01 > 02:00:00:00:00:fe, ethertype IPv4 (0x0800), length 98: 10.203.110.1 > 10.203.120.2: ICMP echo request, id 94, seq 0, length 64
02:00:00:00:00:fe > 02:00:00:00:00:01, ethertype IPv4 (0x0800), length 98: 10.203.120.2 > 10.203.110.1: ICMP echo reply, id 94, seq 0, length 64

Esta es la respuesta a la pregunta de antes. h1 quiere llegar a 10.203.120.2, pero con ARP buscó a 10.203.110.254, su gateway. Y el frame con el ping tiene la MAC del router como destino, y la IP de h2 adentro. Su tabla ARP confirma que nunca oyó hablar de la MAC de h2:

h1$ ip neigh show dev eth1
10.203.110.254 lladdr 02:00:00:00:00:fe REACHABLE

Lo que ve h2. Otro frame, sin etiqueta, con el origen cambiado.

h2$ tcpdump -t -e -n -r /tmp/c.pcap
02:00:00:00:00:fe > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 10.203.120.2 tell 10.203.120.254, length 28
02:00:00:00:00:02 > 02:00:00:00:00:fe, ethertype ARP (0x0806), length 42: Reply 10.203.120.2 is-at 02:00:00:00:00:02, length 28
02:00:00:00:00:fe > 02:00:00:00:00:02, ethertype IPv4 (0x0800), length 98: 10.203.110.1 > 10.203.120.2: ICMP echo request, id 94, seq 0, length 64
02:00:00:00:00:02 > 02:00:00:00:00:fe, ethertype IPv4 (0x0800), length 98: 10.203.120.2 > 10.203.110.1: ICMP echo reply, id 94, seq 0, length 64

Comparen el echo request en los dos lados:

MAC origen MAC destino IP origen IP destino TTL
Visto en h1 …:01 (h1) …:fe (r1) 10.203.110.1 10.203.120.2 64
Visto en h2 …:fe (r1) …:02 (h2) 10.203.110.1 10.203.120.2 63

Las direcciones IP no cambiaron y las MAC sí, porque el frame nació y murió en cada enlace. El TTL bajó en 1 al pasar por r1. Lo comprobamos con tcpdump -v, que muestra ttl 64 en h1 y ttl 63 en h2. Es la idea clave de este capítulo en una sola tabla.

Lo que ve el trunk entre sw1 y sw2. Esta es la captura más interesante.

sw1$ tcpdump -t -e -n -r /tmp/c.pcap
02:00:00:00:00:01 > ff:ff:ff:ff:ff:ff, ethertype 802.1Q (0x8100), length 46: vlan 110, p 0, ethertype ARP (0x0806), Request who-has 10.203.110.254 tell 10.203.110.1, length 28
02:00:00:00:00:fe > 02:00:00:00:00:01, ethertype 802.1Q (0x8100), length 46: vlan 110, p 0, ethertype ARP (0x0806), Reply 10.203.110.254 is-at 02:00:00:00:00:fe, length 28
02:00:00:00:00:01 > 02:00:00:00:00:fe, ethertype 802.1Q (0x8100), length 102: vlan 110, p 0, ethertype IPv4 (0x0800), 10.203.110.1 > 10.203.120.2: ICMP echo request, id 94, seq 0, length 64
02:00:00:00:00:fe > ff:ff:ff:ff:ff:ff, ethertype 802.1Q (0x8100), length 46: vlan 120, p 0, ethertype ARP (0x0806), Request who-has 10.203.120.2 tell 10.203.120.254, length 28
02:00:00:00:00:02 > 02:00:00:00:00:fe, ethertype 802.1Q (0x8100), length 46: vlan 120, p 0, ethertype ARP (0x0806), Reply 10.203.120.2 is-at 02:00:00:00:00:02, length 28
02:00:00:00:00:fe > 02:00:00:00:00:02, ethertype 802.1Q (0x8100), length 102: vlan 120, p 0, ethertype IPv4 (0x0800), 10.203.110.1 > 10.203.120.2: ICMP echo request, id 94, seq 0, length 64
02:00:00:00:00:02 > 02:00:00:00:00:fe, ethertype 802.1Q (0x8100), length 102: vlan 120, p 0, ethertype IPv4 (0x0800), 10.203.120.2 > 10.203.110.1: ICMP echo reply, id 94, seq 0, length 64
02:00:00:00:00:fe > 02:00:00:00:00:01, ethertype 802.1Q (0x8100), length 102: vlan 110, p 0, ethertype IPv4 (0x0800), 10.203.120.2 > 10.203.110.1: ICMP echo reply, id 94, seq 0, length 64

Hay tres cosas para notar.

  • Cada frame lleva su etiqueta. Los tamaños son 46 y 102, en lugar de 42 y 98: los 4 bytes de 802.1Q.
  • El mismo ping cruza el trunk dos veces. h1 y h2 están conectados al mismo switch, sw1. Pero están en VLANs distintas, de modo que el echo request sube hacia r1 por el trunk con la etiqueta 110, y vuelve por el mismo cable con la etiqueta 120. Es el precio de un router colgado: ese tramo se recorre de ida y de vuelta.
  • Nadie cruza de VLAN sin pasar por r1. El cambio de 110 a 120 ocurre solo en el router.
Animación del echo request de h1, en la VLAN 110, a h2, en la VLAN 120. El frame sale de h1 sin etiqueta, con 98 bytes y TTL 64, hacia la MAC del router. sw1 le pone la etiqueta 110 y sube por los trunks, con 102 bytes, pasando por sw2 hasta r1. r1 baja el TTL a 63 y arma un frame nuevo, de …:fe a …:02, con la etiqueta 120, que baja por los mismos cables hasta sw1. sw1 quita la etiqueta y lo entrega a h2 con 98 bytes. La tabla de abajo se llena tramo por tramo; las IP de origen y destino no cambian en ningunoAnimación del echo request de h1, en la VLAN 110, a h2, en la VLAN 120. El frame sale de h1 sin etiqueta, con 98 bytes y TTL 64, hacia la MAC del router. sw1 le pone la etiqueta 110 y sube por los trunks, con 102 bytes, pasando por sw2 hasta r1. r1 baja el TTL a 63 y arma un frame nuevo, de …:fe a …:02, con la etiqueta 120, que baja por los mismos cables hasta sw1. sw1 quita la etiqueta y lo entrega a h2 con 98 bytes. La tabla de abajo se llena tramo por tramo; las IP de origen y destino no cambian en ninguno
Las MAC, la etiqueta y el TTL cambian en r1, y las IP no. h1 y h2 están en el mismo switch, pero entre VLANs todo pasa por el router.

Y estos son los bytes:

sw1$ tcpdump -t -e -n -xx -c 1 -r /tmp/c.pcap "vlan and icmp"
02:00:00:00:00:01 > 02:00:00:00:00:fe, ethertype 802.1Q (0x8100), length 102: vlan 110, p 0, ethertype IPv4 (0x0800), 10.203.110.1 > 10.203.120.2: ICMP echo request, id 94, seq 0, length 64
	0x0000:  0200 0000 00fe 0200 0000 0001 8100 006e
	0x0010:  0800 4500 0054 38fe 4000 4001 0612 0acb

Después de las dos MAC vienen 8100 (el TPID) y 006e. 0x006e es 110. PCP y DEI están en 0. Sigue 0800, el EtherType real. Wireshark lo explica igual; también se puede usar tshark, dentro del contenedor del curso, sobre el mismo archivo:

$ docker cp clab-w03-campus-sw1:/tmp/c.pcap .
$ tshark -r c.pcap -Y 'icmp.type == 8' -O vlan
Frame 3: 102 bytes on wire (816 bits), 102 bytes captured (816 bits)
Ethernet II, Src: 02:00:00:00:00:01 (02:00:00:00:00:01), Dst: 02:00:00:00:00:fe (02:00:00:00:00:fe)
802.1Q Virtual LAN, PRI: 0, DEI: 0, ID: 110
    000. .... .... .... = Priority: Best Effort (default) (0)
    ...0 .... .... .... = DEI: Ineligible
    .... 0000 0110 1110 = ID: 110
    Type: IPv4 (0x0800)
Internet Protocol Version 4, Src: 10.203.110.1, Dst: 10.203.120.2
Internet Control Message Protocol

Lo que ve r1. En eth1, lo mismo que en el trunk. En la subinterfaz, en cambio, la etiqueta ya no está.

r1$ tcpdump -t -e -n -r /tmp/d.pcap icmp
02:00:00:00:00:01 > 02:00:00:00:00:fe, ethertype IPv4 (0x0800), length 98: 10.203.110.1 > 10.203.120.2: ICMP echo request, id 94, seq 0, length 64
02:00:00:00:00:fe > 02:00:00:00:00:01, ethertype IPv4 (0x0800), length 98: 10.203.120.2 > 10.203.110.1: ICMP echo reply, id 94, seq 0, length 64

Error común. Filtrar un archivo de captura con icmp y creer que no hay tráfico. En un trunk, los bytes del frame llevan la etiqueta, y por eso el filtro debe ser vlan and icmp. Sobre /tmp/c.pcap, el filtro icmp solo no devuelve nada. Al capturar en vivo, icmp sí los muestra, porque el kernel entrega la etiqueta aparte del frame. Conviene saberlo para no perder una hora.

Por último, la tabla de los switches demuestra lo que dijimos sobre la pareja (VLAN, MAC):

sw1$ bridge fdb show br br0 | grep -v permanent
02:00:00:00:00:02 dev eth2 vlan 120 master br0
02:00:00:00:00:01 dev eth1 vlan 110 master br0
02:00:00:00:00:04 dev eth3 vlan 120 master br0
02:00:00:00:00:fe dev eth3 vlan 120 master br0
02:00:00:00:00:fe dev eth3 vlan 110 master br0

La MAC del router (…:fe) aparece dos veces, una en cada VLAN. Las dos subinterfaces de r1 usan la misma MAC, y el switch las trata como dos entradas diferentes. El traceroute también muestra al router como un salto entre las dos VLANs, aunque los dos hosts estén a un metro:

h1$ traceroute -n -q 1 10.203.120.4
traceroute to 10.203.120.4 (10.203.120.4), 30 hops max, 46 byte packets
 1  10.203.110.254  0.003 ms
 2  10.203.120.4  0.003 ms

Los hosts h1 y h3, en cambio, están en la misma VLAN aunque en distintos switches: no aparecen saltos entre ellos, porque para IP son vecinos.

Una VLAN que falta en un trunk

Es la falla más común cuando se trabaja con VLANs. Quitemos la VLAN 120 del trunk de sw1.

sw1$ bridge vlan del dev eth3 vid 120
h1$ ping -c 1 -W 1 10.203.120.254 | tail -2
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.083/0.083/0.083 ms
h2$ ping -c 1 -W 1 10.203.120.254 | tail -2
--- 10.203.120.254 ping statistics ---
1 packets transmitted, 0 packets received, 100% packet loss

h1 funciona y h2 no, y por el mismo cable. El enlace está arriba, la VLAN 110 pasa y la VLAN 120 no: el error está en la lista de VLANs permitidas. El script de comprobación del laboratorio, que veremos enseguida, detecta este caso:

$ make check
ok     sw1 tiene el filtrado de VLANs activo
ok     sw2 tiene el filtrado de VLANs activo
FALLA  el trunk de sw1 lleva 110 y 120, sin la VLAN 1
ok     el trunk de sw2 hacia r1 lleva 110 y 120, sin la VLAN 1
ok     h1 llega a h3 (misma VLAN, por el trunk)
FALLA  h2 llega a h4 (misma VLAN, por el trunk)
ok     h1 llega a su gateway
FALLA  h1 llega a h2 pasando por r1
ok     traceroute de h1 a h4 pasa por el gateway

Error común. Dar por hecho que un mismo número de VLAN “conecta” dos switches. El número solo tiene sentido si la VLAN está permitida en todos los trunks del camino. Cuando una VLAN falla y las demás no, lo primero es revisar bridge vlan show en cada switch por donde pasa.

En la práctica. Una red real tiene más opciones que el router colgado. Un switch de capa 3 hace routing entre VLANs por hardware, sin dar la vuelta por un cable. Los switches también intercambian la lista de VLANs por protocolos, y en los trunks suele haber una VLAN nativa que va sin etiqueta. En las redes de los data centers modernos, las VLANs no alcanzan: 4094 son pocas para miles de clientes, y se usan overlays como VXLAN, de la semana 14.

El laboratorio de esta semana

El laboratorio se hace en code/week-03/, dentro del contenedor del curso. Compilen primero con make.

Archivo Para qué
topo/lan.clab.yml Un switch y cuatro hosts: pasos 1 a 3
topo/campus.clab.yml Dos switches, un trunk y un router: paso 4
topo/loop.clab.yml Tres switches en triángulo: la demostración de spanning tree
vlan/ Los scripts de VLANs que usa este capítulo
lab/reto_*.sh Lo que ustedes escriben para el reto
check_lab.py Revisa la topología campus ya configurada

Paso 1: ARP. Levanten la red con make lan. Esperen unos segundos, y miren ip neigh show dev eth1 en un host. Dejen tcpdump en él, hagan ping a un vecino, y capturen el intercambio ARP. Vacíen la tabla con ip neigh flush dev eth1 para repetirlo. Para abrir la captura en Wireshark, cópienla con docker cp.

En la práctica. En los primeros segundos de una topología pueden aparecer mensajes router solicitation de IPv6, en especial en interfaces recién creadas. No tienen que ver con el laboratorio. Por eso capturamos con un filtro como arp or icmp.

Paso 2: aprendizaje. Miren la tabla del switch con bridge fdb show br br0 | grep -v permanent. Antes de hacer un ping, escriban qué puertos van a recibir el primer frame. Compruébenlo con tcpdump en otro host. Luego repitan el experimento de flooding: vacíen la tabla del switch, pero no la tabla ARP del host.

Paso 3: VLANs. Apliquen el script vlan/lan_sw1.sh y comprueben qué pings dejan de funcionar. Capturen en un host de la otra VLAN mientras falla la consulta ARP: ¿le llega?

Paso 4: trunk y router. Destruyan la anterior con make destroy y levanten make campus. Apliquen los tres scripts de vlan/, léanlos línea por línea, y corran:

$ make check
ok     sw1 tiene el filtrado de VLANs activo
ok     sw2 tiene el filtrado de VLANs activo
ok     el trunk de sw1 lleva 110 y 120, sin la VLAN 1
ok     el trunk de sw2 hacia r1 lleva 110 y 120, sin la VLAN 1
ok     h1 llega a h3 (misma VLAN, por el trunk)
ok     h2 llega a h4 (misma VLAN, por el trunk)
ok     h1 llega a su gateway
ok     h1 llega a h2 pasando por r1
ok     traceroute de h1 a h4 pasa por el gateway

Luego, capturen en eth3 de sw1 y encuentren la etiqueta 802.1Q en Wireshark: el campo VID de un ping entre VLANs debe aparecer con los dos valores.

Reto. Mover a h4 a una nueva VLAN 130, con la subred 10.203.130.0/24 y gateway 10.203.130.254, sin que esa VLAN vaya hacia sw1, que no la necesita. Escriban los comandos en lab/reto_sw2.sh, lab/reto_r1.sh y lab/reto_h4.sh, y aplíquenlos sobre la topología ya configurada, con docker exec -i clab-w03-campus-sw2 sh -e < lab/reto_sw2.sh, y así en cada nodo. Comprueben con make check-reto.

Paso 5: entregas. Esta semana se entrega P0 y se publica P1, Tu red. Léanlo: P1 usa VLANs, trunks y un router, pero en una red distinta, y con sus propias reglas. Lo de este capítulo son las herramientas, no la solución.

Al terminar, make clean destruye las topologías y sus redes de Docker. make test corre todas las pruebas de la semana: los programas en C, las afirmaciones sobre CRC y ALOHA, y cada experimento de este capítulo con sus topologías. Tarda unos minutos, y al final no deja nada corriendo. La prueba del reto se omite si no está la carpeta de soluciones.

En la práctica. Si una topología no se levanta, revisen la sección de solución de problemas de labs/README.md. Para saber qué dejaron corriendo: docker ps --filter name=clab-w03.

Resumen

  • La capa física mueve bits, con límites que dependen del medio: atenuación, interferencia, medio compartido. La capacidad de un canal tiene un máximo teórico, dado por Shannon.
  • La capa de enlace entrega frames entre vecinos, un salto a la vez. Sus servicios son framing, acceso al medio, entrega y detección de errores.
  • Paridad detecta errores impares; paridad bidimensional corrige uno. El checksum es barato, pero no ve palabras intercambiadas. El CRC es una división módulo 2, detecta toda ráfaga de hasta r bits, y su costo es casi nulo en hardware.
  • El enlace usa CRC, en cada salto y en hardware. El transporte usa un checksum, de extremo a extremo y por software. Protegen cosas diferentes.
  • El acceso múltiple tiene tres familias: particionar, acceso aleatorio, tomar turnos. ALOHA ranurado aprovecha como mucho el 37 % del canal y ALOHA puro el 18 %; CSMA/CD mejora mucho esa cifra si la propagación es corta frente a la transmisión.
  • En una Ethernet conmutada y full-duplex no hay colisiones, y CSMA/CD no se usa. Permanece el frame: MAC, EtherType, datos de 46 a 1500 bytes y CRC-32.
  • Una MAC es plana y dice a qué vecino; una IP es jerárquica y dice a dónde va el paquete al final. La MAC cambia en cada salto, y la IP no.
  • ARP encuentra la MAC de una IP de la red local. Pregunta por broadcast, porque no sabe a quién dirigirse; responde por unicast, porque sí sabe. Para otra subred, se busca la MAC del gateway.
  • Un switch aprende de las direcciones de origen, y hace forwarding, filtering o flooding según su tabla. No necesita configuración.
  • Sin un árbol, una tormenta de broadcast puede tumbar la red. STP bloquea puertos hasta dejar un árbol, y vuelve a calcularlo si algo falla, aunque el STP clásico tarda decenas de segundos.
  • Una VLAN separa dominios de broadcast dentro de un mismo equipo físico. Los puertos access van sin etiqueta y los trunk con etiqueta 802.1Q. Para pasar de una VLAN a otra hace falta un router.

Ejercicios

  1. Calculen a mano el CRC de los datos 1011010 con el generador 1001. Comprueben con build/crc. Luego cambien un bit de la palabra resultante y revísenla con build/crc -c.
  2. Un CRC usa G = 1011. a) ¿Cuántos bits tiene el CRC? b) ¿Qué ráfagas detecta siempre? c) ¿Tiene a x + 1 como factor? (Pista: un polinomio tiene ese factor si y solo si tiene una cantidad par de unos.) ¿Qué significa eso para los errores de tres bits? Compruébenlo con un error concreto.
  3. Una matriz de paridad bidimensional de 4 × 4 llega con una fila y dos columnas que fallan. ¿Cuántos bits dañados puede haber, como mínimo? ¿Se puede corregir el error? Justifiquen. Den un ejemplo de un error de 4 bits que no se detecte.
  4. En detect.py, agreguen una fila para un daño que invierte el orden de los 64 bytes del frame. Antes de correrlo, predigan cuántos daños no detecta cada método. Para el checksum, piensen qué le pasa a cada palabra de 16 bits y, por lo tanto, a la suma. ¿Sus predicciones se cumplen?
  5. En ALOHA ranurado hay 10 nodos, y cada uno transmite con probabilidad p = 0.1. a) ¿Qué fracción de las ranuras tiene un frame exitoso? b) ¿Cuántas quedan vacías? c) ¿Cuántas tienen colisión? d) Si p sube a 0.3, ¿qué pasa? Calculen, y compárenlo con la columna de aloha.c que corresponda.
  6. Dos tarjetas de red de 100 Mb/s, con CSMA/CD, colisionan por tercera vez consecutiva. a) ¿Cuántos valores puede tomar K? b) ¿Qué probabilidad hay de que elijan el mismo valor? c) ¿Cuánto es lo máximo que puede esperar cada una? d) ¿Por qué dejaría de ser un problema con un switch full-duplex?
  7. Un frame de Ethernet lleva un ping de 56 bytes de datos: 8 de ICMP y 20 de IP encima. a) ¿Cuánto mide el frame sin FCS? Compárenlo con el length de las capturas. b) ¿Cuál es el tamaño de un frame con un mensaje ICMP de 8 bytes en total, sin datos, en un cable real? ¿Hay relleno? ¿Cuánto? c) ¿Qué fracción del cable aprovechan los datos de IP en ese caso?
  8. En make lan, sin VLANs, hagan que h1 haga ping a h2 y luego a h3. Escriban qué va a contener la tabla del switch y la tabla ARP de cada host, antes de comprobarlo. Luego, cambien el cable de h2: apaguen eth2 de sw1 y expliquen qué tendría que pasar para que el switch aprenda una nueva ubicación. ¿Qué pasa con la entrada si nadie vuelve a usar esa MAC?
  9. Un host con IP 10.203.5.7/24 y gateway 10.203.5.1 quiere enviar un paquete a 10.203.6.9. Describan cada frame que aparece, con sus MAC de origen y destino, en estos dos enlaces: del host al router y del router al destino. ¿Cuántas veces se usa ARP si las dos tablas están vacías? ¿Qué cambia si el destino es 10.203.5.9?
  10. Cuatro switches forman un cuadrado: s1–s2–s3–s4–s1. Sus prioridades son 1000, 2000, 3000 y 4000 (identificadores distintos), y todos los enlaces tienen el mismo costo. Determinen la raíz, el puerto raíz de cada uno, los puertos designados y cuál puerto se bloquea. ¿Qué pasa si se corta el enlace s1–s2? Con los temporizadores por defecto, ¿cuánto tarda en reanudarse el tráfico? Comprueben el resultado adaptando topo/loop.clab.yml, sin quitar los retardos.
  11. En make campus, después de configurar las VLANs, provoquen un broadcast desde h1: una consulta ARP por una dirección que no existe (ping -c 1 -W 1 10.203.110.77). a) ¿A qué enlaces llega? ¿Con qué etiqueta? b) ¿Llega a h2, que está en el mismo switch? c) Comprueben capturando en sw1:eth3, h2 y h3. d) Si se pone la VLAN 110 como pvid untagged en eth3 de los dos switches, ¿qué cambia en la captura y qué sigue funcionando?
  12. (Desafío) Escriban un programa en C parecido a arp_probe.c, que escuche con AF_PACKET en una interfaz y lleve una tabla propia con la última MAC vista por cada IP, leyendo los ARP que pasan. Debe avisar cuando una IP cambie de MAC. Pruébenlo con make lan: desde otro host, usen ip link set eth1 address ... para cambiar una MAC, y provoquen una consulta. ¿Por qué un detector así no puede saber cuál de las dos MAC es la legítima? ¿Cómo cambia lo que se ve si se configuran VLANs?