|
@@ -0,0 +1,162 @@
|
|
|
|
+package bridge
|
|
|
|
+
|
|
|
|
+import (
|
|
|
|
+ "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.FixedCIDRv6 != 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 = fmt.Errorf("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("/.dockerinit")
|
|
|
|
+ return !os.IsNotExist(err)
|
|
|
|
+}
|