# Bucle de capa 2

Un bucle de capa 2 en un MikroTik RB5009 que el log de RouterOS no mostró, hallado en el log del kernel al ritmo del hello de STP, y que volvió después.

Source: https://docs.jmrp.io/mikroscope/es/playbooks/loop/

**En resumen:** un bucle de capa 2 detrás de un puerto de un bridge de MikroTik
se ve en el log del kernel de Linux aunque el log de RouterOS y su monitor de
puertos sigan limpios, como aquí: `received packet on <port> with own address as source address`,
con las tramas reflejadas separadas 2,00–2,01 s, que es el
hello interval de STP. El puerto de esa línea es por donde entró la trama
reflejada, no siempre el puerto que el bucle ha dejado aislado, así que compara el
rx de cada puerto del bridge con su tx antes de desenchufar un cable. Aquí el
flujo bajó de 1,49 /s
a 0,03 /s tras actualizar el firmware de los puntos de
acceso de la malla, y una semana después el bucle volvió.

Un fallo real, no provocado. Se encontró por casualidad el 2026-09-12, mientras
se medía otra cosa, en el RB5009UG+S+ de producción del propietario (RouterOS
7.24.2, kernel 5.6.3), y se diagnosticó y desapareció ese mismo día. Una semana
después [volvió](https://docs.jmrp.io/mikroscope/es/playbooks/loop/#volvió). Durante toda su vida, el log de RouterOS y su monitor
de puertos informaron de un equipo sano.

Los pasos 1 a 6 son el diagnóstico en el orden en que se hizo. Síguelos en el
mismo orden en tu propio router.

## Síntoma

`/snapshot` traía un flujo constante de `events` — la fuente `kmsg` del agente —
a **1,49 /s**:

```text
[6] br0: port 2(eth1) entered blocking state
[4] br0: received packet on eth1 with own address as source address (addr:00:00:5e:00:53:5d, vlan:0)
[6] br0: port 2(eth1) entered learning state
```

La dirección de la segunda línea es la del propio router: la MAC de
`sfp-sfpplus1`, volviendo a entrar en el bridge. Aquí aparece como
`00:00:5e:00:53:5d`, del bloque que la
[RFC 9542](https://www.rfc-editor.org/rfc/rfc9542.html#section-2.1.4) reserva
para documentación (sustituyó a la RFC 7042 en 2024); en tu equipo será la de tu
bridge, y así es como reconoces la línea.

## 1. Confirmar y contar

Cuenta sucesos por segundo en una ventana lo bastante larga como para ser
estable. Una sola línea de log es una anécdota; una tasa es una medida, y te dice
después si una corrección funcionó:

```sh
curl -s "http://172.30.10.2:9123/snapshot?seconds=120" \
  | python3 -c '
import sys, json, collections
rows=[json.loads(l) for l in sys.stdin if l.strip()]
secs=sum(r["dt_ns"] for r in rows)/1e9
ev=[e for r in rows for e in r.get("events",[])]
print(f"{len(ev)/secs:.2f} events/s over {secs:.0f}s")
c=collections.Counter(e["msg"].split("(")[0][:60] for e in ev)
for k,v in c.most_common(): print(f"  {v:5d}  {k}")'
```

`?seconds=` admite de 1 a 3600, y el agente solo puede devolver lo que su anillo
aún guarda — 60 s con el `BUFFER_S` por defecto. La longitud de la ventana sale
de sumar el `dt_ns` de cada muestra, no del número que pediste.

## 2. Leer los tiempos

Toma los intervalos entre apariciones consecutivas de un mismo mensaje:

```sh
# intervalos entre apariciones consecutivas de un mismo mensaje
... | python3 -c '
import sys, json
ts=sorted(e["us"]/1e6 for l in sys.stdin if l.strip()
          for e in json.loads(l).get("events",[]) if "own address" in e["msg"])
print([round(ts[i+1]-ts[i],2) for i in range(len(ts)-1)][:12])'
```

`us` es la marca de tiempo del propio kernel para el registro, en microsegundos
desde el arranque sobre el reloj monótono, no el momento en que el agente lo
leyó, así que los intervalos son los del kernel, no los del muestreador.

Los intervalos entre las tramas reflejadas eran de **2,00–2,01 s**, siempre. 2 s
es el _hello interval_ de STP, el [HelloTime por defecto de
RouterOS](https://help.mikrotik.com/docs/spaces/ROS/pages/21725254/Spanning+Tree+Protocol#SpanningTreeProtocol-Defaultvalues).
Así que el router enviaba una BPDU y recibía de vuelta su propia BPDU: un bucle,
no un cliente que se porta mal.

`mikroscope doctor`, ejecutado por sí solo, hace este recuento por ti
([`internal/health/health.go`](https://github.com/jmrplens/mikroscope/blob/main/internal/health/health.go)). Lee una vez el anillo del agente en
marcha e informa de:

- `WARN  layer2-loop`, con el puerto, cuando en la ventana han vuelto tres o más
  tramas con la propia dirección del bridge;
- `stp-churn` para un puerto que STP ha pasado a learning al menos tres veces
  más de las que lo ha dejado reenviar, salvo que ese puerto tenga ya el
  hallazgo de dirección propia.

Dos reglas de alerta de [`internal/dashboards/alerts.go`](https://github.com/jmrplens/mikroscope/blob/main/internal/dashboards/alerts.go) vigilan el
mismo fallo a lo largo del tiempo:

- `mikroscope-l2-loop` salta con el primer registro de dirección propia, desde
  el log del kernel que lee el agente;
- `mikroscope-bridge-port-dark` salta con un puerto del bridge que lleva diez
  minutos recibiendo sin que el bridge le envíe nada. Necesita el tier de API.

Véase [Comprobaciones de salud](https://docs.jmrp.io/mikroscope/es/install/prerequisites/#lo-que-muestra-el-agente-en-marcha)
y [Reglas de alerta](https://docs.jmrp.io/mikroscope/es/dashboards/alerts/).

## 3. Revisar la vista de RouterOS

Revisa de forma explícita el log y el monitor de puertos de RouterOS; la
respuesta decide dónde miras después:

```routeros
/log/print where topics~"bridge" or topics~"stp" or topics~"interface"
/interface/bridge/port/monitor [find] once
```

Las dos volvieron limpias: cero filas de log en esos temas, todos los puertos
`designated-port` / `in-bridge`. RouterOS no estaba ocultando el fallo; no
muestra esta clase de sucesos del kernel.

Eso vale para el log y el monitor de puertos, no para todos los contadores que
ofrece la API. Cuando el bucle volvió, del 2026-09-19 al 23, los contadores rx/tx
por puerto que lee el tier de API sí lo mostraron, como un puerto del bridge que
recibe mientras el bridge no le envía nada. Es la regla
`mikroscope-bridge-port-dark`, contrastada con esos cuatro días. No se comprobó contra el episodio del 2026-09-12.

La línea que habría traído el tema `interface` es la que se busca cuando uno se
topa con este fallo. RouterOS la ha escrito en su propio log, con los temas
`interface,warning`, de esta forma:

```text
<port>: bridge port received packet with own address as source address (<MAC>), probably loop
```

Esa forma sale del log de RouterOS de un usuario, [publicado en el foro de
MikroTik](https://forum.mikrotik.com/t/bridge-port-received-packet-with-own-address-as-source-probably-loop/106109/13)
el 2017-12-10, con el puerto y la dirección sustituidos; qué versiones de RouterOS
la siguen escribiendo no está establecido aquí. En el router de referencia, con
RouterOS 7.24.2, no apareció ninguna línea así, mientras la del propio kernel,
`received packet on eth1 with own address`, llegaba cada 2 s.

Además de STP, la defensa de RouterOS contra un bucle es [Loop
Protect](https://help.mikrotik.com/docs/spaces/ROS/pages/23396469/Loop+Protect),
que envía sus propios paquetes por una interfaz, la desactiva cuando uno de ellos
vuelve y lo deja en el log. La página de MikroTik recomienda (R/M)STP antes que
Loop Protect en los puertos de un bridge. No intervino en este diagnóstico, y no
se afirma que hubiera detectado este bucle.

## 4. Corroborar

Busca una segunda observación, independiente, fuera del log del kernel. La MAC
del mensaje pertenecía a `sfp-sfpplus1`, lo que apuntaba al segmento SFP+; las
propias tablas del bridge apuntaban a otro sitio. Cuenta los hosts aprendidos en
cada puerto:

```routeros
# hosts aprendidos por puerto
:foreach p in=[/interface/bridge/port/find] do={ \
  :local n [/interface/bridge/port/get $p interface]; \
  :put ($n . " hosts=" . [:len [/interface/bridge/host/find interface=$n]]) }
```

| Puerto         | MAC aprendidas | Tráfico                          | `edge` de RSTP |
| -------------- | -------------- | -------------------------------- | -------------- |
| `sfp-sfpplus1` | 60             | 23,8 GB rx                       | true           |
| **`ether2`**   | **0**          | **9,15 GB rx / 22,4 M paquetes** | **false**      |
| el resto       | 0–2            | —                                | true           |

`ether2` estaba pasando 22 millones de paquetes por un enlace sano de 1 Gbps y el
bridge no había aprendido **nada** detrás de él, que es lo que hace un bridge en
un puerto por el que no deja de ver sus propias direcciones. Era además el único
puerto que recibía BPDU: las dos anomalías, en el mismo puerto.

El log del kernel había dicho `eth1`, no `ether2`: el kernel y RouterOS llaman de
forma distinta al mismo puerto. [Nombres de puertos](https://docs.jmrp.io/mikroscope/es/reference/port-names/)
relaciona unos con otros en un solo paso seguro, y explica por qué el agente lo
hace por ti en esta placa.

## 5. Poner a prueba la hipótesis

La hipótesis: el equipo de `ether2` tiene un segundo camino hasta el router a
través de los otros AP, que cuelgan del switch. Primero se hicieron dos cambios
en el switch, y **ninguno movió la tasa**:

| Cambio                | Tasa de sucesos                     |
| --------------------- | ----------------------------------- |
| línea base            | 1,49 /s |
| cambio en el switch 1 | 1,67 /s                             |
| cambio en el switch 2 | 1,47 /s                             |
| (banda de ruido)      | ±0,2 /s                             |

Una lectura más de la misma serie, 1,50 /s, también quedó dentro de esa banda.

Eso descartó el switch con evidencia. **Vuelve a medir después de cada cambio,
incluso uno que esperas que funcione**, y conoce tu banda de ruido antes de
interpretar una diferencia.

## 6. Corregir y confirmar

Se actualizó el firmware de los AP de la malla (unidades Deco); el cableado no se
tocó. Después:

```text
events 9 -> 0.03/s   (baseline 1.49/s)
```

y de esos nueve, _ninguno_ era el mensaje del bucle: eran los AP volviendo
(`eth1: phy link up`, `eth1: set isolation from 0 to 1`). El mensaje del bucle,
que había aparecido cada 2 s (30 veces por minuto, rodeado del doble de registros
de blocking/learning), apareció **cero** veces en 300 s.

Confirma con una segunda medida, independiente. Aquí la visión del bridge se
volvió _coherente_:

|                       | Antes | Después  |
| --------------------- | ----- | -------- |
| MAC en `ether2`       | 0     | **24**   |
| MAC en `sfp-sfpplus1` | 60    | **36**   |
| `edge` de `ether2`    | false | **true** |

Los mismos 60 equipos, repartidos 36/24 en lugar de 60/0. Mientras existió el
bucle, el router aprendía casi todos los equipos en el puerto equivocado; el
coordinador Zigbee `00:4B:12:96:80:33`, por ejemplo, pasó del SFP+ a `ether2`,
donde está. Que encaje una _segunda_ medida no relacionada es lo que
hace de una corrección algo más que una coincidencia en el tiempo. Confirma que
el fallo había desaparecido cuando se midió, no que siga sin aparecer: una semana
después [volvió](https://docs.jmrp.io/mikroscope/es/playbooks/loop/#volvió).

> **No establecido**
>
> El 2026-09-12 el bucle desapareció tras la actualización de firmware, y las dos medidas de arriba
> confirman que ya no estaba. Por qué hacía bucle el firmware anterior no está establecido: la
> corrección se observó, no se explicó. Tampoco está establecido si la reaparición del 2026-09-19
> fue el mismo fallo, ni si el firmware tuvo algo que ver con ninguna de las dos.

## Reaparición

La misma firma reapareció el 2026-09-19, con RouterOS 7.24.4, de nuevo un bucle
de capa 2 por un segundo camino a través de un punto de acceso de la malla, y
duró hasta que ese camino se cortó el 2026-09-23. Durante sus primeras 34 h el puerto al que el
bridge había dejado de entregar tráfico fue `sfp-sfpplus1`, y después `ether2`
durante 62 h. Entre las 12:07 y las 18:55 UTC del 2026-09-21 y del 22, `ether2`
recibió 12 y 13 paquetes por segundo y envió 1; en esas mismas horas del
2026-09-23, tras desaparecer el segundo camino a las 12:06, recibió 192 y envió 178.

La segunda vez hubo dos diferencias:

- Durante la mayor parte de la primera fase, los registros de dirección propia
  del log del kernel nombraban `ether2`, mientras que el puerto que recibía sin
  obtener nada de vuelta era `sfp-sfpplus1`: el mensaje nombra el puerto por el
  que entró la trama reflejada, no el puerto que el bucle había dejado aislado.
- Los contadores por puerto de RouterOS sí lo mostraron, leídos como recepción
  sin transmisión. El contraste de `mikroscope-bridge-port-dark` con los datos
  del 2026-09-19 11:13 al 2026-09-23 22:44 UTC marca esos dos puertos en esos
  dos tramos y ningún otro.

El almacén InfluxDB de referencia guardó las dos mitades, y el gráfico de abajo
sale de él, hora a hora: arriba los registros de dirección propia del kernel, y
debajo lo que cada uno de los dos puertos recibió y se le envió. Mientras el
puerto aislado era `sfp-sfpplus1`, los registros llegaban de pocos en pocos cada
hora; en cuanto lo fue `ether2`, llegaban cada 2 s, como el 2026-09-12.

_La vuelta del bucle, hora a hora_ — Por horas, del 2026-09-19 11:00 al 2026-09-24 11:00 UTC, del almacén InfluxDB de referencia (RB5009UG+S+, RouterOS 7.24.4, Linux 5.6.3). Hasta el 2026-09-20 21:00 el kernel anotó como mucho 12 registros de dirección propia por hora, mientras el bridge no enviaba nada a sfp-sfpplus1 en ninguna hora y recibía 6,5 por segundo de él. Desde entonces y hasta el último registro, el 2026-09-23 12:06:46, registró una mediana de 1 798 por hora, uno cada 2,0 s, y el bridge envió a ether2 0,6 paquetes por segundo frente a 13 recibidos. Después, ether2 recibió 183 y se le enviaron 144. Las tasas son medianas de las medias horarias.

## Firma

**Un fallo real, no provocado** · 2026-09-12

- Un flujo constante y periódico de `events` del kernel en reposo, donde el
  estado sano es cero.
- `received packet on <port> with own address as source address`, con la propia
  MAC del router dentro.
- El puerto de ese mensaje es por donde entró la trama reflejada, no
  necesariamente el puerto que el bucle ha dejado aislado. Del 2026-09-19 al 20
  nombraba `ether2` mientras `sfp-sfpplus1` era el puerto que recibía y no
  enviaba nada. Compara el rx de cada puerto del bridge con su tx antes de
  desenchufar un cable.
- Tiempos entre llegadas de 2,00–2,01 s: el hello interval de STP.
- Un puerto con mucho tráfico en el que el bridge no ha aprendido ningún host,
  con `edge=false` mientras sus vecinos están a `true`.
- `/log/print` y `/interface/bridge/port/monitor`, ambos limpios.

## Conclusiones

- Un texto alarmante es una pista; una tasa es evidencia; una tasa antes y
  después es una conclusión.
- Lee los tiempos entre llegadas. A menudo te nombran el protocolo.
- Prefiere un cambio que discrimine entre hipótesis a un cambio que tal vez lo
  arregle.
- Desconfía de una corrección que solo tu instrumento principal puede confirmar.
- Sigue vigilando después de la corrección. Una corrección confirmada dice que el
  fallo no estaba cuando mediste, y este volvió una semana después.

## Véase también

- [Nombres de puertos](https://docs.jmrp.io/mikroscope/es/reference/port-names/): cómo `eth1` resultó ser `ether2`, y lo
  que el agente incluye para ahorrarte esa hora.
- [Línea base en reposo](https://docs.jmrp.io/mikroscope/es/playbooks/idle/): por qué cero sucesos es la línea base
  contra la que destacó este flujo.
- [Visibilidad del contenedor](https://docs.jmrp.io/mikroscope/es/limits/namespaces/): por qué el log del kernel es
  visible desde el contenedor y los contadores de las interfaces no.
- [Reglas de alerta](https://docs.jmrp.io/mikroscope/es/dashboards/alerts/): `mikroscope-l2-loop` y
  `mikroscope-bridge-port-dark`, las dos reglas que vigilan este fallo.
