Páginas

Buscar este blog

sábado, 14 de noviembre de 2026

Más allá del tema: Red de seguridad para tu FreeBSD con ZFS y bectl

Instalar un tema visual como WhiteSur (estilo macOS) en XFCE es una tarea de bajo riesgo. Al fin y al cabo, los scripts solo escriben en la carpeta de usuario (~/.themes). Si algo sale mal, basta con borrar la carpeta y elegir otro tema en Apariencia.

Sin embargo, esta instalación es la excusa perfecta para aprender y practicar dos herramientas vitales en FreeBSD con ZFS: los snapshots (fotografías del sistema) y los Boot Environments (entornos de arranque). Dominar esto te salvará el día antes de un freebsd-update, un cambio de controladores o una actualización de paquetes.

Contexto: Este tutorial está probado en FreeBSD 14.5 con ZFS. Aprenderemos a crear una red de seguridad de tres niveles: desde lo más rápido (snapshot del home) hasta lo más robusto (arrancar un sistema anterior completo).

Nivel 1: Snapshots ZFS (El "deshacer" instantáneo)

Un snapshot en ZFS es una copia de solo lectura del estado de un dataset en un momento exacto. Son instantáneos, no duplican la información y son tu primera línea de defensa.

1. Crear el snapshot (antes de instalar el tema)

Como el tema se instala en tu directorio personal, basta con hacer un snapshot solo del home. Es rápido y quirúrgico:

sudo zfs snapshot -r zroot/usr/home@antes-whitesur

Si prefieres proteger absolutamente todo el sistema, puedes hacer un snapshot recursivo del pool (aunque para un tema no es necesario):

sudo zfs snapshot -r zroot@antes-whitesur

2. La trampa del rollback recursivo

Aquí hay un matiz que confunde a mucha gente: zfs snapshot -r sí es recursivo (baja a todos los hijos), pero zfs rollback -r no lo es. En el rollback, la opción -r solo destruye snapshots intermedios del mismo dataset, no se aplica a los hijos.

Por lo tanto, si hiciste el snapshot solo del home, revertir es tan sencillo como:

sudo zfs rollback -r zroot/usr/home@antes-whitesur

Reinicia XFCE (o el equipo) y tu carpeta personal volverá al estado exacto anterior a la instalación del tema.

Ojo: Si hiciste un snapshot recursivo de zroot y quieres revertir todo, tendrás que hacerlo dataset por dataset (raíz, home, etc.). En el anexo final te dejo un script que automatiza esta tarea.

Nivel 2: Boot Environments (El "arranque seguro")

Los Snapshots son geniales, pero si el problema afecta al sistema base (por ejemplo, un fallo de arranque), necesitas algo más potente. Aquí entran los Boot Environments (BE): clones completos y arrancables de tu sistema raíz.

En FreeBSD 14.5 no necesitas instalar nada: bectl ya viene incluido en el sistema base. (Aunque existe beadm en los repositorios de paquetes, bectl es la opción nativa y más directa).

1. Crear un entorno de arranque

Antes de aplicar cambios arriesgados (o instalar tu tema), crea un BE:

sudo bectl create antes-whitesur

Puedes verificar que se ha creado y cuál está activo:

bectl list

2. Simulacro de desastre y recuperación

  1. Instalas el tema y, por lo que sea, el entorno gráfico se rompe o el sistema no arranca.
  2. Reinicias el equipo.
  3. En el menú de arranque de FreeBSD (el cargador), seleccionas el entorno antes-whitesur. ¡Listo! El sistema arranca exactamente como estaba antes.
  4. Si quieres que este sea el entorno por defecto de ahora en adelante, ejecuta:
    sudo bectl activate antes-whitesur

El BE con el tema roto seguirá ahí por si quieres arrancarlo para investigar, hasta que decidas eliminarlo con sudo bectl destroy.


El flujo de trabajo recomendado (Paso a paso)

Si quieres tener ambas redes de seguridad antes de personalizar tu sistema:

  1. Snapshot rápido: sudo zfs snapshot -r zroot/usr/home@antes-whitesur
  2. Red de seguridad total: sudo bectl create antes-whitesur
  3. Instala y configura tu tema WhiteSur.
  4. Si solo falla la apariencia: Haz un rollback del home (zfs rollback) y reinicia XFCE.
  5. Si el sistema entero se vuelve inestable: Reinicia y elige el BE antes-whitesur en el menú de arranque.

Anexo: Script para rollback recursivo de ZFS

