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