Skip to content
Open
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 87 additions & 52 deletions intra/netstack/icmp.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,48 @@
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
)

type ICMPHackTarget struct {
Handler func(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool
}

func (t *ICMPHackTarget) Action(pkt *stack.PacketBuffer, hook stack.Hook, r *stack.Route, _ stack.AddressableEndpoint) (stack.RuleVerdict, int) {
transportHdr := pkt.TransportHeader()
if len(transportHdr.Slice()) < 8 {
//the packet may be unparsed
return stack.RuleAccept, 0
}
switch pkt.TransportProtocolNumber {
case header.ICMPv6ProtocolNumber:
icmp6Hdr := header.ICMPv6(transportHdr.Slice())
if icmp6Hdr.Type() == header.ICMPv6EchoRequest {
//https://www.rfc-editor.org/rfc/rfc4443.html#section-2.1
n1 := pkt.Network()
icmpID := icmp6Hdr.Ident()
id := stack.TransportEndpointID{
LocalPort: icmpID,
LocalAddress: n1.DestinationAddress(),
RemotePort: icmpID,
RemoteAddress: n1.SourceAddress(),
}
t.Handler(id, pkt)
return stack.RuleDrop, 0
}
}
return stack.RuleAccept, 0
}

func ICMPHack(s *stack.Stack, target stack.Target) {
ipt := s.IPTables()

table := ipt.GetTable(stack.FilterID, true)
index := table.BuiltinChains[stack.Input]
rules := table.Rules
rules[index].Filter.Protocol = header.ICMPv6ProtocolNumber
rules[index].Filter.CheckProtocol = true
rules[index].Target = target
ipt.ForceReplaceTable(stack.FilterID, table, true)
}

type GICMPHandler interface {
GBaseConnHandler
GEchoConnHandler
Expand All @@ -35,7 +77,6 @@
func OutboundICMP(id string, s *stack.Stack, hdl GICMPHandler) {
// remove default handlers
s.SetTransportProtocolHandler(icmp.ProtocolNumber4, nil)
s.SetTransportProtocolHandler(icmp.ProtocolNumber6, nil)

if hdl == nil {
log.E("icmp: %s: no handler", id)
Expand All @@ -44,7 +85,9 @@

forwarder := newIcmpForwarder(id, s, hdl)
s.SetTransportProtocolHandler(icmp.ProtocolNumber4, forwarder.reply4)
s.SetTransportProtocolHandler(icmp.ProtocolNumber6, forwarder.reply6)
//gvisor v0.0.0-20250816201027-ba3b9ca85f20 never delivers ICMPv6 Echo Request Messages to TransportProtocolHandler
target := ICMPHackTarget{forwarder.reply6}
ICMPHack(s, &target)
}

func newIcmpForwarder(owner string, s *stack.Stack, h GICMPHandler) *icmpForwarder {
Expand Down Expand Up @@ -117,41 +160,30 @@
if !f.h.Ping(data, src, dst) { // unreachable
err = f.icmpErr4(pkt, header.ICMPv4DstUnreachable, header.ICMPv4HostUnreachable)
} else { // reachable
newOptions := f.ipOpts(pkt, ipHdr)

// Correct IP header length (IHL in 32-bit words).
replyHeaderLength := uint8(header.IPv4MinimumSize + len(newOptions))
replyIPHdrView := buffer.NewView(int(replyHeaderLength))
replyIPHdrView.Write(ipHdr[:header.IPv4MinimumSize])
replyIPHdrView.Write(newOptions)

replyIPHdr := header.IPv4(replyIPHdrView.AsSlice())
replyIPHdr.SetHeaderLength(replyHeaderLength >> 2) // IHL in 32-bit words.
replyIPHdr.SetSourceAddress(route.LocalAddress())
replyIPHdr.SetDestinationAddress(route.RemoteAddress())
replyIPHdr.SetTTL(route.DefaultTTL())
replyIPHdr.SetTotalLength(uint16(len(replyIPHdr) + len(replyData.AsSlice())))
replyIPHdr.SetChecksum(0)
replyIPHdr.SetChecksum(^replyIPHdr.CalculateChecksum())

replyICMPHdr := header.ICMPv4(replyData.AsSlice())
originRef := replyData.AsSlice()
replyBuf := buffer.NewViewSize(len(originRef))
replyRef := replyBuf.AsSlice()

copy(replyRef[4:], originRef[4:])
replyICMPHdr := header.ICMPv4(replyRef)
replyICMPHdr.SetType(header.ICMPv4EchoReply)
replyICMPHdr.SetCode(0) // EchoReply must have Code=0.
replyICMPHdr.SetChecksum(0)
replyICMPHdr.SetChecksum(^checksum.Checksum(replyData.AsSlice(), 0))
replyICMPHdr.SetChecksum(^checksum.Checksum(replyRef, 0))

replyBuf := buffer.MakeWithView(replyIPHdrView)
replyBuf.Append(replyData.Clone())
replyPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
ReserveHeaderBytes: int(route.MaxHeaderLength()),
Payload: replyBuf,
Payload: buffer.MakeWithView(replyBuf),
})
defer replyPkt.DecRef()

log.D("icmp: v4: %s: ok type %v/%v sz[%d] from %v <= %v",
f.o, replyICMPHdr.Type(), replyICMPHdr.Code(), len(replyICMPHdr), src, dst)

// github.com/google/gvisor/blob/738e1d995f/pkg/tcpip/network/ipv4/icmp.go#L794
err = route.WriteHeaderIncludedPacket(replyPkt)
err = route.WritePacket(stack.NetworkHeaderParams{
Protocol: header.ICMPv4ProtocolNumber,
TTL: route.DefaultTTL(),
}, replyPkt)
}
loge(err)("icmp: v4: %s: wrote reply to tun; err? %v", f.o, err)
})
Expand Down Expand Up @@ -204,25 +236,28 @@
if !f.h.Ping(data, src, dst) { // unreachable
err = f.icmpErr6(id, pkt, header.ICMPv6DstUnreachable, header.ICMPv6NetworkUnreachable)
} else { // reachable
replyPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
ReserveHeaderBytes: int(route.MaxHeaderLength()) + header.ICMPv6EchoMinimumSize,
Payload: pkt.Data().ToBuffer(),
})
defer replyPkt.DecRef()
replyHdr := header.ICMPv6(replyPkt.TransportHeader().Push(header.ICMPv6EchoMinimumSize))
replyPkt.TransportProtocolNumber = header.ICMPv6ProtocolNumber
copy(replyHdr, hdr)
originRef := stack.PayloadSince(pkt.TransportHeader()).AsSlice()
replyBuf := buffer.NewViewSize(len(originRef))
replyRef := replyBuf.AsSlice()