Si alguna vez necesitas revertir un snapshot recursivo de todo el pool ZFS (por ejemplo, antes de una actualización mayor del sistema), hacerlo dataset por dataset a mano es un tedio. Aquí tienes un pequeño script en sh que lista los datasets, comprueba si tienen el snapshot y los revierte uno por uno, con modo de simulación (--dry-run) para ver qué hará antes de tocar nada.

#!/bin/sh
# Guarda esto como rollback-zfs.sh y dale permisos: chmod +x rollback-zfs.sh
# Uso: sudo ./rollback-zfs.sh zroot antes-whitesur [--dry-run]

set -u
POOL="${1:-}"
SNAP_NAME="${2:-}"
DRY_RUN="no"

for arg in "$@"; do
    [ "$arg" = "--dry-run" ] && DRY_RUN="si"
done

if [ -z "$POOL" ] || [ -z "$SNAP_NAME" ]; then
    echo "Uso: $0 <pool> <nombre_snapshot> [--dry-run]"
    exit 1
fi

DATASETS=$(zfs list -H -r -o name "$POOL" 2>/dev/null)
if [ -z "$DATASETS" ]; then
    echo "Error: No se encontraron datasets en '$POOL'."
    exit 1
fi

if [ "$DRY_RUN" = "si" ]; then
    echo "=== MODO SIMULACIÓN (dry-run) ==="
else
    echo "=== MODO REAL ==="
fi
echo "Pool: $POOL | Snapshot: @$SNAP_NAME"
echo ""

# Fase 1: Mostrar plan
PLAN_OK=0
for ds in $DATASETS; do
    SNAP_FULL="${ds}@${SNAP_NAME}"
    if zfs list -H -t snapshot -o name "$SNAP_FULL" >/dev/null 2>&1; then
        echo "[PLAN] zfs rollback -r $SNAP_FULL"
        PLAN_OK=$((PLAN_OK + 1))
    else
        echo "[SALTADO] ${ds} (no tiene el snapshot)"
    fi
done

if [ "$DRY_RUN" = "si" ]; then
    echo ""
    echo "=== Simulación finalizada. ==="
    exit 0
fi

# Fase 2: Confirmación y ejecución
printf "\n¿Ejecutar rollback real? [s/N]: "
read RESPUESTA
case "$RESPUESTA" in
    s|S|si|SI) ;;
    *) echo "Cancelado."; exit 0 ;;
esac

echo ""
for ds in $DATASETS; do
    SNAP_FULL="${ds}@${SNAP_NAME}"
    if zfs list -H -t snapshot -o name "$SNAP_FULL" >/dev/null 2>&1; then
        echo -n "Revirtiendo ${ds} ... "
        if zfs rollback -r "$SNAP_FULL" 2>/dev/null; then
            echo "OK"
        else
            echo "FALLO (posible clonación o uso activo)"
        fi
    fi
done
echo "=== Proceso finalizado. Reinicia el equipo. ==="
Consejo final: Si haces rollback de la raíz del sistema (zroot/ROOT/default), asegúrate de hacerlo desde el modo single-user (opción 2 del cargador de FreeBSD) para evitar conflictos con procesos activos.

FreeBSD es genial!.

Guía: Transforma FreeBSD y XFCE con el tema macOS WhiteSur (MacBook Pro 2012)

Instalar el tema WhiteSur (macOS) en FreeBSD con XFCE

Contexto: Este tutorial está probado en FreeBSD 14.5 (instalado en una MacBook Pro de 2012) con el entorno de escritorio XFCE.

El tema WhiteSur imita la estética de macOS. Sus scripts de instalación (install.sh) están diseñados exclusivamente para GNU/Linux y fallan en FreeBSD debido a diferencias en herramientas nativas como sed, readlink y setterm.

Para solucionarlo, evitaremos los scripts y haremos una instalación manual. Dividiremos el proceso en dos partes: el tema GTK (interfaz) y los iconos.


Parte 1: Tema GTK (WhiteSur-gtk-theme)

A diferencia del código fuente, los temas ya compilados se encuentran en la página de Releases.

1. Descargar el tema compilado

Visita la página oficial de Releases de WhiteSur-gtk-theme y descarga el paquete comprimido que necesites (por ejemplo, WhiteSur-*.tar.xz).

Nota: No descargues el "Source code", sino el archivo .tar.xz que aparece en la sección Assets.

2. Descomprimir en el directorio de temas

Abre tu terminal y extrae el archivo en la carpeta oculta de temas de tu usuario:

mkdir -p ~/.themes
tar -xf ~/Descargas/WhiteSur-*.tar.xz -C ~/.themes/

