summaryrefslogtreecommitdiffhomepage
path: root/cmd/gobgpd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/gobgpd')
-rw-r--r--cmd/gobgpd/main.go450
-rw-r--r--cmd/gobgpd/util.go103
-rw-r--r--cmd/gobgpd/util_windows.go24
3 files changed, 577 insertions, 0 deletions
diff --git a/cmd/gobgpd/main.go b/cmd/gobgpd/main.go
new file mode 100644
index 00000000..6f1a364d
--- /dev/null
+++ b/cmd/gobgpd/main.go
@@ -0,0 +1,450 @@
+//
+// Copyright (C) 2014-2017 Nippon Telegraph and Telephone Corporation.
+//
+// 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 main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ _ "net/http/pprof"
+ "os"
+ "os/signal"
+ "runtime"
+ "syscall"
+
+ "github.com/golang/protobuf/ptypes/any"
+ "github.com/jessevdk/go-flags"
+ "github.com/kr/pretty"
+ log "github.com/sirupsen/logrus"
+ "golang.org/x/net/context"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/credentials"
+
+ api "github.com/osrg/gobgp/api"
+ "github.com/osrg/gobgp/internal/pkg/config"
+ "github.com/osrg/gobgp/internal/pkg/table"
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+ "github.com/osrg/gobgp/pkg/server"
+)
+
+var version = "master"
+
+func marshalRouteTargets(l []string) ([]*any.Any, error) {
+ rtList := make([]*any.Any, 0, len(l))
+ for _, rtString := range l {
+ rt, err := bgp.ParseRouteTarget(rtString)
+ if err != nil {
+ return nil, err
+ }
+ rtList = append(rtList, api.MarshalRT(rt))
+ }
+ return rtList, nil
+}
+
+func main() {
+ sigCh := make(chan os.Signal, 1)
+ signal.Notify(sigCh, syscall.SIGTERM)
+
+ var opts struct {
+ ConfigFile string `short:"f" long:"config-file" description:"specifying a config file"`
+ ConfigType string `short:"t" long:"config-type" description:"specifying config type (toml, yaml, json)" default:"toml"`
+ LogLevel string `short:"l" long:"log-level" description:"specifying log level"`
+ LogPlain bool `short:"p" long:"log-plain" description:"use plain format for logging (json by default)"`
+ UseSyslog string `short:"s" long:"syslog" description:"use syslogd"`
+ Facility string `long:"syslog-facility" description:"specify syslog facility"`
+ DisableStdlog bool `long:"disable-stdlog" description:"disable standard logging"`
+ CPUs int `long:"cpus" description:"specify the number of CPUs to be used"`
+ GrpcHosts string `long:"api-hosts" description:"specify the hosts that gobgpd listens on" default:":50051"`
+ GracefulRestart bool `short:"r" long:"graceful-restart" description:"flag restart-state in graceful-restart capability"`
+ Dry bool `short:"d" long:"dry-run" description:"check configuration"`
+ PProfHost string `long:"pprof-host" description:"specify the host that gobgpd listens on for pprof" default:"localhost:6060"`
+ PProfDisable bool `long:"pprof-disable" description:"disable pprof profiling"`
+ TLS bool `long:"tls" description:"enable TLS authentication for gRPC API"`
+ TLSCertFile string `long:"tls-cert-file" description:"The TLS cert file"`
+ TLSKeyFile string `long:"tls-key-file" description:"The TLS key file"`
+ Version bool `long:"version" description:"show version number"`
+ }
+ _, err := flags.Parse(&opts)
+ if err != nil {
+ os.Exit(1)
+ }
+
+ if opts.Version {
+ fmt.Println("gobgpd version", version)
+ os.Exit(0)
+ }
+
+ if opts.CPUs == 0 {
+ runtime.GOMAXPROCS(runtime.NumCPU())
+ } else {
+ if runtime.NumCPU() < opts.CPUs {
+ log.Errorf("Only %d CPUs are available but %d is specified", runtime.NumCPU(), opts.CPUs)
+ os.Exit(1)
+ }
+ runtime.GOMAXPROCS(opts.CPUs)
+ }
+
+ if !opts.PProfDisable {
+ go func() {
+ log.Println(http.ListenAndServe(opts.PProfHost, nil))
+ }()
+ }
+
+ switch opts.LogLevel {
+ case "debug":
+ log.SetLevel(log.DebugLevel)
+ case "info":
+ log.SetLevel(log.InfoLevel)
+ default:
+ log.SetLevel(log.InfoLevel)
+ }
+
+ if opts.DisableStdlog {
+ log.SetOutput(ioutil.Discard)
+ } else {
+ log.SetOutput(os.Stdout)
+ }
+
+ if opts.UseSyslog != "" {
+ if err := addSyslogHook(opts.UseSyslog, opts.Facility); err != nil {
+ log.Error("Unable to connect to syslog daemon, ", opts.UseSyslog)
+ }
+ }
+
+ if opts.LogPlain {
+ if opts.DisableStdlog {
+ log.SetFormatter(&log.TextFormatter{
+ DisableColors: true,
+ })
+ }
+ } else {
+ log.SetFormatter(&log.JSONFormatter{})
+ }
+
+ configCh := make(chan *config.BgpConfigSet)
+ if opts.Dry {
+ go config.ReadConfigfileServe(opts.ConfigFile, opts.ConfigType, configCh)
+ c := <-configCh
+ if opts.LogLevel == "debug" {
+ pretty.Println(c)
+ }
+ os.Exit(0)
+ }
+
+ log.Info("gobgpd started")
+ bgpServer := server.NewBgpServer()
+ go bgpServer.Serve()
+
+ var grpcOpts []grpc.ServerOption
+ if opts.TLS {
+ creds, err := credentials.NewServerTLSFromFile(opts.TLSCertFile, opts.TLSKeyFile)
+ if err != nil {
+ log.Fatalf("Failed to generate credentials: %v", err)
+ }
+ grpcOpts = []grpc.ServerOption{grpc.Creds(creds)}
+ }
+ // start grpc Server
+ apiServer := server.NewServer(bgpServer, grpc.NewServer(grpcOpts...), opts.GrpcHosts)
+ go func() {
+ if err := apiServer.Serve(); err != nil {
+ log.Fatalf("failed to listen grpc port: %s", err)
+ }
+ }()
+
+ if opts.ConfigFile != "" {
+ go config.ReadConfigfileServe(opts.ConfigFile, opts.ConfigType, configCh)
+ }
+
+ loop := func() {
+ var c *config.BgpConfigSet
+ for {
+ select {
+ case <-sigCh:
+ apiServer.Shutdown(context.Background(), &api.ShutdownRequest{})
+ return
+ case newConfig := <-configCh:
+ var added, deleted, updated []config.Neighbor
+ var addedPg, deletedPg, updatedPg []config.PeerGroup
+ var updatePolicy bool
+
+ if c == nil {
+ c = newConfig
+ if _, err := apiServer.StartServer(context.Background(), &api.StartServerRequest{
+ Global: server.NewGlobalFromConfigStruct(&c.Global),
+ }); err != nil {
+ log.Fatalf("failed to set global config: %s", err)
+ }
+
+ if newConfig.Zebra.Config.Enabled {
+ tps := c.Zebra.Config.RedistributeRouteTypeList
+ l := make([]string, 0, len(tps))
+ for _, t := range tps {
+ l = append(l, string(t))
+ }
+ if _, err := apiServer.EnableZebra(context.Background(), &api.EnableZebraRequest{
+ Url: c.Zebra.Config.Url,
+ RouteTypes: l,
+ Version: uint32(c.Zebra.Config.Version),
+ NexthopTriggerEnable: c.Zebra.Config.NexthopTriggerEnable,
+ NexthopTriggerDelay: uint32(c.Zebra.Config.NexthopTriggerDelay),
+ }); err != nil {
+ log.Fatalf("failed to set zebra config: %s", err)
+ }
+ }
+
+ if len(newConfig.Collector.Config.Url) > 0 {
+ if _, err := apiServer.AddCollector(context.Background(), &api.AddCollectorRequest{
+ Url: c.Collector.Config.Url,
+ DbName: c.Collector.Config.DbName,
+ TableDumpInterval: c.Collector.Config.TableDumpInterval,
+ }); err != nil {
+ log.Fatalf("failed to set collector config: %s", err)
+ }
+ }
+
+ for _, c := range newConfig.RpkiServers {
+ if _, err := apiServer.AddRpki(context.Background(), &api.AddRpkiRequest{
+ Address: c.Config.Address,
+ Port: c.Config.Port,
+ Lifetime: c.Config.RecordLifetime,
+ }); err != nil {
+ log.Fatalf("failed to set rpki config: %s", err)
+ }
+ }
+ for _, c := range newConfig.BmpServers {
+ if _, err := apiServer.AddBmp(context.Background(), &api.AddBmpRequest{
+ Address: c.Config.Address,
+ Port: c.Config.Port,
+ Type: api.AddBmpRequest_MonitoringPolicy(c.Config.RouteMonitoringPolicy.ToInt()),
+ }); err != nil {
+ log.Fatalf("failed to set bmp config: %s", err)
+ }
+ }
+ for _, vrf := range newConfig.Vrfs {
+ rd, err := bgp.ParseRouteDistinguisher(vrf.Config.Rd)
+ if err != nil {
+ log.Fatalf("failed to load vrf rd config: %s", err)
+ }
+
+ importRtList, err := marshalRouteTargets(vrf.Config.ImportRtList)
+ if err != nil {
+ log.Fatalf("failed to load vrf import rt config: %s", err)
+ }
+ exportRtList, err := marshalRouteTargets(vrf.Config.ExportRtList)
+ if err != nil {
+ log.Fatalf("failed to load vrf export rt config: %s", err)
+ }
+
+ if _, err := apiServer.AddVrf(context.Background(), &api.AddVrfRequest{
+ Vrf: &api.Vrf{
+ Name: vrf.Config.Name,
+ Rd: api.MarshalRD(rd),
+ Id: uint32(vrf.Config.Id),
+ ImportRt: importRtList,
+ ExportRt: exportRtList,
+ },
+ }); err != nil {
+ log.Fatalf("failed to set vrf config: %s", err)
+ }
+ }
+ for _, c := range newConfig.MrtDump {
+ if len(c.Config.FileName) == 0 {
+ continue
+ }
+ if _, err := apiServer.EnableMrt(context.Background(), &api.EnableMrtRequest{
+ DumpType: int32(c.Config.DumpType.ToInt()),
+ Filename: c.Config.FileName,
+ Interval: c.Config.DumpInterval,
+ }); err != nil {
+ log.Fatalf("failed to set mrt config: %s", err)
+ }
+ }
+ p := config.ConfigSetToRoutingPolicy(newConfig)
+ rp, err := server.NewAPIRoutingPolicyFromConfigStruct(p)
+ if err != nil {
+ log.Warn(err)
+ } else {
+ apiServer.UpdatePolicy(context.Background(), &api.UpdatePolicyRequest{
+ Sets: rp.DefinedSet,
+ Policies: rp.PolicyDefinition,
+ })
+ }
+
+ added = newConfig.Neighbors
+ addedPg = newConfig.PeerGroups
+ if opts.GracefulRestart {
+ for i, n := range added {
+ if n.GracefulRestart.Config.Enabled {
+ added[i].GracefulRestart.State.LocalRestarting = true
+ }
+ }
+ }
+
+ } else {
+ addedPg, deletedPg, updatedPg = config.UpdatePeerGroupConfig(c, newConfig)
+ added, deleted, updated = config.UpdateNeighborConfig(c, newConfig)
+ updatePolicy = config.CheckPolicyDifference(config.ConfigSetToRoutingPolicy(c), config.ConfigSetToRoutingPolicy(newConfig))
+
+ if updatePolicy {
+ log.Info("Policy config is updated")
+ p := config.ConfigSetToRoutingPolicy(newConfig)
+ rp, err := server.NewAPIRoutingPolicyFromConfigStruct(p)
+ if err != nil {
+ log.Warn(err)
+ } else {
+ apiServer.UpdatePolicy(context.Background(), &api.UpdatePolicyRequest{
+ Sets: rp.DefinedSet,
+ Policies: rp.PolicyDefinition,
+ })
+ }
+ }
+ // global policy update
+ if !newConfig.Global.ApplyPolicy.Config.Equal(&c.Global.ApplyPolicy.Config) {
+ a := newConfig.Global.ApplyPolicy.Config
+ toDefaultTable := func(r config.DefaultPolicyType) table.RouteType {
+ var def table.RouteType
+ switch r {
+ case config.DEFAULT_POLICY_TYPE_ACCEPT_ROUTE:
+ def = table.ROUTE_TYPE_ACCEPT
+ case config.DEFAULT_POLICY_TYPE_REJECT_ROUTE:
+ def = table.ROUTE_TYPE_REJECT
+ }
+ return def
+ }
+ toPolicies := func(r []string) []*table.Policy {
+ p := make([]*table.Policy, 0, len(r))
+ for _, n := range r {
+ p = append(p, &table.Policy{
+ Name: n,
+ })
+ }
+ return p
+ }
+
+ def := toDefaultTable(a.DefaultImportPolicy)
+ ps := toPolicies(a.ImportPolicyList)
+ apiServer.ReplacePolicyAssignment(context.Background(), &api.ReplacePolicyAssignmentRequest{
+ Assignment: server.NewAPIPolicyAssignmentFromTableStruct(&table.PolicyAssignment{
+ Name: "",
+ Type: table.POLICY_DIRECTION_IMPORT,
+ Policies: ps,
+ Default: def,
+ }),
+ })
+
+ def = toDefaultTable(a.DefaultExportPolicy)
+ ps = toPolicies(a.ExportPolicyList)
+ apiServer.ReplacePolicyAssignment(context.Background(), &api.ReplacePolicyAssignmentRequest{
+ Assignment: server.NewAPIPolicyAssignmentFromTableStruct(&table.PolicyAssignment{
+ Name: "",
+ Type: table.POLICY_DIRECTION_EXPORT,
+ Policies: ps,
+ Default: def,
+ }),
+ })
+
+ updatePolicy = true
+
+ }
+ c = newConfig
+ }
+ for _, pg := range addedPg {
+ log.Infof("PeerGroup %s is added", pg.Config.PeerGroupName)
+ if _, err := apiServer.AddPeerGroup(context.Background(), &api.AddPeerGroupRequest{
+ PeerGroup: server.NewPeerGroupFromConfigStruct(&pg),
+ }); err != nil {
+ log.Warn(err)
+ }
+ }
+ for _, pg := range deletedPg {
+ log.Infof("PeerGroup %s is deleted", pg.Config.PeerGroupName)
+ if _, err := apiServer.DeletePeerGroup(context.Background(), &api.DeletePeerGroupRequest{
+ PeerGroup: server.NewPeerGroupFromConfigStruct(&pg),
+ }); err != nil {
+ log.Warn(err)
+ }
+ }
+ for _, pg := range updatedPg {
+ log.Infof("PeerGroup %v is updated", pg.State.PeerGroupName)
+ if u, err := apiServer.UpdatePeerGroup(context.Background(), &api.UpdatePeerGroupRequest{
+ PeerGroup: server.NewPeerGroupFromConfigStruct(&pg),
+ }); err != nil {
+ log.Warn(err)
+ } else {
+ updatePolicy = updatePolicy || u.NeedsSoftResetIn
+ }
+ }
+ for _, pg := range updatedPg {
+ log.Infof("PeerGroup %s is updated", pg.Config.PeerGroupName)
+ if _, err := apiServer.UpdatePeerGroup(context.Background(), &api.UpdatePeerGroupRequest{
+ PeerGroup: server.NewPeerGroupFromConfigStruct(&pg),
+ }); err != nil {
+ log.Warn(err)
+ }
+ }
+ for _, dn := range newConfig.DynamicNeighbors {
+ log.Infof("Dynamic Neighbor %s is added to PeerGroup %s", dn.Config.Prefix, dn.Config.PeerGroup)
+ if _, err := apiServer.AddDynamicNeighbor(context.Background(), &api.AddDynamicNeighborRequest{
+ DynamicNeighbor: &api.DynamicNeighbor{
+ Prefix: dn.Config.Prefix,
+ PeerGroup: dn.Config.PeerGroup,
+ },
+ }); err != nil {
+ log.Warn(err)
+ }
+ }
+ for _, p := range added {
+ log.Infof("Peer %v is added", p.State.NeighborAddress)
+ if _, err := apiServer.AddNeighbor(context.Background(), &api.AddNeighborRequest{
+ Peer: server.NewPeerFromConfigStruct(&p),
+ }); err != nil {
+ log.Warn(err)
+ }
+ }
+ for _, p := range deleted {
+ log.Infof("Peer %v is deleted", p.State.NeighborAddress)
+ if _, err := apiServer.DeleteNeighbor(context.Background(), &api.DeleteNeighborRequest{
+ Peer: server.NewPeerFromConfigStruct(&p),
+ }); err != nil {
+ log.Warn(err)
+ }
+ }
+ for _, p := range updated {
+ log.Infof("Peer %v is updated", p.State.NeighborAddress)
+ if u, err := apiServer.UpdateNeighbor(context.Background(), &api.UpdateNeighborRequest{
+ Peer: server.NewPeerFromConfigStruct(&p),
+ }); err != nil {
+ log.Warn(err)
+ } else {
+ updatePolicy = updatePolicy || u.NeedsSoftResetIn
+ }
+ }
+
+ if updatePolicy {
+ if _, err := apiServer.SoftResetNeighbor(context.Background(), &api.SoftResetNeighborRequest{
+ Address: "",
+ Direction: api.SoftResetNeighborRequest_IN,
+ }); err != nil {
+ log.Warn(err)
+ }
+ }
+ }
+ }
+ }
+
+ loop()
+}
diff --git a/cmd/gobgpd/util.go b/cmd/gobgpd/util.go
new file mode 100644
index 00000000..1b7885b8
--- /dev/null
+++ b/cmd/gobgpd/util.go
@@ -0,0 +1,103 @@
+// Copyright (C) 2017 Nippon Telegraph and Telephone Corporation.
+//
+// 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.
+
+// +build !windows
+
+package main
+
+import (
+ "log/syslog"
+ "os"
+ "os/signal"
+ "runtime"
+ "runtime/debug"
+ "strings"
+ "syscall"
+
+ log "github.com/sirupsen/logrus"
+ lSyslog "github.com/sirupsen/logrus/hooks/syslog"
+)
+
+func init() {
+ go func() {
+ sigCh := make(chan os.Signal, 1)
+ signal.Notify(sigCh, syscall.SIGUSR1)
+ for range sigCh {
+ runtime.GC()
+ debug.FreeOSMemory()
+ }
+ }()
+}
+
+func addSyslogHook(host, facility string) error {
+ dst := strings.SplitN(host, ":", 2)
+ network := ""
+ addr := ""
+ if len(dst) == 2 {
+ network = dst[0]
+ addr = dst[1]
+ }
+
+ priority := syslog.Priority(0)
+ switch facility {
+ case "kern":
+ priority = syslog.LOG_KERN
+ case "user":
+ priority = syslog.LOG_USER
+ case "mail":
+ priority = syslog.LOG_MAIL
+ case "daemon":
+ priority = syslog.LOG_DAEMON
+ case "auth":
+ priority = syslog.LOG_AUTH
+ case "syslog":
+ priority = syslog.LOG_SYSLOG
+ case "lpr":
+ priority = syslog.LOG_LPR
+ case "news":
+ priority = syslog.LOG_NEWS
+ case "uucp":
+ priority = syslog.LOG_UUCP
+ case "cron":
+ priority = syslog.LOG_CRON
+ case "authpriv":
+ priority = syslog.LOG_AUTHPRIV
+ case "ftp":
+ priority = syslog.LOG_FTP
+ case "local0":
+ priority = syslog.LOG_LOCAL0
+ case "local1":
+ priority = syslog.LOG_LOCAL1
+ case "local2":
+ priority = syslog.LOG_LOCAL2
+ case "local3":
+ priority = syslog.LOG_LOCAL3
+ case "local4":
+ priority = syslog.LOG_LOCAL4
+ case "local5":
+ priority = syslog.LOG_LOCAL5
+ case "local6":
+ priority = syslog.LOG_LOCAL6
+ case "local7":
+ priority = syslog.LOG_LOCAL7
+ }
+
+ hook, err := lSyslog.NewSyslogHook(network, addr, syslog.LOG_INFO|priority, "bgpd")
+ if err != nil {
+ return err
+ }
+ log.AddHook(hook)
+ return nil
+}
diff --git a/cmd/gobgpd/util_windows.go b/cmd/gobgpd/util_windows.go
new file mode 100644
index 00000000..56c5bc2f
--- /dev/null
+++ b/cmd/gobgpd/util_windows.go
@@ -0,0 +1,24 @@
+// Copyright (C) 2017 Nippon Telegraph and Telephone Corporation.
+//
+// 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 main
+
+import (
+ "errors"
+)
+
+func addSyslogHook(_, _ string) error {
+ return errors.New("syslog is not supported on this OS")
+}