From 15c9b723d9282faacf16171cff8af25ae80e687e Mon Sep 17 00:00:00 2001 From: kishiguro Date: Tue, 15 May 2018 12:43:09 -0700 Subject: [PATCH] Update backup config and quagga status get method. --- config/command.go | 10 ++ config/gobgp.go | 40 ++++-- config/grpc.go | 6 +- config/ospf-status.go | 17 ++- config/ospf.go | 4 +- config/quagga-status.go | 35 +++--- config/quagga.go | 7 +- config/quagga_access.go | 228 +++++++++++++++++++++++++++++++++++ config/quagga_access_test.go | 53 ++++++++ config/vrf.go | 46 ++++--- quagga/config.go | 16 +++ quagga/crypt.go | 82 +++++++++++++ quagga/crypt_test.go | 27 +++++ 13 files changed, 515 insertions(+), 56 deletions(-) create mode 100644 config/quagga_access.go create mode 100644 config/quagga_access_test.go create mode 100644 quagga/crypt.go create mode 100644 quagga/crypt_test.go diff --git a/config/command.go b/config/command.go index 044e290..937ba89 100644 --- a/config/command.go +++ b/config/command.go @@ -22,6 +22,7 @@ import ( "github.com/coreswitch/cmd" "github.com/coreswitch/component" + "github.com/coreswitch/openconfigd/quagga" "github.com/coreswitch/process" ) @@ -90,6 +91,12 @@ func showIpBgpNeighbor(Args []string) (inst int, instStr string) { return } +func showQuaggaPassword(Args []string) (inst int, instStr string) { + inst = CliSuccessShow + instStr = fmt.Sprintf("quagga password is: %s", quagga.GetPasswd()) + return +} + func enableFunc(Args []string) (inst int, instStr string) { inst = CliSuccessExec instStr = "CLI_PRIVILEGE=15;_cli_refresh" @@ -355,6 +362,8 @@ func (this *CliComponent) Start() component.Component { &cmd.Param{Helps: []string{"Start", "Process"}}) mode.InstallLine("stop process WORD", stopProcess, &cmd.Param{Helps: []string{"Stop", "Process"}}) + mode.InstallLine("show quagga password", showQuaggaPassword, + &cmd.Param{Helps: []string{"Show running system information", "quagga infromation", "Show password"}}) // Link "run" command to operational node. run := mode.Parser.Lookup("run") @@ -363,6 +372,7 @@ func (this *CliComponent) Start() component.Component { Parser.InstallLine("system host-name WORD", HostnameApi) Parser.InstallLine("system etcd endpoints WORD", EtcdEndpointsApi) Parser.InstallLine("system etcd path WORD", EtcdPathApi) + Parser.InstallLine("system gobgp grpcendpoint WORD", ConfigureGobgpGrpcEndpointApi) Parser.InstallLine("interfaces interface WORD dhcp-relay-group WORD", RelayApi) TopCmd = Cmd diff --git a/config/gobgp.go b/config/gobgp.go index dbc9af6..39206be 100644 --- a/config/gobgp.go +++ b/config/gobgp.go @@ -32,9 +32,12 @@ import ( log "github.com/sirupsen/logrus" ) +const defaultGrpcEndpoint = ":50051" + var ( gobgpConfig GobgpConfig gobgpRouterId string + gobgpGrpcEndpoint string = defaultGrpcEndpoint ) type GobgpConfig struct { @@ -126,6 +129,10 @@ func (lhs *VrfRib) Equal(rhs *VrfRib) bool { return true } +func NewGobgpClient() (*client.Client, error) { + return client.New(gobgpGrpcEndpoint) +} + func GobgpRouterIdRegister(routerId string) { if gobgpRouterId == routerId { return @@ -184,7 +191,7 @@ func GobgpStaticPath(s *Route) (*table.Path, error) { } func GobgpClearVrfRib(c *VrfConfig) error { - client, err := client.New("") + client, err := NewGobgpClient() if err != nil { log.WithFields(log.Fields{ "error": err, @@ -359,7 +366,7 @@ func GobgpSetGlobal(client *client.Client, cfg *GobgpConfig) { } func GobgpSetZebraRoutine() error { - client, err := client.New("") + client, err := NewGobgpClient() if err != nil { return err } @@ -538,7 +545,7 @@ func GobgpClearGlobalPolicy(client *client.Client) { } func GobgpClearAll() { - client, err := client.New("") + client, err := NewGobgpClient() if err != nil { fmt.Println("GobgpStopServer:", err) return @@ -636,7 +643,7 @@ func GobgpUpdateVrf(client *client.Client, cfg *GobgpConfig) { func GobgpUpdate(cfg *GobgpConfig) error { fmt.Println("Updating configuration") - client, err := client.New("") + client, err := NewGobgpClient() if err != nil { return err } @@ -662,8 +669,7 @@ func GobgpUpdate(cfg *GobgpConfig) error { } func GobgpReset(cfg *GobgpConfig) error { - fmt.Println("New configuration") - client, err := client.New("") + client, err := NewGobgpClient() if err != nil { log.WithFields(log.Fields{ "error": err, @@ -855,7 +861,7 @@ func GobgpVrfRibSync(name string, old, new []VrfRib) { } fmt.Println("------") - client, err := client.New("") + client, err := NewGobgpClient() if err != nil { log.WithFields(log.Fields{ "error": err, @@ -1150,3 +1156,23 @@ func stringToCommunityValue(comStr string) uint32 { val, _ := strconv.ParseUint(elem[1], 10, 16) return uint32(asn<<16 | val) } + +func ConfigureGobgpGrpcEndpointApi(set bool, args []interface{}) { + if len(args) != 1 { + return + } + grpcEndPoint := args[0].(string) + if set { + SetGobgpGrpcEndpoint(grpcEndPoint) + } else { + ClearGobgpGrpcEndpoint() + } +} + +func SetGobgpGrpcEndpoint(grpcEndpoint string) { + gobgpGrpcEndpoint = grpcEndpoint +} + +func ClearGobgpGrpcEndpoint() { + gobgpGrpcEndpoint = defaultGrpcEndpoint +} diff --git a/config/grpc.go b/config/grpc.go index 5f8c877..a1585fa 100644 --- a/config/grpc.go +++ b/config/grpc.go @@ -302,11 +302,13 @@ func DynamicCompletion(commands []string, module string, args []string) []string } // RPC component. -type RpcComponent struct{} +type RpcComponent struct { + GrpcEndpoint string +} // RPC component start method. func (this *RpcComponent) Start() component.Component { - lis, err := net.Listen("tcp", ":2650") + lis, err := net.Listen("tcp", this.GrpcEndpoint) if err != nil { grpclog.Fatalf("failed %v", err) } diff --git a/config/ospf-status.go b/config/ospf-status.go index 6a80dea..3e773e2 100644 --- a/config/ospf-status.go +++ b/config/ospf-status.go @@ -26,7 +26,9 @@ import ( "time" "github.com/coreos/etcd/clientv3" + "github.com/coreswitch/openconfigd/quagga" "github.com/mitchellh/mapstructure" + log "github.com/sirupsen/logrus" "golang.org/x/net/context" ) @@ -128,21 +130,16 @@ func OspfStatusRoute(vrfName string, stat *OspfVrfStat) error { } return nil } - func OspfStatusNeighbor(vrfName string, stat *OspfVrfStat) error { - cmd := exec.Command("vtysh", "-c", "show ip ospf neighbor") - env := os.Environ() - env = append(env, fmt.Sprintf("VRF=%s", vrfName)) - env = append(env, "LD_PRELOAD=/usr/bin/vrf_socket.so") - cmd.Env = env - - out, err := cmd.Output() + var in []string + in = append(in, "show ip ospf neighbor\n") + out, err := VrfQuaggaGet(vrfName, "ospfd", quagga.GetPasswd(), time.Second, in) if err != nil { - fmt.Println("OspfStatusNeighbor err:", err) + log.Error("QuaggaStatusBgpSummary: VrfQuaggaGet()", err) return err } - strs := strings.Split(string(out), "\n") + strs := strings.Split(out[0], "\n") for _, str := range strs { r := regexp.MustCompile(`^[0-9]`) if r.MatchString(str) { diff --git a/config/ospf.go b/config/ospf.go index c2f3531..311ebdb 100644 --- a/config/ospf.go +++ b/config/ospf.go @@ -19,6 +19,7 @@ import ( "os" "text/template" + "github.com/coreswitch/openconfigd/quagga" "github.com/coreswitch/process" ) @@ -75,7 +76,7 @@ func (lhs *OspfArray) Equal(rhs *OspfArray) bool { } var ospfTemplate = `! -password 8 AU67iiPSXYm96 +password 8 {{passwdHash}} service password-encryption ! {{range $i, $v := .OspfArray}}{{interfaceIp2Config $v}}{{end}} @@ -164,6 +165,7 @@ func OspfExec(vrfId int, ospfArray *OspfArray) { tmpl := template.Must(template.New("ospfTmpl").Funcs(template.FuncMap{ "areaAuthentication": areaAuthentication, "interfaceIp2Config": interfaceIp2Config, + "passwdHash": quagga.GetHash, }).Parse(ospfTemplate)) tmpl.Execute(f, &TemplValue{OspfArray: ospfArray}) diff --git a/config/quagga-status.go b/config/quagga-status.go index d08f2d7..ad08a33 100644 --- a/config/quagga-status.go +++ b/config/quagga-status.go @@ -17,14 +17,14 @@ package config import ( "encoding/json" "fmt" - "os" - "os/exec" "regexp" "strconv" "strings" "time" "github.com/coreos/etcd/clientv3" + "github.com/coreswitch/openconfigd/quagga" + log "github.com/sirupsen/logrus" "golang.org/x/net/context" ) @@ -33,20 +33,16 @@ type QuaggaStat struct { } func QuaggaStatusBgpSummary(vrf string, stat *GobgpStat) error { - cmd := exec.Command("vtysh", "-c", "show ip bgp sum") - env := os.Environ() - env = append(env, fmt.Sprintf("VRF=%s", vrf)) - env = append(env, "LD_PRELOAD=/usr/bin/vrf_socket.so") - cmd.Env = env - - out, err := cmd.Output() + var in []string + in = append(in, "show ip bgp sum\n") + out, err := VrfQuaggaGet(vrf, "bgpd", quagga.GetPasswd(), time.Second, in) if err != nil { - fmt.Println("QuaggaStatusBgpSummary: err", err) + log.Error("QuaggaStatusBgpSummary: VrfQuaggaGet()", err) return err } neighbor := false - strs := strings.Split(string(out), "\n") + strs := strings.Split(out[0], "\n") for _, str := range strs { if !neighbor { r := regexp.MustCompile(`^BGP[a-z\s]+([\d\.]+).*\s([\d\.]+)$`) @@ -84,20 +80,18 @@ func QuaggaStatusBgpSummary(vrf string, stat *GobgpStat) error { } func QuaggaStatusRib(vrf string, stat *GobgpStat) error { - cmd := exec.Command("vtysh", "-c", "show ip bgp") - env := os.Environ() - env = append(env, fmt.Sprintf("VRF=%s", vrf)) - env = append(env, "LD_PRELOAD=/usr/bin/vrf_socket.so") - cmd.Env = env + var in []string + fmt.Println("QuaggaStatusRib(): neighbor", stat.Neighbor[0].Peer) - out, err := cmd.Output() + in = append(in, fmt.Sprintf("show ip bgp neighbor %s received-routes\n", stat.Neighbor[0].Peer)) + out, err := VrfQuaggaGet(vrf, "bgpd", quagga.GetPasswd(), time.Second, in) if err != nil { - fmt.Println("QuaggaStatusBgpSummary: err", err) + log.Error("QuaggaStatusBgpRib: VrfQuaggaGet()", err) return err } network := false - strs := strings.Split(string(out), "\n") + strs := strings.Split(out[0], "\n") for _, str := range strs { if !network { r := regexp.MustCompile(`^\s+Network`) @@ -154,7 +148,8 @@ func QuaggaStatusUpdate() { stats := QuaggaStat{} for vrfId, _ := range QuaggaProc { - QuaggaStatusVrf(fmt.Sprintf("vrf%d", vrfId), &stats) + vrf := fmt.Sprintf("vrf%d", vrfId) + QuaggaStatusVrf(vrf, &stats) } str := "" diff --git a/config/quagga.go b/config/quagga.go index 0646cff..358b558 100644 --- a/config/quagga.go +++ b/config/quagga.go @@ -18,8 +18,10 @@ import ( "fmt" "io/ioutil" "os" + "regexp" "github.com/coreswitch/netutil" + "github.com/coreswitch/openconfigd/quagga" "github.com/coreswitch/process" ) @@ -38,7 +40,7 @@ func QuaggaExit() { } func QuaggaDelete(vrfId int) { - fmt.Printf("[quagga]delete: vrfId %+v, Processes: %+v\n", vrfId, QuaggaProc[vrfId]) + fmt.Println("[quagga]delete: vrfId, Processes: %+v", vrfId, QuaggaProc[vrfId]) _, ok := QuaggaProc[vrfId] if ok { for _, proc := range QuaggaProc[vrfId] { @@ -56,6 +58,9 @@ func QuaggaDelete(vrfId int) { } func QuaggaExec(vrfId int, interfaceName string, configStr string) { + re := regexp.MustCompile(`password 8 ([0-9A-Za-z]{13})`) + configStr = re.ReplaceAllString(configStr, "password 8 "+quagga.GetHash()) + fmt.Println("[quagga]config: vrfId", vrfId, "Interface Name: ", interfaceName, configStr) configFileName := fmt.Sprintf("/etc/quagga/bgpd-vrf%d-%s.conf", vrfId, interfaceName) zapiSocketName := fmt.Sprintf("/var/run/zserv-vrf%d.api", vrfId) diff --git a/config/quagga_access.go b/config/quagga_access.go new file mode 100644 index 0000000..8f07118 --- /dev/null +++ b/config/quagga_access.go @@ -0,0 +1,228 @@ +// Copyright 2018 OpenConfigd Project. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package config + +import ( + "fmt" + "io/ioutil" + "net" + "os" + "os/exec" + "time" + "github.com/coreswitch/log" + "github.com/jamesharr/expect" + "github.com/vishvananda/netlink" + "golang.org/x/sys/unix" +) + +const ( + passReq = `Password:` +) + +type QuaggaAccess struct { + Exp *expect.Expect + + vrf string + addr net.IP + prog string + prompt string + fp *os.File +} + +func (qa *QuaggaAccess) Init(vrf string, addr net.IP, prog string, timeout time.Duration) error { + var err error + + qa.vrf = vrf + qa.addr = addr + qa.prog = prog + if out, err := exec.Command("hostname").Output(); err == nil { + qa.prompt = string(out[:len(out) - 1]) + "> " + } else { + log.Error("QuaggaAccess.Init(): hostname: ", err) + return err + } + + log.Info("QuaggaAccess.Init(): prompt: ", qa.prompt) + + if qa.fp, err = ioutil.TempFile("", "quagga-" + prog); err == nil { + cmd := fmt.Sprintf("#!/bin/sh\nVRF=%s LD_PRELOAD=/usr/bin/vrf_socket.so ", vrf) + cmd += fmt.Sprintf("telnet '%s' '%s'\n", addr.String(), prog) + _, err = qa.fp.WriteString(cmd) + qa.fp.Close() + if err != nil { + os.Remove(qa.fp.Name()) + log.Error("QuaggaAccess.Init(): ", err) + return err + } + if err = os.Chmod(qa.fp.Name(), 0755); err != nil { + log.Error("QuaggaAccess.Init(): os.Chmod(): ", err) + os.Remove(qa.fp.Name()) + qa.fp = nil + return err + } + if qa.Exp, err = expect.Spawn(qa.fp.Name()); err == nil { + qa.Exp.SetTimeout(timeout) + } else { + log.Error("QuaggaAccess.Init(): expect.Spawn(): ", err) + os.Remove(qa.fp.Name()) + qa.Exp = nil + qa.fp = nil + } + } else { + log.Error("QuaggaAccess.Init(): ioutil.Tempfile(): ", err) + if qa.fp != nil { + os.Remove(qa.fp.Name()) + qa.fp = nil + } + } + return err +} + +func (qa *QuaggaAccess) GetTempFileName() string { + if qa.fp == nil { + return "" + } + return qa.fp.Name() +} + +func (qa *QuaggaAccess) Close() { + os.Remove(qa.fp.Name()) + qa.Exp.Close() + qa.Exp = nil + qa.fp = nil +} + +func (qa *QuaggaAccess) Auth(pass string) error { + qa.Exp.Send("\n") + if _, err := qa.Exp.Expect(passReq); err != nil { + log.Errorf("Error: QuaggaAccess.Auth(): %s", passReq) + return err + } + qa.Exp.Send(pass + "\n") + if _, err := qa.Exp.Expect(qa.prompt); err != nil { + log.Errorf("Error: QuaggaAccess.Auth(): password") + return err + } + return nil +} + +func (qa *QuaggaAccess) Batch(in []string) ([]string) { + var out []string + + for _, s := range in { + qa.Exp.Send(s) + if m, err := qa.Exp.Expect(qa.prompt); err == nil { + log.Info("QuaggaAccess.Batch(): ", m.Before) + out = append(out, m.Before) + } else { + out = append(out, "") + } + } + return out +} + +func VrfIdByName(vrf string) (int, error) { + if l, err := netlink.LinkByName(vrf); err == nil { + switch l := l.(type) { + case *netlink.Vrf: + return int(l.Table), nil + default: + return -1, fmt.Errorf("VrfIdByName(%s): not a VRF", vrf) + } + } else { + return -1, err + } +} + +func VrfGetRoutes(vrf string, family int, tableType int) ([]netlink.Route, error) { + if tid, err := VrfIdByName(vrf); err == nil { + routeFilter := &netlink.Route { + Table: tid, + Type: tableType, + Protocol: unix.RTPROT_KERNEL, + } + filterMask := netlink.RT_FILTER_TABLE | + netlink.RT_FILTER_TYPE | + netlink.RT_FILTER_PROTOCOL + return netlink.RouteListFiltered(family, routeFilter, filterMask) + } else { + log.Errorf("VrfGetRoutes(%s): %v", vrf, err) + return nil, err + } +} + +func VrfGetIPv4localRoutes(vrf string) ([]netlink.Route, error) { + return VrfGetRoutes(vrf, netlink.FAMILY_V4, unix.RTN_LOCAL) +} + +func VrfQuaggaAccess( + vrf string, // VRF name + prog string, // program name: bgpd, ospfd, etc. + pass string, // passowrd + timeout time.Duration) (*QuaggaAccess, error) { + // timeout + log.Infof("VrfQuaggaAccess(%v, %v, %v, %v)", vrf, prog, pass, timeout) + + if routes, err := VrfGetIPv4localRoutes(vrf); err == nil { + for _, route := range routes { + if route.Dst != nil { + log.Infof("VrfQuaggaAccess(%v)", route.Dst.String()) + if ones, _ := route.Dst.Mask.Size(); ones == 32 { + var qa QuaggaAccess + + if err := qa.Init(vrf, route.Dst.IP, prog, timeout); err == nil { + //qa.Exp.SetLogger(expect.StderrLogger()) + if err := qa.Auth(pass); err == nil { + return &qa, nil + } else { + log.Warn("VrfQuaggaAccess(): auth failure") + qa.Close() + return nil, fmt.Errorf("VrfQuaggaAccess(): auth failure") + } + } else { + s := "VrfQuaggaAccess(): QuaggaAccess.Init(%v, %v, %v, %v)" + log.Errorf(s, vrf, route.Dst.IP, prog, timeout) + } + } else { + log.Warnf(" mask is not 32: %v\n", route.Dst.Mask) + continue + } + } + } + return nil, err + } else { + log.Errorf("VrfQuaggaAccess(%v): VrfGetIPv4localRoutes(): %v", vrf, err) + return nil, err + } +} + +func VrfQuaggaGet( + vrf string, // VRF name + prog string, // program name: bgpd, ospfd, etc. + pass string, // passowrd + timeout time.Duration, // timeout + in []string) ([]string, error) { + // array of input string + + if qa, err := VrfQuaggaAccess(vrf, prog, pass, timeout); qa != nil { + + out := qa.Batch(in) + qa.Close() + return out, err + } else { + log.Error("VrfQuaggaGet(): VrfQuaggaAccess()", err) + return nil, err + } +} diff --git a/config/quagga_access_test.go b/config/quagga_access_test.go new file mode 100644 index 0000000..7d8ed1c --- /dev/null +++ b/config/quagga_access_test.go @@ -0,0 +1,53 @@ +// Copyright 2018 OpenConfigd Project. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package config + +import ( + "fmt" + "io/ioutil" + "net" + "testing" + "time" +) + +func TestQuaggaInit(t *testing.T) { + var ( + qa QuaggaAccess + vrf string = "foobar" + addr net.IP = net.ParseIP("127.0.0.1") + prog string = "bgpd" + tm time.Duration = time.Second + cmd string = `#!/bin/sh +VRF=%s LD_PRELOAD=/usr/bin/vrf_socket.so telnet '%s' '%s' +` + ) + + cmd = fmt.Sprintf(cmd, vrf, addr.String(), prog) + + if err := qa.Init(vrf, addr, prog, tm); err != nil { + t.Fatalf("QuaggaAccess.Init(): %v\n", err) + } + if dat, err := ioutil.ReadFile(qa.GetTempFileName()); err == nil { + qa.Close() + file := string(dat) + if cmd != file { + t.Fail() + t.Logf("cmd:\n%v", cmd) + t.Fatalf("\nfile:\n%v", file) + } + } else { + t.Fatalf("ReadFile(%s)\n", qa.GetTempFileName()) + } +} diff --git a/config/vrf.go b/config/vrf.go index 98bf273..cbcebf1 100644 --- a/config/vrf.go +++ b/config/vrf.go @@ -78,22 +78,28 @@ type QuaggaBgp struct { Interface string `mapstructure:"interface" json:"interface,omitempty"` } +type PriorityConfig struct { + Priority string `mapstructure:"priority" json:"priority,omitempty"` +} + type VrfsConfig struct { - Name string `mapstructure:"name" json:"name,omitempty"` - Id int `mapstructure:"vrf_id" json:"vrf_id,omitempty"` - Rd string `mapstructure:"rd" json:"rd,omitempty"` - RtImport string `mapstructure:"rt_import" json:"rt_import,omitempty"` - RtExport string `mapstructure:"rt_export" json:"rt_export,omitempty"` - RtBoth string `mapstructure:"rt_both" json:"rt_both,omitempty"` - VrfRibs []VrfRib `mapstructure:"ribs" json:"ribs,omitempty"` - Hubs []Hub `mapstructure:"hubs" json:"hubs,omitempty"` - HubNode string `mapstructure:"hub_node" json:"hub_node,omitempty"` - Interfaces Interfaces `mapstructure:"interfaces" json:"interfaces,omitempty"` - Vrrp []Vrrp `mapstructure:"vrrp" json:"vrrp,omitempty"` - Dhcp Dhcp `mapstructure:"dhcp" json:"dhcp,omitempty"` - Static Static `mapstructure:"static" json:"static,omitempty"` - Bgp []QuaggaBgp `mapstructure:"bgp" json:"bgp,omitempty"` - Ospf OspfArray `mapstructure:"ospf" json:"ospf,omitempty"` + Name string `mapstructure:"name" json:"name,omitempty"` + Id int `mapstructure:"vrf_id" json:"vrf_id,omitempty"` + Rd string `mapstructure:"rd" json:"rd,omitempty"` + RtImport string `mapstructure:"rt_import" json:"rt_import,omitempty"` + RtExport string `mapstructure:"rt_export" json:"rt_export,omitempty"` + RtBoth string `mapstructure:"rt_both" json:"rt_both,omitempty"` + VrfRibs []VrfRib `mapstructure:"ribs" json:"ribs,omitempty"` + Hubs []Hub `mapstructure:"hubs" json:"hubs,omitempty"` + HubNode string `mapstructure:"hub_node" json:"hub_node,omitempty"` + Interfaces Interfaces `mapstructure:"interfaces" json:"interfaces,omitempty"` + Vrrp []Vrrp `mapstructure:"vrrp" json:"vrrp,omitempty"` + Dhcp Dhcp `mapstructure:"dhcp" json:"dhcp,omitempty"` + Static Static `mapstructure:"static" json:"static,omitempty"` + Bgp []QuaggaBgp `mapstructure:"bgp" json:"bgp,omitempty"` + Ospf OspfArray `mapstructure:"ospf" json:"ospf,omitempty"` + Pair PriorityConfig `mapstructure:"pair" json:"pair,omitempty"` + Gateway PriorityConfig `mapstructure:"gateway" json:"gateway,omitempty"` } var EtcdVrfMap = map[int]VrfsConfig{} @@ -401,6 +407,16 @@ func ProcessVrfUpdate(vrfId int, vrf *VrfsConfig) { func EtcdVrfSync(vrfId int, vrf *VrfsConfig) { ExecLine(fmt.Sprintf("set vrf name vrf%d", vrfId)) + if vrf.Pair.Priority == "backup" { + ExecLine(fmt.Sprintf("set vrf name vrf%d pair-backup", vrfId)) + } else { + ExecLine(fmt.Sprintf("delete vrf name vrf%d pair-backup", vrfId)) + } + if vrf.Gateway.Priority == "backup" { + ExecLine(fmt.Sprintf("set vrf name vrf%d region-backup", vrfId)) + } else { + ExecLine(fmt.Sprintf("delete vrf name vrf%d region-backup", vrfId)) + } Commit() ProcessVrfUpdate(vrfId, vrf) Commit() diff --git a/quagga/config.go b/quagga/config.go index d37fb2f..c3dcb57 100644 --- a/quagga/config.go +++ b/quagga/config.go @@ -2,13 +2,29 @@ package quagga import ( "fmt" + "github.com/coreswitch/cmd" ) var ( configParser *cmd.Node + quaggaPasswd string + quaggaHash string ) +func init() { + quaggaPasswd = GeneratePasswd() + quaggaHash = Crypt(quaggaPasswd) +} + +func GetPasswd() string { + return quaggaPasswd +} + +func GetHash() string { + return quaggaHash +} + /* tag: type: u32 diff --git a/quagga/crypt.go b/quagga/crypt.go new file mode 100644 index 0000000..fb216bb --- /dev/null +++ b/quagga/crypt.go @@ -0,0 +1,82 @@ +// Copyright 2018 OpenConfigd Project. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package quagga + +import ( + "math/rand" + "sync" + "time" + "unsafe" +) + +/* +#include +#include +#include + +#cgo LDFLAGS: -lcrypt +*/ +import "C" + +var ( + letters = []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + passLetters = append(letters, []rune("!#$%&'()*+,-./:;<=>?@`{|}")...) + initialized = false +) + + +func randSeq(n int) string { + b := make([]rune, n) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + return string(b) +} + +func generateHash(key, salt string) string { + var mutex = &sync.Mutex{} + + cKey := C.CString(key) + defer C.free(unsafe.Pointer(cKey)) + cSalt := C.CString(salt) + defer C.free(unsafe.Pointer(cSalt)) + mutex.Lock() + cHash, _ := C.crypt(cKey, cSalt) + mutex.Unlock() + return C.GoString(cHash) +} + +func Crypt(passwd string) string { + return generateHash(passwd, randSeq(5)) +} + +func Verify(passwd, hash string) bool { + if generateHash(passwd, hash) == hash { + return true + } + return false +} + +func GeneratePasswd() string { + if initialized == false { + rand.Seed(time.Now().UnixNano()) + initialized = true + } + b := make([]rune, 8) + for i := range b { + b[i] = passLetters[rand.Intn(len(passLetters))] + } + return string(b) +} diff --git a/quagga/crypt_test.go b/quagga/crypt_test.go new file mode 100644 index 0000000..a873492 --- /dev/null +++ b/quagga/crypt_test.go @@ -0,0 +1,27 @@ +// Copyright 2018 OpenConfigd Project. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package quagga + +import ( + "testing" +) + +func TestCrypt(t *testing.T) { + passwd := GeneratePasswd() + hash := Crypt(passwd) + if Verify(passwd, hash) != true { + t.Fatalf("passwd: %s, hash: %s", passwd, hash) + } +}