pkg/initalizer/iproute/link.go (99 lines of code) (raw):
package iproute
import (
"context"
"fmt"
"github.com/vishvananda/netlink"
"go.amzn.com/eks/eks-pod-identity-agent/configuration"
"go.amzn.com/eks/eks-pod-identity-agent/internal/middleware/logger"
)
type (
// actual implementation of the interface
agentLinkRetriever struct {
netlinkHandle NetlinkHandle
}
)
func NewAgentLinkRetriever(handle *netlink.Handle) AgentLinkRetriever {
return &agentLinkRetriever{
netlinkHandle: handle,
}
}
func (l *agentLinkRetriever) CreateOrGetLink(ctx context.Context) (AgentLink, error) {
log := logger.FromContext(ctx)
attrs := netlink.NewLinkAttrs()
attrs.Name = configuration.AgentLinkName
dummyDevice := &netlink.Dummy{LinkAttrs: attrs}
link, err := l.netlinkHandle.LinkByName(attrs.Name)
if err != nil {
_, errWasLinkNotFound := err.(netlink.LinkNotFoundError)
if !errWasLinkNotFound {
return nil, fmt.Errorf("error finding %s: %w", attrs.Name, err)
}
log.Infof("Link was not found, creating it")
link, err = l.createLink(ctx, dummyDevice)
if err != nil {
return nil, fmt.Errorf("unable to create interface %v: %w", dummyDevice.Name, err)
}
log.Debugf("Link %s created", link.Attrs().Name)
}
return &agentLink{
netlinkHandle: l.netlinkHandle,
link: link,
}, nil
}
func (l *agentLinkRetriever) createLink(ctx context.Context, dummyDevice *netlink.Dummy) (netlink.Link, error) {
log := logger.FromContext(ctx)
err := l.netlinkHandle.LinkAdd(dummyDevice)
if err != nil {
log.Errorf("Unable to create interface %s: %v", dummyDevice.Name, err)
return nil, err
}
return l.netlinkHandle.LinkByName(dummyDevice.Name)
}
type agentLink struct {
netlinkHandle NetlinkHandle
link netlink.Link
}
// SetupForAddrFamily adds the given addr to the interface if its not already
// there (e.g. existing CIDR attached to interface contains the given IP addr)
func (l *agentLink) SetupForAddrFamily(ctx context.Context, addrFamily AddrFamily) error {
// override ctx with logger with metadata
log := logger.FromContext(ctx)
if addrFamily.LinkLocalAddr == nil {
return fmt.Errorf("family 0x%02x does not specify a link-local addr to bind to", addrFamily.Family)
}
isIpAttachedToLink, err := l.isIpAttachedToLink(ctx, addrFamily.Family, addrFamily.LinkLocalAddr)
if err != nil {
return err
}
if !isIpAttachedToLink {
log.Infof("Adding IP %s to %v as it was not found on interface", addrFamily.LinkLocalAddr, l.link.Attrs().Name)
if err = l.addIpToLink(addrFamily.LinkLocalAddr); err != nil {
return err
}
} else {
log.Infof("Found IP %s on interface %s, continuing", addrFamily.LinkLocalAddr, l.link.Attrs().Name)
}
return nil
}
// BringUp is the equivalent of calling `ip link set interface up`
func (l *agentLink) BringUp(ctx context.Context) error {
// bring up the link if it's not up. Calling "set up" on a link
// that is already up is a no-op
log := logger.FromContext(ctx)
log.Infof("Bringing up link: %s", l.link.Attrs().Name)
return l.netlinkHandle.LinkSetUp(l.link)
}
func (l *agentLink) Name() string {
return l.link.Attrs().Name
}
func (l *agentLink) isIpAttachedToLink(ctx context.Context, ipFamily int, linkLocalIp *netlink.Addr) (bool, error) {
log := logger.FromContext(ctx)
addrList, err := l.netlinkHandle.AddrList(l.link, ipFamily)
if err != nil {
log.Errorf("Unable to read address list: %v", err)
return false, err
}
for _, al := range addrList {
log.Tracef("Discovered addr %v attached to link %s", al, l.link.Attrs().Name)
if al.Contains(linkLocalIp.IP) {
return true, nil
}
}
return false, nil
}
func (l *agentLink) addIpToLink(linkLocalIp *netlink.Addr) error {
return l.netlinkHandle.AddrAdd(l.link, linkLocalIp)
}