1
0
mirror of https://github.com/shirou/gopsutil.git synced 2025-04-28 13:48:49 +08:00
shirou_gopsutil/net/net_linux.go

489 lines
11 KiB
Go
Raw Normal View History

2014-04-28 10:16:38 +09:00
// +build linux
2014-12-30 22:09:05 +09:00
package net
2014-04-28 10:16:38 +09:00
import (
"encoding/hex"
2015-11-19 14:19:43 -07:00
"errors"
"fmt"
"io/ioutil"
"net"
"os"
"strconv"
2014-04-28 10:16:38 +09:00
"strings"
"syscall"
2014-11-27 10:25:14 +09:00
"github.com/shirou/gopsutil/internal/common"
2014-04-28 10:16:38 +09:00
)
2015-01-01 21:29:25 +09:00
// NetIOCounters returnes network I/O statistics for every network
// interface installed on the system. If pernic argument is false,
// return only sum of all information (which name is 'all'). If true,
// every network interface installed on the system is returned
// separately.
func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) {
filename := common.HostProc("net/dev")
return NetIOCountersByFile(pernic, filename)
}
func NetIOCountersByFile(pernic bool, filename string) ([]NetIOCountersStat, error) {
2014-11-27 10:25:14 +09:00
lines, err := common.ReadLines(filename)
2014-04-29 14:59:22 +09:00
if err != nil {
return nil, err
2014-04-28 10:16:38 +09:00
}
statlen := len(lines) - 1
2014-04-30 16:16:07 +09:00
ret := make([]NetIOCountersStat, 0, statlen)
2014-04-28 10:16:38 +09:00
for _, line := range lines[2:] {
parts := strings.SplitN(line, ":", 2)
if len(parts) != 2 {
continue
}
interfaceName := strings.TrimSpace(parts[0])
if interfaceName == "" {
2014-04-28 10:16:38 +09:00
continue
}
fields := strings.Fields(strings.TrimSpace(parts[1]))
bytesRecv, err := strconv.ParseUint(fields[0], 10, 64)
if err != nil {
return ret, err
}
packetsRecv, err := strconv.ParseUint(fields[1], 10, 64)
if err != nil {
return ret, err
}
errIn, err := strconv.ParseUint(fields[2], 10, 64)
if err != nil {
return ret, err
}
dropIn, err := strconv.ParseUint(fields[3], 10, 64)
if err != nil {
return ret, err
}
bytesSent, err := strconv.ParseUint(fields[8], 10, 64)
if err != nil {
return ret, err
}
packetsSent, err := strconv.ParseUint(fields[9], 10, 64)
if err != nil {
return ret, err
}
errOut, err := strconv.ParseUint(fields[10], 10, 64)
if err != nil {
return ret, err
}
dropOut, err := strconv.ParseUint(fields[13], 10, 64)
if err != nil {
return ret, err
}
2014-04-30 16:16:07 +09:00
nic := NetIOCountersStat{
Name: interfaceName,
BytesRecv: bytesRecv,
PacketsRecv: packetsRecv,
Errin: errIn,
Dropin: dropIn,
BytesSent: bytesSent,
PacketsSent: packetsSent,
Errout: errOut,
Dropout: dropOut,
2014-04-28 10:16:38 +09:00
}
ret = append(ret, nic)
}
2015-01-01 21:29:25 +09:00
if pernic == false {
return getNetIOCountersAll(ret)
}
2014-04-28 10:16:38 +09:00
return ret, nil
}
2015-11-19 14:19:43 -07:00
var netProtocols = []string{
"ip",
"icmp",
"icmpmsg",
"tcp",
"udp",
"udplite",
}
// NetProtoCounters returns network statistics for the entire system
// If protocols is empty then all protocols are returned, otherwise
// just the protocols in the list are returned.
// Available protocols:
// ip,icmp,icmpmsg,tcp,udp,udplite
func NetProtoCounters(protocols []string) ([]NetProtoCountersStat, error) {
if len(protocols) == 0 {
protocols = netProtocols
}
stats := make([]NetProtoCountersStat, 0, len(protocols))
protos := make(map[string]bool, len(protocols))
for _, p := range protocols {
protos[p] = true
}
filename := common.HostProc("net/snmp")
2015-11-19 14:19:43 -07:00
lines, err := common.ReadLines(filename)
if err != nil {
return nil, err
}
linecount := len(lines)
for i := 0; i < linecount; i++ {
line := lines[i]
r := strings.IndexRune(line, ':')
if r == -1 {
return nil, errors.New(filename + " is not fomatted correctly, expected ':'.")
}
proto := strings.ToLower(line[:r])
if !protos[proto] {
// skip protocol and data line
i++
continue
}
// Read header line
statNames := strings.Split(line[r+2:], " ")
// Read data line
i++
statValues := strings.Split(lines[i][r+2:], " ")
if len(statNames) != len(statValues) {
return nil, errors.New(filename + " is not fomatted correctly, expected same number of columns.")
}
stat := NetProtoCountersStat{
Protocol: proto,
Stats: make(map[string]int64, len(statNames)),
}
for j := range statNames {
value, err := strconv.ParseInt(statValues[j], 10, 64)
if err != nil {
return nil, err
}
stat.Stats[statNames[j]] = value
}
stats = append(stats, stat)
}
return stats, nil
}
// NetFilterCounters returns iptables conntrack statistics
// the currently in use conntrack count and the max.
// If the file does not exist or is invalid it will return nil.
2015-12-08 09:32:45 +11:00
func NetFilterCounters() ([]NetFilterStat, error) {
countfile := common.HostProc("sys/net/netfilter/nf_conntrack_count")
maxfile := common.HostProc("sys/net/netfilter/nf_conntrack_max")
2015-12-08 09:32:45 +11:00
count, err := common.ReadInts(countfile)
if err != nil {
return nil, err
}
2015-12-08 09:32:45 +11:00
stats := make([]NetFilterStat, 0, 1)
2015-12-08 09:32:45 +11:00
max, err := common.ReadInts(maxfile)
if err != nil {
return nil, err
}
2015-12-08 09:32:45 +11:00
payload := NetFilterStat{
ConnTrackCount: count[0],
ConnTrackMax: max[0],
}
2015-12-08 09:32:45 +11:00
stats = append(stats, payload)
return stats, nil
}
type netConnectionKindType struct {
family int
sockType int
}
var KindTCP4 = netConnectionKindType{
family: syscall.AF_INET,
sockType: syscall.SOCK_STREAM,
}
var KindTCP6 = netConnectionKindType{
family: syscall.AF_INET6,
sockType: syscall.SOCK_STREAM,
}
var KindUDP4 = netConnectionKindType{
family: syscall.AF_INET,
sockType: syscall.SOCK_DGRAM,
}
var KindUDP6 = netConnectionKindType{
family: syscall.AF_INET6,
sockType: syscall.SOCK_DGRAM,
}
var KindUNIX = netConnectionKindType{
family: syscall.AF_UNIX,
}
var netConnectionKindMap = map[string][]netConnectionKindType{
"all": []netConnectionKindType{KindTCP4, KindTCP6, KindUDP4, KindUDP6, KindUNIX},
"tcp": []netConnectionKindType{KindTCP4, KindTCP6},
"tcp4": []netConnectionKindType{KindTCP4},
"tcp6": []netConnectionKindType{KindTCP6},
"udp": []netConnectionKindType{KindUDP4, KindUDP6},
"udp4": []netConnectionKindType{KindUDP4},
"udp6": []netConnectionKindType{KindUDP6},
"unix": []netConnectionKindType{KindUNIX},
"inet": []netConnectionKindType{KindTCP4, KindTCP6, KindUDP4, KindUDP6},
"inet4": []netConnectionKindType{KindTCP4, KindUDP4},
"inet6": []netConnectionKindType{KindTCP6, KindUDP6},
}
type inodeMap struct {
pid int32
fd string
}
type bb struct {
fd int
family int
sockType int
laddr string
raddr string
status string
bound_pid int32
}
// Return a list of network connections opened.
func NetConnections(kind string) ([]NetConnectionStat, error) {
return NetConnectionsPid(kind, 0)
}
// Return a list of network connections opened by a process.
func NetConnectionsPid(kind string, pid int32) ([]NetConnectionStat, error) {
tmap, ok := netConnectionKindMap[kind]
if !ok {
return nil, fmt.Errorf("invalid kind, %s", kind)
}
root := common.HostProc()
var err error
var inodes map[string][]inodeMap
if pid == 0 {
inodes, err = getProcInodesAll(root)
} else {
inodes, err = getProcInodes(root, pid)
}
if err != nil {
return nil, fmt.Errorf("cound not get pid(s), %d", pid)
}
for _, t := range tmap {
fmt.Println(t)
fmt.Println(inodes)
var path string
var ls []string
switch t.family {
case syscall.AF_INET:
path = fmt.Sprintf("%d/net/%s", pid, "tcp")
ls, err = processInet(path, t, inodes, pid)
case syscall.AF_INET6:
path = fmt.Sprintf("%d/net/%s", pid, "tcp6")
ls, err = processInet(path, t, inodes, pid)
case syscall.AF_UNIX:
path = fmt.Sprintf("%d/net/%s", pid, "unix")
ls, err = processInet(path, t, inodes, pid)
}
if err != nil {
return nil, err
}
for _, sss := range ls {
fmt.Println(sss)
}
}
return []NetConnectionStat{}, nil
}
// getProcInodes returnes fd of the pid.
func getProcInodes(root string, pid int32) (map[string][]inodeMap, error) {
ret := make(map[string][]inodeMap)
dir := fmt.Sprintf("%s/%d/fd", root, pid)
files, err := ioutil.ReadDir(dir)
if err != nil {
return ret, nil
}
for _, fd := range files {
inodePath := fmt.Sprintf("%s/%d/fd/%s", root, pid, fd.Name())
inode, err := os.Readlink(inodePath)
if err != nil {
continue
}
fmt.Println(inodePath)
if strings.HasPrefix(inode, "socket:[") {
// the process is using a socket
l := len(inode)
inode = inode[8 : l-1]
}
_, ok := ret[inode]
if !ok {
ret[inode] = make([]inodeMap, 0)
}
i := inodeMap{
pid: pid,
fd: fd.Name(),
}
ret[inode] = append(ret[inode], i)
}
return ret, nil
}
// Pids retunres all pids.
// Note: this is a copy of process_linux.Pids()
// FIXME: Import process occures import cycle.
// move to common made other platform breaking. Need consider.
func Pids() ([]int32, error) {
var ret []int32
d, err := os.Open(common.HostProc())
if err != nil {
return nil, err
}
defer d.Close()
fnames, err := d.Readdirnames(-1)
if err != nil {
return nil, err
}
for _, fname := range fnames {
pid, err := strconv.ParseInt(fname, 10, 32)
if err != nil {
// if not numeric name, just skip
continue
}
ret = append(ret, int32(pid))
}
return ret, nil
}
func getProcInodesAll(root string) (map[string][]inodeMap, error) {
pids, err := Pids()
if err != nil {
return nil, err
}
ret := make(map[string][]inodeMap)
for _, pid := range pids {
t, err := getProcInodes(root, pid)
if err != nil {
return ret, err
}
if len(t) == 0 {
continue
}
// TODO: update ret.
fmt.Println(t)
}
return ret, nil
}
// decodeAddress decode addresse represents addr in proc/net/*
// ex:
// "0500000A:0016" -> "10.0.0.5", 22
// "0085002452100113070057A13F025401:0035" -> "2400:8500:1301:1052:a157:7:154:23f", 53
func decodeAddress(family int, src string) (net.IP, int, error) {
t := strings.Split(src, ":")
if len(t) != 2 {
return nil, 0, fmt.Errorf("does not contain port, %s", src)
}
addr := t[0]
port, err := strconv.ParseInt("0x"+t[1], 0, 64)
if err != nil {
return nil, 0, fmt.Errorf("invalid port, %s", src)
}
decoded, err := hex.DecodeString(addr)
if err != nil {
return nil, 0, fmt.Errorf("decode error:", err)
}
var ip net.IP
// Assumes this is little_endian
if family == syscall.AF_INET {
ip = net.IP(Reverse(decoded))
} else { // IPv6
ip, err = parseIPv6HexString(decoded)
if err != nil {
return nil, 0, err
}
}
return ip, int(port), nil
}
// Reverse reverses array of bytes.
func Reverse(s []byte) []byte {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
return s
}
// parseIPv6HexString parse array of bytes to IPv6 string
func parseIPv6HexString(src []byte) (net.IP, error) {
if len(src) != 16 {
return nil, fmt.Errorf("invalid IPv6 string")
}
buf := make([]byte, 0, 16)
for i := 0; i < len(src); i += 4 {
r := Reverse(src[i : i+4])
buf = append(buf, r...)
}
return net.IP(buf), nil
}
func processInet(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]string, error) {
if strings.HasSuffix(file, "6") && !common.PathExists(file) {
// IPv6 not supported, return empty.
return []string{}, nil
}
lines, err := common.ReadLines(file)
if err != nil {
return nil, err
}
// skip first line
for _, line := range lines[1:] {
l := strings.Fields(line)
if len(l) < 10 {
continue
}
laddr := l[1]
raddr := l[2]
status := l[3]
inode, err := strconv.Atoi(l[9])
if err != nil {
continue
}
fmt.Println(laddr)
fmt.Println(raddr)
fmt.Println(status)
fmt.Println(inode)
}
return nil, nil
}
func processUnix(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]string, error) {
return nil, nil
}