From 4d38282082b0d2a2a834e2cb20d905f1d47655c8 Mon Sep 17 00:00:00 2001 From: Flavio Crisciani Date: Fri, 1 Dec 2017 11:24:14 -0800 Subject: [PATCH 1/3] Add option to enable networkDB debug Add a new configuration option to allow the enabling of the networkDB debug. The option is only parsed using the reload event. This will protect the daemon on start or restart if the option is left behind in the config file Signed-off-by: Flavio Crisciani Upstream-commit: a97e45794ea8318a08daf763a5b63b04184a886b Component: engine --- components/engine/cmd/dockerd/config.go | 2 + components/engine/daemon/config/config.go | 41 +++++++------- components/engine/daemon/reload.go | 18 ++++++ components/engine/daemon/reload_test.go | 69 +++++++++++++++++++++++ 4 files changed, 110 insertions(+), 20 deletions(-) diff --git a/components/engine/cmd/dockerd/config.go b/components/engine/cmd/dockerd/config.go index b9d586a4ba..c4ae197335 100644 --- a/components/engine/cmd/dockerd/config.go +++ b/components/engine/cmd/dockerd/config.go @@ -59,6 +59,8 @@ func installCommonConfigFlags(conf *config.Config, flags *pflag.FlagSet) { flags.IntVar(&maxConcurrentDownloads, "max-concurrent-downloads", config.DefaultMaxConcurrentDownloads, "Set the max concurrent downloads for each pull") flags.IntVar(&maxConcurrentUploads, "max-concurrent-uploads", config.DefaultMaxConcurrentUploads, "Set the max concurrent uploads for each push") flags.IntVar(&conf.ShutdownTimeout, "shutdown-timeout", defaultShutdownTimeout, "Set the default shutdown timeout") + flags.IntVar(&conf.NetworkDiagnosticPort, "network-diagnostic-port", 0, "TCP port number of the network diagnostic server") + flags.MarkHidden("network-diagnostic-port") flags.StringVar(&conf.SwarmDefaultAdvertiseAddr, "swarm-default-advertise-addr", "", "Set default address or interface for swarm advertised address") flags.BoolVar(&conf.Experimental, "experimental", false, "Enable experimental features") diff --git a/components/engine/daemon/config/config.go b/components/engine/daemon/config/config.go index 1e22a6fea7..199fae6cf1 100644 --- a/components/engine/daemon/config/config.go +++ b/components/engine/daemon/config/config.go @@ -85,26 +85,27 @@ type CommonTLSOptions struct { // It includes json tags to deserialize configuration from a file // using the same names that the flags in the command line use. type CommonConfig struct { - AuthzMiddleware *authorization.Middleware `json:"-"` - AuthorizationPlugins []string `json:"authorization-plugins,omitempty"` // AuthorizationPlugins holds list of authorization plugins - AutoRestart bool `json:"-"` - Context map[string][]string `json:"-"` - DisableBridge bool `json:"-"` - DNS []string `json:"dns,omitempty"` - DNSOptions []string `json:"dns-opts,omitempty"` - DNSSearch []string `json:"dns-search,omitempty"` - ExecOptions []string `json:"exec-opts,omitempty"` - GraphDriver string `json:"storage-driver,omitempty"` - GraphOptions []string `json:"storage-opts,omitempty"` - Labels []string `json:"labels,omitempty"` - Mtu int `json:"mtu,omitempty"` - Pidfile string `json:"pidfile,omitempty"` - RawLogs bool `json:"raw-logs,omitempty"` - RootDeprecated string `json:"graph,omitempty"` - Root string `json:"data-root,omitempty"` - ExecRoot string `json:"exec-root,omitempty"` - SocketGroup string `json:"group,omitempty"` - CorsHeaders string `json:"api-cors-header,omitempty"` + AuthzMiddleware *authorization.Middleware `json:"-"` + AuthorizationPlugins []string `json:"authorization-plugins,omitempty"` // AuthorizationPlugins holds list of authorization plugins + AutoRestart bool `json:"-"` + Context map[string][]string `json:"-"` + DisableBridge bool `json:"-"` + DNS []string `json:"dns,omitempty"` + DNSOptions []string `json:"dns-opts,omitempty"` + DNSSearch []string `json:"dns-search,omitempty"` + ExecOptions []string `json:"exec-opts,omitempty"` + GraphDriver string `json:"storage-driver,omitempty"` + GraphOptions []string `json:"storage-opts,omitempty"` + Labels []string `json:"labels,omitempty"` + Mtu int `json:"mtu,omitempty"` + NetworkDiagnosticPort int `json:"network-diagnostic-port,omitempty"` + Pidfile string `json:"pidfile,omitempty"` + RawLogs bool `json:"raw-logs,omitempty"` + RootDeprecated string `json:"graph,omitempty"` + Root string `json:"data-root,omitempty"` + ExecRoot string `json:"exec-root,omitempty"` + SocketGroup string `json:"group,omitempty"` + CorsHeaders string `json:"api-cors-header,omitempty"` // TrustKeyPath is used to generate the daemon ID and for signing schema 1 manifests // when pushing to a registry which does not support schema 2. This field is marked as diff --git a/components/engine/daemon/reload.go b/components/engine/daemon/reload.go index 0d16bc8d02..a20eb688f4 100644 --- a/components/engine/daemon/reload.go +++ b/components/engine/daemon/reload.go @@ -61,6 +61,9 @@ func (daemon *Daemon) Reload(conf *config.Config) (err error) { if err := daemon.reloadLiveRestore(conf, attributes); err != nil { return err } + if err := daemon.reloadNetworkDiagnosticPort(conf, attributes); err != nil { + return err + } return nil } @@ -308,3 +311,18 @@ func (daemon *Daemon) reloadLiveRestore(conf *config.Config, attributes map[stri attributes["live-restore"] = fmt.Sprintf("%t", daemon.configStore.LiveRestoreEnabled) return nil } + +// reloadNetworkDiagnosticPort updates the network controller starting the diagnose mode if the config is valid +func (daemon *Daemon) reloadNetworkDiagnosticPort(conf *config.Config, attributes map[string]string) error { + if conf == nil || daemon.netController == nil { + return nil + } + // Enable the network diagnose if the flag is set with a valid port withing the range + if conf.IsValueSet("network-diagnostic-port") && conf.NetworkDiagnosticPort > 0 && conf.NetworkDiagnosticPort < 65536 { + logrus.Warnf("Calling the diagnostic start with %d", conf.NetworkDiagnosticPort) + daemon.netController.StartDiagnose(conf.NetworkDiagnosticPort) + } else { + daemon.netController.StopDiagnose() + } + return nil +} diff --git a/components/engine/daemon/reload_test.go b/components/engine/daemon/reload_test.go index 96b1a2452d..03b249bada 100644 --- a/components/engine/daemon/reload_test.go +++ b/components/engine/daemon/reload_test.go @@ -10,6 +10,7 @@ import ( "github.com/docker/docker/pkg/discovery" _ "github.com/docker/docker/pkg/discovery/memory" "github.com/docker/docker/registry" + "github.com/docker/libnetwork" "github.com/stretchr/testify/assert" ) @@ -479,3 +480,71 @@ func TestDaemonDiscoveryReloadOnlyClusterAdvertise(t *testing.T) { t.Fatal(e) } } + +func TestDaemonReloadNetworkDiagnosticPort(t *testing.T) { + daemon := &Daemon{} + daemon.configStore = &config.Config{} + + valuesSet := make(map[string]interface{}) + valuesSet["network-diagnostic-port"] = 2000 + enableConfig := &config.Config{ + CommonConfig: config.CommonConfig{ + NetworkDiagnosticPort: 2000, + ValuesSet: valuesSet, + }, + } + disableConfig := &config.Config{ + CommonConfig: config.CommonConfig{}, + } + + netOptions, err := daemon.networkOptions(enableConfig, nil, nil) + if err != nil { + t.Fatal(err) + } + controller, err := libnetwork.New(netOptions...) + if err != nil { + t.Fatal(err) + } + daemon.netController = controller + + // Enable/Disable the server for some iterations + for i := 0; i < 10; i++ { + enableConfig.CommonConfig.NetworkDiagnosticPort++ + if err := daemon.Reload(enableConfig); err != nil { + t.Fatal(err) + } + // Check that the diagnose is enabled + if !daemon.netController.IsDiagnoseEnabled() { + t.Fatalf("diagnosed should be enable") + } + + // Reload + if err := daemon.Reload(disableConfig); err != nil { + t.Fatal(err) + } + // Check that the diagnose is disabled + if daemon.netController.IsDiagnoseEnabled() { + t.Fatalf("diagnosed should be disable") + } + } + + enableConfig.CommonConfig.NetworkDiagnosticPort++ + // 2 times the enable should not create problems + if err := daemon.Reload(enableConfig); err != nil { + t.Fatal(err) + } + // Check that the diagnose is enabled + if !daemon.netController.IsDiagnoseEnabled() { + t.Fatalf("diagnosed should be enable") + } + + // Check that another reload does not cause issues + if err := daemon.Reload(enableConfig); err != nil { + t.Fatal(err) + } + // Check that the diagnose is enable + if !daemon.netController.IsDiagnoseEnabled() { + t.Fatalf("diagnosed should be enable") + } + +} From 8afbe15ccfd08d7c0e26a30b5791306414af2f5e Mon Sep 17 00:00:00 2001 From: Flavio Crisciani Date: Fri, 1 Dec 2017 11:26:35 -0800 Subject: [PATCH 2/3] Vendoring of libnetwork Signed-off-by: Flavio Crisciani Upstream-commit: c1e1fb6fa5204bb7014ca9ebab9fe1804c1eb957 Component: engine --- components/engine/vendor.conf | 2 +- .../github.com/docker/libnetwork/agent.go | 8 +- .../docker/libnetwork/controller.go | 36 +++ .../docker/libnetwork/diagnose/diagnose.go | 133 ---------- .../docker/libnetwork/diagnose/server.go | 228 +++++++++++++++++ .../docker/libnetwork/diagnose/types.go | 122 +++++++++ .../libnetwork/drivers/bridge/bridge.go | 162 ++++++------ .../drivers/bridge/setup_firewalld.go | 2 +- .../libnetwork/drivers/overlay/ov_network.go | 11 + .../github.com/docker/libnetwork/ipvs/ipvs.go | 16 ++ .../docker/libnetwork/ipvs/netlink.go | 11 +- .../libnetwork/networkdb/event_delegate.go | 2 +- .../docker/libnetwork/networkdb/networkdb.go | 18 +- .../libnetwork/networkdb/networkdbdiagnose.go | 242 ++++++++++++++---- .../github.com/docker/libnetwork/vendor.conf | 2 +- 15 files changed, 714 insertions(+), 281 deletions(-) delete mode 100644 components/engine/vendor/github.com/docker/libnetwork/diagnose/diagnose.go create mode 100644 components/engine/vendor/github.com/docker/libnetwork/diagnose/server.go create mode 100644 components/engine/vendor/github.com/docker/libnetwork/diagnose/types.go diff --git a/components/engine/vendor.conf b/components/engine/vendor.conf index 120dd9d378..f9cc8a7131 100644 --- a/components/engine/vendor.conf +++ b/components/engine/vendor.conf @@ -30,7 +30,7 @@ github.com/moby/buildkit aaff9d591ef128560018433fe61beb802e149de8 github.com/tonistiigi/fsutil dea3a0da73aee887fc02142d995be764106ac5e2 #get libnetwork packages -github.com/docker/libnetwork 64ae58878fc8f95e4a167499d654e13fa36abdc7 +github.com/docker/libnetwork 9bca9a4a220b158cc94402e0f8c2c7714eb6f503 github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80 github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec diff --git a/components/engine/vendor/github.com/docker/libnetwork/agent.go b/components/engine/vendor/github.com/docker/libnetwork/agent.go index f12006583b..085b8c47ca 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/agent.go +++ b/components/engine/vendor/github.com/docker/libnetwork/agent.go @@ -293,11 +293,13 @@ func (c *controller) agentInit(listenAddr, bindAddrOrInterface, advertiseAddr, d c.Config().Daemon.NetworkControlPlaneMTU, netDBConf.PacketBufferSize) } nDB, err := networkdb.New(netDBConf) - if err != nil { return err } + // Register the diagnose handlers + c.DiagnoseServer.RegisterHandler(nDB, networkdb.NetDbPaths2Func) + var cancelList []func() ch, cancel := nDB.Watch(libnetworkEPTable, "", "") cancelList = append(cancelList, cancel) @@ -436,7 +438,7 @@ func (n *network) Services() map[string]ServiceInfo { for eid, value := range entries { var epRec EndpointRecord nid := n.ID() - if err := proto.Unmarshal(value.([]byte), &epRec); err != nil { + if err := proto.Unmarshal(value.Value, &epRec); err != nil { logrus.Errorf("Unmarshal of libnetworkEPTable failed for endpoint %s in network %s, %v", eid, nid, err) continue } @@ -461,7 +463,7 @@ func (n *network) Services() map[string]ServiceInfo { } entries := agent.networkDB.GetTableByNetwork(table.name, n.id) for key, value := range entries { - epID, info := d.DecodeTableEntry(table.name, key, value.([]byte)) + epID, info := d.DecodeTableEntry(table.name, key, value.Value) if ep, ok := eps[epID]; !ok { logrus.Errorf("Inconsistent driver and libnetwork state for endpoint %s", epID) } else { diff --git a/components/engine/vendor/github.com/docker/libnetwork/controller.go b/components/engine/vendor/github.com/docker/libnetwork/controller.go index 24494ae834..e9389487a3 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/controller.go +++ b/components/engine/vendor/github.com/docker/libnetwork/controller.go @@ -60,6 +60,7 @@ import ( "github.com/docker/libnetwork/cluster" "github.com/docker/libnetwork/config" "github.com/docker/libnetwork/datastore" + "github.com/docker/libnetwork/diagnose" "github.com/docker/libnetwork/discoverapi" "github.com/docker/libnetwork/driverapi" "github.com/docker/libnetwork/drvregistry" @@ -133,6 +134,13 @@ type NetworkController interface { // SetKeys configures the encryption key for gossip and overlay data path SetKeys(keys []*types.EncryptionKey) error + + // StartDiagnose start the network diagnose mode + StartDiagnose(port int) + // StopDiagnose start the network diagnose mode + StopDiagnose() + // IsDiagnoseEnabled returns true if the diagnose is enabled + IsDiagnoseEnabled() bool } // NetworkWalker is a client provided function which will be used to walk the Networks. @@ -167,6 +175,7 @@ type controller struct { agentStopDone chan struct{} keys []*types.EncryptionKey clusterConfigAvailable bool + DiagnoseServer *diagnose.Server sync.Mutex } @@ -185,7 +194,9 @@ func New(cfgOptions ...config.Option) (NetworkController, error) { serviceBindings: make(map[serviceKey]*service), agentInitDone: make(chan struct{}), networkLocker: locker.New(), + DiagnoseServer: diagnose.New(), } + c.DiagnoseServer.Init() if err := c.initStores(); err != nil { return nil, err @@ -1291,3 +1302,28 @@ func (c *controller) Stop() { c.stopExternalKeyListener() osl.GC() } + +// StartDiagnose start the network diagnose mode +func (c *controller) StartDiagnose(port int) { + c.Lock() + if !c.DiagnoseServer.IsDebugEnable() { + c.DiagnoseServer.EnableDebug("127.0.0.1", port) + } + c.Unlock() +} + +// StopDiagnose start the network diagnose mode +func (c *controller) StopDiagnose() { + c.Lock() + if c.DiagnoseServer.IsDebugEnable() { + c.DiagnoseServer.DisableDebug() + } + c.Unlock() +} + +// IsDiagnoseEnabled returns true if the diagnose is enabled +func (c *controller) IsDiagnoseEnabled() bool { + c.Lock() + defer c.Unlock() + return c.DiagnoseServer.IsDebugEnable() +} diff --git a/components/engine/vendor/github.com/docker/libnetwork/diagnose/diagnose.go b/components/engine/vendor/github.com/docker/libnetwork/diagnose/diagnose.go deleted file mode 100644 index 9682fff78b..0000000000 --- a/components/engine/vendor/github.com/docker/libnetwork/diagnose/diagnose.go +++ /dev/null @@ -1,133 +0,0 @@ -package diagnose - -import ( - "fmt" - "net" - "net/http" - "sync" - - "github.com/sirupsen/logrus" -) - -// HTTPHandlerFunc TODO -type HTTPHandlerFunc func(interface{}, http.ResponseWriter, *http.Request) - -type httpHandlerCustom struct { - ctx interface{} - F func(interface{}, http.ResponseWriter, *http.Request) -} - -// ServeHTTP TODO -func (h httpHandlerCustom) ServeHTTP(w http.ResponseWriter, r *http.Request) { - h.F(h.ctx, w, r) -} - -var diagPaths2Func = map[string]HTTPHandlerFunc{ - "/": notImplemented, - "/help": help, - "/ready": ready, -} - -// Server when the debug is enabled exposes a -// This data structure is protected by the Agent mutex so does not require and additional mutex here -type Server struct { - sk net.Listener - port int - mux *http.ServeMux - registeredHanders []string - sync.Mutex -} - -// Init TODO -func (n *Server) Init() { - n.mux = http.NewServeMux() - - // Register local handlers - n.RegisterHandler(n, diagPaths2Func) -} - -// RegisterHandler TODO -func (n *Server) RegisterHandler(ctx interface{}, hdlrs map[string]HTTPHandlerFunc) { - n.Lock() - defer n.Unlock() - for path, fun := range hdlrs { - n.mux.Handle(path, httpHandlerCustom{ctx, fun}) - n.registeredHanders = append(n.registeredHanders, path) - } -} - -// EnableDebug opens a TCP socket to debug the passed network DB -func (n *Server) EnableDebug(ip string, port int) { - n.Lock() - defer n.Unlock() - - n.port = port - logrus.SetLevel(logrus.DebugLevel) - - if n.sk != nil { - logrus.Infof("The server is already up and running") - return - } - - logrus.Infof("Starting the server listening on %d for commands", port) - - // // Create the socket - // var err error - // n.sk, err = net.Listen("tcp", listeningAddr) - // if err != nil { - // log.Fatal(err) - // } - // - // go func() { - // http.Serve(n.sk, n.mux) - // }() - http.ListenAndServe(fmt.Sprintf(":%d", port), n.mux) -} - -// DisableDebug stop the dubug and closes the tcp socket -func (n *Server) DisableDebug() { - n.Lock() - defer n.Unlock() - n.sk.Close() - n.sk = nil -} - -// IsDebugEnable returns true when the debug is enabled -func (n *Server) IsDebugEnable() bool { - n.Lock() - defer n.Unlock() - return n.sk != nil -} - -func notImplemented(ctx interface{}, w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "URL path: %s no method implemented check /help\n", r.URL.Path) -} - -func help(ctx interface{}, w http.ResponseWriter, r *http.Request) { - n, ok := ctx.(*Server) - if ok { - for _, path := range n.registeredHanders { - fmt.Fprintf(w, "%s\n", path) - } - } -} - -func ready(ctx interface{}, w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "OK\n") -} - -// DebugHTTPForm TODO -func DebugHTTPForm(r *http.Request) { - r.ParseForm() - for k, v := range r.Form { - logrus.Debugf("Form[%q] = %q\n", k, v) - } -} - -// HTTPReplyError TODO -func HTTPReplyError(w http.ResponseWriter, message, usage string) { - fmt.Fprintf(w, "%s\n", message) - if usage != "" { - fmt.Fprintf(w, "Usage: %s\n", usage) - } -} diff --git a/components/engine/vendor/github.com/docker/libnetwork/diagnose/server.go b/components/engine/vendor/github.com/docker/libnetwork/diagnose/server.go new file mode 100644 index 0000000000..c841e51966 --- /dev/null +++ b/components/engine/vendor/github.com/docker/libnetwork/diagnose/server.go @@ -0,0 +1,228 @@ +package diagnose + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "sync" + "sync/atomic" + + stackdump "github.com/docker/docker/pkg/signal" + "github.com/docker/libnetwork/common" + "github.com/sirupsen/logrus" +) + +// HTTPHandlerFunc TODO +type HTTPHandlerFunc func(interface{}, http.ResponseWriter, *http.Request) + +type httpHandlerCustom struct { + ctx interface{} + F func(interface{}, http.ResponseWriter, *http.Request) +} + +// ServeHTTP TODO +func (h httpHandlerCustom) ServeHTTP(w http.ResponseWriter, r *http.Request) { + h.F(h.ctx, w, r) +} + +var diagPaths2Func = map[string]HTTPHandlerFunc{ + "/": notImplemented, + "/help": help, + "/ready": ready, + "/stackdump": stackTrace, +} + +// Server when the debug is enabled exposes a +// This data structure is protected by the Agent mutex so does not require and additional mutex here +type Server struct { + enable int32 + srv *http.Server + port int + mux *http.ServeMux + registeredHanders map[string]bool + sync.Mutex +} + +// New creates a new diagnose server +func New() *Server { + return &Server{ + registeredHanders: make(map[string]bool), + } +} + +// Init initialize the mux for the http handling and register the base hooks +func (s *Server) Init() { + s.mux = http.NewServeMux() + + // Register local handlers + s.RegisterHandler(s, diagPaths2Func) +} + +// RegisterHandler allows to register new handlers to the mux and to a specific path +func (s *Server) RegisterHandler(ctx interface{}, hdlrs map[string]HTTPHandlerFunc) { + s.Lock() + defer s.Unlock() + for path, fun := range hdlrs { + if _, ok := s.registeredHanders[path]; ok { + continue + } + s.mux.Handle(path, httpHandlerCustom{ctx, fun}) + s.registeredHanders[path] = true + } +} + +// ServeHTTP this is the method called bu the ListenAndServe, and is needed to allow us to +// use our custom mux +func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + s.mux.ServeHTTP(w, r) +} + +// EnableDebug opens a TCP socket to debug the passed network DB +func (s *Server) EnableDebug(ip string, port int) { + s.Lock() + defer s.Unlock() + + s.port = port + + if s.enable == 1 { + logrus.Info("The server is already up and running") + return + } + + logrus.Infof("Starting the diagnose server listening on %d for commands", port) + srv := &http.Server{Addr: fmt.Sprintf("127.0.0.1:%d", port), Handler: s} + s.srv = srv + s.enable = 1 + go func(n *Server) { + // Ingore ErrServerClosed that is returned on the Shutdown call + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + logrus.Errorf("ListenAndServe error: %s", err) + atomic.SwapInt32(&n.enable, 0) + } + }(s) + +} + +// DisableDebug stop the dubug and closes the tcp socket +func (s *Server) DisableDebug() { + s.Lock() + defer s.Unlock() + + s.srv.Shutdown(context.Background()) + s.srv = nil + s.enable = 0 + logrus.Info("Disabling the diagnose server") +} + +// IsDebugEnable returns true when the debug is enabled +func (s *Server) IsDebugEnable() bool { + s.Lock() + defer s.Unlock() + return s.enable == 1 +} + +func notImplemented(ctx interface{}, w http.ResponseWriter, r *http.Request) { + r.ParseForm() + _, json := ParseHTTPFormOptions(r) + rsp := WrongCommand("not implemented", fmt.Sprintf("URL path: %s no method implemented check /help\n", r.URL.Path)) + + // audit logs + log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) + log.Info("command not implemented done") + + HTTPReply(w, rsp, json) +} + +func help(ctx interface{}, w http.ResponseWriter, r *http.Request) { + r.ParseForm() + _, json := ParseHTTPFormOptions(r) + + // audit logs + log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) + log.Info("help done") + + n, ok := ctx.(*Server) + var result string + if ok { + for path := range n.registeredHanders { + result += fmt.Sprintf("%s\n", path) + } + HTTPReply(w, CommandSucceed(&StringCmd{Info: result}), json) + } +} + +func ready(ctx interface{}, w http.ResponseWriter, r *http.Request) { + r.ParseForm() + _, json := ParseHTTPFormOptions(r) + + // audit logs + log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) + log.Info("ready done") + HTTPReply(w, CommandSucceed(&StringCmd{Info: "OK"}), json) +} + +func stackTrace(ctx interface{}, w http.ResponseWriter, r *http.Request) { + r.ParseForm() + _, json := ParseHTTPFormOptions(r) + + // audit logs + log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) + log.Info("stack trace") + + path, err := stackdump.DumpStacks("/tmp/") + if err != nil { + log.WithError(err).Error("failed to write goroutines dump") + HTTPReply(w, FailCommand(err), json) + } else { + log.Info("stack trace done") + HTTPReply(w, CommandSucceed(&StringCmd{Info: fmt.Sprintf("goroutine stacks written to %s", path)}), json) + } +} + +// DebugHTTPForm helper to print the form url parameters +func DebugHTTPForm(r *http.Request) { + for k, v := range r.Form { + logrus.Debugf("Form[%q] = %q\n", k, v) + } +} + +// JSONOutput contains details on JSON output printing +type JSONOutput struct { + enable bool + prettyPrint bool +} + +// ParseHTTPFormOptions easily parse the JSON printing options +func ParseHTTPFormOptions(r *http.Request) (bool, *JSONOutput) { + _, unsafe := r.Form["unsafe"] + v, json := r.Form["json"] + var pretty bool + if len(v) > 0 { + pretty = v[0] == "pretty" + } + return unsafe, &JSONOutput{enable: json, prettyPrint: pretty} +} + +// HTTPReply helper function that takes care of sending the message out +func HTTPReply(w http.ResponseWriter, r *HTTPResult, j *JSONOutput) (int, error) { + var response []byte + if j.enable { + w.Header().Set("Content-Type", "application/json") + var err error + if j.prettyPrint { + response, err = json.MarshalIndent(r, "", " ") + if err != nil { + response, _ = json.MarshalIndent(FailCommand(err), "", " ") + } + } else { + response, err = json.Marshal(r) + if err != nil { + response, _ = json.Marshal(FailCommand(err)) + } + } + } else { + response = []byte(r.String()) + } + return fmt.Fprint(w, string(response)) +} diff --git a/components/engine/vendor/github.com/docker/libnetwork/diagnose/types.go b/components/engine/vendor/github.com/docker/libnetwork/diagnose/types.go new file mode 100644 index 0000000000..982c54a2b2 --- /dev/null +++ b/components/engine/vendor/github.com/docker/libnetwork/diagnose/types.go @@ -0,0 +1,122 @@ +package diagnose + +import "fmt" + +// StringInterface interface that has to be implemented by messages +type StringInterface interface { + String() string +} + +// CommandSucceed creates a success message +func CommandSucceed(result StringInterface) *HTTPResult { + return &HTTPResult{ + Message: "OK", + Details: result, + } +} + +// FailCommand creates a failure message with error +func FailCommand(err error) *HTTPResult { + return &HTTPResult{ + Message: "FAIL", + Details: &ErrorCmd{Error: err.Error()}, + } +} + +// WrongCommand creates a wrong command response +func WrongCommand(message, usage string) *HTTPResult { + return &HTTPResult{ + Message: message, + Details: &UsageCmd{Usage: usage}, + } +} + +// HTTPResult Diagnose Server HTTP result operation +type HTTPResult struct { + Message string `json:"message"` + Details StringInterface `json:"details"` +} + +func (h *HTTPResult) String() string { + rsp := h.Message + if h.Details != nil { + rsp += "\n" + h.Details.String() + } + return rsp +} + +// UsageCmd command with usage field +type UsageCmd struct { + Usage string `json:"usage"` +} + +func (u *UsageCmd) String() string { + return "Usage: " + u.Usage +} + +// StringCmd command with info string +type StringCmd struct { + Info string `json:"info"` +} + +func (s *StringCmd) String() string { + return s.Info +} + +// ErrorCmd command with error +type ErrorCmd struct { + Error string `json:"error"` +} + +func (e *ErrorCmd) String() string { + return "Error: " + e.Error +} + +// TableObj network db table object +type TableObj struct { + Length int `json:"size"` + Elements []StringInterface `json:"entries"` +} + +func (t *TableObj) String() string { + output := fmt.Sprintf("total entries: %d\n", t.Length) + for _, e := range t.Elements { + output += e.String() + } + return output +} + +// PeerEntryObj entry in the networkdb peer table +type PeerEntryObj struct { + Index int `json:"-"` + Name string `json:"-=name"` + IP string `json:"ip"` +} + +func (p *PeerEntryObj) String() string { + return fmt.Sprintf("%d) %s -> %s\n", p.Index, p.Name, p.IP) +} + +// TableEntryObj network db table entry object +type TableEntryObj struct { + Index int `json:"-"` + Key string `json:"key"` + Value string `json:"value"` + Owner string `json:"owner"` +} + +func (t *TableEntryObj) String() string { + return fmt.Sprintf("%d) k:`%s` -> v:`%s` owner:`%s`\n", t.Index, t.Key, t.Value, t.Owner) +} + +// TableEndpointsResult fully typed message for proper unmarshaling on the client side +type TableEndpointsResult struct { + TableObj + Elements []TableEntryObj `json:"entries"` +} + +// TablePeersResult fully typed message for proper unmarshaling on the client side +type TablePeersResult struct { + TableObj + Elements []PeerEntryObj `json:"entries"` +} diff --git a/components/engine/vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go b/components/engine/vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go index 1742c8df31..1fa8f0e921 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go +++ b/components/engine/vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go @@ -42,6 +42,14 @@ const ( DefaultGatewayV6AuxKey = "DefaultGatewayIPv6" ) +type defaultBridgeNetworkConflict struct { + ID string +} + +func (d defaultBridgeNetworkConflict) Error() string { + return fmt.Sprintf("Stale default bridge network %s", d.ID) +} + type iptableCleanFunc func() error type iptablesCleanFuncs []iptableCleanFunc @@ -137,6 +145,7 @@ type driver struct { networks map[string]*bridgeNetwork store datastore.DataStore nlh *netlink.Handle + configNetwork sync.Mutex sync.Mutex } @@ -322,41 +331,6 @@ func (n *bridgeNetwork) isolateNetwork(others []*bridgeNetwork, enable bool) err return nil } -// Checks whether this network's configuration for the network with this id conflicts with any of the passed networks -func (c *networkConfiguration) conflictsWithNetworks(id string, others []*bridgeNetwork) error { - for _, nw := range others { - - nw.Lock() - nwID := nw.id - nwConfig := nw.config - nwBridge := nw.bridge - nw.Unlock() - - if nwID == id { - continue - } - // Verify the name (which may have been set by newInterface()) does not conflict with - // existing bridge interfaces. Ironically the system chosen name gets stored in the config... - // Basically we are checking if the two original configs were both empty. - if nwConfig.BridgeName == c.BridgeName { - return types.ForbiddenErrorf("conflicts with network %s (%s) by bridge name", nwID, nwConfig.BridgeName) - } - // If this network config specifies the AddressIPv4, we need - // to make sure it does not conflict with any previously allocated - // bridges. This could not be completely caught by the config conflict - // check, because networks which config does not specify the AddressIPv4 - // get their address and subnet selected by the driver (see electBridgeIPv4()) - if c.AddressIPv4 != nil && nwBridge.bridgeIPv4 != nil { - if nwBridge.bridgeIPv4.Contains(c.AddressIPv4.IP) || - c.AddressIPv4.Contains(nwBridge.bridgeIPv4.IP) { - return types.ForbiddenErrorf("conflicts with network %s (%s) by ip network", nwID, nwConfig.BridgeName) - } - } - } - - return nil -} - func (d *driver) configure(option map[string]interface{}) error { var ( config *configuration @@ -602,11 +576,27 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d return err } - err = config.processIPAM(id, ipV4Data, ipV6Data) - if err != nil { + if err = config.processIPAM(id, ipV4Data, ipV6Data); err != nil { return err } + // start the critical section, from this point onward we are dealing with the list of networks + // so to be consistent we cannot allow that the list changes + d.configNetwork.Lock() + defer d.configNetwork.Unlock() + + // check network conflicts + if err = d.checkConflict(config); err != nil { + nerr, ok := err.(defaultBridgeNetworkConflict) + if !ok { + return err + } + // Got a conflict with a stale default network, clean that up and continue + logrus.Warn(nerr) + d.deleteNetwork(nerr.ID) + } + + // there is no conflict, now create the network if err = d.createNetwork(config); err != nil { return err } @@ -614,33 +604,47 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d return d.storeUpdate(config) } +func (d *driver) checkConflict(config *networkConfiguration) error { + networkList := d.getNetworks() + for _, nw := range networkList { + nw.Lock() + nwConfig := nw.config + nw.Unlock() + if err := nwConfig.Conflicts(config); err != nil { + if config.DefaultBridge { + // We encountered and identified a stale default network + // We must delete it as libnetwork is the source of truth + // The default network being created must be the only one + // This can happen only from docker 1.12 on ward + logrus.Infof("Found stale default bridge network %s (%s)", nwConfig.ID, nwConfig.BridgeName) + return defaultBridgeNetworkConflict{nwConfig.ID} + } + + return types.ForbiddenErrorf("cannot create network %s (%s): conflicts with network %s (%s): %s", + config.ID, config.BridgeName, nwConfig.ID, nwConfig.BridgeName, err.Error()) + } + } + return nil +} + func (d *driver) createNetwork(config *networkConfiguration) error { var err error defer osl.InitOSContext()() networkList := d.getNetworks() - for i, nw := range networkList { - nw.Lock() - nwConfig := nw.config - nw.Unlock() - if err := nwConfig.Conflicts(config); err != nil { - if config.DefaultBridge { - // We encountered and identified a stale default network - // We must delete it as libnetwork is the source of thruth - // The default network being created must be the only one - // This can happen only from docker 1.12 on ward - logrus.Infof("Removing stale default bridge network %s (%s)", nwConfig.ID, nwConfig.BridgeName) - if err := d.DeleteNetwork(nwConfig.ID); err != nil { - logrus.Warnf("Failed to remove stale default network: %s (%s): %v. Will remove from store.", nwConfig.ID, nwConfig.BridgeName, err) - d.storeDelete(nwConfig) - } - networkList = append(networkList[:i], networkList[i+1:]...) - } else { - return types.ForbiddenErrorf("cannot create network %s (%s): conflicts with network %s (%s): %s", - config.ID, config.BridgeName, nwConfig.ID, nwConfig.BridgeName, err.Error()) - } - } + + // Initialize handle when needed + d.Lock() + if d.nlh == nil { + d.nlh = ns.NlHandle() + } + d.Unlock() + + // Create or retrieve the bridge L3 interface + bridgeIface, err := newInterface(d.nlh, config) + if err != nil { + return err } // Create and set network handler in driver @@ -649,6 +653,7 @@ func (d *driver) createNetwork(config *networkConfiguration) error { endpoints: make(map[string]*bridgeEndpoint), config: config, portMapper: portmapper.New(d.config.UserlandProxyPath), + bridge: bridgeIface, driver: d, } @@ -665,35 +670,15 @@ func (d *driver) createNetwork(config *networkConfiguration) error { } }() - // Initialize handle when needed - d.Lock() - if d.nlh == nil { - d.nlh = ns.NlHandle() - } - d.Unlock() - - // Create or retrieve the bridge L3 interface - bridgeIface, err := newInterface(d.nlh, config) - if err != nil { - return err - } - network.bridge = bridgeIface - - // Verify the network configuration does not conflict with previously installed - // networks. This step is needed now because driver might have now set the bridge - // name on this config struct. And because we need to check for possible address - // conflicts, so we need to check against operationa lnetworks. - if err = config.conflictsWithNetworks(config.ID, networkList); err != nil { - return err - } - + // Add inter-network communication rules. setupNetworkIsolationRules := func(config *networkConfiguration, i *bridgeInterface) error { if err := network.isolateNetwork(networkList, true); err != nil { - if err := network.isolateNetwork(networkList, false); err != nil { + if err = network.isolateNetwork(networkList, false); err != nil { logrus.Warnf("Failed on removing the inter-network iptables rules on cleanup: %v", err) } return err } + // register the cleanup function network.registerIptCleanFunc(func() error { nwList := d.getNetworks() return network.isolateNetwork(nwList, false) @@ -767,10 +752,17 @@ func (d *driver) createNetwork(config *networkConfiguration) error { } func (d *driver) DeleteNetwork(nid string) error { + + d.configNetwork.Lock() + defer d.configNetwork.Unlock() + + return d.deleteNetwork(nid) +} + +func (d *driver) deleteNetwork(nid string) error { var err error defer osl.InitOSContext()() - // Get network handler and remove it from driver d.Lock() n, ok := d.networks[nid] @@ -814,12 +806,6 @@ func (d *driver) DeleteNetwork(nid string) error { } }() - // Sanity check - if n == nil { - err = driverapi.ErrNoNetwork(nid) - return err - } - switch config.BridgeIfaceCreator { case ifaceCreatedByLibnetwork, ifaceCreatorUnknown: // We only delete the bridge if it was created by the bridge driver and diff --git a/components/engine/vendor/github.com/docker/libnetwork/drivers/bridge/setup_firewalld.go b/components/engine/vendor/github.com/docker/libnetwork/drivers/bridge/setup_firewalld.go index fc45a7e983..50cbdb1ddc 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/drivers/bridge/setup_firewalld.go +++ b/components/engine/vendor/github.com/docker/libnetwork/drivers/bridge/setup_firewalld.go @@ -9,7 +9,7 @@ func (n *bridgeNetwork) setupFirewalld(config *networkConfiguration, i *bridgeIn d.Unlock() // Sanity check. - if driverConfig.EnableIPTables == false { + if !driverConfig.EnableIPTables { return IPTableCfgError(config.BridgeName) } diff --git a/components/engine/vendor/github.com/docker/libnetwork/drivers/overlay/ov_network.go b/components/engine/vendor/github.com/docker/libnetwork/drivers/overlay/ov_network.go index 3fbfccf007..11314170b1 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/drivers/overlay/ov_network.go +++ b/components/engine/vendor/github.com/docker/libnetwork/drivers/overlay/ov_network.go @@ -696,6 +696,12 @@ func (n *network) initSandbox(restore bool) error { var nlSock *nl.NetlinkSocket sbox.InvokeFunc(func() { nlSock, err = nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_NEIGH) + if err != nil { + return + } + // set the receive timeout to not remain stuck on the RecvFrom if the fd gets closed + tv := syscall.NsecToTimeval(soTimeout.Nanoseconds()) + err = nlSock.SetReceiveTimeout(&tv) }) n.setNetlinkSocket(nlSock) @@ -721,6 +727,11 @@ func (n *network) watchMiss(nlSock *nl.NetlinkSocket) { // The netlink socket got closed, simply exit to not leak this goroutine return } + // When the receive timeout expires the receive will return EAGAIN + if err == syscall.EAGAIN { + // we continue here to avoid spam for timeouts + continue + } logrus.Errorf("Failed to receive from netlink: %v ", err) continue } diff --git a/components/engine/vendor/github.com/docker/libnetwork/ipvs/ipvs.go b/components/engine/vendor/github.com/docker/libnetwork/ipvs/ipvs.go index ebcdd808c3..effbb716eb 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/ipvs/ipvs.go +++ b/components/engine/vendor/github.com/docker/libnetwork/ipvs/ipvs.go @@ -5,12 +5,19 @@ package ipvs import ( "net" "syscall" + "time" "fmt" + "github.com/vishvananda/netlink/nl" "github.com/vishvananda/netns" ) +const ( + netlinkRecvSocketsTimeout = 3 * time.Second + netlinkSendSocketTimeout = 30 * time.Second +) + // Service defines an IPVS service in its entirety. type Service struct { // Virtual service address. @@ -82,6 +89,15 @@ func New(path string) (*Handle, error) { if err != nil { return nil, err } + // Add operation timeout to avoid deadlocks + tv := syscall.NsecToTimeval(netlinkSendSocketTimeout.Nanoseconds()) + if err := sock.SetSendTimeout(&tv); err != nil { + return nil, err + } + tv = syscall.NsecToTimeval(netlinkRecvSocketsTimeout.Nanoseconds()) + if err := sock.SetReceiveTimeout(&tv); err != nil { + return nil, err + } return &Handle{sock: sock}, nil } diff --git a/components/engine/vendor/github.com/docker/libnetwork/ipvs/netlink.go b/components/engine/vendor/github.com/docker/libnetwork/ipvs/netlink.go index 2089283d14..c062a1789d 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/ipvs/netlink.go +++ b/components/engine/vendor/github.com/docker/libnetwork/ipvs/netlink.go @@ -203,10 +203,6 @@ func newGenlRequest(familyID int, cmd uint8) *nl.NetlinkRequest { } func execute(s *nl.NetlinkSocket, req *nl.NetlinkRequest, resType uint16) ([][]byte, error) { - var ( - err error - ) - if err := s.Send(req); err != nil { return nil, err } @@ -222,6 +218,13 @@ done: for { msgs, err := s.Receive() if err != nil { + if s.GetFd() == -1 { + return nil, fmt.Errorf("Socket got closed on receive") + } + if err == syscall.EAGAIN { + // timeout fired + continue + } return nil, err } for _, m := range msgs { diff --git a/components/engine/vendor/github.com/docker/libnetwork/networkdb/event_delegate.go b/components/engine/vendor/github.com/docker/libnetwork/networkdb/event_delegate.go index 6075718a63..6e11805610 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/networkdb/event_delegate.go +++ b/components/engine/vendor/github.com/docker/libnetwork/networkdb/event_delegate.go @@ -72,7 +72,7 @@ func (e *eventDelegate) NotifyLeave(mn *memberlist.Node) { // If the node instead left because was going down, then it makes sense to just delete all its state e.nDB.Lock() defer e.nDB.Unlock() - e.nDB.deleteNetworkEntriesForNode(mn.Name) + e.nDB.deleteNodeFromNetworks(mn.Name) e.nDB.deleteNodeTableEntries(mn.Name) if n, ok := e.nDB.nodes[mn.Name]; ok { delete(e.nDB.nodes, mn.Name) diff --git a/components/engine/vendor/github.com/docker/libnetwork/networkdb/networkdb.go b/components/engine/vendor/github.com/docker/libnetwork/networkdb/networkdb.go index 025e0ca843..9ec6beca35 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/networkdb/networkdb.go +++ b/components/engine/vendor/github.com/docker/libnetwork/networkdb/networkdb.go @@ -401,17 +401,23 @@ func (nDB *NetworkDB) UpdateEntry(tname, nid, key string, value []byte) error { return nil } +// TableElem elem +type TableElem struct { + Value []byte + owner string +} + // GetTableByNetwork walks the networkdb by the give table and network id and // returns a map of keys and values -func (nDB *NetworkDB) GetTableByNetwork(tname, nid string) map[string]interface{} { - entries := make(map[string]interface{}) +func (nDB *NetworkDB) GetTableByNetwork(tname, nid string) map[string]*TableElem { + entries := make(map[string]*TableElem) nDB.indexes[byTable].WalkPrefix(fmt.Sprintf("/%s/%s", tname, nid), func(k string, v interface{}) bool { entry := v.(*entry) if entry.deleting { return false } key := k[strings.LastIndex(k, "/")+1:] - entries[key] = entry.value + entries[key] = &TableElem{Value: entry.value, owner: entry.node} return false }) return entries @@ -445,7 +451,7 @@ func (nDB *NetworkDB) DeleteEntry(tname, nid, key string) error { return nil } -func (nDB *NetworkDB) deleteNetworkEntriesForNode(deletedNode string) { +func (nDB *NetworkDB) deleteNodeFromNetworks(deletedNode string) { for nid, nodes := range nDB.networkNodes { updatedNodes := make([]string, 0, len(nodes)) for _, node := range nodes { @@ -547,7 +553,9 @@ func (nDB *NetworkDB) deleteNodeTableEntries(node string) { nDB.deleteEntry(nid, tname, key) - nDB.broadcaster.Write(makeEvent(opDelete, tname, nid, key, oldEntry.value)) + if !oldEntry.deleting { + nDB.broadcaster.Write(makeEvent(opDelete, tname, nid, key, oldEntry.value)) + } return false }) } diff --git a/components/engine/vendor/github.com/docker/libnetwork/networkdb/networkdbdiagnose.go b/components/engine/vendor/github.com/docker/libnetwork/networkdb/networkdbdiagnose.go index 01429a5381..3c6032b4ea 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/networkdb/networkdbdiagnose.go +++ b/components/engine/vendor/github.com/docker/libnetwork/networkdb/networkdbdiagnose.go @@ -1,17 +1,19 @@ package networkdb import ( + "encoding/base64" "fmt" "net/http" "strings" - stackdump "github.com/docker/docker/pkg/signal" + "github.com/docker/libnetwork/common" "github.com/docker/libnetwork/diagnose" "github.com/sirupsen/logrus" ) const ( missingParameter = "missing parameter" + dbNotAvailable = "database not available" ) // NetDbPaths2Func TODO @@ -26,14 +28,21 @@ var NetDbPaths2Func = map[string]diagnose.HTTPHandlerFunc{ "/deleteentry": dbDeleteEntry, "/getentry": dbGetEntry, "/gettable": dbGetTable, - "/dump": dbStackTrace, } func dbJoin(ctx interface{}, w http.ResponseWriter, r *http.Request) { r.ParseForm() diagnose.DebugHTTPForm(r) + _, json := diagnose.ParseHTTPFormOptions(r) + + // audit logs + log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) + log.Info("join cluster") + if len(r.Form["members"]) < 1 { - diagnose.HTTPReplyError(w, missingParameter, fmt.Sprintf("%s?members=ip1,ip2,...", r.URL.Path)) + rsp := diagnose.WrongCommand(missingParameter, fmt.Sprintf("%s?members=ip1,ip2,...", r.URL.Path)) + log.Error("join cluster failed, wrong input") + diagnose.HTTPReply(w, rsp, json) return } @@ -41,51 +50,88 @@ func dbJoin(ctx interface{}, w http.ResponseWriter, r *http.Request) { if ok { err := nDB.Join(strings.Split(r.Form["members"][0], ",")) if err != nil { - fmt.Fprintf(w, "%s error in the DB join %s\n", r.URL.Path, err) + rsp := diagnose.FailCommand(fmt.Errorf("%s error in the DB join %s", r.URL.Path, err)) + log.WithError(err).Error("join cluster failed") + diagnose.HTTPReply(w, rsp, json) return } - fmt.Fprintf(w, "OK\n") + log.Info("join cluster done") + diagnose.HTTPReply(w, diagnose.CommandSucceed(nil), json) + return } + diagnose.HTTPReply(w, diagnose.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json) } func dbPeers(ctx interface{}, w http.ResponseWriter, r *http.Request) { r.ParseForm() diagnose.DebugHTTPForm(r) + _, json := diagnose.ParseHTTPFormOptions(r) + + // audit logs + log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) + log.Info("network peers") + if len(r.Form["nid"]) < 1 { - diagnose.HTTPReplyError(w, missingParameter, fmt.Sprintf("%s?nid=test", r.URL.Path)) + rsp := diagnose.WrongCommand(missingParameter, fmt.Sprintf("%s?nid=test", r.URL.Path)) + log.Error("network peers failed, wrong input") + diagnose.HTTPReply(w, rsp, json) return } nDB, ok := ctx.(*NetworkDB) if ok { peers := nDB.Peers(r.Form["nid"][0]) - fmt.Fprintf(w, "Network:%s Total peers: %d\n", r.Form["nid"], len(peers)) + rsp := &diagnose.TableObj{Length: len(peers)} for i, peerInfo := range peers { - fmt.Fprintf(w, "%d) %s -> %s\n", i, peerInfo.Name, peerInfo.IP) + rsp.Elements = append(rsp.Elements, &diagnose.PeerEntryObj{Index: i, Name: peerInfo.Name, IP: peerInfo.IP}) } + log.WithField("response", fmt.Sprintf("%+v", rsp)).Info("network peers done") + diagnose.HTTPReply(w, diagnose.CommandSucceed(rsp), json) + return } + diagnose.HTTPReply(w, diagnose.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json) } func dbClusterPeers(ctx interface{}, w http.ResponseWriter, r *http.Request) { + r.ParseForm() + diagnose.DebugHTTPForm(r) + _, json := diagnose.ParseHTTPFormOptions(r) + + // audit logs + log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) + log.Info("cluster peers") + nDB, ok := ctx.(*NetworkDB) if ok { peers := nDB.ClusterPeers() - fmt.Fprintf(w, "Total peers: %d\n", len(peers)) + rsp := &diagnose.TableObj{Length: len(peers)} for i, peerInfo := range peers { - fmt.Fprintf(w, "%d) %s -> %s\n", i, peerInfo.Name, peerInfo.IP) + rsp.Elements = append(rsp.Elements, &diagnose.PeerEntryObj{Index: i, Name: peerInfo.Name, IP: peerInfo.IP}) } + log.WithField("response", fmt.Sprintf("%+v", rsp)).Info("cluster peers done") + diagnose.HTTPReply(w, diagnose.CommandSucceed(rsp), json) + return } + diagnose.HTTPReply(w, diagnose.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json) } func dbCreateEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) { r.ParseForm() diagnose.DebugHTTPForm(r) + unsafe, json := diagnose.ParseHTTPFormOptions(r) + + // audit logs + log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) + log.Info("create entry") + if len(r.Form["tname"]) < 1 || len(r.Form["nid"]) < 1 || len(r.Form["key"]) < 1 || len(r.Form["value"]) < 1 { - diagnose.HTTPReplyError(w, missingParameter, fmt.Sprintf("%s?tname=table_name&nid=network_id&key=k&value=v", r.URL.Path)) + rsp := diagnose.WrongCommand(missingParameter, fmt.Sprintf("%s?tname=table_name&nid=network_id&key=k&value=v", r.URL.Path)) + log.Error("create entry failed, wrong input") + diagnose.HTTPReply(w, rsp, json) return } @@ -93,25 +139,48 @@ func dbCreateEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) { nid := r.Form["nid"][0] key := r.Form["key"][0] value := r.Form["value"][0] + decodedValue := []byte(value) + if !unsafe { + var err error + decodedValue, err = base64.StdEncoding.DecodeString(value) + if err != nil { + log.WithError(err).Error("create entry failed") + diagnose.HTTPReply(w, diagnose.FailCommand(err), json) + return + } + } nDB, ok := ctx.(*NetworkDB) if ok { - if err := nDB.CreateEntry(tname, nid, key, []byte(value)); err != nil { - diagnose.HTTPReplyError(w, err.Error(), "") + if err := nDB.CreateEntry(tname, nid, key, decodedValue); err != nil { + rsp := diagnose.FailCommand(err) + diagnose.HTTPReply(w, rsp, json) + log.WithError(err).Error("create entry failed") return } - fmt.Fprintf(w, "OK\n") + log.Info("create entry done") + diagnose.HTTPReply(w, diagnose.CommandSucceed(nil), json) + return } + diagnose.HTTPReply(w, diagnose.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json) } func dbUpdateEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) { r.ParseForm() diagnose.DebugHTTPForm(r) + unsafe, json := diagnose.ParseHTTPFormOptions(r) + + // audit logs + log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) + log.Info("update entry") + if len(r.Form["tname"]) < 1 || len(r.Form["nid"]) < 1 || len(r.Form["key"]) < 1 || len(r.Form["value"]) < 1 { - diagnose.HTTPReplyError(w, missingParameter, fmt.Sprintf("%s?tname=table_name&nid=network_id&key=k&value=v", r.URL.Path)) + rsp := diagnose.WrongCommand(missingParameter, fmt.Sprintf("%s?tname=table_name&nid=network_id&key=k&value=v", r.URL.Path)) + log.Error("update entry failed, wrong input") + diagnose.HTTPReply(w, rsp, json) return } @@ -119,24 +188,46 @@ func dbUpdateEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) { nid := r.Form["nid"][0] key := r.Form["key"][0] value := r.Form["value"][0] + decodedValue := []byte(value) + if !unsafe { + var err error + decodedValue, err = base64.StdEncoding.DecodeString(value) + if err != nil { + log.WithError(err).Error("update entry failed") + diagnose.HTTPReply(w, diagnose.FailCommand(err), json) + return + } + } nDB, ok := ctx.(*NetworkDB) if ok { - if err := nDB.UpdateEntry(tname, nid, key, []byte(value)); err != nil { - diagnose.HTTPReplyError(w, err.Error(), "") + if err := nDB.UpdateEntry(tname, nid, key, decodedValue); err != nil { + log.WithError(err).Error("update entry failed") + diagnose.HTTPReply(w, diagnose.FailCommand(err), json) return } - fmt.Fprintf(w, "OK\n") + log.Info("update entry done") + diagnose.HTTPReply(w, diagnose.CommandSucceed(nil), json) + return } + diagnose.HTTPReply(w, diagnose.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json) } func dbDeleteEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) { r.ParseForm() diagnose.DebugHTTPForm(r) + _, json := diagnose.ParseHTTPFormOptions(r) + + // audit logs + log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) + log.Info("delete entry") + if len(r.Form["tname"]) < 1 || len(r.Form["nid"]) < 1 || len(r.Form["key"]) < 1 { - diagnose.HTTPReplyError(w, missingParameter, fmt.Sprintf("%s?tname=table_name&nid=network_id&key=k", r.URL.Path)) + rsp := diagnose.WrongCommand(missingParameter, fmt.Sprintf("%s?tname=table_name&nid=network_id&key=k", r.URL.Path)) + log.Error("delete entry failed, wrong input") + diagnose.HTTPReply(w, rsp, json) return } @@ -148,20 +239,32 @@ func dbDeleteEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) { if ok { err := nDB.DeleteEntry(tname, nid, key) if err != nil { - diagnose.HTTPReplyError(w, err.Error(), "") + log.WithError(err).Error("delete entry failed") + diagnose.HTTPReply(w, diagnose.FailCommand(err), json) return } - fmt.Fprintf(w, "OK\n") + log.Info("delete entry done") + diagnose.HTTPReply(w, diagnose.CommandSucceed(nil), json) + return } + diagnose.HTTPReply(w, diagnose.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json) } func dbGetEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) { r.ParseForm() diagnose.DebugHTTPForm(r) + unsafe, json := diagnose.ParseHTTPFormOptions(r) + + // audit logs + log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) + log.Info("get entry") + if len(r.Form["tname"]) < 1 || len(r.Form["nid"]) < 1 || len(r.Form["key"]) < 1 { - diagnose.HTTPReplyError(w, missingParameter, fmt.Sprintf("%s?tname=table_name&nid=network_id&key=k", r.URL.Path)) + rsp := diagnose.WrongCommand(missingParameter, fmt.Sprintf("%s?tname=table_name&nid=network_id&key=k", r.URL.Path)) + log.Error("get entry failed, wrong input") + diagnose.HTTPReply(w, rsp, json) return } @@ -173,18 +276,39 @@ func dbGetEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) { if ok { value, err := nDB.GetEntry(tname, nid, key) if err != nil { - diagnose.HTTPReplyError(w, err.Error(), "") + log.WithError(err).Error("get entry failed") + diagnose.HTTPReply(w, diagnose.FailCommand(err), json) return } - fmt.Fprintf(w, "key:`%s` value:`%s`\n", key, string(value)) + + var encodedValue string + if unsafe { + encodedValue = string(value) + } else { + encodedValue = base64.StdEncoding.EncodeToString(value) + } + + rsp := &diagnose.TableEntryObj{Key: key, Value: encodedValue} + log.WithField("response", fmt.Sprintf("%+v", rsp)).Info("update entry done") + diagnose.HTTPReply(w, diagnose.CommandSucceed(rsp), json) + return } + diagnose.HTTPReply(w, diagnose.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json) } func dbJoinNetwork(ctx interface{}, w http.ResponseWriter, r *http.Request) { r.ParseForm() diagnose.DebugHTTPForm(r) + _, json := diagnose.ParseHTTPFormOptions(r) + + // audit logs + log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) + log.Info("join network") + if len(r.Form["nid"]) < 1 { - diagnose.HTTPReplyError(w, missingParameter, fmt.Sprintf("%s?nid=network_id", r.URL.Path)) + rsp := diagnose.WrongCommand(missingParameter, fmt.Sprintf("%s?nid=network_id", r.URL.Path)) + log.Error("join network failed, wrong input") + diagnose.HTTPReply(w, rsp, json) return } @@ -193,18 +317,30 @@ func dbJoinNetwork(ctx interface{}, w http.ResponseWriter, r *http.Request) { nDB, ok := ctx.(*NetworkDB) if ok { if err := nDB.JoinNetwork(nid); err != nil { - diagnose.HTTPReplyError(w, err.Error(), "") + log.WithError(err).Error("join network failed") + diagnose.HTTPReply(w, diagnose.FailCommand(err), json) return } - fmt.Fprintf(w, "OK\n") + log.Info("join network done") + diagnose.HTTPReply(w, diagnose.CommandSucceed(nil), json) + return } + diagnose.HTTPReply(w, diagnose.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json) } func dbLeaveNetwork(ctx interface{}, w http.ResponseWriter, r *http.Request) { r.ParseForm() diagnose.DebugHTTPForm(r) + _, json := diagnose.ParseHTTPFormOptions(r) + + // audit logs + log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) + log.Info("leave network") + if len(r.Form["nid"]) < 1 { - diagnose.HTTPReplyError(w, missingParameter, fmt.Sprintf("%s?nid=network_id", r.URL.Path)) + rsp := diagnose.WrongCommand(missingParameter, fmt.Sprintf("%s?nid=network_id", r.URL.Path)) + log.Error("leave network failed, wrong input") + diagnose.HTTPReply(w, rsp, json) return } @@ -213,19 +349,31 @@ func dbLeaveNetwork(ctx interface{}, w http.ResponseWriter, r *http.Request) { nDB, ok := ctx.(*NetworkDB) if ok { if err := nDB.LeaveNetwork(nid); err != nil { - diagnose.HTTPReplyError(w, err.Error(), "") + log.WithError(err).Error("leave network failed") + diagnose.HTTPReply(w, diagnose.FailCommand(err), json) return } - fmt.Fprintf(w, "OK\n") + log.Info("leave network done") + diagnose.HTTPReply(w, diagnose.CommandSucceed(nil), json) + return } + diagnose.HTTPReply(w, diagnose.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json) } func dbGetTable(ctx interface{}, w http.ResponseWriter, r *http.Request) { r.ParseForm() diagnose.DebugHTTPForm(r) + unsafe, json := diagnose.ParseHTTPFormOptions(r) + + // audit logs + log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) + log.Info("get table") + if len(r.Form["tname"]) < 1 || len(r.Form["nid"]) < 1 { - diagnose.HTTPReplyError(w, missingParameter, fmt.Sprintf("%s?tname=table_name&nid=network_id", r.URL.Path)) + rsp := diagnose.WrongCommand(missingParameter, fmt.Sprintf("%s?tname=table_name&nid=network_id", r.URL.Path)) + log.Error("get table failed, wrong input") + diagnose.HTTPReply(w, rsp, json) return } @@ -235,20 +383,26 @@ func dbGetTable(ctx interface{}, w http.ResponseWriter, r *http.Request) { nDB, ok := ctx.(*NetworkDB) if ok { table := nDB.GetTableByNetwork(tname, nid) - fmt.Fprintf(w, "total elements: %d\n", len(table)) - i := 0 + rsp := &diagnose.TableObj{Length: len(table)} + var i = 0 for k, v := range table { - fmt.Fprintf(w, "%d) k:`%s` -> v:`%s`\n", i, k, string(v.([]byte))) - i++ + var encodedValue string + if unsafe { + encodedValue = string(v.Value) + } else { + encodedValue = base64.StdEncoding.EncodeToString(v.Value) + } + rsp.Elements = append(rsp.Elements, + &diagnose.TableEntryObj{ + Index: i, + Key: k, + Value: encodedValue, + Owner: v.owner, + }) } + log.WithField("response", fmt.Sprintf("%+v", rsp)).Info("get table done") + diagnose.HTTPReply(w, diagnose.CommandSucceed(rsp), json) + return } -} - -func dbStackTrace(ctx interface{}, w http.ResponseWriter, r *http.Request) { - path, err := stackdump.DumpStacks("/tmp/") - if err != nil { - logrus.WithError(err).Error("failed to write goroutines dump") - } else { - fmt.Fprintf(w, "goroutine stacks written to %s", path) - } + diagnose.HTTPReply(w, diagnose.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json) } diff --git a/components/engine/vendor/github.com/docker/libnetwork/vendor.conf b/components/engine/vendor/github.com/docker/libnetwork/vendor.conf index f00d8ced44..bdcb5a1a54 100644 --- a/components/engine/vendor/github.com/docker/libnetwork/vendor.conf +++ b/components/engine/vendor/github.com/docker/libnetwork/vendor.conf @@ -45,7 +45,7 @@ github.com/sirupsen/logrus v1.0.3 github.com/stretchr/testify dab07ac62d4905d3e48d17dc549c684ac3b7c15a github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16 github.com/ugorji/go f1f1a805ed361a0e078bb537e4ea78cd37dcf065 -github.com/vishvananda/netlink bd6d5de5ccef2d66b0a26177928d0d8895d7f969 +github.com/vishvananda/netlink b2de5d10e38ecce8607e6b438b6d174f389a004e github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25 golang.org/x/crypto 558b6879de74bc843225cde5686419267ff707ca golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6 From da56fc49673d3516c2e9ec2d39cc73768fe4b6bd Mon Sep 17 00:00:00 2001 From: Flavio Crisciani Date: Mon, 4 Dec 2017 11:38:20 -0800 Subject: [PATCH 3/3] Vendoring netlink library Signed-off-by: Flavio Crisciani Upstream-commit: 2e75d329773920713e63049ad1a00dfb64abf3c0 Component: engine --- components/engine/vendor.conf | 2 +- .../github.com/vishvananda/netlink/README.md | 10 +- .../vishvananda/netlink/addr_linux.go | 52 +++- .../vishvananda/netlink/bridge_linux.go | 115 +++++++ .../vishvananda/netlink/conntrack_linux.go | 45 ++- .../github.com/vishvananda/netlink/filter.go | 10 - .../vishvananda/netlink/filter_linux.go | 66 ++-- .../vishvananda/netlink/handle_linux.go | 41 ++- .../vishvananda/netlink/handle_unspecified.go | 4 + .../github.com/vishvananda/netlink/link.go | 26 ++ .../vishvananda/netlink/link_linux.go | 286 +++++++++++++++--- .../github.com/vishvananda/netlink/neigh.go | 1 + .../vishvananda/netlink/neigh_linux.go | 29 +- .../netlink/netlink_unspecified.go | 4 + .../vishvananda/netlink/nl/bridge_linux.go | 74 +++++ .../vishvananda/netlink/nl/conntrack_linux.go | 4 +- .../vishvananda/netlink/nl/link_linux.go | 36 ++- .../vishvananda/netlink/nl/nl_linux.go | 14 + .../vishvananda/netlink/qdisc_linux.go | 72 ++--- .../github.com/vishvananda/netlink/route.go | 62 +++- .../vishvananda/netlink/route_linux.go | 101 ++++++- .../github.com/vishvananda/netlink/rule.go | 1 + .../vishvananda/netlink/rule_linux.go | 3 + 23 files changed, 898 insertions(+), 160 deletions(-) create mode 100644 components/engine/vendor/github.com/vishvananda/netlink/bridge_linux.go create mode 100644 components/engine/vendor/github.com/vishvananda/netlink/nl/bridge_linux.go diff --git a/components/engine/vendor.conf b/components/engine/vendor.conf index f9cc8a7131..8c0b15a75f 100644 --- a/components/engine/vendor.conf +++ b/components/engine/vendor.conf @@ -42,7 +42,7 @@ github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa0733f7e github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870 github.com/docker/libkv 1d8431073ae03cdaedb198a89722f3aab6d418ef github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25 -github.com/vishvananda/netlink bd6d5de5ccef2d66b0a26177928d0d8895d7f969 +github.com/vishvananda/netlink b2de5d10e38ecce8607e6b438b6d174f389a004e github.com/BurntSushi/toml f706d00e3de6abe700c994cdd545a1a4915af060 github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374 github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d diff --git a/components/engine/vendor/github.com/vishvananda/netlink/README.md b/components/engine/vendor/github.com/vishvananda/netlink/README.md index 2367fae097..0b61be217e 100644 --- a/components/engine/vendor/github.com/vishvananda/netlink/README.md +++ b/components/engine/vendor/github.com/vishvananda/netlink/README.md @@ -38,15 +38,18 @@ Add a new bridge and add eth1 into it: package main import ( - "net" + "fmt" "github.com/vishvananda/netlink" ) func main() { la := netlink.NewLinkAttrs() la.Name = "foo" - mybridge := &netlink.Bridge{la}} - _ := netlink.LinkAdd(mybridge) + mybridge := &netlink.Bridge{LinkAttrs: la} + err := netlink.LinkAdd(mybridge) + if err != nil { + fmt.Printf("could not add %s: %v\n", la.Name, err) + } eth1, _ := netlink.LinkByName("eth1") netlink.LinkSetMaster(eth1, mybridge) } @@ -63,7 +66,6 @@ Add a new ip address to loopback: package main import ( - "net" "github.com/vishvananda/netlink" ) diff --git a/components/engine/vendor/github.com/vishvananda/netlink/addr_linux.go b/components/engine/vendor/github.com/vishvananda/netlink/addr_linux.go index f33242a7c2..8808b42d9b 100644 --- a/components/engine/vendor/github.com/vishvananda/netlink/addr_linux.go +++ b/components/engine/vendor/github.com/vishvananda/netlink/addr_linux.go @@ -2,7 +2,6 @@ package netlink import ( "fmt" - "log" "net" "strings" "syscall" @@ -65,7 +64,7 @@ func (h *Handle) addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error msg := nl.NewIfAddrmsg(family) msg.Index = uint32(base.Index) msg.Scope = uint8(addr.Scope) - prefixlen, _ := addr.Mask.Size() + prefixlen, masklen := addr.Mask.Size() msg.Prefixlen = uint8(prefixlen) req.AddData(msg) @@ -103,9 +102,14 @@ func (h *Handle) addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error } } - if addr.Broadcast != nil { - req.AddData(nl.NewRtAttr(syscall.IFA_BROADCAST, addr.Broadcast)) + if addr.Broadcast == nil { + calcBroadcast := make(net.IP, masklen/8) + for i := range localAddrData { + calcBroadcast[i] = localAddrData[i] | ^addr.Mask[i] + } + addr.Broadcast = calcBroadcast } + req.AddData(nl.NewRtAttr(syscall.IFA_BROADCAST, addr.Broadcast)) if addr.Label != "" { labelData := nl.NewRtAttr(syscall.IFA_LABEL, nl.ZeroTerminated(addr.Label)) @@ -232,16 +236,34 @@ type AddrUpdate struct { // AddrSubscribe takes a chan down which notifications will be sent // when addresses change. Close the 'done' chan to stop subscription. func AddrSubscribe(ch chan<- AddrUpdate, done <-chan struct{}) error { - return addrSubscribe(netns.None(), netns.None(), ch, done) + return addrSubscribeAt(netns.None(), netns.None(), ch, done, nil) } // AddrSubscribeAt works like AddrSubscribe plus it allows the caller // to choose the network namespace in which to subscribe (ns). func AddrSubscribeAt(ns netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}) error { - return addrSubscribe(ns, netns.None(), ch, done) + return addrSubscribeAt(ns, netns.None(), ch, done, nil) } -func addrSubscribe(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}) error { +// AddrSubscribeOptions contains a set of options to use with +// AddrSubscribeWithOptions. +type AddrSubscribeOptions struct { + Namespace *netns.NsHandle + ErrorCallback func(error) +} + +// AddrSubscribeWithOptions work like AddrSubscribe but enable to +// provide additional options to modify the behavior. Currently, the +// namespace can be provided as well as an error callback. +func AddrSubscribeWithOptions(ch chan<- AddrUpdate, done <-chan struct{}, options AddrSubscribeOptions) error { + if options.Namespace == nil { + none := netns.None() + options.Namespace = &none + } + return addrSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback) +} + +func addrSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}, cberr func(error)) error { s, err := nl.SubscribeAt(newNs, curNs, syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_IFADDR, syscall.RTNLGRP_IPV6_IFADDR) if err != nil { return err @@ -257,20 +279,26 @@ func addrSubscribe(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-cha for { msgs, err := s.Receive() if err != nil { - log.Printf("netlink.AddrSubscribe: Receive() error: %v", err) + if cberr != nil { + cberr(err) + } return } for _, m := range msgs { msgType := m.Header.Type if msgType != syscall.RTM_NEWADDR && msgType != syscall.RTM_DELADDR { - log.Printf("netlink.AddrSubscribe: bad message type: %d", msgType) - continue + if cberr != nil { + cberr(fmt.Errorf("bad message type: %d", msgType)) + } + return } addr, _, ifindex, err := parseAddr(m.Data) if err != nil { - log.Printf("netlink.AddrSubscribe: could not parse address: %v", err) - continue + if cberr != nil { + cberr(fmt.Errorf("could not parse address: %v", err)) + } + return } ch <- AddrUpdate{LinkAddress: *addr.IPNet, diff --git a/components/engine/vendor/github.com/vishvananda/netlink/bridge_linux.go b/components/engine/vendor/github.com/vishvananda/netlink/bridge_linux.go new file mode 100644 index 0000000000..a65d6a1319 --- /dev/null +++ b/components/engine/vendor/github.com/vishvananda/netlink/bridge_linux.go @@ -0,0 +1,115 @@ +package netlink + +import ( + "fmt" + "syscall" + + "github.com/vishvananda/netlink/nl" +) + +// BridgeVlanList gets a map of device id to bridge vlan infos. +// Equivalent to: `bridge vlan show` +func BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) { + return pkgHandle.BridgeVlanList() +} + +// BridgeVlanList gets a map of device id to bridge vlan infos. +// Equivalent to: `bridge vlan show` +func (h *Handle) BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) { + req := h.newNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_DUMP) + msg := nl.NewIfInfomsg(syscall.AF_BRIDGE) + req.AddData(msg) + req.AddData(nl.NewRtAttr(nl.IFLA_EXT_MASK, nl.Uint32Attr(uint32(nl.RTEXT_FILTER_BRVLAN)))) + + msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWLINK) + if err != nil { + return nil, err + } + ret := make(map[int32][]*nl.BridgeVlanInfo) + for _, m := range msgs { + msg := nl.DeserializeIfInfomsg(m) + + attrs, err := nl.ParseRouteAttr(m[msg.Len():]) + if err != nil { + return nil, err + } + for _, attr := range attrs { + switch attr.Attr.Type { + case nl.IFLA_AF_SPEC: + //nested attr + nestAttrs, err := nl.ParseRouteAttr(attr.Value) + if err != nil { + return nil, fmt.Errorf("failed to parse nested attr %v", err) + } + for _, nestAttr := range nestAttrs { + switch nestAttr.Attr.Type { + case nl.IFLA_BRIDGE_VLAN_INFO: + vlanInfo := nl.DeserializeBridgeVlanInfo(nestAttr.Value) + ret[msg.Index] = append(ret[msg.Index], vlanInfo) + } + } + } + } + } + return ret, nil +} + +// BridgeVlanAdd adds a new vlan filter entry +// Equivalent to: `bridge vlan add dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]` +func BridgeVlanAdd(link Link, vid uint16, pvid, untagged, self, master bool) error { + return pkgHandle.BridgeVlanAdd(link, vid, pvid, untagged, self, master) +} + +// BridgeVlanAdd adds a new vlan filter entry +// Equivalent to: `bridge vlan add dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]` +func (h *Handle) BridgeVlanAdd(link Link, vid uint16, pvid, untagged, self, master bool) error { + return h.bridgeVlanModify(syscall.RTM_SETLINK, link, vid, pvid, untagged, self, master) +} + +// BridgeVlanDel adds a new vlan filter entry +// Equivalent to: `bridge vlan del dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]` +func BridgeVlanDel(link Link, vid uint16, pvid, untagged, self, master bool) error { + return pkgHandle.BridgeVlanDel(link, vid, pvid, untagged, self, master) +} + +// BridgeVlanDel adds a new vlan filter entry +// Equivalent to: `bridge vlan del dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]` +func (h *Handle) BridgeVlanDel(link Link, vid uint16, pvid, untagged, self, master bool) error { + return h.bridgeVlanModify(syscall.RTM_DELLINK, link, vid, pvid, untagged, self, master) +} + +func (h *Handle) bridgeVlanModify(cmd int, link Link, vid uint16, pvid, untagged, self, master bool) error { + base := link.Attrs() + h.ensureIndex(base) + req := h.newNetlinkRequest(cmd, syscall.NLM_F_ACK) + + msg := nl.NewIfInfomsg(syscall.AF_BRIDGE) + msg.Index = int32(base.Index) + req.AddData(msg) + + br := nl.NewRtAttr(nl.IFLA_AF_SPEC, nil) + var flags uint16 + if self { + flags |= nl.BRIDGE_FLAGS_SELF + } + if master { + flags |= nl.BRIDGE_FLAGS_MASTER + } + if flags > 0 { + nl.NewRtAttrChild(br, nl.IFLA_BRIDGE_FLAGS, nl.Uint16Attr(flags)) + } + vlanInfo := &nl.BridgeVlanInfo{Vid: vid} + if pvid { + vlanInfo.Flags |= nl.BRIDGE_VLAN_INFO_PVID + } + if untagged { + vlanInfo.Flags |= nl.BRIDGE_VLAN_INFO_UNTAGGED + } + nl.NewRtAttrChild(br, nl.IFLA_BRIDGE_VLAN_INFO, vlanInfo.Serialize()) + req.AddData(br) + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + if err != nil { + return err + } + return nil +} diff --git a/components/engine/vendor/github.com/vishvananda/netlink/conntrack_linux.go b/components/engine/vendor/github.com/vishvananda/netlink/conntrack_linux.go index 20df903006..ecf0445659 100644 --- a/components/engine/vendor/github.com/vishvananda/netlink/conntrack_linux.go +++ b/components/engine/vendor/github.com/vishvananda/netlink/conntrack_linux.go @@ -22,7 +22,11 @@ const ( // https://github.com/torvalds/linux/blob/master/include/uapi/linux/netfilter/nfnetlink.h -> #define NFNL_SUBSYS_CTNETLINK_EXP 2 ConntrackExpectTable = 2 ) - +const ( + // For Parsing Mark + TCP_PROTO = 6 + UDP_PROTO = 17 +) const ( // backward compatibility with golang 1.6 which does not have io.SeekCurrent seekCurrent = 1 @@ -56,7 +60,7 @@ func ConntrackTableFlush(table ConntrackTableType) error { // ConntrackDeleteFilter deletes entries on the specified table on the base of the filter // conntrack -D [table] parameters Delete conntrack or expectation -func ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter *ConntrackFilter) (uint, error) { +func ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter CustomConntrackFilter) (uint, error) { return pkgHandle.ConntrackDeleteFilter(table, family, filter) } @@ -88,7 +92,7 @@ func (h *Handle) ConntrackTableFlush(table ConntrackTableType) error { // ConntrackDeleteFilter deletes entries on the specified table on the base of the filter using the netlink handle passed // conntrack -D [table] parameters Delete conntrack or expectation -func (h *Handle) ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter *ConntrackFilter) (uint, error) { +func (h *Handle) ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter CustomConntrackFilter) (uint, error) { res, err := h.dumpConntrackTable(table, family) if err != nil { return 0, err @@ -142,15 +146,16 @@ type ConntrackFlow struct { FamilyType uint8 Forward ipTuple Reverse ipTuple + Mark uint32 } func (s *ConntrackFlow) String() string { // conntrack cmd output: - // udp 17 src=127.0.0.1 dst=127.0.0.1 sport=4001 dport=1234 [UNREPLIED] src=127.0.0.1 dst=127.0.0.1 sport=1234 dport=4001 - return fmt.Sprintf("%s\t%d src=%s dst=%s sport=%d dport=%d\tsrc=%s dst=%s sport=%d dport=%d", + // udp 17 src=127.0.0.1 dst=127.0.0.1 sport=4001 dport=1234 [UNREPLIED] src=127.0.0.1 dst=127.0.0.1 sport=1234 dport=4001 mark=0 + return fmt.Sprintf("%s\t%d src=%s dst=%s sport=%d dport=%d\tsrc=%s dst=%s sport=%d dport=%d mark=%d", nl.L4ProtoMap[s.Forward.Protocol], s.Forward.Protocol, s.Forward.SrcIP.String(), s.Forward.DstIP.String(), s.Forward.SrcPort, s.Forward.DstPort, - s.Reverse.SrcIP.String(), s.Reverse.DstIP.String(), s.Reverse.SrcPort, s.Reverse.DstPort) + s.Reverse.SrcIP.String(), s.Reverse.DstIP.String(), s.Reverse.SrcPort, s.Reverse.DstPort, s.Mark) } // This method parse the ip tuple structure @@ -160,7 +165,7 @@ func (s *ConntrackFlow) String() string { // // // -func parseIpTuple(reader *bytes.Reader, tpl *ipTuple) { +func parseIpTuple(reader *bytes.Reader, tpl *ipTuple) uint8 { for i := 0; i < 2; i++ { _, t, _, v := parseNfAttrTLV(reader) switch t { @@ -189,6 +194,7 @@ func parseIpTuple(reader *bytes.Reader, tpl *ipTuple) { // Skip some padding 2 byte reader.Seek(2, seekCurrent) } + return tpl.Protocol } func parseNfAttrTLV(r *bytes.Reader) (isNested bool, attrType, len uint16, value []byte) { @@ -216,6 +222,7 @@ func parseBERaw16(r *bytes.Reader, v *uint16) { func parseRawData(data []byte) *ConntrackFlow { s := &ConntrackFlow{} + var proto uint8 // First there is the Nfgenmsg header // consume only the family field reader := bytes.NewReader(data) @@ -234,7 +241,7 @@ func parseRawData(data []byte) *ConntrackFlow { nested, t, l := parseNfAttrTL(reader) if nested && t == nl.CTA_TUPLE_ORIG { if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP { - parseIpTuple(reader, &s.Forward) + proto = parseIpTuple(reader, &s.Forward) } } else if nested && t == nl.CTA_TUPLE_REPLY { if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP { @@ -248,7 +255,19 @@ func parseRawData(data []byte) *ConntrackFlow { } } } - + if proto == TCP_PROTO { + reader.Seek(64, seekCurrent) + _, t, _, v := parseNfAttrTLV(reader) + if t == nl.CTA_MARK { + s.Mark = uint32(v[3]) + } + } else if proto == UDP_PROTO { + reader.Seek(16, seekCurrent) + _, t, _, v := parseNfAttrTLV(reader) + if t == nl.CTA_MARK { + s.Mark = uint32(v[3]) + } + } return s } @@ -290,6 +309,12 @@ const ( ConntrackNatAnyIP // -any-nat ip Source or destination NAT ip ) +type CustomConntrackFilter interface { + // MatchConntrackFlow applies the filter to the flow and returns true if the flow matches + // the filter or false otherwise + MatchConntrackFlow(flow *ConntrackFlow) bool +} + type ConntrackFilter struct { ipFilter map[ConntrackFilterType]net.IP } @@ -342,3 +367,5 @@ func (f *ConntrackFilter) MatchConntrackFlow(flow *ConntrackFlow) bool { return match } + +var _ CustomConntrackFilter = (*ConntrackFilter)(nil) diff --git a/components/engine/vendor/github.com/vishvananda/netlink/filter.go b/components/engine/vendor/github.com/vishvananda/netlink/filter.go index 938b28b0b0..1120c79d6a 100644 --- a/components/engine/vendor/github.com/vishvananda/netlink/filter.go +++ b/components/engine/vendor/github.com/vishvananda/netlink/filter.go @@ -2,8 +2,6 @@ package netlink import ( "fmt" - - "github.com/vishvananda/netlink/nl" ) type Filter interface { @@ -184,14 +182,6 @@ func NewMirredAction(redirIndex int) *MirredAction { } } -// Constants used in TcU32Sel.Flags. -const ( - TC_U32_TERMINAL = nl.TC_U32_TERMINAL - TC_U32_OFFSET = nl.TC_U32_OFFSET - TC_U32_VAROFFSET = nl.TC_U32_VAROFFSET - TC_U32_EAT = nl.TC_U32_EAT -) - // Sel of the U32 filters that contains multiple TcU32Key. This is the copy // and the frontend representation of nl.TcU32Sel. It is serialized into canonical // nl.TcU32Sel with the appropriate endianness. diff --git a/components/engine/vendor/github.com/vishvananda/netlink/filter_linux.go b/components/engine/vendor/github.com/vishvananda/netlink/filter_linux.go index dc0f90af88..5025bd56c1 100644 --- a/components/engine/vendor/github.com/vishvananda/netlink/filter_linux.go +++ b/components/engine/vendor/github.com/vishvananda/netlink/filter_linux.go @@ -11,6 +11,14 @@ import ( "github.com/vishvananda/netlink/nl" ) +// Constants used in TcU32Sel.Flags. +const ( + TC_U32_TERMINAL = nl.TC_U32_TERMINAL + TC_U32_OFFSET = nl.TC_U32_OFFSET + TC_U32_VAROFFSET = nl.TC_U32_VAROFFSET + TC_U32_EAT = nl.TC_U32_EAT +) + // Fw filter filters on firewall marks // NOTE: this is in filter_linux because it refers to nl.TcPolice which // is defined in nl/tc_linux.go @@ -128,9 +136,11 @@ func (h *Handle) FilterAdd(filter Filter) error { req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(filter.Type()))) options := nl.NewRtAttr(nl.TCA_OPTIONS, nil) - if u32, ok := filter.(*U32); ok { + + switch filter := filter.(type) { + case *U32: // Convert TcU32Sel into nl.TcU32Sel as it is without copy. - sel := (*nl.TcU32Sel)(unsafe.Pointer(u32.Sel)) + sel := (*nl.TcU32Sel)(unsafe.Pointer(filter.Sel)) if sel == nil { // match all sel = &nl.TcU32Sel{ @@ -158,56 +168,56 @@ func (h *Handle) FilterAdd(filter Filter) error { } sel.Nkeys = uint8(len(sel.Keys)) nl.NewRtAttrChild(options, nl.TCA_U32_SEL, sel.Serialize()) - if u32.ClassId != 0 { - nl.NewRtAttrChild(options, nl.TCA_U32_CLASSID, nl.Uint32Attr(u32.ClassId)) + if filter.ClassId != 0 { + nl.NewRtAttrChild(options, nl.TCA_U32_CLASSID, nl.Uint32Attr(filter.ClassId)) } actionsAttr := nl.NewRtAttrChild(options, nl.TCA_U32_ACT, nil) // backwards compatibility - if u32.RedirIndex != 0 { - u32.Actions = append([]Action{NewMirredAction(u32.RedirIndex)}, u32.Actions...) + if filter.RedirIndex != 0 { + filter.Actions = append([]Action{NewMirredAction(filter.RedirIndex)}, filter.Actions...) } - if err := EncodeActions(actionsAttr, u32.Actions); err != nil { + if err := EncodeActions(actionsAttr, filter.Actions); err != nil { return err } - } else if fw, ok := filter.(*Fw); ok { - if fw.Mask != 0 { + case *Fw: + if filter.Mask != 0 { b := make([]byte, 4) - native.PutUint32(b, fw.Mask) + native.PutUint32(b, filter.Mask) nl.NewRtAttrChild(options, nl.TCA_FW_MASK, b) } - if fw.InDev != "" { - nl.NewRtAttrChild(options, nl.TCA_FW_INDEV, nl.ZeroTerminated(fw.InDev)) + if filter.InDev != "" { + nl.NewRtAttrChild(options, nl.TCA_FW_INDEV, nl.ZeroTerminated(filter.InDev)) } - if (fw.Police != nl.TcPolice{}) { + if (filter.Police != nl.TcPolice{}) { police := nl.NewRtAttrChild(options, nl.TCA_FW_POLICE, nil) - nl.NewRtAttrChild(police, nl.TCA_POLICE_TBF, fw.Police.Serialize()) - if (fw.Police.Rate != nl.TcRateSpec{}) { - payload := SerializeRtab(fw.Rtab) + nl.NewRtAttrChild(police, nl.TCA_POLICE_TBF, filter.Police.Serialize()) + if (filter.Police.Rate != nl.TcRateSpec{}) { + payload := SerializeRtab(filter.Rtab) nl.NewRtAttrChild(police, nl.TCA_POLICE_RATE, payload) } - if (fw.Police.PeakRate != nl.TcRateSpec{}) { - payload := SerializeRtab(fw.Ptab) + if (filter.Police.PeakRate != nl.TcRateSpec{}) { + payload := SerializeRtab(filter.Ptab) nl.NewRtAttrChild(police, nl.TCA_POLICE_PEAKRATE, payload) } } - if fw.ClassId != 0 { + if filter.ClassId != 0 { b := make([]byte, 4) - native.PutUint32(b, fw.ClassId) + native.PutUint32(b, filter.ClassId) nl.NewRtAttrChild(options, nl.TCA_FW_CLASSID, b) } - } else if bpf, ok := filter.(*BpfFilter); ok { + case *BpfFilter: var bpfFlags uint32 - if bpf.ClassId != 0 { - nl.NewRtAttrChild(options, nl.TCA_BPF_CLASSID, nl.Uint32Attr(bpf.ClassId)) + if filter.ClassId != 0 { + nl.NewRtAttrChild(options, nl.TCA_BPF_CLASSID, nl.Uint32Attr(filter.ClassId)) } - if bpf.Fd >= 0 { - nl.NewRtAttrChild(options, nl.TCA_BPF_FD, nl.Uint32Attr((uint32(bpf.Fd)))) + if filter.Fd >= 0 { + nl.NewRtAttrChild(options, nl.TCA_BPF_FD, nl.Uint32Attr((uint32(filter.Fd)))) } - if bpf.Name != "" { - nl.NewRtAttrChild(options, nl.TCA_BPF_NAME, nl.ZeroTerminated(bpf.Name)) + if filter.Name != "" { + nl.NewRtAttrChild(options, nl.TCA_BPF_NAME, nl.ZeroTerminated(filter.Name)) } - if bpf.DirectAction { + if filter.DirectAction { bpfFlags |= nl.TCA_BPF_FLAG_ACT_DIRECT } nl.NewRtAttrChild(options, nl.TCA_BPF_FLAGS, nl.Uint32Attr(bpfFlags)) diff --git a/components/engine/vendor/github.com/vishvananda/netlink/handle_linux.go b/components/engine/vendor/github.com/vishvananda/netlink/handle_linux.go index a04ceae6b6..d37b087c33 100644 --- a/components/engine/vendor/github.com/vishvananda/netlink/handle_linux.go +++ b/components/engine/vendor/github.com/vishvananda/netlink/handle_linux.go @@ -45,12 +45,27 @@ func (h *Handle) SetSocketTimeout(to time.Duration) error { } tv := syscall.NsecToTimeval(to.Nanoseconds()) for _, sh := range h.sockets { - fd := sh.Socket.GetFd() - err := syscall.SetsockoptTimeval(fd, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &tv) - if err != nil { + if err := sh.Socket.SetSendTimeout(&tv); err != nil { return err } - err = syscall.SetsockoptTimeval(fd, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, &tv) + if err := sh.Socket.SetReceiveTimeout(&tv); err != nil { + return err + } + } + return nil +} + +// SetSocketReceiveBufferSize sets the receive buffer size for each +// socket in the netlink handle. The maximum value is capped by +// /proc/sys/net/core/rmem_max. +func (h *Handle) SetSocketReceiveBufferSize(size int, force bool) error { + opt := syscall.SO_RCVBUF + if force { + opt = syscall.SO_RCVBUFFORCE + } + for _, sh := range h.sockets { + fd := sh.Socket.GetFd() + err := syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, opt, size) if err != nil { return err } @@ -58,6 +73,24 @@ func (h *Handle) SetSocketTimeout(to time.Duration) error { return nil } +// GetSocketReceiveBufferSize gets the receiver buffer size for each +// socket in the netlink handle. The retrieved value should be the +// double to the one set for SetSocketReceiveBufferSize. +func (h *Handle) GetSocketReceiveBufferSize() ([]int, error) { + results := make([]int, len(h.sockets)) + i := 0 + for _, sh := range h.sockets { + fd := sh.Socket.GetFd() + size, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_RCVBUF) + if err != nil { + return nil, err + } + results[i] = size + i++ + } + return results, nil +} + // NewHandle returns a netlink handle on the network namespace // specified by ns. If ns=netns.None(), current network namespace // will be assumed diff --git a/components/engine/vendor/github.com/vishvananda/netlink/handle_unspecified.go b/components/engine/vendor/github.com/vishvananda/netlink/handle_unspecified.go index 32cf022732..7da21a6a18 100644 --- a/components/engine/vendor/github.com/vishvananda/netlink/handle_unspecified.go +++ b/components/engine/vendor/github.com/vishvananda/netlink/handle_unspecified.go @@ -145,6 +145,10 @@ func (h *Handle) LinkSetFlood(link Link, mode bool) error { return ErrNotImplemented } +func (h *Handle) LinkSetTxQLen(link Link, qlen int) error { + return ErrNotImplemented +} + func (h *Handle) setProtinfoAttr(link Link, mode bool, attr int) error { return ErrNotImplemented } diff --git a/components/engine/vendor/github.com/vishvananda/netlink/link.go b/components/engine/vendor/github.com/vishvananda/netlink/link.go index 547e92ec12..5aa3a1790a 100644 --- a/components/engine/vendor/github.com/vishvananda/netlink/link.go +++ b/components/engine/vendor/github.com/vishvananda/netlink/link.go @@ -37,6 +37,7 @@ type LinkAttrs struct { EncapType string Protinfo *Protinfo OperState LinkOperState + NetNsID int } // LinkOperState represents the values of the IFLA_OPERSTATE link @@ -171,6 +172,7 @@ type LinkXdp struct { Fd int Attached bool Flags uint32 + ProgId uint32 } // Device links cannot be created via netlink. These links @@ -339,6 +341,7 @@ type Vxlan struct { UDPCSum bool NoAge bool GBP bool + FlowBased bool Age int Limit int Port int @@ -684,6 +687,7 @@ type Gretap struct { EncapType uint16 EncapFlags uint16 Link uint32 + FlowBased bool } func (gretap *Gretap) Attrs() *LinkAttrs { @@ -729,6 +733,28 @@ func (iptun *Vti) Type() string { return "vti" } +type Gretun struct { + LinkAttrs + Link uint32 + IFlags uint16 + OFlags uint16 + IKey uint32 + OKey uint32 + Local net.IP + Remote net.IP + Ttl uint8 + Tos uint8 + PMtuDisc uint8 +} + +func (gretun *Gretun) Attrs() *LinkAttrs { + return &gretun.LinkAttrs +} + +func (gretun *Gretun) Type() string { + return "gre" +} + type Vrf struct { LinkAttrs Table uint32 diff --git a/components/engine/vendor/github.com/vishvananda/netlink/link_linux.go b/components/engine/vendor/github.com/vishvananda/netlink/link_linux.go index 1c1bc52c4d..e94fd9766c 100644 --- a/components/engine/vendor/github.com/vishvananda/netlink/link_linux.go +++ b/components/engine/vendor/github.com/vishvananda/netlink/link_linux.go @@ -379,6 +379,74 @@ func (h *Handle) LinkSetVfTxRate(link Link, vf, rate int) error { return err } +// LinkSetVfSpoofchk enables/disables spoof check on a vf for the link. +// Equivalent to: `ip link set $link vf $vf spoofchk $check` +func LinkSetVfSpoofchk(link Link, vf int, check bool) error { + return pkgHandle.LinkSetVfSpoofchk(link, vf, check) +} + +// LinkSetVfSpookfchk enables/disables spoof check on a vf for the link. +// Equivalent to: `ip link set $link vf $vf spoofchk $check` +func (h *Handle) LinkSetVfSpoofchk(link Link, vf int, check bool) error { + var setting uint32 + base := link.Attrs() + h.ensureIndex(base) + req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) + + msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) + msg.Index = int32(base.Index) + req.AddData(msg) + + data := nl.NewRtAttr(nl.IFLA_VFINFO_LIST, nil) + info := nl.NewRtAttrChild(data, nl.IFLA_VF_INFO, nil) + if check { + setting = 1 + } + vfmsg := nl.VfSpoofchk{ + Vf: uint32(vf), + Setting: setting, + } + nl.NewRtAttrChild(info, nl.IFLA_VF_SPOOFCHK, vfmsg.Serialize()) + req.AddData(data) + + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + return err +} + +// LinkSetVfTrust enables/disables trust state on a vf for the link. +// Equivalent to: `ip link set $link vf $vf trust $state` +func LinkSetVfTrust(link Link, vf int, state bool) error { + return pkgHandle.LinkSetVfTrust(link, vf, state) +} + +// LinkSetVfTrust enables/disables trust state on a vf for the link. +// Equivalent to: `ip link set $link vf $vf trust $state` +func (h *Handle) LinkSetVfTrust(link Link, vf int, state bool) error { + var setting uint32 + base := link.Attrs() + h.ensureIndex(base) + req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) + + msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) + msg.Index = int32(base.Index) + req.AddData(msg) + + data := nl.NewRtAttr(nl.IFLA_VFINFO_LIST, nil) + info := nl.NewRtAttrChild(data, nl.IFLA_VF_INFO, nil) + if state { + setting = 1 + } + vfmsg := nl.VfTrust{ + Vf: uint32(vf), + Setting: setting, + } + nl.NewRtAttrChild(info, nl.IFLA_VF_TRUST, vfmsg.Serialize()) + req.AddData(data) + + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + return err +} + // LinkSetMaster sets the master of the link device. // Equivalent to: `ip link set $link master $master` func LinkSetMaster(link Link, master *Bridge) error { @@ -500,6 +568,12 @@ func (h *Handle) LinkSetNsFd(link Link, fd int) error { // LinkSetXdpFd adds a bpf function to the driver. The fd must be a bpf // program loaded with bpf(type=BPF_PROG_TYPE_XDP) func LinkSetXdpFd(link Link, fd int) error { + return LinkSetXdpFdWithFlags(link, fd, 0) +} + +// LinkSetXdpFdWithFlags adds a bpf function to the driver with the given +// options. The fd must be a bpf program loaded with bpf(type=BPF_PROG_TYPE_XDP) +func LinkSetXdpFdWithFlags(link Link, fd, flags int) error { base := link.Attrs() ensureIndex(base) req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) @@ -508,7 +582,7 @@ func LinkSetXdpFd(link Link, fd int) error { msg.Index = int32(base.Index) req.AddData(msg) - addXdpAttrs(&LinkXdp{Fd: fd}, req) + addXdpAttrs(&LinkXdp{Fd: fd, Flags: uint32(flags)}, req) _, err := req.Execute(syscall.NETLINK_ROUTE, 0) return err @@ -528,7 +602,13 @@ type vxlanPortRange struct { func addVxlanAttrs(vxlan *Vxlan, linkInfo *nl.RtAttr) { data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) + + if vxlan.FlowBased { + vxlan.VxlanId = 0 + } + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_ID, nl.Uint32Attr(uint32(vxlan.VxlanId))) + if vxlan.VtepDevIndex != 0 { nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LINK, nl.Uint32Attr(uint32(vxlan.VtepDevIndex))) } @@ -569,6 +649,9 @@ func addVxlanAttrs(vxlan *Vxlan, linkInfo *nl.RtAttr) { if vxlan.GBP { nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GBP, []byte{}) } + if vxlan.FlowBased { + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_FLOWBASED, boolAttr(vxlan.FlowBased)) + } if vxlan.NoAge { nl.NewRtAttrChild(data, nl.IFLA_VXLAN_AGEING, nl.Uint32Attr(0)) } else if vxlan.Age > 0 { @@ -818,16 +901,17 @@ func (h *Handle) linkModify(link Link, flags int) error { linkInfo := nl.NewRtAttr(syscall.IFLA_LINKINFO, nil) nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_KIND, nl.NonZeroTerminated(link.Type())) - if vlan, ok := link.(*Vlan); ok { + switch link := link.(type) { + case *Vlan: b := make([]byte, 2) - native.PutUint16(b, uint16(vlan.VlanId)) + native.PutUint16(b, uint16(link.VlanId)) data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) nl.NewRtAttrChild(data, nl.IFLA_VLAN_ID, b) - } else if veth, ok := link.(*Veth); ok { + case *Veth: data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) peer := nl.NewRtAttrChild(data, nl.VETH_INFO_PEER, nil) nl.NewIfInfomsgChild(peer, syscall.AF_UNSPEC) - nl.NewRtAttrChild(peer, syscall.IFLA_IFNAME, nl.ZeroTerminated(veth.PeerName)) + nl.NewRtAttrChild(peer, syscall.IFLA_IFNAME, nl.ZeroTerminated(link.PeerName)) if base.TxQLen >= 0 { nl.NewRtAttrChild(peer, syscall.IFLA_TXQLEN, nl.Uint32Attr(uint32(base.TxQLen))) } @@ -835,35 +919,37 @@ func (h *Handle) linkModify(link Link, flags int) error { nl.NewRtAttrChild(peer, syscall.IFLA_MTU, nl.Uint32Attr(uint32(base.MTU))) } - } else if vxlan, ok := link.(*Vxlan); ok { - addVxlanAttrs(vxlan, linkInfo) - } else if bond, ok := link.(*Bond); ok { - addBondAttrs(bond, linkInfo) - } else if ipv, ok := link.(*IPVlan); ok { + case *Vxlan: + addVxlanAttrs(link, linkInfo) + case *Bond: + addBondAttrs(link, linkInfo) + case *IPVlan: data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) - nl.NewRtAttrChild(data, nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(ipv.Mode))) - } else if macv, ok := link.(*Macvlan); ok { - if macv.Mode != MACVLAN_MODE_DEFAULT { + nl.NewRtAttrChild(data, nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(link.Mode))) + case *Macvlan: + if link.Mode != MACVLAN_MODE_DEFAULT { data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) - nl.NewRtAttrChild(data, nl.IFLA_MACVLAN_MODE, nl.Uint32Attr(macvlanModes[macv.Mode])) + nl.NewRtAttrChild(data, nl.IFLA_MACVLAN_MODE, nl.Uint32Attr(macvlanModes[link.Mode])) } - } else if macv, ok := link.(*Macvtap); ok { - if macv.Mode != MACVLAN_MODE_DEFAULT { + case *Macvtap: + if link.Mode != MACVLAN_MODE_DEFAULT { data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) - nl.NewRtAttrChild(data, nl.IFLA_MACVLAN_MODE, nl.Uint32Attr(macvlanModes[macv.Mode])) + nl.NewRtAttrChild(data, nl.IFLA_MACVLAN_MODE, nl.Uint32Attr(macvlanModes[link.Mode])) } - } else if gretap, ok := link.(*Gretap); ok { - addGretapAttrs(gretap, linkInfo) - } else if iptun, ok := link.(*Iptun); ok { - addIptunAttrs(iptun, linkInfo) - } else if vti, ok := link.(*Vti); ok { - addVtiAttrs(vti, linkInfo) - } else if vrf, ok := link.(*Vrf); ok { - addVrfAttrs(vrf, linkInfo) - } else if bridge, ok := link.(*Bridge); ok { - addBridgeAttrs(bridge, linkInfo) - } else if gtp, ok := link.(*GTP); ok { - addGTPAttrs(gtp, linkInfo) + case *Gretap: + addGretapAttrs(link, linkInfo) + case *Iptun: + addIptunAttrs(link, linkInfo) + case *Gretun: + addGretunAttrs(link, linkInfo) + case *Vti: + addVtiAttrs(link, linkInfo) + case *Vrf: + addVrfAttrs(link, linkInfo) + case *Bridge: + addBridgeAttrs(link, linkInfo) + case *GTP: + addGTPAttrs(link, linkInfo) } req.AddData(linkInfo) @@ -1093,6 +1179,8 @@ func LinkDeserialize(hdr *syscall.NlMsghdr, m []byte) (Link, error) { link = &Gretap{} case "ipip": link = &Iptun{} + case "gre": + link = &Gretun{} case "vti": link = &Vti{} case "vrf": @@ -1124,6 +1212,8 @@ func LinkDeserialize(hdr *syscall.NlMsghdr, m []byte) (Link, error) { parseGretapData(link, data) case "ipip": parseIptunData(link, data) + case "gre": + parseGretunData(link, data) case "vti": parseVtiData(link, data) case "vrf": @@ -1178,6 +1268,8 @@ func LinkDeserialize(hdr *syscall.NlMsghdr, m []byte) (Link, error) { } case syscall.IFLA_OPERSTATE: base.OperState = LinkOperState(uint8(attr.Value[0])) + case nl.IFLA_LINK_NETNSID: + base.NetNsID = int(native.Uint32(attr.Value[0:4])) } } @@ -1239,16 +1331,34 @@ type LinkUpdate struct { // LinkSubscribe takes a chan down which notifications will be sent // when links change. Close the 'done' chan to stop subscription. func LinkSubscribe(ch chan<- LinkUpdate, done <-chan struct{}) error { - return linkSubscribe(netns.None(), netns.None(), ch, done) + return linkSubscribeAt(netns.None(), netns.None(), ch, done, nil) } // LinkSubscribeAt works like LinkSubscribe plus it allows the caller // to choose the network namespace in which to subscribe (ns). func LinkSubscribeAt(ns netns.NsHandle, ch chan<- LinkUpdate, done <-chan struct{}) error { - return linkSubscribe(ns, netns.None(), ch, done) + return linkSubscribeAt(ns, netns.None(), ch, done, nil) } -func linkSubscribe(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-chan struct{}) error { +// LinkSubscribeOptions contains a set of options to use with +// LinkSubscribeWithOptions. +type LinkSubscribeOptions struct { + Namespace *netns.NsHandle + ErrorCallback func(error) +} + +// LinkSubscribeWithOptions work like LinkSubscribe but enable to +// provide additional options to modify the behavior. Currently, the +// namespace can be provided as well as an error callback. +func LinkSubscribeWithOptions(ch chan<- LinkUpdate, done <-chan struct{}, options LinkSubscribeOptions) error { + if options.Namespace == nil { + none := netns.None() + options.Namespace = &none + } + return linkSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback) +} + +func linkSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-chan struct{}, cberr func(error)) error { s, err := nl.SubscribeAt(newNs, curNs, syscall.NETLINK_ROUTE, syscall.RTNLGRP_LINK) if err != nil { return err @@ -1264,12 +1374,18 @@ func linkSubscribe(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-cha for { msgs, err := s.Receive() if err != nil { + if cberr != nil { + cberr(err) + } return } for _, m := range msgs { ifmsg := nl.DeserializeIfInfomsg(m.Data) link, err := LinkDeserialize(&m.Header, m.Data) if err != nil { + if cberr != nil { + cberr(err) + } return } ch <- LinkUpdate{IfInfomsg: *ifmsg, Header: m.Header, Link: link} @@ -1363,6 +1479,33 @@ func (h *Handle) setProtinfoAttr(link Link, mode bool, attr int) error { return nil } +// LinkSetTxQLen sets the transaction queue length for the link. +// Equivalent to: `ip link set $link txqlen $qlen` +func LinkSetTxQLen(link Link, qlen int) error { + return pkgHandle.LinkSetTxQLen(link, qlen) +} + +// LinkSetTxQLen sets the transaction queue length for the link. +// Equivalent to: `ip link set $link txqlen $qlen` +func (h *Handle) LinkSetTxQLen(link Link, qlen int) error { + base := link.Attrs() + h.ensureIndex(base) + req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) + + msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) + msg.Index = int32(base.Index) + req.AddData(msg) + + b := make([]byte, 4) + native.PutUint32(b, uint32(qlen)) + + data := nl.NewRtAttr(syscall.IFLA_TXQLEN, b) + req.AddData(data) + + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + return err +} + func parseVlanData(link Link, data []syscall.NetlinkRouteAttr) { vlan := link.(*Vlan) for _, datum := range data { @@ -1407,6 +1550,8 @@ func parseVxlanData(link Link, data []syscall.NetlinkRouteAttr) { vxlan.UDPCSum = int8(datum.Value[0]) != 0 case nl.IFLA_VXLAN_GBP: vxlan.GBP = true + case nl.IFLA_VXLAN_FLOWBASED: + vxlan.FlowBased = int8(datum.Value[0]) != 0 case nl.IFLA_VXLAN_AGEING: vxlan.Age = int(native.Uint32(datum.Value[0:4])) vxlan.NoAge = vxlan.Age == 0 @@ -1547,6 +1692,12 @@ func linkFlags(rawFlags uint32) net.Flags { func addGretapAttrs(gretap *Gretap, linkInfo *nl.RtAttr) { data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) + if gretap.FlowBased { + // In flow based mode, no other attributes need to be configured + nl.NewRtAttrChild(data, nl.IFLA_GRE_COLLECT_METADATA, boolAttr(gretap.FlowBased)) + return + } + ip := gretap.Local.To4() if ip != nil { nl.NewRtAttrChild(data, nl.IFLA_GRE_LOCAL, []byte(ip)) @@ -1613,6 +1764,69 @@ func parseGretapData(link Link, data []syscall.NetlinkRouteAttr) { gre.EncapType = native.Uint16(datum.Value[0:2]) case nl.IFLA_GRE_ENCAP_FLAGS: gre.EncapFlags = native.Uint16(datum.Value[0:2]) + case nl.IFLA_GRE_COLLECT_METADATA: + gre.FlowBased = int8(datum.Value[0]) != 0 + } + } +} + +func addGretunAttrs(gre *Gretun, linkInfo *nl.RtAttr) { + data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) + + ip := gre.Local.To4() + if ip != nil { + nl.NewRtAttrChild(data, nl.IFLA_GRE_LOCAL, []byte(ip)) + } + ip = gre.Remote.To4() + if ip != nil { + nl.NewRtAttrChild(data, nl.IFLA_GRE_REMOTE, []byte(ip)) + } + + if gre.IKey != 0 { + nl.NewRtAttrChild(data, nl.IFLA_GRE_IKEY, htonl(gre.IKey)) + gre.IFlags |= uint16(nl.GRE_KEY) + } + + if gre.OKey != 0 { + nl.NewRtAttrChild(data, nl.IFLA_GRE_OKEY, htonl(gre.OKey)) + gre.OFlags |= uint16(nl.GRE_KEY) + } + + nl.NewRtAttrChild(data, nl.IFLA_GRE_IFLAGS, htons(gre.IFlags)) + nl.NewRtAttrChild(data, nl.IFLA_GRE_OFLAGS, htons(gre.OFlags)) + + if gre.Link != 0 { + nl.NewRtAttrChild(data, nl.IFLA_GRE_LINK, nl.Uint32Attr(gre.Link)) + } + + nl.NewRtAttrChild(data, nl.IFLA_GRE_PMTUDISC, nl.Uint8Attr(gre.PMtuDisc)) + nl.NewRtAttrChild(data, nl.IFLA_GRE_TTL, nl.Uint8Attr(gre.Ttl)) + nl.NewRtAttrChild(data, nl.IFLA_GRE_TOS, nl.Uint8Attr(gre.Tos)) +} + +func parseGretunData(link Link, data []syscall.NetlinkRouteAttr) { + gre := link.(*Gretun) + for _, datum := range data { + switch datum.Attr.Type { + case nl.IFLA_GRE_OKEY: + gre.IKey = ntohl(datum.Value[0:4]) + case nl.IFLA_GRE_IKEY: + gre.OKey = ntohl(datum.Value[0:4]) + case nl.IFLA_GRE_LOCAL: + gre.Local = net.IP(datum.Value[0:4]) + case nl.IFLA_GRE_REMOTE: + gre.Remote = net.IP(datum.Value[0:4]) + case nl.IFLA_GRE_IFLAGS: + gre.IFlags = ntohs(datum.Value[0:2]) + case nl.IFLA_GRE_OFLAGS: + gre.OFlags = ntohs(datum.Value[0:2]) + + case nl.IFLA_GRE_TTL: + gre.Ttl = uint8(datum.Value[0]) + case nl.IFLA_GRE_TOS: + gre.Tos = uint8(datum.Value[0]) + case nl.IFLA_GRE_PMTUDISC: + gre.PMtuDisc = uint8(datum.Value[0]) } } } @@ -1630,8 +1844,10 @@ func addXdpAttrs(xdp *LinkXdp, req *nl.NetlinkRequest) { b := make([]byte, 4) native.PutUint32(b, uint32(xdp.Fd)) nl.NewRtAttrChild(attrs, nl.IFLA_XDP_FD, b) - native.PutUint32(b, xdp.Flags) - nl.NewRtAttrChild(attrs, nl.IFLA_XDP_FLAGS, b) + if xdp.Flags != 0 { + native.PutUint32(b, xdp.Flags) + nl.NewRtAttrChild(attrs, nl.IFLA_XDP_FLAGS, b) + } req.AddData(attrs) } @@ -1649,6 +1865,8 @@ func parseLinkXdp(data []byte) (*LinkXdp, error) { xdp.Attached = attr.Value[0] != 0 case nl.IFLA_XDP_FLAGS: xdp.Flags = native.Uint32(attr.Value[0:4]) + case nl.IFLA_XDP_PROG_ID: + xdp.ProgId = native.Uint32(attr.Value[0:4]) } } return xdp, nil diff --git a/components/engine/vendor/github.com/vishvananda/netlink/neigh.go b/components/engine/vendor/github.com/vishvananda/netlink/neigh.go index 0e5eb90c9e..6a6f71ce86 100644 --- a/components/engine/vendor/github.com/vishvananda/netlink/neigh.go +++ b/components/engine/vendor/github.com/vishvananda/netlink/neigh.go @@ -14,6 +14,7 @@ type Neigh struct { Flags int IP net.IP HardwareAddr net.HardwareAddr + LLIPAddr net.IP //Used in the case of NHRP } // String returns $ip/$hwaddr $label diff --git a/components/engine/vendor/github.com/vishvananda/netlink/neigh_linux.go b/components/engine/vendor/github.com/vishvananda/netlink/neigh_linux.go index f069db2573..5edc8b4125 100644 --- a/components/engine/vendor/github.com/vishvananda/netlink/neigh_linux.go +++ b/components/engine/vendor/github.com/vishvananda/netlink/neigh_linux.go @@ -128,6 +128,7 @@ func (h *Handle) NeighDel(neigh *Neigh) error { func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error { var family int + if neigh.Family > 0 { family = neigh.Family } else { @@ -151,7 +152,10 @@ func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error { dstData := nl.NewRtAttr(NDA_DST, ipData) req.AddData(dstData) - if neigh.Flags != NTF_PROXY || neigh.HardwareAddr != nil { + if neigh.LLIPAddr != nil { + llIPData := nl.NewRtAttr(NDA_LLADDR, neigh.LLIPAddr.To4()) + req.AddData(llIPData) + } else if neigh.Flags != NTF_PROXY || neigh.HardwareAddr != nil { hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr)) req.AddData(hwData) } @@ -237,12 +241,33 @@ func NeighDeserialize(m []byte) (*Neigh, error) { return nil, err } + // This should be cached for perfomance + // once per table dump + link, err := LinkByIndex(neigh.LinkIndex) + if err != nil { + return nil, err + } + encapType := link.Attrs().EncapType + for _, attr := range attrs { switch attr.Attr.Type { case NDA_DST: neigh.IP = net.IP(attr.Value) case NDA_LLADDR: - neigh.HardwareAddr = net.HardwareAddr(attr.Value) + // BUG: Is this a bug in the netlink library? + // #define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len)) + // #define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0)) + attrLen := attr.Attr.Len - syscall.SizeofRtAttr + if attrLen == 4 && (encapType == "ipip" || + encapType == "sit" || + encapType == "gre") { + neigh.LLIPAddr = net.IP(attr.Value) + } else if attrLen == 16 && + encapType == "tunnel6" { + neigh.IP = net.IP(attr.Value) + } else { + neigh.HardwareAddr = net.HardwareAddr(attr.Value) + } } } diff --git a/components/engine/vendor/github.com/vishvananda/netlink/netlink_unspecified.go b/components/engine/vendor/github.com/vishvananda/netlink/netlink_unspecified.go index 2d57c16d74..86111b92ce 100644 --- a/components/engine/vendor/github.com/vishvananda/netlink/netlink_unspecified.go +++ b/components/engine/vendor/github.com/vishvananda/netlink/netlink_unspecified.go @@ -108,6 +108,10 @@ func LinkSetFlood(link Link, mode bool) error { return ErrNotImplemented } +func LinkSetTxQLen(link Link, qlen int) error { + return ErrNotImplemented +} + func LinkAdd(link Link) error { return ErrNotImplemented } diff --git a/components/engine/vendor/github.com/vishvananda/netlink/nl/bridge_linux.go b/components/engine/vendor/github.com/vishvananda/netlink/nl/bridge_linux.go new file mode 100644 index 0000000000..6c0d333387 --- /dev/null +++ b/components/engine/vendor/github.com/vishvananda/netlink/nl/bridge_linux.go @@ -0,0 +1,74 @@ +package nl + +import ( + "fmt" + "unsafe" +) + +const ( + SizeofBridgeVlanInfo = 0x04 +) + +/* Bridge Flags */ +const ( + BRIDGE_FLAGS_MASTER = iota /* Bridge command to/from master */ + BRIDGE_FLAGS_SELF /* Bridge command to/from lowerdev */ +) + +/* Bridge management nested attributes + * [IFLA_AF_SPEC] = { + * [IFLA_BRIDGE_FLAGS] + * [IFLA_BRIDGE_MODE] + * [IFLA_BRIDGE_VLAN_INFO] + * } + */ +const ( + IFLA_BRIDGE_FLAGS = iota + IFLA_BRIDGE_MODE + IFLA_BRIDGE_VLAN_INFO +) + +const ( + BRIDGE_VLAN_INFO_MASTER = 1 << iota + BRIDGE_VLAN_INFO_PVID + BRIDGE_VLAN_INFO_UNTAGGED + BRIDGE_VLAN_INFO_RANGE_BEGIN + BRIDGE_VLAN_INFO_RANGE_END +) + +// struct bridge_vlan_info { +// __u16 flags; +// __u16 vid; +// }; + +type BridgeVlanInfo struct { + Flags uint16 + Vid uint16 +} + +func (b *BridgeVlanInfo) Serialize() []byte { + return (*(*[SizeofBridgeVlanInfo]byte)(unsafe.Pointer(b)))[:] +} + +func DeserializeBridgeVlanInfo(b []byte) *BridgeVlanInfo { + return (*BridgeVlanInfo)(unsafe.Pointer(&b[0:SizeofBridgeVlanInfo][0])) +} + +func (b *BridgeVlanInfo) PortVID() bool { + return b.Flags&BRIDGE_VLAN_INFO_PVID > 0 +} + +func (b *BridgeVlanInfo) EngressUntag() bool { + return b.Flags&BRIDGE_VLAN_INFO_UNTAGGED > 0 +} + +func (b *BridgeVlanInfo) String() string { + return fmt.Sprintf("%+v", *b) +} + +/* New extended info filters for IFLA_EXT_MASK */ +const ( + RTEXT_FILTER_VF = 1 << iota + RTEXT_FILTER_BRVLAN + RTEXT_FILTER_BRVLAN_COMPRESSED +) diff --git a/components/engine/vendor/github.com/vishvananda/netlink/nl/conntrack_linux.go b/components/engine/vendor/github.com/vishvananda/netlink/nl/conntrack_linux.go index 6692b53ed9..380cc5967b 100644 --- a/components/engine/vendor/github.com/vishvananda/netlink/nl/conntrack_linux.go +++ b/components/engine/vendor/github.com/vishvananda/netlink/nl/conntrack_linux.go @@ -79,8 +79,8 @@ const ( CTA_TUPLE_ORIG = 1 CTA_TUPLE_REPLY = 2 CTA_STATUS = 3 - CTA_TIMEOUT = 8 - CTA_MARK = 9 + CTA_TIMEOUT = 7 + CTA_MARK = 8 CTA_PROTOINFO = 4 ) diff --git a/components/engine/vendor/github.com/vishvananda/netlink/nl/link_linux.go b/components/engine/vendor/github.com/vishvananda/netlink/nl/link_linux.go index f7b9575919..9ae65a12c2 100644 --- a/components/engine/vendor/github.com/vishvananda/netlink/nl/link_linux.go +++ b/components/engine/vendor/github.com/vishvananda/netlink/nl/link_linux.go @@ -231,7 +231,8 @@ const ( * on/off switch */ IFLA_VF_STATS /* network device statistics */ - IFLA_VF_MAX = IFLA_VF_STATS + IFLA_VF_TRUST /* Trust state of VF */ + IFLA_VF_MAX = IFLA_VF_TRUST ) const ( @@ -259,6 +260,7 @@ const ( SizeofVfSpoofchk = 0x08 SizeofVfLinkState = 0x08 SizeofVfRssQueryEn = 0x08 + SizeofVfTrust = 0x08 ) // struct ifla_vf_mac { @@ -419,12 +421,42 @@ func (msg *VfRssQueryEn) Serialize() []byte { return (*(*[SizeofVfRssQueryEn]byte)(unsafe.Pointer(msg)))[:] } +// struct ifla_vf_trust { +// __u32 vf; +// __u32 setting; +// }; + +type VfTrust struct { + Vf uint32 + Setting uint32 +} + +func (msg *VfTrust) Len() int { + return SizeofVfTrust +} + +func DeserializeVfTrust(b []byte) *VfTrust { + return (*VfTrust)(unsafe.Pointer(&b[0:SizeofVfTrust][0])) +} + +func (msg *VfTrust) Serialize() []byte { + return (*(*[SizeofVfTrust]byte)(unsafe.Pointer(msg)))[:] +} + +const ( + XDP_FLAGS_UPDATE_IF_NOEXIST = 1 << iota + XDP_FLAGS_SKB_MODE + XDP_FLAGS_DRV_MODE + XDP_FLAGS_MASK = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE +) + const ( IFLA_XDP_UNSPEC = iota IFLA_XDP_FD /* fd of xdp program to attach, or -1 to remove */ IFLA_XDP_ATTACHED /* read-only bool indicating if prog is attached */ IFLA_XDP_FLAGS /* xdp prog related flags */ - IFLA_XDP_MAX = IFLA_XDP_FLAGS + IFLA_XDP_PROG_ID /* xdp prog id */ + IFLA_XDP_MAX = IFLA_XDP_PROG_ID ) const ( diff --git a/components/engine/vendor/github.com/vishvananda/netlink/nl/nl_linux.go b/components/engine/vendor/github.com/vishvananda/netlink/nl/nl_linux.go index 1329acd864..72f7f6af3c 100644 --- a/components/engine/vendor/github.com/vishvananda/netlink/nl/nl_linux.go +++ b/components/engine/vendor/github.com/vishvananda/netlink/nl/nl_linux.go @@ -621,6 +621,20 @@ func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, error) { return syscall.ParseNetlinkMessage(rb) } +// SetSendTimeout allows to set a send timeout on the socket +func (s *NetlinkSocket) SetSendTimeout(timeout *syscall.Timeval) error { + // Set a send timeout of SOCKET_SEND_TIMEOUT, this will allow the Send to periodically unblock and avoid that a routine + // remains stuck on a send on a closed fd + return syscall.SetsockoptTimeval(int(s.fd), syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, timeout) +} + +// SetReceiveTimeout allows to set a receive timeout on the socket +func (s *NetlinkSocket) SetReceiveTimeout(timeout *syscall.Timeval) error { + // Set a read timeout of SOCKET_READ_TIMEOUT, this will allow the Read to periodically unblock and avoid that a routine + // remains stuck on a recvmsg on a closed fd + return syscall.SetsockoptTimeval(int(s.fd), syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, timeout) +} + func (s *NetlinkSocket) GetPid() (uint32, error) { fd := int(atomic.LoadInt32(&s.fd)) lsa, err := syscall.Getsockname(fd) diff --git a/components/engine/vendor/github.com/vishvananda/netlink/qdisc_linux.go b/components/engine/vendor/github.com/vishvananda/netlink/qdisc_linux.go index 2c0deddb32..1123396e47 100644 --- a/components/engine/vendor/github.com/vishvananda/netlink/qdisc_linux.go +++ b/components/engine/vendor/github.com/vishvananda/netlink/qdisc_linux.go @@ -160,71 +160,73 @@ func qdiscPayload(req *nl.NetlinkRequest, qdisc Qdisc) error { req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(qdisc.Type()))) options := nl.NewRtAttr(nl.TCA_OPTIONS, nil) - if prio, ok := qdisc.(*Prio); ok { + + switch qdisc := qdisc.(type) { + case *Prio: tcmap := nl.TcPrioMap{ - Bands: int32(prio.Bands), - Priomap: prio.PriorityMap, + Bands: int32(qdisc.Bands), + Priomap: qdisc.PriorityMap, } options = nl.NewRtAttr(nl.TCA_OPTIONS, tcmap.Serialize()) - } else if tbf, ok := qdisc.(*Tbf); ok { + case *Tbf: opt := nl.TcTbfQopt{} - opt.Rate.Rate = uint32(tbf.Rate) - opt.Peakrate.Rate = uint32(tbf.Peakrate) - opt.Limit = tbf.Limit - opt.Buffer = tbf.Buffer + opt.Rate.Rate = uint32(qdisc.Rate) + opt.Peakrate.Rate = uint32(qdisc.Peakrate) + opt.Limit = qdisc.Limit + opt.Buffer = qdisc.Buffer nl.NewRtAttrChild(options, nl.TCA_TBF_PARMS, opt.Serialize()) - if tbf.Rate >= uint64(1<<32) { - nl.NewRtAttrChild(options, nl.TCA_TBF_RATE64, nl.Uint64Attr(tbf.Rate)) + if qdisc.Rate >= uint64(1<<32) { + nl.NewRtAttrChild(options, nl.TCA_TBF_RATE64, nl.Uint64Attr(qdisc.Rate)) } - if tbf.Peakrate >= uint64(1<<32) { - nl.NewRtAttrChild(options, nl.TCA_TBF_PRATE64, nl.Uint64Attr(tbf.Peakrate)) + if qdisc.Peakrate >= uint64(1<<32) { + nl.NewRtAttrChild(options, nl.TCA_TBF_PRATE64, nl.Uint64Attr(qdisc.Peakrate)) } - if tbf.Peakrate > 0 { - nl.NewRtAttrChild(options, nl.TCA_TBF_PBURST, nl.Uint32Attr(tbf.Minburst)) + if qdisc.Peakrate > 0 { + nl.NewRtAttrChild(options, nl.TCA_TBF_PBURST, nl.Uint32Attr(qdisc.Minburst)) } - } else if htb, ok := qdisc.(*Htb); ok { + case *Htb: opt := nl.TcHtbGlob{} - opt.Version = htb.Version - opt.Rate2Quantum = htb.Rate2Quantum - opt.Defcls = htb.Defcls + opt.Version = qdisc.Version + opt.Rate2Quantum = qdisc.Rate2Quantum + opt.Defcls = qdisc.Defcls // TODO: Handle Debug properly. For now default to 0 - opt.Debug = htb.Debug - opt.DirectPkts = htb.DirectPkts + opt.Debug = qdisc.Debug + opt.DirectPkts = qdisc.DirectPkts nl.NewRtAttrChild(options, nl.TCA_HTB_INIT, opt.Serialize()) // nl.NewRtAttrChild(options, nl.TCA_HTB_DIRECT_QLEN, opt.Serialize()) - } else if netem, ok := qdisc.(*Netem); ok { + case *Netem: opt := nl.TcNetemQopt{} - opt.Latency = netem.Latency - opt.Limit = netem.Limit - opt.Loss = netem.Loss - opt.Gap = netem.Gap - opt.Duplicate = netem.Duplicate - opt.Jitter = netem.Jitter + opt.Latency = qdisc.Latency + opt.Limit = qdisc.Limit + opt.Loss = qdisc.Loss + opt.Gap = qdisc.Gap + opt.Duplicate = qdisc.Duplicate + opt.Jitter = qdisc.Jitter options = nl.NewRtAttr(nl.TCA_OPTIONS, opt.Serialize()) // Correlation corr := nl.TcNetemCorr{} - corr.DelayCorr = netem.DelayCorr - corr.LossCorr = netem.LossCorr - corr.DupCorr = netem.DuplicateCorr + corr.DelayCorr = qdisc.DelayCorr + corr.LossCorr = qdisc.LossCorr + corr.DupCorr = qdisc.DuplicateCorr if corr.DelayCorr > 0 || corr.LossCorr > 0 || corr.DupCorr > 0 { nl.NewRtAttrChild(options, nl.TCA_NETEM_CORR, corr.Serialize()) } // Corruption corruption := nl.TcNetemCorrupt{} - corruption.Probability = netem.CorruptProb - corruption.Correlation = netem.CorruptCorr + corruption.Probability = qdisc.CorruptProb + corruption.Correlation = qdisc.CorruptCorr if corruption.Probability > 0 { nl.NewRtAttrChild(options, nl.TCA_NETEM_CORRUPT, corruption.Serialize()) } // Reorder reorder := nl.TcNetemReorder{} - reorder.Probability = netem.ReorderProb - reorder.Correlation = netem.ReorderCorr + reorder.Probability = qdisc.ReorderProb + reorder.Correlation = qdisc.ReorderCorr if reorder.Probability > 0 { nl.NewRtAttrChild(options, nl.TCA_NETEM_REORDER, reorder.Serialize()) } - } else if _, ok := qdisc.(*Ingress); ok { + case *Ingress: // ingress filters must use the proper handle if qdisc.Attrs().Parent != HANDLE_INGRESS { return fmt.Errorf("Ingress filters must set Parent to HANDLE_INGRESS") diff --git a/components/engine/vendor/github.com/vishvananda/netlink/route.go b/components/engine/vendor/github.com/vishvananda/netlink/route.go index 03ac4b2391..68c6a2230d 100644 --- a/components/engine/vendor/github.com/vishvananda/netlink/route.go +++ b/components/engine/vendor/github.com/vishvananda/netlink/route.go @@ -16,6 +16,7 @@ type Destination interface { Decode([]byte) error Encode() ([]byte, error) String() string + Equal(Destination) bool } type Encap interface { @@ -23,6 +24,7 @@ type Encap interface { Decode([]byte) error Encode() ([]byte, error) String() string + Equal(Encap) bool } // Route represents a netlink route. @@ -72,6 +74,25 @@ func (r Route) String() string { return fmt.Sprintf("{%s}", strings.Join(elems, " ")) } +func (r Route) Equal(x Route) bool { + return r.LinkIndex == x.LinkIndex && + r.ILinkIndex == x.ILinkIndex && + r.Scope == x.Scope && + ipNetEqual(r.Dst, x.Dst) && + r.Src.Equal(x.Src) && + r.Gw.Equal(x.Gw) && + nexthopInfoSlice(r.MultiPath).Equal(x.MultiPath) && + r.Protocol == x.Protocol && + r.Priority == x.Priority && + r.Table == x.Table && + r.Type == x.Type && + r.Tos == x.Tos && + r.Flags == x.Flags && + (r.MPLSDst == x.MPLSDst || (r.MPLSDst != nil && x.MPLSDst != nil && *r.MPLSDst == *x.MPLSDst)) && + (r.NewDst == x.NewDst || (r.NewDst != nil && r.NewDst.Equal(x.NewDst))) && + (r.Encap == x.Encap || (r.Encap != nil && r.Encap.Equal(x.Encap))) +} + func (r *Route) SetFlag(flag NextHopFlag) { r.Flags |= int(flag) } @@ -110,7 +131,46 @@ func (n *NexthopInfo) String() string { elems = append(elems, fmt.Sprintf("Encap: %s", n.Encap)) } elems = append(elems, fmt.Sprintf("Weight: %d", n.Hops+1)) - elems = append(elems, fmt.Sprintf("Gw: %d", n.Gw)) + elems = append(elems, fmt.Sprintf("Gw: %s", n.Gw)) elems = append(elems, fmt.Sprintf("Flags: %s", n.ListFlags())) return fmt.Sprintf("{%s}", strings.Join(elems, " ")) } + +func (n NexthopInfo) Equal(x NexthopInfo) bool { + return n.LinkIndex == x.LinkIndex && + n.Hops == x.Hops && + n.Gw.Equal(x.Gw) && + n.Flags == x.Flags && + (n.NewDst == x.NewDst || (n.NewDst != nil && n.NewDst.Equal(x.NewDst))) && + (n.Encap == x.Encap || (n.Encap != nil && n.Encap.Equal(x.Encap))) +} + +type nexthopInfoSlice []*NexthopInfo + +func (n nexthopInfoSlice) Equal(x []*NexthopInfo) bool { + if len(n) != len(x) { + return false + } + for i := range n { + if n[i] == nil || x[i] == nil { + return false + } + if !n[i].Equal(*x[i]) { + return false + } + } + return true +} + +// ipNetEqual returns true iff both IPNet are equal +func ipNetEqual(ipn1 *net.IPNet, ipn2 *net.IPNet) bool { + if ipn1 == ipn2 { + return true + } + if ipn1 == nil || ipn2 == nil { + return false + } + m1, _ := ipn1.Mask.Size() + m2, _ := ipn2.Mask.Size() + return m1 == m2 && ipn1.IP.Equal(ipn2.IP) +} diff --git a/components/engine/vendor/github.com/vishvananda/netlink/route_linux.go b/components/engine/vendor/github.com/vishvananda/netlink/route_linux.go index cd739e7146..9234c6986d 100644 --- a/components/engine/vendor/github.com/vishvananda/netlink/route_linux.go +++ b/components/engine/vendor/github.com/vishvananda/netlink/route_linux.go @@ -86,6 +86,34 @@ func (d *MPLSDestination) String() string { return strings.Join(s, "/") } +func (d *MPLSDestination) Equal(x Destination) bool { + o, ok := x.(*MPLSDestination) + if !ok { + return false + } + if d == nil && o == nil { + return true + } + if d == nil || o == nil { + return false + } + if d.Labels == nil && o.Labels == nil { + return true + } + if d.Labels == nil || o.Labels == nil { + return false + } + if len(d.Labels) != len(o.Labels) { + return false + } + for i := range d.Labels { + if d.Labels[i] != o.Labels[i] { + return false + } + } + return true +} + type MPLSEncap struct { Labels []int } @@ -129,6 +157,34 @@ func (e *MPLSEncap) String() string { return strings.Join(s, "/") } +func (e *MPLSEncap) Equal(x Encap) bool { + o, ok := x.(*MPLSEncap) + if !ok { + return false + } + if e == nil && o == nil { + return true + } + if e == nil || o == nil { + return false + } + if e.Labels == nil && o.Labels == nil { + return true + } + if e.Labels == nil || o.Labels == nil { + return false + } + if len(e.Labels) != len(o.Labels) { + return false + } + for i := range e.Labels { + if e.Labels[i] != o.Labels[i] { + return false + } + } + return true +} + // RouteAdd will add a route to the system. // Equivalent to: `ip route add $route` func RouteAdd(route *Route) error { @@ -421,19 +477,8 @@ func (h *Handle) RouteListFiltered(family int, filter *Route, filterMask uint64) continue case filterMask&RT_FILTER_DST != 0: if filter.MPLSDst == nil || route.MPLSDst == nil || (*filter.MPLSDst) != (*route.MPLSDst) { - if filter.Dst == nil { - if route.Dst != nil { - continue - } - } else { - if route.Dst == nil { - continue - } - aMaskLen, aMaskBits := route.Dst.Mask.Size() - bMaskLen, bMaskBits := filter.Dst.Mask.Size() - if !(route.Dst.IP.Equal(filter.Dst.IP) && aMaskLen == bMaskLen && aMaskBits == bMaskBits) { - continue - } + if !ipNetEqual(route.Dst, filter.Dst) { + continue } } } @@ -633,16 +678,34 @@ func (h *Handle) RouteGet(destination net.IP) ([]Route, error) { // RouteSubscribe takes a chan down which notifications will be sent // when routes are added or deleted. Close the 'done' chan to stop subscription. func RouteSubscribe(ch chan<- RouteUpdate, done <-chan struct{}) error { - return routeSubscribeAt(netns.None(), netns.None(), ch, done) + return routeSubscribeAt(netns.None(), netns.None(), ch, done, nil) } // RouteSubscribeAt works like RouteSubscribe plus it allows the caller // to choose the network namespace in which to subscribe (ns). func RouteSubscribeAt(ns netns.NsHandle, ch chan<- RouteUpdate, done <-chan struct{}) error { - return routeSubscribeAt(ns, netns.None(), ch, done) + return routeSubscribeAt(ns, netns.None(), ch, done, nil) } -func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <-chan struct{}) error { +// RouteSubscribeOptions contains a set of options to use with +// RouteSubscribeWithOptions. +type RouteSubscribeOptions struct { + Namespace *netns.NsHandle + ErrorCallback func(error) +} + +// RouteSubscribeWithOptions work like RouteSubscribe but enable to +// provide additional options to modify the behavior. Currently, the +// namespace can be provided as well as an error callback. +func RouteSubscribeWithOptions(ch chan<- RouteUpdate, done <-chan struct{}, options RouteSubscribeOptions) error { + if options.Namespace == nil { + none := netns.None() + options.Namespace = &none + } + return routeSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback) +} + +func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <-chan struct{}, cberr func(error)) error { s, err := nl.SubscribeAt(newNs, curNs, syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_ROUTE, syscall.RTNLGRP_IPV6_ROUTE) if err != nil { return err @@ -658,11 +721,17 @@ func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done < for { msgs, err := s.Receive() if err != nil { + if cberr != nil { + cberr(err) + } return } for _, m := range msgs { route, err := deserializeRoute(m.Data) if err != nil { + if cberr != nil { + cberr(err) + } return } ch <- RouteUpdate{Type: m.Header.Type, Route: route} diff --git a/components/engine/vendor/github.com/vishvananda/netlink/rule.go b/components/engine/vendor/github.com/vishvananda/netlink/rule.go index f0243defd7..e4d9168d6c 100644 --- a/components/engine/vendor/github.com/vishvananda/netlink/rule.go +++ b/components/engine/vendor/github.com/vishvananda/netlink/rule.go @@ -8,6 +8,7 @@ import ( // Rule represents a netlink rule. type Rule struct { Priority int + Family int Table int Mark int Mask int diff --git a/components/engine/vendor/github.com/vishvananda/netlink/rule_linux.go b/components/engine/vendor/github.com/vishvananda/netlink/rule_linux.go index f9cdc855f1..cbd91a56bb 100644 --- a/components/engine/vendor/github.com/vishvananda/netlink/rule_linux.go +++ b/components/engine/vendor/github.com/vishvananda/netlink/rule_linux.go @@ -37,6 +37,9 @@ func (h *Handle) RuleDel(rule *Rule) error { func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error { msg := nl.NewRtMsg() msg.Family = syscall.AF_INET + if rule.Family != 0 { + msg.Family = uint8(rule.Family) + } var dstFamily uint8 var rtAttrs []*nl.RtAttr