1// 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
19const (
20	attrMTU = 1 << iota
21	attrName
22	attrIPAddr
23	attrIfIndex
24)
25
26// An InterfaceInfo represents interface and next-hop identification.
27type InterfaceInfo struct {
28	Class     int // extension object class number
29	Type      int // extension object sub-type
30	Interface *net.Interface
31	Addr      *net.IPAddr
32}
33
34func (ifi *InterfaceInfo) nameLen() int {
35	if len(ifi.Interface.Name) > 63 {
36		return 64
37	}
38	l := 1 + len(ifi.Interface.Name)
39	return (l + 3) &^ 3
40}
41
42func (ifi *InterfaceInfo) attrsAndLen(proto int) (attrs, l int) {
43	l = 4
44	if ifi.Interface != nil && ifi.Interface.Index > 0 {
45		attrs |= attrIfIndex
46		l += 4
47		if len(ifi.Interface.Name) > 0 {
48			attrs |= attrName
49			l += ifi.nameLen()
50		}
51		if ifi.Interface.MTU > 0 {
52			attrs |= attrMTU
53			l += 4
54		}
55	}
56	if ifi.Addr != nil {
57		switch proto {
58		case iana.ProtocolICMP:
59			if ifi.Addr.IP.To4() != nil {
60				attrs |= attrIPAddr
61				l += 4 + net.IPv4len
62			}
63		case iana.ProtocolIPv6ICMP:
64			if ifi.Addr.IP.To16() != nil && ifi.Addr.IP.To4() == nil {
65				attrs |= attrIPAddr
66				l += 4 + net.IPv6len
67			}
68		}
69	}
70	return
71}
72
73// Len implements the Len method of Extension interface.
74func (ifi *InterfaceInfo) Len(proto int) int {
75	_, l := ifi.attrsAndLen(proto)
76	return l
77}
78
79// Marshal implements the Marshal method of Extension interface.
80func (ifi *InterfaceInfo) Marshal(proto int) ([]byte, error) {
81	attrs, l := ifi.attrsAndLen(proto)
82	b := make([]byte, l)
83	if err := ifi.marshal(proto, b, attrs, l); err != nil {
84		return nil, err
85	}
86	return b, nil
87}
88
89func (ifi *InterfaceInfo) marshal(proto int, b []byte, attrs, l int) error {
90	binary.BigEndian.PutUint16(b[:2], uint16(l))
91	b[2], b[3] = classInterfaceInfo, byte(ifi.Type)
92	for b = b[4:]; len(b) > 0 && attrs != 0; {
93		switch {
94		case attrs&attrIfIndex != 0:
95			b = ifi.marshalIfIndex(proto, b)
96			attrs &^= attrIfIndex
97		case attrs&attrIPAddr != 0:
98			b = ifi.marshalIPAddr(proto, b)
99			attrs &^= attrIPAddr
100		case attrs&attrName != 0:
101			b = ifi.marshalName(proto, b)
102			attrs &^= attrName
103		case attrs&attrMTU != 0:
104			b = ifi.marshalMTU(proto, b)
105			attrs &^= attrMTU
106		}
107	}
108	return nil
109}
110
111func (ifi *InterfaceInfo) marshalIfIndex(proto int, b []byte) []byte {
112	binary.BigEndian.PutUint32(b[:4], uint32(ifi.Interface.Index))
113	return b[4:]
114}
115
116func (ifi *InterfaceInfo) parseIfIndex(b []byte) ([]byte, error) {
117	if len(b) < 4 {
118		return nil, errMessageTooShort
119	}
120	ifi.Interface.Index = int(binary.BigEndian.Uint32(b[:4]))
121	return b[4:], nil
122}
123
124func (ifi *InterfaceInfo) marshalIPAddr(proto int, b []byte) []byte {
125	switch proto {
126	case iana.ProtocolICMP:
127		binary.BigEndian.PutUint16(b[:2], uint16(iana.AddrFamilyIPv4))
128		copy(b[4:4+net.IPv4len], ifi.Addr.IP.To4())
129		b = b[4+net.IPv4len:]
130	case iana.ProtocolIPv6ICMP:
131		binary.BigEndian.PutUint16(b[:2], uint16(iana.AddrFamilyIPv6))
132		copy(b[4:4+net.IPv6len], ifi.Addr.IP.To16())
133		b = b[4+net.IPv6len:]
134	}
135	return b
136}
137
138func (ifi *InterfaceInfo) parseIPAddr(b []byte) ([]byte, error) {
139	if len(b) < 4 {
140		return nil, errMessageTooShort
141	}
142	afi := int(binary.BigEndian.Uint16(b[:2]))
143	b = b[4:]
144	switch afi {
145	case iana.AddrFamilyIPv4:
146		if len(b) < net.IPv4len {
147			return nil, errMessageTooShort
148		}
149		ifi.Addr.IP = make(net.IP, net.IPv4len)
150		copy(ifi.Addr.IP, b[:net.IPv4len])
151		b = b[net.IPv4len:]
152	case iana.AddrFamilyIPv6:
153		if len(b) < net.IPv6len {
154			return nil, errMessageTooShort
155		}
156		ifi.Addr.IP = make(net.IP, net.IPv6len)
157		copy(ifi.Addr.IP, b[:net.IPv6len])
158		b = b[net.IPv6len:]
159	}
160	return b, nil
161}
162
163func (ifi *InterfaceInfo) marshalName(proto int, b []byte) []byte {
164	l := byte(ifi.nameLen())
165	b[0] = l
166	copy(b[1:], []byte(ifi.Interface.Name))
167	return b[l:]
168}
169
170func (ifi *InterfaceInfo) parseName(b []byte) ([]byte, error) {
171	if 4 > len(b) || len(b) < int(b[0]) {
172		return nil, errMessageTooShort
173	}
174	l := int(b[0])
175	if l%4 != 0 || 4 > l || l > 64 {
176		return nil, errInvalidExtension
177	}
178	var name [63]byte
179	copy(name[:], b[1:l])
180	ifi.Interface.Name = strings.Trim(string(name[:]), "\000")
181	return b[l:], nil
182}
183
184func (ifi *InterfaceInfo) marshalMTU(proto int, b []byte) []byte {
185	binary.BigEndian.PutUint32(b[:4], uint32(ifi.Interface.MTU))
186	return b[4:]
187}
188
189func (ifi *InterfaceInfo) parseMTU(b []byte) ([]byte, error) {
190	if len(b) < 4 {
191		return nil, errMessageTooShort
192	}
193	ifi.Interface.MTU = int(binary.BigEndian.Uint32(b[:4]))
194	return b[4:], nil
195}
196
197func parseInterfaceInfo(b []byte) (Extension, error) {
198	ifi := &InterfaceInfo{
199		Class: int(b[2]),
200		Type:  int(b[3]),
201	}
202	if ifi.Type&(attrIfIndex|attrName|attrMTU) != 0 {
203		ifi.Interface = &net.Interface{}
204	}
205	if ifi.Type&attrIPAddr != 0 {
206		ifi.Addr = &net.IPAddr{}
207	}
208	attrs := ifi.Type & (attrIfIndex | attrIPAddr | attrName | attrMTU)
209	for b = b[4:]; len(b) > 0 && attrs != 0; {
210		var err error
211		switch {
212		case attrs&attrIfIndex != 0:
213			b, err = ifi.parseIfIndex(b)
214			attrs &^= attrIfIndex
215		case attrs&attrIPAddr != 0:
216			b, err = ifi.parseIPAddr(b)
217			attrs &^= attrIPAddr
218		case attrs&attrName != 0:
219			b, err = ifi.parseName(b)
220			attrs &^= attrName
221		case attrs&attrMTU != 0:
222			b, err = ifi.parseMTU(b)
223			attrs &^= attrMTU
224		}
225		if err != nil {
226			return nil, err
227		}
228	}
229	if ifi.Interface != nil && ifi.Interface.Name != "" && ifi.Addr != nil && ifi.Addr.IP.To16() != nil && ifi.Addr.IP.To4() == nil {
230		ifi.Addr.Zone = ifi.Interface.Name
231	}
232	return ifi, nil
233}
234
235const (
236	classInterfaceIdent    = 3
237	typeInterfaceByName    = 1
238	typeInterfaceByIndex   = 2
239	typeInterfaceByAddress = 3
240)
241
242// An InterfaceIdent represents interface identification.
243type InterfaceIdent struct {
244	Class int    // extension object class number
245	Type  int    // extension object sub-type
246	Name  string // interface name
247	Index int    // interface index
248	AFI   int    // address family identifier; see address family numbers in IANA registry
249	Addr  []byte // address
250}
251
252// Len implements the Len method of Extension interface.
253func (ifi *InterfaceIdent) Len(_ int) int {
254	switch ifi.Type {
255	case typeInterfaceByName:
256		l := len(ifi.Name)
257		if l > 255 {
258			l = 255
259		}
260		return 4 + (l+3)&^3
261	case typeInterfaceByIndex:
262		return 4 + 4
263	case typeInterfaceByAddress:
264		return 4 + 4 + (len(ifi.Addr)+3)&^3
265	default:
266		return 4
267	}
268}
269
270// Marshal implements the Marshal method of Extension interface.
271func (ifi *InterfaceIdent) Marshal(proto int) ([]byte, error) {
272	b := make([]byte, ifi.Len(proto))
273	if err := ifi.marshal(proto, b); err != nil {
274		return nil, err
275	}
276	return b, nil
277}
278
279func (ifi *InterfaceIdent) marshal(proto int, b []byte) error {
280	l := ifi.Len(proto)
281	binary.BigEndian.PutUint16(b[:2], uint16(l))
282	b[2], b[3] = classInterfaceIdent, byte(ifi.Type)
283	switch ifi.Type {
284	case typeInterfaceByName:
285		copy(b[4:], ifi.Name)
286	case typeInterfaceByIndex:
287		binary.BigEndian.PutUint32(b[4:4+4], uint32(ifi.Index))
288	case typeInterfaceByAddress:
289		binary.BigEndian.PutUint16(b[4:4+2], uint16(ifi.AFI))
290		b[4+2] = byte(len(ifi.Addr))
291		copy(b[4+4:], ifi.Addr)
292	}
293	return nil
294}
295
296func parseInterfaceIdent(b []byte) (Extension, error) {
297	ifi := &InterfaceIdent{
298		Class: int(b[2]),
299		Type:  int(b[3]),
300	}
301	switch ifi.Type {
302	case typeInterfaceByName:
303		ifi.Name = strings.Trim(string(b[4:]), "\x00")
304	case typeInterfaceByIndex:
305		if len(b[4:]) < 4 {
306			return nil, errInvalidExtension
307		}
308		ifi.Index = int(binary.BigEndian.Uint32(b[4 : 4+4]))
309	case typeInterfaceByAddress:
310		if len(b[4:]) < 4 {
311			return nil, errInvalidExtension
312		}
313		ifi.AFI = int(binary.BigEndian.Uint16(b[4 : 4+2]))
314		l := int(b[4+2])
315		if len(b[4+4:]) < l {
316			return nil, errInvalidExtension
317		}
318		ifi.Addr = make([]byte, l)
319		copy(ifi.Addr, b[4+4:])
320	}
321	return ifi, nil
322}
323