diff --git a/stream/ch/chain/net/srv/vpn/DapStreamChChainNetSrvVpn.cpp b/stream/ch/chain/net/srv/vpn/DapStreamChChainNetSrvVpn.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..91a9acaf80713b3c39c59b0c10371ba80158833a
--- /dev/null
+++ b/stream/ch/chain/net/srv/vpn/DapStreamChChainNetSrvVpn.cpp
@@ -0,0 +1,407 @@
+/*
+* Authors:
+* Dmitriy Gerasimov <naeper@demlabs.net>
+* Cellframe       https://cellframe.net
+* DeM Labs Inc.   https://demlabs.net
+* Copyright  (c) 2017-2019
+* All rights reserved.
+
+This file is part of CellFrame SDK the open source project
+
+CellFrame SDK is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+CellFrame SDK is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with any CellFrame SDK based project.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include <QtDebug>
+
+#include "DapStreamer.h"
+#include "DapStreamChChainVpnPacket.h"
+#include "DapStreamChChainNetSrvVpn.h"
+
+#if defined(Q_OS_UNIX)
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/ip.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <sys/select.h>
+
+#elif defined(Q_OS_MACOS)
+#elif defined (Q_OS_WIN)
+#include <winsock2.h>
+#endif
+
+#include <QProcess>
+#include <QFile>
+
+
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+using namespace Dap;
+using namespace Dap::Stream;
+
+/**
+ * @brief ChChainNetSrvVpn::delForwardingAll
+ */
+void ChChainNetSrvVpn::delForwardingAll()
+{
+    for (auto s : m_socketsIn)
+        delete s;
+    m_socketsIn.clear();
+
+    for (auto s : m_servs)
+        delete s;
+    m_servs.clear();
+}
+
+/**
+ * @brief ChChainNetSrvVpn::delForwarding
+ * @param sockId
+ */
+void ChChainNetSrvVpn::delForwarding(int sockId)
+{
+    QTcpServer * serv = m_servs[sockId];
+    if (serv) {
+        m_servs.remove(sockId);
+        delete serv;
+    } else qDebug() << "Not found socket by id: " << sockId;
+}
+
+/**
+ * @brief ChChainNetSrvVpn::onFwClientReadyRead
+ */
+void ChChainNetSrvVpn::onFwClientReadyRead()
+{
+    qDebug() << "[onFwClientReadyRead]";
+    QTcpSocket * sock = qobject_cast<QTcpSocket*>(QObject::sender());
+    if (sock) {
+        QByteArray ba = sock->readAll();
+        qDebug() << "From client's socket have read " << ba.length() << "bytes.";
+        Dap::Stream::Packet* pkt = (Dap::Stream::Packet*) calloc(1, ba.length() + sizeof(pkt->header));
+        memcpy(pkt->data, ba.constData(), ba.length());
+        pkt->header.op_data.data_size = ba.length();
+        pkt->header.op_code = STREAM_SF_PACKET_OP_CODE_SEND;
+        pkt->header.socket_id = sock->socketDescriptor();
+        packetOut(pkt);
+    } else{
+        qCritical() << "Cant cast sender() object of type "
+                 <<QObject::sender()->metaObject()->className()<< " to QTcpSocket";
+    }
+}
+
+/**
+ * @brief ChChainNetSrvVpn::onFwClientDisconnected
+ */
+void ChChainNetSrvVpn::onFwClientDisconnected()
+{
+    QTcpSocket *sock = qobject_cast<QTcpSocket*>(QObject::sender());
+    if(sock) {
+        qDebug() << "Disconnected client " << sock->localAddress();
+        m_socketsIn.remove(sock->socketDescriptor());
+
+        Dap::Stream::Packet* pkt = (Dap::Stream::Packet*) calloc(1,sizeof(pkt->header) + 1);
+        pkt->header.op_code = STREAM_SF_PACKET_OP_CODE_DISCONNECT;
+        pkt->header.socket_id = sock->socketDescriptor();
+        pkt->header.raw.data_size = 2; ///TODO prev value is 0
+        packetOut(pkt);
+    } else{
+        qCritical() << "Cant cast sender() object of type "
+                 <<QObject::sender()->metaObject()->className()<< " to QTcpSocket";
+    }
+}
+
+/**
+ * @brief ChChainNetSrvVpn::onFwServerConnected
+ */
+void ChChainNetSrvVpn::onFwServerConnected()
+{
+    qDebug() << "[onFwServerConnected()]";
+    QTcpServer *serv = qobject_cast<QTcpServer*>(QObject::sender());
+    QTcpSocket *sock;
+    QByteArray remoteAddrBA;
+    Dap::Stream::Packet *pkt;
+
+    if(serv) {
+        if(!serv->hasPendingConnections())
+            qDebug() << "No pendidng connections on the server";
+        else do {
+
+            sock = serv->nextPendingConnection();
+            remoteAddrBA = (m_servsRemoteAddr[serv->socketDescriptor()]).toLatin1();
+            m_socketsIn.insert(sock->socketDescriptor(),sock);
+            pkt = (Dap::Stream::Packet*) calloc(1, sizeof(pkt->header) +  remoteAddrBA.length());
+
+            pkt->header.op_code = STREAM_SF_PACKET_OP_CODE_CONNECT;
+            pkt->header.socket_id = sock->socketDescriptor();
+            pkt->header.op_connect.addr_size = remoteAddrBA.length();
+
+            memcpy(pkt->data, remoteAddrBA.constData(), pkt->header.op_connect.addr_size);
+
+            pkt->header.op_connect.port = m_servsRemotePort[serv->socketDescriptor()];
+
+            qDebug() << "New connection enstablished to "
+                     << m_servsRemoteAddr[serv->socketDescriptor()]
+                     << ":" << pkt->header.op_connect.port
+                     << " addr size" << pkt->header.op_connect.addr_size;
+
+            connect(sock, &QTcpSocket::readyRead, this, &ChChainNetSrvVpn::onFwClientReadyRead);
+            connect(sock, &QTcpSocket::disconnected, this, &ChChainNetSrvVpn::onFwClientDisconnected);
+
+            packetOut(pkt);
+
+        } while( serv->hasPendingConnections() );
+    } else
+        qDebug() << "Can't cast object to QTcpServer";
+}
+
+/**
+ * @brief ChChainNetSrvVpn::addForwarding
+ * @param remoteAddr
+ * @param remotePort
+ * @param localPort
+ * @return
+ */
+quint16 ChChainNetSrvVpn::addForwarding(const QString remoteAddr, quint16 remotePort, quint16 localPort)
+{
+    qDebug() << "addForwarding " << QString("127.0.0.1:%1 => %2:%3")
+                                                                    .arg(localPort)
+                                                                    .arg(remoteAddr)
+                                                                    .arg(remotePort);
+    QTcpServer *server = new QTcpServer;
+    if(server->listen(QHostAddress("127.0.0.1"), localPort)) {
+        qDebug() << "Sucessfully set up " << localPort << " to listening";
+        m_servs.insert(server->socketDescriptor(), server);
+        m_servsRemoteAddr.insert(server->socketDescriptor(), remoteAddr);
+        m_servsRemotePort.insert(server->socketDescriptor(), remotePort);
+        connect(server, &QTcpServer::newConnection, this, &ChChainNetSrvVpn::onFwServerConnected);
+        connect(server, &QTcpServer::acceptError,[=]{
+           qDebug() << "QTcpServer::acceptError() "<< server->errorString() ;
+        });
+        return server->serverPort();
+    } else {
+        qWarning() << "Can't open local port for listening";
+        emit error(QString("Can't open local port %1").arg(localPort));
+        delete server;
+        return 0;
+    }
+}
+
+/**
+ * @brief ChChainNetSrvVpn::ChChainNetSrvVpn
+ */
+ChChainNetSrvVpn::ChChainNetSrvVpn(DapStreamer * a_streamer, DapSession * mainDapSession)
+    :DapChBase(nullptr, 'S'), m_streamer(a_streamer), m_mainDapSession(mainDapSession)
+{
+    tun = new DapTunNative();
+    m_fdListener = nullptr;
+    connect(tun, &DapTunNative::created, this, &ChChainNetSrvVpn::tunCreated);
+    connect(tun, &DapTunNative::destroyed, this, &ChChainNetSrvVpn::tunDestroyed);
+    connect(tun, &DapTunNative::error , this, &ChChainNetSrvVpn::tunError);
+    connect(tun, &DapTunNative::packetOut, this, &ChChainNetSrvVpn::packetOut);
+    connect(tun, &DapTunNative::sendCmd, this, &ChChainNetSrvVpn::sendCmdAll);
+    connect(tun, &DapTunNative::bytesRead, this, &ChChainNetSrvVpn::bytesRead);
+    connect(tun, &DapTunNative::bytesWrite, this, &ChChainNetSrvVpn::bytesWrite);
+    connect(tun, &DapTunNative::nativeCreateRequest, this, &ChChainNetSrvVpn::sigTunNativeCreate);
+    connect(tun, &DapTunNative::nativeDestroyRequest, this, &ChChainNetSrvVpn::sigNativeDestroy);
+}
+
+/**
+ * @brief ChChainNetSrvVpn::packetOut
+ * @param pkt
+ */
+void ChChainNetSrvVpn::packetOut(Dap::Stream::Packet *pkt)
+{
+    DapChannelPacketHdr* hdr= (DapChannelPacketHdr *) ::calloc(1, sizeof(DapChannelPacketHdr));
+    hdr->id='S';
+    hdr->type=0x00;
+    hdr->size=sizeof(pkt->header);
+    switch(pkt->header.op_code){
+        case STREAM_SF_PACKET_OP_CODE_SEND:
+        case STREAM_SF_PACKET_OP_CODE_RECV:
+        case STREAM_SF_PACKET_OP_CODE_RAW_SEND:
+        case STREAM_SF_PACKET_OP_CODE_RAW_RECV:
+        case STREAM_SF_PACKET_OP_CODE_RAW_L3_ADDR_REPLY:
+        case STREAM_SF_PACKET_OP_CODE_RAW_L3:
+        case STREAM_SF_PACKET_OP_CODE_RAW_L2:
+        case STREAM_SF_PACKET_OP_CODE_RAW_L3_ADDR_REQUEST:
+            hdr->size+=pkt->header.op_data.data_size;
+        break;
+    default:
+        qWarning() << "Unknown packet code" << pkt->header.op_code;
+    }
+    emit pktChOut(hdr,pkt);
+}
+
+/**
+ * @brief DapChSockForw::requestIP
+ */
+void ChChainNetSrvVpn::requestIP(quint32 a_usageId)
+{
+    emit netConfigRequested();
+    Dap::Stream::Packet * pktOut = reinterpret_cast<Dap::Stream::Packet*>(::calloc(1 ,sizeof(pktOut->header)));
+    pktOut->header.op_code=STREAM_SF_PACKET_OP_CODE_RAW_L3_ADDR_REQUEST;
+    pktOut->header.usage_id = a_usageId;
+    qInfo() << "Request for IP with usage_id: " << pktOut->header.usage_id;
+    packetOut(pktOut);
+    emit ipRequested();
+}
+
+/**
+ * @brief DapChSockForw::netConfigClear
+ */
+void ChChainNetSrvVpn::netConfigClear()
+{
+    m_addr.clear();
+    m_gw.clear();
+    emit netConfigCleared();
+}
+
+/**
+ * @brief ChChainNetSrvVpn::tunCreate
+ * @param a_addr
+ * @param a_gw
+ */
+void ChChainNetSrvVpn::tunCreate(const QString &a_addr, const QString &a_gw)
+{
+    m_addr = a_addr;
+    m_gw = a_gw;
+    tun->create(a_addr,
+                a_gw,
+                m_mainDapSession->upstreamAddress(),
+                m_mainDapSession->upstreamPort(),
+                streamer()->upstreamSocket());
+}
+
+/**
+ * @brief ChChainNetSrvVpn::tunCreate
+ */
+void ChChainNetSrvVpn::tunCreate()
+{
+    tun->create(m_addr,
+                m_gw,
+                m_mainDapSession->upstreamAddress(),
+                m_mainDapSession->upstreamPort(),
+                streamer()->upstreamSocket());
+#ifdef ANDROID
+    if (m_fdListener == nullptr) {
+        m_fdListener = new QTcpServer();
+
+        connect(m_fdListener, &QTcpServer::newConnection, this, [&] {
+            qDebug() << "f0";
+            auto pending = m_fdListener->nextPendingConnection();
+            connect(pending, &QTcpSocket::readyRead, this, [=] {
+                if (pending) {
+                    int val = pending->readAll().toInt();
+                    workerStart(val);
+                    m_fdListener->close();
+                }
+            });
+        });
+    }
+    m_fdListener->listen(QHostAddress::LocalHost, 22500);
+#else
+    tun->workerStart();
+#endif
+}
+
+void ChChainNetSrvVpn::tunDestroy()
+{
+    tun->destroy();
+}
+
+/**
+ * @brief ChChainNetSrvVpn::afterTunCreate
+ * @param a_tunSocket
+ */
+void ChChainNetSrvVpn::workerStart(int a_tunSocket)
+{
+    qDebug() << "set tun socket: " << a_tunSocket;
+    tun->setTunSocket(a_tunSocket);
+    tun->workerStart(); // start loop
+}
+
+/**
+ * @brief ChChainNetSrvVpn::onPktIn
+ * @param pkt
+ */
+void ChChainNetSrvVpn::onPktIn(DapChannelPacket* pkt)
+{
+    // qDebug() << "onPktIn: id ="<<pkt->hdr()->id << " type = "<< pkt->hdr()->type<< " ch_data_size = "<<pkt->hdr()->size;
+    Dap::Stream::Packet * pktSF=(Dap::Stream::Packet *) pkt->data();
+    //qDebug() << " onPktIn: SampSFPacket op_code ="<< pktSF->header.op_code;
+    switch(pktSF->header.op_code){
+        case STREAM_SF_PACKET_OP_CODE_SEND:
+        case STREAM_SF_PACKET_OP_CODE_CONNECT:{
+            qDebug() << "Backforward connection are not processed";
+        } break;
+        case STREAM_SF_PACKET_OP_CODE_DISCONNECT:{
+
+            break;
+            QTcpSocket *sc=m_socketsIn[pktSF->header.socket_id];
+            if (sc) {
+                m_socketsIn.remove(pktSF->header.socket_id);
+                sc->close();
+            } else {
+                qDebug() <<" Unknown socket_id "<<pktSF->header.socket_id;
+            }
+        } break;
+        case STREAM_SF_PACKET_OP_CODE_RECV:{
+            QTcpSocket *sc=m_socketsIn[pktSF->header.socket_id];
+            if (sc) {
+                sc->write((const char*)pktSF->data, pktSF->header.op_data.data_size);
+            } else {
+                qDebug() <<" Unknown socket_id "<<pktSF->header.socket_id;
+            }
+        } break;
+        case STREAM_SF_PACKET_OP_CODE_RAW_L3_ADDR_REPLY:{
+            quint32 l_addr,l_gw;
+            ::memcpy(&l_addr, pktSF->data,sizeof (l_addr));
+            ::memcpy(&l_gw, pktSF->data+sizeof (l_addr),sizeof (l_addr));
+            QString new_addr    = QHostAddress(  ntohl(l_addr) ).toString();
+            QString new_gw      = QHostAddress( ntohl ( l_gw )).toString();
+            if (m_addr == new_addr && new_gw == m_gw) {
+                qDebug() << "Net config is the same, we don't touch Tun";
+                emit netConfigReceivedSame();
+            } else {
+                m_addr = new_addr;
+                m_gw = new_gw;
+                emit netConfigReceived(m_addr,m_gw);
+            }
+        } break;
+        case STREAM_SF_PACKET_OP_CODE_RAW_RECV:{
+            pkt->unleashData(); // Uleash *data section from pkt object
+            tun->tunWriteData(pktSF);
+        }break;
+        /*case STREAM_SF_PACKET_OP_CODE_CONNECTED:
+            qDebug() << "[DapChSockForw] Get connected packet."; // Repsonse to the connect Packet
+            tcp_sock = m_socketsIn[sockForwPacket->header.socket_id];
+            if(tcp_sock)
+                connect(tcp_sock, &QTcpSocket::readyRead, this, &DapChSockForw::onClientReadyRead);
+            else
+                qWarning() << ("[DapChSockForw] onPktIn() Not Find Socket by socket_id!");
+            break;*/
+    }
+
+    delete pkt;
+}
+
+
+
+
+
+
+
diff --git a/stream/ch/chain/net/srv/vpn/DapStreamChChainNetSrvVpn.h b/stream/ch/chain/net/srv/vpn/DapStreamChChainNetSrvVpn.h
new file mode 100644
index 0000000000000000000000000000000000000000..f31ee99b062c27eac2f4b9df70a3b6dcd00b386a
--- /dev/null
+++ b/stream/ch/chain/net/srv/vpn/DapStreamChChainNetSrvVpn.h
@@ -0,0 +1,149 @@
+/*
+* Authors:
+* Dmitriy Gerasimov <naeper@demlabs.net>
+* Cellframe       https://cellframe.net
+* DeM Labs Inc.   https://demlabs.net
+* Copyright  (c) 2017-2019
+* All rights reserved.
+
+This file is part of CellFrame SDK the open source project
+
+CellFrame SDK is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+CellFrame SDK is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with any CellFrame SDK based project.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#pragma once
+#include "DapChBase.h"
+#include "DapSession.h"
+
+//#include "DapSockForwPacket.h"
+#include "DapStreamChChainNetSrv.h"
+#include "DapStreamChChainVpnPacket.h"
+#include "DapTunNative.h"
+#include "DapStreamer.h"
+
+#include <QDebug>
+#include <QTcpSocket>
+#include <QTcpServer>
+
+
+
+#define STREAM_SF_PACKET_OP_CODE_CONNECTED 0x000000a9
+#define STREAM_SF_PACKET_OP_CODE_CONNECT 0x000000aa
+#define STREAM_SF_PACKET_OP_CODE_DISCONNECT 0x000000ab
+#define STREAM_SF_PACKET_OP_CODE_SEND 0x000000ac
+#define STREAM_SF_PACKET_OP_CODE_RECV 0x000000ad
+#define STREAM_SF_PACKET_OP_CODE_PROBLEM 0x000000ae
+
+#define STREAM_SF_PROBLEM_CODE_NO_FREE_ADDR 0x00000001
+#define STREAM_SF_PROBLEM_CODE_TUNNEL_DOWN  0x00000002
+#define STREAM_SF_PROBLEM_CODE_PACKET_LOST  0x00000003
+
+#define STREAM_SF_PACKET_OP_CODE_RAW_L3 0x000000b0
+#define STREAM_SF_PACKET_OP_CODE_RAW_L2 0x000000b1
+#define STREAM_SF_PACKET_OP_CODE_RAW_L3_ADDR_REQUEST 0x000000b2
+#define STREAM_SF_PACKET_OP_CODE_RAW_L3_ADDR_REPLY 0x000000b3
+#define STREAM_SF_PACKET_OP_CODE_RAW_L3_DEST_REPLY 0x000000b4
+
+#define STREAM_SF_PACKET_OP_CODE_RAW_SEND 0x000000bc
+#define STREAM_SF_PACKET_OP_CODE_RAW_RECV 0x000000bd
+
+#define DAP_CHAIN_NET_SRV_VPN_ID
+
+
+namespace Dap {
+    namespace Chain {
+        namespace NetSrv {
+            const Uid UID_VPN = 0x0000000000000001;
+        }
+    }
+    namespace Stream {
+
+        class ChChainNetSrvVpn : public DapChBase
+        {
+            Q_OBJECT
+        public:
+        private:
+            // DATAS
+            DapStreamer * m_streamer;
+            DapSession * m_mainDapSession;
+
+            QMap<int, QTcpSocket* > m_socketsIn;
+            QMap<int, QTcpServer* > m_servs;
+            QMap<int, QString > m_servsRemoteAddr;
+            QMap<int, quint16 > m_servsRemotePort;
+
+            QMap<int, QTcpSocket* > m_socksIn;
+            QMap<int, QTcpSocket* > m_socksOut;
+
+            DapTunNative * tun;
+            // METHODS
+            QString m_addr, m_gw;
+            QTcpServer *m_fdListener;
+        private slots:
+            void onFwServerConnected();
+            void onFwClientReadyRead();
+            void onFwClientDisconnected();
+
+        signals:
+            void bytesRead(int);
+            void bytesWrite(int);
+
+            void fwdDisconnected(int sockServ, int sockClient);
+            void usrMsg(QString);
+            void sigPacketRead();
+            void sigPacketWrite();
+
+            void sigTunNativeCreate();
+            void sigNativeDestroy();
+
+
+        public:
+            ChChainNetSrvVpn(DapStreamer * a_streamer, DapSession * mainDapSession);
+
+            bool isTunCreated(){return tun->isCreated();}
+
+            void tunCreate (const QString& a_addr, const QString& a_gw);
+            void workerStart(int a_tunSocket);
+
+            quint16 addForwarding(const QString remoteAddr, quint16 remotePort, quint16 localPort);
+            void delForwarding(int sockId);
+            void delForwardingAll();
+
+            DapStreamer * streamer(){ return m_streamer; }
+        signals:
+
+            void netConfigReceived(QString,QString);
+            void netConfigRequested();
+            void netConfigReceivedSame();
+            void netConfigCleared();
+            void tunCreated();
+            void tunDestroyed();
+            void tunError(const QString&);
+            void tunWriteData();
+
+            void ipRequested();
+
+            void sendCmdAll(const QString&);
+        public slots:
+            void onPktIn(DapChannelPacket *pkt) override;
+            void packetOut(Dap::Stream::Packet *pkt);
+
+            void requestIP(quint32);
+            void netConfigClear();
+
+            void tunCreate(); // create with all predefined before values
+            void tunDestroy();
+        };
+
+    }
+}
diff --git a/stream/ch/chain/net/srv/vpn/README.md b/stream/ch/chain/net/srv/vpn/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..a6013c932680fc7fdaf2c7b79a84ec940e082dd6
--- /dev/null
+++ b/stream/ch/chain/net/srv/vpn/README.md
@@ -0,0 +1,2 @@
+# libdap-qt-stream-ch-chain-net-srv-vpn
+
diff --git a/stream/ch/chain/net/srv/vpn/libdap-qt-stream-ch-chain-net-srv-vpn.pri b/stream/ch/chain/net/srv/vpn/libdap-qt-stream-ch-chain-net-srv-vpn.pri
new file mode 100755
index 0000000000000000000000000000000000000000..2ea7adae64e13bf83293cda9d0eed39270601ef6
--- /dev/null
+++ b/stream/ch/chain/net/srv/vpn/libdap-qt-stream-ch-chain-net-srv-vpn.pri
@@ -0,0 +1,7 @@
+SOURCES += \
+    $$PWD/DapStreamChChainNetSrvVpn.cpp
+
+HEADERS += \
+    $$PWD/DapStreamChChainNetSrvVpn.h
+
+INCLUDEPATH += $$PWD