diff --git a/cmd/routed-eni-cni-plugin/cni.go b/cmd/routed-eni-cni-plugin/cni.go
index bfecb979c3..25ac9332b0 100644
--- a/cmd/routed-eni-cni-plugin/cni.go
+++ b/cmd/routed-eni-cni-plugin/cni.go
@@ -53,6 +53,10 @@ const dummyInterfacePrefix = "dummy"
 
 const npAgentConnTimeout = 2
 
+const multiNICPodAnnotation = "k8s.amazonaws.com/nicConfig"
+const multiNICAttachment = "multi-nic-attachment"
+const containerVethNamePrefix = "mNicIf"
+
 var version string
 
 // NetConf stores the common network config for the CNI plugin
@@ -72,7 +76,12 @@ type NetConf struct {
 
 	PluginLogFile string `json:"pluginLogFile"`
 
-	PluginLogLevel string `json:"pluginLogLevel"`
+	PluginLogLevel string        `json:"pluginLogLevel"`
+	RuntimeConfig  RuntimeConfig `json:"runtimeConfig"`
+}
+
+type RuntimeConfig struct {
+	PodAnnotations map[string]string `json:"io.kubernetes.cri.pod-annotations"`
 }
 
 // K8sArgs is the valid CNI_ARGS used for Kubernetes
@@ -153,6 +162,20 @@ func add(args *skel.CmdArgs, cniTypes typeswrapper.CNITYPES, grpcClient grpcwrap
 	mtu := networkutils.GetPodMTU(conf.MTU)
 	log.Debugf("MTU value set is %d:", mtu)
 
+	// Derive if Pod requires multiple NIC connected
+	requiresMultiNICAttachment := false
+
+	if value, ok := conf.RuntimeConfig.PodAnnotations[multiNICPodAnnotation]; ok {
+		if value == multiNICAttachment {
+			requiresMultiNICAttachment = true
+		} else {
+			log.Debugf("Multi-NIC annotation mismatch: found=%q, required=%q. Falling back to default configuration.",
+				multiNICAttachment, conf.RuntimeConfig.PodAnnotations[multiNICPodAnnotation])
+		}
+	}
+
+	log.Debugf("pod requires Multi-NIC attachment: %t", requiresMultiNICAttachment)
+
 	// Set up a connection to the ipamD server.
 	conn, err := grpcClient.Dial(ipamdAddress, grpc.WithTransportCredentials(insecure.NewCredentials()))
 	if err != nil {
@@ -174,6 +197,7 @@ func add(args *skel.CmdArgs, cniTypes typeswrapper.CNITYPES, grpcClient grpcwrap
 			ContainerID:                args.ContainerID,
 			NetworkName:                conf.Name,
 			IfName:                     args.IfName,
+			RequiresMultiNICAttachment: requiresMultiNICAttachment,
 		})
 
 	if err != nil {
@@ -191,47 +215,70 @@ func add(args *skel.CmdArgs, cniTypes typeswrapper.CNITYPES, grpcClient grpcwrap
 		args.ContainerID, args.IfName, r)
 
 	// We will let the values in result struct guide us in terms of IP Address Family configured.
-	var v4Addr, v6Addr, addr *net.IPNet
 
-	// We don't support dual stack mode currently so it has to be either v4 or v6 mode.
-	if r.IPv4Addr != "" {
-		v4Addr = &net.IPNet{
-			IP:   net.ParseIP(r.IPv4Addr),
-			Mask: net.CIDRMask(32, 32),
-		}
-		addr = v4Addr
-	} else if r.IPv6Addr != "" {
-		v6Addr = &net.IPNet{
-			IP:   net.ParseIP(r.IPv6Addr),
-			Mask: net.CIDRMask(128, 128),
-		}
-		addr = v6Addr
-	}
 	// AddNetwork guarantees that Gateway string is a valid IPNet
 	gw := net.ParseIP(r.PodENISubnetGW)
 
-	var hostVethName string
+	var vethMetadata []driver.VirtualInterfaceMetadata
+	var podIPs []*current.IPConfig
+	var podInterfaces []*current.Interface
 	var dummyInterface *current.Interface
 
+	for index, ip := range r.IPAddress {
+
+		var hostVethName string
+
+		addr := parseIPAddress(ip)
+		if r.PodVlanId != 0 {
+			hostVethNamePrefix := sgpp.BuildHostVethNamePrefix(conf.VethPrefix, conf.PodSGEnforcingMode)
+			hostVethName = networkutils.GeneratePodHostVethName(hostVethNamePrefix, string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME), index)
+		} else {
+			hostVethName = networkutils.GeneratePodHostVethName(conf.VethPrefix, string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME), index)
+		}
+
+		containerVethName := networkutils.GenerateContainerVethName(args.IfName, containerVethNamePrefix, index)
+
+		vethMetadata = append(vethMetadata, driver.VirtualInterfaceMetadata{
+			IPAddress:         addr,
+			DeviceNumber:      int(ip.DeviceNumber),
+			RouteTable:        int(ip.RouteTableId),
+			HostVethName:      hostVethName,
+			ContainerVethName: containerVethName,
+		})
+
+		// Check if we can use PCIID to store RT-ID ?
+		// The RT here is the host route table Id not the container
+		podInterfaces = append(podInterfaces,
+			&current.Interface{Name: hostVethName},
+			&current.Interface{Name: containerVethName, Sandbox: args.Netns, PciID: fmt.Sprint(ip.RouteTableId)},
+		)
+
+		// This index always points to the container interface so that we get the IP address corresponding to the interface
+		containerInterfaceIndex := len(podInterfaces) - 1
+
+		podIPs = append(podIPs, &current.IPConfig{
+			Interface: &containerInterfaceIndex,
+			Address:   *addr,
+			Gateway:   gw,
+		})
+	}
+
 	// The dummy interface is purely virtual and is stored in the prevResult struct to assist in cleanup during the DEL command.
-	dummyInterfaceName := networkutils.GeneratePodHostVethName(dummyInterfacePrefix, string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME))
+	dummyInterfaceName := networkutils.GeneratePodHostVethName(dummyInterfacePrefix, string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME), 0)
 
 	// Non-zero value means pods are using branch ENI
 	if r.PodVlanId != 0 {
-		hostVethNamePrefix := sgpp.BuildHostVethNamePrefix(conf.VethPrefix, conf.PodSGEnforcingMode)
-		hostVethName = networkutils.GeneratePodHostVethName(hostVethNamePrefix, string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME))
-		err = driverClient.SetupBranchENIPodNetwork(hostVethName, args.IfName, args.Netns, v4Addr, v6Addr, int(r.PodVlanId), r.PodENIMAC,
+		// Since we always return 1 IP, it should be okay to directly address index 0
+		err = driverClient.SetupBranchENIPodNetwork(vethMetadata[0], args.Netns, int(r.PodVlanId), r.PodENIMAC,
 			r.PodENISubnetGW, int(r.ParentIfIndex), mtu, conf.PodSGEnforcingMode, log)
 		// For branch ENI mode, the pod VLAN ID is packed in Interface.Mac
 		dummyInterface = &current.Interface{Name: dummyInterfaceName, Mac: fmt.Sprint(r.PodVlanId)}
 	} else {
-		// build hostVethName
-		// Note: the maximum length for linux interface name is 15
-		hostVethName = networkutils.GeneratePodHostVethName(conf.VethPrefix, string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME))
-		err = driverClient.SetupPodNetwork(hostVethName, args.IfName, args.Netns, v4Addr, v6Addr, int(r.DeviceNumber), mtu, log)
+		err = driverClient.SetupPodNetwork(vethMetadata, args.Netns, mtu, log)
 		// For non-branch ENI, the pod VLAN ID value of 0 is packed in Interface.Mac, while the interface device number is packed in Interface.Sandbox
-		dummyInterface = &current.Interface{Name: dummyInterfaceName, Mac: fmt.Sprint(0), Sandbox: fmt.Sprint(r.DeviceNumber)}
+		dummyInterface = &current.Interface{Name: dummyInterfaceName, Mac: fmt.Sprint(0), Sandbox: fmt.Sprint(vethMetadata[0].DeviceNumber), SocketPath: fmt.Sprint(len(r.IPAddress))}
 	}
+
 	log.Debugf("Using dummy interface: %v", dummyInterface)
 
 	if err != nil {
@@ -258,24 +305,9 @@ func add(args *skel.CmdArgs, cniTypes typeswrapper.CNITYPES, grpcClient grpcwrap
 		return errors.Wrap(err, "add command: failed to setup network")
 	}
 
-	containerInterfaceIndex := 1
-	ips := []*current.IPConfig{
-		{
-			Interface: &containerInterfaceIndex,
-			Address:   *addr,
-			Gateway:   gw,
-		},
-	}
-
-	hostInterface := &current.Interface{Name: hostVethName}
-	containerInterface := &current.Interface{Name: args.IfName, Sandbox: args.Netns}
-
 	result := &current.Result{
-		IPs: ips,
-		Interfaces: []*current.Interface{
-			hostInterface,
-			containerInterface,
-		},
+		IPs:        podIPs,
+		Interfaces: podInterfaces,
 	}
 
 	// dummy interface is appended to PrevResult for use during cleanup
@@ -302,6 +334,7 @@ func add(args *skel.CmdArgs, cniTypes typeswrapper.CNITYPES, grpcClient grpcwrap
 			K8S_POD_NAME:        string(k8sArgs.K8S_POD_NAME),
 			K8S_POD_NAMESPACE:   string(k8sArgs.K8S_POD_NAMESPACE),
 			NETWORK_POLICY_MODE: r.NetworkPolicyMode,
+			InterfaceCount:      int32(len(r.IPAddress)),
 		})
 
 	// No need to cleanup IP and network, kubelet will send delete.
@@ -338,6 +371,10 @@ func del(args *skel.CmdArgs, cniTypes typeswrapper.CNITYPES, grpcClient grpcwrap
 		log.Errorf("Failed to load k8s config from args: %v", err)
 		return errors.Wrap(err, "del cmd: failed to load k8s config from args")
 	}
+	// During del we cannot depend on the annotation as annotation change doesn't restart a POD
+	// We can add it to the dummy interface and use that during the deletion flow
+	// Or Pass the number of interfaces so that it is returned by prev result.
+	// OR store this information in ipamd datastore so that it always returns what is assigned.
 
 	// For pods using branch ENI, try to delete using previous result
 	handled, err := tryDelWithPrevResult(driverClient, conf, k8sArgs, args.IfName, args.Netns, log)
@@ -415,21 +452,21 @@ func del(args *skel.CmdArgs, cniTypes typeswrapper.CNITYPES, grpcClient grpcwrap
 	log.Infof("Received del network response from ipamd for pod %s namespace %s sandbox %s: %+v", string(k8sArgs.K8S_POD_NAME),
 		string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_INFRA_CONTAINER_ID), r)
 
-	var deletedPodIP net.IP
-	var maskLen int
-	if r.IPv4Addr != "" {
-		deletedPodIP = net.ParseIP(r.IPv4Addr)
-		maskLen = 32
-	} else if r.IPv6Addr != "" {
-		deletedPodIP = net.ParseIP(r.IPv6Addr)
-		maskLen = 128
-	}
+	var vethMetadata []driver.VirtualInterfaceMetadata
+
+	for _, ip := range r.IPAddress {
+		addr := parseIPAddress(ip)
 
-	if deletedPodIP != nil {
-		addr := &net.IPNet{
-			IP:   deletedPodIP,
-			Mask: net.CIDRMask(maskLen, maskLen),
+		if addr != nil {
+			vethMetadata = append(vethMetadata, driver.VirtualInterfaceMetadata{
+				IPAddress:    addr,
+				DeviceNumber: int(ip.DeviceNumber),
+				RouteTable:   int(ip.RouteTableId),
+			})
 		}
+	}
+
+	if len(vethMetadata) > 0 {
 
 		// vlanID != 0 means pod using security group
 		if r.PodVlanId != 0 {
@@ -437,9 +474,9 @@ func del(args *skel.CmdArgs, cniTypes typeswrapper.CNITYPES, grpcClient grpcwrap
 				log.Infof("Ignoring TeardownPodENI as Netns is empty for SG pod:%s namespace: %s containerID:%s", k8sArgs.K8S_POD_NAME, k8sArgs.K8S_POD_NAMESPACE, k8sArgs.K8S_POD_INFRA_CONTAINER_ID)
 				return nil
 			}
-			err = driverClient.TeardownBranchENIPodNetwork(addr, int(r.PodVlanId), conf.PodSGEnforcingMode, log)
+			err = driverClient.TeardownBranchENIPodNetwork(vethMetadata[0], int(r.PodVlanId), conf.PodSGEnforcingMode, log)
 		} else {
-			err = driverClient.TeardownPodNetwork(addr, int(r.DeviceNumber), log)
+			err = driverClient.TeardownPodNetwork(vethMetadata, log)
 		}
 
 		if err != nil {
@@ -448,7 +485,7 @@ func del(args *skel.CmdArgs, cniTypes typeswrapper.CNITYPES, grpcClient grpcwrap
 			return errors.Wrap(err, "del cmd: failed on tear down pod network")
 		}
 	} else {
-		log.Warnf("Container %s did not have a valid IP %s", args.ContainerID, r.IPv4Addr)
+		log.Warnf("Container %s did not have a valid IP %+v", args.ContainerID, r.IPAddress)
 	}
 
 	// Set up a connection to the network policy agent
@@ -479,16 +516,16 @@ func del(args *skel.CmdArgs, cniTypes typeswrapper.CNITYPES, grpcClient grpcwrap
 	return nil
 }
 
-func getContainerIP(prevResult *current.Result, contVethName string) (net.IPNet, error) {
-	containerIfaceIndex, _, found := cniutils.FindInterfaceByName(prevResult.Interfaces, contVethName)
+func getContainerNetworkMetadata(prevResult *current.Result, contVethName string) (net.IPNet, *current.Interface, error) {
+	containerIfaceIndex, containerInterface, found := cniutils.FindInterfaceByName(prevResult.Interfaces, contVethName)
 	if !found {
-		return net.IPNet{}, errors.Errorf("cannot find contVethName %s in prevResult", contVethName)
+		return net.IPNet{}, nil, errors.Errorf("cannot find contVethName %s in prevResult", contVethName)
 	}
 	containerIPs := cniutils.FindIPConfigsByIfaceIndex(prevResult.IPs, containerIfaceIndex)
 	if len(containerIPs) != 1 {
-		return net.IPNet{}, errors.Errorf("found %d containerIPs for %v in prevResult", len(containerIPs), contVethName)
+		return net.IPNet{}, nil, errors.Errorf("found %d containerIPs for %v in prevResult", len(containerIPs), contVethName)
 	}
-	return containerIPs[0].Address, nil
+	return containerIPs[0].Address, containerInterface, nil
 }
 
 // tryDelWithPrevResult will try to process CNI delete request without IPAMD.
@@ -500,7 +537,7 @@ func tryDelWithPrevResult(driverClient driver.NetworkAPIs, conf *NetConf, k8sArg
 		return false, nil
 	}
 
-	dummyIfaceName := networkutils.GeneratePodHostVethName(dummyInterfacePrefix, string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME))
+	dummyIfaceName := networkutils.GeneratePodHostVethName(dummyInterfacePrefix, string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME), 0)
 	_, dummyIface, found := cniutils.FindInterfaceByName(prevResult.Interfaces, dummyIfaceName)
 	if !found {
 		return false, nil
@@ -518,12 +555,16 @@ func tryDelWithPrevResult(driverClient driver.NetworkAPIs, conf *NetConf, k8sArg
 		return true, nil
 	}
 
-	containerIP, err := getContainerIP(prevResult, contVethName)
+	containerIP, _, err := getContainerNetworkMetadata(prevResult, contVethName)
 	if err != nil {
 		return false, err
 	}
 
-	if err := driverClient.TeardownBranchENIPodNetwork(&containerIP, podVlanID, conf.PodSGEnforcingMode, log); err != nil {
+	vethMetadata := driver.VirtualInterfaceMetadata{
+		IPAddress: &containerIP,
+	}
+
+	if err := driverClient.TeardownBranchENIPodNetwork(vethMetadata, podVlanID, conf.PodSGEnforcingMode, log); err != nil {
 		return true, err
 	}
 	return true, nil
@@ -538,7 +579,7 @@ func teardownPodNetworkWithPrevResult(driverClient driver.NetworkAPIs, conf *Net
 		log.Infof("PrevResult not available for pod. Pod may have already been deleted.")
 		return false
 	}
-	dummyIfaceName := networkutils.GeneratePodHostVethName(dummyInterfacePrefix, string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME))
+	dummyIfaceName := networkutils.GeneratePodHostVethName(dummyInterfacePrefix, string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME), 0)
 	_, dummyIface, found := cniutils.FindInterfaceByName(prevResult.Interfaces, dummyIfaceName)
 	if !found {
 		return false
@@ -554,13 +595,48 @@ func teardownPodNetworkWithPrevResult(driverClient driver.NetworkAPIs, conf *Net
 		log.Errorf("Invalid device number for pod: %s", dummyIface.Sandbox)
 		return false
 	}
-	containerIP, err := getContainerIP(prevResult, contVethName)
-	if err != nil {
-		log.Errorf("Failed to get container IP: %v", err)
-		return false
+
+	// Since we are always using dummy interface to store the device number of network card 0 IP
+	// RT ID for NC-0 is also stored in the container interface entry. So we have a path for migration where
+	// getting the device number from dummy interface can be deprecated entirely. This is currently done to keep it backwards compatible
+	routeTableId := deviceNumber + 1
+
+	var interfacesAttached = 1
+	if dummyIface.SocketPath != "" {
+		interfacesAttached, err = strconv.Atoi(dummyIface.SocketPath)
+		if err != nil {
+			log.Errorf("error getting number of interfaces attached to the pod: %s", dummyIface.SocketPath)
+			return false
+		}
+	}
+
+	var vethMetadata []driver.VirtualInterfaceMetadata
+	for v := range interfacesAttached {
+		containerInterfaceName := networkutils.GenerateContainerVethName(contVethName, containerVethNamePrefix, v)
+		containerIP, containerInterface, err := getContainerNetworkMetadata(prevResult, containerInterfaceName)
+		if err != nil {
+			log.Errorf("Failed to get container IP: %v", err)
+			return false
+		}
+
+		// If this property is set, that means the container metadata has the route table ID which we can use
+		// If this is not set, it is a pod launched before this change was introduced.
+		// So it is only managing network card 0 at that time and device number + 1 is the route table ID which we have above
+		if dummyIface.SocketPath != "" {
+			routeTableId, err = strconv.Atoi(containerInterface.PciID)
+			if err != nil {
+				log.Errorf("error getting route table number of the interface %s", containerInterface.PciID)
+				return false
+			}
+		}
+
+		vethMetadata = append(vethMetadata, driver.VirtualInterfaceMetadata{
+			IPAddress:  &containerIP,
+			RouteTable: routeTableId,
+		})
 	}
 
-	if err := driverClient.TeardownPodNetwork(&containerIP, deviceNumber, log); err != nil {
+	if err := driverClient.TeardownPodNetwork(vethMetadata, log); err != nil {
 		log.Errorf("Failed to teardown pod network: %v", err)
 		return false
 	}
@@ -587,3 +663,20 @@ func main() {
 	}
 	os.Exit(exitCode)
 }
+
+func parseIPAddress(v *pb.IPAddress) *net.IPNet {
+
+	if v.IPv4Addr != "" {
+		return &net.IPNet{
+			IP:   net.ParseIP(v.IPv4Addr),
+			Mask: net.CIDRMask(32, 32),
+		}
+	} else if v.IPv6Addr != "" {
+		return &net.IPNet{
+			IP:   net.ParseIP(v.IPv6Addr),
+			Mask: net.CIDRMask(128, 128),
+		}
+	}
+
+	return nil
+}
diff --git a/cmd/routed-eni-cni-plugin/cni_test.go b/cmd/routed-eni-cni-plugin/cni_test.go
index 8954489fb4..dfc376b024 100644
--- a/cmd/routed-eni-cni-plugin/cni_test.go
+++ b/cmd/routed-eni-cni-plugin/cni_test.go
@@ -30,6 +30,7 @@ import (
 	"github.com/stretchr/testify/assert"
 	"google.golang.org/grpc"
 
+	"github.com/aws/amazon-vpc-cni-k8s/cmd/routed-eni-cni-plugin/driver"
 	mock_driver "github.com/aws/amazon-vpc-cni-k8s/cmd/routed-eni-cni-plugin/driver/mocks"
 	mock_grpcwrapper "github.com/aws/amazon-vpc-cni-k8s/pkg/grpcwrapper/mocks"
 	mock_rpcwrapper "github.com/aws/amazon-vpc-cni-k8s/pkg/rpcwrapper/mocks"
@@ -103,15 +104,16 @@ func TestCmdAdd(t *testing.T) {
 	enforceNpReply := &rpc.EnforceNpReply{Success: true}
 	mockNP.EXPECT().EnforceNpToPod(gomock.Any(), gomock.Any()).Return(enforceNpReply, nil).Times(1)
 
-	addNetworkReply := &rpc.AddNetworkReply{Success: true, IPv4Addr: ipAddr, DeviceNumber: devNum, NetworkPolicyMode: "none"}
+	addrs := []*rpc.IPAddress{&rpc.IPAddress{
+		IPv4Addr:     ipAddr,
+		DeviceNumber: devNum,
+		RouteTableId: devNum + 1,
+	}}
+
+	addNetworkReply := &rpc.AddNetworkReply{Success: true, IPAddress: addrs, NetworkPolicyMode: "none"}
 	mockC.EXPECT().AddNetwork(gomock.Any(), gomock.Any()).Return(addNetworkReply, nil)
 
-	v4Addr := &net.IPNet{
-		IP:   net.ParseIP(addNetworkReply.IPv4Addr),
-		Mask: net.IPv4Mask(255, 255, 255, 255),
-	}
-	mocksNetwork.EXPECT().SetupPodNetwork(gomock.Any(), cmdArgs.IfName, cmdArgs.Netns,
-		v4Addr, nil, int(addNetworkReply.DeviceNumber), gomock.Any(), gomock.Any()).Return(nil)
+	mocksNetwork.EXPECT().SetupPodNetwork(gomock.Any(), cmdArgs.Netns, gomock.Any(), gomock.Any()).Return(nil)
 
 	mocksTypes.EXPECT().PrintResult(gomock.Any(), gomock.Any()).Return(nil).Times(1)
 
@@ -144,18 +146,19 @@ func TestCmdAddWithNPenabled(t *testing.T) {
 	mockNP := mock_rpc.NewMockNPBackendClient(ctrl)
 	mocksRPC.EXPECT().NewNPBackendClient(npConn).Return(mockNP)
 
-	addNetworkReply := &rpc.AddNetworkReply{Success: true, IPv4Addr: ipAddr, DeviceNumber: devNum, NetworkPolicyMode: "strict"}
+	addrs := []*rpc.IPAddress{&rpc.IPAddress{
+		IPv4Addr:     ipAddr,
+		DeviceNumber: devNum,
+		RouteTableId: devNum + 1,
+	}}
+
+	addNetworkReply := &rpc.AddNetworkReply{Success: true, IPAddress: addrs, NetworkPolicyMode: "strict"}
 	mockC.EXPECT().AddNetwork(gomock.Any(), gomock.Any()).Return(addNetworkReply, nil)
 
 	enforceNpReply := &rpc.EnforceNpReply{Success: true}
 	mockNP.EXPECT().EnforceNpToPod(gomock.Any(), gomock.Any()).Return(enforceNpReply, nil)
 
-	v4Addr := &net.IPNet{
-		IP:   net.ParseIP(addNetworkReply.IPv4Addr),
-		Mask: net.IPv4Mask(255, 255, 255, 255),
-	}
-	mocksNetwork.EXPECT().SetupPodNetwork(gomock.Any(), cmdArgs.IfName, cmdArgs.Netns,
-		v4Addr, nil, int(addNetworkReply.DeviceNumber), gomock.Any(), gomock.Any()).Return(nil)
+	mocksNetwork.EXPECT().SetupPodNetwork(gomock.Any(), cmdArgs.Netns, gomock.Any(), gomock.Any()).Return(nil)
 
 	mocksTypes.EXPECT().PrintResult(gomock.Any(), gomock.Any()).Return(nil)
 
@@ -188,18 +191,19 @@ func TestCmdAddWithNPenabledWithErr(t *testing.T) {
 	mockNP := mock_rpc.NewMockNPBackendClient(ctrl)
 	mocksRPC.EXPECT().NewNPBackendClient(npConn).Return(mockNP)
 
-	addNetworkReply := &rpc.AddNetworkReply{Success: true, IPv4Addr: ipAddr, DeviceNumber: devNum, NetworkPolicyMode: "strict"}
+	addrs := []*rpc.IPAddress{&rpc.IPAddress{
+		IPv4Addr:     ipAddr,
+		DeviceNumber: devNum,
+		RouteTableId: devNum + 1,
+	}}
+
+	addNetworkReply := &rpc.AddNetworkReply{Success: true, IPAddress: addrs, NetworkPolicyMode: "strict"}
 	mockC.EXPECT().AddNetwork(gomock.Any(), gomock.Any()).Return(addNetworkReply, nil)
 
 	enforceNpReply := &rpc.EnforceNpReply{Success: false}
 	mockNP.EXPECT().EnforceNpToPod(gomock.Any(), gomock.Any()).Return(enforceNpReply, errors.New("Error on EnforceNpReply"))
 
-	v4Addr := &net.IPNet{
-		IP:   net.ParseIP(addNetworkReply.IPv4Addr),
-		Mask: net.IPv4Mask(255, 255, 255, 255),
-	}
-	mocksNetwork.EXPECT().SetupPodNetwork(gomock.Any(), cmdArgs.IfName, cmdArgs.Netns,
-		v4Addr, nil, int(addNetworkReply.DeviceNumber), gomock.Any(), gomock.Any()).Return(nil)
+	mocksNetwork.EXPECT().SetupPodNetwork(gomock.Any(), cmdArgs.Netns, gomock.Any(), gomock.Any()).Return(nil)
 
 	err := add(cmdArgs, mocksTypes, mocksGRPC, mocksRPC, mocksNetwork)
 	assert.Error(t, err)
@@ -224,7 +228,13 @@ func TestCmdAddNetworkErr(t *testing.T) {
 	mockC := mock_rpc.NewMockCNIBackendClient(ctrl)
 	mocksRPC.EXPECT().NewCNIBackendClient(conn).Return(mockC)
 
-	addNetworkReply := &rpc.AddNetworkReply{Success: false, IPv4Addr: ipAddr, DeviceNumber: devNum, NetworkPolicyMode: "none"}
+	addrs := []*rpc.IPAddress{&rpc.IPAddress{
+		IPv4Addr:     ipAddr,
+		DeviceNumber: devNum,
+		RouteTableId: devNum + 1,
+	}}
+
+	addNetworkReply := &rpc.AddNetworkReply{Success: false, IPAddress: addrs, NetworkPolicyMode: "none"}
 	mockC.EXPECT().AddNetwork(gomock.Any(), gomock.Any()).Return(addNetworkReply, errors.New("Error on AddNetworkReply"))
 
 	err := add(cmdArgs, mocksTypes, mocksGRPC, mocksRPC, mocksNetwork)
@@ -251,19 +261,18 @@ func TestCmdAddErrSetupPodNetwork(t *testing.T) {
 	mockC := mock_rpc.NewMockCNIBackendClient(ctrl)
 	mocksRPC.EXPECT().NewCNIBackendClient(conn).Return(mockC)
 
-	addNetworkReply := &rpc.AddNetworkReply{Success: true, IPv4Addr: ipAddr, DeviceNumber: devNum, NetworkPolicyMode: "none"}
+	addrs := []*rpc.IPAddress{&rpc.IPAddress{
+		IPv4Addr:     ipAddr,
+		DeviceNumber: devNum,
+		RouteTableId: devNum + 1,
+	}}
+	addNetworkReply := &rpc.AddNetworkReply{Success: true, IPAddress: addrs, NetworkPolicyMode: "none"}
 	mockC.EXPECT().AddNetwork(gomock.Any(), gomock.Any()).Return(addNetworkReply, nil)
 
-	addr := &net.IPNet{
-		IP:   net.ParseIP(addNetworkReply.IPv4Addr),
-		Mask: net.IPv4Mask(255, 255, 255, 255),
-	}
-
-	mocksNetwork.EXPECT().SetupPodNetwork(gomock.Any(), cmdArgs.IfName, cmdArgs.Netns,
-		addr, nil, int(addNetworkReply.DeviceNumber), gomock.Any(), gomock.Any()).Return(errors.New("error on SetupPodNetwork"))
+	mocksNetwork.EXPECT().SetupPodNetwork(gomock.Any(), cmdArgs.Netns, gomock.Any(), gomock.Any()).Return(errors.New("error on SetupPodNetwork"))
 
 	// when SetupPodNetwork fails, expect to return IP back to datastore
-	delNetworkReply := &rpc.DelNetworkReply{Success: true, IPv4Addr: ipAddr, DeviceNumber: devNum}
+	delNetworkReply := &rpc.DelNetworkReply{Success: true, IPAddress: addrs}
 	mockC.EXPECT().DelNetwork(gomock.Any(), gomock.Any()).Return(delNetworkReply, nil)
 
 	err := add(cmdArgs, mocksTypes, mocksGRPC, mocksRPC, mocksNetwork)
@@ -296,18 +305,19 @@ func TestCmdDel(t *testing.T) {
 	mockNP := mock_rpc.NewMockNPBackendClient(ctrl)
 	mocksRPC.EXPECT().NewNPBackendClient(npConn).Return(mockNP)
 
-	delNetworkReply := &rpc.DelNetworkReply{Success: true, IPv4Addr: ipAddr, DeviceNumber: devNum}
+	addrs := []*rpc.IPAddress{&rpc.IPAddress{
+		IPv4Addr:     ipAddr,
+		DeviceNumber: devNum,
+		RouteTableId: devNum + 1,
+	}}
+
+	delNetworkReply := &rpc.DelNetworkReply{Success: true, IPAddress: addrs}
 	mockC.EXPECT().DelNetwork(gomock.Any(), gomock.Any()).Return(delNetworkReply, nil)
 
 	deleteNpReply := &rpc.DeleteNpReply{Success: true}
 	mockNP.EXPECT().DeletePodNp(gomock.Any(), gomock.Any()).Return(deleteNpReply, nil)
 
-	addr := &net.IPNet{
-		IP:   net.ParseIP(delNetworkReply.IPv4Addr),
-		Mask: net.IPv4Mask(255, 255, 255, 255),
-	}
-
-	mocksNetwork.EXPECT().TeardownPodNetwork(addr, int(delNetworkReply.DeviceNumber), gomock.Any()).Return(nil)
+	mocksNetwork.EXPECT().TeardownPodNetwork(gomock.Any(), gomock.Any()).Return(nil)
 
 	err := del(cmdArgs, mocksTypes, mocksGRPC, mocksRPC, mocksNetwork)
 	assert.Nil(t, err)
@@ -332,7 +342,13 @@ func TestCmdDelErrDelNetwork(t *testing.T) {
 	mockC := mock_rpc.NewMockCNIBackendClient(ctrl)
 	mocksRPC.EXPECT().NewCNIBackendClient(conn).Return(mockC)
 
-	delNetworkReply := &rpc.DelNetworkReply{Success: false, IPv4Addr: ipAddr, DeviceNumber: devNum}
+	addrs := []*rpc.IPAddress{&rpc.IPAddress{
+		IPv4Addr:     ipAddr,
+		DeviceNumber: devNum,
+		RouteTableId: devNum + 1,
+	}}
+
+	delNetworkReply := &rpc.DelNetworkReply{Success: false, IPAddress: addrs}
 
 	mockC.EXPECT().DelNetwork(gomock.Any(), gomock.Any()).Return(delNetworkReply, errors.New("error on DelNetwork"))
 
@@ -359,17 +375,16 @@ func TestCmdDelErrTeardown(t *testing.T) {
 	mocksGRPC.EXPECT().Dial(gomock.Any(), gomock.Any()).Return(conn, nil)
 	mockC := mock_rpc.NewMockCNIBackendClient(ctrl)
 	mocksRPC.EXPECT().NewCNIBackendClient(conn).Return(mockC)
-
-	delNetworkReply := &rpc.DelNetworkReply{Success: true, IPv4Addr: ipAddr, DeviceNumber: devNum}
+	addrs := []*rpc.IPAddress{&rpc.IPAddress{
+		IPv4Addr:     ipAddr,
+		DeviceNumber: devNum,
+		RouteTableId: devNum + 1,
+	}}
+	delNetworkReply := &rpc.DelNetworkReply{Success: true, IPAddress: addrs}
 
 	mockC.EXPECT().DelNetwork(gomock.Any(), gomock.Any()).Return(delNetworkReply, nil)
 
-	addr := &net.IPNet{
-		IP:   net.ParseIP(delNetworkReply.IPv4Addr),
-		Mask: net.IPv4Mask(255, 255, 255, 255),
-	}
-
-	mocksNetwork.EXPECT().TeardownPodNetwork(addr, int(delNetworkReply.DeviceNumber), gomock.Any()).Return(errors.New("error on teardown"))
+	mocksNetwork.EXPECT().TeardownPodNetwork(gomock.Any(), gomock.Any()).Return(errors.New("error on teardown"))
 
 	err := del(cmdArgs, mocksTypes, mocksGRPC, mocksRPC, mocksNetwork)
 	assert.Error(t, err)
@@ -400,18 +415,17 @@ func TestCmdAddForPodENINetwork(t *testing.T) {
 	mockNP := mock_rpc.NewMockNPBackendClient(ctrl)
 	mocksRPC.EXPECT().NewNPBackendClient(npConn).Return(mockNP)
 
-	addNetworkReply := &rpc.AddNetworkReply{Success: true, IPv4Addr: ipAddr, PodENISubnetGW: "10.0.0.1", PodVlanId: 1,
+	addrs := []*rpc.IPAddress{&rpc.IPAddress{
+		IPv4Addr: ipAddr}}
+
+	addNetworkReply := &rpc.AddNetworkReply{Success: true, IPAddress: addrs, PodENISubnetGW: "10.0.0.1", PodVlanId: 1,
 		PodENIMAC: "eniHardwareAddr", ParentIfIndex: 2, NetworkPolicyMode: "none"}
 	mockC.EXPECT().AddNetwork(gomock.Any(), gomock.Any()).Return(addNetworkReply, nil)
 
 	enforceNpReply := &rpc.EnforceNpReply{Success: true}
 	mockNP.EXPECT().EnforceNpToPod(gomock.Any(), gomock.Any()).Return(enforceNpReply, nil)
 
-	addr := &net.IPNet{
-		IP:   net.ParseIP(addNetworkReply.IPv4Addr),
-		Mask: net.IPv4Mask(255, 255, 255, 255),
-	}
-	mocksNetwork.EXPECT().SetupBranchENIPodNetwork(gomock.Any(), cmdArgs.IfName, cmdArgs.Netns, addr, nil, 1, "eniHardwareAddr",
+	mocksNetwork.EXPECT().SetupBranchENIPodNetwork(gomock.Any(), cmdArgs.Netns, 1, "eniHardwareAddr",
 		"10.0.0.1", 2, gomock.Any(), sgpp.EnforcingModeStrict, gomock.Any()).Return(nil)
 
 	mocksTypes.EXPECT().PrintResult(gomock.Any(), gomock.Any()).Return(nil)
@@ -446,17 +460,19 @@ func TestCmdDelForPodENINetwork(t *testing.T) {
 	mockNP := mock_rpc.NewMockNPBackendClient(ctrl)
 	mocksRPC.EXPECT().NewNPBackendClient(npConn).Return(mockNP)
 
-	delNetworkReply := &rpc.DelNetworkReply{Success: true, IPv4Addr: ipAddr, PodVlanId: 1}
+	addrs := []*rpc.IPAddress{&rpc.IPAddress{
+		IPv4Addr:     ipAddr,
+		DeviceNumber: devNum,
+		RouteTableId: devNum + 1,
+	}}
+
+	delNetworkReply := &rpc.DelNetworkReply{Success: true, IPAddress: addrs, PodVlanId: 1}
 	mockC.EXPECT().DelNetwork(gomock.Any(), gomock.Any()).Return(delNetworkReply, nil)
 
 	deleteNpReply := &rpc.DeleteNpReply{Success: true}
 	mockNP.EXPECT().DeletePodNp(gomock.Any(), gomock.Any()).Return(deleteNpReply, nil)
 
-	addr := &net.IPNet{
-		IP:   net.ParseIP(delNetworkReply.IPv4Addr),
-		Mask: net.IPv4Mask(255, 255, 255, 255),
-	}
-	mocksNetwork.EXPECT().TeardownBranchENIPodNetwork(addr, 1, sgpp.EnforcingModeStrict, gomock.Any()).Return(nil)
+	mocksNetwork.EXPECT().TeardownBranchENIPodNetwork(gomock.Any(), 1, sgpp.EnforcingModeStrict, gomock.Any()).Return(nil)
 
 	err := del(cmdArgs, mocksTypes, mocksGRPC, mocksRPC, mocksNetwork)
 	assert.Nil(t, err)
@@ -843,7 +859,11 @@ func Test_tryDelWithPrevResult(t *testing.T) {
 
 			driverClient := mock_driver.NewMockNetworkAPIs(ctrl)
 			for _, call := range tt.fields.teardownBranchENIPodNetworkCalls {
-				driverClient.EXPECT().TeardownBranchENIPodNetwork(call.containerAddr, call.vlanID, call.podSGEnforcingMode, gomock.Any()).Return(call.err)
+				vethMetadata := driver.VirtualInterfaceMetadata{
+					IPAddress: call.containerAddr,
+				}
+
+				driverClient.EXPECT().TeardownBranchENIPodNetwork(vethMetadata, call.vlanID, call.podSGEnforcingMode, gomock.Any()).Return(call.err)
 			}
 
 			got, err := tryDelWithPrevResult(driverClient, tt.args.conf, tt.args.k8sArgs, tt.args.contVethName, "/proc/1/ns", testLogger)
@@ -1152,7 +1172,7 @@ func Test_teardownPodNetworkWithPrevResult(t *testing.T) {
 
 			driverClient := mock_driver.NewMockNetworkAPIs(ctrl)
 			for _, call := range tt.fields.teardownPodNetworkCalls {
-				driverClient.EXPECT().TeardownPodNetwork(call.containerAddr, call.deviceNumber, gomock.Any()).Return(call.err)
+				driverClient.EXPECT().TeardownPodNetwork(gomock.Any(), gomock.Any()).Return(call.err)
 			}
 
 			handled := teardownPodNetworkWithPrevResult(driverClient, tt.args.conf, tt.args.k8sArgs, tt.args.contVethName, testLogger)
diff --git a/cmd/routed-eni-cni-plugin/driver/driver.go b/cmd/routed-eni-cni-plugin/driver/driver.go
index 2c650894e6..b57f3b6a1c 100644
--- a/cmd/routed-eni-cni-plugin/driver/driver.go
+++ b/cmd/routed-eni-cni-plugin/driver/driver.go
@@ -45,18 +45,25 @@ const (
 	v6DADTimeout = 10 * time.Second
 )
 
+type VirtualInterfaceMetadata struct {
+	IPAddress         *net.IPNet
+	DeviceNumber      int
+	RouteTable        int
+	HostVethName      string
+	ContainerVethName string
+}
+
 // NetworkAPIs defines network API calls
 type NetworkAPIs interface {
 	// SetupPodNetwork sets up pod network for normal ENI based pods
-	SetupPodNetwork(hostVethName string, contVethName string, netnsPath string, v4Addr *net.IPNet, v6Addr *net.IPNet, deviceNumber int, mtu int, log logger.Logger) error
+	SetupPodNetwork(vethMetadata []VirtualInterfaceMetadata, netnsPath string, mtu int, log logger.Logger) error
 	// TeardownPodNetwork clean up pod network for normal ENI based pods
-	TeardownPodNetwork(containerAddr *net.IPNet, deviceNumber int, log logger.Logger) error
-
+	TeardownPodNetwork(vethMetadata []VirtualInterfaceMetadata, log logger.Logger) error
 	// SetupBranchENIPodNetwork sets up pod network for branch ENI based pods
-	SetupBranchENIPodNetwork(hostVethName string, contVethName string, netnsPath string, v4Addr *net.IPNet, v6Addr *net.IPNet, vlanID int, eniMAC string,
+	SetupBranchENIPodNetwork(vethMetadata VirtualInterfaceMetadata, netnsPath string, vlanID int, eniMAC string,
 		subnetGW string, parentIfIndex int, mtu int, podSGEnforcingMode sgpp.EnforcingMode, log logger.Logger) error
 	// TeardownBranchENIPodNetwork cleans up pod network for branch ENI based pods
-	TeardownBranchENIPodNetwork(containerAddr *net.IPNet, vlanID int, podSGEnforcingMode sgpp.EnforcingMode, log logger.Logger) error
+	TeardownBranchENIPodNetwork(vethMetadata VirtualInterfaceMetadata, vlanID int, podSGEnforcingMode sgpp.EnforcingMode, log logger.Logger) error
 }
 
 type linuxNetwork struct {
@@ -79,24 +86,24 @@ func New() NetworkAPIs {
 type createVethPairContext struct {
 	contVethName string
 	hostVethName string
-	v4Addr       *net.IPNet
-	v6Addr       *net.IPNet
+	ipAddr       *net.IPNet
 	netLink      netlinkwrapper.NetLink
 	ip           ipwrapper.IP
 	mtu          int
 	procSys      procsyswrapper.ProcSys
+	index        int
 }
 
-func newCreateVethPairContext(contVethName string, hostVethName string, v4Addr *net.IPNet, v6Addr *net.IPNet, mtu int) *createVethPairContext {
+func newCreateVethPairContext(contVethName string, hostVethName string, ipAddr *net.IPNet, mtu int, index int) *createVethPairContext {
 	return &createVethPairContext{
 		contVethName: contVethName,
 		hostVethName: hostVethName,
-		v4Addr:       v4Addr,
-		v6Addr:       v6Addr,
+		ipAddr:       ipAddr,
 		netLink:      netlinkwrapper.NewNetLink(),
 		ip:           ipwrapper.NewIP(),
 		mtu:          mtu,
 		procSys:      procsyswrapper.NewProcSys(),
+		index:        index,
 	}
 }
 
@@ -137,7 +144,8 @@ func (createVethContext *createVethPairContext) run(hostNS ns.NetNS) error {
 		return errors.Wrapf(err, "setup NS network: failed to set link %q up", createVethContext.contVethName)
 	}
 
-	if createVethContext.v6Addr != nil && createVethContext.v6Addr.IP.To16() != nil {
+	// this means it's a V6 IP address
+	if createVethContext.ipAddr.IP.To4() == nil {
 		// Enable v6 support on Container's veth interface.
 		if err = createVethContext.procSys.Set(fmt.Sprintf("net/ipv6/conf/%s/disable_ipv6", createVethContext.contVethName), "0"); err != nil {
 			if !os.IsNotExist(err) {
@@ -146,7 +154,7 @@ func (createVethContext *createVethPairContext) run(hostNS ns.NetNS) error {
 		}
 
 		// Enable v6 support on Container's lo interface inside the Pod networking namespace.
-		if err = createVethContext.procSys.Set(fmt.Sprintf("net/ipv6/conf/lo/disable_ipv6"), "0"); err != nil {
+		if err = createVethContext.procSys.Set("net/ipv6/conf/lo/disable_ipv6", "0"); err != nil {
 			if !os.IsNotExist(err) {
 				return errors.Wrapf(err, "setupVeth network: failed to enable IPv6 on container's lo interface")
 			}
@@ -163,27 +171,45 @@ func (createVethContext *createVethPairContext) run(hostNS ns.NetNS) error {
 	var addr *netlink.Addr
 	var defNet *net.IPNet
 
-	if createVethContext.v4Addr != nil {
-		gw = net.IPv4(169, 254, 1, 1)
+	if createVethContext.ipAddr.IP.To4() != nil {
+		gw = net.IPv4(169, 254, 1, byte(createVethContext.index)+1)
 		maskLen = 32
-		addr = &netlink.Addr{IPNet: createVethContext.v4Addr}
+		addr = &netlink.Addr{IPNet: createVethContext.ipAddr}
 		defNet = &net.IPNet{IP: net.IPv4zero, Mask: net.CIDRMask(0, maskLen)}
-	} else if createVethContext.v6Addr != nil {
-		gw = net.IP{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
+	} else {
+		gw = net.IP{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, byte(createVethContext.index) + 1}
 		maskLen = 128
-		addr = &netlink.Addr{IPNet: createVethContext.v6Addr}
+		addr = &netlink.Addr{IPNet: createVethContext.ipAddr}
 		defNet = &net.IPNet{IP: net.IPv6zero, Mask: net.CIDRMask(0, maskLen)}
 	}
 
 	gwNet := &net.IPNet{IP: gw, Mask: net.CIDRMask(maskLen, maskLen)}
 
+	// If Index  > 0 that means it has multiple IPs. Add IP rule + add default route to
+	rtTable := unix.RT_TABLE_MAIN
+	if createVethContext.index > 0 {
+		rtTable = createVethContext.index
+	}
+
 	if err = createVethContext.netLink.RouteReplace(&netlink.Route{
 		LinkIndex: contVeth.Attrs().Index,
 		Scope:     netlink.SCOPE_LINK,
-		Dst:       gwNet}); err != nil {
+		Dst:       gwNet,
+		Table:     rtTable}); err != nil {
 		return errors.Wrap(err, "setup NS network: failed to add default gateway")
 	}
 
+	if createVethContext.index > 0 {
+		// Add a from interface rule
+		fromInterfaceRule := createVethContext.netLink.NewRule()
+		fromInterfaceRule.Src = createVethContext.ipAddr
+		fromInterfaceRule.Priority = networkutils.FromInterfaceRulePriority
+		fromInterfaceRule.Table = rtTable
+		if err := createVethContext.netLink.RuleAdd(fromInterfaceRule); err != nil && !networkutils.IsRuleExistsError(err) {
+			return errors.Wrapf(err, "failed to setup fromInterface rule, containerAddr=%s, rtTable=%v", createVethContext.ipAddr.String(), createVethContext.index)
+		}
+	}
+
 	// Add a default route via dummy next hop(169.254.1.1 or fe80::1). Then all outgoing traffic will be routed by this
 	// default route via dummy next hop (169.254.1.1 or fe80::1)
 	if err = createVethContext.netLink.RouteAdd(&netlink.Route{
@@ -191,6 +217,7 @@ func (createVethContext *createVethPairContext) run(hostNS ns.NetNS) error {
 		Scope:     netlink.SCOPE_UNIVERSE,
 		Dst:       defNet,
 		Gw:        gw,
+		Table:     rtTable,
 	}); err != nil {
 		return errors.Wrap(err, "setup NS network: failed to add default route")
 	}
@@ -212,8 +239,8 @@ func (createVethContext *createVethPairContext) run(hostNS ns.NetNS) error {
 	if err = createVethContext.netLink.NeighAdd(neigh); err != nil {
 		return errors.Wrap(err, "setup NS network: failed to add static ARP")
 	}
-
-	if createVethContext.v6Addr != nil && createVethContext.v6Addr.IP.To16() != nil {
+	// if IP is not IPv4 or a v4 in v6 address, it return nil
+	if createVethContext.ipAddr.IP.To4() == nil {
 		if err := cniutils.WaitForAddressesToBeStable(createVethContext.netLink, createVethContext.contVethName, v6DADTimeout, WAIT_INTERVAL); err != nil {
 			return errors.Wrap(err, "setup NS network: failed while waiting for v6 addresses to be stable")
 		}
@@ -229,55 +256,60 @@ func (createVethContext *createVethPairContext) run(hostNS ns.NetNS) error {
 
 // SetupPodNetwork wires up linux networking for a pod's network
 // we expect v4Addr and v6Addr to have correct IPAddress Family.
-func (n *linuxNetwork) SetupPodNetwork(hostVethName string, contVethName string, netnsPath string, v4Addr *net.IPNet, v6Addr *net.IPNet,
-	deviceNumber int, mtu int, log logger.Logger) error {
-	log.Debugf("SetupPodNetwork: hostVethName=%s, contVethName=%s, netnsPath=%s, v4Addr=%v, v6Addr=%v, deviceNumber=%d, mtu=%d",
-		hostVethName, contVethName, netnsPath, v4Addr, v6Addr, deviceNumber, mtu)
+func (n *linuxNetwork) SetupPodNetwork(vethMetadata []VirtualInterfaceMetadata, netnsPath string, mtu int, log logger.Logger) error {
+	for index, vethData := range vethMetadata {
 
-	hostVeth, err := n.setupVeth(hostVethName, contVethName, netnsPath, v4Addr, v6Addr, mtu, log)
-	if err != nil {
-		return errors.Wrapf(err, "SetupPodNetwork: failed to setup veth pair")
-	}
+		log.Debugf("SetupPodNetwork: hostVethName=%s, contVethName=%s, netnsPath=%s, ipAddr=%v, routeTableNumber=%d, mtu=%d",
+			vethData.HostVethName, vethData.ContainerVethName, netnsPath, vethData.IPAddress, vethData.RouteTable, mtu)
 
-	var containerAddr *net.IPNet
-	if v4Addr != nil {
-		containerAddr = v4Addr
-	} else if v6Addr != nil {
-		containerAddr = v6Addr
-	}
+		hostVeth, err := n.setupVeth(vethData.HostVethName, vethData.ContainerVethName, netnsPath, vethData.IPAddress, mtu, log, index)
+		if err != nil {
+			return errors.Wrapf(err, "SetupPodNetwork: failed to setup veth pair")
+		}
 
-	rtTable := unix.RT_TABLE_MAIN
-	if deviceNumber > 0 {
-		rtTable = deviceNumber + 1
-	}
-	if err := n.setupIPBasedContainerRouteRules(hostVeth, containerAddr, rtTable, log); err != nil {
-		return errors.Wrapf(err, "SetupPodNetwork: unable to setup IP based container routes and rules")
+		rtTable := unix.RT_TABLE_MAIN
+		if vethData.RouteTable > 1 {
+			rtTable = vethData.RouteTable
+		}
+
+		if err := n.setupIPBasedContainerRouteRules(hostVeth, vethData.IPAddress, rtTable, log); err != nil {
+			return errors.Wrapf(err, "SetupPodNetwork: unable to setup IP based container routes and rules")
+		}
 	}
 	return nil
 }
 
 // TeardownPodNetwork cleanup ip rules
-func (n *linuxNetwork) TeardownPodNetwork(containerAddr *net.IPNet, deviceNumber int, log logger.Logger) error {
-	log.Debugf("TeardownPodNetwork: containerAddr=%s, deviceNumber=%d", containerAddr.String(), deviceNumber)
+func (n *linuxNetwork) TeardownPodNetwork(vethMetadata []VirtualInterfaceMetadata, log logger.Logger) error {
 
-	rtTable := unix.RT_TABLE_MAIN
-	if deviceNumber > 0 {
-		rtTable = deviceNumber + 1
-	}
-	if err := n.teardownIPBasedContainerRouteRules(containerAddr, rtTable, log); err != nil {
-		return errors.Wrapf(err, "TeardownPodNetwork: unable to teardown IP based container routes and rules")
+	for _, vethData := range vethMetadata {
+
+		log.Debugf("TeardownPodNetwork: containerAddr=%s, routeTable=%d", vethData.IPAddress.String(), vethData.RouteTable)
+
+		// Route table ID for primary ENI (Network 0, Device 0) => (0* MaxENI + 0 + 1)
+		// which is why we only update if the RT > 1
+		rtTable := unix.RT_TABLE_MAIN
+		if vethData.RouteTable > 1 {
+			rtTable = vethData.RouteTable
+		}
+
+		if err := n.teardownIPBasedContainerRouteRules(vethData.IPAddress, rtTable, log); err != nil {
+			return errors.Wrapf(err, "TeardownPodNetwork: unable to teardown IP based container routes and rules")
+		}
 	}
+
 	return nil
 }
 
 // SetupBranchENIPodNetwork sets up the network ns for pods requesting its own security group
 // we expect v4Addr and v6Addr to have correct IPAddress Family.
-func (n *linuxNetwork) SetupBranchENIPodNetwork(hostVethName string, contVethName string, netnsPath string, v4Addr *net.IPNet, v6Addr *net.IPNet,
+func (n *linuxNetwork) SetupBranchENIPodNetwork(vethMetadata VirtualInterfaceMetadata, netnsPath string,
 	vlanID int, eniMAC string, subnetGW string, parentIfIndex int, mtu int, podSGEnforcingMode sgpp.EnforcingMode, log logger.Logger) error {
-	log.Debugf("SetupBranchENIPodNetwork: hostVethName=%s, contVethName=%s, netnsPath=%s, v4Addr=%v, v6Addr=%v, vlanID=%d, eniMAC=%s, subnetGW=%s, parentIfIndex=%d, mtu=%d, podSGEnforcingMode=%v",
-		hostVethName, contVethName, netnsPath, v4Addr, v6Addr, vlanID, eniMAC, subnetGW, parentIfIndex, mtu, podSGEnforcingMode)
 
-	hostVeth, err := n.setupVeth(hostVethName, contVethName, netnsPath, v4Addr, v6Addr, mtu, log)
+	log.Debugf("SetupBranchENIPodNetwork: hostVethName=%s, contVethName=%s, netnsPath=%s, ipAddr=%v, vlanID=%d, eniMAC=%s, subnetGW=%s, parentIfIndex=%d, mtu=%d, podSGEnforcingMode=%v",
+		vethMetadata.HostVethName, vethMetadata.ContainerVethName, netnsPath, vethMetadata.IPAddress, vlanID, eniMAC, subnetGW, parentIfIndex, mtu, podSGEnforcingMode)
+
+	hostVeth, err := n.setupVeth(vethMetadata.HostVethName, vethMetadata.ContainerVethName, netnsPath, vethMetadata.IPAddress, mtu, log, 0)
 	if err != nil {
 		return errors.Wrapf(err, "SetupBranchENIPodNetwork: failed to setup veth pair")
 	}
@@ -288,13 +320,15 @@ func (n *linuxNetwork) SetupBranchENIPodNetwork(hostVethName string, contVethNam
 	// now since we obtain vlanID from prevResult during pod deletion, we should be able to correctly purge hostVeth during pod deletion and thus don't need this logic.
 	// this logic is kept here for safety purpose.
 	oldFromHostVethRule := n.netLink.NewRule()
-	oldFromHostVethRule.IifName = hostVethName
+	oldFromHostVethRule.IifName = vethMetadata.HostVethName
 	oldFromHostVethRule.Priority = networkutils.VlanRulePriority
-	if v6Addr != nil {
+
+	// If IPv4 it returns the IP address back
+	if vethMetadata.IPAddress.IP.To4() == nil {
 		oldFromHostVethRule.Family = unix.AF_INET6
 	}
 	if err := networkutils.NetLinkRuleDelAll(n.netLink, oldFromHostVethRule); err != nil {
-		return errors.Wrapf(err, "SetupBranchENIPodNetwork: failed to delete hostVeth rule for %s", hostVethName)
+		return errors.Wrapf(err, "SetupBranchENIPodNetwork: failed to delete hostVeth rule for %s", vethMetadata.HostVethName)
 	}
 
 	rtTable := vlanID + 100
@@ -303,20 +337,13 @@ func (n *linuxNetwork) SetupBranchENIPodNetwork(hostVethName string, contVethNam
 		return errors.Wrapf(err, "SetupBranchENIPodNetwork: failed to setup vlan")
 	}
 
-	var containerAddr *net.IPNet
-	if v4Addr != nil {
-		containerAddr = v4Addr
-	} else if v6Addr != nil {
-		containerAddr = v6Addr
-	}
-
 	switch podSGEnforcingMode {
 	case sgpp.EnforcingModeStrict:
-		if err := n.setupIIFBasedContainerRouteRules(hostVeth, containerAddr, vlanLink, rtTable, log); err != nil {
+		if err := n.setupIIFBasedContainerRouteRules(hostVeth, vethMetadata.IPAddress, vlanLink, rtTable, log); err != nil {
 			return errors.Wrapf(err, "SetupBranchENIPodNetwork: unable to setup IIF based container routes and rules")
 		}
 	case sgpp.EnforcingModeStandard:
-		if err := n.setupIPBasedContainerRouteRules(hostVeth, containerAddr, rtTable, log); err != nil {
+		if err := n.setupIPBasedContainerRouteRules(hostVeth, vethMetadata.IPAddress, rtTable, log); err != nil {
 			return errors.Wrapf(err, "SetupBranchENIPodNetwork: unable to setup IP based container routes and rules")
 		}
 	}
@@ -324,15 +351,15 @@ func (n *linuxNetwork) SetupBranchENIPodNetwork(hostVethName string, contVethNam
 }
 
 // TeardownBranchENIPodNetwork tears down the vlan and corresponding ip rules.
-func (n *linuxNetwork) TeardownBranchENIPodNetwork(containerAddr *net.IPNet, vlanID int, _ sgpp.EnforcingMode, log logger.Logger) error {
-	log.Debugf("TeardownBranchENIPodNetwork: containerAddr=%s, vlanID=%d", containerAddr.String(), vlanID)
+func (n *linuxNetwork) TeardownBranchENIPodNetwork(vethMetadata VirtualInterfaceMetadata, vlanID int, _ sgpp.EnforcingMode, log logger.Logger) error {
+	log.Debugf("TeardownBranchENIPodNetwork: containerAddr=%s, vlanID=%d", vethMetadata.IPAddress.String(), vlanID)
 
 	if err := n.teardownVlan(vlanID, log); err != nil {
 		return errors.Wrapf(err, "TeardownBranchENIPodNetwork: failed to teardown vlan")
 	}
 
 	ipFamily := unix.AF_INET
-	if containerAddr.IP.To4() == nil {
+	if vethMetadata.IPAddress.IP.To4() == nil {
 		ipFamily = unix.AF_INET6
 	}
 	// to handle the migration between different enforcingMode, we try to clean up rules under both mode since the pod might be setup with a different mode.
@@ -340,7 +367,7 @@ func (n *linuxNetwork) TeardownBranchENIPodNetwork(containerAddr *net.IPNet, vla
 	if err := n.teardownIIFBasedContainerRouteRules(rtTable, ipFamily, log); err != nil {
 		return errors.Wrapf(err, "TeardownBranchENIPodNetwork: unable to teardown IIF based container routes and rules")
 	}
-	if err := n.teardownIPBasedContainerRouteRules(containerAddr, rtTable, log); err != nil {
+	if err := n.teardownIPBasedContainerRouteRules(vethMetadata.IPAddress, rtTable, log); err != nil {
 		return errors.Wrapf(err, "TeardownBranchENIPodNetwork: unable to teardown IP based container routes and rules")
 	}
 
@@ -348,7 +375,7 @@ func (n *linuxNetwork) TeardownBranchENIPodNetwork(containerAddr *net.IPNet, vla
 }
 
 // setupVeth sets up veth for the pod.
-func (n *linuxNetwork) setupVeth(hostVethName string, contVethName string, netnsPath string, v4Addr *net.IPNet, v6Addr *net.IPNet, mtu int, log logger.Logger) (netlink.Link, error) {
+func (n *linuxNetwork) setupVeth(hostVethName string, contVethName string, netnsPath string, ipAddr *net.IPNet, mtu int, log logger.Logger, index int) (netlink.Link, error) {
 	// Clean up if hostVeth exists.
 	if oldHostVeth, err := n.netLink.LinkByName(hostVethName); err == nil {
 		if err = n.netLink.LinkDel(oldHostVeth); err != nil {
@@ -357,7 +384,7 @@ func (n *linuxNetwork) setupVeth(hostVethName string, contVethName string, netns
 		log.Debugf("Successfully deleted old hostVeth %s", hostVethName)
 	}
 
-	createVethContext := newCreateVethPairContext(contVethName, hostVethName, v4Addr, v6Addr, mtu)
+	createVethContext := newCreateVethPairContext(contVethName, hostVethName, ipAddr, mtu, index)
 	if err := n.ns.WithNetNSPath(netnsPath, createVethContext.run); err != nil {
 		return nil, errors.Wrap(err, "failed to setup veth network")
 	}
diff --git a/cmd/routed-eni-cni-plugin/driver/driver_test.go b/cmd/routed-eni-cni-plugin/driver/driver_test.go
index 7af04ebef5..2f4f5b63ce 100644
--- a/cmd/routed-eni-cni-plugin/driver/driver_test.go
+++ b/cmd/routed-eni-cni-plugin/driver/driver_test.go
@@ -106,10 +106,10 @@ func Test_linuxNetwork_SetupPodNetwork(t *testing.T) {
 		hostVethName string
 		contVethName string
 		netnsPath    string
-		v4Addr       *net.IPNet
-		v6Addr       *net.IPNet
+		ipAddr       *net.IPNet
 		deviceNumber int
 		mtu          int
+		routeTableId int
 	}
 	tests := []struct {
 		name    string
@@ -174,9 +174,9 @@ func Test_linuxNetwork_SetupPodNetwork(t *testing.T) {
 				hostVethName: "eni8ea2c11fe35",
 				contVethName: "eth0",
 				netnsPath:    "/proc/42/ns/net",
-				v4Addr:       containerAddr,
-				v6Addr:       nil,
+				ipAddr:       containerAddr, // v4 address
 				deviceNumber: 0,
+				routeTableId: 1,
 				mtu:          9001,
 			},
 		},
@@ -240,9 +240,9 @@ func Test_linuxNetwork_SetupPodNetwork(t *testing.T) {
 				hostVethName: "eni8ea2c11fe35",
 				contVethName: "eth0",
 				netnsPath:    "/proc/42/ns/net",
-				v4Addr:       containerAddr,
-				v6Addr:       nil,
+				ipAddr:       containerAddr, // v4 address
 				deviceNumber: 3,
+				routeTableId: 4,
 				mtu:          9001,
 			},
 		},
@@ -266,9 +266,9 @@ func Test_linuxNetwork_SetupPodNetwork(t *testing.T) {
 				hostVethName: "eni8ea2c11fe35",
 				contVethName: "eth0",
 				netnsPath:    "/proc/42/ns/net",
-				v4Addr:       containerAddr,
-				v6Addr:       nil,
+				ipAddr:       containerAddr, // v4 address
 				deviceNumber: 3,
+				routeTableId: 4,
 				mtu:          9001,
 			},
 			wantErr: errors.New("SetupPodNetwork: failed to setup veth pair: failed to setup veth network: some error"),
@@ -326,9 +326,9 @@ func Test_linuxNetwork_SetupPodNetwork(t *testing.T) {
 				hostVethName: "eni8ea2c11fe35",
 				contVethName: "eth0",
 				netnsPath:    "/proc/42/ns/net",
-				v4Addr:       containerAddr,
-				v6Addr:       nil,
+				ipAddr:       containerAddr, // v4 Address
 				deviceNumber: 3,
+				routeTableId: 4,
 				mtu:          9001,
 			},
 			wantErr: errors.New("SetupPodNetwork: unable to setup IP based container routes and rules: failed to setup container route, containerAddr=192.168.100.42/32, hostVeth=eni8ea2c11fe35, rtTable=main: some error"),
@@ -374,7 +374,17 @@ func Test_linuxNetwork_SetupPodNetwork(t *testing.T) {
 				ns:      ns,
 				procSys: procSys,
 			}
-			err := n.SetupPodNetwork(tt.args.hostVethName, tt.args.contVethName, tt.args.netnsPath, tt.args.v4Addr, tt.args.v6Addr, tt.args.deviceNumber, tt.args.mtu, testLogger)
+			vIfMetadata := []VirtualInterfaceMetadata{
+				{
+					IPAddress:         tt.args.ipAddr,
+					DeviceNumber:      tt.args.deviceNumber,
+					RouteTable:        tt.args.routeTableId,
+					HostVethName:      tt.args.hostVethName,
+					ContainerVethName: tt.args.contVethName,
+				},
+			}
+
+			err := n.SetupPodNetwork(vIfMetadata, tt.args.netnsPath, tt.args.mtu, testLogger)
 			if tt.wantErr != nil {
 				assert.EqualError(t, err, tt.wantErr.Error())
 			} else {
@@ -420,6 +430,7 @@ func Test_linuxNetwork_TeardownPodNetwork(t *testing.T) {
 	type args struct {
 		containerAddr *net.IPNet
 		deviceNumber  int
+		routeTableId  int
 	}
 	tests := []struct {
 		name    string
@@ -444,6 +455,7 @@ func Test_linuxNetwork_TeardownPodNetwork(t *testing.T) {
 			args: args{
 				containerAddr: containerAddr,
 				deviceNumber:  0,
+				routeTableId:  1,
 			},
 		},
 		{
@@ -470,6 +482,7 @@ func Test_linuxNetwork_TeardownPodNetwork(t *testing.T) {
 			args: args{
 				containerAddr: containerAddr,
 				deviceNumber:  3,
+				routeTableId:  4,
 			},
 		},
 		{
@@ -485,6 +498,7 @@ func Test_linuxNetwork_TeardownPodNetwork(t *testing.T) {
 			args: args{
 				containerAddr: containerAddr,
 				deviceNumber:  3,
+				routeTableId:  4,
 			},
 			wantErr: errors.New("TeardownPodNetwork: unable to teardown IP based container routes and rules: failed to delete toContainer rule, containerAddr=192.168.100.42/32, rtTable=main: some error"),
 		},
@@ -506,7 +520,14 @@ func Test_linuxNetwork_TeardownPodNetwork(t *testing.T) {
 			n := &linuxNetwork{
 				netLink: netLink,
 			}
-			err := n.TeardownPodNetwork(tt.args.containerAddr, tt.args.deviceNumber, testLogger)
+			vIfMetadata := []VirtualInterfaceMetadata{
+				{
+					IPAddress:    tt.args.containerAddr,
+					DeviceNumber: tt.args.deviceNumber,
+					RouteTable:   tt.args.routeTableId,
+				},
+			}
+			err := n.TeardownPodNetwork(vIfMetadata, testLogger)
 			if tt.wantErr != nil {
 				assert.EqualError(t, err, tt.wantErr.Error())
 			} else {
@@ -647,8 +668,7 @@ func Test_linuxNetwork_SetupBranchENIPodNetwork(t *testing.T) {
 		hostVethName       string
 		contVethName       string
 		netnsPath          string
-		v4Addr             *net.IPNet
-		v6Addr             *net.IPNet
+		ipAddr             *net.IPNet
 		vlanID             int
 		eniMAC             string
 		subnetGW           string
@@ -770,8 +790,7 @@ func Test_linuxNetwork_SetupBranchENIPodNetwork(t *testing.T) {
 				hostVethName:       "eni8ea2c11fe35",
 				contVethName:       "eth0",
 				netnsPath:          "/proc/42/ns/net",
-				v4Addr:             containerAddr,
-				v6Addr:             nil,
+				ipAddr:             containerAddr,
 				vlanID:             vlanID,
 				eniMAC:             eniMac,
 				subnetGW:           subnetGW,
@@ -888,8 +907,7 @@ func Test_linuxNetwork_SetupBranchENIPodNetwork(t *testing.T) {
 				hostVethName:       "eni8ea2c11fe35",
 				contVethName:       "eth0",
 				netnsPath:          "/proc/42/ns/net",
-				v4Addr:             nil,
-				v6Addr:             containerV6Addr,
+				ipAddr:             containerV6Addr, // v6Address
 				vlanID:             vlanID,
 				eniMAC:             eniMac,
 				subnetGW:           subnetV6GW,
@@ -1006,8 +1024,7 @@ func Test_linuxNetwork_SetupBranchENIPodNetwork(t *testing.T) {
 				hostVethName:       "eni8ea2c11fe35",
 				contVethName:       "eth0",
 				netnsPath:          "/proc/42/ns/net",
-				v4Addr:             containerAddr,
-				v6Addr:             nil,
+				ipAddr:             containerAddr, //v4 Address
 				vlanID:             vlanID,
 				eniMAC:             eniMac,
 				subnetGW:           subnetGW,
@@ -1124,8 +1141,7 @@ func Test_linuxNetwork_SetupBranchENIPodNetwork(t *testing.T) {
 				hostVethName:       "eni8ea2c11fe35",
 				contVethName:       "eth0",
 				netnsPath:          "/proc/42/ns/net",
-				v4Addr:             nil,
-				v6Addr:             containerV6Addr,
+				ipAddr:             containerV6Addr, // v6
 				vlanID:             vlanID,
 				eniMAC:             eniMac,
 				subnetGW:           subnetV6GW,
@@ -1154,8 +1170,7 @@ func Test_linuxNetwork_SetupBranchENIPodNetwork(t *testing.T) {
 				hostVethName:       "eni8ea2c11fe35",
 				contVethName:       "eth0",
 				netnsPath:          "/proc/42/ns/net",
-				v4Addr:             containerAddr,
-				v6Addr:             nil,
+				ipAddr:             containerAddr,
 				vlanID:             vlanID,
 				eniMAC:             eniMac,
 				subnetGW:           subnetGW,
@@ -1213,8 +1228,7 @@ func Test_linuxNetwork_SetupBranchENIPodNetwork(t *testing.T) {
 				hostVethName:       "eni8ea2c11fe35",
 				contVethName:       "eth0",
 				netnsPath:          "/proc/42/ns/net",
-				v4Addr:             containerAddr,
-				v6Addr:             nil,
+				ipAddr:             containerAddr,
 				vlanID:             vlanID,
 				eniMAC:             eniMac,
 				subnetGW:           subnetGW,
@@ -1282,8 +1296,7 @@ func Test_linuxNetwork_SetupBranchENIPodNetwork(t *testing.T) {
 				hostVethName:       "eni8ea2c11fe35",
 				contVethName:       "eth0",
 				netnsPath:          "/proc/42/ns/net",
-				v4Addr:             containerAddr,
-				v6Addr:             nil,
+				ipAddr:             containerAddr,
 				vlanID:             vlanID,
 				eniMAC:             eniMac,
 				subnetGW:           subnetGW,
@@ -1394,8 +1407,7 @@ func Test_linuxNetwork_SetupBranchENIPodNetwork(t *testing.T) {
 				hostVethName:       "eni8ea2c11fe35",
 				contVethName:       "eth0",
 				netnsPath:          "/proc/42/ns/net",
-				v4Addr:             containerAddr,
-				v6Addr:             nil,
+				ipAddr:             containerAddr,
 				vlanID:             vlanID,
 				eniMAC:             eniMac,
 				subnetGW:           subnetGW,
@@ -1506,8 +1518,7 @@ func Test_linuxNetwork_SetupBranchENIPodNetwork(t *testing.T) {
 				hostVethName:       "eni8ea2c11fe35",
 				contVethName:       "eth0",
 				netnsPath:          "/proc/42/ns/net",
-				v4Addr:             containerAddr,
-				v6Addr:             nil,
+				ipAddr:             containerAddr,
 				vlanID:             vlanID,
 				eniMAC:             eniMac,
 				subnetGW:           subnetGW,
@@ -1571,7 +1582,14 @@ func Test_linuxNetwork_SetupBranchENIPodNetwork(t *testing.T) {
 				ns:      ns,
 				procSys: procSys,
 			}
-			err := n.SetupBranchENIPodNetwork(tt.args.hostVethName, tt.args.contVethName, tt.args.netnsPath, tt.args.v4Addr, tt.args.v6Addr, tt.args.vlanID, tt.args.eniMAC, tt.args.subnetGW, tt.args.parentIfIndex, tt.args.mtu, tt.args.podSGEnforcingMode, testLogger)
+
+			vIfMetadata := VirtualInterfaceMetadata{
+				IPAddress:         tt.args.ipAddr,
+				HostVethName:      tt.args.hostVethName,
+				ContainerVethName: tt.args.contVethName,
+			}
+
+			err := n.SetupBranchENIPodNetwork(vIfMetadata, tt.args.netnsPath, tt.args.vlanID, tt.args.eniMAC, tt.args.subnetGW, tt.args.parentIfIndex, tt.args.mtu, tt.args.podSGEnforcingMode, testLogger)
 			if tt.wantErr != nil {
 				assert.EqualError(t, err, tt.wantErr.Error())
 			} else {
@@ -1952,7 +1970,12 @@ func Test_linuxNetwork_TeardownBranchENIPodNetwork(t *testing.T) {
 			n := &linuxNetwork{
 				netLink: netLink,
 			}
-			err := n.TeardownBranchENIPodNetwork(tt.args.containerAddr, tt.args.vlanID, tt.args.podSGEnforcingMode, testLogger)
+
+			vIfMetadata := VirtualInterfaceMetadata{
+				IPAddress: tt.args.containerAddr,
+			}
+
+			err := n.TeardownBranchENIPodNetwork(vIfMetadata, tt.args.vlanID, tt.args.podSGEnforcingMode, testLogger)
 			if tt.wantErr != nil {
 				assert.EqualError(t, err, tt.wantErr.Error())
 			} else {
@@ -2043,8 +2066,7 @@ func Test_createVethPairContext_run(t *testing.T) {
 	type args struct {
 		contVethName string
 		hostVethName string
-		v4Addr       *net.IPNet
-		v6Addr       *net.IPNet
+		ipAddr       *net.IPNet
 		mtu          int
 	}
 	tests := []struct {
@@ -2091,6 +2113,7 @@ func Test_createVethPairContext_run(t *testing.T) {
 						route: &netlink.Route{
 							LinkIndex: contVethWithIndex1.Attrs().Index,
 							Scope:     netlink.SCOPE_LINK,
+							Table:     254,
 							Dst: &net.IPNet{
 								IP:   net.IPv4(169, 254, 1, 1),
 								Mask: net.CIDRMask(32, 32),
@@ -2103,6 +2126,7 @@ func Test_createVethPairContext_run(t *testing.T) {
 						route: &netlink.Route{
 							LinkIndex: contVethWithIndex1.Attrs().Index,
 							Scope:     netlink.SCOPE_UNIVERSE,
+							Table:     254,
 							Dst: &net.IPNet{
 								IP:   net.IPv4zero,
 								Mask: net.CIDRMask(0, 32),
@@ -2148,12 +2172,11 @@ func Test_createVethPairContext_run(t *testing.T) {
 			args: args{
 				contVethName: "eth0",
 				hostVethName: "eni8ea2c11fe35",
-				v4Addr: &net.IPNet{
+				ipAddr: &net.IPNet{
 					IP:   net.ParseIP("192.168.120.1"),
 					Mask: net.CIDRMask(32, 32),
 				},
-				v6Addr: nil,
-				mtu:    9001,
+				mtu: 9001,
 			},
 		},
 		{
@@ -2198,6 +2221,7 @@ func Test_createVethPairContext_run(t *testing.T) {
 						route: &netlink.Route{
 							LinkIndex: contVethWithIndex1.Attrs().Index,
 							Scope:     netlink.SCOPE_LINK,
+							Table:     254,
 							Dst: &net.IPNet{
 								IP:   net.IP{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
 								Mask: net.CIDRMask(128, 128),
@@ -2210,6 +2234,7 @@ func Test_createVethPairContext_run(t *testing.T) {
 						route: &netlink.Route{
 							LinkIndex: contVethWithIndex1.Attrs().Index,
 							Scope:     netlink.SCOPE_UNIVERSE,
+							Table:     254,
 							Dst: &net.IPNet{
 								IP:   net.IPv6zero,
 								Mask: net.CIDRMask(0, 128),
@@ -2278,8 +2303,7 @@ func Test_createVethPairContext_run(t *testing.T) {
 			args: args{
 				contVethName: "eth0",
 				hostVethName: "eni8ea2c11fe35",
-				v4Addr:       nil,
-				v6Addr: &net.IPNet{
+				ipAddr: &net.IPNet{
 					IP:   net.ParseIP("2001:db8:3333:4444:5555:6666:7777:8888"),
 					Mask: net.CIDRMask(128, 128),
 				},
@@ -2306,12 +2330,11 @@ func Test_createVethPairContext_run(t *testing.T) {
 			args: args{
 				contVethName: "eth0",
 				hostVethName: "eni8ea2c11fe35",
-				v4Addr: &net.IPNet{
+				ipAddr: &net.IPNet{
 					IP:   net.ParseIP("192.168.120.1"),
 					Mask: net.CIDRMask(32, 32),
 				},
-				v6Addr: nil,
-				mtu:    9001,
+				mtu: 9001,
 			},
 			wantErr: errors.New("some error"),
 		},
@@ -2340,12 +2363,11 @@ func Test_createVethPairContext_run(t *testing.T) {
 			args: args{
 				contVethName: "eth0",
 				hostVethName: "eni8ea2c11fe35",
-				v4Addr: &net.IPNet{
+				ipAddr: &net.IPNet{
 					IP:   net.ParseIP("192.168.120.1"),
 					Mask: net.CIDRMask(32, 32),
 				},
-				v6Addr: nil,
-				mtu:    9001,
+				mtu: 9001,
 			},
 			wantErr: errors.New("setup NS network: failed to find link \"eni8ea2c11fe35\": some error"),
 		},
@@ -2380,12 +2402,11 @@ func Test_createVethPairContext_run(t *testing.T) {
 			args: args{
 				contVethName: "eth0",
 				hostVethName: "eni8ea2c11fe35",
-				v4Addr: &net.IPNet{
+				ipAddr: &net.IPNet{
 					IP:   net.ParseIP("192.168.120.1"),
 					Mask: net.CIDRMask(32, 32),
 				},
-				v6Addr: nil,
-				mtu:    9001,
+				mtu: 9001,
 			},
 			wantErr: errors.New("setup NS network: failed to set link \"eni8ea2c11fe35\" up: some error"),
 		},
@@ -2423,12 +2444,11 @@ func Test_createVethPairContext_run(t *testing.T) {
 			args: args{
 				contVethName: "eth0",
 				hostVethName: "eni8ea2c11fe35",
-				v4Addr: &net.IPNet{
+				ipAddr: &net.IPNet{
 					IP:   net.ParseIP("192.168.120.1"),
 					Mask: net.CIDRMask(32, 32),
 				},
-				v6Addr: nil,
-				mtu:    9001,
+				mtu: 9001,
 			},
 			wantErr: errors.New("setup NS network: failed to find link \"eth0\": some error"),
 		},
@@ -2470,12 +2490,11 @@ func Test_createVethPairContext_run(t *testing.T) {
 			args: args{
 				contVethName: "eth0",
 				hostVethName: "eni8ea2c11fe35",
-				v4Addr: &net.IPNet{
+				ipAddr: &net.IPNet{
 					IP:   net.ParseIP("192.168.120.1"),
 					Mask: net.CIDRMask(32, 32),
 				},
-				v6Addr: nil,
-				mtu:    9001,
+				mtu: 9001,
 			},
 			wantErr: errors.New("setup NS network: failed to set link \"eth0\" up: some error"),
 		},
@@ -2517,6 +2536,7 @@ func Test_createVethPairContext_run(t *testing.T) {
 						route: &netlink.Route{
 							LinkIndex: contVethWithIndex1.Attrs().Index,
 							Scope:     netlink.SCOPE_LINK,
+							Table:     254,
 							Dst: &net.IPNet{
 								IP:   net.IPv4(169, 254, 1, 1),
 								Mask: net.CIDRMask(32, 32),
@@ -2529,12 +2549,11 @@ func Test_createVethPairContext_run(t *testing.T) {
 			args: args{
 				contVethName: "eth0",
 				hostVethName: "eni8ea2c11fe35",
-				v4Addr: &net.IPNet{
+				ipAddr: &net.IPNet{
 					IP:   net.ParseIP("192.168.120.1"),
 					Mask: net.CIDRMask(32, 32),
 				},
-				v6Addr: nil,
-				mtu:    9001,
+				mtu: 9001,
 			},
 			wantErr: errors.New("setup NS network: failed to add default gateway: some error"),
 		},
@@ -2576,6 +2595,7 @@ func Test_createVethPairContext_run(t *testing.T) {
 						route: &netlink.Route{
 							LinkIndex: contVethWithIndex1.Attrs().Index,
 							Scope:     netlink.SCOPE_LINK,
+							Table:     254,
 							Dst: &net.IPNet{
 								IP:   net.IPv4(169, 254, 1, 1),
 								Mask: net.CIDRMask(32, 32),
@@ -2588,6 +2608,7 @@ func Test_createVethPairContext_run(t *testing.T) {
 						route: &netlink.Route{
 							LinkIndex: contVethWithIndex1.Attrs().Index,
 							Scope:     netlink.SCOPE_UNIVERSE,
+							Table:     254,
 							Dst: &net.IPNet{
 								IP:   net.IPv4zero,
 								Mask: net.CIDRMask(0, 32),
@@ -2601,12 +2622,11 @@ func Test_createVethPairContext_run(t *testing.T) {
 			args: args{
 				contVethName: "eth0",
 				hostVethName: "eni8ea2c11fe35",
-				v4Addr: &net.IPNet{
+				ipAddr: &net.IPNet{
 					IP:   net.ParseIP("192.168.120.1"),
 					Mask: net.CIDRMask(32, 32),
 				},
-				v6Addr: nil,
-				mtu:    9001,
+				mtu: 9001,
 			},
 			wantErr: errors.New("setup NS network: failed to add default route: some error"),
 		},
@@ -2648,6 +2668,7 @@ func Test_createVethPairContext_run(t *testing.T) {
 						route: &netlink.Route{
 							LinkIndex: contVethWithIndex1.Attrs().Index,
 							Scope:     netlink.SCOPE_LINK,
+							Table:     254,
 							Dst: &net.IPNet{
 								IP:   net.IPv4(169, 254, 1, 1),
 								Mask: net.CIDRMask(32, 32),
@@ -2660,6 +2681,7 @@ func Test_createVethPairContext_run(t *testing.T) {
 						route: &netlink.Route{
 							LinkIndex: contVethWithIndex1.Attrs().Index,
 							Scope:     netlink.SCOPE_UNIVERSE,
+							Table:     254,
 							Dst: &net.IPNet{
 								IP:   net.IPv4zero,
 								Mask: net.CIDRMask(0, 32),
@@ -2684,12 +2706,11 @@ func Test_createVethPairContext_run(t *testing.T) {
 			args: args{
 				contVethName: "eth0",
 				hostVethName: "eni8ea2c11fe35",
-				v4Addr: &net.IPNet{
+				ipAddr: &net.IPNet{
 					IP:   net.ParseIP("192.168.120.1"),
 					Mask: net.CIDRMask(32, 32),
 				},
-				v6Addr: nil,
-				mtu:    9001,
+				mtu: 9001,
 			},
 			wantErr: errors.New("setup NS network: failed to add IP addr to \"eth0\": some error"),
 		},
@@ -2731,6 +2752,7 @@ func Test_createVethPairContext_run(t *testing.T) {
 						route: &netlink.Route{
 							LinkIndex: contVethWithIndex1.Attrs().Index,
 							Scope:     netlink.SCOPE_LINK,
+							Table:     254,
 							Dst: &net.IPNet{
 								IP:   net.IPv4(169, 254, 1, 1),
 								Mask: net.CIDRMask(32, 32),
@@ -2743,6 +2765,7 @@ func Test_createVethPairContext_run(t *testing.T) {
 						route: &netlink.Route{
 							LinkIndex: contVethWithIndex1.Attrs().Index,
 							Scope:     netlink.SCOPE_UNIVERSE,
+							Table:     254,
 							Dst: &net.IPNet{
 								IP:   net.IPv4zero,
 								Mask: net.CIDRMask(0, 32),
@@ -2777,12 +2800,11 @@ func Test_createVethPairContext_run(t *testing.T) {
 			args: args{
 				contVethName: "eth0",
 				hostVethName: "eni8ea2c11fe35",
-				v4Addr: &net.IPNet{
+				ipAddr: &net.IPNet{
 					IP:   net.ParseIP("192.168.120.1"),
 					Mask: net.CIDRMask(32, 32),
 				},
-				v6Addr: nil,
-				mtu:    9001,
+				mtu: 9001,
 			},
 			wantErr: errors.New("setup NS network: failed to add static ARP: some error"),
 		},
@@ -2824,6 +2846,7 @@ func Test_createVethPairContext_run(t *testing.T) {
 						route: &netlink.Route{
 							LinkIndex: contVethWithIndex1.Attrs().Index,
 							Scope:     netlink.SCOPE_LINK,
+							Table:     254,
 							Dst: &net.IPNet{
 								IP:   net.IPv4(169, 254, 1, 1),
 								Mask: net.CIDRMask(32, 32),
@@ -2836,6 +2859,7 @@ func Test_createVethPairContext_run(t *testing.T) {
 						route: &netlink.Route{
 							LinkIndex: contVethWithIndex1.Attrs().Index,
 							Scope:     netlink.SCOPE_UNIVERSE,
+							Table:     254,
 							Dst: &net.IPNet{
 								IP:   net.IPv4zero,
 								Mask: net.CIDRMask(0, 32),
@@ -2881,12 +2905,11 @@ func Test_createVethPairContext_run(t *testing.T) {
 			args: args{
 				contVethName: "eth0",
 				hostVethName: "eni8ea2c11fe35",
-				v4Addr: &net.IPNet{
+				ipAddr: &net.IPNet{
 					IP:   net.ParseIP("192.168.120.1"),
 					Mask: net.CIDRMask(32, 32),
 				},
-				v6Addr: nil,
-				mtu:    9001,
+				mtu: 9001,
 			},
 			wantErr: errors.New("setup NS network: failed to move veth to host netns: some error"),
 		},
@@ -2934,8 +2957,7 @@ func Test_createVethPairContext_run(t *testing.T) {
 			args: args{
 				contVethName: "eth0",
 				hostVethName: "eni8ea2c11fe35",
-				v4Addr:       nil,
-				v6Addr: &net.IPNet{
+				ipAddr: &net.IPNet{
 					IP:   net.ParseIP("2001:db8:3333:4444:5555:6666:7777:8888"),
 					Mask: net.CIDRMask(128, 128),
 				},
@@ -2991,8 +3013,7 @@ func Test_createVethPairContext_run(t *testing.T) {
 			args: args{
 				contVethName: "eth0",
 				hostVethName: "eni8ea2c11fe35",
-				v4Addr:       nil,
-				v6Addr: &net.IPNet{
+				ipAddr: &net.IPNet{
 					IP:   net.ParseIP("2001:db8:3333:4444:5555:6666:7777:8888"),
 					Mask: net.CIDRMask(128, 128),
 				},
@@ -3042,6 +3063,7 @@ func Test_createVethPairContext_run(t *testing.T) {
 						route: &netlink.Route{
 							LinkIndex: contVethWithIndex1.Attrs().Index,
 							Scope:     netlink.SCOPE_LINK,
+							Table:     254,
 							Dst: &net.IPNet{
 								IP:   net.IP{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
 								Mask: net.CIDRMask(128, 128),
@@ -3054,6 +3076,7 @@ func Test_createVethPairContext_run(t *testing.T) {
 						route: &netlink.Route{
 							LinkIndex: contVethWithIndex1.Attrs().Index,
 							Scope:     netlink.SCOPE_UNIVERSE,
+							Table:     254,
 							Dst: &net.IPNet{
 								IP:   net.IPv6zero,
 								Mask: net.CIDRMask(0, 128),
@@ -3104,8 +3127,7 @@ func Test_createVethPairContext_run(t *testing.T) {
 			args: args{
 				contVethName: "eth0",
 				hostVethName: "eni8ea2c11fe35",
-				v4Addr:       nil,
-				v6Addr: &net.IPNet{
+				ipAddr: &net.IPNet{
 					IP:   net.ParseIP("2001:db8:3333:4444:5555:6666:7777:8888"),
 					Mask: net.CIDRMask(128, 128),
 				},
@@ -3161,8 +3183,7 @@ func Test_createVethPairContext_run(t *testing.T) {
 			createVethContext := &createVethPairContext{
 				contVethName: tt.args.contVethName,
 				hostVethName: tt.args.hostVethName,
-				v4Addr:       tt.args.v4Addr,
-				v6Addr:       tt.args.v6Addr,
+				ipAddr:       tt.args.ipAddr,
 				mtu:          tt.args.mtu,
 				netLink:      netLink,
 				procSys:      procSys,
@@ -3592,7 +3613,7 @@ func Test_linuxNetwork_setupVeth(t *testing.T) {
 				ns:      ns,
 				procSys: procSys,
 			}
-			got, err := n.setupVeth(tt.args.hostVethName, tt.args.contVethName, tt.args.netnsPath, nil, nil, tt.args.mtu, testLogger)
+			got, err := n.setupVeth(tt.args.hostVethName, tt.args.contVethName, tt.args.netnsPath, nil, tt.args.mtu, testLogger, 0)
 			if tt.wantErr != nil {
 				assert.EqualError(t, err, tt.wantErr.Error())
 			} else {
diff --git a/cmd/routed-eni-cni-plugin/driver/mocks/driver_mocks.go b/cmd/routed-eni-cni-plugin/driver/mocks/driver_mocks.go
index 7de6603fb6..81da89c7ac 100644
--- a/cmd/routed-eni-cni-plugin/driver/mocks/driver_mocks.go
+++ b/cmd/routed-eni-cni-plugin/driver/mocks/driver_mocks.go
@@ -1,27 +1,13 @@
-// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License"). You may
-// not use this file except in compliance with the License. A copy of the
-// License is located at
-//
-//     http://aws.amazon.com/apache2.0/
-//
-// or in the "license" file accompanying this file. This file is distributed
-// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-// express or implied. See the License for the specific language governing
-// permissions and limitations under the License.
-//
-
 // Code generated by MockGen. DO NOT EDIT.
-// Source: github.com/aws/amazon-vpc-cni-k8s/cmd/routed-eni-cni-plugin/driver (interfaces: NetworkAPIs)
+// Source: driver.go
 
-// Package mock_driver is a generated GoMock package.
-package mock_driver
+// Package mocks is a generated GoMock package.
+package mocks
 
 import (
-	net "net"
 	reflect "reflect"
 
+	driver "github.com/aws/amazon-vpc-cni-k8s/cmd/routed-eni-cni-plugin/driver"
 	sgpp "github.com/aws/amazon-vpc-cni-k8s/pkg/sgpp"
 	logger "github.com/aws/amazon-vpc-cni-k8s/pkg/utils/logger"
 	gomock "github.com/golang/mock/gomock"
@@ -51,57 +37,57 @@ func (m *MockNetworkAPIs) EXPECT() *MockNetworkAPIsMockRecorder {
 }
 
 // SetupBranchENIPodNetwork mocks base method.
-func (m *MockNetworkAPIs) SetupBranchENIPodNetwork(arg0, arg1, arg2 string, arg3, arg4 *net.IPNet, arg5 int, arg6, arg7 string, arg8, arg9 int, arg10 sgpp.EnforcingMode, arg11 logger.Logger) error {
+func (m *MockNetworkAPIs) SetupBranchENIPodNetwork(vethMetadata driver.VirtualInterfaceMetadata, netnsPath string, vlanID int, eniMAC, subnetGW string, parentIfIndex, mtu int, podSGEnforcingMode sgpp.EnforcingMode, log logger.Logger) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SetupBranchENIPodNetwork", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11)
+	ret := m.ctrl.Call(m, "SetupBranchENIPodNetwork", vethMetadata, netnsPath, vlanID, eniMAC, subnetGW, parentIfIndex, mtu, podSGEnforcingMode, log)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
 // SetupBranchENIPodNetwork indicates an expected call of SetupBranchENIPodNetwork.
-func (mr *MockNetworkAPIsMockRecorder) SetupBranchENIPodNetwork(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11 interface{}) *gomock.Call {
+func (mr *MockNetworkAPIsMockRecorder) SetupBranchENIPodNetwork(vethMetadata, netnsPath, vlanID, eniMAC, subnetGW, parentIfIndex, mtu, podSGEnforcingMode, log interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetupBranchENIPodNetwork", reflect.TypeOf((*MockNetworkAPIs)(nil).SetupBranchENIPodNetwork), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetupBranchENIPodNetwork", reflect.TypeOf((*MockNetworkAPIs)(nil).SetupBranchENIPodNetwork), vethMetadata, netnsPath, vlanID, eniMAC, subnetGW, parentIfIndex, mtu, podSGEnforcingMode, log)
 }
 
 // SetupPodNetwork mocks base method.
-func (m *MockNetworkAPIs) SetupPodNetwork(arg0, arg1, arg2 string, arg3, arg4 *net.IPNet, arg5, arg6 int, arg7 logger.Logger) error {
+func (m *MockNetworkAPIs) SetupPodNetwork(vethMetadata []driver.VirtualInterfaceMetadata, netnsPath string, mtu int, log logger.Logger) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SetupPodNetwork", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
+	ret := m.ctrl.Call(m, "SetupPodNetwork", vethMetadata, netnsPath, mtu, log)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
 // SetupPodNetwork indicates an expected call of SetupPodNetwork.
-func (mr *MockNetworkAPIsMockRecorder) SetupPodNetwork(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 interface{}) *gomock.Call {
+func (mr *MockNetworkAPIsMockRecorder) SetupPodNetwork(vethMetadata, netnsPath, mtu, log interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetupPodNetwork", reflect.TypeOf((*MockNetworkAPIs)(nil).SetupPodNetwork), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetupPodNetwork", reflect.TypeOf((*MockNetworkAPIs)(nil).SetupPodNetwork), vethMetadata, netnsPath, mtu, log)
 }
 
 // TeardownBranchENIPodNetwork mocks base method.
-func (m *MockNetworkAPIs) TeardownBranchENIPodNetwork(arg0 *net.IPNet, arg1 int, arg2 sgpp.EnforcingMode, arg3 logger.Logger) error {
+func (m *MockNetworkAPIs) TeardownBranchENIPodNetwork(vethMetadata driver.VirtualInterfaceMetadata, vlanID int, podSGEnforcingMode sgpp.EnforcingMode, log logger.Logger) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "TeardownBranchENIPodNetwork", arg0, arg1, arg2, arg3)
+	ret := m.ctrl.Call(m, "TeardownBranchENIPodNetwork", vethMetadata, vlanID, podSGEnforcingMode, log)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
 // TeardownBranchENIPodNetwork indicates an expected call of TeardownBranchENIPodNetwork.
-func (mr *MockNetworkAPIsMockRecorder) TeardownBranchENIPodNetwork(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
+func (mr *MockNetworkAPIsMockRecorder) TeardownBranchENIPodNetwork(vethMetadata, vlanID, podSGEnforcingMode, log interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TeardownBranchENIPodNetwork", reflect.TypeOf((*MockNetworkAPIs)(nil).TeardownBranchENIPodNetwork), arg0, arg1, arg2, arg3)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TeardownBranchENIPodNetwork", reflect.TypeOf((*MockNetworkAPIs)(nil).TeardownBranchENIPodNetwork), vethMetadata, vlanID, podSGEnforcingMode, log)
 }
 
 // TeardownPodNetwork mocks base method.
-func (m *MockNetworkAPIs) TeardownPodNetwork(arg0 *net.IPNet, arg1 int, arg2 logger.Logger) error {
+func (m *MockNetworkAPIs) TeardownPodNetwork(vethMetadata []driver.VirtualInterfaceMetadata, log logger.Logger) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "TeardownPodNetwork", arg0, arg1, arg2)
+	ret := m.ctrl.Call(m, "TeardownPodNetwork", vethMetadata, log)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
 // TeardownPodNetwork indicates an expected call of TeardownPodNetwork.
-func (mr *MockNetworkAPIsMockRecorder) TeardownPodNetwork(arg0, arg1, arg2 interface{}) *gomock.Call {
+func (mr *MockNetworkAPIsMockRecorder) TeardownPodNetwork(vethMetadata, log interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TeardownPodNetwork", reflect.TypeOf((*MockNetworkAPIs)(nil).TeardownPodNetwork), arg0, arg1, arg2)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TeardownPodNetwork", reflect.TypeOf((*MockNetworkAPIs)(nil).TeardownPodNetwork), vethMetadata, log)
 }
diff --git a/misc/10-aws.conflist b/misc/10-aws.conflist
index dde6b53631..82617a10a6 100644
--- a/misc/10-aws.conflist
+++ b/misc/10-aws.conflist
@@ -10,7 +10,8 @@
       "mtu": "__MTU__",
       "podSGEnforcingMode": "__PODSGENFORCINGMODE__",
       "pluginLogFile": "__PLUGINLOGFILE__",
-      "pluginLogLevel": "__PLUGINLOGLEVEL__"
+      "pluginLogLevel": "__PLUGINLOGLEVEL__",
+      "capabilities": {"io.kubernetes.cri.pod-annotations": true}
     },
     {
       "name": "egress-cni",
diff --git a/pkg/awsutils/awsutils.go b/pkg/awsutils/awsutils.go
index 0f539cf6d8..7e5972a663 100644
--- a/pkg/awsutils/awsutils.go
+++ b/pkg/awsutils/awsutils.go
@@ -99,7 +99,7 @@ var log = logger.Get()
 // APIs defines interfaces calls for adding/getting/deleting ENIs/secondary IPs. The APIs are not thread-safe.
 type APIs interface {
 	// AllocENI creates an ENI and attaches it to the instance
-	AllocENI(useCustomCfg bool, sg []*string, eniCfgSubnet string, numIPs int) (eni string, err error)
+	AllocENI(sg []*string, eniCfgSubnet string, numIPs int, networkCard int) (eni string, err error)
 
 	// FreeENI detaches ENI interface and deletes it
 	FreeENI(eniName string) error
@@ -143,6 +143,9 @@ type APIs interface {
 	// GetLocalIPv4 returns the primary IPv4 address on the primary ENI interface
 	GetLocalIPv4() net.IP
 
+	// GetLocalIPv6 returns the primary IPv6 address on the primary ENI interface
+	GetLocalIPv6() net.IP
+
 	// GetVPCIPv6CIDRs returns VPC's IPv6 CIDRs from instance metadata
 	GetVPCIPv6CIDRs() ([]string, error)
 
@@ -180,7 +183,7 @@ type APIs interface {
 	IsPrimaryENI(eniID string) bool
 
 	//RefreshSGIDs
-	RefreshSGIDs(mac string, store *datastore.DataStore) error
+	RefreshSGIDs(mac string, ds *datastore.DataStoreAccess) error
 
 	//GetInstanceHypervisorFamily returns the hypervisor family for the instance
 	GetInstanceHypervisorFamily() string
@@ -206,6 +209,7 @@ type EC2InstanceMetadataCache struct {
 	securityGroups   StringSet
 	subnetID         string
 	localIPv4        net.IP
+	localIPv6        net.IP
 	v4Enabled        bool
 	v6Enabled        bool
 	instanceID       string
@@ -257,6 +261,9 @@ type ENIMetadata struct {
 
 	// IPv6 Prefixes allocated for the network interface
 	IPv6Prefixes []ec2types.Ipv6PrefixSpecification
+
+	// Network card the ENI is attached on
+	NetworkCard int
 }
 
 // PrimaryIPv4Address returns the primary IPv4 address of this node
@@ -284,11 +291,12 @@ type TagMap map[string]string
 
 // DescribeAllENIsResult contains the fully
 type DescribeAllENIsResult struct {
-	ENIMetadata     []ENIMetadata
-	TagMap          map[string]TagMap
-	TrunkENI        string
-	EFAENIs         map[string]bool
-	MultiCardENIIDs []string
+	ENIMetadata       []ENIMetadata
+	TagMap            map[string]TagMap
+	TrunkENI          string
+	EFAENIs           map[string]bool
+	MultiCardENIIDs   []string
+	NetworkCardENIMap map[int][]string
 }
 
 // msSince returns milliseconds since start.
@@ -497,7 +505,7 @@ func (cache *EC2InstanceMetadataCache) initWithEC2Metadata(ctx context.Context)
 }
 
 // RefreshSGIDs retrieves security groups
-func (cache *EC2InstanceMetadataCache) RefreshSGIDs(mac string, store *datastore.DataStore) error {
+func (cache *EC2InstanceMetadataCache) RefreshSGIDs(mac string, dsAccess *datastore.DataStoreAccess) error {
 	ctx := context.TODO()
 
 	sgIDs, err := cache.imds.GetSecurityGroupIDs(ctx, mac)
@@ -524,19 +532,19 @@ func (cache *EC2InstanceMetadataCache) RefreshSGIDs(mac string, store *datastore
 	cache.securityGroups.Set(sgIDs)
 
 	if !cache.useCustomNetworking && (addedSGsCount != 0 || deletedSGsCount != 0) {
-		eniInfos := store.GetENIInfos()
-
 		var eniIDs []string
 
-		for eniID := range eniInfos.ENIs {
-			eniIDs = append(eniIDs, eniID)
+		for _, ds := range dsAccess.DataStores {
+			eniInfos := ds.GetENIInfos()
+			for eniID := range eniInfos.ENIs {
+				eniIDs = append(eniIDs, eniID)
+			}
 		}
 
 		newENIs := StringSet{}
 		newENIs.Set(eniIDs)
 
-		tempfilteredENIs := newENIs.Difference(&cache.multiCardENIs)
-		filteredENIs := tempfilteredENIs.Difference(&cache.unmanagedENIs)
+		filteredENIs := newENIs.Difference(&cache.unmanagedENIs)
 
 		// This will update SG for managed ENIs created by EKS.
 		for _, eniID := range filteredENIs.SortedList() {
@@ -633,6 +641,7 @@ func (cache *EC2InstanceMetadataCache) getENIMetadata(eniMAC string) (ENIMetadat
 	}
 	ipv4Available := false
 	ipv6Available := false
+	networkCard := 0
 	// Efa-only interfaces do not have any ipv4s or ipv6s associated with it. If we don't find any local-ipv4 or ipv6 info in imds we assume it to be efa-only interface and validate this later via ec2 call
 	for _, field := range macImdsFields {
 		if field == "local-ipv4s" {
@@ -655,6 +664,14 @@ func (cache *EC2InstanceMetadataCache) getENIMetadata(eniMAC string) (ENIMetadat
 				log.Debugf("Found IPv6 addresses associated with interface. This is not efa-only interface")
 			}
 		}
+		if field == "network-card" {
+			networkCard, err = cache.imds.GetNetworkCard(ctx, eniMAC)
+			if err != nil {
+				awsAPIErrInc("GetNetworkCard", err)
+				log.Errorf("Network Card data not found from", networkCard)
+				return ENIMetadata{}, err
+			}
+		}
 	}
 
 	if !ipv4Available && !ipv6Available {
@@ -668,6 +685,7 @@ func (cache *EC2InstanceMetadataCache) getENIMetadata(eniMAC string) (ENIMetadat
 			SubnetIPv6CIDR: "",
 			IPv6Addresses:  make([]ec2types.NetworkInterfaceIpv6Address, 0),
 			IPv6Prefixes:   make([]ec2types.Ipv6PrefixSpecification, 0),
+			NetworkCard:    networkCard,
 		}, nil
 	}
 
@@ -701,10 +719,11 @@ func (cache *EC2InstanceMetadataCache) getENIMetadata(eniMAC string) (ENIMetadat
 	var ec2ip6s []ec2types.NetworkInterfaceIpv6Address
 	var subnetV6Cidr string
 	if cache.v6Enabled {
-		// For IPv6 ENIs, do not error on missing IPv6 information
+		// For IPv6 ENIs, we have to return the error if Subnet is not discovered
 		v6cidr, err := cache.imds.GetSubnetIPv6CIDRBlocks(ctx, eniMAC)
 		if err != nil {
 			awsAPIErrInc("GetSubnetIPv6CIDRBlocks", err)
+			return ENIMetadata{}, err
 		} else {
 			subnetV6Cidr = v6cidr.String()
 		}
@@ -764,11 +783,12 @@ func (cache *EC2InstanceMetadataCache) getENIMetadata(eniMAC string) (ENIMetadat
 		SubnetIPv6CIDR: subnetV6Cidr,
 		IPv6Addresses:  ec2ip6s,
 		IPv6Prefixes:   ec2ipv6Prefixes,
+		NetworkCard:    networkCard,
 	}, nil
 }
 
 // awsGetFreeDeviceNumber calls EC2 API DescribeInstances to get the next free device index
-func (cache *EC2InstanceMetadataCache) awsGetFreeDeviceNumber() (int, error) {
+func (cache *EC2InstanceMetadataCache) awsGetFreeDeviceNumber(networkCard int) (int, error) {
 	input := &ec2.DescribeInstancesInput{
 		InstanceIds: []string{cache.instanceID},
 	}
@@ -793,44 +813,44 @@ func (cache *EC2InstanceMetadataCache) awsGetFreeDeviceNumber() (int, error) {
 	inst := result.Reservations[0].Instances[0]
 	var device [maxENIs]bool
 	for _, eni := range inst.NetworkInterfaces {
-		// We don't support multi-card yet, so only account for network card zero
-		if eni.Attachment != nil && aws.ToInt32(eni.Attachment.NetworkCardIndex) == 0 {
+		if eni.Attachment != nil && aws.ToInt32(eni.Attachment.NetworkCardIndex) == int32(networkCard) {
 			if aws.ToInt32(eni.Attachment.DeviceIndex) > maxENIs {
-				log.Warnf("The Device Index %d of the attached ENI %s > instance max slot %d",
+				log.Warnf("The Device Index %d of the attached ENI %s > instance max slot %d for network card",
 					aws.ToInt32(eni.Attachment.DeviceIndex), aws.ToString(eni.NetworkInterfaceId),
-					maxENIs)
+					maxENIs, networkCard)
 			} else {
-				log.Debugf("Discovered device number is used: %d", aws.ToInt32(eni.Attachment.DeviceIndex))
+				log.Debugf("Discovered device number is used for network card %d: %d", networkCard, aws.ToInt32(eni.Attachment.DeviceIndex))
 				device[aws.ToInt32(eni.Attachment.DeviceIndex)] = true
 			}
 		}
 	}
 
-	for freeDeviceIndex := 0; freeDeviceIndex < maxENIs; freeDeviceIndex++ {
+	for freeDeviceIndex := range maxENIs {
 		if !device[freeDeviceIndex] {
-			log.Debugf("Found a free device number: %d", freeDeviceIndex)
+			log.Debugf("Found a free device number for network card %d : %d", networkCard, freeDeviceIndex)
 			return freeDeviceIndex, nil
 		}
 	}
-	return 0, errors.New("awsGetFreeDeviceNumber: no available device number")
+	return 0, errors.New(fmt.Sprintf("awsGetFreeDeviceNumber: no available device number for network card %d", networkCard))
 }
 
 // AllocENI creates an ENI and attaches it to the instance
 // returns: newly created ENI ID
-func (cache *EC2InstanceMetadataCache) AllocENI(useCustomCfg bool, sg []*string, eniCfgSubnet string, numIPs int) (string, error) {
-	eniID, err := cache.createENI(useCustomCfg, sg, eniCfgSubnet, numIPs)
+func (cache *EC2InstanceMetadataCache) AllocENI(sg []*string, eniCfgSubnet string, numIPs int, networkCard int) (string, error) {
+
+	eniID, err := cache.createENI(sg, eniCfgSubnet, numIPs)
 	if err != nil {
 		return "", errors.Wrap(err, "AllocENI: failed to create ENI")
 	}
 
-	attachmentID, err := cache.attachENI(eniID)
+	attachmentID, err := cache.attachENI(eniID, networkCard)
 	if err != nil {
 		derr := cache.deleteENI(eniID, maxENIBackoffDelay)
 		if derr != nil {
 			awsUtilsErrInc("AllocENIDeleteErr", err)
 			log.Errorf("Failed to delete newly created untagged ENI! %v", err)
 		}
-		return "", errors.Wrap(err, "AllocENI: error attaching ENI")
+		return "", errors.Wrap(err, fmt.Sprintf("AllocENI: error attaching ENI for network card %d", networkCard))
 	}
 
 	// Also change the ENI's attribute so that the ENI will be deleted when the instance is deleted.
@@ -857,14 +877,14 @@ func (cache *EC2InstanceMetadataCache) AllocENI(useCustomCfg bool, sg []*string,
 		return "", errors.Wrap(err, "AllocENI: unable to change the ENI's attribute")
 	}
 
-	log.Infof("Successfully created and attached a new ENI %s to instance", eniID)
+	log.Infof("Successfully created and attached a new ENI %s to instance on network card %d", eniID, networkCard)
 	return eniID, nil
 }
 
 // attachENI calls EC2 API to attach the ENI and returns the attachment id
-func (cache *EC2InstanceMetadataCache) attachENI(eniID string) (string, error) {
+func (cache *EC2InstanceMetadataCache) attachENI(eniID string, networkCard int) (string, error) {
 	// attach to instance
-	freeDevice, err := cache.awsGetFreeDeviceNumber()
+	freeDevice, err := cache.awsGetFreeDeviceNumber(networkCard)
 	if err != nil {
 		return "", errors.Wrap(err, "attachENI: failed to get a free device number")
 	}
@@ -873,7 +893,7 @@ func (cache *EC2InstanceMetadataCache) attachENI(eniID string) (string, error) {
 		DeviceIndex:        aws.Int32(int32(freeDevice)),
 		InstanceId:         aws.String(cache.instanceID),
 		NetworkInterfaceId: aws.String(eniID),
-		NetworkCardIndex:   aws.Int32(0),
+		NetworkCardIndex:   aws.Int32(int32(networkCard)),
 	}
 	start := time.Now()
 	attachOutput, err := cache.ec2SVC.AttachNetworkInterface(context.Background(), attachInput)
@@ -884,58 +904,80 @@ func (cache *EC2InstanceMetadataCache) attachENI(eniID string) (string, error) {
 		awsAPIErrInc("AttachNetworkInterface", err)
 		prometheusmetrics.Ec2ApiErr.WithLabelValues("AttachNetworkInterface").Inc()
 		log.Errorf("Failed to attach ENI %s: %v", eniID, err)
-		return "", errors.Wrap(err, "attachENI: failed to attach ENI")
+		return "", errors.Wrap(err, fmt.Sprintf("attachENI: failed to attach ENI for network card %d", networkCard))
 	}
 	return aws.ToString(attachOutput.AttachmentId), err
 }
 
-// return ENI id, error
-func (cache *EC2InstanceMetadataCache) createENI(useCustomCfg bool, sg []*string, eniCfgSubnet string, numIPs int) (string, error) {
-	eniDescription := eniDescriptionPrefix + cache.instanceID
+// createENITags creates all the tags required to be added to the ENI
+//
+// Parameters:
+// - eniDescription (string):
+//
+// Returns:
+// - []ec2types.TagSpecification: Returns the tags by converting it into AWS SDK class
+func (cache *EC2InstanceMetadataCache) createENITags(eniDescription string) []ec2types.TagSpecification {
+
 	tags := map[string]string{
 		eniCreatedAtTagKey: time.Now().Format(time.RFC3339),
 	}
 	for key, value := range cache.buildENITags() {
 		tags[key] = value
 	}
-	tagSpec := []ec2types.TagSpecification{
+	return []ec2types.TagSpecification{
 		{
 			ResourceType: ec2types.ResourceTypeNetworkInterface,
 			Tags:         convertTagsToSDKTags(tags),
 		},
 	}
-	var needIPs = numIPs
+}
 
-	ipLimit := cache.GetENIIPv4Limit()
-	if ipLimit < needIPs {
-		needIPs = ipLimit
+func (cache *EC2InstanceMetadataCache) createENIInput(eniDescription string, tags []ec2types.TagSpecification, needIPs int) *ec2.CreateNetworkInterfaceInput {
+	input := &ec2.CreateNetworkInterfaceInput{
+		Description:       aws.String(eniDescription),
+		Groups:            cache.securityGroups.SortedList(),
+		SubnetId:          aws.String(cache.subnetID),
+		TagSpecifications: tags,
+	}
+	// Even though IPv6 PD is enabled, we require a Primary IP for the ENI.
+	// This always creates an ENI which has 1 Primary IPv6 address
+	// We use assignIPv6Prefix to assign a prefix during setupENI
+	if cache.v6Enabled {
+		input.Ipv6AddressCount = aws.Int32(int32(needIPs))
+		return input
 	}
-
-	log.Infof("Trying to allocate %d IP addresses on new ENI", needIPs)
-	log.Debugf("PD enabled - %t", cache.enablePrefixDelegation)
-
-	input := &ec2.CreateNetworkInterfaceInput{}
 
 	if cache.enablePrefixDelegation {
-		input = &ec2.CreateNetworkInterfaceInput{
-			Description:       aws.String(eniDescription),
-			Groups:            cache.securityGroups.SortedList(),
-			SubnetId:          aws.String(cache.subnetID),
-			TagSpecifications: tagSpec,
-			Ipv4PrefixCount:   aws.Int32(int32(needIPs)),
-		}
+		input.Ipv4PrefixCount = aws.Int32(int32(needIPs))
 	} else {
-		input = &ec2.CreateNetworkInterfaceInput{
-			Description:                    aws.String(eniDescription),
-			Groups:                         cache.securityGroups.SortedList(),
-			SubnetId:                       aws.String(cache.subnetID),
-			TagSpecifications:              tagSpec,
-			SecondaryPrivateIpAddressCount: aws.Int32(int32(needIPs)),
+		input.SecondaryPrivateIpAddressCount = aws.Int32(int32(needIPs))
+	}
+
+	return input
+}
+
+// return ENI id, error
+func (cache *EC2InstanceMetadataCache) createENI(sg []*string, eniCfgSubnet string, numIPs int) (string, error) {
+	eniDescription := eniDescriptionPrefix + cache.instanceID
+	tags := cache.createENITags(eniDescription)
+
+	var needIPs = numIPs
+
+	if !cache.v6Enabled {
+		ipLimit := cache.GetENIIPv4Limit()
+		if ipLimit < needIPs {
+			needIPs = ipLimit
 		}
 	}
 
+	log.Infof("Trying to allocate %d IP addresses on new ENI", needIPs)
+	log.Debugf("PD enabled - %t", cache.enablePrefixDelegation)
+
 	var err error
 	var networkInterfaceID string
+
+	input := cache.createENIInput(eniDescription, tags, needIPs)
+
 	if cache.useCustomNetworking {
 		input = createENIUsingCustomCfg(sg, eniCfgSubnet, input)
 		log.Infof("Creating ENI with security groups: %v in subnet: %s", input.Groups, aws.ToString(input.SubnetId))
@@ -945,7 +987,7 @@ func (cache *EC2InstanceMetadataCache) createENI(useCustomCfg bool, sg []*string
 			return networkInterfaceID, nil
 		}
 	} else {
-		if cache.useSubnetDiscovery {
+		if cache.useSubnetDiscovery && !cache.v6Enabled {
 			subnetResult, vpcErr := cache.getVpcSubnets()
 			if vpcErr != nil {
 				log.Warnf("Failed to call ec2:DescribeSubnets: %v", vpcErr)
@@ -1346,6 +1388,9 @@ func (cache *EC2InstanceMetadataCache) DescribeAllENIs() (DescribeAllENIsResult,
 		return DescribeAllENIsResult{}, errors.Wrap(err, "DescribeAllENIs: failed to get local ENI metadata")
 	}
 
+	// Get number of Network Cards supported by the instance
+	networkCards := len(cache.GetNetworkCards())
+
 	eniMap := make(map[string]ENIMetadata, len(allENIs))
 	var eniIDs []string
 	for _, eni := range allENIs {
@@ -1405,20 +1450,24 @@ func (cache *EC2InstanceMetadataCache) DescribeAllENIs() (DescribeAllENIsResult,
 	// Collect ENI response into ENI metadata and tags.
 	var trunkENI string
 	var multiCardENIIDs []string
+	networkCardENIMap := make(map[int][]string, networkCards)
 	efaENIs := make(map[string]bool, 0)
 	tagMap := make(map[string]TagMap, len(ec2Response.NetworkInterfaces))
+
 	for _, ec2res := range ec2Response.NetworkInterfaces {
 		eniID := aws.ToString(ec2res.NetworkInterfaceId)
 		attachment := ec2res.Attachment
 		// Validate that Attachment is populated by EC2 response before logging
 		if attachment != nil {
 			log.Infof("Got network card index %v for ENI %v", aws.ToInt32(attachment.NetworkCardIndex), eniID)
-			if aws.ToInt32(attachment.DeviceIndex) == 0 && !aws.ToBool(attachment.DeleteOnTermination) {
+			if aws.ToInt32(attachment.DeviceIndex) == 0 && aws.ToInt32(attachment.NetworkCardIndex) == 0 && !aws.ToBool(attachment.DeleteOnTermination) {
 				log.Warn("Primary ENI will not get deleted when node terminates because 'delete_on_termination' is set to false")
 			}
-			if aws.ToInt32(attachment.NetworkCardIndex) > 0 {
-				multiCardENIIDs = append(multiCardENIIDs, eniID)
+			// Maintain a mapping of NIC to ENIs
+			if attachment != nil {
+				networkCardENIMap[int(aws.ToInt32(attachment.NetworkCardIndex))] = append(networkCardENIMap[int(aws.ToInt32(attachment.NetworkCardIndex))], eniID)
 			}
+
 		} else {
 			log.Infof("Got empty attachment for ENI %v", eniID)
 		}
@@ -1434,6 +1483,7 @@ func (cache *EC2InstanceMetadataCache) DescribeAllENIs() (DescribeAllENIsResult,
 		if interfaceType == "efa" || interfaceType == "efa-only" {
 			efaENIs[eniID] = true
 		}
+
 		if interfaceType != "efa-only" {
 			if len(eniMetadata.IPv4Addresses) == 0 && len(eniMetadata.IPv6Addresses) == 0 {
 				log.Errorf("Missing IP addresses from IMDS. Non efa-only interface should have IP address associated with it %s", eniID)
@@ -1449,11 +1499,12 @@ func (cache *EC2InstanceMetadataCache) DescribeAllENIs() (DescribeAllENIsResult,
 		tagMap[eniMetadata.ENIID] = convertSDKTagsToTags(ec2res.TagSet)
 	}
 	return DescribeAllENIsResult{
-		ENIMetadata:     verifiedENIs,
-		TagMap:          tagMap,
-		TrunkENI:        trunkENI,
-		EFAENIs:         efaENIs,
-		MultiCardENIIDs: multiCardENIIDs,
+		ENIMetadata:       verifiedENIs,
+		TagMap:            tagMap,
+		TrunkENI:          trunkENI,
+		EFAENIs:           efaENIs,
+		MultiCardENIIDs:   multiCardENIIDs,
+		NetworkCardENIMap: networkCardENIMap,
 	}, nil
 }
 
@@ -1786,12 +1837,13 @@ func (cache *EC2InstanceMetadataCache) waitForENIAndIPsAttached(eni string, want
 			if eni == returnedENI.ENIID {
 				// Check how many Secondary IPs or Prefixes have been attached
 				var eniIPCount int
-				log.Debugf("ENI ID: %v IP Addr: %s, IPv4Prefixes:- %v, IPv6Prefixes:- %v", returnedENI.ENIID,
-					returnedENI.IPv4Addresses, returnedENI.IPv4Prefixes, returnedENI.IPv6Prefixes)
+				log.Debugf("ENI ID: %v IP Addr: %d, IPv4Prefixes:- %d, IPv6Prefixes:- %d", returnedENI.ENIID,
+					len(returnedENI.IPv4Addresses), len(returnedENI.IPv4Prefixes), len(returnedENI.IPv6Prefixes))
 				if cache.enablePrefixDelegation {
 					eniIPCount = len(returnedENI.IPv4Prefixes)
+					// We look for IPv6Address instead if prefix here
 					if cache.v6Enabled {
-						eniIPCount = len(returnedENI.IPv6Prefixes)
+						eniIPCount = len(returnedENI.IPv6Addresses)
 					}
 				} else {
 					//Ignore primary IP of the ENI
@@ -1800,7 +1852,7 @@ func (cache *EC2InstanceMetadataCache) waitForENIAndIPsAttached(eni string, want
 				}
 
 				if eniIPCount < 1 {
-					log.Debugf("No secondary IPv4 addresses/prefixes available yet on ENI %s", returnedENI.ENIID)
+					log.Debugf("No secondary IPv4/IPv6 addresses/prefixes available yet on ENI %s", returnedENI.ENIID)
 					return ErrNoSecondaryIPsFound
 				}
 
@@ -1816,6 +1868,7 @@ func (cache *EC2InstanceMetadataCache) waitForENIAndIPsAttached(eni string, want
 		log.Debugf("Not able to find the right ENI yet (attempt %d/%d)", attempt, maxENIEC2APIRetries)
 		return ErrENINotFound
 	})
+
 	prometheusmetrics.AwsAPILatency.WithLabelValues("waitForENIAndIPsAttached", fmt.Sprint(err != nil), awsReqStatus(err)).Observe(msSince(start))
 	if err != nil {
 		// If we have at least 1 Secondary IP, by now return what we have without an error
@@ -2057,6 +2110,18 @@ func (cache *EC2InstanceMetadataCache) GetLocalIPv4() net.IP {
 	return cache.localIPv4
 }
 
+// GetLocalIPv4 returns the primary IP address on the primary interface
+func (cache *EC2InstanceMetadataCache) GetLocalIPv6() net.IP {
+	ctx := context.TODO()
+
+	localIPv6, err := cache.imds.GetLocalIPv6(ctx)
+	if err != nil {
+		awsAPIErrInc("GetIPv6", err)
+	}
+
+	return localIPv6
+}
+
 // GetVPCIPv6CIDRs returns VPC CIDRs
 func (cache *EC2InstanceMetadataCache) GetVPCIPv6CIDRs() ([]string, error) {
 	ctx := context.TODO()
diff --git a/pkg/awsutils/awsutils_test.go b/pkg/awsutils/awsutils_test.go
index 59da59ee6f..3ad27d6637 100644
--- a/pkg/awsutils/awsutils_test.go
+++ b/pkg/awsutils/awsutils_test.go
@@ -59,6 +59,7 @@ const (
 	metadataIPv4Prefixes = "/ipv4-prefix"
 	metadataIPv6s        = "/ipv6s"
 	metadataIPv6Prefixes = "/ipv6-prefix"
+	metadataSubnetV6CIDR = "/subnet-ipv6-cidr-blocks"
 
 	az                   = "us-east-1a"
 	localIP              = "10.0.0.10"
@@ -71,6 +72,7 @@ const (
 	sgs                  = sg1 + " " + sg2
 	subnetID             = "subnet-6b245523"
 	subnetCIDR           = "10.0.1.0/24"
+	subnetCIDRv6         = "2001:db8::/56"
 	vpcID                = "vpc-3c133421"
 	primaryeniID         = "eni-00000000"
 	eniID                = primaryeniID
@@ -83,13 +85,13 @@ const (
 	eni2PrivateIP        = "10.0.0.2"
 	eni2Prefix           = "10.0.2.0/28"
 	eni2v6IP             = "2001:db8:8:4::2"
-	eni2v6Prefix         = "2001:db8::/64"
+	eni2v6Prefix         = "2001:db8::/80"
 	eni2ID               = "eni-12341234"
 	metadataVPCIPv4CIDRs = "192.168.0.0/16	100.66.0.0/1"
 	myNodeName           = "testNodeName"
 	imdsMACFields        = "security-group-ids subnet-id vpc-id vpc-ipv4-cidr-blocks device-number interface-id subnet-ipv4-cidr-block local-ipv4s ipv4-prefix ipv6-prefix"
 	imdsMACFieldsEfaOnly = "security-group-ids subnet-id vpc-id vpc-ipv4-cidr-blocks device-number interface-id subnet-ipv4-cidr-block ipv4-prefix ipv6-prefix"
-	imdsMACFieldsV6Only  = "security-group-ids subnet-id vpc-id vpc-ipv4-cidr-blocks device-number interface-id subnet-ipv6-cidr-blocks ipv6s ipv6-prefix"
+	imdsMACFieldsV6Only  = "security-group-ids subnet-id vpc-id vpc-ipv4-cidr-blocks vpc-ipv6-cidr-blocks device-number interface-id subnet-ipv6-cidr-blocks ipv6s ipv6-prefix"
 	imdsMACFieldsV4AndV6 = "security-group-ids subnet-id vpc-id vpc-ipv4-cidr-blocks device-number interface-id subnet-ipv4-cidr-block ipv6s local-ipv4s"
 )
 
@@ -102,14 +104,15 @@ func testMetadata(overrides map[string]interface{}) FakeIMDS {
 		metadataMAC:                  primaryMAC,
 		metadataMACPath:              primaryMAC,
 		metadataMACPath + primaryMAC: imdsMACFields,
-		metadataMACPath + primaryMAC + metadataDeviceNum:  eni1Device,
-		metadataMACPath + primaryMAC + metadataInterface:  primaryeniID,
-		metadataMACPath + primaryMAC + metadataSGs:        sgs,
-		metadataMACPath + primaryMAC + metadataIPv4s:      eni1PrivateIP,
-		metadataMACPath + primaryMAC + metadataSubnetID:   subnetID,
-		metadataMACPath + primaryMAC + metadataVpcID:      vpcID,
-		metadataMACPath + primaryMAC + metadataSubnetCIDR: subnetCIDR,
-		metadataMACPath + primaryMAC + metadataVPCcidrs:   metadataVPCIPv4CIDRs,
+		metadataMACPath + primaryMAC + metadataDeviceNum:    eni1Device,
+		metadataMACPath + primaryMAC + metadataInterface:    primaryeniID,
+		metadataMACPath + primaryMAC + metadataSGs:          sgs,
+		metadataMACPath + primaryMAC + metadataIPv4s:        eni1PrivateIP,
+		metadataMACPath + primaryMAC + metadataSubnetID:     subnetID,
+		metadataMACPath + primaryMAC + metadataVpcID:        vpcID,
+		metadataMACPath + primaryMAC + metadataSubnetCIDR:   subnetCIDR,
+		metadataMACPath + primaryMAC + metadataSubnetV6CIDR: subnetCIDRv6,
+		metadataMACPath + primaryMAC + metadataVPCcidrs:     metadataVPCIPv4CIDRs,
 	}
 
 	for k, v := range overrides {
@@ -253,6 +256,7 @@ func TestGetAttachedENIsWithIPv6Only(t *testing.T) {
 		metadataMACPath + eni2MAC + metadataDeviceNum:    eni2Device,
 		metadataMACPath + eni2MAC + metadataInterface:    eni2ID,
 		metadataMACPath + eni2MAC + metadataIPv6s:        eni2v6IP,
+		metadataMACPath + eni2MAC + metadataSubnetV6CIDR: subnetCIDRv6,
 		metadataMACPath + eni2MAC + metadataIPv6Prefixes: eni2v6Prefix,
 	})
 
@@ -313,7 +317,7 @@ func TestAWSGetFreeDeviceNumberOnErr(t *testing.T) {
 	mockEC2.EXPECT().DescribeInstances(gomock.Any(), gomock.Any()).Return(nil, errors.New("error on DescribeInstances"))
 
 	cache := &EC2InstanceMetadataCache{ec2SVC: mockEC2}
-	_, err := cache.awsGetFreeDeviceNumber()
+	_, err := cache.awsGetFreeDeviceNumber(0)
 	assert.Error(t, err)
 }
 
@@ -338,7 +342,7 @@ func TestAWSGetFreeDeviceNumberNoDevice(t *testing.T) {
 	mockEC2.EXPECT().DescribeInstances(gomock.Any(), gomock.Any(), gomock.Any()).Return(result, nil)
 
 	cache := &EC2InstanceMetadataCache{ec2SVC: mockEC2}
-	_, err := cache.awsGetFreeDeviceNumber()
+	_, err := cache.awsGetFreeDeviceNumber(0)
 	assert.Error(t, err)
 }
 
@@ -515,7 +519,7 @@ func TestAllocENI(t *testing.T) {
 		useSubnetDiscovery: true,
 	}
 
-	_, err := cache.AllocENI(false, nil, "", 5)
+	_, err := cache.AllocENI(nil, "", 5, 0)
 	assert.NoError(t, err)
 }
 
@@ -565,7 +569,7 @@ func TestAllocENINoFreeDevice(t *testing.T) {
 		useSubnetDiscovery: true,
 	}
 
-	_, err := cache.AllocENI(false, nil, "", 5)
+	_, err := cache.AllocENI(nil, "", 5, 0)
 	assert.Error(t, err)
 }
 
@@ -618,7 +622,7 @@ func TestAllocENIMaxReached(t *testing.T) {
 		useSubnetDiscovery: true,
 	}
 
-	_, err := cache.AllocENI(false, nil, "", 5)
+	_, err := cache.AllocENI(nil, "", 5, 0)
 	assert.Error(t, err)
 }
 
@@ -665,7 +669,7 @@ func TestAllocENIWithIPAddresses(t *testing.T) {
 	mockEC2.EXPECT().ModifyNetworkInterfaceAttribute(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil)
 
 	cache := &EC2InstanceMetadataCache{ec2SVC: mockEC2, instanceType: "c5n.18xlarge", useSubnetDiscovery: true}
-	_, err := cache.AllocENI(false, nil, subnetID, 5)
+	_, err := cache.AllocENI(nil, subnetID, 5, 0)
 	assert.NoError(t, err)
 
 	// when required IP numbers(50) is higher than ENI's limit(49)
@@ -675,7 +679,7 @@ func TestAllocENIWithIPAddresses(t *testing.T) {
 	mockEC2.EXPECT().AttachNetworkInterface(gomock.Any(), gomock.Any(), gomock.Any()).Return(attachResult, nil)
 	mockEC2.EXPECT().ModifyNetworkInterfaceAttribute(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil)
 	cache = &EC2InstanceMetadataCache{ec2SVC: mockEC2, instanceType: "c5n.18xlarge", useSubnetDiscovery: true}
-	_, err = cache.AllocENI(false, nil, subnetID, 49)
+	_, err = cache.AllocENI(nil, subnetID, 49, 0)
 	assert.NoError(t, err)
 }
 
@@ -709,7 +713,7 @@ func TestAllocENIWithIPAddressesAlreadyFull(t *testing.T) {
 		instanceType:       "t3.xlarge",
 		useSubnetDiscovery: true,
 	}
-	_, err := cache.AllocENI(true, nil, "", 14)
+	_, err := cache.AllocENI(nil, "", 14, 0)
 	assert.Error(t, err)
 }
 
@@ -763,7 +767,7 @@ func TestAllocENIWithPrefixAddresses(t *testing.T) {
 		enablePrefixDelegation: true,
 		useSubnetDiscovery:     true,
 	}
-	_, err := cache.AllocENI(false, nil, subnetID, 1)
+	_, err := cache.AllocENI(nil, subnetID, 1, 0)
 	assert.NoError(t, err)
 }
 
@@ -798,7 +802,7 @@ func TestAllocENIWithPrefixesAlreadyFull(t *testing.T) {
 		enablePrefixDelegation: true,
 		useSubnetDiscovery:     true,
 	}
-	_, err := cache.AllocENI(true, nil, "", 1)
+	_, err := cache.AllocENI(nil, "", 1, 0)
 	assert.Error(t, err)
 }
 
@@ -1159,6 +1163,8 @@ func TestEC2InstanceMetadataCache_waitForENIAndPrefixesAttached(t *testing.T) {
 	isPrimary := true
 	primaryIP := eni2PrivateIP
 	prefixIP := eni2Prefix
+	primaryIPv6 := eni1v6IP
+
 	eni1Metadata := ENIMetadata{
 		ENIID:          eni2ID,
 		MAC:            eni2MAC,
@@ -1176,24 +1182,23 @@ func TestEC2InstanceMetadataCache_waitForENIAndPrefixesAttached(t *testing.T) {
 			},
 		},
 	}
-	v6PrefixIP := eni2v6Prefix
+	// v6PrefixIP := eni2v6Prefix
 	eni2Metadata := ENIMetadata{
 		ENIID:          eni2ID,
 		MAC:            eni2MAC,
 		DeviceNumber:   1,
 		SubnetIPv4CIDR: subnetCIDR,
+		SubnetIPv6CIDR: subnetCIDRv6,
 		IPv4Addresses: []ec2types.NetworkInterfacePrivateIpAddress{
 			{
 				Primary:          &isPrimary,
 				PrivateIpAddress: &primaryIP,
 			},
 		},
-		IPv6Prefixes: []ec2types.Ipv6PrefixSpecification{
-			{
-				Ipv6Prefix: &v6PrefixIP,
-			},
+		IPv6Prefixes: []ec2types.Ipv6PrefixSpecification{},
+		IPv6Addresses: []ec2types.NetworkInterfaceIpv6Address{
+			{Ipv6Address: &primaryIPv6},
 		},
-		IPv6Addresses: []ec2types.NetworkInterfaceIpv6Address{},
 	}
 	tests := []struct {
 		name            string
@@ -1210,30 +1215,40 @@ func TestEC2InstanceMetadataCache_waitForENIAndPrefixesAttached(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			ctrl, mockEC2 := setup(t)
 			defer ctrl.Finish()
+
 			eniIPs := eni2PrivateIP
 			eniPrefixes := eni2Prefix
 			metaDataPrefixPath := metadataIPv4Prefixes
+			metaDataSubnetCIDRPath := metadataSubnetCIDR
+			subnetCIDRValue := subnetCIDR
+			macFields := imdsMACFields
+
+			// We create ENI with single v6 IP and then attach the IPv6 Prefixes later
 			if tt.args.v6Enabled {
-				eniPrefixes = eni2v6Prefix
-				metaDataPrefixPath = metadataIPv6Prefixes
+				eniPrefixes = eni1v6IP
+				metaDataPrefixPath = metadataIPv6s
+				metaDataSubnetCIDRPath = metadataSubnetV6CIDR
+				subnetCIDRValue = subnetCIDRv6
+				macFields = imdsMACFieldsV6Only
 			}
 			if tt.args.foundPrefixes == 0 {
 				eniPrefixes = ""
 			}
 			mockMetadata := testMetadata(map[string]interface{}{
-				metadataMACPath:                                primaryMAC + " " + eni2MAC,
-				metadataMACPath + eni2MAC:                      imdsMACFields,
-				metadataMACPath + eni2MAC + metadataDeviceNum:  eni2Device,
-				metadataMACPath + eni2MAC + metadataInterface:  eni2ID,
-				metadataMACPath + eni2MAC + metadataSubnetCIDR: subnetCIDR,
-				metadataMACPath + eni2MAC + metadataIPv4s:      eniIPs,
-				metadataMACPath + eni2MAC + metaDataPrefixPath: eniPrefixes,
+				metadataMACPath:                                    primaryMAC + " " + eni2MAC,
+				metadataMACPath + eni2MAC:                          macFields,
+				metadataMACPath + eni2MAC + metadataDeviceNum:      eni2Device,
+				metadataMACPath + eni2MAC + metadataInterface:      eni2ID,
+				metadataMACPath + eni2MAC + metaDataSubnetCIDRPath: subnetCIDRValue,
+				metadataMACPath + eni2MAC + metadataIPv4s:          eniIPs,
+				metadataMACPath + eni2MAC + metaDataPrefixPath:     eniPrefixes,
 			})
+
 			cache := &EC2InstanceMetadataCache{imds: TypedIMDS{mockMetadata}, ec2SVC: mockEC2,
 				enablePrefixDelegation: true, v4Enabled: tt.args.v4Enabled, v6Enabled: tt.args.v6Enabled}
 			gotEniMetadata, err := cache.waitForENIAndIPsAttached(tt.args.eni, tt.args.wantedSecondaryIPs, tt.args.maxBackoffDelay)
 			if (err != nil) != tt.wantErr {
-				t.Errorf("waitForENIAndIPsAttached() error = %v, wantErr %v", err, tt.wantErr)
+				t.Errorf("waitForENIAndIPsAttached() error = %+v, wantErr %+v", err, tt.wantErr)
 				return
 			}
 			if !reflect.DeepEqual(gotEniMetadata, tt.wantEniMetadata) {
diff --git a/pkg/awsutils/imds.go b/pkg/awsutils/imds.go
index 0c3152442c..80d388db7c 100644
--- a/pkg/awsutils/imds.go
+++ b/pkg/awsutils/imds.go
@@ -463,6 +463,30 @@ func (typedimds TypedIMDS) GetLocalIPv4s(ctx context.Context, mac string) ([]net
 		imdsErr := new(imdsRequestError)
 		oe := new(smithy.OperationError)
 		if errors.As(err, &imdsErr) || errors.As(err, &oe) {
+			if IsNotFound(err) {
+				// No IPv4 address on the interface, not an error
+				return nil, nil
+			}
+			log.Warnf("%v", err)
+			return nil, newIMDSRequestError(err.Error(), err)
+		}
+		return nil, err
+	}
+	return ips, err
+}
+
+// GetLocalIPv4s returns the private IPv6 addresses associated with the interface.  First returned address is the primary address.
+func (typedimds TypedIMDS) GetLocalIPv6s(ctx context.Context, mac string) ([]net.IP, error) {
+	key := fmt.Sprintf("network/interfaces/macs/%s/local-ipv6s", mac)
+	ips, err := typedimds.getIPs(ctx, key)
+	if err != nil {
+		imdsErr := new(imdsRequestError)
+		oe := new(smithy.OperationError)
+		if errors.As(err, &imdsErr) || errors.As(err, &oe) {
+			if IsNotFound(err) {
+				// No IPv6 address on the interface, not an error
+				return nil, nil
+			}
 			log.Warnf("%v", err)
 			return nil, newIMDSRequestError(err.Error(), err)
 		}
@@ -510,6 +534,26 @@ func (typedimds TypedIMDS) GetIPv6Prefixes(ctx context.Context, mac string) ([]n
 	return prefixes, err
 }
 
+// GetLocalIPv6 returns the IPv6 addresses associated with the primary interface.
+func (typedimds TypedIMDS) GetLocalIPv6(ctx context.Context) (net.IP, error) {
+	key := fmt.Sprintf("ipv6")
+	ips, err := typedimds.getIP(ctx, key)
+	if err != nil {
+		imdsErr := new(imdsRequestError)
+		oe := new(smithy.OperationError)
+		if errors.As(err, &imdsErr) || errors.As(err, &oe) {
+			if IsNotFound(err) {
+				// No IPv6.  Not an error, just a disappointment :(
+				return nil, nil
+			}
+			log.Warnf("%v", err)
+			return nil, newIMDSRequestError(err.Error(), err)
+		}
+		return nil, err
+	}
+	return ips, err
+}
+
 // GetIPv6s returns the IPv6 addresses associated with the interface.
 func (typedimds TypedIMDS) GetIPv6s(ctx context.Context, mac string) ([]net.IP, error) {
 	key := fmt.Sprintf("network/interfaces/macs/%s/ipv6s", mac)
@@ -572,6 +616,25 @@ func (typedimds TypedIMDS) GetVPCIPv6CIDRBlocks(ctx context.Context, mac string)
 	return ipnets, err
 }
 
+// GetNetworkCard returns the Network card the interface is attached on
+func (typedimds TypedIMDS) GetNetworkCard(ctx context.Context, mac string) (int, error) {
+	key := fmt.Sprintf("network/interfaces/macs/%s/network-card", mac)
+	networkCard, err := typedimds.getInt(ctx, key)
+	if err != nil {
+		imdsErr := new(imdsRequestError)
+		oe := new(smithy.OperationError)
+		if errors.As(err, &imdsErr) || errors.As(err, &oe) {
+			if IsNotFound(err) {
+				// If no network card field, it is connected to Network card 0
+				return networkCard, nil
+			}
+			log.Warnf("%v", err)
+			return -1, newIMDSRequestError(err.Error(), err)
+		}
+	}
+	return networkCard, err
+}
+
 // GetSubnetIPv6CIDRBlocks returns the IPv6 CIDR block for the subnet in which the interface resides.
 func (typedimds TypedIMDS) GetSubnetIPv6CIDRBlocks(ctx context.Context, mac string) (net.IPNet, error) {
 	key := fmt.Sprintf("network/interfaces/macs/%s/subnet-ipv6-cidr-blocks", mac)
diff --git a/pkg/awsutils/imds_test.go b/pkg/awsutils/imds_test.go
index 5663b64e5a..6db3b30685 100644
--- a/pkg/awsutils/imds_test.go
+++ b/pkg/awsutils/imds_test.go
@@ -159,10 +159,13 @@ func TestGetLocalIPv4s(t *testing.T) {
 		assert.Equal(t, ips, []net.IP{net.IPv4(10, 0, 114, 236), net.IPv4(10, 0, 120, 181)})
 	}
 
-	_, err = f.GetLocalIPv4s(context.TODO(), "00:00:de:ad:be:ef")
-	if assert.Error(t, err) {
-		assert.True(t, IsNotFound(err))
-	}
+	// This can be remove as we check if "local-ipv4s" field is present in the mac fields. The field not found  is verified in the function itself
+	// The function itself will return nil
+
+	// _, err = f.GetLocalIPv4s(context.TODO(), "00:00:de:ad:be:ef")
+	// if assert.Error(t, err) {
+	// 	assert.True(t, IsNotFound(err))
+	// }
 }
 
 func TestGetIPv6s(t *testing.T) {
@@ -217,10 +220,11 @@ func TestGetVPCIPv4CIDRBlocks(t *testing.T) {
 		assert.Equal(t, ips, []net.IPNet{{IP: net.IPv4(10, 0, 0, 0), Mask: net.CIDRMask(16, 32)}})
 	}
 
-	_, err = f.GetLocalIPv4s(context.TODO(), "00:00:de:ad:be:ef")
-	if assert.Error(t, err) {
-		assert.True(t, IsNotFound(err))
-	}
+	// This check can be removed as we check if the field is present in mac supported fields before returning as error not found.
+	// _, err = f.GetLocalIPv4s(context.TODO(), "00:00:de:ad:be:ef")
+	// if assert.Error(t, err) {
+	// 	assert.True(t, IsNotFound(err))
+	// }
 }
 
 func TestGetSubnetIPv6CIDRBlocks(t *testing.T) {
diff --git a/pkg/awsutils/mocks/awsutils_mocks.go b/pkg/awsutils/mocks/awsutils_mocks.go
index 7cefc58316..15ede4adfa 100644
--- a/pkg/awsutils/mocks/awsutils_mocks.go
+++ b/pkg/awsutils/mocks/awsutils_mocks.go
@@ -1,22 +1,8 @@
-// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License"). You may
-// not use this file except in compliance with the License. A copy of the
-// License is located at
-//
-//     http://aws.amazon.com/apache2.0/
-//
-// or in the "license" file accompanying this file. This file is distributed
-// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-// express or implied. See the License for the specific language governing
-// permissions and limitations under the License.
-//
-
 // Code generated by MockGen. DO NOT EDIT.
-// Source: github.com/aws/amazon-vpc-cni-k8s/pkg/awsutils (interfaces: APIs)
+// Source: awsutils.go
 
-// Package mock_awsutils is a generated GoMock package.
-package mock_awsutils
+// Package mocks is a generated GoMock package.
+package mocks
 
 import (
 	net "net"
@@ -54,90 +40,90 @@ func (m *MockAPIs) EXPECT() *MockAPIsMockRecorder {
 }
 
 // AllocENI mocks base method.
-func (m *MockAPIs) AllocENI(arg0 bool, arg1 []*string, arg2 string, arg3 int) (string, error) {
+func (m *MockAPIs) AllocENI(sg []*string, eniCfgSubnet string, numIPs, networkCard int) (string, error) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "AllocENI", arg0, arg1, arg2, arg3)
+	ret := m.ctrl.Call(m, "AllocENI", sg, eniCfgSubnet, numIPs, networkCard)
 	ret0, _ := ret[0].(string)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
 
 // AllocENI indicates an expected call of AllocENI.
-func (mr *MockAPIsMockRecorder) AllocENI(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
+func (mr *MockAPIsMockRecorder) AllocENI(sg, eniCfgSubnet, numIPs, networkCard interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllocENI", reflect.TypeOf((*MockAPIs)(nil).AllocENI), arg0, arg1, arg2, arg3)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllocENI", reflect.TypeOf((*MockAPIs)(nil).AllocENI), sg, eniCfgSubnet, numIPs, networkCard)
 }
 
 // AllocIPAddress mocks base method.
-func (m *MockAPIs) AllocIPAddress(arg0 string) error {
+func (m *MockAPIs) AllocIPAddress(eniID string) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "AllocIPAddress", arg0)
+	ret := m.ctrl.Call(m, "AllocIPAddress", eniID)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
 // AllocIPAddress indicates an expected call of AllocIPAddress.
-func (mr *MockAPIsMockRecorder) AllocIPAddress(arg0 interface{}) *gomock.Call {
+func (mr *MockAPIsMockRecorder) AllocIPAddress(eniID interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllocIPAddress", reflect.TypeOf((*MockAPIs)(nil).AllocIPAddress), arg0)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllocIPAddress", reflect.TypeOf((*MockAPIs)(nil).AllocIPAddress), eniID)
 }
 
 // AllocIPAddresses mocks base method.
-func (m *MockAPIs) AllocIPAddresses(arg0 string, arg1 int) (*ec2.AssignPrivateIpAddressesOutput, error) {
+func (m *MockAPIs) AllocIPAddresses(eniID string, numIPs int) (*ec2.AssignPrivateIpAddressesOutput, error) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "AllocIPAddresses", arg0, arg1)
+	ret := m.ctrl.Call(m, "AllocIPAddresses", eniID, numIPs)
 	ret0, _ := ret[0].(*ec2.AssignPrivateIpAddressesOutput)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
 
 // AllocIPAddresses indicates an expected call of AllocIPAddresses.
-func (mr *MockAPIsMockRecorder) AllocIPAddresses(arg0, arg1 interface{}) *gomock.Call {
+func (mr *MockAPIsMockRecorder) AllocIPAddresses(eniID, numIPs interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllocIPAddresses", reflect.TypeOf((*MockAPIs)(nil).AllocIPAddresses), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllocIPAddresses", reflect.TypeOf((*MockAPIs)(nil).AllocIPAddresses), eniID, numIPs)
 }
 
 // AllocIPv6Prefixes mocks base method.
-func (m *MockAPIs) AllocIPv6Prefixes(arg0 string) ([]*string, error) {
+func (m *MockAPIs) AllocIPv6Prefixes(eniID string) ([]*string, error) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "AllocIPv6Prefixes", arg0)
+	ret := m.ctrl.Call(m, "AllocIPv6Prefixes", eniID)
 	ret0, _ := ret[0].([]*string)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
 
 // AllocIPv6Prefixes indicates an expected call of AllocIPv6Prefixes.
-func (mr *MockAPIsMockRecorder) AllocIPv6Prefixes(arg0 interface{}) *gomock.Call {
+func (mr *MockAPIsMockRecorder) AllocIPv6Prefixes(eniID interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllocIPv6Prefixes", reflect.TypeOf((*MockAPIs)(nil).AllocIPv6Prefixes), arg0)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllocIPv6Prefixes", reflect.TypeOf((*MockAPIs)(nil).AllocIPv6Prefixes), eniID)
 }
 
 // DeallocIPAddresses mocks base method.
-func (m *MockAPIs) DeallocIPAddresses(arg0 string, arg1 []string) error {
+func (m *MockAPIs) DeallocIPAddresses(eniID string, ips []string) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "DeallocIPAddresses", arg0, arg1)
+	ret := m.ctrl.Call(m, "DeallocIPAddresses", eniID, ips)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
 // DeallocIPAddresses indicates an expected call of DeallocIPAddresses.
-func (mr *MockAPIsMockRecorder) DeallocIPAddresses(arg0, arg1 interface{}) *gomock.Call {
+func (mr *MockAPIsMockRecorder) DeallocIPAddresses(eniID, ips interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeallocIPAddresses", reflect.TypeOf((*MockAPIs)(nil).DeallocIPAddresses), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeallocIPAddresses", reflect.TypeOf((*MockAPIs)(nil).DeallocIPAddresses), eniID, ips)
 }
 
 // DeallocPrefixAddresses mocks base method.
-func (m *MockAPIs) DeallocPrefixAddresses(arg0 string, arg1 []string) error {
+func (m *MockAPIs) DeallocPrefixAddresses(eniID string, ips []string) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "DeallocPrefixAddresses", arg0, arg1)
+	ret := m.ctrl.Call(m, "DeallocPrefixAddresses", eniID, ips)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
 // DeallocPrefixAddresses indicates an expected call of DeallocPrefixAddresses.
-func (mr *MockAPIsMockRecorder) DeallocPrefixAddresses(arg0, arg1 interface{}) *gomock.Call {
+func (mr *MockAPIsMockRecorder) DeallocPrefixAddresses(eniID, ips interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeallocPrefixAddresses", reflect.TypeOf((*MockAPIs)(nil).DeallocPrefixAddresses), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeallocPrefixAddresses", reflect.TypeOf((*MockAPIs)(nil).DeallocPrefixAddresses), eniID, ips)
 }
 
 // DescribeAllENIs mocks base method.
@@ -170,17 +156,17 @@ func (mr *MockAPIsMockRecorder) FetchInstanceTypeLimits() *gomock.Call {
 }
 
 // FreeENI mocks base method.
-func (m *MockAPIs) FreeENI(arg0 string) error {
+func (m *MockAPIs) FreeENI(eniName string) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "FreeENI", arg0)
+	ret := m.ctrl.Call(m, "FreeENI", eniName)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
 // FreeENI indicates an expected call of FreeENI.
-func (mr *MockAPIsMockRecorder) FreeENI(arg0 interface{}) *gomock.Call {
+func (mr *MockAPIsMockRecorder) FreeENI(eniName interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FreeENI", reflect.TypeOf((*MockAPIs)(nil).FreeENI), arg0)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FreeENI", reflect.TypeOf((*MockAPIs)(nil).FreeENI), eniName)
 }
 
 // GetAttachedENIs mocks base method.
@@ -227,48 +213,48 @@ func (mr *MockAPIsMockRecorder) GetENILimit() *gomock.Call {
 }
 
 // GetIPv4PrefixesFromEC2 mocks base method.
-func (m *MockAPIs) GetIPv4PrefixesFromEC2(arg0 string) ([]types.Ipv4PrefixSpecification, error) {
+func (m *MockAPIs) GetIPv4PrefixesFromEC2(eniID string) ([]types.Ipv4PrefixSpecification, error) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetIPv4PrefixesFromEC2", arg0)
+	ret := m.ctrl.Call(m, "GetIPv4PrefixesFromEC2", eniID)
 	ret0, _ := ret[0].([]types.Ipv4PrefixSpecification)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
 
 // GetIPv4PrefixesFromEC2 indicates an expected call of GetIPv4PrefixesFromEC2.
-func (mr *MockAPIsMockRecorder) GetIPv4PrefixesFromEC2(arg0 interface{}) *gomock.Call {
+func (mr *MockAPIsMockRecorder) GetIPv4PrefixesFromEC2(eniID interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIPv4PrefixesFromEC2", reflect.TypeOf((*MockAPIs)(nil).GetIPv4PrefixesFromEC2), arg0)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIPv4PrefixesFromEC2", reflect.TypeOf((*MockAPIs)(nil).GetIPv4PrefixesFromEC2), eniID)
 }
 
 // GetIPv4sFromEC2 mocks base method.
-func (m *MockAPIs) GetIPv4sFromEC2(arg0 string) ([]types.NetworkInterfacePrivateIpAddress, error) {
+func (m *MockAPIs) GetIPv4sFromEC2(eniID string) ([]types.NetworkInterfacePrivateIpAddress, error) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetIPv4sFromEC2", arg0)
+	ret := m.ctrl.Call(m, "GetIPv4sFromEC2", eniID)
 	ret0, _ := ret[0].([]types.NetworkInterfacePrivateIpAddress)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
 
 // GetIPv4sFromEC2 indicates an expected call of GetIPv4sFromEC2.
-func (mr *MockAPIsMockRecorder) GetIPv4sFromEC2(arg0 interface{}) *gomock.Call {
+func (mr *MockAPIsMockRecorder) GetIPv4sFromEC2(eniID interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIPv4sFromEC2", reflect.TypeOf((*MockAPIs)(nil).GetIPv4sFromEC2), arg0)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIPv4sFromEC2", reflect.TypeOf((*MockAPIs)(nil).GetIPv4sFromEC2), eniID)
 }
 
 // GetIPv6PrefixesFromEC2 mocks base method.
-func (m *MockAPIs) GetIPv6PrefixesFromEC2(arg0 string) ([]types.Ipv6PrefixSpecification, error) {
+func (m *MockAPIs) GetIPv6PrefixesFromEC2(eniID string) ([]types.Ipv6PrefixSpecification, error) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetIPv6PrefixesFromEC2", arg0)
+	ret := m.ctrl.Call(m, "GetIPv6PrefixesFromEC2", eniID)
 	ret0, _ := ret[0].([]types.Ipv6PrefixSpecification)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
 
 // GetIPv6PrefixesFromEC2 indicates an expected call of GetIPv6PrefixesFromEC2.
-func (mr *MockAPIsMockRecorder) GetIPv6PrefixesFromEC2(arg0 interface{}) *gomock.Call {
+func (mr *MockAPIsMockRecorder) GetIPv6PrefixesFromEC2(eniID interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIPv6PrefixesFromEC2", reflect.TypeOf((*MockAPIs)(nil).GetIPv6PrefixesFromEC2), arg0)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIPv6PrefixesFromEC2", reflect.TypeOf((*MockAPIs)(nil).GetIPv6PrefixesFromEC2), eniID)
 }
 
 // GetInstanceHypervisorFamily mocks base method.
@@ -327,6 +313,20 @@ func (mr *MockAPIsMockRecorder) GetLocalIPv4() *gomock.Call {
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLocalIPv4", reflect.TypeOf((*MockAPIs)(nil).GetLocalIPv4))
 }
 
+// GetLocalIPv6 mocks base method.
+func (m *MockAPIs) GetLocalIPv6() net.IP {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetLocalIPv6")
+	ret0, _ := ret[0].(net.IP)
+	return ret0
+}
+
+// GetLocalIPv6 indicates an expected call of GetLocalIPv6.
+func (mr *MockAPIsMockRecorder) GetLocalIPv6() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLocalIPv6", reflect.TypeOf((*MockAPIs)(nil).GetLocalIPv6))
+}
+
 // GetNetworkCards mocks base method.
 func (m *MockAPIs) GetNetworkCards() []vpc.NetworkCard {
 	m.ctrl.T.Helper()
@@ -412,17 +412,17 @@ func (mr *MockAPIsMockRecorder) InitCachedPrefixDelegation(arg0 interface{}) *go
 }
 
 // IsMultiCardENI mocks base method.
-func (m *MockAPIs) IsMultiCardENI(arg0 string) bool {
+func (m *MockAPIs) IsMultiCardENI(eniID string) bool {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "IsMultiCardENI", arg0)
+	ret := m.ctrl.Call(m, "IsMultiCardENI", eniID)
 	ret0, _ := ret[0].(bool)
 	return ret0
 }
 
 // IsMultiCardENI indicates an expected call of IsMultiCardENI.
-func (mr *MockAPIsMockRecorder) IsMultiCardENI(arg0 interface{}) *gomock.Call {
+func (mr *MockAPIsMockRecorder) IsMultiCardENI(eniID interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsMultiCardENI", reflect.TypeOf((*MockAPIs)(nil).IsMultiCardENI), arg0)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsMultiCardENI", reflect.TypeOf((*MockAPIs)(nil).IsMultiCardENI), eniID)
 }
 
 // IsPrefixDelegationSupported mocks base method.
@@ -440,98 +440,98 @@ func (mr *MockAPIsMockRecorder) IsPrefixDelegationSupported() *gomock.Call {
 }
 
 // IsPrimaryENI mocks base method.
-func (m *MockAPIs) IsPrimaryENI(arg0 string) bool {
+func (m *MockAPIs) IsPrimaryENI(eniID string) bool {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "IsPrimaryENI", arg0)
+	ret := m.ctrl.Call(m, "IsPrimaryENI", eniID)
 	ret0, _ := ret[0].(bool)
 	return ret0
 }
 
 // IsPrimaryENI indicates an expected call of IsPrimaryENI.
-func (mr *MockAPIsMockRecorder) IsPrimaryENI(arg0 interface{}) *gomock.Call {
+func (mr *MockAPIsMockRecorder) IsPrimaryENI(eniID interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsPrimaryENI", reflect.TypeOf((*MockAPIs)(nil).IsPrimaryENI), arg0)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsPrimaryENI", reflect.TypeOf((*MockAPIs)(nil).IsPrimaryENI), eniID)
 }
 
 // IsUnmanagedENI mocks base method.
-func (m *MockAPIs) IsUnmanagedENI(arg0 string) bool {
+func (m *MockAPIs) IsUnmanagedENI(eniID string) bool {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "IsUnmanagedENI", arg0)
+	ret := m.ctrl.Call(m, "IsUnmanagedENI", eniID)
 	ret0, _ := ret[0].(bool)
 	return ret0
 }
 
 // IsUnmanagedENI indicates an expected call of IsUnmanagedENI.
-func (mr *MockAPIsMockRecorder) IsUnmanagedENI(arg0 interface{}) *gomock.Call {
+func (mr *MockAPIsMockRecorder) IsUnmanagedENI(eniID interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsUnmanagedENI", reflect.TypeOf((*MockAPIs)(nil).IsUnmanagedENI), arg0)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsUnmanagedENI", reflect.TypeOf((*MockAPIs)(nil).IsUnmanagedENI), eniID)
 }
 
 // RefreshSGIDs mocks base method.
-func (m *MockAPIs) RefreshSGIDs(arg0 string, arg1 *datastore.DataStore) error {
+func (m *MockAPIs) RefreshSGIDs(mac string, ds *datastore.DataStoreAccess) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "RefreshSGIDs", arg0, arg1)
+	ret := m.ctrl.Call(m, "RefreshSGIDs", mac, ds)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
 // RefreshSGIDs indicates an expected call of RefreshSGIDs.
-func (mr *MockAPIsMockRecorder) RefreshSGIDs(arg0, arg1 interface{}) *gomock.Call {
+func (mr *MockAPIsMockRecorder) RefreshSGIDs(mac, ds interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshSGIDs", reflect.TypeOf((*MockAPIs)(nil).RefreshSGIDs), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshSGIDs", reflect.TypeOf((*MockAPIs)(nil).RefreshSGIDs), mac, ds)
 }
 
 // SetMultiCardENIs mocks base method.
-func (m *MockAPIs) SetMultiCardENIs(arg0 []string) error {
+func (m *MockAPIs) SetMultiCardENIs(eniID []string) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SetMultiCardENIs", arg0)
+	ret := m.ctrl.Call(m, "SetMultiCardENIs", eniID)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
 // SetMultiCardENIs indicates an expected call of SetMultiCardENIs.
-func (mr *MockAPIsMockRecorder) SetMultiCardENIs(arg0 interface{}) *gomock.Call {
+func (mr *MockAPIsMockRecorder) SetMultiCardENIs(eniID interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetMultiCardENIs", reflect.TypeOf((*MockAPIs)(nil).SetMultiCardENIs), arg0)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetMultiCardENIs", reflect.TypeOf((*MockAPIs)(nil).SetMultiCardENIs), eniID)
 }
 
 // SetUnmanagedENIs mocks base method.
-func (m *MockAPIs) SetUnmanagedENIs(arg0 []string) {
+func (m *MockAPIs) SetUnmanagedENIs(eniIDs []string) {
 	m.ctrl.T.Helper()
-	m.ctrl.Call(m, "SetUnmanagedENIs", arg0)
+	m.ctrl.Call(m, "SetUnmanagedENIs", eniIDs)
 }
 
 // SetUnmanagedENIs indicates an expected call of SetUnmanagedENIs.
-func (mr *MockAPIsMockRecorder) SetUnmanagedENIs(arg0 interface{}) *gomock.Call {
+func (mr *MockAPIsMockRecorder) SetUnmanagedENIs(eniIDs interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetUnmanagedENIs", reflect.TypeOf((*MockAPIs)(nil).SetUnmanagedENIs), arg0)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetUnmanagedENIs", reflect.TypeOf((*MockAPIs)(nil).SetUnmanagedENIs), eniIDs)
 }
 
 // TagENI mocks base method.
-func (m *MockAPIs) TagENI(arg0 string, arg1 map[string]string) error {
+func (m *MockAPIs) TagENI(eniID string, currentTags map[string]string) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "TagENI", arg0, arg1)
+	ret := m.ctrl.Call(m, "TagENI", eniID, currentTags)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
 // TagENI indicates an expected call of TagENI.
-func (mr *MockAPIsMockRecorder) TagENI(arg0, arg1 interface{}) *gomock.Call {
+func (mr *MockAPIsMockRecorder) TagENI(eniID, currentTags interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TagENI", reflect.TypeOf((*MockAPIs)(nil).TagENI), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TagENI", reflect.TypeOf((*MockAPIs)(nil).TagENI), eniID, currentTags)
 }
 
 // WaitForENIAndIPsAttached mocks base method.
-func (m *MockAPIs) WaitForENIAndIPsAttached(arg0 string, arg1 int) (awsutils.ENIMetadata, error) {
+func (m *MockAPIs) WaitForENIAndIPsAttached(eni string, wantedSecondaryIPs int) (awsutils.ENIMetadata, error) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "WaitForENIAndIPsAttached", arg0, arg1)
+	ret := m.ctrl.Call(m, "WaitForENIAndIPsAttached", eni, wantedSecondaryIPs)
 	ret0, _ := ret[0].(awsutils.ENIMetadata)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
 
 // WaitForENIAndIPsAttached indicates an expected call of WaitForENIAndIPsAttached.
-func (mr *MockAPIsMockRecorder) WaitForENIAndIPsAttached(arg0, arg1 interface{}) *gomock.Call {
+func (mr *MockAPIsMockRecorder) WaitForENIAndIPsAttached(eni, wantedSecondaryIPs interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForENIAndIPsAttached", reflect.TypeOf((*MockAPIs)(nil).WaitForENIAndIPsAttached), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForENIAndIPsAttached", reflect.TypeOf((*MockAPIs)(nil).WaitForENIAndIPsAttached), eni, wantedSecondaryIPs)
 }
diff --git a/pkg/ipamd/datastore/data_store.go b/pkg/ipamd/datastore/data_store.go
index 02d5cd21f0..8d03b126a2 100644
--- a/pkg/ipamd/datastore/data_store.go
+++ b/pkg/ipamd/datastore/data_store.go
@@ -79,6 +79,9 @@ const backfillNetworkIface = "unknown"
 // ErrUnknownPod is an error when there is no pod in data store matching pod name, namespace, sandbox id
 var ErrUnknownPod = errors.New("datastore: unknown pod")
 
+// ErrNoAvailableIPInDataStore is an error when IPAM cannot assign an IP address from the datastore
+var ErrNoAvailableIPInDataStore = errors.New("AssignPodIPAddress: no available IP/Prefix addresses")
+
 // IPAMKey is the IPAM primary key.  Quoting CNI spec:
 //
 //	Plugins that store state should do so using a primary key of
@@ -103,6 +106,7 @@ func (k IPAMKey) String() string {
 type IPAMMetadata struct {
 	K8SPodNamespace string `json:"k8sPodNamespace,omitempty"`
 	K8SPodName      string `json:"k8sPodName,omitempty"`
+	InterfacesCount int    `json:"interfacesCount,omitempty"`
 }
 
 // ENI represents a single ENI. Exported fields will be marshaled for introspection.
@@ -461,7 +465,9 @@ func (ds *DataStore) AddENI(eniID string, deviceNumber int, isPrimary, isTrunk,
 		IsEFA:              isEFA,
 		ID:                 eniID,
 		DeviceNumber:       deviceNumber,
-		AvailableIPv4Cidrs: make(map[string]*CidrInfo)}
+		AvailableIPv4Cidrs: make(map[string]*CidrInfo),
+		IPv6Cidrs:          make(map[string]*CidrInfo),
+	}
 
 	prometheusmetrics.Enis.Set(float64(len(ds.eniPool)))
 	// Initialize ENI IPs In Use to 0 when an ENI is created
@@ -574,7 +580,7 @@ func (ds *DataStore) AddIPv6CidrToStore(eniID string, ipv6Cidr net.IPNet, isPref
 	}
 	// Check if already present in datastore.
 	_, ok = curENI.IPv6Cidrs[strIPv6Cidr]
-	ds.log.Debugf("IP not in  DS")
+	ds.log.Debugf("IP not in DS")
 	if ok {
 		ds.log.Debugf("IPv6 prefix %s already in DS", strIPv6Cidr)
 		return errors.New(IPAlreadyInStoreError)
@@ -661,7 +667,7 @@ func (ds *DataStore) AssignPodIPv6Address(ipamKey IPAMKey, ipamMetadata IPAMMeta
 		}
 	}
 	prometheusmetrics.NoAvailableIPAddrs.Inc()
-	return "", -1, errors.New("AssignPodIPv6Address: no available IP addresses")
+	return "", -1, ErrNoAvailableIPInDataStore
 }
 
 // AssignPodIPv4Address assigns an IPv4 address to pod
@@ -732,13 +738,13 @@ func (ds *DataStore) AssignPodIPv4Address(ipamKey IPAMKey, ipamMetadata IPAMMeta
 
 	prometheusmetrics.NoAvailableIPAddrs.Inc()
 	ds.log.Errorf("DataStore has no available IP/Prefix addresses")
-	return "", -1, errors.New("AssignPodIPv4Address: no available IP/Prefix addresses")
+	return "", -1, ErrNoAvailableIPInDataStore
 }
 
 // assignPodIPAddressUnsafe mark Address as assigned.
 func (ds *DataStore) assignPodIPAddressUnsafe(addr *AddressInfo, ipamKey IPAMKey, ipamMetadata IPAMMetadata, assignedTime time.Time) {
-	ds.log.Infof("assignPodIPAddressUnsafe: Assign IP %v to sandbox %s",
-		addr.Address, ipamKey)
+	ds.log.Infof("assignPodIPAddressUnsafe: Assign IP %v to sandbox %s with metadata %",
+		addr.Address, ipamKey, ipamMetadata)
 
 	if addr.Assigned() {
 		panic("addr already assigned")
@@ -1082,7 +1088,7 @@ func (ds *DataStore) RemoveENIFromDataStore(eniID string, force bool) error {
 
 // UnassignPodIPAddress a) find out the IP address based on PodName and PodNameSpace
 // b)  mark IP address as unassigned c) returns IP address, ENI's device number, error
-func (ds *DataStore) UnassignPodIPAddress(ipamKey IPAMKey) (e *ENI, ip string, deviceNumber int, err error) {
+func (ds *DataStore) UnassignPodIPAddress(ipamKey IPAMKey) (e *ENI, ip string, deviceNumber int, interfaces int, err error) {
 	ds.lock.Lock()
 	defer ds.lock.Unlock()
 	ds.log.Debugf("UnassignPodIPAddress: IP address pool stats: total %d, assigned %d, sandbox %s", ds.total, ds.assigned, ipamKey)
@@ -1099,7 +1105,7 @@ func (ds *DataStore) UnassignPodIPAddress(ipamKey IPAMKey) (e *ENI, ip string, d
 		// If entry is still not found, IPAMD has no knowledge of this pod, so there is nothing to do.
 		if addr == nil {
 			ds.log.Warnf("UnassignPodIPAddress: Failed to find sandbox %s", ipamKey)
-			return nil, "", 0, ErrUnknownPod
+			return nil, "", 0, -1, ErrUnknownPod
 		}
 	}
 
@@ -1109,17 +1115,22 @@ func (ds *DataStore) UnassignPodIPAddress(ipamKey IPAMKey) (e *ENI, ip string, d
 	if err := ds.writeBackingStoreUnsafe(); err != nil {
 		// Unwind un-assignment
 		ds.assignPodIPAddressUnsafe(addr, ipamKey, originalIPAMMetadata, originalAssignedTime)
-		return nil, "", 0, err
+		return nil, "", 0, -1, err
 	}
 	addr.UnassignedTime = time.Now()
 
+	// Interfaces Count 0 means this property did not exist in the datastore when we restored. A Pod entry always have atleast one interface
+	if originalIPAMMetadata.InterfacesCount == 0 {
+		originalIPAMMetadata.InterfacesCount += 1
+	}
+
 	//Update prometheus for ips per cidr
 	prometheusmetrics.IpsPerCidr.With(prometheus.Labels{"cidr": availableCidr.Cidr.String()}).Dec()
 	ds.log.Infof("UnassignPodIPAddress: sandbox %s's ipAddr %s, DeviceNumber %d",
 		ipamKey, addr.Address, eni.DeviceNumber)
 	// Decrement ENI IP usage when a pod is deallocated
 	prometheusmetrics.EniIPsInUse.WithLabelValues(eni.ID).Dec()
-	return eni, addr.Address, eni.DeviceNumber, nil
+	return eni, addr.Address, eni.DeviceNumber, originalIPAMMetadata.InterfacesCount, nil
 }
 
 // AllocatedIPs returns a recent snapshot of allocated sandbox<->IPs.
@@ -1534,3 +1545,39 @@ func (ds *DataStore) DeleteFromContainerRule(entry *CheckpointEntry) {
 		ds.log.Errorf("failed to delete fromPod rule, addr=%s", addr.String())
 	}
 }
+
+type DataStoreAccess struct {
+	DataStores []*DataStore
+}
+
+func InitializeDataStores(networkCards int, defaultDataStorePath string, enablePD bool, log logger.Logger) *DataStoreAccess {
+	var dslist []*DataStore
+	log.Infof("Initializing %d datastores for managed network cards", networkCards)
+	for i := range networkCards {
+		dsBackingStorePath := defaultDataStorePath
+		if i > 0 {
+			baseName := strings.TrimSuffix(defaultDataStorePath, ".json")
+			dsBackingStorePath = fmt.Sprintf("%s-nic-%d.json", baseName, i)
+		}
+		checkpointer := NewJSONFile(dsBackingStorePath)
+		dslist = append(dslist, NewDataStore(log, checkpointer, enablePD))
+	}
+
+	return &DataStoreAccess{
+		DataStores: dslist,
+	}
+}
+
+func (ds *DataStoreAccess) GetDataStore(index int) *DataStore {
+	return ds.DataStores[index]
+}
+
+func (ds *DataStoreAccess) ReadAllDataStores(enableIPv6 bool) error {
+
+	for i := range ds.DataStores {
+		if err := ds.GetDataStore(i).ReadBackingStore(enableIPv6); err != nil {
+			return errors.Wrap(err, "Error while reading datastore")
+		}
+	}
+	return nil
+}
diff --git a/pkg/ipamd/datastore/data_store_test.go b/pkg/ipamd/datastore/data_store_test.go
index 3efd872dbd..e7b6d90285 100644
--- a/pkg/ipamd/datastore/data_store_test.go
+++ b/pkg/ipamd/datastore/data_store_test.go
@@ -670,14 +670,15 @@ func TestPodIPv4Address(t *testing.T) {
 	_, _, err = ds.AssignPodIPv4Address(key4, IPAMMetadata{K8SPodNamespace: "default", K8SPodName: "sample-pod-4"})
 	assert.Error(t, err)
 	// Unassign unknown Pod
-	_, _, _, err = ds.UnassignPodIPAddress(key4)
+	_, _, _, _, err = ds.UnassignPodIPAddress(key4)
 	assert.Error(t, err)
 
-	_, _, deviceNum, err := ds.UnassignPodIPAddress(key2)
+	_, _, deviceNum, interfaces, err := ds.UnassignPodIPAddress(key2)
 	assert.NoError(t, err)
 	assert.Equal(t, ds.total, 3)
 	assert.Equal(t, ds.assigned, 2)
 	assert.Equal(t, deviceNum, pod1Ns2Device)
+	assert.Equal(t, interfaces, 1)
 	assert.Equal(t, len(ds.eniPool["eni-2"].AvailableIPv4Cidrs), 1)
 	assert.Equal(t, ds.eniPool["eni-2"].AssignedIPv4Addresses(), 0)
 	expectedCheckpointData = &CheckpointData{
@@ -860,10 +861,11 @@ func TestPodIPv4AddressWithPDEnabled(t *testing.T) {
 		cmp.Diff(checkpoint.Data, expectedCheckpointData, checkpointDataCmpOpts),
 	)
 
-	_, _, deviceNum, err := ds.UnassignPodIPAddress(key2)
+	_, _, deviceNum, interfaces, err := ds.UnassignPodIPAddress(key2)
 	assert.NoError(t, err)
 	assert.Equal(t, ds.total, 16)
 	assert.Equal(t, ds.assigned, 2)
+	assert.Equal(t, interfaces, 1)
 	assert.Equal(t, deviceNum, pod1Ns2Device)
 	assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 1)
 	assert.Equal(t, ds.eniPool["eni-1"].AssignedIPv4Addresses(), 2)
@@ -919,7 +921,7 @@ func TestGetIPStatsV4(t *testing.T) {
 		*ds.GetIPStats("4"),
 	)
 
-	_, _, _, err = ds.UnassignPodIPAddress(key2)
+	_, _, _, _, err = ds.UnassignPodIPAddress(key2)
 	assert.NoError(t, err)
 
 	assert.Equal(t,
@@ -972,7 +974,7 @@ func TestGetIPStatsV4WithPD(t *testing.T) {
 		*ds.GetIPStats("4"),
 	)
 
-	_, _, _, err = ds.UnassignPodIPAddress(key2)
+	_, _, _, _, err = ds.UnassignPodIPAddress(key2)
 	assert.NoError(t, err)
 
 	assert.Equal(t,
diff --git a/pkg/ipamd/introspect.go b/pkg/ipamd/introspect.go
index 7db9bd0f8d..8406be8571 100644
--- a/pkg/ipamd/introspect.go
+++ b/pkg/ipamd/introspect.go
@@ -123,7 +123,7 @@ func (c *IPAMContext) setupIntrospectionServer() *http.Server {
 
 func eniV1RequestHandler(ipam *IPAMContext) func(http.ResponseWriter, *http.Request) {
 	return func(w http.ResponseWriter, r *http.Request) {
-		responseJSON, err := json.Marshal(ipam.dataStore.GetENIInfos())
+		responseJSON, err := json.Marshal(ipam.dataStore[0].GetENIInfos())
 		if err != nil {
 			log.Errorf("Failed to marshal ENI data: %v", err)
 			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
diff --git a/pkg/ipamd/ipamd.go b/pkg/ipamd/ipamd.go
index 9b9b07ccdb..30d62e60ac 100644
--- a/pkg/ipamd/ipamd.go
+++ b/pkg/ipamd/ipamd.go
@@ -185,6 +185,8 @@ const (
 	// envEnableNetworkPolicy is used to enable IPAMD/CNI to send pod create events to network policy agent.
 	envNetworkPolicyMode     = "NETWORK_POLICY_ENFORCING_MODE"
 	defaultNetworkPolicyMode = "standard"
+
+	DefaultNetworkCard = 0
 )
 
 var log = logger.Get()
@@ -196,7 +198,8 @@ var (
 // IPAMContext contains node level control information
 type IPAMContext struct {
 	awsClient                 awsutils.APIs
-	dataStore                 *datastore.DataStore
+	dataStore                 []*datastore.DataStore
+	dataStoreAccess           *datastore.DataStoreAccess
 	k8sClient                 client.Client
 	enableIPv4                bool
 	enableIPv6                bool
@@ -207,7 +210,7 @@ type IPAMContext struct {
 	maxIPsPerENI              int
 	maxENI                    int
 	maxPrefixesPerENI         int
-	unmanagedENI              int
+	unmanagedENI              []int
 	numNetworkCards           int
 
 	warmENITarget        int
@@ -364,6 +367,7 @@ func New(k8sClient client.Client) (*IPAMContext, error) {
 	c.enableManageUntaggedMode = enableManageUntaggedMode()
 	c.enablePodIPAnnotation = enablePodIPAnnotation()
 	c.numNetworkCards = len(c.awsClient.GetNetworkCards())
+	c.unmanagedENI = make([]int, c.numNetworkCards)
 
 	c.networkPolicyMode, err = getNetworkPolicyMode()
 	if err != nil {
@@ -383,8 +387,6 @@ func New(k8sClient client.Client) (*IPAMContext, error) {
 
 	c.awsClient.InitCachedPrefixDelegation(c.enablePrefixDelegation)
 	c.myNodeName = os.Getenv(envNodeName)
-	checkpointer := datastore.NewJSONFile(dsBackingStorePath())
-	c.dataStore = datastore.NewDataStore(log, checkpointer, c.enablePrefixDelegation)
 
 	if err := c.nodeInit(); err != nil {
 		return nil, err
@@ -396,44 +398,59 @@ func (c *IPAMContext) nodeInit() error {
 	prometheusmetrics.IpamdActionsInprogress.WithLabelValues("nodeInit").Add(float64(1))
 	defer prometheusmetrics.IpamdActionsInprogress.WithLabelValues("nodeInit").Sub(float64(1))
 	var err error
-	var vpcV4CIDRs []string
+	var vpcCIDRs []string
+	var primaryIP net.IP
 	ctx := context.TODO()
 
 	log.Debugf("Start node init")
-	primaryV4IP := c.awsClient.GetLocalIPv4()
+
 	if err = c.initENIAndIPLimits(); err != nil {
 		return err
 	}
 
 	if c.enableIPv4 {
-		// Subnets currently will have both v4 and v6 CIDRs. Once EC2 launches v6 only Subnets, that will no longer
-		// be true and so it is safe (and only required) to get the v4 CIDR info only when IPv4 mode is enabled.
-		vpcV4CIDRs, err = c.awsClient.GetVPCIPv4CIDRs()
+		primaryIP = c.awsClient.GetLocalIPv4()
+		vpcCIDRs, err = c.awsClient.GetVPCIPv4CIDRs()
+		if err != nil {
+			return err
+		}
+	} else {
+		primaryIP = c.awsClient.GetLocalIPv6()
+		vpcCIDRs, err = c.awsClient.GetVPCIPv6CIDRs()
 		if err != nil {
 			return err
 		}
 	}
 
 	primaryENIMac := c.awsClient.GetPrimaryENImac()
-	err = c.networkClient.SetupHostNetwork(vpcV4CIDRs, primaryENIMac, &primaryV4IP, c.enablePodENI, c.enableIPv4, c.enableIPv6)
+
+	// For multi-card instance, non-VPC traffic is still routed out of Primary ENI
+	err = c.networkClient.SetupHostNetwork(vpcCIDRs, primaryENIMac, &primaryIP, c.enablePodENI, c.enableIPv4, c.enableIPv6)
 	if err != nil {
 		return errors.Wrap(err, "ipamd init: failed to set up host network")
 	}
+
 	err = c.networkClient.CleanUpStaleAWSChains(c.enableIPv4, c.enableIPv6)
 	if err != nil {
 		// We should not error if clean up fails since these chains don't affect the rules
 		log.Debugf("Failed to clean up stale AWS chains: %v", err)
 	}
 
+	// Queries IMDS for all attached ENIs and then compares it against EC2.
+	// Groups the ENIs into different types
 	metadataResult, err := c.awsClient.DescribeAllENIs()
 	if err != nil {
 		return errors.Wrap(err, "ipamd init: failed to retrieve attached ENIs info")
 	}
 
 	log.Debugf("DescribeAllENIs success: ENIs: %d, tagged: %d", len(metadataResult.ENIMetadata), len(metadataResult.TagMap))
-	c.awsClient.SetMultiCardENIs(metadataResult.MultiCardENIIDs)
 	c.setUnmanagedENIs(metadataResult.TagMap)
 	enis := c.filterUnmanagedENIs(metadataResult.ENIMetadata)
+	fmt.Printf("datastore %+v", c.dataStoreAccess)
+
+	if c.dataStoreAccess == nil {
+		c.dataStoreAccess = datastore.InitializeDataStores(c.numNetworkCards, dsBackingStorePath(), c.enablePrefixDelegation, log)
+	}
 
 	for _, eni := range enis {
 		log.Debugf("Discovered ENI %s, trying to set it up", eni.ENIID)
@@ -471,33 +488,21 @@ func (c *IPAMContext) nodeInit() error {
 		}
 	}
 
-	if err := c.dataStore.ReadBackingStore(c.enableIPv6); err != nil {
+	// Read from all backing datastores corresponding to the NICs
+	if err := c.dataStoreAccess.ReadAllDataStores(c.enableIPv6); err != nil {
 		return err
 	}
 
-	if c.enableIPv6 {
-		// Security Groups for Pods cannot be enabled for IPv4 at this point, as Custom Networking must be enabled first.
-		if c.enablePodENI {
-			// Try to patch CNINode with Security Groups for Pods feature.
-			c.tryEnableSecurityGroupsForPods(ctx)
-		}
-		// We will not support upgrading/converting an existing IPv4 cluster to operate in IPv6 mode. So, we will always
-		// start with a clean slate in IPv6 mode. We also do not have to deal with dynamic update of Prefix Delegation
-		// feature in IPv6 mode as we do not support (yet) a non-PD v6 option. In addition, we do not support custom
-		// networking in IPv6 mode yet, so we will skip the corresponding setup. This will save us from checking
-		// if IPv6 is enabled at multiple places. Once we start supporting these features in IPv6 mode, we can do away
-		// with this check and not change anything else in the below setup.
-		return nil
-	}
-
-	if c.enablePrefixDelegation {
-		// During upgrade or if prefix delgation knob is disabled to enabled then we
-		// might have secondary IPs attached to ENIs so doing a cleanup if not used before moving on
-		c.tryUnassignIPsFromENIs()
-	} else {
-		// When prefix delegation knob is enabled to disabled then we might
-		// have unused prefixes attached to the ENIs so need to cleanup
-		c.tryUnassignPrefixesFromENIs()
+	if c.enableIPv4 {
+		if c.enablePrefixDelegation {
+			// During upgrade or if prefix delgation knob is disabled to enabled then we
+			// might have secondary IPs attached to ENIs so doing a cleanup if not used before moving on
+			c.tryUnassignIPsFromENIs()
+		} else {
+			// When prefix delegation knob is enabled to disabled then we might
+			// have unused prefixes attached to the ENIs so need to cleanup
+			c.tryUnassignPrefixesFromENIs()
+		}
 	}
 
 	if err = c.configureIPRulesForPods(); err != nil {
@@ -505,21 +510,21 @@ func (c *IPAMContext) nodeInit() error {
 	}
 	// Spawning updateCIDRsRulesOnChange go-routine
 	go wait.Forever(func() {
-		vpcV4CIDRs = c.updateCIDRsRulesOnChange(vpcV4CIDRs)
+		vpcCIDRs = c.updateCIDRsRulesOnChange(vpcCIDRs)
 	}, 30*time.Second)
 
 	// RefreshSGIDs populates the ENI cache with ENI -> security group ID mappings, and so it must be called:
 	// 1. after managed/unmanaged ENIs have been determined
 	// 2. before any new ENIs are attached
-	if c.enableIPv4 && !c.disableENIProvisioning {
-		if err := c.awsClient.RefreshSGIDs(primaryENIMac, c.dataStore); err != nil {
+	if !c.disableENIProvisioning {
+		if err := c.awsClient.RefreshSGIDs(primaryENIMac, c.dataStoreAccess); err != nil {
 			return err
 		}
 
 		// Refresh security groups and VPC CIDR blocks in the background
 		// Ignoring errors since we will retry in 30s
 		go wait.Forever(func() {
-			c.awsClient.RefreshSGIDs(primaryENIMac, c.dataStore)
+			c.awsClient.RefreshSGIDs(primaryENIMac, c.dataStoreAccess)
 		}, 30*time.Second)
 	}
 
@@ -539,6 +544,8 @@ func (c *IPAMContext) nodeInit() error {
 	}
 	c.maxPods = int(maxPods)
 
+	// Custom Networking is not supported with IPv6 and we do not need to have to add a condition here for IPv6 as we validate
+	// the configs during initialization
 	if c.useCustomNetworking {
 		// When custom networking is enabled and a valid ENIConfig is found, IPAMD patches the CNINode
 		// resource for this instance. The operation is safe as enabling/disabling custom networking
@@ -565,19 +572,15 @@ func (c *IPAMContext) nodeInit() error {
 		c.tryEnableSecurityGroupsForPods(ctx)
 	}
 
-	// On node init, check if datastore pool needs to be increased. If so, attach CIDRs from existing ENIs and attach new ENIs.
-	datastorePoolTooLow, _ := c.isDatastorePoolTooLow()
-	if !c.disableENIProvisioning && datastorePoolTooLow {
-		if err := c.increaseDatastorePool(ctx); err != nil {
-			// Note that the only error currently returned by increaseDatastorePool is an error attaching CIDRs (other than insufficient IPs)
-			podENIErrInc("nodeInit")
-			return errors.New("error while trying to increase datastore pool")
-		}
-		// If custom networking is enabled and the pool is empty, return an error, as there is a misconfiguration and
-		// the node should not become ready.
-		if c.useCustomNetworking && c.isDatastorePoolEmpty() {
-			podENIErrInc("nodeInit")
-			return errors.New("Failed to attach any ENIs for custom networking")
+	if !c.disableENIProvisioning {
+		if c.enableIPv6 {
+			// We do not return an error here as create ENI failure shouldn not cause the node to go in not ready state.
+			// However that does restrict multi-nic pods to get failed
+			c.createSecondaryIPv6ENIs(ctx)
+		} else {
+			if err = c.handlePreScaling(ctx); err != nil {
+				return err
+			}
 		}
 	}
 
@@ -585,6 +588,28 @@ func (c *IPAMContext) nodeInit() error {
 	return nil
 }
 
+func (c *IPAMContext) handlePreScaling(ctx context.Context) error {
+
+	// On node init, check if datastore pool needs to be increased. If so, attach CIDRs from existing ENIs and attach new ENIs.
+	for networkCard, decisions := range c.isDatastorePoolTooLow() {
+		log.Debugf("is datastore pool low for networkCard %d, decision %t", networkCard, decisions.IsLow)
+		if decisions.IsLow {
+			if err := c.increaseDatastorePool(ctx, networkCard); err != nil {
+				// Note that the only error currently returned by increaseDatastorePool is an error attaching CIDRs (other than insufficient IPs)
+				podENIErrInc("nodeInit")
+				return errors.New("error while trying to increase datastore pool")
+			}
+			// If custom networking is enabled and the pool is empty, return an error, as there is a misconfiguration and
+			// the node should not become ready.
+			if c.useCustomNetworking && c.isDatastorePoolEmpty(networkCard) {
+				podENIErrInc("nodeInit")
+				return errors.New("Failed to attach any ENIs for custom networking")
+			}
+		}
+	}
+	return nil
+}
+
 func (c *IPAMContext) configureIPRulesForPods() error {
 	rules, err := c.networkClient.GetRuleList()
 	if err != nil {
@@ -592,15 +617,17 @@ func (c *IPAMContext) configureIPRulesForPods() error {
 		return nil
 	}
 
-	for _, info := range c.dataStore.AllocatedIPs() {
-		// TODO(gus): This should really be done via CNI CHECK calls, rather than in ipam (requires upstream k8s changes).
+	for _, ds := range c.dataStoreAccess.DataStores {
+		for _, info := range ds.AllocatedIPs() {
+			// TODO(gus): This should really be done via CNI CHECK calls, rather than in ipam (requires upstream k8s changes).
 
-		// Update ip rules in case there is a change in VPC CIDRs, AWS_VPC_K8S_CNI_EXTERNALSNAT setting
-		srcIPNet := net.IPNet{IP: net.ParseIP(info.IP), Mask: net.IPv4Mask(255, 255, 255, 255)}
+			// Update ip rules in case there is a change in VPC CIDRs, AWS_VPC_K8S_CNI_EXTERNALSNAT setting
+			srcIPNet := net.IPNet{IP: net.ParseIP(info.IP), Mask: net.IPv4Mask(255, 255, 255, 255)}
 
-		err = c.networkClient.UpdateRuleListBySrc(rules, srcIPNet)
-		if err != nil {
-			log.Warnf("UpdateRuleListBySrc in nodeInit() failed for IP %s: %v", info.IP, err)
+			err = c.networkClient.UpdateRuleListBySrc(rules, srcIPNet)
+			if err != nil {
+				log.Warnf("UpdateRuleListBySrc in nodeInit() failed for IP %s: %v", info.IP, err)
+			}
 		}
 	}
 
@@ -645,7 +672,7 @@ func (c *IPAMContext) updateIPStats(unmanaged int) {
 func (c *IPAMContext) StartNodeIPPoolManager() {
 	// For IPv6, if Security Groups for Pods is enabled, wait until trunk ENI is attached and add it to the datastore.
 	if c.enableIPv6 {
-		if c.enablePodENI && c.dataStore.GetTrunkENI() == "" {
+		if c.enablePodENI && c.dataStoreAccess.GetDataStore(DefaultNetworkCard).GetTrunkENI() == "" {
 			for !c.checkForTrunkENI() {
 				time.Sleep(ipPoolMonitorInterval)
 			}
@@ -671,48 +698,59 @@ func (c *IPAMContext) StartNodeIPPoolManager() {
 
 func (c *IPAMContext) updateIPPoolIfRequired(ctx context.Context) {
 	// When IPv4 Security Groups for Pods is configured, do not write to CNINode until there is room for a trunk ENI
-	if c.enablePodENI && c.enableIPv4 && c.dataStore.GetTrunkENI() == "" {
+	if c.enablePodENI && c.enableIPv4 && c.dataStoreAccess.GetDataStore(DefaultNetworkCard).GetTrunkENI() == "" {
 		c.tryEnableSecurityGroupsForPods(ctx)
 	}
 
-	datastorePoolTooLow, stats := c.isDatastorePoolTooLow()
-	// Each iteration, log the current datastore IP stats
-	log.Debugf("IP stats - total IPs: %d, assigned IPs: %d, cooldown IPs: %d", stats.TotalIPs, stats.AssignedIPs, stats.CooldownIPs)
+	isScaleDownExecuted := false
+	for networkCard, decisions := range c.isDatastorePoolTooLow() {
+		// Each iteration, log the current datastore IP stats
+		log.Debugf("IP stats for Network Card %d - total IPs: %d, assigned IPs: %d, cooldown IPs: %d", networkCard, decisions.Stats.TotalIPs, decisions.Stats.AssignedIPs, decisions.Stats.CooldownIPs)
+		if decisions.IsLow {
+			c.increaseDatastorePool(ctx, networkCard)
+		} else if c.isDatastorePoolTooHigh(decisions.Stats, networkCard) && !c.shouldSkipDataStorePoolDecrease() {
+			isScaleDownExecuted = true
+			c.decreaseDatastorePool(networkCard)
+		}
+		if c.shouldRemoveExtraENIs(decisions.Stats, networkCard) {
+			c.tryFreeENI(networkCard)
+		}
+	}
 
-	if datastorePoolTooLow {
-		c.increaseDatastorePool(ctx)
-	} else if c.isDatastorePoolTooHigh(stats) {
-		c.decreaseDatastorePool(decreaseIPPoolInterval)
+	// Store the last update time so that we only scale down every
+	if isScaleDownExecuted {
+		now := time.Now()
+		c.lastDecreaseIPPool = now
+		c.lastNodeIPPoolAction = now
 	}
-	if c.shouldRemoveExtraENIs() {
-		c.tryFreeENI()
+}
+
+func (c *IPAMContext) shouldSkipDataStorePoolDecrease() bool {
+
+	timeSinceLastPoolDecrease := time.Since(c.lastDecreaseIPPool)
+
+	if timeSinceLastPoolDecrease <= decreaseIPPoolInterval {
+		log.Debugf("Skipping decrease Datastore pool because time since last %v <= %v", timeSinceLastPoolDecrease, decreaseIPPoolInterval)
+		return true
 	}
+
+	return false
 }
 
 // decreaseDatastorePool runs every `interval` and attempts to return unused ENIs and IPs
-func (c *IPAMContext) decreaseDatastorePool(interval time.Duration) {
-	log.Debug("Starting to decrease pool size")
+func (c *IPAMContext) decreaseDatastorePool(networkCard int) {
 	prometheusmetrics.IpamdActionsInprogress.WithLabelValues("decreaseDatastorePool").Add(float64(1))
 	defer prometheusmetrics.IpamdActionsInprogress.WithLabelValues("decreaseDatastorePool").Sub(float64(1))
 
-	now := time.Now()
-	timeSinceLast := now.Sub(c.lastDecreaseIPPool)
-	if timeSinceLast <= interval {
-		log.Debugf("Skipping decrease Datastore pool because time since last %v <= %v", timeSinceLast, interval)
-		return
-	}
-
-	log.Debugf("Starting to decrease Datastore pool")
-	c.tryUnassignCidrsFromAll()
-	c.lastDecreaseIPPool = now
-	c.lastNodeIPPoolAction = now
+	log.Debugf("Starting to decrease pool size for network card %d", networkCard)
+	c.tryUnassignCidrsFromAll(networkCard)
 
 	log.Debugf("Successfully decreased IP pool")
-	c.logPoolStats(c.dataStore.GetIPStats(ipV4AddrFamily))
+	c.logPoolStats(c.dataStoreAccess.GetDataStore(networkCard).GetIPStats(ipV4AddrFamily), networkCard)
 }
 
 // tryFreeENI always tries to free one ENI
-func (c *IPAMContext) tryFreeENI() {
+func (c *IPAMContext) tryFreeENI(networkCard int) {
 	if c.isTerminating() {
 		log.Debug("AWS CNI is terminating, not detaching any ENIs")
 		return
@@ -723,12 +761,12 @@ func (c *IPAMContext) tryFreeENI() {
 		return
 	}
 
-	eni := c.dataStore.RemoveUnusedENIFromStore(c.warmIPTarget, c.minimumIPTarget, c.warmPrefixTarget)
+	eni := c.dataStoreAccess.GetDataStore(networkCard).RemoveUnusedENIFromStore(c.warmIPTarget, c.minimumIPTarget, c.warmPrefixTarget)
 	if eni == "" {
 		return
 	}
 
-	log.Debugf("Start freeing ENI %s", eni)
+	log.Debugf("Start freeing ENI %s from network card %d", eni, networkCard)
 	err := c.awsClient.FreeENI(eni)
 	if err != nil {
 		ipamdErrInc("decreaseIPPoolFreeENIFailed")
@@ -739,18 +777,18 @@ func (c *IPAMContext) tryFreeENI() {
 }
 
 // When warm IP/prefix targets are defined, free extra IPs
-func (c *IPAMContext) tryUnassignCidrsFromAll() {
-	_, over, warmIPTargetsDefined := c.datastoreTargetState(nil)
+func (c *IPAMContext) tryUnassignCidrsFromAll(networkCard int) {
+	_, over, warmIPTargetsDefined := c.datastoreTargetState(nil, networkCard)
 	// If WARM IP targets are not defined, check if WARM_PREFIX_TARGET is defined.
 	if !warmIPTargetsDefined {
-		over = c.computeExtraPrefixesOverWarmTarget()
+		over = c.computeExtraPrefixesOverWarmTarget(networkCard)
 	}
 
 	if over > 0 {
-		eniInfos := c.dataStore.GetENIInfos()
+		eniInfos := c.dataStoreAccess.GetDataStore(networkCard).GetENIInfos()
 		for eniID := range eniInfos.ENIs {
 			// Either returns prefixes or IPs [Cidrs]
-			cidrs := c.dataStore.FindFreeableCidrs(eniID)
+			cidrs := c.dataStoreAccess.GetDataStore(networkCard).FindFreeableCidrs(eniID)
 			if cidrs == nil {
 				log.Errorf("Error finding unassigned IPs for ENI %s", eniID)
 				continue
@@ -769,9 +807,9 @@ func (c *IPAMContext) tryUnassignCidrsFromAll() {
 			for _, toDelete := range cidrs {
 				// Do not force the delete, since a freeable Cidr might have been assigned to a pod
 				// before we get around to deleting it.
-				err := c.dataStore.DelIPv4CidrFromStore(eniID, toDelete.Cidr, false /* force */)
+				err := c.dataStoreAccess.GetDataStore(networkCard).DelIPv4CidrFromStore(eniID, toDelete.Cidr, false /* force */)
 				if err != nil {
-					log.Warnf("Failed to delete Cidr %s on ENI %s from datastore: %s", toDelete, eniID, err)
+					log.Warnf("Failed to delete Cidr %s on ENI %s from datastore for network card %d: %s", toDelete, eniID, networkCard, err)
 					ipamdErrInc("decreaseIPPool")
 					continue
 				} else {
@@ -790,18 +828,25 @@ func (c *IPAMContext) tryUnassignCidrsFromAll() {
 	}
 }
 
-// PRECONDITION: isDatastorePoolTooLow returned true
-func (c *IPAMContext) increaseDatastorePool(ctx context.Context) error {
-	log.Debug("Starting to increase pool size")
-	prometheusmetrics.IpamdActionsInprogress.WithLabelValues("increaseDatastorePool").Add(float64(1))
-	defer prometheusmetrics.IpamdActionsInprogress.WithLabelValues("increaseDatastorePool").Sub(float64(1))
-
+func (c *IPAMContext) isENIAttachmentAllowed() bool {
 	if c.isTerminating() {
 		log.Debug("AWS CNI is terminating, will not try to attach any new IPs or ENIs right now")
-		return nil
+		return false
 	}
 	if !c.manageENIsNonScheduleable && c.isNodeNonSchedulable() {
 		log.Debug("AWS CNI is on a non schedulable node, will not try to attach any new IPs or ENIs right now")
+		return false
+	}
+	return true
+}
+
+// PRECONDITION: isDatastorePoolTooLow returned true
+func (c *IPAMContext) increaseDatastorePool(ctx context.Context, networkCard int) error {
+	log.Debugf("Starting to increase pool size for network card %d", networkCard)
+	prometheusmetrics.IpamdActionsInprogress.WithLabelValues("increaseDatastorePool").Add(float64(1))
+	defer prometheusmetrics.IpamdActionsInprogress.WithLabelValues("increaseDatastorePool").Sub(float64(1))
+
+	if !c.isENIAttachmentAllowed() {
 		return nil
 	}
 
@@ -811,7 +856,7 @@ func (c *IPAMContext) increaseDatastorePool(ctx context.Context) error {
 		return nil
 	}
 
-	increasedPool, err := c.tryAssignCidrs()
+	increasedPool, err := c.tryAssignCidrs(networkCard)
 	if err != nil {
 		if containsInsufficientCIDRsOrSubnetIPs(err) {
 			log.Errorf("Unable to attach IPs/Prefixes for the ENI, subnet doesn't seem to have enough IPs/Prefixes. Consider using new subnet or carve a reserved range using create-subnet-cidr-reservation")
@@ -822,12 +867,12 @@ func (c *IPAMContext) increaseDatastorePool(ctx context.Context) error {
 		return err
 	}
 	if increasedPool {
-		c.updateLastNodeIPPoolAction()
+		c.updateLastNodeIPPoolAction(networkCard)
 	} else {
 		// If we did not add any IPs, try to allocate an ENI.
-		if c.hasRoomForEni() {
-			if err = c.tryAllocateENI(ctx); err == nil {
-				c.updateLastNodeIPPoolAction()
+		if c.hasRoomForEni(networkCard) {
+			if err = c.tryAllocateENI(ctx, networkCard); err == nil {
+				c.updateLastNodeIPPoolAction(networkCard)
 			} else {
 				// Note that no error is returned if ENI allocation fails. This is because ENI allocation failure should not cause node to be "NotReady".
 				log.Debugf("Error trying to allocate ENI: %v", err)
@@ -839,13 +884,46 @@ func (c *IPAMContext) increaseDatastorePool(ctx context.Context) error {
 	return nil
 }
 
-func (c *IPAMContext) updateLastNodeIPPoolAction() {
+func (c *IPAMContext) createSecondaryIPv6ENIs(ctx context.Context) error {
+
+	log.Info("Trying to see if new ENIs need to be created")
+	if !c.isENIAttachmentAllowed() {
+		return nil
+	}
+
+	log.Info("Attaching ENIs allowed")
+
+	for networkCard, ds := range c.dataStoreAccess.DataStores {
+		if networkCard == 0 {
+			continue
+		}
+
+		log.Infof("Checking for DS %d", networkCard)
+		if ds.GetENIs() == 0 {
+			// This means we have not added an ENI before as it would have been detected ENI detection
+			log.Infof("Attaching ENI to Network Card %d, MaxENI :%d", networkCard, c.maxENI)
+			if c.hasRoomForEni(networkCard) {
+				if err := c.tryAllocateENI(ctx, networkCard); err == nil {
+					log.Infof("Allocated ENI successfully to Network Card %d", networkCard)
+				} else {
+					// Note that no error is returned if ENI allocation fails. This is because ENI allocation failure should not cause node to be "NotReady".
+					log.Debugf("Error trying to allocate ENI: %v", err)
+				}
+			} else {
+				log.Debugf("Skipping ENI allocation as the max ENI limit is already reached for the instance")
+			}
+		}
+	}
+	return nil
+}
+
+func (c *IPAMContext) updateLastNodeIPPoolAction(networkCard int) {
 	c.lastNodeIPPoolAction = time.Now()
-	stats := c.dataStore.GetIPStats(ipV4AddrFamily)
-	c.logPoolStats(stats)
+	stats := c.dataStoreAccess.GetDataStore(networkCard).GetIPStats(ipV4AddrFamily)
+	c.logPoolStats(stats, networkCard)
 }
 
-func (c *IPAMContext) tryAllocateENI(ctx context.Context) error {
+func (c *IPAMContext) tryAllocateENI(ctx context.Context, networkCard int) error {
 	var securityGroups []*string
 	var eniCfgSubnet string
 
@@ -864,9 +942,9 @@ func (c *IPAMContext) tryAllocateENI(ctx context.Context) error {
 		eniCfgSubnet = eniCfg.Subnet
 	}
 
-	resourcesToAllocate := c.GetENIResourcesToAllocate()
+	resourcesToAllocate := c.GetENIResourcesToAllocate(networkCard)
 	if resourcesToAllocate > 0 {
-		eni, err := c.awsClient.AllocENI(c.useCustomNetworking, securityGroups, eniCfgSubnet, resourcesToAllocate)
+		eni, err := c.awsClient.AllocENI(securityGroups, eniCfgSubnet, resourcesToAllocate, networkCard)
 		if err != nil {
 			log.Errorf("Failed to increase pool size due to not able to allocate ENI %v", err)
 			ipamdErrInc("increaseIPPoolAllocENI")
@@ -882,15 +960,14 @@ func (c *IPAMContext) tryAllocateENI(ctx context.Context) error {
 		eniMetadata, err := c.awsClient.WaitForENIAndIPsAttached(eni, resourcesToAllocate)
 		if err != nil {
 			ipamdErrInc("increaseIPPoolwaitENIAttachedFailed")
-			log.Errorf("Failed to increase pool size: Unable to discover attached ENI from metadata service %v", err)
+			log.Errorf("Failed to increase pool size for Network card %d: Unable to discover attached ENI from metadata service %v", networkCard, err)
 			return err
 		}
-
 		// The CNI does not create trunk or EFA ENIs, so they will always be false here
 		err = c.setupENI(eni, eniMetadata, false, false)
 		if err != nil {
 			ipamdErrInc("increaseIPPoolsetupENIFailed")
-			log.Errorf("Failed to increase pool size: %v", err)
+			log.Errorf("Failed to increase pool size for network card %d: %v", networkCard, err)
 			return err
 		}
 	} else {
@@ -901,20 +978,20 @@ func (c *IPAMContext) tryAllocateENI(ctx context.Context) error {
 
 // For an ENI, fill in missing IPs or prefixes.
 // PRECONDITION: isDatastorePoolTooLow returned true
-func (c *IPAMContext) tryAssignCidrs() (increasedPool bool, err error) {
+func (c *IPAMContext) tryAssignCidrs(networkCard int) (increasedPool bool, err error) {
 	if c.enablePrefixDelegation {
-		return c.tryAssignPrefixes()
+		return c.tryAssignPrefixes(networkCard)
 	} else {
-		return c.tryAssignIPs()
+		return c.tryAssignIPs(networkCard)
 	}
 }
 
 // For an ENI, try to fill in missing IPs on an existing ENI.
 // PRECONDITION: isDatastorePoolTooLow returned true
-func (c *IPAMContext) tryAssignIPs() (increasedPool bool, err error) {
+func (c *IPAMContext) tryAssignIPs(networkCard int) (increasedPool bool, err error) {
 
 	// If WARM_IP_TARGET is set, only proceed if we are short of target
-	short, _, warmIPTargetsDefined := c.datastoreTargetState(nil)
+	short, _, warmIPTargetsDefined := c.datastoreTargetState(nil, networkCard)
 	if warmIPTargetsDefined && short == 0 {
 		return false, nil
 	}
@@ -926,7 +1003,7 @@ func (c *IPAMContext) tryAssignIPs() (increasedPool bool, err error) {
 	}
 
 	// Find an ENI where we can add more IPs
-	enis := c.dataStore.GetAllocatableENIs(c.maxIPsPerENI, c.useCustomNetworking)
+	enis := c.dataStoreAccess.GetDataStore(networkCard).GetAllocatableENIs(c.maxIPsPerENI, c.useCustomNetworking)
 	for _, eni := range enis {
 		if len(eni.AvailableIPv4Cidrs) < c.maxIPsPerENI {
 			currentNumberOfAllocatedIPs := len(eni.AvailableIPv4Cidrs)
@@ -967,14 +1044,14 @@ func (c *IPAMContext) tryAssignIPs() (increasedPool bool, err error) {
 					ec2ip4s = append(ec2ip4s, ec2types.NetworkInterfacePrivateIpAddress{PrivateIpAddress: ec2Addr.PrivateIpAddress})
 				}
 			}
-			c.addENIsecondaryIPsToDataStore(ec2ip4s, eni.ID)
+			c.addENIsecondaryIPsToDataStore(ec2ip4s, eni.ID, networkCard)
 			return true, nil
 		}
 	}
 	return false, nil
 }
 
-func (c *IPAMContext) assignIPv6Prefix(eniID string) (err error) {
+func (c *IPAMContext) assignIPv6Prefix(eniID string, networkCard int) (err error) {
 	log.Debugf("Assigning an IPv6Prefix for ENI: %s", eniID)
 	//Let's make an EC2 API call to get a list of IPv6 prefixes (if any) that are already attached to the
 	//current ENI. We will make this call only once during boot up/init and doing so will shield us from any
@@ -1009,16 +1086,16 @@ func (c *IPAMContext) assignIPv6Prefix(eniID string) (err error) {
 		//Will use the first IPv6 Prefix attached for IP address allocation.
 		ec2v6Prefixes = []ec2types.Ipv6PrefixSpecification{ec2v6Prefixes[0]}
 	}
-	c.addENIv6prefixesToDataStore(ec2v6Prefixes, eniID)
+	c.addENIv6prefixesToDataStore(ec2v6Prefixes, eniID, networkCard)
 	return nil
 }
 
 // PRECONDITION: isDatastorePoolTooLow returned true
-func (c *IPAMContext) tryAssignPrefixes() (increasedPool bool, err error) {
-	toAllocate := c.getPrefixesNeeded()
+func (c *IPAMContext) tryAssignPrefixes(networkCard int) (increasedPool bool, err error) {
+	toAllocate := c.getPrefixesNeeded(networkCard)
 	// Returns an ENI which has space for more prefixes to be attached, but this
 	// ENI might not suffice the WARM_IP_TARGET/WARM_PREFIX_TARGET
-	enis := c.dataStore.GetAllocatableENIs(c.maxPrefixesPerENI, c.useCustomNetworking)
+	enis := c.dataStoreAccess.GetDataStore(networkCard).GetAllocatableENIs(c.maxPrefixesPerENI, c.useCustomNetworking)
 	for _, eni := range enis {
 		currentNumberOfAllocatedPrefixes := len(eni.AvailableIPv4Cidrs)
 		resourcesToAllocate := min((c.maxPrefixesPerENI - currentNumberOfAllocatedPrefixes), toAllocate)
@@ -1053,7 +1130,7 @@ func (c *IPAMContext) tryAssignPrefixes() (increasedPool bool, err error) {
 			}
 			ec2Prefixes = output.AssignedIpv4Prefixes
 		}
-		c.addENIv4prefixesToDataStore(ec2Prefixes, eni.ID)
+		c.addENIv4prefixesToDataStore(ec2Prefixes, eni.ID, networkCard)
 		return true, nil
 	}
 	return false, nil
@@ -1063,76 +1140,82 @@ func (c *IPAMContext) tryAssignPrefixes() (increasedPool bool, err error) {
 // 1) add ENI to datastore
 // 2) set up linux ENI related networking stack.
 // 3) add all ENI's secondary IP addresses to datastore
+
+// TODO: Will primary ENI so that we can skip Network Setup ?
 func (c *IPAMContext) setupENI(eni string, eniMetadata awsutils.ENIMetadata, isTrunkENI, isEFAENI bool) error {
 	primaryENI := c.awsClient.GetPrimaryENI()
 	// Add the ENI to the datastore
-	err := c.dataStore.AddENI(eni, eniMetadata.DeviceNumber, eni == primaryENI, isTrunkENI, isEFAENI)
+	err := c.dataStoreAccess.GetDataStore(eniMetadata.NetworkCard).AddENI(eni, eniMetadata.DeviceNumber, eni == primaryENI, isTrunkENI, isEFAENI)
 	if err != nil && err.Error() != datastore.DuplicatedENIError {
 		return errors.Wrapf(err, "failed to add ENI %s to data store", eni)
 	}
-	// Store the addressable IP for the ENI
+
+	// Store the addressable IP for the ENI.
 	if c.enableIPv6 {
 		c.primaryIP[eni] = eniMetadata.PrimaryIPv6Address()
 	} else {
 		c.primaryIP[eni] = eniMetadata.PrimaryIPv4Address()
 	}
 
-	if c.enableIPv6 && eni == primaryENI {
-		// In v6 PD mode, VPC CNI will only manage the primary ENI and trunk ENI. Once we start supporting secondary
-		// IP and custom networking modes for IPv6, this restriction can be relaxed.
-		err := c.assignIPv6Prefix(eni)
-		if err != nil {
-			return errors.Wrapf(err, "Failed to allocate IPv6 Prefixes to Primary ENI")
-		}
-	} else {
-		// For other ENIs, set up the network
-		if eni != primaryENI {
-			subnetCidr := eniMetadata.SubnetIPv4CIDR
-			if c.enableIPv6 {
-				subnetCidr = eniMetadata.SubnetIPv6CIDR
-			}
-			err = c.networkClient.SetupENINetwork(c.primaryIP[eni], eniMetadata.MAC, eniMetadata.DeviceNumber, subnetCidr)
+	if c.enableIPv6 {
+		if !isTrunkENI {
+			// In v6 PD mode, VPC CNI will manage the primary ENI, ENIs on NC > 0 and trunk ENI. Once we start supporting secondary
+			// IP and custom networking modes for IPv6, this restriction can be relaxed.
+			err := c.assignIPv6Prefix(eni, eniMetadata.NetworkCard)
 			if err != nil {
-				// Failed to set up the ENI
-				errRemove := c.dataStore.RemoveENIFromDataStore(eni, true)
-				if errRemove != nil {
-					log.Warnf("failed to remove ENI %s: %v", eni, errRemove)
-				}
-				delete(c.primaryIP, eni)
-				return errors.Wrapf(err, "failed to set up ENI %s network", eni)
+				return errors.Wrapf(err, "Failed to allocate IPv6 Prefixes to ENI")
 			}
 		}
-		if !c.enableIPv6 {
-			log.Infof("Found ENIs having %d secondary IPs and %d Prefixes", len(eniMetadata.IPv4Addresses), len(eniMetadata.IPv4Prefixes))
-			// Either case add the IPs and prefixes to datastore.
-			c.addENIsecondaryIPsToDataStore(eniMetadata.IPv4Addresses, eni)
-			c.addENIv4prefixesToDataStore(eniMetadata.IPv4Prefixes, eni)
-		} else {
-			// This is a trunk ENI in IPv6 PD mode, so do not add IPs or prefixes to datastore
-			log.Infof("Found IPv6 trunk ENI having %d secondary IPs and %d Prefixes", len(eniMetadata.IPv6Addresses), len(eniMetadata.IPv6Prefixes))
+	}
+
+	// For other ENIs, set up the network
+	if eni != primaryENI {
+		subnetCidr := eniMetadata.SubnetIPv4CIDR
+		if c.enableIPv6 {
+			subnetCidr = eniMetadata.SubnetIPv6CIDR
+		}
+		err = c.networkClient.SetupENINetwork(c.primaryIP[eni], eniMetadata.MAC, eniMetadata.DeviceNumber, eniMetadata.NetworkCard, subnetCidr, c.maxENI)
+		if err != nil {
+			// Failed to set up the ENI
+			errRemove := c.dataStoreAccess.GetDataStore(eniMetadata.NetworkCard).RemoveENIFromDataStore(eni, true)
+			if errRemove != nil {
+				log.Warnf("failed to remove ENI %s: %v", eni, errRemove)
+			}
+			delete(c.primaryIP, eni)
+			return errors.Wrapf(err, "failed to set up ENI %s network", eni)
 		}
 	}
+
+	if !c.enableIPv6 {
+		log.Infof("Found ENIs having %d secondary IPs and %d Prefixes", len(eniMetadata.IPv4Addresses), len(eniMetadata.IPv4Prefixes))
+		// Either case add the IPs and prefixes to datastore.
+		c.addENIsecondaryIPsToDataStore(eniMetadata.IPv4Addresses, eni, eniMetadata.NetworkCard)
+		c.addENIv4prefixesToDataStore(eniMetadata.IPv4Prefixes, eni, eniMetadata.NetworkCard)
+	} else {
+		log.Infof("Found IPv6 ENI %s having %d secondary IPs and %d Prefixes and isTrunk: %t", eni, len(eniMetadata.IPv6Addresses), len(eniMetadata.IPv6Prefixes), isTrunkENI)
+	}
+
 	return nil
 }
 
-func (c *IPAMContext) addENIsecondaryIPsToDataStore(ec2PrivateIpAddrs []ec2types.NetworkInterfacePrivateIpAddress, eni string) {
+func (c *IPAMContext) addENIsecondaryIPsToDataStore(ec2PrivateIpAddrs []ec2types.NetworkInterfacePrivateIpAddress, eni string, networkCard int) {
 	// Add all the secondary IPs
 	for _, ec2PrivateIpAddr := range ec2PrivateIpAddrs {
 		if aws.ToBool(ec2PrivateIpAddr.Primary) {
 			continue
 		}
 		cidr := net.IPNet{IP: net.ParseIP(aws.ToString(ec2PrivateIpAddr.PrivateIpAddress)), Mask: net.IPv4Mask(255, 255, 255, 255)}
-		err := c.dataStore.AddIPv4CidrToStore(eni, cidr, false)
+		err := c.dataStoreAccess.GetDataStore(networkCard).AddIPv4CidrToStore(eni, cidr, false)
 		if err != nil && err.Error() != datastore.IPAlreadyInStoreError {
-			log.Warnf("Failed to increase IP pool, failed to add IP %s to data store", ec2PrivateIpAddr.PrivateIpAddress)
+			log.Warnf("Failed to increase IP pool, failed to add IP %s to data store for network card %d", ec2PrivateIpAddr.PrivateIpAddress, networkCard)
 			// continue to add next address
 			ipamdErrInc("addENIsecondaryIPsToDataStoreFailed")
 		}
 	}
-	c.logPoolStats(c.dataStore.GetIPStats(ipV4AddrFamily))
+	c.logPoolStats(c.dataStoreAccess.GetDataStore(networkCard).GetIPStats(ipV4AddrFamily), networkCard)
 }
 
-func (c *IPAMContext) addENIv4prefixesToDataStore(ec2PrefixAddrs []ec2types.Ipv4PrefixSpecification, eni string) {
+func (c *IPAMContext) addENIv4prefixesToDataStore(ec2PrefixAddrs []ec2types.Ipv4PrefixSpecification, eni string, networkCard int) {
 	// Walk thru all prefixes
 	for _, ec2PrefixAddr := range ec2PrefixAddrs {
 		strIpv4Prefix := aws.ToString(ec2PrefixAddr.Ipv4Prefix)
@@ -1143,17 +1226,17 @@ func (c *IPAMContext) addENIv4prefixesToDataStore(ec2PrefixAddrs []ec2types.Ipv4
 			continue
 		}
 		cidr := *ipnet
-		err = c.dataStore.AddIPv4CidrToStore(eni, cidr, true)
+		err = c.dataStoreAccess.GetDataStore(networkCard).AddIPv4CidrToStore(eni, cidr, true)
 		if err != nil && err.Error() != datastore.IPAlreadyInStoreError {
-			log.Warnf("Failed to increase Prefix pool, failed to add Prefix %s to data store", ec2PrefixAddr.Ipv4Prefix)
+			log.Warnf("Failed to increase Prefix pool, failed to add Prefix %s to data store for network card %d", ec2PrefixAddr.Ipv4Prefix, networkCard)
 			// continue to add next address
 			ipamdErrInc("addENIv4prefixesToDataStoreFailed")
 		}
 	}
-	c.logPoolStats(c.dataStore.GetIPStats(ipV4AddrFamily))
+	c.logPoolStats(c.dataStoreAccess.GetDataStore(networkCard).GetIPStats(ipV4AddrFamily), networkCard)
 }
 
-func (c *IPAMContext) addENIv6prefixesToDataStore(ec2PrefixAddrs []ec2types.Ipv6PrefixSpecification, eni string) {
+func (c *IPAMContext) addENIv6prefixesToDataStore(ec2PrefixAddrs []ec2types.Ipv6PrefixSpecification, eni string, networkCard int) {
 	log.Debugf("Updating datastore with IPv6Prefix(es) for ENI: %v, count: %v", eni, len(ec2PrefixAddrs))
 	// Walk through all prefixes
 	for _, ec2PrefixAddr := range ec2PrefixAddrs {
@@ -1165,14 +1248,15 @@ func (c *IPAMContext) addENIv6prefixesToDataStore(ec2PrefixAddrs []ec2types.Ipv6
 			continue
 		}
 		cidr := *ipnet
-		err = c.dataStore.AddIPv6CidrToStore(eni, cidr, true)
+		err = c.dataStoreAccess.GetDataStore(networkCard).AddIPv6CidrToStore(eni, cidr, true)
 		if err != nil && err.Error() != datastore.IPAlreadyInStoreError {
-			log.Warnf("Failed to increase Prefix pool, failed to add Prefix %s to data store", ec2PrefixAddr.Ipv6Prefix)
+			log.Warnf("Failed to increase Prefix pool, failed to add Prefix %s to data store for network card %d", ec2PrefixAddr.Ipv6Prefix, networkCard)
 			// continue to add next address
 			ipamdErrInc("addENIv6prefixesToDataStoreFailed")
 		}
+		c.logPoolStats(c.dataStoreAccess.GetDataStore(networkCard).GetIPStats(ipV6AddrFamily), networkCard)
 	}
-	c.logPoolStats(c.dataStore.GetIPStats(ipV6AddrFamily))
+
 }
 
 // getMaxENI returns the maximum number of ENIs to attach to this instance. This is calculated as the lesser of
@@ -1231,17 +1315,18 @@ func getWarmPrefixTarget() int {
 }
 
 // logPoolStats logs usage information for allocated addresses/prefixes.
-func (c *IPAMContext) logPoolStats(dataStoreStats *datastore.DataStoreStats) {
-	prefix := "IP pool stats"
+func (c *IPAMContext) logPoolStats(dataStoreStats *datastore.DataStoreStats, networkCard int) {
+	prefix := "IP pool stats for network card"
 	if c.enablePrefixDelegation {
-		prefix = "Prefix pool stats"
+		prefix = "Prefix pool stats for network card"
 	}
-	log.Debugf("%s: %s, c.maxIPsPerENI = %d", prefix, dataStoreStats, c.maxIPsPerENI)
+	log.Debugf("%s %d: %s, c.maxIPsPerENI = %d", prefix, networkCard, dataStoreStats, c.maxIPsPerENI)
 }
 
 func (c *IPAMContext) tryEnableSecurityGroupsForPods(ctx context.Context) {
-	// For IPv4, check that there is room for a trunk ENI before patching CNINode CRD
-	if c.enableIPv4 && (c.dataStore.GetENIs() >= (c.maxENI - c.unmanagedENI)) {
+
+	// For IPv4, check that there is room for a trunk ENI before patching CNINode CRD. We only check on the Default Network Card
+	if c.enableIPv4 && (c.dataStoreAccess.GetDataStore(DefaultNetworkCard).GetENIs() >= (c.maxENI - c.unmanagedENI[DefaultNetworkCard])) {
 		log.Error("No slot available for a trunk ENI to be attached.")
 		return
 	}
@@ -1260,13 +1345,12 @@ func (c *IPAMContext) tryEnableSecurityGroupsForPods(ctx context.Context) {
 // PD enabled: If the WARM_PREFIX_TARGET is spread across ENIs and we have more than needed, this function will return true.
 // If the number of prefixes are on just one ENI, and there are more than available, it returns true so getDeletableENI will
 // recheck if we need the ENI for prefix target.
-func (c *IPAMContext) shouldRemoveExtraENIs() bool {
+func (c *IPAMContext) shouldRemoveExtraENIs(stats *datastore.DataStoreStats, networkCard int) bool {
 	// When WARM_IP_TARGET is set, return true as verification is always done in getDeletableENI()
 	if c.warmIPTargetsDefined() {
 		return true
 	}
 
-	stats := c.dataStore.GetIPStats(ipV4AddrFamily)
 	available := stats.AvailableAddresses()
 	var shouldRemoveExtra bool
 
@@ -1279,27 +1363,27 @@ func (c *IPAMContext) shouldRemoveExtraENIs() bool {
 
 	shouldRemoveExtra = available >= (warmTarget)*c.maxIPsPerENI
 	if shouldRemoveExtra {
-		c.logPoolStats(stats)
+		c.logPoolStats(stats, networkCard)
 		log.Debugf("It might be possible to remove extra ENIs because available (%d) >= (ENI/Prefix target + 1 (%d) + 1) * addrsPerENI (%d)", available, warmTarget, c.maxIPsPerENI)
 	} else if c.enablePrefixDelegation {
 		// When prefix target count is reduced, datastore would have deleted extra prefixes over the warm prefix target.
 		// Hence available will be less than (warmTarget)*c.maxIPsPerENI, but there can be some extra ENIs which are not used hence see if we can clean it up.
-		shouldRemoveExtra = c.dataStore.CheckFreeableENIexists()
+		shouldRemoveExtra = c.dataStoreAccess.GetDataStore(networkCard).CheckFreeableENIexists()
 	}
 	return shouldRemoveExtra
 }
 
-func (c *IPAMContext) computeExtraPrefixesOverWarmTarget() int {
+func (c *IPAMContext) computeExtraPrefixesOverWarmTarget(networkCard int) int {
 	if !c.warmPrefixTargetDefined() {
 		return 0
 	}
 
-	freePrefixes := c.dataStore.GetFreePrefixes()
+	freePrefixes := c.dataStoreAccess.GetDataStore(networkCard).GetFreePrefixes()
 	over := max(freePrefixes-c.warmPrefixTarget, 0)
 
-	stats := c.dataStore.GetIPStats(ipV4AddrFamily)
+	stats := c.dataStoreAccess.GetDataStore(networkCard).GetIPStats(ipV4AddrFamily)
 	log.Debugf("computeExtraPrefixesOverWarmTarget - available: %d, over: %d, warm_prefix_target: %d", stats.AvailableAddresses(), over, c.warmPrefixTarget)
-	c.logPoolStats(stats)
+	c.logPoolStats(stats, networkCard)
 	return over
 }
 
@@ -1334,12 +1418,20 @@ func (c *IPAMContext) checkForTrunkENI() bool {
 	return false
 }
 
+func (c *IPAMContext) getENIsByNetworkCard(allENIs []awsutils.ENIMetadata) map[int][]awsutils.ENIMetadata {
+	var eniNetworkCardMap = make(map[int][]awsutils.ENIMetadata, c.numNetworkCards)
+	for _, eni := range allENIs {
+		eniNetworkCardMap[eni.NetworkCard] = append(eniNetworkCardMap[eni.NetworkCard], eni)
+	}
+	return eniNetworkCardMap
+}
+
 // nodeIPPoolReconcile reconcile ENI and IP info from metadata service and IP addresses in datastore
 func (c *IPAMContext) nodeIPPoolReconcile(ctx context.Context, interval time.Duration) {
 	// To reduce the number of EC2 API calls, skip reconciliation if IPs were recently added to the datastore.
 	timeSinceLast := time.Since(c.lastNodeIPPoolAction)
 	// Make an exception if node needs a trunk ENI and one is not currently attached.
-	needsTrunkEni := c.enablePodENI && c.dataStore.GetTrunkENI() == ""
+	needsTrunkEni := c.enablePodENI && c.dataStoreAccess.GetDataStore(0).GetTrunkENI() == ""
 	if timeSinceLast <= interval && !needsTrunkEni {
 		return
 	}
@@ -1360,103 +1452,109 @@ func (c *IPAMContext) nodeIPPoolReconcile(ctx context.Context, interval time.Dur
 		ipamdErrInc("reconcileFailedGetENIs")
 		return
 	}
-	attachedENIs := c.filterUnmanagedENIs(allENIs)
-	currentENIs := c.dataStore.GetENIInfos().ENIs
-	trunkENI := c.dataStore.GetTrunkENI()
-	// Initialize the set with the known EFA interfaces
-	efaENIs := c.dataStore.GetEFAENIs()
 
-	// Check if a new ENI was added, if so we need to update the tags.
-	needToUpdateTags := false
-	for _, attachedENI := range attachedENIs {
-		if _, ok := currentENIs[attachedENI.ENIID]; !ok {
-			needToUpdateTags = true
-			break
+	attachedENIs := c.filterUnmanagedENIs(allENIs)
+	attachedENIsByNetworkCard := c.getENIsByNetworkCard(allENIs)
+
+	for networkCard, ds := range c.dataStoreAccess.DataStores {
+		currentENIs := ds.GetENIInfos().ENIs
+		trunkENI := ds.GetTrunkENI()
+		// Initialize the set with the known EFA interfaces
+		efaENIs := ds.GetEFAENIs()
+
+		// Check if a new ENI was added, if so we need to update the tags.
+		needToUpdateTags := false
+		for _, attachedENI := range attachedENIsByNetworkCard[networkCard] {
+			if _, ok := currentENIs[attachedENI.ENIID]; !ok {
+				needToUpdateTags = true
+				break
+			}
 		}
-	}
 
-	var eniTagMap map[string]awsutils.TagMap
-	if needToUpdateTags {
-		log.Debugf("A new ENI added but not by ipamd, updating tags by calling EC2")
-		metadataResult, err := c.awsClient.DescribeAllENIs()
-		if err != nil {
-			log.Warnf("Failed to call EC2 to describe ENIs, aborting reconcile: %v", err)
-			return
-		}
+		var eniTagMap map[string]awsutils.TagMap
+		if needToUpdateTags {
+			log.Debugf("A new ENI added but not by ipamd, updating tags by calling EC2")
+			metadataResult, err := c.awsClient.DescribeAllENIs()
+			if err != nil {
+				log.Warnf("Failed to call EC2 to describe ENIs, aborting reconcile: %v", err)
+				return
+			}
 
-		if c.enablePodENI && metadataResult.TrunkENI != "" {
-			log.Debugf("Trunk interface (%s) has been added to the node already.", metadataResult.TrunkENI)
-		}
-		// Update trunk ENI
-		trunkENI = metadataResult.TrunkENI
-		// Just copy values of the EFA set
-		efaENIs = metadataResult.EFAENIs
-		eniTagMap = metadataResult.TagMap
-		c.setUnmanagedENIs(metadataResult.TagMap)
-		c.awsClient.SetMultiCardENIs(metadataResult.MultiCardENIIDs)
-		attachedENIs = c.filterUnmanagedENIs(metadataResult.ENIMetadata)
-	}
+			if c.enablePodENI && metadataResult.TrunkENI != "" {
+				log.Debugf("Trunk interface (%s) has been added to the node already.", metadataResult.TrunkENI)
+			}
+			// Update trunk ENI
+			trunkENI = metadataResult.TrunkENI
+			// Just copy values of the EFA set
+			efaENIs = metadataResult.EFAENIs
+			eniTagMap = metadataResult.TagMap
+			c.setUnmanagedENIs(metadataResult.TagMap)
+			// c.awsClient.SetMultiCardENIs(metadataResult.MultiCardENIIDs)
+			attachedENIs = c.filterUnmanagedENIs(metadataResult.ENIMetadata)
+		}
+
+		// Mark phase
+		for _, attachedENI := range attachedENIs {
+			eniIPPool, eniPrefixPool, err := ds.GetENICIDRs(attachedENI.ENIID)
+			if err == nil {
+				// If the attached ENI is in the data store
+				log.Debugf("Reconcile existing ENI %s IP pool", attachedENI.ENIID)
+				// Reconcile IP pool
+				c.eniIPPoolReconcile(eniIPPool, attachedENI, attachedENI.ENIID, networkCard)
+				// If the attached ENI is in the data store
+				log.Debugf("Reconcile existing ENI %s IP prefixes", attachedENI.ENIID)
+				// Reconcile IP pool
+				c.eniPrefixPoolReconcile(eniPrefixPool, attachedENI, attachedENI.ENIID, networkCard)
+				// Mark action, remove this ENI from currentENIs map
+				delete(currentENIs, attachedENI.ENIID)
+				continue
+			}
 
-	// Mark phase
-	for _, attachedENI := range attachedENIs {
-		eniIPPool, eniPrefixPool, err := c.dataStore.GetENICIDRs(attachedENI.ENIID)
-		if err == nil {
-			// If the attached ENI is in the data store
-			log.Debugf("Reconcile existing ENI %s IP pool", attachedENI.ENIID)
-			// Reconcile IP pool
-			c.eniIPPoolReconcile(eniIPPool, attachedENI, attachedENI.ENIID)
-			// If the attached ENI is in the data store
-			log.Debugf("Reconcile existing ENI %s IP prefixes", attachedENI.ENIID)
-			// Reconcile IP pool
-			c.eniPrefixPoolReconcile(eniPrefixPool, attachedENI, attachedENI.ENIID)
-			// Mark action, remove this ENI from currentENIs map
-			delete(currentENIs, attachedENI.ENIID)
-			continue
-		}
+			isTrunkENI := attachedENI.ENIID == trunkENI
+			isEFAENI := efaENIs[attachedENI.ENIID]
+			if !isTrunkENI && !c.disableENIProvisioning {
+				if err := c.awsClient.TagENI(attachedENI.ENIID, eniTagMap[attachedENI.ENIID]); err != nil {
+					log.Errorf("IP pool reconcile: failed to tag managed ENI %v: %v", attachedENI.ENIID, err)
+					ipamdErrInc("eniReconcileAdd")
+					continue
+				}
+			}
 
-		isTrunkENI := attachedENI.ENIID == trunkENI
-		isEFAENI := efaENIs[attachedENI.ENIID]
-		if !isTrunkENI && !c.disableENIProvisioning {
-			if err := c.awsClient.TagENI(attachedENI.ENIID, eniTagMap[attachedENI.ENIID]); err != nil {
-				log.Errorf("IP pool reconcile: failed to tag managed ENI %v: %v", attachedENI.ENIID, err)
+			// Add new ENI
+			log.Debugf("Reconcile and add a new ENI %s", attachedENI.ENIID)
+			err = c.setupENI(attachedENI.ENIID, attachedENI, isTrunkENI, isEFAENI)
+			if err != nil {
+				log.Errorf("IP pool reconcile: Failed to set up ENI %s network: %v", attachedENI.ENIID, err)
 				ipamdErrInc("eniReconcileAdd")
+				// Continue if having trouble with ONLY 1 ENI, instead of bailout here?
 				continue
 			}
+			prometheusmetrics.ReconcileCnt.With(prometheus.Labels{"fn": "eniReconcileAdd"}).Inc()
 		}
 
-		// Add new ENI
-		log.Debugf("Reconcile and add a new ENI %s", attachedENI)
-		err = c.setupENI(attachedENI.ENIID, attachedENI, isTrunkENI, isEFAENI)
-		if err != nil {
-			log.Errorf("IP pool reconcile: Failed to set up ENI %s network: %v", attachedENI.ENIID, err)
-			ipamdErrInc("eniReconcileAdd")
-			// Continue if having trouble with ONLY 1 ENI, instead of bailout here?
-			continue
+		// Sweep phase: since the marked ENI have been removed, the remaining ones needs to be sweeped
+		for eni := range currentENIs {
+			log.Infof("Reconcile and delete detached ENI %s", eni)
+			// Force the delete, since aws local metadata has told us that this ENI is no longer
+			// attached, so any IPs assigned from this ENI will no longer work.
+			err = c.dataStoreAccess.GetDataStore(networkCard).RemoveENIFromDataStore(eni, true /* force */)
+			if err != nil {
+				log.Errorf("IP pool reconcile: Failed to delete ENI during reconcile: %v", err)
+				ipamdErrInc("eniReconcileDel")
+				continue
+			}
+			delete(c.primaryIP, eni)
+			prometheusmetrics.ReconcileCnt.With(prometheus.Labels{"fn": "eniReconcileDel"}).Inc()
 		}
-		prometheusmetrics.ReconcileCnt.With(prometheus.Labels{"fn": "eniReconcileAdd"}).Inc()
-	}
 
-	// Sweep phase: since the marked ENI have been removed, the remaining ones needs to be sweeped
-	for eni := range currentENIs {
-		log.Infof("Reconcile and delete detached ENI %s", eni)
-		// Force the delete, since aws local metadata has told us that this ENI is no longer
-		// attached, so any IPs assigned from this ENI will no longer work.
-		err = c.dataStore.RemoveENIFromDataStore(eni, true /* force */)
-		if err != nil {
-			log.Errorf("IP pool reconcile: Failed to delete ENI during reconcile: %v", err)
-			ipamdErrInc("eniReconcileDel")
-			continue
-		}
-		delete(c.primaryIP, eni)
-		prometheusmetrics.ReconcileCnt.With(prometheus.Labels{"fn": "eniReconcileDel"}).Inc()
+		log.Debug("Successfully Reconciled ENI/IP pool")
+		c.logPoolStats(c.dataStoreAccess.GetDataStore(networkCard).GetIPStats(ipV4AddrFamily), networkCard)
 	}
 	c.lastNodeIPPoolAction = time.Now()
 
-	log.Debug("Successfully Reconciled ENI/IP pool")
-	c.logPoolStats(c.dataStore.GetIPStats(ipV4AddrFamily))
 }
 
-func (c *IPAMContext) eniIPPoolReconcile(ipPool []string, attachedENI awsutils.ENIMetadata, eni string) {
+func (c *IPAMContext) eniIPPoolReconcile(ipPool []string, attachedENI awsutils.ENIMetadata, eni string, networkCard int) {
 	attachedENIIPs := attachedENI.IPv4Addresses
 	needEC2Reconcile := true
 	// Here we can't trust attachedENI since the IMDS metadata can be stale. We need to check with EC2 API.
@@ -1475,7 +1573,7 @@ func (c *IPAMContext) eniIPPoolReconcile(ipPool []string, attachedENI awsutils.E
 	}
 
 	// Add all known attached IPs to the datastore
-	seenIPs := c.verifyAndAddIPsToDatastore(eni, attachedENIIPs, needEC2Reconcile)
+	seenIPs := c.verifyAndAddIPsToDatastore(eni, attachedENIIPs, needEC2Reconcile, networkCard)
 
 	// Sweep phase, delete remaining IPs since they should not remain in the datastore
 	for _, existingIP := range ipPool {
@@ -1486,7 +1584,7 @@ func (c *IPAMContext) eniIPPoolReconcile(ipPool []string, attachedENI awsutils.E
 		log.Debugf("Reconcile and delete IP %s on ENI %s", existingIP, eni)
 		// Force the delete, since we have verified with EC2 that these secondary IPs are no longer assigned to this ENI
 		ipv4Addr := net.IPNet{IP: net.ParseIP(existingIP), Mask: net.IPv4Mask(255, 255, 255, 255)}
-		err := c.dataStore.DelIPv4CidrFromStore(eni, ipv4Addr, true /* force */)
+		err := c.dataStoreAccess.GetDataStore(networkCard).DelIPv4CidrFromStore(eni, ipv4Addr, true /* force */)
 		if err != nil {
 			log.Errorf("Failed to reconcile and delete IP %s on ENI %s, %v", existingIP, eni, err)
 			ipamdErrInc("ipReconcileDel")
@@ -1497,7 +1595,7 @@ func (c *IPAMContext) eniIPPoolReconcile(ipPool []string, attachedENI awsutils.E
 	}
 }
 
-func (c *IPAMContext) eniPrefixPoolReconcile(prefixPool []string, attachedENI awsutils.ENIMetadata, eni string) {
+func (c *IPAMContext) eniPrefixPoolReconcile(prefixPool []string, attachedENI awsutils.ENIMetadata, eni string, networkCard int) {
 	attachedENIIPs := attachedENI.IPv4Prefixes
 	needEC2Reconcile := true
 	// Here we can't trust attachedENI since the IMDS metadata can be stale. We need to check with EC2 API.
@@ -1517,7 +1615,7 @@ func (c *IPAMContext) eniPrefixPoolReconcile(prefixPool []string, attachedENI aw
 	}
 
 	// Add all known attached IPs to the datastore
-	seenIPs := c.verifyAndAddPrefixesToDatastore(eni, attachedENIIPs, needEC2Reconcile)
+	seenIPs := c.verifyAndAddPrefixesToDatastore(eni, attachedENIIPs, needEC2Reconcile, networkCard)
 
 	// Sweep phase, delete remaining Prefixes since they should not remain in the datastore
 	for _, existingIP := range prefixPool {
@@ -1532,7 +1630,7 @@ func (c *IPAMContext) eniPrefixPoolReconcile(prefixPool []string, attachedENI aw
 			log.Debugf("Failed to parse so continuing with next prefix")
 			continue
 		}
-		err = c.dataStore.DelIPv4CidrFromStore(eni, *ipv4Cidr, true /* force */)
+		err = c.dataStoreAccess.GetDataStore(networkCard).DelIPv4CidrFromStore(eni, *ipv4Cidr, true /* force */)
 		if err != nil {
 			log.Errorf("Failed to reconcile and delete IP %s on ENI %s, %v", existingIP, eni, err)
 			ipamdErrInc("ipReconcileDel")
@@ -1545,7 +1643,7 @@ func (c *IPAMContext) eniPrefixPoolReconcile(prefixPool []string, attachedENI aw
 
 // verifyAndAddIPsToDatastore updates the datastore with the known secondary IPs. IPs who are out of cooldown gets added
 // back to the datastore after being verified against EC2.
-func (c *IPAMContext) verifyAndAddIPsToDatastore(eni string, attachedENIIPs []ec2types.NetworkInterfacePrivateIpAddress, needEC2Reconcile bool) map[string]bool {
+func (c *IPAMContext) verifyAndAddIPsToDatastore(eni string, attachedENIIPs []ec2types.NetworkInterfacePrivateIpAddress, needEC2Reconcile bool, networkCard int) map[string]bool {
 	var ec2VerifiedAddresses []ec2types.NetworkInterfacePrivateIpAddress
 	seenIPs := make(map[string]bool)
 	for _, privateIPv4 := range attachedENIIPs {
@@ -1599,7 +1697,7 @@ func (c *IPAMContext) verifyAndAddIPsToDatastore(eni string, attachedENIIPs []ec
 		}
 		log.Infof("Trying to add %s", strPrivateIPv4)
 		// Try to add the IP
-		err := c.dataStore.AddIPv4CidrToStore(eni, ipv4Addr, false)
+		err := c.dataStoreAccess.GetDataStore(networkCard).AddIPv4CidrToStore(eni, ipv4Addr, false)
 		if err != nil && err.Error() != datastore.IPAlreadyInStoreError {
 			log.Errorf("Failed to reconcile IP %s on ENI %s", strPrivateIPv4, eni)
 			ipamdErrInc("ipReconcileAdd")
@@ -1616,7 +1714,7 @@ func (c *IPAMContext) verifyAndAddIPsToDatastore(eni string, attachedENIIPs []ec
 
 // verifyAndAddPrefixesToDatastore updates the datastore with the known Prefixes. Prefixes who are out of cooldown gets added
 // back to the datastore after being verified against EC2.
-func (c *IPAMContext) verifyAndAddPrefixesToDatastore(eni string, attachedENIPrefixes []ec2types.Ipv4PrefixSpecification, needEC2Reconcile bool) map[string]bool {
+func (c *IPAMContext) verifyAndAddPrefixesToDatastore(eni string, attachedENIPrefixes []ec2types.Ipv4PrefixSpecification, needEC2Reconcile bool, networkCard int) map[string]bool {
 	var ec2VerifiedAddresses []ec2types.Ipv4PrefixSpecification
 	seenIPs := make(map[string]bool)
 	for _, privateIPv4Cidr := range attachedENIPrefixes {
@@ -1671,7 +1769,7 @@ func (c *IPAMContext) verifyAndAddPrefixesToDatastore(eni string, attachedENIPre
 			}
 		}
 
-		err = c.dataStore.AddIPv4CidrToStore(eni, *ipv4CidrPtr, true)
+		err = c.dataStoreAccess.GetDataStore(networkCard).AddIPv4CidrToStore(eni, *ipv4CidrPtr, true)
 		if err != nil && err.Error() != datastore.IPAlreadyInStoreError {
 			log.Errorf("Failed to reconcile Prefix %s on ENI %s", strPrivateIPv4Cidr, eni)
 			ipamdErrInc("prefixReconcileAdd")
@@ -1801,36 +1899,39 @@ func enablePodIPAnnotation() bool {
 }
 
 // filterUnmanagedENIs filters out ENIs marked with the "node.k8s.amazonaws.com/no_manage" tag
+// TODO: We have to mark some ENIs as primary on multlicard instance for IPv6 and the numfiltered count
 func (c *IPAMContext) filterUnmanagedENIs(enis []awsutils.ENIMetadata) []awsutils.ENIMetadata {
 	numFiltered := 0
 	ret := make([]awsutils.ENIMetadata, 0, len(enis))
+
 	for _, eni := range enis {
 		//Filter out any Unmanaged ENIs. VPC CNI will only work with Primary ENI in IPv6 Prefix Delegation mode until
 		//we open up IPv6 support in Secondary IP and Custom networking modes. Filtering out the ENIs here will
 		//help us avoid myriad of if/else loops elsewhere in the code.
-		if c.enableIPv6 && !c.awsClient.IsPrimaryENI(eni.ENIID) {
-			log.Debugf("Skipping ENI %s: IPv6 Mode is enabled and VPC CNI will only manage Primary ENI in v6 PD mode",
-				eni.ENIID)
-			numFiltered++
-			continue
+		// We shouldn't need the IsPrimaryENI check as ENIs not created by vpc-cni will be marked unmanaged (including trunk ENI)
+		if c.enableIPv6 {
+			if !c.awsClient.IsPrimaryENI(eni.ENIID) && c.awsClient.IsUnmanagedENI(eni.ENIID) {
+				log.Debugf("Skipping ENI %s: IPv6 Mode is enabled and VPC CNI will only ENIs created by it in v6 PD mode",
+					eni.ENIID)
+				numFiltered++
+				c.unmanagedENI[eni.NetworkCard] += 1
+				continue
+			}
 		} else if c.awsClient.IsUnmanagedENI(eni.ENIID) {
 			log.Debugf("Skipping ENI %s: since it is unmanaged", eni.ENIID)
 			numFiltered++
-			continue
-		} else if c.awsClient.IsMultiCardENI(eni.ENIID) {
-			log.Debugf("Skipping ENI %s: since on non-zero network card", eni.ENIID)
+			c.unmanagedENI[eni.NetworkCard] += 1
 			continue
 		}
 		ret = append(ret, eni)
 	}
-	c.unmanagedENI = numFiltered
 	c.updateIPStats(numFiltered)
 	return ret
 }
 
 // datastoreTargetState determines the number of IPs `short` or `over` our WARM_IP_TARGET, accounting for the MINIMUM_IP_TARGET.
 // With prefix delegation, this function determines the number of Prefixes `short` or `over`
-func (c *IPAMContext) datastoreTargetState(stats *datastore.DataStoreStats) (short int, over int, enabled bool) {
+func (c *IPAMContext) datastoreTargetState(stats *datastore.DataStoreStats, networkCard int) (short int, over int, enabled bool) {
 	if !c.warmIPTargetsDefined() {
 		// there is no WARM_IP_TARGET defined and no MINIMUM_IP_TARGET, fallback to use all IP addresses on ENI
 		return 0, 0, false
@@ -1838,7 +1939,7 @@ func (c *IPAMContext) datastoreTargetState(stats *datastore.DataStoreStats) (sho
 
 	// Calculating DataStore stats can be expensive, so allow the caller to optionally pass stats it already calculated
 	if stats == nil {
-		stats = c.dataStore.GetIPStats(ipV4AddrFamily)
+		stats = c.dataStoreAccess.GetDataStore(networkCard).GetIPStats(ipV4AddrFamily)
 	}
 	available := stats.AvailableAddresses()
 
@@ -1870,7 +1971,7 @@ func (c *IPAMContext) datastoreTargetState(stats *datastore.DataStoreStats) (sho
 		// over will be number of prefixes over than needed but could be spread across used prefixes,
 		// say, after couple of pod churns, 3 prefixes are allocated with 1 IP each assigned and warm ip target is 15
 		// (J : is this needed? since we have to walk thru the loop of prefixes)
-		freePrefixes := c.dataStore.GetFreePrefixes()
+		freePrefixes := c.dataStoreAccess.GetDataStore(networkCard).GetFreePrefixes()
 		overPrefix := max(min(freePrefixes, stats.TotalPrefixes-prefixNeededForWarmIP), 0)
 		overPrefix = max(min(overPrefix, stats.TotalPrefixes-prefixNeededForMinIP), 0)
 		return shortPrefix, overPrefix, true
@@ -1879,12 +1980,12 @@ func (c *IPAMContext) datastoreTargetState(stats *datastore.DataStoreStats) (sho
 }
 
 // datastorePrefixTargetState determines the number of prefixes short to reach WARM_PREFIX_TARGET
-func (c *IPAMContext) datastorePrefixTargetState() (short int, enabled bool) {
+func (c *IPAMContext) datastorePrefixTargetState(networkCard int) (short int, enabled bool) {
 	if !c.warmPrefixTargetDefined() {
 		return 0, false
 	}
 	// /28 will consume 16 IPs so let's not allocate if not needed.
-	freePrefixesInStore := c.dataStore.GetFreePrefixes()
+	freePrefixesInStore := c.dataStoreAccess.GetDataStore(networkCard).GetFreePrefixes()
 	toAllocate := max(c.warmPrefixTarget-freePrefixesInStore, 0)
 	log.Debugf("Prefix target is %d, short of %d prefixes, free %d prefixes", c.warmPrefixTarget, toAllocate, freePrefixesInStore)
 
@@ -1954,7 +2055,7 @@ func min(x, y int) int {
 }
 
 func (c *IPAMContext) getTrunkLinkIndex() (int, error) {
-	trunkENI := c.dataStore.GetTrunkENI()
+	trunkENI := c.dataStoreAccess.GetDataStore(0).GetTrunkENI()
 	attachedENIs, err := c.awsClient.GetAttachedENIs()
 	if err != nil {
 		return -1, err
@@ -2048,14 +2149,20 @@ func (c *IPAMContext) AnnotatePod(podName string, podNamespace string, key strin
 
 func (c *IPAMContext) tryUnassignIPsFromENIs() {
 	log.Debugf("tryUnassignIPsFromENIs")
-	eniInfos := c.dataStore.GetENIInfos()
-	for eniID := range eniInfos.ENIs {
-		c.tryUnassignIPFromENI(eniID)
+	// From all datastores, get ENIInfos and unassign IPs
+	for networkCard, ds := range c.dataStoreAccess.DataStores {
+		eniInfos := ds.GetENIInfos()
+		for eniID := range eniInfos.ENIs {
+			c.tryUnassignIPFromENI(eniID, networkCard)
+		}
 	}
+
 }
 
-func (c *IPAMContext) tryUnassignIPFromENI(eniID string) {
-	freeableIPs := c.dataStore.FreeableIPs(eniID)
+func (c *IPAMContext) tryUnassignIPFromENI(eniID string, networkCard int) {
+
+	ds := c.dataStoreAccess.GetDataStore(networkCard)
+	freeableIPs := ds.FreeableIPs(eniID)
 	if len(freeableIPs) == 0 {
 		log.Debugf("No freeable IPs")
 		return
@@ -2066,7 +2173,7 @@ func (c *IPAMContext) tryUnassignIPFromENI(eniID string) {
 	for _, toDelete := range freeableIPs {
 		// Don't force the delete, since a freeable IP might have been assigned to a pod
 		// before we get around to deleting it.
-		err := c.dataStore.DelIPv4CidrFromStore(eniID, toDelete, false /* force */)
+		err := ds.DelIPv4CidrFromStore(eniID, toDelete, false /* force */)
 		if err != nil {
 			log.Warnf("Failed to delete IP %s on ENI %s from datastore: %s", toDelete, eniID, err)
 			ipamdErrInc("decreaseIPPool")
@@ -2086,14 +2193,19 @@ func (c *IPAMContext) tryUnassignIPFromENI(eniID string) {
 
 func (c *IPAMContext) tryUnassignPrefixesFromENIs() {
 	log.Debugf("tryUnassignPrefixesFromENIs")
-	eniInfos := c.dataStore.GetENIInfos()
-	for eniID := range eniInfos.ENIs {
-		c.tryUnassignPrefixFromENI(eniID)
+
+	// From all datastores get ENIs and remove prefixes
+	for networkCard, ds := range c.dataStoreAccess.DataStores {
+		eniInfos := ds.GetENIInfos()
+		for eniID := range eniInfos.ENIs {
+			c.tryUnassignPrefixFromENI(eniID, networkCard)
+		}
 	}
 }
 
-func (c *IPAMContext) tryUnassignPrefixFromENI(eniID string) {
-	freeablePrefixes := c.dataStore.FreeablePrefixes(eniID)
+func (c *IPAMContext) tryUnassignPrefixFromENI(eniID string, networkCard int) {
+	ds := c.dataStoreAccess.GetDataStore(networkCard)
+	freeablePrefixes := ds.FreeablePrefixes(eniID)
 	if len(freeablePrefixes) == 0 {
 		return
 	}
@@ -2102,7 +2214,7 @@ func (c *IPAMContext) tryUnassignPrefixFromENI(eniID string) {
 	for _, toDelete := range freeablePrefixes {
 		// Don't force the delete, since a freeable Prefix might have been assigned to a pod
 		// before we get around to deleting it.
-		err := c.dataStore.DelIPv4CidrFromStore(eniID, toDelete, false /* force */)
+		err := ds.DelIPv4CidrFromStore(eniID, toDelete, false /* force */)
 		if err != nil {
 			log.Warnf("Failed to delete Prefix %s on ENI %s from datastore: %s", toDelete, eniID, err)
 			ipamdErrInc("decreaseIPPool")
@@ -2120,13 +2232,18 @@ func (c *IPAMContext) tryUnassignPrefixFromENI(eniID string) {
 	}
 }
 
-func (c *IPAMContext) GetENIResourcesToAllocate() int {
+func (c *IPAMContext) GetENIResourcesToAllocate(networkCard int) int {
 	var resourcesToAllocate int
 	if c.enablePrefixDelegation {
-		resourcesToAllocate = min(c.getPrefixesNeeded(), c.maxPrefixesPerENI)
+		if c.enableIPv6 {
+			// IPv6 always uses PD, so it should always be 1
+			resourcesToAllocate = 1
+		} else {
+			resourcesToAllocate = min(c.getPrefixesNeeded(networkCard), c.maxPrefixesPerENI)
+		}
 	} else {
 		resourcesToAllocate = c.maxIPsPerENI
-		short, _, warmTargetDefined := c.datastoreTargetState(nil)
+		short, _, warmTargetDefined := c.datastoreTargetState(nil, networkCard)
 		if warmTargetDefined {
 			resourcesToAllocate = min(short, c.maxIPsPerENI)
 		}
@@ -2150,32 +2267,23 @@ func (c *IPAMContext) GetIPv4Limit() (int, int, error) {
 	return maxIPsPerENI, maxPrefixesPerENI, nil
 }
 
-func (c *IPAMContext) isDatastorePoolEmpty() bool {
-	stats := c.dataStore.GetIPStats(ipV4AddrFamily)
+func (c *IPAMContext) isDatastorePoolEmpty(networkCard int) bool {
+	stats := c.dataStoreAccess.GetDataStore(networkCard).GetIPStats(ipV4AddrFamily)
 	return stats.TotalIPs == 0
 }
 
 // Return whether the maximum number of ENIs that can be attached to the node has already been reached
-func (c *IPAMContext) hasRoomForEni() bool {
+func (c *IPAMContext) hasRoomForEni(networkCard int) bool {
 	trunkEni := 0
-	if c.enablePodENI && c.dataStore.GetTrunkENI() == "" {
+	if c.enablePodENI && networkCard == DefaultNetworkCard && c.dataStoreAccess.GetDataStore(DefaultNetworkCard).GetTrunkENI() == "" {
 		trunkEni = 1
 	}
-	return c.dataStore.GetENIs() < (c.maxENI - c.unmanagedENI - trunkEni)
+	return c.dataStoreAccess.GetDataStore(networkCard).GetENIs() < (c.maxENI - c.unmanagedENI[networkCard] - trunkEni)
 }
 
-func (c *IPAMContext) isDatastorePoolTooLow() (bool, *datastore.DataStoreStats) {
-	stats := c.dataStore.GetIPStats(ipV4AddrFamily)
-	// If max pods has been reached, pool is not too low
-	if stats.TotalIPs >= c.maxPods {
-		return false, stats
-	}
-
-	short, _, warmTargetDefined := c.datastoreTargetState(stats)
-	if warmTargetDefined {
-		return short > 0, stats
-	}
+func (c *IPAMContext) isDatastorePoolTooLow() []Decisions {
 
+	decisions := make([]Decisions, 0, len(c.dataStoreAccess.DataStores))
 	warmTarget := c.warmENITarget
 	totalIPs := c.maxIPsPerENI
 	if c.enablePrefixDelegation {
@@ -2184,28 +2292,45 @@ func (c *IPAMContext) isDatastorePoolTooLow() (bool, *datastore.DataStoreStats)
 		totalIPs = maxIpsPerPrefix
 	}
 
-	available := stats.AvailableAddresses()
-	poolTooLow := available < totalIPs*warmTarget || (warmTarget == 0 && available == 0)
-	if poolTooLow {
-		log.Debugf("IP pool is too low: available (%d) < ENI target (%d) * addrsPerENI (%d)", available, warmTarget, totalIPs)
-		c.logPoolStats(stats)
+	for networkCard, ds := range c.dataStoreAccess.DataStores {
+
+		stats := ds.GetIPStats(ipV4AddrFamily)
+		// If max pods has been reached, pool is not too low
+		if stats.TotalIPs >= c.maxPods {
+			decisions = append(decisions, Decisions{Stats: stats, IsLow: false})
+			continue
+		}
+
+		short, _, warmTargetDefined := c.datastoreTargetState(stats, networkCard)
+		if warmTargetDefined {
+			decisions = append(decisions, Decisions{Stats: stats, IsLow: short > 0})
+			continue
+		}
+
+		available := stats.AvailableAddresses()
+		poolTooLow := available < totalIPs*warmTarget || (warmTarget == 0 && available == 0)
+		if poolTooLow {
+			log.Debugf("IP pool is too low for Network Card %d: available (%d) < ENI target (%d) * addrsPerENI (%d)", networkCard, available, warmTarget, totalIPs)
+			c.logPoolStats(stats, networkCard)
+		}
+		decisions = append(decisions, Decisions{Stats: stats, IsLow: poolTooLow})
 	}
-	return poolTooLow, stats
+	return decisions
 }
 
-func (c *IPAMContext) isDatastorePoolTooHigh(stats *datastore.DataStoreStats) bool {
+func (c *IPAMContext) isDatastorePoolTooHigh(stats *datastore.DataStoreStats, networkCard int) bool {
 	// NOTE: IPs may be allocated in chunks (full ENIs of prefixes), so the "too-high" condition does not check max pods. The limit is enforced on the allocation side.
-	_, over, warmTargetDefined := c.datastoreTargetState(stats)
+	_, over, warmTargetDefined := c.datastoreTargetState(stats, networkCard)
 	if warmTargetDefined {
 		return over > 0
 	}
 
 	// For the existing ENIs check if we can cleanup prefixes
 	if c.warmPrefixTargetDefined() {
-		freePrefixes := c.dataStore.GetFreePrefixes()
+		freePrefixes := c.dataStoreAccess.GetDataStore(networkCard).GetFreePrefixes()
 		poolTooHigh := freePrefixes > c.warmPrefixTarget
 		if poolTooHigh {
-			log.Debugf("Prefix pool is high so might be able to deallocate - free prefixes: %d, warm prefix target: %d", freePrefixes, c.warmPrefixTarget)
+			log.Debugf("Prefix pool is high so might be able to deallocate - free prefixes: %d, warm prefix target: %d from NetworkCard %d", freePrefixes, c.warmPrefixTarget, networkCard)
 		}
 		return poolTooHigh
 	}
@@ -2248,14 +2373,14 @@ func (c *IPAMContext) DeallocCidrs(eniID string, deletableCidrs []datastore.Cidr
 }
 
 // getPrefixesNeeded returns the number of prefixes need to be allocated to the ENI
-func (c *IPAMContext) getPrefixesNeeded() int {
+func (c *IPAMContext) getPrefixesNeeded(networkCard int) int {
 	// By default allocate 1 prefix at a time
 	toAllocate := 1
 
 	// TODO - post GA we can evaluate to see if these two calls can be merged.
 	// datastoreTargetState already has complex math so adding Prefix target will make it even more complex.
-	short, _, warmIPTargetsDefined := c.datastoreTargetState(nil)
-	shortPrefixes, warmPrefixTargetDefined := c.datastorePrefixTargetState()
+	short, _, warmIPTargetsDefined := c.datastoreTargetState(nil, networkCard)
+	shortPrefixes, warmPrefixTargetDefined := c.datastorePrefixTargetState(networkCard)
 
 	// WARM_IP_TARGET takes precendence over WARM_PREFIX_TARGET
 	if warmIPTargetsDefined {
@@ -2263,19 +2388,20 @@ func (c *IPAMContext) getPrefixesNeeded() int {
 	} else if warmPrefixTargetDefined {
 		toAllocate = max(toAllocate, shortPrefixes)
 	}
-	log.Debugf("ToAllocate: %d", toAllocate)
+	log.Debugf("ToAllocate: %d, Network Card %d", toAllocate, networkCard)
 	return toAllocate
 }
 
 func (c *IPAMContext) initENIAndIPLimits() (err error) {
-	if c.enableIPv4 {
-		nodeMaxENI, err := c.getMaxENI()
-		if err != nil {
-			log.Error("Failed to get ENI limit")
-			return err
-		}
-		c.maxENI = nodeMaxENI
 
+	nodeMaxENI, err := c.getMaxENI()
+	if err != nil {
+		log.Error("Failed to get ENI limit")
+		return err
+	}
+	c.maxENI = nodeMaxENI
+
+	if c.enableIPv4 {
 		c.maxIPsPerENI, c.maxPrefixesPerENI, err = c.GetIPv4Limit()
 		if err != nil {
 			return err
@@ -2355,3 +2481,8 @@ func (c *IPAMContext) AddFeatureToCNINode(ctx context.Context, featureName rcv1a
 	newCNINode.Spec.Features = append(newCNINode.Spec.Features, newFeature)
 	return c.k8sClient.Patch(ctx, newCNINode, client.MergeFromWithOptions(cniNode, client.MergeFromWithOptimisticLock{}))
 }
+
+type Decisions struct {
+	Stats *datastore.DataStoreStats
+	IsLow bool
+}
diff --git a/pkg/ipamd/ipamd_test.go b/pkg/ipamd/ipamd_test.go
index deaa083f47..8b621eb9c6 100644
--- a/pkg/ipamd/ipamd_test.go
+++ b/pkg/ipamd/ipamd_test.go
@@ -85,6 +85,8 @@ const (
 	v6prefix01             = "2001:db8::/64"
 	instanceID             = "i-0e1f3b9eb950e4980"
 	externalEniConfigLabel = "vpc.amazonaws.com/externalEniConfig"
+	defaultNetworkCard     = 0
+	maxENIPerNIC           = 4
 )
 
 type testMocks struct {
@@ -124,19 +126,22 @@ func TestNodeInit(t *testing.T) {
 	}
 
 	mockContext := &IPAMContext{
-		awsClient:     m.awsutils,
-		k8sClient:     m.k8sClient,
-		maxIPsPerENI:  14,
-		maxENI:        4,
-		warmENITarget: 1,
-		warmIPTarget:  3,
-		primaryIP:     make(map[string]string),
-		terminating:   int32(0),
-		networkClient: m.network,
-		dataStore:     datastore.NewDataStore(log, datastore.NewTestCheckpoint(fakeCheckpoint), false),
-		myNodeName:    myNodeName,
-		enableIPv4:    true,
-		enableIPv6:    false,
+		awsClient:       m.awsutils,
+		k8sClient:       m.k8sClient,
+		maxIPsPerENI:    14,
+		maxENI:          4,
+		warmENITarget:   1,
+		warmIPTarget:    3,
+		numNetworkCards: 1,
+		primaryIP:       make(map[string]string),
+		terminating:     int32(0),
+		networkClient:   m.network,
+		dataStoreAccess: &datastore.DataStoreAccess{
+			DataStores: []*datastore.DataStore{datastore.NewDataStore(log, datastore.NewTestCheckpoint(fakeCheckpoint), false)},
+		},
+		myNodeName: myNodeName,
+		enableIPv4: true,
+		enableIPv6: false,
 	}
 
 	eni1, eni2, _ := getDummyENIMetadata()
@@ -149,8 +154,6 @@ func TestNodeInit(t *testing.T) {
 	m.awsutils.EXPECT().IsUnmanagedENI(eni1.ENIID).Return(false).AnyTimes()
 	m.awsutils.EXPECT().IsUnmanagedENI(eni2.ENIID).Return(false).AnyTimes()
 	m.awsutils.EXPECT().TagENI(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
-	m.awsutils.EXPECT().IsMultiCardENI(eni1.ENIID).Return(false).AnyTimes()
-	m.awsutils.EXPECT().IsMultiCardENI(eni2.ENIID).Return(false).AnyTimes()
 
 	primaryIP := net.ParseIP(ipaddr01)
 	m.awsutils.EXPECT().GetVPCIPv4CIDRs().AnyTimes().Return(cidrs, nil)
@@ -169,7 +172,7 @@ func TestNodeInit(t *testing.T) {
 		MultiCardENIIDs: nil,
 	}
 	m.awsutils.EXPECT().DescribeAllENIs().Return(resp, nil)
-	m.network.EXPECT().SetupENINetwork(gomock.Any(), secMAC, secDevice, secSubnet)
+	m.network.EXPECT().SetupENINetwork(gomock.Any(), secMAC, secDevice, defaultNetworkCard, secSubnet, maxENIPerNIC)
 
 	m.awsutils.EXPECT().SetMultiCardENIs(resp.MultiCardENIIDs).AnyTimes()
 	m.awsutils.EXPECT().GetLocalIPv4().Return(primaryIP)
@@ -213,17 +216,20 @@ func TestNodeInitwithPDenabledIPv4Mode(t *testing.T) {
 	}
 
 	mockContext := &IPAMContext{
-		awsClient:              m.awsutils,
-		k8sClient:              m.k8sClient,
-		maxIPsPerENI:           224,
-		maxPrefixesPerENI:      14,
-		maxENI:                 4,
-		warmENITarget:          1,
-		warmIPTarget:           3,
-		primaryIP:              make(map[string]string),
-		terminating:            int32(0),
-		networkClient:          m.network,
-		dataStore:              datastore.NewDataStore(log, datastore.NewTestCheckpoint(fakeCheckpoint), true),
+		awsClient:         m.awsutils,
+		k8sClient:         m.k8sClient,
+		maxIPsPerENI:      224,
+		maxPrefixesPerENI: 14,
+		maxENI:            4,
+		warmENITarget:     1,
+		numNetworkCards:   1,
+		warmIPTarget:      3,
+		primaryIP:         make(map[string]string),
+		terminating:       int32(0),
+		networkClient:     m.network,
+		dataStoreAccess: &datastore.DataStoreAccess{
+			DataStores: []*datastore.DataStore{datastore.NewDataStore(log, datastore.NewTestCheckpoint(fakeCheckpoint), true)},
+		},
 		myNodeName:             myNodeName,
 		enablePrefixDelegation: true,
 		enableIPv4:             true,
@@ -239,8 +245,6 @@ func TestNodeInitwithPDenabledIPv4Mode(t *testing.T) {
 	m.awsutils.EXPECT().IsUnmanagedENI(eni1.ENIID).Return(false).AnyTimes()
 	m.awsutils.EXPECT().IsUnmanagedENI(eni2.ENIID).Return(false).AnyTimes()
 	m.awsutils.EXPECT().TagENI(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
-	m.awsutils.EXPECT().IsMultiCardENI(eni1.ENIID).Return(false).AnyTimes()
-	m.awsutils.EXPECT().IsMultiCardENI(eni2.ENIID).Return(false).AnyTimes()
 
 	primaryIP := net.ParseIP(ipaddr01)
 	m.awsutils.EXPECT().GetVPCIPv4CIDRs().AnyTimes().Return(cidrs, nil)
@@ -258,10 +262,9 @@ func TestNodeInitwithPDenabledIPv4Mode(t *testing.T) {
 		EFAENIs:     make(map[string]bool),
 	}
 	m.awsutils.EXPECT().DescribeAllENIs().Return(resp, nil)
-	m.network.EXPECT().SetupENINetwork(gomock.Any(), secMAC, secDevice, secSubnet)
+	m.network.EXPECT().SetupENINetwork(gomock.Any(), secMAC, secDevice, defaultNetworkCard, secSubnet, maxENIPerNIC)
 
 	m.awsutils.EXPECT().GetLocalIPv4().Return(primaryIP)
-	m.awsutils.EXPECT().SetMultiCardENIs(resp.MultiCardENIIDs).AnyTimes()
 
 	var rules []netlink.Rule
 	m.network.EXPECT().GetRuleList().Return(rules, nil)
@@ -300,17 +303,19 @@ func TestNodeInitwithPDenabledIPv6Mode(t *testing.T) {
 	}
 
 	mockContext := &IPAMContext{
-		awsClient:              m.awsutils,
-		k8sClient:              m.k8sClient,
-		maxIPsPerENI:           224,
-		maxPrefixesPerENI:      1,
-		maxENI:                 1,
-		warmENITarget:          1,
-		warmIPTarget:           1,
-		primaryIP:              make(map[string]string),
-		terminating:            int32(0),
-		networkClient:          m.network,
-		dataStore:              datastore.NewDataStore(log, datastore.NewTestCheckpoint(fakeCheckpoint), true),
+		awsClient:         m.awsutils,
+		k8sClient:         m.k8sClient,
+		maxIPsPerENI:      224,
+		maxPrefixesPerENI: 1,
+		maxENI:            1,
+		warmENITarget:     1,
+		warmIPTarget:      1,
+		primaryIP:         make(map[string]string),
+		terminating:       int32(0),
+		networkClient:     m.network,
+		dataStoreAccess: &datastore.DataStoreAccess{
+			DataStores: []*datastore.DataStore{datastore.NewDataStore(log, datastore.NewTestCheckpoint(fakeCheckpoint), true)},
+		},
 		myNodeName:             myNodeName,
 		enablePrefixDelegation: true,
 		enableIPv4:             false,
@@ -322,9 +327,9 @@ func TestNodeInitwithPDenabledIPv6Mode(t *testing.T) {
 	var cidrs []string
 	m.awsutils.EXPECT().IsUnmanagedENI(eni1.ENIID).Return(false).AnyTimes()
 	m.awsutils.EXPECT().TagENI(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
-	m.awsutils.EXPECT().IsMultiCardENI(eni1.ENIID).Return(false).AnyTimes()
 
 	primaryIP := net.ParseIP(ipaddr01)
+	primaryIPv6 := net.ParseIP(v6ipaddr01)
 	m.network.EXPECT().SetupHostNetwork(cidrs, eni1.MAC, &primaryIP, false, false, true).Return(nil)
 	m.network.EXPECT().CleanUpStaleAWSChains(false, true).Return(nil)
 	m.awsutils.EXPECT().GetIPv6PrefixesFromEC2(eni1.ENIID).AnyTimes().Return(eni1.IPv6Prefixes, nil)
@@ -339,9 +344,10 @@ func TestNodeInitwithPDenabledIPv6Mode(t *testing.T) {
 		TrunkENI:    "",
 		EFAENIs:     make(map[string]bool),
 	}
+	m.awsutils.EXPECT().GetENILimit().Return(1)
 	m.awsutils.EXPECT().DescribeAllENIs().Return(resp, nil)
 	m.awsutils.EXPECT().GetLocalIPv4().Return(primaryIP)
-	m.awsutils.EXPECT().SetMultiCardENIs(resp.MultiCardENIIDs).AnyTimes()
+	m.awsutils.EXPECT().GetLocalIPv6().Return(primaryIPv6)
 
 	fakeNode := v1.Node{
 		TypeMeta:   metav1.TypeMeta{Kind: "Node"},
@@ -521,9 +527,9 @@ func testIncreaseIPPool(t *testing.T, useENIConfig bool, unschedulabeNode bool,
 		primaryIP:                 make(map[string]string),
 		terminating:               int32(0),
 	}
-	mockContext.dataStore = testDatastore()
+	mockContext.dataStoreAccess = testDatastore()
 	if subnetDiscovery {
-		mockContext.dataStore.AddENI(primaryENIid, primaryDevice, true, false, false)
+		mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AddENI(primaryENIid, primaryDevice, true, false, false)
 	}
 
 	primary := true
@@ -621,7 +627,7 @@ func testIncreaseIPPool(t *testing.T, useENIConfig bool, unschedulabeNode bool,
 		}
 		m.k8sClient.Create(ctx, &fakeENIConfig)
 	}
-	mockContext.increaseDatastorePool(ctx)
+	mockContext.increaseDatastorePool(ctx, defaultNetworkCard)
 }
 
 func assertAllocationExternalCalls(shouldCall bool, useENIConfig bool, m *testMocks, sg []*string, podENIConfig *eniconfigscheme.ENIConfigSpec, eni2 string, eniMetadata []awsutils.ENIMetadata, subnetDiscovery bool) {
@@ -651,7 +657,7 @@ func assertAllocationExternalCalls(shouldCall bool, useENIConfig bool, m *testMo
 	}
 	m.awsutils.EXPECT().GetPrimaryENI().Times(callCount).Return(primaryENIid)
 	m.awsutils.EXPECT().WaitForENIAndIPsAttached(secENIid, 14).Times(callCount).Return(eniMetadata[1], nil)
-	m.network.EXPECT().SetupENINetwork(gomock.Any(), secMAC, secDevice, secSubnet).Times(callCount)
+	m.network.EXPECT().SetupENINetwork(gomock.Any(), secMAC, secDevice, defaultNetworkCard, secSubnet, maxENIPerNIC).Times(callCount)
 }
 
 func TestIncreasePrefixPoolDefault(t *testing.T) {
@@ -691,9 +697,9 @@ func testIncreasePrefixPool(t *testing.T, useENIConfig, subnetDiscovery bool) {
 		enablePrefixDelegation:    true,
 	}
 
-	mockContext.dataStore = testDatastorewithPrefix()
+	mockContext.dataStoreAccess = testDatastorewithPrefix()
 	if subnetDiscovery {
-		mockContext.dataStore.AddENI(primaryENIid, primaryDevice, true, false, false)
+		mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AddENI(primaryENIid, primaryDevice, true, false, false)
 	}
 
 	primary := true
@@ -770,7 +776,7 @@ func testIncreasePrefixPool(t *testing.T, useENIConfig, subnetDiscovery bool) {
 
 	m.awsutils.EXPECT().GetPrimaryENI().Return(primaryENIid)
 	m.awsutils.EXPECT().WaitForENIAndIPsAttached(secENIid, 1).Return(eniMetadata[1], nil)
-	m.network.EXPECT().SetupENINetwork(gomock.Any(), secMAC, secDevice, secSubnet)
+	m.network.EXPECT().SetupENINetwork(gomock.Any(), secMAC, secDevice, defaultNetworkCard, secSubnet, maxENIPerNIC)
 
 	if mockContext.useCustomNetworking {
 		mockContext.myNodeName = myNodeName
@@ -800,7 +806,7 @@ func testIncreasePrefixPool(t *testing.T, useENIConfig, subnetDiscovery bool) {
 		m.k8sClient.Create(ctx, &fakeENIConfig)
 	}
 
-	mockContext.increaseDatastorePool(ctx)
+	mockContext.increaseDatastorePool(ctx, defaultNetworkCard)
 }
 
 // TestDecreaseIPPool checks that the deallocation honors the warm IP targets when deallocations happens across multiple enis
@@ -821,38 +827,38 @@ func TestDecreaseIPPool(t *testing.T) {
 	testAddr2 := net.IPNet{IP: net.ParseIP(ipaddr02), Mask: net.IPv4Mask(255, 255, 255, 255)}
 	testAddr11 := net.IPNet{IP: net.ParseIP(ipaddr11), Mask: net.IPv4Mask(255, 255, 255, 255)}
 	testAddr12 := net.IPNet{IP: net.ParseIP(ipaddr12), Mask: net.IPv4Mask(255, 255, 255, 255)}
+	// TODO Fix getting datastore
+	mockContext.dataStoreAccess = testDatastore()
 
-	mockContext.dataStore = testDatastore()
-
-	mockContext.dataStore.AddENI(primaryENIid, primaryDevice, true, false, false)
-	mockContext.dataStore.AddIPv4CidrToStore(primaryENIid, testAddr1, false)
-	mockContext.dataStore.AddIPv4CidrToStore(primaryENIid, testAddr2, false)
-	mockContext.dataStore.AssignPodIPv4Address(datastore.IPAMKey{ContainerID: "container1"}, datastore.IPAMMetadata{K8SPodName: "pod1"})
+	mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AddENI(primaryENIid, primaryDevice, true, false, false)
+	mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AddIPv4CidrToStore(primaryENIid, testAddr1, false)
+	mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AddIPv4CidrToStore(primaryENIid, testAddr2, false)
+	mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AssignPodIPv4Address(datastore.IPAMKey{ContainerID: "container1"}, datastore.IPAMMetadata{K8SPodName: "pod1"})
 
-	mockContext.dataStore.AddENI(secENIid, secDevice, true, false, false)
-	mockContext.dataStore.AddIPv4CidrToStore(secENIid, testAddr11, false)
-	mockContext.dataStore.AddIPv4CidrToStore(secENIid, testAddr12, false)
-	mockContext.dataStore.AssignPodIPv4Address(datastore.IPAMKey{ContainerID: "container2"}, datastore.IPAMMetadata{K8SPodName: "pod2"})
+	mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AddENI(secENIid, secDevice, true, false, false)
+	mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AddIPv4CidrToStore(secENIid, testAddr11, false)
+	mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AddIPv4CidrToStore(secENIid, testAddr12, false)
+	mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AssignPodIPv4Address(datastore.IPAMKey{ContainerID: "container2"}, datastore.IPAMMetadata{K8SPodName: "pod2"})
 
 	m.awsutils.EXPECT().DeallocPrefixAddresses(gomock.Any(), gomock.Any()).Times(1)
 	m.awsutils.EXPECT().DeallocIPAddresses(gomock.Any(), gomock.Any()).Times(1)
 
-	short, over, enabled := mockContext.datastoreTargetState(nil)
+	short, over, enabled := mockContext.datastoreTargetState(nil, defaultNetworkCard)
 	assert.Equal(t, 0, short)      // there would not be any shortage
 	assert.Equal(t, 1, over)       // out of 4 IPs we have 2 IPs assigned, warm IP target is 1, so over is 1
 	assert.Equal(t, true, enabled) // there is warm ip target enabled with the value of 1
 
-	mockContext.decreaseDatastorePool(10 * time.Second)
+	mockContext.decreaseDatastorePool(defaultNetworkCard)
 
-	short, over, enabled = mockContext.datastoreTargetState(nil)
+	short, over, enabled = mockContext.datastoreTargetState(nil, defaultNetworkCard)
 	assert.Equal(t, 0, short)      // there would not be any shortage
 	assert.Equal(t, 0, over)       // after the above deallocation this should be zero
 	assert.Equal(t, true, enabled) // there is warm ip target enabled with the value of 1
 
 	// make another call just to ensure that more deallocations do not happen
-	mockContext.decreaseDatastorePool(10 * time.Second)
+	mockContext.decreaseDatastorePool(defaultNetworkCard)
 
-	short, over, enabled = mockContext.datastoreTargetState(nil)
+	short, over, enabled = mockContext.datastoreTargetState(nil, defaultNetworkCard)
 	assert.Equal(t, 0, short)      // there would not be any shortage
 	assert.Equal(t, 0, over)       // after the above deallocation this should be zero
 	assert.Equal(t, true, enabled) // there is warm ip target enabled with the value of 1
@@ -884,7 +890,7 @@ func TestTryAddIPToENI(t *testing.T) {
 		terminating:   int32(0),
 	}
 
-	mockContext.dataStore = testDatastore()
+	mockContext.dataStoreAccess = testDatastore()
 
 	m.awsutils.EXPECT().AllocENI(false, nil, "", warmIPTarget).Return(secENIid, nil)
 	eniMetadata := []awsutils.ENIMetadata{
@@ -919,7 +925,7 @@ func TestTryAddIPToENI(t *testing.T) {
 	}
 	m.awsutils.EXPECT().WaitForENIAndIPsAttached(secENIid, 3).Return(eniMetadata[1], nil)
 	m.awsutils.EXPECT().GetPrimaryENI().Return(primaryENIid)
-	m.network.EXPECT().SetupENINetwork(gomock.Any(), secMAC, secDevice, secSubnet)
+	m.network.EXPECT().SetupENINetwork(gomock.Any(), secMAC, secDevice, defaultNetworkCard, secSubnet, maxENIPerNIC)
 
 	mockContext.myNodeName = myNodeName
 
@@ -931,7 +937,7 @@ func TestTryAddIPToENI(t *testing.T) {
 		Status:     v1.NodeStatus{},
 	}
 	m.k8sClient.Create(ctx, &fakeNode)
-	mockContext.increaseDatastorePool(ctx)
+	mockContext.increaseDatastorePool(ctx, defaultNetworkCard)
 }
 
 func TestNodeIPPoolReconcile(t *testing.T) {
@@ -946,7 +952,7 @@ func TestNodeIPPoolReconcile(t *testing.T) {
 		terminating:   int32(0),
 	}
 
-	mockContext.dataStore = testDatastore()
+	mockContext.dataStoreAccess = testDatastore()
 
 	primary := true
 	primaryENIMetadata := getPrimaryENIMetadata()
@@ -970,7 +976,7 @@ func TestNodeIPPoolReconcile(t *testing.T) {
 	m.awsutils.EXPECT().SetMultiCardENIs(resp.MultiCardENIIDs).AnyTimes()
 	mockContext.nodeIPPoolReconcile(ctx, 0)
 
-	curENIs := mockContext.dataStore.GetENIInfos()
+	curENIs := mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).GetENIInfos()
 	assert.Equal(t, 1, len(curENIs.ENIs))
 	assert.Equal(t, 2, curENIs.TotalIPs)
 
@@ -992,7 +998,7 @@ func TestNodeIPPoolReconcile(t *testing.T) {
 	m.awsutils.EXPECT().GetIPv4sFromEC2(primaryENIid).Return(oneIPUnassigned[0].IPv4Addresses, nil)
 
 	mockContext.nodeIPPoolReconcile(ctx, 0)
-	curENIs = mockContext.dataStore.GetENIInfos()
+	curENIs = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).GetENIInfos()
 	assert.Equal(t, 1, len(curENIs.ENIs))
 	assert.Equal(t, 0, curENIs.TotalIPs)
 
@@ -1013,13 +1019,13 @@ func TestNodeIPPoolReconcile(t *testing.T) {
 		MultiCardENIIDs: nil,
 	}
 	m.awsutils.EXPECT().DescribeAllENIs().Return(resp2, nil)
-	m.network.EXPECT().SetupENINetwork(gomock.Any(), secMAC, secDevice, primarySubnet)
+	m.network.EXPECT().SetupENINetwork(gomock.Any(), secMAC, secDevice, defaultNetworkCard, primarySubnet, maxENIPerNIC)
 	m.awsutils.EXPECT().SetMultiCardENIs(resp2.MultiCardENIIDs).AnyTimes()
 
 	mockContext.nodeIPPoolReconcile(ctx, 0)
 
 	// Verify that we now have 2 ENIs, primary ENI with 0 secondary IPs, and secondary ENI with 1 secondary IP
-	curENIs = mockContext.dataStore.GetENIInfos()
+	curENIs = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).GetENIInfos()
 	assert.Equal(t, 2, len(curENIs.ENIs))
 	assert.Equal(t, 1, curENIs.TotalIPs)
 
@@ -1027,7 +1033,7 @@ func TestNodeIPPoolReconcile(t *testing.T) {
 	m.awsutils.EXPECT().GetAttachedENIs().Return(oneIPUnassigned, nil)
 
 	mockContext.nodeIPPoolReconcile(ctx, 0)
-	curENIs = mockContext.dataStore.GetENIInfos()
+	curENIs = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).GetENIInfos()
 	assert.Equal(t, 1, len(curENIs.ENIs))
 	assert.Equal(t, 0, curENIs.TotalIPs)
 }
@@ -1045,7 +1051,7 @@ func TestNodePrefixPoolReconcile(t *testing.T) {
 		enablePrefixDelegation: true,
 	}
 
-	mockContext.dataStore = testDatastorewithPrefix()
+	mockContext.dataStoreAccess = testDatastorewithPrefix()
 
 	primary := true
 	primaryENIMetadata := getPrimaryENIMetadataPDenabled()
@@ -1069,7 +1075,7 @@ func TestNodePrefixPoolReconcile(t *testing.T) {
 	m.awsutils.EXPECT().SetMultiCardENIs(resp.MultiCardENIIDs).AnyTimes()
 	mockContext.nodeIPPoolReconcile(ctx, 0)
 
-	curENIs := mockContext.dataStore.GetENIInfos()
+	curENIs := mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).GetENIInfos()
 	assert.Equal(t, 1, len(curENIs.ENIs))
 	assert.Equal(t, 16, curENIs.TotalIPs)
 
@@ -1094,7 +1100,7 @@ func TestNodePrefixPoolReconcile(t *testing.T) {
 	// m.awsutils.EXPECT().GetIPv4sFromEC2(primaryENIid).Return(oneIPUnassigned[0].IPv4Addresses, nil)
 
 	mockContext.nodeIPPoolReconcile(ctx, 0)
-	curENIs = mockContext.dataStore.GetENIInfos()
+	curENIs = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).GetENIInfos()
 	assert.Equal(t, 1, len(curENIs.ENIs))
 	assert.Equal(t, 0, curENIs.TotalIPs)
 
@@ -1114,13 +1120,13 @@ func TestNodePrefixPoolReconcile(t *testing.T) {
 		EFAENIs:     make(map[string]bool),
 	}
 	m.awsutils.EXPECT().DescribeAllENIs().Return(resp2, nil)
-	m.network.EXPECT().SetupENINetwork(gomock.Any(), secMAC, secDevice, primarySubnet)
+	m.network.EXPECT().SetupENINetwork(gomock.Any(), secMAC, secDevice, defaultNetworkCard, primarySubnet, maxENIPerNIC)
 	m.awsutils.EXPECT().SetMultiCardENIs(resp2.MultiCardENIIDs).AnyTimes()
 
 	mockContext.nodeIPPoolReconcile(ctx, 0)
 
 	// Verify that we now have 2 ENIs, primary ENI with 0 prefixes, and secondary ENI with 1 prefix
-	curENIs = mockContext.dataStore.GetENIInfos()
+	curENIs = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).GetENIInfos()
 	assert.Equal(t, 2, len(curENIs.ENIs))
 	assert.Equal(t, 16, curENIs.TotalIPs)
 
@@ -1128,7 +1134,7 @@ func TestNodePrefixPoolReconcile(t *testing.T) {
 	m.awsutils.EXPECT().GetAttachedENIs().Return(oneIPUnassigned, nil)
 
 	mockContext.nodeIPPoolReconcile(ctx, 0)
-	curENIs = mockContext.dataStore.GetENIInfos()
+	curENIs = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).GetENIInfos()
 	assert.Equal(t, 1, len(curENIs.ENIs))
 	assert.Equal(t, 0, curENIs.TotalIPs)
 }
@@ -1177,38 +1183,38 @@ func TestGetWarmIPTargetState(t *testing.T) {
 		primaryIP:     make(map[string]string),
 		terminating:   int32(0),
 	}
-	mockContext.dataStore = testDatastore()
+	mockContext.dataStoreAccess = testDatastore()
 
-	_, _, warmIPTargetDefined := mockContext.datastoreTargetState(nil)
+	_, _, warmIPTargetDefined := mockContext.datastoreTargetState(nil, defaultNetworkCard)
 	assert.False(t, warmIPTargetDefined)
 
 	mockContext.warmIPTarget = 5
-	short, over, warmIPTargetDefined := mockContext.datastoreTargetState(nil)
+	short, over, warmIPTargetDefined := mockContext.datastoreTargetState(nil, defaultNetworkCard)
 	assert.True(t, warmIPTargetDefined)
 	assert.Equal(t, 5, short)
 	assert.Equal(t, 0, over)
 
 	// add 2 addresses to datastore
-	_ = mockContext.dataStore.AddENI("eni-1", 1, true, false, false)
+	_ = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AddENI("eni-1", 1, true, false, false)
 	ipv4Addr := net.IPNet{IP: net.ParseIP("1.1.1.1"), Mask: net.IPv4Mask(255, 255, 255, 255)}
-	_ = mockContext.dataStore.AddIPv4CidrToStore("eni-1", ipv4Addr, false)
+	_ = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AddIPv4CidrToStore("eni-1", ipv4Addr, false)
 	ipv4Addr = net.IPNet{IP: net.ParseIP("1.1.1.2"), Mask: net.IPv4Mask(255, 255, 255, 255)}
-	_ = mockContext.dataStore.AddIPv4CidrToStore("eni-1", ipv4Addr, false)
+	_ = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AddIPv4CidrToStore("eni-1", ipv4Addr, false)
 
-	short, over, warmIPTargetDefined = mockContext.datastoreTargetState(nil)
+	short, over, warmIPTargetDefined = mockContext.datastoreTargetState(nil, defaultNetworkCard)
 	assert.True(t, warmIPTargetDefined)
 	assert.Equal(t, 3, short)
 	assert.Equal(t, 0, over)
 
 	// add 3 more addresses to datastore
 	ipv4Addr = net.IPNet{IP: net.ParseIP("1.1.1.3"), Mask: net.IPv4Mask(255, 255, 255, 255)}
-	_ = mockContext.dataStore.AddIPv4CidrToStore("eni-1", ipv4Addr, false)
+	_ = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AddIPv4CidrToStore("eni-1", ipv4Addr, false)
 	ipv4Addr = net.IPNet{IP: net.ParseIP("1.1.1.4"), Mask: net.IPv4Mask(255, 255, 255, 255)}
-	_ = mockContext.dataStore.AddIPv4CidrToStore("eni-1", ipv4Addr, false)
+	_ = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AddIPv4CidrToStore("eni-1", ipv4Addr, false)
 	ipv4Addr = net.IPNet{IP: net.ParseIP("1.1.1.5"), Mask: net.IPv4Mask(255, 255, 255, 255)}
-	_ = mockContext.dataStore.AddIPv4CidrToStore("eni-1", ipv4Addr, false)
+	_ = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AddIPv4CidrToStore("eni-1", ipv4Addr, false)
 
-	short, over, warmIPTargetDefined = mockContext.datastoreTargetState(nil)
+	short, over, warmIPTargetDefined = mockContext.datastoreTargetState(nil, defaultNetworkCard)
 	assert.True(t, warmIPTargetDefined)
 	assert.Equal(t, 0, short)
 	assert.Equal(t, 0, over)
@@ -1226,35 +1232,35 @@ func TestGetWarmIPTargetStateWithPDenabled(t *testing.T) {
 		enablePrefixDelegation: true,
 	}
 
-	mockContext.dataStore = testDatastorewithPrefix()
+	mockContext.dataStoreAccess = testDatastorewithPrefix()
 
-	_, _, warmIPTargetDefined := mockContext.datastoreTargetState(nil)
+	_, _, warmIPTargetDefined := mockContext.datastoreTargetState(nil, defaultNetworkCard)
 	assert.False(t, warmIPTargetDefined)
 
 	mockContext.warmIPTarget = 5
-	short, over, warmIPTargetDefined := mockContext.datastoreTargetState(nil)
+	short, over, warmIPTargetDefined := mockContext.datastoreTargetState(nil, defaultNetworkCard)
 	assert.True(t, warmIPTargetDefined)
 	assert.Equal(t, 1, short)
 	assert.Equal(t, 0, over)
 
 	// add 2 addresses to datastore
-	_ = mockContext.dataStore.AddENI("eni-1", 1, true, false, false)
+	_ = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AddENI("eni-1", 1, true, false, false)
 	_, ipnet, _ := net.ParseCIDR("10.1.1.0/28")
-	_ = mockContext.dataStore.AddIPv4CidrToStore("eni-1", *ipnet, true)
-	_ = mockContext.dataStore.AddENI("eni-2", 2, true, false, false)
+	_ = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AddIPv4CidrToStore("eni-1", *ipnet, true)
+	_ = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AddENI("eni-2", 2, true, false, false)
 	_, ipnet, _ = net.ParseCIDR("20.1.1.0/28")
-	_ = mockContext.dataStore.AddIPv4CidrToStore("eni-1", *ipnet, true)
+	_ = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AddIPv4CidrToStore("eni-1", *ipnet, true)
 
-	short, over, warmIPTargetDefined = mockContext.datastoreTargetState(nil)
+	short, over, warmIPTargetDefined = mockContext.datastoreTargetState(nil, defaultNetworkCard)
 	assert.True(t, warmIPTargetDefined)
 	assert.Equal(t, 0, short)
 	assert.Equal(t, 1, over)
 
 	// Del 1 address
 	_, ipnet, _ = net.ParseCIDR("20.1.1.0/28")
-	_ = mockContext.dataStore.DelIPv4CidrFromStore("eni-1", *ipnet, true)
+	_ = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).DelIPv4CidrFromStore("eni-1", *ipnet, true)
 
-	short, over, warmIPTargetDefined = mockContext.datastoreTargetState(nil)
+	short, over, warmIPTargetDefined = mockContext.datastoreTargetState(nil, defaultNetworkCard)
 	assert.True(t, warmIPTargetDefined)
 	assert.Equal(t, 0, short)
 	assert.Equal(t, 0, over)
@@ -1265,12 +1271,12 @@ func TestIPAMContext_nodeIPPoolTooLow(t *testing.T) {
 	defer m.ctrl.Finish()
 
 	type fields struct {
-		maxIPsPerENI  int
-		maxEni        int
-		warmENITarget int
-		warmIPTarget  int
-		datastore     *datastore.DataStore
-		maxPods       int
+		maxIPsPerENI    int
+		maxEni          int
+		warmENITarget   int
+		warmIPTarget    int
+		datastoreAccess *datastore.DataStoreAccess
+		maxPods         int
 	}
 
 	tests := []struct {
@@ -1292,7 +1298,7 @@ func TestIPAMContext_nodeIPPoolTooLow(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			c := &IPAMContext{
 				awsClient:              m.awsutils,
-				dataStore:              tt.fields.datastore,
+				dataStoreAccess:        tt.fields.datastoreAccess,
 				useCustomNetworking:    false,
 				networkClient:          m.network,
 				maxIPsPerENI:           tt.fields.maxIPsPerENI,
@@ -1302,8 +1308,10 @@ func TestIPAMContext_nodeIPPoolTooLow(t *testing.T) {
 				enablePrefixDelegation: false,
 				maxPods:                tt.fields.maxPods,
 			}
-			if got, _ := c.isDatastorePoolTooLow(); got != tt.want {
-				t.Errorf("nodeIPPoolTooLow() = %v, want %v", got, tt.want)
+			if decisions := c.isDatastorePoolTooLow(); decisions != nil {
+				if decisions[defaultNetworkCard].IsLow != tt.want {
+					t.Errorf("nodeIPPoolTooLow() = %v, want %v", decisions[defaultNetworkCard].IsLow, tt.want)
+				}
 			}
 		})
 	}
@@ -1318,7 +1326,7 @@ func TestIPAMContext_nodePrefixPoolTooLow(t *testing.T) {
 		maxEni            int
 		maxPrefixesPerENI int
 		warmPrefixTarget  int
-		datastore         *datastore.DataStore
+		datastoreAccess   *datastore.DataStoreAccess
 		maxPods           int
 	}
 
@@ -1340,7 +1348,7 @@ func TestIPAMContext_nodePrefixPoolTooLow(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			c := &IPAMContext{
 				awsClient:              m.awsutils,
-				dataStore:              tt.fields.datastore,
+				dataStoreAccess:        tt.fields.datastoreAccess,
 				useCustomNetworking:    false,
 				networkClient:          m.network,
 				maxPrefixesPerENI:      tt.fields.maxPrefixesPerENI,
@@ -1350,37 +1358,44 @@ func TestIPAMContext_nodePrefixPoolTooLow(t *testing.T) {
 				enablePrefixDelegation: true,
 				maxPods:                tt.fields.maxPods,
 			}
-			if got, _ := c.isDatastorePoolTooLow(); got != tt.want {
-				t.Errorf("nodeIPPoolTooLow() = %v, want %v", got, tt.want)
+			if decisions := c.isDatastorePoolTooLow(); decisions != nil {
+				if decisions[defaultNetworkCard].IsLow != tt.want {
+					t.Errorf("nodeIPPoolTooLow() = %v, want %v", decisions[defaultNetworkCard].IsLow, tt.want)
+				}
 			}
 		})
 	}
 }
 
-func testDatastore() *datastore.DataStore {
-	return datastore.NewDataStore(log, datastore.NewTestCheckpoint(datastore.CheckpointData{Version: datastore.CheckpointFormatVersion}), false)
+func testDatastore() *datastore.DataStoreAccess {
+	return &datastore.DataStoreAccess{
+		DataStores: []*datastore.DataStore{datastore.NewDataStore(log, datastore.NewTestCheckpoint(datastore.CheckpointData{Version: datastore.CheckpointFormatVersion}), false)},
+	}
 }
 
-func testDatastorewithPrefix() *datastore.DataStore {
-	return datastore.NewDataStore(log, datastore.NewTestCheckpoint(datastore.CheckpointData{Version: datastore.CheckpointFormatVersion}), true)
+func testDatastorewithPrefix() *datastore.DataStoreAccess {
+
+	return &datastore.DataStoreAccess{
+		DataStores: []*datastore.DataStore{datastore.NewDataStore(log, datastore.NewTestCheckpoint(datastore.CheckpointData{Version: datastore.CheckpointFormatVersion}), true)},
+	}
 }
 
-func datastoreWith3FreeIPs() *datastore.DataStore {
+func datastoreWith3FreeIPs() *datastore.DataStoreAccess {
 	datastoreWith3FreeIPs := testDatastore()
-	_ = datastoreWith3FreeIPs.AddENI(primaryENIid, 1, true, false, false)
+	_ = datastoreWith3FreeIPs.GetDataStore(defaultNetworkCard).AddENI(primaryENIid, 1, true, false, false)
 	ipv4Addr := net.IPNet{IP: net.ParseIP(ipaddr01), Mask: net.IPv4Mask(255, 255, 255, 255)}
-	_ = datastoreWith3FreeIPs.AddIPv4CidrToStore(primaryENIid, ipv4Addr, false)
+	_ = datastoreWith3FreeIPs.GetDataStore(defaultNetworkCard).AddIPv4CidrToStore(primaryENIid, ipv4Addr, false)
 	ipv4Addr = net.IPNet{IP: net.ParseIP(ipaddr02), Mask: net.IPv4Mask(255, 255, 255, 255)}
-	_ = datastoreWith3FreeIPs.AddIPv4CidrToStore(primaryENIid, ipv4Addr, false)
+	_ = datastoreWith3FreeIPs.GetDataStore(defaultNetworkCard).AddIPv4CidrToStore(primaryENIid, ipv4Addr, false)
 	ipv4Addr = net.IPNet{IP: net.ParseIP(ipaddr03), Mask: net.IPv4Mask(255, 255, 255, 255)}
-	_ = datastoreWith3FreeIPs.AddIPv4CidrToStore(primaryENIid, ipv4Addr, false)
+	_ = datastoreWith3FreeIPs.GetDataStore(defaultNetworkCard).AddIPv4CidrToStore(primaryENIid, ipv4Addr, false)
 	return datastoreWith3FreeIPs
 }
 
-func datastoreWith1Pod1() *datastore.DataStore {
+func datastoreWith1Pod1() *datastore.DataStoreAccess {
 	datastoreWith1Pod1 := datastoreWith3FreeIPs()
 
-	_, _, _ = datastoreWith1Pod1.AssignPodIPv4Address(datastore.IPAMKey{
+	_, _, _ = datastoreWith1Pod1.GetDataStore(defaultNetworkCard).AssignPodIPv4Address(datastore.IPAMKey{
 		NetworkName: "net0",
 		ContainerID: "sandbox-1",
 		IfName:      "eth0",
@@ -1391,7 +1406,7 @@ func datastoreWith1Pod1() *datastore.DataStore {
 	return datastoreWith1Pod1
 }
 
-func datastoreWith3Pods() *datastore.DataStore {
+func datastoreWith3Pods() *datastore.DataStoreAccess {
 	datastoreWith3Pods := datastoreWith3FreeIPs()
 
 	for i := 0; i < 3; i++ {
@@ -1400,7 +1415,7 @@ func datastoreWith3Pods() *datastore.DataStore {
 			ContainerID: fmt.Sprintf("sandbox-%d", i),
 			IfName:      "eth0",
 		}
-		_, _, _ = datastoreWith3Pods.AssignPodIPv4Address(key, datastore.IPAMMetadata{
+		_, _, _ = datastoreWith3Pods.GetDataStore(defaultNetworkCard).AssignPodIPv4Address(key, datastore.IPAMMetadata{
 			K8SPodNamespace: "default",
 			K8SPodName:      fmt.Sprintf("sample-pod-%d", i),
 		})
@@ -1408,18 +1423,18 @@ func datastoreWith3Pods() *datastore.DataStore {
 	return datastoreWith3Pods
 }
 
-func datastoreWithFreeIPsFromPrefix() *datastore.DataStore {
+func datastoreWithFreeIPsFromPrefix() *datastore.DataStoreAccess {
 	datastoreWithFreeIPs := testDatastorewithPrefix()
-	_ = datastoreWithFreeIPs.AddENI(primaryENIid, 1, true, false, false)
+	_ = datastoreWithFreeIPs.GetDataStore(defaultNetworkCard).AddENI(primaryENIid, 1, true, false, false)
 	_, ipnet, _ := net.ParseCIDR(prefix01)
-	_ = datastoreWithFreeIPs.AddIPv4CidrToStore(primaryENIid, *ipnet, true)
+	_ = datastoreWithFreeIPs.GetDataStore(defaultNetworkCard).AddIPv4CidrToStore(primaryENIid, *ipnet, true)
 	return datastoreWithFreeIPs
 }
 
-func datastoreWith1Pod1FromPrefix() *datastore.DataStore {
+func datastoreWith1Pod1FromPrefix() *datastore.DataStoreAccess {
 	datastoreWith1Pod1 := datastoreWithFreeIPsFromPrefix()
 
-	_, _, _ = datastoreWith1Pod1.AssignPodIPv4Address(datastore.IPAMKey{
+	_, _, _ = datastoreWith1Pod1.GetDataStore(defaultNetworkCard).AssignPodIPv4Address(datastore.IPAMKey{
 		NetworkName: "net0",
 		ContainerID: "sandbox-1",
 		IfName:      "eth0",
@@ -1430,7 +1445,7 @@ func datastoreWith1Pod1FromPrefix() *datastore.DataStore {
 	return datastoreWith1Pod1
 }
 
-func datastoreWith3PodsFromPrefix() *datastore.DataStore {
+func datastoreWith3PodsFromPrefix() *datastore.DataStoreAccess {
 	datastoreWith3Pods := datastoreWithFreeIPsFromPrefix()
 
 	for i := 0; i < 3; i++ {
@@ -1439,7 +1454,7 @@ func datastoreWith3PodsFromPrefix() *datastore.DataStore {
 			ContainerID: fmt.Sprintf("sandbox-%d", i),
 			IfName:      "eth0",
 		}
-		_, _, _ = datastoreWith3Pods.AssignPodIPv4Address(key,
+		_, _, _ = datastoreWith3Pods.GetDataStore(defaultNetworkCard).AssignPodIPv4Address(key,
 			datastore.IPAMMetadata{
 				K8SPodNamespace: "default",
 				K8SPodName:      fmt.Sprintf("sample-pod-%d", i),
@@ -1669,16 +1684,16 @@ func TestNodeIPPoolReconcileBadIMDSData(t *testing.T) {
 		terminating:   int32(0),
 	}
 
-	mockContext.dataStore = testDatastore()
+	mockContext.dataStoreAccess = testDatastore()
 
 	primaryENIMetadata := getPrimaryENIMetadata()
 	testAddr1 := *primaryENIMetadata.IPv4Addresses[0].PrivateIpAddress
 	// Add ENI and IPs to datastore
 	eniID := primaryENIMetadata.ENIID
-	_ = mockContext.dataStore.AddENI(eniID, primaryENIMetadata.DeviceNumber, true, false, false)
+	_ = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AddENI(eniID, primaryENIMetadata.DeviceNumber, true, false, false)
 	mockContext.primaryIP[eniID] = testAddr1
-	mockContext.addENIsecondaryIPsToDataStore(primaryENIMetadata.IPv4Addresses, eniID)
-	curENIs := mockContext.dataStore.GetENIInfos()
+	mockContext.addENIsecondaryIPsToDataStore(primaryENIMetadata.IPv4Addresses, eniID, defaultNetworkCard)
+	curENIs := mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).GetENIInfos()
 	assert.Equal(t, 1, len(curENIs.ENIs))
 	assert.Equal(t, 2, curENIs.TotalIPs)
 	eniMetadataList := []awsutils.ENIMetadata{primaryENIMetadata}
@@ -1708,7 +1723,7 @@ func TestNodeIPPoolReconcileBadIMDSData(t *testing.T) {
 	// eniIPPoolReconcile() calls EC2 to get the actual count, but that call fails
 	m.awsutils.EXPECT().GetIPv4sFromEC2(primaryENIid).Return(nil, errors.New("ec2 API call failed"))
 	mockContext.nodeIPPoolReconcile(ctx, 0)
-	curENIs = mockContext.dataStore.GetENIInfos()
+	curENIs = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).GetENIInfos()
 	assert.Equal(t, 1, len(curENIs.ENIs))
 	assert.Equal(t, 2, curENIs.TotalIPs)
 
@@ -1730,14 +1745,14 @@ func TestNodeIPPoolReconcileBadIMDSData(t *testing.T) {
 	// eniIPPoolReconcile() calls EC2 to get the actual count that should still be 2
 	m.awsutils.EXPECT().GetIPv4sFromEC2(primaryENIid).Return(primaryENIMetadata.IPv4Addresses, nil)
 	mockContext.nodeIPPoolReconcile(ctx, 0)
-	curENIs = mockContext.dataStore.GetENIInfos()
+	curENIs = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).GetENIInfos()
 	assert.Equal(t, 1, len(curENIs.ENIs))
 	assert.Equal(t, 2, curENIs.TotalIPs)
 
 	// If no ENI is found, we abort the reconcile
 	m.awsutils.EXPECT().GetAttachedENIs().Return(nil, nil)
 	mockContext.nodeIPPoolReconcile(ctx, 0)
-	curENIs = mockContext.dataStore.GetENIInfos()
+	curENIs = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).GetENIInfos()
 	assert.Equal(t, 1, len(curENIs.ENIs))
 	assert.Equal(t, 2, curENIs.TotalIPs)
 }
@@ -1755,16 +1770,16 @@ func TestNodePrefixPoolReconcileBadIMDSData(t *testing.T) {
 		enablePrefixDelegation: true,
 	}
 
-	mockContext.dataStore = testDatastorewithPrefix()
+	mockContext.dataStoreAccess = testDatastorewithPrefix()
 
 	primaryENIMetadata := getPrimaryENIMetadataPDenabled()
 	testAddr1 := *primaryENIMetadata.IPv4Addresses[0].PrivateIpAddress
 	// Add ENI and IPs to datastore
 	eniID := primaryENIMetadata.ENIID
-	_ = mockContext.dataStore.AddENI(eniID, primaryENIMetadata.DeviceNumber, true, false, false)
+	_ = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AddENI(eniID, primaryENIMetadata.DeviceNumber, true, false, false)
 	mockContext.primaryIP[eniID] = testAddr1
-	mockContext.addENIv4prefixesToDataStore(primaryENIMetadata.IPv4Prefixes, eniID)
-	curENIs := mockContext.dataStore.GetENIInfos()
+	mockContext.addENIv4prefixesToDataStore(primaryENIMetadata.IPv4Prefixes, eniID, defaultNetworkCard)
+	curENIs := mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).GetENIInfos()
 	assert.Equal(t, 1, len(curENIs.ENIs))
 	assert.Equal(t, 16, curENIs.TotalIPs)
 	eniMetadataList := []awsutils.ENIMetadata{primaryENIMetadata}
@@ -1794,7 +1809,7 @@ func TestNodePrefixPoolReconcileBadIMDSData(t *testing.T) {
 	// eniIPPoolReconcile() calls EC2 to get the actual count, but that call fails
 	m.awsutils.EXPECT().GetIPv4PrefixesFromEC2(primaryENIid).Return(nil, errors.New("ec2 API call failed"))
 	mockContext.nodeIPPoolReconcile(ctx, 0)
-	curENIs = mockContext.dataStore.GetENIInfos()
+	curENIs = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).GetENIInfos()
 	assert.Equal(t, 1, len(curENIs.ENIs))
 	assert.Equal(t, 16, curENIs.TotalIPs)
 
@@ -1816,14 +1831,14 @@ func TestNodePrefixPoolReconcileBadIMDSData(t *testing.T) {
 	// eniIPPoolReconcile() calls EC2 to get the actual count that should still be 16
 	m.awsutils.EXPECT().GetIPv4PrefixesFromEC2(primaryENIid).Return(primaryENIMetadata.IPv4Prefixes, nil)
 	mockContext.nodeIPPoolReconcile(ctx, 0)
-	curENIs = mockContext.dataStore.GetENIInfos()
+	curENIs = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).GetENIInfos()
 	assert.Equal(t, 1, len(curENIs.ENIs))
 	assert.Equal(t, 16, curENIs.TotalIPs)
 
 	// If no ENI is found, we abort the reconcile
 	m.awsutils.EXPECT().GetAttachedENIs().Return(nil, nil)
 	mockContext.nodeIPPoolReconcile(ctx, 0)
-	curENIs = mockContext.dataStore.GetENIInfos()
+	curENIs = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).GetENIInfos()
 	assert.Equal(t, 1, len(curENIs.ENIs))
 	assert.Equal(t, 16, curENIs.TotalIPs)
 }
@@ -1937,7 +1952,7 @@ func TestIPAMContext_setupENI(t *testing.T) {
 	}
 	// mockContext.primaryIP[]
 
-	mockContext.dataStore = testDatastore()
+	mockContext.dataStoreAccess = testDatastore()
 	primary := true
 	notPrimary := false
 	testAddr1 := ipaddr01
@@ -1964,7 +1979,7 @@ func TestIPAMContext_setupENI(t *testing.T) {
 
 	newENIMetadata := getSecondaryENIMetadata()
 	m.awsutils.EXPECT().GetPrimaryENI().Return(primaryENIid)
-	m.network.EXPECT().SetupENINetwork(gomock.Any(), secMAC, secDevice, primarySubnet).Return(errors.New("not able to set route 0.0.0.0/0 via 10.10.10.1 table 2"))
+	m.network.EXPECT().SetupENINetwork(gomock.Any(), secMAC, secDevice, defaultNetworkCard, primarySubnet, maxENIPerNIC).Return(errors.New("not able to set route 0.0.0.0/0 via 10.10.10.1 table 2"))
 
 	err = mockContext.setupENI(newENIMetadata.ENIID, newENIMetadata, false, false)
 	assert.Error(t, err)
@@ -1983,7 +1998,7 @@ func TestIPAMContext_setupENIwithPDenabled(t *testing.T) {
 	}
 	// mockContext.primaryIP[]
 
-	mockContext.dataStore = testDatastorewithPrefix()
+	mockContext.dataStoreAccess = testDatastorewithPrefix()
 	primary := true
 	notPrimary := false
 	testAddr1 := ipaddr01
@@ -2010,7 +2025,7 @@ func TestIPAMContext_setupENIwithPDenabled(t *testing.T) {
 
 	newENIMetadata := getSecondaryENIMetadata()
 	m.awsutils.EXPECT().GetPrimaryENI().Return(primaryENIid)
-	m.network.EXPECT().SetupENINetwork(gomock.Any(), secMAC, secDevice, primarySubnet).Return(errors.New("not able to set route 0.0.0.0/0 via 10.10.10.1 table 2"))
+	m.network.EXPECT().SetupENINetwork(gomock.Any(), secMAC, secDevice, defaultNetworkCard, primarySubnet, maxENIPerNIC).Return(errors.New("not able to set route 0.0.0.0/0 via 10.10.10.1 table 2"))
 
 	err = mockContext.setupENI(newENIMetadata.ENIID, newENIMetadata, false, false)
 	assert.Error(t, err)
@@ -2023,10 +2038,12 @@ func TestIPAMContext_enableSecurityGroupsForPods(t *testing.T) {
 	ctx := context.Background()
 
 	mockContext := &IPAMContext{
-		k8sClient:     m.k8sClient,
-		enableIPv4:    true,
-		enableIPv6:    false,
-		dataStore:     datastore.NewDataStore(log, datastore.NewTestCheckpoint(datastore.CheckpointData{Version: datastore.CheckpointFormatVersion}), false),
+		k8sClient:  m.k8sClient,
+		enableIPv4: true,
+		enableIPv6: false,
+		dataStoreAccess: &datastore.DataStoreAccess{
+			DataStores: []*datastore.DataStore{datastore.NewDataStore(log, datastore.NewTestCheckpoint(datastore.CheckpointData{Version: datastore.CheckpointFormatVersion}), false)},
+		},
 		awsClient:     m.awsutils,
 		networkClient: m.network,
 		primaryIP:     make(map[string]string),
@@ -2050,7 +2067,7 @@ func TestIPAMContext_enableSecurityGroupsForPods(t *testing.T) {
 	err := m.k8sClient.Create(ctx, &fakeCNINode)
 	assert.NoError(t, err)
 
-	_ = mockContext.dataStore.AddENI("eni-1", 1, true, false, false)
+	_ = mockContext.dataStoreAccess.GetDataStore(defaultNetworkCard).AddENI("eni-1", 1, true, false, false)
 	// If ENABLE_POD_ENI is not set, nothing happens
 	mockContext.tryEnableSecurityGroupsForPods(ctx)
 
@@ -2206,7 +2223,6 @@ func TestIsConfigValid(t *testing.T) {
 					m.awsutils.EXPECT().IsPrefixDelegationSupported().Return(false)
 				}
 			}
-			ds := datastore.NewDataStore(log, datastore.NullCheckpoint{}, tt.fields.prefixDelegationEnabled)
 
 			mockContext := &IPAMContext{
 				awsClient:              m.awsutils,
@@ -2216,7 +2232,9 @@ func TestIsConfigValid(t *testing.T) {
 				enablePrefixDelegation: tt.fields.prefixDelegationEnabled,
 				enablePodENI:           tt.fields.podENIEnabled,
 				useCustomNetworking:    tt.fields.customNetworkingEnabled,
-				dataStore:              ds,
+				dataStoreAccess: &datastore.DataStoreAccess{
+					DataStores: []*datastore.DataStore{datastore.NewDataStore(log, datastore.NullCheckpoint{}, tt.fields.prefixDelegationEnabled)},
+				},
 			}
 
 			resp := mockContext.isConfigValid()
@@ -2239,14 +2257,14 @@ func TestAnnotatePod(t *testing.T) {
 	}
 
 	mockContext := &IPAMContext{
-		awsClient:     m.awsutils,
-		k8sClient:     m.k8sClient,
-		primaryIP:     make(map[string]string),
-		terminating:   int32(0),
-		networkClient: m.network,
-		dataStore:     testDatastore(),
-		enableIPv4:    true,
-		enableIPv6:    false,
+		awsClient:       m.awsutils,
+		k8sClient:       m.k8sClient,
+		primaryIP:       make(map[string]string),
+		terminating:     int32(0),
+		networkClient:   m.network,
+		dataStoreAccess: testDatastore(),
+		enableIPv4:      true,
+		enableIPv6:      false,
 	}
 
 	mockContext.k8sClient.Create(ctx, &pod)
@@ -2428,15 +2446,15 @@ func TestPodENIErrInc(t *testing.T) {
 	ctx := context.Background()
 
 	mockContext := &IPAMContext{
-		awsClient:     m.awsutils,
-		k8sClient:     m.k8sClient,
-		networkClient: m.network,
-		primaryIP:     make(map[string]string),
-		terminating:   int32(0),
-		dataStore:     testDatastore(),
-		enableIPv4:    true,
-		enableIPv6:    false,
-		enablePodENI:  true,
+		awsClient:       m.awsutils,
+		k8sClient:       m.k8sClient,
+		networkClient:   m.network,
+		primaryIP:       make(map[string]string),
+		terminating:     int32(0),
+		dataStoreAccess: testDatastore(),
+		enableIPv4:      true,
+		enableIPv6:      false,
+		enablePodENI:    true,
 	}
 
 	// Create a test pod
@@ -2481,7 +2499,7 @@ func TestPodENIErrInc(t *testing.T) {
 
 func (c *IPAMContext) tryAssignPodENI(ctx context.Context, pod *corev1.Pod, fnName string) error {
 	// Mock implementation for the test
-	_, err := c.awsClient.AllocENI(false, nil, "", 0)
+	_, err := c.awsClient.AllocENI(nil, "", 0, defaultNetworkCard)
 	if err != nil {
 		prometheusmetrics.PodENIErr.With(prometheus.Labels{"fn": fnName}).Inc()
 		return err
diff --git a/pkg/ipamd/rpc_handler.go b/pkg/ipamd/rpc_handler.go
index 236fa0f0a9..ef48b90adc 100644
--- a/pkg/ipamd/rpc_handler.go
+++ b/pkg/ipamd/rpc_handler.go
@@ -21,6 +21,7 @@ import (
 	"strings"
 	"syscall"
 
+	multiErr "github.com/hashicorp/go-multierror"
 	"github.com/pkg/errors"
 	"github.com/prometheus/client_golang/prometheus"
 	"golang.org/x/net/context"
@@ -80,6 +81,16 @@ func (s *server) AddNetwork(ctx context.Context, in *rpc.AddNetworkRequest) (*rp
 	var deviceNumber, vlanID, trunkENILinkIndex int
 	var ipv4Addr, ipv6Addr, branchENIMAC, podENISubnetGW string
 	var err error
+
+	var ipamKey datastore.IPAMKey
+	var ipamMetadata datastore.IPAMMetadata
+	var errors error
+	var resp rpc.AddNetworkReply
+
+	// This will be a list of IPs. For now it's just one
+	ipAddrs := []*rpc.IPAddress{}
+
+	// var interfacesCount int
 	if s.ipamContext.enablePodENI {
 		// Check pod spec for Branch ENI
 		pod, err := s.ipamContext.GetPod(in.K8S_POD_NAME, in.K8S_POD_NAMESPACE)
@@ -88,10 +99,15 @@ func (s *server) AddNetwork(ctx context.Context, in *rpc.AddNetworkRequest) (*rp
 			return &failureResponse, nil
 		}
 		limits := pod.Spec.Containers[0].Resources.Limits
+
 		for resName := range limits {
 			if strings.HasPrefix(string(resName), "vpc.amazonaws.com/pod-eni") {
+
+				if in.RequiresMultiNICAttachment {
+					log.Info("SGP pod doesn't support multiple NIC attachments for pod, falling back to default one interface per pod")
+				}
 				// Check that we have a trunk
-				trunkENI := s.ipamContext.dataStore.GetTrunkENI()
+				trunkENI := s.ipamContext.dataStoreAccess.GetDataStore(DefaultNetworkCard).GetTrunkENI()
 				if trunkENI == "" {
 					log.Warn("Send AddNetworkReply: No trunk ENI found, cannot add a pod ENI")
 					return &failureResponse, nil
@@ -149,6 +165,13 @@ func (s *server) AddNetwork(ctx context.Context, in *rpc.AddNetworkRequest) (*rp
 					}
 					podENISubnetGW = gw.String()
 					deviceNumber = -1 // Not needed for branch ENI, they depend on trunkENIDeviceIndex
+
+					ipAddrs = append(ipAddrs, &rpc.IPAddress{
+						IPv4Addr:     ipv4Addr,
+						IPv6Addr:     ipv6Addr,
+						DeviceNumber: int32(deviceNumber),
+						RouteTableId: int32(deviceNumber), // Need to update to route table number
+					})
 				} else {
 					log.Infof("Send AddNetworkReply: failed to get Branch ENI resource")
 					return &failureResponse, nil
@@ -159,79 +182,134 @@ func (s *server) AddNetwork(ctx context.Context, in *rpc.AddNetworkRequest) (*rp
 
 	if s.ipamContext.enableIPv4 && ipv4Addr == "" ||
 		s.ipamContext.enableIPv6 && ipv6Addr == "" {
+
+		ipsRequired := 1
+		ipsAllocated := 0
+
+		if in.RequiresMultiNICAttachment {
+			ipsRequired = len(s.ipamContext.dataStoreAccess.DataStores)
+		}
+
+		log.Infof("Required MultiNIC access %t, interfaces required %+v", in.RequiresMultiNICAttachment, ipsRequired)
+
 		if in.ContainerID == "" || in.IfName == "" || in.NetworkName == "" {
 			log.Errorf("Unable to generate IPAMKey from %+v", in)
 			return &failureResponse, nil
 		}
-		ipamKey := datastore.IPAMKey{
-			ContainerID: in.ContainerID,
-			IfName:      in.IfName,
-			NetworkName: in.NetworkName,
-		}
-		ipamMetadata := datastore.IPAMMetadata{
-			K8SPodNamespace: in.K8S_POD_NAMESPACE,
-			K8SPodName:      in.K8S_POD_NAME,
+
+		for networkCard, ds := range s.ipamContext.dataStoreAccess.DataStores {
+
+			if ipsAllocated == ipsRequired {
+				break
+			}
+
+			ipamKey = datastore.IPAMKey{
+				ContainerID: in.ContainerID,
+				IfName:      in.IfName,
+				NetworkName: in.NetworkName,
+			}
+
+			ipamMetadata = datastore.IPAMMetadata{
+				K8SPodNamespace: in.K8S_POD_NAMESPACE,
+				K8SPodName:      in.K8S_POD_NAME,
+				InterfacesCount: ipsRequired,
+			}
+
+			ipv4Addr, ipv6Addr, deviceNumber, err = ds.AssignPodIPAddress(ipamKey, ipamMetadata, s.ipamContext.enableIPv4, s.ipamContext.enableIPv6)
+
+			if err != nil {
+				log.Warnf("Failed to assign IPs from network card %d: %v", networkCard, err)
+				// continue to look through other datastores till you are unable to find an IP address when ONLY 1 ip is required
+				// if the last datastore also return ErrNoAvailableIPInDataStore, return an error
+				if err == datastore.ErrNoAvailableIPInDataStore && ipsRequired == 1 && networkCard != len(s.ipamContext.dataStoreAccess.DataStores)-1 {
+					continue
+				}
+
+				errors = multiErr.Append(errors, err)
+				break
+			}
+
+			log.Infof("Assigned IP from network card: %d -> IPv4: %s, IPv6: %s, device number: %d, ", networkCard, ipv4Addr, ipv6Addr, deviceNumber)
+			ipAddrs = append(ipAddrs, &rpc.IPAddress{
+				IPv4Addr:     ipv4Addr,
+				IPv6Addr:     ipv6Addr,
+				DeviceNumber: int32(deviceNumber),
+				RouteTableId: int32(networkCard*s.ipamContext.maxENI + deviceNumber + 1),
+			})
+
+			ipsAllocated += 1
 		}
-		ipv4Addr, ipv6Addr, deviceNumber, err = s.ipamContext.dataStore.AssignPodIPAddress(ipamKey, ipamMetadata, s.ipamContext.enableIPv4, s.ipamContext.enableIPv6)
 	}
 
-	var pbVPCV4cidrs, pbVPCV6cidrs []string
-	var useExternalSNAT bool
-	if s.ipamContext.enableIPv4 && ipv4Addr != "" {
-		pbVPCV4cidrs, err = s.ipamContext.awsClient.GetVPCIPv4CIDRs()
-		if err != nil {
-			return nil, err
+	if errors != nil {
+		for index := range ipAddrs {
+			log.Infof("Unassigning IPs from datastore for Network Card %d as there was failure", index)
+			// Try to unassign all the assigned IPs in case of errors.
+			// This is best effort even if it fails we have the reconciler which can clean up leaked IPs for non existing pods
+			s.ipamContext.dataStoreAccess.GetDataStore(index).UnassignPodIPAddress(ipamKey)
 		}
-		for _, cidr := range pbVPCV4cidrs {
-			log.Debugf("VPC CIDR %s", cidr)
-		}
-		useExternalSNAT = s.ipamContext.networkClient.UseExternalSNAT()
-		if !useExternalSNAT {
-			for _, cidr := range s.ipamContext.networkClient.GetExcludeSNATCIDRs() {
-				log.Debugf("CIDR SNAT Exclusion %s", cidr)
-				pbVPCV4cidrs = append(pbVPCV4cidrs, cidr)
+	} else {
+		log.Infof("Address assigned from DS:  %d", len(ipAddrs))
+		var pbVPCV4cidrs, pbVPCV6cidrs []string
+		var useExternalSNAT bool
+
+		if s.ipamContext.enableIPv4 {
+			pbVPCV4cidrs, err = s.ipamContext.awsClient.GetVPCIPv4CIDRs()
+			if err != nil {
+				return nil, err
+			}
+			for _, cidr := range pbVPCV4cidrs {
+				log.Debugf("VPC CIDR %s", cidr)
+			}
+			useExternalSNAT = s.ipamContext.networkClient.UseExternalSNAT()
+			if !useExternalSNAT {
+				for _, cidr := range s.ipamContext.networkClient.GetExcludeSNATCIDRs() {
+					log.Debugf("CIDR SNAT Exclusion %s", cidr)
+					pbVPCV4cidrs = append(pbVPCV4cidrs, cidr)
+				}
+			}
+		} else if s.ipamContext.enableIPv6 {
+			pbVPCV6cidrs, err = s.ipamContext.awsClient.GetVPCIPv6CIDRs()
+			if err != nil {
+				return nil, err
+			}
+			for _, cidr := range pbVPCV6cidrs {
+				log.Debugf("VPC V6 CIDR %s", cidr)
 			}
 		}
-	} else if s.ipamContext.enableIPv6 && ipv6Addr != "" {
-		pbVPCV6cidrs, err = s.ipamContext.awsClient.GetVPCIPv6CIDRs()
-		if err != nil {
-			return nil, err
-		}
-		for _, cidr := range pbVPCV6cidrs {
-			log.Debugf("VPC V6 CIDR %s", cidr)
-		}
-	}
 
-	if s.ipamContext.enablePodIPAnnotation {
-		// On ADD, we pass empty string as there is no IP being released
-		if ipv4Addr != "" {
-			err = s.ipamContext.AnnotatePod(in.K8S_POD_NAME, in.K8S_POD_NAMESPACE, vpccniPodIPKey, ipv4Addr, "")
-			if err != nil {
-				log.Errorf("Failed to add the pod annotation: %v", err)
+		if s.ipamContext.enablePodIPAnnotation {
+			var ipAddr string
+
+			// We are only adding the pods primary IP to annotation
+			if ipAddrs[0].GetIPv4Addr() != "" {
+				ipAddr = ipAddrs[0].GetIPv4Addr()
+			} else {
+				ipAddr = ipAddrs[0].GetIPv6Addr()
 			}
-		} else if ipv6Addr != "" {
-			err = s.ipamContext.AnnotatePod(in.K8S_POD_NAME, in.K8S_POD_NAMESPACE, vpccniPodIPKey, ipv6Addr, "")
+
+			err = s.ipamContext.AnnotatePod(in.K8S_POD_NAME, in.K8S_POD_NAMESPACE, vpccniPodIPKey, ipAddr, "")
 			if err != nil {
 				log.Errorf("Failed to add the pod annotation: %v", err)
 			}
 		}
-	}
-	resp := rpc.AddNetworkReply{
-		Success:           err == nil,
-		IPv4Addr:          ipv4Addr,
-		IPv6Addr:          ipv6Addr,
-		DeviceNumber:      int32(deviceNumber),
-		UseExternalSNAT:   useExternalSNAT,
-		VPCv4CIDRs:        pbVPCV4cidrs,
-		VPCv6CIDRs:        pbVPCV6cidrs,
-		PodVlanId:         int32(vlanID),
-		PodENIMAC:         branchENIMAC,
-		PodENISubnetGW:    podENISubnetGW,
-		ParentIfIndex:     int32(trunkENILinkIndex),
-		NetworkPolicyMode: s.ipamContext.networkPolicyMode,
+
+		resp = rpc.AddNetworkReply{
+			IPAddress:         ipAddrs,
+			UseExternalSNAT:   useExternalSNAT,
+			VPCv4CIDRs:        pbVPCV4cidrs,
+			VPCv6CIDRs:        pbVPCV6cidrs,
+			PodVlanId:         int32(vlanID),
+			PodENIMAC:         branchENIMAC,
+			PodENISubnetGW:    podENISubnetGW,
+			ParentIfIndex:     int32(trunkENILinkIndex),
+			NetworkPolicyMode: s.ipamContext.networkPolicyMode,
+		}
 	}
 
-	log.Infof("Send AddNetworkReply: IPv4Addr: %s, IPv6Addr: %s, DeviceNumber: %d, err: %v", ipv4Addr, ipv6Addr, deviceNumber, err)
+	resp.Success = errors == nil
+
+	log.Infof("Send AddNetworkReply: Success: %t IPAddr: %+v, err: %v", resp.Success, resp.IPAddress, err)
 	return &resp, nil
 }
 
@@ -259,66 +337,111 @@ func (s *server) DelNetwork(ctx context.Context, in *rpc.DelNetworkRequest) (*rp
 		IfName:      in.IfName,
 		NetworkName: in.NetworkName,
 	}
-	eni, ip, deviceNumber, err := s.ipamContext.dataStore.UnassignPodIPAddress(ipamKey)
-	if s.ipamContext.enableIPv4 {
-		ipv4Addr = ip
-		cidr := net.IPNet{IP: net.ParseIP(ip), Mask: net.IPv4Mask(255, 255, 255, 255)}
-		cidrStr = cidr.String()
-	} else if s.ipamContext.enableIPv6 {
-		ipv6Addr = ip
-	}
+	var errors error
+	var ipAddr []*rpc.IPAddress
+
+	ipsToMatch := 1
+	ipsFound := 0
+	for networkCard, ds := range s.ipamContext.dataStoreAccess.DataStores {
+		// All datastores will store this count in its datastore
+		if ipsToMatch == ipsFound {
+			break
+		}
+
+		eni, ip, deviceNumber, ipsAllocated, err := ds.UnassignPodIPAddress(ipamKey)
 
-	if s.ipamContext.enableIPv4 && eni != nil {
-		//cidrStr will be pod IP i.e, IP/32 for v4 (or) IP/128 for v6.
-		// Case 1: PD is enabled but IP/32 key in AvailableIPv4Cidrs[cidrStr] exists, this means it is a secondary IP. Added IsPrefix check just for sanity.
-		// So this IP should be released immediately.
-		// Case 2: PD is disabled then IP/32 key in AvailableIPv4Cidrs[cidrStr] will not exists since key to AvailableIPv4Cidrs will be either /28 prefix or /32
-		// secondary IP. Hence now see if we need free up a prefix is no other pods are using it.
-		if s.ipamContext.enablePrefixDelegation && eni.AvailableIPv4Cidrs[cidrStr] != nil && eni.AvailableIPv4Cidrs[cidrStr].IsPrefix == false {
-			log.Debugf("IP belongs to secondary pool with PD enabled so free IP from EC2")
-			s.ipamContext.tryUnassignIPFromENI(eni.ID)
-		} else if !s.ipamContext.enablePrefixDelegation && eni.AvailableIPv4Cidrs[cidrStr] == nil {
-			log.Debugf("IP belongs to prefix pool with PD disabled so try free prefix from EC2")
-			s.ipamContext.tryUnassignPrefixFromENI(eni.ID)
+		// ipsAllocated will always be same in all datastores for a Pod, so this will not change ever between datastores
+		if ipsAllocated > 0 {
+			log.Debugf("IPs allocated for the pod: %d", ipsAllocated)
+			ipsToMatch = ipsAllocated
 		}
-	}
 
-	if err == datastore.ErrUnknownPod && s.ipamContext.enablePodENI {
-		pod, err := s.ipamContext.GetPod(in.K8S_POD_NAME, in.K8S_POD_NAMESPACE)
-		if err != nil {
-			if k8serror.IsNotFound(err) {
-				log.Warn("Send DelNetworkReply: pod not found")
-				return &rpc.DelNetworkReply{Success: true}, nil
+		if s.ipamContext.enableIPv4 {
+			ipv4Addr = ip
+			cidr := net.IPNet{IP: net.ParseIP(ip), Mask: net.IPv4Mask(255, 255, 255, 255)}
+			cidrStr = cidr.String()
+		} else if s.ipamContext.enableIPv6 {
+			ipv6Addr = ip
+		}
+
+		if s.ipamContext.enableIPv4 && eni != nil {
+			//cidrStr will be pod IP i.e, IP/32 for v4 (or) IP/128 for v6.
+			// Case 1: PD is enabled but IP/32 key in AvailableIPv4Cidrs[cidrStr] exists, this means it is a secondary IP. Added IsPrefix check just for sanity.
+			// So this IP should be released immediately.
+			// Case 2: PD is disabled then IP/32 key in AvailableIPv4Cidrs[cidrStr] will not exists since key to AvailableIPv4Cidrs will be either /28 prefix or /32
+			// secondary IP. Hence now see if we need free up a prefix is no other pods are using it.
+			if s.ipamContext.enablePrefixDelegation && eni.AvailableIPv4Cidrs[cidrStr] != nil && eni.AvailableIPv4Cidrs[cidrStr].IsPrefix {
+				log.Debugf("IP belongs to secondary pool with PD enabled so free IP from EC2")
+				s.ipamContext.tryUnassignIPFromENI(eni.ID, networkCard)
+			} else if !s.ipamContext.enablePrefixDelegation && eni.AvailableIPv4Cidrs[cidrStr] == nil {
+				log.Debugf("IP belongs to prefix pool with PD disabled so try free prefix from EC2")
+				s.ipamContext.tryUnassignPrefixFromENI(eni.ID, networkCard)
 			}
-			log.Warnf("Send DelNetworkReply: Failed to get pod spec: %v", err)
-			return &rpc.DelNetworkReply{Success: false}, err
 		}
-		val, branch := pod.Annotations["vpc.amazonaws.com/pod-eni"]
-		if branch {
-			// Parse JSON data
-			var podENIData []PodENIData
-			err := json.Unmarshal([]byte(val), &podENIData)
-			if err != nil || len(podENIData) < 1 {
-				log.Errorf("Failed to unmarshal PodENIData JSON: %v", err)
+
+		if err == datastore.ErrUnknownPod {
+			if s.ipamContext.enablePodENI {
+				pod, err := s.ipamContext.GetPod(in.K8S_POD_NAME, in.K8S_POD_NAMESPACE)
+				if err != nil {
+					if k8serror.IsNotFound(err) {
+						log.Warn("Send DelNetworkReply: pod not found")
+						return &rpc.DelNetworkReply{Success: true}, nil
+					}
+					log.Warnf("Send DelNetworkReply: Failed to get pod spec: %v", err)
+					return &rpc.DelNetworkReply{Success: false}, err
+				}
+				val, branch := pod.Annotations["vpc.amazonaws.com/pod-eni"]
+				if branch {
+					// Parse JSON data
+					var podENIData []PodENIData
+					err := json.Unmarshal([]byte(val), &podENIData)
+					if err != nil || len(podENIData) < 1 {
+						log.Errorf("Failed to unmarshal PodENIData JSON: %v", err)
+					}
+					return &rpc.DelNetworkReply{
+						Success:   true,
+						PodVlanId: int32(podENIData[0].VlanID),
+						IPAddress: []*rpc.IPAddress{&rpc.IPAddress{IPv4Addr: podENIData[0].PrivateIP}},
+					}, err
+				}
+			}
+
+			// If ip address is still empty, continue till we reach the end of datastore
+			// This happens when pod is not in on network card 0, DS will return datastore.ErrUnknownPod
+			if ip == "" && networkCard != len(s.ipamContext.dataStoreAccess.DataStores)-1 {
+				log.Debugf("Pod doesn't belong to Network Card %d, looking into next datastore", networkCard)
+				continue
+			}
+		}
+
+		if s.ipamContext.enablePodIPAnnotation {
+			// On DEL, we pass IP being released
+			err = s.ipamContext.AnnotatePod(in.K8S_POD_NAME, in.K8S_POD_NAMESPACE, vpccniPodIPKey, "", ip)
+			if err != nil {
+				log.Errorf("Failed to delete the pod annotation: %v", err)
 			}
-			return &rpc.DelNetworkReply{
-				Success:   true,
-				PodVlanId: int32(podENIData[0].VlanID),
-				IPv4Addr:  podENIData[0].PrivateIP}, err
 		}
-	}
 
-	if s.ipamContext.enablePodIPAnnotation {
-		// On DEL, we pass IP being released
-		err = s.ipamContext.AnnotatePod(in.K8S_POD_NAME, in.K8S_POD_NAMESPACE, vpccniPodIPKey, "", ip)
 		if err != nil {
-			log.Errorf("Failed to delete the pod annotation: %v", err)
+			errors = multiErr.Append(errors, err)
 		}
-	}
 
-	log.Infof("Send DelNetworkReply: IPv4Addr: %s, IPv6Addr: %s, DeviceNumber: %d, err: %v", ipv4Addr, ipv6Addr, deviceNumber, err)
+		ipAddr = append(ipAddr, &rpc.IPAddress{
+			IPv4Addr:     ipv4Addr,
+			IPv6Addr:     ipv6Addr,
+			DeviceNumber: int32(deviceNumber),
+			RouteTableId: int32(networkCard*s.ipamContext.maxENI + deviceNumber + 1),
+		})
+		ipsFound += 1
+
+		log.Debugf("IPs allocated for the pod: %d, IPs found: %d", ipsAllocated, ipsFound)
+	}
 
-	return &rpc.DelNetworkReply{Success: err == nil, IPv4Addr: ipv4Addr, IPv6Addr: ipv6Addr, DeviceNumber: int32(deviceNumber)}, err
+	log.Infof("Send DelNetworkReply: IPAddress: %+v, err: %v", ipAddr, errors)
+	return &rpc.DelNetworkReply{
+		Success:   errors == nil,
+		IPAddress: ipAddr,
+	}, errors
 }
 
 func (s *server) GetNetworkPolicyConfigs(ctx context.Context, e *emptypb.Empty) (*rpc.NetworkPolicyAgentConfigReply, error) {
diff --git a/pkg/ipamd/rpc_handler_test.go b/pkg/ipamd/rpc_handler_test.go
index 53389ba9ca..80af8956f0 100644
--- a/pkg/ipamd/rpc_handler_test.go
+++ b/pkg/ipamd/rpc_handler_test.go
@@ -19,6 +19,7 @@ import (
 	"testing"
 
 	"github.com/aws/amazon-vpc-cni-k8s/pkg/ipamd/datastore"
+	"github.com/aws/amazon-vpc-cni-k8s/rpc"
 
 	pb "github.com/aws/amazon-vpc-cni-k8s/rpc"
 
@@ -33,14 +34,15 @@ func TestServer_VersionCheck(t *testing.T) {
 	defer m.ctrl.Finish()
 
 	mockContext := &IPAMContext{
-		awsClient:     m.awsutils,
-		maxIPsPerENI:  14,
-		maxENI:        4,
-		warmENITarget: 1,
-		warmIPTarget:  3,
-		networkClient: m.network,
-		dataStore:     datastore.NewDataStore(log, datastore.NullCheckpoint{}, false),
+		awsClient:       m.awsutils,
+		maxIPsPerENI:    14,
+		maxENI:          4,
+		warmENITarget:   1,
+		warmIPTarget:    3,
+		networkClient:   m.network,
+		dataStoreAccess: datastore.InitializeDataStores(1, "test", false, log),
 	}
+
 	m.awsutils.EXPECT().GetVPCIPv4CIDRs().Return([]string{}, nil).AnyTimes()
 	m.awsutils.EXPECT().GetVPCIPv6CIDRs().Return([]string{}, nil).AnyTimes()
 	m.network.EXPECT().UseExternalSNAT().Return(true).AnyTimes()
@@ -136,9 +138,15 @@ func TestServer_AddNetwork(t *testing.T) {
 				ipV6Enabled: false,
 			},
 			want: &pb.AddNetworkReply{
-				Success:         true,
-				IPv4Addr:        "192.168.1.100",
-				DeviceNumber:    int32(0),
+				Success: true,
+				IPAddress: []*rpc.IPAddress{
+					{
+						IPv4Addr:     "192.168.1.100",
+						IPv6Addr:     "",
+						DeviceNumber: int32(0),
+						RouteTableId: int32(1),
+					},
+				},
 				UseExternalSNAT: true,
 				VPCv4CIDRs:      []string{"10.10.0.0/16"},
 			},
@@ -168,9 +176,15 @@ func TestServer_AddNetwork(t *testing.T) {
 				ipV6Enabled: false,
 			},
 			want: &pb.AddNetworkReply{
-				Success:         true,
-				IPv4Addr:        "192.168.1.100",
-				DeviceNumber:    int32(0),
+				Success: true,
+				IPAddress: []*rpc.IPAddress{
+					{
+						IPv4Addr:     "192.168.1.100",
+						IPv6Addr:     "",
+						DeviceNumber: int32(0),
+						RouteTableId: int32(1),
+					},
+				},
 				UseExternalSNAT: false,
 				VPCv4CIDRs:      []string{"10.10.0.0/16", "10.12.0.0/16", "10.13.0.0/16"},
 			},
@@ -183,8 +197,15 @@ func TestServer_AddNetwork(t *testing.T) {
 				ipV6Enabled:        false,
 			},
 			want: &pb.AddNetworkReply{
-				Success:      false,
-				DeviceNumber: int32(-1),
+				Success: false,
+				IPAddress: []*rpc.IPAddress{
+					{
+						IPv4Addr:     "",
+						IPv6Addr:     "",
+						DeviceNumber: int32(-1),
+						RouteTableId: int32(0),
+					},
+				},
 			},
 		},
 		{
@@ -203,10 +224,16 @@ func TestServer_AddNetwork(t *testing.T) {
 				prefixDelegationEnabled: true,
 			},
 			want: &pb.AddNetworkReply{
-				Success:      true,
-				IPv6Addr:     "2001:db8::",
-				DeviceNumber: int32(0),
-				VPCv6CIDRs:   []string{"2001:db8::/56"},
+				Success: true,
+				IPAddress: []*rpc.IPAddress{
+					{
+						IPv4Addr:     "",
+						IPv6Addr:     "2001:db8::",
+						DeviceNumber: int32(0),
+						RouteTableId: int32(1),
+					},
+				},
+				VPCv6CIDRs: []string{"2001:db8::/56"},
 			},
 		},
 		{
@@ -218,8 +245,15 @@ func TestServer_AddNetwork(t *testing.T) {
 				prefixDelegationEnabled: true,
 			},
 			want: &pb.AddNetworkReply{
-				Success:      false,
-				DeviceNumber: int32(-1),
+				Success: false,
+				IPAddress: []*rpc.IPAddress{
+					{
+						IPv4Addr:     "",
+						IPv6Addr:     "",
+						DeviceNumber: int32(-1),
+						RouteTableId: int32(0),
+					},
+				},
 			},
 		},
 		{
@@ -233,9 +267,15 @@ func TestServer_AddNetwork(t *testing.T) {
 				prefixDelegationEnabled: false,
 			},
 			want: &pb.AddNetworkReply{
-				Success:      false,
-				IPv6Addr:     "",
-				DeviceNumber: int32(-1),
+				Success: false,
+				IPAddress: []*rpc.IPAddress{
+					{
+						IPv4Addr:     "",
+						IPv6Addr:     "",
+						DeviceNumber: int32(-1),
+						RouteTableId: int32(0),
+					},
+				},
 			},
 		},
 	}
@@ -262,19 +302,20 @@ func TestServer_AddNetwork(t *testing.T) {
 			for _, call := range tt.fields.getExcludeSNATCIDRsCalls {
 				m.network.EXPECT().GetExcludeSNATCIDRs().Return(call.snatExclusionCIDRs)
 			}
-			ds := datastore.NewDataStore(log, datastore.NullCheckpoint{}, tt.fields.prefixDelegationEnabled)
+			// ds := datastore.NewDataStore(log, datastore.NullCheckpoint{}, tt.fields.prefixDelegationEnabled)
+			dsAccess := datastore.InitializeDataStores(1, defaultBackingStorePath, tt.fields.prefixDelegationEnabled, log)
 			for eniID, ipv4Addresses := range tt.fields.ipV4AddressByENIID {
-				ds.AddENI(eniID, 0, false, false, false)
+				dsAccess.GetDataStore(0).AddENI(eniID, 0, false, false, false)
 				for _, ipv4Address := range ipv4Addresses {
 					ipv4Addr := net.IPNet{IP: net.ParseIP(ipv4Address), Mask: net.IPv4Mask(255, 255, 255, 255)}
-					ds.AddIPv4CidrToStore(eniID, ipv4Addr, false)
+					dsAccess.GetDataStore(0).AddIPv4CidrToStore(eniID, ipv4Addr, false)
 				}
 			}
 			for eniID, ipv6Prefixes := range tt.fields.ipV6PrefixByENIID {
-				ds.AddENI(eniID, 0, false, false, false)
+				dsAccess.GetDataStore(0).AddENI(eniID, 0, false, false, false)
 				for _, ipv6Prefix := range ipv6Prefixes {
 					_, ipnet, _ := net.ParseCIDR(ipv6Prefix)
-					ds.AddIPv6CidrToStore(eniID, *ipnet, true)
+					dsAccess.GetDataStore(0).AddIPv6CidrToStore(eniID, *ipnet, true)
 				}
 			}
 
@@ -288,7 +329,7 @@ func TestServer_AddNetwork(t *testing.T) {
 				enableIPv4:             tt.fields.ipV4Enabled,
 				enableIPv6:             tt.fields.ipV6Enabled,
 				enablePrefixDelegation: tt.fields.prefixDelegationEnabled,
-				dataStore:              ds,
+				dataStoreAccess:        dsAccess,
 			}
 
 			s := &server{
diff --git a/pkg/networkutils/mocks/network_mocks.go b/pkg/networkutils/mocks/network_mocks.go
index e68b213f44..bc461891ee 100644
--- a/pkg/networkutils/mocks/network_mocks.go
+++ b/pkg/networkutils/mocks/network_mocks.go
@@ -1,22 +1,8 @@
-// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License"). You may
-// not use this file except in compliance with the License. A copy of the
-// License is located at
-//
-//     http://aws.amazon.com/apache2.0/
-//
-// or in the "license" file accompanying this file. This file is distributed
-// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-// express or implied. See the License for the specific language governing
-// permissions and limitations under the License.
-//
-
 // Code generated by MockGen. DO NOT EDIT.
-// Source: github.com/aws/amazon-vpc-cni-k8s/pkg/networkutils (interfaces: NetworkAPIs)
+// Source: network.go
 
-// Package mock_networkutils is a generated GoMock package.
-package mock_networkutils
+// Package mocks is a generated GoMock package.
+package mocks
 
 import (
 	net "net"
@@ -51,17 +37,17 @@ func (m *MockNetworkAPIs) EXPECT() *MockNetworkAPIsMockRecorder {
 }
 
 // CleanUpStaleAWSChains mocks base method.
-func (m *MockNetworkAPIs) CleanUpStaleAWSChains(arg0, arg1 bool) error {
+func (m *MockNetworkAPIs) CleanUpStaleAWSChains(v4Enabled, v6Enabled bool) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "CleanUpStaleAWSChains", arg0, arg1)
+	ret := m.ctrl.Call(m, "CleanUpStaleAWSChains", v4Enabled, v6Enabled)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
 // CleanUpStaleAWSChains indicates an expected call of CleanUpStaleAWSChains.
-func (mr *MockNetworkAPIsMockRecorder) CleanUpStaleAWSChains(arg0, arg1 interface{}) *gomock.Call {
+func (mr *MockNetworkAPIsMockRecorder) CleanUpStaleAWSChains(v4Enabled, v6Enabled interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CleanUpStaleAWSChains", reflect.TypeOf((*MockNetworkAPIs)(nil).CleanUpStaleAWSChains), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CleanUpStaleAWSChains", reflect.TypeOf((*MockNetworkAPIs)(nil).CleanUpStaleAWSChains), v4Enabled, v6Enabled)
 }
 
 // GetExcludeSNATCIDRs mocks base method.
@@ -93,18 +79,18 @@ func (mr *MockNetworkAPIsMockRecorder) GetExternalServiceCIDRs() *gomock.Call {
 }
 
 // GetLinkByMac mocks base method.
-func (m *MockNetworkAPIs) GetLinkByMac(arg0 string, arg1 time.Duration) (netlink.Link, error) {
+func (m *MockNetworkAPIs) GetLinkByMac(mac string, retryInterval time.Duration) (netlink.Link, error) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetLinkByMac", arg0, arg1)
+	ret := m.ctrl.Call(m, "GetLinkByMac", mac, retryInterval)
 	ret0, _ := ret[0].(netlink.Link)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
 
 // GetLinkByMac indicates an expected call of GetLinkByMac.
-func (mr *MockNetworkAPIsMockRecorder) GetLinkByMac(arg0, arg1 interface{}) *gomock.Call {
+func (mr *MockNetworkAPIsMockRecorder) GetLinkByMac(mac, retryInterval interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLinkByMac", reflect.TypeOf((*MockNetworkAPIs)(nil).GetLinkByMac), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLinkByMac", reflect.TypeOf((*MockNetworkAPIs)(nil).GetLinkByMac), mac, retryInterval)
 }
 
 // GetRuleList mocks base method.
@@ -123,88 +109,88 @@ func (mr *MockNetworkAPIsMockRecorder) GetRuleList() *gomock.Call {
 }
 
 // GetRuleListBySrc mocks base method.
-func (m *MockNetworkAPIs) GetRuleListBySrc(arg0 []netlink.Rule, arg1 net.IPNet) ([]netlink.Rule, error) {
+func (m *MockNetworkAPIs) GetRuleListBySrc(ruleList []netlink.Rule, src net.IPNet) ([]netlink.Rule, error) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetRuleListBySrc", arg0, arg1)
+	ret := m.ctrl.Call(m, "GetRuleListBySrc", ruleList, src)
 	ret0, _ := ret[0].([]netlink.Rule)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
 
 // GetRuleListBySrc indicates an expected call of GetRuleListBySrc.
-func (mr *MockNetworkAPIsMockRecorder) GetRuleListBySrc(arg0, arg1 interface{}) *gomock.Call {
+func (mr *MockNetworkAPIsMockRecorder) GetRuleListBySrc(ruleList, src interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRuleListBySrc", reflect.TypeOf((*MockNetworkAPIs)(nil).GetRuleListBySrc), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRuleListBySrc", reflect.TypeOf((*MockNetworkAPIs)(nil).GetRuleListBySrc), ruleList, src)
 }
 
 // SetupENINetwork mocks base method.
-func (m *MockNetworkAPIs) SetupENINetwork(arg0, arg1 string, arg2 int, arg3 string) error {
+func (m *MockNetworkAPIs) SetupENINetwork(eniIP, eniMAC string, deviceNumber, networkCard int, eniSubnetCIDR string, maxENIPerNIC int) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SetupENINetwork", arg0, arg1, arg2, arg3)
+	ret := m.ctrl.Call(m, "SetupENINetwork", eniIP, eniMAC, deviceNumber, networkCard, eniSubnetCIDR, maxENIPerNIC)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
 // SetupENINetwork indicates an expected call of SetupENINetwork.
-func (mr *MockNetworkAPIsMockRecorder) SetupENINetwork(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
+func (mr *MockNetworkAPIsMockRecorder) SetupENINetwork(eniIP, eniMAC, deviceNumber, networkCard, eniSubnetCIDR, maxENIPerNIC interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetupENINetwork", reflect.TypeOf((*MockNetworkAPIs)(nil).SetupENINetwork), arg0, arg1, arg2, arg3)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetupENINetwork", reflect.TypeOf((*MockNetworkAPIs)(nil).SetupENINetwork), eniIP, eniMAC, deviceNumber, networkCard, eniSubnetCIDR, maxENIPerNIC)
 }
 
 // SetupHostNetwork mocks base method.
-func (m *MockNetworkAPIs) SetupHostNetwork(arg0 []string, arg1 string, arg2 *net.IP, arg3, arg4, arg5 bool) error {
+func (m *MockNetworkAPIs) SetupHostNetwork(vpcCIDRs []string, primaryMAC string, primaryAddr *net.IP, enablePodENI, v4Enabled, v6Enabled bool) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SetupHostNetwork", arg0, arg1, arg2, arg3, arg4, arg5)
+	ret := m.ctrl.Call(m, "SetupHostNetwork", vpcCIDRs, primaryMAC, primaryAddr, enablePodENI, v4Enabled, v6Enabled)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
 // SetupHostNetwork indicates an expected call of SetupHostNetwork.
-func (mr *MockNetworkAPIsMockRecorder) SetupHostNetwork(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call {
+func (mr *MockNetworkAPIsMockRecorder) SetupHostNetwork(vpcCIDRs, primaryMAC, primaryAddr, enablePodENI, v4Enabled, v6Enabled interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetupHostNetwork", reflect.TypeOf((*MockNetworkAPIs)(nil).SetupHostNetwork), arg0, arg1, arg2, arg3, arg4, arg5)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetupHostNetwork", reflect.TypeOf((*MockNetworkAPIs)(nil).SetupHostNetwork), vpcCIDRs, primaryMAC, primaryAddr, enablePodENI, v4Enabled, v6Enabled)
 }
 
 // UpdateExternalServiceIpRules mocks base method.
-func (m *MockNetworkAPIs) UpdateExternalServiceIpRules(arg0 []netlink.Rule, arg1 []string) error {
+func (m *MockNetworkAPIs) UpdateExternalServiceIpRules(ruleList []netlink.Rule, externalIPs []string) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "UpdateExternalServiceIpRules", arg0, arg1)
+	ret := m.ctrl.Call(m, "UpdateExternalServiceIpRules", ruleList, externalIPs)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
 // UpdateExternalServiceIpRules indicates an expected call of UpdateExternalServiceIpRules.
-func (mr *MockNetworkAPIsMockRecorder) UpdateExternalServiceIpRules(arg0, arg1 interface{}) *gomock.Call {
+func (mr *MockNetworkAPIsMockRecorder) UpdateExternalServiceIpRules(ruleList, externalIPs interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateExternalServiceIpRules", reflect.TypeOf((*MockNetworkAPIs)(nil).UpdateExternalServiceIpRules), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateExternalServiceIpRules", reflect.TypeOf((*MockNetworkAPIs)(nil).UpdateExternalServiceIpRules), ruleList, externalIPs)
 }
 
 // UpdateHostIptablesRules mocks base method.
-func (m *MockNetworkAPIs) UpdateHostIptablesRules(arg0 []string, arg1 string, arg2 *net.IP, arg3, arg4 bool) error {
+func (m *MockNetworkAPIs) UpdateHostIptablesRules(vpcCIDRs []string, primaryMAC string, primaryAddr *net.IP, v4Enabled, v6Enabled bool) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "UpdateHostIptablesRules", arg0, arg1, arg2, arg3, arg4)
+	ret := m.ctrl.Call(m, "UpdateHostIptablesRules", vpcCIDRs, primaryMAC, primaryAddr, v4Enabled, v6Enabled)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
 // UpdateHostIptablesRules indicates an expected call of UpdateHostIptablesRules.
-func (mr *MockNetworkAPIsMockRecorder) UpdateHostIptablesRules(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
+func (mr *MockNetworkAPIsMockRecorder) UpdateHostIptablesRules(vpcCIDRs, primaryMAC, primaryAddr, v4Enabled, v6Enabled interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHostIptablesRules", reflect.TypeOf((*MockNetworkAPIs)(nil).UpdateHostIptablesRules), arg0, arg1, arg2, arg3, arg4)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHostIptablesRules", reflect.TypeOf((*MockNetworkAPIs)(nil).UpdateHostIptablesRules), vpcCIDRs, primaryMAC, primaryAddr, v4Enabled, v6Enabled)
 }
 
 // UpdateRuleListBySrc mocks base method.
-func (m *MockNetworkAPIs) UpdateRuleListBySrc(arg0 []netlink.Rule, arg1 net.IPNet) error {
+func (m *MockNetworkAPIs) UpdateRuleListBySrc(ruleList []netlink.Rule, src net.IPNet) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "UpdateRuleListBySrc", arg0, arg1)
+	ret := m.ctrl.Call(m, "UpdateRuleListBySrc", ruleList, src)
 	ret0, _ := ret[0].(error)
 	return ret0
 }
 
 // UpdateRuleListBySrc indicates an expected call of UpdateRuleListBySrc.
-func (mr *MockNetworkAPIsMockRecorder) UpdateRuleListBySrc(arg0, arg1 interface{}) *gomock.Call {
+func (mr *MockNetworkAPIsMockRecorder) UpdateRuleListBySrc(ruleList, src interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateRuleListBySrc", reflect.TypeOf((*MockNetworkAPIs)(nil).UpdateRuleListBySrc), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateRuleListBySrc", reflect.TypeOf((*MockNetworkAPIs)(nil).UpdateRuleListBySrc), ruleList, src)
 }
 
 // UseExternalSNAT mocks base method.
diff --git a/pkg/networkutils/names.go b/pkg/networkutils/names.go
index 1e1c1f7af0..69e176b4f2 100644
--- a/pkg/networkutils/names.go
+++ b/pkg/networkutils/names.go
@@ -4,11 +4,16 @@ import (
 	"crypto/sha1"
 	"encoding/hex"
 	"fmt"
+	"strconv"
 )
 
 // GeneratePodHostVethName generates the name for Pod's host-side veth device.
 // The veth name is generated in a way that aligns with the value expected by Calico for NetworkPolicy enforcement.
-func GeneratePodHostVethName(prefix string, podNamespace string, podName string) string {
+func GeneratePodHostVethName(prefix string, podNamespace string, podName string, index int) string {
+
+	if index > 0 {
+		podName = fmt.Sprintf("%s.%s", podName, strconv.Itoa(index))
+	}
 	suffix := GeneratePodHostVethNameSuffix(podNamespace, podName)
 	return fmt.Sprintf("%s%s", prefix, suffix)
 }
@@ -19,3 +24,11 @@ func GeneratePodHostVethNameSuffix(podNamespace string, podName string) string {
 	h.Write([]byte(fmt.Sprintf("%s.%s", podNamespace, podName)))
 	return hex.EncodeToString(h.Sum(nil))[:11]
 }
+
+// Generates the interface name inside the pod namespace
+func GenerateContainerVethName(defaultIfName string, prefix string, index int) string {
+	if index > 0 {
+		return fmt.Sprintf("%s%s", prefix, strconv.Itoa(index))
+	}
+	return defaultIfName
+}
diff --git a/pkg/networkutils/names_test.go b/pkg/networkutils/names_test.go
index e893e7b461..4280edfed1 100644
--- a/pkg/networkutils/names_test.go
+++ b/pkg/networkutils/names_test.go
@@ -38,7 +38,7 @@ func TestGeneratePodHostVethName(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got := GeneratePodHostVethName(tt.args.prefix, tt.args.podNamespace, tt.args.podName)
+			got := GeneratePodHostVethName(tt.args.prefix, tt.args.podNamespace, tt.args.podName, 0)
 			assert.Equal(t, tt.want, got)
 		})
 	}
diff --git a/pkg/networkutils/network.go b/pkg/networkutils/network.go
index c7a7f0a110..e316837440 100644
--- a/pkg/networkutils/network.go
+++ b/pkg/networkutils/network.go
@@ -58,6 +58,9 @@ const (
 	// Rule priority for traffic destined to pod IP
 	ToContainerRulePriority = 512
 
+	// From Interface priority for multi-homed pods
+	FromInterfaceRulePriority = 1
+
 	// 513 - 1023, can be used for priority lower than fromPodRule but higher than default nonVPC CIDR rule
 
 	// 1024 is reserved for (ip rule not to <VPC's subnet> table main)
@@ -148,7 +151,7 @@ type NetworkAPIs interface {
 	SetupHostNetwork(vpcCIDRs []string, primaryMAC string, primaryAddr *net.IP, enablePodENI bool,
 		v4Enabled bool, v6Enabled bool) error
 	// SetupENINetwork performs ENI level network configuration. Not needed on the primary ENI
-	SetupENINetwork(eniIP string, mac string, deviceNumber int, subnetCIDR string) error
+	SetupENINetwork(eniIP string, eniMAC string, deviceNumber int, networkCard int, eniSubnetCIDR string, maxENIPerNIC int) error
 	// UpdateHostIptablesRules updates the nat table iptables rules on the host
 	UpdateHostIptablesRules(vpcCIDRs []string, primaryMAC string, primaryAddr *net.IP, v4Enabled bool, v6Enabled bool) error
 	CleanUpStaleAWSChains(v4Enabled, v6Enabled bool) error
@@ -287,7 +290,7 @@ func (n *linuxNetwork) setupRuleToBlockNodeLocalAccess(protocol iptables.Protoco
 }
 
 // SetupHostNetwork performs node level network configuration
-func (n *linuxNetwork) SetupHostNetwork(vpcv4CIDRs []string, primaryMAC string, primaryAddr *net.IP, enablePodENI bool,
+func (n *linuxNetwork) SetupHostNetwork(vpcCIDRs []string, primaryMAC string, primaryAddr *net.IP, enablePodENI bool,
 	v4Enabled bool, v6Enabled bool) error {
 	log.Info("Setting up host network... ")
 
@@ -366,7 +369,7 @@ func (n *linuxNetwork) SetupHostNetwork(vpcv4CIDRs []string, primaryMAC string,
 		}
 	}
 
-	return n.updateHostIptablesRules(vpcv4CIDRs, primaryMAC, primaryAddr, v4Enabled, v6Enabled)
+	return n.updateHostIptablesRules(vpcCIDRs, primaryMAC, primaryAddr, v4Enabled, v6Enabled)
 }
 
 // UpdateHostIptablesRules updates the NAT table rules based on the VPC CIDRs configuration
@@ -429,10 +432,6 @@ func (n *linuxNetwork) updateHostIptablesRules(vpcCIDRs []string, primaryMAC str
 
 	ipProtocol := iptables.ProtocolIPv4
 	if v6Enabled {
-		// Essentially a stub function for now in V6 mode. We will need it when we support v6 in secondary IP and
-		// custom networking modes. We don't need to install any SNAT rules in v6 mode and currently there is no need
-		// to mark packets entering via Primary ENI as all the pods in v6 mode will be behind primary ENI. Will have to
-		// start doing that once we start supporting custom networking mode in v6.
 		ipProtocol = iptables.ProtocolIPv6
 	}
 
@@ -441,27 +440,25 @@ func (n *linuxNetwork) updateHostIptablesRules(vpcCIDRs []string, primaryMAC str
 		return errors.Wrap(err, "host network setup: failed to create iptables")
 	}
 
-	if v4Enabled {
-		iptablesSNATRules, err := n.buildIptablesSNATRules(vpcCIDRs, primaryAddr, primaryIntf, ipt)
-		if err != nil {
-			return err
-		}
-		if err := n.updateIptablesRules(iptablesSNATRules, ipt); err != nil {
-			return err
-		}
+	iptablesSNATRules, err := n.buildIptablesSNATRules(vpcCIDRs, primaryAddr, primaryIntf, ipt, v6Enabled)
+	if err != nil {
+		return err
+	}
+	if err := n.updateIptablesRules(iptablesSNATRules, ipt); err != nil {
+		return err
+	}
 
-		iptablesConnmarkRules, err := n.buildIptablesConnmarkRules(vpcCIDRs, ipt)
-		if err != nil {
-			return err
-		}
-		if err := n.updateIptablesRules(iptablesConnmarkRules, ipt); err != nil {
-			return err
-		}
+	iptablesConnmarkRules, err := n.buildIptablesConnmarkRules(vpcCIDRs, ipt)
+	if err != nil {
+		return err
+	}
+	if err := n.updateIptablesRules(iptablesConnmarkRules, ipt); err != nil {
+		return err
 	}
 	return nil
 }
 
-func (n *linuxNetwork) buildIptablesSNATRules(vpcCIDRs []string, primaryAddr *net.IP, primaryIntf string, ipt iptableswrapper.IPTablesIface) ([]iptablesRule, error) {
+func (n *linuxNetwork) buildIptablesSNATRules(vpcCIDRs []string, primaryAddr *net.IP, primaryIntf string, ipt iptableswrapper.IPTablesIface, v6Enabled bool) ([]iptablesRule, error) {
 	type snatCIDR struct {
 		cidr        string
 		isExclusion bool
@@ -499,6 +496,7 @@ func (n *linuxNetwork) buildIptablesSNATRules(vpcCIDRs []string, primaryAddr *ne
 			"-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-0",
 		}})
 
+	// Exclude VPC traffic from SNAT rule
 	for _, cidr := range allCIDRs {
 		comment := "AWS SNAT CHAIN"
 		if cidr.isExclusion {
@@ -516,6 +514,18 @@ func (n *linuxNetwork) buildIptablesSNATRules(vpcCIDRs []string, primaryAddr *ne
 			}})
 	}
 
+	// Exclude Multicast link local traffic from SNAT rule. This is required for DHCPv6
+	if v6Enabled {
+		iptableRules = append(iptableRules, iptablesRule{
+			name:        chain,
+			shouldExist: !n.useExternalSNAT,
+			table:       "nat",
+			chain:       chain,
+			rule: []string{
+				"-d", "ff00::/8", "-m", "comment", "--comment", "SKIP LINKLOCAL", "-j", "RETURN",
+			}})
+	}
+
 	// Prepare the Desired Rule for SNAT Rule for non-pod ENIs
 	snatRule := []string{"!", "-o", "vlan+",
 		"-m", "comment", "--comment", "AWS, SNAT",
@@ -1015,21 +1025,27 @@ func GetIPv4Gateway(eniSubnetCIDR *net.IPNet) net.IP {
 }
 
 // SetupENINetwork adds default route to route table (eni-<eni_table>), so it does not need to be called on the primary ENI
-func (n *linuxNetwork) SetupENINetwork(eniIP string, eniMAC string, deviceNumber int, eniSubnetCIDR string) error {
-	return setupENINetwork(eniIP, eniMAC, deviceNumber, eniSubnetCIDR, n.netLink, retryLinkByMacInterval, retryRouteAddInterval, n.mtu)
+func (n *linuxNetwork) SetupENINetwork(eniIP string, eniMAC string, deviceNumber int, networkCard int, eniSubnetCIDR string, maxENIPerNIC int) error {
+	return setupENINetwork(eniIP, eniMAC, deviceNumber, networkCard, eniSubnetCIDR, n.netLink, retryLinkByMacInterval, retryRouteAddInterval, n.mtu, maxENIPerNIC)
 }
 
-func setupENINetwork(eniIP string, eniMAC string, deviceNumber int, eniSubnetCIDR string, netLink netlinkwrapper.NetLink,
-	retryLinkByMacInterval time.Duration, retryRouteAddInterval time.Duration, mtu int) error {
-	if deviceNumber == 0 {
+// Even if this is called for primary IF on multicard instance
+func setupENINetwork(eniIP string, eniMac string, deviceNumber int, networkCard int, eniSubnetCIDR string, netLink netlinkwrapper.NetLink,
+	retryLinkByMacInterval time.Duration, retryRouteAddInterval time.Duration, mtu int, maxENIPerNIC int) error {
+
+	if deviceNumber == 0 && networkCard == 0 {
 		return errors.New("setupENINetwork should never be called on the primary ENI")
 	}
-	tableNumber := deviceNumber + 1
-	log.Infof("Setting up network for an ENI with IP address %s, MAC address %s, CIDR %s and route table %d",
-		eniIP, eniMAC, eniSubnetCIDR, tableNumber)
-	link, err := linkByMac(eniMAC, netLink, retryLinkByMacInterval)
+
+	// CNI will never have route table 1 as we don't setup device number 0 on network card 0
+	// So, table number 1 for CNI is same as main table
+	tableNumber := (networkCard * maxENIPerNIC) + deviceNumber + 1
+	log.Infof("Setting up network for an ENI with IP address %s, MAC address %s, CIDR %s, route table %d and Network Card %d",
+		eniIP, eniMac, eniSubnetCIDR, tableNumber, networkCard)
+
+	link, err := linkByMac(eniMac, netLink, retryLinkByMacInterval)
 	if err != nil {
-		return errors.Wrapf(err, "setupENINetwork: failed to find the link which uses MAC address %s", eniMAC)
+		return errors.Wrapf(err, "setupENINetwork: failed to find the link which uses MAC address %s", eniMac)
 	}
 
 	if err = netLink.LinkSetMTU(link, mtu); err != nil {
@@ -1045,6 +1061,7 @@ func setupENINetwork(eniIP string, eniMAC string, deviceNumber int, eniSubnetCID
 	if err != nil {
 		return errors.Wrapf(err, "setupENINetwork: invalid IP CIDR block %s", eniSubnetCIDR)
 	}
+
 	// Get gateway IP address for ENI
 	var gw net.IP
 	if isV6 {
diff --git a/pkg/networkutils/network_test.go b/pkg/networkutils/network_test.go
index fd5950ab84..b297f644bf 100644
--- a/pkg/networkutils/network_test.go
+++ b/pkg/networkutils/network_test.go
@@ -49,6 +49,8 @@ const (
 	testEniSubnet    = "10.10.0.0/16"
 	testEniV6Subnet  = "2600::/64"
 	testEniV6Gateway = "fe80::c9d:5dff:fec4:f389"
+	testNetworkCard  = 0
+	testMaxENIPerNIC = 4
 	// Default MTU of ENI and veth
 	// defined in plugins/routed-eni/driver/driver.go, pkg/networkutils/network.go
 	testMTU   = 9001
@@ -122,7 +124,7 @@ func TestSetupENINetwork(t *testing.T) {
 
 	mockNetLink.EXPECT().RouteDel(gomock.Any()).Return(nil)
 
-	err = setupENINetwork(testEniIP, testMAC2, testTable, testEniSubnet, mockNetLink, 0*time.Second, 0*time.Second, testMTU)
+	err = setupENINetwork(testEniIP, testMAC2, testTable, testNetworkCard, testEniSubnet, mockNetLink, 0*time.Second, 0*time.Second, testMTU, testMaxENIPerNIC)
 	assert.NoError(t, err)
 }
 
@@ -171,7 +173,7 @@ func TestSetupENIV6Network(t *testing.T) {
 	mockNetLink.EXPECT().RouteReplace(gomock.Any()).Return(nil)
 	mockNetLink.EXPECT().RouteDel(gomock.Any()).Return(nil)
 
-	err = setupENINetwork(testEniIP6, testMAC2, testTable, testEniV6Subnet, mockNetLink, 0*time.Second, 0*time.Second, testMTU)
+	err = setupENINetwork(testEniIP6, testMAC2, testTable, testNetworkCard, testEniV6Subnet, mockNetLink, 0*time.Second, 0*time.Second, testMTU, testMaxENIPerNIC)
 	assert.NoError(t, err)
 }
 
@@ -185,7 +187,7 @@ func TestSetupENINetworkMACFail(t *testing.T) {
 		mockNetLink.EXPECT().LinkList().Return(nil, fmt.Errorf("simulated failure"))
 	}
 
-	err := setupENINetwork(testEniIP, testMAC2, testTable, testEniSubnet, mockNetLink, 0*time.Second, 0*time.Second, testMTU)
+	err := setupENINetwork(testEniIP, testMAC2, testTable, testNetworkCard, testEniSubnet, mockNetLink, 0*time.Second, 0*time.Second, testMTU, testMaxENIPerNIC)
 	assert.Errorf(t, err, "simulated failure")
 }
 
@@ -193,7 +195,7 @@ func TestSetupENINetworkErrorOnPrimaryENI(t *testing.T) {
 	ctrl, mockNetLink, _, _, _ := setup(t)
 	defer ctrl.Finish()
 	deviceNumber := 0
-	err := setupENINetwork(testEniIP, testMAC2, deviceNumber, testEniSubnet, mockNetLink, 0*time.Second, 0*time.Second, testMTU)
+	err := setupENINetwork(testEniIP, testMAC2, deviceNumber, testNetworkCard, testEniSubnet, mockNetLink, 0*time.Second, 0*time.Second, testMTU, testMaxENIPerNIC)
 	assert.Error(t, err)
 }
 
@@ -991,7 +993,7 @@ func TestSetupHostNetworkWithIPv6Enabled(t *testing.T) {
 	setupNetLinkMocks(ctrl, mockNetLink)
 
 	var vpcCIDRs []string
-	err := ln.SetupHostNetwork(vpcCIDRs, loopback, &testEniIPNet, false, false, true)
+	err := ln.SetupHostNetwork(vpcCIDRs, loopback, &testEniIP6Net, false, false, true)
 	assert.NoError(t, err)
 
 	assert.Equal(t, map[string]map[string][][]string{
@@ -1005,6 +1007,37 @@ func TestSetupHostNetworkWithIPv6Enabled(t *testing.T) {
 				},
 			},
 		},
+		"mangle": {
+			"PREROUTING": [][]string{
+				{
+					"-m", "comment", "--comment", "AWS, primary ENI", "-i", "lo", "-m", "addrtype", "--dst-type", "LOCAL", "--limit-iface-in", "-j", "CONNMARK", "--set-mark", "0x80/0x80",
+				},
+				{
+					"-m", "comment", "--comment", "AWS, primary ENI", "-i", "eni+", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80",
+				},
+				{
+					"-m", "comment", "--comment", "AWS, primary ENI", "-i", "vlan+", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80",
+				},
+			},
+		},
+		"nat": {
+			"PREROUTING": [][]string{
+				{"-i", "eni+", "-m", "comment", "--comment", "AWS, outbound connections", "-j", "AWS-CONNMARK-CHAIN-0"},
+				{"-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--restore-mark", "--mask", "0x80"},
+			},
+			"POSTROUTING": [][]string{
+				{"-m", "comment", "--comment", "AWS SNAT CHAIN", "-j", "AWS-SNAT-CHAIN-0"},
+			},
+			"AWS-CONNMARK-CHAIN-0": [][]string{
+				{"-N", "AWS-CONNMARK-CHAIN-0"},
+				{"-m", "comment", "--comment", "AWS, CONNMARK", "-j", "CONNMARK", "--set-xmark", "0x80/0x80"},
+			},
+			"AWS-SNAT-CHAIN-0": [][]string{
+				{"-N", "AWS-SNAT-CHAIN-0"},
+				{"-d", "ff00::/8", "-m", "comment", "--comment", "SKIP LINKLOCAL", "-j", "RETURN"},
+				{"!", "-o", "vlan+", "-m", "comment", "--comment", "AWS, SNAT", "-m", "addrtype", "!", "--dst-type", "LOCAL", "-j", "SNAT", "--to-source", "2600::2"},
+			},
+		},
 	}, mockIptables.(*mock_iptables.MockIptables).DataplaneState)
 }
 
diff --git a/rpc/rpc.pb.go b/rpc/rpc.pb.go
index f58cd2b05b..844effe697 100644
--- a/rpc/rpc.pb.go
+++ b/rpc/rpc.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.31.0
-// 	protoc        v4.25.6
+// 	protoc-gen-go v1.33.0
+// 	protoc        v5.27.1
 // source: rpc.proto
 
 package rpc
@@ -37,7 +37,8 @@ type AddNetworkRequest struct {
 	ContainerID                string `protobuf:"bytes,7,opt,name=ContainerID,proto3" json:"ContainerID,omitempty"`
 	IfName                     string `protobuf:"bytes,5,opt,name=IfName,proto3" json:"IfName,omitempty"`
 	NetworkName                string `protobuf:"bytes,6,opt,name=NetworkName,proto3" json:"NetworkName,omitempty"`
-	Netns                      string `protobuf:"bytes,4,opt,name=Netns,proto3" json:"Netns,omitempty"` // next field: 9
+	Netns                      string `protobuf:"bytes,4,opt,name=Netns,proto3" json:"Netns,omitempty"`
+	RequiresMultiNICAttachment bool   `protobuf:"varint,9,opt,name=RequiresMultiNICAttachment,proto3" json:"RequiresMultiNICAttachment,omitempty"` // next field: 10
 }
 
 func (x *AddNetworkRequest) Reset() {
@@ -128,18 +129,94 @@ func (x *AddNetworkRequest) GetNetns() string {
 	return ""
 }
 
+func (x *AddNetworkRequest) GetRequiresMultiNICAttachment() bool {
+	if x != nil {
+		return x.RequiresMultiNICAttachment
+	}
+	return false
+}
+
+type IPAddress struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	IPv4Addr     string `protobuf:"bytes,1,opt,name=IPv4Addr,proto3" json:"IPv4Addr,omitempty"`
+	IPv6Addr     string `protobuf:"bytes,2,opt,name=IPv6Addr,proto3" json:"IPv6Addr,omitempty"`
+	DeviceNumber int32  `protobuf:"varint,3,opt,name=DeviceNumber,proto3" json:"DeviceNumber,omitempty"`
+	RouteTableId int32  `protobuf:"varint,4,opt,name=RouteTableId,proto3" json:"RouteTableId,omitempty"`
+}
+
+func (x *IPAddress) Reset() {
+	*x = IPAddress{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_rpc_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *IPAddress) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*IPAddress) ProtoMessage() {}
+
+func (x *IPAddress) ProtoReflect() protoreflect.Message {
+	mi := &file_rpc_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use IPAddress.ProtoReflect.Descriptor instead.
+func (*IPAddress) Descriptor() ([]byte, []int) {
+	return file_rpc_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *IPAddress) GetIPv4Addr() string {
+	if x != nil {
+		return x.IPv4Addr
+	}
+	return ""
+}
+
+func (x *IPAddress) GetIPv6Addr() string {
+	if x != nil {
+		return x.IPv6Addr
+	}
+	return ""
+}
+
+func (x *IPAddress) GetDeviceNumber() int32 {
+	if x != nil {
+		return x.DeviceNumber
+	}
+	return 0
+}
+
+func (x *IPAddress) GetRouteTableId() int32 {
+	if x != nil {
+		return x.RouteTableId
+	}
+	return 0
+}
+
 type AddNetworkReply struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Success         bool     `protobuf:"varint,1,opt,name=Success,proto3" json:"Success,omitempty"`
-	IPv4Addr        string   `protobuf:"bytes,2,opt,name=IPv4Addr,proto3" json:"IPv4Addr,omitempty"`
-	IPv6Addr        string   `protobuf:"bytes,11,opt,name=IPv6Addr,proto3" json:"IPv6Addr,omitempty"`
-	DeviceNumber    int32    `protobuf:"varint,4,opt,name=DeviceNumber,proto3" json:"DeviceNumber,omitempty"`
-	UseExternalSNAT bool     `protobuf:"varint,5,opt,name=UseExternalSNAT,proto3" json:"UseExternalSNAT,omitempty"`
-	VPCv4CIDRs      []string `protobuf:"bytes,6,rep,name=VPCv4CIDRs,proto3" json:"VPCv4CIDRs,omitempty"`
-	VPCv6CIDRs      []string `protobuf:"bytes,12,rep,name=VPCv6CIDRs,proto3" json:"VPCv6CIDRs,omitempty"`
+	Success         bool         `protobuf:"varint,1,opt,name=Success,proto3" json:"Success,omitempty"`
+	IPAddress       []*IPAddress `protobuf:"bytes,2,rep,name=IPAddress,proto3" json:"IPAddress,omitempty"`
+	UseExternalSNAT bool         `protobuf:"varint,5,opt,name=UseExternalSNAT,proto3" json:"UseExternalSNAT,omitempty"`
+	VPCv4CIDRs      []string     `protobuf:"bytes,6,rep,name=VPCv4CIDRs,proto3" json:"VPCv4CIDRs,omitempty"`
+	VPCv6CIDRs      []string     `protobuf:"bytes,12,rep,name=VPCv6CIDRs,proto3" json:"VPCv6CIDRs,omitempty"`
 	// start of pod-eni parameters
 	PodVlanId         int32  `protobuf:"varint,7,opt,name=PodVlanId,proto3" json:"PodVlanId,omitempty"`
 	PodENIMAC         string `protobuf:"bytes,8,opt,name=PodENIMAC,proto3" json:"PodENIMAC,omitempty"`
@@ -151,7 +228,7 @@ type AddNetworkReply struct {
 func (x *AddNetworkReply) Reset() {
 	*x = AddNetworkReply{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_rpc_proto_msgTypes[1]
+		mi := &file_rpc_proto_msgTypes[2]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -164,7 +241,7 @@ func (x *AddNetworkReply) String() string {
 func (*AddNetworkReply) ProtoMessage() {}
 
 func (x *AddNetworkReply) ProtoReflect() protoreflect.Message {
-	mi := &file_rpc_proto_msgTypes[1]
+	mi := &file_rpc_proto_msgTypes[2]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -177,7 +254,7 @@ func (x *AddNetworkReply) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use AddNetworkReply.ProtoReflect.Descriptor instead.
 func (*AddNetworkReply) Descriptor() ([]byte, []int) {
-	return file_rpc_proto_rawDescGZIP(), []int{1}
+	return file_rpc_proto_rawDescGZIP(), []int{2}
 }
 
 func (x *AddNetworkReply) GetSuccess() bool {
@@ -187,25 +264,11 @@ func (x *AddNetworkReply) GetSuccess() bool {
 	return false
 }
 
-func (x *AddNetworkReply) GetIPv4Addr() string {
+func (x *AddNetworkReply) GetIPAddress() []*IPAddress {
 	if x != nil {
-		return x.IPv4Addr
+		return x.IPAddress
 	}
-	return ""
-}
-
-func (x *AddNetworkReply) GetIPv6Addr() string {
-	if x != nil {
-		return x.IPv6Addr
-	}
-	return ""
-}
-
-func (x *AddNetworkReply) GetDeviceNumber() int32 {
-	if x != nil {
-		return x.DeviceNumber
-	}
-	return 0
+	return nil
 }
 
 func (x *AddNetworkReply) GetUseExternalSNAT() bool {
@@ -282,7 +345,7 @@ type DelNetworkRequest struct {
 func (x *DelNetworkRequest) Reset() {
 	*x = DelNetworkRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_rpc_proto_msgTypes[2]
+		mi := &file_rpc_proto_msgTypes[3]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -295,7 +358,7 @@ func (x *DelNetworkRequest) String() string {
 func (*DelNetworkRequest) ProtoMessage() {}
 
 func (x *DelNetworkRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_rpc_proto_msgTypes[2]
+	mi := &file_rpc_proto_msgTypes[3]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -308,7 +371,7 @@ func (x *DelNetworkRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use DelNetworkRequest.ProtoReflect.Descriptor instead.
 func (*DelNetworkRequest) Descriptor() ([]byte, []int) {
-	return file_rpc_proto_rawDescGZIP(), []int{2}
+	return file_rpc_proto_rawDescGZIP(), []int{3}
 }
 
 func (x *DelNetworkRequest) GetClientVersion() string {
@@ -372,10 +435,8 @@ type DelNetworkReply struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Success      bool   `protobuf:"varint,1,opt,name=Success,proto3" json:"Success,omitempty"`
-	IPv4Addr     string `protobuf:"bytes,2,opt,name=IPv4Addr,proto3" json:"IPv4Addr,omitempty"`
-	IPv6Addr     string `protobuf:"bytes,5,opt,name=IPv6Addr,proto3" json:"IPv6Addr,omitempty"`
-	DeviceNumber int32  `protobuf:"varint,3,opt,name=DeviceNumber,proto3" json:"DeviceNumber,omitempty"`
+	Success   bool         `protobuf:"varint,1,opt,name=Success,proto3" json:"Success,omitempty"`
+	IPAddress []*IPAddress `protobuf:"bytes,2,rep,name=IPAddress,proto3" json:"IPAddress,omitempty"`
 	// start of pod-eni parameters
 	PodVlanId int32 `protobuf:"varint,4,opt,name=PodVlanId,proto3" json:"PodVlanId,omitempty"` // end of pod-eni parameters
 }
@@ -383,7 +444,7 @@ type DelNetworkReply struct {
 func (x *DelNetworkReply) Reset() {
 	*x = DelNetworkReply{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_rpc_proto_msgTypes[3]
+		mi := &file_rpc_proto_msgTypes[4]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -396,7 +457,7 @@ func (x *DelNetworkReply) String() string {
 func (*DelNetworkReply) ProtoMessage() {}
 
 func (x *DelNetworkReply) ProtoReflect() protoreflect.Message {
-	mi := &file_rpc_proto_msgTypes[3]
+	mi := &file_rpc_proto_msgTypes[4]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -409,7 +470,7 @@ func (x *DelNetworkReply) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use DelNetworkReply.ProtoReflect.Descriptor instead.
 func (*DelNetworkReply) Descriptor() ([]byte, []int) {
-	return file_rpc_proto_rawDescGZIP(), []int{3}
+	return file_rpc_proto_rawDescGZIP(), []int{4}
 }
 
 func (x *DelNetworkReply) GetSuccess() bool {
@@ -419,25 +480,11 @@ func (x *DelNetworkReply) GetSuccess() bool {
 	return false
 }
 
-func (x *DelNetworkReply) GetIPv4Addr() string {
-	if x != nil {
-		return x.IPv4Addr
-	}
-	return ""
-}
-
-func (x *DelNetworkReply) GetIPv6Addr() string {
+func (x *DelNetworkReply) GetIPAddress() []*IPAddress {
 	if x != nil {
-		return x.IPv6Addr
+		return x.IPAddress
 	}
-	return ""
-}
-
-func (x *DelNetworkReply) GetDeviceNumber() int32 {
-	if x != nil {
-		return x.DeviceNumber
-	}
-	return 0
+	return nil
 }
 
 func (x *DelNetworkReply) GetPodVlanId() int32 {
@@ -447,33 +494,33 @@ func (x *DelNetworkReply) GetPodVlanId() int32 {
 	return 0
 }
 
-type EnforceNpRequest struct {
+type DeleteNpRequest struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	K8S_POD_NAME        string `protobuf:"bytes,1,opt,name=K8S_POD_NAME,json=K8SPODNAME,proto3" json:"K8S_POD_NAME,omitempty"`
-	K8S_POD_NAMESPACE   string `protobuf:"bytes,2,opt,name=K8S_POD_NAMESPACE,json=K8SPODNAMESPACE,proto3" json:"K8S_POD_NAMESPACE,omitempty"`
-	NETWORK_POLICY_MODE string `protobuf:"bytes,3,opt,name=NETWORK_POLICY_MODE,json=NETWORKPOLICYMODE,proto3" json:"NETWORK_POLICY_MODE,omitempty"`
+	Success           bool   `protobuf:"varint,1,opt,name=Success,proto3" json:"Success,omitempty"`
+	K8S_POD_NAME      string `protobuf:"bytes,2,opt,name=K8S_POD_NAME,json=K8SPODNAME,proto3" json:"K8S_POD_NAME,omitempty"`
+	K8S_POD_NAMESPACE string `protobuf:"bytes,3,opt,name=K8S_POD_NAMESPACE,json=K8SPODNAMESPACE,proto3" json:"K8S_POD_NAMESPACE,omitempty"`
 }
 
-func (x *EnforceNpRequest) Reset() {
-	*x = EnforceNpRequest{}
+func (x *DeleteNpRequest) Reset() {
+	*x = DeleteNpRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_rpc_proto_msgTypes[4]
+		mi := &file_rpc_proto_msgTypes[5]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
 }
 
-func (x *EnforceNpRequest) String() string {
+func (x *DeleteNpRequest) String() string {
 	return protoimpl.X.MessageStringOf(x)
 }
 
-func (*EnforceNpRequest) ProtoMessage() {}
+func (*DeleteNpRequest) ProtoMessage() {}
 
-func (x *EnforceNpRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_rpc_proto_msgTypes[4]
+func (x *DeleteNpRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_rpc_proto_msgTypes[5]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -484,33 +531,33 @@ func (x *EnforceNpRequest) ProtoReflect() protoreflect.Message {
 	return mi.MessageOf(x)
 }
 
-// Deprecated: Use EnforceNpRequest.ProtoReflect.Descriptor instead.
-func (*EnforceNpRequest) Descriptor() ([]byte, []int) {
-	return file_rpc_proto_rawDescGZIP(), []int{4}
+// Deprecated: Use DeleteNpRequest.ProtoReflect.Descriptor instead.
+func (*DeleteNpRequest) Descriptor() ([]byte, []int) {
+	return file_rpc_proto_rawDescGZIP(), []int{5}
 }
 
-func (x *EnforceNpRequest) GetK8S_POD_NAME() string {
+func (x *DeleteNpRequest) GetSuccess() bool {
 	if x != nil {
-		return x.K8S_POD_NAME
+		return x.Success
 	}
-	return ""
+	return false
 }
 
-func (x *EnforceNpRequest) GetK8S_POD_NAMESPACE() string {
+func (x *DeleteNpRequest) GetK8S_POD_NAME() string {
 	if x != nil {
-		return x.K8S_POD_NAMESPACE
+		return x.K8S_POD_NAME
 	}
 	return ""
 }
 
-func (x *EnforceNpRequest) GetNETWORK_POLICY_MODE() string {
+func (x *DeleteNpRequest) GetK8S_POD_NAMESPACE() string {
 	if x != nil {
-		return x.NETWORK_POLICY_MODE
+		return x.K8S_POD_NAMESPACE
 	}
 	return ""
 }
 
-type EnforceNpReply struct {
+type DeleteNpReply struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
@@ -518,23 +565,23 @@ type EnforceNpReply struct {
 	Success bool `protobuf:"varint,1,opt,name=Success,proto3" json:"Success,omitempty"`
 }
 
-func (x *EnforceNpReply) Reset() {
-	*x = EnforceNpReply{}
+func (x *DeleteNpReply) Reset() {
+	*x = DeleteNpReply{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_rpc_proto_msgTypes[5]
+		mi := &file_rpc_proto_msgTypes[6]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
 }
 
-func (x *EnforceNpReply) String() string {
+func (x *DeleteNpReply) String() string {
 	return protoimpl.X.MessageStringOf(x)
 }
 
-func (*EnforceNpReply) ProtoMessage() {}
+func (*DeleteNpReply) ProtoMessage() {}
 
-func (x *EnforceNpReply) ProtoReflect() protoreflect.Message {
-	mi := &file_rpc_proto_msgTypes[5]
+func (x *DeleteNpReply) ProtoReflect() protoreflect.Message {
+	mi := &file_rpc_proto_msgTypes[6]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -545,44 +592,46 @@ func (x *EnforceNpReply) ProtoReflect() protoreflect.Message {
 	return mi.MessageOf(x)
 }
 
-// Deprecated: Use EnforceNpReply.ProtoReflect.Descriptor instead.
-func (*EnforceNpReply) Descriptor() ([]byte, []int) {
-	return file_rpc_proto_rawDescGZIP(), []int{5}
+// Deprecated: Use DeleteNpReply.ProtoReflect.Descriptor instead.
+func (*DeleteNpReply) Descriptor() ([]byte, []int) {
+	return file_rpc_proto_rawDescGZIP(), []int{6}
 }
 
-func (x *EnforceNpReply) GetSuccess() bool {
+func (x *DeleteNpReply) GetSuccess() bool {
 	if x != nil {
 		return x.Success
 	}
 	return false
 }
 
-type DeleteNpRequest struct {
+type EnforceNpRequest struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	K8S_POD_NAME      string `protobuf:"bytes,1,opt,name=K8S_POD_NAME,json=K8SPODNAME,proto3" json:"K8S_POD_NAME,omitempty"`
-	K8S_POD_NAMESPACE string `protobuf:"bytes,2,opt,name=K8S_POD_NAMESPACE,json=K8SPODNAMESPACE,proto3" json:"K8S_POD_NAMESPACE,omitempty"`
+	K8S_POD_NAME        string `protobuf:"bytes,1,opt,name=K8S_POD_NAME,json=K8SPODNAME,proto3" json:"K8S_POD_NAME,omitempty"`
+	K8S_POD_NAMESPACE   string `protobuf:"bytes,2,opt,name=K8S_POD_NAMESPACE,json=K8SPODNAMESPACE,proto3" json:"K8S_POD_NAMESPACE,omitempty"`
+	NETWORK_POLICY_MODE string `protobuf:"bytes,3,opt,name=NETWORK_POLICY_MODE,json=NETWORKPOLICYMODE,proto3" json:"NETWORK_POLICY_MODE,omitempty"`
+	InterfaceCount      int32  `protobuf:"varint,4,opt,name=InterfaceCount,proto3" json:"InterfaceCount,omitempty"`
 }
 
-func (x *DeleteNpRequest) Reset() {
-	*x = DeleteNpRequest{}
+func (x *EnforceNpRequest) Reset() {
+	*x = EnforceNpRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_rpc_proto_msgTypes[6]
+		mi := &file_rpc_proto_msgTypes[7]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
 }
 
-func (x *DeleteNpRequest) String() string {
+func (x *EnforceNpRequest) String() string {
 	return protoimpl.X.MessageStringOf(x)
 }
 
-func (*DeleteNpRequest) ProtoMessage() {}
+func (*EnforceNpRequest) ProtoMessage() {}
 
-func (x *DeleteNpRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_rpc_proto_msgTypes[6]
+func (x *EnforceNpRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_rpc_proto_msgTypes[7]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -593,26 +642,40 @@ func (x *DeleteNpRequest) ProtoReflect() protoreflect.Message {
 	return mi.MessageOf(x)
 }
 
-// Deprecated: Use DeleteNpRequest.ProtoReflect.Descriptor instead.
-func (*DeleteNpRequest) Descriptor() ([]byte, []int) {
-	return file_rpc_proto_rawDescGZIP(), []int{6}
+// Deprecated: Use EnforceNpRequest.ProtoReflect.Descriptor instead.
+func (*EnforceNpRequest) Descriptor() ([]byte, []int) {
+	return file_rpc_proto_rawDescGZIP(), []int{7}
 }
 
-func (x *DeleteNpRequest) GetK8S_POD_NAME() string {
+func (x *EnforceNpRequest) GetK8S_POD_NAME() string {
 	if x != nil {
 		return x.K8S_POD_NAME
 	}
 	return ""
 }
 
-func (x *DeleteNpRequest) GetK8S_POD_NAMESPACE() string {
+func (x *EnforceNpRequest) GetK8S_POD_NAMESPACE() string {
 	if x != nil {
 		return x.K8S_POD_NAMESPACE
 	}
 	return ""
 }
 
-type DeleteNpReply struct {
+func (x *EnforceNpRequest) GetNETWORK_POLICY_MODE() string {
+	if x != nil {
+		return x.NETWORK_POLICY_MODE
+	}
+	return ""
+}
+
+func (x *EnforceNpRequest) GetInterfaceCount() int32 {
+	if x != nil {
+		return x.InterfaceCount
+	}
+	return 0
+}
+
+type EnforceNpReply struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
@@ -620,23 +683,23 @@ type DeleteNpReply struct {
 	Success bool `protobuf:"varint,1,opt,name=Success,proto3" json:"Success,omitempty"`
 }
 
-func (x *DeleteNpReply) Reset() {
-	*x = DeleteNpReply{}
+func (x *EnforceNpReply) Reset() {
+	*x = EnforceNpReply{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_rpc_proto_msgTypes[7]
+		mi := &file_rpc_proto_msgTypes[8]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
 }
 
-func (x *DeleteNpReply) String() string {
+func (x *EnforceNpReply) String() string {
 	return protoimpl.X.MessageStringOf(x)
 }
 
-func (*DeleteNpReply) ProtoMessage() {}
+func (*EnforceNpReply) ProtoMessage() {}
 
-func (x *DeleteNpReply) ProtoReflect() protoreflect.Message {
-	mi := &file_rpc_proto_msgTypes[7]
+func (x *EnforceNpReply) ProtoReflect() protoreflect.Message {
+	mi := &file_rpc_proto_msgTypes[8]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -647,12 +710,12 @@ func (x *DeleteNpReply) ProtoReflect() protoreflect.Message {
 	return mi.MessageOf(x)
 }
 
-// Deprecated: Use DeleteNpReply.ProtoReflect.Descriptor instead.
-func (*DeleteNpReply) Descriptor() ([]byte, []int) {
-	return file_rpc_proto_rawDescGZIP(), []int{7}
+// Deprecated: Use EnforceNpReply.ProtoReflect.Descriptor instead.
+func (*EnforceNpReply) Descriptor() ([]byte, []int) {
+	return file_rpc_proto_rawDescGZIP(), []int{8}
 }
 
-func (x *DeleteNpReply) GetSuccess() bool {
+func (x *EnforceNpReply) GetSuccess() bool {
 	if x != nil {
 		return x.Success
 	}
@@ -670,7 +733,7 @@ type NetworkPolicyAgentConfigReply struct {
 func (x *NetworkPolicyAgentConfigReply) Reset() {
 	*x = NetworkPolicyAgentConfigReply{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_rpc_proto_msgTypes[8]
+		mi := &file_rpc_proto_msgTypes[9]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -683,7 +746,7 @@ func (x *NetworkPolicyAgentConfigReply) String() string {
 func (*NetworkPolicyAgentConfigReply) ProtoMessage() {}
 
 func (x *NetworkPolicyAgentConfigReply) ProtoReflect() protoreflect.Message {
-	mi := &file_rpc_proto_msgTypes[8]
+	mi := &file_rpc_proto_msgTypes[9]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -696,7 +759,7 @@ func (x *NetworkPolicyAgentConfigReply) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use NetworkPolicyAgentConfigReply.ProtoReflect.Descriptor instead.
 func (*NetworkPolicyAgentConfigReply) Descriptor() ([]byte, []int) {
-	return file_rpc_proto_rawDescGZIP(), []int{8}
+	return file_rpc_proto_rawDescGZIP(), []int{9}
 }
 
 func (x *NetworkPolicyAgentConfigReply) GetNetworkPolicyMode() string {
@@ -711,7 +774,7 @@ var File_rpc_proto protoreflect.FileDescriptor
 var file_rpc_proto_rawDesc = []byte{
 	0x0a, 0x09, 0x72, 0x70, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x72, 0x70, 0x63,
 	0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
-	0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb5, 0x02,
+	0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf5, 0x02,
 	0x0a, 0x11, 0x41, 0x64, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x71, 0x75,
 	0x65, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72,
 	0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x43, 0x6c, 0x69, 0x65,
@@ -731,15 +794,25 @@ var file_rpc_proto_rawDesc = []byte{
 	0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01,
 	0x28, 0x09, 0x52, 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x12,
 	0x14, 0x0a, 0x05, 0x4e, 0x65, 0x74, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
-	0x4e, 0x65, 0x74, 0x6e, 0x73, 0x22, 0xa9, 0x03, 0x0a, 0x0f, 0x41, 0x64, 0x64, 0x4e, 0x65, 0x74,
-	0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x75, 0x63,
-	0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x53, 0x75, 0x63, 0x63,
+	0x4e, 0x65, 0x74, 0x6e, 0x73, 0x12, 0x3e, 0x0a, 0x1a, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65,
+	0x73, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4e, 0x49, 0x43, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d,
+	0x65, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x52, 0x65, 0x71, 0x75, 0x69,
+	0x72, 0x65, 0x73, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4e, 0x49, 0x43, 0x41, 0x74, 0x74, 0x61, 0x63,
+	0x68, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x8b, 0x01, 0x0a, 0x09, 0x49, 0x50, 0x41, 0x64, 0x64, 0x72,
 	0x65, 0x73, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x49, 0x50, 0x76, 0x34, 0x41, 0x64, 0x64, 0x72, 0x18,
-	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x49, 0x50, 0x76, 0x34, 0x41, 0x64, 0x64, 0x72, 0x12,
-	0x1a, 0x0a, 0x08, 0x49, 0x50, 0x76, 0x36, 0x41, 0x64, 0x64, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x49, 0x50, 0x76, 0x34, 0x41, 0x64, 0x64, 0x72, 0x12,
+	0x1a, 0x0a, 0x08, 0x49, 0x50, 0x76, 0x36, 0x41, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28,
 	0x09, 0x52, 0x08, 0x49, 0x50, 0x76, 0x36, 0x41, 0x64, 0x64, 0x72, 0x12, 0x22, 0x0a, 0x0c, 0x44,
-	0x65, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28,
+	0x65, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28,
 	0x05, 0x52, 0x0c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12,
+	0x22, 0x0a, 0x0c, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x64, 0x18,
+	0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c,
+	0x65, 0x49, 0x64, 0x22, 0xfb, 0x02, 0x0a, 0x0f, 0x41, 0x64, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f,
+	0x72, 0x6b, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65,
+	0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73,
+	0x73, 0x12, 0x2c, 0x0a, 0x09, 0x49, 0x50, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x50, 0x41, 0x64, 0x64,
+	0x72, 0x65, 0x73, 0x73, 0x52, 0x09, 0x49, 0x50, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12,
 	0x28, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x4e,
 	0x41, 0x54, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x55, 0x73, 0x65, 0x45, 0x78, 0x74,
 	0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x4e, 0x41, 0x54, 0x12, 0x1e, 0x0a, 0x0a, 0x56, 0x50, 0x43,
@@ -777,71 +850,72 @@ var file_rpc_proto_rawDesc = []byte{
 	0x44, 0x12, 0x16, 0x0a, 0x06, 0x49, 0x66, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28,
 	0x09, 0x52, 0x06, 0x49, 0x66, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x4e, 0x65, 0x74,
 	0x77, 0x6f, 0x72, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
-	0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xa5, 0x01, 0x0a, 0x0f,
-	0x44, 0x65, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12,
-	0x18, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
-	0x52, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x49, 0x50, 0x76,
-	0x34, 0x41, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x49, 0x50, 0x76,
-	0x34, 0x41, 0x64, 0x64, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x49, 0x50, 0x76, 0x36, 0x41, 0x64, 0x64,
-	0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x49, 0x50, 0x76, 0x36, 0x41, 0x64, 0x64,
-	0x72, 0x12, 0x22, 0x0a, 0x0c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65,
-	0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4e,
-	0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x6f, 0x64, 0x56, 0x6c, 0x61, 0x6e,
-	0x49, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x50, 0x6f, 0x64, 0x56, 0x6c, 0x61,
-	0x6e, 0x49, 0x64, 0x22, 0x90, 0x01, 0x0a, 0x10, 0x45, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x4e,
-	0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x4b, 0x38, 0x53, 0x5f,
-	0x50, 0x4f, 0x44, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
-	0x4b, 0x38, 0x53, 0x50, 0x4f, 0x44, 0x4e, 0x41, 0x4d, 0x45, 0x12, 0x2a, 0x0a, 0x11, 0x4b, 0x38,
-	0x53, 0x5f, 0x50, 0x4f, 0x44, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x53, 0x50, 0x41, 0x43, 0x45, 0x18,
-	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x4b, 0x38, 0x53, 0x50, 0x4f, 0x44, 0x4e, 0x41, 0x4d,
-	0x45, 0x53, 0x50, 0x41, 0x43, 0x45, 0x12, 0x2e, 0x0a, 0x13, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52,
-	0x4b, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x18, 0x03, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x11, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x50, 0x4f, 0x4c, 0x49,
-	0x43, 0x59, 0x4d, 0x4f, 0x44, 0x45, 0x22, 0x2a, 0x0a, 0x0e, 0x45, 0x6e, 0x66, 0x6f, 0x72, 0x63,
-	0x65, 0x4e, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63,
-	0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65,
-	0x73, 0x73, 0x22, 0x5f, 0x0a, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x70, 0x52, 0x65,
-	0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x4b, 0x38, 0x53, 0x5f, 0x50, 0x4f, 0x44,
-	0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x4b, 0x38, 0x53,
-	0x50, 0x4f, 0x44, 0x4e, 0x41, 0x4d, 0x45, 0x12, 0x2a, 0x0a, 0x11, 0x4b, 0x38, 0x53, 0x5f, 0x50,
-	0x4f, 0x44, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x53, 0x50, 0x41, 0x43, 0x45, 0x18, 0x02, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x0f, 0x4b, 0x38, 0x53, 0x50, 0x4f, 0x44, 0x4e, 0x41, 0x4d, 0x45, 0x53, 0x50,
-	0x41, 0x43, 0x45, 0x22, 0x29, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x70, 0x52,
-	0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x4d,
-	0x0a, 0x1d, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x41,
-	0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12,
-	0x2c, 0x0a, 0x11, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79,
-	0x4d, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x4e, 0x65, 0x74, 0x77,
-	0x6f, 0x72, 0x6b, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x32, 0x88, 0x01,
-	0x0a, 0x0a, 0x43, 0x4e, 0x49, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x3c, 0x0a, 0x0a,
-	0x41, 0x64, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x16, 0x2e, 0x72, 0x70, 0x63,
-	0x2e, 0x41, 0x64, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x1a, 0x14, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x4e, 0x65, 0x74, 0x77,
-	0x6f, 0x72, 0x6b, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3c, 0x0a, 0x0a, 0x44, 0x65,
-	0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x16, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x44,
-	0x65, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
-	0x1a, 0x14, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72,
-	0x6b, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x32, 0x86, 0x01, 0x0a, 0x09, 0x4e, 0x50, 0x42,
-	0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x3e, 0x0a, 0x0e, 0x45, 0x6e, 0x66, 0x6f, 0x72, 0x63,
-	0x65, 0x4e, 0x70, 0x54, 0x6f, 0x50, 0x6f, 0x64, 0x12, 0x15, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45,
-	0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x4e, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
-	0x13, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x4e, 0x70, 0x52,
-	0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
-	0x50, 0x6f, 0x64, 0x4e, 0x70, 0x12, 0x14, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65,
-	0x74, 0x65, 0x4e, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x72, 0x70,
-	0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22,
-	0x00, 0x32, 0x6e, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x65, 0x72, 0x76, 0x65,
-	0x72, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x57, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x4e,
-	0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, 0x6f, 0x6e, 0x66,
-	0x69, 0x67, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
-	0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x22, 0x2e, 0x72, 0x70,
-	0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x41,
-	0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22,
-	0x00, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
-	0x61, 0x77, 0x73, 0x2f, 0x61, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x2d, 0x76, 0x70, 0x63, 0x2d, 0x63,
-	0x6e, 0x69, 0x2d, 0x6b, 0x38, 0x73, 0x2f, 0x72, 0x70, 0x63, 0x3b, 0x72, 0x70, 0x63, 0x62, 0x06,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x77, 0x0a, 0x0f, 0x44,
+	0x65, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18,
+	0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
+	0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x2c, 0x0a, 0x09, 0x49, 0x50, 0x41, 0x64,
+	0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x72, 0x70,
+	0x63, 0x2e, 0x49, 0x50, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x09, 0x49, 0x50, 0x41,
+	0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x6f, 0x64, 0x56, 0x6c, 0x61,
+	0x6e, 0x49, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x50, 0x6f, 0x64, 0x56, 0x6c,
+	0x61, 0x6e, 0x49, 0x64, 0x22, 0x79, 0x0a, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x70,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65,
+	0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73,
+	0x73, 0x12, 0x20, 0x0a, 0x0c, 0x4b, 0x38, 0x53, 0x5f, 0x50, 0x4f, 0x44, 0x5f, 0x4e, 0x41, 0x4d,
+	0x45, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x4b, 0x38, 0x53, 0x50, 0x4f, 0x44, 0x4e,
+	0x41, 0x4d, 0x45, 0x12, 0x2a, 0x0a, 0x11, 0x4b, 0x38, 0x53, 0x5f, 0x50, 0x4f, 0x44, 0x5f, 0x4e,
+	0x41, 0x4d, 0x45, 0x53, 0x50, 0x41, 0x43, 0x45, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f,
+	0x4b, 0x38, 0x53, 0x50, 0x4f, 0x44, 0x4e, 0x41, 0x4d, 0x45, 0x53, 0x50, 0x41, 0x43, 0x45, 0x22,
+	0x29, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x79,
+	0x12, 0x18, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x08, 0x52, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0xb8, 0x01, 0x0a, 0x10, 0x45,
+	0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x4e, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
+	0x20, 0x0a, 0x0c, 0x4b, 0x38, 0x53, 0x5f, 0x50, 0x4f, 0x44, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x4b, 0x38, 0x53, 0x50, 0x4f, 0x44, 0x4e, 0x41, 0x4d,
+	0x45, 0x12, 0x2a, 0x0a, 0x11, 0x4b, 0x38, 0x53, 0x5f, 0x50, 0x4f, 0x44, 0x5f, 0x4e, 0x41, 0x4d,
+	0x45, 0x53, 0x50, 0x41, 0x43, 0x45, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x4b, 0x38,
+	0x53, 0x50, 0x4f, 0x44, 0x4e, 0x41, 0x4d, 0x45, 0x53, 0x50, 0x41, 0x43, 0x45, 0x12, 0x2e, 0x0a,
+	0x13, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f,
+	0x4d, 0x4f, 0x44, 0x45, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x4e, 0x45, 0x54, 0x57,
+	0x4f, 0x52, 0x4b, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x4d, 0x4f, 0x44, 0x45, 0x12, 0x26, 0x0a,
+	0x0e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18,
+	0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65,
+	0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x2a, 0x0a, 0x0e, 0x45, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65,
+	0x4e, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65,
+	0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73,
+	0x73, 0x22, 0x4d, 0x0a, 0x1d, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x50, 0x6f, 0x6c, 0x69,
+	0x63, 0x79, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x70,
+	0x6c, 0x79, 0x12, 0x2c, 0x0a, 0x11, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x50, 0x6f, 0x6c,
+	0x69, 0x63, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x4e,
+	0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4d, 0x6f, 0x64, 0x65,
+	0x32, 0x88, 0x01, 0x0a, 0x0a, 0x43, 0x4e, 0x49, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12,
+	0x3c, 0x0a, 0x0a, 0x41, 0x64, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x16, 0x2e,
+	0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65,
+	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x4e,
+	0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3c, 0x0a,
+	0x0a, 0x44, 0x65, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x16, 0x2e, 0x72, 0x70,
+	0x63, 0x2e, 0x44, 0x65, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x4e, 0x65, 0x74,
+	0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x32, 0x86, 0x01, 0x0a, 0x09,
+	0x4e, 0x50, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x3e, 0x0a, 0x0e, 0x45, 0x6e, 0x66,
+	0x6f, 0x72, 0x63, 0x65, 0x4e, 0x70, 0x54, 0x6f, 0x50, 0x6f, 0x64, 0x12, 0x15, 0x2e, 0x72, 0x70,
+	0x63, 0x2e, 0x45, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x4e, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x1a, 0x13, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65,
+	0x4e, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x0b, 0x44, 0x65, 0x6c,
+	0x65, 0x74, 0x65, 0x50, 0x6f, 0x64, 0x4e, 0x70, 0x12, 0x14, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x44,
+	0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12,
+	0x2e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x70, 0x52, 0x65, 0x70,
+	0x6c, 0x79, 0x22, 0x00, 0x32, 0x6e, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x65,
+	0x72, 0x76, 0x65, 0x72, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x57, 0x0a, 0x17, 0x47,
+	0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43,
+	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x22,
+	0x2e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x50, 0x6f, 0x6c, 0x69,
+	0x63, 0x79, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x70,
+	0x6c, 0x79, 0x22, 0x00, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
+	0x6f, 0x6d, 0x2f, 0x61, 0x77, 0x73, 0x2f, 0x61, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x2d, 0x76, 0x70,
+	0x63, 0x2d, 0x63, 0x6e, 0x69, 0x2d, 0x6b, 0x38, 0x73, 0x2f, 0x72, 0x70, 0x63, 0x3b, 0x72, 0x70,
+	0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -856,35 +930,38 @@ func file_rpc_proto_rawDescGZIP() []byte {
 	return file_rpc_proto_rawDescData
 }
 
-var file_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
+var file_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
 var file_rpc_proto_goTypes = []interface{}{
 	(*AddNetworkRequest)(nil),             // 0: rpc.AddNetworkRequest
-	(*AddNetworkReply)(nil),               // 1: rpc.AddNetworkReply
-	(*DelNetworkRequest)(nil),             // 2: rpc.DelNetworkRequest
-	(*DelNetworkReply)(nil),               // 3: rpc.DelNetworkReply
-	(*EnforceNpRequest)(nil),              // 4: rpc.EnforceNpRequest
-	(*EnforceNpReply)(nil),                // 5: rpc.EnforceNpReply
-	(*DeleteNpRequest)(nil),               // 6: rpc.DeleteNpRequest
-	(*DeleteNpReply)(nil),                 // 7: rpc.DeleteNpReply
-	(*NetworkPolicyAgentConfigReply)(nil), // 8: rpc.NetworkPolicyAgentConfigReply
-	(*emptypb.Empty)(nil),                 // 9: google.protobuf.Empty
+	(*IPAddress)(nil),                     // 1: rpc.IPAddress
+	(*AddNetworkReply)(nil),               // 2: rpc.AddNetworkReply
+	(*DelNetworkRequest)(nil),             // 3: rpc.DelNetworkRequest
+	(*DelNetworkReply)(nil),               // 4: rpc.DelNetworkReply
+	(*DeleteNpRequest)(nil),               // 5: rpc.DeleteNpRequest
+	(*DeleteNpReply)(nil),                 // 6: rpc.DeleteNpReply
+	(*EnforceNpRequest)(nil),              // 7: rpc.EnforceNpRequest
+	(*EnforceNpReply)(nil),                // 8: rpc.EnforceNpReply
+	(*NetworkPolicyAgentConfigReply)(nil), // 9: rpc.NetworkPolicyAgentConfigReply
+	(*emptypb.Empty)(nil),                 // 10: google.protobuf.Empty
 }
 var file_rpc_proto_depIdxs = []int32{
-	0, // 0: rpc.CNIBackend.AddNetwork:input_type -> rpc.AddNetworkRequest
-	2, // 1: rpc.CNIBackend.DelNetwork:input_type -> rpc.DelNetworkRequest
-	4, // 2: rpc.NPBackend.EnforceNpToPod:input_type -> rpc.EnforceNpRequest
-	6, // 3: rpc.NPBackend.DeletePodNp:input_type -> rpc.DeleteNpRequest
-	9, // 4: rpc.ConfigServerBackend.GetNetworkPolicyConfigs:input_type -> google.protobuf.Empty
-	1, // 5: rpc.CNIBackend.AddNetwork:output_type -> rpc.AddNetworkReply
-	3, // 6: rpc.CNIBackend.DelNetwork:output_type -> rpc.DelNetworkReply
-	5, // 7: rpc.NPBackend.EnforceNpToPod:output_type -> rpc.EnforceNpReply
-	7, // 8: rpc.NPBackend.DeletePodNp:output_type -> rpc.DeleteNpReply
-	8, // 9: rpc.ConfigServerBackend.GetNetworkPolicyConfigs:output_type -> rpc.NetworkPolicyAgentConfigReply
-	5, // [5:10] is the sub-list for method output_type
-	0, // [0:5] is the sub-list for method input_type
-	0, // [0:0] is the sub-list for extension type_name
-	0, // [0:0] is the sub-list for extension extendee
-	0, // [0:0] is the sub-list for field type_name
+	1,  // 0: rpc.AddNetworkReply.IPAddress:type_name -> rpc.IPAddress
+	1,  // 1: rpc.DelNetworkReply.IPAddress:type_name -> rpc.IPAddress
+	0,  // 2: rpc.CNIBackend.AddNetwork:input_type -> rpc.AddNetworkRequest
+	3,  // 3: rpc.CNIBackend.DelNetwork:input_type -> rpc.DelNetworkRequest
+	7,  // 4: rpc.NPBackend.EnforceNpToPod:input_type -> rpc.EnforceNpRequest
+	5,  // 5: rpc.NPBackend.DeletePodNp:input_type -> rpc.DeleteNpRequest
+	10, // 6: rpc.ConfigServerBackend.GetNetworkPolicyConfigs:input_type -> google.protobuf.Empty
+	2,  // 7: rpc.CNIBackend.AddNetwork:output_type -> rpc.AddNetworkReply
+	4,  // 8: rpc.CNIBackend.DelNetwork:output_type -> rpc.DelNetworkReply
+	8,  // 9: rpc.NPBackend.EnforceNpToPod:output_type -> rpc.EnforceNpReply
+	6,  // 10: rpc.NPBackend.DeletePodNp:output_type -> rpc.DeleteNpReply
+	9,  // 11: rpc.ConfigServerBackend.GetNetworkPolicyConfigs:output_type -> rpc.NetworkPolicyAgentConfigReply
+	7,  // [7:12] is the sub-list for method output_type
+	2,  // [2:7] is the sub-list for method input_type
+	2,  // [2:2] is the sub-list for extension type_name
+	2,  // [2:2] is the sub-list for extension extendee
+	0,  // [0:2] is the sub-list for field type_name
 }
 
 func init() { file_rpc_proto_init() }
@@ -906,7 +983,7 @@ func file_rpc_proto_init() {
 			}
 		}
 		file_rpc_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*AddNetworkReply); i {
+			switch v := v.(*IPAddress); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -918,7 +995,7 @@ func file_rpc_proto_init() {
 			}
 		}
 		file_rpc_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*DelNetworkRequest); i {
+			switch v := v.(*AddNetworkReply); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -930,7 +1007,7 @@ func file_rpc_proto_init() {
 			}
 		}
 		file_rpc_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*DelNetworkReply); i {
+			switch v := v.(*DelNetworkRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -942,7 +1019,7 @@ func file_rpc_proto_init() {
 			}
 		}
 		file_rpc_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*EnforceNpRequest); i {
+			switch v := v.(*DelNetworkReply); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -954,7 +1031,7 @@ func file_rpc_proto_init() {
 			}
 		}
 		file_rpc_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*EnforceNpReply); i {
+			switch v := v.(*DeleteNpRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -966,7 +1043,7 @@ func file_rpc_proto_init() {
 			}
 		}
 		file_rpc_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*DeleteNpRequest); i {
+			switch v := v.(*DeleteNpReply); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -978,7 +1055,7 @@ func file_rpc_proto_init() {
 			}
 		}
 		file_rpc_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*DeleteNpReply); i {
+			switch v := v.(*EnforceNpRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -990,6 +1067,18 @@ func file_rpc_proto_init() {
 			}
 		}
 		file_rpc_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*EnforceNpReply); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_rpc_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*NetworkPolicyAgentConfigReply); i {
 			case 0:
 				return &v.state
@@ -1008,7 +1097,7 @@ func file_rpc_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_rpc_proto_rawDesc,
 			NumEnums:      0,
-			NumMessages:   9,
+			NumMessages:   10,
 			NumExtensions: 0,
 			NumServices:   3,
 		},
diff --git a/rpc/rpc.proto b/rpc/rpc.proto
index 794a7eb2e4..6996222705 100644
--- a/rpc/rpc.proto
+++ b/rpc/rpc.proto
@@ -20,14 +20,20 @@ message AddNetworkRequest {
   string IfName = 5;
   string NetworkName = 6;
   string Netns = 4;
-  // next field: 9
+  bool RequiresMultiNICAttachment = 9;
+  // next field: 10
+}
+
+message IPAddress {
+  string IPv4Addr = 1;
+  string IPv6Addr = 2;
+  int32 DeviceNumber = 3;
+  int32 RouteTableId = 4;
 }
 
 message AddNetworkReply {
   bool Success = 1;
-  string IPv4Addr = 2;
-  string IPv6Addr = 11;
-  int32 DeviceNumber = 4;
+  repeated IPAddress IPAddress = 2;
   bool UseExternalSNAT = 5;
   repeated string VPCv4CIDRs = 6;
   repeated string VPCv6CIDRs = 12;
@@ -57,9 +63,7 @@ message DelNetworkRequest {
 
 message DelNetworkReply {
   bool Success = 1;
-  string IPv4Addr = 2;
-  string IPv6Addr = 5;
-  int32 DeviceNumber = 3;
+  repeated IPAddress IPAddress = 2;
 
   // start of pod-eni parameters
   int32 PodVlanId = 4;
@@ -68,6 +72,16 @@ message DelNetworkReply {
   // next field: 6
 }
 
+message DeleteNpRequest {
+  bool Success = 1;
+	string K8S_POD_NAME = 2;
+	string K8S_POD_NAMESPACE = 3;
+}
+
+message DeleteNpReply {
+  bool Success = 1;
+}
+
 // The service definition.
 service NPBackend {
   rpc EnforceNpToPod (EnforceNpRequest) returns (EnforceNpReply) {}
@@ -78,6 +92,7 @@ message EnforceNpRequest {
   string K8S_POD_NAME = 1;
   string K8S_POD_NAMESPACE = 2;
   string NETWORK_POLICY_MODE = 3;
+  int32 InterfaceCount = 4;
 }
 
 message EnforceNpReply {