3. Activar el tema en XFCE

  • Para la interfaz de las aplicaciones: Ve a Configuración → Apariencia → Estilo y selecciona WhiteSur.
  • Para los bordes de las ventanas: Ve a Configuración → Gestor de ventanas → Estilo y selecciona el tema correspondiente.

Parte 2: Iconos (WhiteSur-icon-theme)

El autor de WhiteSur no proporciona paquetes de iconos precompilados en la página de Releases (solo "Source code"). Como el script de instalación falla en FreeBSD por el uso de sed, simularemos manualmente lo que hace el script.

1. Descargar y extraer el código fuente

Descarga el código fuente desde Releases de WhiteSur-icon-theme y descomprímelo. Luego, entra en la carpeta:

cd ~/Descargas/WhiteSur-icon-theme-2026-09-10

2. Crear el directorio de destino y copiar los archivos

Crea la carpeta de iconos para tu usuario y copia el contenido de la carpeta src/ (que incluye los iconos y el archivo index.theme):

mkdir -p ~/.local/share/icons/WhiteSur
cp -R src/* ~/.local/share/icons/WhiteSur/

3. Modificar manualmente index.theme (Paso crucial)

El archivo index.theme generado por el código fuente puede tener un nombre genérico. Usaremos el sed nativo de FreeBSD (que requiere comillas vacías después de -i) para asegurar que el nombre sea correcto:

sed -i '' 's/^Name=.*/Name=WhiteSur/' ~/.local/share/icons/WhiteSur/index.theme
Si este comando da error, simplemente abre ~/.local/share/icons/WhiteSur/index.theme con un editor de texto y cambia la línea Name= a Name=WhiteSur.

4. Verificar y aplicar

Verifica que el archivo de configuración existe:

ls ~/.local/share/icons/WhiteSur/index.theme

Luego, ve a Configuración → Apariencia → Iconos en XFCE y selecciona WhiteSur.


Notas finales y solución de problemas

  • ¿Por qué hacerlo manual? El script original copia src/ a ~/.local/share/icons y usa sed para modificar el index.theme. Como el sed de FreeBSD no soporta la escritura directa igual que el de Linux, el script falla. Al hacer estos dos pasos manualmente, evitas el problema.
  • Caché de iconos: Si algunos iconos no se actualizan, puedes forzar la recarga ejecutando:
    gtk-update-icon-cache ~/.local/share/icons/WhiteSur
    (Si el comando no existe en tu sistema, simplemente cierra sesión y vuelve a entrar).
FreeBSD es genial!.

martes, 10 de noviembre de 2026

Replicación Incremental ZFS en FreeBSD: Script Cliente-Servidor Automatizado

"Este artículo detalla cómo crear un sistema de copias de seguridad ZFS profesional entre dos servidores FreeBSD (cliente y servidor), resolviendo los errores más comunes de desincronización de GUIDs y bloqueos de datasets, y automatizando el proceso con cron."

ZFS Replicación Incremental Cliente-Servidor

Entorno: FreeBSD 14.5
Topología: Cliente solaris (192.168.88.51) → Servidor tormenta (192.168.88.160)
Dataset: zroot/home/carlos → zbackup/solaris/home/carlos
Método: Transferencia por red (SSH) con ZFS nativo.

1. Filosofía de Diseño

Este script (zfsrep.sh) está diseñado para ser extremadamente resiliente. No asume que la red funcione siempre, ni que el cliente esté encendido todos los días. Sus principios son:

  • Uso de -I en lugar de -i: Envía todos los snapshots intermedios. Si el equipo ha estado apagado 5 días, ZFS rellena el hueco automáticamente en lugar de fallar.
  • Cero uso de -F: El flag destructivo de ZFS está prohibido. Si el destino se desincroniza (por ejemplo, por un cambio de GUID), el script fallará, pero nunca borrará datos del destino silenciosamente.
  • Auto-saneamiento: Utiliza zfs receive -A antes de empezar para abortar cualquier recepción que se haya quedado colgada (evitando el temido error "dataset is busy").
  • SSH Keepalive: Utiliza opciones de SSH para evitar que la conexión se quede congelada en redes lentas.

2. El Script Completo (v1.2.1)

Ubicación

/home/carlos/bin/zfsrep.sh

Contenido completo

#!/bin/sh
### BEGIN INFO
# NOMBRE:  Replicación Incremental ZFS cliente - servidor
# DESC:    Replica zroot/home/carlos (solaris) -> zbackup/solaris/home/carlos
# VERSION: 1.2.1
### END INFO

# Detectar fallos en cualquier punto de la tubería (zfs send | ssh).
set -o pipefail

