github.com/isyscore/isc-gobase@v1.5.3-0.20231218061332-cbc7451899e9/system/net/net_linux.go (about)

     1  //go:build linux
     2  
     3  package net
     4  
     5  import (
     6  	"bytes"
     7  	"context"
     8  	"encoding/hex"
     9  	"errors"
    10  	"fmt"
    11  	"github.com/isyscore/isc-gobase/system/common"
    12  	"io"
    13  	"io/ioutil"
    14  	"net"
    15  	"os"
    16  	"strconv"
    17  	"strings"
    18  	"syscall"
    19  )
    20  
    21  const ( // Conntrack Column numbers
    22  	CT_ENTRIES = iota
    23  	CT_SEARCHED
    24  	CT_FOUND
    25  	CT_NEW
    26  	CT_INVALID
    27  	CT_IGNORE
    28  	CT_DELETE
    29  	CT_DELETE_LIST
    30  	CT_INSERT
    31  	CT_INSERT_FAILED
    32  	CT_DROP
    33  	CT_EARLY_DROP
    34  	CT_ICMP_ERROR
    35  	CT_EXPECT_NEW
    36  	CT_EXPECT_CREATE
    37  	CT_EXPECT_DELETE
    38  	CT_SEARCH_RESTART
    39  )
    40  
    41  // NetIOCounters returnes network I/O statistics for every network
    42  // interface installed on the system.  If pernic argument is false,
    43  // return only sum of all information (which name is 'all'). If true,
    44  // every network interface installed on the system is returned
    45  // separately.
    46  func IOCounters(pernic bool) ([]IOCountersStat, error) {
    47  	return IOCountersWithContext(context.Background(), pernic)
    48  }
    49  
    50  func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
    51  	filename := common.HostProc("net/dev")
    52  	return IOCountersByFileWithContext(ctx, pernic, filename)
    53  }
    54  
    55  func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
    56  	return IOCountersByFileWithContext(context.Background(), pernic, filename)
    57  }
    58  
    59  func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) {
    60  	lines, err := common.ReadLines(filename)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	parts := make([]string, 2)
    66  
    67  	statlen := len(lines) - 1
    68  
    69  	ret := make([]IOCountersStat, 0, statlen)
    70  
    71  	for _, line := range lines[2:] {
    72  		separatorPos := strings.LastIndex(line, ":")
    73  		if separatorPos == -1 {
    74  			continue
    75  		}
    76  		parts[0] = line[0:separatorPos]
    77  		parts[1] = line[separatorPos+1:]
    78  
    79  		interfaceName := strings.TrimSpace(parts[0])
    80  		if interfaceName == "" {
    81  			continue
    82  		}
    83  
    84  		fields := strings.Fields(strings.TrimSpace(parts[1]))
    85  		bytesRecv, err := strconv.ParseUint(fields[0], 10, 64)
    86  		if err != nil {
    87  			return ret, err
    88  		}
    89  		packetsRecv, err := strconv.ParseUint(fields[1], 10, 64)
    90  		if err != nil {
    91  			return ret, err
    92  		}
    93  		errIn, err := strconv.ParseUint(fields[2], 10, 64)
    94  		if err != nil {
    95  			return ret, err
    96  		}
    97  		dropIn, err := strconv.ParseUint(fields[3], 10, 64)
    98  		if err != nil {
    99  			return ret, err
   100  		}
   101  		fifoIn, err := strconv.ParseUint(fields[4], 10, 64)
   102  		if err != nil {
   103  			return ret, err
   104  		}
   105  		bytesSent, err := strconv.ParseUint(fields[8], 10, 64)
   106  		if err != nil {
   107  			return ret, err
   108  		}
   109  		packetsSent, err := strconv.ParseUint(fields[9], 10, 64)
   110  		if err != nil {
   111  			return ret, err
   112  		}
   113  		errOut, err := strconv.ParseUint(fields[10], 10, 64)
   114  		if err != nil {
   115  			return ret, err
   116  		}
   117  		dropOut, err := strconv.ParseUint(fields[11], 10, 64)
   118  		if err != nil {
   119  			return ret, err
   120  		}
   121  		fifoOut, err := strconv.ParseUint(fields[12], 10, 64)
   122  		if err != nil {
   123  			return ret, err
   124  		}
   125  
   126  		nic := IOCountersStat{
   127  			Name:        interfaceName,
   128  			BytesRecv:   bytesRecv,
   129  			PacketsRecv: packetsRecv,
   130  			Errin:       errIn,
   131  			Dropin:      dropIn,
   132  			Fifoin:      fifoIn,
   133  			BytesSent:   bytesSent,
   134  			PacketsSent: packetsSent,
   135  			Errout:      errOut,
   136  			Dropout:     dropOut,
   137  			Fifoout:     fifoOut,
   138  		}
   139  		ret = append(ret, nic)
   140  	}
   141  
   142  	if pernic == false {
   143  		return getIOCountersAll(ret)
   144  	}
   145  
   146  	return ret, nil
   147  }
   148  
   149  var netProtocols = []string{
   150  	"ip",
   151  	"icmp",
   152  	"icmpmsg",
   153  	"tcp",
   154  	"udp",
   155  	"udplite",
   156  }
   157  
   158  // NetProtoCounters returns network statistics for the entire system
   159  // If protocols is empty then all protocols are returned, otherwise
   160  // just the protocols in the list are returned.
   161  // Available protocols:
   162  //   ip,icmp,icmpmsg,tcp,udp,udplite
   163  func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
   164  	return ProtoCountersWithContext(context.Background(), protocols)
   165  }
   166  
   167  func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
   168  	if len(protocols) == 0 {
   169  		protocols = netProtocols
   170  	}
   171  
   172  	stats := make([]ProtoCountersStat, 0, len(protocols))
   173  	protos := make(map[string]bool, len(protocols))
   174  	for _, p := range protocols {
   175  		protos[p] = true
   176  	}
   177  
   178  	filename := common.HostProc("net/snmp")
   179  	lines, err := common.ReadLines(filename)
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  
   184  	linecount := len(lines)
   185  	for i := 0; i < linecount; i++ {
   186  		line := lines[i]
   187  		r := strings.IndexRune(line, ':')
   188  		if r == -1 {
   189  			return nil, errors.New(filename + " is not fomatted correctly, expected ':'.")
   190  		}
   191  		proto := strings.ToLower(line[:r])
   192  		if !protos[proto] {
   193  			// skip protocol and data line
   194  			i++
   195  			continue
   196  		}
   197  
   198  		// Read header line
   199  		statNames := strings.Split(line[r+2:], " ")
   200  
   201  		// Read data line
   202  		i++
   203  		statValues := strings.Split(lines[i][r+2:], " ")
   204  		if len(statNames) != len(statValues) {
   205  			return nil, errors.New(filename + " is not fomatted correctly, expected same number of columns.")
   206  		}
   207  		stat := ProtoCountersStat{
   208  			Protocol: proto,
   209  			Stats:    make(map[string]int64, len(statNames)),
   210  		}
   211  		for j := range statNames {
   212  			value, err := strconv.ParseInt(statValues[j], 10, 64)
   213  			if err != nil {
   214  				return nil, err
   215  			}
   216  			stat.Stats[statNames[j]] = value
   217  		}
   218  		stats = append(stats, stat)
   219  	}
   220  	return stats, nil
   221  }
   222  
   223  // NetFilterCounters returns iptables conntrack statistics
   224  // the currently in use conntrack count and the max.
   225  // If the file does not exist or is invalid it will return nil.
   226  func FilterCounters() ([]FilterStat, error) {
   227  	return FilterCountersWithContext(context.Background())
   228  }
   229  
   230  func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
   231  	countfile := common.HostProc("sys/net/netfilter/nf_conntrack_count")
   232  	maxfile := common.HostProc("sys/net/netfilter/nf_conntrack_max")
   233  
   234  	count, err := common.ReadInts(countfile)
   235  
   236  	if err != nil {
   237  		return nil, err
   238  	}
   239  	stats := make([]FilterStat, 0, 1)
   240  
   241  	max, err := common.ReadInts(maxfile)
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  
   246  	payload := FilterStat{
   247  		ConnTrackCount: count[0],
   248  		ConnTrackMax:   max[0],
   249  	}
   250  
   251  	stats = append(stats, payload)
   252  	return stats, nil
   253  }
   254  
   255  // ConntrackStats returns more detailed info about the conntrack table
   256  func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
   257  	return ConntrackStatsWithContext(context.Background(), percpu)
   258  }
   259  
   260  // ConntrackStatsWithContext returns more detailed info about the conntrack table
   261  func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
   262  	return conntrackStatsFromFile(common.HostProc("net/stat/nf_conntrack"), percpu)
   263  }
   264  
   265  // conntrackStatsFromFile returns more detailed info about the conntrack table
   266  // from `filename`
   267  // If 'percpu' is false, the result will contain exactly one item with totals/summary
   268  func conntrackStatsFromFile(filename string, percpu bool) ([]ConntrackStat, error) {
   269  	lines, err := common.ReadLines(filename)
   270  	if err != nil {
   271  		return nil, err
   272  	}
   273  
   274  	statlist := NewConntrackStatList()
   275  
   276  	for _, line := range lines {
   277  		fields := strings.Fields(line)
   278  		if len(fields) == 17 && fields[0] != "entries" {
   279  			statlist.Append(NewConntrackStat(
   280  				common.HexToUint32(fields[CT_ENTRIES]),
   281  				common.HexToUint32(fields[CT_SEARCHED]),
   282  				common.HexToUint32(fields[CT_FOUND]),
   283  				common.HexToUint32(fields[CT_NEW]),
   284  				common.HexToUint32(fields[CT_INVALID]),
   285  				common.HexToUint32(fields[CT_IGNORE]),
   286  				common.HexToUint32(fields[CT_DELETE]),
   287  				common.HexToUint32(fields[CT_DELETE_LIST]),
   288  				common.HexToUint32(fields[CT_INSERT]),
   289  				common.HexToUint32(fields[CT_INSERT_FAILED]),
   290  				common.HexToUint32(fields[CT_DROP]),
   291  				common.HexToUint32(fields[CT_EARLY_DROP]),
   292  				common.HexToUint32(fields[CT_ICMP_ERROR]),
   293  				common.HexToUint32(fields[CT_EXPECT_NEW]),
   294  				common.HexToUint32(fields[CT_EXPECT_CREATE]),
   295  				common.HexToUint32(fields[CT_EXPECT_DELETE]),
   296  				common.HexToUint32(fields[CT_SEARCH_RESTART]),
   297  			))
   298  		}
   299  	}
   300  
   301  	if percpu {
   302  		return statlist.Items(), nil
   303  	}
   304  	return statlist.Summary(), nil
   305  }
   306  
   307  // http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h
   308  var TCPStatuses = map[string]string{
   309  	"01": "ESTABLISHED",
   310  	"02": "SYN_SENT",
   311  	"03": "SYN_RECV",
   312  	"04": "FIN_WAIT1",
   313  	"05": "FIN_WAIT2",
   314  	"06": "TIME_WAIT",
   315  	"07": "CLOSE",
   316  	"08": "CLOSE_WAIT",
   317  	"09": "LAST_ACK",
   318  	"0A": "LISTEN",
   319  	"0B": "CLOSING",
   320  }
   321  
   322  type netConnectionKindType struct {
   323  	family   uint32
   324  	sockType uint32
   325  	filename string
   326  }
   327  
   328  var kindTCP4 = netConnectionKindType{
   329  	family:   syscall.AF_INET,
   330  	sockType: syscall.SOCK_STREAM,
   331  	filename: "tcp",
   332  }
   333  var kindTCP6 = netConnectionKindType{
   334  	family:   syscall.AF_INET6,
   335  	sockType: syscall.SOCK_STREAM,
   336  	filename: "tcp6",
   337  }
   338  var kindUDP4 = netConnectionKindType{
   339  	family:   syscall.AF_INET,
   340  	sockType: syscall.SOCK_DGRAM,
   341  	filename: "udp",
   342  }
   343  var kindUDP6 = netConnectionKindType{
   344  	family:   syscall.AF_INET6,
   345  	sockType: syscall.SOCK_DGRAM,
   346  	filename: "udp6",
   347  }
   348  var kindUNIX = netConnectionKindType{
   349  	family:   syscall.AF_UNIX,
   350  	filename: "unix",
   351  }
   352  
   353  var netConnectionKindMap = map[string][]netConnectionKindType{
   354  	"all":   {kindTCP4, kindTCP6, kindUDP4, kindUDP6, kindUNIX},
   355  	"tcp":   {kindTCP4, kindTCP6},
   356  	"tcp4":  {kindTCP4},
   357  	"tcp6":  {kindTCP6},
   358  	"udp":   {kindUDP4, kindUDP6},
   359  	"udp4":  {kindUDP4},
   360  	"udp6":  {kindUDP6},
   361  	"unix":  {kindUNIX},
   362  	"inet":  {kindTCP4, kindTCP6, kindUDP4, kindUDP6},
   363  	"inet4": {kindTCP4, kindUDP4},
   364  	"inet6": {kindTCP6, kindUDP6},
   365  }
   366  
   367  type inodeMap struct {
   368  	pid int32
   369  	fd  uint32
   370  }
   371  
   372  type connTmp struct {
   373  	fd       uint32
   374  	family   uint32
   375  	sockType uint32
   376  	laddr    Addr
   377  	raddr    Addr
   378  	status   string
   379  	pid      int32
   380  	boundPid int32
   381  	path     string
   382  }
   383  
   384  // Return a list of network connections opened.
   385  func Connections(kind string) ([]ConnectionStat, error) {
   386  	return ConnectionsWithContext(context.Background(), kind)
   387  }
   388  
   389  func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
   390  	return ConnectionsPid(kind, 0)
   391  }
   392  
   393  // Return a list of network connections opened returning at most `max`
   394  // connections for each running process.
   395  func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) {
   396  	return ConnectionsMaxWithContext(context.Background(), kind, max)
   397  }
   398  
   399  func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
   400  	return ConnectionsPidMax(kind, 0, max)
   401  }
   402  
   403  // Return a list of network connections opened, omitting `Uids`.
   404  // WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be
   405  // removed from the API in the future.
   406  func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) {
   407  	return ConnectionsWithoutUidsWithContext(context.Background(), kind)
   408  }
   409  
   410  func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
   411  	return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0)
   412  }
   413  
   414  func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
   415  	return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max)
   416  }
   417  
   418  // Return a list of network connections opened by a process.
   419  func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) {
   420  	return ConnectionsPidWithContext(context.Background(), kind, pid)
   421  }
   422  
   423  func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) {
   424  	return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid)
   425  }
   426  
   427  func ConnectionsPidWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
   428  	return ConnectionsPidMaxWithContext(ctx, kind, pid, 0)
   429  }
   430  
   431  func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
   432  	return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0)
   433  }
   434  
   435  // Return up to `max` network connections opened by a process.
   436  func ConnectionsPidMax(kind string, pid int32, max int) ([]ConnectionStat, error) {
   437  	return ConnectionsPidMaxWithContext(context.Background(), kind, pid, max)
   438  }
   439  
   440  func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) {
   441  	return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max)
   442  }
   443  
   444  func ConnectionsPidMaxWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
   445  	return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max, false)
   446  }
   447  
   448  func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
   449  	return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max, true)
   450  }
   451  
   452  func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int, skipUids bool) ([]ConnectionStat, error) {
   453  	tmap, ok := netConnectionKindMap[kind]
   454  	if !ok {
   455  		return nil, fmt.Errorf("invalid kind, %s", kind)
   456  	}
   457  	root := common.HostProc()
   458  	var err error
   459  	var inodes map[string][]inodeMap
   460  	if pid == 0 {
   461  		inodes, err = getProcInodesAll(root, max)
   462  	} else {
   463  		inodes, err = getProcInodes(root, pid, max)
   464  		if len(inodes) == 0 {
   465  			// no connection for the pid
   466  			return []ConnectionStat{}, nil
   467  		}
   468  	}
   469  	if err != nil {
   470  		return nil, fmt.Errorf("cound not get pid(s), %d: %s", pid, err)
   471  	}
   472  	return statsFromInodes(root, pid, tmap, inodes, skipUids)
   473  }
   474  
   475  func statsFromInodes(root string, pid int32, tmap []netConnectionKindType, inodes map[string][]inodeMap, skipUids bool) ([]ConnectionStat, error) {
   476  	dupCheckMap := make(map[string]struct{})
   477  	var ret []ConnectionStat
   478  
   479  	var err error
   480  	for _, t := range tmap {
   481  		var path string
   482  		var connKey string
   483  		var ls []connTmp
   484  		if pid == 0 {
   485  			path = fmt.Sprintf("%s/net/%s", root, t.filename)
   486  		} else {
   487  			path = fmt.Sprintf("%s/%d/net/%s", root, pid, t.filename)
   488  		}
   489  		switch t.family {
   490  		case syscall.AF_INET, syscall.AF_INET6:
   491  			ls, err = processInet(path, t, inodes, pid)
   492  		case syscall.AF_UNIX:
   493  			ls, err = processUnix(path, t, inodes, pid)
   494  		}
   495  		if err != nil {
   496  			return nil, err
   497  		}
   498  		for _, c := range ls {
   499  			// Build TCP key to id the connection uniquely
   500  			// socket type, src ip, src port, dst ip, dst port and state should be enough
   501  			// to prevent duplications.
   502  			connKey = fmt.Sprintf("%d-%s:%d-%s:%d-%s", c.sockType, c.laddr.IP, c.laddr.Port, c.raddr.IP, c.raddr.Port, c.status)
   503  			if _, ok := dupCheckMap[connKey]; ok {
   504  				continue
   505  			}
   506  
   507  			conn := ConnectionStat{
   508  				Fd:     c.fd,
   509  				Family: c.family,
   510  				Type:   c.sockType,
   511  				Laddr:  c.laddr,
   512  				Raddr:  c.raddr,
   513  				Status: c.status,
   514  				Pid:    c.pid,
   515  			}
   516  			if c.pid == 0 {
   517  				conn.Pid = c.boundPid
   518  			} else {
   519  				conn.Pid = c.pid
   520  			}
   521  
   522  			if !skipUids {
   523  				// fetch process owner Real, effective, saved set, and filesystem UIDs
   524  				proc := process{Pid: conn.Pid}
   525  				conn.Uids, _ = proc.getUids()
   526  			}
   527  
   528  			ret = append(ret, conn)
   529  			dupCheckMap[connKey] = struct{}{}
   530  		}
   531  
   532  	}
   533  
   534  	return ret, nil
   535  }
   536  
   537  // getProcInodes returnes fd of the pid.
   538  func getProcInodes(root string, pid int32, max int) (map[string][]inodeMap, error) {
   539  	ret := make(map[string][]inodeMap)
   540  
   541  	dir := fmt.Sprintf("%s/%d/fd", root, pid)
   542  	f, err := os.Open(dir)
   543  	if err != nil {
   544  		return ret, err
   545  	}
   546  	defer f.Close()
   547  	files, err := f.Readdir(max)
   548  	if err != nil {
   549  		return ret, err
   550  	}
   551  	for _, fd := range files {
   552  		inodePath := fmt.Sprintf("%s/%d/fd/%s", root, pid, fd.Name())
   553  
   554  		inode, err := os.Readlink(inodePath)
   555  		if err != nil {
   556  			continue
   557  		}
   558  		if !strings.HasPrefix(inode, "socket:[") {
   559  			continue
   560  		}
   561  		// the process is using a socket
   562  		l := len(inode)
   563  		inode = inode[8 : l-1]
   564  		_, ok := ret[inode]
   565  		if !ok {
   566  			ret[inode] = make([]inodeMap, 0)
   567  		}
   568  		fd, err := strconv.Atoi(fd.Name())
   569  		if err != nil {
   570  			continue
   571  		}
   572  
   573  		i := inodeMap{
   574  			pid: pid,
   575  			fd:  uint32(fd),
   576  		}
   577  		ret[inode] = append(ret[inode], i)
   578  	}
   579  	return ret, nil
   580  }
   581  
   582  // Pids retunres all pids.
   583  // Note: this is a copy of process_linux.Pids()
   584  // FIXME: Import process occures import cycle.
   585  // move to common made other platform breaking. Need consider.
   586  func Pids() ([]int32, error) {
   587  	return PidsWithContext(context.Background())
   588  }
   589  
   590  func PidsWithContext(ctx context.Context) ([]int32, error) {
   591  	var ret []int32
   592  
   593  	d, err := os.Open(common.HostProc())
   594  	if err != nil {
   595  		return nil, err
   596  	}
   597  	defer d.Close()
   598  
   599  	fnames, err := d.Readdirnames(-1)
   600  	if err != nil {
   601  		return nil, err
   602  	}
   603  	for _, fname := range fnames {
   604  		pid, err := strconv.ParseInt(fname, 10, 32)
   605  		if err != nil {
   606  			// if not numeric name, just skip
   607  			continue
   608  		}
   609  		ret = append(ret, int32(pid))
   610  	}
   611  
   612  	return ret, nil
   613  }
   614  
   615  // Note: the following is based off process_linux structs and methods
   616  // we need these to fetch the owner of a process ID
   617  // FIXME: Import process occures import cycle.
   618  // see remarks on pids()
   619  type process struct {
   620  	Pid  int32 `json:"pid"`
   621  	uids []int32
   622  }
   623  
   624  // Uids returns user ids of the process as a slice of the int
   625  func (p *process) getUids() ([]int32, error) {
   626  	err := p.fillFromStatus()
   627  	if err != nil {
   628  		return []int32{}, err
   629  	}
   630  	return p.uids, nil
   631  }
   632  
   633  // Get status from /proc/(pid)/status
   634  func (p *process) fillFromStatus() error {
   635  	pid := p.Pid
   636  	statPath := common.HostProc(strconv.Itoa(int(pid)), "status")
   637  	contents, err := ioutil.ReadFile(statPath)
   638  	if err != nil {
   639  		return err
   640  	}
   641  	lines := strings.Split(string(contents), "\n")
   642  	for _, line := range lines {
   643  		tabParts := strings.SplitN(line, "\t", 2)
   644  		if len(tabParts) < 2 {
   645  			continue
   646  		}
   647  		value := tabParts[1]
   648  		switch strings.TrimRight(tabParts[0], ":") {
   649  		case "Uid":
   650  			p.uids = make([]int32, 0, 4)
   651  			for _, i := range strings.Split(value, "\t") {
   652  				v, err := strconv.ParseInt(i, 10, 32)
   653  				if err != nil {
   654  					return err
   655  				}
   656  				p.uids = append(p.uids, int32(v))
   657  			}
   658  		}
   659  	}
   660  	return nil
   661  }
   662  
   663  func getProcInodesAll(root string, max int) (map[string][]inodeMap, error) {
   664  	pids, err := Pids()
   665  	if err != nil {
   666  		return nil, err
   667  	}
   668  	ret := make(map[string][]inodeMap)
   669  
   670  	for _, pid := range pids {
   671  		t, err := getProcInodes(root, pid, max)
   672  		if err != nil {
   673  			// skip if permission error or no longer exists
   674  			if os.IsPermission(err) || os.IsNotExist(err) || err == io.EOF {
   675  				continue
   676  			}
   677  			return ret, err
   678  		}
   679  		if len(t) == 0 {
   680  			continue
   681  		}
   682  		// TODO: update ret.
   683  		ret = updateMap(ret, t)
   684  	}
   685  	return ret, nil
   686  }
   687  
   688  // decodeAddress decode addresse represents addr in proc/net/*
   689  // ex:
   690  // "0500000A:0016" -> "10.0.0.5", 22
   691  // "0085002452100113070057A13F025401:0035" -> "2400:8500:1301:1052:a157:7:154:23f", 53
   692  func decodeAddress(family uint32, src string) (Addr, error) {
   693  	t := strings.Split(src, ":")
   694  	if len(t) != 2 {
   695  		return Addr{}, fmt.Errorf("does not contain port, %s", src)
   696  	}
   697  	addr := t[0]
   698  	port, err := strconv.ParseUint(t[1], 16, 16)
   699  	if err != nil {
   700  		return Addr{}, fmt.Errorf("invalid port, %s", src)
   701  	}
   702  	decoded, err := hex.DecodeString(addr)
   703  	if err != nil {
   704  		return Addr{}, fmt.Errorf("decode error, %s", err)
   705  	}
   706  	var ip net.IP
   707  	// Assumes this is little_endian
   708  	if family == syscall.AF_INET {
   709  		ip = net.IP(Reverse(decoded))
   710  	} else { // IPv6
   711  		ip, err = parseIPv6HexString(decoded)
   712  		if err != nil {
   713  			return Addr{}, err
   714  		}
   715  	}
   716  	return Addr{
   717  		IP:   ip.String(),
   718  		Port: uint32(port),
   719  	}, nil
   720  }
   721  
   722  // Reverse reverses array of bytes.
   723  func Reverse(s []byte) []byte {
   724  	return ReverseWithContext(context.Background(), s)
   725  }
   726  
   727  func ReverseWithContext(ctx context.Context, s []byte) []byte {
   728  	for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
   729  		s[i], s[j] = s[j], s[i]
   730  	}
   731  	return s
   732  }
   733  
   734  // parseIPv6HexString parse array of bytes to IPv6 string
   735  func parseIPv6HexString(src []byte) (net.IP, error) {
   736  	if len(src) != 16 {
   737  		return nil, fmt.Errorf("invalid IPv6 string")
   738  	}
   739  
   740  	buf := make([]byte, 0, 16)
   741  	for i := 0; i < len(src); i += 4 {
   742  		r := Reverse(src[i : i+4])
   743  		buf = append(buf, r...)
   744  	}
   745  	return net.IP(buf), nil
   746  }
   747  
   748  func processInet(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) {
   749  
   750  	if strings.HasSuffix(file, "6") && !common.PathExists(file) {
   751  		// IPv6 not supported, return empty.
   752  		return []connTmp{}, nil
   753  	}
   754  
   755  	// Read the contents of the /proc file with a single read sys call.
   756  	// This minimizes duplicates in the returned connections
   757  	contents, err := ioutil.ReadFile(file)
   758  	if err != nil {
   759  		return nil, err
   760  	}
   761  
   762  	lines := bytes.Split(contents, []byte("\n"))
   763  
   764  	var ret []connTmp
   765  	// skip first line
   766  	for _, line := range lines[1:] {
   767  		l := strings.Fields(string(line))
   768  		if len(l) < 10 {
   769  			continue
   770  		}
   771  		laddr := l[1]
   772  		raddr := l[2]
   773  		status := l[3]
   774  		inode := l[9]
   775  		pid := int32(0)
   776  		fd := uint32(0)
   777  		i, exists := inodes[inode]
   778  		if exists {
   779  			pid = i[0].pid
   780  			fd = i[0].fd
   781  		}
   782  		if filterPid > 0 && filterPid != pid {
   783  			continue
   784  		}
   785  		if kind.sockType == syscall.SOCK_STREAM {
   786  			status = TCPStatuses[status]
   787  		} else {
   788  			status = "NONE"
   789  		}
   790  		la, err := decodeAddress(kind.family, laddr)
   791  		if err != nil {
   792  			continue
   793  		}
   794  		ra, err := decodeAddress(kind.family, raddr)
   795  		if err != nil {
   796  			continue
   797  		}
   798  
   799  		ret = append(ret, connTmp{
   800  			fd:       fd,
   801  			family:   kind.family,
   802  			sockType: kind.sockType,
   803  			laddr:    la,
   804  			raddr:    ra,
   805  			status:   status,
   806  			pid:      pid,
   807  		})
   808  	}
   809  
   810  	return ret, nil
   811  }
   812  
   813  func processUnix(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) {
   814  	// Read the contents of the /proc file with a single read sys call.
   815  	// This minimizes duplicates in the returned connections
   816  	// For more info:
   817  	contents, err := ioutil.ReadFile(file)
   818  	if err != nil {
   819  		return nil, err
   820  	}
   821  
   822  	lines := bytes.Split(contents, []byte("\n"))
   823  
   824  	var ret []connTmp
   825  	// skip first line
   826  	for _, line := range lines[1:] {
   827  		tokens := strings.Fields(string(line))
   828  		if len(tokens) < 6 {
   829  			continue
   830  		}
   831  		st, err := strconv.Atoi(tokens[4])
   832  		if err != nil {
   833  			return nil, err
   834  		}
   835  
   836  		inode := tokens[6]
   837  
   838  		var pairs []inodeMap
   839  		pairs, exists := inodes[inode]
   840  		if !exists {
   841  			pairs = []inodeMap{
   842  				{},
   843  			}
   844  		}
   845  		for _, pair := range pairs {
   846  			if filterPid > 0 && filterPid != pair.pid {
   847  				continue
   848  			}
   849  			var path string
   850  			if len(tokens) == 8 {
   851  				path = tokens[len(tokens)-1]
   852  			}
   853  			ret = append(ret, connTmp{
   854  				fd:       pair.fd,
   855  				family:   kind.family,
   856  				sockType: uint32(st),
   857  				laddr: Addr{
   858  					IP: path,
   859  				},
   860  				pid:    pair.pid,
   861  				status: "NONE",
   862  				path:   path,
   863  			})
   864  		}
   865  	}
   866  
   867  	return ret, nil
   868  }
   869  
   870  func updateMap(src map[string][]inodeMap, add map[string][]inodeMap) map[string][]inodeMap {
   871  	for key, value := range add {
   872  		a, exists := src[key]
   873  		if !exists {
   874  			src[key] = value
   875  			continue
   876  		}
   877  		src[key] = append(a, value...)
   878  	}
   879  	return src
   880  }