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