Files
weave-scope/probe/endpoint/procspy/procnet.go
Alban Crequy d715ccc391 ebpf: handle fd_install events from tcptracer-bpf
Since https://github.com/weaveworks/tcptracer-bpf/pull/39, tcptracer-bpf
can generate "fd_install" events when a process installs a new file
descriptor in its fd table. Those events must be requested explicitely
on a per-pid basis with tracer.AddFdInstallWatcher(pid).

This is useful to know about "accept" events that would otherwise be
missed because kretprobes are not triggered for functions that were
called before the installation of the kretprobe.

This patch find all the processes that are currently blocked on an
accept() syscall during the EbpfTracker initialization.
feedInitialConnections() will use tracer.AddFdInstallWatcher() to
subscribe to fd_install  events. When a fd_install event is received,
synthesise an accept event with the connection tuple and the network
namespace (from /proc).
2017-05-19 14:49:38 +02:00

175 lines
3.8 KiB
Go

package procspy
import (
"bytes"
"net"
)
// Used to check whether we are parsing a header line
var slHeader = []byte("sl")
// ProcNet is an iterator to parse /proc/net/tcp{,6} files.
type ProcNet struct {
b []byte
c Connection
bytesLocal, bytesRemote [16]byte
seen map[uint64]struct{}
}
// NewProcNet gives a new ProcNet parser.
func NewProcNet(b []byte) *ProcNet {
return &ProcNet{
b: b,
c: Connection{},
seen: map[uint64]struct{}{},
}
}
// Next returns the next connection. All buffers are re-used, so if you want
// to keep the IPs you have to copy them.
func (p *ProcNet) Next() *Connection {
again:
if len(p.b) == 0 {
return nil
}
b := p.b
var (
sl, local, remote, state, inode []byte
)
sl, b = nextField(b) // 'sl' column
if bytes.Equal(sl, slHeader) {
// Skip header
p.b = nextLine(b)
goto again
}
local, b = nextField(b)
remote, b = nextField(b)
state, b = nextField(b)
switch parseHex(state) {
// Only process established or half-closed connections
case tcpEstablished, tcpFinWait1, tcpFinWait2, tcpCloseWait:
default:
p.b = nextLine(b)
goto again
}
_, b = nextField(b) // 'tx_queue' column
_, b = nextField(b) // 'rx_queue' column
_, b = nextField(b) // 'tr' column
_, b = nextField(b) // 'uid' column
_, b = nextField(b) // 'timeout' column
inode, b = nextField(b)
p.c.LocalAddress, p.c.LocalPort = scanAddressNA(local, &p.bytesLocal)
p.c.RemoteAddress, p.c.RemotePort = scanAddressNA(remote, &p.bytesRemote)
p.c.Inode = parseDec(inode)
p.b = nextLine(b)
if _, alreadySeen := p.seen[p.c.Inode]; alreadySeen {
goto again
}
p.seen[p.c.Inode] = struct{}{}
return &p.c
}
// scanAddressNA parses 'A12CF62E:00AA' to the address/port. Handles IPv4 and
// IPv6 addresses. The address is a big endian 32 bit ints, hex encoded. We
// just decode the hex and flip the bytes in every group of 4.
func scanAddressNA(in []byte, buf *[16]byte) (net.IP, uint16) {
col := bytes.IndexByte(in, ':')
if col == -1 {
return nil, 0
}
// Network address is big endian. Can be either ipv4 or ipv6.
address := hexDecode32bigNA(in[:col], buf)
return net.IP(address), uint16(parseHex(in[col+1:]))
}
// hexDecode32big decodes sequences of 32bit big endian bytes.
func hexDecode32bigNA(src []byte, buf *[16]byte) []byte {
blocks := len(src) / 8
for block := 0; block < blocks; block++ {
for i := 0; i < 4; i++ {
a := fromHexChar(src[block*8+i*2])
b := fromHexChar(src[block*8+i*2+1])
buf[block*4+3-i] = (a << 4) | b
}
}
return buf[:blocks*4]
}
func nextField(s []byte) ([]byte, []byte) {
// Skip whitespace.
for i, b := range s {
if b != ' ' {
s = s[i:]
break
}
}
// Up until the next whitespace field.
for i, b := range s {
if b == ' ' {
return s[:i], s[i:]
}
}
return nil, nil
}
func nextLine(s []byte) []byte {
i := bytes.IndexByte(s, '\n')
if i == -1 {
return nil
}
return s[i+1:]
}
// Simplified copy of strconv.ParseUint(16).
func parseHex(s []byte) uint {
n := uint(0)
for i := 0; i < len(s); i++ {
n *= 16
n += uint(fromHexChar(s[i]))
}
return n
}
// Simplified copy of strconv.ParseUint(10).
func parseDec(s []byte) uint64 {
n := uint64(0)
for _, c := range s {
n *= 10
n += uint64(c - '0')
}
return n
}
// hexDecode32big decodes sequences of 32bit big endian bytes.
func hexDecode32big(src []byte) []byte {
dst := make([]byte, len(src)/2)
blocks := len(src) / 8
for block := 0; block < blocks; block++ {
for i := 0; i < 4; i++ {
a := fromHexChar(src[block*8+i*2])
b := fromHexChar(src[block*8+i*2+1])
dst[block*4+3-i] = (a << 4) | b
}
}
return dst
}
// fromHexChar converts a hex character into its value.
func fromHexChar(c byte) uint8 {
switch {
case '0' <= c && c <= '9':
return c - '0'
case 'a' <= c && c <= 'f':
return c - 'a' + 10
case 'A' <= c && c <= 'F':
return c - 'A' + 10
}
return 0
}