@ -19,6 +19,7 @@ import (
"go4.org/mem"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"inet.af/netaddr"
"tailscale.com/disco"
"tailscale.com/net/packet"
@ -154,12 +155,19 @@ type Wrapper struct {
disableTSMPRejected bool
}
// tunReadResult is the result of a TUN read : Some data and an error .
// The byte slice is not interpreted in the usual way for a Read method.
// tunReadResult is the result of a TUN read , or an injected result pretending to be a TUN read .
// The data is not interpreted in the usual way for a Read method.
// See the comment in the middle of Wrap.Read.
type tunReadResult struct {
data [ ] byte
err error
// Only one of err, packet or data should be set, and are read in that order
// of precendence.
err error
packet * stack . PacketBuffer
data [ ] byte
// injected is set if the read result was generated internally, and contained packets should not
// pass through filters.
injected bool
}
func WrapTAP ( logf logger . Logf , tdev tun . Device ) * Wrapper {
@ -494,15 +502,22 @@ func (t *Wrapper) Read(buf []byte, offset int) (int, error) {
}
metricPacketOut . Add ( 1 )
pkt := res . data
n := copy ( buf [ offset : ] , pkt )
// t.buffer has a fixed location in memory.
// If the packet is not from t.buffer, then it is an injected packet.
// &pkt[0] can be used because empty packets do not reach t.outbound.
isInjectedPacket := & pkt [ 0 ] != & t . buffer [ PacketStartOffset ]
if ! isInjectedPacket {
// We are done with t.buffer. Let poll re-use it.
t . sendBufferConsumed ( )
var n int
if res . packet != nil {
n = copy ( buf [ offset : ] , res . packet . NetworkHeader ( ) . View ( ) )
n += copy ( buf [ offset + n : ] , res . packet . TransportHeader ( ) . View ( ) )
n += copy ( buf [ offset + n : ] , res . packet . Data ( ) . AsRange ( ) . AsView ( ) )
res . packet . DecRef ( )
} else {
n = copy ( buf [ offset : ] , res . data )
// t.buffer has a fixed location in memory.
if & res . data [ 0 ] == & t . buffer [ PacketStartOffset ] {
// We are done with t.buffer. Let poll re-use it.
t . sendBufferConsumed ( )
}
}
p := parsedPacketPool . Get ( ) . ( * packet . Parsed )
@ -516,7 +531,7 @@ func (t *Wrapper) Read(buf []byte, offset int) (int, error) {
}
// Do not filter injected packets.
if ! isInjectedPacket && ! t . disableFilter {
if ! res. injected && ! t . disableFilter {
response := t . filterOut ( p )
if response != filter . Accept {
metricPacketOutDrop . Add ( 1 )
@ -741,7 +756,24 @@ func (t *Wrapper) InjectOutbound(packet []byte) error {
if len ( packet ) == 0 {
return nil
}
t . sendOutbound ( tunReadResult { data : packet } )
t . sendOutbound ( tunReadResult { data : packet , injected : true } )
return nil
}
// InjectOutboundPacketBuffer logically behaves as InjectOutbound. It takes ownership of one
// reference count on the packet, and the packet may be mutated. The packet refcount will be
// decremented after the injected buffer has been read.
func ( t * Wrapper ) InjectOutboundPacketBuffer ( packet * stack . PacketBuffer ) error {
size := packet . Size ( )
if size > MaxPacketSize {
packet . DecRef ( )
return errPacketTooBig
}
if size == 0 {
packet . DecRef ( )
return nil
}
t . sendOutbound ( tunReadResult { packet : packet , injected : true } )
return nil
}