p2p en en navegador

Señalización distribuida en redes de mesh

Publicado por Marcos el July 28, 2019 · 5 minutos de lectura

Tener un protocolo de comunicación de persona a persona, en un navegador y sin dependencia de los servidores es lo que WebRTC se supone que ofrece. Chat, videollamadas, juegos en línea, transferencia de archivos y miles de otros milagros. Pero la verdad es que cuando se quitan los servidores centralizados para que WebRTC funcione, todo se desmorona.

Cómo funciona

Para establecer una comunicación con WebRTC sólo necesita intercambiar un par de datos. Los dos extremos (sí, sólo dos, no es multidireccional) deben tener una serie de datos que son generados por un ida y vuelta. En primer lugar, el usuario “Azul” debe generar una “oferta” que se traduce en algo así:

Azul.createOffer()

v=0
o=- 8078612798140261151 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0
a=msid-semantic: WMS
m=application 9 DTLS/SCTP 5000
c=IN IP4 0.0.0.0
a=ice-ufrag:dBQl
a=ice-pwd:v3k5Ry/ukr8m6w7pHySpdZPr
a=ice-options:trickle
a=fingerprint:sha-256 C4:76:BB:8F:E1:CF:43:4A:1A:BE:E1:AE:F7:AA:F5:4C:1B:80:98:5F:18:42:11:50:F5:78:FA:3E:37:0D:23:C3
a=setup:actpass
a=mid:0
a=sctpmap:5000 webrtc-datachannel 1024

Esto, junto con otra información, debe llegar al usuario “Verde”. Esta entrega es parte de lo que no se resuelve de forma descentralizada, también se conoce como descubrimiento y actualmente cuenta con varias soluciones, todas ellas dependientes de algún servicio centralizado. Fuera del navegador web hay otras formas de solucionarlo, pero dentro del navegador se utilizan servidores, códigos QR o copiar y pegar todo eso.

Supongamos que Azul ha conseguido enviar estos datos a Verde, este los añade como una configuración remota, en ese momento debe crear una respuesta, ¡Que ahora tiene que llegar a Azul de alguna manera!

Propietarios de nuestras redes

Como sucede que estoy en una red comunitaria, que usamos LibreMesh y que soy uno de los desarrolladores :) se me ocurrió agregar un servicio de señalización descentralizado a los routers, y así poder hacer aplicaciones p2p completas que funcionen en navegadores sin depender de servidores centralizados. Este es el tipo de cosas que desbloqueamos cuando empezamos a hackear nuestra infraestructura.

shared-state al rescate

Recientemente hemos incorporado shared-state a LibreMesh, es un “tipo de datos replicados sin conflictos (crdt)” que sincroniza la tabla de datos entre nodos vecinos de la red (hay posts anteriores donde lo explico mejor). La sincronización puede ser activada por algún proceso interno del router (un cliente con una nueva IP por ejemplo) o periódicamente. Del mismo modo, la existencia de nueva información desencadena ganchos y existen scripts que reaccionan realizando diferentes tareas (recreando el db de voucher del portal cautivo, actualizando el servidor DNS, etc). Todo esto a través de simples peticiones POST en HTTP, y un descubrimiento por topología, es decir, los nodos conectados directamente entre sí. Otra peculiaridad es que los datos tienen un tiempo de vida, pero en lugar de estar en segundos o minutos está la cantidad de “compartido” desde la última vez que se publicaron, por lo que si el autor de los datos no los vuelve a publicar, terminan desapareciendo. Esto se debe a la limitada capacidad de los routers para manejar y almacenar grandes cantidades de información.

El código que se ejecuta en los routers está escrito en Lua, pero recientemente he creado un módulo JavaScript que se ejecuta en el navegador y puede dialogar de igual a igual con el estado compartido que ejecuta los routers.

La idea, buscar mejores opciones

Mi primera idea es esta:

  • Generar X cantidad de conexiones ofrecidas y publicarlas en estado compartido
  • Buscar una conexión ofrecida y generar una respuesta
  • Supervisar las conexiones ofrecidas mientras se espera una conexión
  • Por cada conexión contestada con éxito ofrezco una nueva.
  • Repito la dinámica hasta que se consiga un número X de conexiones estables.

Con este primer esquema de conexión p2p lo que hago es empezar a ejecutar shared-state vía WebRTC y mantener los nodos y los navegadores sincronizados al mismo tiempo. Esto permitirá una red de sincronización más rápida, casi instantánea y que no depende de la topología de la mesh (cantidad de saltos por ejemplo) sino de los dispositivos conectados. En este otro shared-state puedo repetir la misma acción de oferta y respuesta pero más direccional, eligiendo específicamente con cuál usuarie que quiero hacer una conexión, intercambiar claves pgp, etc. En otras palabras, todas las acciones que serían caras de realizar en el router.

Trabajo en proceso

Como este es un trabajo en progreso, intentaré publicar los avances y códigos, así como la explicación de cómo funciona cada parte. Por ahora sólo puedo compartir estos dos repositorios: