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
15 changes: 12 additions & 3 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"sync"

"bits.linode.com/netops/lelastic/pkg/helper"
api "github.com/osrg/gobgp/v3/api"
"github.com/osrg/gobgp/v3/pkg/server"
log "github.com/sirupsen/logrus"
Expand All @@ -14,14 +15,14 @@ import (
// Client is the client
type Client struct {
c *server.BgpServer
ips *[]IPNet
ips *[]helper.IPNet
ipv6Plen int
community string
wg *sync.WaitGroup
}

// NewClient instantiates a new client connection
func NewClient(c string, ips *[]IPNet) (*Client, error) {
func NewClient(c string, ips *[]helper.IPNet) (*Client, error) {
maxSize := 256 << 20
grpcOpts := []grpc.ServerOption{grpc.MaxRecvMsgSize(maxSize), grpc.MaxSendMsgSize(maxSize)}

Expand All @@ -42,6 +43,14 @@ func NewClient(c string, ips *[]IPNet) (*Client, error) {
return nil, err
}

if err := cl.WatchEvent(context.Background(), &api.WatchEventRequest{Peer: &api.WatchEventRequest_Peer{}}, func(r *api.WatchEventResponse) {
if p := r.GetPeer(); p != nil && p.Type == api.WatchEventResponse_PeerEvent_STATE {
log.Println(p)
}
}); err != nil {
return nil, err
}

return &Client{
c: cl,
ips: ips,
Expand Down Expand Up @@ -114,7 +123,7 @@ func (c *Client) AddRs(rs string) error {
}

// AddStaticRoute adds a static route in gobgp incl some BGP attributes for export
func (c *Client) AddStaticRoute(nh string, p IPNet, cm string) error {
func (c *Client) AddStaticRoute(nh string, p helper.IPNet, cm string) error {
path, err := getPath(p, nh, cm)
if err != nil {
return fmt.Errorf("unable to compile path pointer: %w", err)
Expand Down
54 changes: 30 additions & 24 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,38 +1,44 @@
module bits.linode.com/netops/lelastic

go 1.17
go 1.21

toolchain go1.21.4

require (
github.com/golang/protobuf v1.5.2
github.com/osrg/gobgp/v3 v3.1.0
github.com/sirupsen/logrus v1.8.1
google.golang.org/grpc v1.45.0
github.com/golang/protobuf v1.5.4
github.com/osrg/gobgp/v3 v3.28.0
github.com/sirupsen/logrus v1.9.3
google.golang.org/grpc v1.65.0
)

require (
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
github.com/eapache/channels v1.1.0 // indirect
github.com/eapache/queue v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/k-sone/critbitgo v1.4.0 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/spf13/afero v1.6.0 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/sagikazarmark/locafero v0.6.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.10.1 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5 // indirect
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/ini.v1 v1.66.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
github.com/spf13/viper v1.19.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/vishvananda/netlink v1.2.1-beta.2 // indirect
github.com/vishvananda/netns v0.0.4 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20240707233637-46b078467d37 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
860 changes: 77 additions & 783 deletions go.sum

Large diffs are not rendered by default.

43 changes: 8 additions & 35 deletions helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,39 +6,13 @@ import (
"strconv"
"strings"

"bits.linode.com/netops/lelastic/pkg/helper"
"github.com/golang/protobuf/ptypes"
"github.com/golang/protobuf/ptypes/any"
api "github.com/osrg/gobgp/v3/api"
log "github.com/sirupsen/logrus"
)

// IPNet is a extension of net.IPNet with some addons
type IPNet net.IPNet

func (i IPNet) String() string {
j, _ := i.Mask.Size()
return fmt.Sprintf("%v/%v", i.IP, j)
}

// Plen returns the prefix len as uint
func (i IPNet) Plen() uint32 {
j, _ := i.Mask.Size()
return uint32(j)
}

// IPNetFromAddr reads net.Addr and converts it to IPNet
func IPNetFromAddr(a net.Addr) (*IPNet, error) {
_, p, err := net.ParseCIDR(a.String())
if err != nil {
return nil, err
}

return &IPNet{
IP: p.IP,
Mask: p.Mask,
}, nil
}

// parse bgp community from string to uint32
func parseCommunity(c string) (uint32, error) {
s := strings.SplitN(c, ":", 2)
Expand All @@ -57,7 +31,7 @@ func parseCommunity(c string) (uint32, error) {
}

// compile goBGP path type from prefix, next hop and bgp community
func getPath(p IPNet, nh string, myCom string) (*api.Path, error) {
func getPath(p helper.IPNet, nh string, myCom string) (*api.Path, error) {
// convert human readable community to uint32
c, err := parseCommunity(myCom)
if err != nil {
Expand Down Expand Up @@ -111,8 +85,8 @@ 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) {
// get all local IPs elegible to be elastic IP
func getIPs(v6Mask int, allIfs bool) (*[]helper.IPNet, error) {
var addrs []net.Addr
var err error

Expand All @@ -131,11 +105,10 @@ func getIPs(v6Mask int, allIfs bool) (*[]IPNet, error) {

sendMask := net.CIDRMask(v6Mask, 128)

// var ips []IPNet
ips := make(map[string]*IPNet)
ips := make(map[string]*helper.IPNet)

for _, addr := range addrs {
ip, err := IPNetFromAddr(addr)
ip, err := helper.IPNetFromAddr(addr)
if err != nil {
log.WithFields(log.Fields{"Topic": "Helper", "Route": addr, "Error": "invalid IP"}).Warn("invalid IP")
continue
Expand Down Expand Up @@ -175,7 +148,7 @@ func getIPs(v6Mask int, allIfs bool) (*[]IPNet, error) {
Warnf("unable to supernet")
continue
}
ip = &IPNet{
ip = &helper.IPNet{
IP: ipNew.IP,
Mask: ipNew.Mask,
}
Expand All @@ -185,7 +158,7 @@ func getIPs(v6Mask int, allIfs bool) (*[]IPNet, error) {
log.WithFields(log.Fields{"Topic": "Helper", "Route": ip}).Debug("handling prefix")
}

var uniqIPs []IPNet
var uniqIPs []helper.IPNet
for _, ip := range ips {
uniqIPs = append(uniqIPs, *ip)
}
Expand Down
29 changes: 25 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package main

import (
"flag"
"fmt"

"bits.linode.com/netops/lelastic/pkg/helper"
log "github.com/sirupsen/logrus"
)

Expand All @@ -28,6 +28,8 @@ func main() {
false,
"Consider all interfaces when detecting elastic IP candidates (not just loopback)",
)
rs, _ := helper.IPNetParse("2600:3c0f::/32")
flag.Var(rs, "prefix", "Route servers IPv6 prefix")

flag.Parse()

Expand All @@ -51,6 +53,25 @@ func main() {
log.WithFields(log.Fields{"Topic": "Main"}).Fatal("use either primary or secondary flag")
}

// check for ipv6
if p := rs.IP.To4(); p != nil {
log.WithFields(log.Fields{"Topic": "Main"}).Fatal("provided prefix is not ipv6. only ipv6 supported")
}

// check length of prefix provided
switch rs.Plen() {
case 32:
rs = rs.Subnet(48, uint32(*dcid))
//rs = rs.Subnet(64, 34)
fallthrough
case 48:
rs = rs.Subnet(64, 34)
case 64:
break
default:
log.WithFields(log.Fields{"Topic": "Main"}).Fatal("provided prefix Lenth not supported")
}

switch *loglevel {
case "trace":
log.SetLevel(log.TraceLevel)
Expand Down Expand Up @@ -95,9 +116,9 @@ func main() {

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 {
for i := uint32(1); i <= 4; i++ {
//rs := fmt.Sprintf("2600:3c0f:%d:34::%d", *dcid, i)
if err := c.AddRs((rs.Subnet(128, i)).IP.String()); err != nil {
log.WithFields(log.Fields{"Topic": "Neighbor", "Neighbor": rs}).Fatal("failed adding neighbor")
}
// log.WithFields(log.Fields{"Topic": "Neighbor", "Neighbor": rs}).Info("added neighbor")
Expand Down
41 changes: 41 additions & 0 deletions pkg/helper/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package helper

import (
"bytes"
"net"
)

// HumanHexConvert converts a decimal number to its *visual* equivalent in hex: i.e. 510 -> 1296 -> which is again 510 in dex print
// we're taking in a uint32 for simplicity but actually only look at 16 bit/2byte
// and we convert decimal to hex, so we actually only can handle <10000 for now, otherwise we need more than 2 byte
func HumanHexConvert(val uint32) uint32 {
//var res, exp10, exp16 uint32 = 0, 100000, 1048576
// exp10 = 10^7, exp16 = 16^7 -> wr got 4 byte/ 8 digits
var res, exp10, exp16 uint32 = 0, 10000000, 268435456

for val > 0 {
res += val / exp10 * exp16
val = val % exp10
exp10 = exp10 / 10
exp16 = exp16 / 16
}

return res
}

// ItoB converts a int to a byte array for byte operations.
func ItoB(val uint32) []byte {
r := make([]byte, (32 / 8))
l := len(r) - 1

for i := 0; i <= l; i++ {
r[l-i] = byte((val >> (8 * i)) & 0xff)
}
return r
}

// IsMask24 checks if the given IPMask is a /24 mask.
func IsMask24(mask net.IPMask) bool {
mask24 := []byte{255, 255, 255, 0}
return bytes.Equal(mask, mask24)
}
Loading