Res de VpnService no intercepta trànsit per si sol. Demana al sistema una interfície tun, li lliura un conjunt d'adreces i rutes i, des d'aquell moment, el nucli copia cada paquet que coincideix amb aquestes rutes en un descriptor de fitxer que l'aplicació pot llegir. El codi de l'aplicació no veu mai els paquets que les rutes no han seleccionat, així que l'abast d'un túnel és una qüestió de taula d'encaminament abans que una qüestió de programació. Per això la mateixa implementació del servei pot engolir cada flux en un telèfon i només una part en un altre, i per això afegir una ruta de vegades repara un túnel que semblava trencat. El que obteniu és el que heu declarat, menys tot el que el constructor ha refusat en silenci.

Les rutes decideixen quins paquets arriben

Builder.addRoute(prefix, prefixLength) instal·la un prefix a la taula d'encaminament de la xarxa VPN. El sistema tracta aquesta xarxa com a predeterminada per a les aplicacions de l'abast, així que les seves entrades es consulten abans que la ruta predeterminada de la xarxa física i un prefix coincident n'hi ha prou per desviar un flux. Per tant, una ruta per a 0.0.0.0/0 pren IPv4 sencer, incloent-hi la impressora, l'objectiu de cast i la pàgina d'administració del router.

Els clients que volen aquesta cobertura sense esborrar l'entrada predeterminada física fan servir en canvi la parella 0.0.0.0/1 i 128.0.0.0/1. Dues rutes més específiques guanyen una ruta predeterminada, juntes cobreixen tot l'espai d'adreces i l'entrada predeterminada del telèfon es queda a la taula per als sockets que encara estan lligats a la xarxa física.

Les adreces i les entrades DNS vénen del mateix constructor i responen una pregunta diferent. addAddress dona a la interfície una adreça amb una longitud de prefix, i addDnsServer instal·la un resolutor que les aplicacions de l'abast consultaran a través de la interfície. Les dues famílies poden seure en una interfície, així que un conjunt de rutes només IPv4 al costat d'una adreça IPv6 és una configuració que es llegeix correctament i tot i així filtra. Si encamineu una família en què la interfície no té adreça, el nucli no té adreça d'origen per a aquests paquets i mai apareixen al descriptor.

Què retorna establish()

establish() retorna un ParcelFileDescriptor, o null quan l'aplicació no està autoritzada. Null és un estat de consentiment, no un error de xarxa: VpnService.prepare() ja ha d'haver retornat null, o l'usuari ha d'haver aprovat el diàleg del sistema abans de la crida. El consentiment es pot revocar mentre el servei s'executa, i la interfície es tanca sota els vostres peus sense cap retorn de crida al vostre bucle de lectura.

El descriptor no és bloquejant tret que digueu el contrari, així que una lectura torna immediatament sense dades en comptes d'aparcar el fil. Un bucle escrit per a un socket bloquejant crema un nucli, i un bucle que tracta una lectura buida com a fatal esquinça el túnel durant un segon inactiu. Builder.setBlocking(true) canvia aquest comportament, i el preu és un fil que espera dins de la lectura.

Els arguments de ruta i adreça es validen com a valors LinkAddress, i les destinacions de multicast com 224.0.0.0/4 es rebutgen de ple. Una interfície tun punt a punt tampoc no té domini de broadcast, així que el descobriment de multicast a través seu necessita un disseny diferent en lloc d'una ruta més. La referència de VpnService.Builder és la llista que cal consultar abans de concloure que una crida s'ha ignorat.

L'abast per aplicació estreny el conjunt, no el túnel

A partir de l'API 21 el constructor accepta addAllowedApplication i addDisallowedApplication, i les dues llistes són mútuament excloents dins d'un mateix constructor. Una llista d'autoritzats fa que el túnel existeixi per a aquests paquets mentre tota la resta conserva la xarxa física; una llista de denegats captura tot excepte els paquets anomenats, que és el que un client ofereix com a túnel dividit. Abans de l'API 21 no hi ha cap mecanisme de plataforma, i el comportament per aplicació venia de clients de tercers que filtraven per UID dins del seu propi reenviador. La distinció importa quan depureu, perquè un túnel per aplicació amb una llista d'un sol paquet gairebé no produeix trànsit i això és un comportament correcte, no una ruta trencada.

Sigui quin sigui l'abast, el socket que la vostra aplicació obre cap al servidor VPN no ha de viatjar pel túnel que està construint. VpnService.protect(socket) lliga aquest socket a la xarxa subjacent perquè els seus paquets saltin el dispositiu tun. Salteu la crida i l'encaixada de mans se l'empassa la interfície que l'encaixada hauria d'aixecar.

VpnService.setUnderlyingNetworks tanca el cercle dient al sistema quina xarxa física porta el túnel. Aquest valor determina com la VPN informa de la connectivitat a les altres aplicacions, i un valor null o obsolet és una causa habitual d'un túnel connectat que la resta del telèfon tracta com a fora de línia. Actualitzeu-lo quan canvia la xarxa subjacent, no només en arrencar.

Sumes de verificació i MTU: on una ruta correcta encara falla

Un cop els paquets arriben, dos detalls a nivell de paquet decideixen si alguna cosa funciona d'extrem a extrem. El primer és l'adreça d'origen: amb diverses adreces d'interfície el nucli pot emetre paquets que porten la primera, així que un reenviador que reescriu capçaleres ha de corregir l'adreça i la suma de verificació que la cobreix.

El segon és l'abast de la suma de verificació. Quan l'aplicació construeix ella mateixa paquets TCP o UDP, la suma cobreix una pseudo-capçalera amb l'adreça d'origen, l'adreça de destinació, el número de protocol i la longitud de transport, i ometre'n qualsevol dona paquets que arriben i es descarten. Aquesta fallada sembla un temps d'espera després d'una resposta que no arriba mai, cosa que torna la majoria de la gent a les rutes. setMtu fixa la MTU de la interfície, i cada lectura retorna exactament un paquet IP complet, així que un búfer escrit per a un flux de bytes fragmentarà el vostre analitzador, no el paquet.

Una passada de verificació que separa les capes

Per trobar on l'abast s'ha equivocat, proveu les capes per ordre. Pregunteu al dispositiu quines rutes té realment la xarxa VPN, després comproveu si un flux cap a una adreça coneguda apareix com una lectura al descriptor, i després comproveu si el paquet que emet el vostre reenviador està ben format. Un problema de ruta no mostra res al descriptor, mentre que un problema de suma de verificació mostra un paquet que arriba i desapareix.

Si res no arriba al descriptor, el sospitós és el conjunt de rutes i no el lector: confirmeu els prefixos, confirmeu que hi ha una adreça a la mateixa família i confirmeu que l'aplicació és dins de l'abast que heu establert. Si els paquets arriben al descriptor però no torna res, inspeccioneu protect() i el valor de la xarxa subjacent abans de tocar una ruta una altra vegada. Si el costat llunyà informa de paquets mal formats, la suma de verificació i la MTU són on cal mirar. Una sonda per capa estalvia un migdia d'endevinalles.

Articles relacionats