//go:build linux
package bridge
import (
"context"
"errors"
"fmt"
"os"
"github.com/containerd/log"
"github.com/moby/moby/v2/daemon/libnetwork/drivers/bridge/internal/firewaller"
)
const (
ipv4ForwardConf = "/proc/sys/net/ipv4/ip_forward"
ipv6ForwardConfDefault = "/proc/sys/net/ipv6/conf/default/forwarding"
ipv6ForwardConfAll = "/proc/sys/net/ipv6/conf/all/forwarding"
)
type filterForwardDropper interface {
FilterForwardDrop(context.Context, firewaller.IPVersion) error
}
func checkIPv4Forwarding() error {
enabled, err := getKernelBoolParam(ipv4ForwardConf)
if err != nil {
return fmt.Errorf("checking IPv4 forwarding: %w", err)
}
if enabled {
return nil
}
// It's the user's responsibility to enable forwarding and secure their host. Or,
// start docker with --ip-forward=false to disable this check.
return errors.New("IPv4 forwarding is disabled: check your host's firewalling and set sysctl net.ipv4.ip_forward=1, or disable this check using daemon option --ip-forward=false")
}
func setupIPv4Forwarding(ffd filterForwardDropper, wantFilterForwardDrop bool) (retErr error) {
changed, err := configureIPForwarding(ipv4ForwardConf, '1')
if err != nil {
return err
}
if changed {
defer func() {
if retErr != nil {
if _, err := configureIPForwarding(ipv4ForwardConf, '0'); err != nil {
log.G(context.TODO()).WithError(err).Error("Cannot disable IPv4 forwarding")
}
}
}()
}
// When enabling ip_forward set the default policy on forward chain to drop.
if changed && wantFilterForwardDrop {
if err := ffd.FilterForwardDrop(context.TODO(), firewaller.IPv4); err != nil {
return err
}
}
return nil
}
func checkIPv6Forwarding() error {
enabledDef, err := getKernelBoolParam(ipv6ForwardConfDefault)
if err != nil {
return fmt.Errorf("checking IPv6 default forwarding: %w", err)
}
enabledAll, err := getKernelBoolParam(ipv6ForwardConfAll)
if err != nil {
return fmt.Errorf("checking IPv6 global forwarding: %w", err)
}
if enabledDef && enabledAll {
return nil
}
// It's the user's responsibility to enable forwarding and secure their host. Or,
// start docker with --ip-forward=false to disable this check.
return errors.New("IPv6 global forwarding is disabled: check your host's firewalling and set sysctls net.ipv6.conf.all.forwarding=1 and net.ipv6.conf.default.forwarding=1, or disable this check using daemon option --ip-forward=false")
}
func setupIPv6Forwarding(ffd filterForwardDropper, wantFilterForwardDrop bool) (retErr error) {
// Set IPv6 default.forwarding, if needed.
// Setting "all" (below) sets "default" as well, but need to check that "default" is
// set even if "all" is already set.
changedDef, err := configureIPForwarding(ipv6ForwardConfDefault, '1')
if err != nil {
return err
}
if changedDef {
defer func() {
if retErr != nil {
if _, err := configureIPForwarding(ipv6ForwardConfDefault, '0'); err != nil {
log.G(context.TODO()).WithError(err).Error("Cannot disable IPv6 default.forwarding")
}
}
}()
}
// Set IPv6 all.forwarding, if needed.
changedAll, err := configureIPForwarding(ipv6ForwardConfAll, '1')
if err != nil {
return err
}
if changedAll {
defer func() {
if retErr != nil {
if _, err := configureIPForwarding(ipv6ForwardConfAll, '0'); err != nil {
log.G(context.TODO()).WithError(err).Error("Cannot disable IPv6 all.forwarding")
}
}
}()
}
if (changedAll || changedDef) && wantFilterForwardDrop {
if err := ffd.FilterForwardDrop(context.TODO(), firewaller.IPv6); err != nil {
return err
}
}
return nil
}
func configureIPForwarding(file string, val byte) (changed bool, _ error) {
data, err := os.ReadFile(file)
if err != nil || len(data) == 0 {
return false, fmt.Errorf("cannot read IP forwarding setup from '%s': %w", file, err)
}
if len(data) == 0 {
return false, fmt.Errorf("cannot read IP forwarding setup from '%s': 0 bytes", file)
}
if data[0] == val {
return false, nil
}
if err := os.WriteFile(file, []byte{val, '\n'}, 0o644); err != nil {
return false, fmt.Errorf("failed to set IP forwarding '%s' = '%c': %w", file, val, err)
}
return true, nil
}