copy(replyRef[4:], originRef[4:])
replyHdr := header.ICMPv6(replyRef)
replyHdr.SetType(header.ICMPv6EchoReply)
replyData := replyPkt.Data()
replyHdr.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
Header: replyHdr,
Src: route.LocalAddress(), // or id.LocalAddress
Dst: route.RemoteAddress(), // or id.RemoteAddress
PayloadCsum: replyData.Checksum(),
PayloadLen: replyData.Size(),
}))
replyHdr.SetCode(0)
replyHdr.SetChecksum(0)
replyHdr.SetChecksum(^checksum.Checksum(replyRef, header.PseudoHeaderChecksum(
header.ICMPv6ProtocolNumber,
route.LocalAddress(), // or id.LocalAddress
route.RemoteAddress(), // or id.RemoteAddress
uint16(len(replyRef)),
)))
log.D("icmp: v6: %s: ok type %v/%v sz[%d] from %v <= %v",
f.o, replyHdr.Type(), replyHdr.Code(), len(replyHdr), src, dst)
replyPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
ReserveHeaderBytes: int(route.MaxHeaderLength()),
Payload: buffer.MakeWithView(replyBuf),
})
defer replyPkt.DecRef()

// github.com/google/gvisor/blob/738e1d995f/pkg/tcpip/network/ipv6/icmp.go#L694
replyclass, _ := l3.TOS()
Expand Down Expand Up @@ -497,25 +532,25 @@
// github.com/google/gvisor/blob/738e1d995f/pkg/tcpip/network/ipv4/ipv4.go#L2007
// optionAction describes possible actions that may be taken on an option
// while processing it.
type optionAction uint8

