From a69a4835f2776504d7239d0b1e29cd4b1fa82c06 Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Sat, 11 Nov 2023 06:58:22 -0800 Subject: [PATCH] netstack: iptables DNAT revision 1 support PiperOrigin-RevId: 581537255 --- pkg/sentry/socket/netfilter/dnat.go | 81 ++++++++++++++++++++++++++ pkg/sentry/socket/netfilter/targets.go | 6 ++ 2 files changed, 87 insertions(+) diff --git a/pkg/sentry/socket/netfilter/dnat.go b/pkg/sentry/socket/netfilter/dnat.go index 2ec38e2a2..304c927bf 100644 --- a/pkg/sentry/socket/netfilter/dnat.go +++ b/pkg/sentry/socket/netfilter/dnat.go @@ -15,6 +15,8 @@ package netfilter import ( + "fmt" + "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/marshal" "gvisor.dev/gvisor/pkg/syserr" @@ -116,3 +118,82 @@ func (*dnatTargetMakerV4) unmarshal(buf []byte, filter stack.IPHeaderFilter) (ta return &target, nil } + +type dnatTargetMakerR1 struct { + NetworkProtocol tcpip.NetworkProtocolNumber +} + +func (st *dnatTargetMakerR1) id() targetID { + return targetID{ + name: DNATTargetName, + networkProtocol: st.NetworkProtocol, + revision: 1, + } +} + +func (*dnatTargetMakerR1) marshal(target target) []byte { + st := target.(*dnatTarget) + nt := linux.XTNATTargetV1{ + Target: linux.XTEntryTarget{ + TargetSize: linux.SizeOfXTNATTargetV1, + }, + Range: linux.NFNATRange{ + Flags: linux.NF_NAT_RANGE_MAP_IPS | linux.NF_NAT_RANGE_PROTO_SPECIFIED, + }, + } + copy(nt.Target.Name[:], DNATTargetName) + copy(nt.Range.MinAddr[:], st.Addr.AsSlice()) + copy(nt.Range.MaxAddr[:], st.Addr.AsSlice()) + nt.Range.MinProto = htons(st.Port) + nt.Range.MaxProto = nt.Range.MinProto + + return marshal.Marshal(&nt) +} + +func (st *dnatTargetMakerR1) unmarshal(buf []byte, filter stack.IPHeaderFilter) (target, *syserr.Error) { + if size := linux.SizeOfXTNATTargetV1; len(buf) < size { + nflog("dnatTargetMakerR1: buf has insufficient size (%d) for DNAT target (%d)", len(buf), size) + return nil, syserr.ErrInvalidArgument + } + + if p := filter.Protocol; p != header.TCPProtocolNumber && p != header.UDPProtocolNumber { + nflog("dnatTargetMakerR1: bad proto %d", p) + return nil, syserr.ErrInvalidArgument + } + + var natRange linux.NFNATRange + natRange.UnmarshalUnsafe(buf[linux.SizeOfXTEntryTarget:]) + + // TODO(gvisor.dev/issue/5697): Support port or address ranges. + if natRange.MinAddr != natRange.MaxAddr { + nflog("dnatTargetMakerR1: MinAddr and MaxAddr are different") + return nil, syserr.ErrInvalidArgument + } + if natRange.MinProto != natRange.MaxProto { + nflog("dnatTargetMakerR1: MinProto and MaxProto are different") + return nil, syserr.ErrInvalidArgument + } + + // TODO(gvisor.dev/issue/5698): Support other NF_NAT_RANGE flags. + if natRange.Flags != linux.NF_NAT_RANGE_MAP_IPS|linux.NF_NAT_RANGE_PROTO_SPECIFIED { + nflog("dnatTargetMakerR1: invalid range flags %d", natRange.Flags) + return nil, syserr.ErrInvalidArgument + } + + target := dnatTarget{ + DNATTarget: stack.DNATTarget{ + NetworkProtocol: filter.NetworkProtocol(), + Port: ntohs(natRange.MinProto), + }, + } + switch st.NetworkProtocol { + case header.IPv4ProtocolNumber: + target.DNATTarget.Addr = tcpip.AddrFrom4Slice(natRange.MinAddr[:4]) + case header.IPv6ProtocolNumber: + target.DNATTarget.Addr = tcpip.AddrFrom16(natRange.MinAddr) + default: + panic(fmt.Sprintf("invalid protocol number: %d", st.NetworkProtocol)) + } + + return &target, nil +} diff --git a/pkg/sentry/socket/netfilter/targets.go b/pkg/sentry/socket/netfilter/targets.go index f7aec13bd..73e1ec8f9 100644 --- a/pkg/sentry/socket/netfilter/targets.go +++ b/pkg/sentry/socket/netfilter/targets.go @@ -83,6 +83,12 @@ func init() { registerTargetMaker(&dnatTargetMakerV4{ NetworkProtocol: header.IPv4ProtocolNumber, }) + registerTargetMaker(&dnatTargetMakerR1{ + NetworkProtocol: header.IPv4ProtocolNumber, + }) + registerTargetMaker(&dnatTargetMakerR1{ + NetworkProtocol: header.IPv6ProtocolNumber, + }) } // The stack package provides some basic, useful targets for us. The following