icmp: add extensions for MPLS
This change implements ICMP multi-part message marshaler, parser and
extensions for MPLS which are used for route trace applications as
described in RFC 4950.
API breaking changes:
type MessageBody interface, Len() int
type Extension interface, Len() int
type Extension interface, Marshal() ([]byte, error)
are replaced with
type MessageBody interface, Len(int) int
type Extension interface, Len(int) int
type Extension interface, Marshal(int) ([]byte, error)
Change-Id: Iee1f2e03916d49b8dfe3a89fe682c702d40ecc85
Reviewed-on: https://go-review.googlesource.com/2794
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/icmp/multipart.go b/icmp/multipart.go
new file mode 100644
index 0000000..3f89a76
--- /dev/null
+++ b/icmp/multipart.go
@@ -0,0 +1,103 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package icmp
+
+import "golang.org/x/net/internal/iana"
+
+// multipartMessageBodyDataLen takes b as an original datagram and
+// exts as extensions, and returns a required length for message body
+// and a required length for a padded original datagram in wire
+// format.
+func multipartMessageBodyDataLen(proto int, b []byte, exts []Extension) (bodyLen, dataLen int) {
+ for _, ext := range exts {
+ bodyLen += ext.Len(proto)
+ }
+ if bodyLen > 0 {
+ dataLen = multipartMessageOrigDatagramLen(proto, b)
+ bodyLen += 4 // length of extension header
+ } else {
+ dataLen = len(b)
+ }
+ bodyLen += dataLen
+ return bodyLen, dataLen
+}
+
+// multipartMessageOrigDatagramLen takes b as an original datagram,
+// and returns a required length for a padded orignal datagram in wire
+// format.
+func multipartMessageOrigDatagramLen(proto int, b []byte) int {
+ roundup := func(b []byte, align int) int {
+ // According to RFC 4884, the padded original datagram
+ // field must contain at least 128 octets.
+ if len(b) < 128 {
+ return 128
+ }
+ r := len(b)
+ return (r + align) &^ (align - 1)
+ }
+ switch proto {
+ case iana.ProtocolICMP:
+ return roundup(b, 4)
+ case iana.ProtocolIPv6ICMP:
+ return roundup(b, 8)
+ default:
+ return len(b)
+ }
+}
+
+// marshalMultipartMessageBody takes data as an original datagram and
+// exts as extesnsions, and returns a binary encoding of message body.
+// It can be used for non-multipart message bodies when exts is nil.
+func marshalMultipartMessageBody(proto int, data []byte, exts []Extension) ([]byte, error) {
+ bodyLen, dataLen := multipartMessageBodyDataLen(proto, data, exts)
+ b := make([]byte, 4+bodyLen)
+ copy(b[4:], data)
+ off := dataLen + 4
+ if len(exts) > 0 {
+ b[dataLen+4] = byte(extensionVersion << 4)
+ off += 4 // length of object header
+ for _, ext := range exts {
+ switch ext := ext.(type) {
+ case *MPLSLabelStack:
+ if err := ext.marshal(proto, b[off:]); err != nil {
+ return nil, err
+ }
+ off += ext.Len(proto)
+ }
+ }
+ s := checksum(b[dataLen+4:])
+ b[dataLen+4+2] ^= byte(s)
+ b[dataLen+4+3] ^= byte(s >> 8)
+ switch proto {
+ case iana.ProtocolICMP:
+ b[1] = byte(dataLen / 4)
+ case iana.ProtocolIPv6ICMP:
+ b[0] = byte(dataLen / 8)
+ }
+ }
+ return b, nil
+}
+
+// parseMultipartMessageBody parses b as either a non-multipart
+// message body or a multipart message body.
+func parseMultipartMessageBody(proto int, b []byte) ([]byte, []Extension, error) {
+ var l int
+ switch proto {
+ case iana.ProtocolICMP:
+ l = 4 * int(b[1])
+ case iana.ProtocolIPv6ICMP:
+ l = 8 * int(b[0])
+ }
+ if len(b) == 4 {
+ return nil, nil, nil
+ }
+ exts, l, err := parseExtensions(b[4:], l)
+ if err != nil {
+ l = len(b) - 4
+ }
+ data := make([]byte, l)
+ copy(data, b[4:])
+ return data, exts, nil
+}