OSDN Git Service

net, syscall: Use native endianness for GNU/Linux netlink code.
[pf3gnuchains/gcc-fork.git] / libgo / go / net / interface_linux.go
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
5 // Network interface identification for Linux
6
7 package net
8
9 import (
10         "os"
11         "syscall"
12         "unsafe"
13 )
14
15 // If the ifindex is zero, interfaceTable returns mappings of all
16 // network interfaces.  Otherwise it returns a mapping of a specific
17 // interface.
18 func interfaceTable(ifindex int) ([]Interface, error) {
19         tab, err := syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC)
20         if err != nil {
21                 return nil, os.NewSyscallError("netlink rib", err)
22         }
23
24         msgs, err := syscall.ParseNetlinkMessage(tab)
25         if err != nil {
26                 return nil, os.NewSyscallError("netlink message", err)
27         }
28
29         var ift []Interface
30         for _, m := range msgs {
31                 switch m.Header.Type {
32                 case syscall.NLMSG_DONE:
33                         goto done
34                 case syscall.RTM_NEWLINK:
35                         ifim := (*syscall.IfInfomsg)(unsafe.Pointer(&m.Data[0]))
36                         if ifindex == 0 || ifindex == int(ifim.Index) {
37                                 attrs, err := syscall.ParseNetlinkRouteAttr(&m)
38                                 if err != nil {
39                                         return nil, os.NewSyscallError("netlink routeattr", err)
40                                 }
41                                 ifi := newLink(ifim, attrs)
42                                 ift = append(ift, ifi)
43                         }
44                 }
45         }
46 done:
47         return ift, nil
48 }
49
50 func newLink(ifim *syscall.IfInfomsg, attrs []syscall.NetlinkRouteAttr) Interface {
51         ifi := Interface{Index: int(ifim.Index), Flags: linkFlags(ifim.Flags)}
52         for _, a := range attrs {
53                 switch a.Attr.Type {
54                 case syscall.IFLA_ADDRESS:
55                         var nonzero bool
56                         for _, b := range a.Value {
57                                 if b != 0 {
58                                         nonzero = true
59                                 }
60                         }
61                         if nonzero {
62                                 ifi.HardwareAddr = a.Value[:]
63                         }
64                 case syscall.IFLA_IFNAME:
65                         ifi.Name = string(a.Value[:len(a.Value)-1])
66                 case syscall.IFLA_MTU:
67                         ifi.MTU = int(*(*uint32)(unsafe.Pointer(&a.Value[:4][0])))
68                 }
69         }
70         return ifi
71 }
72
73 func linkFlags(rawFlags uint32) Flags {
74         var f Flags
75         if rawFlags&syscall.IFF_UP != 0 {
76                 f |= FlagUp
77         }
78         if rawFlags&syscall.IFF_BROADCAST != 0 {
79                 f |= FlagBroadcast
80         }
81         if rawFlags&syscall.IFF_LOOPBACK != 0 {
82                 f |= FlagLoopback
83         }
84         if rawFlags&syscall.IFF_POINTOPOINT != 0 {
85                 f |= FlagPointToPoint
86         }
87         if rawFlags&syscall.IFF_MULTICAST != 0 {
88                 f |= FlagMulticast
89         }
90         return f
91 }
92
93 // If the ifindex is zero, interfaceAddrTable returns addresses
94 // for all network interfaces.  Otherwise it returns addresses
95 // for a specific interface.
96 func interfaceAddrTable(ifindex int) ([]Addr, error) {
97         tab, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
98         if err != nil {
99                 return nil, os.NewSyscallError("netlink rib", err)
100         }
101
102         msgs, err := syscall.ParseNetlinkMessage(tab)
103         if err != nil {
104                 return nil, os.NewSyscallError("netlink message", err)
105         }
106
107         ifat, err := addrTable(msgs, ifindex)
108         if err != nil {
109                 return nil, err
110         }
111         return ifat, nil
112 }
113
114 func addrTable(msgs []syscall.NetlinkMessage, ifindex int) ([]Addr, error) {
115         var ifat []Addr
116         for _, m := range msgs {
117                 switch m.Header.Type {
118                 case syscall.NLMSG_DONE:
119                         goto done
120                 case syscall.RTM_NEWADDR:
121                         ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0]))
122                         if ifindex == 0 || ifindex == int(ifam.Index) {
123                                 attrs, err := syscall.ParseNetlinkRouteAttr(&m)
124                                 if err != nil {
125                                         return nil, os.NewSyscallError("netlink routeattr", err)
126                                 }
127                                 ifat = append(ifat, newAddr(attrs, int(ifam.Family), int(ifam.Prefixlen)))
128                         }
129                 }
130         }
131 done:
132         return ifat, nil
133 }
134
135 func newAddr(attrs []syscall.NetlinkRouteAttr, family, pfxlen int) Addr {
136         ifa := &IPNet{}
137         for _, a := range attrs {
138                 switch a.Attr.Type {
139                 case syscall.IFA_ADDRESS:
140                         switch family {
141                         case syscall.AF_INET:
142                                 ifa.IP = IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3])
143                                 ifa.Mask = CIDRMask(pfxlen, 8*IPv4len)
144                         case syscall.AF_INET6:
145                                 ifa.IP = make(IP, IPv6len)
146                                 copy(ifa.IP, a.Value[:])
147                                 ifa.Mask = CIDRMask(pfxlen, 8*IPv6len)
148                         }
149                 }
150         }
151         return ifa
152 }
153
154 // If the ifindex is zero, interfaceMulticastAddrTable returns
155 // addresses for all network interfaces.  Otherwise it returns
156 // addresses for a specific interface.
157 func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
158         var (
159                 err error
160                 ifi *Interface
161         )
162         if ifindex > 0 {
163                 ifi, err = InterfaceByIndex(ifindex)
164                 if err != nil {
165                         return nil, err
166                 }
167         }
168         ifmat4 := parseProcNetIGMP("/proc/net/igmp", ifi)
169         ifmat6 := parseProcNetIGMP6("/proc/net/igmp6", ifi)
170         return append(ifmat4, ifmat6...), nil
171 }
172
173 func parseProcNetIGMP(path string, ifi *Interface) []Addr {
174         fd, err := open(path)
175         if err != nil {
176                 return nil
177         }
178         defer fd.close()
179
180         var (
181                 ifmat []Addr
182                 name  string
183         )
184         fd.readLine() // skip first line
185         b := make([]byte, IPv4len)
186         for l, ok := fd.readLine(); ok; l, ok = fd.readLine() {
187                 f := splitAtBytes(l, " :\r\t\n")
188                 if len(f) < 4 {
189                         continue
190                 }
191                 switch {
192                 case l[0] != ' ' && l[0] != '\t': // new interface line
193                         name = f[1]
194                 case len(f[0]) == 8:
195                         if ifi == nil || name == ifi.Name {
196                                 // The Linux kernel puts the IP
197                                 // address in /proc/net/igmp in native
198                                 // endianness.
199                                 for i := 0; i+1 < len(f[0]); i += 2 {
200                                         b[i/2], _ = xtoi2(f[0][i:i+2], 0)
201                                 }
202                                 i := *(*uint32)(unsafe.Pointer(&b[:4][0]))
203                                 ifma := IPAddr{IP: IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i))}
204                                 ifmat = append(ifmat, ifma.toAddr())
205                         }
206                 }
207         }
208         return ifmat
209 }
210
211 func parseProcNetIGMP6(path string, ifi *Interface) []Addr {
212         fd, err := open(path)
213         if err != nil {
214                 return nil
215         }
216         defer fd.close()
217
218         var ifmat []Addr
219         b := make([]byte, IPv6len)
220         for l, ok := fd.readLine(); ok; l, ok = fd.readLine() {
221                 f := splitAtBytes(l, " \r\t\n")
222                 if len(f) < 6 {
223                         continue
224                 }
225                 if ifi == nil || f[1] == ifi.Name {
226                         for i := 0; i+1 < len(f[2]); i += 2 {
227                                 b[i/2], _ = xtoi2(f[2][i:i+2], 0)
228                         }
229                         ifma := IPAddr{IP: IP{b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]}}
230                         ifmat = append(ifmat, ifma.toAddr())
231                 }
232         }
233         return ifmat
234 }