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

package inetdiag /* There should be a corresponding struct for every element of this enum defined in uapi/linux/inet_diag.h INET_DIAG_MEMINFO INET_DIAG_INFO // This one is in tcp.go 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 */ import ( "encoding/binary" "errors" "fmt" "net" "runtime" "unsafe" ) // Constants from linux. const ( SOCK_DIAG_BY_FAMILY = 20 // uapi/linux/sock_diag.h ) var ( // ErrBadPid is used when the PID is mismatched between the netlink socket and the calling process. ErrBadPid = errors.New("bad PID, can't listen to NL socket") // ErrBadSequence is used when the Netlink response has a bad sequence number. ErrBadSequence = errors.New("bad sequence number, can't interpret NetLink response") // ErrBadMsgData is used when the NHetlink response has bad or missing data. ErrBadMsgData = errors.New("bad message data from netlink message") ) // ReqV2 is the Netlink request struct, as in linux/inet_diag.h // Note that netlink messages use host byte ordering, unless NLA_F_NET_BYTEORDER flag is present. type ReqV2 struct { SDiagFamily uint8 SDiagProtocol uint8 IDiagExt uint8 Pad uint8 IDiagStates uint32 ID LinuxSockID } // SizeofReqV2 is the size of the struct. // TODO should we just make this explicit in the code? const SizeofReqV2 = int(unsafe.Sizeof(ReqV2{})) // Should be 0x38 // Serialize is provided for json serialization? // TODO - should use binary functions instead? func (req *ReqV2) Serialize() []byte { return (*(*[SizeofReqV2]byte)(unsafe.Pointer(req)))[:] } // Len is provided for json serialization? func (req *ReqV2) Len() int { return SizeofReqV2 } // NewReqV2 creates a new request. func NewReqV2(family, protocol uint8, states uint32) *ReqV2 { return &ReqV2{ SDiagFamily: family, SDiagProtocol: protocol, IDiagStates: states, } } // Types for LinuxSockID fields. type cookieType [8]byte // TODO - remove all these. func (c *cookieType) MarshalCSV() (string, error) { value := binary.LittleEndian.Uint64(c[:]) return fmt.Sprintf("%X", value), nil } type ipType [16]byte func (ipAddr *ipType) Marshal() (string, error) { netIP := ip(*ipAddr) return netIP.String(), nil } // Port encodes a LinuxSockID Port type Port [2]byte func (p *Port) Marshal() (string, error) { value := binary.BigEndian.Uint16(p[:]) return fmt.Sprintf("%d", value), nil } // Interface encodes the LinuxSockID Interface field. type netIF [4]byte func (nif *netIF) Marshal() (string, error) { value := binary.BigEndian.Uint32(nif[:]) return fmt.Sprintf("%d", value), nil } // LinuxSockID is the binary linux representation of a socket, as in linux/inet_diag.h // Linux code comments indicate this struct uses the network byte order!!! // All fields are ignored for bigquery, and handled in code. // TODO make this unexported type LinuxSockID struct { IDiagSPort Port IDiagDPort Port IDiagSrc ipType IDiagDst ipType IDiagIf netIF IDiagCookie cookieType } // SockID is the natural golang struct equivalent of LinuxSockID type SockID struct { SPort uint16 DPort uint16 SrcIP string DstIP string Interface uint32 Cookie uint64 } // GetSockID extracts the SockID from the LinuxSockID. func (id *LinuxSockID) GetSockID() SockID { sid := SockID{ SrcIP: id.SrcIP().String(), SPort: id.SPort(), DstIP: id.DstIP().String(), DPort: id.DPort(), Interface: id.Interface(), Cookie: id.Cookie(), } return sid } // Interface returns the interface number. func (id *LinuxSockID) Interface() uint32 { return binary.BigEndian.Uint32(id.IDiagIf[:]) } // SrcIP returns a golang net encoding of source address. func (id *LinuxSockID) SrcIP() net.IP { return ip(id.IDiagSrc) } // DstIP returns a golang net encoding of destination address. func (id *LinuxSockID) DstIP() net.IP { return ip(id.IDiagDst) } // SPort returns the host byte ordered port. // In general, Netlink is supposed to use host byte order, but this seems to be an exception. // Perhaps Netlink is reading a tcp stack structure that holds the port in network byte order. func (id *LinuxSockID) SPort() uint16 { return binary.BigEndian.Uint16(id.IDiagSPort[:]) } // DPort returns the host byte ordered port. // In general, Netlink is supposed to use host byte order, but this seems to be an exception. // Perhaps Netlink is reading a tcp stack structure that holds the port in network byte order. func (id *LinuxSockID) DPort() uint16 { return binary.BigEndian.Uint16(id.IDiagDPort[:]) } // Cookie returns the LinuxSockID's 64 bit unsigned cookie. func (id *LinuxSockID) Cookie() uint64 { // This is a socket UUID generated within the kernel, and is therefore in host byte order. return binary.LittleEndian.Uint64(id.IDiagCookie[:]) } // TODO should use more net.IP code instead of custom code. // TODO: reconcile this encoding of v4-in-v6 with the encoding used in https://golang.org/src/net/ip.go?s=1216:1245#L35 func ip(bytes [16]byte) net.IP { if isIpv6(bytes) { return ipv6(bytes) } return ipv4(bytes) } func isIpv6(original [16]byte) bool { for i := 4; i < 16; i++ { if original[i] != 0 { return true } } return false } func ipv4(original [16]byte) net.IP { return net.IPv4(original[0], original[1], original[2], original[3]).To4() } func ipv6(original [16]byte) net.IP { return original[:] } // HostCond is related to filters. We don't currently use filters, so we don't actually use this type. type HostCond struct { // inet_diag_hostcond Family uint8 // __u8 family PrefixLen uint8 // __u8 prefix_len Port uint16 // int port Addr uint32 // __be32 addr[0]; } // MarkCond is related to filters. We don't currently use filters, so we don't actually use this type. type MarkCond struct { // inet_diag_markcond Mark uint32 Mask uint32 } // InetDiagMsg is the linux binary representation of a InetDiag message header, as in linux/inet_diag.h // Note that netlink messages use host byte ordering, unless NLA_F_NET_BYTEORDER flag is present. type InetDiagMsg struct { IDiagFamily uint8 IDiagState uint8 IDiagTimer uint8 IDiagRetrans uint8 ID LinuxSockID IDiagExpires uint32 IDiagRqueue uint32 IDiagWqueue uint32 IDiagUID uint32 IDiagInode uint32 } const ( // RTA_ALIGNTO previously came from syscall, but explicit here to work on Darwin. RTA_ALIGNTO = 4 ) // rtaAlignOf rounds the length of a netlink route attribute up to align it properly. func rtaAlignOf(attrlen int) int { return (attrlen + RTA_ALIGNTO - 1) & ^(RTA_ALIGNTO - 1) } // RawInetDiagMsg holds the []byte representation of an InetDiagMsg type RawInetDiagMsg []byte // SplitInetDiagMsg pulls the InetDiagMsg out, and returns the msg and the remaining data slice. func SplitInetDiagMsg(data []byte) (RawInetDiagMsg, []byte) { // TODO - why using rtaAlign on InetDiagMsg ??? align := rtaAlignOf(int(unsafe.Sizeof(InetDiagMsg{}))) if len(data) < align { fmt.Println("Wrong length", len(data), "<", align) _, file, line, _ := runtime.Caller(2) fmt.Println(file, line, data) return nil, nil } return RawInetDiagMsg(data[:align]), data[align:] } // ErrParseFailed is returned if InetDiagMsg parsing fails. var ErrParseFailed = errors.New("Unable to parse InetDiagMsg") // Parse returns the InetDiagMsg itself // Modified from original to also return attribute data array. func (raw RawInetDiagMsg) Parse() (*InetDiagMsg, error) { // TODO - why using rtaAlign on InetDiagMsg ??? align := rtaAlignOf(int(unsafe.Sizeof(InetDiagMsg{}))) if len(raw) < align { return nil, ErrParseFailed } return (*InetDiagMsg)(unsafe.Pointer(&raw[0])), nil } // ErrUnknownAF is returned when the InetDiagMsg.IDiagFamily is unknown. var ErrUnknownAF = errors.New("unknown address family") // SocketMemInfo implements the struct associated with INET_DIAG_SKMEMINFO // Haven't found a corresponding linux struct, but the message is described // in https://manpages.debian.org/stretch/manpages/sock_diag.7.en.html type SocketMemInfo struct { RmemAlloc uint32 Rcvbuf uint32 WmemAlloc uint32 Sndbuf uint32 FwdAlloc uint32 WmemQueued uint32 Optmem uint32 Backlog uint32 Drops uint32 } // MemInfo implements the struct associated with INET_DIAG_MEMINFO, corresponding with // linux struct inet_diag_meminfo in uapi/linux/inet_diag.h. type MemInfo struct { Rmem uint32 Wmem uint32 Fmem uint32 Tmem uint32 } // VegasInfo implements the struct associated with INET_DIAG_VEGASINFO, corresponding with // linux struct tcpvegas_info in uapi/linux/inet_diag.h. type VegasInfo struct { Enabled uint32 RTTCount uint32 RTT uint32 MinRTT uint32 } // DCTCPInfo implements the struct associated with INET_DIAG_DCTCPINFO attribute, corresponding with // linux struct tcp_dctcp_info in uapi/linux/inet_diag.h. type DCTCPInfo struct { Enabled uint16 CEState uint16 Alpha uint32 ABEcn uint32 ABTot uint32 } // BBRInfo implements the struct associated with INET_DIAG_BBRINFO attribute, corresponding with // linux struct tcp_bbr_info in uapi/linux/inet_diag.h. type BBRInfo struct { BW int64 MinRTT uint32 PacingGain uint32 CwndGain uint32 } // LOCALS and PEERS contain an array of sockaddr_storage elements. /* ss.c parses these elements like this: static const char *format_host_sa(struct sockaddr_storage *sa) { union { struct sockaddr_in sin; struct sockaddr_in6 sin6; } *saddr = (void *)sa; switch (sa->ss_family) { case AF_INET: return format_host(AF_INET, 4, &saddr->sin.sin_addr); case AF_INET6: return format_host(AF_INET6, 16, &saddr->sin6.sin6_addr); default: return ""; } } INET_DIAG_LOCALS if (tb[INET_DIAG_LOCALS]) { len = RTA_PAYLOAD(tb[INET_DIAG_LOCALS]); sa = RTA_DATA(tb[INET_DIAG_LOCALS]); printf("locals:%s", format_host_sa(sa)); for (sa++, len -= sizeof(*sa); len > 0; sa++, len -= sizeof(*sa)) printf(",%s", format_host_sa(sa)); } INET_DIAG_PEERS if (tb[INET_DIAG_PEERS]) { len = RTA_PAYLOAD(tb[INET_DIAG_PEERS]); sa = RTA_DATA(tb[INET_DIAG_PEERS]); printf(" peers:%s", format_host_sa(sa)); for (sa++, len -= sizeof(*sa); len > 0; sa++, len -= sizeof(*sa)) printf(",%s", format_host_sa(sa)); } */