NOMBRE="Replicacion Incremental ZFS cliente - servidor"
VERSION="1.2.1"

snap_prefix=snap
retention=30

# Patrón estricto: solo acepta snap-YYYYMMDD (8 dígitos).
# Evita que nombres manuales (snap-14.4-RELEASE) rompan el orden.
snap_pat="${snap_prefix}-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]"

# Rutas absolutas para cron.
date=/bin/date
cut=/usr/bin/cut
grep=/usr/bin/grep
sed=/usr/bin/sed
sort=/usr/bin/sort
xargs=/usr/bin/xargs
zfs=/sbin/zfs

src_0="zroot/home/carlos"
dst_0="zbackup/solaris/home/carlos"
host="root@192.168.88.160"

# Keepalive SSH
SSH_OPTS="-o ConnectTimeout=10 -o ServerAliveInterval=30 \
          -o ServerAliveCountMax=10 -o TCPKeepAlive=yes \
          -o BatchMode=yes"

today="$snap_prefix-`$date +%Y%m%d`"
snap_today="$src_0@$today"
log=/home/carlos/cronlog

echo >> $log
echo "$NOMBRE $VERSION" >> $log
echo "(ejecucion manual)" >> $log
$date >> $log
echo >> $log

# Comprobar conectividad SSH antes de nada.
if ! ssh $SSH_OPTS $host true 2>/dev/null; then
    echo "ERROR: no hay conexion SSH con $host. Abortando." >> $log
    echo "**********" >> $log
    exit 1
fi

# Limpia el estado del destino: desmonta y aborta receives colgados.
# El uso de sh -c evita que csh/tcsh en el remoto malinterprete el 2>.
clean_dst() {
    ssh $SSH_OPTS $host "sh -c 'zfs unmount \"$dst_0\" 2>/dev/null; zfs receive -A \"$dst_0\" 2>/dev/null; true'"
}

# --- Snapshots locales antiguas (para purga) ---
snap_old=`$zfs list -t snapshot -o name \
  | $grep "${src_0}@${snap_pat}" \
  | $sort -r | $sed 1,${retention}d | $sort | $xargs -n 1`

# --- Snapshot base en destino remoto ---
snap_base_name=`ssh $SSH_OPTS $host "zfs list -t snapshot -o name \
  | grep '${dst_0}@${snap_pat}' \
  | sort -r | sed 1q | cut -d@ -f2"`
snap_base="${src_0}@$snap_base_name"

# --- Snapshots remotas antiguas (para purga) ---
snap_old_dst_0=`ssh $SSH_OPTS $host "zfs list -t snapshot -o name \
  | grep '${dst_0}@${snap_pat}' \
  | sort -r | sed 1,${retention}d | sort"`

# --- Snapshot de hoy en origen ---
if $zfs list -H -o name -t snapshot | $grep "${snap_today}$" > /dev/null
then
    echo "La instantánea de hoy '$snap_today' ya existe." >> $log
else
    echo "Tomando la instantánea de hoy: $snap_today" >> $log
    if ! $zfs snapshot "$snap_today" >> $log 2>&1; then
        echo "ERROR: no se pudo crear la instantánea $snap_today." >> $log
        echo "**********" >> $log
        exit 1
    fi
fi

echo >> $log

# --- Comparar la PUNTA de origen y destino ---
snap_last_src=`$zfs list -t snapshot -o name \
  | $grep "${src_0}@${snap_pat}" | $sort -r | sed 1q`
snap_last_dst=`ssh $SSH_OPTS $host "zfs list -t snapshot -o name \
  | grep '${dst_0}@${snap_pat}' | sort -r | sed 1q"`

src_suffix=`echo "$snap_last_src" | $cut -d@ -f2`
dst_suffix=`echo "$snap_last_dst" | $cut -d@ -f2`

if [ -n "$src_suffix" ] && [ "$src_suffix" = "$dst_suffix" ]; then
    echo "Origen y destino ya sincronizados en @$src_suffix. Nada que enviar." >> $log
