diff --git a/pkg/tcpip/header/icmpv4.go b/pkg/tcpip/header/icmpv4.go index 892fe8b91..85410dbd2 100644 --- a/pkg/tcpip/header/icmpv4.go +++ b/pkg/tcpip/header/icmpv4.go @@ -102,13 +102,17 @@ const ( ICMPv4ReassemblyTimeout ICMPv4Code = 1 ) -// ICMP codes for ICMPv4 Destination Unreachable messages as defined in RFC 792. +// ICMP codes for ICMPv4 Destination Unreachable messages as defined in RFC 792, +// RFC 1122 section 3.2.2.1 and RFC 1812 section 5.2.7.1. const ( ICMPv4NetUnreachable ICMPv4Code = 0 ICMPv4HostUnreachable ICMPv4Code = 1 ICMPv4ProtoUnreachable ICMPv4Code = 2 ICMPv4PortUnreachable ICMPv4Code = 3 ICMPv4FragmentationNeeded ICMPv4Code = 4 + ICMPv4NetProhibited ICMPv4Code = 9 + ICMPv4HostProhibited ICMPv4Code = 10 + ICMPv4AdminProhibited ICMPv4Code = 13 ) // ICMPv4UnusedCode is a code to use in ICMP messages where no code is needed. diff --git a/pkg/tcpip/network/ipv4/icmp.go b/pkg/tcpip/network/ipv4/icmp.go index 92f5aa6f0..7260fa84e 100644 --- a/pkg/tcpip/network/ipv4/icmp.go +++ b/pkg/tcpip/network/ipv4/icmp.go @@ -410,6 +410,24 @@ type icmpReason interface { isICMPReason() } +// icmpReasonNetworkProhibited is an error where the destination network is +// prohibited. +type icmpReasonNetworkProhibited struct{} + +func (*icmpReasonNetworkProhibited) isICMPReason() {} + +// icmpReasonHostProhibited is an error where the destination host is +// prohibited. +type icmpReasonHostProhibited struct{} + +func (*icmpReasonHostProhibited) isICMPReason() {} + +// icmpReasonAdministrativelyProhibited is an error where the destination is +// administratively prohibited. +type icmpReasonAdministrativelyProhibited struct{} + +func (*icmpReasonAdministrativelyProhibited) isICMPReason() {} + // icmpReasonPortUnreachable is an error where the transport protocol has no // listener and no alternative means to inform the sender. type icmpReasonPortUnreachable struct{} @@ -560,6 +578,12 @@ func (p *protocol) returnError(reason icmpReason, pkt *stack.PacketBuffer, deliv sent := netEP.stats.icmp.packetsSent icmpType, icmpCode, counter, pointer := func() (header.ICMPv4Type, header.ICMPv4Code, tcpip.MultiCounterStat, byte) { switch reason := reason.(type) { + case *icmpReasonNetworkProhibited: + return header.ICMPv4DstUnreachable, header.ICMPv4NetProhibited, sent.dstUnreachable, 0 + case *icmpReasonHostProhibited: + return header.ICMPv4DstUnreachable, header.ICMPv4HostProhibited, sent.dstUnreachable, 0 + case *icmpReasonAdministrativelyProhibited: + return header.ICMPv4DstUnreachable, header.ICMPv4AdminProhibited, sent.dstUnreachable, 0 case *icmpReasonPortUnreachable: return header.ICMPv4DstUnreachable, header.ICMPv4PortUnreachable, sent.dstUnreachable, 0 case *icmpReasonProtoUnreachable: diff --git a/pkg/tcpip/network/ipv4/ipv4.go b/pkg/tcpip/network/ipv4/ipv4.go index 475bb82cd..85413a868 100644 --- a/pkg/tcpip/network/ipv4/ipv4.go +++ b/pkg/tcpip/network/ipv4/ipv4.go @@ -1129,6 +1129,7 @@ func (e *endpoint) Stats() stack.NetworkEndpointStats { } var _ stack.NetworkProtocol = (*protocol)(nil) +var _ stack.RejectIPv4WithHandler = (*protocol)(nil) var _ fragmentation.TimeoutHandler = (*protocol)(nil) type protocol struct { @@ -1285,6 +1286,26 @@ func (p *protocol) allowICMPReply(icmpType header.ICMPv4Type, code header.ICMPv4 return true } +// SendRejectionError implements stack.RejectIPv4WithHandler. +func (p *protocol) SendRejectionError(pkt *stack.PacketBuffer, rejectWith stack.RejectIPv4WithICMPType, inputHook bool) tcpip.Error { + switch rejectWith { + case stack.RejectIPv4WithICMPNetUnreachable: + return p.returnError(&icmpReasonNetworkUnreachable{}, pkt, inputHook) + case stack.RejectIPv4WithICMPHostUnreachable: + return p.returnError(&icmpReasonHostUnreachable{}, pkt, inputHook) + case stack.RejectIPv4WithICMPPortUnreachable: + return p.returnError(&icmpReasonPortUnreachable{}, pkt, inputHook) + case stack.RejectIPv4WithICMPNetProhibited: + return p.returnError(&icmpReasonNetworkProhibited{}, pkt, inputHook) + case stack.RejectIPv4WithICMPHostProhibited: + return p.returnError(&icmpReasonHostProhibited{}, pkt, inputHook) + case stack.RejectIPv4WithICMPAdminProhibited: + return p.returnError(&icmpReasonAdministrativelyProhibited{}, pkt, inputHook) + default: + panic(fmt.Sprintf("unhandled %[1]T = %[1]d", rejectWith)) + } +} + // calculateNetworkMTU calculates the network-layer payload MTU based on the // link-layer payload mtu. func calculateNetworkMTU(linkMTU, networkHeaderSize uint32) (uint32, tcpip.Error) { diff --git a/pkg/tcpip/network/ipv6/icmp.go b/pkg/tcpip/network/ipv6/icmp.go index 0ae6bb3e8..a74c61517 100644 --- a/pkg/tcpip/network/ipv6/icmp.go +++ b/pkg/tcpip/network/ipv6/icmp.go @@ -678,7 +678,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r }) defer replyPkt.DecRef() icmp := header.ICMPv6(replyPkt.TransportHeader().Push(header.ICMPv6EchoMinimumSize)) - pkt.TransportProtocolNumber = header.ICMPv6ProtocolNumber + replyPkt.TransportProtocolNumber = header.ICMPv6ProtocolNumber copy(icmp, h) icmp.SetType(header.ICMPv6EchoReply) dataRange := replyPkt.Data().AsRange() @@ -964,6 +964,16 @@ func (p *icmpReasonParameterProblem) respondsToMulticast() bool { return p.respondToMulticast } +// icmpReasonAdministrativelyProhibited is an error where the destination is +// administratively prohibited. +type icmpReasonAdministrativelyProhibited struct{} + +func (*icmpReasonAdministrativelyProhibited) isICMPReason() {} + +func (*icmpReasonAdministrativelyProhibited) respondsToMulticast() bool { + return false +} + // icmpReasonPortUnreachable is an error where the transport protocol has no // listener and no alternative means to inform the sender. type icmpReasonPortUnreachable struct{} @@ -1104,6 +1114,8 @@ func (p *protocol) returnError(reason icmpReason, pkt *stack.PacketBuffer, deliv switch reason := reason.(type) { case *icmpReasonParameterProblem: return header.ICMPv6ParamProblem, reason.code, sent.paramProblem, reason.pointer + case *icmpReasonAdministrativelyProhibited: + return header.ICMPv6DstUnreachable, header.ICMPv6Prohibited, sent.dstUnreachable, 0 case *icmpReasonPortUnreachable: return header.ICMPv6DstUnreachable, header.ICMPv6PortUnreachable, sent.dstUnreachable, 0 case *icmpReasonNetUnreachable: diff --git a/pkg/tcpip/network/ipv6/ipv6.go b/pkg/tcpip/network/ipv6/ipv6.go index 8b2d09bc3..fa492c062 100644 --- a/pkg/tcpip/network/ipv6/ipv6.go +++ b/pkg/tcpip/network/ipv6/ipv6.go @@ -1899,6 +1899,7 @@ func (e *endpoint) Stats() stack.NetworkEndpointStats { } var _ stack.NetworkProtocol = (*protocol)(nil) +var _ stack.RejectIPv6WithHandler = (*protocol)(nil) var _ fragmentation.TimeoutHandler = (*protocol)(nil) type protocol struct { @@ -2124,6 +2125,22 @@ func (p *protocol) allowICMPReply(icmpType header.ICMPv6Type) bool { return true } +// SendRejectionError implements stack.RejectIPv6WithHandler. +func (p *protocol) SendRejectionError(pkt *stack.PacketBuffer, rejectWith stack.RejectIPv6WithICMPType, inputHook bool) tcpip.Error { + switch rejectWith { + case stack.RejectIPv6WithICMPNoRoute: + return p.returnError(&icmpReasonNetUnreachable{}, pkt, inputHook) + case stack.RejectIPv6WithICMPAddrUnreachable: + return p.returnError(&icmpReasonHostUnreachable{}, pkt, inputHook) + case stack.RejectIPv6WithICMPPortUnreachable: + return p.returnError(&icmpReasonPortUnreachable{}, pkt, inputHook) + case stack.RejectIPv6WithICMPAdminProhibited: + return p.returnError(&icmpReasonAdministrativelyProhibited{}, pkt, inputHook) + default: + panic(fmt.Sprintf("unhandled %[1]T = %[1]d", rejectWith)) + } +} + // calculateNetworkMTU calculates the network-layer payload MTU based on the // link-layer payload MTU and the length of every IPv6 header. // Note that this is different than the Payload Length field of the IPv6 header, diff --git a/pkg/tcpip/stack/iptables_targets.go b/pkg/tcpip/stack/iptables_targets.go index 13d3a93a9..1af3418e2 100644 --- a/pkg/tcpip/stack/iptables_targets.go +++ b/pkg/tcpip/stack/iptables_targets.go @@ -45,6 +45,88 @@ func (*DropTarget) Action(*PacketBuffer, Hook, *Route, AddressableEndpoint) (Rul return RuleDrop, 0 } +// RejectIPv4WithHandler handles rejecting a packet. +type RejectIPv4WithHandler interface { + // SendRejectionError sends an error packet in response to the packet. + SendRejectionError(pkt *PacketBuffer, rejectWith RejectIPv4WithICMPType, inputHook bool) tcpip.Error +} + +// RejectIPv4WithICMPType indicates the type of ICMP error that should be sent. +type RejectIPv4WithICMPType int + +// The types of errors that may be returned when rejecting IPv4 packets. +const ( + _ RejectIPv4WithICMPType = iota + RejectIPv4WithICMPNetUnreachable + RejectIPv4WithICMPHostUnreachable + RejectIPv4WithICMPPortUnreachable + RejectIPv4WithICMPNetProhibited + RejectIPv4WithICMPHostProhibited + RejectIPv4WithICMPAdminProhibited +) + +// RejectIPv4Target drops packets and sends back an error packet in response to the +// matched packet. +type RejectIPv4Target struct { + Handler RejectIPv4WithHandler + RejectWith RejectIPv4WithICMPType +} + +// Action implements Target.Action. +func (rt *RejectIPv4Target) Action(pkt *PacketBuffer, hook Hook, _ *Route, _ AddressableEndpoint) (RuleVerdict, int) { + switch hook { + case Input, Forward, Output: + // There is nothing reasonable for us to do in response to an error here; + // we already drop the packet. + _ = rt.Handler.SendRejectionError(pkt, rt.RejectWith, hook == Input) + return RuleDrop, 0 + case Prerouting, Postrouting: + panic(fmt.Sprintf("%s hook not supported for REDIRECT", hook)) + default: + panic(fmt.Sprintf("unhandled hook = %s", hook)) + } +} + +// RejectIPv6WithHandler handles rejecting a packet. +type RejectIPv6WithHandler interface { + // SendRejectionError sends an error packet in response to the packet. + SendRejectionError(pkt *PacketBuffer, rejectWith RejectIPv6WithICMPType, forwardingHook bool) tcpip.Error +} + +// RejectIPv6WithICMPType indicates the type of ICMP error that should be sent. +type RejectIPv6WithICMPType int + +// The types of errors that may be returned when rejecting IPv6 packets. +const ( + _ RejectIPv6WithICMPType = iota + RejectIPv6WithICMPNoRoute + RejectIPv6WithICMPAddrUnreachable + RejectIPv6WithICMPPortUnreachable + RejectIPv6WithICMPAdminProhibited +) + +// RejectIPv6Target drops packets and sends back an error packet in response to the +// matched packet. +type RejectIPv6Target struct { + Handler RejectIPv6WithHandler + RejectWith RejectIPv6WithICMPType +} + +// Action implements Target.Action. +func (rt *RejectIPv6Target) Action(pkt *PacketBuffer, hook Hook, _ *Route, _ AddressableEndpoint) (RuleVerdict, int) { + switch hook { + case Input, Forward, Output: + // There is nothing reasonable for us to do in response to an error here; + // we already drop the packet. + _ = rt.Handler.SendRejectionError(pkt, rt.RejectWith, hook == Input) + return RuleDrop, 0 + case Prerouting, Postrouting: + panic(fmt.Sprintf("%s hook not supported for REDIRECT", hook)) + default: + panic(fmt.Sprintf("unhandled hook = %s", hook)) + } +} + // ErrorTarget logs an error and drops the packet. It represents a target that // should be unreachable. type ErrorTarget struct { diff --git a/pkg/tcpip/tests/integration/iptables_test.go b/pkg/tcpip/tests/integration/iptables_test.go index e84193de9..49e1ef4af 100644 --- a/pkg/tcpip/tests/integration/iptables_test.go +++ b/pkg/tcpip/tests/integration/iptables_test.go @@ -3037,3 +3037,295 @@ func TestLocallyRoutedPackets(t *testing.T) { }) } } + +type icmpv4Matcher struct { + icmpType header.ICMPv4Type +} + +func (m *icmpv4Matcher) Match(_ stack.Hook, pkt *stack.PacketBuffer, _, _ string) (matches bool, hotdrop bool) { + if pkt.NetworkProtocolNumber != header.IPv4ProtocolNumber { + return false, false + } + + if pkt.TransportProtocolNumber != header.ICMPv4ProtocolNumber { + return false, false + } + + return header.ICMPv4(pkt.TransportHeader().View()).Type() == m.icmpType, false +} + +type icmpv6Matcher struct { + icmpType header.ICMPv6Type +} + +func (m *icmpv6Matcher) Match(_ stack.Hook, pkt *stack.PacketBuffer, _, _ string) (matches bool, hotdrop bool) { + if pkt.NetworkProtocolNumber != header.IPv6ProtocolNumber { + return false, false + } + + if pkt.TransportProtocolNumber != header.ICMPv6ProtocolNumber { + return false, false + } + + return header.ICMPv6(pkt.TransportHeader().View()).Type() == m.icmpType, false +} + +func TestRejectWith(t *testing.T) { + type natHook struct { + hook stack.Hook + dstAddr tcpip.Address + matcher stack.Matcher + + errorICMPDstAddr tcpip.Address + errorICMPPayload buffer.View + } + + type rejectWithVal struct { + name string + val int + errorICMPCode uint8 + } + + rxICMPv4EchoRequest := func(dst tcpip.Address) buffer.View { + return utils.ICMPv4Echo(utils.Host1IPv4Addr.AddressWithPrefix.Address, dst, ttl, header.ICMPv4Echo) + } + + rxICMPv6EchoRequest := func(dst tcpip.Address) buffer.View { + return utils.ICMPv6Echo(utils.Host1IPv6Addr.AddressWithPrefix.Address, dst, ttl, header.ICMPv6EchoRequest) + } + + tests := []struct { + name string + netProto tcpip.NetworkProtocolNumber + rxICMPEchoRequest func(tcpip.Address) buffer.View + icmpChecker func(*testing.T, buffer.View, tcpip.Address, uint8, uint8, buffer.View) + + natHooks []natHook + + rejectTarget func(*testing.T, stack.NetworkProtocol, int) stack.Target + rejectWithVals []rejectWithVal + errorICMPType uint8 + }{ + { + name: "IPv4", + netProto: header.IPv4ProtocolNumber, + rxICMPEchoRequest: rxICMPv4EchoRequest, + + icmpChecker: func(t *testing.T, v buffer.View, dstAddr tcpip.Address, icmpType, icmpCode uint8, origPayload buffer.View) { + t.Helper() + + checker.IPv4(t, v, + checker.SrcAddr(utils.RouterNIC1IPv4Addr.AddressWithPrefix.Address), + checker.DstAddr(dstAddr), + checker.ICMPv4( + checker.ICMPv4Checksum(), + checker.ICMPv4Type(header.ICMPv4Type(icmpType)), + checker.ICMPv4Code(header.ICMPv4Code(icmpCode)), + checker.ICMPv4Payload(origPayload), + ), + ) + }, + natHooks: []natHook{ + { + hook: stack.Input, + dstAddr: utils.RouterNIC1IPv4Addr.AddressWithPrefix.Address, + matcher: &icmpv4Matcher{icmpType: header.ICMPv4Echo}, + errorICMPDstAddr: utils.Host1IPv4Addr.AddressWithPrefix.Address, + errorICMPPayload: rxICMPv4EchoRequest(utils.RouterNIC1IPv4Addr.AddressWithPrefix.Address), + }, + { + hook: stack.Forward, + dstAddr: utils.Host2IPv4Addr.AddressWithPrefix.Address, + matcher: &icmpv4Matcher{icmpType: header.ICMPv4Echo}, + errorICMPDstAddr: utils.Host1IPv4Addr.AddressWithPrefix.Address, + errorICMPPayload: rxICMPv4EchoRequest(utils.Host2IPv4Addr.AddressWithPrefix.Address), + }, + { + hook: stack.Output, + dstAddr: utils.RouterNIC1IPv4Addr.AddressWithPrefix.Address, + matcher: &icmpv4Matcher{icmpType: header.ICMPv4EchoReply}, + errorICMPDstAddr: utils.RouterNIC1IPv4Addr.AddressWithPrefix.Address, + errorICMPPayload: utils.ICMPv4Echo(utils.RouterNIC1IPv4Addr.AddressWithPrefix.Address, utils.Host1IPv4Addr.AddressWithPrefix.Address, ttl, header.ICMPv4EchoReply), + }, + }, + rejectTarget: func(t *testing.T, netProto stack.NetworkProtocol, rejectWith int) stack.Target { + handler, ok := netProto.(stack.RejectIPv4WithHandler) + if !ok { + t.Fatalf("expected %T to implement %T", netProto, handler) + } + + return &stack.RejectIPv4Target{ + Handler: handler, + RejectWith: stack.RejectIPv4WithICMPType(rejectWith), + } + }, + rejectWithVals: []rejectWithVal{ + { + name: "ICMP Network Unreachable", + val: int(stack.RejectIPv4WithICMPNetUnreachable), + errorICMPCode: uint8(header.ICMPv4NetUnreachable), + }, + { + name: "ICMP Host Unreachable", + val: int(stack.RejectIPv4WithICMPHostUnreachable), + errorICMPCode: uint8(header.ICMPv4HostUnreachable), + }, + { + name: "ICMP Port Unreachable", + val: int(stack.RejectIPv4WithICMPPortUnreachable), + errorICMPCode: uint8(header.ICMPv4PortUnreachable), + }, + { + name: "ICMP Network Prohibited", + val: int(stack.RejectIPv4WithICMPNetProhibited), + errorICMPCode: uint8(header.ICMPv4NetProhibited), + }, + { + name: "ICMP Host Prohibited", + val: int(stack.RejectIPv4WithICMPHostProhibited), + errorICMPCode: uint8(header.ICMPv4HostProhibited), + }, + { + name: "ICMP Administratively Prohibited", + val: int(stack.RejectIPv4WithICMPAdminProhibited), + errorICMPCode: uint8(header.ICMPv4AdminProhibited), + }, + }, + errorICMPType: uint8(header.ICMPv4DstUnreachable), + }, + { + name: "IPv6", + netProto: header.IPv6ProtocolNumber, + rxICMPEchoRequest: rxICMPv6EchoRequest, + + icmpChecker: func(t *testing.T, v buffer.View, dstAddr tcpip.Address, icmpType, icmpCode uint8, origPayload buffer.View) { + t.Helper() + + checker.IPv6(t, v, + checker.SrcAddr(utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address), + checker.DstAddr(dstAddr), + checker.ICMPv6( + checker.ICMPv6Type(header.ICMPv6Type(icmpType)), + checker.ICMPv6Code(header.ICMPv6Code(icmpCode)), + checker.ICMPv6Payload(origPayload), + ), + ) + }, + natHooks: []natHook{ + { + hook: stack.Input, + dstAddr: utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address, + matcher: &icmpv6Matcher{icmpType: header.ICMPv6EchoRequest}, + errorICMPDstAddr: utils.Host1IPv6Addr.AddressWithPrefix.Address, + errorICMPPayload: rxICMPv6EchoRequest(utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address), + }, + { + hook: stack.Forward, + dstAddr: utils.Host2IPv6Addr.AddressWithPrefix.Address, + matcher: &icmpv6Matcher{icmpType: header.ICMPv6EchoRequest}, + errorICMPDstAddr: utils.Host1IPv6Addr.AddressWithPrefix.Address, + errorICMPPayload: rxICMPv6EchoRequest(utils.Host2IPv6Addr.AddressWithPrefix.Address), + }, + { + hook: stack.Output, + dstAddr: utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address, + matcher: &icmpv6Matcher{icmpType: header.ICMPv6EchoReply}, + errorICMPDstAddr: utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address, + errorICMPPayload: utils.ICMPv6Echo(utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address, utils.Host1IPv6Addr.AddressWithPrefix.Address, ttl, header.ICMPv6EchoReply), + }, + }, + rejectTarget: func(t *testing.T, netProto stack.NetworkProtocol, rejectWith int) stack.Target { + handler, ok := netProto.(stack.RejectIPv6WithHandler) + if !ok { + t.Fatalf("expected %T to implement %T", netProto, handler) + } + + return &stack.RejectIPv6Target{ + Handler: handler, + RejectWith: stack.RejectIPv6WithICMPType(rejectWith), + } + }, + rejectWithVals: []rejectWithVal{ + { + name: "ICMP No Route", + val: int(stack.RejectIPv6WithICMPNoRoute), + errorICMPCode: uint8(header.ICMPv6NetworkUnreachable), + }, + { + name: "ICMP Address Unreachable", + val: int(stack.RejectIPv6WithICMPAddrUnreachable), + errorICMPCode: uint8(header.ICMPv6AddressUnreachable), + }, + { + name: "ICMP Port Unreachable", + val: int(stack.RejectIPv6WithICMPPortUnreachable), + errorICMPCode: uint8(header.ICMPv6PortUnreachable), + }, + { + name: "ICMP Administratively Prohibited", + val: int(stack.RejectIPv6WithICMPAdminProhibited), + errorICMPCode: uint8(header.ICMPv6Prohibited), + }, + }, + errorICMPType: uint8(header.ICMPv6DstUnreachable), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + for _, natHook := range test.natHooks { + t.Run(natHook.hook.String(), func(t *testing.T) { + for _, rejectWith := range test.rejectWithVals { + t.Run(rejectWith.name, func(t *testing.T) { + s := stack.New(stack.Options{ + NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol}, + TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol, tcp.NewProtocol}, + }) + + ep1 := channel.New(1, header.IPv6MinimumMTU, "") + ep2 := channel.New(1, header.IPv6MinimumMTU, "") + utils.SetupRouterStack(t, s, ep1, ep2) + + { + ipv6 := test.netProto == ipv6.ProtocolNumber + ipt := s.IPTables() + filter := ipt.GetTable(stack.FilterID, ipv6) + ruleIdx := filter.BuiltinChains[natHook.hook] + filter.Rules[ruleIdx].Matchers = []stack.Matcher{natHook.matcher} + filter.Rules[ruleIdx].Target = test.rejectTarget(t, s.NetworkProtocolInstance(test.netProto), rejectWith.val) + // Make sure the packet is not dropped by the next rule. + filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} + if err := ipt.ReplaceTable(stack.FilterID, filter, ipv6); err != nil { + t.Fatalf("ipt.ReplaceTable(%d, _, %t): %s", stack.FilterID, ipv6, err) + } + } + + func() { + pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ + Data: test.rxICMPEchoRequest(natHook.dstAddr).ToVectorisedView(), + }) + defer pkt.DecRef() + ep1.InjectInbound(test.netProto, pkt) + }() + + { + pkt := ep1.Read() + if pkt == nil { + t.Fatal("expected to read a packet on ep1") + } + test.icmpChecker( + t, + stack.PayloadSince(pkt.NetworkHeader()), + natHook.errorICMPDstAddr, + test.errorICMPType, + rejectWith.errorICMPCode, + natHook.errorICMPPayload, + ) + } + }) + } + }) + } + }) + } +} diff --git a/pkg/tcpip/tests/utils/utils.go b/pkg/tcpip/tests/utils/utils.go index 24afd8794..ea76b536e 100644 --- a/pkg/tcpip/tests/utils/utils.go +++ b/pkg/tcpip/tests/utils/utils.go @@ -353,7 +353,8 @@ func SetupRoutedStacks(t *testing.T, host1Stack, routerStack, host2Stack *stack. }) } -func rxICMPv4Echo(e *channel.Endpoint, src, dst tcpip.Address, ttl uint8, ty header.ICMPv4Type) { +// ICMPv4Echo returns an ICMPv4 echo packet. +func ICMPv4Echo(src, dst tcpip.Address, ttl uint8, ty header.ICMPv4Type) buffer.View { totalLen := header.IPv4MinimumSize + header.ICMPv4MinimumSize hdr := buffer.NewPrependable(totalLen) pkt := header.ICMPv4(hdr.Prepend(header.ICMPv4MinimumSize)) @@ -370,27 +371,31 @@ func rxICMPv4Echo(e *channel.Endpoint, src, dst tcpip.Address, ttl uint8, ty hea DstAddr: dst, }) ip.SetChecksum(^ip.CalculateChecksum()) - - newPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ - Data: hdr.View().ToVectorisedView(), - }) - defer newPkt.DecRef() - e.InjectInbound(header.IPv4ProtocolNumber, newPkt) + return hdr.View() } // RxICMPv4EchoRequest constructs and injects an ICMPv4 echo request packet on // the provided endpoint. func RxICMPv4EchoRequest(e *channel.Endpoint, src, dst tcpip.Address, ttl uint8) { - rxICMPv4Echo(e, src, dst, ttl, header.ICMPv4Echo) + newPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ + Data: ICMPv4Echo(src, dst, ttl, header.ICMPv4Echo).ToVectorisedView(), + }) + defer newPkt.DecRef() + e.InjectInbound(header.IPv4ProtocolNumber, newPkt) } // RxICMPv4EchoReply constructs and injects an ICMPv4 echo reply packet on // the provided endpoint. func RxICMPv4EchoReply(e *channel.Endpoint, src, dst tcpip.Address, ttl uint8) { - rxICMPv4Echo(e, src, dst, ttl, header.ICMPv4EchoReply) + newPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ + Data: ICMPv4Echo(src, dst, ttl, header.ICMPv4EchoReply).ToVectorisedView(), + }) + defer newPkt.DecRef() + e.InjectInbound(header.IPv4ProtocolNumber, newPkt) } -func rxICMPv6Echo(e *channel.Endpoint, src, dst tcpip.Address, ttl uint8, ty header.ICMPv6Type) { +// ICMPv6Echo returns an ICMPv6 echo packet. +func ICMPv6Echo(src, dst tcpip.Address, ttl uint8, ty header.ICMPv6Type) buffer.View { totalLen := header.IPv6MinimumSize + header.ICMPv6MinimumSize hdr := buffer.NewPrependable(totalLen) pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6MinimumSize)) @@ -410,22 +415,25 @@ func rxICMPv6Echo(e *channel.Endpoint, src, dst tcpip.Address, ttl uint8, ty hea SrcAddr: src, DstAddr: dst, }) - - newPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ - Data: hdr.View().ToVectorisedView(), - }) - defer newPkt.DecRef() - e.InjectInbound(header.IPv6ProtocolNumber, newPkt) + return hdr.View() } // RxICMPv6EchoRequest constructs and injects an ICMPv6 echo request packet on // the provided endpoint. func RxICMPv6EchoRequest(e *channel.Endpoint, src, dst tcpip.Address, ttl uint8) { - rxICMPv6Echo(e, src, dst, ttl, header.ICMPv6EchoRequest) + newPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ + Data: ICMPv6Echo(src, dst, ttl, header.ICMPv6EchoRequest).ToVectorisedView(), + }) + defer newPkt.DecRef() + e.InjectInbound(header.IPv6ProtocolNumber, newPkt) } // RxICMPv6EchoReply constructs and injects an ICMPv6 echo reply packet on // the provided endpoint. func RxICMPv6EchoReply(e *channel.Endpoint, src, dst tcpip.Address, ttl uint8) { - rxICMPv6Echo(e, src, dst, ttl, header.ICMPv6EchoReply) + newPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ + Data: ICMPv6Echo(src, dst, ttl, header.ICMPv6EchoReply).ToVectorisedView(), + }) + defer newPkt.DecRef() + e.InjectInbound(header.IPv6ProtocolNumber, newPkt) }