-
Notifications
You must be signed in to change notification settings - Fork 4
/
bgp.go
155 lines (139 loc) · 5.22 KB
/
bgp.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package bgp
import (
"context"
"fmt"
"os/exec"
"strings"
"time"
"github.com/sirupsen/logrus"
)
// The Controller provides an interface for configuring BGP.
// Feed a controller a list of VIP addresses that require configuration,
// and it will manage the whole add/remove/change process.
type Controller interface {
// Get returns the addresses currently in the BGP RIB
Get(ctx context.Context) ([]string, error)
// Set receives a list of ip addresses and performs the necessary
// steps to configure each address in BGP.
Set(ctx context.Context, addresses, configuredAddresses []string, communities []string) error
// SetV6 set, for v6. Very similar to above function
SetV6(ctx context.Context, addresses []string, communities []string) error
// Teardown removes all addresses from BGP.
// Perhaps this will never be applied.
Teardown(context.Context) error
}
type GoBGPDController struct {
commandPath string
logger logrus.FieldLogger
}
// Get fetches a list of configured addresses in gobgp
func (g *GoBGPDController) Get(ctx context.Context) ([]string, error) {
configuredAddrs := []string{}
// set a timeout context for this command
cmdCtx, cmdCtxCancel := context.WithTimeout(ctx, time.Second*20)
defer cmdCtxCancel()
args := []string{"global", "rib", "-a", "ipv4"}
cmd := exec.CommandContext(cmdCtx, g.commandPath, args...)
out, err := cmd.CombinedOutput()
if err != nil {
return configuredAddrs, fmt.Errorf("could not return list of configured addresses from gobgp: %v", err)
}
return parseRIBOutput(out), nil
}
func parseRIBOutput(output []byte) []string {
outputAsList := strings.Split(string(output), "\n")
addresses := []string{}
// start at 1 to skip columnar format line
for i := 1; i < len(outputAsList); i++ {
out := outputAsList[i]
fields := strings.Fields(out)
if len(fields) > 2 {
trimCidr := strings.Replace(fields[1], "/32", "", 1)
addresses = append(addresses, trimCidr)
}
}
return addresses
}
// Set configures the ipvsadm rules for ipv4 with an optional set of community strings. If a community is not set
// or blank, then it will not be used.
func (g *GoBGPDController) Set(ctx context.Context, addresses, configuredAddresses []string, communities []string) error {
// quick check to see if this is already configured. If so, no need to push
// another network update
toAdd := []string{}
for _, addr := range addresses {
var found bool
for _, configured := range configuredAddresses {
if addr == configured {
found = true
break
}
}
if !found {
toAdd = append(toAdd, addr)
}
}
// $PATH/gobgp global rib -a ipv4 add 10.54.213.148/32
for _, address := range toAdd {
cidr := address + "/32"
// g.logger.Debugf("Advertising route to %s", cidr)
args := []string{"global", "rib", "-a", "ipv4", "add", cidr}
// if communities are supplied, add it here as a community
if len(communities) > 0 {
// add community cli option
args = append(args, "community")
// add each community with a comma after it like so: 100:100:100,200:200:200
for _, c := range communities {
args = append(args, c, ",")
}
// remove any trailing commas on the communities arguments
if args[len(args)-1] == "," {
args = args[:len(args)-1]
}
}
// set a timeout context for this command
cmdCtx, cmdCtxCancel := context.WithTimeout(ctx, time.Second*20)
defer cmdCtxCancel()
if err := exec.CommandContext(cmdCtx, g.commandPath, args...).Run(); err != nil {
return fmt.Errorf("adding route %s with %s: %s", cidr, strings.Join(append([]string{g.commandPath}, args...), " "), err)
}
}
return nil
}
// SetV6 set ipvsadm rule with ipv6 syntax. If a blank community slice is supplied, no community is advertised.
func (g *GoBGPDController) SetV6(ctx context.Context, addresses []string, communities []string) error {
// $PATH/gobgp global rib -a ipv6 add [2001:558:1044:1ae:10ad:ba1a:0000:0007]/128
for _, address := range addresses {
cidr := address + "/128"
// g.logger.Debugf("Advertising route to %s", cidr)
args := []string{"global", "rib", "-a", "ipv6", "add", cidr}
// if communities are supplied, add it here as a community
if len(communities) > 0 {
// add community cli option
args = append(args, "community")
// add each community with a comma after it like so: 100:100:100,200:200:200
for _, c := range communities {
args = append(args, c, ",")
}
// remove any trailing commas on the communities arguments
if args[len(args)-1] == "," {
args = args[:len(args)-1]
}
}
// set a timeout context for this command
cmdCtx, cmdCtxCancel := context.WithTimeout(ctx, time.Second*20)
defer cmdCtxCancel()
if err := exec.CommandContext(cmdCtx, g.commandPath, args...).Run(); err != nil {
return fmt.Errorf("adding route %s with %s: %s", cidr, strings.Join(append([]string{g.commandPath}, args...), " "), err)
}
}
return nil
}
func (g *GoBGPDController) Teardown(context.Context) error {
// I suspect that we don't want to remove all addresses' routes,
// but rather one at a time, if any at all.
g.logger.Info("Tear down: Let's leave things how they are.")
return nil
}
func NewBGPDController(executablePath string, logger logrus.FieldLogger) *GoBGPDController {
return &GoBGPDController{commandPath: executablePath, logger: logger}
}