У некоторых людей интересные истории начинаются с приёма жидкостей с содержанием алкоголя. У некоторых с чего-то покрепче… У меня, как у истинного представителя мира IT, история началась… С отключения интернета. Конечно можно было пойти простым путём для решения проблемы, и просто заплатить, но ведь это не истинный путь самурая? Много больших скриншотов

Дело в том, что отключение интернета было обнаружено не сразу. В этот день активно использовал переписку по почте и со своим знакомым. Почта работала отлично, как и шустро бегали сообщения через XMPP на серверах jabber.ru Несмотря на это, основные прелести интернета были недоступны, и, при всех доступных условиях, мысль родилась быстро.

Как вообще работает VPN?


Для понимания принципов работы VPN тоннелей конечно пришлось заплатить за интернет, что бы воспользоваться «тем самым поисковиком». Оказалось, что в основном, туннели строятся с использованием «виртуальных сетевых драйверов» — TUN и TAP. TAP эмулирует Ethernet устройство и работает на канальном уровне модели OSI, оперируя кадрами Ethernet. TUN (сетевой туннель) работает на сетевом уровне модели OSI, оперируя IP пакетами. TAP используется для создания сетевого моста, тогда как TUN для маршрутизации. Для лучшего понимания насколько это круто напомню акие существуют уровни в модели OSI:

Устройство модели OSI
image

