1// Copyright 2016 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package net 6 7import ( 8 "errors" 9 "os" 10) 11 12// If the ifindex is zero, interfaceTable returns mappings of all 13// network interfaces. Otherwise it returns a mapping of a specific 14// interface. 15func interfaceTable(ifindex int) ([]Interface, error) { 16 if ifindex == 0 { 17 n, err := interfaceCount() 18 if err != nil { 19 return nil, err 20 } 21 ifcs := make([]Interface, n) 22 for i := range ifcs { 23 ifc, err := readInterface(i) 24 if err != nil { 25 return nil, err 26 } 27 ifcs[i] = *ifc 28 } 29 return ifcs, nil 30 } 31 32 ifc, err := readInterface(ifindex - 1) 33 if err != nil { 34 return nil, err 35 } 36 return []Interface{*ifc}, nil 37} 38 39func readInterface(i int) (*Interface, error) { 40 ifc := &Interface{ 41 Index: i + 1, // Offset the index by one to suit the contract 42 Name: netdir + "/ipifc/" + itoa(i), // Name is the full path to the interface path in plan9 43 } 44 45 ifcstat := ifc.Name + "/status" 46 ifcstatf, err := open(ifcstat) 47 if err != nil { 48 return nil, err 49 } 50 defer ifcstatf.close() 51 52 line, ok := ifcstatf.readLine() 53 if !ok { 54 return nil, errors.New("invalid interface status file: " + ifcstat) 55 } 56 57 fields := getFields(line) 58 if len(fields) < 4 { 59 return nil, errors.New("invalid interface status file: " + ifcstat) 60 } 61 62 device := fields[1] 63 mtustr := fields[3] 64 65 mtu, _, ok := dtoi(mtustr) 66 if !ok { 67 return nil, errors.New("invalid status file of interface: " + ifcstat) 68 } 69 ifc.MTU = mtu 70 71 // Not a loopback device 72 if device != "/dev/null" { 73 deviceaddrf, err := open(device + "/addr") 74 if err != nil { 75 return nil, err 76 } 77 defer deviceaddrf.close() 78 79 line, ok = deviceaddrf.readLine() 80 if !ok { 81 return nil, errors.New("invalid address file for interface: " + device + "/addr") 82 } 83 84 if len(line) > 0 && len(line)%2 == 0 { 85 ifc.HardwareAddr = make([]byte, len(line)/2) 86 var ok bool 87 for i := range ifc.HardwareAddr { 88 j := (i + 1) * 2 89 ifc.HardwareAddr[i], ok = xtoi2(line[i*2:j], 0) 90 if !ok { 91 ifc.HardwareAddr = ifc.HardwareAddr[:i] 92 break 93 } 94 } 95 } 96 97 ifc.Flags = FlagUp | FlagBroadcast | FlagMulticast 98 } else { 99 ifc.Flags = FlagUp | FlagMulticast | FlagLoopback 100 } 101 102 return ifc, nil 103} 104 105func interfaceCount() (int, error) { 106 d, err := os.Open(netdir + "/ipifc") 107 if err != nil { 108 return -1, err 109 } 110 defer d.Close() 111 112 names, err := d.Readdirnames(0) 113 if err != nil { 114 return -1, err 115 } 116 117 // Assumes that numbered files in ipifc are strictly 118 // the incrementing numbered directories for the 119 // interfaces 120 c := 0 121 for _, name := range names { 122 if _, _, ok := dtoi(name); !ok { 123 continue 124 } 125 c++ 126 } 127 128 return c, nil 129} 130 131// If the ifi is nil, interfaceAddrTable returns addresses for all 132// network interfaces. Otherwise it returns addresses for a specific 133// interface. 134func interfaceAddrTable(ifi *Interface) ([]Addr, error) { 135 var ifcs []Interface 136 if ifi == nil { 137 var err error 138 ifcs, err = interfaceTable(0) 139 if err != nil { 140 return nil, err 141 } 142 } else { 143 ifcs = []Interface{*ifi} 144 } 145 146 var addrs []Addr 147 for _, ifc := range ifcs { 148 status := ifc.Name + "/status" 149 statusf, err := open(status) 150 if err != nil { 151 return nil, err 152 } 153 defer statusf.close() 154 155 // Read but ignore first line as it only contains the table header. 156 // See https://9p.io/magic/man2html/3/ip 157 if _, ok := statusf.readLine(); !ok { 158 return nil, errors.New("cannot read header line for interface: " + status) 159 } 160 161 for line, ok := statusf.readLine(); ok; line, ok = statusf.readLine() { 162 fields := getFields(line) 163 if len(fields) < 1 { 164 return nil, errors.New("cannot parse IP address for interface: " + status) 165 } 166 addr := fields[0] 167 ip := ParseIP(addr) 168 if ip == nil { 169 return nil, errors.New("cannot parse IP address for interface: " + status) 170 } 171 172 // The mask is represented as CIDR relative to the IPv6 address. 173 // Plan 9 internal representation is always IPv6. 174 maskfld := fields[1] 175 maskfld = maskfld[1:] 176 pfxlen, _, ok := dtoi(maskfld) 177 if !ok { 178 return nil, errors.New("cannot parse network mask for interface: " + status) 179 } 180 var mask IPMask 181 if ip.To4() != nil { // IPv4 or IPv6 IPv4-mapped address 182 mask = CIDRMask(pfxlen-8*len(v4InV6Prefix), 8*IPv4len) 183 } 184 if ip.To16() != nil && ip.To4() == nil { // IPv6 address 185 mask = CIDRMask(pfxlen, 8*IPv6len) 186 } 187 188 addrs = append(addrs, &IPNet{IP: ip, Mask: mask}) 189 } 190 } 191 192 return addrs, nil 193} 194 195// interfaceMulticastAddrTable returns addresses for a specific 196// interface. 197func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) { 198 return nil, nil 199} 200