Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ need to make some adjustments to the WireGuard client configuration:
* Use the address of the `udptlspipe` client as an endpoint in your WireGuard
client configuration.
* Add `MTU = 1280` to the `[Peer]` section of both WireGuard client and server
configuration files.
configuration files. The MTU can be increased by passing custom values for `--max-message-length`, `--min-message-length`, and `--max-padding-length`.
* Exclude the `udptlspipe` server IP from `AllowedIPs` in the WireGuard client
configuration. This [calculator][wireguardcalculator] may help you.

Expand Down Expand Up @@ -237,6 +237,9 @@ Application Options:
--tls-keyfile=<path-to-key-file> Path to the private key for the cert specified in tls-certfile.
--probe-reverseproxyurl=<hostname> Unauthorized requests and probes will be proxied to the URL.
-v, --verbose Verbose output (optional).
--max-message-length=<int length> Max message length (default 1320)
--min-message-length=<int length> Min message length (default 100)
--max-padding-length=<int length> Max padding length (default 256)

Help Options:
-h, --help Show this help message
Expand All @@ -248,4 +251,4 @@ Help Options:
* [X] Certificate configuration.
* [X] Use WebSocket for transport instead of the custom binary proto.
* [ ] Use several upstream connections instead of a single one.
* [ ] Automatic TLS certs generation (let's encrypt, lego).
* [ ] Automatic TLS certs generation (let's encrypt, lego).
15 changes: 15 additions & 0 deletions internal/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ func Main() {
log.SetLevel(log.DEBUG)
}

if o.MaxMessageLength == 0 {
o.MaxMessageLength = 1320
}

if o.MaxPaddingLength == 0 {
o.MaxPaddingLength = 256
}

if o.MinMessageLength == 0 {
o.MinMessageLength = 100
}

log.Info("Configuration:\n%s", o)

cfg := &pipe.Config{
Expand All @@ -54,6 +66,9 @@ func Main() {
VerifyCertificate: o.VerifyCertificate,
TLSServerName: o.TLSServerName,
ProbeReverseProxyURL: o.ProbeReverseProxyURL,
MaxMessageLength: o.MaxMessageLength,
MinMessageLength: o.MinMessageLength,
MaxPaddingLength: o.MaxPaddingLength,
}

if o.TLSCertPath != "" {
Expand Down
11 changes: 11 additions & 0 deletions internal/cmd/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,17 @@ type Options struct {

// Verbose defines whether we should write the DEBUG-level log or not.
Verbose bool `yaml:"verbose" short:"v" long:"verbose" description:"Verbose output (optional)." optional:"yes" optional-value:"true"`

// MaxMessageLength is the maximum length that is safe to use.
MaxMessageLength int `yaml:"max-message-length" long:"max-message-length" description:"Max message length (default 1320)" value-name:"<int length>" optional:"yes"`

// MinMessageLength is the minimum message size. If the message is smaller, it
// will be padded with random bytes.
MinMessageLength int `yaml:"min-message-length" long:"min-message-length" description:"Min message length (default 100)" value-name:"<int length>" optional:"yes"`

// MaxPaddingLength is the maximum size of a random padding that's added to
// every message.
MaxPaddingLength int `yaml:"max-padding-length" long:"max-padding-length" description:"Max padding length (default 256)" value-name:"<int length>" optional:"yes"`
}

// type check
Expand Down
29 changes: 27 additions & 2 deletions internal/pipe/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@ type Server struct {
// wg tracks active workers. Stop won't finish until there is at least
// won't finish until there's at least one active worker.
wg sync.WaitGroup

// maxMessageLength is the maximum length that is safe to use.
maxMessageLength int

// minMessageLength is the minimum message size. If the message is smaller, it
// will be padded with random bytes.
minMessageLength int

// maxPaddingLength is the maximum size of a random padding that's added to
// every message.
maxPaddingLength int
}

// Config represents the server configuration.
Expand Down Expand Up @@ -125,6 +136,17 @@ type Config struct {
// proxy to respond to unauthorized or proxy requests. If not specified,
// it will respond with a stub page 403 Forbidden.
ProbeReverseProxyURL string

// MaxMessageLength is the maximum length that is safe to use.
MaxMessageLength int

// MinMessageLength is the minimum message size. If the message is smaller, it
// will be padded with random bytes.
MinMessageLength int

// MaxPaddingLength is the maximum size of a random padding that's added to
// every message.
MaxPaddingLength int
}

// createTLSConfig creates a TLS configuration as per the server configuration.
Expand Down Expand Up @@ -175,6 +197,9 @@ func NewServer(config *Config) (s *Server, err error) {
srcConnsMu: &sync.Mutex{},
dstConns: map[net.Conn]struct{}{},
dstConnsMu: &sync.Mutex{},
maxMessageLength: config.MaxMessageLength,
minMessageLength: config.MinMessageLength,
maxPaddingLength: config.MaxPaddingLength,
}

s.tlsConfig, err = createTLSConfig(config)
Expand Down Expand Up @@ -649,9 +674,9 @@ func (s *Server) processConn(rwc io.ReadWriteCloser) {
// connection between them needs to be wrapped. In server mode it is the
// source connection, in client mode it is the destination connection.
if s.serverMode {
srcRw = tunnel.NewMsgReadWriter(srcRw)
srcRw = tunnel.NewMsgReadWriter(srcRw, s.maxMessageLength, s.minMessageLength, s.maxPaddingLength)
} else {
dstRw = tunnel.NewMsgReadWriter(dstRw)
dstRw = tunnel.NewMsgReadWriter(dstRw, s.maxMessageLength, s.minMessageLength, s.maxPaddingLength)
}

tunnel.Tunnel(s.String(), srcRw, dstRw)
Expand Down
45 changes: 22 additions & 23 deletions internal/tunnel/msgreadwriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,26 @@ import (
"github.com/AdguardTeam/golibs/log"
)

// MaxMessageLength is the maximum length that is safe to use.
// TODO(ameshkov): Make it configurable.
const MaxMessageLength = 1320

// MinMessageLength is the minimum message size. If the message is smaller, it
// will be padded with random bytes.
const MinMessageLength = 100

// MaxPaddingLength is the maximum size of a random padding that's added to
// every message.
const MaxPaddingLength = 256

// MsgReadWriter is a wrapper over io.ReadWriter that encodes messages written
// to and read from the base writer.
type MsgReadWriter struct {
base io.ReadWriter
// MaxMessageLength is the maximum length that is safe to use.
maxMessageLength int
// MaxPaddingLength is the maximum size of a random padding that's added to
// every message.
maxPaddingLength int
// MinMessageLength is the minimum message size. If the message is smaller, it
// will be padded with random bytes.
minMessageLength int
}

// NewMsgReadWriter creates a new instance of *MsgReadWriter.
func NewMsgReadWriter(base io.ReadWriter) (rw *MsgReadWriter) {
return &MsgReadWriter{base: base}
func NewMsgReadWriter(base io.ReadWriter, maxMessageLength, minMessageLength, maxPaddingLength int) (rw *MsgReadWriter) {
return &MsgReadWriter{
base: base,
maxMessageLength: maxMessageLength,
minMessageLength: minMessageLength,
maxPaddingLength: maxPaddingLength,
}
}

// type check
Expand All @@ -38,13 +37,13 @@ var _ io.ReadWriter = (*MsgReadWriter)(nil)
// Read implements the io.ReadWriter interface for *MsgReadWriter.
func (rw *MsgReadWriter) Read(b []byte) (n int, err error) {
// Read the main message (always goes first).
msg, err := readPrefixed(rw.base)
msg, err := rw.readPrefixed(rw.base)
if err != nil {
return 0, err
}

// Skip padding.
_, err = readPrefixed(rw.base)
_, err = rw.readPrefixed(rw.base)
if err != nil {
return 0, err
}
Expand All @@ -62,11 +61,11 @@ func (rw *MsgReadWriter) Read(b []byte) (n int, err error) {
func (rw *MsgReadWriter) Write(b []byte) (n int, err error) {
// Create random padding to make it harder to understand what's inside
// the tunnel.
minLength := MinMessageLength - len(b)
minLength := rw.minMessageLength - len(b)
if minLength <= 0 {
minLength = 1
}
maxLength := MaxPaddingLength
maxLength := rw.maxPaddingLength
if maxLength <= minLength {
maxLength = minLength + 1
}
Expand Down Expand Up @@ -103,19 +102,19 @@ func pack(b, padding []byte) (msg []byte) {
}

// readPrefixed reads a 2-byte prefixed byte array from the reader.
func readPrefixed(r io.Reader) (b []byte, err error) {
func (rw *MsgReadWriter) readPrefixed(r io.Reader) (b []byte, err error) {
var length uint16
err = binary.Read(r, binary.BigEndian, &length)
if err != nil {
return nil, err
}

if length > MaxMessageLength {
if length > uint16(rw.maxMessageLength) {
// Warn the user that this may not work correctly.
log.Error(
"Warning: received message of length %d larger than %d, considering reducing the MTU",
length,
MaxMessageLength,
rw.maxMessageLength,
)
}

Expand Down