Repository: moby/moby
File: libnetwork/service_linux.go (Go)

Code before:
				return fmt.Errorf("failed to add return rule to filter table ingress chain: %v", err)
			}
		}

		for _, chain := range []string{"OUTPUT", "PREROUTING"} {
			if !iptable.Exists(iptables.Nat, chain, "-m", "addrtype", "--dst-type", "LOCAL", "-j", ingressChain) {
				if err := iptable.RawCombinedOutput("-t", "nat", "-I", chain, "-m", "addrtype", "--dst-type", "LOCAL", "-j", ingressChain); err != nil {
					return fmt.Errorf("failed to add jump rule in %s to ingress chain: %v", chain, err)
				}
			}
		}

		if !iptable.Exists(iptables.Filter, "FORWARD", "-j", ingressChain) {
			if err := iptable.RawCombinedOutput("-I", "FORWARD", "-j", ingressChain); err != nil {
				return fmt.Errorf("failed to add jump rule to %s in filter table forward chain: %v", ingressChain, err)
			}
			// The jump to DOCKER-USER needs to be before the jump to DOCKER-INGRESS.
			if err := setupUserChain(iptables.IPv4); err != nil {
				log.G(context.TODO()).Warnf("Failed to restore "+userChain+" after creating "+ingressChain+": %v", err)
			}
		}

		oifName, err := findOIFName(gwIP)
		if err != nil {
			return fmt.Errorf("failed to find gateway bridge interface name for %s: %v", gwIP, err)
		}

		path := filepath.Join("/proc/sys/net/ipv4/conf", oifName, "route_localnet")
		if err := os.WriteFile(path, []byte{'1', '\n'}, 0o644); err != nil { //nolint:gosec // gosec complains about perms here, which must be 0644 in this case
			return fmt.Errorf("could not write to %s: %v", path, err)
		}

		ruleArgs := []string{"-m", "addrtype", "--src-type", "LOCAL", "-o", oifName, "-j", "MASQUERADE"}
		if !iptable.Exists(iptables.Nat, "POSTROUTING", ruleArgs...) {
			if err := iptable.RawCombinedOutput(append([]string{"-t", "nat", "-I", "POSTROUTING"}, ruleArgs...)...); err != nil {
				return fmt.Errorf("failed to add ingress localhost POSTROUTING rule for %s: %v", oifName, err)
			}
		}
	}

	// Filter the ingress ports until port rules start to be added/deleted
	filteredPorts := filterPortConfigs(ingressPorts, isDelete)
	rollbackRules := make([][]string, 0, len(filteredPorts)*3)
	var portErr error
	defer func() {
		if portErr != nil && !isDelete {
			filterPortConfigs(filteredPorts, !isDelete)
			for _, rule := range rollbackRules {
				if err := iptable.RawCombinedOutput(rule...); err != nil {
					log.G(context.TODO()).Warnf("roll back rule failed, %v: %v", rule, err)
				}

Review comment (suggestion):
VERY unlikely to be relevant in this spot and _no need to update_ 👈 , but Go (GoDoc) has some extra semantics for "special" comments (most notably `Deprecated:` but ISTR also to some extent `TODO` / `FIXME` comments); to prevent such comments from being considered part of the comment itself, they expect there to be an empty (comment-)line before them, and for them to be last in the comment block, e.g.;

```suggestion
		// the daemon older than 28.0.1.
		//
		// FIXME(robmry) - should only do this once, on startup.
		if iptable.Exists(iptables.Filter, "FORWARD", "-j", ingressChain) {
```

In this case it unlikely matters as it's a comment internal to the function (which Go itself doesn't really bother with), but I try to stick to that convention when writing these, also to make sure IDEs can spot them (which tend to try to follow Go conventions, although usually are slightly more permissive).

Code after:
			if err := iptable.RawCombinedOutput("-A", ingressChain, "-j", "RETURN"); err != nil {
				return fmt.Errorf("failed to add return rule to filter table ingress chain: %v", err)
			}
		}

		for _, chain := range []string{"OUTPUT", "PREROUTING"} {
			if !iptable.Exists(iptables.Nat, chain, "-m", "addrtype", "--dst-type", "LOCAL", "-j", ingressChain) {
				if err := iptable.RawCombinedOutput("-t", "nat", "-I", chain, "-m", "addrtype", "--dst-type", "LOCAL", "-j", ingressChain); err != nil {
					return fmt.Errorf("failed to add jump rule in %s to ingress chain: %v", chain, err)
				}
			}
		}

		// The DOCKER-FORWARD chain is created by the bridge driver on startup. It's a stable place to
		// put the jump to DOCKER-INGRESS (nothing else will ever be inserted before it, and the jump
		// will precede the bridge driver's other rules).
		if !iptable.Exists(iptables.Filter, bridge.DockerForwardChain, "-j", ingressChain) {
			if err := iptable.RawCombinedOutput("-I", bridge.DockerForwardChain, "-j", ingressChain); err != nil {
				return fmt.Errorf("failed to add jump rule to %s in filter table %s chain: %v",
					ingressChain, bridge.DockerForwardChain, err)
			}
		}
		// Remove the jump from FORWARD to DOCKER-INGRESS, if it was created there by a version of
		// the daemon older than 28.0.1.
		// FIXME(robmry) - should only do this once, on startup.
		if iptable.Exists(iptables.Filter, "FORWARD", "-j", ingressChain) {
			if err := iptable.RawCombinedOutput("-D", "FORWARD", "-j", ingressChain); err != nil {
				log.G(context.TODO()).WithError(err).Debug("Failed to delete jump from FORWARD to " + ingressChain)
			}
		}

		oifName, err := findOIFName(gwIP)
		if err != nil {
			return fmt.Errorf("failed to find gateway bridge interface name for %s: %v", gwIP, err)
		}

		path := filepath.Join("/proc/sys/net/ipv4/conf", oifName, "route_localnet")
		if err := os.WriteFile(path, []byte{'1', '\n'}, 0o644); err != nil { //nolint:gosec // gosec complains about perms here, which must be 0644 in this case
			return fmt.Errorf("could not write to %s: %v", path, err)
		}

		ruleArgs := []string{"-m", "addrtype", "--src-type", "LOCAL", "-o", oifName, "-j", "MASQUERADE"}
		if !iptable.Exists(iptables.Nat, "POSTROUTING", ruleArgs...) {
			if err := iptable.RawCombinedOutput(append([]string{"-t", "nat", "-I", "POSTROUTING"}, ruleArgs...)...); err != nil {
				return fmt.Errorf("failed to add ingress localhost POSTROUTING rule for %s: %v", oifName, err)
			}
		}
	}

	// Filter the ingress ports until port rules start to be added/deleted
	filteredPorts := filterPortConfigs(ingressPorts, isDelete)