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
10	"golang.org/x/net/ipv4"
11	"golang.org/x/net/ipv6"
12)
13
14// An Extension represents an ICMP extension.
15type Extension interface {
16	// Len returns the length of ICMP extension.
17	// The provided proto must be either the ICMPv4 or ICMPv6
18	// protocol number.
19	Len(proto int) int
20
21	// Marshal returns the binary encoding of ICMP extension.
22	// The provided proto must be either the ICMPv4 or ICMPv6
23	// protocol number.
24	Marshal(proto int) ([]byte, error)
25}
26
27const extensionVersion = 2
28
29func validExtensionHeader(b []byte) bool {
30	v := int(b[0]&0xf0) >> 4
31	s := binary.BigEndian.Uint16(b[2:4])
32	if s != 0 {
33		s = checksum(b)
34	}
35	if v != extensionVersion || s != 0 {
36		return false
37	}
38	return true
39}
40
41// parseExtensions parses b as a list of ICMP extensions.
42// The length attribute l must be the length attribute field in
43// received icmp messages.
44//
45// It will return a list of ICMP extensions and an adjusted length
46// attribute that represents the length of the padded original
47// datagram field. Otherwise, it returns an error.
48func parseExtensions(typ Type, b []byte, l int) ([]Extension, int, error) {
49	// Still a lot of non-RFC 4884 compliant implementations are
50	// out there. Set the length attribute l to 128 when it looks
51	// inappropriate for backwards compatibility.
52	//
53	// A minimal extension at least requires 8 octets; 4 octets
54	// for an extension header, and 4 octets for a single object
55	// header.
56	//
57	// See RFC 4884 for further information.
58	switch typ {
59	case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest:
60		if len(b) < 8 || !validExtensionHeader(b) {
61			return nil, -1, errNoExtension
62		}
63		l = 0
64	default:
65		if 128 > l || l+8 > len(b) {
66			l = 128
67		}
68		if l+8 > len(b) {
69			return nil, -1, errNoExtension
70		}
71		if !validExtensionHeader(b[l:]) {
72			if l == 128 {
73				return nil, -1, errNoExtension
74			}
75			l = 128
76			if !validExtensionHeader(b[l:]) {
77				return nil, -1, errNoExtension
78			}
79		}
80	}
81	var exts []Extension
82	for b = b[l+4:]; len(b) >= 4; {
83		ol := int(binary.BigEndian.Uint16(b[:2]))
84		if 4 > ol || ol > len(b) {
85			break
86		}
87		switch b[2] {
88		case classMPLSLabelStack:
89			ext, err := parseMPLSLabelStack(b[:ol])
90			if err != nil {
91				return nil, -1, err
92			}
93			exts = append(exts, ext)
94		case classInterfaceInfo:
95			ext, err := parseInterfaceInfo(b[:ol])
96			if err != nil {
97				return nil, -1, err
98			}
99			exts = append(exts, ext)
100		case classInterfaceIdent:
101			ext, err := parseInterfaceIdent(b[:ol])
102			if err != nil {
103				return nil, -1, err
104			}
105			exts = append(exts, ext)
106		default:
107			ext := &RawExtension{Data: make([]byte, ol)}
108			copy(ext.Data, b[:ol])
109			exts = append(exts, ext)
110		}
111		b = b[ol:]
112	}
113	return exts, l, nil
114}
115
116func validExtensions(typ Type, exts []Extension) bool {
117	switch typ {
118	case ipv4.ICMPTypeDestinationUnreachable, ipv4.ICMPTypeTimeExceeded, ipv4.ICMPTypeParameterProblem,
119		ipv6.ICMPTypeDestinationUnreachable, ipv6.ICMPTypeTimeExceeded:
120		for i := range exts {
121			switch exts[i].(type) {
122			case *MPLSLabelStack, *InterfaceInfo, *RawExtension:
123			default:
124				return false
125			}
126		}
127		return true
128	case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest:
129		var n int
130		for i := range exts {
131			switch exts[i].(type) {
132			case *InterfaceIdent:
133				n++
134			case *RawExtension:
135			default:
136				return false
137			}
138		}
139		// Not a single InterfaceIdent object or a combo of
140		// RawExtension and InterfaceIdent objects is not
141		// allowed.
142		if n == 1 && len(exts) > 1 {
143			return false
144		}
145		return true
146	default:
147		return false
148	}
149}
150
151// A RawExtension represents a raw extension.
152//
153// A raw extension is excluded from message processing and can be used
154// to construct applications such as protocol conformance testing.
155type RawExtension struct {
156	Data []byte // data
157}
158
159// Len implements the Len method of Extension interface.
160func (p *RawExtension) Len(proto int) int {
161	if p == nil {
162		return 0
163	}
164	return len(p.Data)
165}
166
167// Marshal implements the Marshal method of Extension interface.
168func (p *RawExtension) Marshal(proto int) ([]byte, error) {
169	return p.Data, nil
170}
171