icmp: fix miscalculation on multipart message bodies

Also adds test cases for the Len method of MessageBody interface.

Fixes golang/go#13141.

Change-Id: I8ab9e38727104ca094dfdb3020e8d42e611911e0
Reviewed-on: https://go-review.googlesource.com/16616
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/icmp/multipart_test.go b/icmp/multipart_test.go
index 9248e47..966ccb8 100644
--- a/icmp/multipart_test.go
+++ b/icmp/multipart_test.go
@@ -313,3 +313,130 @@
 	}
 	return s[:len(s)-1]
 }
+
+var multipartMessageBodyLenTests = []struct {
+	proto int
+	in    icmp.MessageBody
+	out   int
+}{
+	{
+		iana.ProtocolICMP,
+		&icmp.DstUnreach{
+			Data: make([]byte, ipv4.HeaderLen),
+		},
+		4 + ipv4.HeaderLen, // unused and original datagram
+	},
+	{
+		iana.ProtocolICMP,
+		&icmp.TimeExceeded{
+			Data: make([]byte, ipv4.HeaderLen),
+		},
+		4 + ipv4.HeaderLen, // unused and original datagram
+	},
+	{
+		iana.ProtocolICMP,
+		&icmp.ParamProb{
+			Data: make([]byte, ipv4.HeaderLen),
+		},
+		4 + ipv4.HeaderLen, // [pointer, unused] and original datagram
+	},
+
+	{
+		iana.ProtocolICMP,
+		&icmp.ParamProb{
+			Data: make([]byte, ipv4.HeaderLen),
+			Extensions: []icmp.Extension{
+				&icmp.MPLSLabelStack{},
+			},
+		},
+		4 + 4 + 4 + 0 + 128, // [pointer, length, unused], extension header, object header, object payload, original datagram
+	},
+	{
+		iana.ProtocolICMP,
+		&icmp.ParamProb{
+			Data: make([]byte, 128),
+			Extensions: []icmp.Extension{
+				&icmp.MPLSLabelStack{},
+			},
+		},
+		4 + 4 + 4 + 0 + 128, // [pointer, length, unused], extension header, object header, object payload and original datagram
+	},
+	{
+		iana.ProtocolICMP,
+		&icmp.ParamProb{
+			Data: make([]byte, 129),
+			Extensions: []icmp.Extension{
+				&icmp.MPLSLabelStack{},
+			},
+		},
+		4 + 4 + 4 + 0 + 132, // [pointer, length, unused], extension header, object header, object payload and original datagram
+	},
+
+	{
+		iana.ProtocolIPv6ICMP,
+		&icmp.DstUnreach{
+			Data: make([]byte, ipv6.HeaderLen),
+		},
+		4 + ipv6.HeaderLen, // unused and original datagram
+	},
+	{
+		iana.ProtocolIPv6ICMP,
+		&icmp.PacketTooBig{
+			Data: make([]byte, ipv6.HeaderLen),
+		},
+		4 + ipv6.HeaderLen, // mtu and original datagram
+	},
+	{
+		iana.ProtocolIPv6ICMP,
+		&icmp.TimeExceeded{
+			Data: make([]byte, ipv6.HeaderLen),
+		},
+		4 + ipv6.HeaderLen, // unused and original datagram
+	},
+	{
+		iana.ProtocolIPv6ICMP,
+		&icmp.ParamProb{
+			Data: make([]byte, ipv6.HeaderLen),
+		},
+		4 + ipv6.HeaderLen, // pointer and original datagram
+	},
+
+	{
+		iana.ProtocolIPv6ICMP,
+		&icmp.DstUnreach{
+			Data: make([]byte, 127),
+			Extensions: []icmp.Extension{
+				&icmp.MPLSLabelStack{},
+			},
+		},
+		4 + 4 + 4 + 0 + 128, // [length, unused], extension header, object header, object payload and original datagram
+	},
+	{
+		iana.ProtocolIPv6ICMP,
+		&icmp.DstUnreach{
+			Data: make([]byte, 128),
+			Extensions: []icmp.Extension{
+				&icmp.MPLSLabelStack{},
+			},
+		},
+		4 + 4 + 4 + 0 + 128, // [length, unused], extension header, object header, object payload and original datagram
+	},
+	{
+		iana.ProtocolIPv6ICMP,
+		&icmp.DstUnreach{
+			Data: make([]byte, 129),
+			Extensions: []icmp.Extension{
+				&icmp.MPLSLabelStack{},
+			},
+		},
+		4 + 4 + 4 + 0 + 136, // [length, unused], extension header, object header, object payload and original datagram
+	},
+}
+
+func TestMultipartMessageBodyLen(t *testing.T) {
+	for i, tt := range multipartMessageBodyLenTests {
+		if out := tt.in.Len(tt.proto); out != tt.out {
+			t.Errorf("#%d: got %d; want %d", i, out, tt.out)
+		}
+	}
+}