142 lines
3.1 KiB
Go
142 lines
3.1 KiB
Go
package vip
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/vishvananda/netlink"
|
|
)
|
|
|
|
const (
|
|
defaultValidLft = 60
|
|
)
|
|
|
|
// Network is an interface that enable managing operations for a given IP
|
|
type Network interface {
|
|
AddIP() error
|
|
DeleteIP() error
|
|
IsSet() (bool, error)
|
|
IP() string
|
|
SetIP(ip string) error
|
|
Interface() string
|
|
}
|
|
|
|
// network - This allows network configuration
|
|
type network struct {
|
|
mu sync.Mutex
|
|
|
|
address *netlink.Addr
|
|
link netlink.Link
|
|
isDNS bool
|
|
}
|
|
|
|
// NewConfig will attempt to provide an interface to the kernel network configuration
|
|
func NewConfig(address string, iface string) (Network, error) {
|
|
result := &network{}
|
|
|
|
link, err := netlink.LinkByName(iface)
|
|
if err != nil {
|
|
return result, errors.Wrapf(err, "could not get link for interface '%s'", iface)
|
|
}
|
|
result.link = link
|
|
|
|
if IsIP(address) {
|
|
result.address, err = netlink.ParseAddr(address + "/32")
|
|
if err != nil {
|
|
return result, errors.Wrapf(err, "could not parse address '%s'", address)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// try to resolve the address
|
|
ip, err := lookupHost(address)
|
|
result.isDNS = err == nil
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// we're able to resolve store this as the initial IP
|
|
if result.address, err = netlink.ParseAddr(ip + "/32"); err != nil {
|
|
return result, err
|
|
}
|
|
// set ValidLft so that the VIP expires if the DNS entry is updated, otherwise it'll be refreshed by the DNS prober
|
|
result.address.ValidLft = defaultValidLft
|
|
|
|
return result, err
|
|
}
|
|
|
|
//AddIP - Add an IP address to the interface
|
|
func (configurator *network) AddIP() error {
|
|
if err := netlink.AddrReplace(configurator.link, configurator.address); err != nil {
|
|
return errors.Wrap(err, "could not add ip")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
//DeleteIP - Remove an IP address from the interface
|
|
func (configurator *network) DeleteIP() error {
|
|
result, err := configurator.IsSet()
|
|
if err != nil {
|
|
return errors.Wrap(err, "ip check in DeleteIP failed")
|
|
}
|
|
|
|
// Nothing to delete
|
|
if !result {
|
|
return nil
|
|
}
|
|
|
|
if err = netlink.AddrDel(configurator.link, configurator.address); err != nil {
|
|
return errors.Wrap(err, "could not delete ip")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// IsSet - Check to see if VIP is set
|
|
func (configurator *network) IsSet() (result bool, err error) {
|
|
var addresses []netlink.Addr
|
|
|
|
addresses, err = netlink.AddrList(configurator.link, 0)
|
|
if err != nil {
|
|
err = errors.Wrap(err, "could not list addresses")
|
|
|
|
return
|
|
}
|
|
|
|
for _, address := range addresses {
|
|
if address.Equal(*configurator.address) {
|
|
return true, nil
|
|
}
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
// SetIP updates the IP that is used
|
|
func (configurator *network) SetIP(ip string) error {
|
|
configurator.mu.Lock()
|
|
defer configurator.mu.Unlock()
|
|
|
|
addr, err := netlink.ParseAddr(ip + "/32")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if configurator.address != nil && configurator.isDNS {
|
|
addr.ValidLft = defaultValidLft
|
|
}
|
|
configurator.address = addr
|
|
return nil
|
|
}
|
|
|
|
// IP - return the IP Address
|
|
func (configurator *network) IP() string {
|
|
configurator.mu.Lock()
|
|
defer configurator.mu.Unlock()
|
|
|
|
return configurator.address.IP.String()
|
|
}
|
|
|
|
// Interface - return the Interface name
|
|
func (configurator *network) Interface() string {
|
|
return configurator.link.Attrs().Name
|
|
}
|