diff --git a/pkg/apis/types.go b/pkg/apis/types.go index ceb0f526..0825d5dc 100644 --- a/pkg/apis/types.go +++ b/pkg/apis/types.go @@ -41,6 +41,10 @@ type InterfaceConfig struct { // to be assigned to the interface. Addresses []string `json:"addresses,omitempty"` + // DHCP, if true, indicates that the interface should be configured via DHCP. + // This is mutually exclusive with the 'addresses' field. + DHCP *bool `json:"dhcp,omitempty"` + // MTU is the Maximum Transmission Unit for the interface. MTU *int32 `json:"mtu,omitempty"` diff --git a/pkg/apis/validation.go b/pkg/apis/validation.go index 970742b7..010d78b5 100644 --- a/pkg/apis/validation.go +++ b/pkg/apis/validation.go @@ -128,6 +128,10 @@ func validateInterfaceConfig(cfg *InterfaceConfig, fieldPath string) (allErrors } } + if cfg.DHCP != nil && *cfg.DHCP && len(cfg.Addresses) > 0 { + allErrors = append(allErrors, fmt.Errorf("%s: dhcp and addresses are mutually exclusive", fieldPath)) + } + if cfg.MTU != nil { if *cfg.MTU < MinMTU { allErrors = append(allErrors, fmt.Errorf("%s.mtu: must be at least %d, got %d", fieldPath, MinMTU, *cfg.MTU)) diff --git a/pkg/apis/validation_test.go b/pkg/apis/validation_test.go index ae718365..0691f0b1 100644 --- a/pkg/apis/validation_test.go +++ b/pkg/apis/validation_test.go @@ -253,6 +253,25 @@ func TestValidateInterfaceConfig(t *testing.T) { expectErr: true, errCount: 1, }, + { + name: "valid with dhcp", + cfg: &InterfaceConfig{Name: "eth0", DHCP: ptr.To(true)}, + fieldPath: "iface", + expectErr: false, + }, + { + name: "invalid with dhcp and addresses", + cfg: &InterfaceConfig{Name: "eth0", DHCP: ptr.To(true), Addresses: []string{"10.0.0.1/24"}}, + fieldPath: "iface", + expectErr: true, + errCount: 1, + }, + { + name: "valid with dhcp false and addresses", + cfg: &InterfaceConfig{Name: "eth0", DHCP: ptr.To(false), Addresses: []string{"10.0.0.1/24"}}, + fieldPath: "iface", + expectErr: false, + }, { name: "multiple errors", cfg: &InterfaceConfig{Name: "eth/0", Addresses: []string{"badip"}, MTU: ptr.To[int32](0)}, diff --git a/pkg/driver/dhcp.go b/pkg/driver/dhcp.go index 39504c52..50e73210 100644 --- a/pkg/driver/dhcp.go +++ b/pkg/driver/dhcp.go @@ -27,7 +27,7 @@ import ( "github.com/vishvananda/netlink" ) -func getDHCP(ifName string) (ip string, routes []apis.RouteConfig, err error) { +func getDHCP(ctx context.Context, ifName string) (ip string, routes []apis.RouteConfig, err error) { link, err := netlink.LinkByName(ifName) if err != nil { return "", nil, err @@ -43,7 +43,7 @@ func getDHCP(ifName string) (ip string, routes []apis.RouteConfig, err error) { } defer dhclient.Close() - lease, err := dhclient.Request(context.Background()) + lease, err := dhclient.Request(ctx) if err != nil { return "", nil, fmt.Errorf("fail to obtain DHCP lease on interface %s up: %v", ifName, err) } diff --git a/pkg/driver/dra_hooks.go b/pkg/driver/dra_hooks.go index 8f368706..61bffb73 100644 --- a/pkg/driver/dra_hooks.go +++ b/pkg/driver/dra_hooks.go @@ -166,12 +166,25 @@ func (np *NetworkDriver) prepareResourceClaim(ctx context.Context, claim *resour podCfg.Network.Interface.Name = ifName } - // If there is no custom addresses then use the existing ones - if len(podCfg.Network.Interface.Addresses) == 0 { + // If DHCP is requested, do a DHCP request to gather the network parameters (IPs and Routes) + // ... but we DO NOT apply them in the root namespace + if podCfg.Network.Interface.DHCP != nil && *podCfg.Network.Interface.DHCP { + klog.V(2).Infof("trying to get network configuration via DHCP") + contextCancel, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + ip, routes, err := getDHCP(contextCancel, ifName) + if err != nil { + errorList = append(errorList, fmt.Errorf("fail to get configuration via DHCP for %s: %w", ifName, err)) + } else { + podCfg.Network.Interface.Addresses = []string{ip} + podCfg.Network.Routes = append(podCfg.Network.Routes, routes...) + } + } else if len(podCfg.Network.Interface.Addresses) == 0 { + // If there is no custom addresses and no DHCP, then use the existing ones // get the existing IP addresses nlAddresses, err := nlHandle.AddrList(link, netlink.FAMILY_ALL) if err != nil && !errors.Is(err, netlink.ErrDumpInterrupted) { - klog.Infof("fail to get ip addresses for interface %s : %v", ifName, err) + errorList = append(errorList, fmt.Errorf("fail to get ip addresses for interface %s : %w", ifName, err)) } else { for _, address := range nlAddresses { // Only move IP addresses with global scope because those are not host-specific, auto-configured, @@ -185,21 +198,6 @@ func (np *NetworkDriver) prepareResourceClaim(ctx context.Context, claim *resour } } - // If there are no addresses configured on the interface and the user is not setting them - // this may be an interface that uses DHCP, so we bring it up if necessary and do a DHCP - // request to gather the network parameters (IPs and Routes) ... but we DO NOT apply them - // in the root namespace - if len(podCfg.Network.Interface.Addresses) == 0 { - klog.V(2).Infof("trying to get network configuration via DHCP") - ip, routes, err := getDHCP(ifName) - if err != nil { - klog.Infof("fail to get configuration via DHCP: %v", err) - } else { - podCfg.Network.Interface.Addresses = []string{ip} - podCfg.Network.Routes = append(podCfg.Network.Routes, routes...) - } - } - // Obtain the existing supported ethtool features and validate the config if podCfg.Network.Ethtool != nil { client, err := newEthtoolClient(0)