mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Fix copylocks error about copying IPTables.
IPTables.connections contains a sync.RWMutex. Copying it will trigger copylocks analysis. Tested by manually enabling nogo tests. sync.RWMutex is added to IPTables for the additional race condition discovered. PiperOrigin-RevId: 314817019
This commit is contained in:
committed by
Nicolas Lacasse
parent
f766366091
commit
41da7a568b
@@ -144,31 +144,27 @@ func GetEntries(t *kernel.Task, stack *stack.Stack, outPtr usermem.Addr, outLen
|
||||
}
|
||||
|
||||
func findTable(stk *stack.Stack, tablename linux.TableName) (stack.Table, error) {
|
||||
ipt := stk.IPTables()
|
||||
table, ok := ipt.Tables[tablename.String()]
|
||||
table, ok := stk.IPTables().GetTable(tablename.String())
|
||||
if !ok {
|
||||
return stack.Table{}, fmt.Errorf("couldn't find table %q", tablename)
|
||||
}
|
||||
return table, nil
|
||||
}
|
||||
|
||||
// FillDefaultIPTables sets stack's IPTables to the default tables and
|
||||
// populates them with metadata.
|
||||
func FillDefaultIPTables(stk *stack.Stack) {
|
||||
ipt := stack.DefaultTables()
|
||||
|
||||
// In order to fill in the metadata, we have to translate ipt from its
|
||||
// netstack format to Linux's giant-binary-blob format.
|
||||
for name, table := range ipt.Tables {
|
||||
_, metadata, err := convertNetstackToBinary(name, table)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Unable to set default IP tables: %v", err))
|
||||
// FillIPTablesMetadata populates stack's IPTables with metadata.
|
||||
func FillIPTablesMetadata(stk *stack.Stack) {
|
||||
stk.IPTables().ModifyTables(func(tables map[string]stack.Table) {
|
||||
// In order to fill in the metadata, we have to translate ipt from its
|
||||
// netstack format to Linux's giant-binary-blob format.
|
||||
for name, table := range tables {
|
||||
_, metadata, err := convertNetstackToBinary(name, table)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Unable to set default IP tables: %v", err))
|
||||
}
|
||||
table.SetMetadata(metadata)
|
||||
tables[name] = table
|
||||
}
|
||||
table.SetMetadata(metadata)
|
||||
ipt.Tables[name] = table
|
||||
}
|
||||
|
||||
stk.SetIPTables(ipt)
|
||||
})
|
||||
}
|
||||
|
||||
// convertNetstackToBinary converts the iptables as stored in netstack to the
|
||||
@@ -573,15 +569,13 @@ func SetEntries(stk *stack.Stack, optVal []byte) *syserr.Error {
|
||||
// - There are no chains without an unconditional final rule.
|
||||
// - There are no chains without an unconditional underflow rule.
|
||||
|
||||
ipt := stk.IPTables()
|
||||
table.SetMetadata(metadata{
|
||||
HookEntry: replace.HookEntry,
|
||||
Underflow: replace.Underflow,
|
||||
NumEntries: replace.NumEntries,
|
||||
Size: replace.Size,
|
||||
})
|
||||
ipt.Tables[replace.Name.String()] = table
|
||||
stk.SetIPTables(ipt)
|
||||
stk.IPTables().ReplaceTable(replace.Name.String(), table)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -362,14 +362,13 @@ func (s *Stack) RouteTable() []inet.Route {
|
||||
}
|
||||
|
||||
// IPTables returns the stack's iptables.
|
||||
func (s *Stack) IPTables() (stack.IPTables, error) {
|
||||
func (s *Stack) IPTables() (*stack.IPTables, error) {
|
||||
return s.Stack.IPTables(), nil
|
||||
}
|
||||
|
||||
// FillDefaultIPTables sets the stack's iptables to the default tables, which
|
||||
// allow and do not modify all traffic.
|
||||
func (s *Stack) FillDefaultIPTables() {
|
||||
netfilter.FillDefaultIPTables(s.Stack)
|
||||
// FillIPTablesMetadata populates stack's IPTables with metadata.
|
||||
func (s *Stack) FillIPTablesMetadata() {
|
||||
netfilter.FillIPTablesMetadata(s.Stack)
|
||||
}
|
||||
|
||||
// Resume implements inet.Stack.Resume.
|
||||
|
||||
@@ -43,11 +43,11 @@ const HookUnset = -1
|
||||
|
||||
// DefaultTables returns a default set of tables. Each chain is set to accept
|
||||
// all packets.
|
||||
func DefaultTables() IPTables {
|
||||
func DefaultTables() *IPTables {
|
||||
// TODO(gvisor.dev/issue/170): We may be able to swap out some strings for
|
||||
// iotas.
|
||||
return IPTables{
|
||||
Tables: map[string]Table{
|
||||
return &IPTables{
|
||||
tables: map[string]Table{
|
||||
TablenameNat: Table{
|
||||
Rules: []Rule{
|
||||
Rule{Target: AcceptTarget{}},
|
||||
@@ -106,7 +106,7 @@ func DefaultTables() IPTables {
|
||||
UserChains: map[string]int{},
|
||||
},
|
||||
},
|
||||
Priorities: map[Hook][]string{
|
||||
priorities: map[Hook][]string{
|
||||
Input: []string{TablenameNat, TablenameFilter},
|
||||
Prerouting: []string{TablenameMangle, TablenameNat},
|
||||
Output: []string{TablenameMangle, TablenameNat, TablenameFilter},
|
||||
@@ -158,6 +158,36 @@ func EmptyNatTable() Table {
|
||||
}
|
||||
}
|
||||
|
||||
// GetTable returns table by name.
|
||||
func (it *IPTables) GetTable(name string) (Table, bool) {
|
||||
it.mu.RLock()
|
||||
defer it.mu.RUnlock()
|
||||
t, ok := it.tables[name]
|
||||
return t, ok
|
||||
}
|
||||
|
||||
// ReplaceTable replaces or inserts table by name.
|
||||
func (it *IPTables) ReplaceTable(name string, table Table) {
|
||||
it.mu.Lock()
|
||||
defer it.mu.Unlock()
|
||||
it.tables[name] = table
|
||||
}
|
||||
|
||||
// ModifyTables acquires write-lock and calls fn with internal name-to-table
|
||||
// map. This function can be used to update multiple tables atomically.
|
||||
func (it *IPTables) ModifyTables(fn func(map[string]Table)) {
|
||||
it.mu.Lock()
|
||||
defer it.mu.Unlock()
|
||||
fn(it.tables)
|
||||
}
|
||||
|
||||
// GetPriorities returns slice of priorities associated with hook.
|
||||
func (it *IPTables) GetPriorities(hook Hook) []string {
|
||||
it.mu.RLock()
|
||||
defer it.mu.RUnlock()
|
||||
return it.priorities[hook]
|
||||
}
|
||||
|
||||
// A chainVerdict is what a table decides should be done with a packet.
|
||||
type chainVerdict int
|
||||
|
||||
@@ -184,8 +214,8 @@ func (it *IPTables) Check(hook Hook, pkt *PacketBuffer, gso *GSO, r *Route, addr
|
||||
it.connections.HandlePacket(pkt, hook, gso, r)
|
||||
|
||||
// Go through each table containing the hook.
|
||||
for _, tablename := range it.Priorities[hook] {
|
||||
table := it.Tables[tablename]
|
||||
for _, tablename := range it.GetPriorities(hook) {
|
||||
table, _ := it.GetTable(tablename)
|
||||
ruleIdx := table.BuiltinChains[hook]
|
||||
switch verdict := it.checkChain(hook, pkt, table, ruleIdx, gso, r, address, nicName); verdict {
|
||||
// If the table returns Accept, move on to the next table.
|
||||
|
||||
@@ -16,6 +16,7 @@ package stack
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
@@ -78,13 +79,17 @@ const (
|
||||
|
||||
// IPTables holds all the tables for a netstack.
|
||||
type IPTables struct {
|
||||
// Tables maps table names to tables. User tables have arbitrary names.
|
||||
Tables map[string]Table
|
||||
// mu protects tables and priorities.
|
||||
mu sync.RWMutex
|
||||
|
||||
// Priorities maps each hook to a list of table names. The order of the
|
||||
// tables maps table names to tables. User tables have arbitrary names. mu
|
||||
// needs to be locked for accessing.
|
||||
tables map[string]Table
|
||||
|
||||
// priorities maps each hook to a list of table names. The order of the
|
||||
// list is the order in which each table should be visited for that
|
||||
// hook.
|
||||
Priorities map[Hook][]string
|
||||
// hook. mu needs to be locked for accessing.
|
||||
priorities map[Hook][]string
|
||||
|
||||
connections ConnTrackTable
|
||||
}
|
||||
|
||||
@@ -424,12 +424,8 @@ type Stack struct {
|
||||
// handleLocal allows non-loopback interfaces to loop packets.
|
||||
handleLocal bool
|
||||
|
||||
// tablesMu protects iptables.
|
||||
tablesMu sync.RWMutex
|
||||
|
||||
// tables are the iptables packet filtering and manipulation rules. The are
|
||||
// protected by tablesMu.`
|
||||
tables IPTables
|
||||
// tables are the iptables packet filtering and manipulation rules.
|
||||
tables *IPTables
|
||||
|
||||
// resumableEndpoints is a list of endpoints that need to be resumed if the
|
||||
// stack is being restored.
|
||||
@@ -676,6 +672,7 @@ func New(opts Options) *Stack {
|
||||
clock: clock,
|
||||
stats: opts.Stats.FillIn(),
|
||||
handleLocal: opts.HandleLocal,
|
||||
tables: DefaultTables(),
|
||||
icmpRateLimiter: NewICMPRateLimiter(),
|
||||
seed: generateRandUint32(),
|
||||
ndpConfigs: opts.NDPConfigs,
|
||||
@@ -1741,18 +1738,8 @@ func (s *Stack) IsInGroup(nicID tcpip.NICID, multicastAddr tcpip.Address) (bool,
|
||||
}
|
||||
|
||||
// IPTables returns the stack's iptables.
|
||||
func (s *Stack) IPTables() IPTables {
|
||||
s.tablesMu.RLock()
|
||||
t := s.tables
|
||||
s.tablesMu.RUnlock()
|
||||
return t
|
||||
}
|
||||
|
||||
// SetIPTables sets the stack's iptables.
|
||||
func (s *Stack) SetIPTables(ipt IPTables) {
|
||||
s.tablesMu.Lock()
|
||||
s.tables = ipt
|
||||
s.tablesMu.Unlock()
|
||||
func (s *Stack) IPTables() *IPTables {
|
||||
return s.tables
|
||||
}
|
||||
|
||||
// ICMPLimit returns the maximum number of ICMP messages that can be sent
|
||||
|
||||
@@ -140,11 +140,6 @@ func (e *endpoint) SetOwner(owner tcpip.PacketOwner) {
|
||||
e.owner = owner
|
||||
}
|
||||
|
||||
// IPTables implements tcpip.Endpoint.IPTables.
|
||||
func (e *endpoint) IPTables() (stack.IPTables, error) {
|
||||
return e.stack.IPTables(), nil
|
||||
}
|
||||
|
||||
// Read reads data from the endpoint. This method does not block if
|
||||
// there is no data pending.
|
||||
func (e *endpoint) Read(addr *tcpip.FullAddress) (buffer.View, tcpip.ControlMessages, *tcpip.Error) {
|
||||
|
||||
@@ -132,11 +132,6 @@ func (ep *endpoint) Close() {
|
||||
// ModerateRecvBuf implements tcpip.Endpoint.ModerateRecvBuf.
|
||||
func (ep *endpoint) ModerateRecvBuf(copied int) {}
|
||||
|
||||
// IPTables implements tcpip.Endpoint.IPTables.
|
||||
func (ep *endpoint) IPTables() (stack.IPTables, error) {
|
||||
return ep.stack.IPTables(), nil
|
||||
}
|
||||
|
||||
// Read implements tcpip.Endpoint.Read.
|
||||
func (ep *endpoint) Read(addr *tcpip.FullAddress) (buffer.View, tcpip.ControlMessages, *tcpip.Error) {
|
||||
ep.rcvMu.Lock()
|
||||
|
||||
@@ -166,11 +166,6 @@ func (e *endpoint) SetOwner(owner tcpip.PacketOwner) {
|
||||
e.owner = owner
|
||||
}
|
||||
|
||||
// IPTables implements tcpip.Endpoint.IPTables.
|
||||
func (e *endpoint) IPTables() (stack.IPTables, error) {
|
||||
return e.stack.IPTables(), nil
|
||||
}
|
||||
|
||||
// Read implements tcpip.Endpoint.Read.
|
||||
func (e *endpoint) Read(addr *tcpip.FullAddress) (buffer.View, tcpip.ControlMessages, *tcpip.Error) {
|
||||
if !e.associated {
|
||||
|
||||
@@ -1172,11 +1172,6 @@ func (e *endpoint) SetOwner(owner tcpip.PacketOwner) {
|
||||
e.owner = owner
|
||||
}
|
||||
|
||||
// IPTables implements tcpip.Endpoint.IPTables.
|
||||
func (e *endpoint) IPTables() (stack.IPTables, error) {
|
||||
return e.stack.IPTables(), nil
|
||||
}
|
||||
|
||||
// Read reads data from the endpoint.
|
||||
func (e *endpoint) Read(*tcpip.FullAddress) (buffer.View, tcpip.ControlMessages, *tcpip.Error) {
|
||||
e.LockUser()
|
||||
|
||||
@@ -247,11 +247,6 @@ func (e *endpoint) Close() {
|
||||
// ModerateRecvBuf implements tcpip.Endpoint.ModerateRecvBuf.
|
||||
func (e *endpoint) ModerateRecvBuf(copied int) {}
|
||||
|
||||
// IPTables implements tcpip.Endpoint.IPTables.
|
||||
func (e *endpoint) IPTables() (stack.IPTables, error) {
|
||||
return e.stack.IPTables(), nil
|
||||
}
|
||||
|
||||
// Read reads data from the endpoint. This method does not block if
|
||||
// there is no data pending.
|
||||
func (e *endpoint) Read(addr *tcpip.FullAddress) (buffer.View, tcpip.ControlMessages, *tcpip.Error) {
|
||||
|
||||
@@ -1056,7 +1056,7 @@ func newEmptySandboxNetworkStack(clock tcpip.Clock, uniqueID stack.UniqueID) (in
|
||||
return nil, fmt.Errorf("SetTransportProtocolOption failed: %v", err)
|
||||
}
|
||||
|
||||
s.FillDefaultIPTables()
|
||||
s.FillIPTablesMetadata()
|
||||
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user