blob: 9b90acfac2c122121ecdfc0501af0a32301b13be [file] [log] [blame]
package bridge
import (
"errors"
"fmt"
"io/ioutil"
"os"
"syscall"
"github.com/sirupsen/logrus"
)
// Enumeration type saying which versions of IP protocol to process.
type ipVersion int
const (
ipvnone ipVersion = iota
ipv4
ipv6
ipvboth
)
//Gets the IP version in use ( [ipv4], [ipv6] or [ipv4 and ipv6] )
func getIPVersion(config *networkConfiguration) ipVersion {
ipVersion := ipv4
if config.AddressIPv6 != nil || config.EnableIPv6 {
ipVersion |= ipv6
}
return ipVersion
}
func setupBridgeNetFiltering(config *networkConfiguration, i *bridgeInterface) error {
err := checkBridgeNetFiltering(config, i)
if err != nil {
if ptherr, ok := err.(*os.PathError); ok {
if errno, ok := ptherr.Err.(syscall.Errno); ok && errno == syscall.ENOENT {
if isRunningInContainer() {
logrus.Warnf("running inside docker container, ignoring missing kernel params: %v", err)
err = nil
} else {
err = errors.New("please ensure that br_netfilter kernel module is loaded")
}
}
}
if err != nil {
return fmt.Errorf("cannot restrict inter-container communication: %v", err)
}
}
return nil
}
//Enable bridge net filtering if ip forwarding is enabled. See github issue #11404
func checkBridgeNetFiltering(config *networkConfiguration, i *bridgeInterface) error {
ipVer := getIPVersion(config)
iface := config.BridgeName
doEnable := func(ipVer ipVersion) error {
var ipVerName string
if ipVer == ipv4 {
ipVerName = "IPv4"
} else {
ipVerName = "IPv6"
}
enabled, err := isPacketForwardingEnabled(ipVer, iface)
if err != nil {
logrus.Warnf("failed to check %s forwarding: %v", ipVerName, err)
} else if enabled {
enabled, err := getKernelBoolParam(getBridgeNFKernelParam(ipVer))
if err != nil || enabled {
return err
}
return setKernelBoolParam(getBridgeNFKernelParam(ipVer), true)
}
return nil
}
switch ipVer {
case ipv4, ipv6:
return doEnable(ipVer)
case ipvboth:
v4err := doEnable(ipv4)
v6err := doEnable(ipv6)
if v4err == nil {
return v6err
}
return v4err
default:
return nil
}
}
// Get kernel param path saying whether IPv${ipVer} traffic is being forwarded
// on particular interface. Interface may be specified for IPv6 only. If
// `iface` is empty, `default` will be assumed, which represents default value
// for new interfaces.
func getForwardingKernelParam(ipVer ipVersion, iface string) string {
switch ipVer {
case ipv4:
return "/proc/sys/net/ipv4/ip_forward"
case ipv6:
if iface == "" {
iface = "default"
}
return fmt.Sprintf("/proc/sys/net/ipv6/conf/%s/forwarding", iface)
default:
return ""
}
}
// Get kernel param path saying whether bridged IPv${ipVer} traffic shall be
// passed to ip${ipVer}tables' chains.
func getBridgeNFKernelParam(ipVer ipVersion) string {
switch ipVer {
case ipv4:
return "/proc/sys/net/bridge/bridge-nf-call-iptables"
case ipv6:
return "/proc/sys/net/bridge/bridge-nf-call-ip6tables"
default:
return ""
}
}
//Gets the value of the kernel parameters located at the given path
func getKernelBoolParam(path string) (bool, error) {
enabled := false
line, err := ioutil.ReadFile(path)
if err != nil {
return false, err
}
if len(line) > 0 {
enabled = line[0] == '1'
}
return enabled, err
}
//Sets the value of the kernel parameter located at the given path
func setKernelBoolParam(path string, on bool) error {
value := byte('0')
if on {
value = byte('1')
}
return ioutil.WriteFile(path, []byte{value, '\n'}, 0644)
}
//Checks to see if packet forwarding is enabled
func isPacketForwardingEnabled(ipVer ipVersion, iface string) (bool, error) {
switch ipVer {
case ipv4, ipv6:
return getKernelBoolParam(getForwardingKernelParam(ipVer, iface))
case ipvboth:
enabled, err := getKernelBoolParam(getForwardingKernelParam(ipv4, ""))
if err != nil || !enabled {
return enabled, err
}
return getKernelBoolParam(getForwardingKernelParam(ipv6, iface))
default:
return true, nil
}
}
func isRunningInContainer() bool {
_, err := os.Stat("/.dockerenv")
return !os.IsNotExist(err)
}