⬆️ ⬇️

Friday format: VPN via ... Jabber?

For some people, interesting stories begin with the intake of liquids containing alcohol. Some have something stronger ... I, as a true representative of the IT world, have started a story ... With the Internet turning off. Of course, you could go a simple way to solve a problem, and just pay, but this is not the true way of a samurai? Many great screenshots



The fact is that the shutdown of the Internet was not immediately detected. On this day, actively used the correspondence by mail and with his friends. Mail worked fine, as well as smartly ran messages via XMPP on jabber.ru servers. Despite this, the basic charms of the Internet were not available, and, under all available conditions, the thought was born quickly.



How does VPN work in general?



To understand the principles of the work of VPN tunnels, of course, we had to pay for the Internet in order to take advantage of "thus the search engine." It turned out that basically, the tunnels are built using “virtual network drivers” - TUN and TAP . TAP emulates an Ethernet device and operates on the data link layer of the OSI model, using Ethernet frames. TUN (network tunnel) operates at the network layer of the OSI model, using IP packets. TAP is used to create a network bridge, while TUN is used for routing. For a better understanding of how cool it is to remind you, there are levels in the OSI model:



OSI device
image



It turns out that by emulating the network layer, we can provide performance to all levels above, namely: Transport, session, presentation level, and application layer. The last 2 levels, these are the very protocols we need: HTTP, FTP, SSH, SMB, Skype, BitTorrent and hundreds of others! Moreover, we will provide work and protocols of other levels: SSL, TLS; PPTP, L2TP; TCP, UDP and others. Those. Our virtual network will be almost a full-fledged network, and we can receive data and send data to the interface straight from the client application!

')

Not tru



Since this mini-project does not pretend to be widely used and distributed, I took the most convenient tool for myself: NodeJS, node-tuntap, node-xmpp. In the normal case, on Linux, work with the TUN and TAP interface is performed through the device file / dev / net / tun and / dev / net / tap.



In advance about the problems



The node-tuntap compilation part is unstable, and often crashes in Segfault. It would be nice if someone ran through the debugger and eyes on the module, and understood what was going on. Github module: github.com/binarysec/node-tuntap



Go!



For the network, I decided to use the tun interface. It is easier to work with him, no need to follow the sequence of packet transmission, and to whom we send them. Also on this interface, you can pre-set the IP address, gateway address and subnet mask.



Initialization and connection of the interface is performed as follows:



var tuntap = require('node-tuntap'); try { var tt = tuntap({ type: 'tun', name: 'tun2', mtu: 1500, addr: '192.168.123.1', dest: '192.168.123.2', mask: '255.255.255.192', ethtype_comp: 'none', persist: false, up: true, running: true, }); } catch(e) { console.log('Tuntap creation error: ', e); process.exit(0); } 


After running this code (of course, from the superuser), we get a new network interface in the system (in the screenshot it is called tun2):







Wow! A few lines of code, and already the whole device!



The convenience of the node-tuntap module is also that the network interface can be operated as an instance of a Stream object, hence data can be written to the interface with a simple tt.write (), and data from the stream can be retrieved from the tt.on ('data') event.



XMPP



To test the network, I had to register a couple of additional jabber accounts: ethernet@jabber.ru and ethernet@xmpp.ru. Packet exchange will occur via XMPP messages. Since the messages are text, and the data that we receive from the interface is binary (moreover, represented as a Buffer in NodeJS), the data will be encoded in Base64, and on arrival it will be decoded back into Buffer.



In NodeJS up to version 6, this could be done in the following way:



 new Buffer(data).toString('base64') // ,    new Buffer(message, 'base64') //       


Last step: Receive data from the network interface and send it to the contact from the list, which I conditionally called gatewayContact.



Connecting to a jabber server using xmpp-client:



 var Client = require('xmpp-client').Client; var c = new Client({ jid: login, // ,    ethernet@xmpp.ru/jabber.ru password: password //  }, function() { console.log("I'm connected"); //  this.addListener('message', function(from, message){ console.log('Message from ' + from + ': '+message); }); }); 


It remains to combine both blocks of code together, and we get:



 /** Ethernet over XMPP */ var login = 'ethernet@jabber.ru'; //  var password = ' '; //,     var gatewayContact = 'ethernet@xmpp.ru'; //  ,    -  var idAdress = '192.168.123.3'; // IP  (,       ) var interfaceId = 'tun2'; //   //****************************************************** var tuntap = require('node-tuntap'); try { var tt = tuntap({ type: 'tun', name: interfaceId, mtu: 1500, addr: idAdress, dest: '192.168.123.2', //   ,   mask: '255.255.255.192', ethtype_comp: 'none', persist: false, up: true, running: true, }); } catch(e) { console.log('Tuntap creation error: ', e); process.exit(0); } var Client = require('xmpp-client').Client; var c = new Client({ jid: login, password: password }, function() { console.log("I'm connected"); tt.on('data', function(data){ console.log('>>> Send packet'); //  c.message(gatewayContact, new Buffer(data).toString('base64')); //,   }); this.addListener('message', function(from, message){ if(from.indexOf(gatewayContact) !== -1){ //      console.log('<<< Recived packet'); tt.write(new Buffer(message, 'base64')); // ,     } }); }); 


PROFIT!



I am terribly sorry for naming some variables, I hope you will find the strength to refactor a few lines.



Testing





First, we test ping:







See, it works!



How about something more close to reality? Let's try to use the HTTP protocol.



Install and run Lighttpd:







Testing:







Ho ho!



Let's complicate it:







Booted!



A bit about speed



The average download speed for the Habr logo was 957 bytes / sec. To put it mildly, the Internet is not comfortable at such a speed, however, I believe that the goal has been achieved.







Windows



As you can see, all development and testing was done in Linux Ubuntu. The choice is due to several factors:



  1. TUN / TAP drivers are built into the kernel.
  2. Linux is easier to work with TUN / TAP drivers, and there was already a ready module for NodeJS
  3. It's easier to set up routing, so that the Internet would work through our VPN


Despite this, solving the problem for Windows is not very difficult. There are several implementations of TUN / TAP drivers, the most popular one written for the OpenVPN project, and has accessible and understandable documentation. It would be nice to add support for the driver from OpenVPN to the same node-tuntap module.



Conclusion



Of course, this VPN implementation through XMPP is quite slow. For the sake of the test, I wrote an implementation working with SocketIO through the host machine, in this case the speeds were normal. Despite this, I remind you of responsibility for actions that you can take without thinking, and that all the material of the article is presented solely for informational purposes.



UPD

Added project in npm and on github

https://www.npmjs.com/package/pppoverxmpp

https://github.com/lailune/PPPoverXMPP

Source: https://habr.com/ru/post/320570/



All Articles