# Reglas de detección

Las once reglas de detección de mikroscope sobre el kernel de MikroTik RouterOS: la condición exacta de cada una, qué necesita y qué no puede afirmar.

Source: https://docs.jmrp.io/mikroscope/es/sinks/detections/

Una detección es un evento discreto que el colector pone en la línea temporal: un «mira
aquí», nunca una serie continua y nunca un veredicto. Para cada una de las once reglas:
cuándo salta, qué tiene que aportar el despliegue para que pueda saltar y qué no te puede
decir. Los valores derivados en los que se apoyan algunas reglas están en [Valores
derivados](https://docs.jmrp.io/mikroscope/es/sinks/derive/).

## Campos de una detección

Toda detección tiene los mismos campos:

| Campo            | Significado                                                                        |
| ---------------- | ---------------------------------------------------------------------------------- |
| `rule`           | la regla que saltó                                                                 |
| `key`            | la CPU, el núcleo, la zona o el puerto al que se refiere; vacío para una regla de todo el equipo |
| `seq`, `wall_ns` | la muestra que la provocó                                                          |
| `value`          | la cantidad que comparó la regla                                                   |
| `threshold`      | contra qué la comparó: el umbral propio de la regla, escrito en cada evento        |
| `message`        | el evento en palabras                                                              |

**Una vez por regla y clave cada 10 s.** Tras saltar una regla para una clave, la misma
regla y clave quedan suprimidas durante 10 s del reloj de pared de las muestras, así que una
condición que persiste salta cada 10 s en vez de en cada muestra. La etapa cuenta lo que
suprimió, pero ningún destino exporta esa cuenta.

Las reglas se ejecutan en el proceso del colector y su historia vive allí. Un colector que
se reinicia empieza desde cero cada ventana móvil, cada intervalo y cada valor anterior.

## Destinos

| Destino                         | Forma                                                                                |
| ------------------------------- | ------------------------------------------------------------------------------------ |
| InfluxDB, Telegraf, stdout `lp` | `mikroscope_detection{rule,key}` con `value`, `threshold`, `seq`, `message`          |
| SQL (`--sql`, `--postgres`)     | una fila de `mikroscope_detection`                                                   |
| file, stdout `json`             | una línea `{"detection":…}`                                                          |
| Prometheus                      | `mikroscope_collector_detections_total{rule}`, cada regla a 0 desde el primer scrape |
| Loki                            | una línea en el stream `source="detection"`, `level="warn"`                          |
| OTLP                            | un sum de deltas `mikroscope.detection{rule}` de 1                                   |
| Graphite                        | `detection.<rule>` = 1 en el segundo del evento                                      |
| Elasticsearch                   | un documento con `kind: detection`                                                   |

Los paneles dibujan cada detección como una anotación, y una de las [reglas de
alerta](https://docs.jmrp.io/mikroscope/es/dashboards/alerts/) salta con cualquier detección salvo `microburst` e
`ipc-collapse`: esas dos describen cómo lleva el tráfico un router sano, y se dibujan y se guardan
pero no avisan.

## Reglas

| Regla             | Clave     | Salta cuando                                                                                                                              | Necesita                                |
| ----------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
| `counter-reset`   | —         | la muestra informa de un contador que retrocedió sin un desbordamiento de 32 bits                                                         | cualquier despliegue                    |
| `agent-restart`   | —         | el número de secuencia retrocedió                                                                                                         | cualquier despliegue                    |
| `agent-oom`       | —         | el cgroup propio del contenedor registró un OOM kill                                                                                      | cgroup2 en el contenedor                |
| `microburst`      | `cpu<N>`  | tres muestras `burst` en una CPU en 60 s                                                                                                  | softnet                                 |
| `reboot`          | —         | cambió el identificador de arranque del kernel, o el reloj desde el arranque de un registro del log del kernel es menor que el del registro anterior | cualquier despliegue; la vía del log del kernel [requiere `privileged=yes`](https://docs.jmrp.io/mikroscope/es/limits/privileged/) |
| `link-flap`       | puerto    | dos o más registros de enlace up/down en un puerto en 60 s                                                                                | [requiere `privileged=yes`](https://docs.jmrp.io/mikroscope/es/limits/privileged/)                      |
| `conntrack-cliff` | —         | `nf_conntrack` cayó por debajo de la mitad de su valor guardado anterior                                                                  | [requiere `privileged=yes`](https://docs.jmrp.io/mikroscope/es/limits/privileged/)                      |
| `conntrack-high`  | —         | ocupación por encima del 80 % de `nf_conntrack_max` **y** en aumento durante los últimos 60 s                                             | [requiere `privileged=yes`](https://docs.jmrp.io/mikroscope/es/limits/privileged/)                      |
| `thermal-high`    | zona      | una zona a menos del 15 % de su propio disparo crítico declarado                                                                          | una zona térmica que declare un disparo |
| `thermal-rising`  | zona      | tres subidas consecutivas de un minuto de más de 1 °C cada una                                                                            | una zona térmica                        |
| `ipc-collapse`    | `core<N>` | el IPC de un segundo de un núcleo por debajo de la mitad de su mediana móvil **mientras** su tasa de ciclos está por encima de su mediana | la PMU                                  |

### `counter-reset`

**Salta cuando** el `resets` de la muestra del kernel es mayor que 0: el agente encontró un
contador más bajo que su lectura anterior sin un desbordamiento de 32 bits que lo explique,
y usó el valor del contador tras el reinicio como delta de ese tick, una cota inferior.
`value` es el número de esos contadores, `threshold` 0.

**Necesita** solo una muestra. La misma condición marca la muestra como `suspect`,
y los valores derivados por paquete se omiten para ella.

**No puede afirmar** qué contador se reinició, ni por qué. Cada delta de esa muestra es una
cota inferior.

### `agent-restart`

**Salta cuando** el número de secuencia de una muestra es menor que el de la muestra
anterior. `value` es el nuevo número de secuencia, `threshold` el anterior.

**Dice** lo que el colector tenía de los 30 s anteriores al reinicio, al final del mensaje:
la parte ocupada de la CPU en todos los núcleos y la del núcleo más cargado, MemAvailable (la
última y la más baja) frente a MemTotal, `nf_conntrack` frente a su límite, las caídas y los
`time_squeeze` de softnet, cualquier muerte por OOM o espera de asignación, y cuánto tiempo no
llegó ninguna muestra después de la última. Un reinicio se lleva el anillo del agente y el
log del propio RouterOS, pero no lo que el colector ya había leído. El tiempo sin muestras se
omite cuando el reloj del router tras el reinicio es anterior al de antes, como en un router
cuyo reloj aún no ha puesto en hora NTP. Cuando el agente informa del identificador de
arranque del kernel, el mensaje dice también si cambió: el mismo identificador significa que
el router no se reinició, y que se reinició solo el contenedor del agente (una actualización,
una parada y un arranque, su política de reinicio). Solo lo dice con dos identificadores que
comparar: el primer agente que da uno, después de uno que no lo daba, no dice nada de ello.

**Necesita** que el colector haya visto al menos una muestra antes del reinicio. La
secuencia del agente vuelve a empezar desde 1 en cada arranque, así que así es como se ve un
reinicio desde fuera. En su siguiente lectura de salud, como mucho un minuto, el colector
retrocede su cursor de lectura a la muestra más antigua del anillo nuevo y registra `agent
restarted: its newest sample is N and the cursor was M; resuming from K`; la secuencia baja
del agente nuevo frente a la anterior es lo que casa esta regla
(`TestResyncAfterAgentRestart` cubre el lado del colector).

**No puede afirmar** por qué se reinició el agente. El resumen da lecturas, no una causa. Un colector reiniciado a la vez no tiene
número de secuencia anterior y no ve nada.

### `agent-oom`

**Salta cuando** el cgroup propio del contenedor registra un OOM kill en la muestra — el
kernel mató un proceso dentro del contenedor de mikroscope. `value` es el número de kills.

**Necesita** cgroup2 legible en el contenedor; sin él, el agente no informa de eventos de
cgroup y esta regla no puede saltar.

**No puede afirmar** nada sobre los números que la rodean: todos los números de esa ventana
son sospechosos. Cómo dimensionar la memoria del contenedor según el anillo del agente está
en [el coste del observador](https://docs.jmrp.io/mikroscope/es/cost/).

### `microburst`

**Salta cuando** la muestra de una CPU lleva la marca [`burst`](https://docs.jmrp.io/mikroscope/es/sinks/derive/#burst)
— un descarte, o squeezes por encima del percentil 90 móvil de esa CPU y al menos 3,
mientras su cuenta de paquetes estaba en su mediana móvil o por debajo — y esa CPU tiene ya
al menos tres muestras marcadas en los últimos 60 s. `value` es el número de muestras
marcadas en la ventana, `threshold` 3; el mensaje lleva los squeezes, descartes y paquetes
de la última muestra y la mediana móvil.

**Necesita** `/proc/net/softnet_stat`, que lee todo despliegue, y diez muestras de historia
por CPU antes de su primera marca. Las referencias abarcan diez segundos de reloj de pared a
cualquier cadencia del muestreador.

**Falsos positivos.** El squeeze puede ser el fondo de un router, no un suceso. En el router
medido, la mayoría de las muestras por CPU no llevan ningún squeeze, y `time_squeeze` es 1 en el 11,2 %,
2 en el 1,2 % y 3 en el 0,21 %. Una ventana móvil de una
distribución que es casi toda ceros tiene un percentil 90 de 1, así que «por encima de p90»
lo cumple cualquier 2 — por eso quien manda es el suelo de 3 y no el percentil. Reejecutada
sobre muestras almacenadas, con suelo 2 salta 77,7 /h y con suelo 3
salta 0,5 /h. Un descarte marca por sí solo, con cualquier cuenta de squeezes.

**No puede afirmar** el tamaño de la ráfaga, ni el flujo ni la interfaz que la causó. Dice
que el kernel se quedó sin presupuesto más de lo habitual mientras llevaba menos paquetes
de lo habitual — prueba de algo más corto que el intervalo de muestreo.

### `reboot`

**Salta cuando** el identificador de arranque del kernel difiere del que el colector leyó
antes. El agente lo lee una vez al arrancar y lo publica en
[`/healthz`](https://docs.jmrp.io/mikroscope/es/reference/http/#get-healthz); el kernel genera uno nuevo en cada
arranque y lo mantiene hasta el siguiente, y un contenedor comparte el kernel del router, así
que un identificador nuevo significa que el router se reinició, no solo el contenedor del
agente. El colector registra `router rebooted: the kernel's boot id went from … to …` en la
lectura de salud que lo ve, y la regla salta con la primera muestra del agente que volvió.
`value` y `threshold` son 0.

También salta cuando la marca de tiempo de un registro del log del kernel, en microsegundos
desde el arranque, es menor que la del registro anterior, la única vía para un agente que no
informa del identificador; entonces `value` y `threshold` son las marcas nueva y anterior en
segundos. Un reinicio del que ya informó el identificador de arranque no la hace saltar una
segunda vez. En ambos casos el mensaje lleva el mismo resumen de los 30 s anteriores que el
[`agent-restart`](https://docs.jmrp.io/mikroscope/es/sinks/detections/#agent-restart) que llegó con él.

**Dice**, cuando el colector tiene capa de API, lo que RouterOS escribió sobre el arranque, entre
comillas tras el motivo: quién o qué lo reinició (`RouterOS logged at boot: "router rebooted by
ssh-cmd:admin@192.168.88.10/reboot"`), o que cayó sin apagarse (`"router was rebooted without
proper shutdown"`). La detección espera esa lectura, dos minutos como mucho, porque la conexión de
la API vuelve después que el router ([Log de arranque](https://docs.jmrp.io/mikroscope/es/sinks/api-tier/#el-log-de-arranque)).
Sin capa de API salta con la primera muestra del agente, sin esa parte.

**Necesita** un colector que siga en marcha durante el reinicio mientras el agente vuelve, y
ninguna credencial de la API de RouterOS. El agente que vuelve es un proceso nuevo; el
colector lee su identificador de arranque en la misma lectura de salud que retrocede el cursor
al anillo nuevo, en menos de un minuto (ver [`agent-restart`](https://docs.jmrp.io/mikroscope/es/sinks/detections/#agent-restart)). La vía del
log del kernel necesita `privileged=yes`, que exige el log del kernel. En el laboratorio, un
`/system/reboot` la hizo saltar por el identificador de arranque, y una parada y un arranque del
contenedor del agente no ([Probado en](https://docs.jmrp.io/mikroscope/es/about/status/#reboot-detections)).

**No puede afirmar** que se vea cada reinicio, ni por qué se reinició el router: la línea de
RouterOS nombra quién lo reinició o dice que cayó sin apagarse, no por qué se fue la corriente o
se colgó el router. Un colector
arrancado después del reinicio no tiene un identificador anterior con el que comparar, y dos
reinicios entre dos lecturas de salud, separadas un minuto, son un solo cambio de
identificador. La vía del log del kernel solo ve lo que el agente lee desde el final del log
después de arrancar: cuando el primer registro tras un reinicio tiene un tiempo desde el
arranque posterior al del último antes de él, por esa vía no salta nada.

### `link-flap`

**Salta cuando** un registro del log del kernel que nombra una interfaz se clasifica como
`link-up` o `link-down` —el mismo clasificador que pone un `kind` en cada registro de
puerto— y ese puerto tiene ya dos o más de esos registros en los últimos 60 s. `key` es el
nombre actual del puerto en RouterOS cuando el inventario de interfaces de la capa de la
API lo da, el nombre por defecto de la placa cuando la tabla de puertos del agente traduce
el nombre del kernel, y el nombre del kernel en otro caso. `value` es el número de
registros en la ventana, `threshold` 2.

**Necesita** `privileged=yes`. Un nombre de RouterOS necesita que la placa esté en la tabla
de puertos del agente, y el nombre actual necesita además la capa de la API; consulta
[Nombres de puertos](https://docs.jmrp.io/mikroscope/es/reference/port-names/).

**No puede afirmar** una avería. Un cable desenchufado y vuelto a enchufar en menos de un
minuto son un registro de down y uno de up, y salta. La regla ha saltado con flaps no
provocados sin decir si los causó el cable, el equipo del otro lado u otra cosa; no se ha
capturado ningún flap provocado con ella en marcha
([Probado en](https://docs.jmrp.io/mikroscope/es/about/status/#not-tested)).

### `conntrack-cliff`

**Salta cuando** la cuenta de objetos activos de la caché slab `nf_conntrack` está por
debajo de la mitad de su valor guardado anterior. `value` es la nueva cuenta, `threshold` la
anterior.

**Necesita** `privileged=yes`, para `/proc/slabinfo`. El slab se lee a unos 6 Hz y se guarda
al cambiar, así que «anterior» es la muestra guardada anterior, no el tick anterior.

**Tampoco puede afirmar** una avería: el mensaje dice «a flush or a reset», y un vaciado
deliberado de la tabla de conexiones la hace saltar.

### `conntrack-high`

**Salta cuando** los objetos activos de `nf_conntrack` están por encima de 0,8 del
`nf_conntrack_max` del kernel, **y** la cuenta es mayor que el valor guardado más antiguo de
los últimos 60 s. `value` es la ocupación como fracción, `threshold` 0,8.

**Necesita** `privileged=yes`, el techo publicado por el kernel y al menos dos muestras
guardadas en los últimos 60 s.

**No puede afirmar** cuándo se llenará la tabla: a propósito, no se adjunta ningún tiempo
hasta el llenado. Como escala, la tabla de un router medido
tenía 6 287 entradas, un 0,65 % del techo de su kernel.

### `thermal-high`

**Salta cuando** la lectura de una zona está en 0,85 o más del punto de disparo crítico
declarado más bajo de esa misma zona. `value` es la lectura en °C, `threshold` 0,85 × el
disparo.

**Necesita** una zona térmica que declare un disparo crítico. Una zona que no declara
ninguno nunca salta; no se compara nada con un número compilado.

**No puede afirmar** que haya fallado la refrigeración, ni nada sobre una zona que la placa
no informa.

### `thermal-rising`

**Salta cuando** las últimas cuatro medias de un minuto completas de una zona superan cada
una a la anterior en más de 1 °C — tres subidas consecutivas. `value` es la subida desde la
primera de las cuatro medias hasta la última, `threshold` 3. Un intervalo de un minuto se
cierra con la primera muestra al menos 60 s después de abrirse, y la regla se comprueba cada
vez que se cierra uno, así que lo antes que puede saltar es tras unos cuatro minutos de
lecturas.

**Necesita** una zona térmica. Las medias son sobre las lecturas que llevaban las muestras;
la temperatura se lee a la cadencia de sondeo declarada por la zona
(`mikroscope_thermal_polling_seconds`).

**Resolución.** Un sensor que cuantiza a unos 0,42 °C, como el medido,
resuelve 1 °C por minuto en 2,4 escalones.

**No puede afirmar** una causa, ni una subida más lenta que 1 °C por minuto.

### `ipc-collapse`

**Salta cuando**, en un núcleo, se cierra un intervalo de un segundo con instrucciones por
ciclo por debajo de la mitad de la mediana de los intervalos móviles de ese núcleo
**mientras** su tasa de ciclos está por encima de la mediana de sus tasas móviles. `key` es
`core<N>`, `value` el IPC del intervalo, `threshold` la mitad de la mediana.

**Necesita** los `cycles` e `instructions` de la PMU por CPU (con `privileged=yes`), y veinte
intervalos de un segundo completos de historia para ese núcleo antes de poder saltar; la
historia móvil guarda hasta sesenta.

**No puede afirmar** inactividad, ni nada agregado: la conjunción con la tasa de ciclos es lo
que separa un régimen de bloqueo por memoria de un núcleo que se queda quieto, y la regla es
por núcleo, nunca entre núcleos.

## Comprobaciones de doctor frente a reglas

`doctor` a secas lee el anillo del agente en marcha y hace cuatro comprobaciones propias:
`layer2-loop`, `stp-churn`, `link-flap` y `softnet-drops`, descritas en [Comprobaciones de
salud](https://docs.jmrp.io/mikroscope/es/install/prerequisites/#lo-que-muestra-el-agente-en-marcha).
No son detecciones: se ejecutan una vez, cuando `doctor` las pide, sobre lo que guarde el
anillo hasta sus 10 000 muestras más recientes, 60 s por defecto, y no escriben nada en ningún destino.

Un nombre se comparte y la regla no. El `link-flap` de `doctor` cuenta solo las caídas de
enlace, dos o más en un puerto en cualquier punto del anillo; la detección cuenta subidas y
caídas juntas, dos o más en 60 s, así que un cable desenchufado y vuelto a enchufar una vez
hace saltar la detección y no la comprobación. Un bucle de capa 2 no es ninguna de las once
reglas: el `layer2-loop` de `doctor` lo señala en el anillo, y sobre el histórico lo vigila
en el almacén la [regla de alerta](https://docs.jmrp.io/mikroscope/es/dashboards/alerts/) `mikroscope-l2-loop`.

Cuatro de las once reglas han saltado en un router real, y solo de `microburst` se ha medido
el comportamiento contra las muestras. Las otras siete solo las ejercitan las pruebas
unitarias de la etapa de derivación contra muestras construidas, y no se ha provocado ningún
fallo con las reglas en marcha ([Probado en](https://docs.jmrp.io/mikroscope/es/about/status/#not-tested)).

## Véase también

- [Valores derivados](https://docs.jmrp.io/mikroscope/es/sinks/derive/): la marca `burst` y los demás valores que
  se escriben junto a las muestras.
- [Reglas de alerta](https://docs.jmrp.io/mikroscope/es/dashboards/alerts/): las reglas de Grafana construidas sobre las
  detecciones y los contadores de averías.
- [Bucle de capa 2](https://docs.jmrp.io/mikroscope/es/playbooks/loop/): lo que cazó el log del kernel en un router
  en servicio.
- [Modo privilegiado](https://docs.jmrp.io/mikroscope/es/limits/privileged/): las fuentes de las que dependen la
  mitad de estas reglas.
