Skip to content

Commit

Permalink
Basic udp protocol implementation #41
Browse files Browse the repository at this point in the history
  • Loading branch information
illarion committed Nov 9, 2016
1 parent ade44a2 commit 1371196
Show file tree
Hide file tree
Showing 15 changed files with 1,005 additions and 328 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -96,6 +96,7 @@ MIT. See LICENSE file for more details.
- [Yaroslav Pogrebnyak](http://pogrebnyak.info)
- [Nick Doikov](https://github.com/nickdoikov)
- [Ievgen Ponomarenko](https://github.com/kikom)
- [Illarion Kovalchuk](https://github.com/illarion)

## Logo
Logo by [Max Demchenko](https://www.linkedin.com/in/max-demchenko-116170112)
38 changes: 35 additions & 3 deletions config/gobetween.toml
Expand Up @@ -59,6 +59,37 @@ bind = "localhost:3000"
"localhost:8001"
]

[servers.udpsample]
bind = "localhost:5443"
protocol = "udp"
udp_responses = 1
udp_session_timeout = "10m"

[servers.udpsample.access]
default = "allow"
rules = [
"allow 127.0.0.1",
"deny 192.168.0.1",
"allow 192.168.0.1/24",
]

[servers.udpsample.discovery]
kind = "static"
static_list = [
"8.8.8.8:53",
"8.8.4.4:53",
"91.239.100.100:53",
"1.1.1.1:54"
]

[servers.udpsample.healthcheck]
kind = "exec"
interval = "2s"
timeout = "5s"
exec_command = "./share/healthcheck/udp_dns.sh"
exec_expected_positive_output = "1"
exec_expected_negative_output = "0"


# -------------------- example ----------------------------- #

Expand All @@ -69,13 +100,15 @@ bind = "localhost:3000"
#[servers.default]
#
#bind = "localhost:3000" # (required) "<host>:<port>"
#protocol = "tcp" # (required) "tcp" | "tls"
#protocol = "tcp" # (required) "tcp" | "tls" | "udp"
#balance = "weight" # (optional [weight]) "weight" | "leastconn" | "roundrobin" | "iphash" | "leastbandwidth"
#
#max_connections = 0
#client_idle_timeout = "10m"
#backend_idle_timeout = "10m"
#backend_connection_timeout = "5s"
#udp_responses = 1 # (optional) for udp server - number of expected responses from backend.
#udp_session_timeout = "10m" # (optional) for udp server - udp "session" timeout.
#
#
## ---------------------- tls properties --------------------- #
Expand Down Expand Up @@ -108,15 +141,14 @@ bind = "localhost:3000"
# passes = 1 # (optional) successfull checks to mark backend as active
#
# # -- ping -- #
# kind = "ping"
# kind = "ping" # Unavailable if server.protocol is udp
#
# # -- exec -- #
# kind = "exec"
# exec_command = "/path/to/healthcheck.sh" # (required) command to execute
# exec_expected_positive_output = "1" # (required) expected output of command in case of success
# exec_expected_negative_output = "0" # (required) expected output of command in case of failure
#
#
## -------------------- discovery ---------------------------- #
#
# [servers.default.discovery] # (required)
Expand Down
10 changes: 10 additions & 0 deletions share/healthcheck/udp_dns.sh
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
#
# @author Illarion Kovalchuk <illarion.kovalchuk@gmail.com>
#
# Sample script for exec healthcheck of dns backends for udp protocol

host=$1
port=$2

dig @"$host" -p "$port" +time=1 > /dev/null 2>&1 ; [[ "$?" == "0" ]] && echo -n 1 || echo -n 0
3 changes: 2 additions & 1 deletion src/balance/iphash.go
Expand Up @@ -30,7 +30,8 @@ func (b *IphashBalancer) Elect(context *core.Context, backends []core.Backend) (
}

// TODO: Replace using byte IP addr instead of string
ip := context.Conn.RemoteAddr().String()

ip := (*context).String()

hash := 11
for c := range ip {
Expand Down
2 changes: 2 additions & 0 deletions src/config/config.go
Expand Up @@ -57,6 +57,8 @@ type ConnectionOptions struct {
ClientIdleTimeout *string `toml:"client_idle_timeout" json:"client_idle_timeout"`
BackendIdleTimeout *string `toml:"backend_idle_timeout" json:"backend_idle_timeout"`
BackendConnectionTimeout *string `toml:"backend_connection_timeout" json:"backend_connection_timeout"`
UdpResponses *int `toml:"udp_responses" json:"udp_responses"`
UdpSessionTimeout *string `toml:"udp_session_timeout" json:"udp_session_timeout"`
}

/**
Expand Down
27 changes: 25 additions & 2 deletions src/core/context.go
Expand Up @@ -10,13 +10,36 @@ import (
"net"
)

type Context interface {
String() string
}

/**
* Proxy context
* Proxy tcp context
*/
type Context struct {
type TcpContext struct {

/**
* Current client connection
*/
Conn net.Conn
}

func (t TcpContext) String() string {
return t.Conn.RemoteAddr().String()
}

/**
* Proxy udp context
*/
type UdpContext struct {

/**
* Current client remote address
*/
RemoteAddr net.UDPAddr
}

func (u UdpContext) String() string {
return u.RemoteAddr.String()
}
33 changes: 33 additions & 0 deletions src/core/server.go
@@ -0,0 +1,33 @@
/**
* server.go - server
*
* @author Illarion Kovalchuk
* @author Yaroslav Pogrebnyak <yyyaroslav@gmail.com>
*/

package core

import (
"../config"
)

/**
* Server interface
*/
type Server interface {

/**
* Start server
*/
Start() error

/**
* Stop server and wait until it stop
*/
Stop()

/**
* Get server configuration
*/
Cfg() config.Server
}
53 changes: 48 additions & 5 deletions src/manager/manager.go
Expand Up @@ -7,6 +7,7 @@ package manager

import (
"../config"
"../core"
"../logging"
"../server"
"../utils/codec"
Expand All @@ -18,8 +19,8 @@ import (
/* Map of app current servers */
var servers = struct {
sync.RWMutex
m map[string]*server.Server
}{m: make(map[string]*server.Server)}
m map[string]core.Server
}{m: make(map[string]core.Server)}

/* default configuration for server */
var defaults config.ConnectionOptions
Expand Down Expand Up @@ -125,9 +126,14 @@ func Create(name string, cfg config.Server) error {
if err != nil {
return err
}

if err = server.Start(); err != nil {
return err
}

servers.m[name] = server

return server.Start()
return nil
}

/**
Expand Down Expand Up @@ -229,11 +235,27 @@ func prepareConfig(name string, server config.Server, defaults config.Connection
return config.Server{}, errors.New("Need tls section for tls protocol")
}
fallthrough
case "tcp":
case "tcp", "udp":
default:
return config.Server{}, errors.New("Not supported protocol " + server.Protocol)
}

/* Healthcheck and protocol match */

if server.Healthcheck.Kind == "ping" && server.Protocol == "udp" {
return config.Server{}, errors.New("Cant use ping healthcheck with udp server")
}

/* Udp related options and protocol match */

if server.UdpResponses != nil && server.Protocol != "udp" {
return config.Server{}, errors.New("Cant use max_responses in non udp server")
}

if server.UdpSessionTimeout != nil && server.Protocol != "udp" {
return config.Server{}, errors.New("Cant use max_responses in non udp server")
}

/* Balance */
switch server.Balance {
case
Expand Down Expand Up @@ -286,7 +308,8 @@ func prepareConfig(name string, server config.Server, defaults config.Connection
defaults.MaxConnections = new(int)
}
if server.MaxConnections == nil {
server.MaxConnections = defaults.MaxConnections
server.MaxConnections = new(int)
*server.MaxConnections = *defaults.MaxConnections
}

if defaults.ClientIdleTimeout == nil {
Expand Down Expand Up @@ -316,5 +339,25 @@ func prepareConfig(name string, server config.Server, defaults config.Connection
*server.BackendConnectionTimeout = *defaults.BackendConnectionTimeout
}

if server.Protocol == "udp" {
if defaults.UdpResponses == nil {
defaults.UdpResponses = new(int)
*defaults.UdpResponses = 1
}
if server.UdpResponses == nil {
server.UdpResponses = new(int)
*server.UdpResponses = *defaults.UdpResponses
}

if defaults.UdpSessionTimeout == nil {
defaults.UdpSessionTimeout = new(string)
*defaults.UdpSessionTimeout = "10m"
}
if server.UdpSessionTimeout == nil {
server.UdpSessionTimeout = new(string)
*server.UdpSessionTimeout = *defaults.UdpSessionTimeout
}
}

return server, nil
}

0 comments on commit 1371196

Please sign in to comment.