agones.dev/agones@v1.53.0/cmd/ping/udp.go (about)

     1  // Copyright 2018 Google LLC All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package main
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"math"
    21  	"net"
    22  	"os"
    23  	"sync"
    24  	"time"
    25  
    26  	"agones.dev/agones/pkg/util/runtime"
    27  
    28  	"github.com/pkg/errors"
    29  
    30  	"github.com/sirupsen/logrus"
    31  	"golang.org/x/time/rate"
    32  	"k8s.io/apimachinery/pkg/util/wait"
    33  	"k8s.io/utils/clock"
    34  )
    35  
    36  // udpServer is a rate limited udp server that echos
    37  // udp packets back to senders
    38  type udpServer struct {
    39  	logger      *logrus.Entry
    40  	conn        net.PacketConn
    41  	rateLimit   rate.Limit
    42  	rateBurst   int
    43  	clock       clock.WithTickerAndDelayedExecution
    44  	limitsMutex sync.Mutex
    45  	limits      map[string]*visitor
    46  	healthMutex sync.RWMutex
    47  	health      bool
    48  }
    49  
    50  // visitor tracks when a visitor last sent
    51  // a packet, and it's rate limit
    52  type visitor struct {
    53  	stamp time.Time
    54  	limit *rate.Limiter
    55  }
    56  
    57  // newUDPServer returns a new udpServer implementation
    58  // withe the rate limit
    59  func newUDPServer(rateLimit rate.Limit) *udpServer {
    60  	udpSrv := &udpServer{
    61  		rateLimit:   rateLimit,
    62  		rateBurst:   int(math.Floor(float64(rateLimit))),
    63  		clock:       clock.RealClock{},
    64  		limitsMutex: sync.Mutex{},
    65  		limits:      map[string]*visitor{},
    66  	}
    67  	udpSrv.logger = runtime.NewLoggerWithType(udpSrv)
    68  	return udpSrv
    69  }
    70  
    71  // run runs the udp server. Non blocking operation
    72  func (u *udpServer) run(ctx context.Context) {
    73  	u.healthy()
    74  
    75  	logger.Info("Starting UDP server")
    76  	var err error
    77  	u.conn, err = net.ListenPacket("udp", ":8080")
    78  	if err != nil {
    79  		logger.WithError(err).Fatal("Could not start udp server")
    80  	}
    81  
    82  	go func() {
    83  		defer u.unhealthy()
    84  		wait.Until(u.cleanUp, time.Minute, ctx.Done())
    85  	}()
    86  
    87  	u.readWriteLoop(ctx)
    88  }
    89  
    90  // cleans up visitors, if they are more than a
    91  // minute without being touched
    92  func (u *udpServer) cleanUp() {
    93  	u.limitsMutex.Lock()
    94  	defer u.limitsMutex.Unlock()
    95  	for k, v := range u.limits {
    96  		if u.clock.Now().Sub(v.stamp) > time.Minute {
    97  			delete(u.limits, k)
    98  		}
    99  	}
   100  }
   101  
   102  // readWriteLoop reads the UDP packet in, and then echos the data back
   103  // in a rate limited way
   104  func (u *udpServer) readWriteLoop(ctx context.Context) {
   105  	go func() {
   106  		defer u.unhealthy()
   107  		for {
   108  			select {
   109  			case <-ctx.Done():
   110  				return
   111  			default:
   112  				b := make([]byte, 1024)
   113  				_, sender, err := u.conn.ReadFrom(b)
   114  				if err != nil {
   115  					if ctx.Err() != nil && err == os.ErrClosed {
   116  						return
   117  					}
   118  					u.logger.WithError(err).Error("Error reading udp packet")
   119  					continue
   120  				}
   121  				go u.rateLimitedEchoResponse(b, sender)
   122  			}
   123  		}
   124  	}()
   125  }
   126  
   127  // rateLimitedEchoResponse echos the udp request, but is ignored if
   128  // it is past its rate limit
   129  func (u *udpServer) rateLimitedEchoResponse(b []byte, sender net.Addr) {
   130  	var vis *visitor
   131  	u.limitsMutex.Lock()
   132  	key := sender.String()
   133  	vis, ok := u.limits[key]
   134  	if !ok {
   135  		vis = &visitor{limit: rate.NewLimiter(u.rateLimit, u.rateBurst)}
   136  		u.limits[key] = vis
   137  	}
   138  	vis.stamp = u.clock.Now()
   139  	u.limitsMutex.Unlock()
   140  
   141  	if vis.limit.Allow() {
   142  		b = bytes.TrimRight(b, "\x00")
   143  		if _, err := u.conn.WriteTo(b, sender); err != nil {
   144  			u.logger.WithError(err).Error("Error sending returning udp packet")
   145  		}
   146  	} else {
   147  		logger.WithField("addr", sender.String()).Warn("Rate limited. No response sent")
   148  	}
   149  }
   150  
   151  // close closes and shutdown the udp server
   152  func (u *udpServer) close() {
   153  	if err := u.conn.Close(); err != nil {
   154  		logger.WithError(err).Error("Error closing udp connection")
   155  	}
   156  }
   157  
   158  // healthy marks this udpServer as healthy
   159  func (u *udpServer) healthy() {
   160  	u.healthMutex.Lock()
   161  	defer u.healthMutex.Unlock()
   162  	u.health = true
   163  }
   164  
   165  // unhealthy marks this udpServer as unhealthy
   166  func (u *udpServer) unhealthy() {
   167  	u.healthMutex.Lock()
   168  	defer u.healthMutex.Unlock()
   169  	u.health = false
   170  }
   171  
   172  // Health returns the health of the UDP Server.
   173  // true is healthy, false is not
   174  func (u *udpServer) Health() error {
   175  	u.healthMutex.RLock()
   176  	defer u.healthMutex.RUnlock()
   177  	if !u.health {
   178  		return errors.New("UDP Server is unhealthy")
   179  	}
   180  	return nil
   181  }