inet.af/netstack@v0.0.0-20220214151720-7585b01ddccf/tcpip/stack/iptables_types.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  	"fmt"
    19  	"strings"
    20  	"sync"
    21  
    22  	"inet.af/netstack/tcpip"
    23  	"inet.af/netstack/tcpip/header"
    24  )
    25  
    26  // A Hook specifies one of the hooks built into the network stack.
    27  //
    28  //                      Userspace app          Userspace app
    29  //                            ^                      |
    30  //                            |                      v
    31  //                         [Input]               [Output]
    32  //                            ^                      |
    33  //                            |                      v
    34  //                            |                   routing
    35  //                            |                      |
    36  //                            |                      v
    37  // ----->[Prerouting]----->routing----->[Forward]---------[Postrouting]----->
    38  type Hook uint
    39  
    40  const (
    41  	// Prerouting happens before a packet is routed to applications or to
    42  	// be forwarded.
    43  	Prerouting Hook = iota
    44  
    45  	// Input happens before a packet reaches an application.
    46  	Input
    47  
    48  	// Forward happens once it's decided that a packet should be forwarded
    49  	// to another host.
    50  	Forward
    51  
    52  	// Output happens after a packet is written by an application to be
    53  	// sent out.
    54  	Output
    55  
    56  	// Postrouting happens just before a packet goes out on the wire.
    57  	Postrouting
    58  
    59  	// NumHooks is the total number of hooks.
    60  	NumHooks
    61  )
    62  
    63  // A RuleVerdict is what a rule decides should be done with a packet.
    64  type RuleVerdict int
    65  
    66  const (
    67  	// RuleAccept indicates the packet should continue through netstack.
    68  	RuleAccept RuleVerdict = iota
    69  
    70  	// RuleDrop indicates the packet should be dropped.
    71  	RuleDrop
    72  
    73  	// RuleJump indicates the packet should jump to another chain.
    74  	RuleJump
    75  
    76  	// RuleReturn indicates the packet should return to the previous chain.
    77  	RuleReturn
    78  )
    79  
    80  // IPTables holds all the tables for a netstack.
    81  //
    82  // +stateify savable
    83  type IPTables struct {
    84  	connections ConnTrack
    85  
    86  	// reaperDone can be signaled to stop the reaper goroutine.
    87  	reaperDone chan struct{}
    88  
    89  	mu sync.RWMutex
    90  	// v4Tables and v6tables map tableIDs to tables. They hold builtin
    91  	// tables only, not user tables.
    92  	//
    93  	// +checklocks:mu
    94  	v4Tables [NumTables]Table
    95  	// +checklocks:mu
    96  	v6Tables [NumTables]Table
    97  	// modified is whether tables have been modified at least once. It is
    98  	// used to elide the iptables performance overhead for workloads that
    99  	// don't utilize iptables.
   100  	//
   101  	// +checklocks:mu
   102  	modified bool
   103  }
   104  
   105  // VisitTargets traverses all the targets of all tables and replaces each with
   106  // transform(target).
   107  func (it *IPTables) VisitTargets(transform func(Target) Target) {
   108  	it.mu.Lock()
   109  	defer it.mu.Unlock()
   110  
   111  	for tid := range it.v4Tables {
   112  		for i, rule := range it.v4Tables[tid].Rules {
   113  			it.v4Tables[tid].Rules[i].Target = transform(rule.Target)
   114  		}
   115  	}
   116  	for tid := range it.v6Tables {
   117  		for i, rule := range it.v6Tables[tid].Rules {
   118  			it.v6Tables[tid].Rules[i].Target = transform(rule.Target)
   119  		}
   120  	}
   121  }
   122  
   123  // A Table defines a set of chains and hooks into the network stack.
   124  //
   125  // It is a list of Rules, entry points (BuiltinChains), and error handlers
   126  // (Underflows). As packets traverse netstack, they hit hooks. When a packet
   127  // hits a hook, iptables compares it to Rules starting from that hook's entry
   128  // point. So if a packet hits the Input hook, we look up the corresponding
   129  // entry point in BuiltinChains and jump to that point.
   130  //
   131  // If the Rule doesn't match the packet, iptables continues to the next Rule.
   132  // If a Rule does match, it can issue a verdict on the packet (e.g. RuleAccept
   133  // or RuleDrop) that causes the packet to stop traversing iptables. It can also
   134  // jump to other rules or perform custom actions based on Rule.Target.
   135  //
   136  // Underflow Rules are invoked when a chain returns without reaching a verdict.
   137  //
   138  // +stateify savable
   139  type Table struct {
   140  	// Rules holds the rules that make up the table.
   141  	Rules []Rule
   142  
   143  	// BuiltinChains maps builtin chains to their entrypoint rule in Rules.
   144  	BuiltinChains [NumHooks]int
   145  
   146  	// Underflows maps builtin chains to their underflow rule in Rules
   147  	// (i.e. the rule to execute if the chain returns without a verdict).
   148  	Underflows [NumHooks]int
   149  }
   150  
   151  // ValidHooks returns a bitmap of the builtin hooks for the given table.
   152  func (table *Table) ValidHooks() uint32 {
   153  	hooks := uint32(0)
   154  	for hook, ruleIdx := range table.BuiltinChains {
   155  		if ruleIdx != HookUnset {
   156  			hooks |= 1 << hook
   157  		}
   158  	}
   159  	return hooks
   160  }
   161  
   162  // A Rule is a packet processing rule. It consists of two pieces. First it
   163  // contains zero or more matchers, each of which is a specification of which
   164  // packets this rule applies to. If there are no matchers in the rule, it
   165  // applies to any packet.
   166  //
   167  // +stateify savable
   168  type Rule struct {
   169  	// Filter holds basic IP filtering fields common to every rule.
   170  	Filter IPHeaderFilter
   171  
   172  	// Matchers is the list of matchers for this rule.
   173  	Matchers []Matcher
   174  
   175  	// Target is the action to invoke if all the matchers match the packet.
   176  	Target Target
   177  }
   178  
   179  // IPHeaderFilter performs basic IP header matching common to every rule.
   180  //
   181  // +stateify savable
   182  type IPHeaderFilter struct {
   183  	// Protocol matches the transport protocol.
   184  	Protocol tcpip.TransportProtocolNumber
   185  
   186  	// CheckProtocol determines whether the Protocol field should be
   187  	// checked during matching.
   188  	CheckProtocol bool
   189  
   190  	// Dst matches the destination IP address.
   191  	Dst tcpip.Address
   192  
   193  	// DstMask masks bits of the destination IP address when comparing with
   194  	// Dst.
   195  	DstMask tcpip.Address
   196  
   197  	// DstInvert inverts the meaning of the destination IP check, i.e. when
   198  	// true the filter will match packets that fail the destination
   199  	// comparison.
   200  	DstInvert bool
   201  
   202  	// Src matches the source IP address.
   203  	Src tcpip.Address
   204  
   205  	// SrcMask masks bits of the source IP address when comparing with Src.
   206  	SrcMask tcpip.Address
   207  
   208  	// SrcInvert inverts the meaning of the source IP check, i.e. when true the
   209  	// filter will match packets that fail the source comparison.
   210  	SrcInvert bool
   211  
   212  	// InputInterface matches the name of the incoming interface for the packet.
   213  	InputInterface string
   214  
   215  	// InputInterfaceMask masks the characters of the interface name when
   216  	// comparing with InputInterface.
   217  	InputInterfaceMask string
   218  
   219  	// InputInterfaceInvert inverts the meaning of incoming interface check,
   220  	// i.e. when true the filter will match packets that fail the incoming
   221  	// interface comparison.
   222  	InputInterfaceInvert bool
   223  
   224  	// OutputInterface matches the name of the outgoing interface for the packet.
   225  	OutputInterface string
   226  
   227  	// OutputInterfaceMask masks the characters of the interface name when
   228  	// comparing with OutputInterface.
   229  	OutputInterfaceMask string
   230  
   231  	// OutputInterfaceInvert inverts the meaning of outgoing interface check,
   232  	// i.e. when true the filter will match packets that fail the outgoing
   233  	// interface comparison.
   234  	OutputInterfaceInvert bool
   235  }
   236  
   237  // match returns whether pkt matches the filter.
   238  //
   239  // Preconditions: pkt.NetworkHeader is set and is at least of the minimal IPv4
   240  // or IPv6 header length.
   241  func (fl IPHeaderFilter) match(pkt *PacketBuffer, hook Hook, inNicName, outNicName string) bool {
   242  	// Extract header fields.
   243  	var (
   244  		transProto tcpip.TransportProtocolNumber
   245  		dstAddr    tcpip.Address
   246  		srcAddr    tcpip.Address
   247  	)
   248  	switch proto := pkt.NetworkProtocolNumber; proto {
   249  	case header.IPv4ProtocolNumber:
   250  		hdr := header.IPv4(pkt.NetworkHeader().View())
   251  		transProto = hdr.TransportProtocol()
   252  		dstAddr = hdr.DestinationAddress()
   253  		srcAddr = hdr.SourceAddress()
   254  
   255  	case header.IPv6ProtocolNumber:
   256  		hdr := header.IPv6(pkt.NetworkHeader().View())
   257  		transProto = hdr.TransportProtocol()
   258  		dstAddr = hdr.DestinationAddress()
   259  		srcAddr = hdr.SourceAddress()
   260  
   261  	default:
   262  		panic(fmt.Sprintf("unknown network protocol with EtherType: %d", proto))
   263  	}
   264  
   265  	// Check the transport protocol.
   266  	if fl.CheckProtocol && fl.Protocol != transProto {
   267  		return false
   268  	}
   269  
   270  	// Check the addresses.
   271  	if !filterAddress(dstAddr, fl.DstMask, fl.Dst, fl.DstInvert) ||
   272  		!filterAddress(srcAddr, fl.SrcMask, fl.Src, fl.SrcInvert) {
   273  		return false
   274  	}
   275  
   276  	switch hook {
   277  	case Prerouting, Input:
   278  		return matchIfName(inNicName, fl.InputInterface, fl.InputInterfaceInvert)
   279  	case Output:
   280  		return matchIfName(outNicName, fl.OutputInterface, fl.OutputInterfaceInvert)
   281  	case Forward:
   282  		if !matchIfName(inNicName, fl.InputInterface, fl.InputInterfaceInvert) {
   283  			return false
   284  		}
   285  
   286  		if !matchIfName(outNicName, fl.OutputInterface, fl.OutputInterfaceInvert) {
   287  			return false
   288  		}
   289  
   290  		return true
   291  	case Postrouting:
   292  		return true
   293  	default:
   294  		panic(fmt.Sprintf("unknown hook: %d", hook))
   295  	}
   296  }
   297  
   298  func matchIfName(nicName string, ifName string, invert bool) bool {
   299  	n := len(ifName)
   300  	if n == 0 {
   301  		// If the interface name is omitted in the filter, any interface will match.
   302  		return true
   303  	}
   304  	// If the interface name ends with '+', any interface which begins with the
   305  	// name should be matched.
   306  	var matches bool
   307  	if strings.HasSuffix(ifName, "+") {
   308  		matches = strings.HasPrefix(nicName, ifName[:n-1])
   309  	} else {
   310  		matches = nicName == ifName
   311  	}
   312  	return matches != invert
   313  }
   314  
   315  // NetworkProtocol returns the protocol (IPv4 or IPv6) on to which the header
   316  // applies.
   317  func (fl IPHeaderFilter) NetworkProtocol() tcpip.NetworkProtocolNumber {
   318  	switch len(fl.Src) {
   319  	case header.IPv4AddressSize:
   320  		return header.IPv4ProtocolNumber
   321  	case header.IPv6AddressSize:
   322  		return header.IPv6ProtocolNumber
   323  	}
   324  	panic(fmt.Sprintf("invalid address in IPHeaderFilter: %s", fl.Src))
   325  }
   326  
   327  // filterAddress returns whether addr matches the filter.
   328  func filterAddress(addr, mask, filterAddr tcpip.Address, invert bool) bool {
   329  	matches := true
   330  	for i := range filterAddr {
   331  		if addr[i]&mask[i] != filterAddr[i] {
   332  			matches = false
   333  			break
   334  		}
   335  	}
   336  	return matches != invert
   337  }
   338  
   339  // A Matcher is the interface for matching packets.
   340  type Matcher interface {
   341  	// Match returns whether the packet matches and whether the packet
   342  	// should be "hotdropped", i.e. dropped immediately. This is usually
   343  	// used for suspicious packets.
   344  	//
   345  	// Precondition: packet.NetworkHeader is set.
   346  	Match(hook Hook, packet *PacketBuffer, inputInterfaceName, outputInterfaceName string) (matches bool, hotdrop bool)
   347  }
   348  
   349  // A Target is the interface for taking an action for a packet.
   350  type Target interface {
   351  	// Action takes an action on the packet and returns a verdict on how
   352  	// traversal should (or should not) continue. If the return value is
   353  	// Jump, it also returns the index of the rule to jump to.
   354  	Action(*PacketBuffer, Hook, *Route, AddressableEndpoint) (RuleVerdict, int)
   355  }