mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-05 03:01:11 +00:00
171 lines
3.7 KiB
Go
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
|
|
}
|