Skip to content

Commit

Permalink
add updated skywire specs documents from skycoinpro/skywire-specs
Browse files Browse the repository at this point in the history
  • Loading branch information
0pcom committed May 24, 2024
1 parent a499792 commit 709534a
Show file tree
Hide file tree
Showing 28 changed files with 5,322 additions and 0 deletions.
28 changes: 28 additions & 0 deletions skywire-specs/Drafts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Drafts

## Transport State

The modifiable data associated with a *Transport* is stored in a different structure called the *Transport State*. As a *Transport* has two edges (and hence, two perspectives), a *Transport State* is also updated accordingly.

## Packet types

Transports, Routes and the *Route Setup Service* are to deliver data via Packets. Packets are responsible for setting up routes and streams, and delivering data within the constructed routes and streams.

Here is a summary of all the *Packet Types*.

| Type | Value | Description |
| ---- | ------ | ----------- |
| `Ping` | `0x0` | Sent between a Transport to check if connection is still open, and to determine latency of the Transport. |
| `InitiateRoute` | `0x1` | First packet sent via a route to have it initiated. |
| `RouteInitiated` | `0x2` | Confirms that a route is set up and functional. |
| `DestroyRoute` | `0x3` | Initiate the destruction of a route. |
| `RouteDestroyed` | `0x4` | Confirm the success of a route's destruction. |
| `OpenStream` | `0x5` | Opens a stream within a route. |
| `StreamOpened` | `0x6` | Confirms that a stream is successfully opened. |
| `CloseStream` | `0x7` | Closes a stream. |
| `StreamClosed` | `0x8` | Informs that a stream has successfully closed. |
| `Forward` | `0x9` | Forwards data via a specified stream. |

## Route Setup Service

The *Route Setup Service* is a service which communicates with ...
3 changes: 3 additions & 0 deletions skywire-specs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Skywire Specifications

The Skywire specifications.
2,379 changes: 2,379 additions & 0 deletions skywire-specs/Specifications.md

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions skywire-specs/VPN/Client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# VPN Client

Client is mostly the same as a [server](./Server.md). What really differs server from the client is overall routing. Only outbound traffic should be going through the interface. Not sure if the inbound traffic matters. So, client reads all the outbound from the adapter and passes it to the remote VPN server through the open connection.

Since internal Skywire traffic actually provides the tunnel functionality, we pass all the outbound traffic through the interface except for packets targeting Skywire services. To exclude, we pass IPs of our services to the client on startup via ENVs. During its work we add or remove such direct IPs via network hooks between visor and app.
52 changes: 52 additions & 0 deletions skywire-specs/VPN/Handshake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Client/Server Handshake

Before client and server start exchange the actual traffic the handshake process must take place. Client and server exchange their specific hello messages to agree on exchange details. Both messages are just JSON objects being sent over the app connection. Client sends its message first.

As stated in the [general server description](./Server.md), we need to choose 4 different IP addresses in the same subnetwork to give these to client- and server-side TUN interfaces. These IPs must be in the same network. Interfaces of different clients must not share the same subnet. To make this process deterministic, server will be responsible for choosing these addresses for each connecting client. Obviously for the system to work these generated IPs must not clash neither with any of the IP of the local client network interfaces nor with its default network gateway.

### Client Hello Message

```
type ClientHello struct {
UnavailablePrivateIPs []net.IP `json:"unavailable_private_ips"`
Passcode string `json:"passcode"`
}
```

Here we have only one field - IPs that server must exclude from the generation. Usually client includes in this field IPs of all its local network interfaces plus its default network gateway. Client may also want to include some of the IPs that it's going to connect to directly without VPN.

### Server Hello Message

```
type ServerHello struct {
Status HandshakeStatus `json:"status"`
TUNIP net.IP `json:"tun_ip"`
TUNGateway net.IP `json:"tun_gateway"`
}
```

Status represents the handshake process result. May be one of the following:

