From 629aac1078bdeed63214c58d2110fe672d654774 Mon Sep 17 00:00:00 2001
From: yangfeiyu <yangfeiyu2@huawei.com>
Date: Mon, 26 Oct 2020 20:39:05 +0800
Subject: [PATCH] kata-agent: update nic in guest

reason: add linkByName and support retry of list ip link, because in some scenarios,
the hardware address cannot be queried immediately after the hot plug.

Signed-off-by: yangfeiyu <yangfeiyu2@huawei.com>
---
 network.go | 91 ++++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 72 insertions(+), 19 deletions(-)

diff --git a/network.go b/network.go
index f6e2c17..d928e2b 100644
--- a/network.go
+++ b/network.go
@@ -15,13 +15,14 @@ import (
 	"strings"
 	"sync"
 	"syscall"
+	"time"
 
-	"golang.org/x/sys/unix"
 	agentNet "github.com/kata-containers/agent/pkg/net"
 	"github.com/kata-containers/agent/pkg/types"
 	pb "github.com/kata-containers/agent/protocols/grpc"
 	"github.com/sirupsen/logrus"
 	"github.com/vishvananda/netlink"
+	"golang.org/x/sys/unix"
 	"google.golang.org/grpc/codes"
 	grpcStatus "google.golang.org/grpc/status"
 )
@@ -32,6 +33,7 @@ var (
 	errNoLink               = grpcStatus.Errorf(codes.InvalidArgument, "Need network link")
 	errNoMAC                = grpcStatus.Errorf(codes.InvalidArgument, "Need hardware address")
 	errNoRoutes             = grpcStatus.Errorf(codes.InvalidArgument, "Need network routes")
+	errNoName               = grpcStatus.Errorf(codes.InvalidArgument, "Need network name")
 	guestDNSFile            = "/etc/resolv.conf"
 	kataGuestSandboxDNSFile = "/run/kata-containers/sandbox/resolv.conf"
 )
@@ -46,6 +48,8 @@ const (
 
 	// Use the below address for ipv6 gateway once ipv6 support is added
 	// defaultV6RouteIP = "::"
+
+	maxLinkRetries = 10
 )
 
 // Network fully describes a sandbox network with its interfaces, routes and dns
@@ -100,17 +104,36 @@ func linkByHwAddr(netHandle *netlink.Handle, hwAddr string) (netlink.Link, error
 	return nil, grpcStatus.Errorf(codes.NotFound, "Could not find the link corresponding to HwAddr %q", hwAddr)
 }
 
-func updateLink(netHandle *netlink.Handle, link netlink.Link, iface *types.Interface) error {
+func linkByName(netHandle *netlink.Handle, name string) (netlink.Link, error) {
 	if netHandle == nil {
-		return errNoHandle
+		return nil, errNoHandle
 	}
 
-	if link == nil {
-		return errNoLink
+	if name == "" {
+		return nil, errNoName
 	}
 
-	if iface == nil {
-		return errNoIF
+	links, err := netHandle.LinkList()
+	if err != nil {
+		return nil, err
+	}
+
+	for _, link := range links {
+		if link == nil {
+			continue
+		}
+
+		if link.Attrs() != nil && link.Attrs().Name == name {
+			return link, nil
+		}
+	}
+
+	return nil, grpcStatus.Errorf(codes.NotFound, "Could not find the link corresponding to name %s", name)
+}
+
+func updateLinkIP(netHandle *netlink.Handle, link netlink.Link, iface *types.Interface) error {
+	if len(iface.IPAddresses) == 0 {
+		return nil
 	}
 
 	// As a first step, clear out any existing addresses associated with the link:
@@ -129,13 +152,6 @@ func updateLink(netHandle *netlink.Handle, link netlink.Link, iface *types.Inter
 		netlinkAddrStr := fmt.Sprintf("%s/%s", addr.Address, addr.Mask)
 		netlinkAddr, err := netlink.ParseAddr(netlinkAddrStr)
 
-		// With ipv6 addresses, there is a brief period during which the address is marked as "tentative"
-		// making it unavailable. A process called duplicate address detection(DAD) is performed during this period.
-		// Disble DAD so that networking is available once the container is up. The assumption is
-		// that it is the reponsibility of the upper stack to make sure the addresses assigned to containers
-		// do not conflict. A similar operation is performed by libnetwork:
-		// https://github.com/moby/moby/issues/18871
-
 		if addr.GetFamily() == types.IPFamily_v6 {
 			netlinkAddr.Flags = netlinkAddr.Flags | syscall.IFA_F_NODAD
 		}
@@ -150,14 +166,36 @@ func updateLink(netHandle *netlink.Handle, link netlink.Link, iface *types.Inter
 		}
 	}
 
+	return nil
+}
+
+func updateLink(netHandle *netlink.Handle, link netlink.Link, iface *types.Interface) error {
+	if netHandle == nil {
+		return errNoHandle
+	}
+
+	if link == nil {
+		return errNoLink
+	}
+
+	if iface == nil {
+		return errNoIF
+	}
+
+	if err := updateLinkIP(netHandle, link, iface); err != nil {
+		return err
+	}
+
 	// set the interface name:
 	if err := netHandle.LinkSetName(link, iface.Name); err != nil {
 		return grpcStatus.Errorf(codes.Internal, "Could not set name %s for interface %v: %v", iface.Name, link, err)
 	}
 
 	// set the interface MTU:
-	if err := netHandle.LinkSetMTU(link, int(iface.Mtu)); err != nil {
-		return grpcStatus.Errorf(codes.Internal, "Could not set MTU %d for interface %v: %v", iface.Mtu, link, err)
+	if iface.Mtu > 0 {
+		if err := netHandle.LinkSetMTU(link, int(iface.Mtu)); err != nil {
+			return grpcStatus.Errorf(codes.Internal, "Could not set MTU %d for interface %v: %v", iface.Mtu, link, err)
+		}
 	}
 
 	if iface.RawFlags&unix.IFF_NOARP == uint32(unix.IFF_NOARP) {
@@ -306,8 +344,23 @@ func (s *sandbox) updateInterface(netHandle *netlink.Handle, iface *types.Interf
 	if iface.HwAddr != "" {
 		fieldLogger.Info("Getting interface from MAC address")
 
-		// Find the interface link from its hardware address.
-		link, err = linkByHwAddr(netHandle, iface.HwAddr)
+		// In some scenarios, the hardware address cannot be queried immediately
+		// after the hot plug, add a retry here.
+		for retry := 0; retry < maxLinkRetries; retry++ {
+			// Find the interface link from its hardware address.
+			link, err = linkByHwAddr(netHandle, iface.HwAddr)
+			if err != nil {
+				time.Sleep(100 * time.Millisecond)
+				continue
+			}
+			break
+		}
+		if err != nil {
+			return nil, grpcStatus.Errorf(codes.Internal, "updateInterface: %v", err)
+		}
+	} else if iface.Name != "" {
+		fieldLogger.Info("Getting interface from name")
+		link, err = linkByName(netHandle, iface.Name)
 		if err != nil {
 			return nil, grpcStatus.Errorf(codes.Internal, "updateInterface: %v", err)
 		}
@@ -487,7 +540,7 @@ func (s *sandbox) updateRoutes(netHandle *netlink.Handle, requestedRoutes *pb.Ro
 	}()
 
 	var (
-		added []*types.Route
+		added   []*types.Route
 		removed []*types.Route
 	)
 
-- 
2.23.0

