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