11package endpoint
22
33import (
4+ "bytes"
45 "fmt"
56 "regexp"
67 "strconv"
78 "sync"
9+ "syscall"
810
911 log "github.com/Sirupsen/logrus"
12+ "github.com/weaveworks/common/fs"
1013 "github.com/weaveworks/scope/probe/endpoint/procspy"
1114 "github.com/weaveworks/scope/probe/host"
15+ "github.com/weaveworks/scope/probe/process"
1216 "github.com/weaveworks/tcptracer-bpf/pkg/tracer"
1317)
1418
@@ -23,7 +27,7 @@ type ebpfConnection struct {
2327type eventTracker interface {
2428 handleConnection (ev tracer.EventType , tuple fourTuple , pid int , networkNamespace string )
2529 walkConnections (f func (ebpfConnection ))
26- feedInitialConnections (ci procspy.ConnIter , seenTuples map [string ]fourTuple , hostNodeID string )
30+ feedInitialConnections (ci procspy.ConnIter , seenTuples map [string ]fourTuple , processesWaitingInAccept [] int , hostNodeID string )
2731 isReadyToHandleConnections () bool
2832 isDead () bool
2933 stop ()
@@ -111,8 +115,12 @@ func tcpEventCbV4(e tracer.TcpV4) {
111115
112116 lastTimestampV4 = e .Timestamp
113117
114- tuple := fourTuple {e .SAddr .String (), e .DAddr .String (), e .SPort , e .DPort }
115- ebpfTracker .handleConnection (e .Type , tuple , int (e .Pid ), strconv .Itoa (int (e .NetNS )))
118+ if e .Type == tracer .EventFdInstall {
119+ ebpfTracker .handleFdInstall (e .Type , int (e .Pid ), int (e .Fd ))
120+ } else {
121+ tuple := fourTuple {e .SAddr .String (), e .DAddr .String (), e .SPort , e .DPort }
122+ ebpfTracker .handleConnection (e .Type , tuple , int (e .Pid ), strconv .Itoa (int (e .NetNS )))
123+ }
116124}
117125
118126func tcpEventCbV6 (e tracer.TcpV6 ) {
@@ -125,6 +133,84 @@ func lostCb(count uint64) {
125133 ebpfTracker .stop ()
126134}
127135
136+ func tupleFromPidFd (pid int , fd int ) (tuple fourTuple , netns string , ok bool ) {
137+ // read /proc/$pid/ns/net
138+ //
139+ // probe/endpoint/procspy/proc_linux.go supports Linux < 3.8 but we
140+ // don't need that here since ebpf-enabled kernels will be > 3.8
141+ netNamespacePath := fmt .Sprintf ("/proc/%d/ns/net" , pid )
142+ var statNsFile syscall.Stat_t
143+ err := fs .Stat (netNamespacePath , & statNsFile )
144+ if err != nil {
145+ log .Debugf ("proc file %q disappeared before we could read it" , netNamespacePath )
146+ return fourTuple {}, "" , false
147+ }
148+ netns = fmt .Sprintf ("%d" , statNsFile .Ino )
149+
150+ // find /proc/$pid/fd/$fd's ino
151+ fdFilename := fmt .Sprintf ("/proc/%d/fd/%d" , pid , fd )
152+ var statFdFile syscall.Stat_t
153+ err = fs .Stat (fdFilename , & statFdFile )
154+ if err != nil {
155+ log .Debugf ("proc file %q disappeared before we could read it" , fdFilename )
156+ return fourTuple {}, "" , false
157+ }
158+
159+ if statFdFile .Mode & syscall .S_IFMT != syscall .S_IFSOCK {
160+ log .Errorf ("file %q is not a socket" , fdFilename )
161+ return fourTuple {}, "" , false
162+ }
163+ ino := statFdFile .Ino
164+
165+ // read /proc/pid/net/tcp
166+ buf := bytes .NewBuffer (make ([]byte , 0 , 5000 ))
167+ tcpFilename := fmt .Sprintf ("/proc/%d/net/tcp" , pid )
168+ f , err := fs .Open (tcpFilename )
169+ if err != nil {
170+ log .Debugf ("proc file %q disappeared before we could read it" , tcpFilename )
171+ return fourTuple {}, "" , false
172+ }
173+ defer f .Close ()
174+ _ , err = buf .ReadFrom (f )
175+ if err != nil {
176+ log .Debugf ("proc file %q disappeared before we could read it" , tcpFilename )
177+ return fourTuple {}, "" , false
178+ }
179+
180+ // find /proc/$pid/fd/$fd's ino in /proc/pid/net/tcp
181+ pn := procspy .NewProcNet (buf .Bytes ())
182+ for {
183+ n := pn .Next ()
184+ if n == nil {
185+ log .Debugf ("connection for proc file %q disappeared before we could get it" , fdFilename )
186+ break
187+ }
188+ if n .Inode == ino {
189+ return fourTuple {n .LocalAddress .String (), n .RemoteAddress .String (), n .LocalPort , n .RemotePort }, netns , true
190+ }
191+ }
192+
193+ return fourTuple {}, "" , false
194+ }
195+
196+ func (t * EbpfTracker ) handleFdInstall (ev tracer.EventType , pid int , fd int ) {
197+ tuple , netns , ok := tupleFromPidFd (pid , fd )
198+ log .Debugf ("EbpfTracker: got fd-install event: pid=%d fd=%d -> tuple=%s netns=%s ok=%v" , pid , fd , tuple , netns , ok )
199+ if ! ok {
200+ return
201+ }
202+ conn := ebpfConnection {
203+ incoming : true ,
204+ tuple : tuple ,
205+ pid : pid ,
206+ networkNamespace : netns ,
207+ }
208+ t .openConnections [tuple .String ()] = conn
209+ if ! process .IsProcInAccept ("/proc" , strconv .Itoa (pid )) {
210+ t .tracer .RemoveFdInstallWatcher (uint32 (pid ))
211+ }
212+ }
213+
128214func (t * EbpfTracker ) handleConnection (ev tracer.EventType , tuple fourTuple , pid int , networkNamespace string ) {
129215 t .Lock ()
130216 defer t .Unlock ()
@@ -160,6 +246,8 @@ func (t *EbpfTracker) handleConnection(ev tracer.EventType, tuple fourTuple, pid
160246 } else {
161247 log .Debugf ("EbpfTracker: unmatched close event: %s pid=%d netns=%s" , tuple .String (), pid , networkNamespace )
162248 }
249+ default :
250+ log .Debugf ("EbpfTracker: unknown event: %s (%d)" , ev , ev )
163251 }
164252}
165253
@@ -178,7 +266,7 @@ func (t *EbpfTracker) walkConnections(f func(ebpfConnection)) {
178266 t .closedConnections = t .closedConnections [:0 ]
179267}
180268
181- func (t * EbpfTracker ) feedInitialConnections (conns procspy.ConnIter , seenTuples map [string ]fourTuple , hostNodeID string ) {
269+ func (t * EbpfTracker ) feedInitialConnections (conns procspy.ConnIter , seenTuples map [string ]fourTuple , processesWaitingInAccept [] int , hostNodeID string ) {
182270 t .readyToHandleConnections = true
183271 for conn := conns .Next (); conn != nil ; conn = conns .Next () {
184272 var (
@@ -204,6 +292,10 @@ func (t *EbpfTracker) feedInitialConnections(conns procspy.ConnIter, seenTuples
204292 t .handleConnection (tracer .EventAccept , tuple , int (conn .Proc .PID ), namespaceID )
205293 }
206294 }
295+ for _ , p := range processesWaitingInAccept {
296+ t .tracer .AddFdInstallWatcher (uint32 (p ))
297+ log .Debugf ("EbpfTracker: install fd-install watcher: pid=%d" , p )
298+ }
207299}
208300
209301func (t * EbpfTracker ) isReadyToHandleConnections () bool {
0 commit comments