OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / net / icmp / message.go
1 // Copyright 2012 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
5 // Package icmp provides basic functions for the manipulation of
6 // messages used in the Internet Control Message Protocols,
7 // ICMPv4 and ICMPv6.
8 //
9 // ICMPv4 and ICMPv6 are defined in RFC 792 and RFC 4443.
10 // Multi-part message support for ICMP is defined in RFC 4884.
11 // ICMP extensions for MPLS are defined in RFC 4950.
12 // ICMP extensions for interface and next-hop identification are
13 // defined in RFC 5837.
14 package icmp // import "golang.org/x/net/icmp"
15
16 import (
17         "encoding/binary"
18         "errors"
19         "net"
20         "syscall"
21
22         "golang.org/x/net/internal/iana"
23         "golang.org/x/net/ipv4"
24         "golang.org/x/net/ipv6"
25 )
26
27 // BUG(mikio): This package is not implemented on NaCl and Plan 9.
28
29 var (
30         errMessageTooShort  = errors.New("message too short")
31         errHeaderTooShort   = errors.New("header too short")
32         errBufferTooShort   = errors.New("buffer too short")
33         errOpNoSupport      = errors.New("operation not supported")
34         errNoExtension      = errors.New("no extension")
35         errInvalidExtension = errors.New("invalid extension")
36 )
37
38 func checksum(b []byte) uint16 {
39         csumcv := len(b) - 1 // checksum coverage
40         s := uint32(0)
41         for i := 0; i < csumcv; i += 2 {
42                 s += uint32(b[i+1])<<8 | uint32(b[i])
43         }
44         if csumcv&1 == 0 {
45                 s += uint32(b[csumcv])
46         }
47         s = s>>16 + s&0xffff
48         s = s + s>>16
49         return ^uint16(s)
50 }
51
52 // A Type represents an ICMP message type.
53 type Type interface {
54         Protocol() int
55 }
56
57 // A Message represents an ICMP message.
58 type Message struct {
59         Type     Type        // type, either ipv4.ICMPType or ipv6.ICMPType
60         Code     int         // code
61         Checksum int         // checksum
62         Body     MessageBody // body
63 }
64
65 // Marshal returns the binary encoding of the ICMP message m.
66 //
67 // For an ICMPv4 message, the returned message always contains the
68 // calculated checksum field.
69 //
70 // For an ICMPv6 message, the returned message contains the calculated
71 // checksum field when psh is not nil, otherwise the kernel will
72 // compute the checksum field during the message transmission.
73 // When psh is not nil, it must be the pseudo header for IPv6.
74 func (m *Message) Marshal(psh []byte) ([]byte, error) {
75         var mtype int
76         switch typ := m.Type.(type) {
77         case ipv4.ICMPType:
78                 mtype = int(typ)
79         case ipv6.ICMPType:
80                 mtype = int(typ)
81         default:
82                 return nil, syscall.EINVAL
83         }
84         b := []byte{byte(mtype), byte(m.Code), 0, 0}
85         if m.Type.Protocol() == iana.ProtocolIPv6ICMP && psh != nil {
86                 b = append(psh, b...)
87         }
88         if m.Body != nil && m.Body.Len(m.Type.Protocol()) != 0 {
89                 mb, err := m.Body.Marshal(m.Type.Protocol())
90                 if err != nil {
91                         return nil, err
92                 }
93                 b = append(b, mb...)
94         }
95         if m.Type.Protocol() == iana.ProtocolIPv6ICMP {
96                 if psh == nil { // cannot calculate checksum here
97                         return b, nil
98                 }
99                 off, l := 2*net.IPv6len, len(b)-len(psh)
100                 binary.BigEndian.PutUint32(b[off:off+4], uint32(l))
101         }
102         s := checksum(b)
103         // Place checksum back in header; using ^= avoids the
104         // assumption the checksum bytes are zero.
105         b[len(psh)+2] ^= byte(s)
106         b[len(psh)+3] ^= byte(s >> 8)
107         return b[len(psh):], nil
108 }
109
110 var parseFns = map[Type]func(int, []byte) (MessageBody, error){
111         ipv4.ICMPTypeDestinationUnreachable: parseDstUnreach,
112         ipv4.ICMPTypeTimeExceeded:           parseTimeExceeded,
113         ipv4.ICMPTypeParameterProblem:       parseParamProb,
114
115         ipv4.ICMPTypeEcho:      parseEcho,
116         ipv4.ICMPTypeEchoReply: parseEcho,
117
118         ipv6.ICMPTypeDestinationUnreachable: parseDstUnreach,
119         ipv6.ICMPTypePacketTooBig:           parsePacketTooBig,
120         ipv6.ICMPTypeTimeExceeded:           parseTimeExceeded,
121         ipv6.ICMPTypeParameterProblem:       parseParamProb,
122
123         ipv6.ICMPTypeEchoRequest: parseEcho,
124         ipv6.ICMPTypeEchoReply:   parseEcho,
125 }
126
127 // ParseMessage parses b as an ICMP message.
128 // Proto must be either the ICMPv4 or ICMPv6 protocol number.
129 func ParseMessage(proto int, b []byte) (*Message, error) {
130         if len(b) < 4 {
131                 return nil, errMessageTooShort
132         }
133         var err error
134         m := &Message{Code: int(b[1]), Checksum: int(binary.BigEndian.Uint16(b[2:4]))}
135         switch proto {
136         case iana.ProtocolICMP:
137                 m.Type = ipv4.ICMPType(b[0])
138         case iana.ProtocolIPv6ICMP:
139                 m.Type = ipv6.ICMPType(b[0])
140         default:
141                 return nil, syscall.EINVAL
142         }
143         if fn, ok := parseFns[m.Type]; !ok {
144                 m.Body, err = parseDefaultMessageBody(proto, b[4:])
145         } else {
146                 m.Body, err = fn(proto, b[4:])
147         }
148         if err != nil {
149                 return nil, err
150         }
151         return m, nil
152 }