Skip to content

Commit bd76608

Browse files
ignoramousCopilot
andcommitted
netstack/icmp: reimpl icmp responses
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
1 parent cc7d586 commit bd76608

3 files changed

Lines changed: 41 additions & 85 deletions

File tree

intra/core/wire/icmp4.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ func (t ICMP4Type) String() string {
5555
type ICMP4Code uint8
5656

5757
const (
58-
ICMP4NoCode ICMP4Code = 0
58+
ICMP4NoCode ICMP4Code = 0
59+
ICMP4HostUnreachable ICMP4Code = 1
5960
)
6061

6162
// ICMP4Header is an IPv4+ICMPv4 header.

intra/core/wire/icmp6.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ func (t ICMP6Type) String() string {
5858
type ICMP6Code uint8
5959

6060
const (
61-
ICMP6NoCode ICMP6Code = 0
61+
ICMP6NoCode ICMP6Code = 0
62+
ICMP6NoRoute ICMP6Code = 0 // code 0: no route to destination
6263
)
6364

6465
// ICMP6Header is an IPv4+ICMPv4 header.

intra/netstack/icmpecho.go

Lines changed: 37 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"github.com/celzero/firestack/intra/settings"
1212
"gvisor.dev/gvisor/pkg/buffer"
1313
"gvisor.dev/gvisor/pkg/tcpip"
14-
"gvisor.dev/gvisor/pkg/tcpip/checksum"
1514
"gvisor.dev/gvisor/pkg/tcpip/header"
1615
"gvisor.dev/gvisor/pkg/tcpip/stack"
1716
)
@@ -144,38 +143,54 @@ func (r *icmpResponder) handle(b buffer.Buffer) (handled bool) {
144143
func (r *icmpResponder) process(h GICMPHandler, pkt *wire.Parsed, src, dst netip.AddrPort) {
145144
defer wire.Pool.Put(pkt)
146145

147-
payload := pkt.Transport()
148-
if len(payload) == 0 {
146+
payload, ok := pkt.Payload()
147+
if !ok {
149148
return
150149
}
151150

152-
if !h.Ping(payload, src, dst) {
153-
// Ping failed; nothing to inject back.
151+
icmpMsg := pkt.Transport()
152+
if len(icmpMsg) == 0 {
154153
return
155154
}
156155

157-
var resp []byte
158-
var proto tcpip.NetworkProtocolNumber
159-
var err error
156+
pinged := h.Ping(icmpMsg, src, dst)
160157

158+
resp, proto, err := r.makeReply(pkt, payload, pinged)
159+
if err != nil || len(resp) == 0 {
160+
log.W("icmp: responder: reply %s <= %s (sz: %d); ping? %t; err? %v",
161+
src, dst, len(resp), pinged, err)
162+
return
163+
}
164+
165+
// github.com/tailscale/tailscale/blob/7de1b0b33082cc/wgengine/netstack/netstack.go#L1201-L1212
166+
r.inject(proto, resp)
167+
}
168+
169+
func (r *icmpResponder) makeReply(pkt *wire.Parsed, payload []byte, ok bool) ([]byte, tcpip.NetworkProtocolNumber, error) {
161170
switch pkt.IPVersion {
162171
case 4:
163-
resp, err = buildICMPv4Reply(pkt, payload)
164-
proto = header.IPv4ProtocolNumber
172+
ipHdr := pkt.IP4Header()
173+
ipHdr.ToResponse()
174+
if !ok {
175+
icmpHdr := wire.ICMP4Header{IP4Header: ipHdr, Type: wire.ICMP4Unreachable, Code: wire.ICMP4HostUnreachable}
176+
return wire.Generate(icmpHdr, payload), header.IPv4ProtocolNumber, nil
177+
}
178+
icmpHdr := wire.ICMP4Header{IP4Header: ipHdr}
179+
icmpHdr.ToResponse()
180+
return wire.Generate(icmpHdr, payload), header.IPv4ProtocolNumber, nil
165181
case 6:
166-
resp, err = buildICMPv6Reply(pkt, payload)
167-
proto = header.IPv6ProtocolNumber
182+
ipHdr := pkt.IP6Header()
183+
ipHdr.ToResponse()
184+
if !ok {
185+
icmpHdr := wire.ICMP6Header{IP6Header: ipHdr, Type: wire.ICMP6Unreachable, Code: wire.ICMP6NoRoute}
186+
return wire.Generate(icmpHdr, payload), header.IPv6ProtocolNumber, nil
187+
}
188+
icmpHdr := wire.ICMP6Header{IP6Header: ipHdr}
189+
icmpHdr.ToResponse()
190+
return wire.Generate(icmpHdr, payload), header.IPv6ProtocolNumber, nil
168191
default:
169-
return
192+
return nil, 0, fmt.Errorf("unsupported ip version: %d", pkt.IPVersion)
170193
}
171-
172-
if err != nil || len(resp) <= 0 {
173-
log.W("icmp: responder: build reply %s <= %s (sz: %d); err? %v",
174-
src, dst, len(resp), err)
175-
return
176-
}
177-
178-
r.inject(proto, resp)
179194
}
180195

181196
func (r *icmpResponder) inject(proto tcpip.NetworkProtocolNumber, packet []byte) {
@@ -195,66 +210,5 @@ func (r *icmpResponder) inject(proto tcpip.NetworkProtocolNumber, packet []byte)
195210

196211
sz := pkt.Size()
197212
n, err := r.ep.WritePackets(list)
198-
logeif(e(err))("icmp: responder: inject to tun (n: %d; sz: %d); err? %v", n, sz, err)
199-
}
200-
201-
func buildICMPv4Reply(p *wire.Parsed, req []byte) ([]byte, error) {
202-
if len(req) < header.ICMPv4MinimumSize {
203-
return nil, fmt.Errorf("icmp: responder: v4 payload too small: %d", len(req))
204-
}
205-
206-
reply := make([]byte, len(req))
207-
copy(reply, req)
208-
209-
icmpHdr := header.ICMPv4(reply)
210-
icmpHdr.SetType(header.ICMPv4EchoReply)
211-
icmpHdr.SetCode(0)
212-
icmpHdr.SetChecksum(0)
213-
payload := reply[header.ICMPv4MinimumSize:]
214-
payloadSum := checksum.Checksum(payload, 0)
215-
icmpHdr.SetChecksum(header.ICMPv4Checksum(icmpHdr, payloadSum))
216-
217-
ipHdr := p.IP4Header()
218-
ipHdr.ToResponse()
219-
220-
packet := make([]byte, wire.IP4HeaderLength+len(reply))
221-
copy(packet[wire.IP4HeaderLength:], reply)
222-
if err := ipHdr.Marshal(packet); err != nil {
223-
return nil, err
224-
}
225-
return packet, nil
226-
}
227-
228-
func buildICMPv6Reply(p *wire.Parsed, req []byte) ([]byte, error) {
229-
if len(req) < header.ICMPv6MinimumSize {
230-
return nil, fmt.Errorf("icmp: responder: v6 payload too small: %d", len(req))
231-
}
232-
233-
reply := make([]byte, len(req))
234-
copy(reply, req)
235-
236-
icmpHdr := header.ICMPv6(reply)
237-
icmpHdr.SetType(header.ICMPv6EchoReply)
238-
icmpHdr.SetCode(0)
239-
icmpHdr.SetChecksum(0)
240-
241-
ipHdr := p.IP6Header()
242-
ipHdr.ToResponse()
243-
244-
payload := reply[header.ICMPv6MinimumSize:]
245-
payloadSum := checksum.Checksum(payload, 0)
246-
icmpHdr.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
247-
Header: icmpHdr,
248-
Src: tcpip.AddrFrom16(ipHdr.Src.As16()),
249-
Dst: tcpip.AddrFrom16(ipHdr.Dst.As16()),
250-
PayloadCsum: payloadSum,
251-
PayloadLen: len(payload),
252-
}))
253-
254-
packet := make([]byte, wire.IP6HeaderLength+len(reply))
255-
copy(packet[wire.IP6HeaderLength:], reply)
256-
if err := ipHdr.Marshal(packet); err != nil {
257-
return nil, err
258-
}
259-
return packet, nil
213+
logeif(e(err))("icmp: responder: inject %d to tun (n: %d; sz: %d); err? %v", proto, n, sz, err)
260214
}

0 commit comments

Comments
 (0)