Permalink
Browse files

Work to dump listening ports by netlink

  • Loading branch information...
yuuki committed Jun 16, 2018
1 parent 49120e2 commit 40787db495d83dad44829cba1dbf79e5d4d80fce
Showing with 167 additions and 2 deletions.
  1. +108 −0 netutil/netlink_linux.go
  2. +55 −0 netutil/netutil_linux.go
  3. +4 −2 tcpflow/tcpflow.go
@@ -0,0 +1,108 @@
// +build linux
package netutil
import (
"bytes"
"encoding/binary"
"fmt"
"unsafe"
"github.com/pkg/errors"
"github.com/vishvananda/netlink/nl"
)
const (
// TCPF_ALL is a flag to request all sockets in any TCP state.
TCPF_ALL = ^uint32(0)
// TCPDIAG_GETSOCK is the netlink message type for requesting TCP diag data.
// https://github.com/torvalds/linux/blob/v4.0/include/uapi/linux/inet_diag.h#L7
TCPDIAG_GETSOCK = 18
)
const (
// https://github.com/torvalds/linux/blob/5924bbecd0267d87c24110cbe2041b5075173a25/include/net/tcp_states.h#L16
TCP_ESTABLISHED uint8 = iota + 1
TCP_SYN_SENT
TCP_SYN_RECV
TCP_FIN_WAIT1
TCP_FIN_WAIT2
TCP_TIME_WAIT
TCP_CLOSE
TCP_CLOSE_WAIT
TCP_LAST_ACK
TCP_LISTEN
TCP_CLOSING
)
var (
native = nl.NativeEndian()
networkOrder = binary.BigEndian
)
// inetDiagSockID contains the socket identity.
// https://github.com/torvalds/linux/blob/v4.0/include/uapi/linux/inet_diag.h#L13
type inetDiagSockID struct {
SrcPort [2]byte // Source port (big-endian).
DstPort [2]byte // Destination port (big-endian).
SrcIP [16]byte // Source IP
DstIP [16]byte // Destination IP
Interface uint32
Cookie [2]uint32
}
var sizeofInetDiagMsg = int(unsafe.Sizeof(inetDiagMsg{}))
// inetDiagMsg contains a socket identifier and netstat information.
type inetDiagMsg struct {
Family uint8 // Address family
State uint8 // TCP state
Timer uint8
Retrans uint8
ID inetDiagSockID
Expires uint32
RQueue uint32
WQueue uint32
UID uint32
INode uint32
}
var sizeofInetDiagReq = int(unsafe.Sizeof(inetDiagReq{}))
// inetDiagReq represents request diagnostic data.
// https://github.com/torvalds/linux/blob/v4.0/include/uapi/linux/inet_diag.h#L37
type inetDiagReq struct {
Family uint8
SrcLen uint8
DstLen uint8
Ext uint8
ID inetDiagSockID
States uint32 // States to dump.
DBs uint32 // Tables to dump.
}
func (r *inetDiagReq) Serialize() []byte {
buf := bytes.NewBuffer(make([]byte, sizeofInetDiagReq))
buf.Reset()
if err := binary.Write(buf, native, r); err != nil {
// This never returns an error.
panic(err)
}
return buf.Bytes()
}
func (r *inetDiagReq) Len() int { return sizeofInetDiagReq }
func deserializeInetDiag(b []byte) (*inetDiagMsg, error) {
if len(b) < sizeofInetDiagMsg {
return nil, fmt.Errorf("socket data short read (%d); want %d", len(b), sizeofInetDiagMsg)
}
r := bytes.NewReader(b)
inetDiagMsg := &inetDiagMsg{}
err := binary.Read(r, native, inetDiagMsg)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal inet_diag_msg")
}
return inetDiagMsg, nil
}
@@ -1,3 +1,58 @@
// +build linux
package netutil
import (
"encoding/binary"
"errors"
"fmt"
"syscall"
"github.com/vishvananda/netlink/nl"
)
// LodalListeningPorts returns the local listening ports.
func LocalListeningPorts() ([]string, error) {
s, err := nl.Subscribe(syscall.NETLINK_INET_DIAG)
if err != nil {
return nil, err
}
defer s.Close()
req := nl.NewNetlinkRequest(TCPDIAG_GETSOCK, syscall.NLM_F_REQUEST|syscall.NLM_F_DUMP)
req.AddData(&inetDiagReq{
Family: syscall.AF_INET,
States: 1 << TCP_LISTEN,
})
s.Send(req)
msgs, err := s.Receive()
if err != nil {
return nil, err
}
if len(msgs) == 0 {
return nil, errors.New("no message nor error from netlink")
}
var diagMsgs []*inetDiagMsg
done:
for _, msg := range msgs {
if msg.Header.Type == syscall.NLMSG_DONE {
break done
}
if msg.Header.Type == syscall.NLMSG_ERROR {
errval := native.Uint32(msg.Data[:4])
return nil, fmt.Errorf("netlink error: %d", -errval)
}
diagMsg, err := deserializeInetDiag(msg.Data)
if err != nil {
return nil, err
}
diagMsgs = append(diagMsgs, diagMsg)
}
ports := make([]string, 0, len(diagMsgs))
for _, diagMsg := range diagMsgs {
ports = append(ports,
fmt.Sprintf("%d", binary.BigEndian.Uint16(diagMsg.ID.SrcPort[0:2])))
}
return ports, nil
}
@@ -3,6 +3,7 @@ package tcpflow
import (
"encoding/json"
"fmt"
"log"
"net"
"strconv"
@@ -112,11 +113,12 @@ func (hf HostFlows) insert(flow *HostFlow) {
// GetHostFlows reads /proc/net/tcp and wraps it as HostFlows.
func GetHostFlows() (HostFlows, error) {
conns, err := gnet.Connections("tcp")
ports, err := netutil.LocalListeningPorts()
if err != nil {
return nil, err
}
ports, err := netutil.FilterByLocalListeningPorts(conns)
log.Println(ports)
conns, err := gnet.Connections("tcp")
if err != nil {
return nil, err
}

0 comments on commit 40787db

Please sign in to comment.