diff --git a/dhcp/dhcp_linux.go b/dhcp/dhcp_linux.go index 9e7a029c05..2c3499d074 100644 --- a/dhcp/dhcp_linux.go +++ b/dhcp/dhcp_linux.go @@ -10,6 +10,8 @@ import ( "encoding/binary" "io" "net" + "net/http" + "strings" "time" "github.com/pkg/errors" @@ -37,6 +39,9 @@ const ( hops = 0 secs = 0 flags = 0x8000 // Broadcast flag + + nmAgentSupportedApisURL = "http://168.63.129.16/machine/plugins/?comp=nmagent&type=GetSupportedApis" + dhcpRehydrationAPIStr = "SwiftV2DhcpRehydrationFromGoalState" // Set value provided by host ) // TransactionID represents a 4-byte DHCP transaction ID as defined in RFC 951, @@ -49,6 +54,8 @@ var ( magicCookie = []byte{0x63, 0x82, 0x53, 0x63} // DHCP magic cookie DefaultReadTimeout = 3 * time.Second DefaultTimeout = 3 * time.Second + + errResponseNotOK = errors.New("not ok http status") ) type DHCP struct { @@ -459,3 +466,37 @@ func (c *DHCP) DiscoverRequest(ctx context.Context, mac net.HardwareAddr, ifname res := c.receiveDHCPResponse(ctx, reader, txid) return res } + +// DHCPRehydrationFeatureOnHost queries the host to check if a particular API is supported, +// and if so, returns true; if we are unable to determine if the feature is enabled, an error +// is returned +func (c *DHCP) DHCPRehydrationFeatureOnHost(ctx context.Context) (bool, error) { + var ( + resp *http.Response + httpClient = &http.Client{Timeout: DefaultTimeout} + ) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, nmAgentSupportedApisURL, http.NoBody) + if err != nil { + return false, errors.Wrap(err, "failed to create http request") + } + req.Header.Add("Metadata", "true") + req.Header.Add("x-ms-version", "2012-11-30") + resp, err = httpClient.Do(req) + if err != nil { + return false, errors.Wrap(err, "error issuing http request") + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return false, errResponseNotOK + } + readBytes, err := io.ReadAll(resp.Body) + if err != nil { + return false, errors.Wrap(err, "failed to read response body") + } + str := string(readBytes) + if !strings.Contains(str, dhcpRehydrationAPIStr) { + return false, nil + } + + return true, nil +} diff --git a/dhcp/dhcp_windows.go b/dhcp/dhcp_windows.go index 7b23dbeeff..d40c01da32 100644 --- a/dhcp/dhcp_windows.go +++ b/dhcp/dhcp_windows.go @@ -20,3 +20,7 @@ func New(logger *zap.Logger) *DHCP { func (c *DHCP) DiscoverRequest(_ context.Context, _ net.HardwareAddr, _ string) error { return nil } + +func (c *DHCP) DHCPRehydrationFeatureOnHost(_ context.Context) (bool, error) { + return false, nil +} diff --git a/network/dhcp.go b/network/dhcp.go index 82be4bd977..58430731a1 100644 --- a/network/dhcp.go +++ b/network/dhcp.go @@ -7,10 +7,15 @@ import ( type dhcpClient interface { DiscoverRequest(context.Context, net.HardwareAddr, string) error + DHCPRehydrationFeatureOnHost(context.Context) (bool, error) } type mockDHCP struct{} -func (netns *mockDHCP) DiscoverRequest(context.Context, net.HardwareAddr, string) error { +func (d *mockDHCP) DiscoverRequest(context.Context, net.HardwareAddr, string) error { return nil } + +func (d *mockDHCP) DHCPRehydrationFeatureOnHost(context.Context) (bool, error) { + return false, nil +} diff --git a/network/secondary_endpoint_client_linux.go b/network/secondary_endpoint_client_linux.go index 4a41373e7e..f34f0e90ed 100644 --- a/network/secondary_endpoint_client_linux.go +++ b/network/secondary_endpoint_client_linux.go @@ -138,10 +138,19 @@ func (client *SecondaryEndpointClient) ConfigureContainerInterfacesAndRoutes(epI timeout := time.Duration(numSecs) * time.Second ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(timeout)) defer cancel() - logger.Info("Sending DHCP packet", zap.Any("macAddress", epInfo.MacAddress), zap.String("ifName", epInfo.IfName)) - err := client.dhcpClient.DiscoverRequest(ctx, epInfo.MacAddress, epInfo.IfName) + + // check if the permanent fix on the host has already rolled out + dhcpRehydrationFeatureEnabled, err := client.dhcpClient.DHCPRehydrationFeatureOnHost(ctx) if err != nil { - return errors.Wrapf(err, "failed to issue dhcp discover packet to create mapping in host") + return errors.Wrap(err, "could not determine if dhcp rehydration feature enabled on host") + } + // only send the dhcp packet if the host does not have the permanent fix + if !dhcpRehydrationFeatureEnabled { + logger.Info("Sending DHCP packet", zap.Any("macAddress", epInfo.MacAddress), zap.String("ifName", epInfo.IfName)) + err := client.dhcpClient.DiscoverRequest(ctx, epInfo.MacAddress, epInfo.IfName) + if err != nil { + return errors.Wrap(err, "failed to issue dhcp discover packet to create mapping in host") + } } logger.Info("Finished configuring container interfaces and routes for secondary endpoint client")