interface_plan9.mx raw
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
5 package net
6
7 import (
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.
17 func interfaceTable(ifindex int) ([]Interface, error) {
18 if ifindex == 0 {
19 n, err := interfaceCount()
20 if err != nil {
21 return nil, err
22 }
23 ifcs := []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
41 func 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
61 // If the interface has no device file then we see two spaces between "device" and
62 // "maxtu" and and getFields treats the two spaces as one delimiter.
63 // Insert a gap for the missing device name.
64 // See https://go.dev/issue/72060.
65 if stringslite.HasPrefix(line, "device maxtu ") {
66 fields = append(fields, "")
67 copy(fields[2:], fields[1:])
68 fields[1] = ""
69 }
70
71 if len(fields) < 4 {
72 return nil, errors.New("invalid interface status file: " + ifcstat)
73 }
74
75 device := fields[1]
76 mtustr := fields[3]
77
78 mtu, _, ok := dtoi(mtustr)
79 if !ok {
80 return nil, errors.New("invalid status file of interface: " + ifcstat)
81 }
82 ifc.MTU = mtu
83
84 // Not a loopback device ("/dev/null") or packet interface (e.g. "pkt2")
85 if stringslite.HasPrefix(device, netdir+"/") {
86 deviceaddrf, err := open(device + "/addr")
87 if err != nil {
88 return nil, err
89 }
90 defer deviceaddrf.close()
91
92 line, ok = deviceaddrf.readLine()
93 if !ok {
94 return nil, errors.New("invalid address file for interface: " + device + "/addr")
95 }
96
97 if len(line) > 0 && len(line)%2 == 0 {
98 ifc.HardwareAddr = []byte{:len(line)/2}
99 var ok bool
100 for i := range ifc.HardwareAddr {
101 j := (i + 1) * 2
102 ifc.HardwareAddr[i], ok = xtoi2(line[i*2:j], 0)
103 if !ok {
104 ifc.HardwareAddr = ifc.HardwareAddr[:i]
105 break
106 }
107 }
108 }
109
110 ifc.Flags = FlagUp | FlagRunning | FlagBroadcast | FlagMulticast
111 } else {
112 ifc.Flags = FlagUp | FlagRunning | FlagMulticast | FlagLoopback
113 }
114
115 return ifc, nil
116 }
117
118 func interfaceCount() (int, error) {
119 d, err := os.Open(netdir + "/ipifc")
120 if err != nil {
121 return -1, err
122 }
123 defer d.Close()
124
125 names, err := d.Readdirnames(0)
126 if err != nil {
127 return -1, err
128 }
129
130 // Assumes that numbered files in ipifc are strictly
131 // the incrementing numbered directories for the
132 // interfaces
133 c := 0
134 for _, name := range names {
135 if _, _, ok := dtoi(name); !ok {
136 continue
137 }
138 c++
139 }
140
141 return c, nil
142 }
143
144 // If the ifi is nil, interfaceAddrTable returns addresses for all
145 // network interfaces. Otherwise it returns addresses for a specific
146 // interface.
147 func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
148 var ifcs []Interface
149 if ifi == nil {
150 var err error
151 ifcs, err = interfaceTable(0)
152 if err != nil {
153 return nil, err
154 }
155 } else {
156 ifcs = []Interface{*ifi}
157 }
158
159 var addrs []Addr
160 for _, ifc := range ifcs {
161 status := ifc.Name + "/status"
162 statusf, err := open(status)
163 if err != nil {
164 return nil, err
165 }
166 defer statusf.close()
167
168 // Read but ignore first line as it only contains the table header.
169 // See https://9p.io/magic/man2html/3/ip
170 if _, ok := statusf.readLine(); !ok {
171 return nil, errors.New("cannot read header line for interface: " + status)
172 }
173
174 for line, ok := statusf.readLine(); ok; line, ok = statusf.readLine() {
175 fields := getFields(line)
176 if len(fields) < 1 {
177 continue
178 }
179 addr := fields[0]
180 ip := ParseIP(addr)
181 if ip == nil {
182 return nil, errors.New("cannot parse IP address for interface: " + status)
183 }
184
185 // The mask is represented as CIDR relative to the IPv6 address.
186 // Plan 9 internal representation is always IPv6.
187 maskfld := fields[1]
188 maskfld = maskfld[1:]
189 pfxlen, _, ok := dtoi(maskfld)
190 if !ok {
191 return nil, errors.New("cannot parse network mask for interface: " + status)
192 }
193 var mask IPMask
194 if ip.To4() != nil { // IPv4 or IPv6 IPv4-mapped address
195 mask = CIDRMask(pfxlen-8*len(v4InV6Prefix), 8*IPv4len)
196 }
197 if ip.To16() != nil && ip.To4() == nil { // IPv6 address
198 mask = CIDRMask(pfxlen, 8*IPv6len)
199 }
200
201 addrs = append(addrs, &IPNet{IP: ip, Mask: mask})
202 }
203 }
204
205 return addrs, nil
206 }
207
208 // interfaceMulticastAddrTable returns addresses for a specific
209 // interface.
210 func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
211 return nil, nil
212 }
213