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// +build darwin dragonfly freebsd netbsd openbsd
6
7package route
8
9import (
10	"fmt"
11	"os/exec"
12	"runtime"
13	"time"
14)
15
16func (m *RouteMessage) String() string {
17	return fmt.Sprintf("%s", addrAttrs(nativeEndian.Uint32(m.raw[12:16])))
18}
19
20func (m *InterfaceMessage) String() string {
21	var attrs addrAttrs
22	if runtime.GOOS == "openbsd" {
23		attrs = addrAttrs(nativeEndian.Uint32(m.raw[12:16]))
24	} else {
25		attrs = addrAttrs(nativeEndian.Uint32(m.raw[4:8]))
26	}
27	return fmt.Sprintf("%s", attrs)
28}
29
30func (m *InterfaceAddrMessage) String() string {
31	var attrs addrAttrs
32	if runtime.GOOS == "openbsd" {
33		attrs = addrAttrs(nativeEndian.Uint32(m.raw[12:16]))
34	} else {
35		attrs = addrAttrs(nativeEndian.Uint32(m.raw[4:8]))
36	}
37	return fmt.Sprintf("%s", attrs)
38}
39
40func (m *InterfaceMulticastAddrMessage) String() string {
41	return fmt.Sprintf("%s", addrAttrs(nativeEndian.Uint32(m.raw[4:8])))
42}
43
44func (m *InterfaceAnnounceMessage) String() string {
45	what := "<nil>"
46	switch m.What {
47	case 0:
48		what = "arrival"
49	case 1:
50		what = "departure"
51	}
52	return fmt.Sprintf("(%d %s %s)", m.Index, m.Name, what)
53}
54
55func (m *InterfaceMetrics) String() string {
56	return fmt.Sprintf("(type=%d mtu=%d)", m.Type, m.MTU)
57}
58
59func (m *RouteMetrics) String() string {
60	return fmt.Sprintf("(pmtu=%d)", m.PathMTU)
61}
62
63type addrAttrs uint
64
65var addrAttrNames = [...]string{
66	"dst",
67	"gateway",
68	"netmask",
69	"genmask",
70	"ifp",
71	"ifa",
72	"author",
73	"brd",
74	"df:mpls1-n:tag-o:src", // mpls1 for dragonfly, tag for netbsd, src for openbsd
75	"df:mpls2-o:srcmask",   // mpls2 for dragonfly, srcmask for openbsd
76	"df:mpls3-o:label",     // mpls3 for dragonfly, label for openbsd
77	"o:bfd",                // bfd for openbsd
78	"o:dns",                // dns for openbsd
79	"o:static",             // static for openbsd
80	"o:search",             // search for openbsd
81}
82
83func (attrs addrAttrs) String() string {
84	var s string
85	for i, name := range addrAttrNames {
86		if attrs&(1<<uint(i)) != 0 {
87			if s != "" {
88				s += "|"
89			}
90			s += name
91		}
92	}
93	if s == "" {
94		return "<nil>"
95	}
96	return s
97}
98
99type msgs []Message
100
101func (ms msgs) validate() ([]string, error) {
102	var ss []string
103	for _, m := range ms {
104		switch m := m.(type) {
105		case *RouteMessage:
106			if err := addrs(m.Addrs).match(addrAttrs(nativeEndian.Uint32(m.raw[12:16]))); err != nil {
107				return nil, err
108			}
109			sys := m.Sys()
110			if sys == nil {
111				return nil, fmt.Errorf("no sys for %s", m.String())
112			}
113			ss = append(ss, m.String()+" "+syss(sys).String()+" "+addrs(m.Addrs).String())
114		case *InterfaceMessage:
115			var attrs addrAttrs
116			if runtime.GOOS == "openbsd" {
117				attrs = addrAttrs(nativeEndian.Uint32(m.raw[12:16]))
118			} else {
119				attrs = addrAttrs(nativeEndian.Uint32(m.raw[4:8]))
120			}
121			if err := addrs(m.Addrs).match(attrs); err != nil {
122				return nil, err
123			}
124			sys := m.Sys()
125			if sys == nil {
126				return nil, fmt.Errorf("no sys for %s", m.String())
127			}
128			ss = append(ss, m.String()+" "+syss(sys).String()+" "+addrs(m.Addrs).String())
129		case *InterfaceAddrMessage:
130			var attrs addrAttrs
131			if runtime.GOOS == "openbsd" {
132				attrs = addrAttrs(nativeEndian.Uint32(m.raw[12:16]))
133			} else {
134				attrs = addrAttrs(nativeEndian.Uint32(m.raw[4:8]))
135			}
136			if err := addrs(m.Addrs).match(attrs); err != nil {
137				return nil, err
138			}
139			ss = append(ss, m.String()+" "+addrs(m.Addrs).String())
140		case *InterfaceMulticastAddrMessage:
141			if err := addrs(m.Addrs).match(addrAttrs(nativeEndian.Uint32(m.raw[4:8]))); err != nil {
142				return nil, err
143			}
144			ss = append(ss, m.String()+" "+addrs(m.Addrs).String())
145		case *InterfaceAnnounceMessage:
146			ss = append(ss, m.String())
147		default:
148			ss = append(ss, fmt.Sprintf("%+v", m))
149		}
150	}
151	return ss, nil
152}
153
154type syss []Sys
155
156func (sys syss) String() string {
157	var s string
158	for _, sy := range sys {
159		switch sy := sy.(type) {
160		case *InterfaceMetrics:
161			if len(s) > 0 {
162				s += " "
163			}
164			s += sy.String()
165		case *RouteMetrics:
166			if len(s) > 0 {
167				s += " "
168			}
169			s += sy.String()
170		}
171	}
172	return s
173}
174
175type addrFamily int
176
177func (af addrFamily) String() string {
178	switch af {
179	case sysAF_UNSPEC:
180		return "unspec"
181	case sysAF_LINK:
182		return "link"
183	case sysAF_INET:
184		return "inet4"
185	case sysAF_INET6:
186		return "inet6"
187	default:
188		return fmt.Sprintf("%d", af)
189	}
190}
191
192const hexDigit = "0123456789abcdef"
193
194type llAddr []byte
195
196func (a llAddr) String() string {
197	if len(a) == 0 {
198		return ""
199	}
200	buf := make([]byte, 0, len(a)*3-1)
201	for i, b := range a {
202		if i > 0 {
203			buf = append(buf, ':')
204		}
205		buf = append(buf, hexDigit[b>>4])
206		buf = append(buf, hexDigit[b&0xF])
207	}
208	return string(buf)
209}
210
211type ipAddr []byte
212
213func (a ipAddr) String() string {
214	if len(a) == 0 {
215		return "<nil>"
216	}
217	if len(a) == 4 {
218		return fmt.Sprintf("%d.%d.%d.%d", a[0], a[1], a[2], a[3])
219	}
220	if len(a) == 16 {
221		return fmt.Sprintf("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15])
222	}
223	s := make([]byte, len(a)*2)
224	for i, tn := range a {
225		s[i*2], s[i*2+1] = hexDigit[tn>>4], hexDigit[tn&0xf]
226	}
227	return string(s)
228}
229
230func (a *LinkAddr) String() string {
231	name := a.Name
232	if name == "" {
233		name = "<nil>"
234	}
235	lla := llAddr(a.Addr).String()
236	if lla == "" {
237		lla = "<nil>"
238	}
239	return fmt.Sprintf("(%v %d %s %s)", addrFamily(a.Family()), a.Index, name, lla)
240}
241
242func (a *Inet4Addr) String() string {
243	return fmt.Sprintf("(%v %v)", addrFamily(a.Family()), ipAddr(a.IP[:]))
244}
245
246func (a *Inet6Addr) String() string {
247	return fmt.Sprintf("(%v %v %d)", addrFamily(a.Family()), ipAddr(a.IP[:]), a.ZoneID)
248}
249
250func (a *DefaultAddr) String() string {
251	return fmt.Sprintf("(%v %s)", addrFamily(a.Family()), ipAddr(a.Raw[2:]).String())
252}
253
254type addrs []Addr
255
256func (as addrs) String() string {
257	var s string
258	for _, a := range as {
259		if a == nil {
260			continue
261		}
262		if len(s) > 0 {
263			s += " "
264		}
265		switch a := a.(type) {
266		case *LinkAddr:
267			s += a.String()
268		case *Inet4Addr:
269			s += a.String()
270		case *Inet6Addr:
271			s += a.String()
272		case *DefaultAddr:
273			s += a.String()
274		}
275	}
276	if s == "" {
277		return "<nil>"
278	}
279	return s
280}
281
282func (as addrs) match(attrs addrAttrs) error {
283	var ts addrAttrs
284	af := sysAF_UNSPEC
285	for i := range as {
286		if as[i] != nil {
287			ts |= 1 << uint(i)
288		}
289		switch as[i].(type) {
290		case *Inet4Addr:
291			if af == sysAF_UNSPEC {
292				af = sysAF_INET
293			}
294			if af != sysAF_INET {
295				return fmt.Errorf("got %v; want %v", addrs(as), addrFamily(af))
296			}
297		case *Inet6Addr:
298			if af == sysAF_UNSPEC {
299				af = sysAF_INET6
300			}
301			if af != sysAF_INET6 {
302				return fmt.Errorf("got %v; want %v", addrs(as), addrFamily(af))
303			}
304		}
305	}
306	if ts != attrs && ts > attrs {
307		return fmt.Errorf("%v not included in %v", ts, attrs)
308	}
309	return nil
310}
311
312func fetchAndParseRIB(af int, typ RIBType) ([]Message, error) {
313	var err error
314	var b []byte
315	for i := 0; i < 3; i++ {
316		if b, err = FetchRIB(af, typ, 0); err != nil {
317			time.Sleep(10 * time.Millisecond)
318			continue
319		}
320		break
321	}
322	if err != nil {
323		return nil, fmt.Errorf("%v %d %v", addrFamily(af), typ, err)
324	}
325	ms, err := ParseRIB(typ, b)
326	if err != nil {
327		return nil, fmt.Errorf("%v %d %v", addrFamily(af), typ, err)
328	}
329	return ms, nil
330}
331
332// propVirtual is a proprietary virtual network interface.
333type propVirtual struct {
334	name         string
335	addr, mask   string
336	setupCmds    []*exec.Cmd
337	teardownCmds []*exec.Cmd
338}
339
340func (pv *propVirtual) setup() error {
341	for _, cmd := range pv.setupCmds {
342		if err := cmd.Run(); err != nil {
343			pv.teardown()
344			return err
345		}
346	}
347	return nil
348}
349
350func (pv *propVirtual) teardown() error {
351	for _, cmd := range pv.teardownCmds {
352		if err := cmd.Run(); err != nil {
353			return err
354		}
355	}
356	return nil
357}
358
359func (pv *propVirtual) configure(suffix int) error {
360	if runtime.GOOS == "openbsd" {
361		pv.name = fmt.Sprintf("vether%d", suffix)
362	} else {
363		pv.name = fmt.Sprintf("vlan%d", suffix)
364	}
365	xname, err := exec.LookPath("ifconfig")
366	if err != nil {
367		return err
368	}
369	pv.setupCmds = append(pv.setupCmds, &exec.Cmd{
370		Path: xname,
371		Args: []string{"ifconfig", pv.name, "create"},
372	})
373	if runtime.GOOS == "netbsd" {
374		// NetBSD requires an underlying dot1Q-capable network
375		// interface.
376		pv.setupCmds = append(pv.setupCmds, &exec.Cmd{
377			Path: xname,
378			Args: []string{"ifconfig", pv.name, "vlan", fmt.Sprintf("%d", suffix&0xfff), "vlanif", "wm0"},
379		})
380	}
381	pv.setupCmds = append(pv.setupCmds, &exec.Cmd{
382		Path: xname,
383		Args: []string{"ifconfig", pv.name, "inet", pv.addr, "netmask", pv.mask},
384	})
385	pv.teardownCmds = append(pv.teardownCmds, &exec.Cmd{
386		Path: xname,
387		Args: []string{"ifconfig", pv.name, "destroy"},
388	})
389	return nil
390}
391