else
    echo "Destino desfasado (@${dst_suffix:-vacio} vs @${src_suffix:-vacio}). Replicando..." >> $log

    if [ -n "$snap_base_name" ] && $zfs list -H -o name -t snapshot | $grep "${snap_base}$" > /dev/null
    then
        echo "Usando '$snap_base' como base del incremental (-I)..." >> $log
        clean_dst
        if $zfs send -I "$snap_base" "$snap_today" \
           | ssh $SSH_OPTS $host "zfs receive -vu -x mountpoint '$dst_0'" >> $log 2>&1
        then
            echo "Replicacion completada." >> $log
        else
            echo "ERROR: fallo la replicacion incremental." >> $log
            echo "**********" >> $log
            exit 1
        fi
    else
        echo "No se encontró snapshot común. Realizando envío completo..." >> $log
        clean_dst
        if $zfs send "$snap_today" \
           | ssh $SSH_OPTS $host "zfs receive -vu -x mountpoint '$dst_0'" >> $log 2>&1
        then
            echo "Envío completo finalizado." >> $log
        else
            echo "ERROR: fallo el envio completo." >> $log
            echo "**********" >> $log
            exit 1
        fi
    fi
fi

echo >> $log

# --- Purga local ---
echo "Intentando destruir instantáneas antiguas $src_0..." >> $log
if [ -n "$snap_old" ]; then
    echo "Destruyendo las siguientes instantáneas antiguas:" >> $log
    echo "$snap_old" >> $log
    $zfs list -t snapshot -o name | $grep "${src_0}@${snap_pat}" \
      | $sort -r | $sed 1,${retention}d | $sort | $xargs -n 1 $zfs destroy >> $log 2>&1
else
    echo "No se pudo encontrar ninguna instantánea local para destruir." >> $log
fi

echo >> $log

# --- Purga remota ---
echo "Intentando destruir instantáneas antiguas $dst_0..." >> $log
if [ -n "$snap_old_dst_0" ]; then
    echo "Destruyendo las siguientes instantáneas antiguas en $host:" >> $log
    echo "$snap_old_dst_0" >> $log
    ssh $SSH_OPTS $host "zfs list -t snapshot -o name | grep '${dst_0}@${snap_pat}' \
      | sort -r | sed 1,${retention}d | sort | xargs -n 1 zfs destroy" >> $log 2>&1
else
    echo "No se pudo encontrar ninguna instantánea remota para destruir." >> $log
fi

echo "**********" >> $log
exit 0

3. Solución de Problemas (Lecciones Aprendidas)

3.1. El error "most recent snapshot does not match incremental source"

Síntoma: El script falla al intentar el incremental y en la terminal aparece que la snapshot más reciente del destino no coincide con la fuente incremental.

Causa: Aunque las snapshots tengan el mismo nombre en origen y destino (ej. snap-20261020), sus identificadores únicos (GUIDs) son distintos. Esto ocurre tras restauraciones o movimientos de discos que rompen el "ADN" de ZFS.

Solución: El origen es el maestro, el destino es el esclavo. Nunca se destruyen snapshots del origen para igualar. Hay que destruir el dataset en el destino y forzar un envío completo:

# En el servidor destino (tormenta)
zfs destroy -r zbackup/solaris/home/carlos

El script detectará que no hay base y hará un envío completo automático (Full Send).

3.2. El error "dataset is busy" al destruir

Síntoma: Al intentar borrar el dataset en destino, ZFS responde con cannot destroy ... dataset is busy, incluso sin procesos visibles.

Causa: Un intento de zfs receive fallido o interrumpido deja un candado a nivel de kernel.

Solución paso a paso:

# 1. Abortar recepciones colgadas
zfs receive -A zbackup/solaris/home/carlos

# 2. Forzar desmontaje
zfs unmount -f zbackup/solaris/home/carlos

# 3. Si sigue bloqueado, reiniciar el servidor (limpia los candados del kernel)
shutdown -r now

3.3. El problema del shell `csh` en SSH

En FreeBSD, el shell por defecto de root suele ser csh o tcsh. Al enviar comandos por SSH con redirecciones como 2>, csh lo interpreta como un argumento y el comando falla silenciosamente.

Solución: Envolver el comando remoto en sh -c '...', tal y como hace la función clean_dst del script.

4. Automatización (Cron)

Para que el backup se ejecute todos los días a las 3:00 AM sin intervención, añadir al crontab del usuario:

0 3 * * * /home/carlos/bin/zfsrep.sh > /dev/null 2>&1
Nota: El script ya escribe su propio log detallado en /home/carlos/cronlog.
Resultado validado: El script ha sido probado con éxito transfiriendo 75 GB a 68 MB/s tras una resincronización completa, y ha demostrado ser capaz de reanudar cadenas rotas con -I.
FreeBSD es genial!.

jueves, 5 de noviembre de 2026

Cómo arreglar la suspensión y el trackpad en una MacBook Pro 2012 con FreeBSD 14.5

El reto: hacer que una MacBook Pro de 2012 duerma (y despierte)

