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