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 }