github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/tcpip/stack/iptables.go (about)

     1  // Copyright 2019 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package stack
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"math/rand"
    21  	"reflect"
    22  	"time"
    23  
    24  	"github.com/metacubex/gvisor/pkg/tcpip"
    25  	"github.com/metacubex/gvisor/pkg/tcpip/header"
    26  )
    27  
    28  // TableID identifies a specific table.
    29  type TableID int
    30  
    31  // Each value identifies a specific table.
    32  const (
    33  	NATID TableID = iota
    34  	MangleID
    35  	FilterID
    36  	NumTables
    37  )
    38  
    39  // HookUnset indicates that there is no hook set for an entrypoint or
    40  // underflow.
    41  const HookUnset = -1
    42  
    43  // reaperDelay is how long to wait before starting to reap connections.
    44  const reaperDelay = 5 * time.Second
    45  
    46  // DefaultTables returns a default set of tables. Each chain is set to accept
    47  // all packets.
    48  func DefaultTables(clock tcpip.Clock, rand *rand.Rand) *IPTables {
    49  	return &IPTables{
    50  		v4Tables: [NumTables]Table{
    51  			NATID: {
    52  				Rules: []Rule{
    53  					{Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}},
    54  					{Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}},
    55  					{Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}},
    56  					{Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}},
    57  					{Filter: EmptyFilter4(), Target: &ErrorTarget{NetworkProtocol: header.IPv4ProtocolNumber}},
    58  				},
    59  				BuiltinChains: [NumHooks]int{
    60  					Prerouting:  0,
    61  					Input:       1,
    62  					Forward:     HookUnset,
    63  					Output:      2,
    64  					Postrouting: 3,
    65  				},
    66  				Underflows: [NumHooks]int{
    67  					Prerouting:  0,
    68  					Input:       1,
    69  					Forward:     HookUnset,
    70  					Output:      2,
    71  					Postrouting: 3,
    72  				},
    73  			},
    74  			MangleID: {
    75  				Rules: []Rule{
    76  					{Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}},
    77  					{Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}},
    78  					{Filter: EmptyFilter4(), Target: &ErrorTarget{NetworkProtocol: header.IPv4ProtocolNumber}},
    79  				},
    80  				BuiltinChains: [NumHooks]int{
    81  					Prerouting: 0,
    82  					Output:     1,
    83  				},
    84  				Underflows: [NumHooks]int{
    85  					Prerouting:  0,
    86  					Input:       HookUnset,
    87  					Forward:     HookUnset,
    88  					Output:      1,
    89  					Postrouting: HookUnset,
    90  				},
    91  			},
    92  			FilterID: {
    93  				Rules: []Rule{
    94  					{Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}},
    95  					{Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}},
    96  					{Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}},
    97  					{Filter: EmptyFilter4(), Target: &ErrorTarget{NetworkProtocol: header.IPv4ProtocolNumber}},
    98  				},
    99  				BuiltinChains: [NumHooks]int{
   100  					Prerouting:  HookUnset,
   101  					Input:       0,
   102  					Forward:     1,
   103  					Output:      2,
   104  					Postrouting: HookUnset,
   105  				},
   106  				Underflows: [NumHooks]int{
   107  					Prerouting:  HookUnset,
   108  					Input:       0,
   109  					Forward:     1,
   110  					Output:      2,
   111  					Postrouting: HookUnset,
   112  				},
   113  			},
   114  		},
   115  		v6Tables: [NumTables]Table{
   116  			NATID: {
   117  				Rules: []Rule{
   118  					{Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}},
   119  					{Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}},
   120  					{Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}},
   121  					{Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}},
   122  					{Filter: EmptyFilter6(), Target: &ErrorTarget{NetworkProtocol: header.IPv6ProtocolNumber}},
   123  				},
   124  				BuiltinChains: [NumHooks]int{
   125  					Prerouting:  0,
   126  					Input:       1,
   127  					Forward:     HookUnset,
   128  					Output:      2,
   129  					Postrouting: 3,
   130  				},
   131  				Underflows: [NumHooks]int{
   132  					Prerouting:  0,
   133  					Input:       1,
   134  					Forward:     HookUnset,
   135  					Output:      2,
   136  					Postrouting: 3,
   137  				},
   138  			},
   139  			MangleID: {
   140  				Rules: []Rule{
   141  					{Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}},
   142  					{Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}},
   143  					{Filter: EmptyFilter6(), Target: &ErrorTarget{NetworkProtocol: header.IPv6ProtocolNumber}},
   144  				},
   145  				BuiltinChains: [NumHooks]int{
   146  					Prerouting: 0,
   147  					Output:     1,
   148  				},
   149  				Underflows: [NumHooks]int{
   150  					Prerouting:  0,
   151  					Input:       HookUnset,
   152  					Forward:     HookUnset,
   153  					Output:      1,
   154  					Postrouting: HookUnset,
   155  				},
   156  			},
   157  			FilterID: {
   158  				Rules: []Rule{
   159  					{Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}},
   160  					{Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}},
   161  					{Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}},
   162  					{Filter: EmptyFilter6(), Target: &ErrorTarget{NetworkProtocol: header.IPv6ProtocolNumber}},
   163  				},
   164  				BuiltinChains: [NumHooks]int{
   165  					Prerouting:  HookUnset,
   166  					Input:       0,
   167  					Forward:     1,
   168  					Output:      2,
   169  					Postrouting: HookUnset,
   170  				},
   171  				Underflows: [NumHooks]int{
   172  					Prerouting:  HookUnset,
   173  					Input:       0,
   174  					Forward:     1,
   175  					Output:      2,
   176  					Postrouting: HookUnset,
   177  				},
   178  			},
   179  		},
   180  		connections: ConnTrack{
   181  			seed:  rand.Uint32(),
   182  			clock: clock,
   183  			rand:  rand,
   184  		},
   185  	}
   186  }
   187  
   188  // EmptyFilterTable returns a Table with no rules and the filter table chains
   189  // mapped to HookUnset.
   190  func EmptyFilterTable() Table {
   191  	return Table{
   192  		Rules: []Rule{},
   193  		BuiltinChains: [NumHooks]int{
   194  			Prerouting:  HookUnset,
   195  			Postrouting: HookUnset,
   196  		},
   197  		Underflows: [NumHooks]int{
   198  			Prerouting:  HookUnset,
   199  			Postrouting: HookUnset,
   200  		},
   201  	}
   202  }
   203  
   204  // EmptyNATTable returns a Table with no rules and the filter table chains
   205  // mapped to HookUnset.
   206  func EmptyNATTable() Table {
   207  	return Table{
   208  		Rules: []Rule{},
   209  		BuiltinChains: [NumHooks]int{
   210  			Forward: HookUnset,
   211  		},
   212  		Underflows: [NumHooks]int{
   213  			Forward: HookUnset,
   214  		},
   215  	}
   216  }
   217  
   218  // GetTable returns a table with the given id and IP version. It panics when an
   219  // invalid id is provided.
   220  func (it *IPTables) GetTable(id TableID, ipv6 bool) Table {
   221  	it.mu.RLock()
   222  	defer it.mu.RUnlock()
   223  	return it.getTableRLocked(id, ipv6)
   224  }
   225  
   226  // +checklocksread:it.mu
   227  func (it *IPTables) getTableRLocked(id TableID, ipv6 bool) Table {
   228  	if ipv6 {
   229  		return it.v6Tables[id]
   230  	}
   231  	return it.v4Tables[id]
   232  }
   233  
   234  // ReplaceTable replaces or inserts table by name. It panics when an invalid id
   235  // is provided.
   236  func (it *IPTables) ReplaceTable(id TableID, table Table, ipv6 bool) {
   237  	it.replaceTable(id, table, ipv6, false /* force */)
   238  }
   239  
   240  // ForceReplaceTable replaces or inserts table by name. It panics when an invalid id
   241  // is provided. It enables iptables even when the inserted table is all
   242  // conditionless ACCEPT, skipping our optimization that disables iptables until
   243  // they're modified.
   244  func (it *IPTables) ForceReplaceTable(id TableID, table Table, ipv6 bool) {
   245  	it.replaceTable(id, table, ipv6, true /* force */)
   246  }
   247  
   248  func (it *IPTables) replaceTable(id TableID, table Table, ipv6, force bool) {
   249  	it.mu.Lock()
   250  	defer it.mu.Unlock()
   251  
   252  	// If iptables is being enabled, initialize the conntrack table and
   253  	// reaper.
   254  	if !it.modified {
   255  		// Don't do anything if the table is identical.
   256  		if ((ipv6 && reflect.DeepEqual(table, it.v6Tables[id])) || (!ipv6 && reflect.DeepEqual(table, it.v4Tables[id]))) && !force {
   257  			return
   258  		}
   259  
   260  		it.connections.init()
   261  		it.startReaper(reaperDelay)
   262  	}
   263  	it.modified = true
   264  	if ipv6 {
   265  		it.v6Tables[id] = table
   266  	} else {
   267  		it.v4Tables[id] = table
   268  	}
   269  }
   270  
   271  // A chainVerdict is what a table decides should be done with a packet.
   272  type chainVerdict int
   273  
   274  const (
   275  	// chainAccept indicates the packet should continue through netstack.
   276  	chainAccept chainVerdict = iota
   277  
   278  	// chainDrop indicates the packet should be dropped.
   279  	chainDrop
   280  
   281  	// chainReturn indicates the packet should return to the calling chain
   282  	// or the underflow rule of a builtin chain.
   283  	chainReturn
   284  )
   285  
   286  type checkTable struct {
   287  	fn      checkTableFn
   288  	tableID TableID
   289  	table   Table
   290  }
   291  
   292  // shouldSkipOrPopulateTables returns true iff IPTables should be skipped.
   293  //
   294  // If IPTables should not be skipped, tables will be updated with the
   295  // specified table.
   296  //
   297  // This is called in the hot path even when iptables are disabled, so we ensure
   298  // it does not allocate. We check recursively for heap allocations, but not for:
   299  //   - Stack splitting, which can allocate.
   300  //   - Calls to interfaces, which can allocate.
   301  //   - Calls to dynamic functions, which can allocate.
   302  //
   303  // +checkescape:hard
   304  func (it *IPTables) shouldSkipOrPopulateTables(tables []checkTable, pkt *PacketBuffer) bool {
   305  	switch pkt.NetworkProtocolNumber {
   306  	case header.IPv4ProtocolNumber, header.IPv6ProtocolNumber:
   307  	default:
   308  		// IPTables only supports IPv4/IPv6.
   309  		return true
   310  	}
   311  
   312  	it.mu.RLock()
   313  	defer it.mu.RUnlock()
   314  
   315  	if !it.modified {
   316  		// Many users never configure iptables. Spare them the cost of rule
   317  		// traversal if rules have never been set.
   318  		return true
   319  	}
   320  
   321  	for i := range tables {
   322  		table := &tables[i]
   323  		table.table = it.getTableRLocked(table.tableID, pkt.NetworkProtocolNumber == header.IPv6ProtocolNumber)
   324  	}
   325  	return false
   326  }
   327  
   328  // CheckPrerouting performs the prerouting hook on the packet.
   329  //
   330  // Returns true iff the packet may continue traversing the stack; the packet
   331  // must be dropped if false is returned.
   332  //
   333  // Precondition: The packet's network and transport header must be set.
   334  //
   335  // This is called in the hot path even when iptables are disabled, so we ensure
   336  // that it does not allocate. Note that called functions (e.g.
   337  // getConnAndUpdate) can allocate.
   338  // TODO(b/233951539): checkescape fails on arm sometimes. Fix and re-add.
   339  func (it *IPTables) CheckPrerouting(pkt *PacketBuffer, addressEP AddressableEndpoint, inNicName string) bool {
   340  	tables := [...]checkTable{
   341  		{
   342  			fn:      check,
   343  			tableID: MangleID,
   344  		},
   345  		{
   346  			fn:      checkNAT,
   347  			tableID: NATID,
   348  		},
   349  	}
   350  
   351  	if it.shouldSkipOrPopulateTables(tables[:], pkt) {
   352  		return true
   353  	}
   354  
   355  	pkt.tuple = it.connections.getConnAndUpdate(pkt, false /* skipChecksumValidation */)
   356  
   357  	for _, table := range tables {
   358  		if !table.fn(it, table.table, Prerouting, pkt, nil /* route */, addressEP, inNicName, "" /* outNicName */) {
   359  			return false
   360  		}
   361  	}
   362  
   363  	return true
   364  }
   365  
   366  // CheckInput performs the input hook on the packet.
   367  //
   368  // Returns true iff the packet may continue traversing the stack; the packet
   369  // must be dropped if false is returned.
   370  //
   371  // Precondition: The packet's network and transport header must be set.
   372  //
   373  // This is called in the hot path even when iptables are disabled, so we ensure
   374  // that it does not allocate. Note that called functions (e.g.
   375  // getConnAndUpdate) can allocate.
   376  // TODO(b/233951539): checkescape fails on arm sometimes. Fix and re-add.
   377  func (it *IPTables) CheckInput(pkt *PacketBuffer, inNicName string) bool {
   378  	tables := [...]checkTable{
   379  		{
   380  			fn:      checkNAT,
   381  			tableID: NATID,
   382  		},
   383  		{
   384  			fn:      check,
   385  			tableID: FilterID,
   386  		},
   387  	}
   388  
   389  	if it.shouldSkipOrPopulateTables(tables[:], pkt) {
   390  		return true
   391  	}
   392  
   393  	for _, table := range tables {
   394  		if !table.fn(it, table.table, Input, pkt, nil /* route */, nil /* addressEP */, inNicName, "" /* outNicName */) {
   395  			return false
   396  		}
   397  	}
   398  
   399  	if t := pkt.tuple; t != nil {
   400  		pkt.tuple = nil
   401  		return t.conn.finalize()
   402  	}
   403  	return true
   404  }
   405  
   406  // CheckForward performs the forward hook on the packet.
   407  //
   408  // Returns true iff the packet may continue traversing the stack; the packet
   409  // must be dropped if false is returned.
   410  //
   411  // Precondition: The packet's network and transport header must be set.
   412  //
   413  // This is called in the hot path even when iptables are disabled, so we ensure
   414  // that it does not allocate. Note that called functions (e.g.
   415  // getConnAndUpdate) can allocate.
   416  // TODO(b/233951539): checkescape fails on arm sometimes. Fix and re-add.
   417  func (it *IPTables) CheckForward(pkt *PacketBuffer, inNicName, outNicName string) bool {
   418  	tables := [...]checkTable{
   419  		{
   420  			fn:      check,
   421  			tableID: FilterID,
   422  		},
   423  	}
   424  
   425  	if it.shouldSkipOrPopulateTables(tables[:], pkt) {
   426  		return true
   427  	}
   428  
   429  	for _, table := range tables {
   430  		if !table.fn(it, table.table, Forward, pkt, nil /* route */, nil /* addressEP */, inNicName, outNicName) {
   431  			return false
   432  		}
   433  	}
   434  
   435  	return true
   436  }
   437  
   438  // CheckOutput performs the output hook on the packet.
   439  //
   440  // Returns true iff the packet may continue traversing the stack; the packet
   441  // must be dropped if false is returned.
   442  //
   443  // Precondition: The packet's network and transport header must be set.
   444  //
   445  // This is called in the hot path even when iptables are disabled, so we ensure
   446  // that it does not allocate. Note that called functions (e.g.
   447  // getConnAndUpdate) can allocate.
   448  // TODO(b/233951539): checkescape fails on arm sometimes. Fix and re-add.
   449  func (it *IPTables) CheckOutput(pkt *PacketBuffer, r *Route, outNicName string) bool {
   450  	tables := [...]checkTable{
   451  		{
   452  			fn:      check,
   453  			tableID: MangleID,
   454  		},
   455  		{
   456  			fn:      checkNAT,
   457  			tableID: NATID,
   458  		},
   459  		{
   460  			fn:      check,
   461  			tableID: FilterID,
   462  		},
   463  	}
   464  
   465  	if it.shouldSkipOrPopulateTables(tables[:], pkt) {
   466  		return true
   467  	}
   468  
   469  	// We don't need to validate the checksum in the Output path: we can assume
   470  	// we calculate it correctly, plus checksumming may be deferred due to GSO.
   471  	pkt.tuple = it.connections.getConnAndUpdate(pkt, true /* skipChecksumValidation */)
   472  
   473  	for _, table := range tables {
   474  		if !table.fn(it, table.table, Output, pkt, r, nil /* addressEP */, "" /* inNicName */, outNicName) {
   475  			return false
   476  		}
   477  	}
   478  
   479  	return true
   480  }
   481  
   482  // CheckPostrouting performs the postrouting hook on the packet.
   483  //
   484  // Returns true iff the packet may continue traversing the stack; the packet
   485  // must be dropped if false is returned.
   486  //
   487  // Precondition: The packet's network and transport header must be set.
   488  //
   489  // This is called in the hot path even when iptables are disabled, so we ensure
   490  // that it does not allocate. Note that called functions (e.g.
   491  // getConnAndUpdate) can allocate.
   492  // TODO(b/233951539): checkescape fails on arm sometimes. Fix and re-add.
   493  func (it *IPTables) CheckPostrouting(pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, outNicName string) bool {
   494  	tables := [...]checkTable{
   495  		{
   496  			fn:      check,
   497  			tableID: MangleID,
   498  		},
   499  		{
   500  			fn:      checkNAT,
   501  			tableID: NATID,
   502  		},
   503  	}
   504  
   505  	if it.shouldSkipOrPopulateTables(tables[:], pkt) {
   506  		return true
   507  	}
   508  
   509  	for _, table := range tables {
   510  		if !table.fn(it, table.table, Postrouting, pkt, r, addressEP, "" /* inNicName */, outNicName) {
   511  			return false
   512  		}
   513  	}
   514  
   515  	if t := pkt.tuple; t != nil {
   516  		pkt.tuple = nil
   517  		return t.conn.finalize()
   518  	}
   519  	return true
   520  }
   521  
   522  // Note: this used to omit the *IPTables parameter, but doing so caused
   523  // unnecessary allocations.
   524  type checkTableFn func(it *IPTables, table Table, hook Hook, pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) bool
   525  
   526  func checkNAT(it *IPTables, table Table, hook Hook, pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) bool {
   527  	return it.checkNAT(table, hook, pkt, r, addressEP, inNicName, outNicName)
   528  }
   529  
   530  // checkNAT runs the packet through the NAT table.
   531  //
   532  // See check.
   533  func (it *IPTables) checkNAT(table Table, hook Hook, pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) bool {
   534  	t := pkt.tuple
   535  	if t != nil && t.conn.handlePacket(pkt, hook, r) {
   536  		return true
   537  	}
   538  
   539  	if !it.check(table, hook, pkt, r, addressEP, inNicName, outNicName) {
   540  		return false
   541  	}
   542  
   543  	if t == nil {
   544  		return true
   545  	}
   546  
   547  	dnat, natDone := func() (bool, bool) {
   548  		switch hook {
   549  		case Prerouting, Output:
   550  			return true, pkt.dnatDone
   551  		case Input, Postrouting:
   552  			return false, pkt.snatDone
   553  		case Forward:
   554  			panic("should not attempt NAT in forwarding")
   555  		default:
   556  			panic(fmt.Sprintf("unhandled hook = %d", hook))
   557  		}
   558  	}()
   559  
   560  	// Make sure the connection is NATed.
   561  	//
   562  	// If the packet was already NATed, the connection must be NATed.
   563  	if !natDone {
   564  		t.conn.maybePerformNoopNAT(pkt, hook, r, dnat)
   565  	}
   566  
   567  	return true
   568  }
   569  
   570  func check(it *IPTables, table Table, hook Hook, pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) bool {
   571  	return it.check(table, hook, pkt, r, addressEP, inNicName, outNicName)
   572  }
   573  
   574  // check runs the packet through the rules in the specified table for the
   575  // hook. It returns true if the packet should continue to traverse through the
   576  // network stack or tables, or false when it must be dropped.
   577  //
   578  // Precondition: The packet's network and transport header must be set.
   579  func (it *IPTables) check(table Table, hook Hook, pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) bool {
   580  	ruleIdx := table.BuiltinChains[hook]
   581  	switch verdict := it.checkChain(hook, pkt, table, ruleIdx, r, addressEP, inNicName, outNicName); verdict {
   582  	// If the table returns Accept, move on to the next table.
   583  	case chainAccept:
   584  		return true
   585  	// The Drop verdict is final.
   586  	case chainDrop:
   587  		return false
   588  	case chainReturn:
   589  		// Any Return from a built-in chain means we have to
   590  		// call the underflow.
   591  		underflow := table.Rules[table.Underflows[hook]]
   592  		switch v, _ := underflow.Target.Action(pkt, hook, r, addressEP); v {
   593  		case RuleAccept:
   594  			return true
   595  		case RuleDrop:
   596  			return false
   597  		case RuleJump, RuleReturn:
   598  			panic("Underflows should only return RuleAccept or RuleDrop.")
   599  		default:
   600  			panic(fmt.Sprintf("Unknown verdict: %d", v))
   601  		}
   602  	default:
   603  		panic(fmt.Sprintf("Unknown verdict %v.", verdict))
   604  	}
   605  }
   606  
   607  // beforeSave is invoked by stateify.
   608  func (it *IPTables) beforeSave() {
   609  	// Ensure the reaper exits cleanly.
   610  	it.reaper.Stop()
   611  	// Prevent others from modifying the connection table.
   612  	it.connections.mu.Lock()
   613  }
   614  
   615  // afterLoad is invoked by stateify.
   616  func (it *IPTables) afterLoad(context.Context) {
   617  	it.startReaper(reaperDelay)
   618  }
   619  
   620  // startReaper periodically reaps timed out connections.
   621  func (it *IPTables) startReaper(interval time.Duration) {
   622  	bucket := 0
   623  	it.reaper = it.connections.clock.AfterFunc(interval, func() {
   624  		bucket, interval = it.connections.reapUnused(bucket, interval)
   625  		it.reaper.Reset(interval)
   626  	})
   627  }
   628  
   629  // Preconditions:
   630  //   - pkt is a IPv4 packet of at least length header.IPv4MinimumSize.
   631  //   - pkt.NetworkHeader is not nil.
   632  func (it *IPTables) checkChain(hook Hook, pkt *PacketBuffer, table Table, ruleIdx int, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) chainVerdict {
   633  	// Start from ruleIdx and walk the list of rules until a rule gives us
   634  	// a verdict.
   635  	for ruleIdx < len(table.Rules) {
   636  		switch verdict, jumpTo := it.checkRule(hook, pkt, table, ruleIdx, r, addressEP, inNicName, outNicName); verdict {
   637  		case RuleAccept:
   638  			return chainAccept
   639  
   640  		case RuleDrop:
   641  			return chainDrop
   642  
   643  		case RuleReturn:
   644  			return chainReturn
   645  
   646  		case RuleJump:
   647  			// "Jumping" to the next rule just means we're
   648  			// continuing on down the list.
   649  			if jumpTo == ruleIdx+1 {
   650  				ruleIdx++
   651  				continue
   652  			}
   653  			switch verdict := it.checkChain(hook, pkt, table, jumpTo, r, addressEP, inNicName, outNicName); verdict {
   654  			case chainAccept:
   655  				return chainAccept
   656  			case chainDrop:
   657  				return chainDrop
   658  			case chainReturn:
   659  				ruleIdx++
   660  				continue
   661  			default:
   662  				panic(fmt.Sprintf("Unknown verdict: %d", verdict))
   663  			}
   664  
   665  		default:
   666  			panic(fmt.Sprintf("Unknown verdict: %d", verdict))
   667  		}
   668  
   669  	}
   670  
   671  	// We got through the entire table without a decision. Default to DROP
   672  	// for safety.
   673  	return chainDrop
   674  }
   675  
   676  // Preconditions:
   677  //   - pkt is a IPv4 packet of at least length header.IPv4MinimumSize.
   678  //   - pkt.NetworkHeader is not nil.
   679  //
   680  // * pkt is a IPv4 packet of at least length header.IPv4MinimumSize.
   681  // * pkt.NetworkHeader is not nil.
   682  func (it *IPTables) checkRule(hook Hook, pkt *PacketBuffer, table Table, ruleIdx int, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) (RuleVerdict, int) {
   683  	rule := table.Rules[ruleIdx]
   684  
   685  	// Check whether the packet matches the IP header filter.
   686  	if !rule.Filter.match(pkt, hook, inNicName, outNicName) {
   687  		// Continue on to the next rule.
   688  		return RuleJump, ruleIdx + 1
   689  	}
   690  
   691  	// Go through each rule matcher. If they all match, run
   692  	// the rule target.
   693  	for _, matcher := range rule.Matchers {
   694  		matches, hotdrop := matcher.Match(hook, pkt, inNicName, outNicName)
   695  		if hotdrop {
   696  			return RuleDrop, 0
   697  		}
   698  		if !matches {
   699  			// Continue on to the next rule.
   700  			return RuleJump, ruleIdx + 1
   701  		}
   702  	}
   703  
   704  	// All the matchers matched, so run the target.
   705  	return rule.Target.Action(pkt, hook, r, addressEP)
   706  }
   707  
   708  // OriginalDst returns the original destination of redirected connections. It
   709  // returns an error if the connection doesn't exist or isn't redirected.
   710  func (it *IPTables) OriginalDst(epID TransportEndpointID, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber) (tcpip.Address, uint16, tcpip.Error) {
   711  	it.mu.RLock()
   712  	defer it.mu.RUnlock()
   713  	if !it.modified {
   714  		return tcpip.Address{}, 0, &tcpip.ErrNotConnected{}
   715  	}
   716  	return it.connections.originalDst(epID, netProto, transProto)
   717  }