// +build linux 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) }