diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json
new file mode 100644
index 0000000..40ede40
--- /dev/null
+++ b/Godeps/Godeps.json
@@ -0,0 +1,20 @@
+{
+ "ImportPath": "github.com/docker/libnetwork",
+ "GoVersion": "go1.4.2",
+ "Deps": [
+ {
+ "ImportPath": "github.com/Sirupsen/logrus",
+ "Comment": "v0.6.4-12-g467d9d5",
+ "Rev": "467d9d55c2d2c17248441a8fc661561161f40d5e"
+ },
+ {
+ "ImportPath": "github.com/docker/docker/pkg/reexec",
+ "Comment": "v1.4.1-1379-g8e107a9",
+ "Rev": "8e107a93210c54f22ec1354d969c771b1abfbe05"
+ },
+ {
+ "ImportPath": "github.com/vishvananda/netlink",
+ "Rev": "8eb64238879fed52fd51c5b30ad20b928fb4c36c"
+ }
+ ]
+}
diff --git a/Godeps/Readme b/Godeps/Readme
new file mode 100644
index 0000000..4cdaa53
--- /dev/null
+++ b/Godeps/Readme
@@ -0,0 +1,5 @@
+This directory tree is generated automatically by godep.
+
+Please do not edit.
+
+See https://github.com/tools/godep for more information.
diff --git a/Godeps/_workspace/.gitignore b/Godeps/_workspace/.gitignore
new file mode 100644
index 0000000..f037d68
--- /dev/null
+++ b/Godeps/_workspace/.gitignore
@@ -0,0 +1,2 @@
+/pkg
+/bin
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/.gitignore b/Godeps/_workspace/src/github.com/Sirupsen/logrus/.gitignore
new file mode 100644
index 0000000..66be63a
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/.gitignore
@@ -0,0 +1 @@
+logrus
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/.travis.yml b/Godeps/_workspace/src/github.com/Sirupsen/logrus/.travis.yml
new file mode 100644
index 0000000..2d8c086
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/.travis.yml
@@ -0,0 +1,8 @@
+language: go
+go:
+ - 1.2
+ - 1.3
+ - 1.4
+ - tip
+install:
+ - go get -t ./...
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/LICENSE b/Godeps/_workspace/src/github.com/Sirupsen/logrus/LICENSE
new file mode 100644
index 0000000..f090cb4
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Simon Eskildsen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/README.md b/Godeps/_workspace/src/github.com/Sirupsen/logrus/README.md
new file mode 100644
index 0000000..6227b3f
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/README.md
@@ -0,0 +1,373 @@
+# Logrus
[](https://travis-ci.org/Sirupsen/logrus) [][godoc]
+
+Logrus is a structured logger for Go (golang), completely API compatible with
+the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not
+yet stable (pre 1.0), the core API is unlikely to change much but please version
+control your Logrus to make sure you aren't fetching latest `master` on every
+build.**
+
+Nicely color-coded in development (when a TTY is attached, otherwise just
+plain text):
+
+
+
+With `log.Formatter = new(logrus.JSONFormatter)`, for easy parsing by logstash
+or Splunk:
+
+```json
+{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
+ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
+
+{"level":"warning","msg":"The group's number increased tremendously!",
+"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
+
+{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
+"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
+
+{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
+"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
+
+{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
+"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
+```
+
+With the default `log.Formatter = new(logrus.TextFormatter)` when a TTY is not
+attached, the output is compatible with the
+[logfmt](http://godoc.org/github.com/kr/logfmt) format:
+
+```text
+time="2014-04-20 15:36:23.830442383 -0400 EDT" level="info" msg="A group of walrus emerges from the ocean" animal="walrus" size=10
+time="2014-04-20 15:36:23.830584199 -0400 EDT" level="warning" msg="The group's number increased tremendously!" omg=true number=122
+time="2014-04-20 15:36:23.830596521 -0400 EDT" level="info" msg="A giant walrus appears!" animal="walrus" size=10
+time="2014-04-20 15:36:23.830611837 -0400 EDT" level="info" msg="Tremendously sized cow enters the ocean." animal="walrus" size=9
+time="2014-04-20 15:36:23.830626464 -0400 EDT" level="fatal" msg="The ice breaks!" omg=true number=100
+```
+
+#### Example
+
+The simplest way to use Logrus is simply the package-level exported logger:
+
+```go
+package main
+
+import (
+ log "github.com/Sirupsen/logrus"
+)
+
+func main() {
+ log.WithFields(log.Fields{
+ "animal": "walrus",
+ }).Info("A walrus appears")
+}
+```
+
+Note that it's completely api-compatible with the stdlib logger, so you can
+replace your `log` imports everywhere with `log "github.com/Sirupsen/logrus"`
+and you'll now have the flexibility of Logrus. You can customize it all you
+want:
+
+```go
+package main
+
+import (
+ "os"
+ log "github.com/Sirupsen/logrus"
+ "github.com/Sirupsen/logrus/hooks/airbrake"
+)
+
+func init() {
+ // Log as JSON instead of the default ASCII formatter.
+ log.SetFormatter(&log.JSONFormatter{})
+
+ // Use the Airbrake hook to report errors that have Error severity or above to
+ // an exception tracker. You can create custom hooks, see the Hooks section.
+ log.AddHook(&logrus_airbrake.AirbrakeHook{})
+
+ // Output to stderr instead of stdout, could also be a file.
+ log.SetOutput(os.Stderr)
+
+ // Only log the warning severity or above.
+ log.SetLevel(log.WarnLevel)
+}
+
+func main() {
+ log.WithFields(log.Fields{
+ "animal": "walrus",
+ "size": 10,
+ }).Info("A group of walrus emerges from the ocean")
+
+ log.WithFields(log.Fields{
+ "omg": true,
+ "number": 122,
+ }).Warn("The group's number increased tremendously!")
+
+ log.WithFields(log.Fields{
+ "omg": true,
+ "number": 100,
+ }).Fatal("The ice breaks!")
+}
+```
+
+For more advanced usage such as logging to multiple locations from the same
+application, you can also create an instance of the `logrus` Logger:
+
+```go
+package main
+
+import (
+ "github.com/Sirupsen/logrus"
+)
+
+// Create a new instance of the logger. You can have any number of instances.
+var log = logrus.New()
+
+func main() {
+ // The API for setting attributes is a little different than the package level
+ // exported logger. See Godoc.
+ log.Out = os.Stderr
+
+ log.WithFields(logrus.Fields{
+ "animal": "walrus",
+ "size": 10,
+ }).Info("A group of walrus emerges from the ocean")
+}
+```
+
+#### Fields
+
+Logrus encourages careful, structured logging though logging fields instead of
+long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
+to send event %s to topic %s with key %d")`, you should log the much more
+discoverable:
+
+```go
+log.WithFields(log.Fields{
+ "event": event,
+ "topic": topic,
+ "key": key,
+}).Fatal("Failed to send event")
+```
+
+We've found this API forces you to think about logging in a way that produces
+much more useful logging messages. We've been in countless situations where just
+a single added field to a log statement that was already there would've saved us
+hours. The `WithFields` call is optional.
+
+In general, with Logrus using any of the `printf`-family functions should be
+seen as a hint you should add a field, however, you can still use the
+`printf`-family functions with Logrus.
+
+#### Hooks
+
+You can add hooks for logging levels. For example to send errors to an exception
+tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
+multiple places simultaneously, e.g. syslog.
+
+```go
+// Not the real implementation of the Airbrake hook. Just a simple sample.
+import (
+ log "github.com/Sirupsen/logrus"
+)
+
+func init() {
+ log.AddHook(new(AirbrakeHook))
+}
+
+type AirbrakeHook struct{}
+
+// `Fire()` takes the entry that the hook is fired for. `entry.Data[]` contains
+// the fields for the entry. See the Fields section of the README.
+func (hook *AirbrakeHook) Fire(entry *logrus.Entry) error {
+ err := airbrake.Notify(entry.Data["error"].(error))
+ if err != nil {
+ log.WithFields(log.Fields{
+ "source": "airbrake",
+ "endpoint": airbrake.Endpoint,
+ }).Info("Failed to send error to Airbrake")
+ }
+
+ return nil
+}
+
+// `Levels()` returns a slice of `Levels` the hook is fired for.
+func (hook *AirbrakeHook) Levels() []log.Level {
+ return []log.Level{
+ log.ErrorLevel,
+ log.FatalLevel,
+ log.PanicLevel,
+ }
+}
+```
+
+Logrus comes with built-in hooks. Add those, or your custom hook, in `init`:
+
+```go
+import (
+ log "github.com/Sirupsen/logrus"
+ "github.com/Sirupsen/logrus/hooks/airbrake"
+ "github.com/Sirupsen/logrus/hooks/syslog"
+ "log/syslog"
+)
+
+func init() {
+ log.AddHook(new(logrus_airbrake.AirbrakeHook))
+
+ hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
+ if err != nil {
+ log.Error("Unable to connect to local syslog daemon")
+ } else {
+ log.AddHook(hook)
+ }
+}
+```
+
+* [`github.com/Sirupsen/logrus/hooks/airbrake`](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go)
+ Send errors to an exception tracking service compatible with the Airbrake API.
+ Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes.
+
+* [`github.com/Sirupsen/logrus/hooks/papertrail`](https://github.com/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go)
+ Send errors to the Papertrail hosted logging service via UDP.
+
+* [`github.com/Sirupsen/logrus/hooks/syslog`](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go)
+ Send errors to remote syslog server.
+ Uses standard library `log/syslog` behind the scenes.
+
+* [`github.com/nubo/hiprus`](https://github.com/nubo/hiprus)
+ Send errors to a channel in hipchat.
+
+* [`github.com/sebest/logrusly`](https://github.com/sebest/logrusly)
+ Send logs to Loggly (https://www.loggly.com/)
+
+* [`github.com/johntdyer/slackrus`](https://github.com/johntdyer/slackrus)
+ Hook for Slack chat.
+
+#### Level logging
+
+Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
+
+```go
+log.Debug("Useful debugging information.")
+log.Info("Something noteworthy happened!")
+log.Warn("You should probably take a look at this.")
+log.Error("Something failed but I'm not quitting.")
+// Calls os.Exit(1) after logging
+log.Fatal("Bye.")
+// Calls panic() after logging
+log.Panic("I'm bailing.")
+```
+
+You can set the logging level on a `Logger`, then it will only log entries with
+that severity or anything above it:
+
+```go
+// Will log anything that is info or above (warn, error, fatal, panic). Default.
+log.SetLevel(log.InfoLevel)
+```
+
+It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
+environment if your application has that.
+
+#### Entries
+
+Besides the fields added with `WithField` or `WithFields` some fields are
+automatically added to all logging events:
+
+1. `time`. The timestamp when the entry was created.
+2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
+ the `AddFields` call. E.g. `Failed to send event.`
+3. `level`. The logging level. E.g. `info`.
+
+#### Environments
+
+Logrus has no notion of environment.
+
+If you wish for hooks and formatters to only be used in specific environments,
+you should handle that yourself. For example, if your application has a global
+variable `Environment`, which is a string representation of the environment you
+could do:
+
+```go
+import (
+ log "github.com/Sirupsen/logrus"
+)
+
+init() {
+ // do something here to set environment depending on an environment variable
+ // or command-line flag
+ if Environment == "production" {
+ log.SetFormatter(logrus.JSONFormatter)
+ } else {
+ // The TextFormatter is default, you don't actually have to do this.
+ log.SetFormatter(logrus.TextFormatter)
+ }
+}
+```
+
+This configuration is how `logrus` was intended to be used, but JSON in
+production is mostly only useful if you do log aggregation with tools like
+Splunk or Logstash.
+
+#### Formatters
+
+The built-in logging formatters are:
+
+* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
+ without colors.
+ * *Note:* to force colored output when there is no TTY, set the `ForceColors`
+ field to `true`. To force no colored output even if there is a TTY set the
+ `DisableColors` field to `true`
+* `logrus.JSONFormatter`. Logs fields as JSON.
+
+Third party logging formatters:
+
+* [`zalgo`](https://github.com/aybabtme/logzalgo): invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
+
+You can define your formatter by implementing the `Formatter` interface,
+requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
+`Fields` type (`map[string]interface{}`) with all your fields as well as the
+default ones (see Entries section above):
+
+```go
+type MyJSONFormatter struct {
+}
+
+log.SetFormatter(new(MyJSONFormatter))
+
+func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
+ // Note this doesn't include Time, Level and Message which are available on
+ // the Entry. Consult `godoc` on information about those fields or read the
+ // source of the official loggers.
+ serialized, err := json.Marshal(entry.Data)
+ if err != nil {
+ return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
+ }
+ return append(serialized, '\n'), nil
+}
+```
+
+#### Logger as an `io.Writer`
+
+Logrus can be transormed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsability to close it.
+
+```go
+w := logger.Writer()
+defer w.Close()
+
+srv := http.Server{
+ // create a stdlib log.Logger that writes to
+ // logrus.Logger.
+ ErrorLog: log.New(w, "", 0),
+}
+```
+
+Each line written to that writer will be printed the usual way, using formatters
+and hooks. The level for those entries is `info`.
+
+#### Rotation
+
+Log rotation is not provided with Logrus. Log rotation should be done by an
+external program (like `logrotated(8)`) that can compress and delete old log
+entries. It should not be a feature of the application-level logger.
+
+
+[godoc]: https://godoc.org/github.com/Sirupsen/logrus
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry.go
new file mode 100644
index 0000000..17fe6f7
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry.go
@@ -0,0 +1,252 @@
+package logrus
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+ "time"
+)
+
+// An entry is the final or intermediate Logrus logging entry. It contains all
+// the fields passed with WithField{,s}. It's finally logged when Debug, Info,
+// Warn, Error, Fatal or Panic is called on it. These objects can be reused and
+// passed around as much as you wish to avoid field duplication.
+type Entry struct {
+ Logger *Logger
+
+ // Contains all the fields set by the user.
+ Data Fields
+
+ // Time at which the log entry was created
+ Time time.Time
+
+ // Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic
+ Level Level
+
+ // Message passed to Debug, Info, Warn, Error, Fatal or Panic
+ Message string
+}
+
+func NewEntry(logger *Logger) *Entry {
+ return &Entry{
+ Logger: logger,
+ // Default is three fields, give a little extra room
+ Data: make(Fields, 5),
+ }
+}
+
+// Returns a reader for the entry, which is a proxy to the formatter.
+func (entry *Entry) Reader() (*bytes.Buffer, error) {
+ serialized, err := entry.Logger.Formatter.Format(entry)
+ return bytes.NewBuffer(serialized), err
+}
+
+// Returns the string representation from the reader and ultimately the
+// formatter.
+func (entry *Entry) String() (string, error) {
+ reader, err := entry.Reader()
+ if err != nil {
+ return "", err
+ }
+
+ return reader.String(), err
+}
+
+// Add a single field to the Entry.
+func (entry *Entry) WithField(key string, value interface{}) *Entry {
+ return entry.WithFields(Fields{key: value})
+}
+
+// Add a map of fields to the Entry.
+func (entry *Entry) WithFields(fields Fields) *Entry {
+ data := Fields{}
+ for k, v := range entry.Data {
+ data[k] = v
+ }
+ for k, v := range fields {
+ data[k] = v
+ }
+ return &Entry{Logger: entry.Logger, Data: data}
+}
+
+func (entry *Entry) log(level Level, msg string) {
+ entry.Time = time.Now()
+ entry.Level = level
+ entry.Message = msg
+
+ if err := entry.Logger.Hooks.Fire(level, entry); err != nil {
+ entry.Logger.mu.Lock()
+ fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
+ entry.Logger.mu.Unlock()
+ }
+
+ reader, err := entry.Reader()
+ if err != nil {
+ entry.Logger.mu.Lock()
+ fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
+ entry.Logger.mu.Unlock()
+ }
+
+ entry.Logger.mu.Lock()
+ defer entry.Logger.mu.Unlock()
+
+ _, err = io.Copy(entry.Logger.Out, reader)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
+ }
+
+ // To avoid Entry#log() returning a value that only would make sense for
+ // panic() to use in Entry#Panic(), we avoid the allocation by checking
+ // directly here.
+ if level <= PanicLevel {
+ panic(entry)
+ }
+}
+
+func (entry *Entry) Debug(args ...interface{}) {
+ if entry.Logger.Level >= DebugLevel {
+ entry.log(DebugLevel, fmt.Sprint(args...))
+ }
+}
+
+func (entry *Entry) Print(args ...interface{}) {
+ entry.Info(args...)
+}
+
+func (entry *Entry) Info(args ...interface{}) {
+ if entry.Logger.Level >= InfoLevel {
+ entry.log(InfoLevel, fmt.Sprint(args...))
+ }
+}
+
+func (entry *Entry) Warn(args ...interface{}) {
+ if entry.Logger.Level >= WarnLevel {
+ entry.log(WarnLevel, fmt.Sprint(args...))
+ }
+}
+
+func (entry *Entry) Warning(args ...interface{}) {
+ entry.Warn(args...)
+}
+
+func (entry *Entry) Error(args ...interface{}) {
+ if entry.Logger.Level >= ErrorLevel {
+ entry.log(ErrorLevel, fmt.Sprint(args...))
+ }
+}
+
+func (entry *Entry) Fatal(args ...interface{}) {
+ if entry.Logger.Level >= FatalLevel {
+ entry.log(FatalLevel, fmt.Sprint(args...))
+ }
+ os.Exit(1)
+}
+
+func (entry *Entry) Panic(args ...interface{}) {
+ if entry.Logger.Level >= PanicLevel {
+ entry.log(PanicLevel, fmt.Sprint(args...))
+ }
+ panic(fmt.Sprint(args...))
+}
+
+// Entry Printf family functions
+
+func (entry *Entry) Debugf(format string, args ...interface{}) {
+ if entry.Logger.Level >= DebugLevel {
+ entry.Debug(fmt.Sprintf(format, args...))
+ }
+}
+
+func (entry *Entry) Infof(format string, args ...interface{}) {
+ if entry.Logger.Level >= InfoLevel {
+ entry.Info(fmt.Sprintf(format, args...))
+ }
+}
+
+func (entry *Entry) Printf(format string, args ...interface{}) {
+ entry.Infof(format, args...)
+}
+
+func (entry *Entry) Warnf(format string, args ...interface{}) {
+ if entry.Logger.Level >= WarnLevel {
+ entry.Warn(fmt.Sprintf(format, args...))
+ }
+}
+
+func (entry *Entry) Warningf(format string, args ...interface{}) {
+ entry.Warnf(format, args...)
+}
+
+func (entry *Entry) Errorf(format string, args ...interface{}) {
+ if entry.Logger.Level >= ErrorLevel {
+ entry.Error(fmt.Sprintf(format, args...))
+ }
+}
+
+func (entry *Entry) Fatalf(format string, args ...interface{}) {
+ if entry.Logger.Level >= FatalLevel {
+ entry.Fatal(fmt.Sprintf(format, args...))
+ }
+}
+
+func (entry *Entry) Panicf(format string, args ...interface{}) {
+ if entry.Logger.Level >= PanicLevel {
+ entry.Panic(fmt.Sprintf(format, args...))
+ }
+}
+
+// Entry Println family functions
+
+func (entry *Entry) Debugln(args ...interface{}) {
+ if entry.Logger.Level >= DebugLevel {
+ entry.Debug(entry.sprintlnn(args...))
+ }
+}
+
+func (entry *Entry) Infoln(args ...interface{}) {
+ if entry.Logger.Level >= InfoLevel {
+ entry.Info(entry.sprintlnn(args...))
+ }
+}
+
+func (entry *Entry) Println(args ...interface{}) {
+ entry.Infoln(args...)
+}
+
+func (entry *Entry) Warnln(args ...interface{}) {
+ if entry.Logger.Level >= WarnLevel {
+ entry.Warn(entry.sprintlnn(args...))
+ }
+}
+
+func (entry *Entry) Warningln(args ...interface{}) {
+ entry.Warnln(args...)
+}
+
+func (entry *Entry) Errorln(args ...interface{}) {
+ if entry.Logger.Level >= ErrorLevel {
+ entry.Error(entry.sprintlnn(args...))
+ }
+}
+
+func (entry *Entry) Fatalln(args ...interface{}) {
+ if entry.Logger.Level >= FatalLevel {
+ entry.Fatal(entry.sprintlnn(args...))
+ }
+}
+
+func (entry *Entry) Panicln(args ...interface{}) {
+ if entry.Logger.Level >= PanicLevel {
+ entry.Panic(entry.sprintlnn(args...))
+ }
+}
+
+// Sprintlnn => Sprint no newline. This is to get the behavior of how
+// fmt.Sprintln where spaces are always added between operands, regardless of
+// their type. Instead of vendoring the Sprintln implementation to spare a
+// string allocation, we do the simplest thing.
+func (entry *Entry) sprintlnn(args ...interface{}) string {
+ msg := fmt.Sprintln(args...)
+ return msg[:len(msg)-1]
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry_test.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry_test.go
new file mode 100644
index 0000000..98717df
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry_test.go
@@ -0,0 +1,53 @@
+package logrus
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestEntryPanicln(t *testing.T) {
+ errBoom := fmt.Errorf("boom time")
+
+ defer func() {
+ p := recover()
+ assert.NotNil(t, p)
+
+ switch pVal := p.(type) {
+ case *Entry:
+ assert.Equal(t, "kaboom", pVal.Message)
+ assert.Equal(t, errBoom, pVal.Data["err"])
+ default:
+ t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
+ }
+ }()
+
+ logger := New()
+ logger.Out = &bytes.Buffer{}
+ entry := NewEntry(logger)
+ entry.WithField("err", errBoom).Panicln("kaboom")
+}
+
+func TestEntryPanicf(t *testing.T) {
+ errBoom := fmt.Errorf("boom again")
+
+ defer func() {
+ p := recover()
+ assert.NotNil(t, p)
+
+ switch pVal := p.(type) {
+ case *Entry:
+ assert.Equal(t, "kaboom true", pVal.Message)
+ assert.Equal(t, errBoom, pVal.Data["err"])
+ default:
+ t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
+ }
+ }()
+
+ logger := New()
+ logger.Out = &bytes.Buffer{}
+ entry := NewEntry(logger)
+ entry.WithField("err", errBoom).Panicf("kaboom %v", true)
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go
new file mode 100644
index 0000000..a62ba45
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go
@@ -0,0 +1,40 @@
+package main
+
+import (
+ "github.com/Sirupsen/logrus"
+)
+
+var log = logrus.New()
+
+func init() {
+ log.Formatter = new(logrus.JSONFormatter)
+ log.Formatter = new(logrus.TextFormatter) // default
+}
+
+func main() {
+ defer func() {
+ err := recover()
+ if err != nil {
+ log.WithFields(logrus.Fields{
+ "omg": true,
+ "err": err,
+ "number": 100,
+ }).Fatal("The ice breaks!")
+ }
+ }()
+
+ log.WithFields(logrus.Fields{
+ "animal": "walrus",
+ "size": 10,
+ }).Info("A group of walrus emerges from the ocean")
+
+ log.WithFields(logrus.Fields{
+ "omg": true,
+ "number": 122,
+ }).Warn("The group's number increased tremendously!")
+
+ log.WithFields(logrus.Fields{
+ "animal": "orca",
+ "size": 9009,
+ }).Panic("It's over 9000!")
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go
new file mode 100644
index 0000000..42e7a4c
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go
@@ -0,0 +1,35 @@
+package main
+
+import (
+ "github.com/Sirupsen/logrus"
+ "github.com/Sirupsen/logrus/hooks/airbrake"
+ "github.com/tobi/airbrake-go"
+)
+
+var log = logrus.New()
+
+func init() {
+ log.Formatter = new(logrus.TextFormatter) // default
+ log.Hooks.Add(new(logrus_airbrake.AirbrakeHook))
+}
+
+func main() {
+ airbrake.Endpoint = "https://exceptions.whatever.com/notifier_api/v2/notices.xml"
+ airbrake.ApiKey = "whatever"
+ airbrake.Environment = "production"
+
+ log.WithFields(logrus.Fields{
+ "animal": "walrus",
+ "size": 10,
+ }).Info("A group of walrus emerges from the ocean")
+
+ log.WithFields(logrus.Fields{
+ "omg": true,
+ "number": 122,
+ }).Warn("The group's number increased tremendously!")
+
+ log.WithFields(logrus.Fields{
+ "omg": true,
+ "number": 100,
+ }).Fatal("The ice breaks!")
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/exported.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/exported.go
new file mode 100644
index 0000000..fd092fc
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/exported.go
@@ -0,0 +1,186 @@
+package logrus
+
+import (
+ "io"
+)
+
+var (
+ // std is the name of the standard logger in stdlib `log`
+ std = New()
+)
+
+func StandardLogger() *Logger {
+ return std
+}
+
+// SetOutput sets the standard logger output.
+func SetOutput(out io.Writer) {
+ std.mu.Lock()
+ defer std.mu.Unlock()
+ std.Out = out
+}
+
+// SetFormatter sets the standard logger formatter.
+func SetFormatter(formatter Formatter) {
+ std.mu.Lock()
+ defer std.mu.Unlock()
+ std.Formatter = formatter
+}
+
+// SetLevel sets the standard logger level.
+func SetLevel(level Level) {
+ std.mu.Lock()
+ defer std.mu.Unlock()
+ std.Level = level
+}
+
+// GetLevel returns the standard logger level.
+func GetLevel() Level {
+ return std.Level
+}
+
+// AddHook adds a hook to the standard logger hooks.
+func AddHook(hook Hook) {
+ std.mu.Lock()
+ defer std.mu.Unlock()
+ std.Hooks.Add(hook)
+}
+
+// WithField creates an entry from the standard logger and adds a field to
+// it. If you want multiple fields, use `WithFields`.
+//
+// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
+// or Panic on the Entry it returns.
+func WithField(key string, value interface{}) *Entry {
+ return std.WithField(key, value)
+}
+
+// WithFields creates an entry from the standard logger and adds multiple
+// fields to it. This is simply a helper for `WithField`, invoking it
+// once for each field.
+//
+// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
+// or Panic on the Entry it returns.
+func WithFields(fields Fields) *Entry {
+ return std.WithFields(fields)
+}
+
+// Debug logs a message at level Debug on the standard logger.
+func Debug(args ...interface{}) {
+ std.Debug(args...)
+}
+
+// Print logs a message at level Info on the standard logger.
+func Print(args ...interface{}) {
+ std.Print(args...)
+}
+
+// Info logs a message at level Info on the standard logger.
+func Info(args ...interface{}) {
+ std.Info(args...)
+}
+
+// Warn logs a message at level Warn on the standard logger.
+func Warn(args ...interface{}) {
+ std.Warn(args...)
+}
+
+// Warning logs a message at level Warn on the standard logger.
+func Warning(args ...interface{}) {
+ std.Warning(args...)
+}
+
+// Error logs a message at level Error on the standard logger.
+func Error(args ...interface{}) {
+ std.Error(args...)
+}
+
+// Panic logs a message at level Panic on the standard logger.
+func Panic(args ...interface{}) {
+ std.Panic(args...)
+}
+
+// Fatal logs a message at level Fatal on the standard logger.
+func Fatal(args ...interface{}) {
+ std.Fatal(args...)
+}
+
+// Debugf logs a message at level Debug on the standard logger.
+func Debugf(format string, args ...interface{}) {
+ std.Debugf(format, args...)
+}
+
+// Printf logs a message at level Info on the standard logger.
+func Printf(format string, args ...interface{}) {
+ std.Printf(format, args...)
+}
+
+// Infof logs a message at level Info on the standard logger.
+func Infof(format string, args ...interface{}) {
+ std.Infof(format, args...)
+}
+
+// Warnf logs a message at level Warn on the standard logger.
+func Warnf(format string, args ...interface{}) {
+ std.Warnf(format, args...)
+}
+
+// Warningf logs a message at level Warn on the standard logger.
+func Warningf(format string, args ...interface{}) {
+ std.Warningf(format, args...)
+}
+
+// Errorf logs a message at level Error on the standard logger.
+func Errorf(format string, args ...interface{}) {
+ std.Errorf(format, args...)
+}
+
+// Panicf logs a message at level Panic on the standard logger.
+func Panicf(format string, args ...interface{}) {
+ std.Panicf(format, args...)
+}
+
+// Fatalf logs a message at level Fatal on the standard logger.
+func Fatalf(format string, args ...interface{}) {
+ std.Fatalf(format, args...)
+}
+
+// Debugln logs a message at level Debug on the standard logger.
+func Debugln(args ...interface{}) {
+ std.Debugln(args...)
+}
+
+// Println logs a message at level Info on the standard logger.
+func Println(args ...interface{}) {
+ std.Println(args...)
+}
+
+// Infoln logs a message at level Info on the standard logger.
+func Infoln(args ...interface{}) {
+ std.Infoln(args...)
+}
+
+// Warnln logs a message at level Warn on the standard logger.
+func Warnln(args ...interface{}) {
+ std.Warnln(args...)
+}
+
+// Warningln logs a message at level Warn on the standard logger.
+func Warningln(args ...interface{}) {
+ std.Warningln(args...)
+}
+
+// Errorln logs a message at level Error on the standard logger.
+func Errorln(args ...interface{}) {
+ std.Errorln(args...)
+}
+
+// Panicln logs a message at level Panic on the standard logger.
+func Panicln(args ...interface{}) {
+ std.Panicln(args...)
+}
+
+// Fatalln logs a message at level Fatal on the standard logger.
+func Fatalln(args ...interface{}) {
+ std.Fatalln(args...)
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter.go
new file mode 100644
index 0000000..038ce9f
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter.go
@@ -0,0 +1,44 @@
+package logrus
+
+// The Formatter interface is used to implement a custom Formatter. It takes an
+// `Entry`. It exposes all the fields, including the default ones:
+//
+// * `entry.Data["msg"]`. The message passed from Info, Warn, Error ..
+// * `entry.Data["time"]`. The timestamp.
+// * `entry.Data["level"]. The level the entry was logged at.
+//
+// Any additional fields added with `WithField` or `WithFields` are also in
+// `entry.Data`. Format is expected to return an array of bytes which are then
+// logged to `logger.Out`.
+type Formatter interface {
+ Format(*Entry) ([]byte, error)
+}
+
+// This is to not silently overwrite `time`, `msg` and `level` fields when
+// dumping it. If this code wasn't there doing:
+//
+// logrus.WithField("level", 1).Info("hello")
+//
+// Would just silently drop the user provided level. Instead with this code
+// it'll logged as:
+//
+// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
+//
+// It's not exported because it's still using Data in an opinionated way. It's to
+// avoid code duplication between the two default formatters.
+func prefixFieldClashes(data Fields) {
+ _, ok := data["time"]
+ if ok {
+ data["fields.time"] = data["time"]
+ }
+
+ _, ok = data["msg"]
+ if ok {
+ data["fields.msg"] = data["msg"]
+ }
+
+ _, ok = data["level"]
+ if ok {
+ data["fields.level"] = data["level"]
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter_bench_test.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter_bench_test.go
new file mode 100644
index 0000000..77989da
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter_bench_test.go
@@ -0,0 +1,88 @@
+package logrus
+
+import (
+ "testing"
+ "time"
+)
+
+// smallFields is a small size data set for benchmarking
+var smallFields = Fields{
+ "foo": "bar",
+ "baz": "qux",
+ "one": "two",
+ "three": "four",
+}
+
+// largeFields is a large size data set for benchmarking
+var largeFields = Fields{
+ "foo": "bar",
+ "baz": "qux",
+ "one": "two",
+ "three": "four",
+ "five": "six",
+ "seven": "eight",
+ "nine": "ten",
+ "eleven": "twelve",
+ "thirteen": "fourteen",
+ "fifteen": "sixteen",
+ "seventeen": "eighteen",
+ "nineteen": "twenty",
+ "a": "b",
+ "c": "d",
+ "e": "f",
+ "g": "h",
+ "i": "j",
+ "k": "l",
+ "m": "n",
+ "o": "p",
+ "q": "r",
+ "s": "t",
+ "u": "v",
+ "w": "x",
+ "y": "z",
+ "this": "will",
+ "make": "thirty",
+ "entries": "yeah",
+}
+
+func BenchmarkSmallTextFormatter(b *testing.B) {
+ doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields)
+}
+
+func BenchmarkLargeTextFormatter(b *testing.B) {
+ doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields)
+}
+
+func BenchmarkSmallColoredTextFormatter(b *testing.B) {
+ doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields)
+}
+
+func BenchmarkLargeColoredTextFormatter(b *testing.B) {
+ doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields)
+}
+
+func BenchmarkSmallJSONFormatter(b *testing.B) {
+ doBenchmark(b, &JSONFormatter{}, smallFields)
+}
+
+func BenchmarkLargeJSONFormatter(b *testing.B) {
+ doBenchmark(b, &JSONFormatter{}, largeFields)
+}
+
+func doBenchmark(b *testing.B, formatter Formatter, fields Fields) {
+ entry := &Entry{
+ Time: time.Time{},
+ Level: InfoLevel,
+ Message: "message",
+ Data: fields,
+ }
+ var d []byte
+ var err error
+ for i := 0; i < b.N; i++ {
+ d, err = formatter.Format(entry)
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.SetBytes(int64(len(d)))
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hook_test.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hook_test.go
new file mode 100644
index 0000000..13f34cb
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hook_test.go
@@ -0,0 +1,122 @@
+package logrus
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+type TestHook struct {
+ Fired bool
+}
+
+func (hook *TestHook) Fire(entry *Entry) error {
+ hook.Fired = true
+ return nil
+}
+
+func (hook *TestHook) Levels() []Level {
+ return []Level{
+ DebugLevel,
+ InfoLevel,
+ WarnLevel,
+ ErrorLevel,
+ FatalLevel,
+ PanicLevel,
+ }
+}
+
+func TestHookFires(t *testing.T) {
+ hook := new(TestHook)
+
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Hooks.Add(hook)
+ assert.Equal(t, hook.Fired, false)
+
+ log.Print("test")
+ }, func(fields Fields) {
+ assert.Equal(t, hook.Fired, true)
+ })
+}
+
+type ModifyHook struct {
+}
+
+func (hook *ModifyHook) Fire(entry *Entry) error {
+ entry.Data["wow"] = "whale"
+ return nil
+}
+
+func (hook *ModifyHook) Levels() []Level {
+ return []Level{
+ DebugLevel,
+ InfoLevel,
+ WarnLevel,
+ ErrorLevel,
+ FatalLevel,
+ PanicLevel,
+ }
+}
+
+func TestHookCanModifyEntry(t *testing.T) {
+ hook := new(ModifyHook)
+
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Hooks.Add(hook)
+ log.WithField("wow", "elephant").Print("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["wow"], "whale")
+ })
+}
+
+func TestCanFireMultipleHooks(t *testing.T) {
+ hook1 := new(ModifyHook)
+ hook2 := new(TestHook)
+
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Hooks.Add(hook1)
+ log.Hooks.Add(hook2)
+
+ log.WithField("wow", "elephant").Print("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["wow"], "whale")
+ assert.Equal(t, hook2.Fired, true)
+ })
+}
+
+type ErrorHook struct {
+ Fired bool
+}
+
+func (hook *ErrorHook) Fire(entry *Entry) error {
+ hook.Fired = true
+ return nil
+}
+
+func (hook *ErrorHook) Levels() []Level {
+ return []Level{
+ ErrorLevel,
+ }
+}
+
+func TestErrorHookShouldntFireOnInfo(t *testing.T) {
+ hook := new(ErrorHook)
+
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Hooks.Add(hook)
+ log.Info("test")
+ }, func(fields Fields) {
+ assert.Equal(t, hook.Fired, false)
+ })
+}
+
+func TestErrorHookShouldFireOnError(t *testing.T) {
+ hook := new(ErrorHook)
+
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Hooks.Add(hook)
+ log.Error("test")
+ }, func(fields Fields) {
+ assert.Equal(t, hook.Fired, true)
+ })
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks.go
new file mode 100644
index 0000000..0da2b36
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks.go
@@ -0,0 +1,34 @@
+package logrus
+
+// A hook to be fired when logging on the logging levels returned from
+// `Levels()` on your implementation of the interface. Note that this is not
+// fired in a goroutine or a channel with workers, you should handle such
+// functionality yourself if your call is non-blocking and you don't wish for
+// the logging calls for levels returned from `Levels()` to block.
+type Hook interface {
+ Levels() []Level
+ Fire(*Entry) error
+}
+
+// Internal type for storing the hooks on a logger instance.
+type levelHooks map[Level][]Hook
+
+// Add a hook to an instance of logger. This is called with
+// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
+func (hooks levelHooks) Add(hook Hook) {
+ for _, level := range hook.Levels() {
+ hooks[level] = append(hooks[level], hook)
+ }
+}
+
+// Fire all the hooks for the passed level. Used by `entry.log` to fire
+// appropriate hooks for a log entry.
+func (hooks levelHooks) Fire(level Level, entry *Entry) error {
+ for _, hook := range hooks[level] {
+ if err := hook.Fire(entry); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go
new file mode 100644
index 0000000..880d21e
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go
@@ -0,0 +1,54 @@
+package logrus_airbrake
+
+import (
+ "github.com/Sirupsen/logrus"
+ "github.com/tobi/airbrake-go"
+)
+
+// AirbrakeHook to send exceptions to an exception-tracking service compatible
+// with the Airbrake API. You must set:
+// * airbrake.Endpoint
+// * airbrake.ApiKey
+// * airbrake.Environment (only sends exceptions when set to "production")
+//
+// Before using this hook, to send an error. Entries that trigger an Error,
+// Fatal or Panic should now include an "error" field to send to Airbrake.
+type AirbrakeHook struct{}
+
+func (hook *AirbrakeHook) Fire(entry *logrus.Entry) error {
+ if entry.Data["error"] == nil {
+ entry.Logger.WithFields(logrus.Fields{
+ "source": "airbrake",
+ "endpoint": airbrake.Endpoint,
+ }).Warn("Exceptions sent to Airbrake must have an 'error' key with the error")
+ return nil
+ }
+
+ err, ok := entry.Data["error"].(error)
+ if !ok {
+ entry.Logger.WithFields(logrus.Fields{
+ "source": "airbrake",
+ "endpoint": airbrake.Endpoint,
+ }).Warn("Exceptions sent to Airbrake must have an `error` key of type `error`")
+ return nil
+ }
+
+ airErr := airbrake.Notify(err)
+ if airErr != nil {
+ entry.Logger.WithFields(logrus.Fields{
+ "source": "airbrake",
+ "endpoint": airbrake.Endpoint,
+ "error": airErr,
+ }).Warn("Failed to send error to Airbrake")
+ }
+
+ return nil
+}
+
+func (hook *AirbrakeHook) Levels() []logrus.Level {
+ return []logrus.Level{
+ logrus.ErrorLevel,
+ logrus.FatalLevel,
+ logrus.PanicLevel,
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/README.md b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/README.md
new file mode 100644
index 0000000..ae61e92
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/README.md
@@ -0,0 +1,28 @@
+# Papertrail Hook for Logrus
+
+[Papertrail](https://papertrailapp.com) provides hosted log management. Once stored in Papertrail, you can [group](http://help.papertrailapp.com/kb/how-it-works/groups/) your logs on various dimensions, [search](http://help.papertrailapp.com/kb/how-it-works/search-syntax) them, and trigger [alerts](http://help.papertrailapp.com/kb/how-it-works/alerts).
+
+In most deployments, you'll want to send logs to Papertrail via their [remote_syslog](http://help.papertrailapp.com/kb/configuration/configuring-centralized-logging-from-text-log-files-in-unix/) daemon, which requires no application-specific configuration. This hook is intended for relatively low-volume logging, likely in managed cloud hosting deployments where installing `remote_syslog` is not possible.
+
+## Usage
+
+You can find your Papertrail UDP port on your [Papertrail account page](https://papertrailapp.com/account/destinations). Substitute it below for `YOUR_PAPERTRAIL_UDP_PORT`.
+
+For `YOUR_APP_NAME`, substitute a short string that will readily identify your application or service in the logs.
+
+```go
+import (
+ "log/syslog"
+ "github.com/Sirupsen/logrus"
+ "github.com/Sirupsen/logrus/hooks/papertrail"
+)
+
+func main() {
+ log := logrus.New()
+ hook, err := logrus_papertrail.NewPapertrailHook("logs.papertrailapp.com", YOUR_PAPERTRAIL_UDP_PORT, YOUR_APP_NAME)
+
+ if err == nil {
+ log.Hooks.Add(hook)
+ }
+}
+```
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go
new file mode 100644
index 0000000..c0f10c1
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go
@@ -0,0 +1,55 @@
+package logrus_papertrail
+
+import (
+ "fmt"
+ "net"
+ "os"
+ "time"
+
+ "github.com/Sirupsen/logrus"
+)
+
+const (
+ format = "Jan 2 15:04:05"
+)
+
+// PapertrailHook to send logs to a logging service compatible with the Papertrail API.
+type PapertrailHook struct {
+ Host string
+ Port int
+ AppName string
+ UDPConn net.Conn
+}
+
+// NewPapertrailHook creates a hook to be added to an instance of logger.
+func NewPapertrailHook(host string, port int, appName string) (*PapertrailHook, error) {
+ conn, err := net.Dial("udp", fmt.Sprintf("%s:%d", host, port))
+ return &PapertrailHook{host, port, appName, conn}, err
+}
+
+// Fire is called when a log event is fired.
+func (hook *PapertrailHook) Fire(entry *logrus.Entry) error {
+ date := time.Now().Format(format)
+ msg, _ := entry.String()
+ payload := fmt.Sprintf("<22> %s %s: %s", date, hook.AppName, msg)
+
+ bytesWritten, err := hook.UDPConn.Write([]byte(payload))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Unable to send log line to Papertrail via UDP. Wrote %d bytes before error: %v", bytesWritten, err)
+ return err
+ }
+
+ return nil
+}
+
+// Levels returns the available logging levels.
+func (hook *PapertrailHook) Levels() []logrus.Level {
+ return []logrus.Level{
+ logrus.PanicLevel,
+ logrus.FatalLevel,
+ logrus.ErrorLevel,
+ logrus.WarnLevel,
+ logrus.InfoLevel,
+ logrus.DebugLevel,
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail_test.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail_test.go
new file mode 100644
index 0000000..96318d0
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail_test.go
@@ -0,0 +1,26 @@
+package logrus_papertrail
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/Sirupsen/logrus"
+ "github.com/stvp/go-udp-testing"
+)
+
+func TestWritingToUDP(t *testing.T) {
+ port := 16661
+ udp.SetAddr(fmt.Sprintf(":%d", port))
+
+ hook, err := NewPapertrailHook("localhost", port, "test")
+ if err != nil {
+ t.Errorf("Unable to connect to local UDP server.")
+ }
+
+ log := logrus.New()
+ log.Hooks.Add(hook)
+
+ udp.ShouldReceive(t, "foo", func() {
+ log.Info("foo")
+ })
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/sentry/README.md b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/sentry/README.md
new file mode 100644
index 0000000..a409f3b
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/sentry/README.md
@@ -0,0 +1,61 @@
+# Sentry Hook for Logrus
+
+[Sentry](https://getsentry.com) provides both self-hosted and hosted
+solutions for exception tracking.
+Both client and server are
+[open source](https://github.com/getsentry/sentry).
+
+## Usage
+
+Every sentry application defined on the server gets a different
+[DSN](https://www.getsentry.com/docs/). In the example below replace
+`YOUR_DSN` with the one created for your application.
+
+```go
+import (
+ "github.com/Sirupsen/logrus"
+ "github.com/Sirupsen/logrus/hooks/sentry"
+)
+
+func main() {
+ log := logrus.New()
+ hook, err := logrus_sentry.NewSentryHook(YOUR_DSN, []logrus.Level{
+ logrus.PanicLevel,
+ logrus.FatalLevel,
+ logrus.ErrorLevel,
+ })
+
+ if err == nil {
+ log.Hooks.Add(hook)
+ }
+}
+```
+
+## Special fields
+
+Some logrus fields have a special meaning in this hook,
+these are server_name and logger.
+When logs are sent to sentry these fields are treated differently.
+- server_name (also known as hostname) is the name of the server which
+is logging the event (hostname.example.com)
+- logger is the part of the application which is logging the event.
+In go this usually means setting it to the name of the package.
+
+## Timeout
+
+`Timeout` is the time the sentry hook will wait for a response
+from the sentry server.
+
+If this time elapses with no response from
+the server an error will be returned.
+
+If `Timeout` is set to 0 the SentryHook will not wait for a reply
+and will assume a correct delivery.
+
+The SentryHook has a default timeout of `100 milliseconds` when created
+with a call to `NewSentryHook`. This can be changed by assigning a value to the `Timeout` field:
+
+```go
+hook, _ := logrus_sentry.NewSentryHook(...)
+hook.Timeout = 20*time.Seconds
+```
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/sentry/sentry.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/sentry/sentry.go
new file mode 100644
index 0000000..379f281
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/sentry/sentry.go
@@ -0,0 +1,100 @@
+package logrus_sentry
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/Sirupsen/logrus"
+ "github.com/getsentry/raven-go"
+)
+
+var (
+ severityMap = map[logrus.Level]raven.Severity{
+ logrus.DebugLevel: raven.DEBUG,
+ logrus.InfoLevel: raven.INFO,
+ logrus.WarnLevel: raven.WARNING,
+ logrus.ErrorLevel: raven.ERROR,
+ logrus.FatalLevel: raven.FATAL,
+ logrus.PanicLevel: raven.FATAL,
+ }
+)
+
+func getAndDel(d logrus.Fields, key string) (string, bool) {
+ var (
+ ok bool
+ v interface{}
+ val string
+ )
+ if v, ok = d[key]; !ok {
+ return "", false
+ }
+
+ if val, ok = v.(string); !ok {
+ return "", false
+ }
+ delete(d, key)
+ return val, true
+}
+
+// SentryHook delivers logs to a sentry server.
+type SentryHook struct {
+ // Timeout sets the time to wait for a delivery error from the sentry server.
+ // If this is set to zero the server will not wait for any response and will
+ // consider the message correctly sent
+ Timeout time.Duration
+
+ client *raven.Client
+ levels []logrus.Level
+}
+
+// NewSentryHook creates a hook to be added to an instance of logger
+// and initializes the raven client.
+// This method sets the timeout to 100 milliseconds.
+func NewSentryHook(DSN string, levels []logrus.Level) (*SentryHook, error) {
+ client, err := raven.NewClient(DSN, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &SentryHook{100 * time.Millisecond, client, levels}, nil
+}
+
+// Called when an event should be sent to sentry
+// Special fields that sentry uses to give more information to the server
+// are extracted from entry.Data (if they are found)
+// These fields are: logger and server_name
+func (hook *SentryHook) Fire(entry *logrus.Entry) error {
+ packet := &raven.Packet{
+ Message: entry.Message,
+ Timestamp: raven.Timestamp(entry.Time),
+ Level: severityMap[entry.Level],
+ Platform: "go",
+ }
+
+ d := entry.Data
+
+ if logger, ok := getAndDel(d, "logger"); ok {
+ packet.Logger = logger
+ }
+ if serverName, ok := getAndDel(d, "server_name"); ok {
+ packet.ServerName = serverName
+ }
+ packet.Extra = map[string]interface{}(d)
+
+ _, errCh := hook.client.Capture(packet, nil)
+ timeout := hook.Timeout
+ if timeout != 0 {
+ timeoutCh := time.After(timeout)
+ select {
+ case err := <-errCh:
+ return err
+ case <-timeoutCh:
+ return fmt.Errorf("no response from sentry server in %s", timeout)
+ }
+ }
+ return nil
+}
+
+// Levels returns the available logging levels.
+func (hook *SentryHook) Levels() []logrus.Level {
+ return hook.levels
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/sentry/sentry_test.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/sentry/sentry_test.go
new file mode 100644
index 0000000..45f18d1
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/sentry/sentry_test.go
@@ -0,0 +1,97 @@
+package logrus_sentry
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+
+ "github.com/Sirupsen/logrus"
+ "github.com/getsentry/raven-go"
+)
+
+const (
+ message = "error message"
+ server_name = "testserver.internal"
+ logger_name = "test.logger"
+)
+
+func getTestLogger() *logrus.Logger {
+ l := logrus.New()
+ l.Out = ioutil.Discard
+ return l
+}
+
+func WithTestDSN(t *testing.T, tf func(string, <-chan *raven.Packet)) {
+ pch := make(chan *raven.Packet, 1)
+ s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
+ defer req.Body.Close()
+ d := json.NewDecoder(req.Body)
+ p := &raven.Packet{}
+ err := d.Decode(p)
+ if err != nil {
+ t.Fatal(err.Error())
+ }
+
+ pch <- p
+ }))
+ defer s.Close()
+
+ fragments := strings.SplitN(s.URL, "://", 2)
+ dsn := fmt.Sprintf(
+ "%s://public:secret@%s/sentry/project-id",
+ fragments[0],
+ fragments[1],
+ )
+ tf(dsn, pch)
+}
+
+func TestSpecialFields(t *testing.T) {
+ WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) {
+ logger := getTestLogger()
+
+ hook, err := NewSentryHook(dsn, []logrus.Level{
+ logrus.ErrorLevel,
+ })
+
+ if err != nil {
+ t.Fatal(err.Error())
+ }
+ logger.Hooks.Add(hook)
+ logger.WithFields(logrus.Fields{
+ "server_name": server_name,
+ "logger": logger_name,
+ }).Error(message)
+
+ packet := <-pch
+ if packet.Logger != logger_name {
+ t.Errorf("logger should have been %s, was %s", logger_name, packet.Logger)
+ }
+
+ if packet.ServerName != server_name {
+ t.Errorf("server_name should have been %s, was %s", server_name, packet.ServerName)
+ }
+ })
+}
+
+func TestSentryHandler(t *testing.T) {
+ WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) {
+ logger := getTestLogger()
+ hook, err := NewSentryHook(dsn, []logrus.Level{
+ logrus.ErrorLevel,
+ })
+ if err != nil {
+ t.Fatal(err.Error())
+ }
+ logger.Hooks.Add(hook)
+
+ logger.Error(message)
+ packet := <-pch
+ if packet.Message != message {
+ t.Errorf("message should have been %s, was %s", message, packet.Message)
+ }
+ })
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/README.md b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/README.md
new file mode 100644
index 0000000..4dbb8e7
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/README.md
@@ -0,0 +1,20 @@
+# Syslog Hooks for Logrus
+
+## Usage
+
+```go
+import (
+ "log/syslog"
+ "github.com/Sirupsen/logrus"
+ logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
+)
+
+func main() {
+ log := logrus.New()
+ hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
+
+ if err == nil {
+ log.Hooks.Add(hook)
+ }
+}
+```
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go
new file mode 100644
index 0000000..b6fa374
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go
@@ -0,0 +1,59 @@
+package logrus_syslog
+
+import (
+ "fmt"
+ "github.com/Sirupsen/logrus"
+ "log/syslog"
+ "os"
+)
+
+// SyslogHook to send logs via syslog.
+type SyslogHook struct {
+ Writer *syslog.Writer
+ SyslogNetwork string
+ SyslogRaddr string
+}
+
+// Creates a hook to be added to an instance of logger. This is called with
+// `hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_DEBUG, "")`
+// `if err == nil { log.Hooks.Add(hook) }`
+func NewSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*SyslogHook, error) {
+ w, err := syslog.Dial(network, raddr, priority, tag)
+ return &SyslogHook{w, network, raddr}, err
+}
+
+func (hook *SyslogHook) Fire(entry *logrus.Entry) error {
+ line, err := entry.String()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err)
+ return err
+ }
+
+ switch entry.Level {
+ case logrus.PanicLevel:
+ return hook.Writer.Crit(line)
+ case logrus.FatalLevel:
+ return hook.Writer.Crit(line)
+ case logrus.ErrorLevel:
+ return hook.Writer.Err(line)
+ case logrus.WarnLevel:
+ return hook.Writer.Warning(line)
+ case logrus.InfoLevel:
+ return hook.Writer.Info(line)
+ case logrus.DebugLevel:
+ return hook.Writer.Debug(line)
+ default:
+ return nil
+ }
+}
+
+func (hook *SyslogHook) Levels() []logrus.Level {
+ return []logrus.Level{
+ logrus.PanicLevel,
+ logrus.FatalLevel,
+ logrus.ErrorLevel,
+ logrus.WarnLevel,
+ logrus.InfoLevel,
+ logrus.DebugLevel,
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go
new file mode 100644
index 0000000..42762dc
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go
@@ -0,0 +1,26 @@
+package logrus_syslog
+
+import (
+ "github.com/Sirupsen/logrus"
+ "log/syslog"
+ "testing"
+)
+
+func TestLocalhostAddAndPrint(t *testing.T) {
+ log := logrus.New()
+ hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
+
+ if err != nil {
+ t.Errorf("Unable to connect to local syslog.")
+ }
+
+ log.Hooks.Add(hook)
+
+ for _, level := range hook.Levels() {
+ if len(log.Hooks[level]) != 1 {
+ t.Errorf("SyslogHook was not added. The length of log.Hooks[%v]: %v", level, len(log.Hooks[level]))
+ }
+ }
+
+ log.Info("Congratulations!")
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter.go
new file mode 100644
index 0000000..b09227c
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter.go
@@ -0,0 +1,26 @@
+package logrus
+
+import (
+ "encoding/json"
+ "fmt"
+ "time"
+)
+
+type JSONFormatter struct{}
+
+func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
+ data := make(Fields, len(entry.Data)+3)
+ for k, v := range entry.Data {
+ data[k] = v
+ }
+ prefixFieldClashes(data)
+ data["time"] = entry.Time.Format(time.RFC3339)
+ data["msg"] = entry.Message
+ data["level"] = entry.Level.String()
+
+ serialized, err := json.Marshal(data)
+ if err != nil {
+ return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
+ }
+ return append(serialized, '\n'), nil
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/logger.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/logger.go
new file mode 100644
index 0000000..b392e54
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/logger.go
@@ -0,0 +1,161 @@
+package logrus
+
+import (
+ "io"
+ "os"
+ "sync"
+)
+
+type Logger struct {
+ // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
+ // file, or leave it default which is `os.Stdout`. You can also set this to
+ // something more adventorous, such as logging to Kafka.
+ Out io.Writer
+ // Hooks for the logger instance. These allow firing events based on logging
+ // levels and log entries. For example, to send errors to an error tracking
+ // service, log to StatsD or dump the core on fatal errors.
+ Hooks levelHooks
+ // All log entries pass through the formatter before logged to Out. The
+ // included formatters are `TextFormatter` and `JSONFormatter` for which
+ // TextFormatter is the default. In development (when a TTY is attached) it
+ // logs with colors, but to a file it wouldn't. You can easily implement your
+ // own that implements the `Formatter` interface, see the `README` or included
+ // formatters for examples.
+ Formatter Formatter
+ // The logging level the logger should log at. This is typically (and defaults
+ // to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
+ // logged. `logrus.Debug` is useful in
+ Level Level
+ // Used to sync writing to the log.
+ mu sync.Mutex
+}
+
+// Creates a new logger. Configuration should be set by changing `Formatter`,
+// `Out` and `Hooks` directly on the default logger instance. You can also just
+// instantiate your own:
+//
+// var log = &Logger{
+// Out: os.Stderr,
+// Formatter: new(JSONFormatter),
+// Hooks: make(levelHooks),
+// Level: logrus.DebugLevel,
+// }
+//
+// It's recommended to make this a global instance called `log`.
+func New() *Logger {
+ return &Logger{
+ Out: os.Stdout,
+ Formatter: new(TextFormatter),
+ Hooks: make(levelHooks),
+ Level: InfoLevel,
+ }
+}
+
+// Adds a field to the log entry, note that you it doesn't log until you call
+// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
+// Ff you want multiple fields, use `WithFields`.
+func (logger *Logger) WithField(key string, value interface{}) *Entry {
+ return NewEntry(logger).WithField(key, value)
+}
+
+// Adds a struct of fields to the log entry. All it does is call `WithField` for
+// each `Field`.
+func (logger *Logger) WithFields(fields Fields) *Entry {
+ return NewEntry(logger).WithFields(fields)
+}
+
+func (logger *Logger) Debugf(format string, args ...interface{}) {
+ NewEntry(logger).Debugf(format, args...)
+}
+
+func (logger *Logger) Infof(format string, args ...interface{}) {
+ NewEntry(logger).Infof(format, args...)
+}
+
+func (logger *Logger) Printf(format string, args ...interface{}) {
+ NewEntry(logger).Printf(format, args...)
+}
+
+func (logger *Logger) Warnf(format string, args ...interface{}) {
+ NewEntry(logger).Warnf(format, args...)
+}
+
+func (logger *Logger) Warningf(format string, args ...interface{}) {
+ NewEntry(logger).Warnf(format, args...)
+}
+
+func (logger *Logger) Errorf(format string, args ...interface{}) {
+ NewEntry(logger).Errorf(format, args...)
+}
+
+func (logger *Logger) Fatalf(format string, args ...interface{}) {
+ NewEntry(logger).Fatalf(format, args...)
+}
+
+func (logger *Logger) Panicf(format string, args ...interface{}) {
+ NewEntry(logger).Panicf(format, args...)
+}
+
+func (logger *Logger) Debug(args ...interface{}) {
+ NewEntry(logger).Debug(args...)
+}
+
+func (logger *Logger) Info(args ...interface{}) {
+ NewEntry(logger).Info(args...)
+}
+
+func (logger *Logger) Print(args ...interface{}) {
+ NewEntry(logger).Info(args...)
+}
+
+func (logger *Logger) Warn(args ...interface{}) {
+ NewEntry(logger).Warn(args...)
+}
+
+func (logger *Logger) Warning(args ...interface{}) {
+ NewEntry(logger).Warn(args...)
+}
+
+func (logger *Logger) Error(args ...interface{}) {
+ NewEntry(logger).Error(args...)
+}
+
+func (logger *Logger) Fatal(args ...interface{}) {
+ NewEntry(logger).Fatal(args...)
+}
+
+func (logger *Logger) Panic(args ...interface{}) {
+ NewEntry(logger).Panic(args...)
+}
+
+func (logger *Logger) Debugln(args ...interface{}) {
+ NewEntry(logger).Debugln(args...)
+}
+
+func (logger *Logger) Infoln(args ...interface{}) {
+ NewEntry(logger).Infoln(args...)
+}
+
+func (logger *Logger) Println(args ...interface{}) {
+ NewEntry(logger).Println(args...)
+}
+
+func (logger *Logger) Warnln(args ...interface{}) {
+ NewEntry(logger).Warnln(args...)
+}
+
+func (logger *Logger) Warningln(args ...interface{}) {
+ NewEntry(logger).Warnln(args...)
+}
+
+func (logger *Logger) Errorln(args ...interface{}) {
+ NewEntry(logger).Errorln(args...)
+}
+
+func (logger *Logger) Fatalln(args ...interface{}) {
+ NewEntry(logger).Fatalln(args...)
+}
+
+func (logger *Logger) Panicln(args ...interface{}) {
+ NewEntry(logger).Panicln(args...)
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus.go
new file mode 100644
index 0000000..43ee12e
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus.go
@@ -0,0 +1,94 @@
+package logrus
+
+import (
+ "fmt"
+ "log"
+)
+
+// Fields type, used to pass to `WithFields`.
+type Fields map[string]interface{}
+
+// Level type
+type Level uint8
+
+// Convert the Level to a string. E.g. PanicLevel becomes "panic".
+func (level Level) String() string {
+ switch level {
+ case DebugLevel:
+ return "debug"
+ case InfoLevel:
+ return "info"
+ case WarnLevel:
+ return "warning"
+ case ErrorLevel:
+ return "error"
+ case FatalLevel:
+ return "fatal"
+ case PanicLevel:
+ return "panic"
+ }
+
+ return "unknown"
+}
+
+// ParseLevel takes a string level and returns the Logrus log level constant.
+func ParseLevel(lvl string) (Level, error) {
+ switch lvl {
+ case "panic":
+ return PanicLevel, nil
+ case "fatal":
+ return FatalLevel, nil
+ case "error":
+ return ErrorLevel, nil
+ case "warn", "warning":
+ return WarnLevel, nil
+ case "info":
+ return InfoLevel, nil
+ case "debug":
+ return DebugLevel, nil
+ }
+
+ var l Level
+ return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
+}
+
+// These are the different logging levels. You can set the logging level to log
+// on your instance of logger, obtained with `logrus.New()`.
+const (
+ // PanicLevel level, highest level of severity. Logs and then calls panic with the
+ // message passed to Debug, Info, ...
+ PanicLevel Level = iota
+ // FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the
+ // logging level is set to Panic.
+ FatalLevel
+ // ErrorLevel level. Logs. Used for errors that should definitely be noted.
+ // Commonly used for hooks to send errors to an error tracking service.
+ ErrorLevel
+ // WarnLevel level. Non-critical entries that deserve eyes.
+ WarnLevel
+ // InfoLevel level. General operational entries about what's going on inside the
+ // application.
+ InfoLevel
+ // DebugLevel level. Usually only enabled when debugging. Very verbose logging.
+ DebugLevel
+)
+
+// Won't compile if StdLogger can't be realized by a log.Logger
+var _ StdLogger = &log.Logger{}
+
+// StdLogger is what your logrus-enabled library should take, that way
+// it'll accept a stdlib logger and a logrus logger. There's no standard
+// interface, this is the closest we get, unfortunately.
+type StdLogger interface {
+ Print(...interface{})
+ Printf(string, ...interface{})
+ Println(...interface{})
+
+ Fatal(...interface{})
+ Fatalf(string, ...interface{})
+ Fatalln(...interface{})
+
+ Panic(...interface{})
+ Panicf(string, ...interface{})
+ Panicln(...interface{})
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus_test.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus_test.go
new file mode 100644
index 0000000..7f52c6f
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus_test.go
@@ -0,0 +1,283 @@
+package logrus
+
+import (
+ "bytes"
+ "encoding/json"
+ "strconv"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) {
+ var buffer bytes.Buffer
+ var fields Fields
+
+ logger := New()
+ logger.Out = &buffer
+ logger.Formatter = new(JSONFormatter)
+
+ log(logger)
+
+ err := json.Unmarshal(buffer.Bytes(), &fields)
+ assert.Nil(t, err)
+
+ assertions(fields)
+}
+
+func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) {
+ var buffer bytes.Buffer
+
+ logger := New()
+ logger.Out = &buffer
+ logger.Formatter = &TextFormatter{
+ DisableColors: true,
+ }
+
+ log(logger)
+
+ fields := make(map[string]string)
+ for _, kv := range strings.Split(buffer.String(), " ") {
+ if !strings.Contains(kv, "=") {
+ continue
+ }
+ kvArr := strings.Split(kv, "=")
+ key := strings.TrimSpace(kvArr[0])
+ val := kvArr[1]
+ if kvArr[1][0] == '"' {
+ var err error
+ val, err = strconv.Unquote(val)
+ assert.NoError(t, err)
+ }
+ fields[key] = val
+ }
+ assertions(fields)
+}
+
+func TestPrint(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Print("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test")
+ assert.Equal(t, fields["level"], "info")
+ })
+}
+
+func TestInfo(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Info("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test")
+ assert.Equal(t, fields["level"], "info")
+ })
+}
+
+func TestWarn(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Warn("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test")
+ assert.Equal(t, fields["level"], "warning")
+ })
+}
+
+func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Infoln("test", "test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test test")
+ })
+}
+
+func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Infoln("test", 10)
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test 10")
+ })
+}
+
+func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Infoln(10, 10)
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "10 10")
+ })
+}
+
+func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Infoln(10, 10)
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "10 10")
+ })
+}
+
+func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Info("test", 10)
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test10")
+ })
+}
+
+func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Info("test", "test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "testtest")
+ })
+}
+
+func TestWithFieldsShouldAllowAssignments(t *testing.T) {
+ var buffer bytes.Buffer
+ var fields Fields
+
+ logger := New()
+ logger.Out = &buffer
+ logger.Formatter = new(JSONFormatter)
+
+ localLog := logger.WithFields(Fields{
+ "key1": "value1",
+ })
+
+ localLog.WithField("key2", "value2").Info("test")
+ err := json.Unmarshal(buffer.Bytes(), &fields)
+ assert.Nil(t, err)
+
+ assert.Equal(t, "value2", fields["key2"])
+ assert.Equal(t, "value1", fields["key1"])
+
+ buffer = bytes.Buffer{}
+ fields = Fields{}
+ localLog.Info("test")
+ err = json.Unmarshal(buffer.Bytes(), &fields)
+ assert.Nil(t, err)
+
+ _, ok := fields["key2"]
+ assert.Equal(t, false, ok)
+ assert.Equal(t, "value1", fields["key1"])
+}
+
+func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.WithField("msg", "hello").Info("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test")
+ })
+}
+
+func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.WithField("msg", "hello").Info("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test")
+ assert.Equal(t, fields["fields.msg"], "hello")
+ })
+}
+
+func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.WithField("time", "hello").Info("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["fields.time"], "hello")
+ })
+}
+
+func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.WithField("level", 1).Info("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["level"], "info")
+ assert.Equal(t, fields["fields.level"], 1)
+ })
+}
+
+func TestDefaultFieldsAreNotPrefixed(t *testing.T) {
+ LogAndAssertText(t, func(log *Logger) {
+ ll := log.WithField("herp", "derp")
+ ll.Info("hello")
+ ll.Info("bye")
+ }, func(fields map[string]string) {
+ for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} {
+ if _, ok := fields[fieldName]; ok {
+ t.Fatalf("should not have prefixed %q: %v", fieldName, fields)
+ }
+ }
+ })
+}
+
+func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) {
+
+ var buffer bytes.Buffer
+ var fields Fields
+
+ logger := New()
+ logger.Out = &buffer
+ logger.Formatter = new(JSONFormatter)
+
+ llog := logger.WithField("context", "eating raw fish")
+
+ llog.Info("looks delicious")
+
+ err := json.Unmarshal(buffer.Bytes(), &fields)
+ assert.NoError(t, err, "should have decoded first message")
+ assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
+ assert.Equal(t, fields["msg"], "looks delicious")
+ assert.Equal(t, fields["context"], "eating raw fish")
+
+ buffer.Reset()
+
+ llog.Warn("omg it is!")
+
+ err = json.Unmarshal(buffer.Bytes(), &fields)
+ assert.NoError(t, err, "should have decoded second message")
+ assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
+ assert.Equal(t, fields["msg"], "omg it is!")
+ assert.Equal(t, fields["context"], "eating raw fish")
+ assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry")
+
+}
+
+func TestConvertLevelToString(t *testing.T) {
+ assert.Equal(t, "debug", DebugLevel.String())
+ assert.Equal(t, "info", InfoLevel.String())
+ assert.Equal(t, "warning", WarnLevel.String())
+ assert.Equal(t, "error", ErrorLevel.String())
+ assert.Equal(t, "fatal", FatalLevel.String())
+ assert.Equal(t, "panic", PanicLevel.String())
+}
+
+func TestParseLevel(t *testing.T) {
+ l, err := ParseLevel("panic")
+ assert.Nil(t, err)
+ assert.Equal(t, PanicLevel, l)
+
+ l, err = ParseLevel("fatal")
+ assert.Nil(t, err)
+ assert.Equal(t, FatalLevel, l)
+
+ l, err = ParseLevel("error")
+ assert.Nil(t, err)
+ assert.Equal(t, ErrorLevel, l)
+
+ l, err = ParseLevel("warn")
+ assert.Nil(t, err)
+ assert.Equal(t, WarnLevel, l)
+
+ l, err = ParseLevel("warning")
+ assert.Nil(t, err)
+ assert.Equal(t, WarnLevel, l)
+
+ l, err = ParseLevel("info")
+ assert.Nil(t, err)
+ assert.Equal(t, InfoLevel, l)
+
+ l, err = ParseLevel("debug")
+ assert.Nil(t, err)
+ assert.Equal(t, DebugLevel, l)
+
+ l, err = ParseLevel("invalid")
+ assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error())
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_darwin.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_darwin.go
new file mode 100644
index 0000000..8fe02a4
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_darwin.go
@@ -0,0 +1,12 @@
+// Based on ssh/terminal:
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package logrus
+
+import "syscall"
+
+const ioctlReadTermios = syscall.TIOCGETA
+
+type Termios syscall.Termios
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_freebsd.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_freebsd.go
new file mode 100644
index 0000000..0428ee5
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_freebsd.go
@@ -0,0 +1,20 @@
+/*
+ Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin.
+*/
+package logrus
+
+import (
+ "syscall"
+)
+
+const ioctlReadTermios = syscall.TIOCGETA
+
+type Termios struct {
+ Iflag uint32
+ Oflag uint32
+ Cflag uint32
+ Lflag uint32
+ Cc [20]uint8
+ Ispeed uint32
+ Ospeed uint32
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_linux.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_linux.go
new file mode 100644
index 0000000..a2c0b40
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_linux.go
@@ -0,0 +1,12 @@
+// Based on ssh/terminal:
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package logrus
+
+import "syscall"
+
+const ioctlReadTermios = syscall.TCGETS
+
+type Termios syscall.Termios
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_notwindows.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_notwindows.go
new file mode 100644
index 0000000..80edd32
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_notwindows.go
@@ -0,0 +1,21 @@
+// Based on ssh/terminal:
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux,!appengine darwin freebsd openbsd
+
+package logrus
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func IsTerminal() bool {
+ fd := syscall.Stdout
+ var termios Termios
+ _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
+ return err == 0
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_openbsd.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_openbsd.go
new file mode 100644
index 0000000..d238bfa
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_openbsd.go
@@ -0,0 +1,8 @@
+
+package logrus
+
+import "syscall"
+
+const ioctlReadTermios = syscall.TIOCGETA
+
+type Termios syscall.Termios
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_windows.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_windows.go
new file mode 100644
index 0000000..2e09f6f
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_windows.go
@@ -0,0 +1,27 @@
+// Based on ssh/terminal:
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build windows
+
+package logrus
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+var kernel32 = syscall.NewLazyDLL("kernel32.dll")
+
+var (
+ procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
+)
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func IsTerminal() bool {
+ fd := syscall.Stdout
+ var st uint32
+ r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
+ return r != 0 && e == 0
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter.go
new file mode 100644
index 0000000..78e7889
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter.go
@@ -0,0 +1,124 @@
+package logrus
+
+import (
+ "bytes"
+ "fmt"
+ "regexp"
+ "sort"
+ "strings"
+ "time"
+)
+
+const (
+ nocolor = 0
+ red = 31
+ green = 32
+ yellow = 33
+ blue = 34
+)
+
+var (
+ baseTimestamp time.Time
+ isTerminal bool
+ noQuoteNeeded *regexp.Regexp
+)
+
+func init() {
+ baseTimestamp = time.Now()
+ isTerminal = IsTerminal()
+}
+
+func miniTS() int {
+ return int(time.Since(baseTimestamp) / time.Second)
+}
+
+type TextFormatter struct {
+ // Set to true to bypass checking for a TTY before outputting colors.
+ ForceColors bool
+ DisableColors bool
+ // Set to true to disable timestamp logging (useful when the output
+ // is redirected to a logging system already adding a timestamp)
+ DisableTimestamp bool
+}
+
+func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
+
+ var keys []string
+ for k := range entry.Data {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+
+ b := &bytes.Buffer{}
+
+ prefixFieldClashes(entry.Data)
+
+ isColored := (f.ForceColors || isTerminal) && !f.DisableColors
+
+ if isColored {
+ printColored(b, entry, keys)
+ } else {
+ if !f.DisableTimestamp {
+ f.appendKeyValue(b, "time", entry.Time.Format(time.RFC3339))
+ }
+ f.appendKeyValue(b, "level", entry.Level.String())
+ f.appendKeyValue(b, "msg", entry.Message)
+ for _, key := range keys {
+ f.appendKeyValue(b, key, entry.Data[key])
+ }
+ }
+
+ b.WriteByte('\n')
+ return b.Bytes(), nil
+}
+
+func printColored(b *bytes.Buffer, entry *Entry, keys []string) {
+ var levelColor int
+ switch entry.Level {
+ case WarnLevel:
+ levelColor = yellow
+ case ErrorLevel, FatalLevel, PanicLevel:
+ levelColor = red
+ default:
+ levelColor = blue
+ }
+
+ levelText := strings.ToUpper(entry.Level.String())[0:4]
+
+ fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
+ for _, k := range keys {
+ v := entry.Data[k]
+ fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, v)
+ }
+}
+
+func needsQuoting(text string) bool {
+ for _, ch := range text {
+ if !((ch >= 'a' && ch <= 'z') ||
+ (ch >= 'A' && ch <= 'Z') ||
+ (ch >= '0' && ch < '9') ||
+ ch == '-' || ch == '.') {
+ return false
+ }
+ }
+ return true
+}
+
+func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key, value interface{}) {
+ switch value.(type) {
+ case string:
+ if needsQuoting(value.(string)) {
+ fmt.Fprintf(b, "%v=%s ", key, value)
+ } else {
+ fmt.Fprintf(b, "%v=%q ", key, value)
+ }
+ case error:
+ if needsQuoting(value.(error).Error()) {
+ fmt.Fprintf(b, "%v=%s ", key, value)
+ } else {
+ fmt.Fprintf(b, "%v=%q ", key, value)
+ }
+ default:
+ fmt.Fprintf(b, "%v=%v ", key, value)
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter_test.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter_test.go
new file mode 100644
index 0000000..f604f1b
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter_test.go
@@ -0,0 +1,33 @@
+package logrus
+
+import (
+ "bytes"
+ "errors"
+
+ "testing"
+)
+
+func TestQuoting(t *testing.T) {
+ tf := &TextFormatter{DisableColors: true}
+
+ checkQuoting := func(q bool, value interface{}) {
+ b, _ := tf.Format(WithField("test", value))
+ idx := bytes.Index(b, ([]byte)("test="))
+ cont := bytes.Contains(b[idx+5:], []byte{'"'})
+ if cont != q {
+ if q {
+ t.Errorf("quoting expected for: %#v", value)
+ } else {
+ t.Errorf("quoting not expected for: %#v", value)
+ }
+ }
+ }
+
+ checkQuoting(false, "abcd")
+ checkQuoting(false, "v1.0")
+ checkQuoting(true, "/foobar")
+ checkQuoting(true, "x y")
+ checkQuoting(true, "x,y")
+ checkQuoting(false, errors.New("invalid"))
+ checkQuoting(true, errors.New("invalid argument"))
+}
diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/writer.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/writer.go
new file mode 100644
index 0000000..90d3e01
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/writer.go
@@ -0,0 +1,31 @@
+package logrus
+
+import (
+ "bufio"
+ "io"
+ "runtime"
+)
+
+func (logger *Logger) Writer() (*io.PipeWriter) {
+ reader, writer := io.Pipe()
+
+ go logger.writerScanner(reader)
+ runtime.SetFinalizer(writer, writerFinalizer)
+
+ return writer
+}
+
+func (logger *Logger) writerScanner(reader *io.PipeReader) {
+ scanner := bufio.NewScanner(reader)
+ for scanner.Scan() {
+ logger.Print(scanner.Text())
+ }
+ if err := scanner.Err(); err != nil {
+ logger.Errorf("Error while reading from Writer: %s", err)
+ }
+ reader.Close()
+}
+
+func writerFinalizer(writer *io.PipeWriter) {
+ writer.Close()
+}
diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/MAINTAINERS b/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/MAINTAINERS
new file mode 100644
index 0000000..e48a0c7
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/MAINTAINERS
@@ -0,0 +1 @@
+Michael Crosby (@crosbymichael)
diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/README.md b/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/README.md
new file mode 100644
index 0000000..45592ce
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/README.md
@@ -0,0 +1,5 @@
+## reexec
+
+The `reexec` package facilitates the busybox style reexec of the docker binary that we require because
+of the forking limitations of using Go. Handlers can be registered with a name and the argv 0 of
+the exec of the binary will be used to find and execute custom init paths.
diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_linux.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_linux.go
new file mode 100644
index 0000000..8dc3f3a
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_linux.go
@@ -0,0 +1,18 @@
+// +build linux
+
+package reexec
+
+import (
+ "os/exec"
+ "syscall"
+)
+
+func Command(args ...string) *exec.Cmd {
+ return &exec.Cmd{
+ Path: Self(),
+ Args: args,
+ SysProcAttr: &syscall.SysProcAttr{
+ Pdeathsig: syscall.SIGTERM,
+ },
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_unsupported.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_unsupported.go
new file mode 100644
index 0000000..a579318
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_unsupported.go
@@ -0,0 +1,11 @@
+// +build !linux
+
+package reexec
+
+import (
+ "os/exec"
+)
+
+func Command(args ...string) *exec.Cmd {
+ return nil
+}
diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/reexec.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/reexec.go
new file mode 100644
index 0000000..774e71c
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/reexec.go
@@ -0,0 +1,42 @@
+package reexec
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+)
+
+var registeredInitializers = make(map[string]func())
+
+// Register adds an initialization func under the specified name
+func Register(name string, initializer func()) {
+ if _, exists := registeredInitializers[name]; exists {
+ panic(fmt.Sprintf("reexec func already registred under name %q", name))
+ }
+
+ registeredInitializers[name] = initializer
+}
+
+// Init is called as the first part of the exec process and returns true if an
+// initialization function was called.
+func Init() bool {
+ initializer, exists := registeredInitializers[os.Args[0]]
+ if exists {
+ initializer()
+
+ return true
+ }
+ return false
+}
+
+// Self returns the path to the current processes binary
+func Self() string {
+ name := os.Args[0]
+ if filepath.Base(name) == name {
+ if lp, err := exec.LookPath(name); err == nil {
+ name = lp
+ }
+ }
+ return name
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/.travis.yml b/Godeps/_workspace/src/github.com/vishvananda/netlink/.travis.yml
new file mode 100644
index 0000000..1970069
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/.travis.yml
@@ -0,0 +1,3 @@
+language: go
+install:
+ - go get github.com/vishvananda/netns
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/LICENSE b/Godeps/_workspace/src/github.com/vishvananda/netlink/LICENSE
new file mode 100644
index 0000000..9f64db8
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/LICENSE
@@ -0,0 +1,192 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ Copyright 2014 Vishvananda Ishaya.
+ Copyright 2014 Docker, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/Makefile b/Godeps/_workspace/src/github.com/vishvananda/netlink/Makefile
new file mode 100644
index 0000000..b325018
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/Makefile
@@ -0,0 +1,29 @@
+DIRS := \
+ . \
+ nl
+
+DEPS = \
+ github.com/vishvananda/netns
+
+uniq = $(if $1,$(firstword $1) $(call uniq,$(filter-out $(firstword $1),$1)))
+testdirs = $(call uniq,$(foreach d,$(1),$(dir $(wildcard $(d)/*_test.go))))
+goroot = $(addprefix ../../../,$(1))
+unroot = $(subst ../../../,,$(1))
+fmt = $(addprefix fmt-,$(1))
+
+all: fmt
+
+$(call goroot,$(DEPS)):
+ go get $(call unroot,$@)
+
+.PHONY: $(call testdirs,$(DIRS))
+$(call testdirs,$(DIRS)):
+ sudo -E go test -v github.com/vishvananda/netlink/$@
+
+$(call fmt,$(call testdirs,$(DIRS))):
+ ! gofmt -l $(subst fmt-,,$@)/*.go | grep ''
+
+.PHONY: fmt
+fmt: $(call fmt,$(call testdirs,$(DIRS)))
+
+test: fmt $(call goroot,$(DEPS)) $(call testdirs,$(DIRS))
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/README.md b/Godeps/_workspace/src/github.com/vishvananda/netlink/README.md
new file mode 100644
index 0000000..555f886
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/README.md
@@ -0,0 +1,83 @@
+# netlink - netlink library for go #
+
+[](https://travis-ci.org/vishvananda/netlink) [](https://godoc.org/github.com/vishvananda/netlink)
+
+The netlink package provides a simple netlink library for go. Netlink
+is the interface a user-space program in linux uses to communicate with
+the kernel. It can be used to add and remove interfaces, set ip addresses
+and routes, and configure ipsec. Netlink communication requires elevated
+privileges, so in most cases this code needs to be run as root. Since
+low-level netlink messages are inscrutable at best, the library attempts
+to provide an api that is loosely modeled on the CLI provied by iproute2.
+Actions like `ip link add` will be accomplished via a similarly named
+function like AddLink(). This library began its life as a fork of the
+netlink functionality in
+[docker/libcontainer](https://github.com/docker/libcontainer) but was
+heavily rewritten to improve testability, performance, and to add new
+functionality like ipsec xfrm handling.
+
+## Local Build and Test ##
+
+You can use go get command:
+
+ go get github.com/vishvananda/netlink
+
+Testing dependencies:
+
+ go get github.com/vishvananda/netns
+
+Testing (requires root):
+
+ sudo -E go test github.com/vishvananda/netlink
+
+## Examples ##
+
+Add a new bridge and add eth1 into it:
+
+```go
+package main
+
+import (
+ "net"
+ "github.com/vishvananda/netlink"
+)
+
+func main() {
+ mybridge := &netlink.Bridge{netlink.LinkAttrs{Name: "foo"}}
+ _ := netlink.LinkAdd(mybridge)
+ eth1, _ := netlink.LinkByName("eth1")
+ netlink.LinkSetMaster(eth1, mybridge)
+}
+
+```
+
+Add a new ip address to loopback:
+
+```go
+package main
+
+import (
+ "net"
+ "github.com/vishvananda/netlink"
+)
+
+func main() {
+ lo, _ := netlink.LinkByName("lo")
+ addr, _ := netlink.ParseAddr("169.254.169.254/32")
+ netlink.AddrAdd(lo, addr)
+}
+
+```
+
+## Future Work ##
+
+Many pieces of netlink are not yet fully supported in the high-level
+interface. Aspects of virtually all of the high-level objects don't exist.
+Many of the underlying primitives are there, so its a matter of putting
+the right fields into the high-level objects and making sure that they
+are serialized and deserialized correctly in the Add and List methods.
+
+There are also a few pieces of low level netlink functionality that still
+need to be implemented. Routing rules are not in place and some of the
+more advanced link types. Hopefully there is decent structure and testing
+in place to make these fairly straightforward to add.
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/addr.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/addr.go
new file mode 100644
index 0000000..5c12f4e
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/addr.go
@@ -0,0 +1,43 @@
+package netlink
+
+import (
+ "fmt"
+ "net"
+ "strings"
+)
+
+// Addr represents an IP address from netlink. Netlink ip addresses
+// include a mask, so it stores the address as a net.IPNet.
+type Addr struct {
+ *net.IPNet
+ Label string
+}
+
+// String returns $ip/$netmask $label
+func (addr Addr) String() string {
+ return fmt.Sprintf("%s %s", addr.IPNet, addr.Label)
+}
+
+// ParseAddr parses the string representation of an address in the
+// form $ip/$netmask $label. The label portion is optional
+func ParseAddr(s string) (*Addr, error) {
+ label := ""
+ parts := strings.Split(s, " ")
+ if len(parts) > 1 {
+ s = parts[0]
+ label = parts[1]
+ }
+ m, err := ParseIPNet(s)
+ if err != nil {
+ return nil, err
+ }
+ return &Addr{IPNet: m, Label: label}, nil
+}
+
+// Equal returns true if both Addrs have the same net.IPNet value.
+func (a Addr) Equal(x Addr) bool {
+ sizea, _ := a.Mask.Size()
+ sizeb, _ := x.Mask.Size()
+ // ignore label for comparison
+ return a.IP.Equal(x.IP) && sizea == sizeb
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/addr_linux.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/addr_linux.go
new file mode 100644
index 0000000..dd26f4a
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/addr_linux.go
@@ -0,0 +1,114 @@
+package netlink
+
+import (
+ "fmt"
+ "net"
+ "strings"
+ "syscall"
+
+ "github.com/vishvananda/netlink/nl"
+)
+
+// AddrAdd will add an IP address to a link device.
+// Equivalent to: `ip addr add $addr dev $link`
+func AddrAdd(link Link, addr *Addr) error {
+
+ req := nl.NewNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+ return addrHandle(link, addr, req)
+}
+
+// AddrDel will delete an IP address from a link device.
+// Equivalent to: `ip addr del $addr dev $link`
+func AddrDel(link Link, addr *Addr) error {
+ req := nl.NewNetlinkRequest(syscall.RTM_DELADDR, syscall.NLM_F_ACK)
+ return addrHandle(link, addr, req)
+}
+
+func addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error {
+ base := link.Attrs()
+ if addr.Label != "" && !strings.HasPrefix(addr.Label, base.Name) {
+ return fmt.Errorf("label must begin with interface name")
+ }
+ ensureIndex(base)
+
+ family := nl.GetIPFamily(addr.IP)
+
+ msg := nl.NewIfAddrmsg(family)
+ msg.Index = uint32(base.Index)
+ prefixlen, _ := addr.Mask.Size()
+ msg.Prefixlen = uint8(prefixlen)
+ req.AddData(msg)
+
+ var addrData []byte
+ if family == FAMILY_V4 {
+ addrData = addr.IP.To4()
+ } else {
+ addrData = addr.IP.To16()
+ }
+
+ localData := nl.NewRtAttr(syscall.IFA_LOCAL, addrData)
+ req.AddData(localData)
+
+ addressData := nl.NewRtAttr(syscall.IFA_ADDRESS, addrData)
+ req.AddData(addressData)
+
+ if addr.Label != "" {
+ labelData := nl.NewRtAttr(syscall.IFA_LABEL, nl.ZeroTerminated(addr.Label))
+ req.AddData(labelData)
+ }
+
+ _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+ return err
+}
+
+// AddrList gets a list of IP addresses in the system.
+// Equivalent to: `ip addr show`.
+// The list can be filtered by link and ip family.
+func AddrList(link Link, family int) ([]Addr, error) {
+ req := nl.NewNetlinkRequest(syscall.RTM_GETADDR, syscall.NLM_F_DUMP)
+ msg := nl.NewIfInfomsg(family)
+ req.AddData(msg)
+
+ msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWADDR)
+ if err != nil {
+ return nil, err
+ }
+
+ index := 0
+ if link != nil {
+ base := link.Attrs()
+ ensureIndex(base)
+ index = base.Index
+ }
+
+ res := make([]Addr, 0)
+ for _, m := range msgs {
+ msg := nl.DeserializeIfAddrmsg(m)
+
+ if link != nil && msg.Index != uint32(index) {
+ // Ignore messages from other interfaces
+ continue
+ }
+
+ attrs, err := nl.ParseRouteAttr(m[msg.Len():])
+ if err != nil {
+ return nil, err
+ }
+
+ var addr Addr
+ for _, attr := range attrs {
+ switch attr.Attr.Type {
+ case syscall.IFA_ADDRESS:
+ addr.IPNet = &net.IPNet{
+ IP: attr.Value,
+ Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
+ }
+ case syscall.IFA_LABEL:
+ addr.Label = string(attr.Value[:len(attr.Value)-1])
+ }
+ }
+ res = append(res, addr)
+ }
+
+ return res, nil
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/addr_test.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/addr_test.go
new file mode 100644
index 0000000..45e22c0
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/addr_test.go
@@ -0,0 +1,45 @@
+package netlink
+
+import (
+ "testing"
+)
+
+func TestAddrAddDel(t *testing.T) {
+ tearDown := setUpNetlinkTest(t)
+ defer tearDown()
+
+ link, err := LinkByName("lo")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ addr, err := ParseAddr("127.1.1.1/24 local")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if err = AddrAdd(link, addr); err != nil {
+ t.Fatal(err)
+ }
+
+ addrs, err := AddrList(link, FAMILY_ALL)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(addrs) != 1 || !addr.Equal(addrs[0]) || addrs[0].Label != addr.Label {
+ t.Fatal("Address not added properly")
+ }
+
+ if err = AddrDel(link, addr); err != nil {
+ t.Fatal(err)
+ }
+ addrs, err = AddrList(link, FAMILY_ALL)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(addrs) != 0 {
+ t.Fatal("Address not removed properly")
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/link.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/link.go
new file mode 100644
index 0000000..276c2f8
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/link.go
@@ -0,0 +1,175 @@
+package netlink
+
+import "net"
+
+// Link represents a link device from netlink. Shared link attributes
+// like name may be retrieved using the Attrs() method. Unique data
+// can be retrieved by casting the object to the proper type.
+type Link interface {
+ Attrs() *LinkAttrs
+ Type() string
+}
+
+// LinkAttrs represents data shared by most link types
+type LinkAttrs struct {
+ Index int
+ MTU int
+ TxQLen uint32 // Transmit Queue Length
+ Name string
+ HardwareAddr net.HardwareAddr
+ Flags net.Flags
+ ParentIndex int // index of the parent link device
+ MasterIndex int // must be the index of a bridge
+}
+
+// Device links cannot be created via netlink. These links
+// are links created by udev like 'lo' and 'etho0'
+type Device struct {
+ LinkAttrs
+}
+
+func (device *Device) Attrs() *LinkAttrs {
+ return &device.LinkAttrs
+}
+
+func (device *Device) Type() string {
+ return "device"
+}
+
+// Dummy links are dummy ethernet devices
+type Dummy struct {
+ LinkAttrs
+}
+
+func (dummy *Dummy) Attrs() *LinkAttrs {
+ return &dummy.LinkAttrs
+}
+
+func (dummy *Dummy) Type() string {
+ return "dummy"
+}
+
+// Bridge links are simple linux bridges
+type Bridge struct {
+ LinkAttrs
+}
+
+func (bridge *Bridge) Attrs() *LinkAttrs {
+ return &bridge.LinkAttrs
+}
+
+func (bridge *Bridge) Type() string {
+ return "bridge"
+}
+
+// Vlan links have ParentIndex set in their Attrs()
+type Vlan struct {
+ LinkAttrs
+ VlanId int
+}
+
+func (vlan *Vlan) Attrs() *LinkAttrs {
+ return &vlan.LinkAttrs
+}
+
+func (vlan *Vlan) Type() string {
+ return "vlan"
+}
+
+// Macvlan links have ParentIndex set in their Attrs()
+type Macvlan struct {
+ LinkAttrs
+}
+
+func (macvlan *Macvlan) Attrs() *LinkAttrs {
+ return &macvlan.LinkAttrs
+}
+
+func (macvlan *Macvlan) Type() string {
+ return "macvlan"
+}
+
+// Veth devices must specify PeerName on create
+type Veth struct {
+ LinkAttrs
+ PeerName string // veth on create only
+}
+
+func (veth *Veth) Attrs() *LinkAttrs {
+ return &veth.LinkAttrs
+}
+
+func (veth *Veth) Type() string {
+ return "veth"
+}
+
+// Generic links represent types that are not currently understood
+// by this netlink library.
+type Generic struct {
+ LinkAttrs
+ LinkType string
+}
+
+func (generic *Generic) Attrs() *LinkAttrs {
+ return &generic.LinkAttrs
+}
+
+func (generic *Generic) Type() string {
+ return generic.LinkType
+}
+
+type Vxlan struct {
+ LinkAttrs
+ VxlanId int
+ VtepDevIndex int
+ SrcAddr net.IP
+ Group net.IP
+ TTL int
+ TOS int
+ Learning bool
+ Proxy bool
+ RSC bool
+ L2miss bool
+ L3miss bool
+ NoAge bool
+ Age int
+ Limit int
+ Port int
+ PortLow int
+ PortHigh int
+}
+
+func (vxlan *Vxlan) Attrs() *LinkAttrs {
+ return &vxlan.LinkAttrs
+}
+
+func (vxlan *Vxlan) Type() string {
+ return "vxlan"
+}
+
+type IPVlanMode uint16
+
+const (
+ IPVLAN_MODE_L2 IPVlanMode = iota
+ IPVLAN_MODE_L3
+ IPVLAN_MODE_MAX
+)
+
+type IPVlan struct {
+ LinkAttrs
+ Mode IPVlanMode
+}
+
+func (ipvlan *IPVlan) Attrs() *LinkAttrs {
+ return &ipvlan.LinkAttrs
+}
+
+func (ipvlan *IPVlan) Type() string {
+ return "ipvlan"
+}
+
+// iproute2 supported devices;
+// vlan | veth | vcan | dummy | ifb | macvlan | macvtap |
+// bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan |
+// gre | gretap | ip6gre | ip6gretap | vti | nlmon |
+// bond_slave | ipvlan
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/link_linux.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/link_linux.go
new file mode 100644
index 0000000..aedea16
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/link_linux.go
@@ -0,0 +1,696 @@
+package netlink
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "net"
+ "syscall"
+
+ "github.com/vishvananda/netlink/nl"
+)
+
+var native = nl.NativeEndian()
+var lookupByDump = false
+
+func ensureIndex(link *LinkAttrs) {
+ if link != nil && link.Index == 0 {
+ newlink, _ := LinkByName(link.Name)
+ if newlink != nil {
+ link.Index = newlink.Attrs().Index
+ }
+ }
+}
+
+// LinkSetUp enables the link device.
+// Equivalent to: `ip link set $link up`
+func LinkSetUp(link Link) error {
+ base := link.Attrs()
+ ensureIndex(base)
+ req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK)
+
+ msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+ msg.Change = syscall.IFF_UP
+ msg.Flags = syscall.IFF_UP
+ msg.Index = int32(base.Index)
+ req.AddData(msg)
+
+ _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+ return err
+}
+
+// LinkSetUp disables link device.
+// Equivalent to: `ip link set $link down`
+func LinkSetDown(link Link) error {
+ base := link.Attrs()
+ ensureIndex(base)
+ req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK)
+
+ msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+ msg.Change = syscall.IFF_UP
+ msg.Flags = 0 & ^syscall.IFF_UP
+ msg.Index = int32(base.Index)
+ req.AddData(msg)
+
+ _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+ return err
+}
+
+// LinkSetMTU sets the mtu of the link device.
+// Equivalent to: `ip link set $link mtu $mtu`
+func LinkSetMTU(link Link, mtu int) error {
+ base := link.Attrs()
+ ensureIndex(base)
+ req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
+
+ msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+ msg.Type = syscall.RTM_SETLINK
+ msg.Flags = syscall.NLM_F_REQUEST
+ msg.Index = int32(base.Index)
+ msg.Change = nl.DEFAULT_CHANGE
+ req.AddData(msg)
+
+ b := make([]byte, 4)
+ native.PutUint32(b, uint32(mtu))
+
+ data := nl.NewRtAttr(syscall.IFLA_MTU, b)
+ req.AddData(data)
+
+ _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+ return err
+}
+
+// LinkSetName sets the name of the link device.
+// Equivalent to: `ip link set $link name $name`
+func LinkSetName(link Link, name string) error {
+ base := link.Attrs()
+ ensureIndex(base)
+ req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
+
+ msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+ msg.Type = syscall.RTM_SETLINK
+ msg.Flags = syscall.NLM_F_REQUEST
+ msg.Index = int32(base.Index)
+ msg.Change = nl.DEFAULT_CHANGE
+ req.AddData(msg)
+
+ data := nl.NewRtAttr(syscall.IFLA_IFNAME, []byte(name))
+ req.AddData(data)
+
+ _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+ return err
+}
+
+// LinkSetHardwareAddr sets the hardware address of the link device.
+// Equivalent to: `ip link set $link address $hwaddr`
+func LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error {
+ base := link.Attrs()
+ ensureIndex(base)
+ req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
+
+ msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+ msg.Type = syscall.RTM_SETLINK
+ msg.Flags = syscall.NLM_F_REQUEST
+ msg.Index = int32(base.Index)
+ msg.Change = nl.DEFAULT_CHANGE
+ req.AddData(msg)
+
+ data := nl.NewRtAttr(syscall.IFLA_ADDRESS, []byte(hwaddr))
+ 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 {
+ index := 0
+ if master != nil {
+ masterBase := master.Attrs()
+ ensureIndex(masterBase)
+ index = masterBase.Index
+ }
+ return LinkSetMasterByIndex(link, index)
+}
+
+// LinkSetMasterByIndex sets the master of the link device.
+// Equivalent to: `ip link set $link master $master`
+func LinkSetMasterByIndex(link Link, masterIndex int) error {
+ base := link.Attrs()
+ ensureIndex(base)
+ req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
+
+ msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+ msg.Type = syscall.RTM_SETLINK
+ msg.Flags = syscall.NLM_F_REQUEST
+ msg.Index = int32(base.Index)
+ msg.Change = nl.DEFAULT_CHANGE
+ req.AddData(msg)
+
+ b := make([]byte, 4)
+ native.PutUint32(b, uint32(masterIndex))
+
+ data := nl.NewRtAttr(syscall.IFLA_MASTER, b)
+ req.AddData(data)
+
+ _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+ return err
+}
+
+// LinkSetNsPid puts the device into a new network namespace. The
+// pid must be a pid of a running process.
+// Equivalent to: `ip link set $link netns $pid`
+func LinkSetNsPid(link Link, nspid int) error {
+ base := link.Attrs()
+ ensureIndex(base)
+ req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
+
+ msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+ msg.Type = syscall.RTM_SETLINK
+ msg.Flags = syscall.NLM_F_REQUEST
+ msg.Index = int32(base.Index)
+ msg.Change = nl.DEFAULT_CHANGE
+ req.AddData(msg)
+
+ b := make([]byte, 4)
+ native.PutUint32(b, uint32(nspid))
+
+ data := nl.NewRtAttr(syscall.IFLA_NET_NS_PID, b)
+ req.AddData(data)
+
+ _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+ return err
+}
+
+// LinkSetNsPid puts the device into a new network namespace. The
+// fd must be an open file descriptor to a network namespace.
+// Similar to: `ip link set $link netns $ns`
+func LinkSetNsFd(link Link, fd int) error {
+ base := link.Attrs()
+ ensureIndex(base)
+ req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
+
+ msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+ msg.Type = syscall.RTM_SETLINK
+ msg.Flags = syscall.NLM_F_REQUEST
+ msg.Index = int32(base.Index)
+ msg.Change = nl.DEFAULT_CHANGE
+ req.AddData(msg)
+
+ b := make([]byte, 4)
+ native.PutUint32(b, uint32(fd))
+
+ data := nl.NewRtAttr(nl.IFLA_NET_NS_FD, b)
+ req.AddData(data)
+
+ _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+ return err
+}
+
+func boolAttr(val bool) []byte {
+ var v uint8
+ if val {
+ v = 1
+ }
+ return nl.Uint8Attr(v)
+}
+
+type vxlanPortRange struct {
+ Lo, Hi uint16
+}
+
+func addVxlanAttrs(vxlan *Vxlan, linkInfo *nl.RtAttr) {
+ data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
+ 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)))
+ }
+ if vxlan.SrcAddr != nil {
+ ip := vxlan.SrcAddr.To4()
+ if ip != nil {
+ nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LOCAL, []byte(ip))
+ } else {
+ ip = vxlan.SrcAddr.To16()
+ if ip != nil {
+ nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LOCAL6, []byte(ip))
+ }
+ }
+ }
+ if vxlan.Group != nil {
+ group := vxlan.Group.To4()
+ if group != nil {
+ nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GROUP, []byte(group))
+ } else {
+ group = vxlan.Group.To16()
+ if group != nil {
+ nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GROUP6, []byte(group))
+ }
+ }
+ }
+
+ nl.NewRtAttrChild(data, nl.IFLA_VXLAN_TTL, nl.Uint8Attr(uint8(vxlan.TTL)))
+ nl.NewRtAttrChild(data, nl.IFLA_VXLAN_TOS, nl.Uint8Attr(uint8(vxlan.TOS)))
+ nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LEARNING, boolAttr(vxlan.Learning))
+ nl.NewRtAttrChild(data, nl.IFLA_VXLAN_PROXY, boolAttr(vxlan.Proxy))
+ nl.NewRtAttrChild(data, nl.IFLA_VXLAN_RSC, boolAttr(vxlan.RSC))
+ nl.NewRtAttrChild(data, nl.IFLA_VXLAN_L2MISS, boolAttr(vxlan.L2miss))
+ nl.NewRtAttrChild(data, nl.IFLA_VXLAN_L3MISS, boolAttr(vxlan.L3miss))
+
+ if vxlan.NoAge {
+ nl.NewRtAttrChild(data, nl.IFLA_VXLAN_AGEING, nl.Uint32Attr(0))
+ } else if vxlan.Age > 0 {
+ nl.NewRtAttrChild(data, nl.IFLA_VXLAN_AGEING, nl.Uint32Attr(uint32(vxlan.Age)))
+ }
+ if vxlan.Limit > 0 {
+ nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LIMIT, nl.Uint32Attr(uint32(vxlan.Limit)))
+ }
+ if vxlan.Port > 0 {
+ nl.NewRtAttrChild(data, nl.IFLA_VXLAN_PORT, nl.Uint16Attr(uint16(vxlan.Port)))
+ }
+ if vxlan.PortLow > 0 || vxlan.PortHigh > 0 {
+ pr := vxlanPortRange{uint16(vxlan.PortLow), uint16(vxlan.PortHigh)}
+
+ buf := new(bytes.Buffer)
+ binary.Write(buf, binary.BigEndian, &pr)
+
+ nl.NewRtAttrChild(data, nl.IFLA_VXLAN_PORT_RANGE, buf.Bytes())
+ }
+}
+
+// LinkAdd adds a new link device. The type and features of the device
+// are taken fromt the parameters in the link object.
+// Equivalent to: `ip link add $link`
+func LinkAdd(link Link) error {
+ // TODO: set mtu and hardware address
+ // TODO: support extra data for macvlan
+ base := link.Attrs()
+
+ if base.Name == "" {
+ return fmt.Errorf("LinkAttrs.Name cannot be empty!")
+ }
+
+ req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+
+ msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+ req.AddData(msg)
+
+ if base.ParentIndex != 0 {
+ b := make([]byte, 4)
+ native.PutUint32(b, uint32(base.ParentIndex))
+ data := nl.NewRtAttr(syscall.IFLA_LINK, b)
+ req.AddData(data)
+ } else if link.Type() == "ipvlan" {
+ return fmt.Errorf("Can't create ipvlan link without ParentIndex")
+ }
+
+ nameData := nl.NewRtAttr(syscall.IFLA_IFNAME, nl.ZeroTerminated(base.Name))
+ req.AddData(nameData)
+
+ if base.MTU > 0 {
+ mtu := nl.NewRtAttr(syscall.IFLA_MTU, nl.Uint32Attr(uint32(base.MTU)))
+ req.AddData(mtu)
+ }
+
+ linkInfo := nl.NewRtAttr(syscall.IFLA_LINKINFO, nil)
+ nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_KIND, nl.NonZeroTerminated(link.Type()))
+
+ nl.NewRtAttrChild(linkInfo, syscall.IFLA_TXQLEN, nl.Uint32Attr(base.TxQLen))
+
+ if vlan, ok := link.(*Vlan); ok {
+ b := make([]byte, 2)
+ native.PutUint16(b, uint16(vlan.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 {
+ 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_TXQLEN, nl.Uint32Attr(base.TxQLen))
+ if base.MTU > 0 {
+ nl.NewRtAttrChild(peer, syscall.IFLA_MTU, nl.Uint32Attr(uint32(base.MTU)))
+ }
+ } else if vxlan, ok := link.(*Vxlan); ok {
+ addVxlanAttrs(vxlan, linkInfo)
+ } else if ipv, ok := link.(*IPVlan); ok {
+ data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
+ nl.NewRtAttrChild(data, nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(ipv.Mode)))
+ }
+
+ req.AddData(linkInfo)
+
+ _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+ if err != nil {
+ return err
+ }
+
+ ensureIndex(base)
+
+ // can't set master during create, so set it afterwards
+ if base.MasterIndex != 0 {
+ // TODO: verify MasterIndex is actually a bridge?
+ return LinkSetMasterByIndex(link, base.MasterIndex)
+ }
+ return nil
+}
+
+// LinkDel deletes link device. Either Index or Name must be set in
+// the link object for it to be deleted. The other values are ignored.
+// Equivalent to: `ip link del $link`
+func LinkDel(link Link) error {
+ base := link.Attrs()
+
+ ensureIndex(base)
+
+ req := nl.NewNetlinkRequest(syscall.RTM_DELLINK, syscall.NLM_F_ACK)
+
+ msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+ msg.Index = int32(base.Index)
+ req.AddData(msg)
+
+ _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+ return err
+}
+
+func linkByNameDump(name string) (Link, error) {
+ links, err := LinkList()
+ if err != nil {
+ return nil, err
+ }
+
+ for _, link := range links {
+ if link.Attrs().Name == name {
+ return link, nil
+ }
+ }
+ return nil, fmt.Errorf("Link %s not found", name)
+}
+
+// LinkByName finds a link by name and returns a pointer to the object.
+func LinkByName(name string) (Link, error) {
+ if lookupByDump {
+ return linkByNameDump(name)
+ }
+
+ req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK)
+
+ msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+ req.AddData(msg)
+
+ nameData := nl.NewRtAttr(syscall.IFLA_IFNAME, nl.ZeroTerminated(name))
+ req.AddData(nameData)
+
+ link, err := execGetLink(req)
+ if err == syscall.EINVAL {
+ // older kernels don't support looking up via IFLA_IFNAME
+ // so fall back to dumping all links
+ lookupByDump = true
+ return linkByNameDump(name)
+ }
+
+ return link, err
+}
+
+// LinkByIndex finds a link by index and returns a pointer to the object.
+func LinkByIndex(index int) (Link, error) {
+ req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK)
+
+ msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+ msg.Index = int32(index)
+ req.AddData(msg)
+
+ return execGetLink(req)
+}
+
+func execGetLink(req *nl.NetlinkRequest) (Link, error) {
+ msgs, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+ if err != nil {
+ if errno, ok := err.(syscall.Errno); ok {
+ if errno == syscall.ENODEV {
+ return nil, fmt.Errorf("Link not found")
+ }
+ }
+ return nil, err
+ }
+
+ switch {
+ case len(msgs) == 0:
+ return nil, fmt.Errorf("Link not found")
+
+ case len(msgs) == 1:
+ return linkDeserialize(msgs[0])
+
+ default:
+ return nil, fmt.Errorf("More than one link found")
+ }
+}
+
+// linkDeserialize deserializes a raw message received from netlink into
+// a link object.
+func linkDeserialize(m []byte) (Link, error) {
+ msg := nl.DeserializeIfInfomsg(m)
+
+ attrs, err := nl.ParseRouteAttr(m[msg.Len():])
+ if err != nil {
+ return nil, err
+ }
+
+ base := LinkAttrs{Index: int(msg.Index), Flags: linkFlags(msg.Flags)}
+ var link Link
+ linkType := ""
+ for _, attr := range attrs {
+ switch attr.Attr.Type {
+ case syscall.IFLA_LINKINFO:
+ infos, err := nl.ParseRouteAttr(attr.Value)
+ if err != nil {
+ return nil, err
+ }
+ for _, info := range infos {
+ switch info.Attr.Type {
+ case nl.IFLA_INFO_KIND:
+ linkType = string(info.Value[:len(info.Value)-1])
+ switch linkType {
+ case "dummy":
+ link = &Dummy{}
+ case "bridge":
+ link = &Bridge{}
+ case "vlan":
+ link = &Vlan{}
+ case "veth":
+ link = &Veth{}
+ case "vxlan":
+ link = &Vxlan{}
+ case "ipvlan":
+ link = &IPVlan{}
+ default:
+ link = &Generic{LinkType: linkType}
+ }
+ case nl.IFLA_INFO_DATA:
+ data, err := nl.ParseRouteAttr(info.Value)
+ if err != nil {
+ return nil, err
+ }
+ switch linkType {
+ case "vlan":
+ parseVlanData(link, data)
+ case "vxlan":
+ parseVxlanData(link, data)
+ case "ipvlan":
+ parseIPVlanData(link, data)
+ }
+ }
+ }
+ case syscall.IFLA_ADDRESS:
+ var nonzero bool
+ for _, b := range attr.Value {
+ if b != 0 {
+ nonzero = true
+ }
+ }
+ if nonzero {
+ base.HardwareAddr = attr.Value[:]
+ }
+ case syscall.IFLA_IFNAME:
+ base.Name = string(attr.Value[:len(attr.Value)-1])
+ case syscall.IFLA_MTU:
+ base.MTU = int(native.Uint32(attr.Value[0:4]))
+ case syscall.IFLA_LINK:
+ base.ParentIndex = int(native.Uint32(attr.Value[0:4]))
+ case syscall.IFLA_MASTER:
+ base.MasterIndex = int(native.Uint32(attr.Value[0:4]))
+ case syscall.IFLA_TXQLEN:
+ base.TxQLen = native.Uint32(attr.Value[0:4])
+ }
+ }
+ // Links that don't have IFLA_INFO_KIND are hardware devices
+ if link == nil {
+ link = &Device{}
+ }
+ *link.Attrs() = base
+
+ return link, nil
+}
+
+// LinkList gets a list of link devices.
+// Equivalent to: `ip link show`
+func LinkList() ([]Link, error) {
+ // NOTE(vish): This duplicates functionality in net/iface_linux.go, but we need
+ // to get the message ourselves to parse link type.
+ req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_DUMP)
+
+ msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+ req.AddData(msg)
+
+ msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWLINK)
+ if err != nil {
+ return nil, err
+ }
+
+ res := make([]Link, 0)
+
+ for _, m := range msgs {
+ link, err := linkDeserialize(m)
+ if err != nil {
+ return nil, err
+ }
+ res = append(res, link)
+ }
+
+ return res, nil
+}
+
+func LinkSetHairpin(link Link, mode bool) error {
+ return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_MODE)
+}
+
+func LinkSetGuard(link Link, mode bool) error {
+ return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_GUARD)
+}
+
+func LinkSetFastLeave(link Link, mode bool) error {
+ return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_FAST_LEAVE)
+}
+
+func LinkSetLearning(link Link, mode bool) error {
+ return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_LEARNING)
+}
+
+func LinkSetRootBlock(link Link, mode bool) error {
+ return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_PROTECT)
+}
+
+func LinkSetFlood(link Link, mode bool) error {
+ return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_UNICAST_FLOOD)
+}
+
+func setProtinfoAttr(link Link, mode bool, attr int) error {
+ base := link.Attrs()
+ ensureIndex(base)
+ req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
+
+ msg := nl.NewIfInfomsg(syscall.AF_BRIDGE)
+ msg.Type = syscall.RTM_SETLINK
+ msg.Flags = syscall.NLM_F_REQUEST
+ msg.Index = int32(base.Index)
+ msg.Change = nl.DEFAULT_CHANGE
+ req.AddData(msg)
+
+ br := nl.NewRtAttr(syscall.IFLA_PROTINFO|syscall.NLA_F_NESTED, nil)
+ nl.NewRtAttrChild(br, attr, boolToByte(mode))
+ req.AddData(br)
+ _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func parseVlanData(link Link, data []syscall.NetlinkRouteAttr) {
+ vlan := link.(*Vlan)
+ for _, datum := range data {
+ switch datum.Attr.Type {
+ case nl.IFLA_VLAN_ID:
+ vlan.VlanId = int(native.Uint16(datum.Value[0:2]))
+ }
+ }
+}
+
+func parseVxlanData(link Link, data []syscall.NetlinkRouteAttr) {
+ vxlan := link.(*Vxlan)
+ for _, datum := range data {
+ switch datum.Attr.Type {
+ case nl.IFLA_VXLAN_ID:
+ vxlan.VxlanId = int(native.Uint32(datum.Value[0:4]))
+ case nl.IFLA_VXLAN_LINK:
+ vxlan.VtepDevIndex = int(native.Uint32(datum.Value[0:4]))
+ case nl.IFLA_VXLAN_LOCAL:
+ vxlan.SrcAddr = net.IP(datum.Value[0:4])
+ case nl.IFLA_VXLAN_LOCAL6:
+ vxlan.SrcAddr = net.IP(datum.Value[0:16])
+ case nl.IFLA_VXLAN_GROUP:
+ vxlan.Group = net.IP(datum.Value[0:4])
+ case nl.IFLA_VXLAN_GROUP6:
+ vxlan.Group = net.IP(datum.Value[0:16])
+ case nl.IFLA_VXLAN_TTL:
+ vxlan.TTL = int(datum.Value[0])
+ case nl.IFLA_VXLAN_TOS:
+ vxlan.TOS = int(datum.Value[0])
+ case nl.IFLA_VXLAN_LEARNING:
+ vxlan.Learning = int8(datum.Value[0]) != 0
+ case nl.IFLA_VXLAN_PROXY:
+ vxlan.Proxy = int8(datum.Value[0]) != 0
+ case nl.IFLA_VXLAN_RSC:
+ vxlan.RSC = int8(datum.Value[0]) != 0
+ case nl.IFLA_VXLAN_L2MISS:
+ vxlan.L2miss = int8(datum.Value[0]) != 0
+ case nl.IFLA_VXLAN_L3MISS:
+ vxlan.L3miss = int8(datum.Value[0]) != 0
+ case nl.IFLA_VXLAN_AGEING:
+ vxlan.Age = int(native.Uint32(datum.Value[0:4]))
+ vxlan.NoAge = vxlan.Age == 0
+ case nl.IFLA_VXLAN_LIMIT:
+ vxlan.Limit = int(native.Uint32(datum.Value[0:4]))
+ case nl.IFLA_VXLAN_PORT:
+ vxlan.Port = int(native.Uint16(datum.Value[0:2]))
+ case nl.IFLA_VXLAN_PORT_RANGE:
+ buf := bytes.NewBuffer(datum.Value[0:4])
+ var pr vxlanPortRange
+ if binary.Read(buf, binary.BigEndian, &pr) != nil {
+ vxlan.PortLow = int(pr.Lo)
+ vxlan.PortHigh = int(pr.Hi)
+ }
+ }
+ }
+}
+
+func parseIPVlanData(link Link, data []syscall.NetlinkRouteAttr) {
+ ipv := link.(*IPVlan)
+ for _, datum := range data {
+ if datum.Attr.Type == nl.IFLA_IPVLAN_MODE {
+ ipv.Mode = IPVlanMode(native.Uint32(datum.Value[0:4]))
+ return
+ }
+ }
+}
+
+// copied from pkg/net_linux.go
+func linkFlags(rawFlags uint32) net.Flags {
+ var f net.Flags
+ if rawFlags&syscall.IFF_UP != 0 {
+ f |= net.FlagUp
+ }
+ if rawFlags&syscall.IFF_BROADCAST != 0 {
+ f |= net.FlagBroadcast
+ }
+ if rawFlags&syscall.IFF_LOOPBACK != 0 {
+ f |= net.FlagLoopback
+ }
+ if rawFlags&syscall.IFF_POINTOPOINT != 0 {
+ f |= net.FlagPointToPoint
+ }
+ if rawFlags&syscall.IFF_MULTICAST != 0 {
+ f |= net.FlagMulticast
+ }
+ return f
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/link_test.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/link_test.go
new file mode 100644
index 0000000..05b8e95
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/link_test.go
@@ -0,0 +1,531 @@
+package netlink
+
+import (
+ "bytes"
+ "net"
+ "testing"
+
+ "github.com/vishvananda/netns"
+)
+
+const testTxQLen uint32 = 100
+
+func testLinkAddDel(t *testing.T, link Link) {
+ links, err := LinkList()
+ if err != nil {
+ t.Fatal(err)
+ }
+ num := len(links)
+
+ if err := LinkAdd(link); err != nil {
+ t.Fatal(err)
+ }
+
+ base := link.Attrs()
+
+ result, err := LinkByName(base.Name)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ rBase := result.Attrs()
+
+ if vlan, ok := link.(*Vlan); ok {
+ other, ok := result.(*Vlan)
+ if !ok {
+ t.Fatal("Result of create is not a vlan")
+ }
+ if vlan.VlanId != other.VlanId {
+ t.Fatal("Link.VlanId id doesn't match")
+ }
+ }
+
+ if rBase.ParentIndex == 0 && base.ParentIndex != 0 {
+ t.Fatal("Created link doesn't have a Parent but it should")
+ } else if rBase.ParentIndex != 0 && base.ParentIndex == 0 {
+ t.Fatal("Created link has a Parent but it shouldn't")
+ } else if rBase.ParentIndex != 0 && base.ParentIndex != 0 {
+ if rBase.ParentIndex != base.ParentIndex {
+ t.Fatal("Link.ParentIndex doesn't match")
+ }
+ }
+
+ if veth, ok := link.(*Veth); ok {
+ if veth.TxQLen != testTxQLen {
+ t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, testTxQLen)
+ }
+ if rBase.MTU != base.MTU {
+ t.Fatalf("MTU is %d, should be %d", rBase.MTU, base.MTU)
+ }
+
+ if veth.PeerName != "" {
+ var peer *Veth
+ other, err := LinkByName(veth.PeerName)
+ if err != nil {
+ t.Fatalf("Peer %s not created", veth.PeerName)
+ }
+ if peer, ok = other.(*Veth); !ok {
+ t.Fatalf("Peer %s is incorrect type", veth.PeerName)
+ }
+ if peer.TxQLen != testTxQLen {
+ t.Fatalf("TxQLen of peer is %d, should be %d", peer.TxQLen, testTxQLen)
+ }
+ }
+ }
+
+ if vxlan, ok := link.(*Vxlan); ok {
+ other, ok := result.(*Vxlan)
+ if !ok {
+ t.Fatal("Result of create is not a vxlan")
+ }
+ compareVxlan(t, vxlan, other)
+ }
+
+ if ipv, ok := link.(*IPVlan); ok {
+ other, ok := result.(*IPVlan)
+ if !ok {
+ t.Fatal("Result of create is not a ipvlan")
+ }
+ if ipv.Mode != other.Mode {
+ t.Fatalf("Got unexpected mode: %d, expected: %d", other.Mode, ipv.Mode)
+ }
+ }
+
+ if err = LinkDel(link); err != nil {
+ t.Fatal(err)
+ }
+
+ links, err = LinkList()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(links) != num {
+ t.Fatal("Link not removed properly")
+ }
+}
+
+func compareVxlan(t *testing.T, expected, actual *Vxlan) {
+
+ if actual.VxlanId != expected.VxlanId {
+ t.Fatal("Vxlan.VxlanId doesn't match")
+ }
+ if expected.SrcAddr != nil && !actual.SrcAddr.Equal(expected.SrcAddr) {
+ t.Fatal("Vxlan.SrcAddr doesn't match")
+ }
+ if expected.Group != nil && !actual.Group.Equal(expected.Group) {
+ t.Fatal("Vxlan.Group doesn't match")
+ }
+ if expected.TTL != -1 && actual.TTL != expected.TTL {
+ t.Fatal("Vxlan.TTL doesn't match")
+ }
+ if expected.TOS != -1 && actual.TOS != expected.TOS {
+ t.Fatal("Vxlan.TOS doesn't match")
+ }
+ if actual.Learning != expected.Learning {
+ t.Fatal("Vxlan.Learning doesn't match")
+ }
+ if actual.Proxy != expected.Proxy {
+ t.Fatal("Vxlan.Proxy doesn't match")
+ }
+ if actual.RSC != expected.RSC {
+ t.Fatal("Vxlan.RSC doesn't match")
+ }
+ if actual.L2miss != expected.L2miss {
+ t.Fatal("Vxlan.L2miss doesn't match")
+ }
+ if actual.L3miss != expected.L3miss {
+ t.Fatal("Vxlan.L3miss doesn't match")
+ }
+ if expected.NoAge {
+ if !actual.NoAge {
+ t.Fatal("Vxlan.NoAge doesn't match")
+ }
+ } else if expected.Age > 0 && actual.Age != expected.Age {
+ t.Fatal("Vxlan.Age doesn't match")
+ }
+ if expected.Limit > 0 && actual.Limit != expected.Limit {
+ t.Fatal("Vxlan.Limit doesn't match")
+ }
+ if expected.Port > 0 && actual.Port != expected.Port {
+ t.Fatal("Vxlan.Port doesn't match")
+ }
+ if expected.PortLow > 0 || expected.PortHigh > 0 {
+ if actual.PortLow != expected.PortLow {
+ t.Fatal("Vxlan.PortLow doesn't match")
+ }
+ if actual.PortHigh != expected.PortHigh {
+ t.Fatal("Vxlan.PortHigh doesn't match")
+ }
+ }
+}
+
+func TestLinkAddDelDummy(t *testing.T) {
+ tearDown := setUpNetlinkTest(t)
+ defer tearDown()
+
+ testLinkAddDel(t, &Dummy{LinkAttrs{Name: "foo"}})
+}
+
+func TestLinkAddDelBridge(t *testing.T) {
+ tearDown := setUpNetlinkTest(t)
+ defer tearDown()
+
+ testLinkAddDel(t, &Bridge{LinkAttrs{Name: "foo", MTU: 1400}})
+}
+
+func TestLinkAddDelVlan(t *testing.T) {
+ tearDown := setUpNetlinkTest(t)
+ defer tearDown()
+
+ parent := &Dummy{LinkAttrs{Name: "foo"}}
+ if err := LinkAdd(parent); err != nil {
+ t.Fatal(err)
+ }
+
+ testLinkAddDel(t, &Vlan{LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index}, 900})
+
+ if err := LinkDel(parent); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestLinkAddDelMacvlan(t *testing.T) {
+ tearDown := setUpNetlinkTest(t)
+ defer tearDown()
+
+ parent := &Dummy{LinkAttrs{Name: "foo"}}
+ if err := LinkAdd(parent); err != nil {
+ t.Fatal(err)
+ }
+
+ testLinkAddDel(t, &Macvlan{LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index}})
+
+ if err := LinkDel(parent); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestLinkAddDelVeth(t *testing.T) {
+ tearDown := setUpNetlinkTest(t)
+ defer tearDown()
+
+ testLinkAddDel(t, &Veth{LinkAttrs{Name: "foo", TxQLen: testTxQLen, MTU: 1400}, "bar"})
+}
+
+func TestLinkAddDelBridgeMaster(t *testing.T) {
+ tearDown := setUpNetlinkTest(t)
+ defer tearDown()
+
+ master := &Bridge{LinkAttrs{Name: "foo"}}
+ if err := LinkAdd(master); err != nil {
+ t.Fatal(err)
+ }
+ testLinkAddDel(t, &Dummy{LinkAttrs{Name: "bar", MasterIndex: master.Attrs().Index}})
+
+ if err := LinkDel(master); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestLinkSetUnsetResetMaster(t *testing.T) {
+ tearDown := setUpNetlinkTest(t)
+ defer tearDown()
+
+ master := &Bridge{LinkAttrs{Name: "foo"}}
+ if err := LinkAdd(master); err != nil {
+ t.Fatal(err)
+ }
+
+ newmaster := &Bridge{LinkAttrs{Name: "bar"}}
+ if err := LinkAdd(newmaster); err != nil {
+ t.Fatal(err)
+ }
+
+ slave := &Dummy{LinkAttrs{Name: "baz"}}
+ if err := LinkAdd(slave); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := LinkSetMaster(slave, master); err != nil {
+ t.Fatal(err)
+ }
+
+ link, err := LinkByName("baz")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if link.Attrs().MasterIndex != master.Attrs().Index {
+ t.Fatal("Master not set properly")
+ }
+
+ if err := LinkSetMaster(slave, newmaster); err != nil {
+ t.Fatal(err)
+ }
+
+ link, err = LinkByName("baz")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if link.Attrs().MasterIndex != newmaster.Attrs().Index {
+ t.Fatal("Master not reset properly")
+ }
+
+ if err := LinkSetMaster(slave, nil); err != nil {
+ t.Fatal(err)
+ }
+
+ link, err = LinkByName("baz")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if link.Attrs().MasterIndex != 0 {
+ t.Fatal("Master not unset properly")
+ }
+ if err := LinkDel(slave); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := LinkDel(newmaster); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := LinkDel(master); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestLinkSetNs(t *testing.T) {
+ tearDown := setUpNetlinkTest(t)
+ defer tearDown()
+
+ basens, err := netns.Get()
+ if err != nil {
+ t.Fatal("Failed to get basens")
+ }
+ defer basens.Close()
+
+ newns, err := netns.New()
+ if err != nil {
+ t.Fatal("Failed to create newns")
+ }
+ defer newns.Close()
+
+ link := &Veth{LinkAttrs{Name: "foo"}, "bar"}
+ if err := LinkAdd(link); err != nil {
+ t.Fatal(err)
+ }
+
+ peer, err := LinkByName("bar")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ LinkSetNsFd(peer, int(basens))
+ if err != nil {
+ t.Fatal("Failed to set newns for link")
+ }
+
+ _, err = LinkByName("bar")
+ if err == nil {
+ t.Fatal("Link bar is still in newns")
+ }
+
+ err = netns.Set(basens)
+ if err != nil {
+ t.Fatal("Failed to set basens")
+ }
+
+ peer, err = LinkByName("bar")
+ if err != nil {
+ t.Fatal("Link is not in basens")
+ }
+
+ if err := LinkDel(peer); err != nil {
+ t.Fatal(err)
+ }
+
+ err = netns.Set(newns)
+ if err != nil {
+ t.Fatal("Failed to set newns")
+ }
+
+ _, err = LinkByName("foo")
+ if err == nil {
+ t.Fatal("Other half of veth pair not deleted")
+ }
+
+}
+
+func TestLinkAddDelVxlan(t *testing.T) {
+ tearDown := setUpNetlinkTest(t)
+ defer tearDown()
+
+ parent := &Dummy{
+ LinkAttrs{Name: "foo"},
+ }
+ if err := LinkAdd(parent); err != nil {
+ t.Fatal(err)
+ }
+
+ vxlan := Vxlan{
+ LinkAttrs: LinkAttrs{
+ Name: "bar",
+ },
+ VxlanId: 10,
+ VtepDevIndex: parent.Index,
+ Learning: true,
+ L2miss: true,
+ L3miss: true,
+ }
+
+ testLinkAddDel(t, &vxlan)
+ if err := LinkDel(parent); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestLinkAddDelIPVlanL2(t *testing.T) {
+ tearDown := setUpNetlinkTest(t)
+ defer tearDown()
+ parent := &Dummy{LinkAttrs{Name: "foo"}}
+ if err := LinkAdd(parent); err != nil {
+ t.Fatal(err)
+ }
+
+ ipv := IPVlan{
+ LinkAttrs: LinkAttrs{
+ Name: "bar",
+ ParentIndex: parent.Index,
+ },
+ Mode: IPVLAN_MODE_L2,
+ }
+
+ testLinkAddDel(t, &ipv)
+}
+
+func TestLinkAddDelIPVlanL3(t *testing.T) {
+ tearDown := setUpNetlinkTest(t)
+ defer tearDown()
+ parent := &Dummy{LinkAttrs{Name: "foo"}}
+ if err := LinkAdd(parent); err != nil {
+ t.Fatal(err)
+ }
+
+ ipv := IPVlan{
+ LinkAttrs: LinkAttrs{
+ Name: "bar",
+ ParentIndex: parent.Index,
+ },
+ Mode: IPVLAN_MODE_L3,
+ }
+
+ testLinkAddDel(t, &ipv)
+}
+
+func TestLinkAddDelIPVlanNoParent(t *testing.T) {
+ tearDown := setUpNetlinkTest(t)
+ defer tearDown()
+
+ ipv := IPVlan{
+ LinkAttrs: LinkAttrs{
+ Name: "bar",
+ },
+ Mode: IPVLAN_MODE_L3,
+ }
+ err := LinkAdd(&ipv)
+ if err == nil {
+ t.Fatal("Add should fail if ipvlan creating without ParentIndex")
+ }
+ if err.Error() != "Can't create ipvlan link without ParentIndex" {
+ t.Fatalf("Error should be about missing ParentIndex, got %q", err)
+ }
+}
+
+func TestLinkByIndex(t *testing.T) {
+ tearDown := setUpNetlinkTest(t)
+ defer tearDown()
+
+ dummy := &Dummy{LinkAttrs{Name: "dummy"}}
+ if err := LinkAdd(dummy); err != nil {
+ t.Fatal(err)
+ }
+
+ found, err := LinkByIndex(dummy.Index)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if found.Attrs().Index != dummy.Attrs().Index {
+ t.Fatalf("Indices don't match: %v != %v", found.Attrs().Index, dummy.Attrs().Index)
+ }
+
+ LinkDel(dummy)
+
+ // test not found
+ _, err = LinkByIndex(dummy.Attrs().Index)
+ if err == nil {
+ t.Fatalf("LinkByIndex(%v) found deleted link", err)
+ }
+}
+
+func TestLinkSet(t *testing.T) {
+ tearDown := setUpNetlinkTest(t)
+ defer tearDown()
+
+ iface := &Dummy{LinkAttrs{Name: "foo"}}
+ if err := LinkAdd(iface); err != nil {
+ t.Fatal(err)
+ }
+
+ link, err := LinkByName("foo")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = LinkSetName(link, "bar")
+ if err != nil {
+ t.Fatalf("Could not change interface name: %v", err)
+ }
+
+ link, err = LinkByName("bar")
+ if err != nil {
+ t.Fatalf("Interface name not changed: %v", err)
+ }
+
+ err = LinkSetMTU(link, 1400)
+ if err != nil {
+ t.Fatalf("Could not set MTU: %v", err)
+ }
+
+ link, err = LinkByName("bar")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if link.Attrs().MTU != 1400 {
+ t.Fatal("MTU not changed!")
+ }
+
+ addr, err := net.ParseMAC("00:12:34:56:78:AB")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = LinkSetHardwareAddr(link, addr)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ link, err = LinkByName("bar")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !bytes.Equal(link.Attrs().HardwareAddr, addr) {
+ t.Fatalf("hardware address not changed!")
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/neigh.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/neigh.go
new file mode 100644
index 0000000..0e5eb90
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/neigh.go
@@ -0,0 +1,22 @@
+package netlink
+
+import (
+ "fmt"
+ "net"
+)
+
+// Neigh represents a link layer neighbor from netlink.
+type Neigh struct {
+ LinkIndex int
+ Family int
+ State int
+ Type int
+ Flags int
+ IP net.IP
+ HardwareAddr net.HardwareAddr
+}
+
+// String returns $ip/$hwaddr $label
+func (neigh *Neigh) String() string {
+ return fmt.Sprintf("%s %s", neigh.IP, neigh.HardwareAddr)
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/neigh_linux.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/neigh_linux.go
new file mode 100644
index 0000000..1fdaa3a
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/neigh_linux.go
@@ -0,0 +1,189 @@
+package netlink
+
+import (
+ "net"
+ "syscall"
+ "unsafe"
+
+ "github.com/vishvananda/netlink/nl"
+)
+
+const (
+ NDA_UNSPEC = iota
+ NDA_DST
+ NDA_LLADDR
+ NDA_CACHEINFO
+ NDA_PROBES
+ NDA_VLAN
+ NDA_PORT
+ NDA_VNI
+ NDA_IFINDEX
+ NDA_MAX = NDA_IFINDEX
+)
+
+// Neighbor Cache Entry States.
+const (
+ NUD_NONE = 0x00
+ NUD_INCOMPLETE = 0x01
+ NUD_REACHABLE = 0x02
+ NUD_STALE = 0x04
+ NUD_DELAY = 0x08
+ NUD_PROBE = 0x10
+ NUD_FAILED = 0x20
+ NUD_NOARP = 0x40
+ NUD_PERMANENT = 0x80
+)
+
+// Neighbor Flags
+const (
+ NTF_USE = 0x01
+ NTF_SELF = 0x02
+ NTF_MASTER = 0x04
+ NTF_PROXY = 0x08
+ NTF_ROUTER = 0x80
+)
+
+type Ndmsg struct {
+ Family uint8
+ Index uint32
+ State uint16
+ Flags uint8
+ Type uint8
+}
+
+func deserializeNdmsg(b []byte) *Ndmsg {
+ var dummy Ndmsg
+ return (*Ndmsg)(unsafe.Pointer(&b[0:unsafe.Sizeof(dummy)][0]))
+}
+
+func (msg *Ndmsg) Serialize() []byte {
+ return (*(*[unsafe.Sizeof(*msg)]byte)(unsafe.Pointer(msg)))[:]
+}
+
+func (msg *Ndmsg) Len() int {
+ return int(unsafe.Sizeof(*msg))
+}
+
+// NeighAdd will add an IP to MAC mapping to the ARP table
+// Equivalent to: `ip neigh add ....`
+func NeighAdd(neigh *Neigh) error {
+ return neighAdd(neigh, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL)
+}
+
+// NeighAdd will add or replace an IP to MAC mapping to the ARP table
+// Equivalent to: `ip neigh replace....`
+func NeighSet(neigh *Neigh) error {
+ return neighAdd(neigh, syscall.NLM_F_CREATE)
+}
+
+// NeighAppend will append an entry to FDB
+// Equivalent to: `bridge fdb append...`
+func NeighAppend(neigh *Neigh) error {
+ return neighAdd(neigh, syscall.NLM_F_CREATE|syscall.NLM_F_APPEND)
+}
+
+func neighAdd(neigh *Neigh, mode int) error {
+ req := nl.NewNetlinkRequest(syscall.RTM_NEWNEIGH, mode|syscall.NLM_F_ACK)
+ return neighHandle(neigh, req)
+}
+
+// NeighDel will delete an IP address from a link device.
+// Equivalent to: `ip addr del $addr dev $link`
+func NeighDel(neigh *Neigh) error {
+ req := nl.NewNetlinkRequest(syscall.RTM_DELNEIGH, syscall.NLM_F_ACK)
+ return neighHandle(neigh, req)
+}
+
+func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error {
+ var family int
+ if neigh.Family > 0 {
+ family = neigh.Family
+ } else {
+ family = nl.GetIPFamily(neigh.IP)
+ }
+
+ msg := Ndmsg{
+ Family: uint8(family),
+ Index: uint32(neigh.LinkIndex),
+ State: uint16(neigh.State),
+ Type: uint8(neigh.Type),
+ Flags: uint8(neigh.Flags),
+ }
+ req.AddData(&msg)
+
+ ipData := neigh.IP.To4()
+ if ipData == nil {
+ ipData = neigh.IP.To16()
+ }
+
+ dstData := nl.NewRtAttr(NDA_DST, ipData)
+ req.AddData(dstData)
+
+ hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr))
+ req.AddData(hwData)
+
+ _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+ return err
+}
+
+// NeighList gets a list of IP-MAC mappings in the system (ARP table).
+// Equivalent to: `ip neighbor show`.
+// The list can be filtered by link and ip family.
+func NeighList(linkIndex, family int) ([]Neigh, error) {
+ req := nl.NewNetlinkRequest(syscall.RTM_GETNEIGH, syscall.NLM_F_DUMP)
+ msg := Ndmsg{
+ Family: uint8(family),
+ }
+ req.AddData(&msg)
+
+ msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWNEIGH)
+ if err != nil {
+ return nil, err
+ }
+
+ res := make([]Neigh, 0)
+ for _, m := range msgs {
+ ndm := deserializeNdmsg(m)
+ if linkIndex != 0 && int(ndm.Index) != linkIndex {
+ // Ignore messages from other interfaces
+ continue
+ }
+
+ neigh, err := NeighDeserialize(m)
+ if err != nil {
+ continue
+ }
+
+ res = append(res, *neigh)
+ }
+
+ return res, nil
+}
+
+func NeighDeserialize(m []byte) (*Neigh, error) {
+ msg := deserializeNdmsg(m)
+
+ neigh := Neigh{
+ LinkIndex: int(msg.Index),
+ Family: int(msg.Family),
+ State: int(msg.State),
+ Type: int(msg.Type),
+ Flags: int(msg.Flags),
+ }
+
+ attrs, err := nl.ParseRouteAttr(m[msg.Len():])
+ if err != nil {
+ return nil, err
+ }
+
+ 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)
+ }
+ }
+
+ return &neigh, nil
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/neigh_test.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/neigh_test.go
new file mode 100644
index 0000000..50da59c
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/neigh_test.go
@@ -0,0 +1,104 @@
+package netlink
+
+import (
+ "net"
+ "testing"
+)
+
+type arpEntry struct {
+ ip net.IP
+ mac net.HardwareAddr
+}
+
+func parseMAC(s string) net.HardwareAddr {
+ m, err := net.ParseMAC(s)
+ if err != nil {
+ panic(err)
+ }
+ return m
+}
+
+func dumpContains(dump []Neigh, e arpEntry) bool {
+ for _, n := range dump {
+ if n.IP.Equal(e.ip) && (n.State&NUD_INCOMPLETE) == 0 {
+ return true
+ }
+ }
+ return false
+}
+
+func TestNeighAddDel(t *testing.T) {
+ tearDown := setUpNetlinkTest(t)
+ defer tearDown()
+
+ dummy := Dummy{LinkAttrs{Name: "neigh0"}}
+ if err := LinkAdd(&dummy); err != nil {
+ t.Fatal(err)
+ }
+
+ ensureIndex(dummy.Attrs())
+
+ arpTable := []arpEntry{
+ {net.ParseIP("10.99.0.1"), parseMAC("aa:bb:cc:dd:00:01")},
+ {net.ParseIP("10.99.0.2"), parseMAC("aa:bb:cc:dd:00:02")},
+ {net.ParseIP("10.99.0.3"), parseMAC("aa:bb:cc:dd:00:03")},
+ {net.ParseIP("10.99.0.4"), parseMAC("aa:bb:cc:dd:00:04")},
+ {net.ParseIP("10.99.0.5"), parseMAC("aa:bb:cc:dd:00:05")},
+ }
+
+ // Add the arpTable
+ for _, entry := range arpTable {
+ err := NeighAdd(&Neigh{
+ LinkIndex: dummy.Index,
+ State: NUD_REACHABLE,
+ IP: entry.ip,
+ HardwareAddr: entry.mac,
+ })
+
+ if err != nil {
+ t.Errorf("Failed to NeighAdd: %v", err)
+ }
+ }
+
+ // Dump and see that all added entries are there
+ dump, err := NeighList(dummy.Index, 0)
+ if err != nil {
+ t.Errorf("Failed to NeighList: %v", err)
+ }
+
+ for _, entry := range arpTable {
+ if !dumpContains(dump, entry) {
+ t.Errorf("Dump does not contain: %v", entry)
+ }
+ }
+
+ // Delete the arpTable
+ for _, entry := range arpTable {
+ err := NeighDel(&Neigh{
+ LinkIndex: dummy.Index,
+ IP: entry.ip,
+ HardwareAddr: entry.mac,
+ })
+
+ if err != nil {
+ t.Errorf("Failed to NeighDel: %v", err)
+ }
+ }
+
+ // TODO: seems not working because of cache
+ //// Dump and see that none of deleted entries are there
+ //dump, err = NeighList(dummy.Index, 0)
+ //if err != nil {
+ //t.Errorf("Failed to NeighList: %v", err)
+ //}
+
+ //for _, entry := range arpTable {
+ //if dumpContains(dump, entry) {
+ //t.Errorf("Dump contains: %v", entry)
+ //}
+ //}
+
+ if err := LinkDel(&dummy); err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/netlink.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/netlink.go
new file mode 100644
index 0000000..41ebdb1
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/netlink.go
@@ -0,0 +1,39 @@
+// Package netlink provides a simple library for netlink. Netlink is
+// the interface a user-space program in linux uses to communicate with
+// the kernel. It can be used to add and remove interfaces, set up ip
+// addresses and routes, and confiugre ipsec. Netlink communication
+// requires elevated privileges, so in most cases this code needs to
+// be run as root. The low level primitives for netlink are contained
+// in the nl subpackage. This package attempts to provide a high-level
+// interface that is loosly modeled on the iproute2 cli.
+package netlink
+
+import (
+ "net"
+
+ "github.com/vishvananda/netlink/nl"
+)
+
+const (
+ // Family type definitions
+ FAMILY_ALL = nl.FAMILY_ALL
+ FAMILY_V4 = nl.FAMILY_V4
+ FAMILY_V6 = nl.FAMILY_V6
+)
+
+// ParseIPNet parses a string in ip/net format and returns a net.IPNet.
+// This is valuable because addresses in netlink are often IPNets and
+// ParseCIDR returns an IPNet with the IP part set to the base IP of the
+// range.
+func ParseIPNet(s string) (*net.IPNet, error) {
+ ip, ipNet, err := net.ParseCIDR(s)
+ if err != nil {
+ return nil, err
+ }
+ return &net.IPNet{IP: ip, Mask: ipNet.Mask}, nil
+}
+
+// NewIPNet generates an IPNet from an ip address using a netmask of 32.
+func NewIPNet(ip net.IP) *net.IPNet {
+ return &net.IPNet{IP: ip, Mask: net.CIDRMask(32, 32)}
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/netlink_test.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/netlink_test.go
new file mode 100644
index 0000000..3292b75
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/netlink_test.go
@@ -0,0 +1,34 @@
+package netlink
+
+import (
+ "log"
+ "os"
+ "runtime"
+ "testing"
+
+ "github.com/vishvananda/netns"
+)
+
+type tearDownNetlinkTest func()
+
+func setUpNetlinkTest(t *testing.T) tearDownNetlinkTest {
+ if os.Getuid() != 0 {
+ msg := "Skipped test because it requires root privileges."
+ log.Printf(msg)
+ t.Skip(msg)
+ }
+
+ // new temporary namespace so we don't pollute the host
+ // lock thread since the namespace is thread local
+ runtime.LockOSThread()
+ var err error
+ ns, err := netns.New()
+ if err != nil {
+ t.Fatal("Failed to create newns", ns)
+ }
+
+ return func() {
+ ns.Close()
+ runtime.UnlockOSThread()
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/netlink_unspecified.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/netlink_unspecified.go
new file mode 100644
index 0000000..10c49c1
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/netlink_unspecified.go
@@ -0,0 +1,143 @@
+// +build !linux
+
+package netlink
+
+import (
+ "errors"
+)
+
+var (
+ ErrNotImplemented = errors.New("not implemented")
+)
+
+func LinkSetUp(link *Link) error {
+ return ErrNotImplemented
+}
+
+func LinkSetDown(link *Link) error {
+ return ErrNotImplemented
+}
+
+func LinkSetMTU(link *Link, mtu int) error {
+ return ErrNotImplemented
+}
+
+func LinkSetMaster(link *Link, master *Link) error {
+ return ErrNotImplemented
+}
+
+func LinkSetNsPid(link *Link, nspid int) error {
+ return ErrNotImplemented
+}
+
+func LinkSetNsFd(link *Link, fd int) error {
+ return ErrNotImplemented
+}
+
+func LinkAdd(link *Link) error {
+ return ErrNotImplemented
+}
+
+func LinkDel(link *Link) error {
+ return ErrNotImplemented
+}
+
+func SetHairpin(link Link, mode bool) error {
+ return ErrNotImplemented
+}
+
+func SetGuard(link Link, mode bool) error {
+ return ErrNotImplemented
+}
+
+func SetFastLeave(link Link, mode bool) error {
+ return ErrNotImplemented
+}
+
+func SetLearning(link Link, mode bool) error {
+ return ErrNotImplemented
+}
+
+func SetRootBlock(link Link, mode bool) error {
+ return ErrNotImplemented
+}
+
+func SetFlood(link Link, mode bool) error {
+ return ErrNotImplemented
+}
+
+func LinkList() ([]Link, error) {
+ return nil, ErrNotImplemented
+}
+
+func AddrAdd(link *Link, addr *Addr) error {
+ return ErrNotImplemented
+}
+
+func AddrDel(link *Link, addr *Addr) error {
+ return ErrNotImplemented
+}
+
+func AddrList(link *Link, family int) ([]Addr, error) {
+ return nil, ErrNotImplemented
+}
+
+func RouteAdd(route *Route) error {
+ return ErrNotImplemented
+}
+
+func RouteDel(route *Route) error {
+ return ErrNotImplemented
+}
+
+func RouteList(link *Link, family int) ([]Route, error) {
+ return nil, ErrNotImplemented
+}
+
+func XfrmPolicyAdd(policy *XfrmPolicy) error {
+ return ErrNotImplemented
+}
+
+func XfrmPolicyDel(policy *XfrmPolicy) error {
+ return ErrNotImplemented
+}
+
+func XfrmPolicyList(family int) ([]XfrmPolicy, error) {
+ return nil, ErrNotImplemented
+}
+
+func XfrmStateAdd(policy *XfrmState) error {
+ return ErrNotImplemented
+}
+
+func XfrmStateDel(policy *XfrmState) error {
+ return ErrNotImplemented
+}
+
+func XfrmStateList(family int) ([]XfrmState, error) {
+ return nil, ErrNotImplemented
+}
+
+func NeighAdd(neigh *Neigh) error {
+ return ErrNotImplemented
+}
+
+func NeighSet(neigh *Neigh) error {
+ return ErrNotImplemented
+}
+
+func NeighAppend(neigh *Neigh) error {
+ return ErrNotImplemented
+}
+
+func NeighDel(neigh *Neigh) error {
+ return ErrNotImplemented
+}
+
+func NeighList(linkIndex, family int) ([]Neigh, error) {
+ return nil, ErrNotImplemented
+}
+
+func NeighDeserialize(m []byte) (*Ndmsg, *Neigh, error) {
+ return nil, nil, ErrNotImplemented
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/addr_linux.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/addr_linux.go
new file mode 100644
index 0000000..17088fa
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/addr_linux.go
@@ -0,0 +1,47 @@
+package nl
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+type IfAddrmsg struct {
+ syscall.IfAddrmsg
+}
+
+func NewIfAddrmsg(family int) *IfAddrmsg {
+ return &IfAddrmsg{
+ IfAddrmsg: syscall.IfAddrmsg{
+ Family: uint8(family),
+ },
+ }
+}
+
+// struct ifaddrmsg {
+// __u8 ifa_family;
+// __u8 ifa_prefixlen; /* The prefix length */
+// __u8 ifa_flags; /* Flags */
+// __u8 ifa_scope; /* Address scope */
+// __u32 ifa_index; /* Link index */
+// };
+
+// type IfAddrmsg struct {
+// Family uint8
+// Prefixlen uint8
+// Flags uint8
+// Scope uint8
+// Index uint32
+// }
+// SizeofIfAddrmsg = 0x8
+
+func DeserializeIfAddrmsg(b []byte) *IfAddrmsg {
+ return (*IfAddrmsg)(unsafe.Pointer(&b[0:syscall.SizeofIfAddrmsg][0]))
+}
+
+func (msg *IfAddrmsg) Serialize() []byte {
+ return (*(*[syscall.SizeofIfAddrmsg]byte)(unsafe.Pointer(msg)))[:]
+}
+
+func (msg *IfAddrmsg) Len() int {
+ return syscall.SizeofIfAddrmsg
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/addr_linux_test.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/addr_linux_test.go
new file mode 100644
index 0000000..98c3b21
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/addr_linux_test.go
@@ -0,0 +1,39 @@
+package nl
+
+import (
+ "bytes"
+ "crypto/rand"
+ "encoding/binary"
+ "syscall"
+ "testing"
+)
+
+func (msg *IfAddrmsg) write(b []byte) {
+ native := NativeEndian()
+ b[0] = msg.Family
+ b[1] = msg.Prefixlen
+ b[2] = msg.Flags
+ b[3] = msg.Scope
+ native.PutUint32(b[4:8], msg.Index)
+}
+
+func (msg *IfAddrmsg) serializeSafe() []byte {
+ len := syscall.SizeofIfAddrmsg
+ b := make([]byte, len)
+ msg.write(b)
+ return b
+}
+
+func deserializeIfAddrmsgSafe(b []byte) *IfAddrmsg {
+ var msg = IfAddrmsg{}
+ binary.Read(bytes.NewReader(b[0:syscall.SizeofIfAddrmsg]), NativeEndian(), &msg)
+ return &msg
+}
+
+func TestIfAddrmsgDeserializeSerialize(t *testing.T) {
+ var orig = make([]byte, syscall.SizeofIfAddrmsg)
+ rand.Read(orig)
+ safemsg := deserializeIfAddrmsgSafe(orig)
+ msg := DeserializeIfAddrmsg(orig)
+ testDeserializeSerialize(t, orig, safemsg, msg)
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/link_linux.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/link_linux.go
new file mode 100644
index 0000000..ab0dede
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/link_linux.go
@@ -0,0 +1,81 @@
+package nl
+
+const (
+ DEFAULT_CHANGE = 0xFFFFFFFF
+)
+
+const (
+ IFLA_INFO_UNSPEC = iota
+ IFLA_INFO_KIND
+ IFLA_INFO_DATA
+ IFLA_INFO_XSTATS
+ IFLA_INFO_MAX = IFLA_INFO_XSTATS
+)
+
+const (
+ IFLA_VLAN_UNSPEC = iota
+ IFLA_VLAN_ID
+ IFLA_VLAN_FLAGS
+ IFLA_VLAN_EGRESS_QOS
+ IFLA_VLAN_INGRESS_QOS
+ IFLA_VLAN_PROTOCOL
+ IFLA_VLAN_MAX = IFLA_VLAN_PROTOCOL
+)
+
+const (
+ VETH_INFO_UNSPEC = iota
+ VETH_INFO_PEER
+ VETH_INFO_MAX = VETH_INFO_PEER
+)
+
+const (
+ IFLA_VXLAN_UNSPEC = iota
+ IFLA_VXLAN_ID
+ IFLA_VXLAN_GROUP
+ IFLA_VXLAN_LINK
+ IFLA_VXLAN_LOCAL
+ IFLA_VXLAN_TTL
+ IFLA_VXLAN_TOS
+ IFLA_VXLAN_LEARNING
+ IFLA_VXLAN_AGEING
+ IFLA_VXLAN_LIMIT
+ IFLA_VXLAN_PORT_RANGE
+ IFLA_VXLAN_PROXY
+ IFLA_VXLAN_RSC
+ IFLA_VXLAN_L2MISS
+ IFLA_VXLAN_L3MISS
+ IFLA_VXLAN_PORT
+ IFLA_VXLAN_GROUP6
+ IFLA_VXLAN_LOCAL6
+ IFLA_VXLAN_MAX = IFLA_VXLAN_LOCAL6
+)
+
+const (
+ BRIDGE_MODE_UNSPEC = iota
+ BRIDGE_MODE_HAIRPIN
+)
+
+const (
+ IFLA_BRPORT_UNSPEC = iota
+ IFLA_BRPORT_STATE
+ IFLA_BRPORT_PRIORITY
+ IFLA_BRPORT_COST
+ IFLA_BRPORT_MODE
+ IFLA_BRPORT_GUARD
+ IFLA_BRPORT_PROTECT
+ IFLA_BRPORT_FAST_LEAVE
+ IFLA_BRPORT_LEARNING
+ IFLA_BRPORT_UNICAST_FLOOD
+ IFLA_BRPORT_MAX = IFLA_BRPORT_UNICAST_FLOOD
+)
+
+const (
+ IFLA_IPVLAN_UNSPEC = iota
+ IFLA_IPVLAN_MODE
+ IFLA_IPVLAN_MAX = IFLA_IPVLAN_MODE
+)
+
+const (
+ // not defined in syscall
+ IFLA_NET_NS_FD = 28
+)
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/nl_linux.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/nl_linux.go
new file mode 100644
index 0000000..72f2813
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/nl_linux.go
@@ -0,0 +1,417 @@
+// Package nl has low level primitives for making Netlink calls.
+package nl
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "net"
+ "sync/atomic"
+ "syscall"
+ "unsafe"
+)
+
+const (
+ // Family type definitions
+ FAMILY_ALL = syscall.AF_UNSPEC
+ FAMILY_V4 = syscall.AF_INET
+ FAMILY_V6 = syscall.AF_INET6
+)
+
+var nextSeqNr uint32
+
+// GetIPFamily returns the family type of a net.IP.
+func GetIPFamily(ip net.IP) int {
+ if len(ip) <= net.IPv4len {
+ return FAMILY_V4
+ }
+ if ip.To4() != nil {
+ return FAMILY_V4
+ }
+ return FAMILY_V6
+}
+
+var nativeEndian binary.ByteOrder
+
+// Get native endianness for the system
+func NativeEndian() binary.ByteOrder {
+ if nativeEndian == nil {
+ var x uint32 = 0x01020304
+ if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
+ nativeEndian = binary.BigEndian
+ }
+ nativeEndian = binary.LittleEndian
+ }
+ return nativeEndian
+}
+
+// Byte swap a 16 bit value if we aren't big endian
+func Swap16(i uint16) uint16 {
+ if NativeEndian() == binary.BigEndian {
+ return i
+ }
+ return (i&0xff00)>>8 | (i&0xff)<<8
+}
+
+// Byte swap a 32 bit value if aren't big endian
+func Swap32(i uint32) uint32 {
+ if NativeEndian() == binary.BigEndian {
+ return i
+ }
+ return (i&0xff000000)>>24 | (i&0xff0000)>>8 | (i&0xff00)<<8 | (i&0xff)<<24
+}
+
+type NetlinkRequestData interface {
+ Len() int
+ Serialize() []byte
+}
+
+// IfInfomsg is related to links, but it is used for list requests as well
+type IfInfomsg struct {
+ syscall.IfInfomsg
+}
+
+// Create an IfInfomsg with family specified
+func NewIfInfomsg(family int) *IfInfomsg {
+ return &IfInfomsg{
+ IfInfomsg: syscall.IfInfomsg{
+ Family: uint8(family),
+ },
+ }
+}
+
+func DeserializeIfInfomsg(b []byte) *IfInfomsg {
+ return (*IfInfomsg)(unsafe.Pointer(&b[0:syscall.SizeofIfInfomsg][0]))
+}
+
+func (msg *IfInfomsg) Serialize() []byte {
+ return (*(*[syscall.SizeofIfInfomsg]byte)(unsafe.Pointer(msg)))[:]
+}
+
+func (msg *IfInfomsg) Len() int {
+ return syscall.SizeofIfInfomsg
+}
+
+func rtaAlignOf(attrlen int) int {
+ return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1)
+}
+
+func NewIfInfomsgChild(parent *RtAttr, family int) *IfInfomsg {
+ msg := NewIfInfomsg(family)
+ parent.children = append(parent.children, msg)
+ return msg
+}
+
+// Extend RtAttr to handle data and children
+type RtAttr struct {
+ syscall.RtAttr
+ Data []byte
+ children []NetlinkRequestData
+}
+
+// Create a new Extended RtAttr object
+func NewRtAttr(attrType int, data []byte) *RtAttr {
+ return &RtAttr{
+ RtAttr: syscall.RtAttr{
+ Type: uint16(attrType),
+ },
+ children: []NetlinkRequestData{},
+ Data: data,
+ }
+}
+
+// Create a new RtAttr obj anc add it as a child of an existing object
+func NewRtAttrChild(parent *RtAttr, attrType int, data []byte) *RtAttr {
+ attr := NewRtAttr(attrType, data)
+ parent.children = append(parent.children, attr)
+ return attr
+}
+
+func (a *RtAttr) Len() int {
+ if len(a.children) == 0 {
+ return (syscall.SizeofRtAttr + len(a.Data))
+ }
+
+ l := 0
+ for _, child := range a.children {
+ l += rtaAlignOf(child.Len())
+ }
+ l += syscall.SizeofRtAttr
+ return rtaAlignOf(l + len(a.Data))
+}
+
+// Serialize the RtAttr into a byte array
+// This can't ust unsafe.cast because it must iterate through children.
+func (a *RtAttr) Serialize() []byte {
+ native := NativeEndian()
+
+ length := a.Len()
+ buf := make([]byte, rtaAlignOf(length))
+
+ if a.Data != nil {
+ copy(buf[4:], a.Data)
+ } else {
+ next := 4
+ for _, child := range a.children {
+ childBuf := child.Serialize()
+ copy(buf[next:], childBuf)
+ next += rtaAlignOf(len(childBuf))
+ }
+ }
+
+ if l := uint16(length); l != 0 {
+ native.PutUint16(buf[0:2], l)
+ }
+ native.PutUint16(buf[2:4], a.Type)
+ return buf
+}
+
+type NetlinkRequest struct {
+ syscall.NlMsghdr
+ Data []NetlinkRequestData
+}
+
+// Serialize the Netlink Request into a byte array
+func (msg *NetlinkRequest) Serialize() []byte {
+ length := syscall.SizeofNlMsghdr
+ dataBytes := make([][]byte, len(msg.Data))
+ for i, data := range msg.Data {
+ dataBytes[i] = data.Serialize()
+ length = length + len(dataBytes[i])
+ }
+ msg.Len = uint32(length)
+ b := make([]byte, length)
+ hdr := (*(*[syscall.SizeofNlMsghdr]byte)(unsafe.Pointer(msg)))[:]
+ next := syscall.SizeofNlMsghdr
+ copy(b[0:next], hdr)
+ for _, data := range dataBytes {
+ for _, dataByte := range data {
+ b[next] = dataByte
+ next = next + 1
+ }
+ }
+ return b
+}
+
+func (msg *NetlinkRequest) AddData(data NetlinkRequestData) {
+ if data != nil {
+ msg.Data = append(msg.Data, data)
+ }
+}
+
+// Execute the request against a the given sockType.
+// Returns a list of netlink messages in seriaized format, optionally filtered
+// by resType.
+func (req *NetlinkRequest) Execute(sockType int, resType uint16) ([][]byte, error) {
+ s, err := getNetlinkSocket(sockType)
+ if err != nil {
+ return nil, err
+ }
+ defer s.Close()
+
+ if err := s.Send(req); err != nil {
+ return nil, err
+ }
+
+ pid, err := s.GetPid()
+ if err != nil {
+ return nil, err
+ }
+
+ res := make([][]byte, 0)
+
+done:
+ for {
+ msgs, err := s.Recieve()
+ if err != nil {
+ return nil, err
+ }
+ for _, m := range msgs {
+ if m.Header.Seq != req.Seq {
+ return nil, fmt.Errorf("Wrong Seq nr %d, expected 1", m.Header.Seq)
+ }
+ if m.Header.Pid != pid {
+ return nil, fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
+ }
+ if m.Header.Type == syscall.NLMSG_DONE {
+ break done
+ }
+ if m.Header.Type == syscall.NLMSG_ERROR {
+ native := NativeEndian()
+ error := int32(native.Uint32(m.Data[0:4]))
+ if error == 0 {
+ break done
+ }
+ return nil, syscall.Errno(-error)
+ }
+ if resType != 0 && m.Header.Type != resType {
+ continue
+ }
+ res = append(res, m.Data)
+ if m.Header.Flags&syscall.NLM_F_MULTI == 0 {
+ break done
+ }
+ }
+ }
+ return res, nil
+}
+
+// Create a new netlink request from proto and flags
+// Note the Len value will be inaccurate once data is added until
+// the message is serialized
+func NewNetlinkRequest(proto, flags int) *NetlinkRequest {
+ return &NetlinkRequest{
+ NlMsghdr: syscall.NlMsghdr{
+ Len: uint32(syscall.SizeofNlMsghdr),
+ Type: uint16(proto),
+ Flags: syscall.NLM_F_REQUEST | uint16(flags),
+ Seq: atomic.AddUint32(&nextSeqNr, 1),
+ },
+ }
+}
+
+type NetlinkSocket struct {
+ fd int
+ lsa syscall.SockaddrNetlink
+}
+
+func getNetlinkSocket(protocol int) (*NetlinkSocket, error) {
+ fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, protocol)
+ if err != nil {
+ return nil, err
+ }
+ s := &NetlinkSocket{
+ fd: fd,
+ }
+ s.lsa.Family = syscall.AF_NETLINK
+ if err := syscall.Bind(fd, &s.lsa); err != nil {
+ syscall.Close(fd)
+ return nil, err
+ }
+
+ return s, nil
+}
+
+// Create a netlink socket with a given protocol (e.g. NETLINK_ROUTE)
+// and subscribe it to multicast groups passed in variable argument list.
+// Returns the netlink socket on whic hReceive() method can be called
+// to retrieve the messages from the kernel.
+func Subscribe(protocol int, groups ...uint) (*NetlinkSocket, error) {
+ fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, protocol)
+ if err != nil {
+ return nil, err
+ }
+ s := &NetlinkSocket{
+ fd: fd,
+ }
+ s.lsa.Family = syscall.AF_NETLINK
+
+ for _, g := range groups {
+ s.lsa.Groups |= (1 << (g - 1))
+ }
+
+ if err := syscall.Bind(fd, &s.lsa); err != nil {
+ syscall.Close(fd)
+ return nil, err
+ }
+
+ return s, nil
+}
+
+func (s *NetlinkSocket) Close() {
+ syscall.Close(s.fd)
+}
+
+func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
+ if err := syscall.Sendto(s.fd, request.Serialize(), 0, &s.lsa); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (s *NetlinkSocket) Recieve() ([]syscall.NetlinkMessage, error) {
+ rb := make([]byte, syscall.Getpagesize())
+ nr, _, err := syscall.Recvfrom(s.fd, rb, 0)
+ if err != nil {
+ return nil, err
+ }
+ if nr < syscall.NLMSG_HDRLEN {
+ return nil, fmt.Errorf("Got short response from netlink")
+ }
+ rb = rb[:nr]
+ return syscall.ParseNetlinkMessage(rb)
+}
+
+func (s *NetlinkSocket) GetPid() (uint32, error) {
+ lsa, err := syscall.Getsockname(s.fd)
+ if err != nil {
+ return 0, err
+ }
+ switch v := lsa.(type) {
+ case *syscall.SockaddrNetlink:
+ return v.Pid, nil
+ }
+ return 0, fmt.Errorf("Wrong socket type")
+}
+
+func ZeroTerminated(s string) []byte {
+ bytes := make([]byte, len(s)+1)
+ for i := 0; i < len(s); i++ {
+ bytes[i] = s[i]
+ }
+ bytes[len(s)] = 0
+ return bytes
+}
+
+func NonZeroTerminated(s string) []byte {
+ bytes := make([]byte, len(s))
+ for i := 0; i < len(s); i++ {
+ bytes[i] = s[i]
+ }
+ return bytes
+}
+
+func BytesToString(b []byte) string {
+ n := bytes.Index(b, []byte{0})
+ return string(b[:n])
+}
+
+func Uint8Attr(v uint8) []byte {
+ return []byte{byte(v)}
+}
+
+func Uint16Attr(v uint16) []byte {
+ native := NativeEndian()
+ bytes := make([]byte, 2)
+ native.PutUint16(bytes, v)
+ return bytes
+}
+
+func Uint32Attr(v uint32) []byte {
+ native := NativeEndian()
+ bytes := make([]byte, 4)
+ native.PutUint32(bytes, v)
+ return bytes
+}
+
+func ParseRouteAttr(b []byte) ([]syscall.NetlinkRouteAttr, error) {
+ var attrs []syscall.NetlinkRouteAttr
+ for len(b) >= syscall.SizeofRtAttr {
+ a, vbuf, alen, err := netlinkRouteAttrAndValue(b)
+ if err != nil {
+ return nil, err
+ }
+ ra := syscall.NetlinkRouteAttr{Attr: *a, Value: vbuf[:int(a.Len)-syscall.SizeofRtAttr]}
+ attrs = append(attrs, ra)
+ b = b[alen:]
+ }
+ return attrs, nil
+}
+
+func netlinkRouteAttrAndValue(b []byte) (*syscall.RtAttr, []byte, int, error) {
+ a := (*syscall.RtAttr)(unsafe.Pointer(&b[0]))
+ if int(a.Len) < syscall.SizeofRtAttr || int(a.Len) > len(b) {
+ return nil, nil, 0, syscall.EINVAL
+ }
+ return a, b[syscall.SizeofRtAttr:], rtaAlignOf(int(a.Len)), nil
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/nl_linux_test.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/nl_linux_test.go
new file mode 100644
index 0000000..4672684
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/nl_linux_test.go
@@ -0,0 +1,60 @@
+package nl
+
+import (
+ "bytes"
+ "crypto/rand"
+ "encoding/binary"
+ "reflect"
+ "syscall"
+ "testing"
+)
+
+type testSerializer interface {
+ serializeSafe() []byte
+ Serialize() []byte
+}
+
+func testDeserializeSerialize(t *testing.T, orig []byte, safemsg testSerializer, msg testSerializer) {
+ if !reflect.DeepEqual(safemsg, msg) {
+ t.Fatal("Deserialization failed.\n", safemsg, "\n", msg)
+ }
+ safe := msg.serializeSafe()
+ if !bytes.Equal(safe, orig) {
+ t.Fatal("Safe serialization failed.\n", safe, "\n", orig)
+ }
+ b := msg.Serialize()
+ if !bytes.Equal(b, safe) {
+ t.Fatal("Serialization failed.\n", b, "\n", safe)
+ }
+}
+
+func (msg *IfInfomsg) write(b []byte) {
+ native := NativeEndian()
+ b[0] = msg.Family
+ b[1] = msg.X__ifi_pad
+ native.PutUint16(b[2:4], msg.Type)
+ native.PutUint32(b[4:8], uint32(msg.Index))
+ native.PutUint32(b[8:12], msg.Flags)
+ native.PutUint32(b[12:16], msg.Change)
+}
+
+func (msg *IfInfomsg) serializeSafe() []byte {
+ length := syscall.SizeofIfInfomsg
+ b := make([]byte, length)
+ msg.write(b)
+ return b
+}
+
+func deserializeIfInfomsgSafe(b []byte) *IfInfomsg {
+ var msg = IfInfomsg{}
+ binary.Read(bytes.NewReader(b[0:syscall.SizeofIfInfomsg]), NativeEndian(), &msg)
+ return &msg
+}
+
+func TestIfInfomsgDeserializeSerialize(t *testing.T) {
+ var orig = make([]byte, syscall.SizeofIfInfomsg)
+ rand.Read(orig)
+ safemsg := deserializeIfInfomsgSafe(orig)
+ msg := DeserializeIfInfomsg(orig)
+ testDeserializeSerialize(t, orig, safemsg, msg)
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/route_linux.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/route_linux.go
new file mode 100644
index 0000000..5dde998
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/route_linux.go
@@ -0,0 +1,33 @@
+package nl
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+type RtMsg struct {
+ syscall.RtMsg
+}
+
+func NewRtMsg() *RtMsg {
+ return &RtMsg{
+ RtMsg: syscall.RtMsg{
+ Table: syscall.RT_TABLE_MAIN,
+ Scope: syscall.RT_SCOPE_UNIVERSE,
+ Protocol: syscall.RTPROT_BOOT,
+ Type: syscall.RTN_UNICAST,
+ },
+ }
+}
+
+func (msg *RtMsg) Len() int {
+ return syscall.SizeofRtMsg
+}
+
+func DeserializeRtMsg(b []byte) *RtMsg {
+ return (*RtMsg)(unsafe.Pointer(&b[0:syscall.SizeofRtMsg][0]))
+}
+
+func (msg *RtMsg) Serialize() []byte {
+ return (*(*[syscall.SizeofRtMsg]byte)(unsafe.Pointer(msg)))[:]
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/route_linux_test.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/route_linux_test.go
new file mode 100644
index 0000000..ba9c410
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/route_linux_test.go
@@ -0,0 +1,43 @@
+package nl
+
+import (
+ "bytes"
+ "crypto/rand"
+ "encoding/binary"
+ "syscall"
+ "testing"
+)
+
+func (msg *RtMsg) write(b []byte) {
+ native := NativeEndian()
+ b[0] = msg.Family
+ b[1] = msg.Dst_len
+ b[2] = msg.Src_len
+ b[3] = msg.Tos
+ b[4] = msg.Table
+ b[5] = msg.Protocol
+ b[6] = msg.Scope
+ b[7] = msg.Type
+ native.PutUint32(b[8:12], msg.Flags)
+}
+
+func (msg *RtMsg) serializeSafe() []byte {
+ len := syscall.SizeofRtMsg
+ b := make([]byte, len)
+ msg.write(b)
+ return b
+}
+
+func deserializeRtMsgSafe(b []byte) *RtMsg {
+ var msg = RtMsg{}
+ binary.Read(bytes.NewReader(b[0:syscall.SizeofRtMsg]), NativeEndian(), &msg)
+ return &msg
+}
+
+func TestRtMsgDeserializeSerialize(t *testing.T) {
+ var orig = make([]byte, syscall.SizeofRtMsg)
+ rand.Read(orig)
+ safemsg := deserializeRtMsgSafe(orig)
+ msg := DeserializeRtMsg(orig)
+ testDeserializeSerialize(t, orig, safemsg, msg)
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_linux.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_linux.go
new file mode 100644
index 0000000..d953130
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_linux.go
@@ -0,0 +1,259 @@
+package nl
+
+import (
+ "bytes"
+ "net"
+ "unsafe"
+)
+
+// Infinity for packet and byte counts
+const (
+ XFRM_INF = ^uint64(0)
+)
+
+// Message Types
+const (
+ XFRM_MSG_BASE = 0x10
+ XFRM_MSG_NEWSA = 0x10
+ XFRM_MSG_DELSA = 0x11
+ XFRM_MSG_GETSA = 0x12
+ XFRM_MSG_NEWPOLICY = 0x13
+ XFRM_MSG_DELPOLICY = 0x14
+ XFRM_MSG_GETPOLICY = 0x15
+ XFRM_MSG_ALLOCSPI = 0x16
+ XFRM_MSG_ACQUIRE = 0x17
+ XFRM_MSG_EXPIRE = 0x18
+ XFRM_MSG_UPDPOLICY = 0x19
+ XFRM_MSG_UPDSA = 0x1a
+ XFRM_MSG_POLEXPIRE = 0x1b
+ XFRM_MSG_FLUSHSA = 0x1c
+ XFRM_MSG_FLUSHPOLICY = 0x1d
+ XFRM_MSG_NEWAE = 0x1e
+ XFRM_MSG_GETAE = 0x1f
+ XFRM_MSG_REPORT = 0x20
+ XFRM_MSG_MIGRATE = 0x21
+ XFRM_MSG_NEWSADINFO = 0x22
+ XFRM_MSG_GETSADINFO = 0x23
+ XFRM_MSG_NEWSPDINFO = 0x24
+ XFRM_MSG_GETSPDINFO = 0x25
+ XFRM_MSG_MAPPING = 0x26
+ XFRM_MSG_MAX = 0x26
+ XFRM_NR_MSGTYPES = 0x17
+)
+
+// Attribute types
+const (
+ /* Netlink message attributes. */
+ XFRMA_UNSPEC = 0x00
+ XFRMA_ALG_AUTH = 0x01 /* struct xfrm_algo */
+ XFRMA_ALG_CRYPT = 0x02 /* struct xfrm_algo */
+ XFRMA_ALG_COMP = 0x03 /* struct xfrm_algo */
+ XFRMA_ENCAP = 0x04 /* struct xfrm_algo + struct xfrm_encap_tmpl */
+ XFRMA_TMPL = 0x05 /* 1 or more struct xfrm_user_tmpl */
+ XFRMA_SA = 0x06 /* struct xfrm_usersa_info */
+ XFRMA_POLICY = 0x07 /* struct xfrm_userpolicy_info */
+ XFRMA_SEC_CTX = 0x08 /* struct xfrm_sec_ctx */
+ XFRMA_LTIME_VAL = 0x09
+ XFRMA_REPLAY_VAL = 0x0a
+ XFRMA_REPLAY_THRESH = 0x0b
+ XFRMA_ETIMER_THRESH = 0x0c
+ XFRMA_SRCADDR = 0x0d /* xfrm_address_t */
+ XFRMA_COADDR = 0x0e /* xfrm_address_t */
+ XFRMA_LASTUSED = 0x0f /* unsigned long */
+ XFRMA_POLICY_TYPE = 0x10 /* struct xfrm_userpolicy_type */
+ XFRMA_MIGRATE = 0x11
+ XFRMA_ALG_AEAD = 0x12 /* struct xfrm_algo_aead */
+ XFRMA_KMADDRESS = 0x13 /* struct xfrm_user_kmaddress */
+ XFRMA_ALG_AUTH_TRUNC = 0x14 /* struct xfrm_algo_auth */
+ XFRMA_MARK = 0x15 /* struct xfrm_mark */
+ XFRMA_TFCPAD = 0x16 /* __u32 */
+ XFRMA_REPLAY_ESN_VAL = 0x17 /* struct xfrm_replay_esn */
+ XFRMA_SA_EXTRA_FLAGS = 0x18 /* __u32 */
+ XFRMA_MAX = 0x18
+)
+
+const (
+ SizeofXfrmAddress = 0x10
+ SizeofXfrmSelector = 0x38
+ SizeofXfrmLifetimeCfg = 0x40
+ SizeofXfrmLifetimeCur = 0x20
+ SizeofXfrmId = 0x18
+)
+
+// typedef union {
+// __be32 a4;
+// __be32 a6[4];
+// } xfrm_address_t;
+
+type XfrmAddress [SizeofXfrmAddress]byte
+
+func (x *XfrmAddress) ToIP() net.IP {
+ var empty = [12]byte{}
+ ip := make(net.IP, net.IPv6len)
+ if bytes.Equal(x[4:16], empty[:]) {
+ ip[10] = 0xff
+ ip[11] = 0xff
+ copy(ip[12:16], x[0:4])
+ } else {
+ copy(ip[:], x[:])
+ }
+ return ip
+}
+
+func (x *XfrmAddress) ToIPNet(prefixlen uint8) *net.IPNet {
+ ip := x.ToIP()
+ if GetIPFamily(ip) == FAMILY_V4 {
+ return &net.IPNet{IP: ip, Mask: net.CIDRMask(int(prefixlen), 32)}
+ } else {
+ return &net.IPNet{IP: ip, Mask: net.CIDRMask(int(prefixlen), 128)}
+ }
+}
+
+func (x *XfrmAddress) FromIP(ip net.IP) {
+ var empty = [16]byte{}
+ if len(ip) < net.IPv4len {
+ copy(x[4:16], empty[:])
+ } else if GetIPFamily(ip) == FAMILY_V4 {
+ copy(x[0:4], ip.To4()[0:4])
+ copy(x[4:16], empty[:12])
+ } else {
+ copy(x[0:16], ip.To16()[0:16])
+ }
+}
+
+func DeserializeXfrmAddress(b []byte) *XfrmAddress {
+ return (*XfrmAddress)(unsafe.Pointer(&b[0:SizeofXfrmAddress][0]))
+}
+
+func (msg *XfrmAddress) Serialize() []byte {
+ return (*(*[SizeofXfrmAddress]byte)(unsafe.Pointer(msg)))[:]
+}
+
+// struct xfrm_selector {
+// xfrm_address_t daddr;
+// xfrm_address_t saddr;
+// __be16 dport;
+// __be16 dport_mask;
+// __be16 sport;
+// __be16 sport_mask;
+// __u16 family;
+// __u8 prefixlen_d;
+// __u8 prefixlen_s;
+// __u8 proto;
+// int ifindex;
+// __kernel_uid32_t user;
+// };
+
+type XfrmSelector struct {
+ Daddr XfrmAddress
+ Saddr XfrmAddress
+ Dport uint16 // big endian
+ DportMask uint16 // big endian
+ Sport uint16 // big endian
+ SportMask uint16 // big endian
+ Family uint16
+ PrefixlenD uint8
+ PrefixlenS uint8
+ Proto uint8
+ Pad [3]byte
+ Ifindex int32
+ User uint32
+}
+
+func (msg *XfrmSelector) Len() int {
+ return SizeofXfrmSelector
+}
+
+func DeserializeXfrmSelector(b []byte) *XfrmSelector {
+ return (*XfrmSelector)(unsafe.Pointer(&b[0:SizeofXfrmSelector][0]))
+}
+
+func (msg *XfrmSelector) Serialize() []byte {
+ return (*(*[SizeofXfrmSelector]byte)(unsafe.Pointer(msg)))[:]
+}
+
+// struct xfrm_lifetime_cfg {
+// __u64 soft_byte_limit;
+// __u64 hard_byte_limit;
+// __u64 soft_packet_limit;
+// __u64 hard_packet_limit;
+// __u64 soft_add_expires_seconds;
+// __u64 hard_add_expires_seconds;
+// __u64 soft_use_expires_seconds;
+// __u64 hard_use_expires_seconds;
+// };
+//
+
+type XfrmLifetimeCfg struct {
+ SoftByteLimit uint64
+ HardByteLimit uint64
+ SoftPacketLimit uint64
+ HardPacketLimit uint64
+ SoftAddExpiresSeconds uint64
+ HardAddExpiresSeconds uint64
+ SoftUseExpiresSeconds uint64
+ HardUseExpiresSeconds uint64
+}
+
+func (msg *XfrmLifetimeCfg) Len() int {
+ return SizeofXfrmLifetimeCfg
+}
+
+func DeserializeXfrmLifetimeCfg(b []byte) *XfrmLifetimeCfg {
+ return (*XfrmLifetimeCfg)(unsafe.Pointer(&b[0:SizeofXfrmLifetimeCfg][0]))
+}
+
+func (msg *XfrmLifetimeCfg) Serialize() []byte {
+ return (*(*[SizeofXfrmLifetimeCfg]byte)(unsafe.Pointer(msg)))[:]
+}
+
+// struct xfrm_lifetime_cur {
+// __u64 bytes;
+// __u64 packets;
+// __u64 add_time;
+// __u64 use_time;
+// };
+
+type XfrmLifetimeCur struct {
+ Bytes uint64
+ Packets uint64
+ AddTime uint64
+ UseTime uint64
+}
+
+func (msg *XfrmLifetimeCur) Len() int {
+ return SizeofXfrmLifetimeCur
+}
+
+func DeserializeXfrmLifetimeCur(b []byte) *XfrmLifetimeCur {
+ return (*XfrmLifetimeCur)(unsafe.Pointer(&b[0:SizeofXfrmLifetimeCur][0]))
+}
+
+func (msg *XfrmLifetimeCur) Serialize() []byte {
+ return (*(*[SizeofXfrmLifetimeCur]byte)(unsafe.Pointer(msg)))[:]
+}
+
+// struct xfrm_id {
+// xfrm_address_t daddr;
+// __be32 spi;
+// __u8 proto;
+// };
+
+type XfrmId struct {
+ Daddr XfrmAddress
+ Spi uint32 // big endian
+ Proto uint8
+ Pad [3]byte
+}
+
+func (msg *XfrmId) Len() int {
+ return SizeofXfrmId
+}
+
+func DeserializeXfrmId(b []byte) *XfrmId {
+ return (*XfrmId)(unsafe.Pointer(&b[0:SizeofXfrmId][0]))
+}
+
+func (msg *XfrmId) Serialize() []byte {
+ return (*(*[SizeofXfrmId]byte)(unsafe.Pointer(msg)))[:]
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_linux_test.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_linux_test.go
new file mode 100644
index 0000000..04404d7
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_linux_test.go
@@ -0,0 +1,161 @@
+package nl
+
+import (
+ "bytes"
+ "crypto/rand"
+ "encoding/binary"
+ "testing"
+)
+
+func (msg *XfrmAddress) write(b []byte) {
+ copy(b[0:SizeofXfrmAddress], msg[:])
+}
+
+func (msg *XfrmAddress) serializeSafe() []byte {
+ b := make([]byte, SizeofXfrmAddress)
+ msg.write(b)
+ return b
+}
+
+func deserializeXfrmAddressSafe(b []byte) *XfrmAddress {
+ var msg = XfrmAddress{}
+ binary.Read(bytes.NewReader(b[0:SizeofXfrmAddress]), NativeEndian(), &msg)
+ return &msg
+}
+
+func TestXfrmAddressDeserializeSerialize(t *testing.T) {
+ var orig = make([]byte, SizeofXfrmAddress)
+ rand.Read(orig)
+ safemsg := deserializeXfrmAddressSafe(orig)
+ msg := DeserializeXfrmAddress(orig)
+ testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmSelector) write(b []byte) {
+ const AddrEnd = SizeofXfrmAddress * 2
+ native := NativeEndian()
+ msg.Daddr.write(b[0:SizeofXfrmAddress])
+ msg.Saddr.write(b[SizeofXfrmAddress:AddrEnd])
+ native.PutUint16(b[AddrEnd:AddrEnd+2], msg.Dport)
+ native.PutUint16(b[AddrEnd+2:AddrEnd+4], msg.DportMask)
+ native.PutUint16(b[AddrEnd+4:AddrEnd+6], msg.Sport)
+ native.PutUint16(b[AddrEnd+6:AddrEnd+8], msg.SportMask)
+ native.PutUint16(b[AddrEnd+8:AddrEnd+10], msg.Family)
+ b[AddrEnd+10] = msg.PrefixlenD
+ b[AddrEnd+11] = msg.PrefixlenS
+ b[AddrEnd+12] = msg.Proto
+ copy(b[AddrEnd+13:AddrEnd+16], msg.Pad[:])
+ native.PutUint32(b[AddrEnd+16:AddrEnd+20], uint32(msg.Ifindex))
+ native.PutUint32(b[AddrEnd+20:AddrEnd+24], msg.User)
+}
+
+func (msg *XfrmSelector) serializeSafe() []byte {
+ length := SizeofXfrmSelector
+ b := make([]byte, length)
+ msg.write(b)
+ return b
+}
+
+func deserializeXfrmSelectorSafe(b []byte) *XfrmSelector {
+ var msg = XfrmSelector{}
+ binary.Read(bytes.NewReader(b[0:SizeofXfrmSelector]), NativeEndian(), &msg)
+ return &msg
+}
+
+func TestXfrmSelectorDeserializeSerialize(t *testing.T) {
+ var orig = make([]byte, SizeofXfrmSelector)
+ rand.Read(orig)
+ safemsg := deserializeXfrmSelectorSafe(orig)
+ msg := DeserializeXfrmSelector(orig)
+ testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmLifetimeCfg) write(b []byte) {
+ native := NativeEndian()
+ native.PutUint64(b[0:8], msg.SoftByteLimit)
+ native.PutUint64(b[8:16], msg.HardByteLimit)
+ native.PutUint64(b[16:24], msg.SoftPacketLimit)
+ native.PutUint64(b[24:32], msg.HardPacketLimit)
+ native.PutUint64(b[32:40], msg.SoftAddExpiresSeconds)
+ native.PutUint64(b[40:48], msg.HardAddExpiresSeconds)
+ native.PutUint64(b[48:56], msg.SoftUseExpiresSeconds)
+ native.PutUint64(b[56:64], msg.HardUseExpiresSeconds)
+}
+
+func (msg *XfrmLifetimeCfg) serializeSafe() []byte {
+ length := SizeofXfrmLifetimeCfg
+ b := make([]byte, length)
+ msg.write(b)
+ return b
+}
+
+func deserializeXfrmLifetimeCfgSafe(b []byte) *XfrmLifetimeCfg {
+ var msg = XfrmLifetimeCfg{}
+ binary.Read(bytes.NewReader(b[0:SizeofXfrmLifetimeCfg]), NativeEndian(), &msg)
+ return &msg
+}
+
+func TestXfrmLifetimeCfgDeserializeSerialize(t *testing.T) {
+ var orig = make([]byte, SizeofXfrmLifetimeCfg)
+ rand.Read(orig)
+ safemsg := deserializeXfrmLifetimeCfgSafe(orig)
+ msg := DeserializeXfrmLifetimeCfg(orig)
+ testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmLifetimeCur) write(b []byte) {
+ native := NativeEndian()
+ native.PutUint64(b[0:8], msg.Bytes)
+ native.PutUint64(b[8:16], msg.Packets)
+ native.PutUint64(b[16:24], msg.AddTime)
+ native.PutUint64(b[24:32], msg.UseTime)
+}
+
+func (msg *XfrmLifetimeCur) serializeSafe() []byte {
+ length := SizeofXfrmLifetimeCur
+ b := make([]byte, length)
+ msg.write(b)
+ return b
+}
+
+func deserializeXfrmLifetimeCurSafe(b []byte) *XfrmLifetimeCur {
+ var msg = XfrmLifetimeCur{}
+ binary.Read(bytes.NewReader(b[0:SizeofXfrmLifetimeCur]), NativeEndian(), &msg)
+ return &msg
+}
+
+func TestXfrmLifetimeCurDeserializeSerialize(t *testing.T) {
+ var orig = make([]byte, SizeofXfrmLifetimeCur)
+ rand.Read(orig)
+ safemsg := deserializeXfrmLifetimeCurSafe(orig)
+ msg := DeserializeXfrmLifetimeCur(orig)
+ testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmId) write(b []byte) {
+ native := NativeEndian()
+ msg.Daddr.write(b[0:SizeofXfrmAddress])
+ native.PutUint32(b[SizeofXfrmAddress:SizeofXfrmAddress+4], msg.Spi)
+ b[SizeofXfrmAddress+4] = msg.Proto
+ copy(b[SizeofXfrmAddress+5:SizeofXfrmAddress+8], msg.Pad[:])
+}
+
+func (msg *XfrmId) serializeSafe() []byte {
+ b := make([]byte, SizeofXfrmId)
+ msg.write(b)
+ return b
+}
+
+func deserializeXfrmIdSafe(b []byte) *XfrmId {
+ var msg = XfrmId{}
+ binary.Read(bytes.NewReader(b[0:SizeofXfrmId]), NativeEndian(), &msg)
+ return &msg
+}
+
+func TestXfrmIdDeserializeSerialize(t *testing.T) {
+ var orig = make([]byte, SizeofXfrmId)
+ rand.Read(orig)
+ safemsg := deserializeXfrmIdSafe(orig)
+ msg := DeserializeXfrmId(orig)
+ testDeserializeSerialize(t, orig, safemsg, msg)
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_policy_linux.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_policy_linux.go
new file mode 100644
index 0000000..66f7e03
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_policy_linux.go
@@ -0,0 +1,119 @@
+package nl
+
+import (
+ "unsafe"
+)
+
+const (
+ SizeofXfrmUserpolicyId = 0x40
+ SizeofXfrmUserpolicyInfo = 0xa8
+ SizeofXfrmUserTmpl = 0x40
+)
+
+// struct xfrm_userpolicy_id {
+// struct xfrm_selector sel;
+// __u32 index;
+// __u8 dir;
+// };
+//
+
+type XfrmUserpolicyId struct {
+ Sel XfrmSelector
+ Index uint32
+ Dir uint8
+ Pad [3]byte
+}
+
+func (msg *XfrmUserpolicyId) Len() int {
+ return SizeofXfrmUserpolicyId
+}
+
+func DeserializeXfrmUserpolicyId(b []byte) *XfrmUserpolicyId {
+ return (*XfrmUserpolicyId)(unsafe.Pointer(&b[0:SizeofXfrmUserpolicyId][0]))
+}
+
+func (msg *XfrmUserpolicyId) Serialize() []byte {
+ return (*(*[SizeofXfrmUserpolicyId]byte)(unsafe.Pointer(msg)))[:]
+}
+
+// struct xfrm_userpolicy_info {
+// struct xfrm_selector sel;
+// struct xfrm_lifetime_cfg lft;
+// struct xfrm_lifetime_cur curlft;
+// __u32 priority;
+// __u32 index;
+// __u8 dir;
+// __u8 action;
+// #define XFRM_POLICY_ALLOW 0
+// #define XFRM_POLICY_BLOCK 1
+// __u8 flags;
+// #define XFRM_POLICY_LOCALOK 1 /* Allow user to override global policy */
+// /* Automatically expand selector to include matching ICMP payloads. */
+// #define XFRM_POLICY_ICMP 2
+// __u8 share;
+// };
+
+type XfrmUserpolicyInfo struct {
+ Sel XfrmSelector
+ Lft XfrmLifetimeCfg
+ Curlft XfrmLifetimeCur
+ Priority uint32
+ Index uint32
+ Dir uint8
+ Action uint8
+ Flags uint8
+ Share uint8
+ Pad [4]byte
+}
+
+func (msg *XfrmUserpolicyInfo) Len() int {
+ return SizeofXfrmUserpolicyInfo
+}
+
+func DeserializeXfrmUserpolicyInfo(b []byte) *XfrmUserpolicyInfo {
+ return (*XfrmUserpolicyInfo)(unsafe.Pointer(&b[0:SizeofXfrmUserpolicyInfo][0]))
+}
+
+func (msg *XfrmUserpolicyInfo) Serialize() []byte {
+ return (*(*[SizeofXfrmUserpolicyInfo]byte)(unsafe.Pointer(msg)))[:]
+}
+
+// struct xfrm_user_tmpl {
+// struct xfrm_id id;
+// __u16 family;
+// xfrm_address_t saddr;
+// __u32 reqid;
+// __u8 mode;
+// __u8 share;
+// __u8 optional;
+// __u32 aalgos;
+// __u32 ealgos;
+// __u32 calgos;
+// }
+
+type XfrmUserTmpl struct {
+ XfrmId XfrmId
+ Family uint16
+ Pad1 [2]byte
+ Saddr XfrmAddress
+ Reqid uint32
+ Mode uint8
+ Share uint8
+ Optional uint8
+ Pad2 byte
+ Aalgos uint32
+ Ealgos uint32
+ Calgos uint32
+}
+
+func (msg *XfrmUserTmpl) Len() int {
+ return SizeofXfrmUserTmpl
+}
+
+func DeserializeXfrmUserTmpl(b []byte) *XfrmUserTmpl {
+ return (*XfrmUserTmpl)(unsafe.Pointer(&b[0:SizeofXfrmUserTmpl][0]))
+}
+
+func (msg *XfrmUserTmpl) Serialize() []byte {
+ return (*(*[SizeofXfrmUserTmpl]byte)(unsafe.Pointer(msg)))[:]
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_policy_linux_test.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_policy_linux_test.go
new file mode 100644
index 0000000..08a604b
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_policy_linux_test.go
@@ -0,0 +1,109 @@
+package nl
+
+import (
+ "bytes"
+ "crypto/rand"
+ "encoding/binary"
+ "testing"
+)
+
+func (msg *XfrmUserpolicyId) write(b []byte) {
+ native := NativeEndian()
+ msg.Sel.write(b[0:SizeofXfrmSelector])
+ native.PutUint32(b[SizeofXfrmSelector:SizeofXfrmSelector+4], msg.Index)
+ b[SizeofXfrmSelector+4] = msg.Dir
+ copy(b[SizeofXfrmSelector+5:SizeofXfrmSelector+8], msg.Pad[:])
+}
+
+func (msg *XfrmUserpolicyId) serializeSafe() []byte {
+ b := make([]byte, SizeofXfrmUserpolicyId)
+ msg.write(b)
+ return b
+}
+
+func deserializeXfrmUserpolicyIdSafe(b []byte) *XfrmUserpolicyId {
+ var msg = XfrmUserpolicyId{}
+ binary.Read(bytes.NewReader(b[0:SizeofXfrmUserpolicyId]), NativeEndian(), &msg)
+ return &msg
+}
+
+func TestXfrmUserpolicyIdDeserializeSerialize(t *testing.T) {
+ var orig = make([]byte, SizeofXfrmUserpolicyId)
+ rand.Read(orig)
+ safemsg := deserializeXfrmUserpolicyIdSafe(orig)
+ msg := DeserializeXfrmUserpolicyId(orig)
+ testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmUserpolicyInfo) write(b []byte) {
+ const CfgEnd = SizeofXfrmSelector + SizeofXfrmLifetimeCfg
+ const CurEnd = CfgEnd + SizeofXfrmLifetimeCur
+ native := NativeEndian()
+ msg.Sel.write(b[0:SizeofXfrmSelector])
+ msg.Lft.write(b[SizeofXfrmSelector:CfgEnd])
+ msg.Curlft.write(b[CfgEnd:CurEnd])
+ native.PutUint32(b[CurEnd:CurEnd+4], msg.Priority)
+ native.PutUint32(b[CurEnd+4:CurEnd+8], msg.Index)
+ b[CurEnd+8] = msg.Dir
+ b[CurEnd+9] = msg.Action
+ b[CurEnd+10] = msg.Flags
+ b[CurEnd+11] = msg.Share
+ copy(b[CurEnd+12:CurEnd+16], msg.Pad[:])
+}
+
+func (msg *XfrmUserpolicyInfo) serializeSafe() []byte {
+ b := make([]byte, SizeofXfrmUserpolicyInfo)
+ msg.write(b)
+ return b
+}
+
+func deserializeXfrmUserpolicyInfoSafe(b []byte) *XfrmUserpolicyInfo {
+ var msg = XfrmUserpolicyInfo{}
+ binary.Read(bytes.NewReader(b[0:SizeofXfrmUserpolicyInfo]), NativeEndian(), &msg)
+ return &msg
+}
+
+func TestXfrmUserpolicyInfoDeserializeSerialize(t *testing.T) {
+ var orig = make([]byte, SizeofXfrmUserpolicyInfo)
+ rand.Read(orig)
+ safemsg := deserializeXfrmUserpolicyInfoSafe(orig)
+ msg := DeserializeXfrmUserpolicyInfo(orig)
+ testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmUserTmpl) write(b []byte) {
+ const AddrEnd = SizeofXfrmId + 4 + SizeofXfrmAddress
+ native := NativeEndian()
+ msg.XfrmId.write(b[0:SizeofXfrmId])
+ native.PutUint16(b[SizeofXfrmId:SizeofXfrmId+2], msg.Family)
+ copy(b[SizeofXfrmId+2:SizeofXfrmId+4], msg.Pad1[:])
+ msg.Saddr.write(b[SizeofXfrmId+4 : AddrEnd])
+ native.PutUint32(b[AddrEnd:AddrEnd+4], msg.Reqid)
+ b[AddrEnd+4] = msg.Mode
+ b[AddrEnd+5] = msg.Share
+ b[AddrEnd+6] = msg.Optional
+ b[AddrEnd+7] = msg.Pad2
+ native.PutUint32(b[AddrEnd+8:AddrEnd+12], msg.Aalgos)
+ native.PutUint32(b[AddrEnd+12:AddrEnd+16], msg.Ealgos)
+ native.PutUint32(b[AddrEnd+16:AddrEnd+20], msg.Calgos)
+}
+
+func (msg *XfrmUserTmpl) serializeSafe() []byte {
+ b := make([]byte, SizeofXfrmUserTmpl)
+ msg.write(b)
+ return b
+}
+
+func deserializeXfrmUserTmplSafe(b []byte) *XfrmUserTmpl {
+ var msg = XfrmUserTmpl{}
+ binary.Read(bytes.NewReader(b[0:SizeofXfrmUserTmpl]), NativeEndian(), &msg)
+ return &msg
+}
+
+func TestXfrmUserTmplDeserializeSerialize(t *testing.T) {
+ var orig = make([]byte, SizeofXfrmUserTmpl)
+ rand.Read(orig)
+ safemsg := deserializeXfrmUserTmplSafe(orig)
+ msg := DeserializeXfrmUserTmpl(orig)
+ testDeserializeSerialize(t, orig, safemsg, msg)
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_state_linux.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_state_linux.go
new file mode 100644
index 0000000..4876ce4
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_state_linux.go
@@ -0,0 +1,221 @@
+package nl
+
+import (
+ "unsafe"
+)
+
+const (
+ SizeofXfrmUsersaId = 0x18
+ SizeofXfrmStats = 0x0c
+ SizeofXfrmUsersaInfo = 0xe0
+ SizeofXfrmAlgo = 0x44
+ SizeofXfrmAlgoAuth = 0x48
+ SizeofXfrmEncapTmpl = 0x18
+)
+
+// struct xfrm_usersa_id {
+// xfrm_address_t daddr;
+// __be32 spi;
+// __u16 family;
+// __u8 proto;
+// };
+
+type XfrmUsersaId struct {
+ Daddr XfrmAddress
+ Spi uint32 // big endian
+ Family uint16
+ Proto uint8
+ Pad byte
+}
+
+func (msg *XfrmUsersaId) Len() int {
+ return SizeofXfrmUsersaId
+}
+
+func DeserializeXfrmUsersaId(b []byte) *XfrmUsersaId {
+ return (*XfrmUsersaId)(unsafe.Pointer(&b[0:SizeofXfrmUsersaId][0]))
+}
+
+func (msg *XfrmUsersaId) Serialize() []byte {
+ return (*(*[SizeofXfrmUsersaId]byte)(unsafe.Pointer(msg)))[:]
+}
+
+// struct xfrm_stats {
+// __u32 replay_window;
+// __u32 replay;
+// __u32 integrity_failed;
+// };
+
+type XfrmStats struct {
+ ReplayWindow uint32
+ Replay uint32
+ IntegrityFailed uint32
+}
+
+func (msg *XfrmStats) Len() int {
+ return SizeofXfrmStats
+}
+
+func DeserializeXfrmStats(b []byte) *XfrmStats {
+ return (*XfrmStats)(unsafe.Pointer(&b[0:SizeofXfrmStats][0]))
+}
+
+func (msg *XfrmStats) Serialize() []byte {
+ return (*(*[SizeofXfrmStats]byte)(unsafe.Pointer(msg)))[:]
+}
+
+// struct xfrm_usersa_info {
+// struct xfrm_selector sel;
+// struct xfrm_id id;
+// xfrm_address_t saddr;
+// struct xfrm_lifetime_cfg lft;
+// struct xfrm_lifetime_cur curlft;
+// struct xfrm_stats stats;
+// __u32 seq;
+// __u32 reqid;
+// __u16 family;
+// __u8 mode; /* XFRM_MODE_xxx */
+// __u8 replay_window;
+// __u8 flags;
+// #define XFRM_STATE_NOECN 1
+// #define XFRM_STATE_DECAP_DSCP 2
+// #define XFRM_STATE_NOPMTUDISC 4
+// #define XFRM_STATE_WILDRECV 8
+// #define XFRM_STATE_ICMP 16
+// #define XFRM_STATE_AF_UNSPEC 32
+// #define XFRM_STATE_ALIGN4 64
+// #define XFRM_STATE_ESN 128
+// };
+//
+// #define XFRM_SA_XFLAG_DONT_ENCAP_DSCP 1
+//
+
+type XfrmUsersaInfo struct {
+ Sel XfrmSelector
+ Id XfrmId
+ Saddr XfrmAddress
+ Lft XfrmLifetimeCfg
+ Curlft XfrmLifetimeCur
+ Stats XfrmStats
+ Seq uint32
+ Reqid uint32
+ Family uint16
+ Mode uint8
+ ReplayWindow uint8
+ Flags uint8
+ Pad [7]byte
+}
+
+func (msg *XfrmUsersaInfo) Len() int {
+ return SizeofXfrmUsersaInfo
+}
+
+func DeserializeXfrmUsersaInfo(b []byte) *XfrmUsersaInfo {
+ return (*XfrmUsersaInfo)(unsafe.Pointer(&b[0:SizeofXfrmUsersaInfo][0]))
+}
+
+func (msg *XfrmUsersaInfo) Serialize() []byte {
+ return (*(*[SizeofXfrmUsersaInfo]byte)(unsafe.Pointer(msg)))[:]
+}
+
+// struct xfrm_algo {
+// char alg_name[64];
+// unsigned int alg_key_len; /* in bits */
+// char alg_key[0];
+// };
+
+type XfrmAlgo struct {
+ AlgName [64]byte
+ AlgKeyLen uint32
+ AlgKey []byte
+}
+
+func (msg *XfrmAlgo) Len() int {
+ return SizeofXfrmAlgo + int(msg.AlgKeyLen/8)
+}
+
+func DeserializeXfrmAlgo(b []byte) *XfrmAlgo {
+ ret := XfrmAlgo{}
+ copy(ret.AlgName[:], b[0:64])
+ ret.AlgKeyLen = *(*uint32)(unsafe.Pointer(&b[64]))
+ ret.AlgKey = b[68:ret.Len()]
+ return &ret
+}
+
+func (msg *XfrmAlgo) Serialize() []byte {
+ b := make([]byte, msg.Len())
+ copy(b[0:64], msg.AlgName[:])
+ copy(b[64:68], (*(*[4]byte)(unsafe.Pointer(&msg.AlgKeyLen)))[:])
+ copy(b[68:msg.Len()], msg.AlgKey[:])
+ return b
+}
+
+// struct xfrm_algo_auth {
+// char alg_name[64];
+// unsigned int alg_key_len; /* in bits */
+// unsigned int alg_trunc_len; /* in bits */
+// char alg_key[0];
+// };
+
+type XfrmAlgoAuth struct {
+ AlgName [64]byte
+ AlgKeyLen uint32
+ AlgTruncLen uint32
+ AlgKey []byte
+}
+
+func (msg *XfrmAlgoAuth) Len() int {
+ return SizeofXfrmAlgoAuth + int(msg.AlgKeyLen/8)
+}
+
+func DeserializeXfrmAlgoAuth(b []byte) *XfrmAlgoAuth {
+ ret := XfrmAlgoAuth{}
+ copy(ret.AlgName[:], b[0:64])
+ ret.AlgKeyLen = *(*uint32)(unsafe.Pointer(&b[64]))
+ ret.AlgTruncLen = *(*uint32)(unsafe.Pointer(&b[68]))
+ ret.AlgKey = b[72:ret.Len()]
+ return &ret
+}
+
+func (msg *XfrmAlgoAuth) Serialize() []byte {
+ b := make([]byte, msg.Len())
+ copy(b[0:64], msg.AlgName[:])
+ copy(b[64:68], (*(*[4]byte)(unsafe.Pointer(&msg.AlgKeyLen)))[:])
+ copy(b[68:72], (*(*[4]byte)(unsafe.Pointer(&msg.AlgTruncLen)))[:])
+ copy(b[72:msg.Len()], msg.AlgKey[:])
+ return b
+}
+
+// struct xfrm_algo_aead {
+// char alg_name[64];
+// unsigned int alg_key_len; /* in bits */
+// unsigned int alg_icv_len; /* in bits */
+// char alg_key[0];
+// }
+
+// struct xfrm_encap_tmpl {
+// __u16 encap_type;
+// __be16 encap_sport;
+// __be16 encap_dport;
+// xfrm_address_t encap_oa;
+// };
+
+type XfrmEncapTmpl struct {
+ EncapType uint16
+ EncapSport uint16 // big endian
+ EncapDport uint16 // big endian
+ Pad [2]byte
+ EncapOa XfrmAddress
+}
+
+func (msg *XfrmEncapTmpl) Len() int {
+ return SizeofXfrmEncapTmpl
+}
+
+func DeserializeXfrmEncapTmpl(b []byte) *XfrmEncapTmpl {
+ return (*XfrmEncapTmpl)(unsafe.Pointer(&b[0:SizeofXfrmEncapTmpl][0]))
+}
+
+func (msg *XfrmEncapTmpl) Serialize() []byte {
+ return (*(*[SizeofXfrmEncapTmpl]byte)(unsafe.Pointer(msg)))[:]
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_state_linux_test.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_state_linux_test.go
new file mode 100644
index 0000000..d5281e9
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_state_linux_test.go
@@ -0,0 +1,207 @@
+package nl
+
+import (
+ "bytes"
+ "crypto/rand"
+ "encoding/binary"
+ "testing"
+)
+
+func (msg *XfrmUsersaId) write(b []byte) {
+ native := NativeEndian()
+ msg.Daddr.write(b[0:SizeofXfrmAddress])
+ native.PutUint32(b[SizeofXfrmAddress:SizeofXfrmAddress+4], msg.Spi)
+ native.PutUint16(b[SizeofXfrmAddress+4:SizeofXfrmAddress+6], msg.Family)
+ b[SizeofXfrmAddress+6] = msg.Proto
+ b[SizeofXfrmAddress+7] = msg.Pad
+}
+
+func (msg *XfrmUsersaId) serializeSafe() []byte {
+ b := make([]byte, SizeofXfrmUsersaId)
+ msg.write(b)
+ return b
+}
+
+func deserializeXfrmUsersaIdSafe(b []byte) *XfrmUsersaId {
+ var msg = XfrmUsersaId{}
+ binary.Read(bytes.NewReader(b[0:SizeofXfrmUsersaId]), NativeEndian(), &msg)
+ return &msg
+}
+
+func TestXfrmUsersaIdDeserializeSerialize(t *testing.T) {
+ var orig = make([]byte, SizeofXfrmUsersaId)
+ rand.Read(orig)
+ safemsg := deserializeXfrmUsersaIdSafe(orig)
+ msg := DeserializeXfrmUsersaId(orig)
+ testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmStats) write(b []byte) {
+ native := NativeEndian()
+ native.PutUint32(b[0:4], msg.ReplayWindow)
+ native.PutUint32(b[4:8], msg.Replay)
+ native.PutUint32(b[8:12], msg.IntegrityFailed)
+}
+
+func (msg *XfrmStats) serializeSafe() []byte {
+ b := make([]byte, SizeofXfrmStats)
+ msg.write(b)
+ return b
+}
+
+func deserializeXfrmStatsSafe(b []byte) *XfrmStats {
+ var msg = XfrmStats{}
+ binary.Read(bytes.NewReader(b[0:SizeofXfrmStats]), NativeEndian(), &msg)
+ return &msg
+}
+
+func TestXfrmStatsDeserializeSerialize(t *testing.T) {
+ var orig = make([]byte, SizeofXfrmStats)
+ rand.Read(orig)
+ safemsg := deserializeXfrmStatsSafe(orig)
+ msg := DeserializeXfrmStats(orig)
+ testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmUsersaInfo) write(b []byte) {
+ const IdEnd = SizeofXfrmSelector + SizeofXfrmId
+ const AddressEnd = IdEnd + SizeofXfrmAddress
+ const CfgEnd = AddressEnd + SizeofXfrmLifetimeCfg
+ const CurEnd = CfgEnd + SizeofXfrmLifetimeCur
+ const StatsEnd = CurEnd + SizeofXfrmStats
+ native := NativeEndian()
+ msg.Sel.write(b[0:SizeofXfrmSelector])
+ msg.Id.write(b[SizeofXfrmSelector:IdEnd])
+ msg.Saddr.write(b[IdEnd:AddressEnd])
+ msg.Lft.write(b[AddressEnd:CfgEnd])
+ msg.Curlft.write(b[CfgEnd:CurEnd])
+ msg.Stats.write(b[CurEnd:StatsEnd])
+ native.PutUint32(b[StatsEnd:StatsEnd+4], msg.Seq)
+ native.PutUint32(b[StatsEnd+4:StatsEnd+8], msg.Reqid)
+ native.PutUint16(b[StatsEnd+8:StatsEnd+10], msg.Family)
+ b[StatsEnd+10] = msg.Mode
+ b[StatsEnd+11] = msg.ReplayWindow
+ b[StatsEnd+12] = msg.Flags
+ copy(b[StatsEnd+13:StatsEnd+20], msg.Pad[:])
+}
+
+func (msg *XfrmUsersaInfo) serializeSafe() []byte {
+ b := make([]byte, SizeofXfrmUsersaInfo)
+ msg.write(b)
+ return b
+}
+
+func deserializeXfrmUsersaInfoSafe(b []byte) *XfrmUsersaInfo {
+ var msg = XfrmUsersaInfo{}
+ binary.Read(bytes.NewReader(b[0:SizeofXfrmUsersaInfo]), NativeEndian(), &msg)
+ return &msg
+}
+
+func TestXfrmUsersaInfoDeserializeSerialize(t *testing.T) {
+ var orig = make([]byte, SizeofXfrmUsersaInfo)
+ rand.Read(orig)
+ safemsg := deserializeXfrmUsersaInfoSafe(orig)
+ msg := DeserializeXfrmUsersaInfo(orig)
+ testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmAlgo) write(b []byte) {
+ native := NativeEndian()
+ copy(b[0:64], msg.AlgName[:])
+ native.PutUint32(b[64:68], msg.AlgKeyLen)
+ copy(b[68:msg.Len()], msg.AlgKey[:])
+}
+
+func (msg *XfrmAlgo) serializeSafe() []byte {
+ b := make([]byte, msg.Len())
+ msg.write(b)
+ return b
+}
+
+func deserializeXfrmAlgoSafe(b []byte) *XfrmAlgo {
+ var msg = XfrmAlgo{}
+ copy(msg.AlgName[:], b[0:64])
+ binary.Read(bytes.NewReader(b[64:68]), NativeEndian(), &msg.AlgKeyLen)
+ msg.AlgKey = b[68:msg.Len()]
+ return &msg
+}
+
+func TestXfrmAlgoDeserializeSerialize(t *testing.T) {
+ // use a 32 byte key len
+ var orig = make([]byte, SizeofXfrmAlgo+32)
+ rand.Read(orig)
+ // set the key len to 256 bits
+ orig[64] = 0
+ orig[65] = 1
+ orig[66] = 0
+ orig[67] = 0
+ safemsg := deserializeXfrmAlgoSafe(orig)
+ msg := DeserializeXfrmAlgo(orig)
+ testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmAlgoAuth) write(b []byte) {
+ native := NativeEndian()
+ copy(b[0:64], msg.AlgName[:])
+ native.PutUint32(b[64:68], msg.AlgKeyLen)
+ native.PutUint32(b[68:72], msg.AlgTruncLen)
+ copy(b[72:msg.Len()], msg.AlgKey[:])
+}
+
+func (msg *XfrmAlgoAuth) serializeSafe() []byte {
+ b := make([]byte, msg.Len())
+ msg.write(b)
+ return b
+}
+
+func deserializeXfrmAlgoAuthSafe(b []byte) *XfrmAlgoAuth {
+ var msg = XfrmAlgoAuth{}
+ copy(msg.AlgName[:], b[0:64])
+ binary.Read(bytes.NewReader(b[64:68]), NativeEndian(), &msg.AlgKeyLen)
+ binary.Read(bytes.NewReader(b[68:72]), NativeEndian(), &msg.AlgTruncLen)
+ msg.AlgKey = b[72:msg.Len()]
+ return &msg
+}
+
+func TestXfrmAlgoAuthDeserializeSerialize(t *testing.T) {
+ // use a 32 byte key len
+ var orig = make([]byte, SizeofXfrmAlgoAuth+32)
+ rand.Read(orig)
+ // set the key len to 256 bits
+ orig[64] = 0
+ orig[65] = 1
+ orig[66] = 0
+ orig[67] = 0
+ safemsg := deserializeXfrmAlgoAuthSafe(orig)
+ msg := DeserializeXfrmAlgoAuth(orig)
+ testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmEncapTmpl) write(b []byte) {
+ native := NativeEndian()
+ native.PutUint16(b[0:2], msg.EncapType)
+ native.PutUint16(b[2:4], msg.EncapSport)
+ native.PutUint16(b[4:6], msg.EncapDport)
+ copy(b[6:8], msg.Pad[:])
+ msg.EncapOa.write(b[8:SizeofXfrmAddress])
+}
+
+func (msg *XfrmEncapTmpl) serializeSafe() []byte {
+ b := make([]byte, SizeofXfrmEncapTmpl)
+ msg.write(b)
+ return b
+}
+
+func deserializeXfrmEncapTmplSafe(b []byte) *XfrmEncapTmpl {
+ var msg = XfrmEncapTmpl{}
+ binary.Read(bytes.NewReader(b[0:SizeofXfrmEncapTmpl]), NativeEndian(), &msg)
+ return &msg
+}
+
+func TestXfrmEncapTmplDeserializeSerialize(t *testing.T) {
+ var orig = make([]byte, SizeofXfrmEncapTmpl)
+ rand.Read(orig)
+ safemsg := deserializeXfrmEncapTmplSafe(orig)
+ msg := DeserializeXfrmEncapTmpl(orig)
+ testDeserializeSerialize(t, orig, safemsg, msg)
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/protinfo.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/protinfo.go
new file mode 100644
index 0000000..79396da
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/protinfo.go
@@ -0,0 +1,53 @@
+package netlink
+
+import (
+ "strings"
+)
+
+// Protinfo represents bridge flags from netlink.
+type Protinfo struct {
+ Hairpin bool
+ Guard bool
+ FastLeave bool
+ RootBlock bool
+ Learning bool
+ Flood bool
+}
+
+// String returns a list of enabled flags
+func (prot *Protinfo) String() string {
+ boolStrings := make([]string, 0)
+ if prot.Hairpin {
+ boolStrings = append(boolStrings, "Hairpin")
+ }
+ if prot.Guard {
+ boolStrings = append(boolStrings, "Guard")
+ }
+ if prot.FastLeave {
+ boolStrings = append(boolStrings, "FastLeave")
+ }
+ if prot.RootBlock {
+ boolStrings = append(boolStrings, "RootBlock")
+ }
+ if prot.Learning {
+ boolStrings = append(boolStrings, "Learning")
+ }
+ if prot.Flood {
+ boolStrings = append(boolStrings, "Flood")
+ }
+ return strings.Join(boolStrings, " ")
+}
+
+func boolToByte(x bool) []byte {
+ if x {
+ return []byte{1}
+ }
+ return []byte{0}
+}
+
+func byteToBool(x byte) bool {
+ if uint8(x) != 0 {
+ return true
+ }
+ return false
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/protinfo_linux.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/protinfo_linux.go
new file mode 100644
index 0000000..7181eba
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/protinfo_linux.go
@@ -0,0 +1,60 @@
+package netlink
+
+import (
+ "fmt"
+ "syscall"
+
+ "github.com/vishvananda/netlink/nl"
+)
+
+func LinkGetProtinfo(link Link) (Protinfo, error) {
+ base := link.Attrs()
+ ensureIndex(base)
+ var pi Protinfo
+ req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_DUMP)
+ msg := nl.NewIfInfomsg(syscall.AF_BRIDGE)
+ req.AddData(msg)
+ msgs, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+ if err != nil {
+ return pi, err
+ }
+
+ for _, m := range msgs {
+ ans := nl.DeserializeIfInfomsg(m)
+ if int(ans.Index) != base.Index {
+ continue
+ }
+ attrs, err := nl.ParseRouteAttr(m[ans.Len():])
+ if err != nil {
+ return pi, err
+ }
+ for _, attr := range attrs {
+ if attr.Attr.Type != syscall.IFLA_PROTINFO|syscall.NLA_F_NESTED {
+ continue
+ }
+ infos, err := nl.ParseRouteAttr(attr.Value)
+ if err != nil {
+ return pi, err
+ }
+ var pi Protinfo
+ for _, info := range infos {
+ switch info.Attr.Type {
+ case nl.IFLA_BRPORT_MODE:
+ pi.Hairpin = byteToBool(info.Value[0])
+ case nl.IFLA_BRPORT_GUARD:
+ pi.Guard = byteToBool(info.Value[0])
+ case nl.IFLA_BRPORT_FAST_LEAVE:
+ pi.FastLeave = byteToBool(info.Value[0])
+ case nl.IFLA_BRPORT_PROTECT:
+ pi.RootBlock = byteToBool(info.Value[0])
+ case nl.IFLA_BRPORT_LEARNING:
+ pi.Learning = byteToBool(info.Value[0])
+ case nl.IFLA_BRPORT_UNICAST_FLOOD:
+ pi.Flood = byteToBool(info.Value[0])
+ }
+ }
+ return pi, nil
+ }
+ }
+ return pi, fmt.Errorf("Device with index %d not found", base.Index)
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/protinfo_test.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/protinfo_test.go
new file mode 100644
index 0000000..f94c42b
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/protinfo_test.go
@@ -0,0 +1,98 @@
+package netlink
+
+import "testing"
+
+func TestProtinfo(t *testing.T) {
+ tearDown := setUpNetlinkTest(t)
+ defer tearDown()
+ master := &Bridge{LinkAttrs{Name: "foo"}}
+ if err := LinkAdd(master); err != nil {
+ t.Fatal(err)
+ }
+ iface1 := &Dummy{LinkAttrs{Name: "bar1", MasterIndex: master.Index}}
+ iface2 := &Dummy{LinkAttrs{Name: "bar2", MasterIndex: master.Index}}
+ iface3 := &Dummy{LinkAttrs{Name: "bar3"}}
+
+ if err := LinkAdd(iface1); err != nil {
+ t.Fatal(err)
+ }
+ if err := LinkAdd(iface2); err != nil {
+ t.Fatal(err)
+ }
+ if err := LinkAdd(iface3); err != nil {
+ t.Fatal(err)
+ }
+
+ oldpi1, err := LinkGetProtinfo(iface1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ oldpi2, err := LinkGetProtinfo(iface2)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if err := LinkSetHairpin(iface1, true); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := LinkSetRootBlock(iface1, true); err != nil {
+ t.Fatal(err)
+ }
+
+ pi1, err := LinkGetProtinfo(iface1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !pi1.Hairpin {
+ t.Fatalf("Hairpin mode is not enabled for %s, but should", iface1.Name)
+ }
+ if !pi1.RootBlock {
+ t.Fatalf("RootBlock is not enabled for %s, but should", iface1.Name)
+ }
+ if pi1.Guard != oldpi1.Guard {
+ t.Fatalf("Guard field was changed for %s but shouldn't", iface1.Name)
+ }
+ if pi1.FastLeave != oldpi1.FastLeave {
+ t.Fatalf("FastLeave field was changed for %s but shouldn't", iface1.Name)
+ }
+ if pi1.Learning != oldpi1.Learning {
+ t.Fatalf("Learning field was changed for %s but shouldn't", iface1.Name)
+ }
+ if pi1.Flood != oldpi1.Flood {
+ t.Fatalf("Flood field was changed for %s but shouldn't", iface1.Name)
+ }
+
+ if err := LinkSetGuard(iface2, true); err != nil {
+ t.Fatal(err)
+ }
+ if err := LinkSetLearning(iface2, false); err != nil {
+ t.Fatal(err)
+ }
+ pi2, err := LinkGetProtinfo(iface2)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if pi2.Hairpin {
+ t.Fatalf("Hairpin mode is enabled for %s, but shouldn't", iface2.Name)
+ }
+ if !pi2.Guard {
+ t.Fatalf("Guard is not enabled for %s, but should", iface2.Name)
+ }
+ if pi2.Learning {
+ t.Fatalf("Learning is enabled for %s, but shouldn't", iface2.Name)
+ }
+ if pi2.RootBlock != oldpi2.RootBlock {
+ t.Fatalf("RootBlock field was changed for %s but shouldn't", iface2.Name)
+ }
+ if pi2.FastLeave != oldpi2.FastLeave {
+ t.Fatalf("FastLeave field was changed for %s but shouldn't", iface2.Name)
+ }
+ if pi2.Flood != oldpi2.Flood {
+ t.Fatalf("Flood field was changed for %s but shouldn't", iface2.Name)
+ }
+
+ if err := LinkSetHairpin(iface3, true); err == nil || err.Error() != "operation not supported" {
+ t.Fatalf("Set protinfo attrs for link without master is not supported, but err: %s", err)
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/route.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/route.go
new file mode 100644
index 0000000..6218546
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/route.go
@@ -0,0 +1,35 @@
+package netlink
+
+import (
+ "fmt"
+ "net"
+ "syscall"
+)
+
+// Scope is an enum representing a route scope.
+type Scope uint8
+
+const (
+ SCOPE_UNIVERSE Scope = syscall.RT_SCOPE_UNIVERSE
+ SCOPE_SITE Scope = syscall.RT_SCOPE_SITE
+ SCOPE_LINK Scope = syscall.RT_SCOPE_LINK
+ SCOPE_HOST Scope = syscall.RT_SCOPE_HOST
+ SCOPE_NOWHERE Scope = syscall.RT_SCOPE_NOWHERE
+)
+
+// Route represents a netlink route. A route is associated with a link,
+// has a destination network, an optional source ip, and optional
+// gateway. Advanced route parameters and non-main routing tables are
+// currently not supported.
+type Route struct {
+ LinkIndex int
+ Scope Scope
+ Dst *net.IPNet
+ Src net.IP
+ Gw net.IP
+}
+
+func (r Route) String() string {
+ return fmt.Sprintf("{Ifindex: %d Dst: %s Src: %s Gw: %s}", r.LinkIndex, r.Dst,
+ r.Src, r.Gw)
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/route_linux.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/route_linux.go
new file mode 100644
index 0000000..43872aa
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/route_linux.go
@@ -0,0 +1,225 @@
+package netlink
+
+import (
+ "fmt"
+ "net"
+ "syscall"
+
+ "github.com/vishvananda/netlink/nl"
+)
+
+// RtAttr is shared so it is in netlink_linux.go
+
+// RouteAdd will add a route to the system.
+// Equivalent to: `ip route add $route`
+func RouteAdd(route *Route) error {
+ req := nl.NewNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+ return routeHandle(route, req)
+}
+
+// RouteAdd will delete a route from the system.
+// Equivalent to: `ip route del $route`
+func RouteDel(route *Route) error {
+ req := nl.NewNetlinkRequest(syscall.RTM_DELROUTE, syscall.NLM_F_ACK)
+ return routeHandle(route, req)
+}
+
+func routeHandle(route *Route, req *nl.NetlinkRequest) error {
+ if (route.Dst == nil || route.Dst.IP == nil) && route.Src == nil && route.Gw == nil {
+ return fmt.Errorf("one of Dst.IP, Src, or Gw must not be nil")
+ }
+
+ msg := nl.NewRtMsg()
+ msg.Scope = uint8(route.Scope)
+ family := -1
+ var rtAttrs []*nl.RtAttr
+
+ if route.Dst != nil && route.Dst.IP != nil {
+ dstLen, _ := route.Dst.Mask.Size()
+ msg.Dst_len = uint8(dstLen)
+ dstFamily := nl.GetIPFamily(route.Dst.IP)
+ family = dstFamily
+ var dstData []byte
+ if dstFamily == FAMILY_V4 {
+ dstData = route.Dst.IP.To4()
+ } else {
+ dstData = route.Dst.IP.To16()
+ }
+ rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_DST, dstData))
+ }
+
+ if route.Src != nil {
+ srcFamily := nl.GetIPFamily(route.Src)
+ if family != -1 && family != srcFamily {
+ return fmt.Errorf("source and destination ip are not the same IP family")
+ }
+ family = srcFamily
+ var srcData []byte
+ if srcFamily == FAMILY_V4 {
+ srcData = route.Src.To4()
+ } else {
+ srcData = route.Src.To16()
+ }
+ // The commonly used src ip for routes is actually PREFSRC
+ rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_PREFSRC, srcData))
+ }
+
+ if route.Gw != nil {
+ gwFamily := nl.GetIPFamily(route.Gw)
+ if family != -1 && family != gwFamily {
+ return fmt.Errorf("gateway, source, and destination ip are not the same IP family")
+ }
+ family = gwFamily
+ var gwData []byte
+ if gwFamily == FAMILY_V4 {
+ gwData = route.Gw.To4()
+ } else {
+ gwData = route.Gw.To16()
+ }
+ rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_GATEWAY, gwData))
+ }
+
+ msg.Family = uint8(family)
+
+ req.AddData(msg)
+ for _, attr := range rtAttrs {
+ req.AddData(attr)
+ }
+
+ var (
+ b = make([]byte, 4)
+ native = nl.NativeEndian()
+ )
+ native.PutUint32(b, uint32(route.LinkIndex))
+
+ req.AddData(nl.NewRtAttr(syscall.RTA_OIF, b))
+
+ _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+ return err
+}
+
+// RouteList gets a list of routes in the system.
+// Equivalent to: `ip route show`.
+// The list can be filtered by link and ip family.
+func RouteList(link Link, family int) ([]Route, error) {
+ req := nl.NewNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP)
+ msg := nl.NewIfInfomsg(family)
+ req.AddData(msg)
+
+ msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWROUTE)
+ if err != nil {
+ return nil, err
+ }
+
+ index := 0
+ if link != nil {
+ base := link.Attrs()
+ ensureIndex(base)
+ index = base.Index
+ }
+
+ native := nl.NativeEndian()
+ res := make([]Route, 0)
+ for _, m := range msgs {
+ msg := nl.DeserializeRtMsg(m)
+
+ if msg.Flags&syscall.RTM_F_CLONED != 0 {
+ // Ignore cloned routes
+ continue
+ }
+
+ if msg.Table != syscall.RT_TABLE_MAIN {
+ // Ignore non-main tables
+ continue
+ }
+
+ attrs, err := nl.ParseRouteAttr(m[msg.Len():])
+ if err != nil {
+ return nil, err
+ }
+
+ route := Route{Scope: Scope(msg.Scope)}
+ for _, attr := range attrs {
+ switch attr.Attr.Type {
+ case syscall.RTA_GATEWAY:
+ route.Gw = net.IP(attr.Value)
+ case syscall.RTA_PREFSRC:
+ route.Src = net.IP(attr.Value)
+ case syscall.RTA_DST:
+ route.Dst = &net.IPNet{
+ IP: attr.Value,
+ Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)),
+ }
+ case syscall.RTA_OIF:
+ routeIndex := int(native.Uint32(attr.Value[0:4]))
+ if link != nil && routeIndex != index {
+ // Ignore routes from other interfaces
+ continue
+ }
+ route.LinkIndex = routeIndex
+ }
+ }
+ res = append(res, route)
+ }
+
+ return res, nil
+}
+
+// RouteGet gets a route to a specific destination from the host system.
+// Equivalent to: 'ip route get'.
+func RouteGet(destination net.IP) ([]Route, error) {
+ req := nl.NewNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_REQUEST)
+ family := nl.GetIPFamily(destination)
+ var destinationData []byte
+ var bitlen uint8
+ if family == FAMILY_V4 {
+ destinationData = destination.To4()
+ bitlen = 32
+ } else {
+ destinationData = destination.To16()
+ bitlen = 128
+ }
+ msg := &nl.RtMsg{}
+ msg.Family = uint8(family)
+ msg.Dst_len = bitlen
+ req.AddData(msg)
+
+ rtaDst := nl.NewRtAttr(syscall.RTA_DST, destinationData)
+ req.AddData(rtaDst)
+
+ msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWROUTE)
+ if err != nil {
+ return nil, err
+ }
+
+ native := nl.NativeEndian()
+ res := make([]Route, 0)
+ for _, m := range msgs {
+ msg := nl.DeserializeRtMsg(m)
+ attrs, err := nl.ParseRouteAttr(m[msg.Len():])
+ if err != nil {
+ return nil, err
+ }
+
+ route := Route{}
+ for _, attr := range attrs {
+ switch attr.Attr.Type {
+ case syscall.RTA_GATEWAY:
+ route.Gw = net.IP(attr.Value)
+ case syscall.RTA_PREFSRC:
+ route.Src = net.IP(attr.Value)
+ case syscall.RTA_DST:
+ route.Dst = &net.IPNet{
+ IP: attr.Value,
+ Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)),
+ }
+ case syscall.RTA_OIF:
+ routeIndex := int(native.Uint32(attr.Value[0:4]))
+ route.LinkIndex = routeIndex
+ }
+ }
+ res = append(res, route)
+ }
+ return res, nil
+
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/route_test.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/route_test.go
new file mode 100644
index 0000000..f02bef8
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/route_test.go
@@ -0,0 +1,84 @@
+package netlink
+
+import (
+ "net"
+ "testing"
+)
+
+func TestRouteAddDel(t *testing.T) {
+ tearDown := setUpNetlinkTest(t)
+ defer tearDown()
+
+ // get loopback interface
+ link, err := LinkByName("lo")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // bring the interface up
+ if err = LinkSetUp(link); err != nil {
+ t.Fatal(err)
+ }
+
+ // add a gateway route
+ _, dst, err := net.ParseCIDR("192.168.0.0/24")
+
+ ip := net.ParseIP("127.1.1.1")
+ route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip}
+ err = RouteAdd(&route)
+ if err != nil {
+ t.Fatal(err)
+ }
+ routes, err := RouteList(link, FAMILY_V4)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(routes) != 1 {
+ t.Fatal("Link not added properly")
+ }
+
+ dstIP := net.ParseIP("192.168.0.42")
+ routeToDstIP, err := RouteGet(dstIP)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(routeToDstIP) == 0 {
+ t.Fatal("Default route not present")
+ }
+
+ err = RouteDel(&route)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ routes, err = RouteList(link, FAMILY_V4)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(routes) != 0 {
+ t.Fatal("Route not removed properly")
+ }
+
+}
+
+func TestRouteAddIncomplete(t *testing.T) {
+ tearDown := setUpNetlinkTest(t)
+ defer tearDown()
+
+ // get loopback interface
+ link, err := LinkByName("lo")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // bring the interface up
+ if err = LinkSetUp(link); err != nil {
+ t.Fatal(err)
+ }
+
+ route := Route{LinkIndex: link.Attrs().Index}
+ if err := RouteAdd(&route); err == nil {
+ t.Fatal("Adding incomplete route should fail")
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm.go
new file mode 100644
index 0000000..621ffb6
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm.go
@@ -0,0 +1,64 @@
+package netlink
+
+import (
+ "fmt"
+ "syscall"
+)
+
+// Proto is an enum representing an ipsec protocol.
+type Proto uint8
+
+const (
+ XFRM_PROTO_ROUTE2 Proto = syscall.IPPROTO_ROUTING
+ XFRM_PROTO_ESP Proto = syscall.IPPROTO_ESP
+ XFRM_PROTO_AH Proto = syscall.IPPROTO_AH
+ XFRM_PROTO_HAO Proto = syscall.IPPROTO_DSTOPTS
+ XFRM_PROTO_COMP Proto = syscall.IPPROTO_COMP
+ XFRM_PROTO_IPSEC_ANY Proto = syscall.IPPROTO_RAW
+)
+
+func (p Proto) String() string {
+ switch p {
+ case XFRM_PROTO_ROUTE2:
+ return "route2"
+ case XFRM_PROTO_ESP:
+ return "esp"
+ case XFRM_PROTO_AH:
+ return "ah"
+ case XFRM_PROTO_HAO:
+ return "hao"
+ case XFRM_PROTO_COMP:
+ return "comp"
+ case XFRM_PROTO_IPSEC_ANY:
+ return "ipsec-any"
+ }
+ return fmt.Sprintf("%d", p)
+}
+
+// Mode is an enum representing an ipsec transport.
+type Mode uint8
+
+const (
+ XFRM_MODE_TRANSPORT Mode = iota
+ XFRM_MODE_TUNNEL
+ XFRM_MODE_ROUTEOPTIMIZATION
+ XFRM_MODE_IN_TRIGGER
+ XFRM_MODE_BEET
+ XFRM_MODE_MAX
+)
+
+func (m Mode) String() string {
+ switch m {
+ case XFRM_MODE_TRANSPORT:
+ return "transport"
+ case XFRM_MODE_TUNNEL:
+ return "tunnel"
+ case XFRM_MODE_ROUTEOPTIMIZATION:
+ return "ro"
+ case XFRM_MODE_IN_TRIGGER:
+ return "in_trigger"
+ case XFRM_MODE_BEET:
+ return "beet"
+ }
+ return fmt.Sprintf("%d", m)
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_policy.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_policy.go
new file mode 100644
index 0000000..d85c65d
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_policy.go
@@ -0,0 +1,59 @@
+package netlink
+
+import (
+ "fmt"
+ "net"
+)
+
+// Dir is an enum representing an ipsec template direction.
+type Dir uint8
+
+const (
+ XFRM_DIR_IN Dir = iota
+ XFRM_DIR_OUT
+ XFRM_DIR_FWD
+ XFRM_SOCKET_IN
+ XFRM_SOCKET_OUT
+ XFRM_SOCKET_FWD
+)
+
+func (d Dir) String() string {
+ switch d {
+ case XFRM_DIR_IN:
+ return "dir in"
+ case XFRM_DIR_OUT:
+ return "dir out"
+ case XFRM_DIR_FWD:
+ return "dir fwd"
+ case XFRM_SOCKET_IN:
+ return "socket in"
+ case XFRM_SOCKET_OUT:
+ return "socket out"
+ case XFRM_SOCKET_FWD:
+ return "socket fwd"
+ }
+ return fmt.Sprintf("socket %d", d-XFRM_SOCKET_IN)
+}
+
+// XfrmPolicyTmpl encapsulates a rule for the base addresses of an ipsec
+// policy. These rules are matched with XfrmState to determine encryption
+// and authentication algorithms.
+type XfrmPolicyTmpl struct {
+ Dst net.IP
+ Src net.IP
+ Proto Proto
+ Mode Mode
+ Reqid int
+}
+
+// XfrmPolicy represents an ipsec policy. It represents the overlay network
+// and has a list of XfrmPolicyTmpls representing the base addresses of
+// the policy.
+type XfrmPolicy struct {
+ Dst *net.IPNet
+ Src *net.IPNet
+ Dir Dir
+ Priority int
+ Index int
+ Tmpls []XfrmPolicyTmpl
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_policy_linux.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_policy_linux.go
new file mode 100644
index 0000000..6fe1b63
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_policy_linux.go
@@ -0,0 +1,127 @@
+package netlink
+
+import (
+ "syscall"
+
+ "github.com/vishvananda/netlink/nl"
+)
+
+func selFromPolicy(sel *nl.XfrmSelector, policy *XfrmPolicy) {
+ sel.Family = uint16(nl.GetIPFamily(policy.Dst.IP))
+ sel.Daddr.FromIP(policy.Dst.IP)
+ sel.Saddr.FromIP(policy.Src.IP)
+ prefixlenD, _ := policy.Dst.Mask.Size()
+ sel.PrefixlenD = uint8(prefixlenD)
+ prefixlenS, _ := policy.Src.Mask.Size()
+ sel.PrefixlenS = uint8(prefixlenS)
+}
+
+// XfrmPolicyAdd will add an xfrm policy to the system.
+// Equivalent to: `ip xfrm policy add $policy`
+func XfrmPolicyAdd(policy *XfrmPolicy) error {
+ req := nl.NewNetlinkRequest(nl.XFRM_MSG_NEWPOLICY, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+
+ msg := &nl.XfrmUserpolicyInfo{}
+ selFromPolicy(&msg.Sel, policy)
+ msg.Priority = uint32(policy.Priority)
+ msg.Index = uint32(policy.Index)
+ msg.Dir = uint8(policy.Dir)
+ msg.Lft.SoftByteLimit = nl.XFRM_INF
+ msg.Lft.HardByteLimit = nl.XFRM_INF
+ msg.Lft.SoftPacketLimit = nl.XFRM_INF
+ msg.Lft.HardPacketLimit = nl.XFRM_INF
+ req.AddData(msg)
+
+ tmplData := make([]byte, nl.SizeofXfrmUserTmpl*len(policy.Tmpls))
+ for i, tmpl := range policy.Tmpls {
+ start := i * nl.SizeofXfrmUserTmpl
+ userTmpl := nl.DeserializeXfrmUserTmpl(tmplData[start : start+nl.SizeofXfrmUserTmpl])
+ userTmpl.XfrmId.Daddr.FromIP(tmpl.Dst)
+ userTmpl.Saddr.FromIP(tmpl.Src)
+ userTmpl.XfrmId.Proto = uint8(tmpl.Proto)
+ userTmpl.Mode = uint8(tmpl.Mode)
+ userTmpl.Reqid = uint32(tmpl.Reqid)
+ userTmpl.Aalgos = ^uint32(0)
+ userTmpl.Ealgos = ^uint32(0)
+ userTmpl.Calgos = ^uint32(0)
+ }
+ if len(tmplData) > 0 {
+ tmpls := nl.NewRtAttr(nl.XFRMA_TMPL, tmplData)
+ req.AddData(tmpls)
+ }
+
+ _, err := req.Execute(syscall.NETLINK_XFRM, 0)
+ return err
+}
+
+// XfrmPolicyDel will delete an xfrm policy from the system. Note that
+// the Tmpls are ignored when matching the policy to delete.
+// Equivalent to: `ip xfrm policy del $policy`
+func XfrmPolicyDel(policy *XfrmPolicy) error {
+ req := nl.NewNetlinkRequest(nl.XFRM_MSG_DELPOLICY, syscall.NLM_F_ACK)
+
+ msg := &nl.XfrmUserpolicyId{}
+ selFromPolicy(&msg.Sel, policy)
+ msg.Index = uint32(policy.Index)
+ msg.Dir = uint8(policy.Dir)
+ req.AddData(msg)
+
+ _, err := req.Execute(syscall.NETLINK_XFRM, 0)
+ return err
+}
+
+// XfrmPolicyList gets a list of xfrm policies in the system.
+// Equivalent to: `ip xfrm policy show`.
+// The list can be filtered by ip family.
+func XfrmPolicyList(family int) ([]XfrmPolicy, error) {
+ req := nl.NewNetlinkRequest(nl.XFRM_MSG_GETPOLICY, syscall.NLM_F_DUMP)
+
+ msg := nl.NewIfInfomsg(family)
+ req.AddData(msg)
+
+ msgs, err := req.Execute(syscall.NETLINK_XFRM, nl.XFRM_MSG_NEWPOLICY)
+ if err != nil {
+ return nil, err
+ }
+
+ res := make([]XfrmPolicy, 0)
+ for _, m := range msgs {
+ msg := nl.DeserializeXfrmUserpolicyInfo(m)
+
+ if family != FAMILY_ALL && family != int(msg.Sel.Family) {
+ continue
+ }
+
+ var policy XfrmPolicy
+
+ policy.Dst = msg.Sel.Daddr.ToIPNet(msg.Sel.PrefixlenD)
+ policy.Src = msg.Sel.Saddr.ToIPNet(msg.Sel.PrefixlenS)
+ policy.Priority = int(msg.Priority)
+ policy.Index = int(msg.Index)
+ policy.Dir = Dir(msg.Dir)
+
+ attrs, err := nl.ParseRouteAttr(m[msg.Len():])
+ if err != nil {
+ return nil, err
+ }
+
+ for _, attr := range attrs {
+ switch attr.Attr.Type {
+ case nl.XFRMA_TMPL:
+ max := len(attr.Value)
+ for i := 0; i < max; i += nl.SizeofXfrmUserTmpl {
+ var resTmpl XfrmPolicyTmpl
+ tmpl := nl.DeserializeXfrmUserTmpl(attr.Value[i : i+nl.SizeofXfrmUserTmpl])
+ resTmpl.Dst = tmpl.XfrmId.Daddr.ToIP()
+ resTmpl.Src = tmpl.Saddr.ToIP()
+ resTmpl.Proto = Proto(tmpl.XfrmId.Proto)
+ resTmpl.Mode = Mode(tmpl.Mode)
+ resTmpl.Reqid = int(tmpl.Reqid)
+ policy.Tmpls = append(policy.Tmpls, resTmpl)
+ }
+ }
+ }
+ res = append(res, policy)
+ }
+ return res, nil
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_policy_test.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_policy_test.go
new file mode 100644
index 0000000..06d178d
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_policy_test.go
@@ -0,0 +1,49 @@
+package netlink
+
+import (
+ "net"
+ "testing"
+)
+
+func TestXfrmPolicyAddDel(t *testing.T) {
+ tearDown := setUpNetlinkTest(t)
+ defer tearDown()
+
+ src, _ := ParseIPNet("127.1.1.1/32")
+ dst, _ := ParseIPNet("127.1.1.2/32")
+ policy := XfrmPolicy{
+ Src: src,
+ Dst: dst,
+ Dir: XFRM_DIR_OUT,
+ }
+ tmpl := XfrmPolicyTmpl{
+ Src: net.ParseIP("127.0.0.1"),
+ Dst: net.ParseIP("127.0.0.2"),
+ Proto: XFRM_PROTO_ESP,
+ Mode: XFRM_MODE_TUNNEL,
+ }
+ policy.Tmpls = append(policy.Tmpls, tmpl)
+ if err := XfrmPolicyAdd(&policy); err != nil {
+ t.Fatal(err)
+ }
+ policies, err := XfrmPolicyList(FAMILY_ALL)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(policies) != 1 {
+ t.Fatal("Policy not added properly")
+ }
+
+ if err = XfrmPolicyDel(&policy); err != nil {
+ t.Fatal(err)
+ }
+
+ policies, err = XfrmPolicyList(FAMILY_ALL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(policies) != 0 {
+ t.Fatal("Policy not removed properly")
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_state.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_state.go
new file mode 100644
index 0000000..5b8f2df
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_state.go
@@ -0,0 +1,53 @@
+package netlink
+
+import (
+ "net"
+)
+
+// XfrmStateAlgo represents the algorithm to use for the ipsec encryption.
+type XfrmStateAlgo struct {
+ Name string
+ Key []byte
+ TruncateLen int // Auth only
+}
+
+// EncapType is an enum representing an ipsec template direction.
+type EncapType uint8
+
+const (
+ XFRM_ENCAP_ESPINUDP_NONIKE EncapType = iota + 1
+ XFRM_ENCAP_ESPINUDP
+)
+
+func (e EncapType) String() string {
+ switch e {
+ case XFRM_ENCAP_ESPINUDP_NONIKE:
+ return "espinudp-nonike"
+ case XFRM_ENCAP_ESPINUDP:
+ return "espinudp"
+ }
+ return "unknown"
+}
+
+// XfrmEncap represents the encapsulation to use for the ipsec encryption.
+type XfrmStateEncap struct {
+ Type EncapType
+ SrcPort int
+ DstPort int
+ OriginalAddress net.IP
+}
+
+// XfrmState represents the state of an ipsec policy. It optionally
+// contains an XfrmStateAlgo for encryption and one for authentication.
+type XfrmState struct {
+ Dst net.IP
+ Src net.IP
+ Proto Proto
+ Mode Mode
+ Spi int
+ Reqid int
+ ReplayWindow int
+ Auth *XfrmStateAlgo
+ Crypt *XfrmStateAlgo
+ Encap *XfrmStateEncap
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_state_linux.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_state_linux.go
new file mode 100644
index 0000000..0f1fbd0
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_state_linux.go
@@ -0,0 +1,181 @@
+package netlink
+
+import (
+ "fmt"
+ "syscall"
+
+ "github.com/vishvananda/netlink/nl"
+)
+
+func writeStateAlgo(a *XfrmStateAlgo) []byte {
+ algo := nl.XfrmAlgo{
+ AlgKeyLen: uint32(len(a.Key) * 8),
+ AlgKey: a.Key,
+ }
+ end := len(a.Name)
+ if end > 64 {
+ end = 64
+ }
+ copy(algo.AlgName[:end], a.Name)
+ return algo.Serialize()
+}
+
+func writeStateAlgoAuth(a *XfrmStateAlgo) []byte {
+ algo := nl.XfrmAlgoAuth{
+ AlgKeyLen: uint32(len(a.Key) * 8),
+ AlgTruncLen: uint32(a.TruncateLen),
+ AlgKey: a.Key,
+ }
+ end := len(a.Name)
+ if end > 64 {
+ end = 64
+ }
+ copy(algo.AlgName[:end], a.Name)
+ return algo.Serialize()
+}
+
+// XfrmStateAdd will add an xfrm state to the system.
+// Equivalent to: `ip xfrm state add $state`
+func XfrmStateAdd(state *XfrmState) error {
+ // A state with spi 0 can't be deleted so don't allow it to be set
+ if state.Spi == 0 {
+ return fmt.Errorf("Spi must be set when adding xfrm state.")
+ }
+ req := nl.NewNetlinkRequest(nl.XFRM_MSG_NEWSA, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+
+ msg := &nl.XfrmUsersaInfo{}
+ msg.Family = uint16(nl.GetIPFamily(state.Dst))
+ msg.Id.Daddr.FromIP(state.Dst)
+ msg.Saddr.FromIP(state.Src)
+ msg.Id.Proto = uint8(state.Proto)
+ msg.Mode = uint8(state.Mode)
+ msg.Id.Spi = nl.Swap32(uint32(state.Spi))
+ msg.Reqid = uint32(state.Reqid)
+ msg.ReplayWindow = uint8(state.ReplayWindow)
+ msg.Lft.SoftByteLimit = nl.XFRM_INF
+ msg.Lft.HardByteLimit = nl.XFRM_INF
+ msg.Lft.SoftPacketLimit = nl.XFRM_INF
+ msg.Lft.HardPacketLimit = nl.XFRM_INF
+ req.AddData(msg)
+
+ if state.Auth != nil {
+ out := nl.NewRtAttr(nl.XFRMA_ALG_AUTH_TRUNC, writeStateAlgoAuth(state.Auth))
+ req.AddData(out)
+ }
+ if state.Crypt != nil {
+ out := nl.NewRtAttr(nl.XFRMA_ALG_CRYPT, writeStateAlgo(state.Crypt))
+ req.AddData(out)
+ }
+ if state.Encap != nil {
+ encapData := make([]byte, nl.SizeofXfrmEncapTmpl)
+ encap := nl.DeserializeXfrmEncapTmpl(encapData)
+ encap.EncapType = uint16(state.Encap.Type)
+ encap.EncapSport = nl.Swap16(uint16(state.Encap.SrcPort))
+ encap.EncapDport = nl.Swap16(uint16(state.Encap.DstPort))
+ encap.EncapOa.FromIP(state.Encap.OriginalAddress)
+ out := nl.NewRtAttr(nl.XFRMA_ENCAP, encapData)
+ req.AddData(out)
+ }
+
+ _, err := req.Execute(syscall.NETLINK_XFRM, 0)
+ return err
+}
+
+// XfrmStateDel will delete an xfrm state from the system. Note that
+// the Algos are ignored when matching the state to delete.
+// Equivalent to: `ip xfrm state del $state`
+func XfrmStateDel(state *XfrmState) error {
+ req := nl.NewNetlinkRequest(nl.XFRM_MSG_DELSA, syscall.NLM_F_ACK)
+
+ msg := &nl.XfrmUsersaId{}
+ msg.Daddr.FromIP(state.Dst)
+ msg.Family = uint16(nl.GetIPFamily(state.Dst))
+ msg.Proto = uint8(state.Proto)
+ msg.Spi = nl.Swap32(uint32(state.Spi))
+ req.AddData(msg)
+
+ saddr := nl.XfrmAddress{}
+ saddr.FromIP(state.Src)
+ srcdata := nl.NewRtAttr(nl.XFRMA_SRCADDR, saddr.Serialize())
+
+ req.AddData(srcdata)
+
+ _, err := req.Execute(syscall.NETLINK_XFRM, 0)
+ return err
+}
+
+// XfrmStateList gets a list of xfrm states in the system.
+// Equivalent to: `ip xfrm state show`.
+// The list can be filtered by ip family.
+func XfrmStateList(family int) ([]XfrmState, error) {
+ req := nl.NewNetlinkRequest(nl.XFRM_MSG_GETSA, syscall.NLM_F_DUMP)
+
+ msg := nl.NewIfInfomsg(family)
+ req.AddData(msg)
+
+ msgs, err := req.Execute(syscall.NETLINK_XFRM, nl.XFRM_MSG_NEWSA)
+ if err != nil {
+ return nil, err
+ }
+
+ res := make([]XfrmState, 0)
+ for _, m := range msgs {
+ msg := nl.DeserializeXfrmUsersaInfo(m)
+
+ if family != FAMILY_ALL && family != int(msg.Family) {
+ continue
+ }
+
+ var state XfrmState
+
+ state.Dst = msg.Id.Daddr.ToIP()
+ state.Src = msg.Saddr.ToIP()
+ state.Proto = Proto(msg.Id.Proto)
+ state.Mode = Mode(msg.Mode)
+ state.Spi = int(nl.Swap32(msg.Id.Spi))
+ state.Reqid = int(msg.Reqid)
+ state.ReplayWindow = int(msg.ReplayWindow)
+
+ attrs, err := nl.ParseRouteAttr(m[msg.Len():])
+ if err != nil {
+ return nil, err
+ }
+
+ for _, attr := range attrs {
+ switch attr.Attr.Type {
+ case nl.XFRMA_ALG_AUTH, nl.XFRMA_ALG_CRYPT:
+ var resAlgo *XfrmStateAlgo
+ if attr.Attr.Type == nl.XFRMA_ALG_AUTH {
+ if state.Auth == nil {
+ state.Auth = new(XfrmStateAlgo)
+ }
+ resAlgo = state.Auth
+ } else {
+ state.Crypt = new(XfrmStateAlgo)
+ resAlgo = state.Crypt
+ }
+ algo := nl.DeserializeXfrmAlgo(attr.Value[:])
+ (*resAlgo).Name = nl.BytesToString(algo.AlgName[:])
+ (*resAlgo).Key = algo.AlgKey
+ case nl.XFRMA_ALG_AUTH_TRUNC:
+ if state.Auth == nil {
+ state.Auth = new(XfrmStateAlgo)
+ }
+ algo := nl.DeserializeXfrmAlgoAuth(attr.Value[:])
+ state.Auth.Name = nl.BytesToString(algo.AlgName[:])
+ state.Auth.Key = algo.AlgKey
+ state.Auth.TruncateLen = int(algo.AlgTruncLen)
+ case nl.XFRMA_ENCAP:
+ encap := nl.DeserializeXfrmEncapTmpl(attr.Value[:])
+ state.Encap = new(XfrmStateEncap)
+ state.Encap.Type = EncapType(encap.EncapType)
+ state.Encap.SrcPort = int(nl.Swap16(encap.EncapSport))
+ state.Encap.DstPort = int(nl.Swap16(encap.EncapDport))
+ state.Encap.OriginalAddress = encap.EncapOa.ToIP()
+ }
+
+ }
+ res = append(res, state)
+ }
+ return res, nil
+}
diff --git a/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_state_test.go b/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_state_test.go
new file mode 100644
index 0000000..df57ef8
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_state_test.go
@@ -0,0 +1,50 @@
+package netlink
+
+import (
+ "net"
+ "testing"
+)
+
+func TestXfrmStateAddDel(t *testing.T) {
+ tearDown := setUpNetlinkTest(t)
+ defer tearDown()
+
+ state := XfrmState{
+ Src: net.ParseIP("127.0.0.1"),
+ Dst: net.ParseIP("127.0.0.2"),
+ Proto: XFRM_PROTO_ESP,
+ Mode: XFRM_MODE_TUNNEL,
+ Spi: 1,
+ Auth: &XfrmStateAlgo{
+ Name: "hmac(sha256)",
+ Key: []byte("abcdefghijklmnopqrstuvwzyzABCDEF"),
+ },
+ Crypt: &XfrmStateAlgo{
+ Name: "cbc(aes)",
+ Key: []byte("abcdefghijklmnopqrstuvwzyzABCDEF"),
+ },
+ }
+ if err := XfrmStateAdd(&state); err != nil {
+ t.Fatal(err)
+ }
+ policies, err := XfrmStateList(FAMILY_ALL)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(policies) != 1 {
+ t.Fatal("State not added properly")
+ }
+
+ if err = XfrmStateDel(&state); err != nil {
+ t.Fatal(err)
+ }
+
+ policies, err = XfrmStateList(FAMILY_ALL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(policies) != 0 {
+ t.Fatal("State not removed properly")
+ }
+}
diff --git a/circle.yml b/circle.yml
index 807dbc1..8045da5 100644
--- a/circle.yml
+++ b/circle.yml
@@ -2,8 +2,11 @@ machine:
# sudo -E doesn't preserve $PATH, so go isn't found anymore.
environment:
GO_BIN: $(which go)
+ GO_PATH: /home/ubuntu/.go_workspace/bin/
dependencies:
+ override:
+ - go get github.com/tools/godep
post:
- go get github.com/axw/gocov/gocov
- go get github.com/golang/lint/golint
@@ -11,7 +14,7 @@ dependencies:
test:
override:
- - test -z "$(goimports -d . | tee /dev/stderr)"
+ - test -z "$(goimports -l . | grep -v Godeps/_workspace/src/ | tee /dev/stderr)"
- go vet ./...
- test -z "$(golint ./... | tee /dev/stderr)"
- sudo -E $GO_BIN test -test.v ./...