Friday format: VPN through ... Jabber?
- Tutorial
For some people, interesting stories begin with liquids containing alcohol. Some people have something stronger ... I, as a true representative of the IT world, the story began ... With the disconnection of the Internet. Of course, you could go the simple way to solve the problem, and just pay, but this is not the true way of the samurai? Many great screenshots.
The fact is that the Internet disconnection was not immediately detected. On this day, I actively used correspondence by mail and with my friend. The mail worked fine, as well as running messages quickly through XMPP on jabber.ru servers. Despite this, the basic delights of the Internet were unavailable, and, under all available conditions, the idea was born quickly.
To understand the principles of VPN tunnels, of course I had to pay for the Internet in order to use the “same search engine”. It turned out that basically tunnels are built using “virtual network drivers” - TUN and TAP . TAP emulates an Ethernet device and operates on the link layer of the OSI model, operating with Ethernet frames. TUN (network tunnel) operates at the network level of the OSI model, operating with IP packets. TAP is used to create a network bridge, while TUN is for routing. To better understand how cool it is, let me remind you what levels exist in the OSI model:
It turns out that by emulating the network level, we can provide performance to all levels above, namely: Transport, session, presentation level, and application level. The last 2 levels, these are the very protocols we need: HTTP, FTP, SSH, SMB, Skype, BitTorrent and hundreds of others! Not only that, 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 get data and send data to the interface directly from the client application!
Since this mini-project does not pretend to be widely used and distributed, I took a convenient toolkit 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.
The compiled part of node-tuntap is unstable, and often crashes in Segfault. It would be nice if someone runs a debugger and eyes modulo, and understands what's the matter. Github module: github.com/binarysec/node-tuntap
For the network, I decided to use the tun interface. It is easier to work with it, you do not need to monitor 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:
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):

Nothing at all! A few lines of code, and the whole device!
The convenience of the node-tuntap module is also that you can work with the network interface as an instance of the Stream object, therefore, you can write data to the interface with simple tt.write (), and receive data from the stream by the tt.on ('data') event.
To test the network, I had to register a couple of additional jabber accounts: ethernet@jabber.ru and ethernet@xmpp.ru. Packet exchange will take place via XMPP messages. Since the messages are text, and the data that we get from the interface is binary (moreover, they are presented as Buffer in NodeJS), the data will be encoded in Base64, and decoded back to Buffer upon arrival.
In NodeJS prior to version 6, this could be done this way:
Last step: Receive data from the network interface and send it to a contact from the list, which I conditionally called gatewayContact.
Connect to jabber server using xmpp-client:
It remains to combine both blocks of code together, and we get:
PROFIT!
I terribly apologize for naming some variables, I hope you find the strength to refactor several lines.
To begin with, we test ping:

Look, it works!
How about something closer to reality? Let's try using the HTTP protocol.
We put and run Lighttpd:

Test:

Ho-ho!
Let's complicate it: Booted

!
The average download speed of the Habr’s logo was 957 bytes / sec. Going to the Internet with such speed is, to put it mildly, not comfortable, however, I believe that the goal has been achieved.

As you may have noticed, all development and testing was done on Linux Ubuntu. The choice is due to several factors:
Despite this, solving the problem for Windows is not very difficult. There are several implementations of TUN / TAP drivers, the most popular is written for the OpenVPN project, and has accessible and understandable documentation. OpenVPN driver support would be nice to add to the same node-tuntap module.
Of course, this VPN implementation through XMPP is rather slow. For the sake of the test, I wrote an implementation that works with SocketIO through the host machine, in this case the speeds were normal. Despite this, I remind you of the responsibility for actions that you can take without thinking, and that all the material in the article is presented for informational purposes only.
UPD
Added the project in npm and on github
https://www.npmjs.com/package/pppoverxmpp
https://github.com/lailune/PPPoverXMPP
The fact is that the Internet disconnection was not immediately detected. On this day, I actively used correspondence by mail and with my friend. The mail worked fine, as well as running messages quickly through XMPP on jabber.ru servers. Despite this, the basic delights of the Internet were unavailable, and, under all available conditions, the idea was born quickly.
How does a VPN work?
To understand the principles of VPN tunnels, of course I had to pay for the Internet in order to use the “same search engine”. It turned out that basically tunnels are built using “virtual network drivers” - TUN and TAP . TAP emulates an Ethernet device and operates on the link layer of the OSI model, operating with Ethernet frames. TUN (network tunnel) operates at the network level of the OSI model, operating with IP packets. TAP is used to create a network bridge, while TUN is for routing. To better understand how cool it is, let me remind you what levels exist in the OSI model:
OSI device model

It turns out that by emulating the network level, we can provide performance to all levels above, namely: Transport, session, presentation level, and application level. The last 2 levels, these are the very protocols we need: HTTP, FTP, SSH, SMB, Skype, BitTorrent and hundreds of others! Not only that, 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 get data and send data to the interface directly from the client application!
Do not tru
Since this mini-project does not pretend to be widely used and distributed, I took a convenient toolkit 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 problems
The compiled part of node-tuntap is unstable, and often crashes in Segfault. It would be nice if someone runs a debugger and eyes modulo, and understands what's the matter. Github module: github.com/binarysec/node-tuntap
Go!
For the network, I decided to use the tun interface. It is easier to work with it, you do not need to monitor 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):
Nothing at all! A few lines of code, and the whole device!
The convenience of the node-tuntap module is also that you can work with the network interface as an instance of the Stream object, therefore, you can write data to the interface with simple tt.write (), and receive data from the stream by 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 take place via XMPP messages. Since the messages are text, and the data that we get from the interface is binary (moreover, they are presented as Buffer in NodeJS), the data will be encoded in Base64, and decoded back to Buffer upon arrival.
In NodeJS prior to version 6, this could be done this way:
new Buffer(data).toString('base64') //это данные, готовые для отправки
new Buffer(message, 'base64') //здесь мы имеем данные пакета после раскодировки
Last step: Receive data from the network interface and send it to a contact from the list, which I conditionally called gatewayContact.
Connect to 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!
Testing
- One of the virtual machines has the address 192.168.123.3, the second 192.168.123.1
- Between virtual machines there is a regular network behind a router, and a regular Internet. We believe that this does not affect the test results.
- Screenshots were made in several takes
To begin with, we test ping:

Look, it works!
How about something closer to reality? Let's try using the HTTP protocol.
We put and run Lighttpd:

Test:

Ho-ho!
Let's complicate it: Booted

!
Little bit about speed
The average download speed of the Habr’s logo was 957 bytes / sec. Going to the Internet with such speed is, to put it mildly, not comfortable, however, I believe that the goal has been achieved.

Windows
As you may have noticed, all development and testing was done on Linux Ubuntu. The choice is due to several factors:
- TUN / TAP drivers built into the kernel
- On Linux, it’s easier to work with TUN / TAP drivers, and there was already a ready-made module for NodeJS
- It’s easier to configure routing so that the Internet works 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 is written for the OpenVPN project, and has accessible and understandable documentation. OpenVPN driver support would be nice to add to the same node-tuntap module.
Conclusion
Of course, this VPN implementation through XMPP is rather slow. For the sake of the test, I wrote an implementation that works with SocketIO through the host machine, in this case the speeds were normal. Despite this, I remind you of the responsibility for actions that you can take without thinking, and that all the material in the article is presented for informational purposes only.
UPD
Added the project in npm and on github
https://www.npmjs.com/package/pppoverxmpp
https://github.com/lailune/PPPoverXMPP