diff --git a/pkg/sentry/socket/netfilter/BUILD b/pkg/sentry/socket/netfilter/BUILD index edb5007b8..1e59090c6 100644 --- a/pkg/sentry/socket/netfilter/BUILD +++ b/pkg/sentry/socket/netfilter/BUILD @@ -38,11 +38,14 @@ go_library( ) go_test( - name = "netfilter_test", - srcs = ["netfilter_test.go"], - embedsrcs = ["istio_blob"], - library = ":netfilter", + name = "netfilter_x_test", + srcs = ["netfilter_x_test.go"], + embedsrcs = [ + "accept_blob", + "istio_blob", + ], deps = [ + ":netfilter", "//pkg/sentry/kernel/auth", "//pkg/tcpip/stack", ], diff --git a/pkg/sentry/socket/netfilter/accept_blob b/pkg/sentry/socket/netfilter/accept_blob new file mode 100755 index 000000000..8ff78529c Binary files /dev/null and b/pkg/sentry/socket/netfilter/accept_blob differ diff --git a/pkg/sentry/socket/netfilter/netfilter_test.go b/pkg/sentry/socket/netfilter/netfilter_x_test.go similarity index 52% rename from pkg/sentry/socket/netfilter/netfilter_test.go rename to pkg/sentry/socket/netfilter/netfilter_x_test.go index bd36d5f52..8ee57f4c4 100644 --- a/pkg/sentry/socket/netfilter/netfilter_test.go +++ b/pkg/sentry/socket/netfilter/netfilter_x_test.go @@ -12,26 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -package netfilter +package netfilter_test import ( _ "embed" "testing" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" + "gvisor.dev/gvisor/pkg/sentry/socket/netfilter" "gvisor.dev/gvisor/pkg/tcpip/stack" ) -// istioBlob is a golden iptables ruleset generated by Istio. It is already in -// the format ready to by passed to IPT_SO_SET_REPLACE. -// -// Updating this requires running Istio, calling IPT_SO_GET_INFO and -// IPT_SO_GET_ENTRIES, then stitching the structs up. So be careful when -// updating! -// -//go:embed istio_blob -var istioBlob []byte - // FakeIDMapper implements IDMapper. type FakeIDMapper struct{} @@ -45,12 +36,50 @@ func (*FakeIDMapper) MapToKGID(auth.GID) auth.KGID { return 0 } +// istioBlob is an iptables NAT ruleset generated by Istio. It is already in +// the format ready to by passed to IPT_SO_SET_REPLACE. +// +// Updating this requires running Istio, calling IPT_SO_GET_INFO and +// IPT_SO_GET_ENTRIES, then stitching the structs up. So be careful when +// updating! +// +//go:embed istio_blob +var istioBlob []byte + // TestIstioBlob tests that we support the iptables ruleset generated by Istio, // i.e. that we can parse the rules and set them in netstack. func TestIstioBlob(t *testing.T) { mapper := FakeIDMapper{} - stack := stack.New(stack.Options{}) - if err := SetEntries(&mapper, stack, istioBlob, false); err != nil { + stk := stack.New(stack.Options{}) + if err := netfilter.SetEntries(&mapper, stk, istioBlob, false); err != nil { t.Fatalf("failed to set Istio rules, try setting enableLogging and running again: %v", err) } } + +// acceptBlob is an iptables NAT ruleset that instructs iptables to ACCEPT all +// and modify nothing. This is the Linux default: a newly booted machine just +// doesn't NAT anything until someone sets the rules. +// +// Updating this requires running Istio, calling IPT_SO_GET_INFO and +// IPT_SO_GET_ENTRIES, then stitching the structs up. So be careful when +// updating! +// +//go:embed accept_blob +var acceptBlob []byte + +// TestAcceptBlob tests that updating the default (all ACCEPT) NAT table with +// an identical table doesn't actually set the modified flag. In doing so, we +// can preserve our performance optimization in which iptables is off until +// non-default rules are set. +func TestAcceptBlob(t *testing.T) { + mapper := FakeIDMapper{} + stk := stack.New(stack.Options{ + DefaultIPTables: netfilter.DefaultLinuxTables, + }) + if err := netfilter.SetEntries(&mapper, stk, acceptBlob, false); err != nil { + t.Fatalf("failed to set Istio rules, try setting enableLogging and running again: %v", err) + } + if stk.IPTables().Modified() { + t.Fatalf("ACCEPT rules shouldn't cause iptables modifications, but did") + } +} diff --git a/pkg/sentry/socket/netfilter/tcp_matcher.go b/pkg/sentry/socket/netfilter/tcp_matcher.go index 264a5b0ae..e09853fd1 100644 --- a/pkg/sentry/socket/netfilter/tcp_matcher.go +++ b/pkg/sentry/socket/netfilter/tcp_matcher.go @@ -49,6 +49,8 @@ func (tcpMarshaler) marshal(mr matcher) []byte { SourcePortEnd: matcher.sourcePortEnd, DestinationPortStart: matcher.destinationPortStart, DestinationPortEnd: matcher.destinationPortEnd, + FlagMask: matcher.flagMask, + FlagCompare: matcher.flagCompare, } return marshalEntryMatch(matcherNameTCP, marshal.Marshal(&xttcp)) } @@ -65,10 +67,7 @@ func (tcpMarshaler) unmarshal(_ IDMapper, buf []byte, filter stack.IPHeaderFilte matchData.UnmarshalUnsafe(buf) nflog("parseMatchers: parsed XTTCP: %+v", matchData) - if matchData.Option != 0 || - matchData.FlagMask != 0 || - matchData.FlagCompare != 0 || - matchData.InverseFlags != 0 { + if matchData.Option != 0 || matchData.InverseFlags != 0 { return nil, fmt.Errorf("unsupported TCP matcher flags set") } @@ -81,6 +80,8 @@ func (tcpMarshaler) unmarshal(_ IDMapper, buf []byte, filter stack.IPHeaderFilte sourcePortEnd: matchData.SourcePortEnd, destinationPortStart: matchData.DestinationPortStart, destinationPortEnd: matchData.DestinationPortEnd, + flagMask: matchData.FlagMask, + flagCompare: matchData.FlagCompare, }, nil } @@ -90,6 +91,8 @@ type TCPMatcher struct { sourcePortEnd uint16 destinationPortStart uint16 destinationPortEnd uint16 + flagMask uint8 + flagCompare uint8 } // name implements matcher.name. @@ -146,5 +149,10 @@ func (tm *TCPMatcher) Match(hook stack.Hook, pkt stack.PacketBufferPtr, _, _ str return false, false } + // Check the flags. + if uint8(tcpHeader.Flags())&tm.flagMask != tm.flagCompare { + return false, false + } + return true, false } diff --git a/pkg/tcpip/network/ipv4/ipv4_test.go b/pkg/tcpip/network/ipv4/ipv4_test.go index 4b3704eb6..4f5efb048 100644 --- a/pkg/tcpip/network/ipv4/ipv4_test.go +++ b/pkg/tcpip/network/ipv4/ipv4_test.go @@ -3388,7 +3388,7 @@ func TestWriteStats(t *testing.T) { filter := ipt.GetTable(stack.FilterID, false /* ipv6 */) ruleIdx := filter.BuiltinChains[stack.Output] filter.Rules[ruleIdx].Target = &stack.DropTarget{} - ipt.ReplaceTable(stack.FilterID, filter, false /* ipv6 */) + ipt.ForceReplaceTable(stack.FilterID, filter, false /* ipv6 */) }, allowPackets: math.MaxInt32, expectSent: 0, @@ -3402,7 +3402,7 @@ func TestWriteStats(t *testing.T) { filter := ipt.GetTable(stack.NATID, false /* ipv6 */) ruleIdx := filter.BuiltinChains[stack.Postrouting] filter.Rules[ruleIdx].Target = &stack.DropTarget{} - ipt.ReplaceTable(stack.NATID, filter, false /* ipv6 */) + ipt.ForceReplaceTable(stack.NATID, filter, false /* ipv6 */) }, allowPackets: math.MaxInt32, expectSent: 0, @@ -3422,7 +3422,7 @@ func TestWriteStats(t *testing.T) { filter.Rules[ruleIdx].Matchers = []stack.Matcher{&limitedMatcher{nPackets - 1}} // Make sure the next rule is ACCEPT. filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} - ipt.ReplaceTable(stack.FilterID, filter, false /* ipv6 */) + ipt.ForceReplaceTable(stack.FilterID, filter, false /* ipv6 */) }, allowPackets: math.MaxInt32, expectSent: nPackets - 1, @@ -3442,7 +3442,7 @@ func TestWriteStats(t *testing.T) { filter.Rules[ruleIdx].Matchers = []stack.Matcher{&limitedMatcher{nPackets - 1}} // Make sure the next rule is ACCEPT. filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} - ipt.ReplaceTable(stack.NATID, filter, false /* ipv6 */) + ipt.ForceReplaceTable(stack.NATID, filter, false /* ipv6 */) }, allowPackets: math.MaxInt32, expectSent: nPackets - 1, @@ -3805,7 +3805,7 @@ func TestCloseLocking(t *testing.T) { stack.Postrouting: 3, }, } - s.IPTables().ReplaceTable(stack.NATID, table, false /* ipv6 */) + s.IPTables().ForceReplaceTable(stack.NATID, table, false /* ipv6 */) e := channel.New(0, defaultMTU, "") defer e.Close() diff --git a/pkg/tcpip/network/ipv6/ipv6_test.go b/pkg/tcpip/network/ipv6/ipv6_test.go index 6d2aa6f93..cbeb7a81a 100644 --- a/pkg/tcpip/network/ipv6/ipv6_test.go +++ b/pkg/tcpip/network/ipv6/ipv6_test.go @@ -2519,7 +2519,7 @@ func TestWriteStats(t *testing.T) { filter := ipt.GetTable(stack.FilterID, true /* ipv6 */) ruleIdx := filter.BuiltinChains[stack.Output] filter.Rules[ruleIdx].Target = &stack.DropTarget{} - ipt.ReplaceTable(stack.FilterID, filter, true /* ipv6 */) + ipt.ForceReplaceTable(stack.FilterID, filter, true /* ipv6 */) }, allowPackets: math.MaxInt32, expectSent: 0, @@ -2534,7 +2534,7 @@ func TestWriteStats(t *testing.T) { filter := ipt.GetTable(stack.NATID, true /* ipv6 */) ruleIdx := filter.BuiltinChains[stack.Postrouting] filter.Rules[ruleIdx].Target = &stack.DropTarget{} - ipt.ReplaceTable(stack.NATID, filter, true /* ipv6 */) + ipt.ForceReplaceTable(stack.NATID, filter, true /* ipv6 */) }, allowPackets: math.MaxInt32, expectSent: 0, @@ -2554,7 +2554,7 @@ func TestWriteStats(t *testing.T) { filter.Rules[ruleIdx].Matchers = []stack.Matcher{&limitedMatcher{nPackets - 1}} // Make sure the next rule is ACCEPT. filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} - ipt.ReplaceTable(stack.FilterID, filter, true /* ipv6 */) + ipt.ForceReplaceTable(stack.FilterID, filter, true /* ipv6 */) }, allowPackets: math.MaxInt32, expectSent: nPackets - 1, @@ -2574,7 +2574,7 @@ func TestWriteStats(t *testing.T) { filter.Rules[ruleIdx].Matchers = []stack.Matcher{&limitedMatcher{nPackets - 1}} // Make sure the next rule is ACCEPT. filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} - ipt.ReplaceTable(stack.NATID, filter, true /* ipv6 */) + ipt.ForceReplaceTable(stack.NATID, filter, true /* ipv6 */) }, allowPackets: math.MaxInt32, expectSent: nPackets - 1, diff --git a/pkg/tcpip/stack/iptables.go b/pkg/tcpip/stack/iptables.go index 9efeb5953..f961ba8a4 100644 --- a/pkg/tcpip/stack/iptables.go +++ b/pkg/tcpip/stack/iptables.go @@ -17,6 +17,7 @@ package stack import ( "fmt" "math/rand" + "reflect" "time" "gvisor.dev/gvisor/pkg/tcpip" @@ -48,11 +49,11 @@ func DefaultTables(clock tcpip.Clock, rand *rand.Rand) *IPTables { v4Tables: [NumTables]Table{ NATID: { Rules: []Rule{ - {Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, - {Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, - {Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, - {Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, - {Target: &ErrorTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, + {Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, + {Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, + {Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, + {Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, + {Filter: EmptyFilter4(), Target: &ErrorTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, }, BuiltinChains: [NumHooks]int{ Prerouting: 0, @@ -71,9 +72,9 @@ func DefaultTables(clock tcpip.Clock, rand *rand.Rand) *IPTables { }, MangleID: { Rules: []Rule{ - {Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, - {Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, - {Target: &ErrorTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, + {Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, + {Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, + {Filter: EmptyFilter4(), Target: &ErrorTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, }, BuiltinChains: [NumHooks]int{ Prerouting: 0, @@ -89,10 +90,10 @@ func DefaultTables(clock tcpip.Clock, rand *rand.Rand) *IPTables { }, FilterID: { Rules: []Rule{ - {Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, - {Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, - {Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, - {Target: &ErrorTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, + {Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, + {Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, + {Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, + {Filter: EmptyFilter4(), Target: &ErrorTarget{NetworkProtocol: header.IPv4ProtocolNumber}}, }, BuiltinChains: [NumHooks]int{ Prerouting: HookUnset, @@ -113,11 +114,11 @@ func DefaultTables(clock tcpip.Clock, rand *rand.Rand) *IPTables { v6Tables: [NumTables]Table{ NATID: { Rules: []Rule{ - {Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, - {Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, - {Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, - {Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, - {Target: &ErrorTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, + {Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, + {Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, + {Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, + {Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, + {Filter: EmptyFilter6(), Target: &ErrorTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, }, BuiltinChains: [NumHooks]int{ Prerouting: 0, @@ -136,9 +137,9 @@ func DefaultTables(clock tcpip.Clock, rand *rand.Rand) *IPTables { }, MangleID: { Rules: []Rule{ - {Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, - {Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, - {Target: &ErrorTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, + {Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, + {Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, + {Filter: EmptyFilter6(), Target: &ErrorTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, }, BuiltinChains: [NumHooks]int{ Prerouting: 0, @@ -154,10 +155,10 @@ func DefaultTables(clock tcpip.Clock, rand *rand.Rand) *IPTables { }, FilterID: { Rules: []Rule{ - {Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, - {Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, - {Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, - {Target: &ErrorTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, + {Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, + {Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, + {Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, + {Filter: EmptyFilter6(), Target: &ErrorTarget{NetworkProtocol: header.IPv6ProtocolNumber}}, }, BuiltinChains: [NumHooks]int{ Prerouting: HookUnset, @@ -232,11 +233,29 @@ func (it *IPTables) getTableRLocked(id TableID, ipv6 bool) Table { // ReplaceTable replaces or inserts table by name. It panics when an invalid id // is provided. func (it *IPTables) ReplaceTable(id TableID, table Table, ipv6 bool) { + it.replaceTable(id, table, ipv6, false /* force */) +} + +// ForceReplaceTable replaces or inserts table by name. It panics when an invalid id +// is provided. It enables iptables even when the inserted table is all +// conditionless ACCEPT, skipping our optimization that disables iptables until +// they're modified. +func (it *IPTables) ForceReplaceTable(id TableID, table Table, ipv6 bool) { + it.replaceTable(id, table, ipv6, true /* force */) +} + +func (it *IPTables) replaceTable(id TableID, table Table, ipv6, force bool) { it.mu.Lock() defer it.mu.Unlock() + // If iptables is being enabled, initialize the conntrack table and // reaper. if !it.modified { + // Don't do anything if the table is identical. + if ((ipv6 && reflect.DeepEqual(table, it.v6Tables[id])) || (!ipv6 && reflect.DeepEqual(table, it.v4Tables[id]))) && !force { + return + } + it.connections.init() it.startReaper(reaperDelay) } diff --git a/pkg/tcpip/stack/iptables_test.go b/pkg/tcpip/stack/iptables_test.go index bcbcbd17a..654803eb7 100644 --- a/pkg/tcpip/stack/iptables_test.go +++ b/pkg/tcpip/stack/iptables_test.go @@ -300,7 +300,7 @@ func TestNATAlwaysPerformed(t *testing.T) { iptables := DefaultTables(clock, rand.New(rand.NewSource(0 /* seed */))) // Just to make sure the iptables is not short circuited. - iptables.ReplaceTable(NATID, iptables.GetTable(NATID, ipv6), ipv6) + iptables.ForceReplaceTable(NATID, iptables.GetTable(NATID, ipv6), ipv6) pkt := v6PacketBuffer() diff --git a/pkg/tcpip/stack/iptables_types.go b/pkg/tcpip/stack/iptables_types.go index 3a908f9ea..7240cb0d6 100644 --- a/pkg/tcpip/stack/iptables_types.go +++ b/pkg/tcpip/stack/iptables_types.go @@ -103,6 +103,14 @@ type IPTables struct { modified bool } +// Modified returns whether iptables has been modified. It is inherently racy +// and intended for use only in tests. +func (it *IPTables) Modified() bool { + it.mu.Lock() + defer it.mu.Unlock() + return it.modified +} + // VisitTargets traverses all the targets of all tables and replaces each with // transform(target). func (it *IPTables) VisitTargets(transform func(Target) Target) { @@ -235,6 +243,26 @@ type IPHeaderFilter struct { OutputInterfaceInvert bool } +// EmptyFilter4 returns an initialized IPv4 header filter. +func EmptyFilter4() IPHeaderFilter { + return IPHeaderFilter{ + Dst: tcpip.AddrFrom4([4]byte{}), + DstMask: tcpip.AddrFrom4([4]byte{}), + Src: tcpip.AddrFrom4([4]byte{}), + SrcMask: tcpip.AddrFrom4([4]byte{}), + } +} + +// EmptyFilter6 returns an initialized IPv6 header filter. +func EmptyFilter6() IPHeaderFilter { + return IPHeaderFilter{ + Dst: tcpip.AddrFrom16([16]byte{}), + DstMask: tcpip.AddrFrom16([16]byte{}), + Src: tcpip.AddrFrom16([16]byte{}), + SrcMask: tcpip.AddrFrom16([16]byte{}), + } +} + // match returns whether pkt matches the filter. // // Preconditions: pkt.NetworkHeader is set and is at least of the minimal IPv4 diff --git a/pkg/tcpip/tests/integration/iptables_test.go b/pkg/tcpip/tests/integration/iptables_test.go index c972699d4..c61a6fecf 100644 --- a/pkg/tcpip/tests/integration/iptables_test.go +++ b/pkg/tcpip/tests/integration/iptables_test.go @@ -189,7 +189,7 @@ func TestIPTablesStatsForInput(t *testing.T) { filter.Rules[ruleIdx].Matchers = []stack.Matcher{&inputIfNameMatcher{nicName}} // Make sure the packet is not dropped by the next rule. filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} - ipt.ReplaceTable(stack.FilterID, filter, true /* ipv6 */) + ipt.ForceReplaceTable(stack.FilterID, filter, true /* ipv6 */) }, genPacket: genPacketV6, proto: header.IPv6ProtocolNumber, @@ -208,7 +208,7 @@ func TestIPTablesStatsForInput(t *testing.T) { filter.Rules[ruleIdx].Target = &stack.DropTarget{} filter.Rules[ruleIdx].Matchers = []stack.Matcher{&inputIfNameMatcher{nicName}} filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} - ipt.ReplaceTable(stack.FilterID, filter, false /* ipv6 */) + ipt.ForceReplaceTable(stack.FilterID, filter, false /* ipv6 */) }, genPacket: genPacketV4, proto: header.IPv4ProtocolNumber, @@ -226,7 +226,7 @@ func TestIPTablesStatsForInput(t *testing.T) { filter.Rules[ruleIdx].Filter = stack.IPHeaderFilter{InputInterface: anotherNicName} filter.Rules[ruleIdx].Target = &stack.DropTarget{} filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} - ipt.ReplaceTable(stack.FilterID, filter, true /* ipv6 */) + ipt.ForceReplaceTable(stack.FilterID, filter, true /* ipv6 */) }, genPacket: genPacketV6, proto: header.IPv6ProtocolNumber, @@ -244,7 +244,7 @@ func TestIPTablesStatsForInput(t *testing.T) { filter.Rules[ruleIdx].Filter = stack.IPHeaderFilter{InputInterface: anotherNicName} filter.Rules[ruleIdx].Target = &stack.DropTarget{} filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} - ipt.ReplaceTable(stack.FilterID, filter, false /* ipv6 */) + ipt.ForceReplaceTable(stack.FilterID, filter, false /* ipv6 */) }, genPacket: genPacketV4, proto: header.IPv4ProtocolNumber, @@ -265,7 +265,7 @@ func TestIPTablesStatsForInput(t *testing.T) { } filter.Rules[ruleIdx].Target = &stack.DropTarget{} filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} - ipt.ReplaceTable(stack.FilterID, filter, true /* ipv6 */) + ipt.ForceReplaceTable(stack.FilterID, filter, true /* ipv6 */) }, genPacket: genPacketV6, proto: header.IPv6ProtocolNumber, @@ -286,7 +286,7 @@ func TestIPTablesStatsForInput(t *testing.T) { } filter.Rules[ruleIdx].Target = &stack.DropTarget{} filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} - ipt.ReplaceTable(stack.FilterID, filter, false /* ipv6 */) + ipt.ForceReplaceTable(stack.FilterID, filter, false /* ipv6 */) }, genPacket: genPacketV4, proto: header.IPv4ProtocolNumber, @@ -304,7 +304,7 @@ func TestIPTablesStatsForInput(t *testing.T) { filter.Rules[ruleIdx].Target = &stack.DropTarget{} filter.Rules[ruleIdx].Matchers = []stack.Matcher{&inputIfNameMatcher{anotherNicName}} filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} - ipt.ReplaceTable(stack.FilterID, filter, true /* ipv6 */) + ipt.ForceReplaceTable(stack.FilterID, filter, true /* ipv6 */) }, genPacket: genPacketV6, proto: header.IPv6ProtocolNumber, @@ -322,7 +322,7 @@ func TestIPTablesStatsForInput(t *testing.T) { filter.Rules[ruleIdx].Target = &stack.DropTarget{} filter.Rules[ruleIdx].Matchers = []stack.Matcher{&inputIfNameMatcher{anotherNicName}} filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} - ipt.ReplaceTable(stack.FilterID, filter, false /* ipv6 */) + ipt.ForceReplaceTable(stack.FilterID, filter, false /* ipv6 */) }, genPacket: genPacketV4, proto: header.IPv4ProtocolNumber, @@ -466,7 +466,7 @@ func TestIPTableWritePackets(t *testing.T) { }, } - s.IPTables().ReplaceTable(stack.FilterID, table, false /* ipv4 */) + s.IPTables().ForceReplaceTable(stack.FilterID, table, false /* ipv4 */) }, genPacket: func(r *stack.Route) stack.PacketBufferList { var pkts stack.PacketBufferList @@ -555,7 +555,7 @@ func TestIPTableWritePackets(t *testing.T) { }, } - s.IPTables().ReplaceTable(stack.FilterID, table, true /* ipv6 */) + s.IPTables().ForceReplaceTable(stack.FilterID, table, true /* ipv6 */) }, genPacket: func(r *stack.Route) stack.PacketBufferList { var pkts stack.PacketBufferList @@ -708,7 +708,7 @@ func setupDropFilter(hook stack.Hook, f stack.IPHeaderFilter) func(*testing.T, * filter.Rules[ruleIdx].Target = &stack.DropTarget{NetworkProtocol: netProto} // Make sure the packet is not dropped by the next rule. filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{NetworkProtocol: netProto} - ipt.ReplaceTable(stack.FilterID, filter, ipv6) + ipt.ForceReplaceTable(stack.FilterID, filter, ipv6) } } @@ -1216,7 +1216,7 @@ func setupNAT(t *testing.T, s *stack.Stack, netProto tcpip.NetworkProtocolNumber table.Rules[ruleIdx].Target = target // Make sure the packet is not dropped by the next rule. table.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} - ipt.ReplaceTable(stack.NATID, table, ipv6) + ipt.ForceReplaceTable(stack.NATID, table, ipv6) } func setupDNAT(t *testing.T, s *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, target stack.Target) { @@ -1309,7 +1309,7 @@ func setupTwiceNAT(t *testing.T, s *stack.Stack, netProto tcpip.NetworkProtocolN }, } - ipt.ReplaceTable(stack.NATID, table, ipv6) + ipt.ForceReplaceTable(stack.NATID, table, ipv6) } type natType struct { @@ -2527,7 +2527,7 @@ func TestNATICMPError(t *testing.T) { }, } - ipt.ReplaceTable(stack.NATID, table, ipv6) + ipt.ForceReplaceTable(stack.NATID, table, ipv6) buf := transportType.buf @@ -2898,7 +2898,7 @@ func TestSNATHandlePortOrIdentConflicts(t *testing.T) { }, } - ipt.ReplaceTable(stack.NATID, table, ipv6) + ipt.ForceReplaceTable(stack.NATID, table, ipv6) for i, srcAddr := range test.srcAddrs { t.Run(fmt.Sprintf("Packet#%d", i), func(t *testing.T) { @@ -2979,7 +2979,7 @@ func TestLocallyRoutedPackets(t *testing.T) { ipv6 := test.netProto == ipv6.ProtocolNumber ipt := s.IPTables() filter := ipt.GetTable(stack.FilterID, ipv6) - ipt.ReplaceTable(stack.FilterID, filter, ipv6) + ipt.ForceReplaceTable(stack.FilterID, filter, ipv6) } var wq waiter.Queue @@ -3303,7 +3303,7 @@ func TestRejectWith(t *testing.T) { 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{} - ipt.ReplaceTable(stack.FilterID, filter, ipv6) + ipt.ForceReplaceTable(stack.FilterID, filter, ipv6) } func() { @@ -3405,7 +3405,7 @@ func TestInvalidTransportHeader(t *testing.T) { // Enable iptables and conntrack. ipt := s.IPTables() filter := ipt.GetTable(stack.FilterID, false /* ipv6 */) - ipt.ReplaceTable(stack.FilterID, filter, false /* ipv6 */) + ipt.ForceReplaceTable(stack.FilterID, filter, false /* ipv6 */) // This can panic if conntrack isn't checking lengths. e.InjectInbound(header.IPv4ProtocolNumber, test.genPacket(test.offset)) diff --git a/runsc/config/config.go b/runsc/config/config.go index 7f97ef15c..e697d3019 100644 --- a/runsc/config/config.go +++ b/runsc/config/config.go @@ -330,10 +330,6 @@ type Config struct { // explicitlySet contains whether a flag was explicitly set on the command-line from which this // Config was constructed. Nil when the Config was not initialized from a FlagSet. explicitlySet map[string]struct{} - - // ScrapeNAT, when true, tells runsc to scrape the host network - // namespace's NAT iptables and reproduce it inside the sandbox. - ReproduceNAT bool `flag:"EXPERIMENTAL-reproduce-nat"` } func (c *Config) validate() error { diff --git a/runsc/config/flags.go b/runsc/config/flags.go index 8659a38e3..662f0a075 100644 --- a/runsc/config/flags.go +++ b/runsc/config/flags.go @@ -120,7 +120,6 @@ func RegisterFlags(flagSet *flag.FlagSet) { flagSet.Int("num-network-channels", 1, "number of underlying channels(FDs) to use for network link endpoints.") flagSet.Bool("buffer-pooling", true, "enable allocation of buffers from a shared pool instead of the heap.") flagSet.Bool("EXPERIMENTAL-afxdp", false, "EXPERIMENTAL. Use an AF_XDP socket to receive packets.") - flagSet.Bool("EXPERIMENTAL-reproduce-nat", false, "EXPERIMENTAL. Scrape the host netns NAT table and reproduce it in the sandbox.") // Flags that control sandbox runtime behavior: accelerator related. flagSet.Bool("nvproxy", false, "EXPERIMENTAL: enable support for Nvidia GPUs") @@ -141,12 +140,11 @@ func RegisterFlags(flagSet *flag.FlagSet) { var overrideAllowlist = map[string]struct { check func(name string, value string) error }{ - "debug": {}, - "strace": {}, - "strace-syscalls": {}, - "strace-log-size": {}, - "host-uds": {}, - "EXPERIMENTAL-reproduce-nat": {}, + "debug": {}, + "strace": {}, + "strace-syscalls": {}, + "strace-log-size": {}, + "host-uds": {}, "oci-seccomp": {check: checkOciSeccomp}, } diff --git a/runsc/sandbox/network.go b/runsc/sandbox/network.go index 1189771c0..bf49910c5 100644 --- a/runsc/sandbox/network.go +++ b/runsc/sandbox/network.go @@ -319,13 +319,14 @@ func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, conf *con args.FilePayload.Files = append(args.FilePayload.Files, pcap) } - // Pass the host's NAT table if requested. - if conf.ReproduceNAT { + // Pass the host's NAT table. + f, err := writeNATBlob() + if err != nil { + // We warn rather than error because not every kernel has + // iptables enabled. + log.Warningf("failed to write NAT blob: %v", err) + } else { args.NATBlob = true - f, err := writeNATBlob() - if err != nil { - return fmt.Errorf("failed to write NAT blob: %v", err) - } args.FilePayload.Files = append(args.FilePayload.Files, f) }