- PVSM.RU - https://www.pvsm.ru -
У некоторых людей интересные истории начинаются с приёма жидкостей с содержанием алкоголя. У некоторых с чего-то покрепче… У меня, как у истинного представителя мира IT, история началась… С отключения интернета. Конечно можно было пойти простым путём для решения проблемы, и просто заплатить, но ведь это не истинный путь самурая? Много больших скриншотов
Дело в том, что отключение интернета было обнаружено не сразу. В этот день активно использовал переписку по почте и со своим знакомым. Почта работала отлично, как и шустро бегали сообщения через XMPP на серверах jabber.ru Несмотря на это, основные прелести интернета были недоступны, и, при всех доступных условиях, мысль родилась быстро.
Для понимания принципов работы VPN тоннелей конечно пришлось заплатить за интернет, что бы воспользоваться «тем самым поисковиком». Оказалось, что в основном, туннели строятся с использованием «виртуальных сетевых драйверов» — TUN и TAP [1]. 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 [2]
Для сети я решил использовать 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').
Для тестирования сети пришлось зарегестрировать парочку дополнительных 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!
Я жутко извиняюсь за именование некоторых переменных, надеюсь вы найдёте в себе силы отрефакторить несколько строк.
Для начала тестируем ping:
Смотрите, работает!
Как насчёт чего-то более приближенного к реальности? Попробуем воспользоватся протоколом HTTP.
Ставим и запускаем Lighttpd:
Тестируем:
Хо-хо!
Усложним ещё:
Загрузилась!
Средняя скорость загрузки логотипа хабра была 957 байт/сек. В интернет с такой скоростью выходить мягко говоря не комфортно, однако, я считаю, что цель достигнута.
Как вы могли заметить, вся разработка и тестирование выполнялась в Linux Ubuntu. Выбор обусловлен несколькими факторами:
Несмотря на это решить проблему для Windows не очень сложно. Существует несколько реализаций TUN/TAP драйверов, самая популярная написана для проекта OpenVPN, и имеет доступную и понятную документацию. Поддержку драйвера от OpenVPN было бы неплохо внести в тот-же модуль node-tuntap.
Конечно эта реализация VPN через XMPP это достаточно медленная. Ради теста я написал реализацию, работающую с помощью SocketIO через хост машину, в этом случае скорости были нормальные. Несмотря на это напоминаю про ответственность за действия, которые вы можете совершить не подумав, и, что весь материал статьи представлен исключительно в ознакомительных целях.
Автор: jhonyxakep
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/node-js/237717
Ссылки в тексте:
[1] TUN и TAP: https://ru.wikipedia.org/wiki/TUN/TAP
[2] github.com/binarysec/node-tuntap: https://github.com/binarysec/node-tuntap
[3] Источник: https://habrahabr.ru/post/320570/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.