@node Client
@section Client part
-Except for common @code{-mtu}, @code{-stats}, @code{-egd}
-options client has the following ones:
+Except for common @code{-stats}, @code{-egd} options client has the
+following ones:
@table @code
+@item -mtu
+Expected TAP interface @ref{MTU}.
+
@item -proto
@ref{Network, network protocol} to use. Can be either @emph{udp}
(default) or @emph{tcp}.
@item You have got @code{wlan0} NIC with 192.168.0/24 network on it.
@item You want to create virtual encrypted and authenticated 172.16.0/24
network and use it as a default transport.
-@item @code{wlan0} MTU is 1500, 20 bytes overhead per IPv4. So MTU for
-GoVPN is 1500 - 20 - 8 = 1472.
-@item During startup client and server will say that TAP interface MTU
-is 1432.
+@item Assume that outgoing GoVPN packets can be fragmented, so we do not
+bother configuring MTU of TAP interfaces. For better performance just
+lower it and check that no fragmentation of outgoing UDP packets occurs.
@end itemize
@strong{Install}. At first you must @ref{Installation, install} this
server% umask 077
server% ip addr add 192.168.0.1/24 dev wlan0
server% tunctl -t tap10
-server% ip link set mtu 1432 dev tap10
server% ip addr add 172.16.0.1/24 dev tap10
server% ip link set up dev tap10
@end example
@strong{Run server daemon itself}:
@example
-server% govpn-server -bind 192.168.0.1:1194 -mtu 1472
+server% govpn-server -bind 192.168.0.1:1194
@end example
@strong{Prepare network on GNU/Linux IPv4 client}:
client% utils/storekey.sh key.txt
client% ip addr add 192.168.0.2/24 dev wlan0
client% tunctl -t tap10
-client% ip link set mtu 1432 dev tap10
client% ip addr add 172.16.0.2/24 dev tap10
client% ip link set up dev tap10
client% ip route add default via 172.16.0.1
-key key.txt \
-verifier '$argon2d$m=4096,t=128,p=1$bwR5VjeCYIQaa8SeaI3rqg' \
-iface tap10 \
- -remote 192.168.0.1:1194 \
- -mtu 1472
+ -remote 192.168.0.1:1194
@end example
@strong{FreeBSD IPv6 similar client-server example}:
@example
client% ifconfig me0 inet6 -ifdisabled auto_linklocal
client% ifconfig tap10
-client% ifconfig tap10 inet6 fc00::2/96 mtu 1412 up
+client% ifconfig tap10 inet6 fc00::2/96 up
client% route -6 add default fc00::1
client% govpn-client \
-key key.txt \
message.
If @ref{Noise} is enabled, then junk data is inserted before
-@code{IDtag} to full up packet to MTU's size.
+@code{IDtag} to fill up packet to MTU's size.
@strong{Preparation stage}:
@node MTU
@section Maximum Transmission Unit
-MTU command line argument is maximum allowable size of outgoing GoVPN's
-packets. It varies and depends on your environment, so probably has to
-be tuned. By default MTU equals to 1452 bytes: 40 bytes per IPv6 and 8
-bytes per UDP.
+MTU option tells what maximum transmission unit is expected to get from
+TAP interface. It is per-user configuration. If the program gets bigger
+size packet (including the padding byte), then it will ignore that
+packet. If either @ref{Noise, noise}, or @ref{CPR} are enabled, then all
+outgoing packets are filled up to that MTU value.
-Underlying TAP interface has lower MTU value -- 42 bytes smaller: 26
-bytes overhead on transport message and 14 bytes for Ethernet frame.
-Client and server will print what MTU value should be used on TAP
-interface.
+Default MTU equals to 1514 bytes (1500 bytes of Ethernet payload, 14
+bytes of Ethernet header).
@node Server
@section Server part
-Except for common @code{-mtu}, @code{-stats}, @code{-egd} options server
-has the following ones:
+Except for common @code{-stats}, @code{-egd} options server has the
+following ones:
@table @code
{
"stargrave": { <-- Peer human readable name
"iface": "tap10", <-- OPTIONAL TAP interface name
+ "mtu": 1514, <-- OPTIONAL overriden MTU
"up": "./stargrave-up.sh", <-- OPTIONAL up-script
"down": "./stargrave-down.sh", <-- OPTIONAL down-script
"timeout": 60, <-- OPTIONAL overriden timeout
stats = flag.String("stats", "", "Enable stats retrieving on host:port")
proxyAddr = flag.String("proxy", "", "Use HTTP proxy on host:port")
proxyAuth = flag.String("proxy-auth", "", "user:password Basic proxy auth")
- mtu = flag.Int("mtu", 1452, "MTU for outgoing packets")
+ mtu = flag.Int("mtu", govpn.MTUDefault, "MTU of TAP interface")
timeoutP = flag.Int("timeout", 60, "Timeout seconds")
noisy = flag.Bool("noise", false, "Enable noise appending")
encless = flag.Bool("encless", false, "Encryptionless mode")
var err error
log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
- govpn.MTU = *mtu
-
if *egdPath != "" {
log.Println("Using", *egdPath, "EGD")
govpn.EGDInit(*egdPath)
conf = &govpn.PeerConf{
Id: verifier.Id,
Iface: *ifaceName,
+ MTU: *mtu,
Timeout: time.Second * time.Duration(timeout),
Noise: *noisy,
CPR: *cpr,
idsCache = govpn.NewCipherCache([]govpn.PeerId{*verifier.Id})
log.Println(govpn.VersionGet())
- tap, err = govpn.TAPListen(*ifaceName)
+ tap, err = govpn.TAPListen(*ifaceName, *mtu)
if err != nil {
log.Fatalln("Can not listen on TAP interface:", err)
}
- log.Println("Max MTU on TAP interface:", govpn.TAPMaxMTU())
if *stats != "" {
log.Println("Stats are going to listen on", *stats)
statsPort, err := net.Listen("tcp", *stats)
func handleTCP(conn *net.TCPConn, timeouted, rehandshaking, termination chan struct{}) {
hs := govpn.HandshakeStart(*remoteAddr, conn, conf)
- buf := make([]byte, 2*(govpn.EncLessEnlargeSize+govpn.MTU)+govpn.MTU)
+ buf := make([]byte, 2*(govpn.EncLessEnlargeSize+*mtu)+*mtu)
var n int
var err error
var prev int
log.Println("Connected to UDP:" + *remoteAddr)
hs := govpn.HandshakeStart(*remoteAddr, conn, conf)
- buf := make([]byte, govpn.MTU)
+ buf := make([]byte, *mtu*2)
var n int
var timeouts int
var peer *govpn.Peer
if pc.EncLess {
pc.Noise = true
}
+ if pc.MTU == 0 {
+ pc.MTU = govpn.MTUDefault
+ }
conf := govpn.PeerConf{
Verifier: verifier,
Id: verifier.Id,
Name: name,
Iface: pc.Iface,
+ MTU: pc.MTU,
Up: pc.Up,
Down: pc.Down,
Noise: pc.Noise,
confPath = flag.String("conf", "peers.json", "Path to configuration JSON")
stats = flag.String("stats", "", "Enable stats retrieving on host:port")
proxy = flag.String("proxy", "", "Enable HTTP proxy on host:port")
- mtu = flag.Int("mtu", 1452, "MTU for outgoing packets")
egdPath = flag.String("egd", "", "Optional path to EGD socket")
)
log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
log.Println(govpn.VersionGet())
- govpn.MTU = *mtu
confInit()
knownPeers = govpn.KnownPeers(make(map[string]**govpn.Peer))
hsHeartbeat := time.Tick(timeout)
go func() { <-hsHeartbeat }()
- log.Println("Max MTU on TAP interface:", govpn.TAPMaxMTU())
if *stats != "" {
log.Println("Stats are going to listen on", *stats)
statsPort, err := net.Listen("tcp", *stats)
func handleTCP(conn net.Conn) {
addr := conn.RemoteAddr().String()
- buf := make([]byte, govpn.EncLessEnlargeSize+2*govpn.MTU)
+ buf := make([]byte, govpn.EncLessEnlargeSize+2*govpn.MTUMax)
var n int
var err error
var prev int
peer = nil
break
}
- tap, err = govpn.TAPListen(ifaceName)
+ tap, err = govpn.TAPListen(ifaceName, peer.MTU)
if err != nil {
log.Println("Unable to create TAP:", err)
peer = nil
}
log.Println("Listening on UDP:" + *bindAddr)
- udpBufs <- make([]byte, govpn.MTU)
+ udpBufs <- make([]byte, govpn.MTUMax)
go func() {
var buf []byte
var raddr *net.UDPAddr
hsLock.Unlock()
go func() {
- udpBufs <- make([]byte, govpn.MTU)
- udpBufs <- make([]byte, govpn.MTU)
+ udpBufs <- make([]byte, govpn.MTUMax)
+ udpBufs <- make([]byte, govpn.MTUMax)
}()
peersByIdLock.RLock()
addrPrev, exists = peersById[*peer.Id]
if err != nil {
return
}
- tap, err := govpn.TAPListen(ifaceName)
+ tap, err := govpn.TAPListen(ifaceName, peer.MTU)
if err != nil {
log.Println("Unable to create TAP:", err)
return
const (
TimeoutDefault = 60
+ MTUMax = 9000
+ MTUDefault = 1500 + 14
)
var (
- MTU int
Version string
)
if rate == 0 {
return time.Duration(0)
}
- return time.Second / time.Duration(rate*(1<<10)/MTU)
+ return time.Second / time.Duration(rate*(1<<10)/MTUMax)
}
Id *PeerId `json:"-"`
Name string `json:"name"`
Iface string `json:"iface"`
+ MTU int `json:"mtu"`
Up string `json:"up"`
Down string `json:"down"`
TimeoutInt int `json:"timeout"`
}
var enc []byte
if conf.Noise {
- enc = make([]byte, MTU-xtea.BlockSize-RSize)
+ enc = make([]byte, conf.MTU-xtea.BlockSize-RSize)
} else {
enc = make([]byte, 32)
}
func (h *Handshake) Server(data []byte) *Peer {
// R + ENC(H(DSAPub), R, El(CDHPub)) + IDtag
if h.rNonce == nil && ((!h.Conf.EncLess && len(data) >= 48) ||
- (h.Conf.EncLess && len(data) == EncLessEnlargeSize+MTU)) {
+ (h.Conf.EncLess && len(data) == EncLessEnlargeSize+h.Conf.MTU)) {
h.rNonce = new([RSize]byte)
copy(h.rNonce[:], data[:RSize])
var encPub []byte
var err error
if h.Conf.EncLess {
- encPub = make([]byte, MTU)
+ encPub = make([]byte, h.Conf.MTU)
copy(encPub, dhPubRepr[:])
encPub, err = EncLessEncode(h.dsaPubH, h.rNonceNext(1), encPub)
if err != nil {
}
var encRs []byte
if h.Conf.Noise && !h.Conf.EncLess {
- encRs = make([]byte, MTU-len(encPub)-xtea.BlockSize)
+ encRs = make([]byte, h.Conf.MTU-len(encPub)-xtea.BlockSize)
} else if h.Conf.EncLess {
- encRs = make([]byte, MTU-xtea.BlockSize)
+ encRs = make([]byte, h.Conf.MTU-xtea.BlockSize)
} else {
encRs = make([]byte, RSize+SSize)
}
} else
// ENC(K, R+1, RS + RC + SC + Sign(DSAPriv, K)) + IDtag
if h.rClient == nil && ((!h.Conf.EncLess && len(data) >= 120) ||
- (h.Conf.EncLess && len(data) == EncLessEnlargeSize+MTU)) {
+ (h.Conf.EncLess && len(data) == EncLessEnlargeSize+h.Conf.MTU)) {
var dec []byte
var err error
if h.Conf.EncLess {
// Send final answer to client
var enc []byte
if h.Conf.Noise {
- enc = make([]byte, MTU-xtea.BlockSize)
+ enc = make([]byte, h.Conf.MTU-xtea.BlockSize)
} else {
enc = make([]byte, RSize)
}
// ENC(H(DSAPub), R+1, El(SDHPub)) + ENC(K, R, RS + SS) + IDtag
if h.rServer == nil && h.key == nil &&
((!h.Conf.EncLess && len(data) >= 80) ||
- (h.Conf.EncLess && len(data) == 2*(EncLessEnlargeSize+MTU))) {
+ (h.Conf.EncLess && len(data) == 2*(EncLessEnlargeSize+h.Conf.MTU))) {
// Decrypt remote public key
sDHRepr := new([32]byte)
var tmp []byte
var enc []byte
if h.Conf.Noise {
- enc = make([]byte, MTU-xtea.BlockSize)
+ enc = make([]byte, h.Conf.MTU-xtea.BlockSize)
} else {
enc = make([]byte, RSize+RSize+SSize+ed25519.SignatureSize)
}
} else
// ENC(K, R+2, RC) + IDtag
if h.key != nil && ((!h.Conf.EncLess && len(data) >= 16) ||
- (h.Conf.EncLess && len(data) == EncLessEnlargeSize+MTU)) {
+ (h.Conf.EncLess && len(data) == EncLessEnlargeSize+h.Conf.MTU)) {
var err error
// Decrypt rClient
var dec []byte
"bytes"
"encoding/binary"
"io"
+ "log"
"sync"
"sync/atomic"
"time"
CPR int
CPRCycle time.Duration `json:"-"`
EncLess bool
+ MTU int
// Cryptography related
Key *[SSize]byte `json:"-"`
timeout = timeout / TimeoutHeartbeat
}
- bufSize := S20BS + MTU + NonceSize
+ bufSize := S20BS + 2*conf.MTU
if conf.EncLess {
bufSize += EncLessEnlargeSize
noiseEnable = true
CPR: conf.CPR,
CPRCycle: cprCycle,
EncLess: conf.EncLess,
+ MTU: conf.MTU,
Key: key,
NonceCipher: newNonceCipher(key),
// that he is free to receive new packets. Encrypted and authenticated
// packets will be sent to remote Peer side immediately.
func (p *Peer) EthProcess(data []byte) {
+ if len(data) > p.MTU-1 { // 1 is for padding byte
+ log.Println("Padded data packet size", len(data)+1, "is bigger than MTU", p.MTU, p)
+ return
+ }
p.now = time.Now()
p.BusyT.Lock()
}
if p.NoiseEnable && !p.EncLess {
- p.frameT = p.bufT[S20BS : S20BS+MTU-TagSize]
+ p.frameT = p.bufT[S20BS : S20BS+p.MTU-TagSize]
} else if p.EncLess {
- p.frameT = p.bufT[S20BS : S20BS+MTU]
+ p.frameT = p.bufT[S20BS : S20BS+p.MTU]
} else {
p.frameT = p.bufT[S20BS : S20BS+len(data)+1+NonceSize]
}
}
func init() {
- MTU = 1500
id := new([IDSize]byte)
peerId := PeerId(*id)
conf = &PeerConf{
Id: &peerId,
+ MTU: MTUDefault,
Timeout: time.Second * time.Duration(TimeoutDefault),
- Noise: false,
- CPR: 0,
}
peer = newPeer(true, "foo", Dummy{&ciphertext}, conf, new([SSize]byte))
plaintext = make([]byte, 789)
import (
"io"
-
- "golang.org/x/crypto/poly1305"
-)
-
-const (
- EtherSize = 14
)
type TAP struct {
taps = make(map[string]*TAP)
)
-// Return maximal acceptable TAP interface MTU. This is daemon's MTU
-// minus nonce, MAC, pad and Ethernet header sizes.
-func TAPMaxMTU() int {
- return MTU - poly1305.TagSize - NonceSize - 1 - EtherSize
-}
-
-func NewTAP(ifaceName string) (*TAP, error) {
- maxIfacePktSize := TAPMaxMTU() + EtherSize
+func NewTAP(ifaceName string, mtu int) (*TAP, error) {
tapRaw, err := newTAPer(ifaceName)
if err != nil {
return nil, err
tap := TAP{
Name: ifaceName,
dev: tapRaw,
- buf0: make([]byte, maxIfacePktSize),
- buf1: make([]byte, maxIfacePktSize),
+ buf0: make([]byte, mtu),
+ buf1: make([]byte, mtu),
Sink: make(chan []byte),
}
go func() {
return t.dev.Write(data)
}
-func TAPListen(ifaceName string) (*TAP, error) {
+func TAPListen(ifaceName string, mtu int) (*TAP, error) {
tap, exists := taps[ifaceName]
if exists {
return tap, nil
}
- tap, err := NewTAP(ifaceName)
+ tap, err := NewTAP(ifaceName, mtu)
if err != nil {
return nil, err
}