blob: c7bf8dd1a6dd0555c5626242143a71b332a695b9 [file] [log] [blame]
Mikio Harac84eff72015-01-21 20:17:41 +09001// Copyright 2015 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 icmp
6
7import (
8 "net"
9 "strings"
10
11 "golang.org/x/net/internal/iana"
12)
13
14const (
15 classInterfaceInfo = 2
16
17 afiIPv4 = 1
18 afiIPv6 = 2
19)
20
21const (
22 attrMTU = 1 << iota
23 attrName
24 attrIPAddr
25 attrIfIndex
26)
27
28// An InterfaceInfo represents interface and next-hop identification.
29type InterfaceInfo struct {
30 Class int // extension object class number
31 Type int // extension object sub-type
32 Interface *net.Interface
33 Addr *net.IPAddr
34}
35
36func (ifi *InterfaceInfo) nameLen() int {
37 if len(ifi.Interface.Name) > 63 {
38 return 64
39 }
40 l := 1 + len(ifi.Interface.Name)
41 return (l + 3) &^ 3
42}
43
44func (ifi *InterfaceInfo) attrsAndLen(proto int) (attrs, l int) {
45 l = 4
46 if ifi.Interface != nil && ifi.Interface.Index > 0 {
47 attrs |= attrIfIndex
48 l += 4
49 if len(ifi.Interface.Name) > 0 {
50 attrs |= attrName
51 l += ifi.nameLen()
52 }
53 if ifi.Interface.MTU > 0 {
54 attrs |= attrMTU
55 l += 4
56 }
57 }
58 if ifi.Addr != nil {
59 switch proto {
60 case iana.ProtocolICMP:
61 if ifi.Addr.IP.To4() != nil {
62 attrs |= attrIPAddr
63 l += 4 + net.IPv4len
64 }
65 case iana.ProtocolIPv6ICMP:
66 if ifi.Addr.IP.To16() != nil && ifi.Addr.IP.To4() == nil {
67 attrs |= attrIPAddr
68 l += 4 + net.IPv6len
69 }
70 }
71 }
72 return
73}
74
75// Len implements the Len method of Extension interface.
76func (ifi *InterfaceInfo) Len(proto int) int {
77 _, l := ifi.attrsAndLen(proto)
78 return l
79}
80
81// Marshal implements the Marshal method of Extension interface.
82func (ifi *InterfaceInfo) Marshal(proto int) ([]byte, error) {
83 attrs, l := ifi.attrsAndLen(proto)
84 b := make([]byte, l)
85 if err := ifi.marshal(proto, b, attrs, l); err != nil {
86 return nil, err
87 }
88 return b, nil
89}
90
91func (ifi *InterfaceInfo) marshal(proto int, b []byte, attrs, l int) error {
92 b[0], b[1] = byte(l>>8), byte(l)
93 b[2], b[3] = classInterfaceInfo, byte(ifi.Type)
94 for b = b[4:]; len(b) > 0 && attrs != 0; {
95 switch {
96 case attrs&attrIfIndex != 0:
97 b = ifi.marshalIfIndex(proto, b)
98 attrs &^= attrIfIndex
99 case attrs&attrIPAddr != 0:
100 b = ifi.marshalIPAddr(proto, b)
101 attrs &^= attrIPAddr
102 case attrs&attrName != 0:
103 b = ifi.marshalName(proto, b)
104 attrs &^= attrName
105 case attrs&attrMTU != 0:
106 b = ifi.marshalMTU(proto, b)
107 attrs &^= attrMTU
108 }
109 }
110 return nil
111}
112
113func (ifi *InterfaceInfo) marshalIfIndex(proto int, b []byte) []byte {
114 b[0], b[1], b[2], b[3] = byte(ifi.Interface.Index>>24), byte(ifi.Interface.Index>>16), byte(ifi.Interface.Index>>8), byte(ifi.Interface.Index)
115 return b[4:]
116}
117
118func (ifi *InterfaceInfo) parseIfIndex(b []byte) ([]byte, error) {
119 if len(b) < 4 {
120 return nil, errMessageTooShort
121 }
122 ifi.Interface.Index = int(b[0])<<24 | int(b[1])<<16 | int(b[2])<<8 | int(b[3])
123 return b[4:], nil
124}
125
126func (ifi *InterfaceInfo) marshalIPAddr(proto int, b []byte) []byte {
127 switch proto {
128 case iana.ProtocolICMP:
129 b[0], b[1] = byte(afiIPv4>>8), byte(afiIPv4)
130 copy(b[4:4+net.IPv4len], ifi.Addr.IP.To4())
131 b = b[4+net.IPv4len:]
132 case iana.ProtocolIPv6ICMP:
133 b[0], b[1] = byte(afiIPv6>>8), byte(afiIPv6)
134 copy(b[4:4+net.IPv6len], ifi.Addr.IP.To16())
135 b = b[4+net.IPv6len:]
136 }
137 return b
138}
139
140func (ifi *InterfaceInfo) parseIPAddr(b []byte) ([]byte, error) {
141 if len(b) < 4 {
142 return nil, errMessageTooShort
143 }
144 afi := int(b[0])<<8 | int(b[1])
145 b = b[4:]
146 switch afi {
147 case afiIPv4:
148 if len(b) < net.IPv4len {
149 return nil, errMessageTooShort
150 }
151 ifi.Addr.IP = make(net.IP, net.IPv4len)
152 copy(ifi.Addr.IP, b[:net.IPv4len])
153 b = b[net.IPv4len:]
154 case afiIPv6:
155 if len(b) < net.IPv6len {
156 return nil, errMessageTooShort
157 }
158 ifi.Addr.IP = make(net.IP, net.IPv6len)
159 copy(ifi.Addr.IP, b[:net.IPv6len])
160 b = b[net.IPv6len:]
161 }
162 return b, nil
163}
164
165func (ifi *InterfaceInfo) marshalName(proto int, b []byte) []byte {
166 l := byte(ifi.nameLen())
167 b[0] = l
168 copy(b[1:], []byte(ifi.Interface.Name))
169 return b[l:]
170}
171
172func (ifi *InterfaceInfo) parseName(b []byte) ([]byte, error) {
173 if 4 > len(b) || len(b) < int(b[0]) {
174 return nil, errMessageTooShort
175 }
176 l := int(b[0])
Mikio Harabdcab5d2015-05-26 12:22:57 +0900177 if l%4 != 0 || 4 > l || l > 64 {
178 return nil, errInvalidExtension
179 }
Mikio Harac84eff72015-01-21 20:17:41 +0900180 var name [63]byte
181 copy(name[:], b[1:l])
182 ifi.Interface.Name = strings.Trim(string(name[:]), "\000")
183 return b[l:], nil
184}
185
186func (ifi *InterfaceInfo) marshalMTU(proto int, b []byte) []byte {
187 b[0], b[1], b[2], b[3] = byte(ifi.Interface.MTU>>24), byte(ifi.Interface.MTU>>16), byte(ifi.Interface.MTU>>8), byte(ifi.Interface.MTU)
188 return b[4:]
189}
190
191func (ifi *InterfaceInfo) parseMTU(b []byte) ([]byte, error) {
192 if len(b) < 4 {
193 return nil, errMessageTooShort
194 }
195 ifi.Interface.MTU = int(b[0])<<24 | int(b[1])<<16 | int(b[2])<<8 | int(b[3])
196 return b[4:], nil
197}
198
199func parseInterfaceInfo(b []byte) (Extension, error) {
200 ifi := &InterfaceInfo{
201 Class: int(b[2]),
202 Type: int(b[3]),
203 }
204 if ifi.Type&(attrIfIndex|attrName|attrMTU) != 0 {
205 ifi.Interface = &net.Interface{}
206 }
207 if ifi.Type&attrIPAddr != 0 {
208 ifi.Addr = &net.IPAddr{}
209 }
210 attrs := ifi.Type & (attrIfIndex | attrIPAddr | attrName | attrMTU)
211 for b = b[4:]; len(b) > 0 && attrs != 0; {
212 var err error
213 switch {
214 case attrs&attrIfIndex != 0:
215 b, err = ifi.parseIfIndex(b)
216 attrs &^= attrIfIndex
217 case attrs&attrIPAddr != 0:
218 b, err = ifi.parseIPAddr(b)
219 attrs &^= attrIPAddr
220 case attrs&attrName != 0:
221 b, err = ifi.parseName(b)
222 attrs &^= attrName
223 case attrs&attrMTU != 0:
224 b, err = ifi.parseMTU(b)
225 attrs &^= attrMTU
226 }
227 if err != nil {
228 return nil, err
229 }
230 }
231 if ifi.Interface != nil && ifi.Interface.Name != "" && ifi.Addr != nil && ifi.Addr.IP.To16() != nil && ifi.Addr.IP.To4() == nil {
232 ifi.Addr.Zone = ifi.Interface.Name
233 }
234 return ifi, nil
235}