Instalar FreeBSD en hardware de Apple es un ejercicio de paciencia. Los MacBooks son máquinas excelentes, pero su firmware ACPI y sus controladores propietarios (como el del trackpad o el de la virtualización) no están pensados para colaborar con sistemas operativos que no sean macOS.

Hace unos días me propuse arreglar dos de los problemas más frustrantes de mi MacBook Pro de 2012 con FreeBSD 14.5: conseguir que el equipo suspenda correctamente (S3) sin apagarse, y lograr que el trackpad siga funcionando al despertar. Lo que sigue es el diario de a bordo de cómo lo resolví, con los comandos exactos y las trampas que encontré por el camino. Gran parte de las pistas iniciales las encontré en la excelente guía de Joshua: A Full Guide: FreeBSD on a MacBook Pro 11.4 (A1398).

El hardware: MacBook Pro de 2012 (Ivy Bridge), con FreeBSD 14.5-RELEASE. El sistema usa el driver gráfico i915kms y el sistema de gestión de energía ACPI.

Problema 1: El equipo se apaga en lugar de suspender

Al ejecutar acpiconf -s 3 o cerrar la tapa, el sistema no entraba en S3 (suspensión): se apagaba por completo. El primer paso fue activar el "modo de prueba" de ACPI para ver qué driver estaba fallando. Si el sistema sobrevive al bounce de suspensión, el problema está en la reanudación; si se apaga, el problema está en la preparación.

# Activar el modo de rebote y probar la suspensión
sysctl debug.acpi.suspend_bounce=1
acpiconf -s 3

En mi caso, el equipo se apagaba de golpe. La causa, documentada en la guía de Joshua sobre FreeBSD en MacBooks, era el módulo vmm (el hipervisor de bhyve). Al intentar suspender, el chip de virtualización de Intel se quedaba en un estado inconsistente y el kernel entraba en pánico.

La regla de oro: si usas vm_enable="YES" en /etc/rc.conf, el módulo vmm se carga automáticamente. Este módulo rompe la suspensión en hardware Apple. La solución es descargarlo antes de dormir.

Añadí los siguientes comandos a los scripts de suspensión y reanudación de FreeBSD para gestionar vmm automáticamente:

# En /etc/rc.suspend (antes del exit 0)
/sbin/kldunload -f vmm

# En /etc/rc.resume (después de los sync)
/sbin/kldload vmm

Con esto, el equipo ya entraba en suspensión sin apagarse. El primer gran obstáculo estaba superado.

Problema 2: El trackpad se queda "muerto" al despertar

Una vez resuelta la suspensión, apareció el segundo problema (y el más puñetero). Al despertar, el trackpad dejaba de responder por completo. En FreeBSD 14, los drivers para el hardware de Apple (wsp y atp) ya vienen compilados en el kernel o se cargan automáticamente. El conflicto surge porque, tras la reanudación, el driver atp se le adelanta al wsp y secuestra el dispositivo, dejando el trackpad inoperativo.

Comprobé qué driver tenía el control tras el arranque:

usbconfig -d ugen2.9 show_ifdrv

# Salida:
# ugen2.9.0: ukbd0: <Apple Inc. Apple Internal Keyboard / Trackpad>
# ugen2.9.1: atp0: <Apple Inc. Apple Internal Keyboard / Trackpad>
# ugen2.9.2: ums0: <Apple Inc. Apple Internal Keyboard / Trackpad>

Ahí estaba el conflicto: los drivers atp0 y ums0 estaban capturando el dispositivo, impidiendo que el driver correcto de Apple, wsp, pudiera inicializar el trackpad. Tras probar varios quirks en el loader.conf (sin éxito), el descubrimiento clave fue ejecutar manualmente el siguiente comando al despertar:

/usr/sbin/devctl detach atp0

¡Bingo! Al liberar atp0, el trackpad volvía a la vida inmediatamente. El problema era que atp0 secuestraba el dispositivo durante la reanudación, bloqueando la carga de wsp y dejando el hardware inoperativo.

La solución final: automatizar la liberación del driver

El comando devctl detach funcionaba a mano, pero al meterlo en /etc/rc.resume fallaba porque se ejecutaba antes de que el bus USB hubiera registrado atp0. La solución fue crear un bucle de espera que intentara desconectar el driver varias veces hasta que el dispositivo estuviera listo:

# En /etc/rc.resume (al final, antes del exit 0)
# Esperar a que atp0 aparezca y luego desconectarlo
for i in $(seq 1 20); do
    if /usr/sbin/devctl detach atp0 2>/dev/null; then
        break
    fi
    sleep 0.5
done

