Skip to content

Commit 06219cd

Browse files
srebhanaboch
authored andcommitted
Implement UDP socket diagnostics
Signed-off-by: Sven Rebhan <[email protected]>
1 parent 8d48f50 commit 06219cd

File tree

5 files changed

+161
-25
lines changed

5 files changed

+161
-25
lines changed

inet_diag.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,8 @@ type InetDiagTCPInfoResp struct {
2929
TCPInfo *TCPInfo
3030
TCPBBRInfo *TCPBBRInfo
3131
}
32+
33+
type InetDiagUDPInfoResp struct {
34+
InetDiagMsg *Socket
35+
Memory *MemInfo
36+
}

socket_linux.go

Lines changed: 117 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,18 @@ func SocketGet(local, remote net.Addr) (*Socket, error) {
174174

175175
// SocketDiagTCPInfo requests INET_DIAG_INFO for TCP protocol for specified family type and return with extension TCP info.
176176
func SocketDiagTCPInfo(family uint8) ([]*InetDiagTCPInfoResp, error) {
177+
// Construct the request
178+
req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP)
179+
req.AddData(&socketRequest{
180+
Family: family,
181+
Protocol: unix.IPPROTO_TCP,
182+
Ext: (1 << (INET_DIAG_VEGASINFO - 1)) | (1 << (INET_DIAG_INFO - 1)),
183+
States: uint32(0xfff), // all states
184+
})
185+
186+
// Do the query and parse the result
177187
var result []*InetDiagTCPInfoResp
178-
err := socketDiagTCPExecutor(family, func(m syscall.NetlinkMessage) error {
188+
err := socketDiagExecutor(req, func(m syscall.NetlinkMessage) error {
179189
sockInfo := &Socket{}
180190
if err := sockInfo.deserialize(m.Data); err != nil {
181191
return err
@@ -201,8 +211,18 @@ func SocketDiagTCPInfo(family uint8) ([]*InetDiagTCPInfoResp, error) {
201211

202212
// SocketDiagTCP requests INET_DIAG_INFO for TCP protocol for specified family type and return related socket.
203213
func SocketDiagTCP(family uint8) ([]*Socket, error) {
214+
// Construct the request
215+
req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP)
216+
req.AddData(&socketRequest{
217+
Family: family,
218+
Protocol: unix.IPPROTO_TCP,
219+
Ext: (1 << (INET_DIAG_VEGASINFO - 1)) | (1 << (INET_DIAG_INFO - 1)),
220+
States: uint32(0xfff), // all states
221+
})
222+
223+
// Do the query and parse the result
204224
var result []*Socket
205-
err := socketDiagTCPExecutor(family, func(m syscall.NetlinkMessage) error {
225+
err := socketDiagExecutor(req, func(m syscall.NetlinkMessage) error {
206226
sockInfo := &Socket{}
207227
if err := sockInfo.deserialize(m.Data); err != nil {
208228
return err
@@ -216,21 +236,82 @@ func SocketDiagTCP(family uint8) ([]*Socket, error) {
216236
return result, nil
217237
}
218238

219-
// socketDiagTCPExecutor requests INET_DIAG_INFO for TCP protocol for specified family type.
220-
func socketDiagTCPExecutor(family uint8, receiver func(syscall.NetlinkMessage) error) error {
221-
s, err := nl.Subscribe(unix.NETLINK_INET_DIAG)
239+
// SocketDiagUDPInfo requests INET_DIAG_INFO for UDP protocol for specified family type and return with extension info.
240+
func SocketDiagUDPInfo(family uint8) ([]*InetDiagUDPInfoResp, error) {
241+
// Construct the request
242+
var extensions uint8
243+
extensions = 1 << (INET_DIAG_VEGASINFO - 1)
244+
extensions |= 1 << (INET_DIAG_INFO - 1)
245+
extensions |= 1 << (INET_DIAG_MEMINFO - 1)
246+
247+
req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP)
248+
req.AddData(&socketRequest{
249+
Family: family,
250+
Protocol: unix.IPPROTO_UDP,
251+
Ext: extensions,
252+
States: uint32(0xfff), // all states
253+
})
254+
255+
// Do the query and parse the result
256+
var result []*InetDiagUDPInfoResp
257+
err := socketDiagExecutor(req, func(m syscall.NetlinkMessage) error {
258+
sockInfo := &Socket{}
259+
if err := sockInfo.deserialize(m.Data); err != nil {
260+
return err
261+
}
262+
attrs, err := nl.ParseRouteAttr(m.Data[sizeofSocket:])
263+
if err != nil {
264+
return err
265+
}
266+
267+
res, err := attrsToInetDiagUDPInfoResp(attrs, sockInfo)
268+
if err != nil {
269+
return err
270+
}
271+
272+
result = append(result, res)
273+
return nil
274+
})
222275
if err != nil {
223-
return err
276+
return nil, err
224277
}
225-
defer s.Close()
278+
return result, nil
279+
}
226280

281+
// SocketDiagUDP requests INET_DIAG_INFO for UDP protocol for specified family type and return related socket.
282+
func SocketDiagUDP(family uint8) ([]*Socket, error) {
283+
// Construct the request
227284
req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP)
228285
req.AddData(&socketRequest{
229286
Family: family,
230-
Protocol: unix.IPPROTO_TCP,
287+
Protocol: unix.IPPROTO_UDP,
231288
Ext: (1 << (INET_DIAG_VEGASINFO - 1)) | (1 << (INET_DIAG_INFO - 1)),
232-
States: uint32(0xfff), // All TCP states
289+
States: uint32(0xfff), // all states
233290
})
291+
292+
// Do the query and parse the result
293+
var result []*Socket
294+
err := socketDiagExecutor(req, func(m syscall.NetlinkMessage) error {
295+
sockInfo := &Socket{}
296+
if err := sockInfo.deserialize(m.Data); err != nil {
297+
return err
298+
}
299+
result = append(result, sockInfo)
300+
return nil
301+
})
302+
if err != nil {
303+
return nil, err
304+
}
305+
return result, nil
306+
}
307+
308+
// socketDiagExecutor requests diagnoses info from the NETLINK_INET_DIAG socket for the specified request.
309+
func socketDiagExecutor(req *nl.NetlinkRequest, receiver func(syscall.NetlinkMessage) error) error {
310+
s, err := nl.Subscribe(unix.NETLINK_INET_DIAG)
311+
if err != nil {
312+
return err
313+
}
314+
defer s.Close()
234315
s.Send(req)
235316

236317
loop:
@@ -240,7 +321,7 @@ loop:
240321
return err
241322
}
242323
if from.Pid != nl.PidKernel {
243-
return fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel)
324+
return fmt.Errorf("wrong sender portid %d, expected %d", from.Pid, nl.PidKernel)
244325
}
245326
if len(msgs) == 0 {
246327
return errors.New("no message nor error from netlink")
@@ -263,29 +344,40 @@ loop:
263344
}
264345

265346
func attrsToInetDiagTCPInfoResp(attrs []syscall.NetlinkRouteAttr, sockInfo *Socket) (*InetDiagTCPInfoResp, error) {
266-
var tcpInfo *TCPInfo
267-
var tcpBBRInfo *TCPBBRInfo
347+
info := &InetDiagTCPInfoResp{
348+
InetDiagMsg: sockInfo,
349+
}
268350
for _, a := range attrs {
269-
if a.Attr.Type == INET_DIAG_INFO {
270-
tcpInfo = &TCPInfo{}
271-
if err := tcpInfo.deserialize(a.Value); err != nil {
351+
switch a.Attr.Type {
352+
case INET_DIAG_INFO:
353+
info.TCPInfo = &TCPInfo{}
354+
if err := info.TCPInfo.deserialize(a.Value); err != nil {
355+
return nil, err
356+
}
357+
case INET_DIAG_BBRINFO:
358+
info.TCPBBRInfo = &TCPBBRInfo{}
359+
if err := info.TCPBBRInfo.deserialize(a.Value); err != nil {
272360
return nil, err
273361
}
274-
continue
275362
}
363+
}
364+
365+
return info, nil
366+
}
276367

277-
if a.Attr.Type == INET_DIAG_BBRINFO {
278-
tcpBBRInfo = &TCPBBRInfo{}
279-
if err := tcpBBRInfo.deserialize(a.Value); err != nil {
368+
func attrsToInetDiagUDPInfoResp(attrs []syscall.NetlinkRouteAttr, sockInfo *Socket) (*InetDiagUDPInfoResp, error) {
369+
info := &InetDiagUDPInfoResp{
370+
InetDiagMsg: sockInfo,
371+
}
372+
for _, a := range attrs {
373+
switch a.Attr.Type {
374+
case INET_DIAG_MEMINFO:
375+
info.Memory = &MemInfo{}
376+
if err := info.Memory.deserialize(a.Value); err != nil {
280377
return nil, err
281378
}
282-
continue
283379
}
284380
}
285381

286-
return &InetDiagTCPInfoResp{
287-
InetDiagMsg: sockInfo,
288-
TCPInfo: tcpInfo,
289-
TCPBBRInfo: tcpBBRInfo,
290-
}, nil
382+
return info, nil
291383
}

socket_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//go:build linux
12
// +build linux
23

34
package netlink
@@ -75,3 +76,18 @@ func TestSocketDiagTCPInfo(t *testing.T) {
7576
}
7677
}
7778
}
79+
80+
func TestSocketDiagUDPnfo(t *testing.T) {
81+
for _, want := range []uint8{syscall.AF_INET, syscall.AF_INET6} {
82+
result, err := SocketDiagUDPInfo(want)
83+
if err != nil {
84+
t.Fatal(err)
85+
}
86+
87+
for _, r := range result {
88+
if got := r.InetDiagMsg.Family; got != want {
89+
t.Fatalf("protocol family = %v, want %v", got, want)
90+
}
91+
}
92+
}
93+
}

tcp.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,11 @@ type TCPBBRInfo struct {
8282
BBRPacingGain uint32
8383
BBRCwndGain uint32
8484
}
85+
86+
// According to https://man7.org/linux/man-pages/man7/sock_diag.7.html
87+
type MemInfo struct {
88+
RMem uint32
89+
WMem uint32
90+
FMem uint32
91+
TMem uint32
92+
}

tcp_linux.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
const (
1010
tcpBBRInfoLen = 20
11+
memInfoLen = 16
1112
)
1213

1314
func checkDeserErr(err error) error {
@@ -351,3 +352,17 @@ func (t *TCPBBRInfo) deserialize(b []byte) error {
351352

352353
return nil
353354
}
355+
356+
func (m *MemInfo) deserialize(b []byte) error {
357+
if len(b) != memInfoLen {
358+
return errors.New("Invalid length")
359+
}
360+
361+
rb := bytes.NewBuffer(b)
362+
m.RMem = native.Uint32(rb.Next(4))
363+
m.WMem = native.Uint32(rb.Next(4))
364+
m.FMem = native.Uint32(rb.Next(4))
365+
m.TMem = native.Uint32(rb.Next(4))
366+
367+
return nil
368+
}

0 commit comments

Comments
 (0)