1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package icmp
16
17 import (
18 "encoding/binary"
19 "errors"
20 "net"
21 "runtime"
22
23 "golang.org/x/net/internal/iana"
24 "golang.org/x/net/ipv4"
25 "golang.org/x/net/ipv6"
26 )
27
28
29
30 var (
31 errInvalidConn = errors.New("invalid connection")
32 errInvalidProtocol = errors.New("invalid protocol")
33 errMessageTooShort = errors.New("message too short")
34 errHeaderTooShort = errors.New("header too short")
35 errBufferTooShort = errors.New("buffer too short")
36 errInvalidBody = errors.New("invalid body")
37 errNoExtension = errors.New("no extension")
38 errInvalidExtension = errors.New("invalid extension")
39 errNotImplemented = errors.New("not implemented on " + runtime.GOOS + "/" + runtime.GOARCH)
40 )
41
42 func checksum(b []byte) uint16 {
43 csumcv := len(b) - 1
44 s := uint32(0)
45 for i := 0; i < csumcv; i += 2 {
46 s += uint32(b[i+1])<<8 | uint32(b[i])
47 }
48 if csumcv&1 == 0 {
49 s += uint32(b[csumcv])
50 }
51 s = s>>16 + s&0xffff
52 s = s + s>>16
53 return ^uint16(s)
54 }
55
56
57 type Type interface {
58 Protocol() int
59 }
60
61
62 type Message struct {
63 Type Type
64 Code int
65 Checksum int
66 Body MessageBody
67 }
68
69
70
71
72
73
74
75
76
77
78 func (m *Message) Marshal(psh []byte) ([]byte, error) {
79 var mtype byte
80 switch typ := m.Type.(type) {
81 case ipv4.ICMPType:
82 mtype = byte(typ)
83 case ipv6.ICMPType:
84 mtype = byte(typ)
85 default:
86 return nil, errInvalidProtocol
87 }
88 b := []byte{mtype, byte(m.Code), 0, 0}
89 proto := m.Type.Protocol()
90 if proto == iana.ProtocolIPv6ICMP && psh != nil {
91 b = append(psh, b...)
92 }
93 if m.Body != nil && m.Body.Len(proto) != 0 {
94 mb, err := m.Body.Marshal(proto)
95 if err != nil {
96 return nil, err
97 }
98 b = append(b, mb...)
99 }
100 if proto == iana.ProtocolIPv6ICMP {
101 if psh == nil {
102 return b, nil
103 }
104 off, l := 2*net.IPv6len, len(b)-len(psh)
105 binary.BigEndian.PutUint32(b[off:off+4], uint32(l))
106 }
107 s := checksum(b)
108
109
110 b[len(psh)+2] ^= byte(s)
111 b[len(psh)+3] ^= byte(s >> 8)
112 return b[len(psh):], nil
113 }
114
115 var parseFns = map[Type]func(int, Type, []byte) (MessageBody, error){
116 ipv4.ICMPTypeDestinationUnreachable: parseDstUnreach,
117 ipv4.ICMPTypeTimeExceeded: parseTimeExceeded,
118 ipv4.ICMPTypeParameterProblem: parseParamProb,
119
120 ipv4.ICMPTypeEcho: parseEcho,
121 ipv4.ICMPTypeEchoReply: parseEcho,
122 ipv4.ICMPTypeExtendedEchoRequest: parseExtendedEchoRequest,
123 ipv4.ICMPTypeExtendedEchoReply: parseExtendedEchoReply,
124
125 ipv6.ICMPTypeDestinationUnreachable: parseDstUnreach,
126 ipv6.ICMPTypePacketTooBig: parsePacketTooBig,
127 ipv6.ICMPTypeTimeExceeded: parseTimeExceeded,
128 ipv6.ICMPTypeParameterProblem: parseParamProb,
129
130 ipv6.ICMPTypeEchoRequest: parseEcho,
131 ipv6.ICMPTypeEchoReply: parseEcho,
132 ipv6.ICMPTypeExtendedEchoRequest: parseExtendedEchoRequest,
133 ipv6.ICMPTypeExtendedEchoReply: parseExtendedEchoReply,
134 }
135
136
137
138
139 func ParseMessage(proto int, b []byte) (*Message, error) {
140 if len(b) < 4 {
141 return nil, errMessageTooShort
142 }
143 var err error
144 m := &Message{Code: int(b[1]), Checksum: int(binary.BigEndian.Uint16(b[2:4]))}
145 switch proto {
146 case iana.ProtocolICMP:
147 m.Type = ipv4.ICMPType(b[0])
148 case iana.ProtocolIPv6ICMP:
149 m.Type = ipv6.ICMPType(b[0])
150 default:
151 return nil, errInvalidProtocol
152 }
153 if fn, ok := parseFns[m.Type]; !ok {
154 m.Body, err = parseRawBody(proto, b[4:])
155 } else {
156 m.Body, err = fn(proto, m.Type, b[4:])
157 }
158 if err != nil {
159 return nil, err
160 }
161 return m, nil
162 }
163
View as plain text