Este bucle espera hasta 10 segundos (20 intentos de 0,5 segundos) para que atp0 esté disponible. En cuanto lo detecta, lo desconecta, liberando el trackpad para que wsp lo recupere.

Archivos finales de configuración

Para que todo quede documentado, estos son los archivos clave tal y como quedaron en mi sistema:

/boot/loader.conf

No se necesitan módulos adicionales para los drivers del trackpad: vienen en el kernel.

kern.geom.label.disk_ident.enable="0"
kern.geom.label.gptid.enable="0"
cryptodev_load="YES"
zfs_load="YES"
asmc_load="YES"
coretemp_load="YES"
hint.hdaa.0.nid21.config="as=1 seq=15"
hint.hdaa.0.nid20.config="as=1 seq=0"
acpi_video_load="YES"
screen.font="8x14"
loader_logo="beastie"
autoboot_delay="3"

/etc/rc.suspend

# ... (código base del script)
# Descargar vmm antes de dormir
/sbin/kldunload -f vmm

/bin/rm -f /var/run/rc.suspend.pid
if [ $subsystem = "apm" ]; then
	/usr/sbin/zzz
else
	/usr/sbin/acpiconf -k 0
fi

exit 0

/etc/rc.resume

# ... (código base del script)
# Recargar vmm
/sbin/kldload vmm

# Esperar a que atp0 aparezca y luego desconectarlo
for i in $(seq 1 20); do
    if /usr/sbin/devctl detach atp0 2>/dev/null; then
        logger -t resume "Trackpad liberado (atp0 desconectado)"
        break
    fi
    sleep 0.5
done

# Ejecutar scripts de reanudación estándar
files=`rcorder ${rcorder_opts} /etc/rc.d/* ${local_rc} 2>/dev/null`
# ...
exit 0
Consejo de depuración: Añadiendo el logger -t resume dentro del bucle, dejamos constancia en /var/log/messages de que el script hizo su trabajo (o de si falló). Es muy útil para diagnosticar problemas en el futuro.
carlos@macbsd:~ $ grep "Trackpad liberado (atp0 desconectado)" /var/log/messages
Nov  6 00:05:06 macbsd resume[2530]: Trackpad liberado (atp0 desconectado)
Nov  6 00:05:26 macbsd resume[2977]: Trackpad liberado (atp0 desconectado)
Nov  6 08:24:47 macbsd resume[3407]: Trackpad liberado (atp0 desconectado)
Nov  6 08:51:22 macbsd resume[2500]: Trackpad liberado (atp0 desconectado)
Nov  6 09:49:32 macbsd resume[3018]: Trackpad liberado (atp0 desconectado)
# /etc/rc.suspend

...
/bin/sync && /bin/sync && /bin/sync
/bin/sleep 3

# Descargar vmm para evitar el cuelgue en suspend
/sbin/kldunload -f vmm

/bin/rm -f /var/run/rc.suspend.pid
if [ $subsystem = "apm" ]; then
        /usr/sbin/zzz
else
        # Notify the kernel to continue the suspend process
        /usr/sbin/acpiconf -k 0
fi

exit 0
# /etc/rc.resume

...
/bin/sync && /bin/sync && /bin/sync

# Volver a cargar vmm
/sbin/kldload vmm

# Desconectar el driver atp para liberar el trackpad
# /usr/sbin/devctl detach atp0
# Esperar a que atp0 aparezca y luego desconectarlo

for i in $(seq 1 15); do
    if /usr/sbin/devctl detach atp0 2>/dev/null; then
        logger -t resume "Trackpad liberado (atp0 desconectado)"
        break
    fi
    sleep 0.5
done
...

El resultado: una MacBook Pro renacida

Con estos cambios, la MacBook Pro de 2012 ahora:

  • Suspende correctamente al cerrar la tapa o pulsar el botón de encendido.
  • Despierta sin apagarse, gracias a la gestión de vmm.
  • Recupera el trackpad automáticamente, con gestos y clicks funcionando.
  • Mantiene la red y el resto de periféricos operativos al despertar.
Conclusión: FreeBSD en hardware Apple requiere paciencia, pero es totalmente funcional. El truco está en entender la guerra de drivers (wsp vs atp), gestionar los módulos del kernel (vmm) con scripts de suspensión, y usar devctl detach para liberar dispositivos secuestrados. Con estos ajustes, una MacBook de 2012 puede seguir siendo un portátil productivo con FreeBSD.

FreeBSD es genial!.

sábado, 31 de octubre de 2026

Mejorar Compatibilidad y Conexión MikroTik ax3

