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