1// Copyright 2011 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	"sync"
10	"time"
11)
12
13// BUG(mikio): On JS, methods and functions related to
14// Interface are not implemented.
15
16// BUG(mikio): On AIX, DragonFly BSD, NetBSD, OpenBSD, Plan 9 and
17// Solaris, the MulticastAddrs method of Interface is not implemented.
18
19var (
20	errInvalidInterface         = errors.New("invalid network interface")
21	errInvalidInterfaceIndex    = errors.New("invalid network interface index")
22	errInvalidInterfaceName     = errors.New("invalid network interface name")
23	errNoSuchInterface          = errors.New("no such network interface")
24	errNoSuchMulticastInterface = errors.New("no such multicast network interface")
25)
26
27// Interface represents a mapping between network interface name
28// and index. It also represents network interface facility
29// information.
30type Interface struct {
31	Index        int          // positive integer that starts at one, zero is never used
32	MTU          int          // maximum transmission unit
33	Name         string       // e.g., "en0", "lo0", "eth0.100"
34	HardwareAddr HardwareAddr // IEEE MAC-48, EUI-48 and EUI-64 form
35	Flags        Flags        // e.g., FlagUp, FlagLoopback, FlagMulticast
36}
37
38type Flags uint
39
40const (
41	FlagUp           Flags = 1 << iota // interface is up
42	FlagBroadcast                      // interface supports broadcast access capability
43	FlagLoopback                       // interface is a loopback interface
44	FlagPointToPoint                   // interface belongs to a point-to-point link
45	FlagMulticast                      // interface supports multicast access capability
46)
47
48var flagNames = []string{
49	"up",
50	"broadcast",
51	"loopback",
52	"pointtopoint",
53	"multicast",
54}
55
56func (f Flags) String() string {
57	s := ""
58	for i, name := range flagNames {
59		if f&(1<<uint(i)) != 0 {
60			if s != "" {
61				s += "|"
62			}
63			s += name
64		}
65	}
66	if s == "" {
67		s = "0"
68	}
69	return s
70}
71
72// Addrs returns a list of unicast interface addresses for a specific
73// interface.
74func (ifi *Interface) Addrs() ([]Addr, error) {
75	if ifi == nil {
76		return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterface}
77	}
78	ifat, err := interfaceAddrTable(ifi)
79	if err != nil {
80		err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
81	}
82	return ifat, err
83}
84
85// MulticastAddrs returns a list of multicast, joined group addresses
86// for a specific interface.
87func (ifi *Interface) MulticastAddrs() ([]Addr, error) {
88	if ifi == nil {
89		return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterface}
90	}
91	ifat, err := interfaceMulticastAddrTable(ifi)
92	if err != nil {
93		err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
94	}
95	return ifat, err
96}
97
98// Interfaces returns a list of the system's network interfaces.
99func Interfaces() ([]Interface, error) {
100	ift, err := interfaceTable(0)
101	if err != nil {
102		return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
103	}
104	if len(ift) != 0 {
105		zoneCache.update(ift, false)
106	}
107	return ift, nil
108}
109
110// InterfaceAddrs returns a list of the system's unicast interface
111// addresses.
112//
113// The returned list does not identify the associated interface; use
114// Interfaces and Interface.Addrs for more detail.
115func InterfaceAddrs() ([]Addr, error) {
116	ifat, err := interfaceAddrTable(nil)
117	if err != nil {
118		err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
119	}
120	return ifat, err
121}
122
123// InterfaceByIndex returns the interface specified by index.
124//
125// On Solaris, it returns one of the logical network interfaces
126// sharing the logical data link; for more precision use
127// InterfaceByName.
128func InterfaceByIndex(index int) (*Interface, error) {
129	if index <= 0 {
130		return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterfaceIndex}
131	}
132	ift, err := interfaceTable(index)
133	if err != nil {
134		return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
135	}
136	ifi, err := interfaceByIndex(ift, index)
137	if err != nil {
138		err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
139	}
140	return ifi, err
141}
142
143func interfaceByIndex(ift []Interface, index int) (*Interface, error) {
144	for _, ifi := range ift {
145		if index == ifi.Index {
146			return &ifi, nil
147		}
148	}
149	return nil, errNoSuchInterface
150}
151
152// InterfaceByName returns the interface specified by name.
153func InterfaceByName(name string) (*Interface, error) {
154	if name == "" {
155		return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterfaceName}
156	}
157	ift, err := interfaceTable(0)
158	if err != nil {
159		return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
160	}
161	if len(ift) != 0 {
162		zoneCache.update(ift, false)
163	}
164	for _, ifi := range ift {
165		if name == ifi.Name {
166			return &ifi, nil
167		}
168	}
169	return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errNoSuchInterface}
170}
171
172// An ipv6ZoneCache represents a cache holding partial network
173// interface information. It is used for reducing the cost of IPv6
174// addressing scope zone resolution.
175//
176// Multiple names sharing the index are managed by first-come
177// first-served basis for consistency.
178type ipv6ZoneCache struct {
179	sync.RWMutex                // guard the following
180	lastFetched  time.Time      // last time routing information was fetched
181	toIndex      map[string]int // interface name to its index
182	toName       map[int]string // interface index to its name
183}
184
185var zoneCache = ipv6ZoneCache{
186	toIndex: make(map[string]int),
187	toName:  make(map[int]string),
188}
189
190// update refreshes the network interface information if the cache was last
191// updated more than 1 minute ago, or if force is set. It reports whether the
192// cache was updated.
193func (zc *ipv6ZoneCache) update(ift []Interface, force bool) (updated bool) {
194	zc.Lock()
195	defer zc.Unlock()
196	now := time.Now()
197	if !force && zc.lastFetched.After(now.Add(-60*time.Second)) {
198		return false
199	}
200	zc.lastFetched = now
201	if len(ift) == 0 {
202		var err error
203		if ift, err = interfaceTable(0); err != nil {
204			return false
205		}
206	}
207	zc.toIndex = make(map[string]int, len(ift))
208	zc.toName = make(map[int]string, len(ift))
209	for _, ifi := range ift {
210		zc.toIndex[ifi.Name] = ifi.Index
211		if _, ok := zc.toName[ifi.Index]; !ok {
212			zc.toName[ifi.Index] = ifi.Name
213		}
214	}
215	return true
216}
217
218func (zc *ipv6ZoneCache) name(index int) string {
219	if index == 0 {
220		return ""
221	}
222	updated := zoneCache.update(nil, false)
223	zoneCache.RLock()
224	name, ok := zoneCache.toName[index]
225	zoneCache.RUnlock()
226	if !ok && !updated {
227		zoneCache.update(nil, true)
228		zoneCache.RLock()
229		name, ok = zoneCache.toName[index]
230		zoneCache.RUnlock()
231	}
232	if !ok { // last resort
233		name = uitoa(uint(index))
234	}
235	return name
236}
237
238func (zc *ipv6ZoneCache) index(name string) int {
239	if name == "" {
240		return 0
241	}
242	updated := zoneCache.update(nil, false)
243	zoneCache.RLock()
244	index, ok := zoneCache.toIndex[name]
245	zoneCache.RUnlock()
246	if !ok && !updated {
247		zoneCache.update(nil, true)
248		zoneCache.RLock()
249		index, ok = zoneCache.toIndex[name]
250		zoneCache.RUnlock()
251	}
252	if !ok { // last resort
253		index, _, _ = dtoi(name)
254	}
255	return index
256}
257