Browse code

Move internal/modprobe to daemon/libnetwork/internal/modprobe

Signed-off-by: Derek McGowan <derek@mcg.dev>

Derek McGowan authored on 2025/07/15 01:25:55
Showing 5 changed files
... ...
@@ -8,8 +8,8 @@ import (
8 8
 
9 9
 	"github.com/containerd/log"
10 10
 	"github.com/docker/docker/daemon/libnetwork/drivers/bridge/internal/firewaller"
11
+	"github.com/docker/docker/daemon/libnetwork/internal/modprobe"
11 12
 	"github.com/docker/docker/daemon/libnetwork/iptables"
12
-	"github.com/docker/docker/internal/modprobe"
13 13
 )
14 14
 
15 15
 const (
... ...
@@ -10,7 +10,7 @@ import (
10 10
 	"syscall"
11 11
 
12 12
 	"github.com/containerd/log"
13
-	"github.com/docker/docker/internal/modprobe"
13
+	"github.com/docker/docker/daemon/libnetwork/internal/modprobe"
14 14
 )
15 15
 
16 16
 // setupIPv4BridgeNetFiltering checks whether IPv4 forwarding is enabled and, if
17 17
new file mode 100644
... ...
@@ -0,0 +1,110 @@
0
+// Package modprobe attempts to load kernel modules. It may have more success
1
+// than simply running "modprobe", particularly for docker-in-docker.
2
+package modprobe
3
+
4
+import (
5
+	"context"
6
+	"errors"
7
+	"fmt"
8
+	"os/exec"
9
+	"strings"
10
+
11
+	"github.com/containerd/log"
12
+	"golang.org/x/sys/unix"
13
+)
14
+
15
+// LoadModules attempts to load kernel modules, if necessary.
16
+//
17
+// isLoaded must be a function that checks whether the modules are loaded. It may
18
+// be called multiple times. isLoaded must return an error to indicate that the
19
+// modules still need to be loaded, otherwise nil.
20
+//
21
+// For each method of loading modules, LoadModules will attempt the load for each
22
+// of modNames, then it will call isLoaded to check the result - moving on to try
23
+// the next method if needed, and there is one.
24
+//
25
+// The returned error is the result of the final call to isLoaded.
26
+func LoadModules(ctx context.Context, isLoaded func() error, modNames ...string) error {
27
+	if isLoaded() == nil {
28
+		log.G(ctx).WithFields(log.Fields{
29
+			"modules": modNames,
30
+		}).Debug("Modules already loaded")
31
+		return nil
32
+	}
33
+
34
+	if err := tryLoad(ctx, isLoaded, modNames, ioctlLoader{}); err != nil {
35
+		return tryLoad(ctx, isLoaded, modNames, modprobeLoader{})
36
+	}
37
+	return nil
38
+}
39
+
40
+type loader interface {
41
+	name() string
42
+	load(modName string) error
43
+}
44
+
45
+func tryLoad(ctx context.Context, isLoaded func() error, modNames []string, loader loader) error {
46
+	var loadErrs []error
47
+	for _, modName := range modNames {
48
+		if err := loader.load(modName); err != nil {
49
+			loadErrs = append(loadErrs, err)
50
+		}
51
+	}
52
+
53
+	if checkResult := isLoaded(); checkResult != nil {
54
+		log.G(ctx).WithFields(log.Fields{
55
+			"loader":      loader.name(),
56
+			"modules":     modNames,
57
+			"loadErrors":  errors.Join(loadErrs...),
58
+			"checkResult": checkResult,
59
+		}).Debug("Modules not loaded")
60
+		return checkResult
61
+	}
62
+
63
+	log.G(ctx).WithFields(log.Fields{
64
+		"loader":     loader.name(),
65
+		"modules":    modNames,
66
+		"loadErrors": errors.Join(loadErrs...),
67
+	}).Debug("Modules loaded")
68
+	return nil
69
+}
70
+
71
+// ioctlLoader attempts to load the module using an ioctl() to get the interface index
72
+// of a module - it won't have one, but the kernel may load the module. This tends to
73
+// work in docker-in-docker, where the inner-docker may not have "modprobe" or access
74
+// to modules in the host's filesystem.
75
+type ioctlLoader struct{}
76
+
77
+func (il ioctlLoader) name() string { return "ioctl" }
78
+
79
+func (il ioctlLoader) load(modName string) error {
80
+	sd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
81
+	if err != nil {
82
+		return fmt.Errorf("creating socket for ioctl load of %s: %w", modName, err)
83
+	}
84
+	defer unix.Close(sd)
85
+
86
+	// This tends to work, if running with CAP_SYS_MODULE, because...
87
+	//   https://github.com/torvalds/linux/blob/6f7da290413ba713f0cdd9ff1a2a9bb129ef4f6c/net/core/dev_ioctl.c#L457
88
+	//   https://github.com/torvalds/linux/blob/6f7da290413ba713f0cdd9ff1a2a9bb129ef4f6c/net/core/dev_ioctl.c#L371-L372
89
+	ifreq, err := unix.NewIfreq(modName)
90
+	if err != nil {
91
+		return fmt.Errorf("creating ifreq for %s: %w", modName, err)
92
+	}
93
+	// An error is returned even if the module load is successful. So, ignore it.
94
+	_ = unix.IoctlIfreq(sd, unix.SIOCGIFINDEX, ifreq)
95
+	return nil
96
+}
97
+
98
+// modprobeLoader attempts to load a kernel module using modprobe.
99
+type modprobeLoader struct{}
100
+
101
+func (ml modprobeLoader) name() string { return "modprobe" }
102
+
103
+func (ml modprobeLoader) load(modName string) error {
104
+	out, err := exec.Command("modprobe", "-va", modName).CombinedOutput()
105
+	if err != nil {
106
+		return fmt.Errorf("modprobe %s failed with message: %q, error: %w", modName, strings.TrimSpace(string(out)), err)
107
+	}
108
+	return nil
109
+}
... ...
@@ -7,7 +7,7 @@ import (
7 7
 	"time"
8 8
 
9 9
 	"github.com/containerd/log"
10
-	"github.com/docker/docker/internal/modprobe"
10
+	"github.com/docker/docker/daemon/libnetwork/internal/modprobe"
11 11
 	"github.com/docker/docker/internal/nlwrap"
12 12
 	"github.com/vishvananda/netns"
13 13
 )
14 14
deleted file mode 100644
... ...
@@ -1,110 +0,0 @@
1
-// Package modprobe attempts to load kernel modules. It may have more success
2
-// than simply running "modprobe", particularly for docker-in-docker.
3
-package modprobe
4
-
5
-import (
6
-	"context"
7
-	"errors"
8
-	"fmt"
9
-	"os/exec"
10
-	"strings"
11
-
12
-	"github.com/containerd/log"
13
-	"golang.org/x/sys/unix"
14
-)
15
-
16
-// LoadModules attempts to load kernel modules, if necessary.
17
-//
18
-// isLoaded must be a function that checks whether the modules are loaded. It may
19
-// be called multiple times. isLoaded must return an error to indicate that the
20
-// modules still need to be loaded, otherwise nil.
21
-//
22
-// For each method of loading modules, LoadModules will attempt the load for each
23
-// of modNames, then it will call isLoaded to check the result - moving on to try
24
-// the next method if needed, and there is one.
25
-//
26
-// The returned error is the result of the final call to isLoaded.
27
-func LoadModules(ctx context.Context, isLoaded func() error, modNames ...string) error {
28
-	if isLoaded() == nil {
29
-		log.G(ctx).WithFields(log.Fields{
30
-			"modules": modNames,
31
-		}).Debug("Modules already loaded")
32
-		return nil
33
-	}
34
-
35
-	if err := tryLoad(ctx, isLoaded, modNames, ioctlLoader{}); err != nil {
36
-		return tryLoad(ctx, isLoaded, modNames, modprobeLoader{})
37
-	}
38
-	return nil
39
-}
40
-
41
-type loader interface {
42
-	name() string
43
-	load(modName string) error
44
-}
45
-
46
-func tryLoad(ctx context.Context, isLoaded func() error, modNames []string, loader loader) error {
47
-	var loadErrs []error
48
-	for _, modName := range modNames {
49
-		if err := loader.load(modName); err != nil {
50
-			loadErrs = append(loadErrs, err)
51
-		}
52
-	}
53
-
54
-	if checkResult := isLoaded(); checkResult != nil {
55
-		log.G(ctx).WithFields(log.Fields{
56
-			"loader":      loader.name(),
57
-			"modules":     modNames,
58
-			"loadErrors":  errors.Join(loadErrs...),
59
-			"checkResult": checkResult,
60
-		}).Debug("Modules not loaded")
61
-		return checkResult
62
-	}
63
-
64
-	log.G(ctx).WithFields(log.Fields{
65
-		"loader":     loader.name(),
66
-		"modules":    modNames,
67
-		"loadErrors": errors.Join(loadErrs...),
68
-	}).Debug("Modules loaded")
69
-	return nil
70
-}
71
-
72
-// ioctlLoader attempts to load the module using an ioctl() to get the interface index
73
-// of a module - it won't have one, but the kernel may load the module. This tends to
74
-// work in docker-in-docker, where the inner-docker may not have "modprobe" or access
75
-// to modules in the host's filesystem.
76
-type ioctlLoader struct{}
77
-
78
-func (il ioctlLoader) name() string { return "ioctl" }
79
-
80
-func (il ioctlLoader) load(modName string) error {
81
-	sd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
82
-	if err != nil {
83
-		return fmt.Errorf("creating socket for ioctl load of %s: %w", modName, err)
84
-	}
85
-	defer unix.Close(sd)
86
-
87
-	// This tends to work, if running with CAP_SYS_MODULE, because...
88
-	//   https://github.com/torvalds/linux/blob/6f7da290413ba713f0cdd9ff1a2a9bb129ef4f6c/net/core/dev_ioctl.c#L457
89
-	//   https://github.com/torvalds/linux/blob/6f7da290413ba713f0cdd9ff1a2a9bb129ef4f6c/net/core/dev_ioctl.c#L371-L372
90
-	ifreq, err := unix.NewIfreq(modName)
91
-	if err != nil {
92
-		return fmt.Errorf("creating ifreq for %s: %w", modName, err)
93
-	}
94
-	// An error is returned even if the module load is successful. So, ignore it.
95
-	_ = unix.IoctlIfreq(sd, unix.SIOCGIFINDEX, ifreq)
96
-	return nil
97
-}
98
-
99
-// modprobeLoader attempts to load a kernel module using modprobe.
100
-type modprobeLoader struct{}
101
-
102
-func (ml modprobeLoader) name() string { return "modprobe" }
103
-
104
-func (ml modprobeLoader) load(modName string) error {
105
-	out, err := exec.Command("modprobe", "-va", modName).CombinedOutput()
106
-	if err != nil {
107
-		return fmt.Errorf("modprobe %s failed with message: %q, error: %w", modName, strings.TrimSpace(string(out)), err)
108
-	}
109
-	return nil
110
-}