pkg/tcp_metrics/inetdiag/inetdiag.go (114 lines of code) (raw):

// Package inetdiag provides basic structs and utilities for INET_DIAG messaages. // Based on uapi/linux/inet_diag.h. package inetdiag import ( "fmt" "syscall" "github.com/GoogleCloudPlatform/netd/pkg/tcp_metrics/tcp" "github.com/vishvananda/netlink/nl" "golang.org/x/sys/unix" ) // Pretty basic code slightly adapted from code copied from // https://gist.github.com/gwind/05f5f649d93e6015cf47ffa2b2fd9713 // Original source no longer available at https://github.com/eleme/netlink/blob/master/inetdiag.go // Adaptations are Copyright 2018 M-Lab Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /* IMPORTANT NOTES This 2002 article describes Netlink Sockets https://pdfs.semanticscholar.org/6efd/e161a2582ba5846e4b8fea5a53bc305a64f3.pdf "Netlink messages are aligned to 32 bits and, generally speaking, they contain data that is expressed in host-byte order" */ // inet_diag.h // NOTE: darwin unix.AF_INET6 and syscall.AF_INET6 are incorrect for // our purposes (0x1e), so, we set this explicitly. const AF_INET6 = 0x0a // NOTE: windows does not have unix.AF_INET available. const AF_INET = 0x02 const ( INET_DIAG_NONE = iota INET_DIAG_MEMINFO INET_DIAG_INFO INET_DIAG_VEGASINFO INET_DIAG_CONG INET_DIAG_TOS INET_DIAG_TCLASS INET_DIAG_SKMEMINFO INET_DIAG_SHUTDOWN INET_DIAG_DCTCPINFO INET_DIAG_PROTOCOL INET_DIAG_SKV6ONLY INET_DIAG_LOCALS INET_DIAG_PEERS INET_DIAG_PAD INET_DIAG_MARK INET_DIAG_BBRINFO INET_DIAG_CLASS_ID INET_DIAG_MD5SIG // TODO - Should check whether this matches the current linux header. INET_DIAG_MAX ) // InetDiagType provides human readable strings for decoding attribute types. var InetDiagType = map[int32]string{ INET_DIAG_MEMINFO: "MemInfo", INET_DIAG_INFO: "TCPInfo", INET_DIAG_VEGASINFO: "Vegas", INET_DIAG_CONG: "Congestion", INET_DIAG_TOS: "TOS", INET_DIAG_TCLASS: "TClass", INET_DIAG_SKMEMINFO: "SKMemInfo", INET_DIAG_SHUTDOWN: "Shutdown", INET_DIAG_DCTCPINFO: "DCTCPInfo", INET_DIAG_PROTOCOL: "Protocol", INET_DIAG_SKV6ONLY: "SKV6Only", INET_DIAG_LOCALS: "Locals", INET_DIAG_PEERS: "Peers", INET_DIAG_PAD: "Pad", INET_DIAG_MARK: "Mark", INET_DIAG_BBRINFO: "BBRInfo", INET_DIAG_CLASS_ID: "ClassID", INET_DIAG_MD5SIG: "MD5Sig", } var diagFamilyMap = map[uint8]string{ AF_INET: "tcp", AF_INET6: "tcp6", // because darwin values for AF_INET6 are incorrect. } // Protocol defines the type corresponding to INET_DIAG_PROTOCOL 8 bit field. type Protocol uint8 const ( // Protocol_IPPROTO_UNUSED ... Protocol_IPPROTO_UNUSED Protocol = 0 // Protocol_IPPROTO_TCP indicates TCP traffic. Protocol_IPPROTO_TCP Protocol = 6 // Protocol_IPPROTO_UDP indicates UDP traffic. Protocol_IPPROTO_UDP Protocol = 17 // Protocol_IPPROTO_DCCP indicates DCCP traffic. Protocol_IPPROTO_DCCP Protocol = 33 ) // ProtocolName is used to convert Protocol values to strings. var ProtocolName = map[int32]string{ 0: "IPPROTO_UNUSED", 6: "IPPROTO_TCP", 17: "IPPROTO_UDP", 33: "IPPROTO_DCCP", } // TODO - Figure out why we aren't seeing INET_DIAG_DCTCPINFO or INET_DIAG_BBRINFO messages. func MakeReq(inetType uint8) *nl.NetlinkRequest { req := nl.NewNetlinkRequest(SOCK_DIAG_BY_FAMILY, syscall.NLM_F_DUMP|syscall.NLM_F_REQUEST) msg := NewReqV2(inetType, syscall.IPPROTO_TCP, tcp.AllFlags & ^((1<<uint(tcp.SYN_RECV))|(1<<uint(tcp.TIME_WAIT))|(1<<uint(tcp.CLOSE)))) msg.IDiagExt |= (1 << (INET_DIAG_MEMINFO - 1)) msg.IDiagExt |= (1 << (INET_DIAG_INFO - 1)) msg.IDiagExt |= (1 << (INET_DIAG_VEGASINFO - 1)) msg.IDiagExt |= (1 << (INET_DIAG_CONG - 1)) msg.IDiagExt |= (1 << (INET_DIAG_TCLASS - 1)) msg.IDiagExt |= (1 << (INET_DIAG_TOS - 1)) msg.IDiagExt |= (1 << (INET_DIAG_SKMEMINFO - 1)) msg.IDiagExt |= (1 << (INET_DIAG_SHUTDOWN - 1)) req.AddData(msg) req.NlMsghdr.Type = SOCK_DIAG_BY_FAMILY req.NlMsghdr.Flags |= syscall.NLM_F_DUMP | syscall.NLM_F_REQUEST return req } func ProcessMessage(m *syscall.NetlinkMessage, seq uint32, pid uint32) (msg *syscall.NetlinkMessage, shouldContinue bool, err error) { if m.Header.Seq != seq { fmt.Printf("Wrong Seq nr %d, expected %d", m.Header.Seq, seq) return nil, false, ErrBadSequence } if m.Header.Pid != pid { fmt.Printf("Wrong pid %d, expected %d", m.Header.Pid, pid) return nil, false, ErrBadPid } if m.Header.Type == unix.NLMSG_DONE { return nil, false, nil } if m.Header.Type == unix.NLMSG_ERROR { native := nl.NativeEndian() if len(m.Data) < 4 { return nil, false, ErrBadMsgData } error := int32(native.Uint32(m.Data[0:4])) if error == 0 { return nil, false, nil } fmt.Println(syscall.Errno(-error)) } if m.Header.Flags&unix.NLM_F_MULTI == 0 { return m, false, nil } return m, true, nil }