Check failure on line 535 in intra/netstack/icmp.go

View workflow job for this annotation

GitHub Actions / 🧭 Lint

type optionAction is unused (U1000)

const (
// optionRemove says that the option should not be in the output option set.
optionRemove optionAction = iota

Check failure on line 539 in intra/netstack/icmp.go

View workflow job for this annotation

GitHub Actions / 🧭 Lint

const optionRemove is unused (U1000)

// optionProcess says that the option should be fully processed.
optionProcess

Check failure on line 542 in intra/netstack/icmp.go

View workflow job for this annotation

GitHub Actions / 🧭 Lint

const optionProcess is unused (U1000)

// optionVerify says the option should be checked and passed unchanged.
optionVerify

Check failure on line 545 in intra/netstack/icmp.go

View workflow job for this annotation

GitHub Actions / 🧭 Lint

const optionVerify is unused (U1000)

// optionPass says to pass the output set without checking.
optionPass

Check failure on line 548 in intra/netstack/icmp.go

View workflow job for this annotation

GitHub Actions / 🧭 Lint

const optionPass is unused (U1000)
)

// github.com/google/gvisor/blob/738e1d995f6/pkg/tcpip/network/ipv4/ipv4.go#L2026
// optionActions list what to do for each option in a given scenario.
type optionActions struct {

Check failure on line 553 in intra/netstack/icmp.go

View workflow job for this annotation

GitHub Actions / 🧭 Lint

type optionActions is unused (U1000)
// timestamp controls what to do with a Timestamp option.
timestamp optionAction

Expand All @@ -529,7 +564,7 @@
unknown optionAction
}

func (f *icmpForwarder) ipOpts(pkt *stack.PacketBuffer, iph header.IPv4) (o header.IPv4Options) {

Check failure on line 567 in intra/netstack/icmp.go

View workflow job for this annotation

GitHub Actions / 🧭 Lint

func (*icmpForwarder).ipOpts is unused (U1000)
if opts := iph.Options(); len(opts) != 0 {
// RFC 1122 section 3.2.2.6 (page 43) (and similar for other round trip
// type ICMP packets):
Expand Down Expand Up @@ -567,7 +602,7 @@
//
// If there were no errors during parsing, the new set of options is returned as
// a new buffer.
func (f *icmpForwarder) processIPOptions(pkt *stack.PacketBuffer, opts header.IPv4Options, usage optionActions) (header.IPv4Options, *header.IPv4OptParameterProblem) {

Check failure on line 605 in intra/netstack/icmp.go

View workflow job for this annotation

GitHub Actions / 🧭 Lint

func (*icmpForwarder).processIPOptions is unused (U1000)
optIter := opts.MakeIterator()

// Except NOP, each option must only appear at most once (RFC 791 section 3.1,
Expand Down Expand Up @@ -668,14 +703,14 @@
}

func l3l4(pkt *stack.PacketBuffer, sz int64) (b buffer.Buffer, err error) {
l3 := pkt.NetworkHeader().View()
l4 := pkt.TransportHeader().View()
combined := buffer.MakeWithView(l3)
if err = combined.Append(l4); err == nil {
payload := pkt.Data().ToBuffer()
combined.Merge(&payload)
combined.Truncate(sz)
b = combined
}
return
l3 := pkt.NetworkHeader().View()
l4 := pkt.TransportHeader().View()
combined := buffer.MakeWithView(l3)
if err = combined.Append(l4); err == nil {
payload := pkt.Data().ToBuffer()
combined.Merge(&payload)
combined.Truncate(sz)
b = combined
}
return
}
Loading