Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 66 additions & 40 deletions helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
42 changes: 19 additions & 23 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -83,28 +79,28 @@ 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)

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....")
Expand Down