github.com/osrg/gobgp/v3@v3.30.0/cmd/gobgpd/main.go (about) 1 // 2 // Copyright (C) 2014-2017 Nippon Telegraph and Telephone Corporation. 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 // implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 package main 18 19 import ( 20 "context" 21 "crypto/tls" 22 "crypto/x509" 23 "fmt" 24 "io" 25 "net/http" 26 "net/http/pprof" 27 "os" 28 "os/signal" 29 "runtime" 30 "syscall" 31 "time" 32 33 "github.com/coreos/go-systemd/v22/daemon" 34 grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" 35 "github.com/jessevdk/go-flags" 36 "github.com/kr/pretty" 37 "github.com/prometheus/client_golang/prometheus" 38 "github.com/prometheus/client_golang/prometheus/promhttp" 39 "github.com/sirupsen/logrus" 40 "golang.org/x/time/rate" 41 "google.golang.org/grpc" 42 "google.golang.org/grpc/credentials" 43 44 "github.com/osrg/gobgp/v3/internal/pkg/metrics" 45 "github.com/osrg/gobgp/v3/internal/pkg/version" 46 "github.com/osrg/gobgp/v3/pkg/config" 47 "github.com/osrg/gobgp/v3/pkg/server" 48 ) 49 50 var logger = logrus.New() 51 52 func main() { 53 sigCh := make(chan os.Signal, 1) 54 signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT) 55 56 var opts struct { 57 ConfigFile string `short:"f" long:"config-file" description:"specifying a config file"` 58 ConfigType string `short:"t" long:"config-type" description:"specifying config type (toml, yaml, json)" default:"toml"` 59 ConfigAutoReload bool `short:"a" long:"config-auto-reload" description:"activate config auto reload on changes"` 60 LogLevel string `short:"l" long:"log-level" description:"specifying log level"` 61 LogPlain bool `short:"p" long:"log-plain" description:"use plain format for logging (json by default)"` 62 UseSyslog string `short:"s" long:"syslog" description:"use syslogd"` 63 Facility string `long:"syslog-facility" description:"specify syslog facility"` 64 DisableStdlog bool `long:"disable-stdlog" description:"disable standard logging"` 65 CPUs int `long:"cpus" description:"specify the number of CPUs to be used"` 66 GrpcHosts string `long:"api-hosts" description:"specify the hosts that gobgpd listens on" default:":50051"` 67 GracefulRestart bool `short:"r" long:"graceful-restart" description:"flag restart-state in graceful-restart capability"` 68 Dry bool `short:"d" long:"dry-run" description:"check configuration"` 69 PProfHost string `long:"pprof-host" description:"specify the host that gobgpd listens on for pprof and metrics" default:"localhost:6060"` 70 PProfDisable bool `long:"pprof-disable" description:"disable pprof profiling"` 71 MetricsPath string `long:"metrics-path" description:"specify path for prometheus metrics, empty value disables them" default:"/metrics"` 72 UseSdNotify bool `long:"sdnotify" description:"use sd_notify protocol"` 73 TLS bool `long:"tls" description:"enable TLS authentication for gRPC API"` 74 TLSCertFile string `long:"tls-cert-file" description:"The TLS cert file"` 75 TLSKeyFile string `long:"tls-key-file" description:"The TLS key file"` 76 TLSClientCAFile string `long:"tls-client-ca-file" description:"Optional TLS client CA file to authenticate clients against"` 77 Version bool `long:"version" description:"show version number"` 78 } 79 _, err := flags.Parse(&opts) 80 if err != nil { 81 os.Exit(1) 82 } 83 84 if opts.Version { 85 fmt.Println("gobgpd version", version.Version()) 86 os.Exit(0) 87 } 88 89 if opts.CPUs == 0 { 90 runtime.GOMAXPROCS(runtime.NumCPU()) 91 } else { 92 if runtime.NumCPU() < opts.CPUs { 93 logger.Errorf("Only %d CPUs are available but %d is specified", runtime.NumCPU(), opts.CPUs) 94 os.Exit(1) 95 } 96 runtime.GOMAXPROCS(opts.CPUs) 97 } 98 99 httpMux := http.NewServeMux() 100 if !opts.PProfDisable { 101 httpMux.HandleFunc("/debug/pprof/", pprof.Index) 102 httpMux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) 103 httpMux.HandleFunc("/debug/pprof/profile", pprof.Profile) 104 httpMux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) 105 httpMux.HandleFunc("/debug/pprof/trace", pprof.Trace) 106 } 107 if opts.MetricsPath != "" { 108 httpMux.Handle(opts.MetricsPath, promhttp.Handler()) 109 } 110 if !opts.PProfDisable || opts.MetricsPath != "" { 111 go func() { 112 logger.Println(http.ListenAndServe(opts.PProfHost, httpMux)) 113 }() 114 } 115 116 switch opts.LogLevel { 117 case "debug": 118 logger.SetLevel(logrus.DebugLevel) 119 case "info": 120 logger.SetLevel(logrus.InfoLevel) 121 default: 122 logger.SetLevel(logrus.InfoLevel) 123 } 124 125 if opts.DisableStdlog { 126 logger.SetOutput(io.Discard) 127 } else { 128 logger.SetOutput(os.Stdout) 129 } 130 131 if opts.UseSyslog != "" { 132 if err := addSyslogHook(opts.UseSyslog, opts.Facility); err != nil { 133 logger.Error("Unable to connect to syslog daemon, ", opts.UseSyslog) 134 } 135 } 136 137 if opts.LogPlain { 138 if opts.DisableStdlog { 139 logger.SetFormatter(&logrus.TextFormatter{ 140 DisableColors: true, 141 }) 142 } 143 } else { 144 logger.SetFormatter(&logrus.JSONFormatter{}) 145 } 146 147 if opts.Dry { 148 c, err := config.ReadConfigFile(opts.ConfigFile, opts.ConfigType) 149 if err != nil { 150 logger.WithFields(logrus.Fields{ 151 "Topic": "Config", 152 "Error": err, 153 }).Fatalf("Can't read config file %s", opts.ConfigFile) 154 } 155 logger.WithFields(logrus.Fields{ 156 "Topic": "Config", 157 }).Info("Finished reading the config file") 158 if opts.LogLevel == "debug" { 159 pretty.Println(c) 160 } 161 os.Exit(0) 162 } 163 164 maxSize := 256 << 20 165 grpcOpts := []grpc.ServerOption{grpc.MaxRecvMsgSize(maxSize), grpc.MaxSendMsgSize(maxSize)} 166 if opts.TLS { 167 // server cert/key 168 cert, err := tls.LoadX509KeyPair(opts.TLSCertFile, opts.TLSKeyFile) 169 if err != nil { 170 logger.Fatalf("Failed to load server certificate/key pair: %v", err) 171 } 172 tlsConfig := &tls.Config{Certificates: []tls.Certificate{cert}} 173 174 // client CA 175 if len(opts.TLSClientCAFile) != 0 { 176 tlsConfig.ClientCAs = x509.NewCertPool() 177 pemCerts, err := os.ReadFile(opts.TLSClientCAFile) 178 if err != nil { 179 logger.Fatalf("Failed to load client CA certificates from %q: %v", opts.TLSClientCAFile, err) 180 } 181 if ok := tlsConfig.ClientCAs.AppendCertsFromPEM(pemCerts); !ok { 182 logger.Fatalf("No valid client CA certificates in %q", opts.TLSClientCAFile) 183 } 184 tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert 185 } 186 187 creds := credentials.NewTLS(tlsConfig) 188 grpcOpts = append(grpcOpts, grpc.Creds(creds)) 189 } 190 191 if opts.MetricsPath != "" { 192 grpcOpts = append( 193 grpcOpts, 194 grpc.StreamInterceptor(grpc_prometheus.StreamServerInterceptor), 195 grpc.UnaryInterceptor(grpc_prometheus.UnaryServerInterceptor), 196 ) 197 } 198 199 logger.Info("gobgpd started") 200 bgpServer := server.NewBgpServer(server.GrpcListenAddress(opts.GrpcHosts), server.GrpcOption(grpcOpts), server.LoggerOption(&builtinLogger{logger: logger})) 201 prometheus.MustRegister(metrics.NewBgpCollector(bgpServer)) 202 go bgpServer.Serve() 203 204 if opts.UseSdNotify { 205 if status, err := daemon.SdNotify(false, daemon.SdNotifyReady); !status { 206 if err != nil { 207 logger.Warnf("Failed to send notification via sd_notify(): %s", err) 208 } else { 209 logger.Warnf("The socket sd_notify() isn't available") 210 } 211 } 212 } 213 214 if opts.ConfigFile == "" { 215 <-sigCh 216 stopServer(bgpServer, opts.UseSdNotify) 217 return 218 } 219 220 signal.Notify(sigCh, syscall.SIGHUP) 221 222 initialConfig, err := config.ReadConfigFile(opts.ConfigFile, opts.ConfigType) 223 if err != nil { 224 logger.WithFields(logrus.Fields{ 225 "Topic": "Config", 226 "Error": err, 227 }).Fatalf("Can't read config file %s", opts.ConfigFile) 228 } 229 logger.WithFields(logrus.Fields{ 230 "Topic": "Config", 231 }).Info("Finished reading the config file") 232 233 currentConfig, err := config.InitialConfig(context.Background(), bgpServer, initialConfig, opts.GracefulRestart) 234 if err != nil { 235 logger.WithFields(logrus.Fields{ 236 "Topic": "Config", 237 "Error": err, 238 }).Fatalf("Failed to apply initial configuration %s", opts.ConfigFile) 239 } 240 241 if opts.ConfigAutoReload { 242 logger.WithFields(logrus.Fields{ 243 "Topic": "Config", 244 }).Info("Watching for config changes to trigger auto-reload") 245 246 // Writing to the config may trigger many events in quick successions 247 // To prevent abusive reloads, we ignore any event in a 100ms window 248 rateLimiter := rate.Sometimes{Interval: 100 * time.Millisecond} 249 250 config.WatchConfigFile(opts.ConfigFile, opts.ConfigType, func() { 251 rateLimiter.Do(func() { 252 logger.WithFields(logrus.Fields{ 253 "Topic": "Config", 254 }).Info("Config changes detected, reloading configuration") 255 256 sigCh <- syscall.SIGHUP 257 }) 258 }) 259 } 260 261 for sig := range sigCh { 262 if sig != syscall.SIGHUP { 263 stopServer(bgpServer, opts.UseSdNotify) 264 return 265 } 266 267 logger.WithFields(logrus.Fields{ 268 "Topic": "Config", 269 }).Info("Reload the config file") 270 newConfig, err := config.ReadConfigFile(opts.ConfigFile, opts.ConfigType) 271 if err != nil { 272 logger.WithFields(logrus.Fields{ 273 "Topic": "Config", 274 "Error": err, 275 }).Warningf("Can't read config file %s", opts.ConfigFile) 276 continue 277 } 278 279 currentConfig, err = config.UpdateConfig(context.Background(), bgpServer, currentConfig, newConfig) 280 if err != nil { 281 logrus.WithFields(logrus.Fields{ 282 "Topic": "Config", 283 "Error": err, 284 }).Warningf("Failed to update config %s", opts.ConfigFile) 285 continue 286 } 287 } 288 } 289 290 func stopServer(bgpServer *server.BgpServer, useSdNotify bool) { 291 logger.Info("stopping gobgpd server") 292 293 bgpServer.Stop() 294 if useSdNotify { 295 daemon.SdNotify(false, daemon.SdNotifyStopping) 296 } 297 }