Para mejorar la compatibilidad y conexión entre tu MikroTik hAP ax3 y los clientes , el enfoque debe centrarse en ajustar la seguridad y el roaming del router, así como en corregir posibles configuracines del dispositivo cliente. Dado que el ax3 es un router Wi-Fi 6 potente, los problemas suelen derivar de portocolos de seguridad mixtos o funciones de traspaso de red (roaming) que el dispositivo gestiona de forma particular.

1. Si tienes RouterOS 7.13 o superior, usa el menú WiFi (no el antiguo "Wireless") para las interfaces AX.

2. Actualiza RouterOS

3. Configura la Seguridad Híbrida (WPA2/WPA3) mejora la compatibilidad y el roaming en dispositivos.

4. Desactiva "ft-over-ds" (Fast Transition) En la configuración roaming del router (WiFi Interface wifi1 FT), busca la opción FT Over DS y desactiva. (WiFi Interface wifi2 FT), busca la opción FT Over DS y desactiva. Usuarios de MikroTik reportan que esto soluciona problemas de conexión y desconexiones extrañas con dispositivos Samsung y Apple.

5. Prioridad de Conexión. Si tienes para evitar que el cliente se aferre a la banda 2.4GHHz, ajusta el parámetro connect-priority en el perfil de seguridad (prueba con 0/1) para mejorar la experiencia de roaming.

El punto 5, connect-priority (prioridad de conexión), es un mecanismo de RouterOS que controla si un cliente puede conectarse a un nuevo punto de acceso (AP) cuando su dirección MAC ya está asociada a otro AP activo. En esencia, decide qué AP "retiene" al dispositivo cuando hay señales superpuestas.

⚙️ Cómo funciona el parámetro connect-priority

Este parámetro se compone de dos valores separados por una barra: accept-priority/hold-priority (prioridad de aceptación / prioridad de retención). • accept-priority (Prioridad de Aceptación): Define la "predisposición" de un AP a aceptar la conexión de un cliente.

• hold-priority (Prioridad de Retención): Define la "fuerza" con la que un AP retiene a un cliente ya conectado.  La lógica de decisión de RouterOS se basa en comparar estos valores entre dos APs (por ejemplo, tu banda de 5GHz vs. 2.4GHz):

1. Si accept-priority del AP nuevo < hold-priority del AP actual: El cliente se conecta al nuevo AP y se desconecta del anterior. Esto permite un traspaso (roaming) rápido y agresivo.

2. Si accept-priority del AP nuevo = hold-priority del AP actual: El cliente solo se conectará al nuevo AP si ya no puede alcanzar el AP anterior. Esto provoca el efecto "cliente pegajoso" (sticky client), donde el dispositivo se aferra a una señal débil hasta que se pierde por completo.

3. Si accept-priority del AP nuevo > hold-priority del AP actual: El cliente no será aceptado en el nuevo AP, forzándolo a quedarse en el actual. 

Por qué se recomienda 0/1

Por defecto, si solo se configura un número (ej. 0), hold-priority es igual a accept-priority . Esto provoca el escenario 2, que suele ser la causa de que un cliente se quede "colgado" en la banda de 5GHz con mala señal en lugar de cambiar a 2.4GHz.

Al configurar connect-priority=0/1:
• Estableces el accept-priority del AP en 0. • Estableces el hold-priority del AP en 1. 
Como 0 < 1, el AP permitirá que el cliente se conecte a él (por ejemplo, al 2.4GHz) y suelte inmediatamente la conexión del AP anterior (el 5GHz débil), forzando el traspaso de banda de forma efectiva.

Advertencia de seguridad importante
MikroTik y la comunidad han señalado que usar connect-priority=0/1 (o cualquier configuración que permita el traspaso sin verificar que el AP anterior ya no alcanza al cliente) hace que la red sea vulnerable a un ataque conocido como "MacStealer" .‌

• ¿Qué implica?: Un atacante podría potencialmente secuestrar la sesión de un cliente al hacerle creer que está en un AP legítimo, aunque el riesgo real y la facilidad de explotación en redes domésticas son bajos.

• Recomendación: Dado que es una red doméstica, el beneficio de estabilidad y roaming para los clientes probablemente supera el riesgo. Sin embargo, si tienes dispositivos IoT sensibles o realizas actividades críticas, tenlo en cuenta. Si en el futuro configuras una red de invitados aislada (Client Isolation), quizás quieras desactivar esta opción solo para esa red. 
Si tras aplicar 0/1 notas que el móvil ya no se queda "pegado" a la señal débil y el roaming es fluido, has resuelto el problema de raíz.
FreeBSD es genial!.