Skip to content

Commit 05df474

Browse files
committed
core/wire: decode truncated packets
1 parent f35084e commit 05df474

2 files changed

Lines changed: 32 additions & 13 deletions

File tree

intra/core/wire/parsed.go

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ type Parsed struct {
7979
// length is the total length of the packet.
8080
// This is not the same as len(b) because b can have trailing zeros.
8181
length int
82+
// truncated indicates if the packet was truncated.
83+
trunc bool
8284

8385
// IPVersion is the IP protocol version of the packet (4 or
8486
// 6), or 0 if the packet doesn't look like IPv4 or IPv6.
@@ -116,12 +118,21 @@ func (p *Parsed) String() string {
116118
return string(b)
117119
}
118120

121+
func (q *Parsed) Decode(b []byte) {
122+
q.decode(b, false /*truncated*/)
123+
}
124+
125+
func (q *Parsed) DecodeTrunc(b []byte, trunc bool) {
126+
q.decode(b, trunc /*truncated*/)
127+
}
128+
119129
// Decode extracts data from the packet in b into q.
120130
// It performs extremely simple packet decoding for basic IPv4 and IPv6 packet types.
121131
// It extracts only the subprotocol id, IP addresses, and (if any) ports,
122132
// and shouldn't need any memory allocation.
123-
func (q *Parsed) Decode(b []byte) {
133+
func (q *Parsed) decode(b []byte, trunc bool) {
124134
q.b = b
135+
q.trunc = trunc
125136
q.CaptureMeta = CaptureMeta{} // Clear any capture metadata if it exists.
126137

127138
if len(b) < 1 {
@@ -152,7 +163,7 @@ func (q *Parsed) decode4(b []byte) {
152163
// Check that it's IPv4.
153164
q.IPProto = Proto(b[9])
154165
q.length = int(binary.BigEndian.Uint16(b[2:4]))
155-
if len(b) < q.length {
166+
if !q.trunc && len(b) < q.length {
156167
// Packet was cut off before full IPv4 length.
157168
q.IPProto = unknown
158169
return
@@ -283,7 +294,7 @@ func (q *Parsed) decode6(b []byte) {
283294

284295
q.IPProto = Proto(b[6])
285296
q.length = int(binary.BigEndian.Uint16(b[4:6])) + IP6HeaderLength
286-
if len(b) < q.length {
297+
if !q.trunc && len(b) < q.length {
287298
// Packet was cut off before the full IPv6 length.
288299
q.IPProto = unknown
289300
return
@@ -394,7 +405,7 @@ func (q *Parsed) ICMPHeaderString() string {
394405
case ICMPv6:
395406
return fmt.Sprintf("%v", q.ICMP6Header())
396407
}
397-
return "ICMP{???}"
408+
return "ICMP" + string(q.IPVersion) + "{???}"
398409
}
399410

400411
func (q *Parsed) ICMP4Header() ICMP4Header {
@@ -429,13 +440,19 @@ func (q *Parsed) Buffer() []byte {
429440

430441
// Payload returns the payload of the IP subprotocol section.
431442
// This is a read-only view; that is, q retains the ownership of the buffer.
432-
func (q *Parsed) Payload() []byte {
443+
func (q *Parsed) Payload() ([]byte, bool) {
433444
// If the packet is truncated, return nothing instead of crashing.
434-
if q.length > len(q.b) || q.dataofs > len(q.b) {
435-
return nil
445+
if q.dataofs > len(q.b) {
446+
return nil, q.trunc
447+
}
448+
if q.length > len(q.b) {
449+
if q.trunc {
450+
return q.b[q.dataofs:], true
451+
}
452+
return nil, q.trunc
436453
}
437454

438-
return q.b[q.dataofs:q.length]
455+
return q.b[q.dataofs:q.length], false
439456
}
440457

441458
// Transport returns the transport header and payload (IP subprotocol, such as TCP or UDP).

intra/netstack/icmpecho.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,9 @@ func (r *icmpResponder) handle(b buffer.Buffer) (handled bool) {
8686
return
8787
}
8888

89+
truncated := inSize > int64(w.Len())
8990
parsed := wire.Pool.Get()
90-
parsed.Decode(w.Copy())
91+
parsed.DecodeTrunc(w.Copy(), truncated)
9192

9293
// Only echo requests are handled; other ICMP packets are dropped to avoid
9394
// feeding them back into netstack.
@@ -105,14 +106,15 @@ func (r *icmpResponder) handle(b buffer.Buffer) (handled bool) {
105106
dst := parsed.Dst
106107
has := parsed.HasTransportData()
107108

108-
logwv(!has)("icmp: responder: request ipv%d; %s => %s; h: %s; ok? %t", parsed.IPVersion, src, dst, parsed.ICMPHeaderString(), has)
109+
logwv(!has)("icmp: responder: request ipv%d; %s => %s; h: %s; trunc? %t, ok? %t",
110+
parsed.IPVersion, src, dst, parsed.ICMPHeaderString(), truncated, has)
109111

110112
if !has {
111113
wire.Pool.Put(parsed)
112114
return
113115
}
114116

115-
if inSize > int64(w.Len()) {
117+
if truncated {
116118
// There is more data beyond the minimum ICMP echo request.
117119
// Reconstruct the full packet.
118120
w.Reset()
@@ -122,8 +124,8 @@ func (r *icmpResponder) handle(b buffer.Buffer) (handled bool) {
122124

123125
if !parsed.IsEchoRequest() {
124126
if settings.Debug {
125-
log.VV("icmp: responder: not echo request ipv%d; %s => %s; h: %s; %x",
126-
parsed.IPVersion, src, dst, parsed.ICMPHeaderString(), parsed.Buffer())
127+
log.VV("icmp: responder: not echo request ipv%d (trunc? %t); %s => %s; h: %s; %x",
128+
parsed.IPVersion, truncated, src, dst, parsed.ICMPHeaderString(), parsed.Buffer())
127129
}
128130
wire.Pool.Put(parsed)
129131
return

0 commit comments

Comments
 (0)