- 0 - OK
- 1 - Client message was malformed
- 2 - No free IPs left to give
- 3 - Internal server error
- 4 - Forbidden (invalid passcode)

`TUNIP` and `TUNGateway` fields are used by the client to set up its local TUN interface

### Server-Side IP Generation

We need to generate 4 different IPs lying in the same network. For this we'll use the `/29` (`255.255.255.248`) mask. Server iterates over all private IP ranges:
- `192.168.0.0` - `192.168.255.255`
- `172.16.0.0` - `172.31.255.255`
- `10.0.0.0` - `10.255.255.255`

Generation step is 8, so the IPs will be generate like:
`192.168.0.0, 192.168.0.8, 192.168.0.16, ...`

This way the generated IP address will be the address of the subnet. Let's say we have the generated IP - `192.168.0.0`. Then server will assign the following addresses:
- `192.168.0.1` - Server-side TUN gateway
- `192.168.0.2` - Server-side TUN IP
- `192.168.0.3` - Client-side TUN gateway
- `192.168.0.4` - Client-side TUN IP
41 changes: 41 additions & 0 deletions skywire-specs/VPN/Server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# VPN Server

Server is responsible for listening to incoming VPN client connections over Skywire network, creating TUN/TAP adapter, setting routing up, reading packets from adapter and passing them to the remote VPN server.

## Implementation Note

Due to the firewall used on MacOS server cannot be implemented for the system. Windows needs to be investigated.

## Routing

We allocate TUN interface for each VPN client. This way we may easily distribute traffic between clients. For the system to work both client and server TUN interfaces must be in the same subnetwork. And they must have different IPs. Gateway probably may be the same, but just to be sure, we're giving different ones. For the generation process details please consult [handshake](./Handshake.md) section.

Let's say we have server-side TUN `tun0` with IP `192.168.255.2` and gateway `192.168.255.1`.

- Linux
```
/sbin/ifconfig tun0 192.168.255.2 192.168.255.1 mtu 1500 netmask 255.255.255.248 up
```

Then we set up routing. First, we need to allow kernel pass packets from one interface to another. This is done like this:
- Linux
```
sudo sysctl -w net.ipv4.ip_forward=1 // for IPv4
sudo sysctl -w net.ipv6.conf.all.forwarding=1 // for IPv6
```

Then we need to let the system work as NAT, so that packets flow as expected with their source IPs changed to the IP of the default interface.
```
sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
```
Here `wlan0` is a default network interface in the system. May be fetched from the output of `netstat -rn` on Unix-like systems.

For cleanup we may fetch the old value of forwarding like:
```
sudo sysctl net.ipv4.ip_forward
sudo sysctl net.ipv6.conf.all.forwarding
```
and then we may assign old values on cleanup. Routing rule may be removed like this:
```
sudo iptables -t nat -D POSTROUTING -o wlan0 -j MASQUERADE
```
113 changes: 113 additions & 0 deletions skywire-specs/VPN/Specs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# VPN

Basically VPN consists of 2 applications - [client](./Client.md) and [server](./Server.md). Both applications are made in form of a Skywire app (like skychat currently). They are run under control of a Skywire visor.

## TUN/TAP adapter

We use TUN/TAP to create a virtual interface for both client and server. Client and server are connected through a tunnel (SkyTunnel?). This is not a tunnel in common understanding like direct connection, apps are connected over Skywire network through visors like all other apps.

All the system traffic is routed through created virtual network interface to the application which had created it. For the initial implementation I suggest that we go with TUN adapter which allows us to inspect and handle IP packets, so we may concentrate on the overall VPN functionality.

### TUN/TAP creation

To create the adapter we may use the following library. Linux is fully supported, MacOS is only for TUN adapter (initial implementation).

https://github.com/songgao/water

Also, these links may help:
- http://tuntaposx.sourceforge.net/

