mirror of
https://github.com/vishvananda/netlink.git
synced 2025-09-26 20:01:13 +08:00
Implement UDP socket diagnostics
Signed-off-by: Sven Rebhan <srebhan@influxdata.com>
This commit is contained in:

committed by
Alessandro Boch

parent
8d48f50f55
commit
06219cde3e
@@ -29,3 +29,8 @@ type InetDiagTCPInfoResp struct {
|
|||||||
TCPInfo *TCPInfo
|
TCPInfo *TCPInfo
|
||||||
TCPBBRInfo *TCPBBRInfo
|
TCPBBRInfo *TCPBBRInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InetDiagUDPInfoResp struct {
|
||||||
|
InetDiagMsg *Socket
|
||||||
|
Memory *MemInfo
|
||||||
|
}
|
||||||
|
152
socket_linux.go
152
socket_linux.go
@@ -174,8 +174,18 @@ func SocketGet(local, remote net.Addr) (*Socket, error) {
|
|||||||
|
|
||||||
// SocketDiagTCPInfo requests INET_DIAG_INFO for TCP protocol for specified family type and return with extension TCP info.
|
// SocketDiagTCPInfo requests INET_DIAG_INFO for TCP protocol for specified family type and return with extension TCP info.
|
||||||
func SocketDiagTCPInfo(family uint8) ([]*InetDiagTCPInfoResp, error) {
|
func SocketDiagTCPInfo(family uint8) ([]*InetDiagTCPInfoResp, error) {
|
||||||
|
// Construct the request
|
||||||
|
req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP)
|
||||||
|
req.AddData(&socketRequest{
|
||||||
|
Family: family,
|
||||||
|
Protocol: unix.IPPROTO_TCP,
|
||||||
|
Ext: (1 << (INET_DIAG_VEGASINFO - 1)) | (1 << (INET_DIAG_INFO - 1)),
|
||||||
|
States: uint32(0xfff), // all states
|
||||||
|
})
|
||||||
|
|
||||||
|
// Do the query and parse the result
|
||||||
var result []*InetDiagTCPInfoResp
|
var result []*InetDiagTCPInfoResp
|
||||||
err := socketDiagTCPExecutor(family, func(m syscall.NetlinkMessage) error {
|
err := socketDiagExecutor(req, func(m syscall.NetlinkMessage) error {
|
||||||
sockInfo := &Socket{}
|
sockInfo := &Socket{}
|
||||||
if err := sockInfo.deserialize(m.Data); err != nil {
|
if err := sockInfo.deserialize(m.Data); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -201,8 +211,18 @@ func SocketDiagTCPInfo(family uint8) ([]*InetDiagTCPInfoResp, error) {
|
|||||||
|
|
||||||
// SocketDiagTCP requests INET_DIAG_INFO for TCP protocol for specified family type and return related socket.
|
// SocketDiagTCP requests INET_DIAG_INFO for TCP protocol for specified family type and return related socket.
|
||||||
func SocketDiagTCP(family uint8) ([]*Socket, error) {
|
func SocketDiagTCP(family uint8) ([]*Socket, error) {
|
||||||
|
// Construct the request
|
||||||
|
req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP)
|
||||||
|
req.AddData(&socketRequest{
|
||||||
|
Family: family,
|
||||||
|
Protocol: unix.IPPROTO_TCP,
|
||||||
|
Ext: (1 << (INET_DIAG_VEGASINFO - 1)) | (1 << (INET_DIAG_INFO - 1)),
|
||||||
|
States: uint32(0xfff), // all states
|
||||||
|
})
|
||||||
|
|
||||||
|
// Do the query and parse the result
|
||||||
var result []*Socket
|
var result []*Socket
|
||||||
err := socketDiagTCPExecutor(family, func(m syscall.NetlinkMessage) error {
|
err := socketDiagExecutor(req, func(m syscall.NetlinkMessage) error {
|
||||||
sockInfo := &Socket{}
|
sockInfo := &Socket{}
|
||||||
if err := sockInfo.deserialize(m.Data); err != nil {
|
if err := sockInfo.deserialize(m.Data); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -216,21 +236,82 @@ func SocketDiagTCP(family uint8) ([]*Socket, error) {
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// socketDiagTCPExecutor requests INET_DIAG_INFO for TCP protocol for specified family type.
|
// SocketDiagUDPInfo requests INET_DIAG_INFO for UDP protocol for specified family type and return with extension info.
|
||||||
func socketDiagTCPExecutor(family uint8, receiver func(syscall.NetlinkMessage) error) error {
|
func SocketDiagUDPInfo(family uint8) ([]*InetDiagUDPInfoResp, error) {
|
||||||
|
// Construct the request
|
||||||
|
var extensions uint8
|
||||||
|
extensions = 1 << (INET_DIAG_VEGASINFO - 1)
|
||||||
|
extensions |= 1 << (INET_DIAG_INFO - 1)
|
||||||
|
extensions |= 1 << (INET_DIAG_MEMINFO - 1)
|
||||||
|
|
||||||
|
req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP)
|
||||||
|
req.AddData(&socketRequest{
|
||||||
|
Family: family,
|
||||||
|
Protocol: unix.IPPROTO_UDP,
|
||||||
|
Ext: extensions,
|
||||||
|
States: uint32(0xfff), // all states
|
||||||
|
})
|
||||||
|
|
||||||
|
// Do the query and parse the result
|
||||||
|
var result []*InetDiagUDPInfoResp
|
||||||
|
err := socketDiagExecutor(req, func(m syscall.NetlinkMessage) error {
|
||||||
|
sockInfo := &Socket{}
|
||||||
|
if err := sockInfo.deserialize(m.Data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
attrs, err := nl.ParseRouteAttr(m.Data[sizeofSocket:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := attrsToInetDiagUDPInfoResp(attrs, sockInfo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, res)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SocketDiagUDP requests INET_DIAG_INFO for UDP protocol for specified family type and return related socket.
|
||||||
|
func SocketDiagUDP(family uint8) ([]*Socket, error) {
|
||||||
|
// Construct the request
|
||||||
|
req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP)
|
||||||
|
req.AddData(&socketRequest{
|
||||||
|
Family: family,
|
||||||
|
Protocol: unix.IPPROTO_UDP,
|
||||||
|
Ext: (1 << (INET_DIAG_VEGASINFO - 1)) | (1 << (INET_DIAG_INFO - 1)),
|
||||||
|
States: uint32(0xfff), // all states
|
||||||
|
})
|
||||||
|
|
||||||
|
// Do the query and parse the result
|
||||||
|
var result []*Socket
|
||||||
|
err := socketDiagExecutor(req, func(m syscall.NetlinkMessage) error {
|
||||||
|
sockInfo := &Socket{}
|
||||||
|
if err := sockInfo.deserialize(m.Data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
result = append(result, sockInfo)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// socketDiagExecutor requests diagnoses info from the NETLINK_INET_DIAG socket for the specified request.
|
||||||
|
func socketDiagExecutor(req *nl.NetlinkRequest, receiver func(syscall.NetlinkMessage) error) error {
|
||||||
s, err := nl.Subscribe(unix.NETLINK_INET_DIAG)
|
s, err := nl.Subscribe(unix.NETLINK_INET_DIAG)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP)
|
|
||||||
req.AddData(&socketRequest{
|
|
||||||
Family: family,
|
|
||||||
Protocol: unix.IPPROTO_TCP,
|
|
||||||
Ext: (1 << (INET_DIAG_VEGASINFO - 1)) | (1 << (INET_DIAG_INFO - 1)),
|
|
||||||
States: uint32(0xfff), // All TCP states
|
|
||||||
})
|
|
||||||
s.Send(req)
|
s.Send(req)
|
||||||
|
|
||||||
loop:
|
loop:
|
||||||
@@ -240,7 +321,7 @@ loop:
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if from.Pid != nl.PidKernel {
|
if from.Pid != nl.PidKernel {
|
||||||
return fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel)
|
return fmt.Errorf("wrong sender portid %d, expected %d", from.Pid, nl.PidKernel)
|
||||||
}
|
}
|
||||||
if len(msgs) == 0 {
|
if len(msgs) == 0 {
|
||||||
return errors.New("no message nor error from netlink")
|
return errors.New("no message nor error from netlink")
|
||||||
@@ -263,29 +344,40 @@ loop:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func attrsToInetDiagTCPInfoResp(attrs []syscall.NetlinkRouteAttr, sockInfo *Socket) (*InetDiagTCPInfoResp, error) {
|
func attrsToInetDiagTCPInfoResp(attrs []syscall.NetlinkRouteAttr, sockInfo *Socket) (*InetDiagTCPInfoResp, error) {
|
||||||
var tcpInfo *TCPInfo
|
info := &InetDiagTCPInfoResp{
|
||||||
var tcpBBRInfo *TCPBBRInfo
|
InetDiagMsg: sockInfo,
|
||||||
|
}
|
||||||
for _, a := range attrs {
|
for _, a := range attrs {
|
||||||
if a.Attr.Type == INET_DIAG_INFO {
|
switch a.Attr.Type {
|
||||||
tcpInfo = &TCPInfo{}
|
case INET_DIAG_INFO:
|
||||||
if err := tcpInfo.deserialize(a.Value); err != nil {
|
info.TCPInfo = &TCPInfo{}
|
||||||
|
if err := info.TCPInfo.deserialize(a.Value); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
continue
|
case INET_DIAG_BBRINFO:
|
||||||
}
|
info.TCPBBRInfo = &TCPBBRInfo{}
|
||||||
|
if err := info.TCPBBRInfo.deserialize(a.Value); err != nil {
|
||||||
if a.Attr.Type == INET_DIAG_BBRINFO {
|
|
||||||
tcpBBRInfo = &TCPBBRInfo{}
|
|
||||||
if err := tcpBBRInfo.deserialize(a.Value); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &InetDiagTCPInfoResp{
|
return info, nil
|
||||||
InetDiagMsg: sockInfo,
|
}
|
||||||
TCPInfo: tcpInfo,
|
|
||||||
TCPBBRInfo: tcpBBRInfo,
|
func attrsToInetDiagUDPInfoResp(attrs []syscall.NetlinkRouteAttr, sockInfo *Socket) (*InetDiagUDPInfoResp, error) {
|
||||||
}, nil
|
info := &InetDiagUDPInfoResp{
|
||||||
|
InetDiagMsg: sockInfo,
|
||||||
|
}
|
||||||
|
for _, a := range attrs {
|
||||||
|
switch a.Attr.Type {
|
||||||
|
case INET_DIAG_MEMINFO:
|
||||||
|
info.Memory = &MemInfo{}
|
||||||
|
if err := info.Memory.deserialize(a.Value); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
//go:build linux
|
||||||
// +build linux
|
// +build linux
|
||||||
|
|
||||||
package netlink
|
package netlink
|
||||||
@@ -75,3 +76,18 @@ func TestSocketDiagTCPInfo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSocketDiagUDPnfo(t *testing.T) {
|
||||||
|
for _, want := range []uint8{syscall.AF_INET, syscall.AF_INET6} {
|
||||||
|
result, err := SocketDiagUDPInfo(want)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, r := range result {
|
||||||
|
if got := r.InetDiagMsg.Family; got != want {
|
||||||
|
t.Fatalf("protocol family = %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
8
tcp.go
8
tcp.go
@@ -82,3 +82,11 @@ type TCPBBRInfo struct {
|
|||||||
BBRPacingGain uint32
|
BBRPacingGain uint32
|
||||||
BBRCwndGain uint32
|
BBRCwndGain uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// According to https://man7.org/linux/man-pages/man7/sock_diag.7.html
|
||||||
|
type MemInfo struct {
|
||||||
|
RMem uint32
|
||||||
|
WMem uint32
|
||||||
|
FMem uint32
|
||||||
|
TMem uint32
|
||||||
|
}
|
||||||
|
15
tcp_linux.go
15
tcp_linux.go
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
tcpBBRInfoLen = 20
|
tcpBBRInfoLen = 20
|
||||||
|
memInfoLen = 16
|
||||||
)
|
)
|
||||||
|
|
||||||
func checkDeserErr(err error) error {
|
func checkDeserErr(err error) error {
|
||||||
@@ -351,3 +352,17 @@ func (t *TCPBBRInfo) deserialize(b []byte) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MemInfo) deserialize(b []byte) error {
|
||||||
|
if len(b) != memInfoLen {
|
||||||
|
return errors.New("Invalid length")
|
||||||
|
}
|
||||||
|
|
||||||
|
rb := bytes.NewBuffer(b)
|
||||||
|
m.RMem = native.Uint32(rb.Next(4))
|
||||||
|
m.WMem = native.Uint32(rb.Next(4))
|
||||||
|
m.FMem = native.Uint32(rb.Next(4))
|
||||||
|
m.TMem = native.Uint32(rb.Next(4))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user