github.com/fafucoder/cilium@v1.6.11/proxylib/test/accesslog_server.go (about) 1 // Copyright 2017-2018 Authors of Cilium 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 test 16 17 import ( 18 "io" 19 "net" 20 "os" 21 "path/filepath" 22 "strings" 23 "sync/atomic" 24 "syscall" 25 "time" 26 27 "github.com/cilium/proxy/go/cilium/api" 28 "github.com/golang/protobuf/proto" 29 log "github.com/sirupsen/logrus" 30 ) 31 32 type AccessLogServer struct { 33 Path string 34 Logs chan cilium.LogEntry 35 closing uint32 // non-zero if closing, accessed atomically 36 listener *net.UnixListener 37 conns []*net.UnixConn 38 } 39 40 // Close removes the unix domain socket from the filesystem 41 func (s *AccessLogServer) Close() { 42 if s != nil { 43 atomic.StoreUint32(&s.closing, 1) 44 s.listener.Close() 45 for _, conn := range s.conns { 46 conn.Close() 47 } 48 os.Remove(s.Path) 49 } 50 } 51 52 // Clear empties the access log server buffer, counting the passes and drops 53 func (s *AccessLogServer) Clear() (passed, drops int) { 54 passes, drops := 0, 0 55 empty := false 56 for !empty { 57 select { 58 case pblog := <-s.Logs: 59 if pblog.EntryType == cilium.EntryType_Denied { 60 drops++ 61 } else { 62 passes++ 63 } 64 case <-time.After(10 * time.Millisecond): 65 empty = true 66 } 67 } 68 return passes, drops 69 } 70 71 // StartAccessLogServer starts the access log server. 72 func StartAccessLogServer(accessLogName string, bufSize int) *AccessLogServer { 73 accessLogPath := filepath.Join(Tmpdir, accessLogName) 74 75 server := &AccessLogServer{ 76 Path: accessLogPath, 77 Logs: make(chan cilium.LogEntry, bufSize), 78 } 79 80 // Create the access log listener 81 os.Remove(accessLogPath) // Remove/Unlink the old unix domain socket, if any. 82 var err error 83 server.listener, err = net.ListenUnix("unixpacket", &net.UnixAddr{Name: accessLogPath, Net: "unixpacket"}) 84 if err != nil { 85 log.Fatalf("Failed to open access log listen socket at %s: %v", accessLogPath, err) 86 } 87 server.listener.SetUnlinkOnClose(true) 88 89 // Make the socket accessible by non-root Envoy proxies, e.g. running in 90 // sidecar containers. 91 if err = os.Chmod(accessLogPath, 0777); err != nil { 92 log.Fatalf("Failed to change mode of access log listen socket at %s: %v", accessLogPath, err) 93 } 94 95 log.Debug("Starting Access Log Server") 96 go func() { 97 for { 98 // Each Envoy listener opens a new connection over the Unix domain socket. 99 // Multiple worker threads serving the listener share that same connection 100 uc, err := server.listener.AcceptUnix() 101 if err != nil { 102 // These errors are expected when we are closing down 103 if atomic.LoadUint32(&server.closing) != 0 || 104 strings.Contains(err.Error(), "closed network connection") || 105 strings.Contains(err.Error(), "invalid argument") { 106 break 107 } 108 log.WithError(err).Warn("Failed to accept access log connection") 109 continue 110 } 111 log.Debug("Accepted access log connection") 112 113 server.conns = append(server.conns, uc) 114 // Serve this access log socket in a goroutine, so we can serve multiple 115 // connections concurrently. 116 go server.accessLogger(uc) 117 } 118 }() 119 120 return server 121 } 122 123 // isEOF returns true if the error message ends in "EOF". ReadMsgUnix returns extra info in the beginning. 124 func isEOF(err error) bool { 125 strerr := err.Error() 126 errlen := len(strerr) 127 return errlen >= 3 && strerr[errlen-3:] == io.EOF.Error() 128 } 129 130 func (s *AccessLogServer) accessLogger(conn *net.UnixConn) { 131 defer func() { 132 log.Debug("Closing access log connection") 133 conn.Close() 134 }() 135 136 buf := make([]byte, 4096) 137 for { 138 n, _, flags, _, err := conn.ReadMsgUnix(buf, nil) 139 if err != nil { 140 if !isEOF(err) && atomic.LoadUint32(&s.closing) == 0 { 141 log.WithError(err).Error("Error while reading from access log connection") 142 } 143 break 144 } 145 if flags&syscall.MSG_TRUNC != 0 { 146 log.Warning("Discarded truncated access log message") 147 continue 148 } 149 pblog := cilium.LogEntry{} 150 err = proto.Unmarshal(buf[:n], &pblog) 151 if err != nil { 152 log.WithError(err).Warning("Discarded invalid access log message") 153 continue 154 } 155 156 log.Debugf("Access log message: %s", pblog.String()) 157 s.Logs <- pblog 158 } 159 }