For Windows this can only be achieved with the installation of `WinTUN` driver and if running client with the `SYSTEM` account, apparently having administrator privileges is not enough. Library wrapper to create the actual interface we use:

- https://golang.zx2c4.com/wireguard/tun

The easiest way to get `WinTUN` installed is to install the `Wireguard` itself. Also acquiring `SYSTEM` rights is a plain pain, so, unfortunately I can not guide through these processes. This is not user-ready in any case. But the only thing I'm sure of is that it works, so this approach can be used as a proof of concept.

The route setup trick is done by OpenVPN on all platforms, it's the same, and we do the same.

- Linux/MacOS

This one sets up the interface itself with `192.168.255.6` as interface address and `192.168.255.5` as a destination address for P2P connection:
```
/sbin/ifconfig utun2 192.168.255.6 192.168.255.5 mtu 1500 netmask 255.255.255.255 up
```

Example of setting routing up:
```
/sbin/route add -net 134.209.17.43 192.168.1.1
/sbin/route add -net 0.0.0.0 192.168.255.5 128.0.0.1
/sbin/route add -net 128.0.0.0 192.168.255.5 128.0.0.1
```

And the corresponding cleanup:
```
/sbin/route delete -net 134.209.17.43 192.168.1.1
/sbin/route delete -net 0.0.0.0 192.168.255.5
/sbin/route delete -net 128.0.0.0 192.168.255.5
```

Setup:
- `134.209.17.43` is IP of one of Skywire services (we add all the IPs we need to connect directly. Like setup node, Dmsg servers, discoveries, etc).
- `192.168.1.1` is our router IP which serves as a default gateway when VPN is down. This IP can be fetched on Unix machines by: `netstat -rn | grep default | grep {DEFAULT_INTERFACE_NAME} | awk '{print $2}`
- `192.168.255.6` is a TUN interface's IP (question is how this one is being chosen. on this current run my laptop IP in the local network is 192.168.1.5. Probably OpenVPN just gets this addr from `ifconfig` and changes its 3rd octet to 255)
- `192.168.255.5` is a gateway for TUN interface (destination IP for P2P connection) (can't say for now how this one is determined, probably TUN interface's IP and plus 1 to the 4th octet)

Basically in this example we do the following. Route all the traffic to the Skywire services through our router, like a usual connection does by default. Then we route all traffic from subnets `0.0.0.0` and `128.0.0.0` to the VPN gateway `192.168.255.5`. So, we cover all the IPv4 range of addresses with this. Netmask `128.0.0.0` should be applied to both half ranges. So first half range covers `0.0.0.0` through `127.255.255.255` and the second one covers `128.0.0.0` through `255.255.255.255`. We could use a single route `0.0.0.0/0` but this would override the default route in the system and will make cleanup more complicated. This way we will be routing all the IPv4 traffic from the system to gateway `192.168.255.5`, it will go to `192.168.255.6` by the P2P connection and we'll be reading this traffic out of TUN interface in the app.

This command set should work for all Unix systems, the only difference is the binaries location.

Localhost traffic shouldn't be affected by all this routing. So app/visor communication will be going on as usual. The part that bothers us is visor-to-other-services communication. All of the used services are put into visor's config. So, when visor starts apps, it's fully initialized itself. So, it may take all of external services and pass their domains/IPs to the VPN app. This way VPN app can resolve IPs and add needed routes. The problem for now is Dmsg servers and other visors that are being add to the local STCP table. These entities are being added at runtime, so we need to pass these to the VPN app and to update the routing table. Based on this link https://unix.stackexchange.com/questions/188584/which-order-is-the-route-table-analyzed-in , routing table is being consulted from the most specific rules to the least specific. We're adding highly specific routes, so it should work like a charm. App should have a mechanism to get new values from the visor on the fly.

- Windows

We provide just the examples of commands we use, without specific IPs, cause it is already demonstrated above.

Setting up interface and its MTU requires 2 separate commands:

