Дело в том, что отключение интернета было обнаружено не сразу. В этот день активно использовал переписку по почте и со своим знакомым. Почта работала отлично, как и шустро бегали сообщения через XMPP на серверах jabber.ru Несмотря на это, основные прелести интернета были недоступны, и, при всех доступных условиях, мысль родилась быстро.
Как вообще работает VPN?
Для понимания принципов работы VPN тоннелей конечно пришлось заплатить за интернет, что бы воспользоваться «тем самым поисковиком». Оказалось, что в основном, туннели строятся с использованием «виртуальных сетевых драйверов» — TUN и TAP. TAP эмулирует Ethernet устройство и работает на канальном уровне модели OSI, оперируя кадрами Ethernet. TUN (сетевой туннель) работает на сетевом уровне модели OSI, оперируя IP пакетами. TAP используется для создания сетевого моста, тогда как TUN для маршрутизации. Для лучшего понимания насколько это круто напомню акие существуют уровни в модели OSI:
Получается, что эмулируя сетевой уровень, мы можем предоставить работоспособность всем уровням выше, а именно: Транспортному, сеансовому, уровню представления, и прикладному уровню. Последние 2 уровня, это и есть те самые, нужные нам протоколы: HTTP, FTP, SSH, SMB, Skype, BitTorrent и сотни других! Мало того, мы обеспечим работой и протоколы других уровней: SSL, TLS; PPTP, L2TP; TCP, UDP и другие. Т.е. наша виртуальная сеть будет практически полноценной сетью, а получать данные и отправлять данные в интерфейс мы можем прямиком из клиентского приложения!
Не труЪ
Поскольку этот мини-проект не претендует на широкое применение и распространение, я взял удобный для себя инструментарий: NodeJS, node-tuntap, node-xmpp. В нормальном случае в Linux работа с TUN и TAP интерфейсом выполняется через файл устройства /dev/net/tun и /dev/net/tap.
Заранее о проблемах
Компилируемая часть node-tuntap нестабильна, и часто падает в Segfault. Будет неплохо, если кто-то пробежится дебаггером и глазами по модулю, и поймет в чём дело. Github модуля: github.com/binarysec/node-tuntap
Поехали!
Для сети я решил использовать tun интерфейс. С ним проще работать, не нужно следить за последовательностью передачи пакетов, и кому мы их отправляем. Также на этом интерфейсе можно заранее задать IP адрес, адрес шлюза и маску подсети.
Инициализация и подключение интерфейса выполняется так:
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);
}
После запуска этого кода (конечно, от суперпользователя), получаем новый сетевой интерфейс в системе (на скриншоте он называется tun2):
Ничегосебе! Несколько строк кода, и уже целое устройство!
Удобство модуля node-tuntap также в том, что с сетевым интерфейсом можно работать как с экземпляром объекта Stream, следовательно записать данные в интерфейс можно простым tt.write(), а получать данные из потока по событию tt.on('data').
XMPP
Для тестирования сети пришлось зарегестрировать парочку дополнительных jabber аккаунтов: ethernet@jabber.ru и ethernet@xmpp.ru. Обмен пакетами будет происходить через сообщения по протоколу XMPP. Поскольку сообщения текстовые, а данные, которые мы получаем из интерфейса — бинарные (мало того представлены в виде Buffer в NodeJS), данные будут кодироватся в Base64, и по приходу раскодироватся обратно в Buffer.
В NodeJS до 6ой версии это можно было сделать таким путём:
new Buffer(data).toString('base64') //это данные, готовые для отправки
new Buffer(message, 'base64') //здесь мы имеем данные пакета после раскодировки
Последний этап: Получать данные из сетевого интерфейса и отправлять их контакту из списка, который я условно назвал gatewayContact.
Подключение к серверу jabber с помощью 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);
});
});
Осталось соединить воедино оба блока кода, и мы получим:
/**
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!
Тестирование
- Одна из виртуалок имеет адрес 192.168.123.3, второе 192.168.123.1
- Между виртуалками находится обычная сеть за роутером, и обычный интернет. Считаем, что на результаты тестирования это не влияет.
- Скриншоты делались в несколько дублей
Для начала тестируем ping:
Смотрите, работает!
Как насчёт чего-то более приближенного к реальности? Попробуем воспользоватся протоколом HTTP.
Ставим и запускаем Lighttpd:
Тестируем:
Хо-хо!
Усложним ещё:
Загрузилась!
Немного о скорости
Средняя скорость загрузки логотипа хабра была 957 байт/сек. В интернет с такой скоростью выходить мягко говоря не комфортно, однако, я считаю, что цель достигнута.
Windows
Как вы могли заметить, вся разработка и тестирование выполнялась в Linux Ubuntu. Выбор обусловлен несколькими факторами:
- Драйвера TUN/TAP встроены в ядро
- В Linux проще работать с TUN/TAP драйверами, и уже был готовый модуль для NodeJS
- Проще настроить маршрутизацию, что-бы через наш VPN работал интернет
Несмотря на это решить проблему для Windows не очень сложно. Существует несколько реализаций TUN/TAP драйверов, самая популярная написана для проекта OpenVPN, и имеет доступную и понятную документацию. Поддержку драйвера от OpenVPN было бы неплохо внести в тот-же модуль node-tuntap.
Заключение
Конечно эта реализация VPN через XMPP это достаточно медленная. Ради теста я написал реализацию, работающую с помощью SocketIO через хост машину, в этом случае скорости были нормальные. Несмотря на это напоминаю про ответственность за действия, которые вы можете совершить не подумав, и, что весь материал статьи представлен исключительно в ознакомительных целях.
UPD
Добавил проект в npm и на github
https://www.npmjs.com/package/pppoverxmpp
https://github.com/lailune/PPPoverXMPP
Комментарии (32)
ZoomLS
27.01.2017 02:21>>напоминаю про ответственность за действия, которые вы можете совершить не подумав
И чего нарушает пользователь в данном случае? Ничего же.jhonyxakep
27.01.2017 09:15+1В конце концов метод позволяет обойти ограничения связи, которые накладываются провайдером за неуплату.
15432
27.01.2017 09:49+1Есть же iodine и icmptunnel. Очень странно, что джаббер продолжал работать. Обычно блочат весь UDP/TCP трафик.
Zagrebelion
27.01.2017 09:53Несколько раз видел рассказы про то, как интернет отключен за неулпату, а телеграм работает.
worldxaker
27.01.2017 13:18та же yota двигает это как фишку, правда там месседжеры только за границой работают без интернета
Erelecano
29.01.2017 02:23> месседжеры только за границой работают без интернета
Через libastral.so конектятся что ли???
jhonyxakep
27.01.2017 09:56Про существование iodine и icmptunnel знал, но провайдер подменяет большинство dns запросов на свои для показа странички «заплатите за интернет». Я думаю работоспособность jabber и сервисов Яндекса при отсутствии оплаты используется как реклама. Также доступно большинство онлайн банков.
Вообще я конечно преследовал цель больше в организации тоннеля через «любую» среду, XMPP просто как конкретный пример.15432
27.01.2017 15:18Точно ли DNS подменяется? Обычно перехватывается TCP коннект и выдается 302 редирект. Подмену DNS не встречал.
jhonyxakep
27.01.2017 15:37Я особо не изучал эту подмену, визуально похоже на работу блокировки типа «роскомнадзор», может ошибаюсь.
NeonXP
27.01.2017 11:26+4Вот это круто для пятницы!
Ещё нужно сделать через Telegram, тогда можно будет через Йоту заграницей бесплатно в роуминге сидеть.jhonyxakep
27.01.2017 11:47Честно говоря я изначально пробовал передавать данные через телеграмм (на моем провайдере тоже не блокируется), но модуль telegramm-cli часто отпадал из-за какой-то ошибки, а разбираться было откровенно лень.
Йота действительно позволяет использовать телеграмм в роуминге без тарификации?NeonXP
27.01.2017 11:58Так заявлено. Но как я понимаю, они делают DIP дабы не слали большие картинки и файлы. Но не думаю что VPN трафику это повредит.
worldxaker
27.01.2017 14:32будет круто реализовать это через бота. для ботов на js дофига либ
jhonyxakep
27.01.2017 14:48Бот вроде не может общатся с ботом? Разве что как-нибудь через общую группу. Надо попробовать
worldxaker
27.01.2017 15:09так это и не надо. грубо говоря бот играет роль сервера. клиентские устройства логинятся со своего личного аккаунта и потом общаются с ботом. при этом в самом месседжере кидает бота в мут и получаем и рабочий VPN и рабочий месседжер
jhonyxakep
27.01.2017 15:39Понял. Я в общем то пытался сделать коннект клиент-клиент, но, как написал выше, единственный адекватный способ работать с MTProto на NodeJs — telegramm-cli далёк от стабильности.
TimsTims
27.01.2017 18:33+1Вот только йоте потом тоже счет выставляют нехилый. И после нескольких таких залетов они просто отключат эту бесплатную фишку.
Поэтому, лучше не ссать в колодец...
smple
27.01.2017 13:18+1давным давно когда трафик у провайдеров был еще помегабайтным (и был дешевый или бесплатный локальный трафик) у нас в локальной сети появился xmpp сервер от провайдером, со связью наружу.
Не долго думая был использован один из удаленных серверов где нормальный безлимитный интернет и туда был установлен xmpp.
И был поднят тонель между моим компом -> локальным xmpp сервером -> внешний xmpp сервер и клиент, которые проксировали мой трафик :D
Проблема была одна, высокий пинг, в остальном все работало)
SunX
27.01.2017 13:55Классно, но все же возражу, что это скорее не VPN, а PPP.
Кстати вспомнил, что во времена небезлимитного Интернета что-то подобное кто-то делал на C++: https://code.google.com/archive/p/pppoj/jhonyxakep
27.01.2017 14:17Вы правы, в данном случае больше подходит определение PPP. Несмотря на это создавая tun интерфейс вместо tap можно делать полноценный VPN с несколькими устройствами в виртуальном хабе.
Сейчас ещё погуглил, тема PPP через XMPP активно обсуждалась на ЛОРе. Несмотря на это протокол XMPP я в основном взял как пример, наиболее понятный. Организовать туннель в данном случае можно почти через любую среду. Например: у меня в планах попробовать прокинуть сеть через радио канал и IR, just for fun.SunX
27.01.2017 15:20Ну и с tun можно организовать VPN, если серверную часть научить этому.
А вообще идея PPP over любой поток — это интересно =)jhonyxakep
27.01.2017 15:23+1Я тогда в ближайшее время оформлю проект как npm модуль, с возможностью подключения плагинов, чтобы все смогли пользоваться :)
Stanislavvv
27.01.2017 17:26+1Радиолюбители давно уже пользуются AX.25, рекомендую обратить внимание.
Если получится — просьба статью :-)
degster
27.01.2017 17:10Следующий шаг — Интернет через СМС
jhonyxakep
27.01.2017 17:11Почему бы и нет? Бывают случаи, когда ничего кроме SMS недоступно, а доступ в интернет, хоть медленный, но нужен
ValdikSS
jhonyxakep
возможно. Пробую позже протестировать на локальном jabber севере