inetdiag.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. // +build linux
  2. package linux
  3. import (
  4. "bytes"
  5. "encoding/binary"
  6. "fmt"
  7. "hash/fnv"
  8. "io"
  9. "net"
  10. "os"
  11. "syscall"
  12. "unsafe"
  13. "notabug.org/themusicgod1/gosigar/sys"
  14. "github.com/pkg/errors"
  15. )
  16. // Enums / Constants
  17. const (
  18. // AllTCPStates is a flag to request all sockets in any TCP state.
  19. AllTCPStates = ^uint32(0)
  20. // TCPDIAG_GETSOCK is the netlink message type for requesting TCP diag data.
  21. // https://github.com/torvalds/linux/blob/v4.0/include/uapi/linux/inet_diag.h#L7
  22. TCPDIAG_GETSOCK = 18
  23. // SOCK_DIAG_BY_FAMILY is the netlink message type for requestion socket
  24. // diag data by family. This is newer and can be used with inet_diag_req_v2.
  25. // https://github.com/torvalds/linux/blob/v4.0/include/uapi/linux/sock_diag.h#L6
  26. SOCK_DIAG_BY_FAMILY = 20
  27. )
  28. // AddressFamily is the address family of the socket.
  29. type AddressFamily uint8
  30. // https://github.com/torvalds/linux/blob/5924bbecd0267d87c24110cbe2041b5075173a25/include/linux/socket.h#L159
  31. const (
  32. AF_INET AddressFamily = 2
  33. AF_INET6 = 10
  34. )
  35. var addressFamilyNames = map[AddressFamily]string{
  36. AF_INET: "ipv4",
  37. AF_INET6: "ipv6",
  38. }
  39. func (af AddressFamily) String() string {
  40. if fam, found := addressFamilyNames[af]; found {
  41. return fam
  42. }
  43. return fmt.Sprintf("UNKNOWN (%d)", af)
  44. }
  45. // TCPState represents the state of a TCP connection.
  46. type TCPState uint8
  47. // https://github.com/torvalds/linux/blob/5924bbecd0267d87c24110cbe2041b5075173a25/include/net/tcp_states.h#L16
  48. const (
  49. TCP_ESTABLISHED TCPState = iota + 1
  50. TCP_SYN_SENT
  51. TCP_SYN_RECV
  52. TCP_FIN_WAIT1
  53. TCP_FIN_WAIT2
  54. TCP_TIME_WAIT
  55. TCP_CLOSE
  56. TCP_CLOSE_WAIT
  57. TCP_LAST_ACK
  58. TCP_LISTEN
  59. TCP_CLOSING /* Now a valid state */
  60. )
  61. var tcpStateNames = map[TCPState]string{
  62. TCP_ESTABLISHED: "ESTAB",
  63. TCP_SYN_SENT: "SYN-SENT",
  64. TCP_SYN_RECV: "SYN-RECV",
  65. TCP_FIN_WAIT1: "FIN-WAIT-1",
  66. TCP_FIN_WAIT2: "FIN-WAIT-2",
  67. TCP_TIME_WAIT: "TIME-WAIT",
  68. TCP_CLOSE: "UNCONN",
  69. TCP_CLOSE_WAIT: "CLOSE-WAIT",
  70. TCP_LAST_ACK: "LAST-ACK",
  71. TCP_LISTEN: "LISTEN",
  72. TCP_CLOSING: "CLOSING",
  73. }
  74. func (s TCPState) String() string {
  75. if state, found := tcpStateNames[s]; found {
  76. return state
  77. }
  78. return "UNKNOWN"
  79. }
  80. // Extensions that can be used in the InetDiagReqV2 request to ask for
  81. // additional data.
  82. // https://github.com/torvalds/linux/blob/v4.0/include/uapi/linux/inet_diag.h#L103
  83. const (
  84. INET_DIAG_NONE = 0
  85. INET_DIAG_MEMINFO = 1 << iota
  86. INET_DIAG_INFO
  87. INET_DIAG_VEGASINFO
  88. INET_DIAG_CONG
  89. INET_DIAG_TOS
  90. INET_DIAG_TCLASS
  91. INET_DIAG_SKMEMINFO
  92. INET_DIAG_SHUTDOWN
  93. INET_DIAG_DCTCPINFO
  94. INET_DIAG_PROTOCOL /* response attribute only */
  95. INET_DIAG_SKV6ONLY
  96. INET_DIAG_LOCALS
  97. INET_DIAG_PEERS
  98. INET_DIAG_PAD
  99. INET_DIAG_MARK
  100. )
  101. var (
  102. byteOrder = sys.GetEndian()
  103. )
  104. // NetlinkInetDiag sends the given netlink request parses the responses with the
  105. // assumption that they are inet_diag_msgs. This will allocate a temporary
  106. // buffer for reading from the socket whose size will be the length of a page
  107. // (usually 32k). Use NetlinkInetDiagWithBuf if you want to provide your own
  108. // buffer.
  109. func NetlinkInetDiag(request syscall.NetlinkMessage) ([]*InetDiagMsg, error) {
  110. return NetlinkInetDiagWithBuf(request, nil, nil)
  111. }
  112. // NetlinkInetDiagWithBuf sends the given netlink request parses the responses
  113. // with the assumption that they are inet_diag_msgs. readBuf will be used to
  114. // hold the raw data read from the socket. If the length is not large enough to
  115. // hold the socket contents the data will be truncated. If readBuf is nil then a
  116. // temporary buffer will be allocated for each invocation. The resp writer, if
  117. // non-nil, will receive a copy of all bytes read (this is useful for
  118. // debugging).
  119. func NetlinkInetDiagWithBuf(request syscall.NetlinkMessage, readBuf []byte, resp io.Writer) ([]*InetDiagMsg, error) {
  120. s, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_INET_DIAG)
  121. if err != nil {
  122. return nil, err
  123. }
  124. defer syscall.Close(s)
  125. lsa := &syscall.SockaddrNetlink{Family: syscall.AF_NETLINK}
  126. if err := syscall.Sendto(s, serialize(request), 0, lsa); err != nil {
  127. return nil, err
  128. }
  129. if len(readBuf) == 0 {
  130. // Default size used in libnl.
  131. readBuf = make([]byte, os.Getpagesize())
  132. }
  133. var inetDiagMsgs []*InetDiagMsg
  134. done:
  135. for {
  136. buf := readBuf
  137. nr, _, err := syscall.Recvfrom(s, buf, 0)
  138. if err != nil {
  139. return nil, err
  140. }
  141. if nr < syscall.NLMSG_HDRLEN {
  142. return nil, syscall.EINVAL
  143. }
  144. buf = buf[:nr]
  145. // Dump raw data for inspection purposes.
  146. if resp != nil {
  147. if _, err := resp.Write(buf); err != nil {
  148. return nil, err
  149. }
  150. }
  151. msgs, err := syscall.ParseNetlinkMessage(buf)
  152. if err != nil {
  153. return nil, err
  154. }
  155. for _, m := range msgs {
  156. if m.Header.Type == syscall.NLMSG_DONE {
  157. break done
  158. }
  159. if m.Header.Type == syscall.NLMSG_ERROR {
  160. return nil, ParseNetlinkError(m.Data)
  161. }
  162. inetDiagMsg, err := ParseInetDiagMsg(m.Data)
  163. if err != nil {
  164. return nil, err
  165. }
  166. inetDiagMsgs = append(inetDiagMsgs, inetDiagMsg)
  167. }
  168. }
  169. return inetDiagMsgs, nil
  170. }
  171. func serialize(msg syscall.NetlinkMessage) []byte {
  172. msg.Header.Len = uint32(syscall.SizeofNlMsghdr + len(msg.Data))
  173. b := make([]byte, msg.Header.Len)
  174. byteOrder.PutUint32(b[0:4], msg.Header.Len)
  175. byteOrder.PutUint16(b[4:6], msg.Header.Type)
  176. byteOrder.PutUint16(b[6:8], msg.Header.Flags)
  177. byteOrder.PutUint32(b[8:12], msg.Header.Seq)
  178. byteOrder.PutUint32(b[12:16], msg.Header.Pid)
  179. copy(b[16:], msg.Data)
  180. return b
  181. }
  182. // Request messages.
  183. var sizeofInetDiagReq = int(unsafe.Sizeof(InetDiagReq{}))
  184. // InetDiagReq (inet_diag_req) is used to request diagnostic data from older
  185. // kernels.
  186. // https://github.com/torvalds/linux/blob/v4.0/include/uapi/linux/inet_diag.h#L25
  187. type InetDiagReq struct {
  188. Family uint8
  189. SrcLen uint8
  190. DstLen uint8
  191. Ext uint8
  192. ID InetDiagSockID
  193. States uint32 // States to dump.
  194. DBs uint32 // Tables to dump.
  195. }
  196. func (r InetDiagReq) toWireFormat() []byte {
  197. buf := bytes.NewBuffer(make([]byte, sizeofInetDiagReq))
  198. buf.Reset()
  199. if err := binary.Write(buf, byteOrder, r); err != nil {
  200. // This never returns an error.
  201. panic(err)
  202. }
  203. return buf.Bytes()
  204. }
  205. // NewInetDiagReq returns a new NetlinkMessage whose payload is an InetDiagReq.
  206. // Callers should set their own sequence number in the returned message header.
  207. func NewInetDiagReq() syscall.NetlinkMessage {
  208. hdr := syscall.NlMsghdr{
  209. Type: uint16(TCPDIAG_GETSOCK),
  210. Flags: uint16(syscall.NLM_F_DUMP | syscall.NLM_F_REQUEST),
  211. Pid: uint32(0),
  212. }
  213. req := InetDiagReq{
  214. Family: uint8(AF_INET), // This returns both ipv4 and ipv6.
  215. States: AllTCPStates,
  216. }
  217. return syscall.NetlinkMessage{Header: hdr, Data: req.toWireFormat()}
  218. }
  219. // V2 Request
  220. var sizeofInetDiagReqV2 = int(unsafe.Sizeof(InetDiagReqV2{}))
  221. // InetDiagReqV2 (inet_diag_req_v2) is used to request diagnostic data.
  222. // https://github.com/torvalds/linux/blob/v4.0/include/uapi/linux/inet_diag.h#L37
  223. type InetDiagReqV2 struct {
  224. Family uint8
  225. Protocol uint8
  226. Ext uint8
  227. Pad uint8
  228. States uint32
  229. ID InetDiagSockID
  230. }
  231. func (r InetDiagReqV2) toWireFormat() []byte {
  232. buf := bytes.NewBuffer(make([]byte, sizeofInetDiagReqV2))
  233. buf.Reset()
  234. if err := binary.Write(buf, byteOrder, r); err != nil {
  235. // This never returns an error.
  236. panic(err)
  237. }
  238. return buf.Bytes()
  239. }
  240. // NewInetDiagReqV2 returns a new NetlinkMessage whose payload is an
  241. // InetDiagReqV2. Callers should set their own sequence number in the returned
  242. // message header.
  243. func NewInetDiagReqV2(af AddressFamily) syscall.NetlinkMessage {
  244. hdr := syscall.NlMsghdr{
  245. Type: uint16(SOCK_DIAG_BY_FAMILY),
  246. Flags: uint16(syscall.NLM_F_DUMP | syscall.NLM_F_REQUEST),
  247. Pid: uint32(0),
  248. }
  249. req := InetDiagReqV2{
  250. Family: uint8(af),
  251. Protocol: syscall.IPPROTO_TCP,
  252. States: AllTCPStates,
  253. }
  254. return syscall.NetlinkMessage{Header: hdr, Data: req.toWireFormat()}
  255. }
  256. // Response messages.
  257. // InetDiagMsg (inet_diag_msg) is the base info structure. It contains socket
  258. // identity (addrs/ports/cookie) and the information shown by netstat.
  259. // https://github.com/torvalds/linux/blob/v4.0/include/uapi/linux/inet_diag.h#L86
  260. type InetDiagMsg struct {
  261. Family uint8 // Address family.
  262. State uint8 // TCP State
  263. Timer uint8
  264. Retrans uint8
  265. ID InetDiagSockID
  266. Expires uint32
  267. RQueue uint32 // Recv-Q
  268. WQueue uint32 // Send-Q
  269. UID uint32 // UID
  270. Inode uint32 // Inode of socket.
  271. }
  272. // ParseInetDiagMsg parse an InetDiagMsg from a byte slice. It assumes the
  273. // InetDiagMsg starts at the beginning of b. Invoke this method to parse the
  274. // payload of a netlink response.
  275. func ParseInetDiagMsg(b []byte) (*InetDiagMsg, error) {
  276. r := bytes.NewReader(b)
  277. inetDiagMsg := &InetDiagMsg{}
  278. err := binary.Read(r, byteOrder, inetDiagMsg)
  279. if err != nil {
  280. return nil, errors.Wrap(err, "failed to unmarshal inet_diag_msg")
  281. }
  282. return inetDiagMsg, nil
  283. }
  284. // SrcPort returns the source (local) port.
  285. func (m InetDiagMsg) SrcPort() int { return int(binary.BigEndian.Uint16(m.ID.SPort[:])) }
  286. // DstPort returns the destination (remote) port.
  287. func (m InetDiagMsg) DstPort() int { return int(binary.BigEndian.Uint16(m.ID.DPort[:])) }
  288. // SrcIP returns the source (local) IP.
  289. func (m InetDiagMsg) SrcIP() net.IP { return ip(m.ID.Src, AddressFamily(m.Family)) }
  290. // DstIP returns the destination (remote) IP.
  291. func (m InetDiagMsg) DstIP() net.IP { return ip(m.ID.Dst, AddressFamily(m.Family)) }
  292. func (m InetDiagMsg) srcIPBytes() []byte { return ipBytes(m.ID.Src, AddressFamily(m.Family)) }
  293. func (m InetDiagMsg) dstIPBytes() []byte { return ipBytes(m.ID.Dst, AddressFamily(m.Family)) }
  294. func ip(data [16]byte, af AddressFamily) net.IP {
  295. if af == AF_INET {
  296. return net.IPv4(data[0], data[1], data[2], data[3])
  297. }
  298. return net.IP(data[:])
  299. }
  300. func ipBytes(data [16]byte, af AddressFamily) []byte {
  301. if af == AF_INET {
  302. return data[:4]
  303. }
  304. return data[:]
  305. }
  306. // FastHash returns a hash calculated using FNV-1 of the source and destination
  307. // addresses.
  308. func (m *InetDiagMsg) FastHash() uint64 {
  309. // Hash using FNV-1 algorithm.
  310. h := fnv.New64()
  311. h.Write(m.srcIPBytes()) // Must trim non-zero garbage from ipv4 buffers.
  312. h.Write(m.dstIPBytes())
  313. h.Write(m.ID.SPort[:])
  314. h.Write(m.ID.DPort[:])
  315. return h.Sum64()
  316. }
  317. // InetDiagSockID (inet_diag_sockid) contains the socket identity.
  318. // https://github.com/torvalds/linux/blob/v4.0/include/uapi/linux/inet_diag.h#L13
  319. type InetDiagSockID struct {
  320. SPort [2]byte // Source port (big-endian).
  321. DPort [2]byte // Destination port (big-endian).
  322. Src [16]byte // Source IP
  323. Dst [16]byte // Destination IP
  324. If uint32
  325. Cookie [2]uint32
  326. }