```
netsh interface ip set address name="${INTERFACE_NAME}" source=static addr=${IP} mask=${MASK} gateway=${GATEWAY}
netsh interface ipv4 set subinterface "${INTERFACE_NAME}" mtu=${MTU}
```

After we use these commands there's a lag before we can set up routes, cause interface doesn't get ready instantly (Windows, what can I say). Just be aware, that it may take several seconds (we wait for 10 in our code just to be sure).

Setting and removing routes:
```
route add ${IP} mask ${MASK} ${GATEWAY}
route delete ${IP} mask ${MASK} ${GATEWAY}
```

#### Cleanup

Regardless of other cleanup routines that need to be run on app shutdown, I guess all the possible interruption signals should be caught so we could at least remove the routes and let the system network stack work as usual not to ruin UX.

### MTU

MTU setup is not yet clear. I see that my OpenVPN instance uses 1500 which is an Ethernet MTU. Is it fixed for all hardware configurations possible? We'll have it fixed for now. These links may be useful:
- https://community.spiceworks.com/topic/217130-mtu-issues-in-vpn-connections
- https://www.sonassi.com/help/troubleshooting/setting-correct-mtu-for-openvpn

## Configuration

Both client and server can be configured like any other VPN app.

Server flags:
- `--pk` - server pub key;
- `--sk` - server secret key;
- `--passcode` - password for the client to authenticate;
- `--secure` - by default client can access machines in the server's local network (SSH in, for example). Some people may use this as a feature, while others consider this a security breach. So, setting this flag forbids access to the local network.

Client flags:
- `--srv` - server's pub key;
- `--pk` - client's pub key;
- `--sk` - client's secret key;
- `--passcode` - password to authenticate;
- `--killswitch` - If VPN tunnel goes down and client tries to reconnect. By default during this process direct Internet access gets restored. If we set this flag, there won't be any direct Internet access, user will wait till VPN tunnel is up again.

## Encryption

We rely on underlying Skywire transports for encryption.

## Authentication

Authentication is implied by the Skywire protocol itself. No further actions needed.
Binary file added skywire-specs/img/routing-table-diagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions skywire-specs/specifications/01-Overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Overview

Skywire is an decentralized network that attempts to replace the current internet. The *Skywire Network* is made up of physical *Skywire Nodes* which run the *Skywire-Visor*. There are currently two types of *Skywire Nodes*; *Skywire Visor* and *Setup Node*.

Each *Skywire Visor* is represented by a unique public key. A direct line of communication between two *Skywire Visors* is called a *Transport*. Each *Transport* is represented by a unique *Transport ID* which is of a *Transport Type*, and the two *Skywire Visors* that are connected via the *Transport* are named the *Transport Edges*.

A *Route* is unidirectional and delivers data units called *Packets*. It is made up of multiple hops where each hop is a *Transport*. Two *Routes* of opposite directions make a *Loop* when associated with the given *Ports* at each *Loop Edge*. *Loops* handle the communication between two *Skywire Apps* and are represented via the *Loop's* source and destination visor's public keys and the source and destination ports (similar to how TCP/UDP handles ports).

A *Packet* is prefixed with a *Route ID* which helps *Skywire Visors* identify how the *Packet* is to be handled (either to be forward to a remote node, or to be consumed internally). Every *Skywire Visor* has a *Routing Table* that has the *Routing Rules* for that particular *Skywire Visor*.

In summary,

- *Transports* are responsible for single-hop communication between two *Skywire Visors* and are bidirectional.
- *Routes* are responsible for multi-hop communication between two *Skywire Visors* and are unidirectional.
- *Loops* are responsible for communication between two *Skywire Apps* and are bidirectional.

There are many ways in which we can implement a *Transport*. Each unique method is called a *Transport Type*.

Initially, we need to implement a MVP in which we assume that there are no malicious nodes in the network and discovery of routes, transports and nodes are to be done in a centralized manner.
Loading

0 comments on commit 709534a

Please sign in to comment.