diff --git a/cmd/network/ipv4ll.go b/cmd/network/ipv4ll.go new file mode 100644 index 00000000..301fd353 --- /dev/null +++ b/cmd/network/ipv4ll.go @@ -0,0 +1,75 @@ +package network + +import ( + "encoding/binary" + "fmt" + "math/rand" + "net" + + log "github.com/Sirupsen/logrus" + + "github.com/j-keck/arping" + "github.com/vishvananda/netlink" +) + +func AssignLinkLocalIP(link netlink.Link) error { + ifaceName := link.Attrs().Name + iface, err := net.InterfaceByName(ifaceName) + if err != nil { + log.Error("could not get information about interface") + return err + } + addrs, err := iface.Addrs() + if err != nil { + log.Error("Error fetching existing ip on interface") + } + for _, addr := range addrs { + if addr.String()[:7] == "169.254" { + log.Info("Link Local IP already set on interface") + return nil + } + } + randSource, err := getPseudoRandomGenerator(link.Attrs().HardwareAddr) + if err != nil { + return err + } + // try a random address upto 10 times + for i := 0; i < 10; i++ { + randGenerator := rand.New(*randSource) + randomNum := randGenerator.Uint32() + dstIP := getNewIPV4LLAddr(randomNum) + if dstIP[2] == 0 || dstIP[2] == 255 { + i-- + continue + } + _, _, err := arping.PingOverIfaceByName(dstIP, ifaceName) + if err != nil { + // this ip is not being used + addr, err := netlink.ParseAddr(dstIP.String() + "/16") + if err != nil { + log.Errorf("error while parsing ipv4ll addr, err = %v", err) + return err + } + if err := netlink.AddrAdd(link, addr); err != nil { + log.Error("ipv4ll addr add failed") + return err + } + log.Infof("Set %s on %s", dstIP.String(), link.Attrs().Name) + return nil + } + } + log.Error("Could not find a suitable ipv4ll") + return fmt.Errorf("Could not find a suitable ipv4ll") +} + +func getNewIPV4LLAddr(randomNum uint32) net.IP { + byte1 := randomNum & 255 // use least significant 8 bits + byte2 := randomNum >> 24 // use most significant 8 bits + return []byte{169, 254, byte(byte1), byte(byte2)} +} + +func getPseudoRandomGenerator(haAddr []byte) (*rand.Source, error) { + seed, _ := binary.Varint(haAddr) + src := rand.NewSource(seed) + return &src, nil +} diff --git a/cmd/network/network.go b/cmd/network/network.go index bdda724a..db1ec5b2 100644 --- a/cmd/network/network.go +++ b/cmd/network/network.go @@ -101,6 +101,11 @@ func applyNetConf(link netlink.Link, netConf config.InterfaceConfig) error { if err := cmd.Run(); err != nil { log.Error(err) } + } else if netConf.IPV4LL { + if err := AssignLinkLocalIP(link); err != nil { + log.Error("IPV4LL set failed") + return err + } } else if netConf.Address == "" { return nil } else { diff --git a/config/types.go b/config/types.go index f914a99c..651c0f07 100644 --- a/config/types.go +++ b/config/types.go @@ -75,6 +75,7 @@ type InterfaceConfig struct { Match string `yaml:"match,omitempty"` DHCP bool `yaml:"dhcp,omitempty"` Address string `yaml:"address,omitempty"` + IPV4LL bool `yaml:"ipv4ll,omitempty"` Gateway string `yaml:"gateway,omitempty"` MTU int `yaml:"mtu,omitempty"` }