1// Copyright 2018 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	"internal/poll"
9	"internal/syscall/unix"
10	"syscall"
11	"unsafe"
12)
13
14type rawSockaddrDatalink struct {
15	Len    uint8
16	Family uint8
17	Index  uint16
18	Type   uint8
19	Nlen   uint8
20	Alen   uint8
21	Slen   uint8
22	Data   [120]byte
23}
24
25type ifreq struct {
26	Name [16]uint8
27	Ifru [16]byte
28}
29
30const _KINFO_RT_IFLIST = (0x1 << 8) | 3 | (1 << 30)
31
32const _RTAX_NETMASK = 2
33const _RTAX_IFA = 5
34const _RTAX_MAX = 8
35
36func getIfList() ([]byte, error) {
37	needed, err := syscall.Getkerninfo(_KINFO_RT_IFLIST, 0, 0, 0)
38	if err != nil {
39		return nil, err
40	}
41	tab := make([]byte, needed)
42	_, err = syscall.Getkerninfo(_KINFO_RT_IFLIST, uintptr(unsafe.Pointer(&tab[0])), uintptr(unsafe.Pointer(&needed)), 0)
43	if err != nil {
44		return nil, err
45	}
46	return tab[:needed], nil
47}
48
49// If the ifindex is zero, interfaceTable returns mappings of all
50// network interfaces. Otherwise it returns a mapping of a specific
51// interface.
52func interfaceTable(ifindex int) ([]Interface, error) {
53	tab, err := getIfList()
54	if err != nil {
55		return nil, err
56	}
57
58	sock, err := sysSocket(syscall.AF_INET, syscall.SOCK_DGRAM, 0)
59	if err != nil {
60		return nil, err
61	}
62	defer poll.CloseFunc(sock)
63
64	var ift []Interface
65	for len(tab) > 0 {
66		ifm := (*syscall.IfMsgHdr)(unsafe.Pointer(&tab[0]))
67		if ifm.Msglen == 0 {
68			break
69		}
70		if ifm.Type == syscall.RTM_IFINFO {
71			if ifindex == 0 || ifindex == int(ifm.Index) {
72				sdl := (*rawSockaddrDatalink)(unsafe.Pointer(&tab[syscall.SizeofIfMsghdr]))
73
74				ifi := &Interface{Index: int(ifm.Index), Flags: linkFlags(ifm.Flags)}
75				ifi.Name = string(sdl.Data[:sdl.Nlen])
76				ifi.HardwareAddr = sdl.Data[sdl.Nlen : sdl.Nlen+sdl.Alen]
77
78				// Retrieve MTU
79				ifr := &ifreq{}
80				copy(ifr.Name[:], ifi.Name)
81				err = unix.Ioctl(sock, syscall.SIOCGIFMTU, unsafe.Pointer(ifr))
82				if err != nil {
83					return nil, err
84				}
85				ifi.MTU = int(ifr.Ifru[0])<<24 | int(ifr.Ifru[1])<<16 | int(ifr.Ifru[2])<<8 | int(ifr.Ifru[3])
86
87				ift = append(ift, *ifi)
88				if ifindex == int(ifm.Index) {
89					break
90				}
91			}
92		}
93		tab = tab[ifm.Msglen:]
94	}
95
96	return ift, nil
97}
98
99func linkFlags(rawFlags int32) Flags {
100	var f Flags
101	if rawFlags&syscall.IFF_UP != 0 {
102		f |= FlagUp
103	}
104	if rawFlags&syscall.IFF_RUNNING != 0 {
105		f |= FlagRunning
106	}
107	if rawFlags&syscall.IFF_BROADCAST != 0 {
108		f |= FlagBroadcast
109	}
110	if rawFlags&syscall.IFF_LOOPBACK != 0 {
111		f |= FlagLoopback
112	}
113	if rawFlags&syscall.IFF_POINTOPOINT != 0 {
114		f |= FlagPointToPoint
115	}
116	if rawFlags&syscall.IFF_MULTICAST != 0 {
117		f |= FlagMulticast
118	}
119	return f
120}
121
122// If the ifi is nil, interfaceAddrTable returns addresses for all
123// network interfaces. Otherwise it returns addresses for a specific
124// interface.
125func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
126	tab, err := getIfList()
127	if err != nil {
128		return nil, err
129	}
130
131	var ifat []Addr
132	for len(tab) > 0 {
133		ifm := (*syscall.IfMsgHdr)(unsafe.Pointer(&tab[0]))
134		if ifm.Msglen == 0 {
135			break
136		}
137		if ifm.Type == syscall.RTM_NEWADDR {
138			if ifi == nil || ifi.Index == int(ifm.Index) {
139				mask := ifm.Addrs
140				off := uint(syscall.SizeofIfMsghdr)
141
142				var iprsa, nmrsa *syscall.RawSockaddr
143				for i := uint(0); i < _RTAX_MAX; i++ {
144					if mask&(1<<i) == 0 {
145						continue
146					}
147					rsa := (*syscall.RawSockaddr)(unsafe.Pointer(&tab[off]))
148					if i == _RTAX_NETMASK {
149						nmrsa = rsa
150					}
151					if i == _RTAX_IFA {
152						iprsa = rsa
153					}
154					off += (uint(rsa.Len) + 3) &^ 3
155				}
156				if iprsa != nil && nmrsa != nil {
157					var mask IPMask
158					var ip IP
159
160					switch iprsa.Family {
161					case syscall.AF_INET:
162						ipsa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(iprsa))
163						nmsa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(nmrsa))
164						ip = IPv4(ipsa.Addr[0], ipsa.Addr[1], ipsa.Addr[2], ipsa.Addr[3])
165						mask = IPv4Mask(nmsa.Addr[0], nmsa.Addr[1], nmsa.Addr[2], nmsa.Addr[3])
166					case syscall.AF_INET6:
167						ipsa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(iprsa))
168						nmsa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(nmrsa))
169						ip = make(IP, IPv6len)
170						copy(ip, ipsa.Addr[:])
171						mask = make(IPMask, IPv6len)
172						copy(mask, nmsa.Addr[:])
173					}
174					ifa := &IPNet{IP: ip, Mask: mask}
175					ifat = append(ifat, ifa)
176				}
177			}
178		}
179		tab = tab[ifm.Msglen:]
180	}
181
182	return ifat, nil
183}
184
185// interfaceMulticastAddrTable returns addresses for a specific
186// interface.
187func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
188	return nil, nil
189}
190