github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/tcpip/stack/iptables_targets.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  
    20  	"github.com/SagerNet/gvisor/pkg/log"
    21  	"github.com/SagerNet/gvisor/pkg/tcpip"
    22  	"github.com/SagerNet/gvisor/pkg/tcpip/header"
    23  )
    24  
    25  // AcceptTarget accepts packets.
    26  type AcceptTarget struct {
    27  	// NetworkProtocol is the network protocol the target is used with.
    28  	NetworkProtocol tcpip.NetworkProtocolNumber
    29  }
    30  
    31  // Action implements Target.Action.
    32  func (*AcceptTarget) Action(*PacketBuffer, *ConnTrack, Hook, *Route, tcpip.Address) (RuleVerdict, int) {
    33  	return RuleAccept, 0
    34  }
    35  
    36  // DropTarget drops packets.
    37  type DropTarget struct {
    38  	// NetworkProtocol is the network protocol the target is used with.
    39  	NetworkProtocol tcpip.NetworkProtocolNumber
    40  }
    41  
    42  // Action implements Target.Action.
    43  func (*DropTarget) Action(*PacketBuffer, *ConnTrack, Hook, *Route, tcpip.Address) (RuleVerdict, int) {
    44  	return RuleDrop, 0
    45  }
    46  
    47  // ErrorTarget logs an error and drops the packet. It represents a target that
    48  // should be unreachable.
    49  type ErrorTarget struct {
    50  	// NetworkProtocol is the network protocol the target is used with.
    51  	NetworkProtocol tcpip.NetworkProtocolNumber
    52  }
    53  
    54  // Action implements Target.Action.
    55  func (*ErrorTarget) Action(*PacketBuffer, *ConnTrack, Hook, *Route, tcpip.Address) (RuleVerdict, int) {
    56  	log.Debugf("ErrorTarget triggered.")
    57  	return RuleDrop, 0
    58  }
    59  
    60  // UserChainTarget marks a rule as the beginning of a user chain.
    61  type UserChainTarget struct {
    62  	// Name is the chain name.
    63  	Name string
    64  
    65  	// NetworkProtocol is the network protocol the target is used with.
    66  	NetworkProtocol tcpip.NetworkProtocolNumber
    67  }
    68  
    69  // Action implements Target.Action.
    70  func (*UserChainTarget) Action(*PacketBuffer, *ConnTrack, Hook, *Route, tcpip.Address) (RuleVerdict, int) {
    71  	panic("UserChainTarget should never be called.")
    72  }
    73  
    74  // ReturnTarget returns from the current chain. If the chain is a built-in, the
    75  // hook's underflow should be called.
    76  type ReturnTarget struct {
    77  	// NetworkProtocol is the network protocol the target is used with.
    78  	NetworkProtocol tcpip.NetworkProtocolNumber
    79  }
    80  
    81  // Action implements Target.Action.
    82  func (*ReturnTarget) Action(*PacketBuffer, *ConnTrack, Hook, *Route, tcpip.Address) (RuleVerdict, int) {
    83  	return RuleReturn, 0
    84  }
    85  
    86  // RedirectTarget redirects the packet to this machine by modifying the
    87  // destination port/IP. Outgoing packets are redirected to the loopback device,
    88  // and incoming packets are redirected to the incoming interface (rather than
    89  // forwarded).
    90  type RedirectTarget struct {
    91  	// Port indicates port used to redirect. It is immutable.
    92  	Port uint16
    93  
    94  	// NetworkProtocol is the network protocol the target is used with. It
    95  	// is immutable.
    96  	NetworkProtocol tcpip.NetworkProtocolNumber
    97  }
    98  
    99  // Action implements Target.Action.
   100  func (rt *RedirectTarget) Action(pkt *PacketBuffer, ct *ConnTrack, hook Hook, r *Route, address tcpip.Address) (RuleVerdict, int) {
   101  	// Sanity check.
   102  	if rt.NetworkProtocol != pkt.NetworkProtocolNumber {
   103  		panic(fmt.Sprintf(
   104  			"RedirectTarget.Action with NetworkProtocol %d called on packet with NetworkProtocolNumber %d",
   105  			rt.NetworkProtocol, pkt.NetworkProtocolNumber))
   106  	}
   107  
   108  	// Packet is already manipulated.
   109  	if pkt.NatDone {
   110  		return RuleAccept, 0
   111  	}
   112  
   113  	// Drop the packet if network and transport header are not set.
   114  	if pkt.NetworkHeader().View().IsEmpty() || pkt.TransportHeader().View().IsEmpty() {
   115  		return RuleDrop, 0
   116  	}
   117  
   118  	// Change the address to loopback (127.0.0.1 or ::1) in Output and to
   119  	// the primary address of the incoming interface in Prerouting.
   120  	switch hook {
   121  	case Output:
   122  		if pkt.NetworkProtocolNumber == header.IPv4ProtocolNumber {
   123  			address = tcpip.Address([]byte{127, 0, 0, 1})
   124  		} else {
   125  			address = header.IPv6Loopback
   126  		}
   127  	case Prerouting:
   128  		// No-op, as address is already set correctly.
   129  	default:
   130  		panic("redirect target is supported only on output and prerouting hooks")
   131  	}
   132  
   133  	switch protocol := pkt.TransportProtocolNumber; protocol {
   134  	case header.UDPProtocolNumber:
   135  		udpHeader := header.UDP(pkt.TransportHeader().View())
   136  
   137  		if hook == Output {
   138  			// Only calculate the checksum if offloading isn't supported.
   139  			requiresChecksum := r.RequiresTXTransportChecksum()
   140  			rewritePacket(
   141  				pkt.Network(),
   142  				udpHeader,
   143  				false, /* updateSRCFields */
   144  				requiresChecksum,
   145  				requiresChecksum,
   146  				rt.Port,
   147  				address,
   148  			)
   149  		} else {
   150  			udpHeader.SetDestinationPort(rt.Port)
   151  		}
   152  
   153  		pkt.NatDone = true
   154  	case header.TCPProtocolNumber:
   155  		if ct == nil {
   156  			return RuleAccept, 0
   157  		}
   158  
   159  		// Set up conection for matching NAT rule. Only the first
   160  		// packet of the connection comes here. Other packets will be
   161  		// manipulated in connection tracking.
   162  		if conn := ct.insertRedirectConn(pkt, hook, rt.Port, address); conn != nil {
   163  			ct.handlePacket(pkt, hook, r)
   164  		}
   165  	default:
   166  		return RuleDrop, 0
   167  	}
   168  
   169  	return RuleAccept, 0
   170  }
   171  
   172  // SNATTarget modifies the source port/IP in the outgoing packets.
   173  type SNATTarget struct {
   174  	Addr tcpip.Address
   175  	Port uint16
   176  
   177  	// NetworkProtocol is the network protocol the target is used with. It
   178  	// is immutable.
   179  	NetworkProtocol tcpip.NetworkProtocolNumber
   180  }
   181  
   182  // Action implements Target.Action.
   183  func (st *SNATTarget) Action(pkt *PacketBuffer, ct *ConnTrack, hook Hook, r *Route, address tcpip.Address) (RuleVerdict, int) {
   184  	// Sanity check.
   185  	if st.NetworkProtocol != pkt.NetworkProtocolNumber {
   186  		panic(fmt.Sprintf(
   187  			"SNATTarget.Action with NetworkProtocol %d called on packet with NetworkProtocolNumber %d",
   188  			st.NetworkProtocol, pkt.NetworkProtocolNumber))
   189  	}
   190  
   191  	// Packet is already manipulated.
   192  	if pkt.NatDone {
   193  		return RuleAccept, 0
   194  	}
   195  
   196  	// Drop the packet if network and transport header are not set.
   197  	if pkt.NetworkHeader().View().IsEmpty() || pkt.TransportHeader().View().IsEmpty() {
   198  		return RuleDrop, 0
   199  	}
   200  
   201  	switch hook {
   202  	case Postrouting, Input:
   203  	case Prerouting, Output, Forward:
   204  		panic(fmt.Sprintf("%s not supported", hook))
   205  	default:
   206  		panic(fmt.Sprintf("%s unrecognized", hook))
   207  	}
   208  
   209  	switch protocol := pkt.TransportProtocolNumber; protocol {
   210  	case header.UDPProtocolNumber:
   211  		// Only calculate the checksum if offloading isn't supported.
   212  		requiresChecksum := r.RequiresTXTransportChecksum()
   213  		rewritePacket(
   214  			pkt.Network(),
   215  			header.UDP(pkt.TransportHeader().View()),
   216  			true, /* updateSRCFields */
   217  			requiresChecksum,
   218  			requiresChecksum,
   219  			st.Port,
   220  			st.Addr,
   221  		)
   222  
   223  		pkt.NatDone = true
   224  	case header.TCPProtocolNumber:
   225  		if ct == nil {
   226  			return RuleAccept, 0
   227  		}
   228  
   229  		// Set up conection for matching NAT rule. Only the first
   230  		// packet of the connection comes here. Other packets will be
   231  		// manipulated in connection tracking.
   232  		if conn := ct.insertSNATConn(pkt, hook, st.Port, st.Addr); conn != nil {
   233  			ct.handlePacket(pkt, hook, r)
   234  		}
   235  	default:
   236  		return RuleDrop, 0
   237  	}
   238  
   239  	return RuleAccept, 0
   240  }
   241  
   242  func rewritePacket(n header.Network, t header.ChecksummableTransport, updateSRCFields, fullChecksum, updatePseudoHeader bool, newPort uint16, newAddr tcpip.Address) {
   243  	if updateSRCFields {
   244  		if fullChecksum {
   245  			t.SetSourcePortWithChecksumUpdate(newPort)
   246  		} else {
   247  			t.SetSourcePort(newPort)
   248  		}
   249  	} else {
   250  		if fullChecksum {
   251  			t.SetDestinationPortWithChecksumUpdate(newPort)
   252  		} else {
   253  			t.SetDestinationPort(newPort)
   254  		}
   255  	}
   256  
   257  	if updatePseudoHeader {
   258  		var oldAddr tcpip.Address
   259  		if updateSRCFields {
   260  			oldAddr = n.SourceAddress()
   261  		} else {
   262  			oldAddr = n.DestinationAddress()
   263  		}
   264  
   265  		t.UpdateChecksumPseudoHeaderAddress(oldAddr, newAddr, fullChecksum)
   266  	}
   267  
   268  	if checksummableNetHeader, ok := n.(header.ChecksummableNetwork); ok {
   269  		if updateSRCFields {
   270  			checksummableNetHeader.SetSourceAddressWithChecksumUpdate(newAddr)
   271  		} else {
   272  			checksummableNetHeader.SetDestinationAddressWithChecksumUpdate(newAddr)
   273  		}
   274  	} else if updateSRCFields {
   275  		n.SetSourceAddress(newAddr)
   276  	} else {
   277  		n.SetDestinationAddress(newAddr)
   278  	}
   279  }