mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add /proc/sys/net/ipv4/ip_forward
This commit is contained in:
@@ -53,6 +53,7 @@ go_library(
|
||||
"//pkg/sentry/usermem",
|
||||
"//pkg/syserror",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/network/ipv4",
|
||||
"//pkg/waiter",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs/ramfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/inet"
|
||||
"gvisor.dev/gvisor/pkg/sentry/usermem"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
@@ -280,11 +281,115 @@ func (p *proc) newSysNetCore(ctx context.Context, msrc *fs.MountSource, s inet.S
|
||||
return newProcInode(ctx, d, msrc, fs.SpecialDirectory, nil)
|
||||
}
|
||||
|
||||
// ipForwarding implements fs.InodeOperations.
|
||||
//
|
||||
// ipForwarding is used to enable/disable packet forwarding of netstack.
|
||||
//
|
||||
// +stateify savable
|
||||
type ipForwarding struct {
|
||||
stack inet.Stack `state:".(ipForwardingState)"`
|
||||
fsutil.SimpleFileInode
|
||||
}
|
||||
|
||||
// ipForwardingState is used to stores a state of netstack
|
||||
// for packet forwarding because netstack itself is stateless.
|
||||
//
|
||||
// +stateify savable
|
||||
type ipForwardingState struct {
|
||||
stack inet.Stack `state:"wait"`
|
||||
|
||||
// enabled stores packet forwarding settings during save, and sets it back
|
||||
// in netstack in restore. We must save/restore this here, since
|
||||
// netstack itself is stateless.
|
||||
enabled bool
|
||||
}
|
||||
|
||||
func newIPForwardingInode(ctx context.Context, msrc *fs.MountSource, s inet.Stack) *fs.Inode {
|
||||
ipf := &ipForwarding{
|
||||
SimpleFileInode: *fsutil.NewSimpleFileInode(ctx, fs.RootOwner, fs.FilePermsFromMode(0444), linux.PROC_SUPER_MAGIC),
|
||||
stack: s,
|
||||
}
|
||||
sattr := fs.StableAttr{
|
||||
DeviceID: device.ProcDevice.DeviceID(),
|
||||
InodeID: device.ProcDevice.NextIno(),
|
||||
BlockSize: usermem.PageSize,
|
||||
Type: fs.SpecialFile,
|
||||
}
|
||||
return fs.NewInode(ctx, ipf, msrc, sattr)
|
||||
}
|
||||
|
||||
// +stateify savable
|
||||
type ipForwardingFile struct {
|
||||
fsutil.FileGenericSeek `state:"nosave"`
|
||||
fsutil.FileNoIoctl `state:"nosave"`
|
||||
fsutil.FileNoMMap `state:"nosave"`
|
||||
fsutil.FileNoSplice `state:"nosave"`
|
||||
fsutil.FileNoopRelease `state:"nosave"`
|
||||
fsutil.FileNoopFlush `state:"nosave"`
|
||||
fsutil.FileNoopFsync `state:"nosave"`
|
||||
fsutil.FileNotDirReaddir `state:"nosave"`
|
||||
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
|
||||
waiter.AlwaysReady `state:"nosave"`
|
||||
|
||||
stack inet.Stack `state:"wait"`
|
||||
}
|
||||
|
||||
// GetFile implements fs.InodeOperations.GetFile.
|
||||
func (ipf *ipForwarding) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
|
||||
flags.Pread = true
|
||||
flags.Pwrite = true
|
||||
return fs.NewFile(ctx, dirent, flags, &ipForwardingFile{
|
||||
stack: ipf.stack,
|
||||
}), nil
|
||||
}
|
||||
|
||||
// Read implements fs.FileOperations.Read.
|
||||
func (f *ipForwardingFile) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequence, offset int64) (int64, error) {
|
||||
if offset != 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
val := "0\n"
|
||||
if f.stack.Forwarding(ipv4.ProtocolNumber) {
|
||||
// Technically, this is not quite compatible with Linux. Linux
|
||||
// stores these as an integer, so if you write "2" into
|
||||
// ip_forward, you should get 2 back.
|
||||
val = "1\n"
|
||||
}
|
||||
|
||||
n, err := dst.CopyOut(ctx, []byte(val))
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
// Write implements fs.FileOperations.Write.
|
||||
//
|
||||
// Offset is ignored, multiple writes are not supported.
|
||||
func (f *ipForwardingFile) Write(ctx context.Context, _ *fs.File, src usermem.IOSequence, offset int64) (int64, error) {
|
||||
if src.NumBytes() == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Only consider size of one memory page for input.
|
||||
src = src.TakeFirst(usermem.PageSize - 1)
|
||||
|
||||
var v int32
|
||||
n, err := usermem.CopyInt32StringInVec(ctx, src.IO, src.Addrs, &v, src.Opts)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
enabled := v != 0
|
||||
return n, f.stack.SetForwarding(ipv4.ProtocolNumber, enabled)
|
||||
}
|
||||
|
||||
func (p *proc) newSysNetIPv4Dir(ctx context.Context, msrc *fs.MountSource, s inet.Stack) *fs.Inode {
|
||||
contents := map[string]*fs.Inode{
|
||||
// Add tcp_sack.
|
||||
"tcp_sack": newTCPSackInode(ctx, msrc, s),
|
||||
|
||||
// Add ip_forward.
|
||||
"ip_forward": newIPForwardingInode(ctx, msrc, s),
|
||||
|
||||
// The following files are simple stubs until they are
|
||||
// implemented in netstack, most of these files are
|
||||
// configuration related. We use the value closest to the
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
|
||||
package proc
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||
)
|
||||
|
||||
// beforeSave is invoked by stateify.
|
||||
func (t *tcpMemInode) beforeSave() {
|
||||
@@ -40,3 +43,19 @@ func (s *tcpSack) afterLoad() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// saveStack is invoked by stateify.
|
||||
func (ipf *ipForwarding) saveStack() ipForwardingState {
|
||||
return ipForwardingState{
|
||||
ipf.stack,
|
||||
ipf.stack.Forwarding(ipv4.ProtocolNumber),
|
||||
}
|
||||
}
|
||||
|
||||
// loadStack is invoked by stateify.
|
||||
func (ipf *ipForwarding) loadStack(s ipForwardingState) {
|
||||
ipf.stack = s.stack
|
||||
if err := ipf.stack.SetForwarding(ipv4.ProtocolNumber, s.enabled); err != nil {
|
||||
panic(fmt.Sprintf("failed to set previous IPv4 forwarding configuration [%v]: %v", s.enabled, err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,3 +123,71 @@ func TestConfigureRecvBufferSize(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigureIPForwarding(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
s := inet.NewTestStack()
|
||||
|
||||
var cases = []struct {
|
||||
comment string
|
||||
initial bool
|
||||
str string
|
||||
final bool
|
||||
}{
|
||||
{
|
||||
comment: `Forwarding is disabled; write 1 and enable forwarding`,
|
||||
initial: false,
|
||||
str: "1",
|
||||
final: true,
|
||||
},
|
||||
{
|
||||
comment: `Forwarding is disabled; write 0 and disable forwarding`,
|
||||
initial: false,
|
||||
str: "0",
|
||||
final: false,
|
||||
},
|
||||
{
|
||||
comment: `Forwarding is enabled; write 1 and enable forwarding`,
|
||||
initial: true,
|
||||
str: "1",
|
||||
final: true,
|
||||
},
|
||||
{
|
||||
comment: `Forwarding is enabled; write 0 and disable forwarding`,
|
||||
initial: true,
|
||||
str: "0",
|
||||
final: false,
|
||||
},
|
||||
{
|
||||
comment: `Forwarding is disabled; write 2404 and enable forwarding`,
|
||||
initial: false,
|
||||
str: "2404",
|
||||
final: true,
|
||||
},
|
||||
{
|
||||
comment: `Forwarding is enabled; write 2404 and enable forwarding`,
|
||||
initial: true,
|
||||
str: "2404",
|
||||
final: true,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.comment, func(t *testing.T) {
|
||||
s.IPForwarding = c.initial
|
||||
|
||||
file := &ipForwardingFile{stack: s}
|
||||
|
||||
// Write the values.
|
||||
src := usermem.BytesIOSequence([]byte(c.str))
|
||||
if n, err := file.Write(ctx, nil, src, 0); n != int64(len(c.str)) || err != nil {
|
||||
t.Errorf("file.Write(ctx, nil, %v, 0) = (%d, %v); wanted (%d, nil)", c.str, n, err, len(c.str))
|
||||
}
|
||||
|
||||
// Read the values from the stack and check them.
|
||||
if s.IPForwarding != c.final {
|
||||
t.Errorf("s.IPForwarding = %v; wanted %v", s.IPForwarding, c.final)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,5 +13,8 @@ go_library(
|
||||
"test_stack.go",
|
||||
],
|
||||
importpath = "gvisor.dev/gvisor/pkg/sentry/inet",
|
||||
deps = ["//pkg/sentry/context"],
|
||||
deps = [
|
||||
"//pkg/sentry/context",
|
||||
"//pkg/tcpip",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
// Package inet defines semantics for IP stacks.
|
||||
package inet
|
||||
|
||||
import "gvisor.dev/gvisor/pkg/tcpip"
|
||||
|
||||
// Stack represents a TCP/IP stack.
|
||||
type Stack interface {
|
||||
// Interfaces returns all network interfaces as a mapping from interface
|
||||
@@ -58,6 +60,12 @@ type Stack interface {
|
||||
|
||||
// Resume restarts the network stack after restore.
|
||||
Resume()
|
||||
|
||||
// Forwarding returns if packet forwarding between NICs is enabled.
|
||||
Forwarding(protocol tcpip.NetworkProtocolNumber) bool
|
||||
|
||||
// SetForwarding enables or disables packet forwarding between NICs.
|
||||
SetForwarding(protocol tcpip.NetworkProtocolNumber, enable bool) error
|
||||
}
|
||||
|
||||
// Interface contains information about a network interface.
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
|
||||
package inet
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
)
|
||||
|
||||
// TestStack is a dummy implementation of Stack for tests.
|
||||
type TestStack struct {
|
||||
InterfacesMap map[int32]Interface
|
||||
@@ -23,6 +27,7 @@ type TestStack struct {
|
||||
TCPRecvBufSize TCPBufferSize
|
||||
TCPSendBufSize TCPBufferSize
|
||||
TCPSACKFlag bool
|
||||
IPForwarding bool
|
||||
}
|
||||
|
||||
// NewTestStack returns a TestStack with no network interfaces. The value of
|
||||
@@ -96,3 +101,14 @@ func (s *TestStack) RouteTable() []Route {
|
||||
// Resume implements Stack.Resume.
|
||||
func (s *TestStack) Resume() {
|
||||
}
|
||||
|
||||
// Forwarding implements inet.Stack.Forwarding.
|
||||
func (s *TestStack) Forwarding(protocol tcpip.NetworkProtocolNumber) bool {
|
||||
return s.IPForwarding
|
||||
}
|
||||
|
||||
// SetForwarding implements inet.Stack.SetForwarding.
|
||||
func (s *TestStack) SetForwarding(protocol tcpip.NetworkProtocolNumber, enable bool) error {
|
||||
s.IPForwarding = enable
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -32,6 +32,9 @@ go_library(
|
||||
"//pkg/sentry/usermem",
|
||||
"//pkg/syserr",
|
||||
"//pkg/syserror",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/network/ipv4",
|
||||
"//pkg/tcpip/network/ipv6",
|
||||
"//pkg/waiter",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -31,6 +31,9 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/usermem"
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||
)
|
||||
|
||||
var defaultRecvBufSize = inet.TCPBufferSize{
|
||||
@@ -57,6 +60,8 @@ type Stack struct {
|
||||
tcpSACKEnabled bool
|
||||
netDevFile *os.File
|
||||
netSNMPFile *os.File
|
||||
ipv4Forwarding bool
|
||||
ipv6Forwarding bool
|
||||
}
|
||||
|
||||
// NewStack returns an empty Stack containing no configuration.
|
||||
@@ -116,6 +121,13 @@ func (s *Stack) Configure() error {
|
||||
s.netSNMPFile = f
|
||||
}
|
||||
|
||||
s.ipv4Forwarding = false
|
||||
if ipForwarding, err := ioutil.ReadFile("/proc/sys/net/ipv4/ip_forward"); err == nil {
|
||||
s.ipv4Forwarding = strings.TrimSpace(string(ipForwarding)) != "0"
|
||||
} else {
|
||||
log.Warningf("Failed to read if IPv4 forwarding is enabled, setting to false")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -442,3 +454,21 @@ func (s *Stack) RouteTable() []inet.Route {
|
||||
|
||||
// Resume implements inet.Stack.Resume.
|
||||
func (s *Stack) Resume() {}
|
||||
|
||||
// Forwarding implements inet.Stack.Forwarding.
|
||||
func (s *Stack) Forwarding(protocol tcpip.NetworkProtocolNumber) bool {
|
||||
switch protocol {
|
||||
case ipv4.ProtocolNumber:
|
||||
return s.ipv4Forwarding
|
||||
case ipv6.ProtocolNumber:
|
||||
return s.ipv6Forwarding
|
||||
default:
|
||||
log.Warningf("Forwarding(%v) failed: unsupported protocol", protocol)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// SetForwarding implements inet.Stack.SetForwarding.
|
||||
func (s *Stack) SetForwarding(protocol tcpip.NetworkProtocolNumber, enable bool) error {
|
||||
return syserror.EACCES
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/inet"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/netfilter"
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/iptables"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||
@@ -291,3 +292,26 @@ func (s *Stack) FillDefaultIPTables() {
|
||||
func (s *Stack) Resume() {
|
||||
s.Stack.Resume()
|
||||
}
|
||||
|
||||
// Forwarding implements inet.Stack.Forwarding.
|
||||
func (s *Stack) Forwarding(protocol tcpip.NetworkProtocolNumber) bool {
|
||||
switch protocol {
|
||||
case ipv4.ProtocolNumber, ipv6.ProtocolNumber:
|
||||
return s.Stack.Forwarding(protocol)
|
||||
default:
|
||||
log.Warningf("Forwarding(%v) failed: unsupported protocol", protocol)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// SetForwarding implements inet.Stack.SetForwarding.
|
||||
func (s *Stack) SetForwarding(protocol tcpip.NetworkProtocolNumber, enable bool) error {
|
||||
switch protocol {
|
||||
case ipv4.ProtocolNumber, ipv6.ProtocolNumber:
|
||||
s.Stack.SetForwarding(protocol, enable)
|
||||
default:
|
||||
log.Warningf("SetForwarding(%v) failed: unsupported protocol", protocol)
|
||||
return syserr.ErrProtocolNotSupported.ToError()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/rpcinet/conn"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/rpcinet/notifier"
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/unet"
|
||||
)
|
||||
|
||||
@@ -165,3 +166,13 @@ func (s *Stack) RouteTable() []inet.Route {
|
||||
|
||||
// Resume implements inet.Stack.Resume.
|
||||
func (s *Stack) Resume() {}
|
||||
|
||||
// Forwarding implements inet.Stack.Forwarding.
|
||||
func (s *Stack) Forwarding(protocol tcpip.NetworkProtocolNumber) bool {
|
||||
panic("rpcinet handles procfs directly this method should not be called")
|
||||
}
|
||||
|
||||
// SetForwarding implements inet.Stack.SetForwarding.
|
||||
func (s *Stack) SetForwarding(protocol tcpip.NetworkProtocolNumber, enable bool) error {
|
||||
panic("rpcinet handles procfs directly this method should not be called")
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ go_library(
|
||||
go_test(
|
||||
name = "buffer_test",
|
||||
size = "small",
|
||||
srcs = ["view_test.go"],
|
||||
srcs = [
|
||||
"prependable_test.go",
|
||||
"view_test.go",
|
||||
],
|
||||
embed = [":buffer"],
|
||||
)
|
||||
|
||||
@@ -32,13 +32,19 @@ func NewPrependable(size int) Prependable {
|
||||
return Prependable{buf: NewView(size), usedIdx: size}
|
||||
}
|
||||
|
||||
// NewPrependableFromView creates an entirely-used Prependable from a View.
|
||||
// NewPrependableFromView creates a Prependable from a View and allocates
|
||||
// additional space if needed.
|
||||
//
|
||||
// NewPrependableFromView takes ownership of v. Note that since the entire
|
||||
// prependable is used, further attempts to call Prepend will note that size >
|
||||
// p.usedIdx and return nil.
|
||||
func NewPrependableFromView(v View) Prependable {
|
||||
return Prependable{buf: v, usedIdx: 0}
|
||||
// NewPrependableFromView takes ownership of v. Note that if the entire
|
||||
// prependable is used, further attempts to call Prepend will note that
|
||||
// size > p.usedIdx and return nil.
|
||||
func NewPrependableFromView(v View, extraCap int) Prependable {
|
||||
if extraCap == 0 {
|
||||
return Prependable{buf: v, usedIdx: 0}
|
||||
}
|
||||
buf := make([]byte, extraCap, extraCap + len(v))
|
||||
buf = append(buf, v...)
|
||||
return Prependable{buf: buf, usedIdx: extraCap}
|
||||
}
|
||||
|
||||
// NewEmptyPrependableFromView creates a new prependable buffer from a View.
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2019 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
package buffer
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewPrependableFromView(t *testing.T) {
|
||||
tests := []struct {
|
||||
comment string
|
||||
view View
|
||||
extraSize int
|
||||
want Prependable
|
||||
}{
|
||||
{
|
||||
comment: "Reserve extra space",
|
||||
view: View("abc"),
|
||||
extraSize: 2,
|
||||
want: Prependable{buf: View("\x00\x00abc"), usedIdx: 2},
|
||||
},
|
||||
{
|
||||
comment: "Don't reserve extra space",
|
||||
view: View("abc"),
|
||||
extraSize: 0,
|
||||
want: Prependable{buf: View("abc"), usedIdx: 0},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range tests {
|
||||
t.Run(testCase.comment, func(t *testing.T) {
|
||||
prep := NewPrependableFromView(testCase.view, testCase.extraSize)
|
||||
if !reflect.DeepEqual(prep, testCase.want) {
|
||||
t.Errorf("NewPrependableFromView(%#v, %d) = %#v; want %#v", testCase.view, testCase.extraSize, prep, testCase.want)
|
||||
}
|
||||
} )
|
||||
}
|
||||
}
|
||||
@@ -307,7 +307,9 @@ func (e *endpoint) WriteHeaderIncludedPacket(r *stack.Route, payload buffer.Vect
|
||||
return nil
|
||||
}
|
||||
|
||||
hdr := buffer.NewPrependableFromView(payload.ToView())
|
||||
// If we want to send the packet to a link-layer,
|
||||
// we have to reserve space for an Ethernet header.
|
||||
hdr := buffer.NewPrependableFromView(payload.ToView(), int(e.linkEP.MaxHeaderLength()))
|
||||
r.Stats().IP.PacketsSent.Increment()
|
||||
return e.linkEP.WritePacket(r, nil /* gso */, hdr, buffer.VectorisedView{}, ProtocolNumber)
|
||||
}
|
||||
|
||||
@@ -780,7 +780,7 @@ func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remote, local tcpip.Link
|
||||
// packet and forward it to the NIC.
|
||||
//
|
||||
// TODO: Should we be forwarding the packet even if promiscuous?
|
||||
if n.stack.Forwarding() {
|
||||
if n.stack.Forwarding(protocol) {
|
||||
r, err := n.stack.FindRoute(0, "", dst, protocol, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
n.stack.stats.IP.InvalidAddressesReceived.Increment()
|
||||
@@ -805,9 +805,12 @@ func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remote, local tcpip.Link
|
||||
} else {
|
||||
// n doesn't have a destination endpoint.
|
||||
// Send the packet out of n.
|
||||
hdr := buffer.NewPrependableFromView(vv.First())
|
||||
// If we want to send the packet to a link-layer,
|
||||
// we have to reserve space for an Ethernet header.
|
||||
hdr := buffer.NewPrependableFromView(vv.First(), int(n.linkEP.MaxHeaderLength()))
|
||||
vv.RemoveFirst()
|
||||
|
||||
// TODO(gvisor.dev/issue/1085): According to the RFC, we must decrease the TTL field for ipv4/ipv6.
|
||||
// TODO(b/128629022): use route.WritePacket.
|
||||
if err := n.linkEP.WritePacket(&r, nil /* gso */, hdr, vv, protocol); err != nil {
|
||||
r.Stats().IP.OutgoingPacketErrors.Increment()
|
||||
|
||||
+62
-13
@@ -21,7 +21,9 @@ package stack
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/time/rate"
|
||||
@@ -48,6 +50,42 @@ const (
|
||||
DefaultTOS = 0
|
||||
)
|
||||
|
||||
const (
|
||||
// fakeNetNumber is used as a protocol number in tests.
|
||||
//
|
||||
// This constant should match fakeNetNumber in stack_test.go.
|
||||
fakeNetNumber tcpip.NetworkProtocolNumber = math.MaxUint32
|
||||
)
|
||||
|
||||
type forwardingFlag uint32
|
||||
|
||||
// Packet forwarding flags. Forwarding settings for different network protocols
|
||||
// are stored as bit flags in an uint32 number.
|
||||
const (
|
||||
forwardingIPv4 forwardingFlag = 1 << iota
|
||||
forwardingIPv6
|
||||
|
||||
// forwardingFake is used to test package forwarding with a fake protocol.
|
||||
forwardingFake
|
||||
)
|
||||
|
||||
func getForwardingFlag(protocol tcpip.NetworkProtocolNumber) forwardingFlag {
|
||||
var flag forwardingFlag
|
||||
switch protocol {
|
||||
case header.IPv4ProtocolNumber:
|
||||
flag = forwardingIPv4
|
||||
case header.IPv6ProtocolNumber:
|
||||
flag = forwardingIPv6
|
||||
case fakeNetNumber:
|
||||
// This network protocol number is used in stack_test to test
|
||||
// packet forwarding.
|
||||
flag = forwardingFake
|
||||
default:
|
||||
// We only support forwarding for IPv4 and IPv6.
|
||||
}
|
||||
return flag
|
||||
}
|
||||
|
||||
type transportProtocolState struct {
|
||||
proto TransportProtocol
|
||||
defaultHandler func(r *Route, id TransportEndpointID, netHeader buffer.View, vv buffer.VectorisedView) bool
|
||||
@@ -363,7 +401,10 @@ type Stack struct {
|
||||
|
||||
mu sync.RWMutex
|
||||
nics map[tcpip.NICID]*NIC
|
||||
forwarding bool
|
||||
|
||||
// forwarding contains the enable bits for packet forwarding for different
|
||||
// network protocols.
|
||||
forwarding uint32
|
||||
|
||||
// route is the route table passed in by the user via SetRouteTable(),
|
||||
// it is used by FindRoute() to build a route for a specific
|
||||
@@ -630,20 +671,28 @@ func (s *Stack) Stats() tcpip.Stats {
|
||||
return s.stats
|
||||
}
|
||||
|
||||
// SetForwarding enables or disables the packet forwarding between NICs.
|
||||
func (s *Stack) SetForwarding(enable bool) {
|
||||
// TODO(igudger, bgeffon): Expose via /proc/sys/net/ipv4/ip_forward.
|
||||
s.mu.Lock()
|
||||
s.forwarding = enable
|
||||
s.mu.Unlock()
|
||||
// SetForwarding enables or disables packet forwarding between NICs.
|
||||
func (s *Stack) SetForwarding(protocol tcpip.NetworkProtocolNumber, enable bool) {
|
||||
flag := getForwardingFlag(protocol)
|
||||
for {
|
||||
forwarding := forwardingFlag(atomic.LoadUint32(&s.forwarding))
|
||||
var newValue forwardingFlag
|
||||
if enable {
|
||||
newValue = forwarding | flag
|
||||
} else {
|
||||
newValue = forwarding & ^flag
|
||||
}
|
||||
if atomic.CompareAndSwapUint32(&s.forwarding, uint32(forwarding), uint32(newValue)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Forwarding returns if the packet forwarding between NICs is enabled.
|
||||
func (s *Stack) Forwarding() bool {
|
||||
// TODO(igudger, bgeffon): Expose via /proc/sys/net/ipv4/ip_forward.
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.forwarding
|
||||
// Forwarding returns if packet forwarding between NICs is enabled.
|
||||
func (s *Stack) Forwarding(protocol tcpip.NetworkProtocolNumber) bool {
|
||||
flag := getForwardingFlag(protocol)
|
||||
forwarding := forwardingFlag(atomic.LoadUint32(&s.forwarding))
|
||||
return forwarding & flag != 0
|
||||
}
|
||||
|
||||
// SetRouteTable assigns the route table to be used by this stack. It
|
||||
|
||||
@@ -36,6 +36,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// fakeNetNumber is used as a protocol number in tests.
|
||||
//
|
||||
// This constant should match fakeNetNumber in stack.go.
|
||||
fakeNetNumber tcpip.NetworkProtocolNumber = math.MaxUint32
|
||||
fakeNetHeaderLen = 12
|
||||
fakeDefaultPrefixLen = 8
|
||||
@@ -1825,7 +1828,7 @@ func TestNICForwarding(t *testing.T) {
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
|
||||
})
|
||||
s.SetForwarding(true)
|
||||
s.SetForwarding(fakeNetNumber, true)
|
||||
|
||||
ep1 := channel.New(10, defaultMTU, "")
|
||||
if err := s.CreateNIC(1, ep1); err != nil {
|
||||
|
||||
@@ -528,7 +528,7 @@ func TestTransportForwarding(t *testing.T) {
|
||||
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
|
||||
TransportProtocols: []stack.TransportProtocol{fakeTransFactory()},
|
||||
})
|
||||
s.SetForwarding(true)
|
||||
s.SetForwarding(fakeNetNumber, true)
|
||||
|
||||
// TODO(b/123449044): Change this to a channel NIC.
|
||||
ep1 := loopback.New()
|
||||
|
||||
@@ -326,6 +326,42 @@ TEST(ProcNetSnmp, UdpIn) {
|
||||
EXPECT_EQ(oldInDatagrams, newInDatagrams - 1);
|
||||
}
|
||||
|
||||
TEST(ProcSysNetIpv4IpForward, Exists) {
|
||||
auto fd =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/sys/net/ipv4/ip_forward", O_RDWR));
|
||||
}
|
||||
|
||||
TEST(ProcSysNetIpv4IpForward, DefaultValueEqZero) {
|
||||
auto const fd =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/sys/net/ipv4/ip_forward", O_RDWR));
|
||||
|
||||
char buf = 101;
|
||||
EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0),
|
||||
SyscallSucceedsWithValue(sizeof(buf)));
|
||||
|
||||
EXPECT_TRUE(buf == '0') << "unexpected ip_forward: " << buf;
|
||||
}
|
||||
|
||||
TEST(ProcSysNetIpv4IpForward, CanReadAndWrite) {
|
||||
auto const fd =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/sys/net/ipv4/ip_forward", O_RDWR));
|
||||
|
||||
char buf = 101;
|
||||
EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0),
|
||||
SyscallSucceedsWithValue(sizeof(buf)));
|
||||
|
||||
EXPECT_TRUE(buf == '0') << "unexpected ip_forward: " << buf;
|
||||
|
||||
constexpr char to_write = '1';
|
||||
EXPECT_THAT(PwriteFd(fd.get(), &to_write, sizeof(to_write), 0),
|
||||
SyscallSucceedsWithValue(sizeof(to_write)));
|
||||
|
||||
buf = 101;
|
||||
EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0),
|
||||
SyscallSucceedsWithValue(sizeof(buf)));
|
||||
EXPECT_EQ(buf, to_write);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
|
||||
Reference in New Issue
Block a user