Получается, что эмулируя сетевой уровень, мы можем предоставить работоспособность всем уровням выше, а именно: Транспортному, сеансовому, уровню представления, и прикладному уровню. Последние 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. Выбор обусловлен несколькими факторами:

  1. Драйвера TUN/TAP встроены в ядро
  2. В Linux проще работать с TUN/TAP драйверами, и уже был готовый модуль для NodeJS
  3. Проще настроить маршрутизацию, что-бы через наш 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)


  1. ValdikSS
    27.01.2017 00:19

    Конечно эта реализация VPN через XMPP это достаточно медленная.
    Нет, это jabber.ru ограничивает скорость.


    1. jhonyxakep
      27.01.2017 00:37
      +1

      возможно. Пробую позже протестировать на локальном jabber севере


  1. ZoomLS
    27.01.2017 02:21

    >>напоминаю про ответственность за действия, которые вы можете совершить не подумав

    И чего нарушает пользователь в данном случае? Ничего же.


    1. jhonyxakep
      27.01.2017 09:15
      +1

      В конце концов метод позволяет обойти ограничения связи, которые накладываются провайдером за неуплату.


      1. 15432
        27.01.2017 09:49
        +1

        Есть же iodine и icmptunnel. Очень странно, что джаббер продолжал работать. Обычно блочат весь UDP/TCP трафик.


        1. Zagrebelion
          27.01.2017 09:53

          Несколько раз видел рассказы про то, как интернет отключен за неулпату, а телеграм работает.


          1. worldxaker
            27.01.2017 13:18

            та же yota двигает это как фишку, правда там месседжеры только за границой работают без интернета


            1. Erelecano
              29.01.2017 02:23

              > месседжеры только за границой работают без интернета

              Через libastral.so конектятся что ли???


        1. jhonyxakep
          27.01.2017 09:56

          Про существование iodine и icmptunnel знал, но провайдер подменяет большинство dns запросов на свои для показа странички «заплатите за интернет». Я думаю работоспособность jabber и сервисов Яндекса при отсутствии оплаты используется как реклама. Также доступно большинство онлайн банков.
          Вообще я конечно преследовал цель больше в организации тоннеля через «любую» среду, XMPP просто как конкретный пример.


          1. 15432
            27.01.2017 15:18

            Точно ли DNS подменяется? Обычно перехватывается TCP коннект и выдается 302 редирект. Подмену DNS не встречал.


            1. jhonyxakep
              27.01.2017 15:37

              Я особо не изучал эту подмену, визуально похоже на работу блокировки типа «роскомнадзор», может ошибаюсь.


  1. scronheim
    27.01.2017 08:41
    -2

    Мсье знает толк


  1. NeonXP
    27.01.2017 11:26
    +4

    Вот это круто для пятницы!
    Ещё нужно сделать через Telegram, тогда можно будет через Йоту заграницей бесплатно в роуминге сидеть.


    1. jhonyxakep
      27.01.2017 11:47

      Честно говоря я изначально пробовал передавать данные через телеграмм (на моем провайдере тоже не блокируется), но модуль telegramm-cli часто отпадал из-за какой-то ошибки, а разбираться было откровенно лень.
      Йота действительно позволяет использовать телеграмм в роуминге без тарификации?


      1. NeonXP
        27.01.2017 11:58

        Так заявлено. Но как я понимаю, они делают DIP дабы не слали большие картинки и файлы. Но не думаю что VPN трафику это повредит.


      1. worldxaker
        27.01.2017 14:32

        будет круто реализовать это через бота. для ботов на js дофига либ


        1. jhonyxakep
          27.01.2017 14:48

          Бот вроде не может общатся с ботом? Разве что как-нибудь через общую группу. Надо попробовать


          1. worldxaker
            27.01.2017 15:09

            так это и не надо. грубо говоря бот играет роль сервера. клиентские устройства логинятся со своего личного аккаунта и потом общаются с ботом. при этом в самом месседжере кидает бота в мут и получаем и рабочий VPN и рабочий месседжер


            1. jhonyxakep
              27.01.2017 15:39

              Понял. Я в общем то пытался сделать коннект клиент-клиент, но, как написал выше, единственный адекватный способ работать с MTProto на NodeJs — telegramm-cli далёк от стабильности.


    1. TimsTims
      27.01.2017 18:33
      +1

      Вот только йоте потом тоже счет выставляют нехилый. И после нескольких таких залетов они просто отключат эту бесплатную фишку.
      Поэтому, лучше не ссать в колодец...


  1. gbg
    27.01.2017 13:03
    +1

    А tuntox видели? Он тоже сносно работает, пуская тоннель через TOX. И аккаунтов не надо.


  1. smple
    27.01.2017 13:18
    +1

    давным давно когда трафик у провайдеров был еще помегабайтным (и был дешевый или бесплатный локальный трафик) у нас в локальной сети появился xmpp сервер от провайдером, со связью наружу.

    Не долго думая был использован один из удаленных серверов где нормальный безлимитный интернет и туда был установлен xmpp.

    И был поднят тонель между моим компом -> локальным xmpp сервером -> внешний xmpp сервер и клиент, которые проксировали мой трафик :D

    Проблема была одна, высокий пинг, в остальном все работало)


  1. SunX
    27.01.2017 13:55

    Классно, но все же возражу, что это скорее не VPN, а PPP.

    Кстати вспомнил, что во времена небезлимитного Интернета что-то подобное кто-то делал на C++: https://code.google.com/archive/p/pppoj/


    1. jhonyxakep
      27.01.2017 14:17

      Вы правы, в данном случае больше подходит определение PPP. Несмотря на это создавая tun интерфейс вместо tap можно делать полноценный VPN с несколькими устройствами в виртуальном хабе.

      Сейчас ещё погуглил, тема PPP через XMPP активно обсуждалась на ЛОРе. Несмотря на это протокол XMPP я в основном взял как пример, наиболее понятный. Организовать туннель в данном случае можно почти через любую среду. Например: у меня в планах попробовать прокинуть сеть через радио канал и IR, just for fun.


      1. SunX
        27.01.2017 15:20

        Ну и с tun можно организовать VPN, если серверную часть научить этому.

        А вообще идея PPP over любой поток — это интересно =)


        1. jhonyxakep
          27.01.2017 15:23
          +1

          Я тогда в ближайшее время оформлю проект как npm модуль, с возможностью подключения плагинов, чтобы все смогли пользоваться :)


      1. Stanislavvv
        27.01.2017 17:26
        +1

        Радиолюбители давно уже пользуются AX.25, рекомендую обратить внимание.
        Если получится — просьба статью :-)


  1. degster
    27.01.2017 17:10

    Следующий шаг — Интернет через СМС


    1. jhonyxakep
      27.01.2017 17:11

      Почему бы и нет? Бывают случаи, когда ничего кроме SMS недоступно, а доступ в интернет, хоть медленный, но нужен


      1. jhonyxakep
        27.01.2017 19:27

        да, но там только урезанный браузер, а у нас полноценная сеть


    1. ZoomLS
      27.01.2017 19:13

      Уже есть такое: https://habrahabr.ru/post/237165/ ;)


  1. Erelecano
    27.01.2017 18:37

    http://ipop-project.org/