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 "golang.org/x/net/internal/iana"
8
9// multipartMessageBodyDataLen takes b as an original datagram and
10// exts as extensions, and returns a required length for message body
11// and a required length for a padded original datagram in wire
12// format.
13func multipartMessageBodyDataLen(proto int, withOrigDgram bool, b []byte, exts []Extension) (bodyLen, dataLen int) {
14	bodyLen = 4 // length of leading octets
15	var extLen int
16	var rawExt bool // raw extension may contain an empty object
17	for _, ext := range exts {
18		extLen += ext.Len(proto)
19		if _, ok := ext.(*RawExtension); ok {
20			rawExt = true
21		}
22	}
23	if extLen > 0 && withOrigDgram {
24		dataLen = multipartMessageOrigDatagramLen(proto, b)
25	} else {
26		dataLen = len(b)
27	}
28	if extLen > 0 || rawExt {
29		bodyLen += 4 // length of extension header
30	}
31	bodyLen += dataLen + extLen
32	return bodyLen, dataLen
33}
34
35// multipartMessageOrigDatagramLen takes b as an original datagram,
36// and returns a required length for a padded orignal datagram in wire
37// format.
38func multipartMessageOrigDatagramLen(proto int, b []byte) int {
39	roundup := func(b []byte, align int) int {
40		// According to RFC 4884, the padded original datagram
41		// field must contain at least 128 octets.
42		if len(b) < 128 {
43			return 128
44		}
45		r := len(b)
46		return (r + align - 1) &^ (align - 1)
47	}
48	switch proto {
49	case iana.ProtocolICMP:
50		return roundup(b, 4)
51	case iana.ProtocolIPv6ICMP:
52		return roundup(b, 8)
53	default:
54		return len(b)
55	}
56}
57
58// marshalMultipartMessageBody takes data as an original datagram and
59// exts as extesnsions, and returns a binary encoding of message body.
60// It can be used for non-multipart message bodies when exts is nil.
61func marshalMultipartMessageBody(proto int, withOrigDgram bool, data []byte, exts []Extension) ([]byte, error) {
62	bodyLen, dataLen := multipartMessageBodyDataLen(proto, withOrigDgram, data, exts)
63	b := make([]byte, bodyLen)
64	copy(b[4:], data)
65	if len(exts) > 0 {
66		b[4+dataLen] = byte(extensionVersion << 4)
67		off := 4 + dataLen + 4 // leading octets, data, extension header
68		for _, ext := range exts {
69			switch ext := ext.(type) {
70			case *MPLSLabelStack:
71				if err := ext.marshal(proto, b[off:]); err != nil {
72					return nil, err
73				}
74				off += ext.Len(proto)
75			case *InterfaceInfo:
76				attrs, l := ext.attrsAndLen(proto)
77				if err := ext.marshal(proto, b[off:], attrs, l); err != nil {
78					return nil, err
79				}
80				off += ext.Len(proto)
81			case *InterfaceIdent:
82				if err := ext.marshal(proto, b[off:]); err != nil {
83					return nil, err
84				}
85				off += ext.Len(proto)
86			case *RawExtension:
87				copy(b[off:], ext.Data)
88				off += ext.Len(proto)
89			}
90		}
91		s := checksum(b[4+dataLen:])
92		b[4+dataLen+2] ^= byte(s)
93		b[4+dataLen+3] ^= byte(s >> 8)
94		if withOrigDgram {
95			switch proto {
96			case iana.ProtocolICMP:
97				b[1] = byte(dataLen / 4)
98			case iana.ProtocolIPv6ICMP:
99				b[0] = byte(dataLen / 8)
100			}
101		}
102	}
103	return b, nil
104}
105
106// parseMultipartMessageBody parses b as either a non-multipart
107// message body or a multipart message body.
108func parseMultipartMessageBody(proto int, typ Type, b []byte) ([]byte, []Extension, error) {
109	var l int
110	switch proto {
111	case iana.ProtocolICMP:
112		l = 4 * int(b[1])
113	case iana.ProtocolIPv6ICMP:
114		l = 8 * int(b[0])
115	}
116	if len(b) == 4 {
117		return nil, nil, nil
118	}
119	exts, l, err := parseExtensions(typ, b[4:], l)
120	if err != nil {
121		l = len(b) - 4
122	}
123	var data []byte
124	if l > 0 {
125		data = make([]byte, l)
126		copy(data, b[4:])
127	}
128	return data, exts, nil
129}
130