Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement interval jitter #109

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ Usage of ./endlessh-go
Supplier to obtain Geohash of IPs. Possible values are "off", "ip-api", "max-mind-db" (default "off")
-host string
SSH listening address (default "0.0.0.0")
-interval_jitter int
Interval jitter as a percentage. Set to 0 to disable. (default 0)
-interval_ms int
Message millisecond delay (default 1000)
-line_length int
Expand Down
15 changes: 13 additions & 2 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@ type Client struct {
start time.Time
last time.Time
interval time.Duration
jitter int64
bytesSent int
}

func NewClient(conn net.Conn, interval time.Duration, maxClients int64) *Client {
func NewClient(conn net.Conn, interval time.Duration, jitter int64, maxClients int64) *Client {
for numCurrentClients >= maxClients {
time.Sleep(interval)
}
Expand All @@ -62,6 +63,7 @@ func NewClient(conn net.Conn, interval time.Duration, maxClients int64) *Client
start: time.Now(),
last: time.Now(),
interval: interval,
jitter: jitter,
bytesSent: 0,
}
}
Expand All @@ -78,7 +80,7 @@ func (c *Client) Send(bannerMaxLength int64) (int, error) {
if time.Now().Before(c.next) {
time.Sleep(c.next.Sub(time.Now()))
}
c.next = time.Now().Add(c.interval)
c.next = time.Now().Add(c.Interval())
length := rand.Int63n(bannerMaxLength)
bytesSent, err := c.conn.Write(randStringBytes(length))
if err != nil {
Expand All @@ -88,6 +90,15 @@ func (c *Client) Send(bannerMaxLength int64) (int, error) {
return bytesSent, nil
}

func (c *Client) Interval() time.Duration {
if c.jitter != 0 {
// generate an integer offset in the interval [-jitter, +jitter]
offset := time.Duration(rand.Int63n(2*c.jitter+1)-c.jitter) * time.Millisecond
return c.interval + offset
}
return c.interval
}

func (c *Client) MillisecondsSinceLast() int64 {
millisecondsSpent := time.Now().Sub(c.last).Milliseconds()
c.last = time.Now()
Expand Down
14 changes: 11 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func startSending(maxClients int64, bannerMaxLength int64, records chan<- metric
return clients
}

func startAccepting(maxClients int64, connType, connHost, connPort string, interval time.Duration, clients chan<- *client.Client, records chan<- metrics.RecordEntry) {
func startAccepting(maxClients int64, connType, connHost, connPort string, interval time.Duration, jitter int64, clients chan<- *client.Client, records chan<- metrics.RecordEntry) {
go func() {
l, err := net.Listen(connType, connHost+":"+connPort)
if err != nil {
Expand All @@ -84,7 +84,7 @@ func startAccepting(maxClients int64, connType, connHost, connPort string, inter
glog.Errorf("Error accepting connection from port %v: %v", connPort, err)
os.Exit(1)
}
c := client.NewClient(conn, interval, maxClients)
c := client.NewClient(conn, interval, jitter, maxClients)
remoteIpAddr := c.RemoteIpAddr()
records <- metrics.RecordEntry{
RecordType: metrics.RecordEntryTypeStart,
Expand Down Expand Up @@ -113,6 +113,7 @@ var connPorts arrayStrings

func main() {
intervalMs := flag.Int("interval_ms", 1000, "Message millisecond delay")
intervalJitter := flag.Int64("interval_jitter", 0, "Interval jitter as a percentage. Set to 0 to disable. (default 0)")
bannerMaxLength := flag.Int64("line_length", 32, "Maximum banner line length")
maxClients := flag.Int64("max_clients", 4096, "Maximum number of clients")
connType := flag.String("conn_type", "tcp", "Connection type. Possible values are tcp, tcp4, tcp6")
Expand Down Expand Up @@ -154,8 +155,15 @@ func main() {
if len(connPorts) == 0 {
connPorts = append(connPorts, defaultPort)
}

if *intervalJitter < 0 || *intervalJitter > 100 {
glog.Errorf("Error: interval_jitter must be a number between 0 and 100. Got: %v", *intervalJitter)
os.Exit(1)
}
// use float to compute the jitter limit to avoid overflow
jitter := int64(float64(*intervalMs) * (float64(*intervalJitter) / 100))
for _, connPort := range connPorts {
startAccepting(*maxClients, *connType, *connHost, connPort, interval, clients, records)
startAccepting(*maxClients, *connType, *connHost, connPort, interval, jitter, clients, records)
}
for {
if *prometheusCleanUnseenSeconds <= 0 {
Expand Down