agones.dev/agones@v1.54.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 }