diff --git a/helpers.go b/helpers.go index 7092c33..ec69cd9 100644 --- a/helpers.go +++ b/helpers.go @@ -27,8 +27,8 @@ func (i IPNet) Plen() uint32 { } // IPNetFromAddr reads net.Addr and converts it to IPNet -func IPNetFromAddr(a net.Addr) (*IPNet, error) { - _, p, err := net.ParseCIDR(a.String()) +func IPNetFromString(a string) (*IPNet, error) { + _, p, err := net.ParseCIDR(a) if err != nil { return nil, err } @@ -39,6 +39,10 @@ func IPNetFromAddr(a net.Addr) (*IPNet, error) { }, nil } +func IPNetFromAddr(a net.Addr) (*IPNet, error) { + return IPNetFromString(a.String()) +} + // parse bgp community from string to uint32 func parseCommunity(c string) (uint32, error) { s := strings.SplitN(c, ":", 2) @@ -111,55 +115,26 @@ func getPath(p IPNet, nh string, myCom string) (*api.Path, error) { }, nil } -//get all local IPs elegible to be elastic IP -func getIPs(v6Mask int, allIfs bool) (*[]IPNet, error) { - var addrs []net.Addr - var err error - - if allIfs { - addrs, err = net.InterfaceAddrs() - } else { - lo, err := net.InterfaceByName("lo") - if err != nil { - return nil, err - } - addrs, err = lo.Addrs() - } - if err != nil { - return nil, err - } - - sendMask := net.CIDRMask(v6Mask, 128) - - // var ips []IPNet - ips := make(map[string]*IPNet) - - for _, addr := range addrs { - ip, err := IPNetFromAddr(addr) - if err != nil { - log.WithFields(log.Fields{"Topic": "Helper", "Route": addr, "Error": "invalid IP"}).Warn("invalid IP") - continue - } - +func validateIP(ip *IPNet, v6Mask int, sendMask net.IPMask) (*IPNet) { // ignore loopback IPs if ip.IP.IsLoopback() { log.WithFields(log.Fields{"Topic": "Helper", "Route": ip, "Warn": "not acceptable elastic IP"}). Trace("ignoring loopback IPs") - continue + return nil } // ignore link local IPs if ip.IP.IsLinkLocalUnicast() { log.WithFields(log.Fields{"Topic": "Helper", "Route": ip, "Warn": "not acceptable elastic IP"}). Trace("ignoring linklocal IPs") - continue + return nil } // for ipv4 only a /32 is acceptable if ip.IP.To4() != nil && ip.Plen() != 32 { log.WithFields(log.Fields{"Topic": "Helper", "Route": ip, "Warn": "not accepted prefix length"}). Warn("not accepted prefix length") - continue + return nil } // for ipv6 lets find the greater subnet we're part of, make it a /64 (or if asked a /56) and advertise that @@ -173,16 +148,67 @@ func getIPs(v6Mask int, allIfs bool) (*[]IPNet, error) { if err != nil { log.WithFields(log.Fields{"Topic": "Helper", "Route": ip, "Error": "invalid IP"}). Warnf("unable to supernet") - continue + return nil } - ip = &IPNet{ + return &IPNet{ IP: ipNew.IP, Mask: ipNew.Mask, } } - ips[ip.String()] = ip - log.WithFields(log.Fields{"Topic": "Helper", "Route": ip}).Debug("handling prefix") + return ip +} + +//get all local IPs elegible to be elastic IP +func getIPs(v6Mask int, allIfs bool, manualIPs []string) (*[]IPNet, error) { + var addrs []net.Addr + var err error + ips := make(map[string]*IPNet) + + sendMask := net.CIDRMask(v6Mask, 128) + + // Manually provided IPs + for _, a := range manualIPs { + ip, err := IPNetFromString(a) + if err != nil { + log.WithFields(log.Fields{"Topic": "Main", "Route": a, "Error": "invalid IP"}).Warnf("Invalid IP: %v", err) + continue + } + ip = validateIP(ip, v6Mask, sendMask) + if ip != nil { + ips[ip.String()] = ip + log.WithFields(log.Fields{"Topic": "Helper", "Route": ip}).Debug("handling prefix (manual)") + } + } + + + if allIfs { + addrs, err = net.InterfaceAddrs() + } else { + lo, err := net.InterfaceByName("lo") + if err != nil { + log.WithFields(log.Fields{"Topic": "Helper"}).Warnf("Cannot find 'lo' interface: %v", err) + } else { + addrs, err = lo.Addrs() + } + } + if err != nil { + log.WithFields(log.Fields{"Topic": "Helper"}).Warnf("Cannot load interface addresses: %v", err) + } + + + for _, addr := range addrs { + ip, err := IPNetFromAddr(addr) + if err != nil { + log.WithFields(log.Fields{"Topic": "Helper", "Route": addr, "Error": "invalid IP"}).Warnf("Invalid IP: %v", err) + continue + } + + ip = validateIP(ip, v6Mask, sendMask) + if ip != nil { + ips[ip.String()] = ip + log.WithFields(log.Fields{"Topic": "Helper", "Route": ip}).Debug("handling prefix") + } } var uniqIPs []IPNet @@ -191,7 +217,7 @@ func getIPs(v6Mask int, allIfs bool) (*[]IPNet, error) { } if len(uniqIPs) == 0 { - return nil, fmt.Errorf("didn't find any configured elastic IPs") + return nil, fmt.Errorf("Didn't find any elastic IPs") } return &uniqIPs, nil diff --git a/main.go b/main.go index 3d63d36..e23673d 100644 --- a/main.go +++ b/main.go @@ -17,12 +17,12 @@ const ( ) func main() { - primary := flag.Bool("primary", false, "advertise as primary") - secondary := flag.Bool("secondary", false, "advertise as secondary") - loglevel := flag.String("loglevel", "info", "set log level: trace, debug, info or warn") - logjson := flag.Bool("logjson", false, "set log format to json") - dcid := flag.Int("dcid", 0, "dcid for your DC") - send56 := flag.Bool("send56", false, "Advertise ipv6 as /56 subnet (defaults to /64)") + primary := flag.Bool("primary", false, "Advertise as primary (default if -secondary not specified)") + secondary := flag.Bool("secondary", false, "Advertise as secondary") + loglevel := flag.String("loglevel", "info", "Set log level: trace, debug, info or warn") + logjson := flag.Bool("logjson", false, "Set log format to json") + dcid := flag.Int("dcid", 0, "Your Linode data center id") + send56 := flag.Bool("send56", false, "Advertise IPv6 as /56 subnet (defaults to /64)") allIfs := flag.Bool( "allifs", false, @@ -43,14 +43,16 @@ func main() { if *dcid <= 1 { flag.Usage() - log.WithFields(log.Fields{"Topic": "Main"}).Fatal("dcid not provided, I need this info") + log.WithFields(log.Fields{"Topic": "Main"}).Fatal("Required -dcid not provided") } - if !*primary && !*secondary { + if *primary && *secondary { flag.Usage() - log.WithFields(log.Fields{"Topic": "Main"}).Fatal("use either primary or secondary flag") + log.WithFields(log.Fields{"Topic": "Main"}).Fatal("Use either -primary or -secondary flag, not both!") } + + switch *loglevel { case "trace": log.SetLevel(log.TraceLevel) @@ -62,19 +64,13 @@ func main() { log.SetLevel(log.WarnLevel) default: log.WithFields(log.Fields{"Topic": "Main"}). - Warn("unknown log level, only trace, debug, info and warn are supported, falling back to loglevel info") + Warn("Unknown log level, only trace, debug, info and warn are supported, falling back to loglevel info") log.SetLevel(log.InfoLevel) } - var myCommunity string - - switch { - case *primary: - myCommunity = communityPrimary - case *secondary: + myCommunity := communityPrimary; + if *secondary { myCommunity = communitySecondary - default: - log.WithFields(log.Fields{"Topic": "Main"}).Fatal("use either primary or secondary flag") } //ips @@ -83,14 +79,14 @@ func main() { v6Mask = 56 } - ips, err := getIPs(v6Mask, *allIfs) + ips, err := getIPs(v6Mask, *allIfs, flag.Args()) if err != nil { - log.WithFields(log.Fields{"Topic": "Main"}).Fatalf("unable to detect IPs: %v", err) + log.WithFields(log.Fields{"Topic": "Main"}).Fatalf("No IPs: %v", err) } c, err := NewClient(myCommunity, ips) if err != nil { - log.WithFields(log.Fields{"Topic": "Main"}).Fatal("failed to initiate the client: ", err) + log.WithFields(log.Fields{"Topic": "Main"}).Fatal("Failed to initiate the BGP client: ", err) } c.wg.Add(1) @@ -98,13 +94,13 @@ func main() { for i := 1; i <= 4; i++ { rs := fmt.Sprintf("2600:3c0f:%d:34::%d", *dcid, i) if err := c.AddRs(rs); err != nil { - log.WithFields(log.Fields{"Topic": "Neighbor", "Neighbor": rs}).Fatal("failed adding neighbor") + log.WithFields(log.Fields{"Topic": "Neighbor", "Neighbor": rs}).Fatal("Failed adding neighbor") } // log.WithFields(log.Fields{"Topic": "Neighbor", "Neighbor": rs}).Info("added neighbor") } if err := c.AddRoutes(); err != nil { - log.WithFields(log.Fields{"Topic": "IPs"}).Fatal("failed adding IP advertisements: ", err) + log.WithFields(log.Fields{"Topic": "IPs"}).Fatal("Failed adding IP advertisements: ", err) } log.WithFields(log.Fields{"Topic": "Main"}).Info("Running....")