Skip to content

Commit 4a7c5f5

Browse files
committed
ebpf: handle fd_install
1 parent c3a0432 commit 4a7c5f5

13 files changed

Lines changed: 181 additions & 46 deletions

probe/endpoint/connection_tracker.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,8 @@ func (t *connectionTracker) performWalkProc(rpt *report.Report, hostNodeID strin
171171
// once to initialize ebpfTracker
172172
func (t *connectionTracker) getInitialState() {
173173
var processCache *process.CachingWalker
174-
processCache = process.NewCachingWalker(process.NewWalker(t.conf.ProcRoot))
174+
walker := process.NewWalker(t.conf.ProcRoot, true)
175+
processCache = process.NewCachingWalker(walker)
175176
processCache.Tick()
176177

177178
scanner := procspy.NewSyncConnectionScanner(processCache)
@@ -194,7 +195,14 @@ func (t *connectionTracker) getInitialState() {
194195
}
195196
scanner.Stop()
196197

197-
t.ebpfTracker.feedInitialConnections(conns, seenTuples, report.MakeHostNodeID(t.conf.HostID))
198+
processesWaitingInAccept := []int{}
199+
processCache.Walk(func(p, prev process.Process) {
200+
if p.IsWaitingInAccept {
201+
processesWaitingInAccept = append(processesWaitingInAccept, p.PID)
202+
}
203+
})
204+
205+
t.ebpfTracker.feedInitialConnections(conns, seenTuples, processesWaitingInAccept, report.MakeHostNodeID(t.conf.HostID))
198206
}
199207

200208
func (t *connectionTracker) performEbpfTrack(rpt *report.Report, hostNodeID string) error {

probe/endpoint/ebpf.go

Lines changed: 96 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
package endpoint
22

33
import (
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 {
2327
type 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

118126
func 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+
128214
func (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

209301
func (t *EbpfTracker) isReadyToHandleConnections() bool {

probe/endpoint/procspy/proc_internal_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func TestWalkProcPid(t *testing.T) {
6262
defer fs_hook.Restore()
6363

6464
buf := bytes.Buffer{}
65-
walker := process.NewWalker(procRoot)
65+
walker := process.NewWalker(procRoot, false)
6666
ticker := time.NewTicker(time.Millisecond)
6767
defer ticker.Stop()
6868
pWalker := newPidWalker(walker, ticker.C, 1)

probe/endpoint/procspy/procnet.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,12 @@ again:
6363

6464
p.c.LocalAddress, p.c.LocalPort = scanAddressNA(local, &p.bytesLocal)
6565
p.c.RemoteAddress, p.c.RemotePort = scanAddressNA(remote, &p.bytesRemote)
66-
p.c.inode = parseDec(inode)
66+
p.c.Inode = parseDec(inode)
6767
p.b = nextLine(b)
68-
if _, alreadySeen := p.seen[p.c.inode]; alreadySeen {
68+
if _, alreadySeen := p.seen[p.c.Inode]; alreadySeen {
6969
goto again
7070
}
71-
p.seen[p.c.inode] = struct{}{}
71+
p.seen[p.c.Inode] = struct{}{}
7272
return &p.c
7373
}
7474

probe/endpoint/procspy/procnet_internal_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,28 @@ func TestProcNet(t *testing.T) {
2020
LocalPort: 0xa6c0,
2121
RemoteAddress: net.IP([]byte{0, 0, 0, 0}),
2222
RemotePort: 0x0,
23-
inode: 5107,
23+
Inode: 5107,
2424
},
2525
{
2626
LocalAddress: net.IP([]byte{0, 0, 0, 0}),
2727
LocalPort: 0x006f,
2828
RemoteAddress: net.IP([]byte{0, 0, 0, 0}),
2929
RemotePort: 0x0,
30-
inode: 5084,
30+
Inode: 5084,
3131
},
3232
{
3333
LocalAddress: net.IP([]byte{0x7f, 0x0, 0x0, 0x01}),
3434
LocalPort: 0x0019,
3535
RemoteAddress: net.IP([]byte{0, 0, 0, 0}),
3636
RemotePort: 0x0,
37-
inode: 10550,
37+
Inode: 10550,
3838
},
3939
{
4040
LocalAddress: net.IP([]byte{0x2e, 0xf6, 0x2c, 0xa1}),
4141
LocalPort: 0xe4d7,
4242
RemoteAddress: net.IP([]byte{0xc0, 0x1e, 0xfc, 0x57}),
4343
RemotePort: 0x01bb,
44-
inode: 639474,
44+
Inode: 639474,
4545
},
4646
}
4747
for i := 0; i < 4; i++ {
@@ -73,7 +73,7 @@ func TestTransport6(t *testing.T) {
7373
RemoteAddress: net.IP(make([]byte, 16)),
7474
RemotePort: 0x0,
7575
// uid: 0,
76-
inode: 23661201,
76+
Inode: 23661201,
7777
},
7878
{
7979
// state: 1,
@@ -92,7 +92,7 @@ func TestTransport6(t *testing.T) {
9292
}),
9393
RemotePort: 0x01bb,
9494
// uid: 1000,
95-
inode: 36856710,
95+
Inode: 36856710,
9696
},
9797
}
9898

@@ -148,7 +148,7 @@ func TestProcNetFiltersDuplicates(t *testing.T) {
148148
LocalPort: 0xa6c0,
149149
RemoteAddress: net.IP([]byte{0, 0, 0, 0}),
150150
RemotePort: 0x0,
151-
inode: 5107,
151+
Inode: 5107,
152152
}
153153
have := p.Next()
154154
want := expected

probe/endpoint/procspy/spy.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ type Connection struct {
2222
LocalPort uint16
2323
RemoteAddress net.IP
2424
RemotePort uint16
25-
inode uint64
25+
Inode uint64
2626
Proc Proc
2727
}
2828

probe/endpoint/procspy/spy_linux.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func (c *pnConnIter) Next() *Connection {
2626
bufPool.Put(c.buf)
2727
return nil
2828
}
29-
if proc, ok := c.procs[n.inode]; ok {
29+
if proc, ok := c.procs[n.Inode]; ok {
3030
n.Proc = *proc
3131
}
3232
return n

probe/endpoint/procspy/spy_linux_internal_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
func TestLinuxConnections(t *testing.T) {
1515
fs_hook.Mock(mockFS)
1616
defer fs_hook.Restore()
17-
scanner := NewConnectionScanner(process.NewWalker("/proc"))
17+
scanner := NewConnectionScanner(process.NewWalker("/proc", false))
1818
defer scanner.Stop()
1919

2020
// let the background scanner finish its first pass
@@ -30,7 +30,7 @@ func TestLinuxConnections(t *testing.T) {
3030
LocalPort: 42688,
3131
RemoteAddress: net.ParseIP("0.0.0.0").To4(),
3232
RemotePort: 0,
33-
inode: 5107,
33+
Inode: 5107,
3434
Proc: Proc{
3535
PID: 1,
3636
Name: "foo",

probe/process/walker.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@ import "sync"
44

55
// Process represents a single process.
66
type Process struct {
7-
PID, PPID int
8-
Name string
9-
Cmdline string
10-
Threads int
11-
Jiffies uint64
12-
RSSBytes uint64
13-
RSSBytesLimit uint64
14-
OpenFilesCount int
15-
OpenFilesLimit uint64
7+
PID, PPID int
8+
Name string
9+
Cmdline string
10+
Threads int
11+
Jiffies uint64
12+
RSSBytes uint64
13+
RSSBytesLimit uint64
14+
OpenFilesCount int
15+
OpenFilesLimit uint64
16+
IsWaitingInAccept bool
1617
}
1718

1819
// Walker is something that walks the /proc directory

0 commit comments

Comments
 (0)