Files
weave-scope/probe/endpoint/procspy/procnet.go
2015-12-09 11:06:04 +00:00

171 lines
3.7 KiB
Go

package procspy
import (
"bytes"
"net"
)
// ProcNet is an iterator to parse /proc/net/tcp{,6} files.
type ProcNet struct {
b []byte
c Connection
wantedState uint
bytesLocal, bytesRemote [16]byte
seen map[uint64]struct{}
}
// NewProcNet gives a new ProcNet parser.
func NewProcNet(b []byte, wantedState uint) *ProcNet {
return &ProcNet{
b: b,
c: Connection{},
wantedState: wantedState,
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
if p.b[2] == 's' {
// Skip header
p.b = nextLine(b)
goto again
}
var (
local, remote, state, inode []byte
)
_, b = nextField(b) // 'sl' column
local, b = nextField(b)
remote, b = nextField(b)
state, b = nextField(b)
if parseHex(state) != p.wantedState {
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
}