upgrade all dependencies

The dependabot does not update dependencies when they do not use a tag.
This patch upgrades all untagged depenencies if possible.

You can upgrade all dependencies with `go get -u ./... && make vendor`
in theory however this failed since the k8s changes do not compile on
go v1.16 so I only updated the other dependencies.

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
This commit is contained in:
Paul Holzinger
2022-01-17 17:49:00 +01:00
parent 55ad6188b0
commit 774271c38a
148 changed files with 4352 additions and 1543 deletions
go.modgo.sum
vendor
github.com
buger/goterm
docker/go-plugins-helpers/volume
felixge/httpsnoop
google/shlex
gorilla/handlers
opencontainers
seccomp/libseccomp-golang
vishvananda/netlink
golang.org/x/sys
modules.txt

24
go.mod

@ -5,7 +5,7 @@ go 1.16
require ( require (
github.com/BurntSushi/toml v1.0.0 github.com/BurntSushi/toml v1.0.0
github.com/blang/semver v3.5.1+incompatible github.com/blang/semver v3.5.1+incompatible
github.com/buger/goterm v0.0.0-20181115115552-c206103e1f37 github.com/buger/goterm v1.0.1
github.com/checkpoint-restore/checkpointctl v0.0.0-20211204171957-54b4ebfdb681 github.com/checkpoint-restore/checkpointctl v0.0.0-20211204171957-54b4ebfdb681
github.com/checkpoint-restore/go-criu/v5 v5.3.0 github.com/checkpoint-restore/go-criu/v5 v5.3.0
github.com/container-orchestrated-devices/container-device-interface v0.0.0-20220111162300-46367ec063fd github.com/container-orchestrated-devices/container-device-interface v0.0.0-20220111162300-46367ec063fd
@ -22,19 +22,19 @@ require (
github.com/coreos/stream-metadata-go v0.0.0-20210225230131-70edb9eb47b3 github.com/coreos/stream-metadata-go v0.0.0-20210225230131-70edb9eb47b3
github.com/cyphar/filepath-securejoin v0.2.3 github.com/cyphar/filepath-securejoin v0.2.3
github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew v1.1.1
github.com/digitalocean/go-qemu v0.0.0-20210209191958-152a1535e49f github.com/digitalocean/go-qemu v0.0.0-20210326154740-ac9e0b687001
github.com/docker/distribution v2.7.1+incompatible github.com/docker/distribution v2.7.1+incompatible
github.com/docker/docker v20.10.12+incompatible github.com/docker/docker v20.10.12+incompatible
github.com/docker/go-connections v0.4.0 github.com/docker/go-connections v0.4.0
github.com/docker/go-plugins-helpers v0.0.0-20200102110956-c9a8a2d92ccc github.com/docker/go-plugins-helpers v0.0.0-20211224144127-6eecb7beb651
github.com/docker/go-units v0.4.0 github.com/docker/go-units v0.4.0
github.com/dtylman/scp v0.0.0-20181017070807-f3000a34aef4 github.com/dtylman/scp v0.0.0-20181017070807-f3000a34aef4
github.com/fsnotify/fsnotify v1.5.1 github.com/fsnotify/fsnotify v1.5.1
github.com/ghodss/yaml v1.0.0 github.com/ghodss/yaml v1.0.0
github.com/godbus/dbus/v5 v5.0.6 github.com/godbus/dbus/v5 v5.0.6
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0
github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33 github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0 github.com/gorilla/mux v1.8.0
github.com/gorilla/schema v1.2.0 github.com/gorilla/schema v1.2.0
github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-multierror v1.1.1
@ -46,10 +46,10 @@ require (
github.com/onsi/ginkgo v1.16.5 github.com/onsi/ginkgo v1.16.5
github.com/onsi/gomega v1.17.0 github.com/onsi/gomega v1.17.0
github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.3-0.20211215212317-ea0209f50ae1 github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198
github.com/opencontainers/runc v1.1.0 github.com/opencontainers/runc v1.0.3
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 github.com/opencontainers/runtime-spec v1.0.3-0.20211214071223-8958f93039ab
github.com/opencontainers/runtime-tools v0.9.1-0.20211020193359-09d837bf40a7 github.com/opencontainers/runtime-tools v0.9.1-0.20220110225228-7e2d60f1e41f
github.com/opencontainers/selinux v1.10.0 github.com/opencontainers/selinux v1.10.0
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.0 github.com/pmezard/go-difflib v1.0.0
@ -62,11 +62,11 @@ require (
github.com/uber/jaeger-client-go v2.30.0+incompatible github.com/uber/jaeger-client-go v2.30.0+incompatible
github.com/ulikunitz/xz v0.5.10 github.com/ulikunitz/xz v0.5.10
github.com/vbauerster/mpb/v6 v6.0.4 github.com/vbauerster/mpb/v6 v6.0.4
github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5 github.com/vishvananda/netlink v1.1.1-0.20220115184804-dd687eb2f2d4
go.etcd.io/bbolt v1.3.6 go.etcd.io/bbolt v1.3.6
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
golang.org/x/text v0.3.7 golang.org/x/text v0.3.7
google.golang.org/protobuf v1.27.1 google.golang.org/protobuf v1.27.1
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b

56
go.sum

@ -160,6 +160,7 @@ github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edY
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI= github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI=
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blizzy78/varnamelen v0.3.0/go.mod h1:hbwRdBvoBqxk34XyQ6HA0UH3G0/1TKuv5AC4eaBT0Ec= github.com/blizzy78/varnamelen v0.3.0/go.mod h1:hbwRdBvoBqxk34XyQ6HA0UH3G0/1TKuv5AC4eaBT0Ec=
@ -167,8 +168,8 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dR
github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc=
github.com/breml/bidichk v0.1.1/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso= github.com/breml/bidichk v0.1.1/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso=
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
github.com/buger/goterm v0.0.0-20181115115552-c206103e1f37 h1:uxxtrnACqI9zK4ENDMf0WpXfUsHP5V8liuq5QdgDISU= github.com/buger/goterm v1.0.1 h1:kSgw3jcjYUzC0Uh/eG8ULjccuz353solup27lUH8Zug=
github.com/buger/goterm v0.0.0-20181115115552-c206103e1f37/go.mod h1:u9UyCz2eTrSGy6fbupqJ54eY5c4IC8gREQ1053dK12U= github.com/buger/goterm v1.0.1/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE=
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
@ -203,7 +204,6 @@ github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLI
github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
@ -240,7 +240,6 @@ github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on
github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ=
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
@ -388,8 +387,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/digitalocean/go-libvirt v0.0.0-20201209184759-e2a69bcd5bd1 h1:j6vGflaQ2T7yOWqVgPdiRF73j/U2Zmpbbzab8nyDCRQ= github.com/digitalocean/go-libvirt v0.0.0-20201209184759-e2a69bcd5bd1 h1:j6vGflaQ2T7yOWqVgPdiRF73j/U2Zmpbbzab8nyDCRQ=
github.com/digitalocean/go-libvirt v0.0.0-20201209184759-e2a69bcd5bd1/go.mod h1:QS1XzqZLcDniNYrN7EZefq3wIyb/M2WmJbql4ZKoc1Q= github.com/digitalocean/go-libvirt v0.0.0-20201209184759-e2a69bcd5bd1/go.mod h1:QS1XzqZLcDniNYrN7EZefq3wIyb/M2WmJbql4ZKoc1Q=
github.com/digitalocean/go-qemu v0.0.0-20210209191958-152a1535e49f h1:N2HvbwONtcvzegFxOAgGt15JsajIk5QzY3j5X3VzFDI= github.com/digitalocean/go-qemu v0.0.0-20210326154740-ac9e0b687001 h1:WAg57gnaAWWjMAELcwHjc2xy0PoXQ5G+vn3+XS6s1jI=
github.com/digitalocean/go-qemu v0.0.0-20210209191958-152a1535e49f/go.mod h1:IetBE52JfFxK46p2n2Rqm+p5Gx1gpu2hRHsrbnPOWZQ= github.com/digitalocean/go-qemu v0.0.0-20210326154740-ac9e0b687001/go.mod h1:IetBE52JfFxK46p2n2Rqm+p5Gx1gpu2hRHsrbnPOWZQ=
github.com/disiqueira/gotree/v3 v3.0.2 h1:ik5iuLQQoufZBNPY518dXhiO5056hyNBIK9lWhkNRq8= github.com/disiqueira/gotree/v3 v3.0.2 h1:ik5iuLQQoufZBNPY518dXhiO5056hyNBIK9lWhkNRq8=
github.com/disiqueira/gotree/v3 v3.0.2/go.mod h1:ZuyjE4+mUQZlbpkI24AmruZKhg3VHEgPLDY8Qk+uUu8= github.com/disiqueira/gotree/v3 v3.0.2/go.mod h1:ZuyjE4+mUQZlbpkI24AmruZKhg3VHEgPLDY8Qk+uUu8=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
@ -411,8 +410,8 @@ github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6Uezg
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
github.com/docker/go-plugins-helpers v0.0.0-20200102110956-c9a8a2d92ccc h1:/A+mPcpajLsWiX9gSnzdVKM/IzZoYiNqXHe83z50k2c= github.com/docker/go-plugins-helpers v0.0.0-20211224144127-6eecb7beb651 h1:YcvzLmdrP/b8kLAGJ8GT7bdncgCAiWxJZIlt84D+RJg=
github.com/docker/go-plugins-helpers v0.0.0-20200102110956-c9a8a2d92ccc/go.mod h1:LFyLie6XcDbyKGeVK6bHe+9aJTYCxWLBg5IrJZOaXKA= github.com/docker/go-plugins-helpers v0.0.0-20211224144127-6eecb7beb651/go.mod h1:LFyLie6XcDbyKGeVK6bHe+9aJTYCxWLBg5IrJZOaXKA=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316 h1:moehPjPiGUaWdwgOl92xRyFHJyaqXDHcCyW9M6nmCK4= github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316 h1:moehPjPiGUaWdwgOl92xRyFHJyaqXDHcCyW9M6nmCK4=
@ -452,6 +451,8 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@ -620,8 +621,8 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmIpZJGRgNFg2ioYPvFaUxdqpDsg= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/trillian v1.3.11/go.mod h1:0tPraVHrSDkA3BO6vKX67zgLXs6SsOAbHEivX+9mPgw= github.com/google/trillian v1.3.11/go.mod h1:0tPraVHrSDkA3BO6vKX67zgLXs6SsOAbHEivX+9mPgw=
github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -642,8 +643,9 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR
github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
github.com/gordonklaus/ineffassign v0.0.0-20210225214923-2e10b2664254/go.mod h1:M9mZEtGIsR1oDaZagNPNG9iq9n2HrhZ17dsXk73V3Lw= github.com/gordonklaus/ineffassign v0.0.0-20210225214923-2e10b2664254/go.mod h1:M9mZEtGIsR1oDaZagNPNG9iq9n2HrhZ17dsXk73V3Lw=
github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA= github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA=
github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33 h1:893HsJqtxp9z1SF76gg6hY70hRY1wVlTSnC/h1yUDCo=
github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
@ -899,6 +901,7 @@ github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/moby/sys/mount v0.2.0 h1:WhCW5B355jtxndN5ovugJlMFJawbUODuW8fSnEH6SSM= github.com/moby/sys/mount v0.2.0 h1:WhCW5B355jtxndN5ovugJlMFJawbUODuW8fSnEH6SSM=
@ -976,29 +979,30 @@ github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zM
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/image-spec v1.0.3-0.20211202193544-a5463b7f9c84/go.mod h1:Qnt1q4cjDNQI9bT832ziho5Iw2BhK8o1KwLOwW56VP4= github.com/opencontainers/image-spec v1.0.3-0.20211202193544-a5463b7f9c84/go.mod h1:Qnt1q4cjDNQI9bT832ziho5Iw2BhK8o1KwLOwW56VP4=
github.com/opencontainers/image-spec v1.0.3-0.20211215212317-ea0209f50ae1 h1:R0VgKjcNQhH6AKteTyHcdoTgGRGEYZhXT05Gv1LJLh4= github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 h1:+czc/J8SlhPKLOtVLMQc+xDCFBT73ZStMsRhSsUhsSg=
github.com/opencontainers/image-spec v1.0.3-0.20211215212317-ea0209f50ae1/go.mod h1:j4h1pJW6ZcJTgMZWP3+7RlG3zTaP02aDZ/Qw0sppK7Q= github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198/go.mod h1:j4h1pJW6ZcJTgMZWP3+7RlG3zTaP02aDZ/Qw0sppK7Q=
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0=
github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
github.com/opencontainers/runc v1.0.3 h1:1hbqejyQWCJBvtKAfdO0b1FmaEf2z/bxnjqbARass5k=
github.com/opencontainers/runc v1.0.3/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= github.com/opencontainers/runc v1.0.3/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
github.com/opencontainers/runc v1.1.0 h1:O9+X96OcDjkmmZyfaG996kV7yq8HsoU2h1XRRQcefG8=
github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc=
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc= github.com/opencontainers/runtime-spec v1.0.3-0.20201121164853-7413a7f753e1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.3-0.20211214071223-8958f93039ab h1:YQZXa3elcHgKXAa2GjVFC9M3JeP7ZPyFD1YByDx/dgQ=
github.com/opencontainers/runtime-spec v1.0.3-0.20211214071223-8958f93039ab/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
github.com/opencontainers/runtime-tools v0.0.0-20190417131837-cd1349b7c47e/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opencontainers/runtime-tools v0.0.0-20190417131837-cd1349b7c47e/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
github.com/opencontainers/runtime-tools v0.9.0/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opencontainers/runtime-tools v0.9.0/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
github.com/opencontainers/runtime-tools v0.9.1-0.20211020193359-09d837bf40a7 h1:6JHkPc2wUOsj2XBpYzyvmCL5Y/fA3TFaomYv/Iggt1g= github.com/opencontainers/runtime-tools v0.9.1-0.20220110225228-7e2d60f1e41f h1:MMcsVl0FAVEahmXTy+uXoDTw3yJq7nGrK8ITs/kkreo=
github.com/opencontainers/runtime-tools v0.9.1-0.20211020193359-09d837bf40a7/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opencontainers/runtime-tools v0.9.1-0.20220110225228-7e2d60f1e41f/go.mod h1:/tgP02fPXGHkU3/qKK1Y0Db4yqNyGm03vLq/mzHzcS4=
github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE=
github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
@ -1110,9 +1114,8 @@ github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZ
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
github.com/seccomp/libseccomp-golang v0.9.2-0.20200616122406-847368b35ebf h1:b0+ZBD3rohnkQ4q5duD1+RyTXTg9yk+qTOPMSQtapO0=
github.com/seccomp/libseccomp-golang v0.9.2-0.20200616122406-847368b35ebf/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/seccomp/libseccomp-golang v0.9.2-0.20200616122406-847368b35ebf/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921 h1:58EBmR2dMNL2n/FnbQewK3D14nXr0V9CObDSvMJLq+Y=
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
github.com/securego/gosec/v2 v2.9.1/go.mod h1:oDcDLcatOJxkCGaCaq8lua1jTnYf6Sou4wdiJ1n4iHc= github.com/securego/gosec/v2 v2.9.1/go.mod h1:oDcDLcatOJxkCGaCaq8lua1jTnYf6Sou4wdiJ1n4iHc=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs=
@ -1217,6 +1220,7 @@ github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0o
github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.19.1/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
@ -1237,8 +1241,9 @@ github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5 h1:+UB2BJA852UkGH42H+Oee69djmxS3ANzl2b/JtT1YiA=
github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
github.com/vishvananda/netlink v1.1.1-0.20220115184804-dd687eb2f2d4 h1:fB26rIBlWTVJyEB6ONHdoEvUbvwoudH0/cMEXHiD1RU=
github.com/vishvananda/netlink v1.1.1-0.20220115184804-dd687eb2f2d4/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
@ -1330,8 +1335,9 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce h1:Roh6XWxHFKrPgC/EQhVubSAGQ6Ozk6IdxHSzt1mR0EI=
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -1558,6 +1564,7 @@ golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1575,7 +1582,6 @@ golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -1584,11 +1590,11 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211107104306-e0b2ad06fe42/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211107104306-e0b2ad06fe42/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d h1:1oIt9o40TWWI9FUaveVpUvBe13FNqBNVXy3ue2fcfkw=
golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=

1
vendor/github.com/buger/goterm/.gitignore generated vendored Normal file

@ -0,0 +1 @@
*.swp

@ -2,7 +2,9 @@ package goterm
import ( import (
"bytes" "bytes"
"regexp"
"strings" "strings"
_ "unicode/utf8"
) )
const DEFAULT_BORDER = "- │ ┌ ┐ └ ┘" const DEFAULT_BORDER = "- │ ┌ ┐ └ ┘"
@ -61,7 +63,9 @@ func (b *Box) Write(p []byte) (int, error) {
return b.Buf.Write(p) return b.Buf.Write(p)
} }
// Render Box var ANSI_RE = regexp.MustCompile(`\\0\d+\[\d+(?:;\d+)?m`)
// String renders Box
func (b *Box) String() (out string) { func (b *Box) String() (out string) {
borders := strings.Split(b.Border, " ") borders := strings.Split(b.Border, " ")
lines := strings.Split(b.Buf.String(), "\n") lines := strings.Split(b.Buf.String(), "\n")
@ -74,7 +78,6 @@ func (b *Box) String() (out string) {
// Content width without borders and padding // Content width without borders and padding
contentWidth := b.Width - (b.PaddingX+1)*2 contentWidth := b.Width - (b.PaddingX+1)*2
for y := 0; y < b.Height; y++ { for y := 0; y < b.Height; y++ {
var line string var line string
@ -99,12 +102,63 @@ func (b *Box) String() (out string) {
line = "" line = ""
} }
if len(line) > contentWidth-1 { r := []rune(line)
lastAnsii := ""
withoutAnsii := []rune{}
withOffset := []rune{}
i := 0
for {
if i >= len(r) {
break
}
if r[i] == 27 {
lastAnsii = ""
withOffset = append(withOffset, r[i])
lastAnsii += string(r[i])
i++
for {
i++
if i > len(r) {
break
}
withOffset = append(withOffset, r[i])
lastAnsii += string(r[i])
if r[i] == 'm' {
i++
break
}
}
}
if i >= len(r) {
break
}
withoutAnsii = append(withoutAnsii, r[i])
if len(withoutAnsii) <= contentWidth {
withOffset = append(withOffset, r[i])
}
i++
}
if len(withoutAnsii) > contentWidth {
// If line is too large limit it // If line is too large limit it
line = line[0:contentWidth] line = string(withOffset)
} else { } else {
// If line is too small enlarge it by adding spaces // If line is too small enlarge it by adding spaces
line = line + strings.Repeat(" ", contentWidth-len(line)) line += strings.Repeat(" ", contentWidth-len(withoutAnsii))
}
if lastAnsii != "" {
line += RESET
} }
line = prefix + line + suffix line = prefix + line + suffix
@ -112,7 +166,7 @@ func (b *Box) String() (out string) {
// Don't add newline for last element // Don't add newline for last element
if y != b.Height-1 { if y != b.Height-1 {
line = line + "\n" line += "\n"
} }
out += line out += line

5
vendor/github.com/buger/goterm/go.mod generated vendored Normal file

@ -0,0 +1,5 @@
module github.com/buger/goterm
go 1.15
require golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54

2
vendor/github.com/buger/goterm/go.sum generated vendored Normal file

@ -0,0 +1,2 @@
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54 h1:rF3Ohx8DRyl8h2zw9qojyLHLhrJpEMgyPOImREEryf0=
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"math" "math"
"strings" "strings"
"unicode/utf8"
) )
const ( const (
@ -119,7 +120,7 @@ func (c *LineChart) DrawAxes(maxX, minX, maxY, minY float64, index int) {
c.writeText(ff(minX), c.paddingX, 0) c.writeText(ff(minX), c.paddingX, 0)
x_col := c.data.columns[0] x_col := c.data.columns[0]
c.writeText(c.data.columns[0], c.Width/2-len(x_col)/2, 1) c.writeText(c.data.columns[0], c.Width/2-utf8.RuneCountInString(x_col)/2, 1)
if c.Flags&DRAW_INDEPENDENT != 0 || len(c.data.columns) < 3 { if c.Flags&DRAW_INDEPENDENT != 0 || len(c.data.columns) < 3 {
col := c.data.columns[index] col := c.data.columns[index]

@ -71,7 +71,7 @@ type winsize struct {
// Its not recommended write to buffer dirrectly, use package Print,Printf,Println fucntions instead. // Its not recommended write to buffer dirrectly, use package Print,Printf,Println fucntions instead.
var Screen *bytes.Buffer = new(bytes.Buffer) var Screen *bytes.Buffer = new(bytes.Buffer)
// Get relative or absolute coordinates // GetXY gets relative or absolute coordinates
// To get relative, set PCT flag to number: // To get relative, set PCT flag to number:
// //
// // Get 10% of total width to `x` and 20 to y // // Get 10% of total width to `x` and 20 to y
@ -145,7 +145,7 @@ func MoveTo(str string, x int, y int) (out string) {
}) })
} }
// Return carrier to start of line // ResetLine returns carrier to start of line
func ResetLine(str string) (out string) { func ResetLine(str string) (out string) {
return applyTransform(str, func(idx int, line string) string { return applyTransform(str, func(idx int, line string) string {
return fmt.Sprintf("%s%s", RESET_LINE, line) return fmt.Sprintf("%s%s", RESET_LINE, line)
@ -188,7 +188,7 @@ func Background(str string, color int) string {
}) })
} }
// Get console width // Width gets console width
func Width() int { func Width() int {
ws, err := getWinsize() ws, err := getWinsize()
@ -199,7 +199,7 @@ func Width() int {
return int(ws.Col) return int(ws.Col)
} }
// Get console height // Height gets console height
func Height() int { func Height() int {
ws, err := getWinsize() ws, err := getWinsize()
if err != nil { if err != nil {
@ -208,7 +208,7 @@ func Height() int {
return int(ws.Row) return int(ws.Row)
} }
// Get current height. Line count in Screen buffer. // CurrentHeight gets current height. Line count in Screen buffer.
func CurrentHeight() int { func CurrentHeight() int {
return strings.Count(Screen.String(), "\n") return strings.Count(Screen.String(), "\n")
} }

@ -1,4 +1,4 @@
// +build windows plan9 solaris // +build plan9 solaris
package goterm package goterm

@ -4,6 +4,7 @@ package goterm
import ( import (
"os" "os"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )

23
vendor/github.com/buger/goterm/terminal_windows.go generated vendored Normal file

@ -0,0 +1,23 @@
// +build windows
package goterm
import (
"os"
"golang.org/x/sys/windows"
)
func getWinsize() (*winsize, error) {
ws := new(winsize)
fd := os.Stdout.Fd()
var info windows.ConsoleScreenBufferInfo
if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
return nil, err
}
ws.Col = uint16(info.Window.Right - info.Window.Left + 1)
ws.Row = uint16(info.Window.Bottom - info.Window.Top + 1)
return ws, nil
}

@ -1,7 +1,6 @@
package volume package volume
import ( import (
"log"
"net/http" "net/http"
"github.com/docker/go-plugins-helpers/sdk" "github.com/docker/go-plugins-helpers/sdk"
@ -130,7 +129,6 @@ func NewHandler(driver Driver) *Handler {
func (h *Handler) initMux() { func (h *Handler) initMux() {
h.HandleFunc(createPath, func(w http.ResponseWriter, r *http.Request) { h.HandleFunc(createPath, func(w http.ResponseWriter, r *http.Request) {
log.Println("Entering go-plugins-helpers createPath")
req := &CreateRequest{} req := &CreateRequest{}
err := sdk.DecodeRequest(w, r, req) err := sdk.DecodeRequest(w, r, req)
if err != nil { if err != nil {
@ -144,7 +142,6 @@ func (h *Handler) initMux() {
sdk.EncodeResponse(w, struct{}{}, false) sdk.EncodeResponse(w, struct{}{}, false)
}) })
h.HandleFunc(removePath, func(w http.ResponseWriter, r *http.Request) { h.HandleFunc(removePath, func(w http.ResponseWriter, r *http.Request) {
log.Println("Entering go-plugins-helpers removePath")
req := &RemoveRequest{} req := &RemoveRequest{}
err := sdk.DecodeRequest(w, r, req) err := sdk.DecodeRequest(w, r, req)
if err != nil { if err != nil {
@ -158,7 +155,6 @@ func (h *Handler) initMux() {
sdk.EncodeResponse(w, struct{}{}, false) sdk.EncodeResponse(w, struct{}{}, false)
}) })
h.HandleFunc(mountPath, func(w http.ResponseWriter, r *http.Request) { h.HandleFunc(mountPath, func(w http.ResponseWriter, r *http.Request) {
log.Println("Entering go-plugins-helpers mountPath")
req := &MountRequest{} req := &MountRequest{}
err := sdk.DecodeRequest(w, r, req) err := sdk.DecodeRequest(w, r, req)
if err != nil { if err != nil {
@ -172,7 +168,6 @@ func (h *Handler) initMux() {
sdk.EncodeResponse(w, res, false) sdk.EncodeResponse(w, res, false)
}) })
h.HandleFunc(hostVirtualPath, func(w http.ResponseWriter, r *http.Request) { h.HandleFunc(hostVirtualPath, func(w http.ResponseWriter, r *http.Request) {
log.Println("Entering go-plugins-helpers hostVirtualPath")
req := &PathRequest{} req := &PathRequest{}
err := sdk.DecodeRequest(w, r, req) err := sdk.DecodeRequest(w, r, req)
if err != nil { if err != nil {
@ -186,7 +181,6 @@ func (h *Handler) initMux() {
sdk.EncodeResponse(w, res, false) sdk.EncodeResponse(w, res, false)
}) })
h.HandleFunc(getPath, func(w http.ResponseWriter, r *http.Request) { h.HandleFunc(getPath, func(w http.ResponseWriter, r *http.Request) {
log.Println("Entering go-plugins-helpers getPath")
req := &GetRequest{} req := &GetRequest{}
err := sdk.DecodeRequest(w, r, req) err := sdk.DecodeRequest(w, r, req)
if err != nil { if err != nil {
@ -200,7 +194,6 @@ func (h *Handler) initMux() {
sdk.EncodeResponse(w, res, false) sdk.EncodeResponse(w, res, false)
}) })
h.HandleFunc(unmountPath, func(w http.ResponseWriter, r *http.Request) { h.HandleFunc(unmountPath, func(w http.ResponseWriter, r *http.Request) {
log.Println("Entering go-plugins-helpers unmountPath")
req := &UnmountRequest{} req := &UnmountRequest{}
err := sdk.DecodeRequest(w, r, req) err := sdk.DecodeRequest(w, r, req)
if err != nil { if err != nil {
@ -214,7 +207,6 @@ func (h *Handler) initMux() {
sdk.EncodeResponse(w, struct{}{}, false) sdk.EncodeResponse(w, struct{}{}, false)
}) })
h.HandleFunc(listPath, func(w http.ResponseWriter, r *http.Request) { h.HandleFunc(listPath, func(w http.ResponseWriter, r *http.Request) {
log.Println("Entering go-plugins-helpers listPath")
res, err := h.driver.List() res, err := h.driver.List()
if err != nil { if err != nil {
sdk.EncodeResponse(w, NewErrorResponse(err.Error()), true) sdk.EncodeResponse(w, NewErrorResponse(err.Error()), true)
@ -224,7 +216,6 @@ func (h *Handler) initMux() {
}) })
h.HandleFunc(capabilitiesPath, func(w http.ResponseWriter, r *http.Request) { h.HandleFunc(capabilitiesPath, func(w http.ResponseWriter, r *http.Request) {
log.Println("Entering go-plugins-helpers capabilitiesPath")
sdk.EncodeResponse(w, h.driver.Capabilities(), false) sdk.EncodeResponse(w, h.driver.Capabilities(), false)
}) })
} }

0
vendor/github.com/felixge/httpsnoop/.gitignore generated vendored Normal file

6
vendor/github.com/felixge/httpsnoop/.travis.yml generated vendored Normal file

@ -0,0 +1,6 @@
language: go
go:
- 1.6
- 1.7
- 1.8

19
vendor/github.com/felixge/httpsnoop/LICENSE.txt generated vendored Normal file

@ -0,0 +1,19 @@
Copyright (c) 2016 Felix Geisendörfer (felix@debuggable.com)
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.

10
vendor/github.com/felixge/httpsnoop/Makefile generated vendored Normal file

@ -0,0 +1,10 @@
.PHONY: ci generate clean
ci: clean generate
go test -v ./...
generate:
go generate .
clean:
rm -rf *_generated*.go

94
vendor/github.com/felixge/httpsnoop/README.md generated vendored Normal file

@ -0,0 +1,94 @@
# httpsnoop
Package httpsnoop provides an easy way to capture http related metrics (i.e.
response time, bytes written, and http status code) from your application's
http.Handlers.
Doing this requires non-trivial wrapping of the http.ResponseWriter interface,
which is also exposed for users interested in a more low-level API.
[![GoDoc](https://godoc.org/github.com/felixge/httpsnoop?status.svg)](https://godoc.org/github.com/felixge/httpsnoop)
[![Build Status](https://travis-ci.org/felixge/httpsnoop.svg?branch=master)](https://travis-ci.org/felixge/httpsnoop)
## Usage Example
```go
// myH is your app's http handler, perhaps a http.ServeMux or similar.
var myH http.Handler
// wrappedH wraps myH in order to log every request.
wrappedH := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
m := httpsnoop.CaptureMetrics(myH, w, r)
log.Printf(
"%s %s (code=%d dt=%s written=%d)",
r.Method,
r.URL,
m.Code,
m.Duration,
m.Written,
)
})
http.ListenAndServe(":8080", wrappedH)
```
## Why this package exists
Instrumenting an application's http.Handler is surprisingly difficult.
However if you google for e.g. "capture ResponseWriter status code" you'll find
lots of advise and code examples that suggest it to be a fairly trivial
undertaking. Unfortunately everything I've seen so far has a high chance of
breaking your application.
The main problem is that a `http.ResponseWriter` often implements additional
interfaces such as `http.Flusher`, `http.CloseNotifier`, `http.Hijacker`, `http.Pusher`, and
`io.ReaderFrom`. So the naive approach of just wrapping `http.ResponseWriter`
in your own struct that also implements the `http.ResponseWriter` interface
will hide the additional interfaces mentioned above. This has a high change of
introducing subtle bugs into any non-trivial application.
Another approach I've seen people take is to return a struct that implements
all of the interfaces above. However, that's also problematic, because it's
difficult to fake some of these interfaces behaviors when the underlying
`http.ResponseWriter` doesn't have an implementation. It's also dangerous,
because an application may choose to operate differently, merely because it
detects the presence of these additional interfaces.
This package solves this problem by checking which additional interfaces a
`http.ResponseWriter` implements, returning a wrapped version implementing the
exact same set of interfaces.
Additionally this package properly handles edge cases such as `WriteHeader` not
being called, or called more than once, as well as concurrent calls to
`http.ResponseWriter` methods, and even calls happening after the wrapped
`ServeHTTP` has already returned.
Unfortunately this package is not perfect either. It's possible that it is
still missing some interfaces provided by the go core (let me know if you find
one), and it won't work for applications adding their own interfaces into the
mix.
However, hopefully the explanation above has sufficiently scared you of rolling
your own solution to this problem. httpsnoop may still break your application,
but at least it tries to avoid it as much as possible.
Anyway, the real problem here is that smuggling additional interfaces inside
`http.ResponseWriter` is a problematic design choice, but it probably goes as
deep as the Go language specification itself. But that's okay, I still prefer
Go over the alternatives ;).
## Performance
```
BenchmarkBaseline-8 20000 94912 ns/op
BenchmarkCaptureMetrics-8 20000 95461 ns/op
```
As you can see, using `CaptureMetrics` on a vanilla http.Handler introduces an
overhead of ~500 ns per http request on my machine. However, the margin of
error appears to be larger than that, therefor it should be reasonable to
assume that the overhead introduced by `CaptureMetrics` is absolutely
negligible.
## License
MIT

84
vendor/github.com/felixge/httpsnoop/capture_metrics.go generated vendored Normal file

@ -0,0 +1,84 @@
package httpsnoop
import (
"io"
"net/http"
"sync"
"time"
)
// Metrics holds metrics captured from CaptureMetrics.
type Metrics struct {
// Code is the first http response code passed to the WriteHeader func of
// the ResponseWriter. If no such call is made, a default code of 200 is
// assumed instead.
Code int
// Duration is the time it took to execute the handler.
Duration time.Duration
// Written is the number of bytes successfully written by the Write or
// ReadFrom function of the ResponseWriter. ResponseWriters may also write
// data to their underlaying connection directly (e.g. headers), but those
// are not tracked. Therefor the number of Written bytes will usually match
// the size of the response body.
Written int64
}
// CaptureMetrics wraps the given hnd, executes it with the given w and r, and
// returns the metrics it captured from it.
func CaptureMetrics(hnd http.Handler, w http.ResponseWriter, r *http.Request) Metrics {
return CaptureMetricsFn(w, func(ww http.ResponseWriter) {
hnd.ServeHTTP(ww, r)
})
}
// CaptureMetricsFn wraps w and calls fn with the wrapped w and returns the
// resulting metrics. This is very similar to CaptureMetrics (which is just
// sugar on top of this func), but is a more usable interface if your
// application doesn't use the Go http.Handler interface.
func CaptureMetricsFn(w http.ResponseWriter, fn func(http.ResponseWriter)) Metrics {
var (
start = time.Now()
m = Metrics{Code: http.StatusOK}
headerWritten bool
lock sync.Mutex
hooks = Hooks{
WriteHeader: func(next WriteHeaderFunc) WriteHeaderFunc {
return func(code int) {
next(code)
lock.Lock()
defer lock.Unlock()
if !headerWritten {
m.Code = code
headerWritten = true
}
}
},
Write: func(next WriteFunc) WriteFunc {
return func(p []byte) (int, error) {
n, err := next(p)
lock.Lock()
defer lock.Unlock()
m.Written += int64(n)
headerWritten = true
return n, err
}
},
ReadFrom: func(next ReadFromFunc) ReadFromFunc {
return func(src io.Reader) (int64, error) {
n, err := next(src)
lock.Lock()
defer lock.Unlock()
headerWritten = true
m.Written += n
return n, err
}
},
}
)
fn(Wrap(w, hooks))
m.Duration = time.Since(start)
return m
}

10
vendor/github.com/felixge/httpsnoop/docs.go generated vendored Normal file

@ -0,0 +1,10 @@
// Package httpsnoop provides an easy way to capture http related metrics (i.e.
// response time, bytes written, and http status code) from your application's
// http.Handlers.
//
// Doing this requires non-trivial wrapping of the http.ResponseWriter
// interface, which is also exposed for users interested in a more low-level
// API.
package httpsnoop
//go:generate go run codegen/main.go

3
vendor/github.com/felixge/httpsnoop/go.mod generated vendored Normal file

@ -0,0 +1,3 @@
module github.com/felixge/httpsnoop
go 1.13

@ -0,0 +1,385 @@
// +build go1.8
// Code generated by "httpsnoop/codegen"; DO NOT EDIT
package httpsnoop
import (
"bufio"
"io"
"net"
"net/http"
)
// HeaderFunc is part of the http.ResponseWriter interface.
type HeaderFunc func() http.Header
// WriteHeaderFunc is part of the http.ResponseWriter interface.
type WriteHeaderFunc func(code int)
// WriteFunc is part of the http.ResponseWriter interface.
type WriteFunc func(b []byte) (int, error)
// FlushFunc is part of the http.Flusher interface.
type FlushFunc func()
// CloseNotifyFunc is part of the http.CloseNotifier interface.
type CloseNotifyFunc func() <-chan bool
// HijackFunc is part of the http.Hijacker interface.
type HijackFunc func() (net.Conn, *bufio.ReadWriter, error)
// ReadFromFunc is part of the io.ReaderFrom interface.
type ReadFromFunc func(src io.Reader) (int64, error)
// PushFunc is part of the http.Pusher interface.
type PushFunc func(target string, opts *http.PushOptions) error
// Hooks defines a set of method interceptors for methods included in
// http.ResponseWriter as well as some others. You can think of them as
// middleware for the function calls they target. See Wrap for more details.
type Hooks struct {
Header func(HeaderFunc) HeaderFunc
WriteHeader func(WriteHeaderFunc) WriteHeaderFunc
Write func(WriteFunc) WriteFunc
Flush func(FlushFunc) FlushFunc
CloseNotify func(CloseNotifyFunc) CloseNotifyFunc
Hijack func(HijackFunc) HijackFunc
ReadFrom func(ReadFromFunc) ReadFromFunc
Push func(PushFunc) PushFunc
}
// Wrap returns a wrapped version of w that provides the exact same interface
// as w. Specifically if w implements any combination of:
//
// - http.Flusher
// - http.CloseNotifier
// - http.Hijacker
// - io.ReaderFrom
// - http.Pusher
//
// The wrapped version will implement the exact same combination. If no hooks
// are set, the wrapped version also behaves exactly as w. Hooks targeting
// methods not supported by w are ignored. Any other hooks will intercept the
// method they target and may modify the call's arguments and/or return values.
// The CaptureMetrics implementation serves as a working example for how the
// hooks can be used.
func Wrap(w http.ResponseWriter, hooks Hooks) http.ResponseWriter {
rw := &rw{w: w, h: hooks}
_, i0 := w.(http.Flusher)
_, i1 := w.(http.CloseNotifier)
_, i2 := w.(http.Hijacker)
_, i3 := w.(io.ReaderFrom)
_, i4 := w.(http.Pusher)
switch {
// combination 1/32
case !i0 && !i1 && !i2 && !i3 && !i4:
return struct {
http.ResponseWriter
}{rw}
// combination 2/32
case !i0 && !i1 && !i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.Pusher
}{rw, rw}
// combination 3/32
case !i0 && !i1 && !i2 && i3 && !i4:
return struct {
http.ResponseWriter
io.ReaderFrom
}{rw, rw}
// combination 4/32
case !i0 && !i1 && !i2 && i3 && i4:
return struct {
http.ResponseWriter
io.ReaderFrom
http.Pusher
}{rw, rw, rw}
// combination 5/32
case !i0 && !i1 && i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.Hijacker
}{rw, rw}
// combination 6/32
case !i0 && !i1 && i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.Hijacker
http.Pusher
}{rw, rw, rw}
// combination 7/32
case !i0 && !i1 && i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.Hijacker
io.ReaderFrom
}{rw, rw, rw}
// combination 8/32
case !i0 && !i1 && i2 && i3 && i4:
return struct {
http.ResponseWriter
http.Hijacker
io.ReaderFrom
http.Pusher
}{rw, rw, rw, rw}
// combination 9/32
case !i0 && i1 && !i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.CloseNotifier
}{rw, rw}
// combination 10/32
case !i0 && i1 && !i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.CloseNotifier
http.Pusher
}{rw, rw, rw}
// combination 11/32
case !i0 && i1 && !i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.CloseNotifier
io.ReaderFrom
}{rw, rw, rw}
// combination 12/32
case !i0 && i1 && !i2 && i3 && i4:
return struct {
http.ResponseWriter
http.CloseNotifier
io.ReaderFrom
http.Pusher
}{rw, rw, rw, rw}
// combination 13/32
case !i0 && i1 && i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.CloseNotifier
http.Hijacker
}{rw, rw, rw}
// combination 14/32
case !i0 && i1 && i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.CloseNotifier
http.Hijacker
http.Pusher
}{rw, rw, rw, rw}
// combination 15/32
case !i0 && i1 && i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.CloseNotifier
http.Hijacker
io.ReaderFrom
}{rw, rw, rw, rw}
// combination 16/32
case !i0 && i1 && i2 && i3 && i4:
return struct {
http.ResponseWriter
http.CloseNotifier
http.Hijacker
io.ReaderFrom
http.Pusher
}{rw, rw, rw, rw, rw}
// combination 17/32
case i0 && !i1 && !i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.Flusher
}{rw, rw}
// combination 18/32
case i0 && !i1 && !i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.Flusher
http.Pusher
}{rw, rw, rw}
// combination 19/32
case i0 && !i1 && !i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.Flusher
io.ReaderFrom
}{rw, rw, rw}
// combination 20/32
case i0 && !i1 && !i2 && i3 && i4:
return struct {
http.ResponseWriter
http.Flusher
io.ReaderFrom
http.Pusher
}{rw, rw, rw, rw}
// combination 21/32
case i0 && !i1 && i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.Flusher
http.Hijacker
}{rw, rw, rw}
// combination 22/32
case i0 && !i1 && i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.Flusher
http.Hijacker
http.Pusher
}{rw, rw, rw, rw}
// combination 23/32
case i0 && !i1 && i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.Flusher
http.Hijacker
io.ReaderFrom
}{rw, rw, rw, rw}
// combination 24/32
case i0 && !i1 && i2 && i3 && i4:
return struct {
http.ResponseWriter
http.Flusher
http.Hijacker
io.ReaderFrom
http.Pusher
}{rw, rw, rw, rw, rw}
// combination 25/32
case i0 && i1 && !i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
}{rw, rw, rw}
// combination 26/32
case i0 && i1 && !i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
http.Pusher
}{rw, rw, rw, rw}
// combination 27/32
case i0 && i1 && !i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
io.ReaderFrom
}{rw, rw, rw, rw}
// combination 28/32
case i0 && i1 && !i2 && i3 && i4:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
io.ReaderFrom
http.Pusher
}{rw, rw, rw, rw, rw}
// combination 29/32
case i0 && i1 && i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
http.Hijacker
}{rw, rw, rw, rw}
// combination 30/32
case i0 && i1 && i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
http.Hijacker
http.Pusher
}{rw, rw, rw, rw, rw}
// combination 31/32
case i0 && i1 && i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
http.Hijacker
io.ReaderFrom
}{rw, rw, rw, rw, rw}
// combination 32/32
case i0 && i1 && i2 && i3 && i4:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
http.Hijacker
io.ReaderFrom
http.Pusher
}{rw, rw, rw, rw, rw, rw}
}
panic("unreachable")
}
type rw struct {
w http.ResponseWriter
h Hooks
}
func (w *rw) Header() http.Header {
f := w.w.(http.ResponseWriter).Header
if w.h.Header != nil {
f = w.h.Header(f)
}
return f()
}
func (w *rw) WriteHeader(code int) {
f := w.w.(http.ResponseWriter).WriteHeader
if w.h.WriteHeader != nil {
f = w.h.WriteHeader(f)
}
f(code)
}
func (w *rw) Write(b []byte) (int, error) {
f := w.w.(http.ResponseWriter).Write
if w.h.Write != nil {
f = w.h.Write(f)
}
return f(b)
}
func (w *rw) Flush() {
f := w.w.(http.Flusher).Flush
if w.h.Flush != nil {
f = w.h.Flush(f)
}
f()
}
func (w *rw) CloseNotify() <-chan bool {
f := w.w.(http.CloseNotifier).CloseNotify
if w.h.CloseNotify != nil {
f = w.h.CloseNotify(f)
}
return f()
}
func (w *rw) Hijack() (net.Conn, *bufio.ReadWriter, error) {
f := w.w.(http.Hijacker).Hijack
if w.h.Hijack != nil {
f = w.h.Hijack(f)
}
return f()
}
func (w *rw) ReadFrom(src io.Reader) (int64, error) {
f := w.w.(io.ReaderFrom).ReadFrom
if w.h.ReadFrom != nil {
f = w.h.ReadFrom(f)
}
return f(src)
}
func (w *rw) Push(target string, opts *http.PushOptions) error {
f := w.w.(http.Pusher).Push
if w.h.Push != nil {
f = w.h.Push(f)
}
return f(target, opts)
}

@ -0,0 +1,243 @@
// +build !go1.8
// Code generated by "httpsnoop/codegen"; DO NOT EDIT
package httpsnoop
import (
"bufio"
"io"
"net"
"net/http"
)
// HeaderFunc is part of the http.ResponseWriter interface.
type HeaderFunc func() http.Header
// WriteHeaderFunc is part of the http.ResponseWriter interface.
type WriteHeaderFunc func(code int)
// WriteFunc is part of the http.ResponseWriter interface.
type WriteFunc func(b []byte) (int, error)
// FlushFunc is part of the http.Flusher interface.
type FlushFunc func()
// CloseNotifyFunc is part of the http.CloseNotifier interface.
type CloseNotifyFunc func() <-chan bool
// HijackFunc is part of the http.Hijacker interface.
type HijackFunc func() (net.Conn, *bufio.ReadWriter, error)
// ReadFromFunc is part of the io.ReaderFrom interface.
type ReadFromFunc func(src io.Reader) (int64, error)
// Hooks defines a set of method interceptors for methods included in
// http.ResponseWriter as well as some others. You can think of them as
// middleware for the function calls they target. See Wrap for more details.
type Hooks struct {
Header func(HeaderFunc) HeaderFunc
WriteHeader func(WriteHeaderFunc) WriteHeaderFunc
Write func(WriteFunc) WriteFunc
Flush func(FlushFunc) FlushFunc
CloseNotify func(CloseNotifyFunc) CloseNotifyFunc
Hijack func(HijackFunc) HijackFunc
ReadFrom func(ReadFromFunc) ReadFromFunc
}
// Wrap returns a wrapped version of w that provides the exact same interface
// as w. Specifically if w implements any combination of:
//
// - http.Flusher
// - http.CloseNotifier
// - http.Hijacker
// - io.ReaderFrom
//
// The wrapped version will implement the exact same combination. If no hooks
// are set, the wrapped version also behaves exactly as w. Hooks targeting
// methods not supported by w are ignored. Any other hooks will intercept the
// method they target and may modify the call's arguments and/or return values.
// The CaptureMetrics implementation serves as a working example for how the
// hooks can be used.
func Wrap(w http.ResponseWriter, hooks Hooks) http.ResponseWriter {
rw := &rw{w: w, h: hooks}
_, i0 := w.(http.Flusher)
_, i1 := w.(http.CloseNotifier)
_, i2 := w.(http.Hijacker)
_, i3 := w.(io.ReaderFrom)
switch {
// combination 1/16
case !i0 && !i1 && !i2 && !i3:
return struct {
http.ResponseWriter
}{rw}
// combination 2/16
case !i0 && !i1 && !i2 && i3:
return struct {
http.ResponseWriter
io.ReaderFrom
}{rw, rw}
// combination 3/16
case !i0 && !i1 && i2 && !i3:
return struct {
http.ResponseWriter
http.Hijacker
}{rw, rw}
// combination 4/16
case !i0 && !i1 && i2 && i3:
return struct {
http.ResponseWriter
http.Hijacker
io.ReaderFrom
}{rw, rw, rw}
// combination 5/16
case !i0 && i1 && !i2 && !i3:
return struct {
http.ResponseWriter
http.CloseNotifier
}{rw, rw}
// combination 6/16
case !i0 && i1 && !i2 && i3:
return struct {
http.ResponseWriter
http.CloseNotifier
io.ReaderFrom
}{rw, rw, rw}
// combination 7/16
case !i0 && i1 && i2 && !i3:
return struct {
http.ResponseWriter
http.CloseNotifier
http.Hijacker
}{rw, rw, rw}
// combination 8/16
case !i0 && i1 && i2 && i3:
return struct {
http.ResponseWriter
http.CloseNotifier
http.Hijacker
io.ReaderFrom
}{rw, rw, rw, rw}
// combination 9/16
case i0 && !i1 && !i2 && !i3:
return struct {
http.ResponseWriter
http.Flusher
}{rw, rw}
// combination 10/16
case i0 && !i1 && !i2 && i3:
return struct {
http.ResponseWriter
http.Flusher
io.ReaderFrom
}{rw, rw, rw}
// combination 11/16
case i0 && !i1 && i2 && !i3:
return struct {
http.ResponseWriter
http.Flusher
http.Hijacker
}{rw, rw, rw}
// combination 12/16
case i0 && !i1 && i2 && i3:
return struct {
http.ResponseWriter
http.Flusher
http.Hijacker
io.ReaderFrom
}{rw, rw, rw, rw}
// combination 13/16
case i0 && i1 && !i2 && !i3:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
}{rw, rw, rw}
// combination 14/16
case i0 && i1 && !i2 && i3:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
io.ReaderFrom
}{rw, rw, rw, rw}
// combination 15/16
case i0 && i1 && i2 && !i3:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
http.Hijacker
}{rw, rw, rw, rw}
// combination 16/16
case i0 && i1 && i2 && i3:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
http.Hijacker
io.ReaderFrom
}{rw, rw, rw, rw, rw}
}
panic("unreachable")
}
type rw struct {
w http.ResponseWriter
h Hooks
}
func (w *rw) Header() http.Header {
f := w.w.(http.ResponseWriter).Header
if w.h.Header != nil {
f = w.h.Header(f)
}
return f()
}
func (w *rw) WriteHeader(code int) {
f := w.w.(http.ResponseWriter).WriteHeader
if w.h.WriteHeader != nil {
f = w.h.WriteHeader(f)
}
f(code)
}
func (w *rw) Write(b []byte) (int, error) {
f := w.w.(http.ResponseWriter).Write
if w.h.Write != nil {
f = w.h.Write(f)
}
return f(b)
}
func (w *rw) Flush() {
f := w.w.(http.Flusher).Flush
if w.h.Flush != nil {
f = w.h.Flush(f)
}
f()
}
func (w *rw) CloseNotify() <-chan bool {
f := w.w.(http.CloseNotifier).CloseNotify
if w.h.CloseNotify != nil {
f = w.h.CloseNotify(f)
}
return f()
}
func (w *rw) Hijack() (net.Conn, *bufio.ReadWriter, error) {
f := w.w.(http.Hijacker).Hijack
if w.h.Hijack != nil {
f = w.h.Hijack(f)
}
return f()
}
func (w *rw) ReadFrom(src io.Reader) (int64, error) {
f := w.w.(io.ReaderFrom).ReadFrom
if w.h.ReadFrom != nil {
f = w.h.ReadFrom(f)
}
return f(src)
}

3
vendor/github.com/google/shlex/go.mod generated vendored Normal file

@ -0,0 +1,3 @@
module github.com/google/shlex
go 1.13

@ -1,8 +0,0 @@
language: go
go:
- 1.1
- 1.2
- 1.3
- 1.4
- tip

@ -1,28 +1,32 @@
gorilla/handlers gorilla/handlers
================ ================
[![GoDoc](https://godoc.org/github.com/gorilla/handlers?status.svg)](https://godoc.org/github.com/gorilla/handlers) [![Build Status](https://travis-ci.org/gorilla/handlers.svg?branch=master)](https://travis-ci.org/gorilla/handlers) [![GoDoc](https://godoc.org/github.com/gorilla/handlers?status.svg)](https://godoc.org/github.com/gorilla/handlers)
[![CircleCI](https://circleci.com/gh/gorilla/handlers.svg?style=svg)](https://circleci.com/gh/gorilla/handlers)
[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/handlers/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/handlers?badge)
Package handlers is a collection of handlers (aka "HTTP middleware") for use Package handlers is a collection of handlers (aka "HTTP middleware") for use
with Go's `net/http` package (or any framework supporting `http.Handler`), including: with Go's `net/http` package (or any framework supporting `http.Handler`), including:
* `LoggingHandler` for logging HTTP requests in the Apache [Common Log * [**LoggingHandler**](https://godoc.org/github.com/gorilla/handlers#LoggingHandler) for logging HTTP requests in the Apache [Common Log
Format](http://httpd.apache.org/docs/2.2/logs.html#common). Format](http://httpd.apache.org/docs/2.2/logs.html#common).
* `CombinedLoggingHandler` for logging HTTP requests in the Apache [Combined Log * [**CombinedLoggingHandler**](https://godoc.org/github.com/gorilla/handlers#CombinedLoggingHandler) for logging HTTP requests in the Apache [Combined Log
Format](http://httpd.apache.org/docs/2.2/logs.html#combined) commonly used by Format](http://httpd.apache.org/docs/2.2/logs.html#combined) commonly used by
both Apache and nginx. both Apache and nginx.
* `CompressHandler` for gzipping responses. * [**CompressHandler**](https://godoc.org/github.com/gorilla/handlers#CompressHandler) for gzipping responses.
* `ContentTypeHandler` for validating requests against a list of accepted * [**ContentTypeHandler**](https://godoc.org/github.com/gorilla/handlers#ContentTypeHandler) for validating requests against a list of accepted
content types. content types.
* `MethodHandler` for matching HTTP methods against handlers in a * [**MethodHandler**](https://godoc.org/github.com/gorilla/handlers#MethodHandler) for matching HTTP methods against handlers in a
`map[string]http.Handler` `map[string]http.Handler`
* `ProxyHeaders` for populating `r.RemoteAddr` and `r.URL.Scheme` based on the * [**ProxyHeaders**](https://godoc.org/github.com/gorilla/handlers#ProxyHeaders) for populating `r.RemoteAddr` and `r.URL.Scheme` based on the
`X-Forwarded-For`, `X-Real-IP`, `X-Forwarded-Proto` and RFC7239 `Forwarded` `X-Forwarded-For`, `X-Real-IP`, `X-Forwarded-Proto` and RFC7239 `Forwarded`
headers when running a Go server behind a HTTP reverse proxy. headers when running a Go server behind a HTTP reverse proxy.
* `CanonicalHost` for re-directing to the preferred host when handling multiple * [**CanonicalHost**](https://godoc.org/github.com/gorilla/handlers#CanonicalHost) for re-directing to the preferred host when handling multiple
domains (i.e. multiple CNAME aliases). domains (i.e. multiple CNAME aliases).
* [**RecoveryHandler**](https://godoc.org/github.com/gorilla/handlers#RecoveryHandler) for recovering from unexpected panics.
Other handlers are documented [on the Gorilla Other handlers are documented [on the Gorilla
website](http://www.gorillatoolkit.org/pkg/handlers). website](https://www.gorillatoolkit.org/pkg/handlers).
## Example ## Example

@ -18,7 +18,6 @@ type canonical struct {
// //
// Note: If the provided domain is considered invalid by url.Parse or otherwise // Note: If the provided domain is considered invalid by url.Parse or otherwise
// returns an empty scheme or host, clients are not re-directed. // returns an empty scheme or host, clients are not re-directed.
// not re-directed.
// //
// Example: // Example:
// //
@ -54,7 +53,11 @@ func (c canonical) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !strings.EqualFold(cleanHost(r.Host), dest.Host) { if !strings.EqualFold(cleanHost(r.Host), dest.Host) {
// Re-build the destination URL // Re-build the destination URL
dest := dest.Scheme + "://" + dest.Host + r.URL.Path dest := dest.Scheme + "://" + dest.Host + r.URL.Path
if r.URL.RawQuery != "" {
dest += "?" + r.URL.RawQuery
}
http.Redirect(w, r, dest, c.code) http.Redirect(w, r, dest, c.code)
return
} }
c.h.ServeHTTP(w, r) c.h.ServeHTTP(w, r)

@ -10,75 +10,134 @@ import (
"io" "io"
"net/http" "net/http"
"strings" "strings"
"github.com/felixge/httpsnoop"
) )
const acceptEncoding string = "Accept-Encoding"
type compressResponseWriter struct { type compressResponseWriter struct {
io.Writer compressor io.Writer
http.ResponseWriter w http.ResponseWriter
http.Hijacker
} }
func (w *compressResponseWriter) Header() http.Header { func (cw *compressResponseWriter) WriteHeader(c int) {
return w.ResponseWriter.Header() cw.w.Header().Del("Content-Length")
cw.w.WriteHeader(c)
} }
func (w *compressResponseWriter) Write(b []byte) (int, error) { func (cw *compressResponseWriter) Write(b []byte) (int, error) {
h := w.ResponseWriter.Header() h := cw.w.Header()
if h.Get("Content-Type") == "" { if h.Get("Content-Type") == "" {
h.Set("Content-Type", http.DetectContentType(b)) h.Set("Content-Type", http.DetectContentType(b))
} }
h.Del("Content-Length")
return w.Writer.Write(b) return cw.compressor.Write(b)
}
func (cw *compressResponseWriter) ReadFrom(r io.Reader) (int64, error) {
return io.Copy(cw.compressor, r)
}
type flusher interface {
Flush() error
}
func (w *compressResponseWriter) Flush() {
// Flush compressed data if compressor supports it.
if f, ok := w.compressor.(flusher); ok {
f.Flush()
}
// Flush HTTP response.
if f, ok := w.w.(http.Flusher); ok {
f.Flush()
}
} }
// CompressHandler gzip compresses HTTP responses for clients that support it // CompressHandler gzip compresses HTTP responses for clients that support it
// via the 'Accept-Encoding' header. // via the 'Accept-Encoding' header.
//
// Compressing TLS traffic may leak the page contents to an attacker if the
// page contains user input: http://security.stackexchange.com/a/102015/12208
func CompressHandler(h http.Handler) http.Handler { func CompressHandler(h http.Handler) http.Handler {
return CompressHandlerLevel(h, gzip.DefaultCompression)
}
// CompressHandlerLevel gzip compresses HTTP responses with specified compression level
// for clients that support it via the 'Accept-Encoding' header.
//
// The compression level should be gzip.DefaultCompression, gzip.NoCompression,
// or any integer value between gzip.BestSpeed and gzip.BestCompression inclusive.
// gzip.DefaultCompression is used in case of invalid compression level.
func CompressHandlerLevel(h http.Handler, level int) http.Handler {
if level < gzip.DefaultCompression || level > gzip.BestCompression {
level = gzip.DefaultCompression
}
const (
gzipEncoding = "gzip"
flateEncoding = "deflate"
)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
L: // detect what encoding to use
for _, enc := range strings.Split(r.Header.Get("Accept-Encoding"), ",") { var encoding string
switch strings.TrimSpace(enc) { for _, curEnc := range strings.Split(r.Header.Get(acceptEncoding), ",") {
case "gzip": curEnc = strings.TrimSpace(curEnc)
w.Header().Set("Content-Encoding", "gzip") if curEnc == gzipEncoding || curEnc == flateEncoding {
w.Header().Add("Vary", "Accept-Encoding") encoding = curEnc
break
gw := gzip.NewWriter(w)
defer gw.Close()
h, hok := w.(http.Hijacker)
if !hok { /* w is not Hijacker... oh well... */
h = nil
}
w = &compressResponseWriter{
Writer: gw,
ResponseWriter: w,
Hijacker: h,
}
break L
case "deflate":
w.Header().Set("Content-Encoding", "deflate")
w.Header().Add("Vary", "Accept-Encoding")
fw, _ := flate.NewWriter(w, flate.DefaultCompression)
defer fw.Close()
h, hok := w.(http.Hijacker)
if !hok { /* w is not Hijacker... oh well... */
h = nil
}
w = &compressResponseWriter{
Writer: fw,
ResponseWriter: w,
Hijacker: h,
}
break L
} }
} }
// always add Accept-Encoding to Vary to prevent intermediate caches corruption
w.Header().Add("Vary", acceptEncoding)
// if we weren't able to identify an encoding we're familiar with, pass on the
// request to the handler and return
if encoding == "" {
h.ServeHTTP(w, r)
return
}
if r.Header.Get("Upgrade") != "" {
h.ServeHTTP(w, r)
return
}
// wrap the ResponseWriter with the writer for the chosen encoding
var encWriter io.WriteCloser
if encoding == gzipEncoding {
encWriter, _ = gzip.NewWriterLevel(w, level)
} else if encoding == flateEncoding {
encWriter, _ = flate.NewWriter(w, level)
}
defer encWriter.Close()
w.Header().Set("Content-Encoding", encoding)
r.Header.Del(acceptEncoding)
cw := &compressResponseWriter{
w: w,
compressor: encWriter,
}
w = httpsnoop.Wrap(w, httpsnoop.Hooks{
Write: func(httpsnoop.WriteFunc) httpsnoop.WriteFunc {
return cw.Write
},
WriteHeader: func(httpsnoop.WriteHeaderFunc) httpsnoop.WriteHeaderFunc {
return cw.WriteHeader
},
Flush: func(httpsnoop.FlushFunc) httpsnoop.FlushFunc {
return cw.Flush
},
ReadFrom: func(rff httpsnoop.ReadFromFunc) httpsnoop.ReadFromFunc {
return cw.ReadFrom
},
})
h.ServeHTTP(w, r) h.ServeHTTP(w, r)
}) })
} }

355
vendor/github.com/gorilla/handlers/cors.go generated vendored Normal file

@ -0,0 +1,355 @@
package handlers
import (
"net/http"
"strconv"
"strings"
)
// CORSOption represents a functional option for configuring the CORS middleware.
type CORSOption func(*cors) error
type cors struct {
h http.Handler
allowedHeaders []string
allowedMethods []string
allowedOrigins []string
allowedOriginValidator OriginValidator
exposedHeaders []string
maxAge int
ignoreOptions bool
allowCredentials bool
optionStatusCode int
}
// OriginValidator takes an origin string and returns whether or not that origin is allowed.
type OriginValidator func(string) bool
var (
defaultCorsOptionStatusCode = 200
defaultCorsMethods = []string{"GET", "HEAD", "POST"}
defaultCorsHeaders = []string{"Accept", "Accept-Language", "Content-Language", "Origin"}
// (WebKit/Safari v9 sends the Origin header by default in AJAX requests)
)
const (
corsOptionMethod string = "OPTIONS"
corsAllowOriginHeader string = "Access-Control-Allow-Origin"
corsExposeHeadersHeader string = "Access-Control-Expose-Headers"
corsMaxAgeHeader string = "Access-Control-Max-Age"
corsAllowMethodsHeader string = "Access-Control-Allow-Methods"
corsAllowHeadersHeader string = "Access-Control-Allow-Headers"
corsAllowCredentialsHeader string = "Access-Control-Allow-Credentials"
corsRequestMethodHeader string = "Access-Control-Request-Method"
corsRequestHeadersHeader string = "Access-Control-Request-Headers"
corsOriginHeader string = "Origin"
corsVaryHeader string = "Vary"
corsOriginMatchAll string = "*"
)
func (ch *cors) ServeHTTP(w http.ResponseWriter, r *http.Request) {
origin := r.Header.Get(corsOriginHeader)
if !ch.isOriginAllowed(origin) {
if r.Method != corsOptionMethod || ch.ignoreOptions {
ch.h.ServeHTTP(w, r)
}
return
}
if r.Method == corsOptionMethod {
if ch.ignoreOptions {
ch.h.ServeHTTP(w, r)
return
}
if _, ok := r.Header[corsRequestMethodHeader]; !ok {
w.WriteHeader(http.StatusBadRequest)
return
}
method := r.Header.Get(corsRequestMethodHeader)
if !ch.isMatch(method, ch.allowedMethods) {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
requestHeaders := strings.Split(r.Header.Get(corsRequestHeadersHeader), ",")
allowedHeaders := []string{}
for _, v := range requestHeaders {
canonicalHeader := http.CanonicalHeaderKey(strings.TrimSpace(v))
if canonicalHeader == "" || ch.isMatch(canonicalHeader, defaultCorsHeaders) {
continue
}
if !ch.isMatch(canonicalHeader, ch.allowedHeaders) {
w.WriteHeader(http.StatusForbidden)
return
}
allowedHeaders = append(allowedHeaders, canonicalHeader)
}
if len(allowedHeaders) > 0 {
w.Header().Set(corsAllowHeadersHeader, strings.Join(allowedHeaders, ","))
}
if ch.maxAge > 0 {
w.Header().Set(corsMaxAgeHeader, strconv.Itoa(ch.maxAge))
}
if !ch.isMatch(method, defaultCorsMethods) {
w.Header().Set(corsAllowMethodsHeader, method)
}
} else {
if len(ch.exposedHeaders) > 0 {
w.Header().Set(corsExposeHeadersHeader, strings.Join(ch.exposedHeaders, ","))
}
}
if ch.allowCredentials {
w.Header().Set(corsAllowCredentialsHeader, "true")
}
if len(ch.allowedOrigins) > 1 {
w.Header().Set(corsVaryHeader, corsOriginHeader)
}
returnOrigin := origin
if ch.allowedOriginValidator == nil && len(ch.allowedOrigins) == 0 {
returnOrigin = "*"
} else {
for _, o := range ch.allowedOrigins {
// A configuration of * is different than explicitly setting an allowed
// origin. Returning arbitrary origin headers in an access control allow
// origin header is unsafe and is not required by any use case.
if o == corsOriginMatchAll {
returnOrigin = "*"
break
}
}
}
w.Header().Set(corsAllowOriginHeader, returnOrigin)
if r.Method == corsOptionMethod {
w.WriteHeader(ch.optionStatusCode)
return
}
ch.h.ServeHTTP(w, r)
}
// CORS provides Cross-Origin Resource Sharing middleware.
// Example:
//
// import (
// "net/http"
//
// "github.com/gorilla/handlers"
// "github.com/gorilla/mux"
// )
//
// func main() {
// r := mux.NewRouter()
// r.HandleFunc("/users", UserEndpoint)
// r.HandleFunc("/projects", ProjectEndpoint)
//
// // Apply the CORS middleware to our top-level router, with the defaults.
// http.ListenAndServe(":8000", handlers.CORS()(r))
// }
//
func CORS(opts ...CORSOption) func(http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
ch := parseCORSOptions(opts...)
ch.h = h
return ch
}
}
func parseCORSOptions(opts ...CORSOption) *cors {
ch := &cors{
allowedMethods: defaultCorsMethods,
allowedHeaders: defaultCorsHeaders,
allowedOrigins: []string{},
optionStatusCode: defaultCorsOptionStatusCode,
}
for _, option := range opts {
option(ch)
}
return ch
}
//
// Functional options for configuring CORS.
//
// AllowedHeaders adds the provided headers to the list of allowed headers in a
// CORS request.
// This is an append operation so the headers Accept, Accept-Language,
// and Content-Language are always allowed.
// Content-Type must be explicitly declared if accepting Content-Types other than
// application/x-www-form-urlencoded, multipart/form-data, or text/plain.
func AllowedHeaders(headers []string) CORSOption {
return func(ch *cors) error {
for _, v := range headers {
normalizedHeader := http.CanonicalHeaderKey(strings.TrimSpace(v))
if normalizedHeader == "" {
continue
}
if !ch.isMatch(normalizedHeader, ch.allowedHeaders) {
ch.allowedHeaders = append(ch.allowedHeaders, normalizedHeader)
}
}
return nil
}
}
// AllowedMethods can be used to explicitly allow methods in the
// Access-Control-Allow-Methods header.
// This is a replacement operation so you must also
// pass GET, HEAD, and POST if you wish to support those methods.
func AllowedMethods(methods []string) CORSOption {
return func(ch *cors) error {
ch.allowedMethods = []string{}
for _, v := range methods {
normalizedMethod := strings.ToUpper(strings.TrimSpace(v))
if normalizedMethod == "" {
continue
}
if !ch.isMatch(normalizedMethod, ch.allowedMethods) {
ch.allowedMethods = append(ch.allowedMethods, normalizedMethod)
}
}
return nil
}
}
// AllowedOrigins sets the allowed origins for CORS requests, as used in the
// 'Allow-Access-Control-Origin' HTTP header.
// Note: Passing in a []string{"*"} will allow any domain.
func AllowedOrigins(origins []string) CORSOption {
return func(ch *cors) error {
for _, v := range origins {
if v == corsOriginMatchAll {
ch.allowedOrigins = []string{corsOriginMatchAll}
return nil
}
}
ch.allowedOrigins = origins
return nil
}
}
// AllowedOriginValidator sets a function for evaluating allowed origins in CORS requests, represented by the
// 'Allow-Access-Control-Origin' HTTP header.
func AllowedOriginValidator(fn OriginValidator) CORSOption {
return func(ch *cors) error {
ch.allowedOriginValidator = fn
return nil
}
}
// OptionStatusCode sets a custom status code on the OPTIONS requests.
// Default behaviour sets it to 200 to reflect best practices. This is option is not mandatory
// and can be used if you need a custom status code (i.e 204).
//
// More informations on the spec:
// https://fetch.spec.whatwg.org/#cors-preflight-fetch
func OptionStatusCode(code int) CORSOption {
return func(ch *cors) error {
ch.optionStatusCode = code
return nil
}
}
// ExposedHeaders can be used to specify headers that are available
// and will not be stripped out by the user-agent.
func ExposedHeaders(headers []string) CORSOption {
return func(ch *cors) error {
ch.exposedHeaders = []string{}
for _, v := range headers {
normalizedHeader := http.CanonicalHeaderKey(strings.TrimSpace(v))
if normalizedHeader == "" {
continue
}
if !ch.isMatch(normalizedHeader, ch.exposedHeaders) {
ch.exposedHeaders = append(ch.exposedHeaders, normalizedHeader)
}
}
return nil
}
}
// MaxAge determines the maximum age (in seconds) between preflight requests. A
// maximum of 10 minutes is allowed. An age above this value will default to 10
// minutes.
func MaxAge(age int) CORSOption {
return func(ch *cors) error {
// Maximum of 10 minutes.
if age > 600 {
age = 600
}
ch.maxAge = age
return nil
}
}
// IgnoreOptions causes the CORS middleware to ignore OPTIONS requests, instead
// passing them through to the next handler. This is useful when your application
// or framework has a pre-existing mechanism for responding to OPTIONS requests.
func IgnoreOptions() CORSOption {
return func(ch *cors) error {
ch.ignoreOptions = true
return nil
}
}
// AllowCredentials can be used to specify that the user agent may pass
// authentication details along with the request.
func AllowCredentials() CORSOption {
return func(ch *cors) error {
ch.allowCredentials = true
return nil
}
}
func (ch *cors) isOriginAllowed(origin string) bool {
if origin == "" {
return false
}
if ch.allowedOriginValidator != nil {
return ch.allowedOriginValidator(origin)
}
if len(ch.allowedOrigins) == 0 {
return true
}
for _, allowedOrigin := range ch.allowedOrigins {
if allowedOrigin == origin || allowedOrigin == corsOriginMatchAll {
return true
}
}
return false
}
func (ch *cors) isMatch(needle string, haystack []string) bool {
for _, v := range haystack {
if v == needle {
return true
}
}
return false
}

5
vendor/github.com/gorilla/handlers/go.mod generated vendored Normal file

@ -0,0 +1,5 @@
module github.com/gorilla/handlers
go 1.14
require github.com/felixge/httpsnoop v1.0.1

2
vendor/github.com/gorilla/handlers/go.sum generated vendored Normal file

@ -0,0 +1,2 @@
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=

@ -7,27 +7,22 @@ package handlers
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"io"
"net" "net"
"net/http" "net/http"
"net/url"
"sort" "sort"
"strconv"
"strings" "strings"
"time"
"unicode/utf8"
) )
// MethodHandler is an http.Handler that dispatches to a handler whose key in the MethodHandler's // MethodHandler is an http.Handler that dispatches to a handler whose key in the
// map matches the name of the HTTP request's method, eg: GET // MethodHandler's map matches the name of the HTTP request's method, eg: GET
// //
// If the request's method is OPTIONS and OPTIONS is not a key in the map then the handler // If the request's method is OPTIONS and OPTIONS is not a key in the map then
// responds with a status of 200 and sets the Allow header to a comma-separated list of // the handler responds with a status of 200 and sets the Allow header to a
// available methods. // comma-separated list of available methods.
// //
// If the request's method doesn't match any of its keys the handler responds with // If the request's method doesn't match any of its keys the handler responds
// a status of 405, Method not allowed and sets the Allow header to a comma-separated list // with a status of HTTP 405 "Method Not Allowed" and sets the Allow header to a
// of available methods. // comma-separated list of available methods.
type MethodHandler map[string]http.Handler type MethodHandler map[string]http.Handler
func (h MethodHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (h MethodHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
@ -48,74 +43,15 @@ func (h MethodHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
} }
} }
// loggingHandler is the http.Handler implementation for LoggingHandlerTo and its friends // responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP
type loggingHandler struct { // status code and body size
writer io.Writer
handler http.Handler
}
// combinedLoggingHandler is the http.Handler implementation for LoggingHandlerTo and its friends
type combinedLoggingHandler struct {
writer io.Writer
handler http.Handler
}
func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
t := time.Now()
logger := makeLogger(w)
url := *req.URL
h.handler.ServeHTTP(logger, req)
writeLog(h.writer, req, url, t, logger.Status(), logger.Size())
}
func (h combinedLoggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
t := time.Now()
logger := makeLogger(w)
url := *req.URL
h.handler.ServeHTTP(logger, req)
writeCombinedLog(h.writer, req, url, t, logger.Status(), logger.Size())
}
func makeLogger(w http.ResponseWriter) loggingResponseWriter {
var logger loggingResponseWriter = &responseLogger{w: w}
if _, ok := w.(http.Hijacker); ok {
logger = &hijackLogger{responseLogger{w: w}}
}
h, ok1 := logger.(http.Hijacker)
c, ok2 := w.(http.CloseNotifier)
if ok1 && ok2 {
return hijackCloseNotifier{logger, h, c}
}
if ok2 {
return &closeNotifyWriter{logger, c}
}
return logger
}
type loggingResponseWriter interface {
http.ResponseWriter
http.Flusher
Status() int
Size() int
}
// responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP status
// code and body size
type responseLogger struct { type responseLogger struct {
w http.ResponseWriter w http.ResponseWriter
status int status int
size int size int
} }
func (l *responseLogger) Header() http.Header {
return l.w.Header()
}
func (l *responseLogger) Write(b []byte) (int, error) { func (l *responseLogger) Write(b []byte) (int, error) {
if l.status == 0 {
// The status will be StatusOK if WriteHeader has not been called yet
l.status = http.StatusOK
}
size, err := l.w.Write(b) size, err := l.w.Write(b)
l.size += size l.size += size
return size, err return size, err
@ -134,187 +70,18 @@ func (l *responseLogger) Size() int {
return l.size return l.size
} }
func (l *responseLogger) Flush() { func (l *responseLogger) Hijack() (net.Conn, *bufio.ReadWriter, error) {
f, ok := l.w.(http.Flusher) conn, rw, err := l.w.(http.Hijacker).Hijack()
if ok { if err == nil && l.status == 0 {
f.Flush() // The status will be StatusSwitchingProtocols if there was no error and
} // WriteHeader has not been called yet
} l.status = http.StatusSwitchingProtocols
type hijackLogger struct {
responseLogger
}
func (l *hijackLogger) Hijack() (net.Conn, *bufio.ReadWriter, error) {
h := l.responseLogger.w.(http.Hijacker)
conn, rw, err := h.Hijack()
if err == nil && l.responseLogger.status == 0 {
// The status will be StatusSwitchingProtocols if there was no error and WriteHeader has not been called yet
l.responseLogger.status = http.StatusSwitchingProtocols
} }
return conn, rw, err return conn, rw, err
} }
type closeNotifyWriter struct { // isContentType validates the Content-Type header matches the supplied
loggingResponseWriter // contentType. That is, its type and subtype match.
http.CloseNotifier
}
type hijackCloseNotifier struct {
loggingResponseWriter
http.Hijacker
http.CloseNotifier
}
const lowerhex = "0123456789abcdef"
func appendQuoted(buf []byte, s string) []byte {
var runeTmp [utf8.UTFMax]byte
for width := 0; len(s) > 0; s = s[width:] {
r := rune(s[0])
width = 1
if r >= utf8.RuneSelf {
r, width = utf8.DecodeRuneInString(s)
}
if width == 1 && r == utf8.RuneError {
buf = append(buf, `\x`...)
buf = append(buf, lowerhex[s[0]>>4])
buf = append(buf, lowerhex[s[0]&0xF])
continue
}
if r == rune('"') || r == '\\' { // always backslashed
buf = append(buf, '\\')
buf = append(buf, byte(r))
continue
}
if strconv.IsPrint(r) {
n := utf8.EncodeRune(runeTmp[:], r)
buf = append(buf, runeTmp[:n]...)
continue
}
switch r {
case '\a':
buf = append(buf, `\a`...)
case '\b':
buf = append(buf, `\b`...)
case '\f':
buf = append(buf, `\f`...)
case '\n':
buf = append(buf, `\n`...)
case '\r':
buf = append(buf, `\r`...)
case '\t':
buf = append(buf, `\t`...)
case '\v':
buf = append(buf, `\v`...)
default:
switch {
case r < ' ':
buf = append(buf, `\x`...)
buf = append(buf, lowerhex[s[0]>>4])
buf = append(buf, lowerhex[s[0]&0xF])
case r > utf8.MaxRune:
r = 0xFFFD
fallthrough
case r < 0x10000:
buf = append(buf, `\u`...)
for s := 12; s >= 0; s -= 4 {
buf = append(buf, lowerhex[r>>uint(s)&0xF])
}
default:
buf = append(buf, `\U`...)
for s := 28; s >= 0; s -= 4 {
buf = append(buf, lowerhex[r>>uint(s)&0xF])
}
}
}
}
return buf
}
// buildCommonLogLine builds a log entry for req in Apache Common Log Format.
// ts is the timestamp with which the entry should be logged.
// status and size are used to provide the response HTTP status and size.
func buildCommonLogLine(req *http.Request, url url.URL, ts time.Time, status int, size int) []byte {
username := "-"
if url.User != nil {
if name := url.User.Username(); name != "" {
username = name
}
}
host, _, err := net.SplitHostPort(req.RemoteAddr)
if err != nil {
host = req.RemoteAddr
}
uri := url.RequestURI()
buf := make([]byte, 0, 3*(len(host)+len(username)+len(req.Method)+len(uri)+len(req.Proto)+50)/2)
buf = append(buf, host...)
buf = append(buf, " - "...)
buf = append(buf, username...)
buf = append(buf, " ["...)
buf = append(buf, ts.Format("02/Jan/2006:15:04:05 -0700")...)
buf = append(buf, `] "`...)
buf = append(buf, req.Method...)
buf = append(buf, " "...)
buf = appendQuoted(buf, uri)
buf = append(buf, " "...)
buf = append(buf, req.Proto...)
buf = append(buf, `" `...)
buf = append(buf, strconv.Itoa(status)...)
buf = append(buf, " "...)
buf = append(buf, strconv.Itoa(size)...)
return buf
}
// writeLog writes a log entry for req to w in Apache Common Log Format.
// ts is the timestamp with which the entry should be logged.
// status and size are used to provide the response HTTP status and size.
func writeLog(w io.Writer, req *http.Request, url url.URL, ts time.Time, status, size int) {
buf := buildCommonLogLine(req, url, ts, status, size)
buf = append(buf, '\n')
w.Write(buf)
}
// writeCombinedLog writes a log entry for req to w in Apache Combined Log Format.
// ts is the timestamp with which the entry should be logged.
// status and size are used to provide the response HTTP status and size.
func writeCombinedLog(w io.Writer, req *http.Request, url url.URL, ts time.Time, status, size int) {
buf := buildCommonLogLine(req, url, ts, status, size)
buf = append(buf, ` "`...)
buf = appendQuoted(buf, req.Referer())
buf = append(buf, `" "`...)
buf = appendQuoted(buf, req.UserAgent())
buf = append(buf, '"', '\n')
w.Write(buf)
}
// CombinedLoggingHandler return a http.Handler that wraps h and logs requests to out in
// Apache Combined Log Format.
//
// See http://httpd.apache.org/docs/2.2/logs.html#combined for a description of this format.
//
// LoggingHandler always sets the ident field of the log to -
func CombinedLoggingHandler(out io.Writer, h http.Handler) http.Handler {
return combinedLoggingHandler{out, h}
}
// LoggingHandler return a http.Handler that wraps h and logs requests to out in
// Apache Common Log Format (CLF).
//
// See http://httpd.apache.org/docs/2.2/logs.html#common for a description of this format.
//
// LoggingHandler always sets the ident field of the log to -
func LoggingHandler(out io.Writer, h http.Handler) http.Handler {
return loggingHandler{out, h}
}
// isContentType validates the Content-Type header
// is contentType. That is, its type and subtype match.
func isContentType(h http.Header, contentType string) bool { func isContentType(h http.Header, contentType string) bool {
ct := h.Get("Content-Type") ct := h.Get("Content-Type")
if i := strings.IndexRune(ct, ';'); i != -1 { if i := strings.IndexRune(ct, ';'); i != -1 {
@ -323,9 +90,9 @@ func isContentType(h http.Header, contentType string) bool {
return ct == contentType return ct == contentType
} }
// ContentTypeHandler wraps and returns a http.Handler, validating the request content type // ContentTypeHandler wraps and returns a http.Handler, validating the request
// is acompatible with the contentTypes list. // content type is compatible with the contentTypes list. It writes a HTTP 415
// It writes a HTTP 415 error if that fails. // error if that fails.
// //
// Only PUT, POST, and PATCH requests are considered. // Only PUT, POST, and PATCH requests are considered.
func ContentTypeHandler(h http.Handler, contentTypes ...string) http.Handler { func ContentTypeHandler(h http.Handler, contentTypes ...string) http.Handler {
@ -354,12 +121,14 @@ const (
HTTPMethodOverrideFormKey = "_method" HTTPMethodOverrideFormKey = "_method"
) )
// HTTPMethodOverrideHandler wraps and returns a http.Handler which checks for the X-HTTP-Method-Override header // HTTPMethodOverrideHandler wraps and returns a http.Handler which checks for
// or the _method form key, and overrides (if valid) request.Method with its value. // the X-HTTP-Method-Override header or the _method form key, and overrides (if
// valid) request.Method with its value.
// //
// This is especially useful for http clients that don't support many http verbs. // This is especially useful for HTTP clients that don't support many http verbs.
// It isn't secure to override e.g a GET to a POST, so only POST requests are considered. // It isn't secure to override e.g a GET to a POST, so only POST requests are
// Likewise, the override method can only be a "write" method: PUT, PATCH or DELETE. // considered. Likewise, the override method can only be a "write" method: PUT,
// PATCH or DELETE.
// //
// Form method takes precedence over header method. // Form method takes precedence over header method.
func HTTPMethodOverrideHandler(h http.Handler) http.Handler { func HTTPMethodOverrideHandler(h http.Handler) http.Handler {

244
vendor/github.com/gorilla/handlers/logging.go generated vendored Normal file

@ -0,0 +1,244 @@
// Copyright 2013 The Gorilla 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 handlers
import (
"io"
"net"
"net/http"
"net/url"
"strconv"
"time"
"unicode/utf8"
"github.com/felixge/httpsnoop"
)
// Logging
// LogFormatterParams is the structure any formatter will be handed when time to log comes
type LogFormatterParams struct {
Request *http.Request
URL url.URL
TimeStamp time.Time
StatusCode int
Size int
}
// LogFormatter gives the signature of the formatter function passed to CustomLoggingHandler
type LogFormatter func(writer io.Writer, params LogFormatterParams)
// loggingHandler is the http.Handler implementation for LoggingHandlerTo and its
// friends
type loggingHandler struct {
writer io.Writer
handler http.Handler
formatter LogFormatter
}
func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
t := time.Now()
logger, w := makeLogger(w)
url := *req.URL
h.handler.ServeHTTP(w, req)
if req.MultipartForm != nil {
req.MultipartForm.RemoveAll()
}
params := LogFormatterParams{
Request: req,
URL: url,
TimeStamp: t,
StatusCode: logger.Status(),
Size: logger.Size(),
}
h.formatter(h.writer, params)
}
func makeLogger(w http.ResponseWriter) (*responseLogger, http.ResponseWriter) {
logger := &responseLogger{w: w, status: http.StatusOK}
return logger, httpsnoop.Wrap(w, httpsnoop.Hooks{
Write: func(httpsnoop.WriteFunc) httpsnoop.WriteFunc {
return logger.Write
},
WriteHeader: func(httpsnoop.WriteHeaderFunc) httpsnoop.WriteHeaderFunc {
return logger.WriteHeader
},
})
}
const lowerhex = "0123456789abcdef"
func appendQuoted(buf []byte, s string) []byte {
var runeTmp [utf8.UTFMax]byte
for width := 0; len(s) > 0; s = s[width:] {
r := rune(s[0])
width = 1
if r >= utf8.RuneSelf {
r, width = utf8.DecodeRuneInString(s)
}
if width == 1 && r == utf8.RuneError {
buf = append(buf, `\x`...)
buf = append(buf, lowerhex[s[0]>>4])
buf = append(buf, lowerhex[s[0]&0xF])
continue
}
if r == rune('"') || r == '\\' { // always backslashed
buf = append(buf, '\\')
buf = append(buf, byte(r))
continue
}
if strconv.IsPrint(r) {
n := utf8.EncodeRune(runeTmp[:], r)
buf = append(buf, runeTmp[:n]...)
continue
}
switch r {
case '\a':
buf = append(buf, `\a`...)
case '\b':
buf = append(buf, `\b`...)
case '\f':
buf = append(buf, `\f`...)
case '\n':
buf = append(buf, `\n`...)
case '\r':
buf = append(buf, `\r`...)
case '\t':
buf = append(buf, `\t`...)
case '\v':
buf = append(buf, `\v`...)
default:
switch {
case r < ' ':
buf = append(buf, `\x`...)
buf = append(buf, lowerhex[s[0]>>4])
buf = append(buf, lowerhex[s[0]&0xF])
case r > utf8.MaxRune:
r = 0xFFFD
fallthrough
case r < 0x10000:
buf = append(buf, `\u`...)
for s := 12; s >= 0; s -= 4 {
buf = append(buf, lowerhex[r>>uint(s)&0xF])
}
default:
buf = append(buf, `\U`...)
for s := 28; s >= 0; s -= 4 {
buf = append(buf, lowerhex[r>>uint(s)&0xF])
}
}
}
}
return buf
}
// buildCommonLogLine builds a log entry for req in Apache Common Log Format.
// ts is the timestamp with which the entry should be logged.
// status and size are used to provide the response HTTP status and size.
func buildCommonLogLine(req *http.Request, url url.URL, ts time.Time, status int, size int) []byte {
username := "-"
if url.User != nil {
if name := url.User.Username(); name != "" {
username = name
}
}
host, _, err := net.SplitHostPort(req.RemoteAddr)
if err != nil {
host = req.RemoteAddr
}
uri := req.RequestURI
// Requests using the CONNECT method over HTTP/2.0 must use
// the authority field (aka r.Host) to identify the target.
// Refer: https://httpwg.github.io/specs/rfc7540.html#CONNECT
if req.ProtoMajor == 2 && req.Method == "CONNECT" {
uri = req.Host
}
if uri == "" {
uri = url.RequestURI()
}
buf := make([]byte, 0, 3*(len(host)+len(username)+len(req.Method)+len(uri)+len(req.Proto)+50)/2)
buf = append(buf, host...)
buf = append(buf, " - "...)
buf = append(buf, username...)
buf = append(buf, " ["...)
buf = append(buf, ts.Format("02/Jan/2006:15:04:05 -0700")...)
buf = append(buf, `] "`...)
buf = append(buf, req.Method...)
buf = append(buf, " "...)
buf = appendQuoted(buf, uri)
buf = append(buf, " "...)
buf = append(buf, req.Proto...)
buf = append(buf, `" `...)
buf = append(buf, strconv.Itoa(status)...)
buf = append(buf, " "...)
buf = append(buf, strconv.Itoa(size)...)
return buf
}
// writeLog writes a log entry for req to w in Apache Common Log Format.
// ts is the timestamp with which the entry should be logged.
// status and size are used to provide the response HTTP status and size.
func writeLog(writer io.Writer, params LogFormatterParams) {
buf := buildCommonLogLine(params.Request, params.URL, params.TimeStamp, params.StatusCode, params.Size)
buf = append(buf, '\n')
writer.Write(buf)
}
// writeCombinedLog writes a log entry for req to w in Apache Combined Log Format.
// ts is the timestamp with which the entry should be logged.
// status and size are used to provide the response HTTP status and size.
func writeCombinedLog(writer io.Writer, params LogFormatterParams) {
buf := buildCommonLogLine(params.Request, params.URL, params.TimeStamp, params.StatusCode, params.Size)
buf = append(buf, ` "`...)
buf = appendQuoted(buf, params.Request.Referer())
buf = append(buf, `" "`...)
buf = appendQuoted(buf, params.Request.UserAgent())
buf = append(buf, '"', '\n')
writer.Write(buf)
}
// CombinedLoggingHandler return a http.Handler that wraps h and logs requests to out in
// Apache Combined Log Format.
//
// See http://httpd.apache.org/docs/2.2/logs.html#combined for a description of this format.
//
// LoggingHandler always sets the ident field of the log to -
func CombinedLoggingHandler(out io.Writer, h http.Handler) http.Handler {
return loggingHandler{out, h, writeCombinedLog}
}
// LoggingHandler return a http.Handler that wraps h and logs requests to out in
// Apache Common Log Format (CLF).
//
// See http://httpd.apache.org/docs/2.2/logs.html#common for a description of this format.
//
// LoggingHandler always sets the ident field of the log to -
//
// Example:
//
// r := mux.NewRouter()
// r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// w.Write([]byte("This is a catch-all route"))
// })
// loggedRouter := handlers.LoggingHandler(os.Stdout, r)
// http.ListenAndServe(":1123", loggedRouter)
//
func LoggingHandler(out io.Writer, h http.Handler) http.Handler {
return loggingHandler{out, h, writeLog}
}
// CustomLoggingHandler provides a way to supply a custom log formatter
// while taking advantage of the mechanisms in this package
func CustomLoggingHandler(out io.Writer, h http.Handler, f LogFormatter) http.Handler {
return loggingHandler{out, h, f}
}

@ -8,9 +8,11 @@ import (
var ( var (
// De-facto standard header keys. // De-facto standard header keys.
xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For") xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For")
xRealIP = http.CanonicalHeaderKey("X-Real-IP") xForwardedHost = http.CanonicalHeaderKey("X-Forwarded-Host")
xForwardedProto = http.CanonicalHeaderKey("X-Forwarded-Scheme") xForwardedProto = http.CanonicalHeaderKey("X-Forwarded-Proto")
xForwardedScheme = http.CanonicalHeaderKey("X-Forwarded-Scheme")
xRealIP = http.CanonicalHeaderKey("X-Real-IP")
) )
var ( var (
@ -28,9 +30,9 @@ var (
// ProxyHeaders inspects common reverse proxy headers and sets the corresponding // ProxyHeaders inspects common reverse proxy headers and sets the corresponding
// fields in the HTTP request struct. These are X-Forwarded-For and X-Real-IP // fields in the HTTP request struct. These are X-Forwarded-For and X-Real-IP
// for the remote (client) IP address, X-Forwarded-Proto for the scheme // for the remote (client) IP address, X-Forwarded-Proto or X-Forwarded-Scheme
// (http|https) and the RFC7239 Forwarded header, which may include both client // for the scheme (http|https), X-Forwarded-Host for the host and the RFC7239
// IPs and schemes. // Forwarded header, which may include both client IPs and schemes.
// //
// NOTE: This middleware should only be used when behind a reverse // NOTE: This middleware should only be used when behind a reverse
// proxy like nginx, HAProxy or Apache. Reverse proxies that don't (or are // proxy like nginx, HAProxy or Apache. Reverse proxies that don't (or are
@ -49,7 +51,10 @@ func ProxyHeaders(h http.Handler) http.Handler {
if scheme := getScheme(r); scheme != "" { if scheme := getScheme(r); scheme != "" {
r.URL.Scheme = scheme r.URL.Scheme = scheme
} }
// Set the host with the value passed by the proxy
if r.Header.Get(xForwardedHost) != "" {
r.Host = r.Header.Get(xForwardedHost)
}
// Call the next handler in the chain. // Call the next handler in the chain.
h.ServeHTTP(w, r) h.ServeHTTP(w, r)
} }
@ -99,7 +104,9 @@ func getScheme(r *http.Request) string {
// Retrieve the scheme from X-Forwarded-Proto. // Retrieve the scheme from X-Forwarded-Proto.
if proto := r.Header.Get(xForwardedProto); proto != "" { if proto := r.Header.Get(xForwardedProto); proto != "" {
scheme = strings.ToLower(proto) scheme = strings.ToLower(proto)
} else if proto := r.Header.Get(forwarded); proto != "" { } else if proto = r.Header.Get(xForwardedScheme); proto != "" {
scheme = strings.ToLower(proto)
} else if proto = r.Header.Get(forwarded); proto != "" {
// match should contain at least two elements if the protocol was // match should contain at least two elements if the protocol was
// specified in the Forwarded header. The first element will always be // specified in the Forwarded header. The first element will always be
// the 'proto=' capture, which we ignore. In the case of multiple proto // the 'proto=' capture, which we ignore. In the case of multiple proto

96
vendor/github.com/gorilla/handlers/recovery.go generated vendored Normal file

@ -0,0 +1,96 @@
package handlers
import (
"log"
"net/http"
"runtime/debug"
)
// RecoveryHandlerLogger is an interface used by the recovering handler to print logs.
type RecoveryHandlerLogger interface {
Println(...interface{})
}
type recoveryHandler struct {
handler http.Handler
logger RecoveryHandlerLogger
printStack bool
}
// RecoveryOption provides a functional approach to define
// configuration for a handler; such as setting the logging
// whether or not to print stack traces on panic.
type RecoveryOption func(http.Handler)
func parseRecoveryOptions(h http.Handler, opts ...RecoveryOption) http.Handler {
for _, option := range opts {
option(h)
}
return h
}
// RecoveryHandler is HTTP middleware that recovers from a panic,
// logs the panic, writes http.StatusInternalServerError, and
// continues to the next handler.
//
// Example:
//
// r := mux.NewRouter()
// r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// panic("Unexpected error!")
// })
//
// http.ListenAndServe(":1123", handlers.RecoveryHandler()(r))
func RecoveryHandler(opts ...RecoveryOption) func(h http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
r := &recoveryHandler{handler: h}
return parseRecoveryOptions(r, opts...)
}
}
// RecoveryLogger is a functional option to override
// the default logger
func RecoveryLogger(logger RecoveryHandlerLogger) RecoveryOption {
return func(h http.Handler) {
r := h.(*recoveryHandler)
r.logger = logger
}
}
// PrintRecoveryStack is a functional option to enable
// or disable printing stack traces on panic.
func PrintRecoveryStack(print bool) RecoveryOption {
return func(h http.Handler) {
r := h.(*recoveryHandler)
r.printStack = print
}
}
func (h recoveryHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
defer func() {
if err := recover(); err != nil {
w.WriteHeader(http.StatusInternalServerError)
h.log(err)
}
}()
h.handler.ServeHTTP(w, req)
}
func (h recoveryHandler) log(v ...interface{}) {
if h.logger != nil {
h.logger.Println(v...)
} else {
log.Println(v...)
}
if h.printStack {
stack := string(debug.Stack())
if h.logger != nil {
h.logger.Println(stack)
} else {
log.Println(stack)
}
}
}

@ -3,6 +3,7 @@ package apparmor
import ( import (
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"sync" "sync"
@ -18,7 +19,7 @@ var (
func isEnabled() bool { func isEnabled() bool {
checkAppArmor.Do(func() { checkAppArmor.Do(func() {
if _, err := os.Stat("/sys/kernel/security/apparmor"); err == nil { if _, err := os.Stat("/sys/kernel/security/apparmor"); err == nil {
buf, err := os.ReadFile("/sys/module/apparmor/parameters/enabled") buf, err := ioutil.ReadFile("/sys/module/apparmor/parameters/enabled")
appArmorEnabled = err == nil && len(buf) > 1 && buf[0] == 'Y' appArmorEnabled = err == nil && len(buf) > 1 && buf[0] == 'Y'
} }
}) })
@ -51,7 +52,7 @@ func setProcAttr(attr, value string) error {
// changeOnExec reimplements aa_change_onexec from libapparmor in Go // changeOnExec reimplements aa_change_onexec from libapparmor in Go
func changeOnExec(name string) error { func changeOnExec(name string) error {
if err := setProcAttr("exec", "exec "+name); err != nil { if err := setProcAttr("exec", "exec "+name); err != nil {
return fmt.Errorf("apparmor failed to apply profile: %w", err) return fmt.Errorf("apparmor failed to apply profile: %s", err)
} }
return nil return nil
} }

@ -1,4 +1,3 @@
//go:build !linux
// +build !linux // +build !linux
package apparmor package apparmor

@ -1,3 +1,5 @@
// +build linux
package cgroups package cgroups
import ( import (

@ -0,0 +1,3 @@
// +build !linux
package cgroups

@ -2,27 +2,20 @@ package cgroups
import ( import (
"bytes" "bytes"
"errors"
"fmt"
"os" "os"
"path"
"strconv"
"strings" "strings"
"sync" "sync"
"github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
// OpenFile opens a cgroup file in a given dir with given flags. // OpenFile opens a cgroup file in a given dir with given flags.
// It is supposed to be used for cgroup files only, and returns // It is supposed to be used for cgroup files only.
// an error if the file is not a cgroup file.
//
// Arguments dir and file are joined together to form an absolute path
// to a file being opened.
func OpenFile(dir, file string, flags int) (*os.File, error) { func OpenFile(dir, file string, flags int) (*os.File, error) {
if dir == "" { if dir == "" {
return nil, fmt.Errorf("no directory specified for %s", file) return nil, errors.Errorf("no directory specified for %s", file)
} }
return openFile(dir, file, flags) return openFile(dir, file, flags)
} }
@ -50,8 +43,7 @@ func WriteFile(dir, file, data string) error {
} }
defer fd.Close() defer fd.Close()
if err := retryingWriteFile(fd, data); err != nil { if err := retryingWriteFile(fd, data); err != nil {
// Having data in the error message helps in debugging. return errors.Wrapf(err, "failed to write %q", data)
return fmt.Errorf("failed to write %q: %w", data, err)
} }
return nil return nil
} }
@ -89,7 +81,7 @@ func prepareOpenat2() error {
}) })
if err != nil { if err != nil {
prepErr = &os.PathError{Op: "openat2", Path: cgroupfsDir, Err: err} prepErr = &os.PathError{Op: "openat2", Path: cgroupfsDir, Err: err}
if err != unix.ENOSYS { //nolint:errorlint // unix errors are bare if err != unix.ENOSYS {
logrus.Warnf("falling back to securejoin: %s", prepErr) logrus.Warnf("falling back to securejoin: %s", prepErr)
} else { } else {
logrus.Debug("openat2 not available, falling back to securejoin") logrus.Debug("openat2 not available, falling back to securejoin")
@ -115,6 +107,8 @@ func prepareOpenat2() error {
return prepErr return prepErr
} }
// OpenFile opens a cgroup file in a given dir with given flags.
// It is supposed to be used for cgroup files only.
func openFile(dir, file string, flags int) (*os.File, error) { func openFile(dir, file string, flags int) (*os.File, error) {
mode := os.FileMode(0) mode := os.FileMode(0)
if TestMode && flags&os.O_WRONLY != 0 { if TestMode && flags&os.O_WRONLY != 0 {
@ -122,52 +116,34 @@ func openFile(dir, file string, flags int) (*os.File, error) {
flags |= os.O_TRUNC | os.O_CREATE flags |= os.O_TRUNC | os.O_CREATE
mode = 0o600 mode = 0o600
} }
path := path.Join(dir, file)
if prepareOpenat2() != nil { if prepareOpenat2() != nil {
return openFallback(path, flags, mode) return openFallback(dir, file, flags, mode)
} }
relPath := strings.TrimPrefix(path, cgroupfsPrefix) reldir := strings.TrimPrefix(dir, cgroupfsPrefix)
if len(relPath) == len(path) { // non-standard path, old system? if len(reldir) == len(dir) { // non-standard path, old system?
return openFallback(path, flags, mode) return openFallback(dir, file, flags, mode)
} }
fd, err := unix.Openat2(cgroupFd, relPath, relname := reldir + "/" + file
fd, err := unix.Openat2(cgroupFd, relname,
&unix.OpenHow{ &unix.OpenHow{
Resolve: resolveFlags, Resolve: resolveFlags,
Flags: uint64(flags) | unix.O_CLOEXEC, Flags: uint64(flags) | unix.O_CLOEXEC,
Mode: uint64(mode), Mode: uint64(mode),
}) })
if err != nil { if err != nil {
err = &os.PathError{Op: "openat2", Path: path, Err: err} return nil, &os.PathError{Op: "openat2", Path: dir + "/" + file, Err: err}
// Check if cgroupFd is still opened to cgroupfsDir
// (happens when this package is incorrectly used
// across the chroot/pivot_root/mntns boundary, or
// when /sys/fs/cgroup is remounted).
//
// TODO: if such usage will ever be common, amend this
// to reopen cgroupFd and retry openat2.
fdStr := strconv.Itoa(cgroupFd)
fdDest, _ := os.Readlink("/proc/self/fd/" + fdStr)
if fdDest != cgroupfsDir {
// Wrap the error so it is clear that cgroupFd
// is opened to an unexpected/wrong directory.
err = fmt.Errorf("cgroupFd %s unexpectedly opened to %s != %s: %w",
fdStr, fdDest, cgroupfsDir, err)
}
return nil, err
} }
return os.NewFile(uintptr(fd), path), nil return os.NewFile(uintptr(fd), cgroupfsPrefix+relname), nil
} }
var errNotCgroupfs = errors.New("not a cgroup file") var errNotCgroupfs = errors.New("not a cgroup file")
// Can be changed by unit tests. // openFallback is used when openat2(2) is not available. It checks the opened
var openFallback = openAndCheck
// openAndCheck is used when openat2(2) is not available. It checks the opened
// file is on cgroupfs, returning an error otherwise. // file is on cgroupfs, returning an error otherwise.
func openAndCheck(path string, flags int, mode os.FileMode) (*os.File, error) { func openFallback(dir, file string, flags int, mode os.FileMode) (*os.File, error) {
path := dir + "/" + file
fd, err := os.OpenFile(path, flags, mode) fd, err := os.OpenFile(path, flags, mode)
if err != nil { if err != nil {
return nil, err return nil, err

@ -1,27 +0,0 @@
package cgroups
import (
"io/fs"
"path/filepath"
)
// GetAllPids returns all pids from the cgroup identified by path, and all its
// sub-cgroups.
func GetAllPids(path string) ([]int, error) {
var pids []int
err := filepath.WalkDir(path, func(p string, d fs.DirEntry, iErr error) error {
if iErr != nil {
return iErr
}
if !d.IsDir() {
return nil
}
cPids, err := readProcsFile(p)
if err != nil {
return err
}
pids = append(pids, cPids...)
return nil
})
return pids, err
}

@ -1,3 +1,5 @@
// +build linux
package cgroups package cgroups
type ThrottlingData struct { type ThrottlingData struct {
@ -124,7 +126,7 @@ type BlkioStatEntry struct {
} }
type BlkioStats struct { type BlkioStats struct {
// number of bytes transferred to and from the block device // number of bytes tranferred to and from the block device
IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive,omitempty"` IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive,omitempty"`
IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recursive,omitempty"` IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recursive,omitempty"`
IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive,omitempty"` IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive,omitempty"`
@ -144,17 +146,6 @@ type HugetlbStats struct {
Failcnt uint64 `json:"failcnt"` Failcnt uint64 `json:"failcnt"`
} }
type RdmaEntry struct {
Device string `json:"device,omitempty"`
HcaHandles uint32 `json:"hca_handles,omitempty"`
HcaObjects uint32 `json:"hca_objects,omitempty"`
}
type RdmaStats struct {
RdmaLimit []RdmaEntry `json:"rdma_limit,omitempty"`
RdmaCurrent []RdmaEntry `json:"rdma_current,omitempty"`
}
type Stats struct { type Stats struct {
CpuStats CpuStats `json:"cpu_stats,omitempty"` CpuStats CpuStats `json:"cpu_stats,omitempty"`
CPUSetStats CPUSetStats `json:"cpuset_stats,omitempty"` CPUSetStats CPUSetStats `json:"cpuset_stats,omitempty"`
@ -163,7 +154,6 @@ type Stats struct {
BlkioStats BlkioStats `json:"blkio_stats,omitempty"` BlkioStats BlkioStats `json:"blkio_stats,omitempty"`
// the map is in the format "size of hugepage: stats of the hugepage" // the map is in the format "size of hugepage: stats of the hugepage"
HugetlbStats map[string]HugetlbStats `json:"hugetlb_stats,omitempty"` HugetlbStats map[string]HugetlbStats `json:"hugetlb_stats,omitempty"`
RdmaStats RdmaStats `json:"rdma_stats,omitempty"`
} }
func NewStats() *Stats { func NewStats() *Stats {

@ -1,3 +1,5 @@
// +build linux
package cgroups package cgroups
import ( import (
@ -5,6 +7,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
@ -20,14 +23,11 @@ import (
const ( const (
CgroupProcesses = "cgroup.procs" CgroupProcesses = "cgroup.procs"
unifiedMountpoint = "/sys/fs/cgroup" unifiedMountpoint = "/sys/fs/cgroup"
hybridMountpoint = "/sys/fs/cgroup/unified"
) )
var ( var (
isUnifiedOnce sync.Once isUnifiedOnce sync.Once
isUnified bool isUnified bool
isHybridOnce sync.Once
isHybrid bool
) )
// IsCgroup2UnifiedMode returns whether we are running in cgroup v2 unified mode. // IsCgroup2UnifiedMode returns whether we are running in cgroup v2 unified mode.
@ -49,24 +49,6 @@ func IsCgroup2UnifiedMode() bool {
return isUnified return isUnified
} }
// IsCgroup2HybridMode returns whether we are running in cgroup v2 hybrid mode.
func IsCgroup2HybridMode() bool {
isHybridOnce.Do(func() {
var st unix.Statfs_t
err := unix.Statfs(hybridMountpoint, &st)
if err != nil {
if os.IsNotExist(err) {
// ignore the "not found" error
isHybrid = false
return
}
panic(fmt.Sprintf("cannot statfs cgroup root: %s", err))
}
isHybrid = st.Type == unix.CGROUP2_SUPER_MAGIC
})
return isHybrid
}
type Mount struct { type Mount struct {
Mountpoint string Mountpoint string
Root string Root string
@ -136,8 +118,8 @@ func GetAllSubsystems() ([]string, error) {
return subsystems, nil return subsystems, nil
} }
func readProcsFile(dir string) ([]int, error) { func readProcsFile(file string) ([]int, error) {
f, err := OpenFile(dir, CgroupProcesses, os.O_RDONLY) f, err := os.Open(file)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -228,7 +210,7 @@ func EnterPid(cgroupPaths map[string]string, pid int) error {
func rmdir(path string) error { func rmdir(path string) error {
err := unix.Rmdir(path) err := unix.Rmdir(path)
if err == nil || err == unix.ENOENT { //nolint:errorlint // unix errors are bare if err == nil || err == unix.ENOENT {
return nil return nil
} }
return &os.PathError{Op: "rmdir", Path: path, Err: err} return &os.PathError{Op: "rmdir", Path: path, Err: err}
@ -242,7 +224,7 @@ func RemovePath(path string) error {
return nil return nil
} }
infos, err := os.ReadDir(path) infos, err := ioutil.ReadDir(path)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
err = nil err = nil
@ -302,61 +284,40 @@ func RemovePaths(paths map[string]string) (err error) {
return fmt.Errorf("Failed to remove paths: %v", paths) return fmt.Errorf("Failed to remove paths: %v", paths)
} }
var ( func GetHugePageSize() ([]string, error) {
hugePageSizes []string dir, err := os.OpenFile("/sys/kernel/mm/hugepages", unix.O_DIRECTORY|unix.O_RDONLY, 0)
initHPSOnce sync.Once if err != nil {
) return nil, err
}
files, err := dir.Readdirnames(0)
dir.Close()
if err != nil {
return nil, err
}
func HugePageSizes() []string { return getHugePageSizeFromFilenames(files)
initHPSOnce.Do(func() {
dir, err := os.OpenFile("/sys/kernel/mm/hugepages", unix.O_DIRECTORY|unix.O_RDONLY, 0)
if err != nil {
return
}
files, err := dir.Readdirnames(0)
dir.Close()
if err != nil {
return
}
hugePageSizes, err = getHugePageSizeFromFilenames(files)
if err != nil {
logrus.Warn("HugePageSizes: ", err)
}
})
return hugePageSizes
} }
func getHugePageSizeFromFilenames(fileNames []string) ([]string, error) { func getHugePageSizeFromFilenames(fileNames []string) ([]string, error) {
pageSizes := make([]string, 0, len(fileNames)) pageSizes := make([]string, 0, len(fileNames))
var warn error
for _, file := range fileNames { for _, file := range fileNames {
// example: hugepages-1048576kB // example: hugepages-1048576kB
val := strings.TrimPrefix(file, "hugepages-") val := strings.TrimPrefix(file, "hugepages-")
if len(val) == len(file) { if len(val) == len(file) {
// Unexpected file name: no prefix found, ignore it. // unexpected file name: no prefix found
continue continue
} }
// The suffix is always "kB" (as of Linux 5.13). If we find // The suffix is always "kB" (as of Linux 5.9)
// something else, produce an error but keep going.
eLen := len(val) - 2 eLen := len(val) - 2
val = strings.TrimSuffix(val, "kB") val = strings.TrimSuffix(val, "kB")
if len(val) != eLen { if len(val) != eLen {
// Highly unlikely. logrus.Warnf("GetHugePageSize: %s: invalid filename suffix (expected \"kB\")", file)
if warn == nil {
warn = errors.New(file + `: invalid suffix (expected "kB")`)
}
continue continue
} }
size, err := strconv.Atoi(val) size, err := strconv.Atoi(val)
if err != nil { if err != nil {
// Highly unlikely. return nil, err
if warn == nil {
warn = fmt.Errorf("%s: %w", file, err)
}
continue
} }
// Model after https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/mm/hugetlb_cgroup.c?id=eff48ddeab782e35e58ccc8853f7386bbae9dec4#n574 // Model after https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/mm/hugetlb_cgroup.c?id=eff48ddeab782e35e58ccc8853f7386bbae9dec4#n574
// but in our case the size is in KB already. // but in our case the size is in KB already.
@ -370,12 +331,34 @@ func getHugePageSizeFromFilenames(fileNames []string) ([]string, error) {
pageSizes = append(pageSizes, val) pageSizes = append(pageSizes, val)
} }
return pageSizes, warn return pageSizes, nil
} }
// GetPids returns all pids, that were added to cgroup at path. // GetPids returns all pids, that were added to cgroup at path.
func GetPids(dir string) ([]int, error) { func GetPids(dir string) ([]int, error) {
return readProcsFile(dir) return readProcsFile(filepath.Join(dir, CgroupProcesses))
}
// GetAllPids returns all pids, that were added to cgroup at path and to all its
// subcgroups.
func GetAllPids(path string) ([]int, error) {
var pids []int
// collect pids from all sub-cgroups
err := filepath.Walk(path, func(p string, info os.FileInfo, iErr error) error {
if iErr != nil {
return iErr
}
if info.IsDir() || info.Name() != CgroupProcesses {
return nil
}
cPids, err := readProcsFile(p)
if err != nil {
return err
}
pids = append(pids, cPids...)
return nil
})
return pids, err
} }
// WriteCgroupProc writes the specified pid into the cgroup's cgroup.procs file // WriteCgroupProc writes the specified pid into the cgroup's cgroup.procs file
@ -393,7 +376,7 @@ func WriteCgroupProc(dir string, pid int) error {
file, err := OpenFile(dir, CgroupProcesses, os.O_WRONLY) file, err := OpenFile(dir, CgroupProcesses, os.O_WRONLY)
if err != nil { if err != nil {
return fmt.Errorf("failed to write %v: %w", pid, err) return fmt.Errorf("failed to write %v to %v: %v", pid, CgroupProcesses, err)
} }
defer file.Close() defer file.Close()
@ -410,7 +393,7 @@ func WriteCgroupProc(dir string, pid int) error {
continue continue
} }
return fmt.Errorf("failed to write %v: %w", pid, err) return fmt.Errorf("failed to write %v to %v: %v", pid, CgroupProcesses, err)
} }
return err return err
} }
@ -463,5 +446,5 @@ func ConvertBlkIOToIOWeightValue(blkIoWeight uint16) uint64 {
if blkIoWeight == 0 { if blkIoWeight == 0 {
return 0 return 0
} }
return 1 + (uint64(blkIoWeight)-10)*9999/990 return uint64(1 + (uint64(blkIoWeight)-10)*9999/990)
} }

@ -46,8 +46,11 @@ func NewNotFoundError(sub string) error {
} }
func IsNotFound(err error) bool { func IsNotFound(err error) bool {
var nfErr *NotFoundError if err == nil {
return errors.As(err, &nfErr) return false
}
_, ok := err.(*NotFoundError)
return ok
} }
func tryDefaultPath(cgroupPath, subsystem string) string { func tryDefaultPath(cgroupPath, subsystem string) string {
@ -113,11 +116,6 @@ func FindCgroupMountpoint(cgroupPath, subsystem string) (string, error) {
return "", errUnified return "", errUnified
} }
// If subsystem is empty, we look for the cgroupv2 hybrid path.
if len(subsystem) == 0 {
return hybridMountpoint, nil
}
// Avoid parsing mountinfo by trying the default path first, if possible. // Avoid parsing mountinfo by trying the default path first, if possible.
if path := tryDefaultPath(cgroupPath, subsystem); path != "" { if path := tryDefaultPath(cgroupPath, subsystem); path != "" {
return path, nil return path, nil
@ -156,7 +154,7 @@ func findCgroupMountpointAndRootFromMI(mounts []*mountinfo.Info, cgroupPath, sub
func (m Mount) GetOwnCgroup(cgroups map[string]string) (string, error) { func (m Mount) GetOwnCgroup(cgroups map[string]string) (string, error) {
if len(m.Subsystems) == 0 { if len(m.Subsystems) == 0 {
return "", errors.New("no subsystem for mount") return "", fmt.Errorf("no subsystem for mount")
} }
return getControllerPath(m.Subsystems[0], cgroups) return getControllerPath(m.Subsystems[0], cgroups)
@ -228,11 +226,6 @@ func GetOwnCgroupPath(subsystem string) (string, error) {
return "", err return "", err
} }
// If subsystem is empty, we look for the cgroupv2 hybrid path.
if len(subsystem) == 0 {
return hybridMountpoint, nil
}
return getCgroupPathHelper(subsystem, cgroup) return getCgroupPathHelper(subsystem, cgroup)
} }

@ -28,26 +28,17 @@ type Cgroup struct {
// ScopePrefix describes prefix for the scope name // ScopePrefix describes prefix for the scope name
ScopePrefix string `json:"scope_prefix"` ScopePrefix string `json:"scope_prefix"`
// Paths represent the absolute cgroups paths to join.
// This takes precedence over Path.
Paths map[string]string
// Resources contains various cgroups settings to apply // Resources contains various cgroups settings to apply
*Resources *Resources
// Systemd tells if systemd should be used to manage cgroups.
Systemd bool
// SystemdProps are any additional properties for systemd, // SystemdProps are any additional properties for systemd,
// derived from org.systemd.property.xxx annotations. // derived from org.systemd.property.xxx annotations.
// Ignored unless systemd is used for managing cgroups. // Ignored unless systemd is used for managing cgroups.
SystemdProps []systemdDbus.Property `json:"-"` SystemdProps []systemdDbus.Property `json:"-"`
// Rootless tells if rootless cgroups should be used.
Rootless bool
// The host UID that should own the cgroup, or nil to accept
// the default ownership. This should only be set when the
// cgroupfs is to be mounted read/write.
// Not all cgroup manager implementations support changing
// the ownership.
OwnerUID *int `json:"owner_uid,omitempty"`
} }
type Resources struct { type Resources struct {
@ -126,9 +117,6 @@ type Resources struct {
// Set class identifier for container's network packets // Set class identifier for container's network packets
NetClsClassid uint32 `json:"net_cls_classid_u"` NetClsClassid uint32 `json:"net_cls_classid_u"`
// Rdma resource restriction configuration
Rdma map[string]LinuxRdma `json:"rdma"`
// Used on cgroups v2: // Used on cgroups v2:
// CpuWeight sets a proportional bandwidth limit. // CpuWeight sets a proportional bandwidth limit.

@ -1,4 +1,3 @@
//go:build !linux
// +build !linux // +build !linux
package configs package configs

@ -7,10 +7,10 @@ import (
"os/exec" "os/exec"
"time" "time"
"github.com/sirupsen/logrus"
"github.com/opencontainers/runc/libcontainer/devices" "github.com/opencontainers/runc/libcontainer/devices"
"github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
) )
type Rlimit struct { type Rlimit struct {
@ -31,12 +31,10 @@ type IDMap struct {
// for syscalls. Additional architectures can be added by specifying them in // for syscalls. Additional architectures can be added by specifying them in
// Architectures. // Architectures.
type Seccomp struct { type Seccomp struct {
DefaultAction Action `json:"default_action"` DefaultAction Action `json:"default_action"`
Architectures []string `json:"architectures"` Architectures []string `json:"architectures"`
Syscalls []*Syscall `json:"syscalls"` Syscalls []*Syscall `json:"syscalls"`
DefaultErrnoRet *uint `json:"default_errno_ret"` DefaultErrnoRet *uint `json:"default_errno_ret"`
ListenerPath string `json:"listener_path,omitempty"`
ListenerMetadata string `json:"listener_metadata,omitempty"`
} }
// Action is taken upon rule match in Seccomp // Action is taken upon rule match in Seccomp
@ -49,9 +47,6 @@ const (
Allow Allow
Trace Trace
Log Log
Notify
KillThread
KillProcess
) )
// Operator is a comparison operator to be used when matching syscall arguments in Seccomp // Operator is a comparison operator to be used when matching syscall arguments in Seccomp
@ -251,19 +246,6 @@ const (
Poststop HookName = "poststop" Poststop HookName = "poststop"
) )
// KnownHookNames returns the known hook names.
// Used by `runc features`.
func KnownHookNames() []string {
return []string{
string(Prestart), // deprecated
string(CreateRuntime),
string(CreateContainer),
string(StartContainer),
string(Poststart),
string(Poststop),
}
}
type Capabilities struct { type Capabilities struct {
// Bounding is the set of capabilities checked by the kernel. // Bounding is the set of capabilities checked by the kernel.
Bounding []string Bounding []string
@ -280,7 +262,7 @@ type Capabilities struct {
func (hooks HookList) RunHooks(state *specs.State) error { func (hooks HookList) RunHooks(state *specs.State) error {
for i, h := range hooks { for i, h := range hooks {
if err := h.Run(state); err != nil { if err := h.Run(state); err != nil {
return fmt.Errorf("error running hook #%d: %w", i, err) return errors.Wrapf(err, "Running hook #%d:", i)
} }
} }
@ -393,7 +375,7 @@ func (c Command) Run(s *specs.State) error {
go func() { go func() {
err := cmd.Wait() err := cmd.Wait()
if err != nil { if err != nil {
err = fmt.Errorf("error running hook: %w, stdout: %s, stderr: %s", err, stdout.String(), stderr.String()) err = fmt.Errorf("error running hook: %v, stdout: %s, stderr: %s", err, stdout.String(), stderr.String())
} }
errC <- err errC <- err
}() }()

@ -1,24 +1,17 @@
package configs package configs
import "errors" import "fmt"
var (
errNoUIDMap = errors.New("User namespaces enabled, but no uid mappings found.")
errNoUserMap = errors.New("User namespaces enabled, but no user mapping found.")
errNoGIDMap = errors.New("User namespaces enabled, but no gid mappings found.")
errNoGroupMap = errors.New("User namespaces enabled, but no group mapping found.")
)
// HostUID gets the translated uid for the process on host which could be // HostUID gets the translated uid for the process on host which could be
// different when user namespaces are enabled. // different when user namespaces are enabled.
func (c Config) HostUID(containerId int) (int, error) { func (c Config) HostUID(containerId int) (int, error) {
if c.Namespaces.Contains(NEWUSER) { if c.Namespaces.Contains(NEWUSER) {
if c.UidMappings == nil { if c.UidMappings == nil {
return -1, errNoUIDMap return -1, fmt.Errorf("User namespaces enabled, but no uid mappings found.")
} }
id, found := c.hostIDFromMapping(containerId, c.UidMappings) id, found := c.hostIDFromMapping(containerId, c.UidMappings)
if !found { if !found {
return -1, errNoUserMap return -1, fmt.Errorf("User namespaces enabled, but no user mapping found.")
} }
return id, nil return id, nil
} }
@ -37,11 +30,11 @@ func (c Config) HostRootUID() (int, error) {
func (c Config) HostGID(containerId int) (int, error) { func (c Config) HostGID(containerId int) (int, error) {
if c.Namespaces.Contains(NEWUSER) { if c.Namespaces.Contains(NEWUSER) {
if c.GidMappings == nil { if c.GidMappings == nil {
return -1, errNoGIDMap return -1, fmt.Errorf("User namespaces enabled, but no gid mappings found.")
} }
id, found := c.hostIDFromMapping(containerId, c.GidMappings) id, found := c.hostIDFromMapping(containerId, c.GidMappings)
if !found { if !found {
return -1, errNoGroupMap return -1, fmt.Errorf("User namespaces enabled, but no group mapping found.")
} }
return id, nil return id, nil
} }

@ -1,4 +1,3 @@
//go:build gofuzz
// +build gofuzz // +build gofuzz
package configs package configs

@ -1,9 +1,6 @@
package configs package configs
type IntelRdt struct { type IntelRdt struct {
// The identity for RDT Class of Service
ClosID string `json:"closID,omitempty"`
// The schema for L3 cache id and capacity bitmask (CBM) // The schema for L3 cache id and capacity bitmask (CBM)
// Format: "L3:<cache_id0>=<cbm0>;<cache_id1>=<cbm1>;..." // Format: "L3:<cache_id0>=<cbm0>;<cache_id1>=<cbm1>;..."
L3CacheSchema string `json:"l3_cache_schema,omitempty"` L3CacheSchema string `json:"l3_cache_schema,omitempty"`

@ -1,7 +1,5 @@
package configs package configs
import "golang.org/x/sys/unix"
const ( const (
// EXT_COPYUP is a directive to copy up the contents of a directory when // EXT_COPYUP is a directive to copy up the contents of a directory when
// a tmpfs is mounted over it. // a tmpfs is mounted over it.
@ -30,9 +28,6 @@ type Mount struct {
// Relabel source if set, "z" indicates shared, "Z" indicates unshared. // Relabel source if set, "z" indicates shared, "Z" indicates unshared.
Relabel string `json:"relabel"` Relabel string `json:"relabel"`
// RecAttr represents mount properties to be applied recursively (AT_RECURSIVE), see mount_setattr(2).
RecAttr *unix.MountAttr `json:"rec_attr"`
// Extensions are additional flags that are specific to runc. // Extensions are additional flags that are specific to runc.
Extensions int `json:"extensions"` Extensions int `json:"extensions"`
@ -42,7 +37,3 @@ type Mount struct {
// Optional Command to be run after Source is mounted. // Optional Command to be run after Source is mounted.
PostmountCmds []Command `json:"postmount_cmds"` PostmountCmds []Command `json:"postmount_cmds"`
} }
func (m *Mount) IsBind() bool {
return m.Flags&unix.MS_BIND != 0
}

@ -1,4 +1,3 @@
//go:build linux
// +build linux // +build linux
package configs package configs

@ -1,4 +1,3 @@
//go:build !linux && !windows
// +build !linux,!windows // +build !linux,!windows
package configs package configs

@ -1,4 +1,3 @@
//go:build !linux
// +build !linux // +build !linux
package configs package configs

@ -1,9 +0,0 @@
package configs
// LinuxRdma for Linux cgroup 'rdma' resource management (Linux 4.11)
type LinuxRdma struct {
// Maximum number of HCA handles that can be opened. Default is "no limit".
HcaHandles *uint32 `json:"hca_handles,omitempty"`
// Maximum number of HCA objects that can be created. Default is "no limit".
HcaObjects *uint32 `json:"hca_objects,omitempty"`
}

@ -1,10 +1,10 @@
//go:build !windows
// +build !windows // +build !windows
package devices package devices
import ( import (
"errors" "errors"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
@ -16,8 +16,8 @@ var ErrNotADevice = errors.New("not a device node")
// Testing dependencies // Testing dependencies
var ( var (
unixLstat = unix.Lstat unixLstat = unix.Lstat
osReadDir = os.ReadDir ioutilReadDir = ioutil.ReadDir
) )
func mkDev(d *Rule) (uint64, error) { func mkDev(d *Rule) (uint64, error) {
@ -40,7 +40,7 @@ func DeviceFromPath(path, permissions string) (*Device, error) {
var ( var (
devType Type devType Type
mode = stat.Mode mode = stat.Mode
devNumber = uint64(stat.Rdev) //nolint:unconvert // Rdev is uint32 on e.g. MIPS. devNumber = uint64(stat.Rdev)
major = unix.Major(devNumber) major = unix.Major(devNumber)
minor = unix.Minor(devNumber) minor = unix.Minor(devNumber)
) )
@ -76,7 +76,7 @@ func HostDevices() ([]*Device, error) {
// GetDevices recursively traverses a directory specified by path // GetDevices recursively traverses a directory specified by path
// and returns all devices found there. // and returns all devices found there.
func GetDevices(path string) ([]*Device, error) { func GetDevices(path string) ([]*Device, error) {
files, err := osReadDir(path) files, err := ioutilReadDir(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -103,7 +103,7 @@ func GetDevices(path string) ([]*Device, error) {
} }
device, err := DeviceFromPath(filepath.Join(path, f.Name()), "rwm") device, err := DeviceFromPath(filepath.Join(path, f.Name()), "rwm")
if err != nil { if err != nil {
if errors.Is(err, ErrNotADevice) { if err == ErrNotADevice {
continue continue
} }
if os.IsNotExist(err) { if os.IsNotExist(err) {

@ -1,4 +1,3 @@
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build darwin dragonfly freebsd linux netbsd openbsd solaris // +build darwin dragonfly freebsd linux netbsd openbsd solaris
package user package user

@ -120,7 +120,7 @@ func ParsePasswdFileFilter(path string, filter func(User) bool) ([]User, error)
func ParsePasswdFilter(r io.Reader, filter func(User) bool) ([]User, error) { func ParsePasswdFilter(r io.Reader, filter func(User) bool) ([]User, error) {
if r == nil { if r == nil {
return nil, errors.New("nil source for passwd-formatted data") return nil, fmt.Errorf("nil source for passwd-formatted data")
} }
var ( var (
@ -178,7 +178,7 @@ func ParseGroupFileFilter(path string, filter func(Group) bool) ([]Group, error)
func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) { func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) {
if r == nil { if r == nil {
return nil, errors.New("nil source for group-formatted data") return nil, fmt.Errorf("nil source for group-formatted data")
} }
rd := bufio.NewReader(r) rd := bufio.NewReader(r)
out := []Group{} out := []Group{}
@ -339,7 +339,7 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
if userArg == "" { if userArg == "" {
userArg = strconv.Itoa(user.Uid) userArg = strconv.Itoa(user.Uid)
} }
return nil, fmt.Errorf("unable to find user %s: %w", userArg, err) return nil, fmt.Errorf("unable to find user %s: %v", userArg, err)
} }
var matchedUserName string var matchedUserName string
@ -355,7 +355,7 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
if uidErr != nil { if uidErr != nil {
// Not numeric. // Not numeric.
return nil, fmt.Errorf("unable to find user %s: %w", userArg, ErrNoPasswdEntries) return nil, fmt.Errorf("unable to find user %s: %v", userArg, ErrNoPasswdEntries)
} }
user.Uid = uidArg user.Uid = uidArg
@ -390,7 +390,7 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
return g.Name == groupArg return g.Name == groupArg
}) })
if err != nil && group != nil { if err != nil && group != nil {
return nil, fmt.Errorf("unable to find groups for spec %v: %w", matchedUserName, err) return nil, fmt.Errorf("unable to find groups for spec %v: %v", matchedUserName, err)
} }
// Only start modifying user.Gid if it is in explicit form. // Only start modifying user.Gid if it is in explicit form.
@ -404,7 +404,7 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
if gidErr != nil { if gidErr != nil {
// Not numeric. // Not numeric.
return nil, fmt.Errorf("unable to find group %s: %w", groupArg, ErrNoGroupEntries) return nil, fmt.Errorf("unable to find group %s: %v", groupArg, ErrNoGroupEntries)
} }
user.Gid = gidArg user.Gid = gidArg
@ -445,7 +445,7 @@ func GetAdditionalGroups(additionalGroups []string, group io.Reader) ([]int, err
return false return false
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("Unable to find additional groups %v: %w", additionalGroups, err) return nil, fmt.Errorf("Unable to find additional groups %v: %v", additionalGroups, err)
} }
} }
@ -468,8 +468,7 @@ func GetAdditionalGroups(additionalGroups []string, group io.Reader) ([]int, err
if !found { if !found {
gid, err := strconv.ParseInt(ag, 10, 64) gid, err := strconv.ParseInt(ag, 10, 64)
if err != nil { if err != nil {
// Not a numeric ID either. return nil, fmt.Errorf("Unable to find group %s", ag)
return nil, fmt.Errorf("Unable to find group %s: %w", ag, ErrNoGroupEntries)
} }
// Ensure gid is inside gid range. // Ensure gid is inside gid range.
if gid < minID || gid > maxID { if gid < minID || gid > maxID {
@ -522,7 +521,7 @@ func ParseSubIDFileFilter(path string, filter func(SubID) bool) ([]SubID, error)
func ParseSubIDFilter(r io.Reader, filter func(SubID) bool) ([]SubID, error) { func ParseSubIDFilter(r io.Reader, filter func(SubID) bool) ([]SubID, error) {
if r == nil { if r == nil {
return nil, errors.New("nil source for subid-formatted data") return nil, fmt.Errorf("nil source for subid-formatted data")
} }
var ( var (
@ -575,7 +574,7 @@ func ParseIDMapFileFilter(path string, filter func(IDMap) bool) ([]IDMap, error)
func ParseIDMapFilter(r io.Reader, filter func(IDMap) bool) ([]IDMap, error) { func ParseIDMapFilter(r io.Reader, filter func(IDMap) bool) ([]IDMap, error) {
if r == nil { if r == nil {
return nil, errors.New("nil source for idmap-formatted data") return nil, fmt.Errorf("nil source for idmap-formatted data")
} }
var ( var (

@ -1,4 +1,3 @@
//go:build gofuzz
// +build gofuzz // +build gofuzz
package user package user

@ -1,4 +1,3 @@
//go:build gofuzz
// +build gofuzz // +build gofuzz
package userns package userns

@ -1,4 +1,3 @@
//go:build !linux
// +build !linux // +build !linux
package userns package userns

@ -1,3 +1,5 @@
// +build linux
package utils package utils
/* /*
@ -86,11 +88,6 @@ func SendFd(socket *os.File, name string, fd uintptr) error {
if len(name) >= MaxNameLen { if len(name) >= MaxNameLen {
return fmt.Errorf("sendfd: filename too long: %s", name) return fmt.Errorf("sendfd: filename too long: %s", name)
} }
return SendFds(socket, []byte(name), int(fd)) oob := unix.UnixRights(int(fd))
} return unix.Sendmsg(int(socket.Fd()), []byte(name), oob, nil, 0)
// SendFds sends a list of files descriptor and msg over the given AF_UNIX socket.
func SendFds(socket *os.File, msg []byte, fds ...int) error {
oob := unix.UnixRights(fds...)
return unix.Sendmsg(int(socket.Fd()), msg, oob, nil, 0)
} }

@ -11,7 +11,7 @@ import (
"strings" "strings"
"unsafe" "unsafe"
securejoin "github.com/cyphar/filepath-securejoin" "github.com/cyphar/filepath-securejoin"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@ -33,6 +33,16 @@ func init() {
} }
} }
// ResolveRootfs ensures that the current working directory is
// not a symlink and returns the absolute path to the rootfs
func ResolveRootfs(uncleanRootfs string) (string, error) {
rootfs, err := filepath.Abs(uncleanRootfs)
if err != nil {
return "", err
}
return filepath.EvalSymlinks(rootfs)
}
// ExitStatus returns the correct exit status for a process based on if it // ExitStatus returns the correct exit status for a process based on if it
// was signaled or exited cleanly // was signaled or exited cleanly
func ExitStatus(status unix.WaitStatus) int { func ExitStatus(status unix.WaitStatus) int {
@ -110,7 +120,7 @@ func WithProcfd(root, unsafePath string, fn func(procfd string) error) error {
unsafePath = stripRoot(root, unsafePath) unsafePath = stripRoot(root, unsafePath)
path, err := securejoin.SecureJoin(root, unsafePath) path, err := securejoin.SecureJoin(root, unsafePath)
if err != nil { if err != nil {
return fmt.Errorf("resolving path inside rootfs failed: %w", err) return fmt.Errorf("resolving path inside rootfs failed: %v", err)
} }
// Open the target path. // Open the target path.

@ -1,4 +1,3 @@
//go:build !windows
// +build !windows // +build !windows
package utils package utils
@ -15,7 +14,7 @@ import (
func EnsureProcHandle(fh *os.File) error { func EnsureProcHandle(fh *os.File) error {
var buf unix.Statfs_t var buf unix.Statfs_t
if err := unix.Fstatfs(int(fh.Fd()), &buf); err != nil { if err := unix.Fstatfs(int(fh.Fd()), &buf); err != nil {
return fmt.Errorf("ensure %s is on procfs: %w", fh.Name(), err) return fmt.Errorf("ensure %s is on procfs: %v", fh.Name(), err)
} }
if buf.Type != unix.PROC_SUPER_MAGIC { if buf.Type != unix.PROC_SUPER_MAGIC {
return fmt.Errorf("%s is not on procfs", fh.Name()) return fmt.Errorf("%s is not on procfs", fh.Name())
@ -53,7 +52,7 @@ func CloseExecFrom(minFd int) error {
// Intentionally ignore errors from unix.CloseOnExec -- the cases where // Intentionally ignore errors from unix.CloseOnExec -- the cases where
// this might fail are basically file descriptors that have already // this might fail are basically file descriptors that have already
// been closed (including and especially the one that was created when // been closed (including and especially the one that was created when
// os.ReadDir did the "opendir" syscall). // ioutil.ReadDir did the "opendir" syscall).
unix.CloseOnExec(fd) unix.CloseOnExec(fd)
} }
return nil return nil

@ -15,7 +15,7 @@ type Spec struct {
// Mounts configures additional mounts (on top of Root). // Mounts configures additional mounts (on top of Root).
Mounts []Mount `json:"mounts,omitempty"` Mounts []Mount `json:"mounts,omitempty"`
// Hooks configures callbacks for container lifecycle events. // Hooks configures callbacks for container lifecycle events.
Hooks *Hooks `json:"hooks,omitempty" platform:"linux,solaris"` Hooks *Hooks `json:"hooks,omitempty" platform:"linux,solaris,zos"`
// Annotations contains arbitrary metadata for the container. // Annotations contains arbitrary metadata for the container.
Annotations map[string]string `json:"annotations,omitempty"` Annotations map[string]string `json:"annotations,omitempty"`
@ -27,6 +27,8 @@ type Spec struct {
Windows *Windows `json:"windows,omitempty" platform:"windows"` Windows *Windows `json:"windows,omitempty" platform:"windows"`
// VM specifies configuration for virtual-machine-based containers. // VM specifies configuration for virtual-machine-based containers.
VM *VM `json:"vm,omitempty" platform:"vm"` VM *VM `json:"vm,omitempty" platform:"vm"`
// ZOS is platform-specific configuration for z/OS based containers.
ZOS *ZOS `json:"zos,omitempty" platform:"zos"`
} }
// Process contains information to start a specific application inside the container. // Process contains information to start a specific application inside the container.
@ -49,7 +51,7 @@ type Process struct {
// Capabilities are Linux capabilities that are kept for the process. // Capabilities are Linux capabilities that are kept for the process.
Capabilities *LinuxCapabilities `json:"capabilities,omitempty" platform:"linux"` Capabilities *LinuxCapabilities `json:"capabilities,omitempty" platform:"linux"`
// Rlimits specifies rlimit options to apply to the process. // Rlimits specifies rlimit options to apply to the process.
Rlimits []POSIXRlimit `json:"rlimits,omitempty" platform:"linux,solaris"` Rlimits []POSIXRlimit `json:"rlimits,omitempty" platform:"linux,solaris,zos"`
// NoNewPrivileges controls whether additional privileges could be gained by processes in the container. // NoNewPrivileges controls whether additional privileges could be gained by processes in the container.
NoNewPrivileges bool `json:"noNewPrivileges,omitempty" platform:"linux"` NoNewPrivileges bool `json:"noNewPrivileges,omitempty" platform:"linux"`
// ApparmorProfile specifies the apparmor profile for the container. // ApparmorProfile specifies the apparmor profile for the container.
@ -86,11 +88,11 @@ type Box struct {
// User specifies specific user (and group) information for the container process. // User specifies specific user (and group) information for the container process.
type User struct { type User struct {
// UID is the user id. // UID is the user id.
UID uint32 `json:"uid" platform:"linux,solaris"` UID uint32 `json:"uid" platform:"linux,solaris,zos"`
// GID is the group id. // GID is the group id.
GID uint32 `json:"gid" platform:"linux,solaris"` GID uint32 `json:"gid" platform:"linux,solaris,zos"`
// Umask is the umask for the init process. // Umask is the umask for the init process.
Umask *uint32 `json:"umask,omitempty" platform:"linux,solaris"` Umask *uint32 `json:"umask,omitempty" platform:"linux,solaris,zos"`
// AdditionalGids are additional group ids set for the container's process. // AdditionalGids are additional group ids set for the container's process.
AdditionalGids []uint32 `json:"additionalGids,omitempty" platform:"linux,solaris"` AdditionalGids []uint32 `json:"additionalGids,omitempty" platform:"linux,solaris"`
// Username is the user name. // Username is the user name.
@ -110,7 +112,7 @@ type Mount struct {
// Destination is the absolute path where the mount will be placed in the container. // Destination is the absolute path where the mount will be placed in the container.
Destination string `json:"destination"` Destination string `json:"destination"`
// Type specifies the mount kind. // Type specifies the mount kind.
Type string `json:"type,omitempty" platform:"linux,solaris"` Type string `json:"type,omitempty" platform:"linux,solaris,zos"`
// Source specifies the source path of the mount. // Source specifies the source path of the mount.
Source string `json:"source,omitempty"` Source string `json:"source,omitempty"`
// Options are fstab style mount options. // Options are fstab style mount options.
@ -178,7 +180,7 @@ type Linux struct {
// MountLabel specifies the selinux context for the mounts in the container. // MountLabel specifies the selinux context for the mounts in the container.
MountLabel string `json:"mountLabel,omitempty"` MountLabel string `json:"mountLabel,omitempty"`
// IntelRdt contains Intel Resource Director Technology (RDT) information for // IntelRdt contains Intel Resource Director Technology (RDT) information for
// handling resource constraints (e.g., L3 cache, memory bandwidth) for the container // handling resource constraints and monitoring metrics (e.g., L3 cache, memory bandwidth) for the container
IntelRdt *LinuxIntelRdt `json:"intelRdt,omitempty"` IntelRdt *LinuxIntelRdt `json:"intelRdt,omitempty"`
// Personality contains configuration for the Linux personality syscall // Personality contains configuration for the Linux personality syscall
Personality *LinuxPersonality `json:"personality,omitempty"` Personality *LinuxPersonality `json:"personality,omitempty"`
@ -683,8 +685,9 @@ type LinuxSyscall struct {
Args []LinuxSeccompArg `json:"args,omitempty"` Args []LinuxSeccompArg `json:"args,omitempty"`
} }
// LinuxIntelRdt has container runtime resource constraints for Intel RDT // LinuxIntelRdt has container runtime resource constraints for Intel RDT CAT and MBA
// CAT and MBA features which introduced in Linux 4.10 and 4.12 kernel // features and flags enabling Intel RDT CMT and MBM features.
// Intel RDT features are available in Linux 4.14 and newer kernel versions.
type LinuxIntelRdt struct { type LinuxIntelRdt struct {
// The identity for RDT Class of Service // The identity for RDT Class of Service
ClosID string `json:"closID,omitempty"` ClosID string `json:"closID,omitempty"`
@ -697,4 +700,36 @@ type LinuxIntelRdt struct {
// The unit of memory bandwidth is specified in "percentages" by // The unit of memory bandwidth is specified in "percentages" by
// default, and in "MBps" if MBA Software Controller is enabled. // default, and in "MBps" if MBA Software Controller is enabled.
MemBwSchema string `json:"memBwSchema,omitempty"` MemBwSchema string `json:"memBwSchema,omitempty"`
// EnableCMT is the flag to indicate if the Intel RDT CMT is enabled. CMT (Cache Monitoring Technology) supports monitoring of
// the last-level cache (LLC) occupancy for the container.
EnableCMT bool `json:"enableCMT,omitempty"`
// EnableMBM is the flag to indicate if the Intel RDT MBM is enabled. MBM (Memory Bandwidth Monitoring) supports monitoring of
// total and local memory bandwidth for the container.
EnableMBM bool `json:"enableMBM,omitempty"`
}
// ZOS contains platform-specific configuration for z/OS based containers.
type ZOS struct {
// Devices are a list of device nodes that are created for the container
Devices []ZOSDevice `json:"devices,omitempty"`
}
// ZOSDevice represents the mknod information for a z/OS special device file
type ZOSDevice struct {
// Path to the device.
Path string `json:"path"`
// Device type, block, char, etc.
Type string `json:"type"`
// Major is the device's major number.
Major int64 `json:"major"`
// Minor is the device's minor number.
Minor int64 `json:"minor"`
// FileMode permission bits for the device.
FileMode *os.FileMode `json:"fileMode,omitempty"`
// UID of the device.
UID *uint32 `json:"uid,omitempty"`
// Gid of the device.
GID *uint32 `json:"gid,omitempty"`
} }

@ -123,6 +123,13 @@ func (g *Generator) initConfigLinuxResourcesPids() {
} }
} }
func (g *Generator) initConfigLinuxResourcesUnified() {
g.initConfigLinuxResources()
if g.Config.Linux.Resources.Unified == nil {
g.Config.Linux.Resources.Unified = map[string]string{}
}
}
func (g *Generator) initConfigSolaris() { func (g *Generator) initConfigSolaris() {
g.initConfig() g.initConfig()
if g.Config.Solaris == nil { if g.Config.Solaris == nil {
@ -185,24 +192,3 @@ func (g *Generator) initConfigVM() {
g.Config.VM = &rspec.VM{} g.Config.VM = &rspec.VM{}
} }
} }
func (g *Generator) initConfigVMHypervisor() {
g.initConfigVM()
if &g.Config.VM.Hypervisor == nil {
g.Config.VM.Hypervisor = rspec.VMHypervisor{}
}
}
func (g *Generator) initConfigVMKernel() {
g.initConfigVM()
if &g.Config.VM.Kernel == nil {
g.Config.VM.Kernel = rspec.VMKernel{}
}
}
func (g *Generator) initConfigVMImage() {
g.initConfigVM()
if &g.Config.VM.Image == nil {
g.Config.VM.Image = rspec.VMImage{}
}
}

@ -604,6 +604,12 @@ func (g *Generator) SetLinuxCgroupsPath(path string) {
g.Config.Linux.CgroupsPath = path g.Config.Linux.CgroupsPath = path
} }
// SetLinuxIntelRdtClosID sets g.Config.Linux.IntelRdt.ClosID
func (g *Generator) SetLinuxIntelRdtClosID(clos string) {
g.initConfigLinuxIntelRdt()
g.Config.Linux.IntelRdt.ClosID = clos
}
// SetLinuxIntelRdtL3CacheSchema sets g.Config.Linux.IntelRdt.L3CacheSchema // SetLinuxIntelRdtL3CacheSchema sets g.Config.Linux.IntelRdt.L3CacheSchema
func (g *Generator) SetLinuxIntelRdtL3CacheSchema(schema string) { func (g *Generator) SetLinuxIntelRdtL3CacheSchema(schema string) {
g.initConfigLinuxIntelRdt() g.initConfigLinuxIntelRdt()
@ -851,6 +857,28 @@ func (g *Generator) DropLinuxResourcesHugepageLimit(pageSize string) {
} }
} }
// AddLinuxResourcesUnified sets the g.Config.Linux.Resources.Unified
func (g *Generator) SetLinuxResourcesUnified(unified map[string]string) {
g.initConfigLinuxResourcesUnified()
for k, v := range unified {
g.Config.Linux.Resources.Unified[k] = v
}
}
// AddLinuxResourcesUnified adds or updates the key-value pair from g.Config.Linux.Resources.Unified
func (g *Generator) AddLinuxResourcesUnified(key, val string) {
g.initConfigLinuxResourcesUnified()
g.Config.Linux.Resources.Unified[key] = val
}
// DropLinuxResourcesUnified drops a key-value pair from g.Config.Linux.Resources.Unified
func (g *Generator) DropLinuxResourcesUnified(key string) {
if g.Config == nil || g.Config.Linux == nil || g.Config.Linux.Resources == nil || g.Config.Linux.Resources.Unified == nil {
return
}
delete(g.Config.Linux.Resources.Unified, key)
}
// SetLinuxResourcesMemoryLimit sets g.Config.Linux.Resources.Memory.Limit. // SetLinuxResourcesMemoryLimit sets g.Config.Linux.Resources.Memory.Limit.
func (g *Generator) SetLinuxResourcesMemoryLimit(limit int64) { func (g *Generator) SetLinuxResourcesMemoryLimit(limit int64) {
g.initConfigLinuxResourcesMemory() g.initConfigLinuxResourcesMemory()
@ -1025,10 +1053,9 @@ func (g *Generator) ClearPreStartHooks() {
} }
// AddPreStartHook add a prestart hook into g.Config.Hooks.Prestart. // AddPreStartHook add a prestart hook into g.Config.Hooks.Prestart.
func (g *Generator) AddPreStartHook(preStartHook rspec.Hook) error { func (g *Generator) AddPreStartHook(preStartHook rspec.Hook) {
g.initConfigHooks() g.initConfigHooks()
g.Config.Hooks.Prestart = append(g.Config.Hooks.Prestart, preStartHook) g.Config.Hooks.Prestart = append(g.Config.Hooks.Prestart, preStartHook)
return nil
} }
// ClearPostStopHooks clear g.Config.Hooks.Poststop. // ClearPostStopHooks clear g.Config.Hooks.Poststop.
@ -1040,10 +1067,9 @@ func (g *Generator) ClearPostStopHooks() {
} }
// AddPostStopHook adds a poststop hook into g.Config.Hooks.Poststop. // AddPostStopHook adds a poststop hook into g.Config.Hooks.Poststop.
func (g *Generator) AddPostStopHook(postStopHook rspec.Hook) error { func (g *Generator) AddPostStopHook(postStopHook rspec.Hook) {
g.initConfigHooks() g.initConfigHooks()
g.Config.Hooks.Poststop = append(g.Config.Hooks.Poststop, postStopHook) g.Config.Hooks.Poststop = append(g.Config.Hooks.Poststop, postStopHook)
return nil
} }
// ClearPostStartHooks clear g.Config.Hooks.Poststart. // ClearPostStartHooks clear g.Config.Hooks.Poststart.
@ -1055,10 +1081,9 @@ func (g *Generator) ClearPostStartHooks() {
} }
// AddPostStartHook adds a poststart hook into g.Config.Hooks.Poststart. // AddPostStartHook adds a poststart hook into g.Config.Hooks.Poststart.
func (g *Generator) AddPostStartHook(postStartHook rspec.Hook) error { func (g *Generator) AddPostStartHook(postStartHook rspec.Hook) {
g.initConfigHooks() g.initConfigHooks()
g.Config.Hooks.Poststart = append(g.Config.Hooks.Poststart, postStartHook) g.Config.Hooks.Poststart = append(g.Config.Hooks.Poststart, postStartHook)
return nil
} }
// AddMount adds a mount into g.Config.Mounts. // AddMount adds a mount into g.Config.Mounts.
@ -1560,12 +1585,8 @@ func (g *Generator) RemoveLinuxResourcesDevice(allow bool, devType string, major
return return
} }
} }
return
} }
// strPtr returns the pointer pointing to the string s.
func strPtr(s string) *string { return &s }
// SetSyscallAction adds rules for syscalls with the specified action // SetSyscallAction adds rules for syscalls with the specified action
func (g *Generator) SetSyscallAction(arguments seccomp.SyscallOpts) error { func (g *Generator) SetSyscallAction(arguments seccomp.SyscallOpts) error {
g.initConfigLinuxSeccomp() g.initConfigLinuxSeccomp()
@ -1691,14 +1712,14 @@ func (g *Generator) SetVMHypervisorPath(path string) error {
if !strings.HasPrefix(path, "/") { if !strings.HasPrefix(path, "/") {
return fmt.Errorf("hypervisorPath %v is not an absolute path", path) return fmt.Errorf("hypervisorPath %v is not an absolute path", path)
} }
g.initConfigVMHypervisor() g.initConfigVM()
g.Config.VM.Hypervisor.Path = path g.Config.VM.Hypervisor.Path = path
return nil return nil
} }
// SetVMHypervisorParameters sets g.Config.VM.Hypervisor.Parameters // SetVMHypervisorParameters sets g.Config.VM.Hypervisor.Parameters
func (g *Generator) SetVMHypervisorParameters(parameters []string) { func (g *Generator) SetVMHypervisorParameters(parameters []string) {
g.initConfigVMHypervisor() g.initConfigVM()
g.Config.VM.Hypervisor.Parameters = parameters g.Config.VM.Hypervisor.Parameters = parameters
} }
@ -1707,14 +1728,14 @@ func (g *Generator) SetVMKernelPath(path string) error {
if !strings.HasPrefix(path, "/") { if !strings.HasPrefix(path, "/") {
return fmt.Errorf("kernelPath %v is not an absolute path", path) return fmt.Errorf("kernelPath %v is not an absolute path", path)
} }
g.initConfigVMKernel() g.initConfigVM()
g.Config.VM.Kernel.Path = path g.Config.VM.Kernel.Path = path
return nil return nil
} }
// SetVMKernelParameters sets g.Config.VM.Kernel.Parameters // SetVMKernelParameters sets g.Config.VM.Kernel.Parameters
func (g *Generator) SetVMKernelParameters(parameters []string) { func (g *Generator) SetVMKernelParameters(parameters []string) {
g.initConfigVMKernel() g.initConfigVM()
g.Config.VM.Kernel.Parameters = parameters g.Config.VM.Kernel.Parameters = parameters
} }
@ -1723,7 +1744,7 @@ func (g *Generator) SetVMKernelInitRD(initrd string) error {
if !strings.HasPrefix(initrd, "/") { if !strings.HasPrefix(initrd, "/") {
return fmt.Errorf("kernelInitrd %v is not an absolute path", initrd) return fmt.Errorf("kernelInitrd %v is not an absolute path", initrd)
} }
g.initConfigVMKernel() g.initConfigVM()
g.Config.VM.Kernel.InitRD = initrd g.Config.VM.Kernel.InitRD = initrd
return nil return nil
} }
@ -1733,7 +1754,7 @@ func (g *Generator) SetVMImagePath(path string) error {
if !strings.HasPrefix(path, "/") { if !strings.HasPrefix(path, "/") {
return fmt.Errorf("imagePath %v is not an absolute path", path) return fmt.Errorf("imagePath %v is not an absolute path", path)
} }
g.initConfigVMImage() g.initConfigVM()
g.Config.VM.Image.Path = path g.Config.VM.Image.Path = path
return nil return nil
} }
@ -1749,7 +1770,7 @@ func (g *Generator) SetVMImageFormat(format string) error {
default: default:
return fmt.Errorf("Commonly supported formats are: raw, qcow2, vdi, vmdk, vhd") return fmt.Errorf("Commonly supported formats are: raw, qcow2, vdi, vmdk, vhd")
} }
g.initConfigVMImage() g.initConfigVM()
g.Config.VM.Image.Format = format g.Config.VM.Image.Format = format
return nil return nil
} }

@ -4,9 +4,4 @@ const (
seccompOverwrite = "overwrite" seccompOverwrite = "overwrite"
seccompAppend = "append" seccompAppend = "append"
nothing = "nothing" nothing = "nothing"
kill = "kill"
trap = "trap"
trace = "trace"
allow = "allow"
errno = "errno"
) )

@ -1,3 +1,4 @@
//go:build linux
// +build linux // +build linux
package seccomp package seccomp

@ -1,3 +1,4 @@
//go:build !linux
// +build !linux // +build !linux
package seccomp package seccomp

@ -92,22 +92,6 @@ func identical(config1, config2 *rspec.LinuxSyscall) bool {
return reflect.DeepEqual(config1, config2) return reflect.DeepEqual(config1, config2)
} }
func identicalExceptAction(config1, config2 *rspec.LinuxSyscall) bool {
samename := sameName(config1, config2)
sameAction := sameAction(config1, config2)
sameArgs := sameArgs(config1, config2)
return samename && !sameAction && sameArgs
}
func identicalExceptArgs(config1, config2 *rspec.LinuxSyscall) bool {
samename := sameName(config1, config2)
sameAction := sameAction(config1, config2)
sameArgs := sameArgs(config1, config2)
return samename && sameAction && !sameArgs
}
func sameName(config1, config2 *rspec.LinuxSyscall) bool { func sameName(config1, config2 *rspec.LinuxSyscall) bool {
return reflect.DeepEqual(config1.Names, config2.Names) return reflect.DeepEqual(config1.Names, config2.Names)
} }

@ -131,9 +131,8 @@ func JSONSchemaURL(version string) (url string, err error) {
if err != nil { if err != nil {
return "", specerror.NewError(specerror.SpecVersionInSemVer, err, rspec.Version) return "", specerror.NewError(specerror.SpecVersionInSemVer, err, rspec.Version)
} }
configRenamedToConfigSchemaVersion, err := semver.Parse("1.0.0-rc2") // config.json became config-schema.json in 1.0.0-rc2 if ver.LT(semver.Version{Major: 1, Minor: 0, Patch: 2}) {
if ver.Compare(configRenamedToConfigSchemaVersion) == -1 { return "", errors.New("unsupported configuration version (older than 1.0.2)")
return "", fmt.Errorf("unsupported configuration version (older than %s)", configRenamedToConfigSchemaVersion)
} }
return fmt.Sprintf(configSchemaTemplate, version), nil return fmt.Sprintf(configSchemaTemplate, version), nil
} }

@ -1,3 +1,4 @@
//go:build linux
// +build linux // +build linux
package validate package validate

@ -1,3 +1,4 @@
//go:build !linux
// +build !linux // +build !linux
package validate package validate

@ -19,39 +19,19 @@ os:
language: go language: go
jobs:
include:
- name: "last libseccomp 2.5.0"
env:
- SECCOMP_VER=2.5.0
- SECCOMP_SHA256SUM=1ffa7038d2720ad191919816db3479295a4bcca1ec14e02f672539f4983014f3
- name: "compat libseccomp 2.4.4"
env:
- SECCOMP_VER=2.4.4
- SECCOMP_SHA256SUM=4e79738d1ef3c9b7ca9769f1f8b8d84fc17143c2c1c432e53b9c64787e0ff3eb
- name: "compat libseccomp 2.2.1"
env:
- SECCOMP_VER=2.2.1
- SECCOMP_SHA256SUM=0ba1789f54786c644af54cdffc9fd0dd0a8bb2b2ee153933f658855d2851a740
addons: addons:
apt: apt:
packages: packages:
- build-essential - build-essential
- astyle # TODO: use the main libseccomp git repo instead of a distro package
- golint - libseccomp2
- gperf - libseccomp-dev
install: install:
- go get -u golang.org/x/lint/golint - go get -u golang.org/x/lint/golint
# run all of the tests independently, fail if any of the tests error # run all of the tests independently, fail if any of the tests error
script: script:
- wget https://github.com/seccomp/libseccomp/releases/download/v$SECCOMP_VER/libseccomp-$SECCOMP_VER.tar.gz
- echo $SECCOMP_SHA256SUM libseccomp-$SECCOMP_VER.tar.gz | sha256sum -c
- tar xf libseccomp-$SECCOMP_VER.tar.gz
- pushd libseccomp-$SECCOMP_VER && ./configure --prefix=/opt/libseccomp-$SECCOMP_VER && make && sudo make install && popd
- make check-syntax - make check-syntax
- make lint - make lint
- PKG_CONFIG_PATH=/opt/libseccomp-$SECCOMP_VER/lib/pkgconfig LD_LIBRARY_PATH=/opt/libseccomp-$SECCOMP_VER/lib make vet - make check
- PKG_CONFIG_PATH=/opt/libseccomp-$SECCOMP_VER/lib/pkgconfig LD_LIBRARY_PATH=/opt/libseccomp-$SECCOMP_VER/lib make test

@ -18,14 +18,8 @@ fix-syntax:
vet: vet:
go vet -v go vet -v
# Previous bugs have made the tests freeze until the timeout. Golang default
# timeout for tests is 10 minutes, which is too long, considering current tests
# can be executed in less than 1 second. Reduce the timeout, so problems can
# be noticed earlier in the CI.
TEST_TIMEOUT=10s
test: test:
go test -v -timeout $(TEST_TIMEOUT) go test -v
lint: lint:
@$(if $(shell which golint),true,$(error "install golint and include it in your PATH")) @$(if $(shell which golint),true,$(error "install golint and include it in your PATH"))

@ -2,7 +2,7 @@
=============================================================================== ===============================================================================
https://github.com/seccomp/libseccomp-golang https://github.com/seccomp/libseccomp-golang
[![Build Status](https://img.shields.io/travis/seccomp/libseccomp-golang/main.svg)](https://travis-ci.org/seccomp/libseccomp-golang) [![Build Status](https://img.shields.io/travis/seccomp/libseccomp-golang/master.svg)](https://travis-ci.org/seccomp/libseccomp-golang)
The libseccomp library provides an easy to use, platform independent, interface The libseccomp library provides an easy to use, platform independent, interface
to the Linux Kernel's syscall filtering mechanism. The libseccomp API is to the Linux Kernel's syscall filtering mechanism. The libseccomp API is

@ -20,13 +20,6 @@ import (
// C wrapping code // C wrapping code
// To compile libseccomp-golang against a specific version of libseccomp:
// cd ../libseccomp && mkdir -p prefix
// ./configure --prefix=$PWD/prefix && make && make install
// cd ../libseccomp-golang
// PKG_CONFIG_PATH=$PWD/../libseccomp/prefix/lib/pkgconfig/ make
// LD_PRELOAD=$PWD/../libseccomp/prefix/lib/libseccomp.so.2.5.0 PKG_CONFIG_PATH=$PWD/../libseccomp/prefix/lib/pkgconfig/ make test
// #cgo pkg-config: libseccomp // #cgo pkg-config: libseccomp
// #include <stdlib.h> // #include <stdlib.h>
// #include <seccomp.h> // #include <seccomp.h>
@ -41,25 +34,19 @@ type VersionError struct {
minimum string minimum string
} }
func init() {
// This forces the cgo libseccomp to initialize its internal API support state,
// which is necessary on older versions of libseccomp in order to work
// correctly.
GetAPI()
}
func (e VersionError) Error() string { func (e VersionError) Error() string {
messageStr := "" format := "Libseccomp version too low: "
if e.message != "" { if e.message != "" {
messageStr = e.message + ": " format += e.message + ": "
} }
minimumStr := "" format += "minimum supported is "
if e.minimum != "" { if e.minimum != "" {
minimumStr = e.minimum format += e.minimum + ": "
} else { } else {
minimumStr = "2.2.0" format += "2.2.0: "
} }
return fmt.Sprintf("Libseccomp version too low: %sminimum supported is %s: detected %d.%d.%d", messageStr, minimumStr, verMajor, verMinor, verMicro) format += "detected %d.%d.%d"
return fmt.Sprintf(format, verMajor, verMinor, verMicro)
} }
// ScmpArch represents a CPU architecture. Seccomp can restrict syscalls on a // ScmpArch represents a CPU architecture. Seccomp can restrict syscalls on a
@ -82,61 +69,9 @@ type ScmpCondition struct {
Operand2 uint64 `json:"operand_two,omitempty"` Operand2 uint64 `json:"operand_two,omitempty"`
} }
// Seccomp userspace notification structures associated with filters that use the ActNotify action. // ScmpSyscall represents a Linux System Call
// ScmpSyscall identifies a Linux System Call by its number.
type ScmpSyscall int32 type ScmpSyscall int32
// ScmpFd represents a file-descriptor used for seccomp userspace notifications.
type ScmpFd int32
// ScmpNotifData describes the system call context that triggered a notification.
//
// Syscall: the syscall number
// Arch: the filter architecture
// InstrPointer: address of the instruction that triggered a notification
// Args: arguments (up to 6) for the syscall
//
type ScmpNotifData struct {
Syscall ScmpSyscall `json:"syscall,omitempty"`
Arch ScmpArch `json:"arch,omitempty"`
InstrPointer uint64 `json:"instr_pointer,omitempty"`
Args []uint64 `json:"args,omitempty"`
}
// ScmpNotifReq represents a seccomp userspace notification. See NotifReceive() for
// info on how to pull such a notification.
//
// ID: notification ID
// Pid: process that triggered the notification event
// Flags: filter flags (see seccomp(2))
// Data: system call context that triggered the notification
//
type ScmpNotifReq struct {
ID uint64 `json:"id,omitempty"`
Pid uint32 `json:"pid,omitempty"`
Flags uint32 `json:"flags,omitempty"`
Data ScmpNotifData `json:"data,omitempty"`
}
// ScmpNotifResp represents a seccomp userspace notification response. See NotifRespond()
// for info on how to push such a response.
//
// ID: notification ID (must match the corresponding ScmpNotifReq ID)
// Error: must be 0 if no error occurred, or an error constant from package
// syscall (e.g., syscall.EPERM, etc). In the latter case, it's used
// as an error return from the syscall that created the notification.
// Val: return value for the syscall that created the notification. Only
// relevant if Error is 0.
// Flags: userspace notification response flag (e.g., NotifRespFlagContinue)
//
type ScmpNotifResp struct {
ID uint64 `json:"id,omitempty"`
Error int32 `json:"error,omitempty"`
Val uint64 `json:"val,omitempty"`
Flags uint32 `json:"flags,omitempty"`
}
// Exported Constants // Exported Constants
const ( const (
@ -182,10 +117,6 @@ const (
ArchS390 ScmpArch = iota ArchS390 ScmpArch = iota
// ArchS390X represents 64-bit System z/390 syscalls // ArchS390X represents 64-bit System z/390 syscalls
ArchS390X ScmpArch = iota ArchS390X ScmpArch = iota
// ArchPARISC represents 32-bit PA-RISC
ArchPARISC ScmpArch = iota
// ArchPARISC64 represents 64-bit PA-RISC
ArchPARISC64 ScmpArch = iota
) )
const ( const (
@ -199,9 +130,6 @@ const (
ActKill ScmpAction = iota ActKill ScmpAction = iota
// ActTrap throws SIGSYS // ActTrap throws SIGSYS
ActTrap ScmpAction = iota ActTrap ScmpAction = iota
// ActNotify triggers a userspace notification. This action is only usable when
// libseccomp API level 6 or higher is supported.
ActNotify ScmpAction = iota
// ActErrno causes the syscall to return a negative error code. This // ActErrno causes the syscall to return a negative error code. This
// code can be set with the SetReturnCode method // code can be set with the SetReturnCode method
ActErrno ScmpAction = iota ActErrno ScmpAction = iota
@ -253,21 +181,6 @@ const (
CompareMaskedEqual ScmpCompareOp = iota CompareMaskedEqual ScmpCompareOp = iota
) )
var (
// ErrSyscallDoesNotExist represents an error condition where
// libseccomp is unable to resolve the syscall
ErrSyscallDoesNotExist = fmt.Errorf("could not resolve syscall name")
)
const (
// Userspace notification response flags
// NotifRespFlagContinue tells the kernel to continue executing the system
// call that triggered the notification. Must only be used when the notication
// response's error is 0.
NotifRespFlagContinue uint32 = 1
)
// Helpers for types // Helpers for types
// GetArchFromString returns an ScmpArch constant from a string representing an // GetArchFromString returns an ScmpArch constant from a string representing an
@ -310,10 +223,6 @@ func GetArchFromString(arch string) (ScmpArch, error) {
return ArchS390, nil return ArchS390, nil
case "s390x": case "s390x":
return ArchS390X, nil return ArchS390X, nil
case "parisc":
return ArchPARISC, nil
case "parisc64":
return ArchPARISC64, nil
default: default:
return ArchInvalid, fmt.Errorf("cannot convert unrecognized string %q", arch) return ArchInvalid, fmt.Errorf("cannot convert unrecognized string %q", arch)
} }
@ -354,10 +263,6 @@ func (a ScmpArch) String() string {
return "s390" return "s390"
case ArchS390X: case ArchS390X:
return "s390x" return "s390x"
case ArchPARISC:
return "parisc"
case ArchPARISC64:
return "parisc64"
case ArchNative: case ArchNative:
return "native" return "native"
case ArchInvalid: case ArchInvalid:
@ -405,8 +310,6 @@ func (a ScmpAction) String() string {
case ActTrace: case ActTrace:
return fmt.Sprintf("Action: Notify tracing processes with code %d", return fmt.Sprintf("Action: Notify tracing processes with code %d",
(a >> 16)) (a >> 16))
case ActNotify:
return "Action: Notify userspace"
case ActLog: case ActLog:
return "Action: Log system call" return "Action: Log system call"
case ActAllow: case ActAllow:
@ -446,7 +349,7 @@ func GetLibraryVersion() (major, minor, micro uint) {
// Returns a positive int containing the API level, or 0 with an error if the // Returns a positive int containing the API level, or 0 with an error if the
// API level could not be detected due to the library being older than v2.4.0. // API level could not be detected due to the library being older than v2.4.0.
// See the seccomp_api_get(3) man page for details on available API levels: // See the seccomp_api_get(3) man page for details on available API levels:
// https://github.com/seccomp/libseccomp/blob/main/doc/man/man3/seccomp_api_get.3 // https://github.com/seccomp/libseccomp/blob/master/doc/man/man3/seccomp_api_get.3
func GetAPI() (uint, error) { func GetAPI() (uint, error) {
return getAPI() return getAPI()
} }
@ -456,7 +359,7 @@ func GetAPI() (uint, error) {
// Returns an error if the API level could not be set. An error is always // Returns an error if the API level could not be set. An error is always
// returned if the library is older than v2.4.0 // returned if the library is older than v2.4.0
// See the seccomp_api_get(3) man page for details on available API levels: // See the seccomp_api_get(3) man page for details on available API levels:
// https://github.com/seccomp/libseccomp/blob/main/doc/man/man3/seccomp_api_get.3 // https://github.com/seccomp/libseccomp/blob/master/doc/man/man3/seccomp_api_get.3
func SetAPI(api uint) error { func SetAPI(api uint) error {
return setAPI(api) return setAPI(api)
} }
@ -483,7 +386,7 @@ func (s ScmpSyscall) GetNameByArch(arch ScmpArch) (string, error) {
cString := C.seccomp_syscall_resolve_num_arch(arch.toNative(), C.int(s)) cString := C.seccomp_syscall_resolve_num_arch(arch.toNative(), C.int(s))
if cString == nil { if cString == nil {
return "", ErrSyscallDoesNotExist return "", fmt.Errorf("could not resolve syscall name for %#x", int32(s))
} }
defer C.free(unsafe.Pointer(cString)) defer C.free(unsafe.Pointer(cString))
@ -506,7 +409,7 @@ func GetSyscallFromName(name string) (ScmpSyscall, error) {
result := C.seccomp_syscall_resolve_name(cString) result := C.seccomp_syscall_resolve_name(cString)
if result == scmpError { if result == scmpError {
return 0, ErrSyscallDoesNotExist return 0, fmt.Errorf("could not resolve name to syscall: %q", name)
} }
return ScmpSyscall(result), nil return ScmpSyscall(result), nil
@ -530,7 +433,7 @@ func GetSyscallFromNameByArch(name string, arch ScmpArch) (ScmpSyscall, error) {
result := C.seccomp_syscall_resolve_name_arch(arch.toNative(), cString) result := C.seccomp_syscall_resolve_name_arch(arch.toNative(), cString)
if result == scmpError { if result == scmpError {
return 0, ErrSyscallDoesNotExist return 0, fmt.Errorf("could not resolve name to syscall: %q on %v", name, arch)
} }
return ScmpSyscall(result), nil return ScmpSyscall(result), nil
@ -603,10 +506,11 @@ type ScmpFilter struct {
lock sync.Mutex lock sync.Mutex
} }
// NewFilter creates and returns a new filter context. Accepts a default action to be // NewFilter creates and returns a new filter context.
// taken for syscalls which match no rules in the filter. // Accepts a default action to be taken for syscalls which match no rules in
// Returns a reference to a valid filter context, or nil and an error // the filter.
// if the filter context could not be created or an invalid default action was given. // Returns a reference to a valid filter context, or nil and an error if the
// filter context could not be created or an invalid default action was given.
func NewFilter(defaultAction ScmpAction) (*ScmpFilter, error) { func NewFilter(defaultAction ScmpAction) (*ScmpFilter, error) {
if err := ensureSupportedVersion(); err != nil { if err := ensureSupportedVersion(); err != nil {
return nil, err return nil, err
@ -626,8 +530,8 @@ func NewFilter(defaultAction ScmpAction) (*ScmpFilter, error) {
filter.valid = true filter.valid = true
runtime.SetFinalizer(filter, filterFinalizer) runtime.SetFinalizer(filter, filterFinalizer)
// Enable TSync so all goroutines will receive the same rules. // Enable TSync so all goroutines will receive the same rules
// If the kernel does not support TSYNC, allow us to continue without error. // If the kernel does not support TSYNC, allow us to continue without error
if err := filter.setFilterAttr(filterAttrTsync, 0x1); err != nil && err != syscall.ENOTSUP { if err := filter.setFilterAttr(filterAttrTsync, 0x1); err != nil && err != syscall.ENOTSUP {
filter.Release() filter.Release()
return nil, fmt.Errorf("could not create filter - error setting tsync bit: %v", err) return nil, fmt.Errorf("could not create filter - error setting tsync bit: %v", err)
@ -874,9 +778,8 @@ func (f *ScmpFilter) GetNoNewPrivsBit() (bool, error) {
func (f *ScmpFilter) GetLogBit() (bool, error) { func (f *ScmpFilter) GetLogBit() (bool, error) {
log, err := f.getFilterAttr(filterAttrLog) log, err := f.getFilterAttr(filterAttrLog)
if err != nil { if err != nil {
// Ignore error, if not supported returns apiLevel == 0 api, apiErr := getAPI()
apiLevel, _ := GetAPI() if (apiErr != nil && api == 0) || (apiErr == nil && api < 3) {
if apiLevel < 3 {
return false, fmt.Errorf("getting the log bit is only supported in libseccomp 2.4.0 and newer with API level 3 or higher") return false, fmt.Errorf("getting the log bit is only supported in libseccomp 2.4.0 and newer with API level 3 or higher")
} }
@ -890,30 +793,6 @@ func (f *ScmpFilter) GetLogBit() (bool, error) {
return true, nil return true, nil
} }
// GetSSB returns the current state the SSB bit will be set to on the filter
// being loaded, or an error if an issue was encountered retrieving the value.
// The SSB bit tells the kernel that a seccomp user is not interested in enabling
// Speculative Store Bypass mitigation.
// The SSB bit is only usable when libseccomp API level 4 or higher is
// supported.
func (f *ScmpFilter) GetSSB() (bool, error) {
ssb, err := f.getFilterAttr(filterAttrSSB)
if err != nil {
api, apiErr := getAPI()
if (apiErr != nil && api == 0) || (apiErr == nil && api < 4) {
return false, fmt.Errorf("getting the SSB flag is only supported in libseccomp 2.5.0 and newer with API level 4 or higher")
}
return false, err
}
if ssb == 0 {
return false, nil
}
return true, nil
}
// SetBadArchAction sets the default action taken on a syscall for an // SetBadArchAction sets the default action taken on a syscall for an
// architecture not in the filter, or an error if an issue was encountered // architecture not in the filter, or an error if an issue was encountered
// setting the value. // setting the value.
@ -952,33 +831,10 @@ func (f *ScmpFilter) SetLogBit(state bool) error {
} }
err := f.setFilterAttr(filterAttrLog, toSet) err := f.setFilterAttr(filterAttrLog, toSet)
if err != nil {
// Ignore error, if not supported returns apiLevel == 0
apiLevel, _ := GetAPI()
if apiLevel < 3 {
return fmt.Errorf("setting the log bit is only supported in libseccomp 2.4.0 and newer with API level 3 or higher")
}
}
return err
}
// SetSSB sets the state of the SSB bit, which will be applied on filter
// load, or an error if an issue was encountered setting the value.
// The SSB bit is only usable when libseccomp API level 4 or higher is
// supported.
func (f *ScmpFilter) SetSSB(state bool) error {
var toSet C.uint32_t = 0x0
if state {
toSet = 0x1
}
err := f.setFilterAttr(filterAttrSSB, toSet)
if err != nil { if err != nil {
api, apiErr := getAPI() api, apiErr := getAPI()
if (apiErr != nil && api == 0) || (apiErr == nil && api < 4) { if (apiErr != nil && api == 0) || (apiErr == nil && api < 3) {
return fmt.Errorf("setting the SSB flag is only supported in libseccomp 2.5.0 and newer with API level 4 or higher") return fmt.Errorf("setting the log bit is only supported in libseccomp 2.4.0 and newer with API level 3 or higher")
} }
} }
@ -1091,36 +947,3 @@ func (f *ScmpFilter) ExportBPF(file *os.File) error {
return nil return nil
} }
// Userspace Notification API
// GetNotifFd returns the userspace notification file descriptor associated with the given
// filter context. Such a file descriptor is only valid after the filter has been loaded
// and only when the filter uses the ActNotify action. The file descriptor can be used to
// retrieve and respond to notifications associated with the filter (see NotifReceive(),
// NotifRespond(), and NotifIDValid()).
func (f *ScmpFilter) GetNotifFd() (ScmpFd, error) {
return f.getNotifFd()
}
// NotifReceive retrieves a seccomp userspace notification from a filter whose ActNotify
// action has triggered. The caller is expected to process the notification and return a
// response via NotifRespond(). Each invocation of this function returns one
// notification. As multiple notifications may be pending at any time, this function is
// normally called within a polling loop.
func NotifReceive(fd ScmpFd) (*ScmpNotifReq, error) {
return notifReceive(fd)
}
// NotifRespond responds to a notification retrieved via NotifReceive(). The response Id
// must match that of the corresponding notification retrieved via NotifReceive().
func NotifRespond(fd ScmpFd, scmpResp *ScmpNotifResp) error {
return notifRespond(fd, scmpResp)
}
// NotifIDValid checks if a notification is still valid. An return value of nil means the
// notification is still valid. Otherwise the notification is not valid. This can be used
// to mitigate time-of-check-time-of-use (TOCTOU) attacks as described in seccomp_notify_id_valid(2).
func NotifIDValid(fd ScmpFd, id uint64) error {
return notifIDValid(fd, id)
}

@ -14,13 +14,6 @@ import (
// Get the seccomp header in scope // Get the seccomp header in scope
// Need stdlib.h for free() on cstrings // Need stdlib.h for free() on cstrings
// To compile libseccomp-golang against a specific version of libseccomp:
// cd ../libseccomp && mkdir -p prefix
// ./configure --prefix=$PWD/prefix && make && make install
// cd ../libseccomp-golang
// PKG_CONFIG_PATH=$PWD/../libseccomp/prefix/lib/pkgconfig/ make
// LD_PRELOAD=$PWD/../libseccomp/prefix/lib/libseccomp.so.2.5.0 PKG_CONFIG_PATH=$PWD/../libseccomp/prefix/lib/pkgconfig/ make test
// #cgo pkg-config: libseccomp // #cgo pkg-config: libseccomp
/* /*
#include <errno.h> #include <errno.h>
@ -57,14 +50,6 @@ const uint32_t C_ARCH_BAD = ARCH_BAD;
#define SCMP_ARCH_S390X ARCH_BAD #define SCMP_ARCH_S390X ARCH_BAD
#endif #endif
#ifndef SCMP_ARCH_PARISC
#define SCMP_ARCH_PARISC ARCH_BAD
#endif
#ifndef SCMP_ARCH_PARISC64
#define SCMP_ARCH_PARISC64 ARCH_BAD
#endif
const uint32_t C_ARCH_NATIVE = SCMP_ARCH_NATIVE; const uint32_t C_ARCH_NATIVE = SCMP_ARCH_NATIVE;
const uint32_t C_ARCH_X86 = SCMP_ARCH_X86; const uint32_t C_ARCH_X86 = SCMP_ARCH_X86;
const uint32_t C_ARCH_X86_64 = SCMP_ARCH_X86_64; const uint32_t C_ARCH_X86_64 = SCMP_ARCH_X86_64;
@ -82,8 +67,6 @@ const uint32_t C_ARCH_PPC64 = SCMP_ARCH_PPC64;
const uint32_t C_ARCH_PPC64LE = SCMP_ARCH_PPC64LE; const uint32_t C_ARCH_PPC64LE = SCMP_ARCH_PPC64LE;
const uint32_t C_ARCH_S390 = SCMP_ARCH_S390; const uint32_t C_ARCH_S390 = SCMP_ARCH_S390;
const uint32_t C_ARCH_S390X = SCMP_ARCH_S390X; const uint32_t C_ARCH_S390X = SCMP_ARCH_S390X;
const uint32_t C_ARCH_PARISC = SCMP_ARCH_PARISC;
const uint32_t C_ARCH_PARISC64 = SCMP_ARCH_PARISC64;
#ifndef SCMP_ACT_LOG #ifndef SCMP_ACT_LOG
#define SCMP_ACT_LOG 0x7ffc0000U #define SCMP_ACT_LOG 0x7ffc0000U
@ -97,10 +80,6 @@ const uint32_t C_ARCH_PARISC64 = SCMP_ARCH_PARISC64;
#define SCMP_ACT_KILL_THREAD 0x00000000U #define SCMP_ACT_KILL_THREAD 0x00000000U
#endif #endif
#ifndef SCMP_ACT_NOTIFY
#define SCMP_ACT_NOTIFY 0x7fc00000U
#endif
const uint32_t C_ACT_KILL = SCMP_ACT_KILL; const uint32_t C_ACT_KILL = SCMP_ACT_KILL;
const uint32_t C_ACT_KILL_PROCESS = SCMP_ACT_KILL_PROCESS; const uint32_t C_ACT_KILL_PROCESS = SCMP_ACT_KILL_PROCESS;
const uint32_t C_ACT_KILL_THREAD = SCMP_ACT_KILL_THREAD; const uint32_t C_ACT_KILL_THREAD = SCMP_ACT_KILL_THREAD;
@ -109,7 +88,6 @@ const uint32_t C_ACT_ERRNO = SCMP_ACT_ERRNO(0);
const uint32_t C_ACT_TRACE = SCMP_ACT_TRACE(0); const uint32_t C_ACT_TRACE = SCMP_ACT_TRACE(0);
const uint32_t C_ACT_LOG = SCMP_ACT_LOG; const uint32_t C_ACT_LOG = SCMP_ACT_LOG;
const uint32_t C_ACT_ALLOW = SCMP_ACT_ALLOW; const uint32_t C_ACT_ALLOW = SCMP_ACT_ALLOW;
const uint32_t C_ACT_NOTIFY = SCMP_ACT_NOTIFY;
// The libseccomp SCMP_FLTATR_CTL_LOG member of the scmp_filter_attr enum was // The libseccomp SCMP_FLTATR_CTL_LOG member of the scmp_filter_attr enum was
// added in v2.4.0 // added in v2.4.0
@ -117,16 +95,12 @@ const uint32_t C_ACT_NOTIFY = SCMP_ACT_NOTIFY;
(SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 4) (SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 4)
#define SCMP_FLTATR_CTL_LOG _SCMP_FLTATR_MIN #define SCMP_FLTATR_CTL_LOG _SCMP_FLTATR_MIN
#endif #endif
#if SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 5
#define SCMP_FLTATR_CTL_SSB _SCMP_FLTATR_MIN
#endif
const uint32_t C_ATTRIBUTE_DEFAULT = (uint32_t)SCMP_FLTATR_ACT_DEFAULT; const uint32_t C_ATTRIBUTE_DEFAULT = (uint32_t)SCMP_FLTATR_ACT_DEFAULT;
const uint32_t C_ATTRIBUTE_BADARCH = (uint32_t)SCMP_FLTATR_ACT_BADARCH; const uint32_t C_ATTRIBUTE_BADARCH = (uint32_t)SCMP_FLTATR_ACT_BADARCH;
const uint32_t C_ATTRIBUTE_NNP = (uint32_t)SCMP_FLTATR_CTL_NNP; const uint32_t C_ATTRIBUTE_NNP = (uint32_t)SCMP_FLTATR_CTL_NNP;
const uint32_t C_ATTRIBUTE_TSYNC = (uint32_t)SCMP_FLTATR_CTL_TSYNC; const uint32_t C_ATTRIBUTE_TSYNC = (uint32_t)SCMP_FLTATR_CTL_TSYNC;
const uint32_t C_ATTRIBUTE_LOG = (uint32_t)SCMP_FLTATR_CTL_LOG; const uint32_t C_ATTRIBUTE_LOG = (uint32_t)SCMP_FLTATR_CTL_LOG;
const uint32_t C_ATTRIBUTE_SSB = (uint32_t)SCMP_FLTATR_CTL_SSB;
const int C_CMP_NE = (int)SCMP_CMP_NE; const int C_CMP_NE = (int)SCMP_CMP_NE;
const int C_CMP_LT = (int)SCMP_CMP_LT; const int C_CMP_LT = (int)SCMP_CMP_LT;
@ -215,51 +189,6 @@ void add_struct_arg_cmp(
return; return;
} }
// The seccomp notify API functions were added in v2.5.0
#if (SCMP_VER_MAJOR < 2) || \
(SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 5)
struct seccomp_data {
int nr;
__u32 arch;
__u64 instruction_pointer;
__u64 args[6];
};
struct seccomp_notif {
__u64 id;
__u32 pid;
__u32 flags;
struct seccomp_data data;
};
struct seccomp_notif_resp {
__u64 id;
__s64 val;
__s32 error;
__u32 flags;
};
int seccomp_notify_alloc(struct seccomp_notif **req, struct seccomp_notif_resp **resp) {
return -EOPNOTSUPP;
}
int seccomp_notify_fd(const scmp_filter_ctx ctx) {
return -EOPNOTSUPP;
}
void seccomp_notify_free(struct seccomp_notif *req, struct seccomp_notif_resp *resp) {
}
int seccomp_notify_id_valid(int fd, uint64_t id) {
return -EOPNOTSUPP;
}
int seccomp_notify_receive(int fd, struct seccomp_notif *req) {
return -EOPNOTSUPP;
}
int seccomp_notify_respond(int fd, struct seccomp_notif_resp *resp) {
return -EOPNOTSUPP;
}
#endif
*/ */
import "C" import "C"
@ -274,7 +203,6 @@ const (
filterAttrNNP scmpFilterAttr = iota filterAttrNNP scmpFilterAttr = iota
filterAttrTsync scmpFilterAttr = iota filterAttrTsync scmpFilterAttr = iota
filterAttrLog scmpFilterAttr = iota filterAttrLog scmpFilterAttr = iota
filterAttrSSB scmpFilterAttr = iota
) )
const ( const (
@ -282,7 +210,7 @@ const (
scmpError C.int = -1 scmpError C.int = -1
// Comparison boundaries to check for architecture validity // Comparison boundaries to check for architecture validity
archStart ScmpArch = ArchNative archStart ScmpArch = ArchNative
archEnd ScmpArch = ArchPARISC64 archEnd ScmpArch = ArchS390X
// Comparison boundaries to check for action validity // Comparison boundaries to check for action validity
actionStart ScmpAction = ActKill actionStart ScmpAction = ActKill
actionEnd ScmpAction = ActKillProcess actionEnd ScmpAction = ActKillProcess
@ -532,10 +460,6 @@ func archFromNative(a C.uint32_t) (ScmpArch, error) {
return ArchS390, nil return ArchS390, nil
case C.C_ARCH_S390X: case C.C_ARCH_S390X:
return ArchS390X, nil return ArchS390X, nil
case C.C_ARCH_PARISC:
return ArchPARISC, nil
case C.C_ARCH_PARISC64:
return ArchPARISC64, nil
default: default:
return 0x0, fmt.Errorf("unrecognized architecture %#x", uint32(a)) return 0x0, fmt.Errorf("unrecognized architecture %#x", uint32(a))
} }
@ -576,10 +500,6 @@ func (a ScmpArch) toNative() C.uint32_t {
return C.C_ARCH_S390 return C.C_ARCH_S390
case ArchS390X: case ArchS390X:
return C.C_ARCH_S390X return C.C_ARCH_S390X
case ArchPARISC:
return C.C_ARCH_PARISC
case ArchPARISC64:
return C.C_ARCH_PARISC64
case ArchNative: case ArchNative:
return C.C_ARCH_NATIVE return C.C_ARCH_NATIVE
default: default:
@ -628,8 +548,6 @@ func actionFromNative(a C.uint32_t) (ScmpAction, error) {
return ActLog, nil return ActLog, nil
case C.C_ACT_ALLOW: case C.C_ACT_ALLOW:
return ActAllow, nil return ActAllow, nil
case C.C_ACT_NOTIFY:
return ActNotify, nil
default: default:
return 0x0, fmt.Errorf("unrecognized action %#x", uint32(a)) return 0x0, fmt.Errorf("unrecognized action %#x", uint32(a))
} }
@ -654,8 +572,6 @@ func (a ScmpAction) toNative() C.uint32_t {
return C.C_ACT_LOG return C.C_ACT_LOG
case ActAllow: case ActAllow:
return C.C_ACT_ALLOW return C.C_ACT_ALLOW
case ActNotify:
return C.C_ACT_NOTIFY
default: default:
return 0x0 return 0x0
} }
@ -674,181 +590,7 @@ func (a scmpFilterAttr) toNative() uint32 {
return uint32(C.C_ATTRIBUTE_TSYNC) return uint32(C.C_ATTRIBUTE_TSYNC)
case filterAttrLog: case filterAttrLog:
return uint32(C.C_ATTRIBUTE_LOG) return uint32(C.C_ATTRIBUTE_LOG)
case filterAttrSSB:
return uint32(C.C_ATTRIBUTE_SSB)
default: default:
return 0x0 return 0x0
} }
} }
func (a ScmpSyscall) toNative() C.uint32_t {
return C.uint32_t(a)
}
func syscallFromNative(a C.int) ScmpSyscall {
return ScmpSyscall(a)
}
func notifReqFromNative(req *C.struct_seccomp_notif) (*ScmpNotifReq, error) {
scmpArgs := make([]uint64, 6)
for i := 0; i < len(scmpArgs); i++ {
scmpArgs[i] = uint64(req.data.args[i])
}
arch, err := archFromNative(req.data.arch)
if err != nil {
return nil, err
}
scmpData := ScmpNotifData{
Syscall: syscallFromNative(req.data.nr),
Arch: arch,
InstrPointer: uint64(req.data.instruction_pointer),
Args: scmpArgs,
}
scmpReq := &ScmpNotifReq{
ID: uint64(req.id),
Pid: uint32(req.pid),
Flags: uint32(req.flags),
Data: scmpData,
}
return scmpReq, nil
}
func (scmpResp *ScmpNotifResp) toNative(resp *C.struct_seccomp_notif_resp) {
resp.id = C.__u64(scmpResp.ID)
resp.val = C.__s64(scmpResp.Val)
resp.error = (C.__s32(scmpResp.Error) * -1) // kernel requires a negated value
resp.flags = C.__u32(scmpResp.Flags)
}
// Userspace Notification API
// Calls to C.seccomp_notify* hidden from seccomp.go
func (f *ScmpFilter) getNotifFd() (ScmpFd, error) {
f.lock.Lock()
defer f.lock.Unlock()
if !f.valid {
return -1, errBadFilter
}
// Ignore error, if not supported returns apiLevel == 0
apiLevel, _ := GetAPI()
if apiLevel < 6 {
return -1, fmt.Errorf("seccomp notification requires API level >= 6; current level = %d", apiLevel)
}
fd := C.seccomp_notify_fd(f.filterCtx)
return ScmpFd(fd), nil
}
func notifReceive(fd ScmpFd) (*ScmpNotifReq, error) {
var req *C.struct_seccomp_notif
var resp *C.struct_seccomp_notif_resp
// Ignore error, if not supported returns apiLevel == 0
apiLevel, _ := GetAPI()
if apiLevel < 6 {
return nil, fmt.Errorf("seccomp notification requires API level >= 6; current level = %d", apiLevel)
}
// we only use the request here; the response is unused
if retCode := C.seccomp_notify_alloc(&req, &resp); retCode != 0 {
return nil, errRc(retCode)
}
defer func() {
C.seccomp_notify_free(req, resp)
}()
for {
retCode, errno := C.seccomp_notify_receive(C.int(fd), req)
if retCode == 0 {
break
}
if errno == syscall.EINTR {
continue
}
if errno == syscall.ENOENT {
return nil, errno
}
return nil, errRc(retCode)
}
return notifReqFromNative(req)
}
func notifRespond(fd ScmpFd, scmpResp *ScmpNotifResp) error {
var req *C.struct_seccomp_notif
var resp *C.struct_seccomp_notif_resp
// Ignore error, if not supported returns apiLevel == 0
apiLevel, _ := GetAPI()
if apiLevel < 6 {
return fmt.Errorf("seccomp notification requires API level >= 6; current level = %d", apiLevel)
}
// we only use the reponse here; the request is discarded
if retCode := C.seccomp_notify_alloc(&req, &resp); retCode != 0 {
return errRc(retCode)
}
defer func() {
C.seccomp_notify_free(req, resp)
}()
scmpResp.toNative(resp)
for {
retCode, errno := C.seccomp_notify_respond(C.int(fd), resp)
if retCode == 0 {
break
}
if errno == syscall.EINTR {
continue
}
if errno == syscall.ENOENT {
return errno
}
return errRc(retCode)
}
return nil
}
func notifIDValid(fd ScmpFd, id uint64) error {
// Ignore error, if not supported returns apiLevel == 0
apiLevel, _ := GetAPI()
if apiLevel < 6 {
return fmt.Errorf("seccomp notification requires API level >= 6; current level = %d", apiLevel)
}
for {
retCode, errno := C.seccomp_notify_id_valid(C.int(fd), C.uint64_t(id))
if retCode == 0 {
break
}
if errno == syscall.EINTR {
continue
}
if errno == syscall.ENOENT {
return errno
}
return errRc(retCode)
}
return nil
}

@ -1,20 +0,0 @@
language: go
go:
- "1.12.x"
- "1.13.x"
- "1.14.x"
before_script:
# make sure we keep path in tact when we sudo
- sudo sed -i -e 's/^Defaults\tsecure_path.*$//' /etc/sudoers
# modprobe ip_gre or else the first gre device can't be deleted
- sudo modprobe ip_gre
# modprobe nf_conntrack for the conntrack testing
- sudo modprobe nf_conntrack
- sudo modprobe nf_conntrack_netlink
- sudo modprobe nf_conntrack_ipv4
- sudo modprobe nf_conntrack_ipv6
- sudo modprobe sch_hfsc
- sudo modprobe sch_sfq
install:
- go get -v -t ./...
go_import_path: github.com/vishvananda/netlink

@ -1,6 +1,6 @@
# netlink - netlink library for go # # netlink - netlink library for go #
[![Build Status](https://travis-ci.org/vishvananda/netlink.png?branch=master)](https://travis-ci.org/vishvananda/netlink) [![GoDoc](https://godoc.org/github.com/vishvananda/netlink?status.svg)](https://godoc.org/github.com/vishvananda/netlink) ![Build Status](https://github.com/vishvananda/netlink/actions/workflows/main.yml/badge.svg) [![GoDoc](https://godoc.org/github.com/vishvananda/netlink?status.svg)](https://godoc.org/github.com/vishvananda/netlink)
The netlink package provides a simple netlink library for go. 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 is the interface a user-space program in linux uses to communicate with

@ -268,7 +268,7 @@ func parseAddr(m []byte) (addr Addr, family int, err error) {
// But obviously, as there are IPv6 PtP addresses, too, // But obviously, as there are IPv6 PtP addresses, too,
// IFA_LOCAL should also be handled for IPv6. // IFA_LOCAL should also be handled for IPv6.
if local != nil { if local != nil {
if family == FAMILY_V4 && local.IP.Equal(dst.IP) { if family == FAMILY_V4 && dst != nil && local.IP.Equal(dst.IP) {
addr.IPNet = dst addr.IPNet = dst
} else { } else {
addr.IPNet = local addr.IPNet = local
@ -357,7 +357,8 @@ func addrSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-c
msgs, from, err := s.Receive() msgs, from, err := s.Receive()
if err != nil { if err != nil {
if cberr != nil { if cberr != nil {
cberr(err) cberr(fmt.Errorf("Receive failed: %v",
err))
} }
return return
} }
@ -372,7 +373,6 @@ func addrSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-c
continue continue
} }
if m.Header.Type == unix.NLMSG_ERROR { if m.Header.Type == unix.NLMSG_ERROR {
native := nl.NativeEndian()
error := int32(native.Uint32(m.Data[0:4])) error := int32(native.Uint32(m.Data[0:4]))
if error == 0 { if error == 0 {
continue continue

@ -16,6 +16,30 @@ const (
BPF_PROG_TYPE_SCHED_ACT BPF_PROG_TYPE_SCHED_ACT
BPF_PROG_TYPE_TRACEPOINT BPF_PROG_TYPE_TRACEPOINT
BPF_PROG_TYPE_XDP BPF_PROG_TYPE_XDP
BPF_PROG_TYPE_PERF_EVENT
BPF_PROG_TYPE_CGROUP_SKB
BPF_PROG_TYPE_CGROUP_SOCK
BPF_PROG_TYPE_LWT_IN
BPF_PROG_TYPE_LWT_OUT
BPF_PROG_TYPE_LWT_XMIT
BPF_PROG_TYPE_SOCK_OPS
BPF_PROG_TYPE_SK_SKB
BPF_PROG_TYPE_CGROUP_DEVICE
BPF_PROG_TYPE_SK_MSG
BPF_PROG_TYPE_RAW_TRACEPOINT
BPF_PROG_TYPE_CGROUP_SOCK_ADDR
BPF_PROG_TYPE_LWT_SEG6LOCAL
BPF_PROG_TYPE_LIRC_MODE2
BPF_PROG_TYPE_SK_REUSEPORT
BPF_PROG_TYPE_FLOW_DISSECTOR
BPF_PROG_TYPE_CGROUP_SYSCTL
BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE
BPF_PROG_TYPE_CGROUP_SOCKOPT
BPF_PROG_TYPE_TRACING
BPF_PROG_TYPE_STRUCT_OPS
BPF_PROG_TYPE_EXT
BPF_PROG_TYPE_LSM
BPF_PROG_TYPE_SK_LOOKUP
) )
type BPFAttr struct { type BPFAttr struct {

@ -191,9 +191,9 @@ func classPayload(req *nl.NetlinkRequest, class Class) error {
opt.Fsc.Set(fm1/8, fd, fm2/8) opt.Fsc.Set(fm1/8, fd, fm2/8)
um1, ud, um2 := hfsc.Usc.Attrs() um1, ud, um2 := hfsc.Usc.Attrs()
opt.Usc.Set(um1/8, ud, um2/8) opt.Usc.Set(um1/8, ud, um2/8)
nl.NewRtAttrChild(options, nl.TCA_HFSC_RSC, nl.SerializeHfscCurve(&opt.Rsc)) options.AddRtAttr(nl.TCA_HFSC_RSC, nl.SerializeHfscCurve(&opt.Rsc))
nl.NewRtAttrChild(options, nl.TCA_HFSC_FSC, nl.SerializeHfscCurve(&opt.Fsc)) options.AddRtAttr(nl.TCA_HFSC_FSC, nl.SerializeHfscCurve(&opt.Fsc))
nl.NewRtAttrChild(options, nl.TCA_HFSC_USC, nl.SerializeHfscCurve(&opt.Usc)) options.AddRtAttr(nl.TCA_HFSC_USC, nl.SerializeHfscCurve(&opt.Usc))
} }
req.AddData(options) req.AddData(options)
return nil return nil
@ -341,7 +341,6 @@ func parseHfscClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, err
func parseTcStats(data []byte) (*ClassStatistics, error) { func parseTcStats(data []byte) (*ClassStatistics, error) {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
buf.Write(data) buf.Write(data)
native := nl.NativeEndian()
tcStats := &tcStats{} tcStats := &tcStats{}
if err := binary.Read(buf, native, tcStats); err != nil { if err := binary.Read(buf, native, tcStats); err != nil {
return nil, err return nil, err
@ -363,7 +362,6 @@ func parseTcStats(data []byte) (*ClassStatistics, error) {
func parseGnetStats(data []byte, gnetStats interface{}) error { func parseGnetStats(data []byte, gnetStats interface{}) error {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
buf.Write(data) buf.Write(data)
native := nl.NativeEndian()
return binary.Read(buf, native, gnetStats) return binary.Read(buf, native, gnetStats)
} }

@ -6,6 +6,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
"time"
"github.com/vishvananda/netlink/nl" "github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
@ -145,16 +146,23 @@ type ConntrackFlow struct {
Forward ipTuple Forward ipTuple
Reverse ipTuple Reverse ipTuple
Mark uint32 Mark uint32
TimeStart uint64
TimeStop uint64
TimeOut uint32
} }
func (s *ConntrackFlow) String() string { func (s *ConntrackFlow) String() string {
// conntrack cmd output: // conntrack cmd output:
// udp 17 src=127.0.0.1 dst=127.0.0.1 sport=4001 dport=1234 packets=5 bytes=532 [UNREPLIED] src=127.0.0.1 dst=127.0.0.1 sport=1234 dport=4001 packets=10 bytes=1078 mark=0 // udp 17 src=127.0.0.1 dst=127.0.0.1 sport=4001 dport=1234 packets=5 bytes=532 [UNREPLIED] src=127.0.0.1 dst=127.0.0.1 sport=1234 dport=4001 packets=10 bytes=1078 mark=0
return fmt.Sprintf("%s\t%d src=%s dst=%s sport=%d dport=%d packets=%d bytes=%d\tsrc=%s dst=%s sport=%d dport=%d packets=%d bytes=%d mark=%d", // start=2019-07-26 01:26:21.557800506 +0000 UTC stop=1970-01-01 00:00:00 +0000 UTC timeout=30(sec)
start := time.Unix(0, int64(s.TimeStart))
stop := time.Unix(0, int64(s.TimeStop))
timeout := int32(s.TimeOut)
return fmt.Sprintf("%s\t%d src=%s dst=%s sport=%d dport=%d packets=%d bytes=%d\tsrc=%s dst=%s sport=%d dport=%d packets=%d bytes=%d mark=0x%x start=%v stop=%v timeout=%d(sec)",
nl.L4ProtoMap[s.Forward.Protocol], s.Forward.Protocol, nl.L4ProtoMap[s.Forward.Protocol], s.Forward.Protocol,
s.Forward.SrcIP.String(), s.Forward.DstIP.String(), s.Forward.SrcPort, s.Forward.DstPort, s.Forward.Packets, s.Forward.Bytes, s.Forward.SrcIP.String(), s.Forward.DstIP.String(), s.Forward.SrcPort, s.Forward.DstPort, s.Forward.Packets, s.Forward.Bytes,
s.Reverse.SrcIP.String(), s.Reverse.DstIP.String(), s.Reverse.SrcPort, s.Reverse.DstPort, s.Reverse.Packets, s.Reverse.Bytes, s.Reverse.SrcIP.String(), s.Reverse.DstIP.String(), s.Reverse.SrcPort, s.Reverse.DstPort, s.Reverse.Packets, s.Reverse.Bytes,
s.Mark) s.Mark, start, stop, timeout)
} }
// This method parse the ip tuple structure // This method parse the ip tuple structure
@ -174,25 +182,43 @@ func parseIpTuple(reader *bytes.Reader, tpl *ipTuple) uint8 {
tpl.DstIP = v tpl.DstIP = v
} }
} }
// Skip the next 4 bytes nl.NLA_F_NESTED|nl.CTA_TUPLE_PROTO // Get total length of nested protocol-specific info.
reader.Seek(4, seekCurrent) _, _, protoInfoTotalLen := parseNfAttrTL(reader)
_, t, _, v := parseNfAttrTLV(reader) _, t, l, v := parseNfAttrTLV(reader)
// Track the number of bytes read.
protoInfoBytesRead := uint16(nl.SizeofNfattr) + l
if t == nl.CTA_PROTO_NUM { if t == nl.CTA_PROTO_NUM {
tpl.Protocol = uint8(v[0]) tpl.Protocol = uint8(v[0])
} }
// Skip some padding 3 bytes // We only parse TCP & UDP headers. Skip the others.
if tpl.Protocol != 6 && tpl.Protocol != 17 {
// skip the rest
bytesRemaining := protoInfoTotalLen - protoInfoBytesRead
reader.Seek(int64(bytesRemaining), seekCurrent)
return tpl.Protocol
}
// Skip 3 bytes of padding
reader.Seek(3, seekCurrent) reader.Seek(3, seekCurrent)
protoInfoBytesRead += 3
for i := 0; i < 2; i++ { for i := 0; i < 2; i++ {
_, t, _ := parseNfAttrTL(reader) _, t, _ := parseNfAttrTL(reader)
protoInfoBytesRead += uint16(nl.SizeofNfattr)
switch t { switch t {
case nl.CTA_PROTO_SRC_PORT: case nl.CTA_PROTO_SRC_PORT:
parseBERaw16(reader, &tpl.SrcPort) parseBERaw16(reader, &tpl.SrcPort)
protoInfoBytesRead += 2
case nl.CTA_PROTO_DST_PORT: case nl.CTA_PROTO_DST_PORT:
parseBERaw16(reader, &tpl.DstPort) parseBERaw16(reader, &tpl.DstPort)
protoInfoBytesRead += 2
} }
// Skip some padding 2 byte // Skip 2 bytes of padding
reader.Seek(2, seekCurrent) reader.Seek(2, seekCurrent)
protoInfoBytesRead += 2
} }
// Skip any remaining/unknown parts of the message
bytesRemaining := protoInfoTotalLen - protoInfoBytesRead
reader.Seek(int64(bytesRemaining), seekCurrent)
return tpl.Protocol return tpl.Protocol
} }
@ -211,10 +237,14 @@ func parseNfAttrTL(r *bytes.Reader) (isNested bool, attrType, len uint16) {
binary.Read(r, nl.NativeEndian(), &attrType) binary.Read(r, nl.NativeEndian(), &attrType)
isNested = (attrType & nl.NLA_F_NESTED) == nl.NLA_F_NESTED isNested = (attrType & nl.NLA_F_NESTED) == nl.NLA_F_NESTED
attrType = attrType & (nl.NLA_F_NESTED - 1) attrType = attrType & (nl.NLA_F_NESTED - 1)
return isNested, attrType, len return isNested, attrType, len
} }
func skipNfAttrValue(r *bytes.Reader, len uint16) {
len = (len + nl.NLA_ALIGNTO - 1) & ^(nl.NLA_ALIGNTO - 1)
r.Seek(int64(len), seekCurrent)
}
func parseBERaw16(r *bytes.Reader, v *uint16) { func parseBERaw16(r *bytes.Reader, v *uint16) {
binary.Read(r, binary.BigEndian, v) binary.Read(r, binary.BigEndian, v)
} }
@ -241,6 +271,36 @@ func parseByteAndPacketCounters(r *bytes.Reader) (bytes, packets uint64) {
return return
} }
// when the flow is alive, only the timestamp_start is returned in structure
func parseTimeStamp(r *bytes.Reader, readSize uint16) (tstart, tstop uint64) {
var numTimeStamps int
oneItem := nl.SizeofNfattr + 8 // 4 bytes attr header + 8 bytes timestamp
if readSize == uint16(oneItem) {
numTimeStamps = 1
} else if readSize == 2*uint16(oneItem) {
numTimeStamps = 2
} else {
return
}
for i := 0; i < numTimeStamps; i++ {
switch _, t, _ := parseNfAttrTL(r); t {
case nl.CTA_TIMESTAMP_START:
parseBERaw64(r, &tstart)
case nl.CTA_TIMESTAMP_STOP:
parseBERaw64(r, &tstop)
default:
return
}
}
return
}
func parseTimeOut(r *bytes.Reader) (ttimeout uint32) {
parseBERaw32(r, &ttimeout)
return
}
func parseConnectionMark(r *bytes.Reader) (mark uint32) { func parseConnectionMark(r *bytes.Reader) (mark uint32) {
parseBERaw32(r, &mark) parseBERaw32(r, &mark)
return return
@ -266,25 +326,37 @@ func parseRawData(data []byte) *ConntrackFlow {
if nested, t, l := parseNfAttrTL(reader); nested { if nested, t, l := parseNfAttrTL(reader); nested {
switch t { switch t {
case nl.CTA_TUPLE_ORIG: case nl.CTA_TUPLE_ORIG:
if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP { if nested, t, l = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP {
parseIpTuple(reader, &s.Forward) parseIpTuple(reader, &s.Forward)
} }
case nl.CTA_TUPLE_REPLY: case nl.CTA_TUPLE_REPLY:
if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP { if nested, t, l = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP {
parseIpTuple(reader, &s.Reverse) parseIpTuple(reader, &s.Reverse)
} else { } else {
// Header not recognized skip it // Header not recognized skip it
reader.Seek(int64(l), seekCurrent) skipNfAttrValue(reader, l)
} }
case nl.CTA_COUNTERS_ORIG: case nl.CTA_COUNTERS_ORIG:
s.Forward.Bytes, s.Forward.Packets = parseByteAndPacketCounters(reader) s.Forward.Bytes, s.Forward.Packets = parseByteAndPacketCounters(reader)
case nl.CTA_COUNTERS_REPLY: case nl.CTA_COUNTERS_REPLY:
s.Reverse.Bytes, s.Reverse.Packets = parseByteAndPacketCounters(reader) s.Reverse.Bytes, s.Reverse.Packets = parseByteAndPacketCounters(reader)
case nl.CTA_TIMESTAMP:
s.TimeStart, s.TimeStop = parseTimeStamp(reader, l)
case nl.CTA_PROTOINFO:
skipNfAttrValue(reader, l)
default:
skipNfAttrValue(reader, l)
} }
} else { } else {
switch t { switch t {
case nl.CTA_MARK: case nl.CTA_MARK:
s.Mark = parseConnectionMark(reader) s.Mark = parseConnectionMark(reader)
case nl.CTA_TIMEOUT:
s.TimeOut = parseTimeOut(reader)
case nl.CTA_STATUS, nl.CTA_USE, nl.CTA_ID:
skipNfAttrValue(reader, l)
default:
skipNfAttrValue(reader, l)
} }
} }
} }
@ -346,23 +418,34 @@ type CustomConntrackFilter interface {
} }
type ConntrackFilter struct { type ConntrackFilter struct {
ipFilter map[ConntrackFilterType]net.IP ipNetFilter map[ConntrackFilterType]*net.IPNet
portFilter map[ConntrackFilterType]uint16 portFilter map[ConntrackFilterType]uint16
protoFilter uint8 protoFilter uint8
} }
// AddIP adds an IP to the conntrack filter // AddIPNet adds a IP subnet to the conntrack filter
func (f *ConntrackFilter) AddIP(tp ConntrackFilterType, ip net.IP) error { func (f *ConntrackFilter) AddIPNet(tp ConntrackFilterType, ipNet *net.IPNet) error {
if f.ipFilter == nil { if ipNet == nil {
f.ipFilter = make(map[ConntrackFilterType]net.IP) return fmt.Errorf("Filter attribute empty")
} }
if _, ok := f.ipFilter[tp]; ok { if f.ipNetFilter == nil {
f.ipNetFilter = make(map[ConntrackFilterType]*net.IPNet)
}
if _, ok := f.ipNetFilter[tp]; ok {
return errors.New("Filter attribute already present") return errors.New("Filter attribute already present")
} }
f.ipFilter[tp] = ip f.ipNetFilter[tp] = ipNet
return nil return nil
} }
// AddIP adds an IP to the conntrack filter
func (f *ConntrackFilter) AddIP(tp ConntrackFilterType, ip net.IP) error {
if ip == nil {
return fmt.Errorf("Filter attribute empty")
}
return f.AddIPNet(tp, NewIPNet(ip))
}
// AddPort adds a Port to the conntrack filter if the Layer 4 protocol allows it // AddPort adds a Port to the conntrack filter if the Layer 4 protocol allows it
func (f *ConntrackFilter) AddPort(tp ConntrackFilterType, port uint16) error { func (f *ConntrackFilter) AddPort(tp ConntrackFilterType, port uint16) error {
switch f.protoFilter { switch f.protoFilter {
@ -394,7 +477,7 @@ func (f *ConntrackFilter) AddProtocol(proto uint8) error {
// MatchConntrackFlow applies the filter to the flow and returns true if the flow matches the filter // MatchConntrackFlow applies the filter to the flow and returns true if the flow matches the filter
// false otherwise // false otherwise
func (f *ConntrackFilter) MatchConntrackFlow(flow *ConntrackFlow) bool { func (f *ConntrackFilter) MatchConntrackFlow(flow *ConntrackFlow) bool {
if len(f.ipFilter) == 0 && len(f.portFilter) == 0 && f.protoFilter == 0 { if len(f.ipNetFilter) == 0 && len(f.portFilter) == 0 && f.protoFilter == 0 {
// empty filter always not match // empty filter always not match
return false return false
} }
@ -408,30 +491,30 @@ func (f *ConntrackFilter) MatchConntrackFlow(flow *ConntrackFlow) bool {
match := true match := true
// IP conntrack filter // IP conntrack filter
if len(f.ipFilter) > 0 { if len(f.ipNetFilter) > 0 {
// -orig-src ip Source address from original direction // -orig-src ip Source address from original direction
if elem, found := f.ipFilter[ConntrackOrigSrcIP]; found { if elem, found := f.ipNetFilter[ConntrackOrigSrcIP]; found {
match = match && elem.Equal(flow.Forward.SrcIP) match = match && elem.Contains(flow.Forward.SrcIP)
} }
// -orig-dst ip Destination address from original direction // -orig-dst ip Destination address from original direction
if elem, found := f.ipFilter[ConntrackOrigDstIP]; match && found { if elem, found := f.ipNetFilter[ConntrackOrigDstIP]; match && found {
match = match && elem.Equal(flow.Forward.DstIP) match = match && elem.Contains(flow.Forward.DstIP)
} }
// -src-nat ip Source NAT ip // -src-nat ip Source NAT ip
if elem, found := f.ipFilter[ConntrackReplySrcIP]; match && found { if elem, found := f.ipNetFilter[ConntrackReplySrcIP]; match && found {
match = match && elem.Equal(flow.Reverse.SrcIP) match = match && elem.Contains(flow.Reverse.SrcIP)
} }
// -dst-nat ip Destination NAT ip // -dst-nat ip Destination NAT ip
if elem, found := f.ipFilter[ConntrackReplyDstIP]; match && found { if elem, found := f.ipNetFilter[ConntrackReplyDstIP]; match && found {
match = match && elem.Equal(flow.Reverse.DstIP) match = match && elem.Contains(flow.Reverse.DstIP)
} }
// Match source or destination reply IP // Match source or destination reply IP
if elem, found := f.ipFilter[ConntrackReplyAnyIP]; match && found { if elem, found := f.ipNetFilter[ConntrackReplyAnyIP]; match && found {
match = match && (elem.Equal(flow.Reverse.SrcIP) || elem.Equal(flow.Reverse.DstIP)) match = match && (elem.Contains(flow.Reverse.SrcIP) || elem.Contains(flow.Reverse.DstIP))
} }
} }

@ -1,9 +1,11 @@
package netlink package netlink
import ( import (
"fmt"
"net"
"strings"
"syscall" "syscall"
"fmt"
"github.com/vishvananda/netlink/nl" "github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@ -27,6 +29,20 @@ type DevlinkDevice struct {
Attrs DevlinkDevAttrs Attrs DevlinkDevAttrs
} }
// DevlinkPortFn represents port function and its attributes
type DevlinkPortFn struct {
HwAddr net.HardwareAddr
State uint8
OpState uint8
}
// DevlinkPortFnSetAttrs represents attributes to set
type DevlinkPortFnSetAttrs struct {
FnAttrs DevlinkPortFn
HwAddrValid bool
StateValid bool
}
// DevlinkPort represents port and its attributes // DevlinkPort represents port and its attributes
type DevlinkPort struct { type DevlinkPort struct {
BusName string BusName string
@ -37,6 +53,35 @@ type DevlinkPort struct {
NetdevIfIndex uint32 NetdevIfIndex uint32
RdmaDeviceName string RdmaDeviceName string
PortFlavour uint16 PortFlavour uint16
Fn *DevlinkPortFn
}
type DevLinkPortAddAttrs struct {
Controller uint32
SfNumber uint32
PortIndex uint32
PfNumber uint16
SfNumberValid bool
PortIndexValid bool
ControllerValid bool
}
// DevlinkDeviceInfo represents devlink info
type DevlinkDeviceInfo struct {
Driver string
SerialNumber string
BoardID string
FwApp string
FwAppBoundleID string
FwAppName string
FwBoundleID string
FwMgmt string
FwMgmtAPI string
FwMgmtBuild string
FwNetlist string
FwNetlistBuild string
FwPsidAPI string
FwUndi string
} }
func parseDevLinkDeviceList(msgs [][]byte) ([]*DevlinkDevice, error) { func parseDevLinkDeviceList(msgs [][]byte) ([]*DevlinkDevice, error) {
@ -107,9 +152,9 @@ func (d *DevlinkDevice) parseAttributes(attrs []syscall.NetlinkRouteAttr) error
for _, a := range attrs { for _, a := range attrs {
switch a.Attr.Type { switch a.Attr.Type {
case nl.DEVLINK_ATTR_BUS_NAME: case nl.DEVLINK_ATTR_BUS_NAME:
d.BusName = string(a.Value) d.BusName = string(a.Value[:len(a.Value)-1])
case nl.DEVLINK_ATTR_DEV_NAME: case nl.DEVLINK_ATTR_DEV_NAME:
d.DeviceName = string(a.Value) d.DeviceName = string(a.Value[:len(a.Value)-1])
case nl.DEVLINK_ATTR_ESWITCH_MODE: case nl.DEVLINK_ATTR_ESWITCH_MODE:
d.Attrs.Eswitch.Mode = parseEswitchMode(native.Uint16(a.Value)) d.Attrs.Eswitch.Mode = parseEswitchMode(native.Uint16(a.Value))
case nl.DEVLINK_ATTR_ESWITCH_INLINE_MODE: case nl.DEVLINK_ATTR_ESWITCH_INLINE_MODE:
@ -138,12 +183,12 @@ func (h *Handle) getEswitchAttrs(family *GenlFamily, dev *DevlinkDevice) {
req := h.newNetlinkRequest(int(family.ID), unix.NLM_F_REQUEST|unix.NLM_F_ACK) req := h.newNetlinkRequest(int(family.ID), unix.NLM_F_REQUEST|unix.NLM_F_ACK)
req.AddData(msg) req.AddData(msg)
b := make([]byte, len(dev.BusName)) b := make([]byte, len(dev.BusName)+1)
copy(b, dev.BusName) copy(b, dev.BusName)
data := nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, b) data := nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, b)
req.AddData(data) req.AddData(data)
b = make([]byte, len(dev.DeviceName)) b = make([]byte, len(dev.DeviceName)+1)
copy(b, dev.DeviceName) copy(b, dev.DeviceName)
data = nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, b) data = nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, b)
req.AddData(data) req.AddData(data)
@ -287,21 +332,33 @@ func (port *DevlinkPort) parseAttributes(attrs []syscall.NetlinkRouteAttr) error
for _, a := range attrs { for _, a := range attrs {
switch a.Attr.Type { switch a.Attr.Type {
case nl.DEVLINK_ATTR_BUS_NAME: case nl.DEVLINK_ATTR_BUS_NAME:
port.BusName = string(a.Value) port.BusName = string(a.Value[:len(a.Value)-1])
case nl.DEVLINK_ATTR_DEV_NAME: case nl.DEVLINK_ATTR_DEV_NAME:
port.DeviceName = string(a.Value) port.DeviceName = string(a.Value[:len(a.Value)-1])
case nl.DEVLINK_ATTR_PORT_INDEX: case nl.DEVLINK_ATTR_PORT_INDEX:
port.PortIndex = native.Uint32(a.Value) port.PortIndex = native.Uint32(a.Value)
case nl.DEVLINK_ATTR_PORT_TYPE: case nl.DEVLINK_ATTR_PORT_TYPE:
port.PortType = native.Uint16(a.Value) port.PortType = native.Uint16(a.Value)
case nl.DEVLINK_ATTR_PORT_NETDEV_NAME: case nl.DEVLINK_ATTR_PORT_NETDEV_NAME:
port.NetdeviceName = string(a.Value) port.NetdeviceName = string(a.Value[:len(a.Value)-1])
case nl.DEVLINK_ATTR_PORT_NETDEV_IFINDEX: case nl.DEVLINK_ATTR_PORT_NETDEV_IFINDEX:
port.NetdevIfIndex = native.Uint32(a.Value) port.NetdevIfIndex = native.Uint32(a.Value)
case nl.DEVLINK_ATTR_PORT_IBDEV_NAME: case nl.DEVLINK_ATTR_PORT_IBDEV_NAME:
port.RdmaDeviceName = string(a.Value) port.RdmaDeviceName = string(a.Value[:len(a.Value)-1])
case nl.DEVLINK_ATTR_PORT_FLAVOUR: case nl.DEVLINK_ATTR_PORT_FLAVOUR:
port.PortFlavour = native.Uint16(a.Value) port.PortFlavour = native.Uint16(a.Value)
case nl.DEVLINK_ATTR_PORT_FUNCTION:
port.Fn = &DevlinkPortFn{}
for nested := range nl.ParseAttributes(a.Value) {
switch nested.Type {
case nl.DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR:
port.Fn.HwAddr = nested.Value[:]
case nl.DEVLINK_PORT_FN_ATTR_STATE:
port.Fn.State = uint8(nested.Value[0])
case nl.DEVLINK_PORT_FN_ATTR_OPSTATE:
port.Fn.OpState = uint8(nested.Value[0])
}
}
} }
} }
return nil return nil
@ -391,3 +448,281 @@ func (h *Handle) DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint
func DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint32) (*DevlinkPort, error) { func DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint32) (*DevlinkPort, error) {
return pkgHandle.DevLinkGetPortByIndex(Bus, Device, PortIndex) return pkgHandle.DevLinkGetPortByIndex(Bus, Device, PortIndex)
} }
// DevLinkPortAdd adds a devlink port and returns a port on success
// otherwise returns nil port and an error code.
func (h *Handle) DevLinkPortAdd(Bus string, Device string, Flavour uint16, Attrs DevLinkPortAddAttrs) (*DevlinkPort, error) {
_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_NEW, Bus, Device)
if err != nil {
return nil, err
}
req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FLAVOUR, nl.Uint16Attr(Flavour)))
req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_PF_NUMBER, nl.Uint16Attr(Attrs.PfNumber)))
if Flavour == nl.DEVLINK_PORT_FLAVOUR_PCI_SF && Attrs.SfNumberValid {
req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_SF_NUMBER, nl.Uint32Attr(Attrs.SfNumber)))
}
if Attrs.PortIndexValid {
req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(Attrs.PortIndex)))
}
if Attrs.ControllerValid {
req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_CONTROLLER_NUMBER, nl.Uint32Attr(Attrs.Controller)))
}
respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
}
port, err := parseDevlinkPortMsg(respmsg)
return port, err
}
// DevLinkPortAdd adds a devlink port and returns a port on success
// otherwise returns nil port and an error code.
func DevLinkPortAdd(Bus string, Device string, Flavour uint16, Attrs DevLinkPortAddAttrs) (*DevlinkPort, error) {
return pkgHandle.DevLinkPortAdd(Bus, Device, Flavour, Attrs)
}
// DevLinkPortDel deletes a devlink port and returns success or error code.
func (h *Handle) DevLinkPortDel(Bus string, Device string, PortIndex uint32) error {
_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_DEL, Bus, Device)
if err != nil {
return err
}
req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(PortIndex)))
_, err = req.Execute(unix.NETLINK_GENERIC, 0)
return err
}
// DevLinkPortDel deletes a devlink port and returns success or error code.
func DevLinkPortDel(Bus string, Device string, PortIndex uint32) error {
return pkgHandle.DevLinkPortDel(Bus, Device, PortIndex)
}
// DevlinkPortFnSet sets one or more port function attributes specified by the attribute mask.
// It returns 0 on success or error code.
func (h *Handle) DevlinkPortFnSet(Bus string, Device string, PortIndex uint32, FnAttrs DevlinkPortFnSetAttrs) error {
_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_SET, Bus, Device)
if err != nil {
return err
}
req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(PortIndex)))
fnAttr := nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FUNCTION|unix.NLA_F_NESTED, nil)
if FnAttrs.HwAddrValid {
fnAttr.AddRtAttr(nl.DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, []byte(FnAttrs.FnAttrs.HwAddr))
}
if FnAttrs.StateValid {
fnAttr.AddRtAttr(nl.DEVLINK_PORT_FN_ATTR_STATE, nl.Uint8Attr(FnAttrs.FnAttrs.State))
}
req.AddData(fnAttr)
_, err = req.Execute(unix.NETLINK_GENERIC, 0)
return err
}
// DevlinkPortFnSet sets one or more port function attributes specified by the attribute mask.
// It returns 0 on success or error code.
func DevlinkPortFnSet(Bus string, Device string, PortIndex uint32, FnAttrs DevlinkPortFnSetAttrs) error {
return pkgHandle.DevlinkPortFnSet(Bus, Device, PortIndex, FnAttrs)
}
// devlinkInfoGetter is function that is responsible for getting devlink info message
// this is introduced for test purpose
type devlinkInfoGetter func(bus, device string) ([]byte, error)
// DevlinkGetDeviceInfoByName returns devlink info for selected device,
// otherwise returns an error code.
// Equivalent to: `devlink dev info $dev`
func (h *Handle) DevlinkGetDeviceInfoByName(Bus string, Device string, getInfoMsg devlinkInfoGetter) (*DevlinkDeviceInfo, error) {
info, err := h.DevlinkGetDeviceInfoByNameAsMap(Bus, Device, getInfoMsg)
if err != nil {
return nil, err
}
return parseInfoData(info), nil
}
// DevlinkGetDeviceInfoByName returns devlink info for selected device,
// otherwise returns an error code.
// Equivalent to: `devlink dev info $dev`
func DevlinkGetDeviceInfoByName(Bus string, Device string) (*DevlinkDeviceInfo, error) {
return pkgHandle.DevlinkGetDeviceInfoByName(Bus, Device, pkgHandle.getDevlinkInfoMsg)
}
// DevlinkGetDeviceInfoByNameAsMap returns devlink info for selected device as a map,
// otherwise returns an error code.
// Equivalent to: `devlink dev info $dev`
func (h *Handle) DevlinkGetDeviceInfoByNameAsMap(Bus string, Device string, getInfoMsg devlinkInfoGetter) (map[string]string, error) {
response, err := getInfoMsg(Bus, Device)
if err != nil {
return nil, err
}
info, err := parseInfoMsg(response)
if err != nil {
return nil, err
}
return info, nil
}
// DevlinkGetDeviceInfoByNameAsMap returns devlink info for selected device as a map,
// otherwise returns an error code.
// Equivalent to: `devlink dev info $dev`
func DevlinkGetDeviceInfoByNameAsMap(Bus string, Device string) (map[string]string, error) {
return pkgHandle.DevlinkGetDeviceInfoByNameAsMap(Bus, Device, pkgHandle.getDevlinkInfoMsg)
}
// GetDevlinkInfo returns devlink info for target device,
// otherwise returns an error code.
func (d *DevlinkDevice) GetDevlinkInfo() (*DevlinkDeviceInfo, error) {
return pkgHandle.DevlinkGetDeviceInfoByName(d.BusName, d.DeviceName, pkgHandle.getDevlinkInfoMsg)
}
// GetDevlinkInfoAsMap returns devlink info for target device as a map,
// otherwise returns an error code.
func (d *DevlinkDevice) GetDevlinkInfoAsMap() (map[string]string, error) {
return pkgHandle.DevlinkGetDeviceInfoByNameAsMap(d.BusName, d.DeviceName, pkgHandle.getDevlinkInfoMsg)
}
func (h *Handle) getDevlinkInfoMsg(bus, device string) ([]byte, error) {
_, req, err := h.createCmdReq(nl.DEVLINK_CMD_INFO_GET, bus, device)
if err != nil {
return nil, err
}
response, err := req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
}
if len(response) < 1 {
return nil, fmt.Errorf("getDevlinkInfoMsg: message too short")
}
return response[0], nil
}
func parseInfoMsg(msg []byte) (map[string]string, error) {
if len(msg) < nl.SizeofGenlmsg {
return nil, fmt.Errorf("parseInfoMsg: message too short")
}
info := make(map[string]string)
err := collectInfoData(msg[nl.SizeofGenlmsg:], info)
if err != nil {
return nil, err
}
return info, nil
}
func collectInfoData(msg []byte, data map[string]string) error {
attrs, err := nl.ParseRouteAttr(msg)
if err != nil {
return err
}
for _, attr := range attrs {
switch attr.Attr.Type {
case nl.DEVLINK_ATTR_INFO_DRIVER_NAME:
data["driver"] = parseInfoValue(attr.Value)
case nl.DEVLINK_ATTR_INFO_SERIAL_NUMBER:
data["serialNumber"] = parseInfoValue(attr.Value)
case nl.DEVLINK_ATTR_INFO_VERSION_RUNNING, nl.DEVLINK_ATTR_INFO_VERSION_FIXED,
nl.DEVLINK_ATTR_INFO_VERSION_STORED:
key, value, err := getNestedInfoData(attr.Value)
if err != nil {
return err
}
data[key] = value
}
}
if len(data) == 0 {
return fmt.Errorf("collectInfoData: could not read attributes")
}
return nil
}
func getNestedInfoData(msg []byte) (string, string, error) {
nestedAttrs, err := nl.ParseRouteAttr(msg)
var key, value string
if err != nil {
return "", "", err
}
if len(nestedAttrs) != 2 {
return "", "", fmt.Errorf("getNestedInfoData: too few attributes in nested structure")
}
for _, nestedAttr := range nestedAttrs {
switch nestedAttr.Attr.Type {
case nl.DEVLINK_ATTR_INFO_VERSION_NAME:
key = parseInfoValue(nestedAttr.Value)
case nl.DEVLINK_ATTR_INFO_VERSION_VALUE:
value = parseInfoValue(nestedAttr.Value)
}
}
if key == "" {
return "", "", fmt.Errorf("getNestedInfoData: key not found")
}
if value == "" {
return "", "", fmt.Errorf("getNestedInfoData: value not found")
}
return key, value, nil
}
func parseInfoData(data map[string]string) *DevlinkDeviceInfo {
info := new(DevlinkDeviceInfo)
for key, value := range data {
switch key {
case "driver":
info.Driver = value
case "serialNumber":
info.SerialNumber = value
case "board.id":
info.BoardID = value
case "fw.app":
info.FwApp = value
case "fw.app.bundle_id":
info.FwAppBoundleID = value
case "fw.app.name":
info.FwAppName = value
case "fw.bundle_id":
info.FwBoundleID = value
case "fw.mgmt":
info.FwMgmt = value
case "fw.mgmt.api":
info.FwMgmtAPI = value
case "fw.mgmt.build":
info.FwMgmtBuild = value
case "fw.netlist":
info.FwNetlist = value
case "fw.netlist.build":
info.FwNetlistBuild = value
case "fw.psid.api":
info.FwPsidAPI = value
case "fw.undi":
info.FwUndi = value
}
}
return info
}
func parseInfoValue(value []byte) string {
v := strings.ReplaceAll(string(value), "\x00", "")
return strings.TrimSpace(v)
}

@ -260,6 +260,40 @@ func NewSkbEditAction() *SkbEditAction {
} }
} }
type PoliceAction struct {
ActionAttrs
Rate uint32 // in byte per second
Burst uint32 // in byte
RCellLog int
Mtu uint32
Mpu uint16 // in byte
PeakRate uint32 // in byte per second
PCellLog int
AvRate uint32 // in byte per second
Overhead uint16
LinkLayer int
ExceedAction TcPolAct
NotExceedAction TcPolAct
}
func (action *PoliceAction) Type() string {
return "police"
}
func (action *PoliceAction) Attrs() *ActionAttrs {
return &action.ActionAttrs
}
func NewPoliceAction() *PoliceAction {
return &PoliceAction{
RCellLog: -1,
PCellLog: -1,
LinkLayer: 1, // ETHERNET
ExceedAction: TC_POLICE_RECLASSIFY,
NotExceedAction: TC_POLICE_OK,
}
}
// MatchAll filters match all packets // MatchAll filters match all packets
type MatchAll struct { type MatchAll struct {
FilterAttrs FilterAttrs
@ -275,20 +309,20 @@ func (filter *MatchAll) Type() string {
return "matchall" return "matchall"
} }
type FilterFwAttrs struct { type FwFilter struct {
ClassId uint32 FilterAttrs
InDev string ClassId uint32
Mask uint32 InDev string
Index uint32 Mask uint32
Buffer uint32 Police *PoliceAction
Mtu uint32 }
Mpu uint16
Rate uint32 func (filter *FwFilter) Attrs() *FilterAttrs {
AvRate uint32 return &filter.FilterAttrs
PeakRate uint32 }
Action TcPolAct
Overhead uint16 func (filter *FwFilter) Type() string {
LinkLayer int return "fw"
} }
type BpfFilter struct { type BpfFilter struct {

@ -6,6 +6,7 @@ import (
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt" "fmt"
"net"
"syscall" "syscall"
"github.com/vishvananda/netlink/nl" "github.com/vishvananda/netlink/nl"
@ -50,74 +51,129 @@ func (filter *U32) Type() string {
return "u32" return "u32"
} }
// Fw filter filters on firewall marks type Flower struct {
// NOTE: this is in filter_linux because it refers to nl.TcPolice which
// is defined in nl/tc_linux.go
type Fw struct {
FilterAttrs FilterAttrs
ClassId uint32 DestIP net.IP
// TODO remove nl type from interface DestIPMask net.IPMask
Police nl.TcPolice SrcIP net.IP
InDev string SrcIPMask net.IPMask
// TODO Action EthType uint16
Mask uint32 EncDestIP net.IP
AvRate uint32 EncDestIPMask net.IPMask
Rtab [256]uint32 EncSrcIP net.IP
Ptab [256]uint32 EncSrcIPMask net.IPMask
EncDestPort uint16
EncKeyId uint32
Actions []Action
} }
func NewFw(attrs FilterAttrs, fattrs FilterFwAttrs) (*Fw, error) { func (filter *Flower) Attrs() *FilterAttrs {
var rtab [256]uint32
var ptab [256]uint32
rcellLog := -1
pcellLog := -1
avrate := fattrs.AvRate / 8
police := nl.TcPolice{}
police.Rate.Rate = fattrs.Rate / 8
police.PeakRate.Rate = fattrs.PeakRate / 8
buffer := fattrs.Buffer
linklayer := nl.LINKLAYER_ETHERNET
if fattrs.LinkLayer != nl.LINKLAYER_UNSPEC {
linklayer = fattrs.LinkLayer
}
police.Action = int32(fattrs.Action)
if police.Rate.Rate != 0 {
police.Rate.Mpu = fattrs.Mpu
police.Rate.Overhead = fattrs.Overhead
if CalcRtable(&police.Rate, rtab[:], rcellLog, fattrs.Mtu, linklayer) < 0 {
return nil, errors.New("TBF: failed to calculate rate table")
}
police.Burst = Xmittime(uint64(police.Rate.Rate), uint32(buffer))
}
police.Mtu = fattrs.Mtu
if police.PeakRate.Rate != 0 {
police.PeakRate.Mpu = fattrs.Mpu
police.PeakRate.Overhead = fattrs.Overhead
if CalcRtable(&police.PeakRate, ptab[:], pcellLog, fattrs.Mtu, linklayer) < 0 {
return nil, errors.New("POLICE: failed to calculate peak rate table")
}
}
return &Fw{
FilterAttrs: attrs,
ClassId: fattrs.ClassId,
InDev: fattrs.InDev,
Mask: fattrs.Mask,
Police: police,
AvRate: avrate,
Rtab: rtab,
Ptab: ptab,
}, nil
}
func (filter *Fw) Attrs() *FilterAttrs {
return &filter.FilterAttrs return &filter.FilterAttrs
} }
func (filter *Fw) Type() string { func (filter *Flower) Type() string {
return "fw" return "flower"
}
func (filter *Flower) encodeIP(parent *nl.RtAttr, ip net.IP, mask net.IPMask, v4Type, v6Type int, v4MaskType, v6MaskType int) {
ipType := v4Type
maskType := v4MaskType
encodeMask := mask
if mask == nil {
encodeMask = net.CIDRMask(32, 32)
}
v4IP := ip.To4()
if v4IP == nil {
ipType = v6Type
maskType = v6MaskType
if mask == nil {
encodeMask = net.CIDRMask(128, 128)
}
} else {
ip = v4IP
}
parent.AddRtAttr(ipType, ip)
parent.AddRtAttr(maskType, encodeMask)
}
func (filter *Flower) encode(parent *nl.RtAttr) error {
if filter.EthType != 0 {
parent.AddRtAttr(nl.TCA_FLOWER_KEY_ETH_TYPE, htons(filter.EthType))
}
if filter.SrcIP != nil {
filter.encodeIP(parent, filter.SrcIP, filter.SrcIPMask,
nl.TCA_FLOWER_KEY_IPV4_SRC, nl.TCA_FLOWER_KEY_IPV6_SRC,
nl.TCA_FLOWER_KEY_IPV4_SRC_MASK, nl.TCA_FLOWER_KEY_IPV6_SRC_MASK)
}
if filter.DestIP != nil {
filter.encodeIP(parent, filter.DestIP, filter.DestIPMask,
nl.TCA_FLOWER_KEY_IPV4_DST, nl.TCA_FLOWER_KEY_IPV6_DST,
nl.TCA_FLOWER_KEY_IPV4_DST_MASK, nl.TCA_FLOWER_KEY_IPV6_DST_MASK)
}
if filter.EncSrcIP != nil {
filter.encodeIP(parent, filter.EncSrcIP, filter.EncSrcIPMask,
nl.TCA_FLOWER_KEY_ENC_IPV4_SRC, nl.TCA_FLOWER_KEY_ENC_IPV6_SRC,
nl.TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, nl.TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK)
}
if filter.EncDestIP != nil {
filter.encodeIP(parent, filter.EncDestIP, filter.EncSrcIPMask,
nl.TCA_FLOWER_KEY_ENC_IPV4_DST, nl.TCA_FLOWER_KEY_ENC_IPV6_DST,
nl.TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, nl.TCA_FLOWER_KEY_ENC_IPV6_DST_MASK)
}
if filter.EncDestPort != 0 {
parent.AddRtAttr(nl.TCA_FLOWER_KEY_ENC_UDP_DST_PORT, htons(filter.EncDestPort))
}
if filter.EncKeyId != 0 {
parent.AddRtAttr(nl.TCA_FLOWER_KEY_ENC_KEY_ID, htonl(filter.EncKeyId))
}
actionsAttr := parent.AddRtAttr(nl.TCA_FLOWER_ACT, nil)
if err := EncodeActions(actionsAttr, filter.Actions); err != nil {
return err
}
return nil
}
func (filter *Flower) decode(data []syscall.NetlinkRouteAttr) error {
for _, datum := range data {
switch datum.Attr.Type {
case nl.TCA_FLOWER_KEY_ETH_TYPE:
filter.EthType = ntohs(datum.Value)
case nl.TCA_FLOWER_KEY_IPV4_SRC, nl.TCA_FLOWER_KEY_IPV6_SRC:
filter.SrcIP = datum.Value
case nl.TCA_FLOWER_KEY_IPV4_SRC_MASK, nl.TCA_FLOWER_KEY_IPV6_SRC_MASK:
filter.SrcIPMask = datum.Value
case nl.TCA_FLOWER_KEY_IPV4_DST, nl.TCA_FLOWER_KEY_IPV6_DST:
filter.DestIP = datum.Value
case nl.TCA_FLOWER_KEY_IPV4_DST_MASK, nl.TCA_FLOWER_KEY_IPV6_DST_MASK:
filter.DestIPMask = datum.Value
case nl.TCA_FLOWER_KEY_ENC_IPV4_SRC, nl.TCA_FLOWER_KEY_ENC_IPV6_SRC:
filter.EncSrcIP = datum.Value
case nl.TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, nl.TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK:
filter.EncSrcIPMask = datum.Value
case nl.TCA_FLOWER_KEY_ENC_IPV4_DST, nl.TCA_FLOWER_KEY_ENC_IPV6_DST:
filter.EncDestIP = datum.Value
case nl.TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, nl.TCA_FLOWER_KEY_ENC_IPV6_DST_MASK:
filter.EncDestIPMask = datum.Value
case nl.TCA_FLOWER_KEY_ENC_UDP_DST_PORT:
filter.EncDestPort = ntohs(datum.Value)
case nl.TCA_FLOWER_KEY_ENC_KEY_ID:
filter.EncKeyId = ntohl(datum.Value)
case nl.TCA_FLOWER_ACT:
tables, err := nl.ParseRouteAttr(datum.Value)
if err != nil {
return err
}
filter.Actions, err = parseActions(tables)
if err != nil {
return err
}
}
}
return nil
} }
// FilterDel will delete a filter from the system. // FilterDel will delete a filter from the system.
@ -169,7 +225,6 @@ func (h *Handle) FilterReplace(filter Filter) error {
} }
func (h *Handle) filterModify(filter Filter, flags int) error { func (h *Handle) filterModify(filter Filter, flags int) error {
native = nl.NativeEndian()
req := h.newNetlinkRequest(unix.RTM_NEWTFILTER, flags|unix.NLM_F_ACK) req := h.newNetlinkRequest(unix.RTM_NEWTFILTER, flags|unix.NLM_F_ACK)
base := filter.Attrs() base := filter.Attrs()
msg := &nl.TcMsg{ msg := &nl.TcMsg{
@ -237,7 +292,7 @@ func (h *Handle) filterModify(filter Filter, flags int) error {
if err := EncodeActions(actionsAttr, filter.Actions); err != nil { if err := EncodeActions(actionsAttr, filter.Actions); err != nil {
return err return err
} }
case *Fw: case *FwFilter:
if filter.Mask != 0 { if filter.Mask != 0 {
b := make([]byte, 4) b := make([]byte, 4)
native.PutUint32(b, filter.Mask) native.PutUint32(b, filter.Mask)
@ -246,17 +301,10 @@ func (h *Handle) filterModify(filter Filter, flags int) error {
if filter.InDev != "" { if filter.InDev != "" {
options.AddRtAttr(nl.TCA_FW_INDEV, nl.ZeroTerminated(filter.InDev)) options.AddRtAttr(nl.TCA_FW_INDEV, nl.ZeroTerminated(filter.InDev))
} }
if (filter.Police != nl.TcPolice{}) { if filter.Police != nil {
police := options.AddRtAttr(nl.TCA_FW_POLICE, nil) police := options.AddRtAttr(nl.TCA_FW_POLICE, nil)
police.AddRtAttr(nl.TCA_POLICE_TBF, filter.Police.Serialize()) if err := encodePolice(police, filter.Police); err != nil {
if (filter.Police.Rate != nl.TcRateSpec{}) { return err
payload := SerializeRtab(filter.Rtab)
police.AddRtAttr(nl.TCA_POLICE_RATE, payload)
}
if (filter.Police.PeakRate != nl.TcRateSpec{}) {
payload := SerializeRtab(filter.Ptab)
police.AddRtAttr(nl.TCA_POLICE_PEAKRATE, payload)
} }
} }
if filter.ClassId != 0 { if filter.ClassId != 0 {
@ -287,6 +335,10 @@ func (h *Handle) filterModify(filter Filter, flags int) error {
if filter.ClassId != 0 { if filter.ClassId != 0 {
options.AddRtAttr(nl.TCA_MATCHALL_CLASSID, nl.Uint32Attr(filter.ClassId)) options.AddRtAttr(nl.TCA_MATCHALL_CLASSID, nl.Uint32Attr(filter.ClassId))
} }
case *Flower:
if err := filter.encode(options); err != nil {
return err
}
} }
req.AddData(options) req.AddData(options)
@ -350,11 +402,13 @@ func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
case "u32": case "u32":
filter = &U32{} filter = &U32{}
case "fw": case "fw":
filter = &Fw{} filter = &FwFilter{}
case "bpf": case "bpf":
filter = &BpfFilter{} filter = &BpfFilter{}
case "matchall": case "matchall":
filter = &MatchAll{} filter = &MatchAll{}
case "flower":
filter = &Flower{}
default: default:
filter = &GenericFilter{FilterType: filterType} filter = &GenericFilter{FilterType: filterType}
} }
@ -384,6 +438,11 @@ func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
case "flower":
detailed, err = parseFlowerData(filter, data)
if err != nil {
return nil, err
}
default: default:
detailed = true detailed = true
} }
@ -415,6 +474,53 @@ func toAttrs(tcgen *nl.TcGen, attrs *ActionAttrs) {
attrs.Bindcnt = int(tcgen.Bindcnt) attrs.Bindcnt = int(tcgen.Bindcnt)
} }
func encodePolice(attr *nl.RtAttr, action *PoliceAction) error {
var rtab [256]uint32
var ptab [256]uint32
police := nl.TcPolice{}
police.Index = uint32(action.Attrs().Index)
police.Bindcnt = int32(action.Attrs().Bindcnt)
police.Capab = uint32(action.Attrs().Capab)
police.Refcnt = int32(action.Attrs().Refcnt)
police.Rate.Rate = action.Rate
police.PeakRate.Rate = action.PeakRate
police.Action = int32(action.ExceedAction)
if police.Rate.Rate != 0 {
police.Rate.Mpu = action.Mpu
police.Rate.Overhead = action.Overhead
if CalcRtable(&police.Rate, rtab[:], action.RCellLog, action.Mtu, action.LinkLayer) < 0 {
return errors.New("TBF: failed to calculate rate table")
}
police.Burst = Xmittime(uint64(police.Rate.Rate), action.Burst)
}
police.Mtu = action.Mtu
if police.PeakRate.Rate != 0 {
police.PeakRate.Mpu = action.Mpu
police.PeakRate.Overhead = action.Overhead
if CalcRtable(&police.PeakRate, ptab[:], action.PCellLog, action.Mtu, action.LinkLayer) < 0 {
return errors.New("POLICE: failed to calculate peak rate table")
}
}
attr.AddRtAttr(nl.TCA_POLICE_TBF, police.Serialize())
if police.Rate.Rate != 0 {
attr.AddRtAttr(nl.TCA_POLICE_RATE, SerializeRtab(rtab))
}
if police.PeakRate.Rate != 0 {
attr.AddRtAttr(nl.TCA_POLICE_PEAKRATE, SerializeRtab(ptab))
}
if action.AvRate != 0 {
attr.AddRtAttr(nl.TCA_POLICE_AVRATE, nl.Uint32Attr(action.AvRate))
}
if action.NotExceedAction != 0 {
attr.AddRtAttr(nl.TCA_POLICE_RESULT, nl.Uint32Attr(uint32(action.NotExceedAction)))
}
return nil
}
func EncodeActions(attr *nl.RtAttr, actions []Action) error { func EncodeActions(attr *nl.RtAttr, actions []Action) error {
tabIndex := int(nl.TCA_ACT_TAB) tabIndex := int(nl.TCA_ACT_TAB)
@ -422,6 +528,14 @@ func EncodeActions(attr *nl.RtAttr, actions []Action) error {
switch action := action.(type) { switch action := action.(type) {
default: default:
return fmt.Errorf("unknown action type %s", action.Type()) return fmt.Errorf("unknown action type %s", action.Type())
case *PoliceAction:
table := attr.AddRtAttr(tabIndex, nil)
tabIndex++
table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("police"))
aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
if err := encodePolice(aopts, action); err != nil {
return err
}
case *MirredAction: case *MirredAction:
table := attr.AddRtAttr(tabIndex, nil) table := attr.AddRtAttr(tabIndex, nil)
tabIndex++ tabIndex++
@ -516,6 +630,29 @@ func EncodeActions(attr *nl.RtAttr, actions []Action) error {
return nil return nil
} }
func parsePolice(data syscall.NetlinkRouteAttr, police *PoliceAction) {
switch data.Attr.Type {
case nl.TCA_POLICE_RESULT:
police.NotExceedAction = TcPolAct(native.Uint32(data.Value[0:4]))
case nl.TCA_POLICE_AVRATE:
police.AvRate = native.Uint32(data.Value[0:4])
case nl.TCA_POLICE_TBF:
p := *nl.DeserializeTcPolice(data.Value)
police.ActionAttrs = ActionAttrs{}
police.Attrs().Index = int(p.Index)
police.Attrs().Bindcnt = int(p.Bindcnt)
police.Attrs().Capab = int(p.Capab)
police.Attrs().Refcnt = int(p.Refcnt)
police.ExceedAction = TcPolAct(p.Action)
police.Rate = p.Rate.Rate
police.PeakRate = p.PeakRate.Rate
police.Burst = Xmitsize(uint64(p.Rate.Rate), p.Burst)
police.Mtu = p.Mtu
police.LinkLayer = int(p.Rate.Linklayer) & nl.TC_LINKLAYER_MASK
police.Overhead = p.Rate.Overhead
}
}
func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) { func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
var actions []Action var actions []Action
for _, table := range tables { for _, table := range tables {
@ -544,6 +681,8 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
action = &TunnelKeyAction{} action = &TunnelKeyAction{}
case "skbedit": case "skbedit":
action = &SkbEditAction{} action = &SkbEditAction{}
case "police":
action = &PoliceAction{}
default: default:
break nextattr break nextattr
} }
@ -622,6 +761,8 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
gen := *nl.DeserializeTcGen(adatum.Value) gen := *nl.DeserializeTcGen(adatum.Value)
toAttrs(&gen, action.Attrs()) toAttrs(&gen, action.Attrs())
} }
case "police":
parsePolice(adatum, action.(*PoliceAction))
} }
} }
} }
@ -632,7 +773,6 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
} }
func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) { func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
native = nl.NativeEndian()
u32 := filter.(*U32) u32 := filter.(*U32)
detailed := false detailed := false
for _, datum := range data { for _, datum := range data {
@ -678,8 +818,7 @@ func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error)
} }
func parseFwData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) { func parseFwData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
native = nl.NativeEndian() fw := filter.(*FwFilter)
fw := filter.(*Fw)
detailed := true detailed := true
for _, datum := range data { for _, datum := range data {
switch datum.Attr.Type { switch datum.Attr.Type {
@ -690,24 +829,18 @@ func parseFwData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
case nl.TCA_FW_INDEV: case nl.TCA_FW_INDEV:
fw.InDev = string(datum.Value[:len(datum.Value)-1]) fw.InDev = string(datum.Value[:len(datum.Value)-1])
case nl.TCA_FW_POLICE: case nl.TCA_FW_POLICE:
var police PoliceAction
adata, _ := nl.ParseRouteAttr(datum.Value) adata, _ := nl.ParseRouteAttr(datum.Value)
for _, aattr := range adata { for _, aattr := range adata {
switch aattr.Attr.Type { parsePolice(aattr, &police)
case nl.TCA_POLICE_TBF:
fw.Police = *nl.DeserializeTcPolice(aattr.Value)
case nl.TCA_POLICE_RATE:
fw.Rtab = DeserializeRtab(aattr.Value)
case nl.TCA_POLICE_PEAKRATE:
fw.Ptab = DeserializeRtab(aattr.Value)
}
} }
fw.Police = &police
} }
} }
return detailed, nil return detailed, nil
} }
func parseBpfData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) { func parseBpfData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
native = nl.NativeEndian()
bpf := filter.(*BpfFilter) bpf := filter.(*BpfFilter)
detailed := true detailed := true
for _, datum := range data { for _, datum := range data {
@ -733,7 +866,6 @@ func parseBpfData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error)
} }
func parseMatchAllData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) { func parseMatchAllData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
native = nl.NativeEndian()
matchall := filter.(*MatchAll) matchall := filter.(*MatchAll)
detailed := true detailed := true
for _, datum := range data { for _, datum := range data {
@ -754,6 +886,10 @@ func parseMatchAllData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, er
return detailed, nil return detailed, nil
} }
func parseFlowerData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
return true, filter.(*Flower).decode(data)
}
func AlignToAtm(size uint) uint { func AlignToAtm(size uint) uint {
var linksize, cells int var linksize, cells int
cells = int(size / nl.ATM_CELL_PAYLOAD) cells = int(size / nl.ATM_CELL_PAYLOAD)
@ -801,14 +937,12 @@ func CalcRtable(rate *nl.TcRateSpec, rtab []uint32, cellLog int, mtu uint32, lin
func DeserializeRtab(b []byte) [256]uint32 { func DeserializeRtab(b []byte) [256]uint32 {
var rtab [256]uint32 var rtab [256]uint32
native := nl.NativeEndian()
r := bytes.NewReader(b) r := bytes.NewReader(b)
_ = binary.Read(r, native, &rtab) _ = binary.Read(r, native, &rtab)
return rtab return rtab
} }
func SerializeRtab(rtab [256]uint32) []byte { func SerializeRtab(rtab [256]uint32) []byte {
native := nl.NativeEndian()
var w bytes.Buffer var w bytes.Buffer
_ = binary.Write(&w, native, rtab) _ = binary.Write(&w, native, rtab)
return w.Bytes() return w.Bytes()

@ -15,7 +15,7 @@ var pkgHandle = &Handle{}
// Handle is an handle for the netlink requests on a // Handle is an handle for the netlink requests on a
// specific network namespace. All the requests on the // specific network namespace. All the requests on the
// same netlink family share the same netlink socket, // same netlink family share the same netlink socket,
// which gets released when the handle is deleted. // which gets released when the handle is Close'd.
type Handle struct { type Handle struct {
sockets map[int]*nl.SocketHandle sockets map[int]*nl.SocketHandle
lookupByDump bool lookupByDump bool
@ -136,14 +136,22 @@ func newHandle(newNs, curNs netns.NsHandle, nlFamilies ...int) (*Handle, error)
return h, nil return h, nil
} }
// Delete releases the resources allocated to this handle // Close releases the resources allocated to this handle
func (h *Handle) Delete() { func (h *Handle) Close() {
for _, sh := range h.sockets { for _, sh := range h.sockets {
sh.Close() sh.Close()
} }
h.sockets = nil h.sockets = nil
} }
// Delete releases the resources allocated to this handle
//
// Deprecated: use Close instead which is in line with typical resource release
// patterns for files and other resources.
func (h *Handle) Delete() {
h.Close()
}
func (h *Handle) newNetlinkRequest(proto, flags int) *nl.NetlinkRequest { func (h *Handle) newNetlinkRequest(proto, flags int) *nl.NetlinkRequest {
// Do this so that package API still use nl package variable nextSeqNr // Do this so that package API still use nl package variable nextSeqNr
if h.sockets == nil { if h.sockets == nil {

@ -23,6 +23,8 @@ func NewHandleAtFrom(newNs, curNs netns.NsHandle) (*Handle, error) {
return nil, ErrNotImplemented return nil, ErrNotImplemented
} }
func (h *Handle) Close() {}
func (h *Handle) Delete() {} func (h *Handle) Delete() {}
func (h *Handle) SupportsNetlinkFamily(nlFamily int) bool { func (h *Handle) SupportsNetlinkFamily(nlFamily int) bool {

@ -1,6 +1,7 @@
package netlink package netlink
import ( import (
"encoding/binary"
"log" "log"
"net" "net"
"syscall" "syscall"
@ -11,12 +12,19 @@ import (
// IPSetEntry is used for adding, updating, retreiving and deleting entries // IPSetEntry is used for adding, updating, retreiving and deleting entries
type IPSetEntry struct { type IPSetEntry struct {
Comment string Comment string
MAC net.HardwareAddr MAC net.HardwareAddr
IP net.IP IP net.IP
Timeout *uint32 CIDR uint8
Packets *uint64 Timeout *uint32
Bytes *uint64 Packets *uint64
Bytes *uint64
Protocol *uint8
Port *uint16
IP2 net.IP
CIDR2 uint8
IFace string
Mark *uint32
Replace bool // replace existing entry Replace bool // replace existing entry
} }
@ -32,6 +40,12 @@ type IPSetResult struct {
SetName string SetName string
TypeName string TypeName string
Comment string Comment string
MarkMask uint32
IPFrom net.IP
IPTo net.IP
PortFrom uint16
PortTo uint16
HashSize uint32 HashSize uint32
NumEntries uint32 NumEntries uint32
@ -52,6 +66,12 @@ type IpsetCreateOptions struct {
Counters bool Counters bool
Comments bool Comments bool
Skbinfo bool Skbinfo bool
Revision uint8
IPFrom net.IP
IPTo net.IP
PortFrom uint16
PortTo uint16
} }
// IpsetProtocol returns the ipset protocol version from the kernel // IpsetProtocol returns the ipset protocol version from the kernel
@ -86,12 +106,12 @@ func IpsetListAll() ([]IPSetResult, error) {
// IpsetAdd adds an entry to an existing ipset. // IpsetAdd adds an entry to an existing ipset.
func IpsetAdd(setname string, entry *IPSetEntry) error { func IpsetAdd(setname string, entry *IPSetEntry) error {
return pkgHandle.ipsetAddDel(nl.IPSET_CMD_ADD, setname, entry) return pkgHandle.IpsetAdd(setname, entry)
} }
// IpsetDel deletes an entry from an existing ipset. // IpsetDel deletes an entry from an existing ipset.
func IpsetDel(setname string, entry *IPSetEntry) error { func IpsetDel(setname string, entry *IPSetEntry) error {
return pkgHandle.ipsetAddDel(nl.IPSET_CMD_DEL, setname, entry) return pkgHandle.IpsetDel(setname, entry)
} }
func (h *Handle) IpsetProtocol() (protocol uint8, minVersion uint8, err error) { func (h *Handle) IpsetProtocol() (protocol uint8, minVersion uint8, err error) {
@ -114,11 +134,30 @@ func (h *Handle) IpsetCreate(setname, typename string, options IpsetCreateOption
req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname))) req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname)))
req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_TYPENAME, nl.ZeroTerminated(typename))) req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_TYPENAME, nl.ZeroTerminated(typename)))
req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_REVISION, nl.Uint8Attr(0)))
req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_FAMILY, nl.Uint8Attr(2))) // 2 == inet revision := options.Revision
if revision == 0 {
revision = getIpsetDefaultWithTypeName(typename)
}
req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_REVISION, nl.Uint8Attr(revision)))
data := nl.NewRtAttr(nl.IPSET_ATTR_DATA|int(nl.NLA_F_NESTED), nil) data := nl.NewRtAttr(nl.IPSET_ATTR_DATA|int(nl.NLA_F_NESTED), nil)
var family uint8
switch typename {
case "hash:mac":
case "bitmap:port":
buf := make([]byte, 4)
binary.BigEndian.PutUint16(buf, options.PortFrom)
binary.BigEndian.PutUint16(buf[2:], options.PortTo)
data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_PORT_FROM|int(nl.NLA_F_NET_BYTEORDER), buf[:2]))
data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_PORT_TO|int(nl.NLA_F_NET_BYTEORDER), buf[2:]))
default:
family = unix.AF_INET
}
req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_FAMILY, nl.Uint8Attr(family)))
if timeout := options.Timeout; timeout != nil { if timeout := options.Timeout; timeout != nil {
data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER, Value: *timeout}) data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER, Value: *timeout})
} }
@ -187,6 +226,16 @@ func (h *Handle) IpsetListAll() ([]IPSetResult, error) {
return result, nil return result, nil
} }
// IpsetAdd adds an entry to an existing ipset.
func (h *Handle) IpsetAdd(setname string, entry *IPSetEntry) error {
return h.ipsetAddDel(nl.IPSET_CMD_ADD, setname, entry)
}
// IpsetDel deletes an entry from an existing ipset.
func (h *Handle) IpsetDel(setname string, entry *IPSetEntry) error {
return h.ipsetAddDel(nl.IPSET_CMD_DEL, setname, entry)
}
func (h *Handle) ipsetAddDel(nlCmd int, setname string, entry *IPSetEntry) error { func (h *Handle) ipsetAddDel(nlCmd int, setname string, entry *IPSetEntry) error {
req := h.newIpsetRequest(nlCmd) req := h.newIpsetRequest(nlCmd)
req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname))) req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname)))
@ -204,15 +253,49 @@ func (h *Handle) ipsetAddDel(nlCmd int, setname string, entry *IPSetEntry) error
if entry.Timeout != nil { if entry.Timeout != nil {
data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER, Value: *entry.Timeout}) data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER, Value: *entry.Timeout})
} }
if entry.MAC != nil {
nestedData := nl.NewRtAttr(nl.IPSET_ATTR_ETHER|int(nl.NLA_F_NET_BYTEORDER), entry.MAC)
data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_ETHER|int(nl.NLA_F_NESTED), nestedData.Serialize()))
}
if entry.IP != nil { if entry.IP != nil {
nestedData := nl.NewRtAttr(nl.IPSET_ATTR_IP|int(nl.NLA_F_NET_BYTEORDER), entry.IP) nestedData := nl.NewRtAttr(nl.IPSET_ATTR_IP|int(nl.NLA_F_NET_BYTEORDER), entry.IP)
data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_IP|int(nl.NLA_F_NESTED), nestedData.Serialize())) data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_IP|int(nl.NLA_F_NESTED), nestedData.Serialize()))
} }
if entry.MAC != nil {
data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_ETHER, entry.MAC))
}
if entry.CIDR != 0 {
data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_CIDR, nl.Uint8Attr(entry.CIDR)))
}
if entry.IP2 != nil {
nestedData := nl.NewRtAttr(nl.IPSET_ATTR_IP|int(nl.NLA_F_NET_BYTEORDER), entry.IP2)
data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_IP2|int(nl.NLA_F_NESTED), nestedData.Serialize()))
}
if entry.CIDR2 != 0 {
data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_CIDR2, nl.Uint8Attr(entry.CIDR2)))
}
if entry.Port != nil {
if entry.Protocol == nil {
// use tcp protocol as default
val := uint8(unix.IPPROTO_TCP)
entry.Protocol = &val
}
data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_PROTO, nl.Uint8Attr(*entry.Protocol)))
buf := make([]byte, 2)
binary.BigEndian.PutUint16(buf, *entry.Port)
data.AddChild(nl.NewRtAttr(int(nl.IPSET_ATTR_PORT|nl.NLA_F_NET_BYTEORDER), buf))
}
if entry.IFace != "" {
data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_IFACE, nl.ZeroTerminated(entry.IFace)))
}
if entry.Mark != nil {
data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_MARK | nl.NLA_F_NET_BYTEORDER, Value: *entry.Mark})
}
data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_LINENO | nl.NLA_F_NET_BYTEORDER, Value: 0}) data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_LINENO | nl.NLA_F_NET_BYTEORDER, Value: 0})
req.AddData(data) req.AddData(data)
@ -235,6 +318,17 @@ func (h *Handle) newIpsetRequest(cmd int) *nl.NetlinkRequest {
return req return req
} }
func getIpsetDefaultWithTypeName(typename string) uint8 {
switch typename {
case "hash:ip,port",
"hash:ip,port,ip",
"hash:ip,port,net",
"hash:net,port":
return 1
}
return 0
}
func ipsetExecute(req *nl.NetlinkRequest) (msgs [][]byte, err error) { func ipsetExecute(req *nl.NetlinkRequest) (msgs [][]byte, err error) {
msgs, err = req.Execute(unix.NETLINK_NETFILTER, 0) msgs, err = req.Execute(unix.NETLINK_NETFILTER, 0)
@ -278,6 +372,8 @@ func (result *IPSetResult) unserialize(msg []byte) {
result.parseAttrADT(attr.Value) result.parseAttrADT(attr.Value)
case nl.IPSET_ATTR_PROTOCOL_MIN: case nl.IPSET_ATTR_PROTOCOL_MIN:
result.ProtocolMinVersion = attr.Value[0] result.ProtocolMinVersion = attr.Value[0]
case nl.IPSET_ATTR_MARKMASK:
result.MarkMask = attr.Uint32()
default: default:
log.Printf("unknown ipset attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK) log.Printf("unknown ipset attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK)
} }
@ -307,12 +403,31 @@ func (result *IPSetResult) parseAttrData(data []byte) {
switch nested.Type { switch nested.Type {
case nl.IPSET_ATTR_IP | nl.NLA_F_NET_BYTEORDER: case nl.IPSET_ATTR_IP | nl.NLA_F_NET_BYTEORDER:
result.Entries = append(result.Entries, IPSetEntry{IP: nested.Value}) result.Entries = append(result.Entries, IPSetEntry{IP: nested.Value})
case nl.IPSET_ATTR_IP:
result.IPFrom = nested.Value
default:
log.Printf("unknown nested ipset data attribute from kernel: %+v %v", nested, nested.Type&nl.NLA_TYPE_MASK)
} }
} }
case nl.IPSET_ATTR_IP_TO | nl.NLA_F_NESTED:
for nested := range nl.ParseAttributes(attr.Value) {
switch nested.Type {
case nl.IPSET_ATTR_IP:
result.IPTo = nested.Value
default:
log.Printf("unknown nested ipset data attribute from kernel: %+v %v", nested, nested.Type&nl.NLA_TYPE_MASK)
}
}
case nl.IPSET_ATTR_PORT_FROM | nl.NLA_F_NET_BYTEORDER:
result.PortFrom = networkOrder.Uint16(attr.Value)
case nl.IPSET_ATTR_PORT_TO | nl.NLA_F_NET_BYTEORDER:
result.PortTo = networkOrder.Uint16(attr.Value)
case nl.IPSET_ATTR_CADT_LINENO | nl.NLA_F_NET_BYTEORDER: case nl.IPSET_ATTR_CADT_LINENO | nl.NLA_F_NET_BYTEORDER:
result.LineNo = attr.Uint32() result.LineNo = attr.Uint32()
case nl.IPSET_ATTR_COMMENT: case nl.IPSET_ATTR_COMMENT:
result.Comment = nl.BytesToString(attr.Value) result.Comment = nl.BytesToString(attr.Value)
case nl.IPSET_ATTR_MARKMASK:
result.MarkMask = attr.Uint32()
default: default:
log.Printf("unknown ipset data attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK) log.Printf("unknown ipset data attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK)
} }
@ -357,6 +472,30 @@ func parseIPSetEntry(data []byte) (entry IPSetEntry) {
log.Printf("unknown nested ADT attribute from kernel: %+v", attr) log.Printf("unknown nested ADT attribute from kernel: %+v", attr)
} }
} }
case nl.IPSET_ATTR_IP2 | nl.NLA_F_NESTED:
for attr := range nl.ParseAttributes(attr.Value) {
switch attr.Type {
case nl.IPSET_ATTR_IP:
entry.IP2 = net.IP(attr.Value)
default:
log.Printf("unknown nested ADT attribute from kernel: %+v", attr)
}
}
case nl.IPSET_ATTR_CIDR:
entry.CIDR = attr.Value[0]
case nl.IPSET_ATTR_CIDR2:
entry.CIDR2 = attr.Value[0]
case nl.IPSET_ATTR_PORT | nl.NLA_F_NET_BYTEORDER:
val := networkOrder.Uint16(attr.Value)
entry.Port = &val
case nl.IPSET_ATTR_PROTO:
val := attr.Value[0]
entry.Protocol = &val
case nl.IPSET_ATTR_IFACE:
entry.IFace = nl.BytesToString(attr.Value)
case nl.IPSET_ATTR_MARK | nl.NLA_F_NET_BYTEORDER:
val := attr.Uint32()
entry.Mark = &val
default: default:
log.Printf("unknown ADT attribute from kernel: %+v", attr) log.Printf("unknown ADT attribute from kernel: %+v", attr)
} }

@ -35,10 +35,13 @@ type LinkAttrs struct {
Alias string Alias string
Statistics *LinkStatistics Statistics *LinkStatistics
Promisc int Promisc int
Allmulti int
Multi int
Xdp *LinkXdp Xdp *LinkXdp
EncapType string EncapType string
Protinfo *Protinfo Protinfo *Protinfo
OperState LinkOperState OperState LinkOperState
PhysSwitchID int
NetNsID int NetNsID int
NumTxQueues int NumTxQueues int
NumRxQueues int NumRxQueues int
@ -456,6 +459,19 @@ func (ipvlan *IPVlan) Type() string {
return "ipvlan" return "ipvlan"
} }
// IPVtap - IPVtap is a virtual interfaces based on ipvlan
type IPVtap struct {
IPVlan
}
func (ipvtap *IPVtap) Attrs() *LinkAttrs {
return &ipvtap.LinkAttrs
}
func (ipvtap IPVtap) Type() string {
return "ipvtap"
}
// VlanProtocol type // VlanProtocol type
type VlanProtocol int type VlanProtocol int
@ -946,6 +962,14 @@ func (b *BondSlave) SlaveType() string {
return "bond" return "bond"
} }
type VrfSlave struct {
Table uint32
}
func (v *VrfSlave) SlaveType() string {
return "vrf"
}
// Geneve devices must specify RemoteIP and ID (VNI) on create // Geneve devices must specify RemoteIP and ID (VNI) on create
// https://github.com/torvalds/linux/blob/47ec5303d73ea344e84f46660fff693c57641386/drivers/net/geneve.c#L1209-L1223 // https://github.com/torvalds/linux/blob/47ec5303d73ea344e84f46660fff693c57641386/drivers/net/geneve.c#L1209-L1223
type Geneve struct { type Geneve struct {
@ -1049,6 +1073,37 @@ func (ip6tnl *Ip6tnl) Type() string {
return "ip6tnl" return "ip6tnl"
} }
// from https://elixir.bootlin.com/linux/v5.15.4/source/include/uapi/linux/if_tunnel.h#L84
type TunnelEncapType uint16
const (
None TunnelEncapType = iota
FOU
GUE
)
// from https://elixir.bootlin.com/linux/v5.15.4/source/include/uapi/linux/if_tunnel.h#L91
type TunnelEncapFlag uint16
const (
CSum TunnelEncapFlag = 1 << 0
CSum6 = 1 << 1
RemCSum = 1 << 2
)
// from https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/ip6_tunnel.h#L12
type IP6TunnelFlag uint16
const (
IP6_TNL_F_IGN_ENCAP_LIMIT IP6TunnelFlag = 1 // don't add encapsulation limit if one isn't present in inner packet
IP6_TNL_F_USE_ORIG_TCLASS = 2 // copy the traffic class field from the inner packet
IP6_TNL_F_USE_ORIG_FLOWLABEL = 4 // copy the flowlabel from the inner packet
IP6_TNL_F_MIP6_DEV = 8 // being used for Mobile IPv6
IP6_TNL_F_RCV_DSCP_COPY = 10 // copy DSCP from the outer packet
IP6_TNL_F_USE_ORIG_FWMARK = 20 // copy fwmark from inner packet
IP6_TNL_F_ALLOW_LOCAL_REMOTE = 40 // allow remote endpoint on the local node
)
type Sittun struct { type Sittun struct {
LinkAttrs LinkAttrs
Link uint32 Link uint32
@ -1260,11 +1315,27 @@ func (ipoib *IPoIB) Type() string {
return "ipoib" return "ipoib"
} }
type BareUDP struct {
LinkAttrs
Port uint16
EtherType uint16
SrcPortMin uint16
MultiProto bool
}
func (bareudp *BareUDP) Attrs() *LinkAttrs {
return &bareudp.LinkAttrs
}
func (bareudp *BareUDP) Type() string {
return "bareudp"
}
// iproute2 supported devices; // iproute2 supported devices;
// vlan | veth | vcan | dummy | ifb | macvlan | macvtap | // vlan | veth | vcan | dummy | ifb | macvlan | macvtap |
// bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan | // bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan |
// gre | gretap | ip6gre | ip6gretap | vti | vti6 | nlmon | // gre | gretap | ip6gre | ip6gretap | vti | vti6 | nlmon |
// bond_slave | ipvlan | xfrm // bond_slave | ipvlan | xfrm | bareudp
// LinkNotFoundError wraps the various not found errors when // LinkNotFoundError wraps the various not found errors when
// getting/reading links. This is intended for better error // getting/reading links. This is intended for better error

@ -55,8 +55,6 @@ const (
VF_LINK_STATE_DISABLE uint32 = 2 VF_LINK_STATE_DISABLE uint32 = 2
) )
var lookupByDump = false
var macvlanModes = [...]uint32{ var macvlanModes = [...]uint32{
0, 0,
nl.MACVLAN_MODE_PRIVATE, nl.MACVLAN_MODE_PRIVATE,
@ -153,7 +151,6 @@ func (h *Handle) LinkSetAllmulticastOn(link Link) error {
msg := nl.NewIfInfomsg(unix.AF_UNSPEC) msg := nl.NewIfInfomsg(unix.AF_UNSPEC)
msg.Change = unix.IFF_ALLMULTI msg.Change = unix.IFF_ALLMULTI
msg.Flags = unix.IFF_ALLMULTI msg.Flags = unix.IFF_ALLMULTI
msg.Index = int32(base.Index) msg.Index = int32(base.Index)
req.AddData(msg) req.AddData(msg)
@ -183,6 +180,51 @@ func (h *Handle) LinkSetAllmulticastOff(link Link) error {
return err return err
} }
// LinkSetMulticastOn enables the reception of multicast packets for the link device.
// Equivalent to: `ip link set $link multicast on`
func LinkSetMulticastOn(link Link) error {
return pkgHandle.LinkSetMulticastOn(link)
}
// LinkSetMulticastOn enables the reception of multicast packets for the link device.
// Equivalent to: `ip link set $link multicast on`
func (h *Handle) LinkSetMulticastOn(link Link) error {
base := link.Attrs()
h.ensureIndex(base)
req := h.newNetlinkRequest(unix.RTM_NEWLINK, unix.NLM_F_ACK)
msg := nl.NewIfInfomsg(unix.AF_UNSPEC)
msg.Change = unix.IFF_MULTICAST
msg.Flags = unix.IFF_MULTICAST
msg.Index = int32(base.Index)
req.AddData(msg)
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
return err
}
// LinkSetAllmulticastOff disables the reception of multicast packets for the link device.
// Equivalent to: `ip link set $link multicast off`
func LinkSetMulticastOff(link Link) error {
return pkgHandle.LinkSetMulticastOff(link)
}
// LinkSetAllmulticastOff disables the reception of multicast packets for the link device.
// Equivalent to: `ip link set $link multicast off`
func (h *Handle) LinkSetMulticastOff(link Link) error {
base := link.Attrs()
h.ensureIndex(base)
req := h.newNetlinkRequest(unix.RTM_NEWLINK, unix.NLM_F_ACK)
msg := nl.NewIfInfomsg(unix.AF_UNSPEC)
msg.Change = unix.IFF_MULTICAST
msg.Index = int32(base.Index)
req.AddData(msg)
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
return err
}
func MacvlanMACAddrAdd(link Link, addr net.HardwareAddr) error { func MacvlanMACAddrAdd(link Link, addr net.HardwareAddr) error {
return pkgHandle.MacvlanMACAddrAdd(link, addr) return pkgHandle.MacvlanMACAddrAdd(link, addr)
} }
@ -547,13 +589,13 @@ func (h *Handle) LinkSetVfVlanQos(link Link, vf, vlan, qos int) error {
req.AddData(msg) req.AddData(msg)
data := nl.NewRtAttr(unix.IFLA_VFINFO_LIST, nil) data := nl.NewRtAttr(unix.IFLA_VFINFO_LIST, nil)
info := nl.NewRtAttrChild(data, nl.IFLA_VF_INFO, nil) info := data.AddRtAttr(nl.IFLA_VF_INFO, nil)
vfmsg := nl.VfVlan{ vfmsg := nl.VfVlan{
Vf: uint32(vf), Vf: uint32(vf),
Vlan: uint32(vlan), Vlan: uint32(vlan),
Qos: uint32(qos), Qos: uint32(qos),
} }
nl.NewRtAttrChild(info, nl.IFLA_VF_VLAN, vfmsg.Serialize()) info.AddRtAttr(nl.IFLA_VF_VLAN, vfmsg.Serialize())
req.AddData(data) req.AddData(data)
_, err := req.Execute(unix.NETLINK_ROUTE, 0) _, err := req.Execute(unix.NETLINK_ROUTE, 0)
@ -1104,6 +1146,10 @@ func (h *Handle) LinkAdd(link Link) error {
return h.linkModify(link, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK) return h.linkModify(link, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK)
} }
func LinkModify(link Link) error {
return pkgHandle.LinkModify(link)
}
func (h *Handle) LinkModify(link Link) error { func (h *Handle) LinkModify(link Link) error {
return h.linkModify(link, unix.NLM_F_REQUEST|unix.NLM_F_ACK) return h.linkModify(link, unix.NLM_F_REQUEST|unix.NLM_F_ACK)
} }
@ -1218,9 +1264,26 @@ func (h *Handle) linkModify(link Link, flags int) error {
} }
control := func(file *os.File, f func(fd uintptr)) error {
name := file.Name()
conn, err := file.SyscallConn()
if err != nil {
return fmt.Errorf("SyscallConn() failed on %s: %v", name, err)
}
if err := conn.Control(f); err != nil {
return fmt.Errorf("Failed to get file descriptor for %s: %v", name, err)
}
return nil
}
// only persist interface if NonPersist is NOT set // only persist interface if NonPersist is NOT set
if !tuntap.NonPersist { if !tuntap.NonPersist {
_, _, errno := unix.Syscall(unix.SYS_IOCTL, fds[0].Fd(), uintptr(unix.TUNSETPERSIST), 1) var errno syscall.Errno
if err := control(fds[0], func(fd uintptr) {
_, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, uintptr(unix.TUNSETPERSIST), 1)
}); err != nil {
return err
}
if errno != 0 { if errno != 0 {
cleanupFds(fds) cleanupFds(fds)
return fmt.Errorf("Tuntap IOCTL TUNSETPERSIST failed, errno %v", errno) return fmt.Errorf("Tuntap IOCTL TUNSETPERSIST failed, errno %v", errno)
@ -1237,7 +1300,10 @@ func (h *Handle) linkModify(link Link, flags int) error {
// un-persist (e.g. allow the interface to be removed) the tuntap // un-persist (e.g. allow the interface to be removed) the tuntap
// should not hurt if not set prior, condition might be not needed // should not hurt if not set prior, condition might be not needed
if !tuntap.NonPersist { if !tuntap.NonPersist {
_, _, _ = unix.Syscall(unix.SYS_IOCTL, fds[0].Fd(), uintptr(unix.TUNSETPERSIST), 0) // ignore error
_ = control(fds[0], func(fd uintptr) {
_, _, _ = unix.Syscall(unix.SYS_IOCTL, fd, uintptr(unix.TUNSETPERSIST), 0)
})
} }
cleanupFds(fds) cleanupFds(fds)
return err return err
@ -1409,6 +1475,10 @@ func (h *Handle) linkModify(link Link, flags int) error {
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
data.AddRtAttr(nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(link.Mode))) data.AddRtAttr(nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(link.Mode)))
data.AddRtAttr(nl.IFLA_IPVLAN_FLAG, nl.Uint16Attr(uint16(link.Flag))) data.AddRtAttr(nl.IFLA_IPVLAN_FLAG, nl.Uint16Attr(uint16(link.Flag)))
case *IPVtap:
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
data.AddRtAttr(nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(link.Mode)))
data.AddRtAttr(nl.IFLA_IPVLAN_FLAG, nl.Uint16Attr(uint16(link.Flag)))
case *Macvlan: case *Macvlan:
if link.Mode != MACVLAN_MODE_DEFAULT { if link.Mode != MACVLAN_MODE_DEFAULT {
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil) data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
@ -1443,6 +1513,8 @@ func (h *Handle) linkModify(link Link, flags int) error {
addXfrmiAttrs(link, linkInfo) addXfrmiAttrs(link, linkInfo)
case *IPoIB: case *IPoIB:
addIPoIBAttrs(link, linkInfo) addIPoIBAttrs(link, linkInfo)
case *BareUDP:
addBareUDPAttrs(link, linkInfo)
} }
req.AddData(linkInfo) req.AddData(linkInfo)
@ -1624,7 +1696,7 @@ func execGetLink(req *nl.NetlinkRequest) (Link, error) {
} }
} }
// linkDeserialize deserializes a raw message received from netlink into // LinkDeserialize deserializes a raw message received from netlink into
// a link object. // a link object.
func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) { func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
msg := nl.DeserializeIfInfomsg(m) msg := nl.DeserializeIfInfomsg(m)
@ -1639,9 +1711,17 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
base.RawFlags = msg.Flags base.RawFlags = msg.Flags
base.Flags = linkFlags(msg.Flags) base.Flags = linkFlags(msg.Flags)
base.EncapType = msg.EncapType() base.EncapType = msg.EncapType()
base.NetNsID = -1
if msg.Flags&unix.IFF_PROMISC != 0 { if msg.Flags&unix.IFF_PROMISC != 0 {
base.Promisc = 1 base.Promisc = 1
} }
if msg.Flags&unix.IFF_ALLMULTI != 0 {
base.Allmulti = 1
}
if msg.Flags&unix.IFF_MULTICAST != 0 {
base.Multi = 1
}
var ( var (
link Link link Link
stats32 *LinkStatistics32 stats32 *LinkStatistics32
@ -1680,6 +1760,8 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
link = &Bond{} link = &Bond{}
case "ipvlan": case "ipvlan":
link = &IPVlan{} link = &IPVlan{}
case "ipvtap":
link = &IPVtap{}
case "macvlan": case "macvlan":
link = &Macvlan{} link = &Macvlan{}
case "macvtap": case "macvtap":
@ -1714,6 +1796,8 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
link = &IPoIB{} link = &IPoIB{}
case "can": case "can":
link = &Can{} link = &Can{}
case "bareudp":
link = &BareUDP{}
default: default:
link = &GenericLink{LinkType: linkType} link = &GenericLink{LinkType: linkType}
} }
@ -1731,6 +1815,8 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
parseBondData(link, data) parseBondData(link, data)
case "ipvlan": case "ipvlan":
parseIPVlanData(link, data) parseIPVlanData(link, data)
case "ipvtap":
parseIPVtapData(link, data)
case "macvlan": case "macvlan":
parseMacvlanData(link, data) parseMacvlanData(link, data)
case "macvtap": case "macvtap":
@ -1767,13 +1853,19 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
parseIPoIBData(link, data) parseIPoIBData(link, data)
case "can": case "can":
parseCanData(link, data) parseCanData(link, data)
case "bareudp":
parseBareUDPData(link, data)
} }
case nl.IFLA_INFO_SLAVE_KIND: case nl.IFLA_INFO_SLAVE_KIND:
slaveType = string(info.Value[:len(info.Value)-1]) slaveType = string(info.Value[:len(info.Value)-1])
switch slaveType { switch slaveType {
case "bond": case "bond":
linkSlave = &BondSlave{} linkSlave = &BondSlave{}
case "vrf":
linkSlave = &VrfSlave{}
} }
case nl.IFLA_INFO_SLAVE_DATA: case nl.IFLA_INFO_SLAVE_DATA:
switch slaveType { switch slaveType {
case "bond": case "bond":
@ -1782,6 +1874,12 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
return nil, err return nil, err
} }
parseBondSlaveData(linkSlave, data) parseBondSlaveData(linkSlave, data)
case "vrf":
data, err := nl.ParseRouteAttr(info.Value)
if err != nil {
return nil, err
}
parseVrfSlaveData(linkSlave, data)
} }
} }
} }
@ -1835,6 +1933,8 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
} }
case unix.IFLA_OPERSTATE: case unix.IFLA_OPERSTATE:
base.OperState = LinkOperState(uint8(attr.Value[0])) base.OperState = LinkOperState(uint8(attr.Value[0]))
case unix.IFLA_PHYS_SWITCH_ID:
base.PhysSwitchID = int(native.Uint32(attr.Value[0:4]))
case unix.IFLA_LINK_NETNSID: case unix.IFLA_LINK_NETNSID:
base.NetNsID = int(native.Uint32(attr.Value[0:4])) base.NetNsID = int(native.Uint32(attr.Value[0:4]))
case unix.IFLA_GSO_MAX_SIZE: case unix.IFLA_GSO_MAX_SIZE:
@ -2023,7 +2123,8 @@ func linkSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-c
msgs, from, err := s.Receive() msgs, from, err := s.Receive()
if err != nil { if err != nil {
if cberr != nil { if cberr != nil {
cberr(err) cberr(fmt.Errorf("Receive failed: %v",
err))
} }
return return
} }
@ -2038,15 +2139,15 @@ func linkSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-c
continue continue
} }
if m.Header.Type == unix.NLMSG_ERROR { if m.Header.Type == unix.NLMSG_ERROR {
native := nl.NativeEndian()
error := int32(native.Uint32(m.Data[0:4])) error := int32(native.Uint32(m.Data[0:4]))
if error == 0 { if error == 0 {
continue continue
} }
if cberr != nil { if cberr != nil {
cberr(syscall.Errno(-error)) cberr(fmt.Errorf("error message: %v",
syscall.Errno(-error)))
} }
return continue
} }
ifmsg := nl.DeserializeIfInfomsg(m.Data) ifmsg := nl.DeserializeIfInfomsg(m.Data)
header := unix.NlMsghdr(m.Header) header := unix.NlMsghdr(m.Header)
@ -2055,7 +2156,7 @@ func linkSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-c
if cberr != nil { if cberr != nil {
cberr(err) cberr(err)
} }
return continue
} }
ch <- LinkUpdate{IfInfomsg: *ifmsg, Header: header, Link: link} ch <- LinkUpdate{IfInfomsg: *ifmsg, Header: header, Link: link}
} }
@ -2404,6 +2505,16 @@ func parseBondSlaveData(slave LinkSlave, data []syscall.NetlinkRouteAttr) {
} }
} }
func parseVrfSlaveData(slave LinkSlave, data []syscall.NetlinkRouteAttr) {
vrfSlave := slave.(*VrfSlave)
for i := range data {
switch data[i].Attr.Type {
case nl.IFLA_BOND_SLAVE_STATE:
vrfSlave.Table = native.Uint32(data[i].Value[0:4])
}
}
}
func parseIPVlanData(link Link, data []syscall.NetlinkRouteAttr) { func parseIPVlanData(link Link, data []syscall.NetlinkRouteAttr) {
ipv := link.(*IPVlan) ipv := link.(*IPVlan)
for _, datum := range data { for _, datum := range data {
@ -2416,6 +2527,18 @@ func parseIPVlanData(link Link, data []syscall.NetlinkRouteAttr) {
} }
} }
func parseIPVtapData(link Link, data []syscall.NetlinkRouteAttr) {
ipv := link.(*IPVtap)
for _, datum := range data {
switch datum.Attr.Type {
case nl.IFLA_IPVLAN_MODE:
ipv.Mode = IPVlanMode(native.Uint32(datum.Value[0:4]))
case nl.IFLA_IPVLAN_FLAG:
ipv.Flag = IPVlanFlag(native.Uint32(datum.Value[0:4]))
}
}
}
func parseMacvtapData(link Link, data []syscall.NetlinkRouteAttr) { func parseMacvtapData(link Link, data []syscall.NetlinkRouteAttr) {
macv := link.(*Macvtap) macv := link.(*Macvtap)
parseMacvlanData(&macv.Macvlan, data) parseMacvlanData(&macv.Macvlan, data)
@ -2756,6 +2879,10 @@ func addIptunAttrs(iptun *Iptun, linkInfo *nl.RtAttr) {
func parseIptunData(link Link, data []syscall.NetlinkRouteAttr) { func parseIptunData(link Link, data []syscall.NetlinkRouteAttr) {
iptun := link.(*Iptun) iptun := link.(*Iptun)
for _, datum := range data { for _, datum := range data {
// NOTE: same with vxlan, ip tunnel may also has null datum.Value
if len(datum.Value) == 0 {
continue
}
switch datum.Attr.Type { switch datum.Attr.Type {
case nl.IFLA_IPTUN_LOCAL: case nl.IFLA_IPTUN_LOCAL:
iptun.Local = net.IP(datum.Value[0:4]) iptun.Local = net.IP(datum.Value[0:4])
@ -3303,3 +3430,32 @@ func addIPoIBAttrs(ipoib *IPoIB, linkInfo *nl.RtAttr) {
data.AddRtAttr(nl.IFLA_IPOIB_MODE, nl.Uint16Attr(uint16(ipoib.Mode))) data.AddRtAttr(nl.IFLA_IPOIB_MODE, nl.Uint16Attr(uint16(ipoib.Mode)))
data.AddRtAttr(nl.IFLA_IPOIB_UMCAST, nl.Uint16Attr(uint16(ipoib.Umcast))) data.AddRtAttr(nl.IFLA_IPOIB_UMCAST, nl.Uint16Attr(uint16(ipoib.Umcast)))
} }
func addBareUDPAttrs(bareudp *BareUDP, linkInfo *nl.RtAttr) {
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
data.AddRtAttr(nl.IFLA_BAREUDP_PORT, nl.Uint16Attr(nl.Swap16(bareudp.Port)))
data.AddRtAttr(nl.IFLA_BAREUDP_ETHERTYPE, nl.Uint16Attr(nl.Swap16(bareudp.EtherType)))
if bareudp.SrcPortMin != 0 {
data.AddRtAttr(nl.IFLA_BAREUDP_SRCPORT_MIN, nl.Uint16Attr(bareudp.SrcPortMin))
}
if bareudp.MultiProto {
data.AddRtAttr(nl.IFLA_BAREUDP_MULTIPROTO_MODE, []byte{})
}
}
func parseBareUDPData(link Link, data []syscall.NetlinkRouteAttr) {
bareudp := link.(*BareUDP)
for _, attr := range data {
switch attr.Attr.Type {
case nl.IFLA_BAREUDP_PORT:
bareudp.Port = binary.BigEndian.Uint16(attr.Value)
case nl.IFLA_BAREUDP_ETHERTYPE:
bareudp.EtherType = binary.BigEndian.Uint16(attr.Value)
case nl.IFLA_BAREUDP_SRCPORT_MIN:
bareudp.SrcPortMin = native.Uint16(attr.Value)
case nl.IFLA_BAREUDP_MULTIPROTO_MODE:
bareudp.MultiProto = true
}
}
}

@ -12,6 +12,7 @@ type Neigh struct {
State int State int
Type int Type int
Flags int Flags int
FlagsExt int
IP net.IP IP net.IP
HardwareAddr net.HardwareAddr HardwareAddr net.HardwareAddr
LLIPAddr net.IP //Used in the case of NHRP LLIPAddr net.IP //Used in the case of NHRP

@ -24,7 +24,11 @@ const (
NDA_MASTER NDA_MASTER
NDA_LINK_NETNSID NDA_LINK_NETNSID
NDA_SRC_VNI NDA_SRC_VNI
NDA_MAX = NDA_SRC_VNI NDA_PROTOCOL
NDA_NH_ID
NDA_FDB_EXT_ATTRS
NDA_FLAGS_EXT
NDA_MAX = NDA_FLAGS_EXT
) )
// Neighbor Cache Entry States. // Neighbor Cache Entry States.
@ -42,11 +46,19 @@ const (
// Neighbor Flags // Neighbor Flags
const ( const (
NTF_USE = 0x01 NTF_USE = 0x01
NTF_SELF = 0x02 NTF_SELF = 0x02
NTF_MASTER = 0x04 NTF_MASTER = 0x04
NTF_PROXY = 0x08 NTF_PROXY = 0x08
NTF_ROUTER = 0x80 NTF_EXT_LEARNED = 0x10
NTF_OFFLOADED = 0x20
NTF_STICKY = 0x40
NTF_ROUTER = 0x80
)
// Extended Neighbor Flags
const (
NTF_EXT_MANAGED = 0x00000001
) )
// Ndmsg is for adding, removing or receiving information about a neighbor table entry // Ndmsg is for adding, removing or receiving information about a neighbor table entry
@ -162,11 +174,16 @@ func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error {
if neigh.LLIPAddr != nil { if neigh.LLIPAddr != nil {
llIPData := nl.NewRtAttr(NDA_LLADDR, neigh.LLIPAddr.To4()) llIPData := nl.NewRtAttr(NDA_LLADDR, neigh.LLIPAddr.To4())
req.AddData(llIPData) req.AddData(llIPData)
} else if neigh.Flags != NTF_PROXY || neigh.HardwareAddr != nil { } else if neigh.HardwareAddr != nil {
hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr)) hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr))
req.AddData(hwData) req.AddData(hwData)
} }
if neigh.FlagsExt != 0 {
flagsExtData := nl.NewRtAttr(NDA_FLAGS_EXT, nl.Uint32Attr(uint32(neigh.FlagsExt)))
req.AddData(flagsExtData)
}
if neigh.Vlan != 0 { if neigh.Vlan != 0 {
vlanData := nl.NewRtAttr(NDA_VLAN, nl.Uint16Attr(uint16(neigh.Vlan))) vlanData := nl.NewRtAttr(NDA_VLAN, nl.Uint16Attr(uint16(neigh.Vlan)))
req.AddData(vlanData) req.AddData(vlanData)
@ -305,6 +322,8 @@ func NeighDeserialize(m []byte) (*Neigh, error) {
} else { } else {
neigh.HardwareAddr = net.HardwareAddr(attr.Value) neigh.HardwareAddr = net.HardwareAddr(attr.Value)
} }
case NDA_FLAGS_EXT:
neigh.FlagsExt = int(native.Uint32(attr.Value[0:4]))
case NDA_VLAN: case NDA_VLAN:
neigh.Vlan = int(native.Uint16(attr.Value[0:2])) neigh.Vlan = int(native.Uint16(attr.Value[0:2]))
case NDA_VNI: case NDA_VNI:
@ -408,7 +427,6 @@ func neighSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- NeighUpdate, done <
continue continue
} }
if m.Header.Type == unix.NLMSG_ERROR { if m.Header.Type == unix.NLMSG_ERROR {
native := nl.NativeEndian()
error := int32(native.Uint32(m.Data[0:4])) error := int32(native.Uint32(m.Data[0:4]))
if error == 0 { if error == 0 {
continue continue

@ -180,14 +180,30 @@ func RouteAdd(route *Route) error {
return ErrNotImplemented return ErrNotImplemented
} }
func RouteAppend(route *Route) error {
return ErrNotImplemented
}
func RouteDel(route *Route) error { func RouteDel(route *Route) error {
return ErrNotImplemented return ErrNotImplemented
} }
func RouteGet(destination net.IP) ([]Route, error) {
return nil, ErrNotImplemented
}
func RouteList(link Link, family int) ([]Route, error) { func RouteList(link Link, family int) ([]Route, error) {
return nil, ErrNotImplemented return nil, ErrNotImplemented
} }
func RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, error) {
return nil, ErrNotImplemented
}
func RouteReplace(route *Route) error {
return ErrNotImplemented
}
func XfrmPolicyAdd(policy *XfrmPolicy) error { func XfrmPolicyAdd(policy *XfrmPolicy) error {
return ErrNotImplemented return ErrNotImplemented
} }

@ -87,7 +87,7 @@ func (h *Handle) getNetNsId(attrType int, val uint32) (int, error) {
rtgen := nl.NewRtGenMsg() rtgen := nl.NewRtGenMsg()
req.AddData(rtgen) req.AddData(rtgen)
b := make([]byte, 4, 4) b := make([]byte, 4)
native.PutUint32(b, val) native.PutUint32(b, val)
attr := nl.NewRtAttr(attrType, b) attr := nl.NewRtAttr(attrType, b)
req.AddData(attr) req.AddData(attr)
@ -126,12 +126,12 @@ func (h *Handle) setNetNsId(attrType int, val uint32, newnsid uint32) error {
rtgen := nl.NewRtGenMsg() rtgen := nl.NewRtGenMsg()
req.AddData(rtgen) req.AddData(rtgen)
b := make([]byte, 4, 4) b := make([]byte, 4)
native.PutUint32(b, val) native.PutUint32(b, val)
attr := nl.NewRtAttr(attrType, b) attr := nl.NewRtAttr(attrType, b)
req.AddData(attr) req.AddData(attr)
b1 := make([]byte, 4, 4) b1 := make([]byte, 4)
native.PutUint32(b1, newnsid) native.PutUint32(b1, newnsid)
attr1 := nl.NewRtAttr(NETNSA_NSID, b1) attr1 := nl.NewRtAttr(NETNSA_NSID, b1)
req.AddData(attr1) req.AddData(attr1)

@ -44,6 +44,7 @@ const (
NLA_F_NESTED uint16 = (1 << 15) // #define NLA_F_NESTED (1 << 15) NLA_F_NESTED uint16 = (1 << 15) // #define NLA_F_NESTED (1 << 15)
NLA_F_NET_BYTEORDER uint16 = (1 << 14) // #define NLA_F_NESTED (1 << 14) NLA_F_NET_BYTEORDER uint16 = (1 << 14) // #define NLA_F_NESTED (1 << 14)
NLA_TYPE_MASK = ^(NLA_F_NESTED | NLA_F_NET_BYTEORDER) NLA_TYPE_MASK = ^(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
NLA_ALIGNTO uint16 = 4 // #define NLA_ALIGNTO 4
) )
// enum ctattr_type { // enum ctattr_type {

@ -11,22 +11,37 @@ const (
const ( const (
DEVLINK_CMD_GET = 1 DEVLINK_CMD_GET = 1
DEVLINK_CMD_PORT_GET = 5 DEVLINK_CMD_PORT_GET = 5
DEVLINK_CMD_PORT_SET = 6
DEVLINK_CMD_PORT_NEW = 7
DEVLINK_CMD_PORT_DEL = 8
DEVLINK_CMD_ESWITCH_GET = 29 DEVLINK_CMD_ESWITCH_GET = 29
DEVLINK_CMD_ESWITCH_SET = 30 DEVLINK_CMD_ESWITCH_SET = 30
DEVLINK_CMD_INFO_GET = 51
) )
const ( const (
DEVLINK_ATTR_BUS_NAME = 1 DEVLINK_ATTR_BUS_NAME = 1
DEVLINK_ATTR_DEV_NAME = 2 DEVLINK_ATTR_DEV_NAME = 2
DEVLINK_ATTR_PORT_INDEX = 3 DEVLINK_ATTR_PORT_INDEX = 3
DEVLINK_ATTR_PORT_TYPE = 4 DEVLINK_ATTR_PORT_TYPE = 4
DEVLINK_ATTR_PORT_NETDEV_IFINDEX = 6 DEVLINK_ATTR_PORT_NETDEV_IFINDEX = 6
DEVLINK_ATTR_PORT_NETDEV_NAME = 7 DEVLINK_ATTR_PORT_NETDEV_NAME = 7
DEVLINK_ATTR_PORT_IBDEV_NAME = 8 DEVLINK_ATTR_PORT_IBDEV_NAME = 8
DEVLINK_ATTR_ESWITCH_MODE = 25 DEVLINK_ATTR_ESWITCH_MODE = 25
DEVLINK_ATTR_ESWITCH_INLINE_MODE = 26 DEVLINK_ATTR_ESWITCH_INLINE_MODE = 26
DEVLINK_ATTR_ESWITCH_ENCAP_MODE = 62 DEVLINK_ATTR_ESWITCH_ENCAP_MODE = 62
DEVLINK_ATTR_PORT_FLAVOUR = 77 DEVLINK_ATTR_PORT_FLAVOUR = 77
DEVLINK_ATTR_INFO_DRIVER_NAME = 98
DEVLINK_ATTR_INFO_SERIAL_NUMBER = 99
DEVLINK_ATTR_INFO_VERSION_FIXED = 100
DEVLINK_ATTR_INFO_VERSION_RUNNING = 101
DEVLINK_ATTR_INFO_VERSION_STORED = 102
DEVLINK_ATTR_INFO_VERSION_NAME = 103
DEVLINK_ATTR_INFO_VERSION_VALUE = 104
DEVLINK_ATTR_PORT_PCI_PF_NUMBER = 127
DEVLINK_ATTR_PORT_FUNCTION = 145
DEVLINK_ATTR_PORT_CONTROLLER_NUMBER = 150
DEVLINK_ATTR_PORT_PCI_SF_NUMBER = 164
) )
const ( const (
@ -53,6 +68,8 @@ const (
DEVLINK_PORT_FLAVOUR_PCI_PF = 3 DEVLINK_PORT_FLAVOUR_PCI_PF = 3
DEVLINK_PORT_FLAVOUR_PCI_VF = 4 DEVLINK_PORT_FLAVOUR_PCI_VF = 4
DEVLINK_PORT_FLAVOUR_VIRTUAL = 5 DEVLINK_PORT_FLAVOUR_VIRTUAL = 5
DEVLINK_PORT_FLAVOUR_UNUSED = 6
DEVLINK_PORT_FLAVOUR_PCI_SF = 7
) )
const ( const (
@ -61,3 +78,19 @@ const (
DEVLINK_PORT_TYPE_ETH = 2 DEVLINK_PORT_TYPE_ETH = 2
DEVLINK_PORT_TYPE_IB = 3 DEVLINK_PORT_TYPE_IB = 3
) )
const (
DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR = 1
DEVLINK_PORT_FN_ATTR_STATE = 2
DEVLINK_PORT_FN_ATTR_OPSTATE = 3
)
const (
DEVLINK_PORT_FN_STATE_INACTIVE = 0
DEVLINK_PORT_FN_STATE_ACTIVE = 1
)
const (
DEVLINK_PORT_FN_OPSTATE_DETACHED = 0
DEVLINK_PORT_FN_OPSTATE_ATTACHED = 1
)

@ -709,3 +709,12 @@ const (
IFLA_CAN_BITRATE_MAX IFLA_CAN_BITRATE_MAX
IFLA_CAN_MAX = IFLA_CAN_BITRATE_MAX IFLA_CAN_MAX = IFLA_CAN_BITRATE_MAX
) )
const (
IFLA_BAREUDP_UNSPEC = iota
IFLA_BAREUDP_PORT
IFLA_BAREUDP_ETHERTYPE
IFLA_BAREUDP_SRCPORT_MIN
IFLA_BAREUDP_MULTIPROTO_MODE
IFLA_BAREUDP_MAX = IFLA_BAREUDP_MULTIPROTO_MODE
)

29
vendor/github.com/vishvananda/netlink/nl/lwt_linux.go generated vendored Normal file

@ -0,0 +1,29 @@
package nl
const (
LWT_BPF_PROG_UNSPEC = iota
LWT_BPF_PROG_FD
LWT_BPF_PROG_NAME
__LWT_BPF_PROG_MAX
)
const (
LWT_BPF_PROG_MAX = __LWT_BPF_PROG_MAX - 1
)
const (
LWT_BPF_UNSPEC = iota
LWT_BPF_IN
LWT_BPF_OUT
LWT_BPF_XMIT
LWT_BPF_XMIT_HEADROOM
__LWT_BPF_MAX
)
const (
LWT_BPF_MAX = __LWT_BPF_MAX - 1
)
const (
LWT_BPF_MAX_HEADROOM = 256
)

Some files were not shown because too many files have changed in this diff Show More