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