diff --git a/common/libnetwork/netavark/config.go b/common/libnetwork/netavark/config.go index aa98831d26..297338ffbf 100644 --- a/common/libnetwork/netavark/config.go +++ b/common/libnetwork/netavark/config.go @@ -209,7 +209,11 @@ func (n *netavarkNetwork) networkCreate(newNetwork *types.Network, defaultNet bo return nil, err } default: - return nil, fmt.Errorf("unsupported driver %s: %w", newNetwork.Driver, types.ErrInvalidArg) + net, err := n.createPlugin(newNetwork) + if err != nil { + return nil, err + } + newNetwork = net } // when we do not have ipam we must disable dns @@ -403,3 +407,55 @@ func validateIPAMDriver(n *types.Network) error { } return nil } + +var errInvalidPluginResult = errors.New("invalid plugin result") + +func (n *netavarkNetwork) createPlugin(net *types.Network) (*types.Network, error) { + path, err := getPlugin(net.Driver, n.pluginDirs) + if err != nil { + return nil, err + } + result := new(types.Network) + err = n.execPlugin(path, []string{"create"}, net, result) + if err != nil { + return nil, fmt.Errorf("plugin %s failed: %w", path, err) + } + // now make sure that neither the name, ID, driver were changed by the plugin + if net.Name != result.Name { + return nil, fmt.Errorf("%w: changed network name", errInvalidPluginResult) + } + if net.ID != result.ID { + return nil, fmt.Errorf("%w: changed network ID", errInvalidPluginResult) + } + if net.Driver != result.Driver { + return nil, fmt.Errorf("%w: changed network driver", errInvalidPluginResult) + } + return result, nil +} + +func getAllPlugins(dirs []string) []string { + var plugins []string + for _, dir := range dirs { + entries, err := os.ReadDir(dir) + if err == nil { + for _, entry := range entries { + name := entry.Name() + if !util.StringInSlice(name, plugins) { + plugins = append(plugins, name) + } + } + } + } + return plugins +} + +func getPlugin(name string, dirs []string) (string, error) { + for _, dir := range dirs { + fullpath := filepath.Join(dir, name) + st, err := os.Stat(fullpath) + if err == nil && st.Mode().IsRegular() { + return fullpath, nil + } + } + return "", fmt.Errorf("failed to find driver or plugin %q", name) +} diff --git a/common/libnetwork/netavark/config_test.go b/common/libnetwork/netavark/config_test.go index 51bd7365ab..fff934605d 100644 --- a/common/libnetwork/netavark/config_test.go +++ b/common/libnetwork/netavark/config_test.go @@ -925,7 +925,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("unsupported driver someDriver")) + Expect(err.Error()).To(ContainSubstring(`failed to find driver or plugin "someDriver"`)) }) It("network create internal and dns", func() { diff --git a/common/libnetwork/netavark/exec.go b/common/libnetwork/netavark/exec.go index 93c0ac364a..7ce085bca6 100644 --- a/common/libnetwork/netavark/exec.go +++ b/common/libnetwork/netavark/exec.go @@ -77,6 +77,23 @@ func getRustLogEnv() string { // All errors return by this function should be of the type netavarkError // to provide a helpful error message. func (n *netavarkNetwork) execNetavark(args []string, stdin, result interface{}) error { + // set the netavark log level to the same as the podman + env := append(os.Environ(), getRustLogEnv()) + // if we run with debug log level lets also set RUST_BACKTRACE=1 so we can get the full stack trace in case of panics + if logrus.IsLevelEnabled(logrus.DebugLevel) { + env = append(env, "RUST_BACKTRACE=1") + } + if n.dnsBindPort != 0 { + env = append(env, "NETAVARK_DNS_PORT="+strconv.Itoa(int(n.dnsBindPort))) + } + return n.execBinary(n.netavarkBinary, append(n.getCommonNetavarkOptions(), args...), stdin, result, env) +} + +func (n *netavarkNetwork) execPlugin(path string, args []string, stdin, result interface{}) error { + return n.execBinary(path, args, stdin, result, nil) +} + +func (n *netavarkNetwork) execBinary(path string, args []string, stdin, result interface{}, env []string) error { stdinR, stdinW, err := os.Pipe() if err != nil { return newNetavarkError("failed to create stdin pipe", err) @@ -108,20 +125,12 @@ func (n *netavarkNetwork) execNetavark(args []string, stdin, result interface{}) logWriter = io.MultiWriter(logWriter, &logrusNetavarkWriter{}) } - cmd := exec.Command(n.netavarkBinary, append(n.getCommonNetavarkOptions(), args...)...) + cmd := exec.Command(path, args...) // connect the pipes to stdin and stdout cmd.Stdin = stdinR cmd.Stdout = stdoutW cmd.Stderr = logWriter - // set the netavark log level to the same as the podman - cmd.Env = append(os.Environ(), getRustLogEnv()) - // if we run with debug log level lets also set RUST_BACKTRACE=1 so we can get the full stack trace in case of panics - if logrus.IsLevelEnabled(logrus.DebugLevel) { - cmd.Env = append(cmd.Env, "RUST_BACKTRACE=1") - } - if n.dnsBindPort != 0 { - cmd.Env = append(cmd.Env, "NETAVARK_DNS_PORT="+strconv.Itoa(int(n.dnsBindPort))) - } + cmd.Env = env err = cmd.Start() if err != nil { diff --git a/common/libnetwork/netavark/network.go b/common/libnetwork/netavark/network.go index 3e7306c727..a1fc166f81 100644 --- a/common/libnetwork/netavark/network.go +++ b/common/libnetwork/netavark/network.go @@ -46,6 +46,9 @@ type netavarkNetwork struct { // dnsBindPort is set the the port to pass to netavark for aardvark dnsBindPort uint16 + // pluginDirs list of directories were netavark plugins are located + pluginDirs []string + // ipamDBPath is the path to the ip allocation bolt db ipamDBPath string @@ -86,6 +89,9 @@ type InitConfig struct { // DNSBindPort is set the the port to pass to netavark for aardvark DNSBindPort uint16 + // PluginDirs list of directories were netavark plugins are located + PluginDirs []string + // Syslog describes whenever the netavark debbug output should be log to the syslog as well. // This will use logrus to do so, make sure logrus is set up to log to the syslog. Syslog bool @@ -143,6 +149,7 @@ func NewNetworkInterface(conf *InitConfig) (types.ContainerNetwork, error) { defaultSubnet: defaultNet, defaultsubnetPools: defaultSubnetPools, dnsBindPort: conf.DNSBindPort, + pluginDirs: conf.PluginDirs, lock: lock, syslog: conf.Syslog, } @@ -153,7 +160,8 @@ func NewNetworkInterface(conf *InitConfig) (types.ContainerNetwork, error) { // Drivers will return the list of supported network drivers // for this interface. func (n *netavarkNetwork) Drivers() []string { - return []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver} + paths := getAllPlugins(n.pluginDirs) + return append([]string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver}, paths...) } // DefaultNetworkName will return the default netavark network name.