Files
openlan/pkg/switch/bgp_linux.go
Daniel Ding db26f4881f
Some checks failed
Coverage CI / build (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled
Ubuntu CI / build (push) Has been cancelled
fea: state for bgp neighbor.
2025-09-04 16:39:49 +08:00

251 lines
5.2 KiB
Go

package cswitch
import (
"os/exec"
"strings"
"text/template"
"github.com/luscis/openlan/pkg/api"
co "github.com/luscis/openlan/pkg/config"
"github.com/luscis/openlan/pkg/libol"
"github.com/luscis/openlan/pkg/schema"
)
const (
BgpBin = "/var/openlan/script/frr-client"
BgpEtc = "/etc/frr/frr.conf"
)
type BgpWorker struct {
*WorkerImpl
spec *co.BgpSpecifies
}
func NewBgpWorker(c *co.Network) *BgpWorker {
w := &BgpWorker{
WorkerImpl: NewWorkerApi(c),
}
w.spec, _ = c.Specifies.(*co.BgpSpecifies)
return w
}
var BgpTmpl = `! GENERATE BY OPENALN
!
service integrated-vtysh-config
{{- if .RouterId }}
router bgp {{ .LocalAs }}
bgp router-id {{ .RouterId }}
no bgp default ipv4-unicast
{{- range .Neighbors }}
neighbor {{ .Address }} remote-as {{ .RemoteAs }}
{{- if .Password }}
neighbor {{ .Address }} password {{ .Password }}
{{- end }}
{{- end }}
!
address-family ipv4 unicast
redistribute connected
redistribute kernel
{{- range .Neighbors }}
neighbor {{ .Address }} activate
neighbor {{ .Address }} route-map {{ .Address }}-in in
neighbor {{ .Address }} route-map {{ .Address }}-out out
{{- end }}
exit-address-family
!
{{- range $nei := .Neighbors }}
{{- range $seq, $prefix := .Advertis }}
ip prefix-list {{ $nei.Address }}-out seq {{ inc $seq }} permit {{ $prefix }} le 32
{{- end }}
ip prefix-list {{ $nei.Address }}-out seq 65535 deny any
{{- range $seq, $prefix := .Receives }}
ip prefix-list {{ $nei.Address }}-in seq {{ inc $seq }} permit {{ $prefix }} le 32
{{- end }}
ip prefix-list {{ $nei.Address }}-in seq 65535 deny any
{{- end }}
!
{{- range .Neighbors }}
route-map {{ .Address }}-in permit 10
match ip address prefix-list {{ .Address }}-in
!
{{- end }}
{{- range .Neighbors }}
route-map {{ .Address }}-out permit 10
match ip address prefix-list {{ .Address }}-out
!
{{- end }}
{{- end }}
!
`
func (w *BgpWorker) Initialize() {
w.out.Info("BgpWorker.Initialize")
}
func (w *BgpWorker) save() {
file := BgpEtc
out, err := libol.CreateFile(file)
if err != nil || out == nil {
return
}
defer out.Close()
maps := template.FuncMap{
"inc": func(i int) int {
return i + 1
},
}
if obj, err := template.New("main").Funcs(maps).Parse(BgpTmpl); err != nil {
w.out.Warn("BgpWorker.save: %s", err)
} else {
if err := obj.Execute(out, w.spec); err != nil {
w.out.Warn("BgpWorker.save: %s", err)
}
}
}
func (w *BgpWorker) reload() {
w.save()
cmd := exec.Command(BgpBin, "--reload")
if err := cmd.Run(); err != nil {
w.out.Warn("BgpWorker.reload: %s", err)
return
}
}
func (w *BgpWorker) Start(v api.Switcher) {
w.uuid = v.UUID()
w.out.Info("BgpWorker.Start")
w.reload()
}
func (w *BgpWorker) Stop() {
w.out.Info("BgpWorker.Stop")
}
func (w *BgpWorker) Enable(data schema.Bgp) {
w.spec.LocalAs = data.LocalAs
w.spec.RouterId = data.RouterId
w.reload()
}
func (w *BgpWorker) Disable() {
w.spec.RouterId = ""
w.spec.LocalAs = 0
w.reload()
}
func (w *BgpWorker) Get() *schema.Bgp {
data := &schema.Bgp{
LocalAs: w.spec.LocalAs,
RouterId: w.spec.RouterId,
}
show := map[string]struct {
State string `json:"state"`
}{}
out, err := exec.Command(BgpBin, "--show-neighbors").CombinedOutput()
if err == nil {
if err := libol.Unmarshal(&show, out); err != nil {
w.out.Warn("BgpWorker.Get.Status: %s", err)
}
} else {
w.out.Warn("BgpWorker.Get.Status: %s", err)
}
for _, nei := range w.spec.Neighbors {
obj := schema.BgpNeighbor{
Address: nei.Address,
RemoteAs: nei.RemoteAs,
Password: nei.Password,
Receives: nei.Receives,
Advertis: nei.Advertis,
}
if state, ok := show[nei.Address]; ok {
obj.State = strings.ToLower(state.State)
}
data.Neighbors = append(data.Neighbors, obj)
}
return data
}
func (w *BgpWorker) Reload(v api.Switcher) {
w.Stop()
w.Initialize()
w.Start(v)
}
func (w *BgpWorker) AddNeighbor(data schema.BgpNeighbor) {
obj := &co.BgpNeighbor{
Address: data.Address,
RemoteAs: data.RemoteAs,
Password: data.Password,
}
obj.Correct()
if nei, _ := w.spec.FindNeighbor(obj); nei == nil {
w.spec.AddNeighbor(obj)
} else {
nei.RemoteAs = data.RemoteAs
nei.Password = data.Password
}
w.reload()
}
func (w *BgpWorker) DelNeighbor(data schema.BgpNeighbor) {
obj := &co.BgpNeighbor{
Address: data.Address,
RemoteAs: data.RemoteAs,
}
obj.Correct()
if _, removed := w.spec.DelNeighbor(obj); removed {
w.reload()
}
}
func (w *BgpWorker) AddReceives(data schema.BgpPrefix) {
obj := &co.BgpNeighbor{
Address: data.Neighbor,
}
if nei, _ := w.spec.FindNeighbor(obj); nei != nil {
if nei.AddReceives(data.Prefix) {
w.reload()
}
}
}
func (w *BgpWorker) DelReceives(data schema.BgpPrefix) {
obj := &co.BgpNeighbor{
Address: data.Neighbor,
}
if nei, _ := w.spec.FindNeighbor(obj); nei != nil {
if nei.DelReceives(data.Prefix) {
w.reload()
}
}
}
func (w *BgpWorker) AddAdvertis(data schema.BgpPrefix) {
obj := &co.BgpNeighbor{
Address: data.Neighbor,
}
if nei, _ := w.spec.FindNeighbor(obj); nei != nil {
if nei.AddAdvertis(data.Prefix) {
w.reload()
}
}
}
func (w *BgpWorker) DelAdvertis(data schema.BgpPrefix) {
obj := &co.BgpNeighbor{
Address: data.Neighbor,
}
if nei, _ := w.spec.FindNeighbor(obj); nei != nil {
if nei.DelAdvertis(data.Prefix) {
w.reload()
}
}
}