From f035e9b97353faa0e5508fa5f8820178a1bb8c57 Mon Sep 17 00:00:00 2001 From: Zexi Li Date: Mon, 5 Feb 2024 11:59:51 +0800 Subject: [PATCH 1/3] feat: init cni plugin --- cmd/ocnet-cni/10-ocnet-cni.conflist | 9 + cmd/ocnet-cni/main.go | 22 ++ go.mod | 2 + go.sum | 27 ++ pkg/cni-plugin/plugin/plugin.go | 99 ++++++ pkg/cni-plugin/plugin/types.go | 24 ++ pkg/cni-plugin/plugin/utils.go | 91 ++++++ pkg/cni-plugin/plugin/utils_test.go | 42 +++ .../containernetworking/cni/LICENSE | 202 ++++++++++++ .../containernetworking/cni/pkg/skel/skel.go | 296 +++++++++++++++++ .../cni/pkg/types/020/types.go | 189 +++++++++++ .../cni/pkg/types/040/types.go | 306 +++++++++++++++++ .../cni/pkg/types/100/types.go | 307 ++++++++++++++++++ .../containernetworking/cni/pkg/types/args.go | 122 +++++++ .../cni/pkg/types/create/create.go | 56 ++++ .../cni/pkg/types/internal/convert.go | 92 ++++++ .../cni/pkg/types/internal/create.go | 66 ++++ .../cni/pkg/types/types.go | 234 +++++++++++++ .../cni/pkg/utils/utils.go | 84 +++++ .../cni/pkg/version/conf.go | 26 ++ .../cni/pkg/version/plugin.go | 144 ++++++++ .../cni/pkg/version/reconcile.go | 49 +++ .../cni/pkg/version/version.go | 73 +++++ .../containernetworking/plugins/LICENSE | 201 ++++++++++++ .../plugins/pkg/ns/README.md | 41 +++ .../plugins/pkg/ns/ns_linux.go | 234 +++++++++++++ vendor/modules.txt | 14 + 27 files changed, 3052 insertions(+) create mode 100644 cmd/ocnet-cni/10-ocnet-cni.conflist create mode 100644 cmd/ocnet-cni/main.go create mode 100644 pkg/cni-plugin/plugin/plugin.go create mode 100644 pkg/cni-plugin/plugin/types.go create mode 100644 pkg/cni-plugin/plugin/utils.go create mode 100644 pkg/cni-plugin/plugin/utils_test.go create mode 100644 vendor/github.com/containernetworking/cni/LICENSE create mode 100644 vendor/github.com/containernetworking/cni/pkg/skel/skel.go create mode 100644 vendor/github.com/containernetworking/cni/pkg/types/020/types.go create mode 100644 vendor/github.com/containernetworking/cni/pkg/types/040/types.go create mode 100644 vendor/github.com/containernetworking/cni/pkg/types/100/types.go create mode 100644 vendor/github.com/containernetworking/cni/pkg/types/args.go create mode 100644 vendor/github.com/containernetworking/cni/pkg/types/create/create.go create mode 100644 vendor/github.com/containernetworking/cni/pkg/types/internal/convert.go create mode 100644 vendor/github.com/containernetworking/cni/pkg/types/internal/create.go create mode 100644 vendor/github.com/containernetworking/cni/pkg/types/types.go create mode 100644 vendor/github.com/containernetworking/cni/pkg/utils/utils.go create mode 100644 vendor/github.com/containernetworking/cni/pkg/version/conf.go create mode 100644 vendor/github.com/containernetworking/cni/pkg/version/plugin.go create mode 100644 vendor/github.com/containernetworking/cni/pkg/version/reconcile.go create mode 100644 vendor/github.com/containernetworking/cni/pkg/version/version.go create mode 100644 vendor/github.com/containernetworking/plugins/LICENSE create mode 100644 vendor/github.com/containernetworking/plugins/pkg/ns/README.md create mode 100644 vendor/github.com/containernetworking/plugins/pkg/ns/ns_linux.go diff --git a/cmd/ocnet-cni/10-ocnet-cni.conflist b/cmd/ocnet-cni/10-ocnet-cni.conflist new file mode 100644 index 000000000..ae9915b78 --- /dev/null +++ b/cmd/ocnet-cni/10-ocnet-cni.conflist @@ -0,0 +1,9 @@ +{ + "name": "pod-network", + "cniVersion": "0.3.1", + "plugins": [ + { + "type": "ocnet-cni" + } + ] +} diff --git a/cmd/ocnet-cni/main.go b/cmd/ocnet-cni/main.go new file mode 100644 index 000000000..054531b2f --- /dev/null +++ b/cmd/ocnet-cni/main.go @@ -0,0 +1,22 @@ +package main + +import ( + "os" + "path/filepath" + + "yunion.io/x/log" + + "yunion.io/x/kubecomps/pkg/cni-plugin/plugin" +) + +func main() { + log.Infof("-------start") + // Use the name of the binary to determine which routine to run. + _, filename := filepath.Split(os.Args[0]) + switch filename { + case "ocnet-cni": + plugin.Main("unknown version") + default: + log.Fatalf("Unsupported %s", filename) + } +} diff --git a/go.mod b/go.mod index a0669a35b..322710f88 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,8 @@ require ( github.com/Masterminds/semver/v3 v3.1.0 github.com/Masterminds/sprig v2.22.0+incompatible github.com/ceph/go-ceph v0.0.0-20181217221554-e32f9f0f2e94 + github.com/containernetworking/cni v1.0.0 + github.com/containernetworking/plugins v0.8.7 github.com/fsnotify/fsnotify v1.5.1 github.com/ghodss/yaml v1.0.0 github.com/gofrs/flock v0.8.0 diff --git a/go.sum b/go.sum index b6b156e60..77b220660 100644 --- a/go.sum +++ b/go.sum @@ -108,8 +108,10 @@ github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZ github.com/Masterminds/squirrel v1.4.0 h1:he5i/EXixZxrBUWcxzDYMiju9WZ3ld/l7QBNuo/eN3w= github.com/Masterminds/squirrel v1.4.0/go.mod h1:yaPeOnPG5ZRwL9oKdTsO/prlkPbXWZlRVMQ/gGlzIuA= github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= +github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= github.com/Microsoft/hcsshim v0.8.10-0.20200715222032-5eafd1556990 h1:1xpVY4dSUSbW3PcSGxZJhI8Z+CJiqbd933kM7HIinTc= github.com/Microsoft/hcsshim v0.8.10-0.20200715222032-5eafd1556990/go.mod h1:ay/0dTb7NsG8QMDfsRfLHgZo/6xAJShLe1+ePPflihk= @@ -138,6 +140,7 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -179,6 +182,7 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dR github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bshuster-repo/logrus-logstash-hook v0.4.1 h1:pgAtgj+A31JBVtEHu2uHuEx0n+2ukqUJnS2vVe5pQNA= github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= @@ -232,9 +236,14 @@ github.com/containerd/ttrpc v1.0.0/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/containerd/typeurl v1.0.0/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v1.0.0 h1:9VJe1a5uKtdeJIHC/UvbTweracOh6GafT0nfbEGVcQ0= +github.com/containernetworking/cni v1.0.0/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtrBI6QcRV0NiNt15Y= +github.com/containernetworking/plugins v0.8.7 h1:bU7QieuAp+sACI2vCzESJ3FoT860urYP+lThyZkb/2M= +github.com/containernetworking/plugins v0.8.7/go.mod h1:R7lXeZaBzpfqapcAbHRW8/CYwm0dHzbz0XEjofx0uB0= github.com/coredns/corefile-migration v1.0.10/go.mod h1:RMy/mXdeDlYwzt0vdMEJvT2hGJ2I86/eO0UdXmH9XNI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= @@ -256,6 +265,10 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= +github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= +github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= +github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -470,6 +483,7 @@ github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kE github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -659,6 +673,7 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/ishidawataru/sctp v0.0.0-20190723014705-7c296d48a2b5/go.mod h1:DM4VvS+hD/kDi1U1QsX2fnZowwBhqD0Dk3bRPKF/Oc8= +github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mod h1:wK6yTYYcgjHE1Z1QtXACPDjcFJyBskHEdagmnq3vsP8= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -776,6 +791,7 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= @@ -868,6 +884,7 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ= +github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -875,8 +892,10 @@ github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4= github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -1028,15 +1047,18 @@ github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNue github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil/v3 v3.22.10 h1:4KMHdfBRYXGF9skjDWiL4RA2N+E8dRdodU/bOZpPoVg= github.com/shirou/gopsutil/v3 v3.22.10/go.mod h1:QNza6r4YQoydyCfo6rH0blGfKahgibh4dQmV5xdFkQk= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= @@ -1118,8 +1140,10 @@ github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLY github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= +github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +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-20200520041808-52d707b772fe/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg= @@ -1206,6 +1230,7 @@ go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1277,6 +1302,7 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1390,6 +1416,7 @@ golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/pkg/cni-plugin/plugin/plugin.go b/pkg/cni-plugin/plugin/plugin.go new file mode 100644 index 000000000..4881eb64d --- /dev/null +++ b/pkg/cni-plugin/plugin/plugin.go @@ -0,0 +1,99 @@ +package plugin + +import ( + "encoding/json" + "fmt" + "runtime" + + "github.com/containernetworking/cni/pkg/skel" + "github.com/containernetworking/cni/pkg/types" + cniversion "github.com/containernetworking/cni/pkg/version" + "github.com/containernetworking/plugins/pkg/ns" + + "yunion.io/x/log" + "yunion.io/x/log/hooks" + "yunion.io/x/pkg/errors" +) + +func init() { + // this ensures that main runs only on main thread (thread group leader). + // since namespace ops (unshare, setns) are done for a single thread, we + // must ensure that the goroutine does not jump from OS thread to thread + runtime.LockOSThread() + + // init log hook + initLog() +} + +func initLog() { + fileHook := &hooks.LogFileRotateHook{ + LogFileHook: hooks.LogFileHook{ + FileDir: "/tmp/ocnet-log", + FileName: "ocnet-cni.log", + }, + RotateNum: 10, + RotateSize: 1024, + } + if err := fileHook.Init(); err != nil { + panic(fmt.Sprintf("fileHook.Init: %v", err)) + } + log.Logger().AddHook(fileHook) + log.DisableColors() +} + +type NetConf struct { + types.NetConf +} + +func loadNetConf(bytes []byte, envArgs string) (*NetConf, string, error) { + n := &NetConf{} + if err := json.Unmarshal(bytes, n); err != nil { + return nil, "", fmt.Errorf("failed to load netconf: %v", err) + } + return n, n.CNIVersion, nil +} + +func cmdAdd(args *skel.CmdArgs) error { + n, cniVersion, err := loadNetConf(args.StdinData, args.Args) + if err != nil { + return errors.Wrap(err, "loadNetConf") + } + log.Infof("===args.Args: %s, n: %#v, cniVersion: %s", args.Args, n, cniVersion) + pod, err := NewCloudPodFromCNIArgs(args.Args) + if err != nil { + return errors.Wrap(err, "NewCloudPodFromCNIArgs") + } + + netns, err := ns.GetNS(args.Netns) + if err != nil { + return errors.Wrapf(err, "failed to open netns %q", args.Netns) + } + defer netns.Close() + + nics := pod.GetDesc().Nics + if len(nics) == 0 { + return fmt.Errorf("Pod %s doesn't have nics", pod.Name) + } + + for idx, nic := range nics { + defaultGw := false + if idx == 0 { + defaultGw = true + } + log.Infof("--nic: %#v, defaultGw: %v", nic, defaultGw) + } + + return nil +} + +func cmdDel(args *skel.CmdArgs) error { + return nil +} + +func cmdCheck(args *skel.CmdArgs) error { + return nil +} + +func Main(version string) { + skel.PluginMain(cmdAdd, cmdCheck, cmdDel, cniversion.All, version) +} diff --git a/pkg/cni-plugin/plugin/types.go b/pkg/cni-plugin/plugin/types.go new file mode 100644 index 000000000..49ad3a7cc --- /dev/null +++ b/pkg/cni-plugin/plugin/types.go @@ -0,0 +1,24 @@ +package plugin + +type PodDesc struct { + Id string `json:"id"` + Name string `json:"name"` + Status string `json:"status"` + Nics []PodNic `json:"nics"` +} + +type PodNic struct { + Index int `json:"index"` + Bridge string `json:"bridge"` + Ifname string `json:"ifname"` + Ip string `json:"ip"` + Mac string `json:"mac"` + Gateway string `json:"gateway"` + Bandwidth int `json:"bw"` + Dns string `json:"dns"` + Mtu int `json:"mtu"` + Masklen int `json:"masklen,omitempty"` + Domain string `json:"domain,omitempty"` + NetId string `json:"net_id"` + WireId string `json:"wire_id"` +} diff --git a/pkg/cni-plugin/plugin/utils.go b/pkg/cni-plugin/plugin/utils.go new file mode 100644 index 000000000..05de60a40 --- /dev/null +++ b/pkg/cni-plugin/plugin/utils.go @@ -0,0 +1,91 @@ +package plugin + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "path/filepath" + "strings" + + "yunion.io/x/pkg/errors" +) + +const ( + K8S_POD_NAMESPACE = "K8S_POD_NAMESPACE" + K8S_POD_NAME = "K8S_POD_NAME" + K8S_POD_INFRA_CONTAINER_ID = "K8S_POD_INFRA_CONTAINER_ID" + K8S_POD_UID = "K8S_POD_UID" +) + +type PodInfo struct { + Id string + Name string + Namespace string + ContainerId string +} + +func NewPodInfoFromCNIArgs(args string) (*PodInfo, error) { + segs := strings.Split(args, ";") + ret := new(PodInfo) + for _, seg := range segs { + kv := strings.Split(seg, "=") + if len(kv) != 2 { + return nil, fmt.Errorf("Invalid args part: %q", seg) + } + key := kv[0] + val := kv[1] + switch key { + case K8S_POD_NAMESPACE: + ret.Namespace = val + case K8S_POD_NAME: + ret.Name = val + case K8S_POD_INFRA_CONTAINER_ID: + ret.ContainerId = val + case K8S_POD_UID: + ret.Id = val + } + } + if ret.Id == "" { + return nil, errors.Errorf("Not found %s from args %s", K8S_POD_UID, args) + } + return ret, nil +} + +func (p PodInfo) GetDescPath() string { + return filepath.Join(GetCloudServerDir(), p.Id, "desc") +} + +type CloudPod struct { + *PodInfo + desc *PodDesc +} + +func GetCloudServerDir() string { + // TODO: make it configurable + return "/opt/cloud/workspace/servers" +} + +func NewCloudPodFromCNIArgs(args string) (*CloudPod, error) { + info, err := NewPodInfoFromCNIArgs(args) + if err != nil { + return nil, errors.Wrap(err, "NewPodInfoFromCNIArgs") + } + descFile := info.GetDescPath() + descData, err := ioutil.ReadFile(descFile) + if err != nil { + return nil, errors.Wrap(err, "read desc file") + } + desc := new(PodDesc) + if err := json.Unmarshal(descData, desc); err != nil { + return nil, errors.Wrap(err, "json.Unmarshal") + } + pod := &CloudPod{ + PodInfo: info, + desc: desc, + } + return pod, nil +} + +func (p CloudPod) GetDesc() *PodDesc { + return p.desc +} diff --git a/pkg/cni-plugin/plugin/utils_test.go b/pkg/cni-plugin/plugin/utils_test.go new file mode 100644 index 000000000..49de962d3 --- /dev/null +++ b/pkg/cni-plugin/plugin/utils_test.go @@ -0,0 +1,42 @@ +package plugin + +import ( + "reflect" + "testing" +) + +func Test_parseCNIArgs(t *testing.T) { + tests := []struct { + args string + want *PodInfo + wantErr bool + }{ + { + args: "IgnoreUnknown=1;K8S_POD_NAMESPACE=27c9464ab54947328a29298761895be3;K8S_POD_NAME=test-pod5;K8S_POD_INFRA_CONTAINER_ID=c73d1df43df96b6804330a257855a5c2c8355d3f84019c57bcc8b5ede14a11ed;K8S_POD_UID=e25e38ef-fe98-4993-8641-699cd0530fc0", + want: &PodInfo{ + Namespace: "27c9464ab54947328a29298761895be3", + Name: "test-pod5", + ContainerId: "c73d1df43df96b6804330a257855a5c2c8355d3f84019c57bcc8b5ede14a11ed", + Id: "e25e38ef-fe98-4993-8641-699cd0530fc0", + }, + wantErr: false, + }, + { + args: "", + want: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.args, func(t *testing.T) { + got, err := NewPodInfoFromCNIArgs(tt.args) + if (err != nil) != tt.wantErr { + t.Errorf("NewPodInfoFromCNIArgs() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewPodInfoFromCNIArgs() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/vendor/github.com/containernetworking/cni/LICENSE b/vendor/github.com/containernetworking/cni/LICENSE new file mode 100644 index 000000000..8f71f43fe --- /dev/null +++ b/vendor/github.com/containernetworking/cni/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/vendor/github.com/containernetworking/cni/pkg/skel/skel.go b/vendor/github.com/containernetworking/cni/pkg/skel/skel.go new file mode 100644 index 000000000..da42db559 --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/skel/skel.go @@ -0,0 +1,296 @@ +// Copyright 2014-2016 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package skel provides skeleton code for a CNI plugin. +// In particular, it implements argument parsing and validation. +package skel + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "strings" + + "github.com/containernetworking/cni/pkg/types" + "github.com/containernetworking/cni/pkg/utils" + "github.com/containernetworking/cni/pkg/version" +) + +// CmdArgs captures all the arguments passed in to the plugin +// via both env vars and stdin +type CmdArgs struct { + ContainerID string + Netns string + IfName string + Args string + Path string + StdinData []byte +} + +type dispatcher struct { + Getenv func(string) string + Stdin io.Reader + Stdout io.Writer + Stderr io.Writer + + ConfVersionDecoder version.ConfigDecoder + VersionReconciler version.Reconciler +} + +type reqForCmdEntry map[string]bool + +func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, *types.Error) { + var cmd, contID, netns, ifName, args, path string + + vars := []struct { + name string + val *string + reqForCmd reqForCmdEntry + }{ + { + "CNI_COMMAND", + &cmd, + reqForCmdEntry{ + "ADD": true, + "CHECK": true, + "DEL": true, + }, + }, + { + "CNI_CONTAINERID", + &contID, + reqForCmdEntry{ + "ADD": true, + "CHECK": true, + "DEL": true, + }, + }, + { + "CNI_NETNS", + &netns, + reqForCmdEntry{ + "ADD": true, + "CHECK": true, + "DEL": false, + }, + }, + { + "CNI_IFNAME", + &ifName, + reqForCmdEntry{ + "ADD": true, + "CHECK": true, + "DEL": true, + }, + }, + { + "CNI_ARGS", + &args, + reqForCmdEntry{ + "ADD": false, + "CHECK": false, + "DEL": false, + }, + }, + { + "CNI_PATH", + &path, + reqForCmdEntry{ + "ADD": true, + "CHECK": true, + "DEL": true, + }, + }, + } + + argsMissing := make([]string, 0) + for _, v := range vars { + *v.val = t.Getenv(v.name) + if *v.val == "" { + if v.reqForCmd[cmd] || v.name == "CNI_COMMAND" { + argsMissing = append(argsMissing, v.name) + } + } + } + + if len(argsMissing) > 0 { + joined := strings.Join(argsMissing, ",") + return "", nil, types.NewError(types.ErrInvalidEnvironmentVariables, fmt.Sprintf("required env variables [%s] missing", joined), "") + } + + if cmd == "VERSION" { + t.Stdin = bytes.NewReader(nil) + } + + stdinData, err := ioutil.ReadAll(t.Stdin) + if err != nil { + return "", nil, types.NewError(types.ErrIOFailure, fmt.Sprintf("error reading from stdin: %v", err), "") + } + + cmdArgs := &CmdArgs{ + ContainerID: contID, + Netns: netns, + IfName: ifName, + Args: args, + Path: path, + StdinData: stdinData, + } + return cmd, cmdArgs, nil +} + +func (t *dispatcher) checkVersionAndCall(cmdArgs *CmdArgs, pluginVersionInfo version.PluginInfo, toCall func(*CmdArgs) error) *types.Error { + configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData) + if err != nil { + return types.NewError(types.ErrDecodingFailure, err.Error(), "") + } + verErr := t.VersionReconciler.Check(configVersion, pluginVersionInfo) + if verErr != nil { + return types.NewError(types.ErrIncompatibleCNIVersion, "incompatible CNI versions", verErr.Details()) + } + + if err = toCall(cmdArgs); err != nil { + if e, ok := err.(*types.Error); ok { + // don't wrap Error in Error + return e + } + return types.NewError(types.ErrInternal, err.Error(), "") + } + + return nil +} + +func validateConfig(jsonBytes []byte) *types.Error { + var conf struct { + Name string `json:"name"` + } + if err := json.Unmarshal(jsonBytes, &conf); err != nil { + return types.NewError(types.ErrDecodingFailure, fmt.Sprintf("error unmarshall network config: %v", err), "") + } + if conf.Name == "" { + return types.NewError(types.ErrInvalidNetworkConfig, "missing network name", "") + } + if err := utils.ValidateNetworkName(conf.Name); err != nil { + return err + } + return nil +} + +func (t *dispatcher) pluginMain(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) *types.Error { + cmd, cmdArgs, err := t.getCmdArgsFromEnv() + if err != nil { + // Print the about string to stderr when no command is set + if err.Code == types.ErrInvalidEnvironmentVariables && t.Getenv("CNI_COMMAND") == "" && about != "" { + _, _ = fmt.Fprintln(t.Stderr, about) + return nil + } + return err + } + + if cmd != "VERSION" { + if err = validateConfig(cmdArgs.StdinData); err != nil { + return err + } + if err = utils.ValidateContainerID(cmdArgs.ContainerID); err != nil { + return err + } + if err = utils.ValidateInterfaceName(cmdArgs.IfName); err != nil { + return err + } + } + + switch cmd { + case "ADD": + err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdAdd) + case "CHECK": + configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData) + if err != nil { + return types.NewError(types.ErrDecodingFailure, err.Error(), "") + } + if gtet, err := version.GreaterThanOrEqualTo(configVersion, "0.4.0"); err != nil { + return types.NewError(types.ErrDecodingFailure, err.Error(), "") + } else if !gtet { + return types.NewError(types.ErrIncompatibleCNIVersion, "config version does not allow CHECK", "") + } + for _, pluginVersion := range versionInfo.SupportedVersions() { + gtet, err := version.GreaterThanOrEqualTo(pluginVersion, configVersion) + if err != nil { + return types.NewError(types.ErrDecodingFailure, err.Error(), "") + } else if gtet { + if err := t.checkVersionAndCall(cmdArgs, versionInfo, cmdCheck); err != nil { + return err + } + return nil + } + } + return types.NewError(types.ErrIncompatibleCNIVersion, "plugin version does not allow CHECK", "") + case "DEL": + err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdDel) + case "VERSION": + if err := versionInfo.Encode(t.Stdout); err != nil { + return types.NewError(types.ErrIOFailure, err.Error(), "") + } + default: + return types.NewError(types.ErrInvalidEnvironmentVariables, fmt.Sprintf("unknown CNI_COMMAND: %v", cmd), "") + } + + if err != nil { + return err + } + return nil +} + +// PluginMainWithError is the core "main" for a plugin. It accepts +// callback functions for add, check, and del CNI commands and returns an error. +// +// The caller must also specify what CNI spec versions the plugin supports. +// +// It is the responsibility of the caller to check for non-nil error return. +// +// For a plugin to comply with the CNI spec, it must print any error to stdout +// as JSON and then exit with nonzero status code. +// +// To let this package automatically handle errors and call os.Exit(1) for you, +// use PluginMain() instead. +func PluginMainWithError(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) *types.Error { + return (&dispatcher{ + Getenv: os.Getenv, + Stdin: os.Stdin, + Stdout: os.Stdout, + Stderr: os.Stderr, + }).pluginMain(cmdAdd, cmdCheck, cmdDel, versionInfo, about) +} + +// PluginMain is the core "main" for a plugin which includes automatic error handling. +// +// The caller must also specify what CNI spec versions the plugin supports. +// +// The caller can specify an "about" string, which is printed on stderr +// when no CNI_COMMAND is specified. The recommended output is "CNI plugin v" +// +// When an error occurs in either cmdAdd, cmdCheck, or cmdDel, PluginMain will print the error +// as JSON to stdout and call os.Exit(1). +// +// To have more control over error handling, use PluginMainWithError() instead. +func PluginMain(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) { + if e := PluginMainWithError(cmdAdd, cmdCheck, cmdDel, versionInfo, about); e != nil { + if err := e.Print(); err != nil { + log.Print("Error writing error JSON to stdout: ", err) + } + os.Exit(1) + } +} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/020/types.go b/vendor/github.com/containernetworking/cni/pkg/types/020/types.go new file mode 100644 index 000000000..99b151ff2 --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/types/020/types.go @@ -0,0 +1,189 @@ +// Copyright 2016 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types020 + +import ( + "encoding/json" + "fmt" + "io" + "net" + "os" + + "github.com/containernetworking/cni/pkg/types" + convert "github.com/containernetworking/cni/pkg/types/internal" +) + +const ImplementedSpecVersion string = "0.2.0" + +var supportedVersions = []string{"", "0.1.0", ImplementedSpecVersion} + +// Register converters for all versions less than the implemented spec version +func init() { + convert.RegisterConverter("0.1.0", []string{ImplementedSpecVersion}, convertFrom010) + convert.RegisterConverter(ImplementedSpecVersion, []string{"0.1.0"}, convertTo010) + + // Creator + convert.RegisterCreator(supportedVersions, NewResult) +} + +// Compatibility types for CNI version 0.1.0 and 0.2.0 + +// NewResult creates a new Result object from JSON data. The JSON data +// must be compatible with the CNI versions implemented by this type. +func NewResult(data []byte) (types.Result, error) { + result := &Result{} + if err := json.Unmarshal(data, result); err != nil { + return nil, err + } + for _, v := range supportedVersions { + if result.CNIVersion == v { + if result.CNIVersion == "" { + result.CNIVersion = "0.1.0" + } + return result, nil + } + } + return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q", + supportedVersions, result.CNIVersion) +} + +// GetResult converts the given Result object to the ImplementedSpecVersion +// and returns the concrete type or an error +func GetResult(r types.Result) (*Result, error) { + result020, err := convert.Convert(r, ImplementedSpecVersion) + if err != nil { + return nil, err + } + result, ok := result020.(*Result) + if !ok { + return nil, fmt.Errorf("failed to convert result") + } + return result, nil +} + +func convertFrom010(from types.Result, toVersion string) (types.Result, error) { + if toVersion != "0.2.0" { + panic("only converts to version 0.2.0") + } + fromResult := from.(*Result) + return &Result{ + CNIVersion: ImplementedSpecVersion, + IP4: fromResult.IP4.Copy(), + IP6: fromResult.IP6.Copy(), + DNS: *fromResult.DNS.Copy(), + }, nil +} + +func convertTo010(from types.Result, toVersion string) (types.Result, error) { + if toVersion != "0.1.0" { + panic("only converts to version 0.1.0") + } + fromResult := from.(*Result) + return &Result{ + CNIVersion: "0.1.0", + IP4: fromResult.IP4.Copy(), + IP6: fromResult.IP6.Copy(), + DNS: *fromResult.DNS.Copy(), + }, nil +} + +// Result is what gets returned from the plugin (via stdout) to the caller +type Result struct { + CNIVersion string `json:"cniVersion,omitempty"` + IP4 *IPConfig `json:"ip4,omitempty"` + IP6 *IPConfig `json:"ip6,omitempty"` + DNS types.DNS `json:"dns,omitempty"` +} + +func (r *Result) Version() string { + return r.CNIVersion +} + +func (r *Result) GetAsVersion(version string) (types.Result, error) { + // If the creator of the result did not set the CNIVersion, assume it + // should be the highest spec version implemented by this Result + if r.CNIVersion == "" { + r.CNIVersion = ImplementedSpecVersion + } + return convert.Convert(r, version) +} + +func (r *Result) Print() error { + return r.PrintTo(os.Stdout) +} + +func (r *Result) PrintTo(writer io.Writer) error { + data, err := json.MarshalIndent(r, "", " ") + if err != nil { + return err + } + _, err = writer.Write(data) + return err +} + +// IPConfig contains values necessary to configure an interface +type IPConfig struct { + IP net.IPNet + Gateway net.IP + Routes []types.Route +} + +func (i *IPConfig) Copy() *IPConfig { + if i == nil { + return nil + } + + var routes []types.Route + for _, fromRoute := range i.Routes { + routes = append(routes, *fromRoute.Copy()) + } + return &IPConfig{ + IP: i.IP, + Gateway: i.Gateway, + Routes: routes, + } +} + +// net.IPNet is not JSON (un)marshallable so this duality is needed +// for our custom IPNet type + +// JSON (un)marshallable types +type ipConfig struct { + IP types.IPNet `json:"ip"` + Gateway net.IP `json:"gateway,omitempty"` + Routes []types.Route `json:"routes,omitempty"` +} + +func (c *IPConfig) MarshalJSON() ([]byte, error) { + ipc := ipConfig{ + IP: types.IPNet(c.IP), + Gateway: c.Gateway, + Routes: c.Routes, + } + + return json.Marshal(ipc) +} + +func (c *IPConfig) UnmarshalJSON(data []byte) error { + ipc := ipConfig{} + if err := json.Unmarshal(data, &ipc); err != nil { + return err + } + + c.IP = net.IPNet(ipc.IP) + c.Gateway = ipc.Gateway + c.Routes = ipc.Routes + return nil +} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/040/types.go b/vendor/github.com/containernetworking/cni/pkg/types/040/types.go new file mode 100644 index 000000000..3633b0eaa --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/types/040/types.go @@ -0,0 +1,306 @@ +// Copyright 2016 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types040 + +import ( + "encoding/json" + "fmt" + "io" + "net" + "os" + + "github.com/containernetworking/cni/pkg/types" + types020 "github.com/containernetworking/cni/pkg/types/020" + convert "github.com/containernetworking/cni/pkg/types/internal" +) + +const ImplementedSpecVersion string = "0.4.0" + +var supportedVersions = []string{"0.3.0", "0.3.1", ImplementedSpecVersion} + +// Register converters for all versions less than the implemented spec version +func init() { + // Up-converters + convert.RegisterConverter("0.1.0", supportedVersions, convertFrom02x) + convert.RegisterConverter("0.2.0", supportedVersions, convertFrom02x) + convert.RegisterConverter("0.3.0", supportedVersions, convertInternal) + convert.RegisterConverter("0.3.1", supportedVersions, convertInternal) + + // Down-converters + convert.RegisterConverter("0.4.0", []string{"0.3.0", "0.3.1"}, convertInternal) + convert.RegisterConverter("0.4.0", []string{"0.1.0", "0.2.0"}, convertTo02x) + convert.RegisterConverter("0.3.1", []string{"0.1.0", "0.2.0"}, convertTo02x) + convert.RegisterConverter("0.3.0", []string{"0.1.0", "0.2.0"}, convertTo02x) + + // Creator + convert.RegisterCreator(supportedVersions, NewResult) +} + +func NewResult(data []byte) (types.Result, error) { + result := &Result{} + if err := json.Unmarshal(data, result); err != nil { + return nil, err + } + for _, v := range supportedVersions { + if result.CNIVersion == v { + return result, nil + } + } + return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q", + supportedVersions, result.CNIVersion) +} + +func GetResult(r types.Result) (*Result, error) { + resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion) + if err != nil { + return nil, err + } + result, ok := resultCurrent.(*Result) + if !ok { + return nil, fmt.Errorf("failed to convert result") + } + return result, nil +} + +func NewResultFromResult(result types.Result) (*Result, error) { + newResult, err := convert.Convert(result, ImplementedSpecVersion) + if err != nil { + return nil, err + } + return newResult.(*Result), nil +} + +// Result is what gets returned from the plugin (via stdout) to the caller +type Result struct { + CNIVersion string `json:"cniVersion,omitempty"` + Interfaces []*Interface `json:"interfaces,omitempty"` + IPs []*IPConfig `json:"ips,omitempty"` + Routes []*types.Route `json:"routes,omitempty"` + DNS types.DNS `json:"dns,omitempty"` +} + +func convert020IPConfig(from *types020.IPConfig, ipVersion string) *IPConfig { + return &IPConfig{ + Version: ipVersion, + Address: from.IP, + Gateway: from.Gateway, + } +} + +func convertFrom02x(from types.Result, toVersion string) (types.Result, error) { + fromResult := from.(*types020.Result) + toResult := &Result{ + CNIVersion: toVersion, + DNS: *fromResult.DNS.Copy(), + Routes: []*types.Route{}, + } + if fromResult.IP4 != nil { + toResult.IPs = append(toResult.IPs, convert020IPConfig(fromResult.IP4, "4")) + for _, fromRoute := range fromResult.IP4.Routes { + toResult.Routes = append(toResult.Routes, fromRoute.Copy()) + } + } + + if fromResult.IP6 != nil { + toResult.IPs = append(toResult.IPs, convert020IPConfig(fromResult.IP6, "6")) + for _, fromRoute := range fromResult.IP6.Routes { + toResult.Routes = append(toResult.Routes, fromRoute.Copy()) + } + } + + return toResult, nil +} + +func convertInternal(from types.Result, toVersion string) (types.Result, error) { + fromResult := from.(*Result) + toResult := &Result{ + CNIVersion: toVersion, + DNS: *fromResult.DNS.Copy(), + Routes: []*types.Route{}, + } + for _, fromIntf := range fromResult.Interfaces { + toResult.Interfaces = append(toResult.Interfaces, fromIntf.Copy()) + } + for _, fromIPC := range fromResult.IPs { + toResult.IPs = append(toResult.IPs, fromIPC.Copy()) + } + for _, fromRoute := range fromResult.Routes { + toResult.Routes = append(toResult.Routes, fromRoute.Copy()) + } + return toResult, nil +} + +func convertTo02x(from types.Result, toVersion string) (types.Result, error) { + fromResult := from.(*Result) + toResult := &types020.Result{ + CNIVersion: toVersion, + DNS: *fromResult.DNS.Copy(), + } + + for _, fromIP := range fromResult.IPs { + // Only convert the first IP address of each version as 0.2.0 + // and earlier cannot handle multiple IP addresses + if fromIP.Version == "4" && toResult.IP4 == nil { + toResult.IP4 = &types020.IPConfig{ + IP: fromIP.Address, + Gateway: fromIP.Gateway, + } + } else if fromIP.Version == "6" && toResult.IP6 == nil { + toResult.IP6 = &types020.IPConfig{ + IP: fromIP.Address, + Gateway: fromIP.Gateway, + } + } + if toResult.IP4 != nil && toResult.IP6 != nil { + break + } + } + + for _, fromRoute := range fromResult.Routes { + is4 := fromRoute.Dst.IP.To4() != nil + if is4 && toResult.IP4 != nil { + toResult.IP4.Routes = append(toResult.IP4.Routes, types.Route{ + Dst: fromRoute.Dst, + GW: fromRoute.GW, + }) + } else if !is4 && toResult.IP6 != nil { + toResult.IP6.Routes = append(toResult.IP6.Routes, types.Route{ + Dst: fromRoute.Dst, + GW: fromRoute.GW, + }) + } + } + + // 0.2.0 and earlier require at least one IP address in the Result + if toResult.IP4 == nil && toResult.IP6 == nil { + return nil, fmt.Errorf("cannot convert: no valid IP addresses") + } + + return toResult, nil +} + +func (r *Result) Version() string { + return r.CNIVersion +} + +func (r *Result) GetAsVersion(version string) (types.Result, error) { + // If the creator of the result did not set the CNIVersion, assume it + // should be the highest spec version implemented by this Result + if r.CNIVersion == "" { + r.CNIVersion = ImplementedSpecVersion + } + return convert.Convert(r, version) +} + +func (r *Result) Print() error { + return r.PrintTo(os.Stdout) +} + +func (r *Result) PrintTo(writer io.Writer) error { + data, err := json.MarshalIndent(r, "", " ") + if err != nil { + return err + } + _, err = writer.Write(data) + return err +} + +// Interface contains values about the created interfaces +type Interface struct { + Name string `json:"name"` + Mac string `json:"mac,omitempty"` + Sandbox string `json:"sandbox,omitempty"` +} + +func (i *Interface) String() string { + return fmt.Sprintf("%+v", *i) +} + +func (i *Interface) Copy() *Interface { + if i == nil { + return nil + } + newIntf := *i + return &newIntf +} + +// Int returns a pointer to the int value passed in. Used to +// set the IPConfig.Interface field. +func Int(v int) *int { + return &v +} + +// IPConfig contains values necessary to configure an IP address on an interface +type IPConfig struct { + // IP version, either "4" or "6" + Version string + // Index into Result structs Interfaces list + Interface *int + Address net.IPNet + Gateway net.IP +} + +func (i *IPConfig) String() string { + return fmt.Sprintf("%+v", *i) +} + +func (i *IPConfig) Copy() *IPConfig { + if i == nil { + return nil + } + + ipc := &IPConfig{ + Version: i.Version, + Address: i.Address, + Gateway: i.Gateway, + } + if i.Interface != nil { + intf := *i.Interface + ipc.Interface = &intf + } + return ipc +} + +// JSON (un)marshallable types +type ipConfig struct { + Version string `json:"version"` + Interface *int `json:"interface,omitempty"` + Address types.IPNet `json:"address"` + Gateway net.IP `json:"gateway,omitempty"` +} + +func (c *IPConfig) MarshalJSON() ([]byte, error) { + ipc := ipConfig{ + Version: c.Version, + Interface: c.Interface, + Address: types.IPNet(c.Address), + Gateway: c.Gateway, + } + + return json.Marshal(ipc) +} + +func (c *IPConfig) UnmarshalJSON(data []byte) error { + ipc := ipConfig{} + if err := json.Unmarshal(data, &ipc); err != nil { + return err + } + + c.Version = ipc.Version + c.Interface = ipc.Interface + c.Address = net.IPNet(ipc.Address) + c.Gateway = ipc.Gateway + return nil +} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/100/types.go b/vendor/github.com/containernetworking/cni/pkg/types/100/types.go new file mode 100644 index 000000000..0e1e8b857 --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/types/100/types.go @@ -0,0 +1,307 @@ +// Copyright 2016 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types100 + +import ( + "encoding/json" + "fmt" + "io" + "net" + "os" + + "github.com/containernetworking/cni/pkg/types" + types040 "github.com/containernetworking/cni/pkg/types/040" + convert "github.com/containernetworking/cni/pkg/types/internal" +) + +const ImplementedSpecVersion string = "1.0.0" + +var supportedVersions = []string{ImplementedSpecVersion} + +// Register converters for all versions less than the implemented spec version +func init() { + // Up-converters + convert.RegisterConverter("0.1.0", supportedVersions, convertFrom02x) + convert.RegisterConverter("0.2.0", supportedVersions, convertFrom02x) + convert.RegisterConverter("0.3.0", supportedVersions, convertFrom04x) + convert.RegisterConverter("0.3.1", supportedVersions, convertFrom04x) + convert.RegisterConverter("0.4.0", supportedVersions, convertFrom04x) + + // Down-converters + convert.RegisterConverter("1.0.0", []string{"0.3.0", "0.3.1", "0.4.0"}, convertTo04x) + convert.RegisterConverter("1.0.0", []string{"0.1.0", "0.2.0"}, convertTo02x) + + // Creator + convert.RegisterCreator(supportedVersions, NewResult) +} + +func NewResult(data []byte) (types.Result, error) { + result := &Result{} + if err := json.Unmarshal(data, result); err != nil { + return nil, err + } + for _, v := range supportedVersions { + if result.CNIVersion == v { + return result, nil + } + } + return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q", + supportedVersions, result.CNIVersion) +} + +func GetResult(r types.Result) (*Result, error) { + resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion) + if err != nil { + return nil, err + } + result, ok := resultCurrent.(*Result) + if !ok { + return nil, fmt.Errorf("failed to convert result") + } + return result, nil +} + +func NewResultFromResult(result types.Result) (*Result, error) { + newResult, err := convert.Convert(result, ImplementedSpecVersion) + if err != nil { + return nil, err + } + return newResult.(*Result), nil +} + +// Result is what gets returned from the plugin (via stdout) to the caller +type Result struct { + CNIVersion string `json:"cniVersion,omitempty"` + Interfaces []*Interface `json:"interfaces,omitempty"` + IPs []*IPConfig `json:"ips,omitempty"` + Routes []*types.Route `json:"routes,omitempty"` + DNS types.DNS `json:"dns,omitempty"` +} + +func convertFrom02x(from types.Result, toVersion string) (types.Result, error) { + result040, err := convert.Convert(from, "0.4.0") + if err != nil { + return nil, err + } + result100, err := convertFrom04x(result040, ImplementedSpecVersion) + if err != nil { + return nil, err + } + return result100, nil +} + +func convertIPConfigFrom040(from *types040.IPConfig) *IPConfig { + to := &IPConfig{ + Address: from.Address, + Gateway: from.Gateway, + } + if from.Interface != nil { + intf := *from.Interface + to.Interface = &intf + } + return to +} + +func convertInterfaceFrom040(from *types040.Interface) *Interface { + return &Interface{ + Name: from.Name, + Mac: from.Mac, + Sandbox: from.Sandbox, + } +} + +func convertFrom04x(from types.Result, toVersion string) (types.Result, error) { + fromResult := from.(*types040.Result) + toResult := &Result{ + CNIVersion: toVersion, + DNS: *fromResult.DNS.Copy(), + Routes: []*types.Route{}, + } + for _, fromIntf := range fromResult.Interfaces { + toResult.Interfaces = append(toResult.Interfaces, convertInterfaceFrom040(fromIntf)) + } + for _, fromIPC := range fromResult.IPs { + toResult.IPs = append(toResult.IPs, convertIPConfigFrom040(fromIPC)) + } + for _, fromRoute := range fromResult.Routes { + toResult.Routes = append(toResult.Routes, fromRoute.Copy()) + } + return toResult, nil +} + +func convertIPConfigTo040(from *IPConfig) *types040.IPConfig { + version := "6" + if from.Address.IP.To4() != nil { + version = "4" + } + to := &types040.IPConfig{ + Version: version, + Address: from.Address, + Gateway: from.Gateway, + } + if from.Interface != nil { + intf := *from.Interface + to.Interface = &intf + } + return to +} + +func convertInterfaceTo040(from *Interface) *types040.Interface { + return &types040.Interface{ + Name: from.Name, + Mac: from.Mac, + Sandbox: from.Sandbox, + } +} + +func convertTo04x(from types.Result, toVersion string) (types.Result, error) { + fromResult := from.(*Result) + toResult := &types040.Result{ + CNIVersion: toVersion, + DNS: *fromResult.DNS.Copy(), + Routes: []*types.Route{}, + } + for _, fromIntf := range fromResult.Interfaces { + toResult.Interfaces = append(toResult.Interfaces, convertInterfaceTo040(fromIntf)) + } + for _, fromIPC := range fromResult.IPs { + toResult.IPs = append(toResult.IPs, convertIPConfigTo040(fromIPC)) + } + for _, fromRoute := range fromResult.Routes { + toResult.Routes = append(toResult.Routes, fromRoute.Copy()) + } + return toResult, nil +} + +func convertTo02x(from types.Result, toVersion string) (types.Result, error) { + // First convert to 0.4.0 + result040, err := convertTo04x(from, "0.4.0") + if err != nil { + return nil, err + } + result02x, err := convert.Convert(result040, toVersion) + if err != nil { + return nil, err + } + return result02x, nil +} + +func (r *Result) Version() string { + return r.CNIVersion +} + +func (r *Result) GetAsVersion(version string) (types.Result, error) { + // If the creator of the result did not set the CNIVersion, assume it + // should be the highest spec version implemented by this Result + if r.CNIVersion == "" { + r.CNIVersion = ImplementedSpecVersion + } + return convert.Convert(r, version) +} + +func (r *Result) Print() error { + return r.PrintTo(os.Stdout) +} + +func (r *Result) PrintTo(writer io.Writer) error { + data, err := json.MarshalIndent(r, "", " ") + if err != nil { + return err + } + _, err = writer.Write(data) + return err +} + +// Interface contains values about the created interfaces +type Interface struct { + Name string `json:"name"` + Mac string `json:"mac,omitempty"` + Sandbox string `json:"sandbox,omitempty"` +} + +func (i *Interface) String() string { + return fmt.Sprintf("%+v", *i) +} + +func (i *Interface) Copy() *Interface { + if i == nil { + return nil + } + newIntf := *i + return &newIntf +} + +// Int returns a pointer to the int value passed in. Used to +// set the IPConfig.Interface field. +func Int(v int) *int { + return &v +} + +// IPConfig contains values necessary to configure an IP address on an interface +type IPConfig struct { + // Index into Result structs Interfaces list + Interface *int + Address net.IPNet + Gateway net.IP +} + +func (i *IPConfig) String() string { + return fmt.Sprintf("%+v", *i) +} + +func (i *IPConfig) Copy() *IPConfig { + if i == nil { + return nil + } + + ipc := &IPConfig{ + Address: i.Address, + Gateway: i.Gateway, + } + if i.Interface != nil { + intf := *i.Interface + ipc.Interface = &intf + } + return ipc +} + +// JSON (un)marshallable types +type ipConfig struct { + Interface *int `json:"interface,omitempty"` + Address types.IPNet `json:"address"` + Gateway net.IP `json:"gateway,omitempty"` +} + +func (c *IPConfig) MarshalJSON() ([]byte, error) { + ipc := ipConfig{ + Interface: c.Interface, + Address: types.IPNet(c.Address), + Gateway: c.Gateway, + } + + return json.Marshal(ipc) +} + +func (c *IPConfig) UnmarshalJSON(data []byte) error { + ipc := ipConfig{} + if err := json.Unmarshal(data, &ipc); err != nil { + return err + } + + c.Interface = ipc.Interface + c.Address = net.IPNet(ipc.Address) + c.Gateway = ipc.Gateway + return nil +} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/args.go b/vendor/github.com/containernetworking/cni/pkg/types/args.go new file mode 100644 index 000000000..7516f03ef --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/types/args.go @@ -0,0 +1,122 @@ +// Copyright 2015 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "encoding" + "fmt" + "reflect" + "strings" +) + +// UnmarshallableBool typedef for builtin bool +// because builtin type's methods can't be declared +type UnmarshallableBool bool + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// Returns boolean true if the string is "1" or "[Tt]rue" +// Returns boolean false if the string is "0" or "[Ff]alse" +func (b *UnmarshallableBool) UnmarshalText(data []byte) error { + s := strings.ToLower(string(data)) + switch s { + case "1", "true": + *b = true + case "0", "false": + *b = false + default: + return fmt.Errorf("boolean unmarshal error: invalid input %s", s) + } + return nil +} + +// UnmarshallableString typedef for builtin string +type UnmarshallableString string + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// Returns the string +func (s *UnmarshallableString) UnmarshalText(data []byte) error { + *s = UnmarshallableString(data) + return nil +} + +// CommonArgs contains the IgnoreUnknown argument +// and must be embedded by all Arg structs +type CommonArgs struct { + IgnoreUnknown UnmarshallableBool `json:"ignoreunknown,omitempty"` +} + +// GetKeyField is a helper function to receive Values +// Values that represent a pointer to a struct +func GetKeyField(keyString string, v reflect.Value) reflect.Value { + return v.Elem().FieldByName(keyString) +} + +// UnmarshalableArgsError is used to indicate error unmarshalling args +// from the args-string in the form "K=V;K2=V2;..." +type UnmarshalableArgsError struct { + error +} + +// LoadArgs parses args from a string in the form "K=V;K2=V2;..." +func LoadArgs(args string, container interface{}) error { + if args == "" { + return nil + } + + containerValue := reflect.ValueOf(container) + + pairs := strings.Split(args, ";") + unknownArgs := []string{} + for _, pair := range pairs { + kv := strings.Split(pair, "=") + if len(kv) != 2 { + return fmt.Errorf("ARGS: invalid pair %q", pair) + } + keyString := kv[0] + valueString := kv[1] + keyField := GetKeyField(keyString, containerValue) + if !keyField.IsValid() { + unknownArgs = append(unknownArgs, pair) + continue + } + + var keyFieldInterface interface{} + switch { + case keyField.Kind() == reflect.Ptr: + keyField.Set(reflect.New(keyField.Type().Elem())) + keyFieldInterface = keyField.Interface() + case keyField.CanAddr() && keyField.Addr().CanInterface(): + keyFieldInterface = keyField.Addr().Interface() + default: + return UnmarshalableArgsError{fmt.Errorf("field '%s' has no valid interface", keyString)} + } + u, ok := keyFieldInterface.(encoding.TextUnmarshaler) + if !ok { + return UnmarshalableArgsError{fmt.Errorf( + "ARGS: cannot unmarshal into field '%s' - type '%s' does not implement encoding.TextUnmarshaler", + keyString, reflect.TypeOf(keyFieldInterface))} + } + err := u.UnmarshalText([]byte(valueString)) + if err != nil { + return fmt.Errorf("ARGS: error parsing value of pair %q: %w", pair, err) + } + } + + isIgnoreUnknown := GetKeyField("IgnoreUnknown", containerValue).Bool() + if len(unknownArgs) > 0 && !isIgnoreUnknown { + return fmt.Errorf("ARGS: unknown args %q", unknownArgs) + } + return nil +} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/create/create.go b/vendor/github.com/containernetworking/cni/pkg/types/create/create.go new file mode 100644 index 000000000..ed28b33e8 --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/types/create/create.go @@ -0,0 +1,56 @@ +// Copyright 2016 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package create + +import ( + "encoding/json" + "fmt" + + "github.com/containernetworking/cni/pkg/types" + convert "github.com/containernetworking/cni/pkg/types/internal" +) + +// DecodeVersion returns the CNI version from CNI configuration or result JSON, +// or an error if the operation could not be performed. +func DecodeVersion(jsonBytes []byte) (string, error) { + var conf struct { + CNIVersion string `json:"cniVersion"` + } + err := json.Unmarshal(jsonBytes, &conf) + if err != nil { + return "", fmt.Errorf("decoding version from network config: %w", err) + } + if conf.CNIVersion == "" { + return "0.1.0", nil + } + return conf.CNIVersion, nil +} + +// Create creates a CNI Result using the given JSON with the expected +// version, or an error if the creation could not be performed +func Create(version string, bytes []byte) (types.Result, error) { + return convert.Create(version, bytes) +} + +// CreateFromBytes creates a CNI Result from the given JSON, automatically +// detecting the CNI spec version of the result. An error is returned if the +// operation could not be performed. +func CreateFromBytes(bytes []byte) (types.Result, error) { + version, err := DecodeVersion(bytes) + if err != nil { + return nil, err + } + return convert.Create(version, bytes) +} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/internal/convert.go b/vendor/github.com/containernetworking/cni/pkg/types/internal/convert.go new file mode 100644 index 000000000..bdbe4b0a5 --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/types/internal/convert.go @@ -0,0 +1,92 @@ +// Copyright 2016 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package convert + +import ( + "fmt" + + "github.com/containernetworking/cni/pkg/types" +) + +// ConvertFn should convert from the given arbitrary Result type into a +// Result implementing CNI specification version passed in toVersion. +// The function is guaranteed to be passed a Result type matching the +// fromVersion it was registered with, and is guaranteed to be +// passed a toVersion matching one of the toVersions it was registered with. +type ConvertFn func(from types.Result, toVersion string) (types.Result, error) + +type converter struct { + // fromVersion is the CNI Result spec version that convertFn accepts + fromVersion string + // toVersions is a list of versions that convertFn can convert to + toVersions []string + convertFn ConvertFn +} + +var converters []*converter + +func findConverter(fromVersion, toVersion string) *converter { + for _, c := range converters { + if c.fromVersion == fromVersion { + for _, v := range c.toVersions { + if v == toVersion { + return c + } + } + } + } + return nil +} + +// Convert converts a CNI Result to the requested CNI specification version, +// or returns an error if the conversion could not be performed or failed +func Convert(from types.Result, toVersion string) (types.Result, error) { + if toVersion == "" { + toVersion = "0.1.0" + } + + fromVersion := from.Version() + + // Shortcut for same version + if fromVersion == toVersion { + return from, nil + } + + // Otherwise find the right converter + c := findConverter(fromVersion, toVersion) + if c == nil { + return nil, fmt.Errorf("no converter for CNI result version %s to %s", + fromVersion, toVersion) + } + return c.convertFn(from, toVersion) +} + +// RegisterConverter registers a CNI Result converter. SHOULD NOT BE CALLED +// EXCEPT FROM CNI ITSELF. +func RegisterConverter(fromVersion string, toVersions []string, convertFn ConvertFn) { + // Make sure there is no converter already registered for these + // from and to versions + for _, v := range toVersions { + if findConverter(fromVersion, v) != nil { + panic(fmt.Sprintf("converter already registered for %s to %s", + fromVersion, v)) + } + } + converters = append(converters, &converter{ + fromVersion: fromVersion, + toVersions: toVersions, + convertFn: convertFn, + }) +} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/internal/create.go b/vendor/github.com/containernetworking/cni/pkg/types/internal/create.go new file mode 100644 index 000000000..963630912 --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/types/internal/create.go @@ -0,0 +1,66 @@ +// Copyright 2016 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package convert + +import ( + "fmt" + + "github.com/containernetworking/cni/pkg/types" +) + +type ResultFactoryFunc func([]byte) (types.Result, error) + +type creator struct { + // CNI Result spec versions that createFn can create a Result for + versions []string + createFn ResultFactoryFunc +} + +var creators []*creator + +func findCreator(version string) *creator { + for _, c := range creators { + for _, v := range c.versions { + if v == version { + return c + } + } + } + return nil +} + +// Create creates a CNI Result using the given JSON, or an error if the creation +// could not be performed +func Create(version string, bytes []byte) (types.Result, error) { + if c := findCreator(version); c != nil { + return c.createFn(bytes) + } + return nil, fmt.Errorf("unsupported CNI result version %q", version) +} + +// RegisterCreator registers a CNI Result creator. SHOULD NOT BE CALLED +// EXCEPT FROM CNI ITSELF. +func RegisterCreator(versions []string, createFn ResultFactoryFunc) { + // Make sure there is no creator already registered for these versions + for _, v := range versions { + if findCreator(v) != nil { + panic(fmt.Sprintf("creator already registered for %s", v)) + } + } + creators = append(creators, &creator{ + versions: versions, + createFn: createFn, + }) +} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/types.go b/vendor/github.com/containernetworking/cni/pkg/types/types.go new file mode 100644 index 000000000..fba17dfc0 --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/types/types.go @@ -0,0 +1,234 @@ +// Copyright 2015 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "encoding/json" + "fmt" + "io" + "net" + "os" +) + +// like net.IPNet but adds JSON marshalling and unmarshalling +type IPNet net.IPNet + +// ParseCIDR takes a string like "10.2.3.1/24" and +// return IPNet with "10.2.3.1" and /24 mask +func ParseCIDR(s string) (*net.IPNet, error) { + ip, ipn, err := net.ParseCIDR(s) + if err != nil { + return nil, err + } + + ipn.IP = ip + return ipn, nil +} + +func (n IPNet) MarshalJSON() ([]byte, error) { + return json.Marshal((*net.IPNet)(&n).String()) +} + +func (n *IPNet) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + + tmp, err := ParseCIDR(s) + if err != nil { + return err + } + + *n = IPNet(*tmp) + return nil +} + +// NetConf describes a network. +type NetConf struct { + CNIVersion string `json:"cniVersion,omitempty"` + + Name string `json:"name,omitempty"` + Type string `json:"type,omitempty"` + Capabilities map[string]bool `json:"capabilities,omitempty"` + IPAM IPAM `json:"ipam,omitempty"` + DNS DNS `json:"dns"` + + RawPrevResult map[string]interface{} `json:"prevResult,omitempty"` + PrevResult Result `json:"-"` +} + +type IPAM struct { + Type string `json:"type,omitempty"` +} + +// NetConfList describes an ordered list of networks. +type NetConfList struct { + CNIVersion string `json:"cniVersion,omitempty"` + + Name string `json:"name,omitempty"` + DisableCheck bool `json:"disableCheck,omitempty"` + Plugins []*NetConf `json:"plugins,omitempty"` +} + +// Result is an interface that provides the result of plugin execution +type Result interface { + // The highest CNI specification result version the result supports + // without having to convert + Version() string + + // Returns the result converted into the requested CNI specification + // result version, or an error if conversion failed + GetAsVersion(version string) (Result, error) + + // Prints the result in JSON format to stdout + Print() error + + // Prints the result in JSON format to provided writer + PrintTo(writer io.Writer) error +} + +func PrintResult(result Result, version string) error { + newResult, err := result.GetAsVersion(version) + if err != nil { + return err + } + return newResult.Print() +} + +// DNS contains values interesting for DNS resolvers +type DNS struct { + Nameservers []string `json:"nameservers,omitempty"` + Domain string `json:"domain,omitempty"` + Search []string `json:"search,omitempty"` + Options []string `json:"options,omitempty"` +} + +func (d *DNS) Copy() *DNS { + if d == nil { + return nil + } + + to := &DNS{Domain: d.Domain} + for _, ns := range d.Nameservers { + to.Nameservers = append(to.Nameservers, ns) + } + for _, s := range d.Search { + to.Search = append(to.Search, s) + } + for _, o := range d.Options { + to.Options = append(to.Options, o) + } + return to +} + +type Route struct { + Dst net.IPNet + GW net.IP +} + +func (r *Route) String() string { + return fmt.Sprintf("%+v", *r) +} + +func (r *Route) Copy() *Route { + if r == nil { + return nil + } + + return &Route{ + Dst: r.Dst, + GW: r.GW, + } +} + +// Well known error codes +// see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes +const ( + ErrUnknown uint = iota // 0 + ErrIncompatibleCNIVersion // 1 + ErrUnsupportedField // 2 + ErrUnknownContainer // 3 + ErrInvalidEnvironmentVariables // 4 + ErrIOFailure // 5 + ErrDecodingFailure // 6 + ErrInvalidNetworkConfig // 7 + ErrTryAgainLater uint = 11 + ErrInternal uint = 999 +) + +type Error struct { + Code uint `json:"code"` + Msg string `json:"msg"` + Details string `json:"details,omitempty"` +} + +func NewError(code uint, msg, details string) *Error { + return &Error{ + Code: code, + Msg: msg, + Details: details, + } +} + +func (e *Error) Error() string { + details := "" + if e.Details != "" { + details = fmt.Sprintf("; %v", e.Details) + } + return fmt.Sprintf("%v%v", e.Msg, details) +} + +func (e *Error) Print() error { + return prettyPrint(e) +} + +// net.IPNet is not JSON (un)marshallable so this duality is needed +// for our custom IPNet type + +// JSON (un)marshallable types +type route struct { + Dst IPNet `json:"dst"` + GW net.IP `json:"gw,omitempty"` +} + +func (r *Route) UnmarshalJSON(data []byte) error { + rt := route{} + if err := json.Unmarshal(data, &rt); err != nil { + return err + } + + r.Dst = net.IPNet(rt.Dst) + r.GW = rt.GW + return nil +} + +func (r Route) MarshalJSON() ([]byte, error) { + rt := route{ + Dst: IPNet(r.Dst), + GW: r.GW, + } + + return json.Marshal(rt) +} + +func prettyPrint(obj interface{}) error { + data, err := json.MarshalIndent(obj, "", " ") + if err != nil { + return err + } + _, err = os.Stdout.Write(data) + return err +} diff --git a/vendor/github.com/containernetworking/cni/pkg/utils/utils.go b/vendor/github.com/containernetworking/cni/pkg/utils/utils.go new file mode 100644 index 000000000..b8ec38874 --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/utils/utils.go @@ -0,0 +1,84 @@ +// Copyright 2019 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "bytes" + "fmt" + "regexp" + "unicode" + + "github.com/containernetworking/cni/pkg/types" +) + +const ( + // cniValidNameChars is the regexp used to validate valid characters in + // containerID and networkName + cniValidNameChars = `[a-zA-Z0-9][a-zA-Z0-9_.\-]` + + // maxInterfaceNameLength is the length max of a valid interface name + maxInterfaceNameLength = 15 +) + +var cniReg = regexp.MustCompile(`^` + cniValidNameChars + `*$`) + +// ValidateContainerID will validate that the supplied containerID is not empty does not contain invalid characters +func ValidateContainerID(containerID string) *types.Error { + + if containerID == "" { + return types.NewError(types.ErrUnknownContainer, "missing containerID", "") + } + if !cniReg.MatchString(containerID) { + return types.NewError(types.ErrInvalidEnvironmentVariables, "invalid characters in containerID", containerID) + } + return nil +} + +// ValidateNetworkName will validate that the supplied networkName does not contain invalid characters +func ValidateNetworkName(networkName string) *types.Error { + + if networkName == "" { + return types.NewError(types.ErrInvalidNetworkConfig, "missing network name:", "") + } + if !cniReg.MatchString(networkName) { + return types.NewError(types.ErrInvalidNetworkConfig, "invalid characters found in network name", networkName) + } + return nil +} + +// ValidateInterfaceName will validate the interface name based on the three rules below +// 1. The name must not be empty +// 2. The name must be less than 16 characters +// 3. The name must not be "." or ".." +// 3. The name must not contain / or : or any whitespace characters +// ref to https://github.com/torvalds/linux/blob/master/net/core/dev.c#L1024 +func ValidateInterfaceName(ifName string) *types.Error { + if len(ifName) == 0 { + return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is empty", "") + } + if len(ifName) > maxInterfaceNameLength { + return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is too long", fmt.Sprintf("interface name should be less than %d characters", maxInterfaceNameLength+1)) + } + if ifName == "." || ifName == ".." { + return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is . or ..", "") + } + for _, r := range bytes.Runes([]byte(ifName)) { + if r == '/' || r == ':' || unicode.IsSpace(r) { + return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name contains / or : or whitespace characters", "") + } + } + + return nil +} diff --git a/vendor/github.com/containernetworking/cni/pkg/version/conf.go b/vendor/github.com/containernetworking/cni/pkg/version/conf.go new file mode 100644 index 000000000..808c33b83 --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/version/conf.go @@ -0,0 +1,26 @@ +// Copyright 2016 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package version + +import ( + "github.com/containernetworking/cni/pkg/types/create" +) + +// ConfigDecoder can decode the CNI version available in network config data +type ConfigDecoder struct{} + +func (*ConfigDecoder) Decode(jsonBytes []byte) (string, error) { + return create.DecodeVersion(jsonBytes) +} diff --git a/vendor/github.com/containernetworking/cni/pkg/version/plugin.go b/vendor/github.com/containernetworking/cni/pkg/version/plugin.go new file mode 100644 index 000000000..d4bc9d169 --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/version/plugin.go @@ -0,0 +1,144 @@ +// Copyright 2016 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package version + +import ( + "encoding/json" + "fmt" + "io" + "strconv" + "strings" +) + +// PluginInfo reports information about CNI versioning +type PluginInfo interface { + // SupportedVersions returns one or more CNI spec versions that the plugin + // supports. If input is provided in one of these versions, then the plugin + // promises to use the same CNI version in its response + SupportedVersions() []string + + // Encode writes this CNI version information as JSON to the given Writer + Encode(io.Writer) error +} + +type pluginInfo struct { + CNIVersion_ string `json:"cniVersion"` + SupportedVersions_ []string `json:"supportedVersions,omitempty"` +} + +// pluginInfo implements the PluginInfo interface +var _ PluginInfo = &pluginInfo{} + +func (p *pluginInfo) Encode(w io.Writer) error { + return json.NewEncoder(w).Encode(p) +} + +func (p *pluginInfo) SupportedVersions() []string { + return p.SupportedVersions_ +} + +// PluginSupports returns a new PluginInfo that will report the given versions +// as supported +func PluginSupports(supportedVersions ...string) PluginInfo { + if len(supportedVersions) < 1 { + panic("programmer error: you must support at least one version") + } + return &pluginInfo{ + CNIVersion_: Current(), + SupportedVersions_: supportedVersions, + } +} + +// PluginDecoder can decode the response returned by a plugin's VERSION command +type PluginDecoder struct{} + +func (*PluginDecoder) Decode(jsonBytes []byte) (PluginInfo, error) { + var info pluginInfo + err := json.Unmarshal(jsonBytes, &info) + if err != nil { + return nil, fmt.Errorf("decoding version info: %w", err) + } + if info.CNIVersion_ == "" { + return nil, fmt.Errorf("decoding version info: missing field cniVersion") + } + if len(info.SupportedVersions_) == 0 { + if info.CNIVersion_ == "0.2.0" { + return PluginSupports("0.1.0", "0.2.0"), nil + } + return nil, fmt.Errorf("decoding version info: missing field supportedVersions") + } + return &info, nil +} + +// ParseVersion parses a version string like "3.0.1" or "0.4.5" into major, +// minor, and micro numbers or returns an error +func ParseVersion(version string) (int, int, int, error) { + var major, minor, micro int + if version == "" { + return -1, -1, -1, fmt.Errorf("invalid version %q: the version is empty", version) + } + + parts := strings.Split(version, ".") + if len(parts) >= 4 { + return -1, -1, -1, fmt.Errorf("invalid version %q: too many parts", version) + } + + major, err := strconv.Atoi(parts[0]) + if err != nil { + return -1, -1, -1, fmt.Errorf("failed to convert major version part %q: %w", parts[0], err) + } + + if len(parts) >= 2 { + minor, err = strconv.Atoi(parts[1]) + if err != nil { + return -1, -1, -1, fmt.Errorf("failed to convert minor version part %q: %w", parts[1], err) + } + } + + if len(parts) >= 3 { + micro, err = strconv.Atoi(parts[2]) + if err != nil { + return -1, -1, -1, fmt.Errorf("failed to convert micro version part %q: %w", parts[2], err) + } + } + + return major, minor, micro, nil +} + +// GreaterThanOrEqualTo takes two string versions, parses them into major/minor/micro +// numbers, and compares them to determine whether the first version is greater +// than or equal to the second +func GreaterThanOrEqualTo(version, otherVersion string) (bool, error) { + firstMajor, firstMinor, firstMicro, err := ParseVersion(version) + if err != nil { + return false, err + } + + secondMajor, secondMinor, secondMicro, err := ParseVersion(otherVersion) + if err != nil { + return false, err + } + + if firstMajor > secondMajor { + return true, nil + } else if firstMajor == secondMajor { + if firstMinor > secondMinor { + return true, nil + } else if firstMinor == secondMinor && firstMicro >= secondMicro { + return true, nil + } + } + return false, nil +} diff --git a/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go b/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go new file mode 100644 index 000000000..25c3810b2 --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go @@ -0,0 +1,49 @@ +// Copyright 2016 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package version + +import "fmt" + +type ErrorIncompatible struct { + Config string + Supported []string +} + +func (e *ErrorIncompatible) Details() string { + return fmt.Sprintf("config is %q, plugin supports %q", e.Config, e.Supported) +} + +func (e *ErrorIncompatible) Error() string { + return fmt.Sprintf("incompatible CNI versions: %s", e.Details()) +} + +type Reconciler struct{} + +func (r *Reconciler) Check(configVersion string, pluginInfo PluginInfo) *ErrorIncompatible { + return r.CheckRaw(configVersion, pluginInfo.SupportedVersions()) +} + +func (*Reconciler) CheckRaw(configVersion string, supportedVersions []string) *ErrorIncompatible { + for _, supportedVersion := range supportedVersions { + if configVersion == supportedVersion { + return nil + } + } + + return &ErrorIncompatible{ + Config: configVersion, + Supported: supportedVersions, + } +} diff --git a/vendor/github.com/containernetworking/cni/pkg/version/version.go b/vendor/github.com/containernetworking/cni/pkg/version/version.go new file mode 100644 index 000000000..709a1fb3d --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/version/version.go @@ -0,0 +1,73 @@ +// Copyright 2016 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package version + +import ( + "encoding/json" + "fmt" + + "github.com/containernetworking/cni/pkg/types" + "github.com/containernetworking/cni/pkg/types/100" + "github.com/containernetworking/cni/pkg/types/create" +) + +// Current reports the version of the CNI spec implemented by this library +func Current() string { + return types100.ImplementedSpecVersion +} + +// Legacy PluginInfo describes a plugin that is backwards compatible with the +// CNI spec version 0.1.0. In particular, a runtime compiled against the 0.1.0 +// library ought to work correctly with a plugin that reports support for +// Legacy versions. +// +// Any future CNI spec versions which meet this definition should be added to +// this list. +var Legacy = PluginSupports("0.1.0", "0.2.0") +var All = PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0", "1.0.0") + +// Finds a Result object matching the requested version (if any) and asks +// that object to parse the plugin result, returning an error if parsing failed. +func NewResult(version string, resultBytes []byte) (types.Result, error) { + return create.Create(version, resultBytes) +} + +// ParsePrevResult parses a prevResult in a NetConf structure and sets +// the NetConf's PrevResult member to the parsed Result object. +func ParsePrevResult(conf *types.NetConf) error { + if conf.RawPrevResult == nil { + return nil + } + + // Prior to 1.0.0, Result types may not marshal a CNIVersion. Since the + // result version must match the config version, if the Result's version + // is empty, inject the config version. + if ver, ok := conf.RawPrevResult["CNIVersion"]; !ok || ver == "" { + conf.RawPrevResult["CNIVersion"] = conf.CNIVersion + } + + resultBytes, err := json.Marshal(conf.RawPrevResult) + if err != nil { + return fmt.Errorf("could not serialize prevResult: %w", err) + } + + conf.RawPrevResult = nil + conf.PrevResult, err = create.Create(conf.CNIVersion, resultBytes) + if err != nil { + return fmt.Errorf("could not parse prevResult: %w", err) + } + + return nil +} diff --git a/vendor/github.com/containernetworking/plugins/LICENSE b/vendor/github.com/containernetworking/plugins/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/vendor/github.com/containernetworking/plugins/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/containernetworking/plugins/pkg/ns/README.md b/vendor/github.com/containernetworking/plugins/pkg/ns/README.md new file mode 100644 index 000000000..1e265c7a0 --- /dev/null +++ b/vendor/github.com/containernetworking/plugins/pkg/ns/README.md @@ -0,0 +1,41 @@ +### Namespaces, Threads, and Go +On Linux each OS thread can have a different network namespace. Go's thread scheduling model switches goroutines between OS threads based on OS thread load and whether the goroutine would block other goroutines. This can result in a goroutine switching network namespaces without notice and lead to errors in your code. + +### Namespace Switching +Switching namespaces with the `ns.Set()` method is not recommended without additional strategies to prevent unexpected namespace changes when your goroutines switch OS threads. + +Go provides the `runtime.LockOSThread()` function to ensure a specific goroutine executes on its current OS thread and prevents any other goroutine from running in that thread until the locked one exits. Careful usage of `LockOSThread()` and goroutines can provide good control over which network namespace a given goroutine executes in. + +For example, you cannot rely on the `ns.Set()` namespace being the current namespace after the `Set()` call unless you do two things. First, the goroutine calling `Set()` must have previously called `LockOSThread()`. Second, you must ensure `runtime.UnlockOSThread()` is not called somewhere in-between. You also cannot rely on the initial network namespace remaining the current network namespace if any other code in your program switches namespaces, unless you have already called `LockOSThread()` in that goroutine. Note that `LockOSThread()` prevents the Go scheduler from optimally scheduling goroutines for best performance, so `LockOSThread()` should only be used in small, isolated goroutines that release the lock quickly. + +### Do() The Recommended Thing +The `ns.Do()` method provides **partial** control over network namespaces for you by implementing these strategies. All code dependent on a particular network namespace (including the root namespace) should be wrapped in the `ns.Do()` method to ensure the correct namespace is selected for the duration of your code. For example: + +```go +err = targetNs.Do(func(hostNs ns.NetNS) error { + dummy := &netlink.Dummy{ + LinkAttrs: netlink.LinkAttrs{ + Name: "dummy0", + }, + } + return netlink.LinkAdd(dummy) +}) +``` + +Note this requirement to wrap every network call is very onerous - any libraries you call might call out to network services such as DNS, and all such calls need to be protected after you call `ns.Do()`. All goroutines spawned from within the `ns.Do` will not inherit the new namespace. The CNI plugins all exit very soon after calling `ns.Do()` which helps to minimize the problem. + +When a new thread is spawned in Linux, it inherits the namespace of its parent. In versions of go **prior to 1.10**, if the runtime spawns a new OS thread, it picks the parent randomly. If the chosen parent thread has been moved to a new namespace (even temporarily), the new OS thread will be permanently "stuck in the wrong namespace", and goroutines will non-deterministically switch namespaces as they are rescheduled. + +In short, **there was no safe way to change network namespaces, even temporarily, from within a long-lived, multithreaded Go process**. If you wish to do this, you must use go 1.10 or greater. + + +### Creating network namespaces +Earlier versions of this library managed namespace creation, but as CNI does not actually utilize this feature (and it was essentially unmaintained), it was removed. If you're writing a container runtime, you should implement namespace management yourself. However, there are some gotchas when doing so, especially around handling `/var/run/netns`. A reasonably correct reference implementation, borrowed from `rkt`, can be found in `pkg/testutils/netns_linux.go` if you're in need of a source of inspiration. + + +### Further Reading + - https://github.com/golang/go/wiki/LockOSThread + - http://morsmachine.dk/go-scheduler + - https://github.com/containernetworking/cni/issues/262 + - https://golang.org/pkg/runtime/ + - https://www.weave.works/blog/linux-namespaces-and-go-don-t-mix diff --git a/vendor/github.com/containernetworking/plugins/pkg/ns/ns_linux.go b/vendor/github.com/containernetworking/plugins/pkg/ns/ns_linux.go new file mode 100644 index 000000000..3b745d491 --- /dev/null +++ b/vendor/github.com/containernetworking/plugins/pkg/ns/ns_linux.go @@ -0,0 +1,234 @@ +// Copyright 2015-2017 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ns + +import ( + "fmt" + "os" + "runtime" + "sync" + "syscall" + + "golang.org/x/sys/unix" +) + +// Returns an object representing the current OS thread's network namespace +func GetCurrentNS() (NetNS, error) { + // Lock the thread in case other goroutine executes in it and changes its + // network namespace after getCurrentThreadNetNSPath(), otherwise it might + // return an unexpected network namespace. + runtime.LockOSThread() + defer runtime.UnlockOSThread() + return GetNS(getCurrentThreadNetNSPath()) +} + +func getCurrentThreadNetNSPath() string { + // /proc/self/ns/net returns the namespace of the main thread, not + // of whatever thread this goroutine is running on. Make sure we + // use the thread's net namespace since the thread is switching around + return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid()) +} + +func (ns *netNS) Close() error { + if err := ns.errorIfClosed(); err != nil { + return err + } + + if err := ns.file.Close(); err != nil { + return fmt.Errorf("Failed to close %q: %v", ns.file.Name(), err) + } + ns.closed = true + + return nil +} + +func (ns *netNS) Set() error { + if err := ns.errorIfClosed(); err != nil { + return err + } + + if err := unix.Setns(int(ns.Fd()), unix.CLONE_NEWNET); err != nil { + return fmt.Errorf("Error switching to ns %v: %v", ns.file.Name(), err) + } + + return nil +} + +type NetNS interface { + // Executes the passed closure in this object's network namespace, + // attempting to restore the original namespace before returning. + // However, since each OS thread can have a different network namespace, + // and Go's thread scheduling is highly variable, callers cannot + // guarantee any specific namespace is set unless operations that + // require that namespace are wrapped with Do(). Also, no code called + // from Do() should call runtime.UnlockOSThread(), or the risk + // of executing code in an incorrect namespace will be greater. See + // https://github.com/golang/go/wiki/LockOSThread for further details. + Do(toRun func(NetNS) error) error + + // Sets the current network namespace to this object's network namespace. + // Note that since Go's thread scheduling is highly variable, callers + // cannot guarantee the requested namespace will be the current namespace + // after this function is called; to ensure this wrap operations that + // require the namespace with Do() instead. + Set() error + + // Returns the filesystem path representing this object's network namespace + Path() string + + // Returns a file descriptor representing this object's network namespace + Fd() uintptr + + // Cleans up this instance of the network namespace; if this instance + // is the last user the namespace will be destroyed + Close() error +} + +type netNS struct { + file *os.File + closed bool +} + +// netNS implements the NetNS interface +var _ NetNS = &netNS{} + +const ( + // https://github.com/torvalds/linux/blob/master/include/uapi/linux/magic.h + NSFS_MAGIC = 0x6e736673 + PROCFS_MAGIC = 0x9fa0 +) + +type NSPathNotExistErr struct{ msg string } + +func (e NSPathNotExistErr) Error() string { return e.msg } + +type NSPathNotNSErr struct{ msg string } + +func (e NSPathNotNSErr) Error() string { return e.msg } + +func IsNSorErr(nspath string) error { + stat := syscall.Statfs_t{} + if err := syscall.Statfs(nspath, &stat); err != nil { + if os.IsNotExist(err) { + err = NSPathNotExistErr{msg: fmt.Sprintf("failed to Statfs %q: %v", nspath, err)} + } else { + err = fmt.Errorf("failed to Statfs %q: %v", nspath, err) + } + return err + } + + switch stat.Type { + case PROCFS_MAGIC, NSFS_MAGIC: + return nil + default: + return NSPathNotNSErr{msg: fmt.Sprintf("unknown FS magic on %q: %x", nspath, stat.Type)} + } +} + +// Returns an object representing the namespace referred to by @path +func GetNS(nspath string) (NetNS, error) { + err := IsNSorErr(nspath) + if err != nil { + return nil, err + } + + fd, err := os.Open(nspath) + if err != nil { + return nil, err + } + + return &netNS{file: fd}, nil +} + +func (ns *netNS) Path() string { + return ns.file.Name() +} + +func (ns *netNS) Fd() uintptr { + return ns.file.Fd() +} + +func (ns *netNS) errorIfClosed() error { + if ns.closed { + return fmt.Errorf("%q has already been closed", ns.file.Name()) + } + return nil +} + +func (ns *netNS) Do(toRun func(NetNS) error) error { + if err := ns.errorIfClosed(); err != nil { + return err + } + + containedCall := func(hostNS NetNS) error { + threadNS, err := GetCurrentNS() + if err != nil { + return fmt.Errorf("failed to open current netns: %v", err) + } + defer threadNS.Close() + + // switch to target namespace + if err = ns.Set(); err != nil { + return fmt.Errorf("error switching to ns %v: %v", ns.file.Name(), err) + } + defer func() { + err := threadNS.Set() // switch back + if err == nil { + // Unlock the current thread only when we successfully switched back + // to the original namespace; otherwise leave the thread locked which + // will force the runtime to scrap the current thread, that is maybe + // not as optimal but at least always safe to do. + runtime.UnlockOSThread() + } + }() + + return toRun(hostNS) + } + + // save a handle to current network namespace + hostNS, err := GetCurrentNS() + if err != nil { + return fmt.Errorf("Failed to open current namespace: %v", err) + } + defer hostNS.Close() + + var wg sync.WaitGroup + wg.Add(1) + + // Start the callback in a new green thread so that if we later fail + // to switch the namespace back to the original one, we can safely + // leave the thread locked to die without a risk of the current thread + // left lingering with incorrect namespace. + var innerError error + go func() { + defer wg.Done() + runtime.LockOSThread() + innerError = containedCall(hostNS) + }() + wg.Wait() + + return innerError +} + +// WithNetNSPath executes the passed closure under the given network +// namespace, restoring the original namespace afterwards. +func WithNetNSPath(nspath string, toRun func(NetNS) error) error { + ns, err := GetNS(nspath) + if err != nil { + return err + } + defer ns.Close() + return ns.Do(toRun) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 6de5b30bf..0ae8d3f98 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -137,6 +137,20 @@ github.com/containerd/containerd/version # github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41 ## explicit; go 1.11 github.com/containerd/continuity/pathdriver +# github.com/containernetworking/cni v1.0.0 +## explicit; go 1.14 +github.com/containernetworking/cni/pkg/skel +github.com/containernetworking/cni/pkg/types +github.com/containernetworking/cni/pkg/types/020 +github.com/containernetworking/cni/pkg/types/040 +github.com/containernetworking/cni/pkg/types/100 +github.com/containernetworking/cni/pkg/types/create +github.com/containernetworking/cni/pkg/types/internal +github.com/containernetworking/cni/pkg/utils +github.com/containernetworking/cni/pkg/version +# github.com/containernetworking/plugins v0.8.7 +## explicit; go 1.14 +github.com/containernetworking/plugins/pkg/ns # github.com/coreos/go-semver v0.3.0 ## explicit github.com/coreos/go-semver/semver From 974a47604b8a02cd9530c8d5db8ba6824916362f Mon Sep 17 00:00:00 2001 From: Zexi Li Date: Wed, 7 Feb 2024 17:36:46 +0800 Subject: [PATCH 2/3] cni: sdnagent --- Makefile | 1 + go.mod | 15 +- go.sum | 25 +- pkg/cni-plugin/plugin/ovs.go | 49 + pkg/cni-plugin/plugin/plugin.go | 17 +- pkg/cni-plugin/plugin/types.go | 11 + pkg/cni-plugin/plugin/utils.go | 258 +++++ .../digitalocean/go-openvswitch/AUTHORS | 15 + .../digitalocean/go-openvswitch/LICENSE.md | 195 ++++ .../digitalocean/go-openvswitch/ovs/README.md | 31 + .../digitalocean/go-openvswitch/ovs/action.go | 603 ++++++++++ .../go-openvswitch/ovs/actionparser.go | 393 +++++++ .../digitalocean/go-openvswitch/ovs/client.go | 348 ++++++ .../go-openvswitch/ovs/codegen.go | 62 + .../digitalocean/go-openvswitch/ovs/doc.go | 17 + .../digitalocean/go-openvswitch/ovs/flow.go | 389 +++++++ .../go-openvswitch/ovs/flowstats.go | 96 ++ .../digitalocean/go-openvswitch/ovs/match.go | 1026 +++++++++++++++++ .../go-openvswitch/ovs/matchflow.go | 169 +++ .../go-openvswitch/ovs/matchparser.go | 414 +++++++ .../go-openvswitch/ovs/openflow.go | 478 ++++++++ .../digitalocean/go-openvswitch/ovs/ovs.go | 89 ++ .../go-openvswitch/ovs/portrange.go | 130 +++ .../go-openvswitch/ovs/portstats.go | 178 +++ .../digitalocean/go-openvswitch/ovs/table.go | 99 ++ .../go-openvswitch/ovs/vswitch.go | 311 +++++ .../github.com/fsnotify/fsnotify/CHANGELOG.md | 20 +- .../fsnotify/fsnotify/CONTRIBUTING.md | 17 - vendor/github.com/fsnotify/fsnotify/README.md | 24 +- .../fsnotify/fsnotify/fsnotify_unsupported.go | 36 + .../github.com/fsnotify/fsnotify/inotify.go | 13 + .../fsnotify/fsnotify/inotify_poller.go | 1 - vendor/github.com/fsnotify/fsnotify/kqueue.go | 13 + .../github.com/fsnotify/fsnotify/windows.go | 28 +- vendor/modules.txt | 20 +- vendor/yunion.io/x/log/log.go | 3 + .../x/onecloud/pkg/apis/compute/storage.go | 1 + .../x/onecloud/pkg/apis/identity/consts.go | 1 + .../x/onecloud/pkg/cloudcommon/consts/db.go | 16 + .../x/onecloud/pkg/cloudcommon/database.go | 4 + .../x/onecloud/pkg/cloudcommon/db/checksum.go | 19 +- .../onecloud/pkg/cloudcommon/db/jointbase.go | 4 + .../pkg/cloudcommon/options/options.go | 2 + .../x/onecloud/pkg/mcclient/auth/auth.go | 22 +- .../x/onecloud/pkg/util/netutils2/netutils.go | 7 +- .../x/onecloud/pkg/util/splitable/metadata.go | 2 +- vendor/yunion.io/x/sdnagent/LICENSE | 201 ++++ .../yunion.io/x/sdnagent/pkg/agent/client.go | 50 + vendor/yunion.io/x/sdnagent/pkg/agent/doc.go | 15 + .../x/sdnagent/pkg/agent/proto/agent.pb.go | 975 ++++++++++++++++ .../x/sdnagent/pkg/agent/proto/agent.proto | 79 ++ .../x/sdnagent/pkg/agent/proto/agent_pb2.py | 687 +++++++++++ .../pkg/agent/proto/agent_pb2_grpc.py | 192 +++ .../x/sdnagent/pkg/agent/proto/common.go | 20 + .../x/sdnagent/pkg/agent/proto/doc.go | 15 + .../x/sdnagent/pkg/agent/proto/flow.go | 42 + vendor/yunion.io/x/sqlchemy/backends.go | 10 +- .../sqlchemy/backends/clickhouse/functions.go | 15 + .../x/sqlchemy/backends/dameng/column.go | 6 +- .../x/sqlchemy/backends/dameng/dameng.go | 7 + .../x/sqlchemy/backends/dameng/functions.go | 38 +- .../x/sqlchemy/backends/mysql/functions.go | 15 + .../x/sqlchemy/backends/sqlite/functions.go | 15 + vendor/yunion.io/x/sqlchemy/backends_base.go | 20 + vendor/yunion.io/x/sqlchemy/functions.go | 55 +- vendor/yunion.io/x/sqlchemy/querydefs.go | 3 + vendor/yunion.io/x/sqlchemy/queryfield.go | 7 +- vendor/yunion.io/x/sqlchemy/subquery.go | 9 +- vendor/yunion.io/x/sqlchemy/table.go | 6 +- vendor/yunion.io/x/sqlchemy/union.go | 7 +- 70 files changed, 8022 insertions(+), 139 deletions(-) create mode 100644 pkg/cni-plugin/plugin/ovs.go create mode 100644 vendor/github.com/digitalocean/go-openvswitch/AUTHORS create mode 100644 vendor/github.com/digitalocean/go-openvswitch/LICENSE.md create mode 100644 vendor/github.com/digitalocean/go-openvswitch/ovs/README.md create mode 100644 vendor/github.com/digitalocean/go-openvswitch/ovs/action.go create mode 100644 vendor/github.com/digitalocean/go-openvswitch/ovs/actionparser.go create mode 100644 vendor/github.com/digitalocean/go-openvswitch/ovs/client.go create mode 100644 vendor/github.com/digitalocean/go-openvswitch/ovs/codegen.go create mode 100644 vendor/github.com/digitalocean/go-openvswitch/ovs/doc.go create mode 100644 vendor/github.com/digitalocean/go-openvswitch/ovs/flow.go create mode 100644 vendor/github.com/digitalocean/go-openvswitch/ovs/flowstats.go create mode 100644 vendor/github.com/digitalocean/go-openvswitch/ovs/match.go create mode 100644 vendor/github.com/digitalocean/go-openvswitch/ovs/matchflow.go create mode 100644 vendor/github.com/digitalocean/go-openvswitch/ovs/matchparser.go create mode 100644 vendor/github.com/digitalocean/go-openvswitch/ovs/openflow.go create mode 100644 vendor/github.com/digitalocean/go-openvswitch/ovs/ovs.go create mode 100644 vendor/github.com/digitalocean/go-openvswitch/ovs/portrange.go create mode 100644 vendor/github.com/digitalocean/go-openvswitch/ovs/portstats.go create mode 100644 vendor/github.com/digitalocean/go-openvswitch/ovs/table.go create mode 100644 vendor/github.com/digitalocean/go-openvswitch/ovs/vswitch.go create mode 100644 vendor/github.com/fsnotify/fsnotify/fsnotify_unsupported.go create mode 100644 vendor/yunion.io/x/sdnagent/LICENSE create mode 100644 vendor/yunion.io/x/sdnagent/pkg/agent/client.go create mode 100644 vendor/yunion.io/x/sdnagent/pkg/agent/doc.go create mode 100644 vendor/yunion.io/x/sdnagent/pkg/agent/proto/agent.pb.go create mode 100644 vendor/yunion.io/x/sdnagent/pkg/agent/proto/agent.proto create mode 100644 vendor/yunion.io/x/sdnagent/pkg/agent/proto/agent_pb2.py create mode 100644 vendor/yunion.io/x/sdnagent/pkg/agent/proto/agent_pb2_grpc.py create mode 100644 vendor/yunion.io/x/sdnagent/pkg/agent/proto/common.go create mode 100644 vendor/yunion.io/x/sdnagent/pkg/agent/proto/doc.go create mode 100644 vendor/yunion.io/x/sdnagent/pkg/agent/proto/flow.go diff --git a/Makefile b/Makefile index 896650263..c6d495fed 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,7 @@ prepare_dir: mod: GOPROXY=$(GOPROXY) GONOSUMDB=yunion.io/x go get yunion.io/x/onecloud@$(RELEASE_BRANCH) + GOPROXY=$(GOPROXY) GONOSUMDB=yunion.io/x go get yunion.io/x/sdnagent@$(RELEASE_BRANCH) #GOPROXY=$(GOPROXY) GONOSUMDB=yunion.io/x go get $(patsubst %,%@master,$(shell GO111MODULE=on go mod edit -print | sed -n -e 's|.*\(yunion.io/x/[a-z].*\) v.*|\1|p' | grep -v '/onecloud$$')) go mod tidy go mod vendor -v diff --git a/go.mod b/go.mod index 322710f88..294a5c0d3 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/ceph/go-ceph v0.0.0-20181217221554-e32f9f0f2e94 github.com/containernetworking/cni v1.0.0 github.com/containernetworking/plugins v0.8.7 - github.com/fsnotify/fsnotify v1.5.1 + github.com/fsnotify/fsnotify v1.5.4 github.com/ghodss/yaml v1.0.0 github.com/gofrs/flock v0.8.0 github.com/goharbor/go-client v0.26.2 @@ -21,6 +21,7 @@ require ( github.com/regclient/regclient v0.4.8 github.com/smartystreets/goconvey v1.7.2 github.com/stretchr/testify v1.8.1 + github.com/vishvananda/netlink v1.1.0 go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5 golang.org/x/crypto v0.8.0 golang.org/x/sync v0.1.0 @@ -41,10 +42,11 @@ require ( sigs.k8s.io/yaml v1.2.0 yunion.io/x/code-generator v0.0.0-20230130032150-a6851cfe4737 yunion.io/x/jsonutils v1.0.1-0.20240203102553-4096f103b401 - yunion.io/x/log v1.0.1-0.20230411060016-feb3f46ab361 - yunion.io/x/onecloud v0.0.0-20240305022950-ed2dd1f548c2 + yunion.io/x/log v1.0.1-0.20240305175729-7cf2d6cd5a91 + yunion.io/x/onecloud v0.0.0-20240312053540-299846783d7f yunion.io/x/pkg v1.10.1-0.20240303050651-73685b15a96e - yunion.io/x/sqlchemy v1.1.3-0.20240304110946-16faa82225e6 + yunion.io/x/sdnagent v1.2.10-0.20240129094758-082d26e0e076 + yunion.io/x/sqlchemy v1.1.3-0.20240309151155-b34f29f02c79 ) require ( @@ -84,6 +86,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/deislabs/oras v0.8.1 // indirect github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect + github.com/digitalocean/go-openvswitch v0.0.0-20190515160856-1141932ed5cf // indirect github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492 // indirect github.com/docker/distribution v2.7.1+incompatible // indirect github.com/docker/docker v1.4.2-0.20200309214505-aa6a9891b09c // indirect @@ -198,7 +201,6 @@ require ( github.com/tklauser/numcpus v0.4.0 // indirect github.com/tredoe/osutil/v2 v2.0.0-rc.16 // indirect github.com/ugorji/go/codec v1.1.7 // indirect - github.com/vishvananda/netlink v1.1.0 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect @@ -241,13 +243,14 @@ require ( rsc.io/letsencrypt v0.0.3 // indirect sigs.k8s.io/kustomize v2.0.3+incompatible // indirect sigs.k8s.io/structured-merge-diff/v4 v4.0.1 // indirect - yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240304114831-feebc346e513 // indirect + yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240308095624-f1aeca8dcd51 // indirect yunion.io/x/executor v0.0.0-20230705125604-c5ac3141db32 // indirect yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e // indirect yunion.io/x/structarg v0.0.0-20231017124457-df4d5009457c // indirect ) replace ( + github.com/digitalocean/go-openvswitch => github.com/yousong/go-openvswitch v0.0.0-20200422025222-6b2d502be872 github.com/go-logr/logr => github.com/go-logr/logr v0.4.0 github.com/go-openapi/analysis => github.com/go-openapi/analysis v0.19.8 diff --git a/go.sum b/go.sum index 77b220660..03c21769d 100644 --- a/go.sum +++ b/go.sum @@ -339,8 +339,8 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= -github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 h1:LofdAjjjqCSXMwLGgOgnE+rdPuvX9DxCqaHwKy7i/ko= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/gernest/wow v0.1.0/go.mod h1:dEPabJRi5BneI1Nev1VWo0ZlcTWibHWp43qxKms4elY= @@ -1166,6 +1166,8 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yousong/go-openvswitch v0.0.0-20200422025222-6b2d502be872 h1:U0XgUKrnQXhCtO8r9EX5uOQ4I/FHnDfuTrpgyZBAf50= +github.com/yousong/go-openvswitch v0.0.0-20200422025222-6b2d502be872/go.mod h1:RGiG58L66+ZNIg7TqKQIQfF7TXLRc2FxWAhX+pq1XqE= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1485,6 +1487,7 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1792,8 +1795,8 @@ sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= -yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240304114831-feebc346e513 h1:p6TyojYdVP5S3jTTgOOj/Pm5uRRAv6sLRj8xLxADRe4= -yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240304114831-feebc346e513/go.mod h1:dsUESXIbXJ+/ywbNClhldOrbPOiBi2udrgOnB/ffoWk= +yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240308095624-f1aeca8dcd51 h1:9a58ALsmGgjtFRKELyenb0Hb7lKoMmAZ2QbfMYIOWG0= +yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240308095624-f1aeca8dcd51/go.mod h1:dsUESXIbXJ+/ywbNClhldOrbPOiBi2udrgOnB/ffoWk= yunion.io/x/code-generator v0.0.0-20230130032150-a6851cfe4737 h1:+wbsJEsGDbuMZWeflXb+sQwuqrUOa4QSa/MFmgicD3w= yunion.io/x/code-generator v0.0.0-20230130032150-a6851cfe4737/go.mod h1:kUCyyjSbaDW0A+ZXuUg3avTpaS1LUw3LxYkHZymjgz8= yunion.io/x/executor v0.0.0-20230705125604-c5ac3141db32 h1:v7POYkQwo1XzOxBoIoRVr/k0V9Y5JyjpshlIFa9raug= @@ -1804,10 +1807,10 @@ yunion.io/x/jsonutils v1.0.1-0.20240203102553-4096f103b401/go.mod h1:VK4Z93dgiKg yunion.io/x/log v0.0.0-20190514041436-04ce53b17c6b/go.mod h1:+gauLs73omeJAPlsXcevLsJLKixV+sR/E7WSYTSx1fE= yunion.io/x/log v0.0.0-20190629062853-9f6483a7103d/go.mod h1:LC6f/4FozL0iaAbnFt2eDX9jlsyo3WiOUPm03d7+U4U= yunion.io/x/log v0.0.0-20200313080802-57a4ce5966b3/go.mod h1:LC6f/4FozL0iaAbnFt2eDX9jlsyo3WiOUPm03d7+U4U= -yunion.io/x/log v1.0.1-0.20230411060016-feb3f46ab361 h1:c5LyNdhbvBe/92pXs9jgXOU/S+RgYh/DHe538LpT/Mo= -yunion.io/x/log v1.0.1-0.20230411060016-feb3f46ab361/go.mod h1:LC6f/4FozL0iaAbnFt2eDX9jlsyo3WiOUPm03d7+U4U= -yunion.io/x/onecloud v0.0.0-20240305022950-ed2dd1f548c2 h1:a//bOiPceVRMdBV7ziolAoxFS8mQSKZU1kv8Oe26Muc= -yunion.io/x/onecloud v0.0.0-20240305022950-ed2dd1f548c2/go.mod h1:MoJDV5/Sc1+dA8r7F1upUOQy/D0BWDh2JAk+iTmUC/A= +yunion.io/x/log v1.0.1-0.20240305175729-7cf2d6cd5a91 h1:inY5o3LDa/zgsIZuPN0HmpzKIsu/lLgsBmMttuDPGj4= +yunion.io/x/log v1.0.1-0.20240305175729-7cf2d6cd5a91/go.mod h1:LC6f/4FozL0iaAbnFt2eDX9jlsyo3WiOUPm03d7+U4U= +yunion.io/x/onecloud v0.0.0-20240312053540-299846783d7f h1:YhcxhGDyHVszGpUKNqHa7flzeYYvDaL0zbnbyc4Nviw= +yunion.io/x/onecloud v0.0.0-20240312053540-299846783d7f/go.mod h1:Ey8ePslLI2IQRZ2dNkabkQj+Yc3rBdOjbkzqpOUPoj8= yunion.io/x/pkg v0.0.0-20190620104149-945c25821dbf/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E= yunion.io/x/pkg v0.0.0-20190628082551-f4033ba2ea30/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E= yunion.io/x/pkg v0.0.0-20200416145704-22c189971435/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E= @@ -1815,7 +1818,9 @@ yunion.io/x/pkg v1.10.1-0.20240303050651-73685b15a96e h1:NuYu+z0dOT93aoNjg3W3PkL yunion.io/x/pkg v1.10.1-0.20240303050651-73685b15a96e/go.mod h1:ksCJVQ+DwKrJ5QBEoU8pzrDFfDaZVAFH/iJ6yQCYxJk= yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e h1:v+EzIadodSwkdZ/7bremd7J8J50Cise/HCylsOJngmo= yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e/go.mod h1:0iFKpOs1y4lbCxeOmq3Xx/0AcQoewVPwj62eRluioEo= -yunion.io/x/sqlchemy v1.1.3-0.20240304110946-16faa82225e6 h1:3R34JW8CxuweUn4mCrFMSTwU5Njwh5aCB5x9x6l5GBA= -yunion.io/x/sqlchemy v1.1.3-0.20240304110946-16faa82225e6/go.mod h1:5W8ghvJ4TNt/r2yDjjD3i4QsZgIiJX45dhRQBGWPHsQ= +yunion.io/x/sdnagent v1.2.10-0.20240129094758-082d26e0e076 h1:EZIgOmlNGwYjOQMb6H3gpqJnzaxpdd/xCnQ9EGx9u1w= +yunion.io/x/sdnagent v1.2.10-0.20240129094758-082d26e0e076/go.mod h1:aKFiXhcU4EaJLwADLmx5xnBUQbPJv06Fz5Lzz3ptdqw= +yunion.io/x/sqlchemy v1.1.3-0.20240309151155-b34f29f02c79 h1:tY92xg+5hdsvVaqPpC1J/6LV9mDaeOBWbkxZkazwOLQ= +yunion.io/x/sqlchemy v1.1.3-0.20240309151155-b34f29f02c79/go.mod h1:5W8ghvJ4TNt/r2yDjjD3i4QsZgIiJX45dhRQBGWPHsQ= yunion.io/x/structarg v0.0.0-20231017124457-df4d5009457c h1:QuLab2kSRECZRxo4Lo2KcYn6XjQFDGaZ1+x0pYDVVwQ= yunion.io/x/structarg v0.0.0-20231017124457-df4d5009457c/go.mod h1:EP6NSv2C0zzqBDTKumv8hPWLb3XvgMZDHQRfyuOrQng= diff --git a/pkg/cni-plugin/plugin/ovs.go b/pkg/cni-plugin/plugin/ovs.go new file mode 100644 index 000000000..3d73da1ad --- /dev/null +++ b/pkg/cni-plugin/plugin/ovs.go @@ -0,0 +1,49 @@ +package plugin + +import ( + "context" + "time" + + "yunion.io/x/pkg/errors" + "yunion.io/x/sdnagent/pkg/agent" + pb "yunion.io/x/sdnagent/pkg/agent/proto" +) + +type OVSClient interface { + AddPort(bridge, port string) error + DeletePort(bridge, port string) error +} + +func NewOVSClient() (OVSClient, error) { + cli, err := agent.NewClient("/var/run/onecloud/sdnagent.sock") + if err != nil { + return nil, errors.Wrap(err, "new yunion sdnagent client") + } + sw := &ovsClient{ + agentCli: cli, + } + return sw, nil +} + +type ovsClient struct { + agentCli *agent.AgentClient +} + +func newTimeoutCtx() context.Context { + ctx, _ := context.WithTimeout(context.Background(), time.Duration(10*time.Second)) + return ctx +} + +func (sw *ovsClient) AddPort(bridge, port string) error { + return sw.agentCli.W(sw.agentCli.VSwitch.AddBridgePort(newTimeoutCtx(), &pb.AddBridgePortRequest{ + Bridge: bridge, + Port: port, + })) +} + +func (sw *ovsClient) DeletePort(bridge, port string) error { + return sw.agentCli.W(sw.agentCli.VSwitch.DelBridgePort(newTimeoutCtx(), &pb.DelBridgePortRequest{ + Bridge: bridge, + Port: port, + })) +} diff --git a/pkg/cni-plugin/plugin/plugin.go b/pkg/cni-plugin/plugin/plugin.go index 4881eb64d..0016f0fe6 100644 --- a/pkg/cni-plugin/plugin/plugin.go +++ b/pkg/cni-plugin/plugin/plugin.go @@ -7,6 +7,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" + current "github.com/containernetworking/cni/pkg/types/040" cniversion "github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/plugins/pkg/ns" @@ -54,11 +55,10 @@ func loadNetConf(bytes []byte, envArgs string) (*NetConf, string, error) { } func cmdAdd(args *skel.CmdArgs) error { - n, cniVersion, err := loadNetConf(args.StdinData, args.Args) + _, cniVersion, err := loadNetConf(args.StdinData, args.Args) if err != nil { return errors.Wrap(err, "loadNetConf") } - log.Infof("===args.Args: %s, n: %#v, cniVersion: %s", args.Args, n, cniVersion) pod, err := NewCloudPodFromCNIArgs(args.Args) if err != nil { return errors.Wrap(err, "NewCloudPodFromCNIArgs") @@ -75,15 +75,18 @@ func cmdAdd(args *skel.CmdArgs) error { return fmt.Errorf("Pod %s doesn't have nics", pod.Name) } + result, err := GenerateNetworkResultByNics(nics) + if err != nil { + return errors.Wrap(err, "GenerateNetworkResultByNics") + } + for idx, nic := range nics { - defaultGw := false - if idx == 0 { - defaultGw = true + if err := setupNic(idx, nic, netns); err != nil { + return errors.Wrap(err, "setupNic") } - log.Infof("--nic: %#v, defaultGw: %v", nic, defaultGw) } - return nil + return types.PrintResult(result, cniVersion) } func cmdDel(args *skel.CmdArgs) error { diff --git a/pkg/cni-plugin/plugin/types.go b/pkg/cni-plugin/plugin/types.go index 49ad3a7cc..ef0ce46e1 100644 --- a/pkg/cni-plugin/plugin/types.go +++ b/pkg/cni-plugin/plugin/types.go @@ -1,5 +1,7 @@ package plugin +import "fmt" + type PodDesc struct { Id string `json:"id"` Name string `json:"name"` @@ -11,6 +13,7 @@ type PodNic struct { Index int `json:"index"` Bridge string `json:"bridge"` Ifname string `json:"ifname"` + Interface string `json:"interface"` Ip string `json:"ip"` Mac string `json:"mac"` Gateway string `json:"gateway"` @@ -22,3 +25,11 @@ type PodNic struct { NetId string `json:"net_id"` WireId string `json:"wire_id"` } + +func (n PodNic) GetInterface(idx int) string { + defaultName := fmt.Sprintf("eth%d", idx) + if n.Interface != "" { + return n.Interface + } + return defaultName +} diff --git a/pkg/cni-plugin/plugin/utils.go b/pkg/cni-plugin/plugin/utils.go index 05de60a40..84a47215a 100644 --- a/pkg/cni-plugin/plugin/utils.go +++ b/pkg/cni-plugin/plugin/utils.go @@ -4,9 +4,16 @@ import ( "encoding/json" "fmt" "io/ioutil" + "net" "path/filepath" "strings" + "github.com/containernetworking/cni/pkg/types" + current "github.com/containernetworking/cni/pkg/types/040" + "github.com/containernetworking/plugins/pkg/ns" + "github.com/vishvananda/netlink" + + "yunion.io/x/log" "yunion.io/x/pkg/errors" ) @@ -89,3 +96,254 @@ func NewCloudPodFromCNIArgs(args string) (*CloudPod, error) { func (p CloudPod) GetDesc() *PodDesc { return p.desc } + +func GenerateNetworkResultByNics(nics []PodNic) (*current.Result, error) { + result := ¤t.Result{} + ipConfs := make([]*current.IPConfig, 0) + ifs := make([]*current.Interface, 0) + ifRoutes := make([]*types.Route, 0) + for idx, nic := range nics { + defaultGw := false + if idx == 0 { + defaultGw = true + } + ctrIf, ipConfigs, routes, err := getNetworkConfig(idx, nic, defaultGw) + if err != nil { + return nil, errors.Wrap(err, "getNetworkConfig") + } + ipConfs = append(ipConfs, ipConfigs...) + ifs = append(ifs, ctrIf) + ifRoutes = append(ifRoutes, routes...) + } + return result, nil +} + +func getIPNet(ip string, mask int) (*net.IPNet, error) { + cidr := fmt.Sprintf("%s/%d", ip, mask) + ipAddr, ipNet, err := net.ParseCIDR(cidr) + if err != nil { + return nil, errors.Wrapf(err, "ParseCIDR: %s", cidr) + } + return &net.IPNet{ + IP: ipAddr, + Mask: ipNet.Mask, + }, nil +} + +func getNetworkConfig(idx int, nic PodNic, defaultGw bool) (*current.Interface, []*current.IPConfig, []*types.Route, error) { + ipConfigs := make([]*current.IPConfig, 0) + ipn, err := getIPNet(nic.Ip, nic.Masklen) + if err != nil { + return nil, nil, nil, errors.Wrap(err, "get ip network") + } + gatewayIp := net.ParseIP(nic.Gateway) + ipConfig := ¤t.IPConfig{ + Version: "4", + Interface: &idx, + Address: *ipn, + Gateway: gatewayIp, + } + ipConfigs = append(ipConfigs, ipConfig) + ctrIf := ¤t.Interface{ + Name: nic.Interface, + Mac: nic.Mac, + } + routes := make([]*types.Route, 0) + if defaultGw { + _, defaultNet, _ := net.ParseCIDR("0.0.0.0/0") + defaultGateway := ipConfigs[0].Gateway + route := &types.Route{ + Dst: *defaultNet, + GW: defaultGateway, + } + routes = append(routes, route) + } + return ctrIf, ipConfigs, routes, nil +} + +func setupNic(index int, nic PodNic, netns ns.NetNS) error { + // Create OVS client + ovsCli, err := NewOVSClient() + if err != nil { + return errors.Wrap(err, "NewOVSClient") + } + + hostIfname := nic.Ifname + ctrIfname := nic.GetInterface(index) + ctrMac := nic.Mac + hostInterface, ctrInterface, err := setupVeth(ovsCli, index, netns, nic.Bridge) + if err != nil { + return errors.Wrap(err, "setupVeth") + } + + // Configure the container hardware address and IP address(es) + if err := netns.Do(func(_ ns.NetNS) error { + ctrVeth, err := net.InterfaceByName(ctrIfname) + if err != nil { + return errors.Wrapf(err, "net.InterfaceByName %q", ctrIfname) + } + + // Add the IP to the interface + if err := ConfigureIface(ctrIfname, nic); err != nil { + return errors.Wrap(err, "ConfigureIface") + } + return nil + }); err != nil { + return errors.Wrap(err, "Configure the container hardware address and IP address(es)") + } + return nil +} + +func setupVeth( + cli OVSClient, + index int, nic PodNic, netns ns.NetNS) (*current.Interface, *current.Interface, error) { + ctrIf := ¤t.Interface{} + hostIf := ¤t.Interface{} + + hostIfname := nic.Ifname + ctrIfname := nic.GetInterface(index) + if err := ensureVethDeleted(hostIfname); err != nil { + return nil, nil, errors.Wrapf(err, "ensure veth %q deleted", hostIfname) + } + + if err := netns.Do(func(hostNS ns.NetNS) error { + // create the veth pair in the container and move host endside into host netns + hostVeth, ctrVeth, err := setupYunionVeth(index, hostIfname, ctrIfname, nic.Mac, nic.Mtu, hostNS) + if err != nil { + return errors.Wrap(err, "setupYunionVeth") + } + log.Infof("makeVethPair hostVeth: %#v, containerVeth: %#v", hostVeth, ctrVeth) + + ctrIf.Name = ctrVeth.Name + ctrIf.Mac = ctrVeth.HardwareAddr.String() + ctrIf.Sandbox = netns.Path() + hostIf.Name = hostVeth.Name + + // ip link set lo up + if err := setLinkup("lo"); err != nil { + return errors.Wrap(err, "set loopback nic up") + } + return nil + }); err != nil { + return nil, nil, errors.Wrap(err, "netns.Do") + } + + // need to lookup hostVeth again as its index has changed during ns move + hostVeth, err := netlink.LinkByName(hostIf.Name) + if err != nil { + return nil, nil, errors.Wrapf(err, "failed to lookup host veth: %q", hostIf.Name) + } + hostIf.Mac = hostVeth.Attrs().HardwareAddr.String() + + if err := cli.AddPort(nic.Bridge, hostIf.Name); err != nil { + return nil, nil, errors.Wrapf(err, "Add port to OVS: %s -> %s", hostIf.Name, nic.Bridge) + } + log.Infof("Port %q added to %q", hostIf.Name, nic.Bridge) + return hostIf, ctrIf, nil +} + +func ensureVethDeleted(name string) error { + // clean up if peer veth exists + oldPeerVethName, err := netlink.LinkByName(name) + if err == nil { + if err := netlink.LinkDel(oldPeerVethName); err != nil { + return errors.Wrapf(err, "failed to delete old peer veth %q", name) + } + } + if err != nil { + log.Warningf("delete %q peer veth err: %v", name, err) + } + return nil +} + +func setupYunionVeth(index int, hostVethName string, ctrIfName string, ctrMac string, mtu int, hostNS ns.NetNS) (net.Interface, net.Interface, error) { + ctrVeth, err := makeVethPair(index, ctrIfName, hostVethName, mtu) + if err != nil { + return net.Interface{}, net.Interface{}, errors.Wrap(err, "makeVethPair") + } + + if mac, err := net.ParseMAC(ctrMac); err != nil { + return net.Interface{}, net.Interface{}, errors.Wrapf(err, "ParseMAC: %q", ctrMac) + } else { + if err := netlink.LinkSetHardwareAddr(ctrVeth, mac); err != nil { + return net.Interface{}, net.Interface{}, errors.Wrapf(err, "netlink.LinkSetHardwareAddr: %q", mac.String()) + } + } + + if err := netlink.LinkSetUp(ctrVeth); err != nil { + return net.Interface{}, net.Interface{}, errors.Wrapf(err, "netlink.LinkSetup: %q", ctrVeth.Type()) + } + + hostVeth, err := netlink.LinkByName(hostVethName) + if err != nil { + return net.Interface{}, net.Interface{}, errors.Wrapf(err, "failed to lookup host veth: %q", hostVethName) + } + if err := netlink.LinkSetNsFd(hostVeth, int(hostNS.Fd())); err != nil { + return net.Interface{}, net.Interface{}, errors.Wrapf(err, "failed to move veth %q to host netns %#v", hostVeth, hostNS) + } + + if err := hostNS.Do(func(_, ns.NetNS) error { + hostVeth, err := netlink.LinkByName(hostVethName) + if err != nil { + return errors.Wrapf(err, "failed to lookup host veth after moved: %q", hostVethName) + } + if err := netlink.LinkSetUp(hostVeth); err != nil { + return errors.Wrapf(err, "failed to set host veth up after moved: %q", hostVethName) + } + return nil + }); err != nil { + return net.Interface{}, net.Interface{}, errors.Wrapf(err, "set link up at hostNS: %s", hostVethName) + } + + return ifaceFromNetlinkLink(hostVeth), ifaceFromNetlinkLink(ctrVeth), nil +} + +func ifaceFromNetlinkLink(l netlink.Link) net.Interface { + a := l.Attrs() + return net.Interface{ + Index: a.Index, + MTU: a.MTU, + Name: a.Name, + HardwareAddr: a.HardwareAddr, + Flags: a.Flags, + } +} + +func makeVethPair(index int, peerVethName, hostVethName string, mtu int) (netlink.Link, error) { + veth := &netlink.Veth{ + LinkAttrs: netlink.LinkAttrs{ + Name: peerVethName, + Flags: net.FlagUp, + MTU: mtu, + }, + PeerName: hostVethName, + } + if err := netlink.LinkAdd(veth); err != nil { + return nil, errors.Wrap(err, "netlink.LinkAdd") + } + return veth, nil +} + +func setLinkUp(name string) error { + iface, err := netlink.LinkByName(name) + if err != nil { + return err + } + return netlink.LinkSetUp(iface) +} + +func ConfigureIface(ifName string, nic PodNic) error { + link, err := netlink.LinkByName(ifName) + if err != nil { + return errors.Wrap(err, "LinkByName") + } + + if err := netlink.LinkSetUp(link); err != nil { + return errors.Wrap(err, "LinkSetUp") + } + + var v4gw, v6gw net.IP + addr := &netlink.Addr{ + IPNet: nic.Ip + } +} diff --git a/vendor/github.com/digitalocean/go-openvswitch/AUTHORS b/vendor/github.com/digitalocean/go-openvswitch/AUTHORS new file mode 100644 index 000000000..22d385bd6 --- /dev/null +++ b/vendor/github.com/digitalocean/go-openvswitch/AUTHORS @@ -0,0 +1,15 @@ +Maintainer +---------- +DigitalOcean, Inc + +Original Authors +---------------- +Matt Layher + +Contributors +------------ +Michael Ben-Ami +Tejas Kokje +Kei Nohguchi +Neal Shrader +Sangeetha Srikanth diff --git a/vendor/github.com/digitalocean/go-openvswitch/LICENSE.md b/vendor/github.com/digitalocean/go-openvswitch/LICENSE.md new file mode 100644 index 000000000..84a5a44c2 --- /dev/null +++ b/vendor/github.com/digitalocean/go-openvswitch/LICENSE.md @@ -0,0 +1,195 @@ +Apache License +============== + +_Version 2.0, January 2004_ +_<>_ + +### Terms and Conditions for use, reproduction, and distribution + +#### 1. Definitions + +“License” shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +“Licensor” shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +“Legal Entity” shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, “control” means **(i)** the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the +outstanding shares, or **(iii)** beneficial ownership of such entity. + +“You” (or “Your”) shall mean an individual or Legal Entity exercising +permissions granted by this License. + +“Source” form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +“Object” form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +“Work” shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +“Derivative Works” shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +“Contribution” shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +“submitted” means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as “Not a Contribution.” + +“Contributor” shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +#### 2. Grant of Copyright License + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +#### 3. Grant of Patent License + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +#### 4. Redistribution + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +* **(a)** You must give any other recipients of the Work or Derivative Works a copy of +this License; and +* **(b)** You must cause any modified files to carry prominent notices stating that You +changed the files; and +* **(c)** You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +#### 5. Submission of Contributions + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +#### 6. Trademarks + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +#### 7. Disclaimer of Warranty + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +#### 8. Limitation of Liability + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +#### 9. Accepting Warranty or Additional Liability + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +_END OF TERMS AND CONDITIONS_ + +### APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets `[]` replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same “printed page” as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/vendor/github.com/digitalocean/go-openvswitch/ovs/README.md b/vendor/github.com/digitalocean/go-openvswitch/ovs/README.md new file mode 100644 index 000000000..c3d6e152e --- /dev/null +++ b/vendor/github.com/digitalocean/go-openvswitch/ovs/README.md @@ -0,0 +1,31 @@ +ovs +=== + +Package `ovs` is a client library for Open vSwitch which enables programmatic +control of the virtual switch. + +Package `ovs` is a wrapper around the `ovs-vsctl` and `ovs-ofctl` utilities, but +in the future, it may speak OVSDB and OpenFlow directly with the same interface. + +```go +// Create a *ovs.Client. Specify ovs.OptionFuncs to customize it. +c := ovs.New( + // Prepend "sudo" to all commands. + ovs.Sudo(), +) + +// $ sudo ovs-vsctl --may-exist add-br ovsbr0 +if err := c.VSwitch.AddBridge("ovsbr0"); err != nil { + log.Fatalf("failed to add bridge: %v", err) +} + +// $ sudo ovs-ofctl add-flow ovsbr0 priority=100,ip,actions=drop +err := c.OpenFlow.AddFlow("ovsbr0", &ovs.Flow{ + Priority: 100, + Protocol: ovs.ProtocolIPv4, + Actions: []ovs.Action{ovs.Drop()}, +}) +if err != nil { + log.Fatalf("failed to add flow: %v", err) +} +``` \ No newline at end of file diff --git a/vendor/github.com/digitalocean/go-openvswitch/ovs/action.go b/vendor/github.com/digitalocean/go-openvswitch/ovs/action.go new file mode 100644 index 000000000..8dbc9121b --- /dev/null +++ b/vendor/github.com/digitalocean/go-openvswitch/ovs/action.go @@ -0,0 +1,603 @@ +// Copyright 2017 DigitalOcean. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ovs + +import ( + "encoding" + "errors" + "fmt" + "net" + "strconv" +) + +var ( + // errCTNoArguments is returned when no arguments are passed to ActionCT. + errCTNoArguments = errors.New("no arguments for connection tracking") + + // errInvalidVLANVID is returned when an input VLAN VID is out of range + // for a valid VLAN VID. + errInvalidVLANVID = errors.New("VLAN VID must be between 0 and 4095") + + // errOutputNegativePort is returned when Output is called with a + // negative integer port. + errOutputNegativePort = errors.New("output port number must not be negative") + + // errResubmitPortTableZero is returned when Resubmit is called with + // both port and table value set to zero. + errResubmitPortTableZero = errors.New("both port and table are zero for action resubmit") + + // errLoadSetFieldZero is returned when Load or SetField is called with value and/or + // field set to empty strings. + errLoadSetFieldZero = errors.New("value and/or field for action load or set_field are empty") + + // errMoveFieldZero is returned when Move is called with src and/or dest + // field set to empty strings. + errMoveFieldZero = errors.New("src and/or dest field for action move are empty") + + // errResubmitPortInvalid is returned when ResubmitPort is given a port number that is + // invalid per the openflow spec. + errResubmitPortInvalid = errors.New("resubmit port must be between 0 and 65279 inclusive") + + // errTooManyDimensions is returned when the specified dimension exceeds the total dimension + // in a conjunction action. + errDimensionTooLarge = errors.New("dimension number exceeds total number of dimensions") +) + +// Action strings in lower case, as those are compared to the lower case letters +// in parseAction(). +const ( + actionDrop = "drop" + actionFlood = "flood" + actionInPort = "in_port" + actionLocal = "local" + actionNormal = "normal" + actionStripVLAN = "strip_vlan" +) + +// An Action is a type which can be marshaled into an OpenFlow action. Actions can be +// used with Flows to perform operations when the Flow matches an input packet. +// +// Actions must also implement fmt.GoStringer for code generation purposes. +type Action interface { + encoding.TextMarshaler + fmt.GoStringer +} + +// A textAction is an Action which is referred to by a name only, with no arguments. +type textAction struct { + action string +} + +// MarshalText implements Action. +func (a *textAction) MarshalText() ([]byte, error) { + return []byte(a.action), nil +} + +// GoString implements Action. +func (a *textAction) GoString() string { + switch a.action { + case actionDrop: + return "ovs.Drop()" + case actionFlood: + return "ovs.Flood()" + case actionInPort: + return "ovs.InPort()" + case actionLocal: + return "ovs.Local()" + case actionNormal: + return "ovs.Normal()" + case actionStripVLAN: + return "ovs.StripVLAN()" + default: + return fmt.Sprintf("// BUG(mdlayher): unimplemented OVS text action: %q", a.action) + } +} + +// Drop immediately discards the packet. It must be the only Action +// specified when used. +func Drop() Action { + return &textAction{ + action: actionDrop, + } +} + +// Flood outputs the packet on all switch ports other than the port on which it +// was received, which have flooding enabled. +func Flood() Action { + return &textAction{ + action: actionFlood, + } +} + +// InPort outputs the packet on the port from which it was received. +func InPort() Action { + return &textAction{ + action: actionInPort, + } +} + +// Local outputs the packet on the local port, which corresponds to +// the network device that has the same name as the bridge. +func Local() Action { + return &textAction{ + action: actionLocal, + } +} + +// Normal subjects the packet to the device's normal L2/L3 processing. +func Normal() Action { + return &textAction{ + action: actionNormal, + } +} + +// StripVLAN strips the VLAN tag from a packet, if one is present. +func StripVLAN() Action { + return &textAction{ + action: actionStripVLAN, + } +} + +// printf-style patterns for marshaling and unmarshaling actions. +const ( + patConnectionTracking = "ct(%s)" + patConjunction = "conjunction(%d,%d/%d)" + patModDataLinkDestination = "mod_dl_dst:%s" + patModDataLinkSource = "mod_dl_src:%s" + patModNetworkDestination = "mod_nw_dst:%s" + patModNetworkSource = "mod_nw_src:%s" + patModTransportDestinationPort = "mod_tp_dst:%d" + patModTransportSourcePort = "mod_tp_src:%d" + patModVLANVID = "mod_vlan_vid:%d" + patOutput = "output:%d" + patResubmitPort = "resubmit:%s" + patResubmitPortTable = "resubmit(%s,%s)" +) + +// ConnectionTracking sends a packet through the host's connection tracker. +func ConnectionTracking(args string) Action { + return &ctAction{ + args: args, + } +} + +// A ctAction is an Action which is used by ConneectionTracking. +type ctAction struct { + // TODO(mdlayher): implement arguments type for ct() actions + args string +} + +// MarshalText implements Action. +func (a *ctAction) MarshalText() ([]byte, error) { + if a.args == "" { + return nil, errCTNoArguments + } + + return bprintf(patConnectionTracking, a.args), nil +} + +// GoString implements Action. +func (a *ctAction) GoString() string { + return fmt.Sprintf("ovs.ConnectionTracking(%q)", a.args) +} + +// ModDataLinkDestination modifies the data link destination of a packet. +func ModDataLinkDestination(addr net.HardwareAddr) Action { + return &modDataLinkAction{ + srcdst: destination, + addr: addr, + } +} + +// ModDataLinkSource modifies the data link source of a packet. +func ModDataLinkSource(addr net.HardwareAddr) Action { + return &modDataLinkAction{ + srcdst: source, + addr: addr, + } +} + +// A modDataLinkAction is an Action which is used by +// ModDataLink{Source,Destination}. +type modDataLinkAction struct { + srcdst string + addr net.HardwareAddr +} + +// MarshalText implements Action. +func (a *modDataLinkAction) MarshalText() ([]byte, error) { + if len(a.addr) != ethernetAddrLen { + return nil, fmt.Errorf("hardware address must be %d octets, but got %d", + ethernetAddrLen, len(a.addr)) + } + + if a.srcdst == source { + return bprintf(patModDataLinkSource, a.addr.String()), nil + } + + return bprintf(patModDataLinkDestination, a.addr.String()), nil +} + +// GoString implements Action. +func (a *modDataLinkAction) GoString() string { + if a.srcdst == source { + return fmt.Sprintf("ovs.ModDataLinkSource(%s)", hwAddrGoString(a.addr)) + } + + return fmt.Sprintf("ovs.ModDataLinkDestination(%s)", hwAddrGoString(a.addr)) +} + +// ModNetworkDestination modifies the destination IPv4 address of a packet. +func ModNetworkDestination(ip net.IP) Action { + return &modNetworkAction{ + srcdst: destination, + ip: ip.To4(), + } +} + +// ModNetworkSource modifies the source IPv4 address of a packet. +func ModNetworkSource(ip net.IP) Action { + return &modNetworkAction{ + srcdst: source, + ip: ip.To4(), + } +} + +// A modNetworkAction is an Action which is used by +// ModNetwork{Source,Destination}. +type modNetworkAction struct { + srcdst string + ip net.IP +} + +// MarshalText implements Action. +func (a *modNetworkAction) MarshalText() ([]byte, error) { + if a.ip == nil { + return nil, errors.New("invalid IPv4 address for ModNetwork action") + } + + if a.srcdst == source { + return bprintf(patModNetworkSource, a.ip.String()), nil + } + + return bprintf(patModNetworkDestination, a.ip.String()), nil +} + +// GoString implements Action. +func (a *modNetworkAction) GoString() string { + if a.srcdst == source { + return fmt.Sprintf("ovs.ModNetworkSource(%s)", ipv4GoString(a.ip)) + } + + return fmt.Sprintf("ovs.ModNetworkDestination(%s)", ipv4GoString(a.ip)) +} + +// ModTransportDestinationPort modifies the destination port of a packet. +func ModTransportDestinationPort(port uint16) Action { + return &modTransportPortAction{ + srcdst: destination, + port: port, + } +} + +// ModTransportSourcePort modifies the source port of a packet. +func ModTransportSourcePort(port uint16) Action { + return &modTransportPortAction{ + srcdst: source, + port: port, + } +} + +// A modTransportPortAction is an Action which is used by +// ModTransport{Source,Destination}Port. +type modTransportPortAction struct { + srcdst string + port uint16 +} + +// MarshalText implements Action. +func (a *modTransportPortAction) MarshalText() ([]byte, error) { + if a.srcdst == source { + return bprintf(patModTransportSourcePort, a.port), nil + } + + return bprintf(patModTransportDestinationPort, a.port), nil +} + +// GoString implements Action. +func (a *modTransportPortAction) GoString() string { + if a.srcdst == source { + return fmt.Sprintf("ovs.ModTransportSourcePort(%d)", a.port) + } + + return fmt.Sprintf("ovs.ModTransportDestinationPort(%d)", a.port) +} + +// ModVLANVID modifies the VLAN ID (VID) on a packet. It adds a VLAN +// tag if one is not already present. vid must be a valid VLAN VID, within +// the range of 0 to 4095. +func ModVLANVID(vid int) Action { + return &modVLANVIDAction{ + vid: vid, + } +} + +// A modVLANVIDAction is an Action which is used by ModVLANVID. +type modVLANVIDAction struct { + vid int +} + +// MarshalText implements Action. +func (a *modVLANVIDAction) MarshalText() ([]byte, error) { + if !validVLANVID(a.vid) { + return nil, errInvalidVLANVID + } + + return bprintf(patModVLANVID, a.vid), nil +} + +// GoString implements Action. +func (a *modVLANVIDAction) GoString() string { + return fmt.Sprintf("ovs.ModVLANVID(%d)", a.vid) +} + +// Output outputs the packet to the specified switch port. Use +// InPortLocal to output the packet to the LOCAL port. port must either +// be a non-negative integer. +func Output(port int) Action { + return &outputAction{ + port: port, + } +} + +// An outputAction is an Action which is used by Output. +type outputAction struct { + port int +} + +// MarshalText implements Action. +func (a *outputAction) MarshalText() ([]byte, error) { + if a.port < 0 { + return nil, errOutputNegativePort + } + + return bprintf(patOutput, a.port), nil +} + +// GoString implements Action. +func (a *outputAction) GoString() string { + return fmt.Sprintf("ovs.Output(%d)", a.port) +} + +// Conjunction associates a flow with a certain conjunction ID to match on more than +// one dimension across multiple set matches. +func Conjunction(id int, dimensionNumber int, dimensionSize int) Action { + return &conjunctionAction{ + id: id, + dimensionNumber: dimensionNumber, + dimensionSize: dimensionSize, + } +} + +// A conjuctionAction is an Action which is used by Conjunction. +type conjunctionAction struct { + id int + dimensionNumber int + dimensionSize int +} + +// MarshalText implements Action. +func (a *conjunctionAction) MarshalText() ([]byte, error) { + if a.dimensionNumber > a.dimensionSize { + return nil, errDimensionTooLarge + } + + return bprintf(patConjunction, a.id, a.dimensionNumber, a.dimensionSize), nil +} + +// GoString implements Action. +func (a *conjunctionAction) GoString() string { + return fmt.Sprintf("ovs.Conjunction(%d, %d, %d)", a.id, a.dimensionNumber, a.dimensionSize) +} + +// Resubmit resubmits a packet for further processing by matching +// flows with the specified port and table. If port or table are zero, +// they are set to empty in the output Action. If both are zero, an +// error is returned. +func Resubmit(port int, table int) Action { + return &resubmitAction{ + port: port, + table: table, + } +} + +// A resubmitAction is an Action which is used by ConneectionTracking. +type resubmitAction struct { + port int + table int +} + +// ResubmitPort resubmits a packet into the current table with its context modified +// to look like it originated from the specified openflow port ID. +func ResubmitPort(port int) Action { + return &resubmitPortAction{ + port: port, + } +} + +// A resubmitPortAction is an Action which is used by ConneectionTracking. +type resubmitPortAction struct { + port int +} + +// MarshalText implements Action. +func (a *resubmitPortAction) MarshalText() ([]byte, error) { + // Largest valid port ID is 0xfffffeff per openflow spec. + if a.port < 0 || a.port > 0xfffffeff { + return nil, errResubmitPortInvalid + } + + p := strconv.Itoa(a.port) + + return bprintf(patResubmitPort, p), nil +} + +// GoString implements Action. +func (a *resubmitPortAction) GoString() string { + return fmt.Sprintf("ovs.ResubmitPort(%d)", a.port) +} + +// MarshalText implements Action. +func (a *resubmitAction) MarshalText() ([]byte, error) { + if a.port == 0 && a.table == 0 { + return nil, errResubmitPortTableZero + } + + p := "" + if a.port != 0 { + p = strconv.Itoa(a.port) + } + + t := "" + if a.table != 0 { + t = strconv.Itoa(a.table) + } + + return bprintf(patResubmitPortTable, p, t), nil +} + +// GoString implements Action. +func (a *resubmitAction) GoString() string { + return fmt.Sprintf("ovs.Resubmit(%d, %d)", a.port, a.table) +} + +// SetField overwrites the specified field with the specified value. +// If either string is empty, an error is returned. +func SetField(value string, field string) Action { + return &loadSetFieldAction{ + value: value, + field: field, + typ: actionSetField, + } +} + +// Load loads the specified value into the specified field. +// If either string is empty, an error is returned. +func Load(value string, field string) Action { + return &loadSetFieldAction{ + value: value, + field: field, + typ: actionLoad, + } +} + +// Specifies whether SetField or Load was called to construct a +// loadSetFieldAction. +const ( + actionSetField = iota + actionLoad +) + +// A loadSetFieldAction is an Action which is used by Load and SetField. +type loadSetFieldAction struct { + value string + field string + typ int +} + +// MarshalText implements Action. +func (a *loadSetFieldAction) MarshalText() ([]byte, error) { + if a.value == "" || a.field == "" { + return nil, errLoadSetFieldZero + } + + if a.typ == actionLoad { + return bprintf("load:%s->%s", a.value, a.field), nil + } + + return bprintf("set_field:%s->%s", a.value, a.field), nil +} + +// GoString implements Action. +func (a *loadSetFieldAction) GoString() string { + if a.typ == actionLoad { + return fmt.Sprintf("ovs.Load(%q, %q)", a.value, a.field) + } + + return fmt.Sprintf("ovs.SetField(%q, %q)", a.value, a.field) +} + +// Move loads the specified value into the specified field. +// If either string is empty, an error is returned. +func Move(src string, dest string) Action { + return &moveAction{ + src: src, + dest: dest, + } +} + +// Specifies whether Move was called to construct a +// moveAction. +const ( + actionMove = iota +) + +// A moveAction is an Action which is used by Move. +type moveAction struct { + src string + dest string +} + +// MarshalText implements Action. +func (a *moveAction) MarshalText() ([]byte, error) { + if a.src == "" || a.dest == "" { + return nil, errMoveFieldZero + } + + return bprintf("move:%s->%s", a.src, a.dest), nil +} + +// GoString implements Action. +func (a *moveAction) GoString() string { + return fmt.Sprintf("ovs.Move(%q, %q)", a.src, a.dest) +} + +// SetTunnel sets the tunnel id, e.g. VNI if vxlan is the tunnel protocol. +func SetTunnel(tunnelID uint64) Action { + return &setTunnelAction{ + tunnelID: tunnelID, + } +} + +// A setTunnelAction is an Action used by SetTunnel. +type setTunnelAction struct { + tunnelID uint64 +} + +// GoString implements Action. +func (a *setTunnelAction) GoString() string { + return fmt.Sprintf("ovs.SetTunnel(%#x)", a.tunnelID) +} + +// MarshalText implements Action. +func (a *setTunnelAction) MarshalText() ([]byte, error) { + return bprintf("set_tunnel:%#x", a.tunnelID), nil +} + +// validVLANVID indicates if a VLAN VID falls within the valid range +// for a VLAN VID. +func validVLANVID(vid int) bool { + return vid >= 0x000 && vid <= 0xfff +} diff --git a/vendor/github.com/digitalocean/go-openvswitch/ovs/actionparser.go b/vendor/github.com/digitalocean/go-openvswitch/ovs/actionparser.go new file mode 100644 index 000000000..77861f4f1 --- /dev/null +++ b/vendor/github.com/digitalocean/go-openvswitch/ovs/actionparser.go @@ -0,0 +1,393 @@ +// Copyright 2017 DigitalOcean. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ovs + +import ( + "bufio" + "bytes" + "fmt" + "io" + "net" + "regexp" + "strconv" + "strings" +) + +// An actionParser is a parser for OVS flow actions. +type actionParser struct { + r *bufio.Reader + s stack +} + +// newActionParser creates a new actionParser which wraps the input +// io.Reader. +func newActionParser(r io.Reader) *actionParser { + return &actionParser{ + r: bufio.NewReader(r), + s: make(stack, 0), + } +} + +// eof is a sentinel rune for end of file. +var eof = rune(0) + +// read reads a single rune from the wrapped io.Reader. It returns eof +// if no more runes are present. +func (p *actionParser) read() rune { + ch, _, err := p.r.ReadRune() + if err != nil { + return eof + } + return ch +} + +// Parse parses a slice of Actions using the wrapped io.Reader. The raw +// action strings are also returned for inspection if needed. +func (p *actionParser) Parse() ([]Action, []string, error) { + var actions []Action + var raw []string + + for { + a, r, err := p.parseAction() + if err != nil { + // No more actions remain + if err == io.EOF { + break + } + + return nil, nil, err + } + + actions = append(actions, a) + raw = append(raw, r) + } + + return actions, raw, nil +} + +// parseAction parses a single Action and its raw text from the wrapped +// io.Reader. +func (p *actionParser) parseAction() (Action, string, error) { + // Track runes encountered + var buf bytes.Buffer + + for { + ch := p.read() + + // If comma encountered and no open parentheses, at end of this + // action string + if ch == ',' && p.s.len() == 0 { + break + } + + // If EOF encountered, at end of string + if ch == eof { + // If no items in buffer, end of this action + if buf.Len() == 0 { + return nil, "", io.EOF + } + + // Parse action from buffer + break + } + + // Track open and closing parentheses using a stack to ensure + // that they are appropriately matched + switch ch { + case '(': + p.s.push() + case ')': + p.s.pop() + } + + _, _ = buf.WriteRune(ch) + } + + // Found an unmatched set of parentheses + if p.s.len() > 0 { + return nil, "", fmt.Errorf("invalid action: %q", buf.String()) + } + + s := buf.String() + act, err := parseAction(s) + return act, s, err +} + +// A stack is a basic stack with elements that have no value. +type stack []struct{} + +// len returns the current length of the stack. +func (s *stack) len() int { + return len(*s) +} + +// push adds an element to the stack. +func (s *stack) push() { + *s = append(*s, struct{}{}) +} + +// pop removes an element from the stack. +func (s *stack) pop() { + *s = (*s)[:s.len()-1] +} + +var ( + // resubmitRe is the regex used to match the resubmit action + // with port and table specified + resubmitRe = regexp.MustCompile(`resubmit\((\d*),(\d*)\)`) + + // resubmitPortRe is the regex used to match the resubmit action + // when only a port is specified + resubmitPortRe = regexp.MustCompile(`resubmit:(\d+)`) + + // ctRe is the regex used to match the ct action with its + // parameter list. + ctRe = regexp.MustCompile(`ct\((\S+)\)`) + + // loadRe is the regex used to match the load action + // with its parameters. + loadRe = regexp.MustCompile(`load:(\S+)->(\S+)`) + + // setFieldRe is the regex used to match the set_field action + // with its parameters. + setFieldRe = regexp.MustCompile(`set_field:(\S+)->(\S+)`) + + // moveRe is the regex used to match the move action + // with its parameters. + moveRe = regexp.MustCompile(`move:(\S+)->(\S+)`) +) + +// TODO(mdlayher): replace parsing regex with arguments parsers + +// parseAction creates an Action function from the input string. +func parseAction(s string) (Action, error) { + // Simple actions which match a basic string + switch strings.ToLower(s) { + case actionDrop: + return Drop(), nil + case actionFlood: + return Flood(), nil + case actionInPort: + return InPort(), nil + case actionLocal: + return Local(), nil + case actionNormal: + return Normal(), nil + case actionStripVLAN: + return StripVLAN(), nil + } + + // ActionCT, with its arguments + if ss := ctRe.FindAllStringSubmatch(s, 1); len(ss) > 0 && len(ss[0]) == 2 { + // Results are: + // - full string + // - arguments list + return ConnectionTracking(ss[0][1]), nil + } + + // ActionModDataLinkDestination, with its hardware address. + if strings.HasPrefix(s, patModDataLinkDestination[:len(patModDataLinkDestination)-2]) { + var addr string + n, err := fmt.Sscanf(s, patModDataLinkDestination, &addr) + if err != nil { + return nil, err + } + if n > 0 { + mac, err := net.ParseMAC(addr) + if err != nil { + return nil, err + } + + return ModDataLinkDestination(mac), nil + } + } + + // ActionModDataLinkSource, with its hardware address. + if strings.HasPrefix(s, patModDataLinkSource[:len(patModDataLinkSource)-2]) { + var addr string + n, err := fmt.Sscanf(s, patModDataLinkSource, &addr) + if err != nil { + return nil, err + } + if n > 0 { + mac, err := net.ParseMAC(addr) + if err != nil { + return nil, err + } + + return ModDataLinkSource(mac), nil + } + } + + // ActionModNetworkDestination, with it hardware address + if strings.HasPrefix(s, patModNetworkDestination[:len(patModNetworkDestination)-2]) { + var ip string + n, err := fmt.Sscanf(s, patModNetworkDestination, &ip) + if err != nil { + return nil, err + } + if n > 0 { + ip4 := net.ParseIP(ip).To4() + if ip4 == nil { + return nil, fmt.Errorf("invalid IPv4 address: %s", ip) + } + + return ModNetworkDestination(ip4), nil + } + } + + // ActionModNetworkSource, with it hardware address + if strings.HasPrefix(s, patModNetworkSource[:len(patModNetworkSource)-2]) { + var ip string + n, err := fmt.Sscanf(s, patModNetworkSource, &ip) + if err != nil { + return nil, err + } + if n > 0 { + ip4 := net.ParseIP(ip).To4() + if ip4 == nil { + return nil, fmt.Errorf("invalid IPv4 address: %s", ip) + } + + return ModNetworkSource(ip4), nil + } + } + + // ActionModTransportDestinationPort, with its port. + if strings.HasPrefix(s, patModTransportDestinationPort[:len(patModTransportDestinationPort)-2]) { + var port uint16 + n, err := fmt.Sscanf(s, patModTransportDestinationPort, &port) + if err != nil { + return nil, err + } + if n > 0 { + return ModTransportDestinationPort(port), nil + } + } + + // ActionModTransportSourcePort, with its port. + if strings.HasPrefix(s, patModTransportSourcePort[:len(patModTransportSourcePort)-2]) { + var port uint16 + n, err := fmt.Sscanf(s, patModTransportSourcePort, &port) + if err != nil { + return nil, err + } + if n > 0 { + return ModTransportSourcePort(port), nil + } + } + + // ActionModVLANVID, with its VLAN ID + if strings.HasPrefix(s, patModVLANVID[:len(patModVLANVID)-2]) { + var vlan int + n, err := fmt.Sscanf(s, patModVLANVID, &vlan) + if err != nil { + return nil, err + } + if n > 0 { + return ModVLANVID(vlan), nil + } + } + + // ActionConjunction, with it's id, dimension number, and dimension size + if strings.HasPrefix(s, patConjunction[:len(patConjunction)-10]) { + var id, dimensionNumber, dimensionSize int + n, err := fmt.Sscanf(s, patConjunction, &id, &dimensionNumber, &dimensionSize) + if err != nil { + return nil, err + } + if n > 0 { + return Conjunction(id, dimensionNumber, dimensionSize), nil + } + } + + // ActionOutput, with its port number + if strings.HasPrefix(s, patOutput[:len(patOutput)-2]) { + var port int + n, err := fmt.Sscanf(s, patOutput, &port) + if err != nil { + return nil, err + } + if n > 0 { + return Output(port), nil + } + } + + // ActionResubmit, with both port number and table number + if ss := resubmitRe.FindAllStringSubmatch(s, 1); len(ss) > 0 && len(ss[0]) == 3 { + var ( + port int + table int + + err error + ) + + // Results are: + // - full string + // - port in parenthesis + // - table in parenthesis + + if s := ss[0][1]; s != "" { + port, err = strconv.Atoi(s) + if err != nil { + return nil, err + } + } + if s := ss[0][2]; s != "" { + table, err = strconv.Atoi(s) + if err != nil { + return nil, err + } + } + + return Resubmit(port, table), nil + } + + // ActionResubmitPort, with only a port number + if ss := resubmitPortRe.FindAllStringSubmatch(s, 1); len(ss) > 0 && len(ss[0]) == 2 { + port, err := strconv.Atoi(ss[0][1]) + if err != nil { + return nil, err + } + + return ResubmitPort(port), nil + } + + if ss := loadRe.FindAllStringSubmatch(s, 2); len(ss) > 0 && len(ss[0]) == 3 { + // Results are: + // - full string + // - value + // - field + return Load(ss[0][1], ss[0][2]), nil + } + + if ss := setFieldRe.FindAllStringSubmatch(s, 2); len(ss) > 0 && len(ss[0]) == 3 { + // Results are: + // - full string + // - value + // - field + return SetField(ss[0][1], ss[0][2]), nil + } + + if ss := moveRe.FindAllStringSubmatch(s, 2); len(ss) > 0 && len(ss[0]) == 3 { + // Results are: + // - full string + // - src + // - dest + return Move(ss[0][1], ss[0][2]), nil + } + + return nil, fmt.Errorf("no action matched for %q", s) +} diff --git a/vendor/github.com/digitalocean/go-openvswitch/ovs/client.go b/vendor/github.com/digitalocean/go-openvswitch/ovs/client.go new file mode 100644 index 000000000..e11adcc69 --- /dev/null +++ b/vendor/github.com/digitalocean/go-openvswitch/ovs/client.go @@ -0,0 +1,348 @@ +// Copyright 2017 DigitalOcean. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ovs + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "log" + "os/exec" + "strings" +) + +// A Client is a client type which enables programmatic control of Open +// vSwitch. +type Client struct { + // OpenFlow wraps functionality of the 'ovs-ofctl' binary. + OpenFlow *OpenFlowService + + // VSwitch wraps functionality of the 'ovs-vsctl' binary. + VSwitch *VSwitchService + + // Additional flags applied to all OVS actions, such as timeouts + // or retries. + flags []string + + // Additional flags applied to 'ovs-ofctl' commands. + ofctlFlags []string + + // Enable or disable debugging log messages for OVS commands. + debug bool + + // Prefix all commands with "sudo". + sudo bool + + // Implementation of ExecFunc. + execFunc ExecFunc + + // Implementation of PipeFunc. + pipeFunc PipeFunc +} + +// An ExecFunc is a function which accepts input arguments and returns raw +// byte output and an error. ExecFuncs are swappable to enable testing +// without OVS installed. +type ExecFunc func(cmd string, args ...string) ([]byte, error) + +// shellExec is an ExecFunc which shells out to the binary cmd using the +// arguments args, and returns its combined stdout and stderr and any errors +// which may have occurred. +func shellExec(cmd string, args ...string) ([]byte, error) { + return exec.Command(cmd, args...).CombinedOutput() +} + +// exec executes an ExecFunc using the values from cmd and args. +// The ExecFunc may shell out to an appropriate binary, or may be swapped +// for testing. +func (c *Client) exec(cmd string, args ...string) ([]byte, error) { + // Prepend recurring flags before arguments + flags := append(c.flags, args...) + + // If needed, prefix sudo. + if c.sudo { + flags = append([]string{cmd}, flags...) + cmd = "sudo" + } + + c.debugf("exec: %s %v", cmd, flags) + + // Execute execFunc with all flags and clean up any whitespace or + // newlines from its output. + out, err := c.execFunc(cmd, flags...) + if out != nil { + out = bytes.TrimSpace(out) + c.debugf("exec: %q", string(out)) + } + if err != nil { + // Wrap errors in Error type for further introspection + return nil, &Error{ + Out: out, + Err: err, + } + } + + return out, nil +} + +// A PipeFunc is a function which accepts an input stdin stream, command, +// and arguments, and returns command output and an error. PipeFuncs are +// swappable to enable testing without OVS installed. +type PipeFunc func(stdin io.Reader, cmd string, args ...string) ([]byte, error) + +// shellPipe is a PipeFunc which shells out to the binary cmd using the arguments +// args, and writing to the command's stdin using stdin. +func shellPipe(stdin io.Reader, cmd string, args ...string) ([]byte, error) { + command := exec.Command(cmd, args...) + + stdout, err := command.StdoutPipe() + if err != nil { + return nil, err + } + stderr, err := command.StderrPipe() + if err != nil { + return nil, err + } + + wc, err := command.StdinPipe() + if err != nil { + return nil, err + } + + if err := command.Start(); err != nil { + return nil, err + } + + if _, err := io.Copy(wc, stdin); err != nil { + return nil, err + } + + // Needed to indicate to ovs-ofctl that stdin is done being read. + // "... if the command being run will not exit until standard input is + // closed, the caller must close the pipe." + // Reference: https://golang.org/pkg/os/exec/#Cmd.StdinPipe + if err := wc.Close(); err != nil { + return nil, err + } + + mr := io.MultiReader(stdout, stderr) + b, err := ioutil.ReadAll(mr) + if err != nil { + return nil, err + } + + return b, command.Wait() +} + +// pipe executes a PipeFunc using the values from stdin, cmd, and args. +// stdin is used to feed input data to the stdin of a forked process. +// The PipeFunc may shell out to an appropriate binary, or may be swapped +// for testing. +func (c *Client) pipe(stdin io.Reader, cmd string, args ...string) error { + // Prepend recurring flags before arguments + flags := append(c.flags, args...) + + // If needed, prefix sudo. + if c.sudo { + flags = append([]string{cmd}, flags...) + cmd = "sudo" + } + + c.debugf("pipe: %s %v", cmd, flags) + c.debugf("bundle:") + + tr := io.TeeReader(stdin, writerFunc(func(p []byte) (int, error) { + c.debugf("%s", string(p)) + return len(p), nil + })) + + if out, err := c.pipeFunc(tr, cmd, flags...); err != nil { + c.debugf("pipe error: %v: %q", err, string(out)) + return &pipeError{ + out: out, + err: err, + } + } + + return nil + +} + +// A pipeError is an error returned by Client.pipe, containing combined +// stdout/stderr from a process as well as its error. +type pipeError struct { + out []byte + err error +} + +// Error returns the string representation of a pipeError. +func (e *pipeError) Error() string { + return fmt.Sprintf("pipe error: %v: %q", e.err, string(e.out)) +} + +// debugf prints a logging debug message when debugging is enabled. +func (c *Client) debugf(format string, a ...interface{}) { + if !c.debug { + return + } + + log.Printf("ovs: "+format, a...) +} + +// New creates a new Client with zero or more OptionFunc configurations +// applied. +func New(options ...OptionFunc) *Client { + // Always execute and pipe using shell when created with New. + c := &Client{ + flags: make([]string, 0), + ofctlFlags: make([]string, 0), + execFunc: shellExec, + pipeFunc: shellPipe, + } + for _, o := range options { + o(c) + } + + vss := &VSwitchService{ + c: c, + } + vss.Get = &VSwitchGetService{ + v: vss, + } + vss.Set = &VSwitchSetService{ + v: vss, + } + c.VSwitch = vss + + ofs := &OpenFlowService{ + c: c, + } + c.OpenFlow = ofs + + return c +} + +// An OptionFunc is a function which can apply configuration to a Client. +type OptionFunc func(c *Client) + +// Timeout returns an OptionFunc which sets a timeout in seconds for all +// Open vSwitch interactions. +func Timeout(seconds int) OptionFunc { + return func(c *Client) { + c.flags = append(c.flags, fmt.Sprintf("--timeout=%d", seconds)) + } +} + +// Debug returns an OptionFunc which enables debugging output for the Client +// type. +func Debug(enable bool) OptionFunc { + return func(c *Client) { + c.debug = enable + } +} + +// Exec returns an OptionFunc which sets an ExecFunc for use with a Client. +// This function should typically only be used in tests. +func Exec(fn ExecFunc) OptionFunc { + return func(c *Client) { + c.execFunc = fn + } +} + +// Pipe returns an OptionFunc which sets a PipeFunc for use with a Client. +// This function should typically only be used in tests. +func Pipe(fn PipeFunc) OptionFunc { + return func(c *Client) { + c.pipeFunc = fn + } +} + +// Strict deactivates wildcards for matching purposes when shelling to +// 'ovs-ofctl'. +func Strict() OptionFunc { + return func(c *Client) { + c.ofctlFlags = append(c.ofctlFlags, "--strict") + } +} + +const ( + // FlowFormatNXMTableID is a flow format which allows Nicira Extended match + // with the ability to place a flow in a specific table. + FlowFormatNXMTableID = "NXM+table_id" + + // FlowFormatOXMOpenFlow14 is a flow format which allows Open vSwitch + // extensible match. + FlowFormatOXMOpenFlow14 = "OXM-OpenFlow14" +) + +// FlowFormat specifies the flow format to be used when shelling to +// 'ovs-ofctl'. +func FlowFormat(format string) OptionFunc { + return func(c *Client) { + c.ofctlFlags = append(c.ofctlFlags, fmt.Sprintf("--flow-format=%s", format)) + } +} + +// Protocol constants for use with Protocols and BridgeOptions. +const ( + ProtocolOpenFlow10 = "OpenFlow10" + ProtocolOpenFlow11 = "OpenFlow11" + ProtocolOpenFlow12 = "OpenFlow12" + ProtocolOpenFlow13 = "OpenFlow13" + ProtocolOpenFlow14 = "OpenFlow14" + ProtocolOpenFlow15 = "OpenFlow15" +) + +// Protocols specifies one or more OpenFlow protocol versions to be used when shelling +// to 'ovs-ofctl'. +func Protocols(versions []string) OptionFunc { + return func(c *Client) { + c.ofctlFlags = append(c.ofctlFlags, + fmt.Sprintf("--protocols=%s", strings.Join(versions, ",")), + ) + } +} + +// SetSSLParam configures SSL authentication using a private key, certificate, +// and CA certificate for use with ovs-ofctl. +func SetSSLParam(pkey string, cert string, cacert string) OptionFunc { + return func(c *Client) { + c.ofctlFlags = append(c.ofctlFlags, fmt.Sprintf("--private-key=%s", pkey), + fmt.Sprintf("--certificate=%s", cert), fmt.Sprintf("--ca-cert=%s", cacert)) + } +} + +// SetTCPParam configures the OVSDB connection using a TCP format ip:port +// for use with all ovs-vsctl commands. +func SetTCPParam(addr string) OptionFunc { + return func(c *Client) { + c.flags = append(c.flags, fmt.Sprintf("--db=tcp:%s", addr)) + } +} + +// Sudo specifies that "sudo" should be prefixed to all OVS commands. +func Sudo() OptionFunc { + return func(c *Client) { + c.sudo = true + } +} + +// A writerFunc is an adapter for a function to be used as an io.Writer. +type writerFunc func(p []byte) (n int, err error) + +func (fn writerFunc) Write(p []byte) (int, error) { + return fn(p) +} diff --git a/vendor/github.com/digitalocean/go-openvswitch/ovs/codegen.go b/vendor/github.com/digitalocean/go-openvswitch/ovs/codegen.go new file mode 100644 index 000000000..40377b1f4 --- /dev/null +++ b/vendor/github.com/digitalocean/go-openvswitch/ovs/codegen.go @@ -0,0 +1,62 @@ +// Copyright 2017 DigitalOcean. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ovs + +import ( + "bytes" + "fmt" + "net" + "strconv" +) + +// hwAddrGoString converts a net.HardwareAddr into its Go syntax representation. +func hwAddrGoString(addr net.HardwareAddr) string { + buf := bytes.NewBufferString("net.HardwareAddr{") + for i, b := range addr { + _, _ = buf.WriteString(fmt.Sprintf("0x%02x", b)) + + if i != len(addr)-1 { + _, _ = buf.WriteString(", ") + } + } + _, _ = buf.WriteString("}") + + return buf.String() +} + +// ipv4GoString converts a net.IP (IPv4 only) into its Go syntax representation. +func ipv4GoString(ip net.IP) string { + ip4 := ip.To4() + if ip4 == nil { + return `panic("invalid IPv4 address")` + } + + buf := bytes.NewBufferString("net.IPv4(") + for i, b := range ip4 { + _, _ = buf.WriteString(strconv.Itoa(int(b))) + + if i != len(ip4)-1 { + _, _ = buf.WriteString(", ") + } + } + _, _ = buf.WriteString(")") + + return buf.String() +} + +// bprintf is fmt.Sprintf, but it returns a byte slice instead of a string. +func bprintf(format string, a ...interface{}) []byte { + return []byte(fmt.Sprintf(format, a...)) +} diff --git a/vendor/github.com/digitalocean/go-openvswitch/ovs/doc.go b/vendor/github.com/digitalocean/go-openvswitch/ovs/doc.go new file mode 100644 index 000000000..1e98ec408 --- /dev/null +++ b/vendor/github.com/digitalocean/go-openvswitch/ovs/doc.go @@ -0,0 +1,17 @@ +// Copyright 2017 DigitalOcean. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package ovs is a client library for Open vSwitch which enables programmatic +// control of the virtual switch. +package ovs diff --git a/vendor/github.com/digitalocean/go-openvswitch/ovs/flow.go b/vendor/github.com/digitalocean/go-openvswitch/ovs/flow.go new file mode 100644 index 000000000..54ff0c96c --- /dev/null +++ b/vendor/github.com/digitalocean/go-openvswitch/ovs/flow.go @@ -0,0 +1,389 @@ +// Copyright 2017 DigitalOcean. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ovs + +import ( + "errors" + "fmt" + "strconv" + "strings" +) + +const ( + // PortLOCAL is a special in_port value which refers to the local port + // of an OVS bridge. + PortLOCAL = -1 +) + +// Possible errors which may be encountered while marshaling or unmarshaling +// a Flow. +var ( + errActionsWithDrop = errors.New("Flow actions include drop, but multiple actions specified") + errInvalidActions = errors.New("invalid actions for Flow") + errNoActions = errors.New("no actions defined for Flow") + errNotEnoughElements = errors.New("not enough elements for valid Flow") + errPriorityNotFirst = errors.New("priority field is not first in Flow") +) + +// A Protocol is an OpenFlow protocol designation accepted by Open vSwitch. +type Protocol string + +// Protocol constants which can be used in OVS flow configurations. +const ( + ProtocolARP Protocol = "arp" + ProtocolICMPv4 Protocol = "icmp" + ProtocolICMPv6 Protocol = "icmp6" + ProtocolIPv4 Protocol = "ip" + ProtocolIPv6 Protocol = "ipv6" + ProtocolTCPv4 Protocol = "tcp" + ProtocolTCPv6 Protocol = "tcp6" + ProtocolUDPv4 Protocol = "udp" + ProtocolUDPv6 Protocol = "udp6" +) + +// A Flow is an OpenFlow flow meant for adding flows to a software bridge. It can be marshaled +// to and from its textual form for use with Open vSwitch. +type Flow struct { + Priority int + Protocol Protocol + InPort int + Matches []Match + Table int + IdleTimeout int + Cookie uint64 + Actions []Action +} + +var _ error = &FlowError{} + +// A FlowError is an error encountered while marshaling or unmarshaling +// a Flow. +type FlowError struct { + // Str indicates the string, if any, that caused the flow to + // fail while unmarshaling. + Str string + + // Err indicates the error that halted flow marshaling or unmarshaling. + Err error +} + +// Error returns the string representation of a FlowError. +func (e *FlowError) Error() string { + if e.Str == "" { + return e.Err.Error() + } + + return fmt.Sprintf("flow error due to string %q: %v", + e.Str, e.Err) +} + +// Constants and variables used repeatedly to reduce errors in code. +const ( + priorityString = "priority=" + + priority = "priority" + cookie = "cookie" + keyActions = "actions" + idleTimeout = "idle_timeout" + inPort = "in_port" + table = "table" + duration = "duration" + nPackets = "n_packets" + nBytes = "n_bytes" + hardAge = "hard_age" + idleAge = "idle_age" + + portLOCAL = "LOCAL" +) + +var ( + priorityBytes = []byte(priorityString) +) + +// MarshalText marshals a Flow into its textual form. +func (f *Flow) MarshalText() ([]byte, error) { + if len(f.Actions) == 0 { + return nil, &FlowError{ + Err: errNoActions, + } + } + + actions, err := f.marshalActions() + if err != nil { + return nil, err + } + + matches, err := f.marshalMatches() + if err != nil { + return nil, err + } + + // Action "drop" must only be specified by itself + if len(actions) > 1 { + for _, a := range actions { + if a == actionDrop { + return nil, &FlowError{ + Err: errActionsWithDrop, + } + } + } + } + + b := make([]byte, len(priorityBytes)) + copy(b, priorityBytes) + + b = strconv.AppendInt(b, int64(f.Priority), 10) + + if f.Protocol != "" { + b = append(b, ',') + b = append(b, f.Protocol...) + } + + if f.InPort != 0 { + b = append(b, ","+inPort+"="...) + + // Special case, InPortLOCAL is converted to the literal string LOCAL + if f.InPort == PortLOCAL { + b = append(b, portLOCAL...) + } else { + b = strconv.AppendInt(b, int64(f.InPort), 10) + } + } + + if len(matches) > 0 { + b = append(b, ","+strings.Join(matches, ",")...) + } + + b = append(b, ","+table+"="...) + b = strconv.AppendInt(b, int64(f.Table), 10) + + b = append(b, ","+idleTimeout+"="...) + b = strconv.AppendInt(b, int64(f.IdleTimeout), 10) + + if f.Cookie > 0 { + // Hexadecimal cookies are much easier to read. + b = append(b, ","+cookie+"="...) + b = append(b, paddedHexUint64(f.Cookie)...) + } + + b = append(b, ","+keyActions+"="+strings.Join(actions, ",")...) + + return b, nil +} + +// UnmarshalText unmarshals flow text into a Flow. +func (f *Flow) UnmarshalText(b []byte) error { + // Make a copy per documentation for encoding.TextUnmarshaler. + // A string is easier to work with in this case. + s := string(b) + + // Must have one and only one actions=... field in the flow. + ss := strings.Split(s, keyActions+"=") + if len(ss) != 2 || ss[1] == "" { + return &FlowError{ + Err: errNoActions, + } + } + if len(ss) < 2 { + return &FlowError{ + Err: errNotEnoughElements, + } + } + matchers, actions := strings.TrimSpace(ss[0]), strings.TrimSpace(ss[1]) + + // Handle matchers first. + ss = strings.Split(matchers, ",") + f.Matches = make([]Match, 0) + for i := 0; i < len(ss); i++ { + if !strings.Contains(ss[i], "=") { + // that means this will be a protocol field. + if ss[i] != "" { + f.Protocol = Protocol(ss[i]) + } + continue + } + + // All remaining comma-separated values should be in key=value format, + // but parsing "actions" is done later because actions can use the form: + // actions=foo,bar,baz + kv := strings.Split(ss[i], "=") + if len(kv) != 2 { + continue + } + kv[1] = strings.TrimSpace(kv[1]) + + switch strings.TrimSpace(kv[0]) { + case priority: + // Parse priority into struct field. + pri, err := strconv.ParseInt(kv[1], 10, 0) + if err != nil { + return &FlowError{ + Str: kv[1], + Err: err, + } + } + f.Priority = int(pri) + continue + case cookie: + // Parse cookie into struct field. + cookie, err := strconv.ParseUint(kv[1], 0, 64) + if err != nil { + return &FlowError{ + Str: kv[1], + Err: err, + } + } + f.Cookie = cookie + continue + case keyActions: + // Parse actions outside this loop. + continue + case inPort: + // Parse in_port into struct field. + s := kv[1] + if strings.TrimSpace(s) == portLOCAL { + f.InPort = PortLOCAL + continue + } + + port, err := strconv.ParseInt(s, 10, 0) + if err != nil { + return &FlowError{ + Str: s, + Err: err, + } + } + f.InPort = int(port) + continue + case idleTimeout: + // Parse idle_timeout into struct field. + timeout, err := strconv.ParseInt(kv[1], 10, 0) + if err != nil { + return &FlowError{ + Str: kv[1], + Err: err, + } + } + f.IdleTimeout = int(timeout) + continue + case table: + // Parse table into struct field. + table, err := strconv.ParseInt(kv[1], 10, 0) + if err != nil { + return &FlowError{ + Str: kv[1], + Err: err, + } + } + f.Table = int(table) + continue + case duration, nPackets, nBytes, hardAge, idleAge: + // ignore those fields. + continue + } + + // All arbitrary key/value pairs that + // don't match the case above. + match, err := parseMatch(kv[0], kv[1]) + if err != nil { + return err + } + f.Matches = append(f.Matches, match) + } + + // Parse all actions from the flow. + p := newActionParser(strings.NewReader(actions)) + out, raw, err := p.Parse() + if err != nil { + return &FlowError{ + Str: actions, + Err: errInvalidActions, + } + } + f.Actions = out + + // Action "drop" must only be specified by itself. + if len(raw) > 1 { + for _, a := range raw { + if a == actionDrop { + return &FlowError{ + Err: errActionsWithDrop, + } + } + } + } + + return nil +} + +// MatchFlow converts Flow into MatchFlow. +func (f *Flow) MatchFlow() *MatchFlow { + return &MatchFlow{ + Protocol: f.Protocol, + InPort: f.InPort, + Matches: f.Matches, + Table: f.Table, + Cookie: f.Cookie, + } +} + +// MatchFlowStrict converts Flow into a strict-matching-aware MatchFlow +func (f *Flow) MatchFlowStrict() *MatchFlow { + mf := f.MatchFlow() + mf.Priority = f.Priority + mf.Strict = true + return mf +} + +// marshalActions marshals all Actions in a Flow to their text form. +func (f *Flow) marshalActions() ([]string, error) { + fns := make([]func() ([]byte, error), 0, len(f.Actions)) + for _, fn := range f.Actions { + fns = append(fns, fn.MarshalText) + } + + return f.marshalFunctions(fns) +} + +// marshalMatches marshals all Matches in a Flow to their text form. +func (f *Flow) marshalMatches() ([]string, error) { + fns := make([]func() ([]byte, error), 0, len(f.Matches)) + for _, fn := range f.Matches { + fns = append(fns, fn.MarshalText) + } + + return f.marshalFunctions(fns) +} + +// marshalFunctions marshals a slice of functions to their text form. +func (f *Flow) marshalFunctions(fns []func() ([]byte, error)) ([]string, error) { + out := make([]string, 0, len(fns)) + for _, fn := range fns { + o, err := fn() + if err != nil { + return nil, err + } + + out = append(out, string(o)) + } + + return out, nil +} + +// paddedHexUint64 formats and displays a uint64 as a hexadecimal string +// prefixed with 0x and zero-padded up to 16 characters in length. +func paddedHexUint64(i uint64) string { + return fmt.Sprintf("%#016x", i) +} diff --git a/vendor/github.com/digitalocean/go-openvswitch/ovs/flowstats.go b/vendor/github.com/digitalocean/go-openvswitch/ovs/flowstats.go new file mode 100644 index 000000000..c2c4b27cb --- /dev/null +++ b/vendor/github.com/digitalocean/go-openvswitch/ovs/flowstats.go @@ -0,0 +1,96 @@ +// Copyright 2017 DigitalOcean. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ovs + +import ( + "errors" + "strconv" + "strings" +) + +var ( + // ErrInvalidFlowStats is returned when flow statistics from 'ovs-ofctl + // dump-aggregate' do not match the expected output format. + ErrInvalidFlowStats = errors.New("invalid flow statistics") +) + +// FlowStats contains a variety of statistics about an Open vSwitch port, +// including its port ID and numbers about packet receive and transmit +// operations. +type FlowStats struct { + PacketCount uint64 + ByteCount uint64 +} + +// UnmarshalText unmarshals a FlowStats from textual form. +func (f *FlowStats) UnmarshalText(b []byte) error { + // Make a copy per documentation for encoding.TextUnmarshaler. + s := string(b) + + // Constants only needed within this method, to avoid polluting the + // package namespace with generic names + const ( + packetCount = "packet_count" + byteCount = "byte_count" + flowCount = "flow_count" + ) + + // Find the index of packet count to find stats. + idx := strings.Index(s, packetCount) + if idx == -1 { + return ErrInvalidFlowStats + } + + // Assume the last three fields are packets, bytes, and flows, in that order. + ss := strings.Fields(s[idx:]) + fields := []string{ + packetCount, + byteCount, + flowCount, + } + + if len(ss) != len(fields) { + return ErrInvalidFlowStats + } + + var values []uint64 + for i := range ss { + // Split key from its integer value. + kv := strings.Split(ss[i], "=") + if len(kv) != 2 { + return ErrInvalidFlowStats + } + + // Verify keys appear in expected order. + if kv[0] != fields[i] { + return ErrInvalidFlowStats + } + + n, err := strconv.ParseUint(kv[1], 10, 64) + if err != nil { + return err + } + + values = append(values, n) + } + + *f = FlowStats{ + PacketCount: values[0], + ByteCount: values[1], + // Flow count unused. + } + + return nil +} diff --git a/vendor/github.com/digitalocean/go-openvswitch/ovs/match.go b/vendor/github.com/digitalocean/go-openvswitch/ovs/match.go new file mode 100644 index 000000000..d7059fb71 --- /dev/null +++ b/vendor/github.com/digitalocean/go-openvswitch/ovs/match.go @@ -0,0 +1,1026 @@ +// Copyright 2017 DigitalOcean. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ovs + +import ( + "bytes" + "encoding" + "fmt" + "net" + "strings" +) + +// Constants for use in Match names. +const ( + source = "src" + destination = "dst" + + sourceHardwareAddr = "sha" + targetHardwareAddr = "tha" + sourceProtocolAddr = "spa" + targetProtocolAddr = "tpa" +) + +// Constants of full Match names. +const ( + arpOp = "arp_op" + arpSHA = "arp_sha" + arpSPA = "arp_spa" + arpTHA = "arp_tha" + arpTPA = "arp_tpa" + conjID = "conj_id" + ctMark = "ct_mark" + ctState = "ct_state" + ctZone = "ct_zone" + dlSRC = "dl_src" + dlDST = "dl_dst" + dlType = "dl_type" + dlVLAN = "dl_vlan" + icmpType = "icmp_type" + ipv6DST = "ipv6_dst" + ipv6SRC = "ipv6_src" + ndSLL = "nd_sll" + ndTLL = "nd_tll" + ndTarget = "nd_target" + nwDST = "nw_dst" + nwProto = "nw_proto" + nwSRC = "nw_src" + tcpFlags = "tcp_flags" + tpDST = "tp_dst" + tpSRC = "tp_src" + tunID = "tun_id" + vlanTCI = "vlan_tci" +) + +// A Match is a type which can be marshaled into an OpenFlow packet matching +// statement. Matches can be used with Flows to match specific packet types +// and fields. +// +// Matches must also implement fmt.GoStringer for code generation purposes. +type Match interface { + encoding.TextMarshaler + fmt.GoStringer +} + +// DataLinkSource matches packets with a source hardware address and optional +// wildcard mask matching addr. +func DataLinkSource(addr string) Match { + return &dataLinkMatch{ + srcdst: source, + addr: addr, + } +} + +// DataLinkDestination matches packets with a destination hardware address +// and optional wildcard mask matching addr. +func DataLinkDestination(addr string) Match { + return &dataLinkMatch{ + srcdst: destination, + addr: addr, + } +} + +const ( + // ethernetAddrLen is the length in bytes of an ethernet hardware address. + ethernetAddrLen = 6 +) + +var _ Match = &dataLinkMatch{} + +// A dataLinkMatch is a Match returned by DataLink{Source,Destination}. +type dataLinkMatch struct { + srcdst string + addr string +} + +// GoString implements Match. +func (m *dataLinkMatch) GoString() string { + if m.srcdst == source { + return fmt.Sprintf("ovs.DataLinkSource(%q)", m.addr) + } + + return fmt.Sprintf("ovs.DataLinkDestination(%q)", m.addr) +} + +// MarshalText implements Match. +func (m *dataLinkMatch) MarshalText() ([]byte, error) { + // Split the string before possible wildcard mask + ss := strings.SplitN(m.addr, "/", 2) + + hwAddr, err := net.ParseMAC(ss[0]) + if err != nil { + return nil, err + } + if len(hwAddr) != ethernetAddrLen { + return nil, fmt.Errorf("hardware address must be %d octets, but got %d", + ethernetAddrLen, len(hwAddr)) + } + + if len(ss) == 1 { + // Address has no wildcard mask + return bprintf("dl_%s=%s", m.srcdst, hwAddr.String()), nil + } + + wildcard, err := net.ParseMAC(ss[1]) + if err != nil { + return nil, err + } + if len(wildcard) != ethernetAddrLen { + return nil, fmt.Errorf("wildcard mask must be %d octets, but got %d", + ethernetAddrLen, len(wildcard)) + } + + return bprintf("dl_%s=%s/%s", m.srcdst, hwAddr.String(), wildcard.String()), nil +} + +// DataLinkType matches packets with the specified EtherType. +func DataLinkType(etherType uint16) Match { + return &dataLinkTypeMatch{ + etherType: etherType, + } +} + +var _ Match = &dataLinkTypeMatch{} + +// A dataLinkTypeMatch is a Match returned by DataLinkType. +type dataLinkTypeMatch struct { + etherType uint16 +} + +// MarshalText implements Match. +func (m *dataLinkTypeMatch) MarshalText() ([]byte, error) { + return bprintf("%s=0x%04x", dlType, m.etherType), nil +} + +// GoString implements Match. +func (m *dataLinkTypeMatch) GoString() string { + return fmt.Sprintf("ovs.DataLinkType(0x%04x)", m.etherType) +} + +// VLANNone is a special value which indicates that DataLinkVLAN should only +// match packets with no VLAN tag specified. +const VLANNone = 0xffff + +// DataLinkVLAN matches packets with the specified VLAN ID matching vid. +func DataLinkVLAN(vid int) Match { + return &dataLinkVLANMatch{ + vid: vid, + } +} + +var _ Match = &dataLinkVLANMatch{} + +// A dataLinkVLANMatch is a Match returned by DataLinkVLAN. +type dataLinkVLANMatch struct { + vid int +} + +// MarshalText implements Match. +func (m *dataLinkVLANMatch) MarshalText() ([]byte, error) { + if !validVLANVID(m.vid) && m.vid != VLANNone { + return nil, errInvalidVLANVID + } + + if m.vid == VLANNone { + return bprintf("%s=0xffff", dlVLAN), nil + } + + return bprintf("%s=%d", dlVLAN, m.vid), nil +} + +// GoString implements Match. +func (m *dataLinkVLANMatch) GoString() string { + if m.vid == VLANNone { + return "ovs.DataLinkVLAN(ovs.VLANNone)" + } + + return fmt.Sprintf("ovs.DataLinkVLAN(%d)", m.vid) +} + +// NetworkSource matches packets with a source IPv4 address or IPv4 CIDR +// block matching ip. +func NetworkSource(ip string) Match { + return &networkMatch{ + srcdst: source, + ip: ip, + } +} + +// NetworkDestination matches packets with a destination IPv4 address or +// IPv4 CIDR block matching ip. +func NetworkDestination(ip string) Match { + return &networkMatch{ + srcdst: destination, + ip: ip, + } +} + +var _ Match = &networkMatch{} + +// A networkMatch is a Match returned by Network{Source,Destination}. +type networkMatch struct { + srcdst string + ip string +} + +// MarshalText implements Match. +func (m *networkMatch) MarshalText() ([]byte, error) { + return matchIPv4AddressOrCIDR(fmt.Sprintf("nw_%s", m.srcdst), m.ip) +} + +// GoString implements Match. +func (m *networkMatch) GoString() string { + if m.srcdst == source { + return fmt.Sprintf("ovs.NetworkSource(%q)", m.ip) + } + + return fmt.Sprintf("ovs.NetworkDestination(%q)", m.ip) +} + +type regMatch struct { + n int + val uint32 + mask uint32 +} + +// RegMatch matches flows' associated register fields +func RegMatch(n int, val, mask uint32) Match { + return ®Match{ + n: n, + val: val, + mask: mask, + } +} + +// MarshalText implements Match. +func (m *regMatch) MarshalText() ([]byte, error) { + if m.mask == 0 { + return []byte{}, nil + } else if m.mask == ^uint32(0) { + if m.val == 0 { + return bprintf("reg%d=0", m.n), nil + } + return bprintf("reg%d=0x%x", m.n, m.val), nil + } else { + return bprintf("reg%d=0x%x/0x%x", m.n, m.val, m.mask), nil + } +} + +// GoString implements Match. +func (m *regMatch) GoString() string { + return fmt.Sprintf("ovs.RegMatch(%q, %q, %q)", m.n, m.val, m.mask) +} + +// ConjunctionID matches flows that have matched all dimension of a conjunction +// inside of the openflow table. +func ConjunctionID(id uint32) Match { + return &conjunctionIDMatch{ + id: id, + } +} + +// A conjunctionIDMatch is a Match returned by ConjunctionID +type conjunctionIDMatch struct { + id uint32 +} + +// MarshalText implements Match. +func (m *conjunctionIDMatch) MarshalText() ([]byte, error) { + return bprintf("conj_id=%v", m.id), nil +} + +// GoString implements Match. +func (m *conjunctionIDMatch) GoString() string { + return fmt.Sprintf("ovs.ConjunctionID(%v)", m.id) +} + +// NetworkProtocol matches packets with the specified IP or IPv6 protocol +// number matching num. For example, specifying 1 when a Flow's Protocol +// is IPv4 matches ICMP packets, or 58 when Protocol is IPv6 matches ICMPv6 +// packets. +func NetworkProtocol(num uint8) Match { + return &networkProtocolMatch{ + num: num, + } +} + +var _ Match = &networkProtocolMatch{} + +// A networkProtocolMatch is a Match returned by NetworkProtocol. +type networkProtocolMatch struct { + num uint8 +} + +// MarshalText implements Match. +func (m *networkProtocolMatch) MarshalText() ([]byte, error) { + return bprintf("%s=%d", nwProto, m.num), nil +} + +// GoString implements Match. +func (m *networkProtocolMatch) GoString() string { + return fmt.Sprintf("ovs.NetworkProtocol(%d)", m.num) +} + +// IPv6Source matches packets with a source IPv6 address or IPv6 CIDR +// block matching ip. +func IPv6Source(ip string) Match { + return &ipv6Match{ + srcdst: source, + ip: ip, + } +} + +// IPv6Destination matches packets with a destination IPv6 address or +// IPv6 CIDR block matching ip. +func IPv6Destination(ip string) Match { + return &ipv6Match{ + srcdst: destination, + ip: ip, + } +} + +var _ Match = &ipv6Match{} + +// An ipv6Match is a Match returned by IPv6{Source,Destination}. +type ipv6Match struct { + srcdst string + ip string +} + +// MarshalText implements Match. +func (m *ipv6Match) MarshalText() ([]byte, error) { + return matchIPv6AddressOrCIDR(fmt.Sprintf("ipv6_%s", m.srcdst), m.ip) +} + +// GoString implements Match. +func (m *ipv6Match) GoString() string { + if m.srcdst == source { + return fmt.Sprintf("ovs.IPv6Source(%q)", m.ip) + } + + return fmt.Sprintf("ovs.IPv6Destination(%q)", m.ip) +} + +// ICMPType matches packets with the specified ICMP type matching typ. +func ICMPType(typ uint8) Match { + return &icmpTypeMatch{ + typ: typ, + } +} + +var _ Match = &icmpTypeMatch{} + +// An icmpTypeMatch is a Match returned by ICMPType. +type icmpTypeMatch struct { + typ uint8 +} + +// MarshalText implements Match. +func (m *icmpTypeMatch) MarshalText() ([]byte, error) { + return bprintf("%s=%d", icmpType, m.typ), nil +} + +// GoString implements Match. +func (m *icmpTypeMatch) GoString() string { + return fmt.Sprintf("ovs.ICMPType(%d)", m.typ) +} + +// NeighborDiscoveryTarget matches packets with an IPv6 neighbor discovery target +// IPv6 address or IPv6 CIDR block matching ip. +func NeighborDiscoveryTarget(ip string) Match { + return &neighborDiscoveryTargetMatch{ + ip: ip, + } +} + +var _ Match = &neighborDiscoveryTargetMatch{} + +// A neighborDiscoveryTargetMatch is a Match returned by NeighborDiscoveryTarget. +type neighborDiscoveryTargetMatch struct { + ip string +} + +// MarshalText implements Match. +func (m *neighborDiscoveryTargetMatch) MarshalText() ([]byte, error) { + return matchIPv6AddressOrCIDR(ndTarget, m.ip) +} + +// GoString implements Match. +func (m *neighborDiscoveryTargetMatch) GoString() string { + return fmt.Sprintf("ovs.NeighborDiscoveryTarget(%q)", m.ip) +} + +// NeighborDiscoverySourceLinkLayer matches packets with an IPv6 neighbor +// solicitation source link-layer address matching addr. +func NeighborDiscoverySourceLinkLayer(addr net.HardwareAddr) Match { + return &neighborDiscoveryLinkLayerMatch{ + srctgt: source, + addr: addr, + } +} + +// NeighborDiscoveryTargetLinkLayer matches packets with an IPv6 neighbor +// solicitation target link-layer address matching addr. +func NeighborDiscoveryTargetLinkLayer(addr net.HardwareAddr) Match { + return &neighborDiscoveryLinkLayerMatch{ + srctgt: destination, + addr: addr, + } +} + +var _ Match = &neighborDiscoveryLinkLayerMatch{} + +// A neighborDiscoveryLinkLayerMatch is a Match returned by DataLinkVLAN. +type neighborDiscoveryLinkLayerMatch struct { + srctgt string + addr net.HardwareAddr +} + +// MarshalText implements Match. +func (m *neighborDiscoveryLinkLayerMatch) MarshalText() ([]byte, error) { + if m.srctgt == source { + return matchEthernetHardwareAddress(ndSLL, m.addr) + } + + return matchEthernetHardwareAddress(ndTLL, m.addr) +} + +// GoString implements Match. +func (m *neighborDiscoveryLinkLayerMatch) GoString() string { + syntax := hwAddrGoString(m.addr) + + if m.srctgt == source { + return fmt.Sprintf("ovs.NeighborDiscoverySourceLinkLayer(%s)", syntax) + } + + return fmt.Sprintf("ovs.NeighborDiscoveryTargetLinkLayer(%s)", syntax) +} + +// ARPSourceHardwareAddress matches packets with an ARP source hardware address +// (SHA) matching addr. +func ARPOp(op uint16) Match { + return &arpOpMatch{ + op: op, + } +} + +// ARPSourceHardwareAddress matches packets with an ARP source hardware address +// (SHA) matching addr. +func ARPSourceHardwareAddress(addr net.HardwareAddr) Match { + return &arpHardwareAddressMatch{ + srctgt: source, + addr: addr, + } +} + +// ARPTargetHardwareAddress matches packets with an ARP target hardware address +// (THA) matching addr. +func ARPTargetHardwareAddress(addr net.HardwareAddr) Match { + return &arpHardwareAddressMatch{ + srctgt: destination, + addr: addr, + } +} + +var _ Match = &arpHardwareAddressMatch{} + +// An arpHardwareAddressMatch is a Match returned by ARP{Source,Target}HardwareAddress. +type arpHardwareAddressMatch struct { + srctgt string + addr net.HardwareAddr +} + +// MarshalText implements Match. +func (m *arpHardwareAddressMatch) MarshalText() ([]byte, error) { + if m.srctgt == source { + return matchEthernetHardwareAddress(arpSHA, m.addr) + } + + return matchEthernetHardwareAddress(arpTHA, m.addr) +} + +// GoString implements Match. +func (m *arpHardwareAddressMatch) GoString() string { + syntax := hwAddrGoString(m.addr) + + if m.srctgt == source { + return fmt.Sprintf("ovs.ARPSourceHardwareAddress(%s)", syntax) + } + + return fmt.Sprintf("ovs.ARPTargetHardwareAddress(%s)", syntax) +} + +// An arpOpMatch is a Match returned by ARPOp. +type arpOpMatch struct { + op uint16 +} + +// MarshalText implements Match. +func (m *arpOpMatch) MarshalText() ([]byte, error) { + return bprintf("%s=%d", arpOp, m.op), nil +} + +// GoString implements Match. +func (m *arpOpMatch) GoString() string { + return fmt.Sprintf("ovs.ARPOp(%d)", m.op) +} + +// ARPSourceProtocolAddress matches packets with an ARP source protocol address +// (SPA) IPv4 address or IPv4 CIDR block matching addr. +func ARPSourceProtocolAddress(ip string) Match { + return &arpProtocolAddressMatch{ + srctgt: source, + ip: ip, + } +} + +// ARPTargetProtocolAddress matches packets with an ARP target protocol address +// (TPA) IPv4 address or IPv4 CIDR block matching addr. +func ARPTargetProtocolAddress(ip string) Match { + return &arpProtocolAddressMatch{ + srctgt: destination, + ip: ip, + } +} + +var _ Match = &arpProtocolAddressMatch{} + +// An arpProtocolAddressMatch is a Match returned by ARP{Source,Target}ProtocolAddress. +type arpProtocolAddressMatch struct { + srctgt string + ip string +} + +// MarshalText implements Match. +func (m *arpProtocolAddressMatch) MarshalText() ([]byte, error) { + if m.srctgt == source { + return matchIPv4AddressOrCIDR(arpSPA, m.ip) + } + + return matchIPv4AddressOrCIDR(arpTPA, m.ip) +} + +// GoString implements Match. +func (m *arpProtocolAddressMatch) GoString() string { + if m.srctgt == source { + return fmt.Sprintf("ovs.ARPSourceProtocolAddress(%q)", m.ip) + } + + return fmt.Sprintf("ovs.ARPTargetProtocolAddress(%q)", m.ip) +} + +// TransportSourcePort matches packets with a transport layer (TCP/UDP) source +// port matching port. +func TransportSourcePort(port uint16) Match { + return &transportPortMatch{ + srcdst: source, + port: port, + mask: 0, + } +} + +// TransportDestinationPort matches packets with a transport layer (TCP/UDP) +// destination port matching port. +func TransportDestinationPort(port uint16) Match { + return &transportPortMatch{ + srcdst: destination, + port: port, + mask: 0, + } +} + +// TransportSourceMaskedPort matches packets with a transport layer (TCP/UDP) +// source port matching a masked port range. +func TransportSourceMaskedPort(port uint16, mask uint16) Match { + return &transportPortMatch{ + srcdst: source, + port: port, + mask: mask, + } +} + +// TransportDestinationMaskedPort matches packets with a transport layer (TCP/UDP) +// destination port matching a masked port range. +func TransportDestinationMaskedPort(port uint16, mask uint16) Match { + return &transportPortMatch{ + srcdst: destination, + port: port, + mask: mask, + } +} + +// A transportPortMatch is a Match returned by Transport{Source,Destination}Port. +type transportPortMatch struct { + srcdst string + port uint16 + mask uint16 +} + +var _ Match = &transportPortMatch{} + +// A TransportPortRanger represents a port range that can be expressed as an array of bitwise matches. +type TransportPortRanger interface { + MaskedPorts() ([]Match, error) +} + +// A TransportPortRange reprsents the start and end values of a transport protocol port range. +type transportPortRange struct { + srcdst string + startPort uint16 + endPort uint16 +} + +// TransportDestinationPortRange represent a port range intended for a transport protocol destination port. +func TransportDestinationPortRange(startPort uint16, endPort uint16) TransportPortRanger { + return &transportPortRange{ + srcdst: destination, + startPort: startPort, + endPort: endPort, + } +} + +// TransportSourcePortRange represent a port range intended for a transport protocol source port. +func TransportSourcePortRange(startPort uint16, endPort uint16) TransportPortRanger { + return &transportPortRange{ + srcdst: source, + startPort: startPort, + endPort: endPort, + } +} + +// MaskedPorts returns the represented port ranges as an array of bitwise matches. +func (pr *transportPortRange) MaskedPorts() ([]Match, error) { + portRange := PortRange{ + Start: pr.startPort, + End: pr.endPort, + } + + bitRanges, err := portRange.BitwiseMatch() + if err != nil { + return nil, err + } + + var ports []Match + + for _, br := range bitRanges { + maskedPortRange := &transportPortMatch{ + srcdst: pr.srcdst, + port: br.Value, + mask: br.Mask, + } + ports = append(ports, maskedPortRange) + } + + return ports, nil +} + +// MarshalText implements Match. +func (m *transportPortMatch) MarshalText() ([]byte, error) { + return matchTransportPort(m.srcdst, m.port, m.mask) +} + +// GoString implements Match. +func (m *transportPortMatch) GoString() string { + if m.mask > 0 { + if m.srcdst == source { + return fmt.Sprintf("ovs.TransportSourceMaskedPort(%#x, %#x)", m.port, m.mask) + } + + return fmt.Sprintf("ovs.TransportDestinationMaskedPort(%#x, %#x)", m.port, m.mask) + } + + if m.srcdst == source { + return fmt.Sprintf("ovs.TransportSourcePort(%d)", m.port) + } + + return fmt.Sprintf("ovs.TransportDestinationPort(%d)", m.port) +} + +// A vlanTCIMatch is a Match returned by VLANTCI. +type vlanTCIMatch struct { + tci uint16 + mask uint16 +} + +// VLANTCI matches packets based on their VLAN tag control information, using +// the specified TCI and optional mask value. +func VLANTCI(tci, mask uint16) Match { + return &vlanTCIMatch{ + tci: tci, + mask: mask, + } +} + +// MarshalText implements Match. +func (m *vlanTCIMatch) MarshalText() ([]byte, error) { + if m.mask != 0 { + return bprintf("%s=0x%04x/0x%04x", vlanTCI, m.tci, m.mask), nil + } + + return bprintf("%s=0x%04x", vlanTCI, m.tci), nil +} + +// GoString implements Match. +func (m *vlanTCIMatch) GoString() string { + return fmt.Sprintf("ovs.VLANTCI(0x%04x, 0x%04x)", m.tci, m.mask) +} + +// A connectionTrackingMarkMatch is a Match returned by ConnectionTrackingMark. +type connectionTrackingMarkMatch struct { + mark uint32 + mask uint32 +} + +// ConnectionTrackingMark matches a metadata associated with a connection tracking entry +func ConnectionTrackingMark(mark, mask uint32) Match { + return &connectionTrackingMarkMatch{ + mark: mark, + mask: mask, + } +} + +// MarshalText implements Match. +func (m *connectionTrackingMarkMatch) MarshalText() ([]byte, error) { + if m.mask != 0 { + return bprintf("%s=0x%08x/0x%08x", ctMark, m.mark, m.mask), nil + } + + return bprintf("%s=0x%08x", ctMark, m.mark), nil +} + +// GoString implements Match. +func (m *connectionTrackingMarkMatch) GoString() string { + return fmt.Sprintf("ovs.ConnectionTrackingMark(0x%08x, 0x%08x)", m.mark, m.mask) +} + +// A connectionTrackingZoneMatch is a Match returned by ConnectionTrackingZone. +type connectionTrackingZoneMatch struct { + zone uint16 +} + +// ConnectionTrackingZone is a mechanism to define separate connection tracking contexts. +func ConnectionTrackingZone(zone uint16) Match { + return &connectionTrackingZoneMatch{ + zone: zone, + } +} + +// MarshalText implements Match. +func (m *connectionTrackingZoneMatch) MarshalText() ([]byte, error) { + return bprintf("%s=%d", ctZone, m.zone), nil +} + +// GoString implements Match. +func (m *connectionTrackingZoneMatch) GoString() string { + return fmt.Sprintf("ovs.ConnectionTrackingZone(%d)", m.zone) +} + +// ConnectionTrackingState matches packets using their connection state, when +// connection tracking is enabled on the host. Use the SetState and UnsetState +// functions to populate the parameter list for this function. +func ConnectionTrackingState(state ...string) Match { + return &connectionTrackingMatch{ + state: state, + } +} + +var _ Match = &connectionTrackingMatch{} + +// A connectionTrackingMatch is a Match returned by ConnectionTrackingState. +type connectionTrackingMatch struct { + state []string +} + +// MarshalText implements Match. +func (m *connectionTrackingMatch) MarshalText() ([]byte, error) { + return bprintf("%s=%s", ctState, strings.Join(m.state, "")), nil +} + +// GoString implements Match. +func (m *connectionTrackingMatch) GoString() string { + buf := bytes.NewBuffer(nil) + for i, s := range m.state { + _, _ = buf.WriteString(fmt.Sprintf("%q", s)) + + if i != len(m.state)-1 { + _, _ = buf.WriteString(", ") + } + } + + return fmt.Sprintf("ovs.ConnectionTrackingState(%s)", buf.String()) +} + +// CTState is a connection tracking state, which can be used with the +// ConnectionTrackingState function. +type CTState string + +// List of common CTState constants available in OVS 2.5. Reference the +// ovs-ofctl man-page for a description of each one. +const ( + CTStateNew CTState = "new" + CTStateEstablished CTState = "est" + CTStateRelated CTState = "rel" + CTStateReply CTState = "rpl" + CTStateInvalid CTState = "inv" + CTStateTracked CTState = "trk" +) + +// SetState sets the specified CTState flag. This helper should be used +// with ConnectionTrackingState. +func SetState(state CTState) string { + return fmt.Sprintf("+%s", state) +} + +// UnsetState unsets the specified CTState flag. This helper should be used +// with ConnectionTrackingState. +func UnsetState(state CTState) string { + return fmt.Sprintf("-%s", state) +} + +// TCPFlags matches packets using their enabled TCP flags, when matching TCP +// flags on a TCP segment. Use the SetTCPFlag and UnsetTCPFlag functions to +// populate the parameter list for this function. +func TCPFlags(flags ...string) Match { + return &tcpFlagsMatch{ + flags: flags, + } +} + +var _ Match = &tcpFlagsMatch{} + +// A tcpFlagsMatch is a Match returned by TCPFlags. +type tcpFlagsMatch struct { + flags []string +} + +// MarshalText implements Match. +func (m *tcpFlagsMatch) MarshalText() ([]byte, error) { + return bprintf("%s=%s", tcpFlags, strings.Join(m.flags, "")), nil +} + +// GoString implements Match. +func (m *tcpFlagsMatch) GoString() string { + buf := bytes.NewBuffer(nil) + for i, s := range m.flags { + _, _ = buf.WriteString(fmt.Sprintf("%q", s)) + + if i != len(m.flags)-1 { + _, _ = buf.WriteString(", ") + } + } + + return fmt.Sprintf("ovs.TCPFlags(%s)", buf.String()) +} + +// TCPFlag represents a flag in the TCP header, which can be used with the +// TCPFlags function. +type TCPFlag string + +// RFC 793 TCP Flags +const ( + TCPFlagURG TCPFlag = "urg" + TCPFlagACK TCPFlag = "ack" + TCPFlagPSH TCPFlag = "psh" + TCPFlagRST TCPFlag = "rst" + TCPFlagSYN TCPFlag = "syn" + TCPFlagFIN TCPFlag = "fin" +) + +// SetTCPFlag sets the specified TCPFlag. This helper should be used +// with TCPFlags. +func SetTCPFlag(flag TCPFlag) string { + return fmt.Sprintf("+%s", flag) +} + +// UnsetTCPFlag unsets the specified TCPFlag. This helper should be used +// with TCPFlags. +func UnsetTCPFlag(flag TCPFlag) string { + return fmt.Sprintf("-%s", flag) +} + +// TunnelID returns a Match that matches the given ID exactly. +func TunnelID(id uint64) Match { + return &tunnelIDMatch{ + id: id, + mask: 0, + } +} + +// TunnelIDWithMask returns a Match with specified ID and mask. +func TunnelIDWithMask(id, mask uint64) Match { + return &tunnelIDMatch{ + id: id, + mask: mask, + } +} + +var _ Match = &tunnelIDMatch{} + +// A tunnelIDMatch is a Match against a tunnel ID. +type tunnelIDMatch struct { + id uint64 + mask uint64 +} + +// GoString implements Match. +func (m *tunnelIDMatch) GoString() string { + if m.mask > 0 { + return fmt.Sprintf("ovs.TunnelIDWithMask(%#x, %#x)", m.id, m.mask) + } + + return fmt.Sprintf("ovs.TunnelID(%#x)", m.id) +} + +// MarshalText implements Match. +func (m *tunnelIDMatch) MarshalText() ([]byte, error) { + if m.mask == 0 { + return bprintf("%s=%#x", tunID, m.id), nil + } + + return bprintf("%s=%#x/%#x", tunID, m.id, m.mask), nil +} + +// matchIPv4AddressOrCIDR attempts to create a Match using the specified key +// and input string, which could be interpreted as an IPv4 address or IPv4 +// CIDR block. +func matchIPv4AddressOrCIDR(key string, ip string) ([]byte, error) { + errInvalidIPv4 := fmt.Errorf("%q is not a valid IPv4 address or IPv4 CIDR block", ip) + + if ipAddr, _, err := net.ParseCIDR(ip); err == nil { + if ipAddr.To4() == nil { + return nil, errInvalidIPv4 + } + + return bprintf("%s=%s", key, ip), nil + } + + if ipAddr := net.ParseIP(ip); ipAddr != nil { + if ipAddr.To4() == nil { + return nil, errInvalidIPv4 + } + + return bprintf("%s=%s", key, ipAddr.String()), nil + } + + return nil, errInvalidIPv4 +} + +// matchIPv6AddressOrCIDR attempts to create a Match using the specified key +// and input string, which could be interpreted as an IPv6 address or IPv6 +// CIDR block. +func matchIPv6AddressOrCIDR(key string, ip string) ([]byte, error) { + errInvalidIPv6 := fmt.Errorf("%q is not a valid IPv6 address or IPv6 CIDR block", ip) + + if ipAddr, _, err := net.ParseCIDR(ip); err == nil { + if ipAddr.To16() == nil || ipAddr.To4() != nil { + return nil, errInvalidIPv6 + } + + return bprintf("%s=%s", key, ip), nil + } + + if ipAddr := net.ParseIP(ip); ipAddr != nil { + if ipAddr.To16() == nil || ipAddr.To4() != nil { + return nil, errInvalidIPv6 + } + + return bprintf("%s=%s", key, ipAddr.String()), nil + } + + return nil, errInvalidIPv6 +} + +// matchEthernetHardwareAddress attempts to create a Match using the specified +// key and input hardware address, which must be a 6-octet Ethernet hardware +// address. +func matchEthernetHardwareAddress(key string, addr net.HardwareAddr) ([]byte, error) { + if len(addr) != ethernetAddrLen { + return nil, fmt.Errorf("hardware address must be %d octets, but got %d", + ethernetAddrLen, len(addr)) + } + + return bprintf("%s=%s", key, addr.String()), nil +} + +// matchTransportPort is the common implementation for +// Transport{Source,Destination}Port. +func matchTransportPort(srcdst string, port uint16, mask uint16) ([]byte, error) { + // No mask specified + if mask == 0 { + return bprintf("tp_%s=%d", srcdst, port), nil + } + + return bprintf("tp_%s=0x%04x/0x%04x", srcdst, port, mask), nil +} diff --git a/vendor/github.com/digitalocean/go-openvswitch/ovs/matchflow.go b/vendor/github.com/digitalocean/go-openvswitch/ovs/matchflow.go new file mode 100644 index 000000000..4db1a1ec6 --- /dev/null +++ b/vendor/github.com/digitalocean/go-openvswitch/ovs/matchflow.go @@ -0,0 +1,169 @@ +// Copyright 2017 DigitalOcean. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ovs + +import ( + "bytes" + "errors" + "fmt" + "strconv" + "strings" +) + +const ( + // AnyTable is a special table value to match flows in any table. + AnyTable = -1 +) + +var ( + // errEmptyMatchFlow is returned when a MatchFlow has no arguments. + errEmptyMatchFlow = errors.New("match flow is empty") +) + +// TODO(mdlayher): it would be nice if MatchFlow was just a Flow. + +// A MatchFlow is an OpenFlow flow intended for flow deletion. It can be marshaled to its textual +// form for use with Open vSwitch. +type MatchFlow struct { + Strict bool + InPort int + Priority int + Protocol Protocol + Matches []Match + Table int + + // Cookie indicates a cookie value to use when matching flows. + Cookie uint64 + + // CookieMask is a mask used alongside Cookie to enable matching flows + // which match a mask. If CookieMask is not set, Cookie will be matched + // exactly. + CookieMask uint64 +} + +var _ error = &MatchFlowError{} + +// A MatchFlowError is an error encountered while marshaling or unmarshaling +// a MatchFlow. +type MatchFlowError struct { + // Str indicates the string, if any, that caused the flow to + // fail while unmarshaling. + Str string + + // Err indicates the error that halted flow marshaling or unmarshaling. + Err error +} + +// Error returns the string representation of a MatchFlowError. +func (e *MatchFlowError) Error() string { + if e.Str == "" { + return e.Err.Error() + } + + return fmt.Sprintf("flow error due to string %q: %v", + e.Str, e.Err) +} + +// MarshalText marshals a MatchFlow into its textual form. +func (f *MatchFlow) MarshalText() ([]byte, error) { + matches, err := f.marshalMatches() + if err != nil { + return nil, err + } + + var b []byte + + if f.Strict { + b = append(b, priority+"="...) + b = strconv.AppendInt(b, int64(f.Priority), 10) + b = append(b, ',') + } + if f.Protocol != "" { + b = append(b, f.Protocol...) + b = append(b, ',') + } + + if f.InPort != 0 { + b = append(b, inPort+"="...) + + // Special case, InPortLOCAL is converted to the literal string LOCAL + if f.InPort == PortLOCAL { + b = append(b, portLOCAL...) + } else { + b = strconv.AppendInt(b, int64(f.InPort), 10) + } + b = append(b, ',') + } + + if len(matches) > 0 { + b = append(b, strings.Join(matches, ",")...) + b = append(b, ',') + } + + if f.Cookie > 0 { + // Hexadecimal cookies and masks are much easier to read. + b = append(b, cookie+"="...) + b = append(b, paddedHexUint64(f.Cookie)...) + b = append(b, '/') + + if f.CookieMask == 0 { + b = append(b, "-1"...) + } else { + b = append(b, paddedHexUint64(f.CookieMask)...) + } + + b = append(b, ',') + } + + if f.Table != AnyTable { + b = append(b, table+"="...) + b = strconv.AppendInt(b, int64(f.Table), 10) + } + + b = bytes.Trim(b, ",") + + if len(b) == 0 { + return nil, &MatchFlowError{ + Err: errEmptyMatchFlow, + } + } + + return b, nil +} + +// marshalMatches marshals all Matches in a MatchFlow to their text form. +func (f *MatchFlow) marshalMatches() ([]string, error) { + fns := make([]func() ([]byte, error), 0, len(f.Matches)) + for _, fn := range f.Matches { + fns = append(fns, fn.MarshalText) + } + + return f.marshalFunctions(fns) +} + +// marshalFunctions marshals a slice of functions to their text form. +func (f *MatchFlow) marshalFunctions(fns []func() ([]byte, error)) ([]string, error) { + out := make([]string, 0, len(fns)) + for _, fn := range fns { + o, err := fn() + if err != nil { + return nil, err + } + + out = append(out, string(o)) + } + + return out, nil +} diff --git a/vendor/github.com/digitalocean/go-openvswitch/ovs/matchparser.go b/vendor/github.com/digitalocean/go-openvswitch/ovs/matchparser.go new file mode 100644 index 000000000..27f4c7f3c --- /dev/null +++ b/vendor/github.com/digitalocean/go-openvswitch/ovs/matchparser.go @@ -0,0 +1,414 @@ +// Copyright 2017 DigitalOcean. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ovs + +import ( + "bytes" + "errors" + "fmt" + "math" + "net" + "strconv" + "strings" +) + +// parseMatch creates a Match function from the input string. +func parseMatch(key string, value string) (Match, error) { + switch key { + case arpOp: + return parseIntMatch(key, value, 255) + case arpSHA, arpTHA, ndSLL, ndTLL: + return parseMACMatch(key, value) + case icmpType, nwProto: + return parseIntMatch(key, value, math.MaxUint8) + case ctZone: + return parseIntMatch(key, value, math.MaxUint16) + case tpSRC, tpDST: + return parsePort(key, value, math.MaxUint16) + case conjID: + return parseIntMatch(key, value, math.MaxUint32) + case arpSPA: + return ARPSourceProtocolAddress(value), nil + case arpTPA: + return ARPTargetProtocolAddress(value), nil + case ctState: + return parseCTState(value) + case tcpFlags: + return parseTCPFlags(value) + case dlSRC: + return DataLinkSource(value), nil + case dlDST: + return DataLinkDestination(value), nil + case dlType: + etherType, err := parseHexUint16(value) + if err != nil { + return nil, err + } + + return DataLinkType(etherType), nil + case dlVLAN: + return parseDataLinkVLAN(value) + case ndTarget: + return NeighborDiscoveryTarget(value), nil + case ipv6SRC: + return IPv6Source(value), nil + case ipv6DST: + return IPv6Destination(value), nil + case nwSRC: + return NetworkSource(value), nil + case nwDST: + return NetworkDestination(value), nil + case vlanTCI: + return parseVLANTCI(value) + case ctMark: + return parseCTMark(value) + case tunID: + return parseTunID(value) + } + + if strings.HasPrefix(key, "reg") { + return parseRegMatch(key, value) + } + + return nil, fmt.Errorf("no match parser found for %s=%s", key, value) +} + +func parseRegMatch(key, value string) (Match, error) { + n, err := strconv.Atoi(key[3:]) + if err != nil { + return nil, err + } + values := []uint32{} + for _, s := range strings.Split(value, "/") { + if !strings.HasPrefix(s, hexPrefix) { + v, err := strconv.Atoi(s) + if err != nil { + return nil, err + } + + values = append(values, uint32(v)) + continue + } + + v, err := parseHexUint32(s) + if err != nil { + return nil, err + } + + values = append(values, v) + } + switch len(values) { + case 1: + return RegMatch(n, values[0], ^uint32(0)), nil + case 2: + return RegMatch(n, values[0], values[1]), nil + // Match had too many parts, e.g. "vlan_tci=10/10/10" + default: + return nil, fmt.Errorf("invalid reg%d match: %q", n, value) + } +} + +// parseClampInt calls strconv.Atoi on s, and then ensures that s is less than +// or equal to the integer specified by max. +func parseClampInt(s string, max int) (int, error) { + t, err := strconv.Atoi(s) + if err != nil { + return 0, err + } + if t > max { + return 0, fmt.Errorf("integer %d too large; %d > %d", t, t, max) + } + + return t, nil +} + +// parseIntMatch parses an integer Match value from the input key and value, +// with a maximum possible value of max. +func parseIntMatch(key string, value string, max int) (Match, error) { + t, err := parseClampInt(value, max) + if err != nil { + return nil, err + } + + switch key { + case arpOp: + return ARPOp(uint16(t)), nil + case icmpType: + return ICMPType(uint8(t)), nil + case nwProto: + return NetworkProtocol(uint8(t)), nil + case ctZone: + return ConnectionTrackingZone(uint16(t)), nil + case conjID: + return ConjunctionID(uint32(t)), nil + } + + return nil, fmt.Errorf("no action matched for %s=%s", key, value) +} + +// parsePort parses a port or port/mask Match value from the input key and value, +// with a maximum possible value of max. +func parsePort(key string, value string, max int) (Match, error) { + + var values []uint64 + //Split the string + ss := strings.Split(value, "/") + + //If input is just port + switch len(ss) { + case 1: + val, err := parseClampInt(value, max) + if err != nil { + return nil, err + } + values = append(values, uint64(val)) + values = append(values, 0) + // If input is port/mask + case 2: + for _, s := range ss { + val, err := parseHexUint64(s) + if err != nil { + return nil, err + } + // Return error if val > 65536 (uint16) + if val > uint64(max) { + return nil, fmt.Errorf("integer %d too large; %d > %d", val, val, max) + } + + values = append(values, val) + } + default: + return nil, fmt.Errorf("invalid value, no action matched for %s=%s", key, value) + } + + switch key { + case tpSRC: + return TransportSourceMaskedPort(uint16(values[0]), uint16(values[1])), nil + case tpDST: + return TransportDestinationMaskedPort(uint16(values[0]), uint16(values[1])), nil + } + // Return error if input is invalid + return nil, fmt.Errorf("no action matched for %s=%s", key, value) +} + +// parseMACMatch parses a MAC address Match value from the input key and value. +func parseMACMatch(key string, value string) (Match, error) { + mac, err := net.ParseMAC(value) + if err != nil { + return nil, err + } + + switch key { + case arpSHA: + return ARPSourceHardwareAddress(mac), nil + case arpTHA: + return ARPTargetHardwareAddress(mac), nil + case ndSLL: + return NeighborDiscoverySourceLinkLayer(mac), nil + case ndTLL: + return NeighborDiscoveryTargetLinkLayer(mac), nil + } + + return nil, fmt.Errorf("no action matched for %s=%s", key, value) +} + +// parseCTState parses a series of connection tracking values into a Match. +func parseCTState(value string) (Match, error) { + if len(value)%4 != 0 { + return nil, errors.New("ct_state length must be divisible by 4") + } + + var buf bytes.Buffer + var states []string + + for i, r := range value { + if i != 0 && i%4 == 0 { + states = append(states, buf.String()) + buf.Reset() + } + + _, _ = buf.WriteRune(r) + } + states = append(states, buf.String()) + + return ConnectionTrackingState(states...), nil +} + +// parseTCPFlags parses a series of TCP flags into a Match. Open vSwitch's representation +// of These TCP flags are outlined in the ovs-field(7) man page, +func parseTCPFlags(value string) (Match, error) { + if len(value)%4 != 0 { + return nil, errors.New("tcp_flags length must be divisible by 4") + } + + var buf bytes.Buffer + var flags []string + + for i, r := range value { + if i != 0 && i%4 == 0 { + flags = append(flags, buf.String()) + buf.Reset() + } + + _, _ = buf.WriteRune(r) + } + flags = append(flags, buf.String()) + + return TCPFlags(flags...), nil +} + +// hexPrefix denotes that a string integer is in hex format. +const hexPrefix = "0x" + +// parseDataLinkVLAN parses a DataLinkVLAN Match from value. +func parseDataLinkVLAN(value string) (Match, error) { + if !strings.HasPrefix(value, hexPrefix) { + vlan, err := strconv.Atoi(value) + if err != nil { + return nil, err + } + + return DataLinkVLAN(vlan), nil + } + + vlan, err := parseHexUint16(value) + if err != nil { + return nil, err + } + + return DataLinkVLAN(int(vlan)), nil +} + +// parseVLANTCI parses a VLANTCI Match from value. +func parseVLANTCI(value string) (Match, error) { + var values []uint16 + for _, s := range strings.Split(value, "/") { + if !strings.HasPrefix(s, hexPrefix) { + v, err := strconv.Atoi(s) + if err != nil { + return nil, err + } + + values = append(values, uint16(v)) + continue + } + + v, err := parseHexUint16(s) + if err != nil { + return nil, err + } + + values = append(values, v) + } + + switch len(values) { + case 1: + return VLANTCI(values[0], 0), nil + case 2: + return VLANTCI(values[0], values[1]), nil + // Match had too many parts, e.g. "vlan_tci=10/10/10" + default: + return nil, fmt.Errorf("invalid vlan_tci match: %q", value) + } +} + +// parseCTMark parses a CTMark Match from value. +func parseCTMark(value string) (Match, error) { + var values []uint32 + for _, s := range strings.Split(value, "/") { + if !strings.HasPrefix(s, hexPrefix) { + v, err := strconv.Atoi(s) + if err != nil { + return nil, err + } + + values = append(values, uint32(v)) + continue + } + + v, err := parseHexUint32(s) + if err != nil { + return nil, err + } + + values = append(values, v) + } + + switch len(values) { + case 1: + return ConnectionTrackingMark(values[0], 0), nil + case 2: + return ConnectionTrackingMark(values[0], values[1]), nil + // Match had too many parts, e.g. "ct_mark=10/10/10" + default: + return nil, fmt.Errorf("invalid ct_mark match: %q", value) + } +} + +// parseTunID parses a tunID Match from value. +func parseTunID(value string) (Match, error) { + var values []uint64 + for _, s := range strings.Split(value, "/") { + if !strings.HasPrefix(s, hexPrefix) { + v, err := strconv.Atoi(s) + if err != nil { + return nil, err + } + + values = append(values, uint64(v)) + continue + } + + v, err := parseHexUint64(s) + if err != nil { + return nil, err + } + + values = append(values, v) + } + + switch len(values) { + case 1: + return TunnelID(values[0]), nil + case 2: + return TunnelIDWithMask(values[0], values[1]), nil + // Match had too many parts, e.g. "tun_id=10/10/10" + default: + return nil, fmt.Errorf("invalid tun_id match: %q", value) + } +} + +// parseHexUint16 parses a uint16 value from a hexadecimal string. +func parseHexUint16(value string) (uint16, error) { + val, err := strconv.ParseUint(strings.TrimPrefix(value, hexPrefix), 16, 32) + if err != nil { + return 0, err + } + return uint16(val), nil +} + +// parseHexUint32 parses a uint32 value from a hexadecimal string. +func parseHexUint32(value string) (uint32, error) { + val, err := strconv.ParseUint(strings.TrimPrefix(value, hexPrefix), 16, 32) + if err != nil { + return 0, err + } + return uint32(val), nil +} + +// parseHexUint64 parses a uint64 value from a hexadecimal string. +func parseHexUint64(value string) (uint64, error) { + return strconv.ParseUint(strings.TrimPrefix(value, hexPrefix), 16, 64) +} diff --git a/vendor/github.com/digitalocean/go-openvswitch/ovs/openflow.go b/vendor/github.com/digitalocean/go-openvswitch/ovs/openflow.go new file mode 100644 index 000000000..b1e1247a3 --- /dev/null +++ b/vendor/github.com/digitalocean/go-openvswitch/ovs/openflow.go @@ -0,0 +1,478 @@ +// Copyright 2017 DigitalOcean. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ovs + +import ( + "bufio" + "bytes" + "encoding" + "errors" + "fmt" + "io" +) + +var ( + // errMultipleValues is returned when a function should only retrieve a + // single value, but multiple values are found instead. + errMultipleValues = errors.New("multiple values returned") + + // errNotCommitted is returned when a flow bundle transaction is not + // committed, and thus, the contents of the bundle are discarded. + errNotCommitted = errors.New("flow bundle not committed, discarding flows") +) + +// An OpenFlowService is used in a Client to execute 'ovs-ofctl' commands. +type OpenFlowService struct { + // Wrapped Client for ExecFunc and debugging. + c *Client +} + +// AddFlow adds a Flow to a bridge attached to Open vSwitch. +func (o *OpenFlowService) AddFlow(bridge string, flow *Flow) error { + fb, err := flow.MarshalText() + if err != nil { + return err + } + + args := []string{"add-flow"} + args = append(args, o.c.ofctlFlags...) + args = append(args, []string{bridge, string(fb)}...) + + _, err = o.exec(args...) + return err +} + +// A FlowTransaction is a transaction used when adding or deleting +// multiple flows using an Open vSwitch flow bundle. +type FlowTransaction struct { + flows []flowDirective + committed bool + err error +} + +// A flowDirective is a directive and flow string pair, used to perform +// multiple operations within a Transaction. +type flowDirective struct { + directive string + flow string +} + +// Possible flowDirective directive values. +const ( + dirAdd = "add" + dirDelete = "delete" + dirDeleteStrict = "delete_strict" +) + +// Add pushes zero or more Flows on to the transaction, to be added by +// Open vSwitch. If any of the flows are invalid, Add becomes a no-op +// and the error will be surfaced when Commit is called. +func (tx *FlowTransaction) Add(flows ...*Flow) { + if tx.err != nil { + return + } + + tms := make([]encoding.TextMarshaler, 0, len(flows)) + for _, f := range flows { + tms = append(tms, f) + } + + tx.push(dirAdd, tms...) +} + +// Delete pushes zero or more MatchFlows on to the transaction, to be deleted +// by Open vSwitch. If any of the flows are invalid, Delete becomes a no-op +// and the error will be surfaced when Commit is called. +func (tx *FlowTransaction) Delete(flows ...*MatchFlow) { + if tx.err != nil { + return + } + + tms := make([]encoding.TextMarshaler, 0, len(flows)) + for _, f := range flows { + tms = append(tms, f) + } + + tx.push(dirDelete, tms...) +} + +// DeleteStrict is almost the same as Delete, except that the matching process +// will be strict. +func (tx *FlowTransaction) DeleteStrict(flows ...*MatchFlow) { + if tx.err != nil { + return + } + + tms := make([]encoding.TextMarshaler, 0, len(flows)) + for _, f := range flows { + tms = append(tms, f) + } + + tx.push(dirDeleteStrict, tms...) +} + +// push pushes zero or more encoding.TextMarshalers on to the transaction +// (typically a Flow or MatchFlow). +func (tx *FlowTransaction) push(directive string, flows ...encoding.TextMarshaler) { + for _, f := range flows { + fb, err := f.MarshalText() + if err != nil { + tx.err = err + return + } + + tx.flows = append(tx.flows, flowDirective{ + directive: directive, + flow: string(fb), + }) + } +} + +// Commit finalizes an AddFlowTransaction, returning any errors that may +// have occurred while adding flows. Commit must be called at the end +// of a successful transaction, but may return an error if one was encountered +// during a call to Add. +func (tx *FlowTransaction) Commit() error { + if tx.err != nil { + return tx.err + } + + tx.committed = true + return nil +} + +// Discard discards the contents of an AddFlowTransaction, returning an +// error wrapping the input error. Discard should be called if any +// operations fail in the middle of an AddFlowTransaction function. +func (tx *FlowTransaction) Discard(err error) error { + tx.flows = make([]flowDirective, 0) + return fmt.Errorf("discarding add flow transaction: %v", err) +} + +// AddFlowBundle creates an Open vSwitch flow bundle and enables adding and +// removing flows to and from the specified bridge using a FlowTransaction. +// This function enables atomic addition and deletion of flows to and from +// Open vSwitch. +func (o *OpenFlowService) AddFlowBundle(bridge string, fn func(tx *FlowTransaction) error) error { + // Flows will be added to and read from an in-memory buffer. The buffer's + // contents are piped to 'ovs-ofctl' using stdin. + buf := bytes.NewBuffer(nil) + + tx := &FlowTransaction{} + if err := fn(tx); err != nil { + // Errors from "tx.Commit()" or "tx.Discard()" will be returned here. + return err + } + + // Require an explicit "tx.Commit()" to make changes. + if !tx.committed { + return errNotCommitted + } + + for _, flow := range tx.flows { + // Syntax for adding a flow in the file is: + // "add priority=10,ip,actions=drop\n" + s := fmt.Sprintf("%s %s\n", flow.directive, flow.flow) + if _, err := io.WriteString(buf, s); err != nil { + return err + } + } + + args := []string{"--bundle", "add-flow"} + args = append(args, o.c.ofctlFlags...) + // Read from stdin. + args = append(args, bridge, "-") + + return o.pipe(buf, args...) +} + +// DelFlows removes flows that match MatchFlow from a bridge attached to Open vSwitch. +// +// If flow is nil, all flows will be deleted from the specified bridge. +func (o *OpenFlowService) DelFlows(bridge string, flow *MatchFlow) error { + if flow == nil { + // This means we'll flush the entire flows + // from the specifided bridge. + _, err := o.exec("del-flows", bridge) + return err + } + fb, err := flow.MarshalText() + if err != nil { + return err + } + + _, err = o.exec("del-flows", bridge, string(fb)) + return err +} + +// ModPort modifies the specified characteristics for the specified port. +func (o *OpenFlowService) ModPort(bridge string, port string, action PortAction) error { + _, err := o.exec("mod-port", bridge, string(port), string(action)) + return err +} + +// DumpPort retrieves statistics about the specified port attached to the +// specified bridge. +func (o *OpenFlowService) DumpPort(bridge string, port string) (*PortStats, error) { + stats, err := o.dumpPorts(bridge, port) + if err != nil { + return nil, err + } + if len(stats) != 1 { + return nil, errMultipleValues + } + + return stats[0], nil +} + +// DumpPorts retrieves statistics about all ports attached to the specified +// bridge. +func (o *OpenFlowService) DumpPorts(bridge string) ([]*PortStats, error) { + return o.dumpPorts(bridge, "") +} + +// DumpTables retrieves statistics about all tables for the specified bridge. +// If a table has no active flows and has not been used for a lookup or matched +// by an incoming packet, it is filtered from the output. +func (o *OpenFlowService) DumpTables(bridge string) ([]*Table, error) { + out, err := o.exec("dump-tables", bridge) + if err != nil { + return nil, err + } + + var tables []*Table + err = parseEach(out, dumpTablesPrefix, func(b []byte) error { + t := new(Table) + if err := t.UnmarshalText(b); err != nil { + return err + } + + // Ignore empty tables + if t.Active == 0 && t.Lookup == 0 && t.Matched == 0 { + return nil + } + + tables = append(tables, t) + return nil + }) + + return tables, err +} + +// DumpFlows retrieves statistics about all flows for the specified bridge. +// If a table has no active flows and has not been used for a lookup or matched +// by an incoming packet, it is filtered from the output. +func (o *OpenFlowService) DumpFlows(bridge string) ([]*Flow, error) { + out, err := o.exec("dump-flows", bridge) + if err != nil { + return nil, err + } + + var flows []*Flow + err = parseEachLine(out, dumpFlowsPrefix, func(b []byte) error { + // Do not attempt to parse NXST_FLOW messages. + if bytes.HasPrefix(b, dumpFlowsPrefix) { + return nil + } + + f := new(Flow) + if err := f.UnmarshalText(b); err != nil { + return err + } + + flows = append(flows, f) + return nil + }) + + return flows, err +} + +// DumpAggregate retrieves statistics about the specified flow attached to the +// specified bridge. +func (o *OpenFlowService) DumpAggregate(bridge string, flow *MatchFlow) (*FlowStats, error) { + stats, err := o.dumpAggregate(bridge, flow) + if err != nil { + return nil, err + } + + return stats, nil +} + +var ( + // dumpPortsPrefix is a sentinel value returned at the beginning of + // the output from 'ovs-ofctl dump-ports'. + dumpPortsPrefix = []byte("OFPST_PORT reply") + + // dumpTablesPrefix is a sentinel value returned at the beginning of + // the output from 'ovs-ofctl dump-tables'. + dumpTablesPrefix = []byte("OFPST_TABLE reply") + + // dumpFlowsPrefix is a sentinel value returned at the beginning of + // the output from 'ovs-ofctl dump-flows'. + dumpFlowsPrefix = []byte("NXST_FLOW reply") + + // dumpAggregatePrefix is a sentinel value returned at the beginning of + // the output from "ovs-ofctl dump-aggregate" + dumpAggregatePrefix = []byte("NXST_AGGREGATE reply") +) + +// dumpPorts calls 'ovs-ofctl dump-ports' with the specified arguments and +// parses the output into zero or more PortStats structs. +func (o *OpenFlowService) dumpPorts(bridge string, port string) ([]*PortStats, error) { + args := []string{ + "dump-ports", + bridge, + } + + args = append(o.c.ofctlFlags, args...) + + // Attach port argument only if non-empty. + if port != "" { + args = append(args, string(port)) + } + + out, err := o.exec(args...) + if err != nil { + return nil, err + } + + var stats []*PortStats + err = parseEach(out, dumpPortsPrefix, func(b []byte) error { + s := new(PortStats) + if err := s.UnmarshalText(b); err != nil { + return err + } + + stats = append(stats, s) + return nil + }) + + return stats, err +} + +// dumpAggregate calls 'ovs-ofctl dump-aggregate' with the specified arguments and +// parses the output into zero or more FlowStat structs. +func (o *OpenFlowService) dumpAggregate(bridge string, flow *MatchFlow) (*FlowStats, error) { + + flowText, err := flow.MarshalText() + if err != nil { + return nil, err + } + + args := []string{ + "dump-aggregate", + bridge, + string(flowText), + } + + args = append(o.c.ofctlFlags, args...) + + out, err := o.exec(args...) + if err != nil { + return nil, err + } + + var stats FlowStats + if err := stats.UnmarshalText(out); err != nil { + return nil, err + } + + return &stats, err +} + +// parseEachLine parses ovs-ofctl output from the input buffer, ensuring it has the +// specified prefix, and invoking the input function on each line scanned, +// so more complex structures can be parsed. +func parseEachLine(in []byte, prefix []byte, fn func(b []byte) error) error { + // First line must not be empty + scanner := bufio.NewScanner(bytes.NewReader(in)) + scanner.Split(bufio.ScanLines) + if !scanner.Scan() { + return io.ErrUnexpectedEOF + } + + // First line must contain prefix returned by OVS + if !bytes.HasPrefix(scanner.Bytes(), prefix) { + return io.ErrUnexpectedEOF + } + + // Scan every line to retrieve information needed to unmarshal + // a single Flow struct. + for scanner.Scan() { + b := make([]byte, len(scanner.Bytes())) + copy(b, scanner.Bytes()) + if err := fn(b); err != nil { + return err + } + } + + return scanner.Err() +} + +// parseEach parses ovs-ofctl output from the input buffer, ensuring it has the +// specified prefix, and invoking the input function on each two lines scanned, +// so more complex structures can be parsed. +func parseEach(in []byte, prefix []byte, fn func(b []byte) error) error { + // First line must not be empty + scanner := bufio.NewScanner(bytes.NewReader(in)) + scanner.Split(bufio.ScanLines) + if !scanner.Scan() { + return io.ErrUnexpectedEOF + } + + // First line must contain prefix returned by OVS + if !bytes.HasPrefix(scanner.Bytes(), prefix) { + return io.ErrUnexpectedEOF + } + + // OVS with OpenFlow 1.3+ returns an additional line with more metadata + // which must be ignored. A banner appears containing "(OF1.x)" which we + // detect here to discover if the last line should be discarded. + hasDuration := bytes.Contains(scanner.Bytes(), []byte("(OF1.")) + + // Scan every two lines to retrieve information needed to unmarshal + // a single PortStats struct. + for scanner.Scan() { + b := make([]byte, len(scanner.Bytes())) + copy(b, scanner.Bytes()) + + // Must always scan two lines + if !scanner.Scan() { + return io.ErrUnexpectedEOF + } + b = append(b, scanner.Bytes()...) + + // Discard the third line of information if applicable. + if hasDuration && !scanner.Scan() { + return io.ErrUnexpectedEOF + } + + if err := fn(b); err != nil { + return err + } + } + + return scanner.Err() +} + +// exec executes an ExecFunc using 'ovs-ofctl'. +func (o *OpenFlowService) exec(args ...string) ([]byte, error) { + return o.c.exec("ovs-ofctl", args...) +} + +// pipe executes a PipeFunc using 'ovs-ofctl'. +func (o *OpenFlowService) pipe(stdin io.Reader, args ...string) error { + return o.c.pipe(stdin, "ovs-ofctl", args...) +} diff --git a/vendor/github.com/digitalocean/go-openvswitch/ovs/ovs.go b/vendor/github.com/digitalocean/go-openvswitch/ovs/ovs.go new file mode 100644 index 000000000..c211a3291 --- /dev/null +++ b/vendor/github.com/digitalocean/go-openvswitch/ovs/ovs.go @@ -0,0 +1,89 @@ +// Copyright 2017 DigitalOcean. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ovs + +import ( + "bytes" + "fmt" +) + +// A FailMode is a failure mode which Open vSwitch uses when it cannot +// contact a controller. +type FailMode string + +// FailMode constants which can be used in OVS configurations. +const ( + FailModeStandalone FailMode = "standalone" + FailModeSecure FailMode = "secure" +) + +// An InterfaceType is a network interface type recognized by Open vSwitch. +type InterfaceType string + +// InterfaceType constants which can be used in OVS configurations. +const ( + InterfaceTypeGRE InterfaceType = "gre" + InterfaceTypeInternal InterfaceType = "internal" + InterfaceTypePatch InterfaceType = "patch" + InterfaceTypeSTT InterfaceType = "stt" + InterfaceTypeVXLAN InterfaceType = "vxlan" +) + +// A PortAction is a port actions to change the port characteristics of the +// specific port through the ModPort API. +type PortAction string + +// PortAction constants for ModPort API. +const ( + PortActionUp PortAction = "up" + PortActionDown PortAction = "down" + PortActionSTP PortAction = "stp" + PortActionNoSTP PortAction = "no-stp" + PortActionReceive PortAction = "receive" + PortActionNoReceive PortAction = "no-receive" + PortActionReceiveSTP PortAction = "receive-stp" + PortActionNoReceiveSTP PortAction = "no-receive-stp" + PortActionForward PortAction = "forward" + PortActionNoForward PortAction = "no-forward" + PortActionFlood PortAction = "flood" + PortActionNoFlood PortAction = "no-flood" + PortActionPacketIn PortAction = "packet-in" + PortActionNoPacketIn PortAction = "no-packet-in" +) + +// An Error is an error returned when shelling out to an Open vSwitch control +// program. It captures the combined stdout and stderr as well as the exit +// code. +type Error struct { + Out []byte + Err error +} + +// Error returns the string representation of an Error. +func (e *Error) Error() string { + return fmt.Sprintf("%s: %s", e.Err, string(e.Out)) +} + +// IsPortNotExist checks if err is of type Error and is caused by asking OVS for +// information regarding a non-existent port. +func IsPortNotExist(err error) bool { + oerr, ok := err.(*Error) + if !ok { + return false + } + + return bytes.HasPrefix(oerr.Out, []byte("ovs-vsctl: no port named ")) && + oerr.Err.Error() == "exit status 1" +} diff --git a/vendor/github.com/digitalocean/go-openvswitch/ovs/portrange.go b/vendor/github.com/digitalocean/go-openvswitch/ovs/portrange.go new file mode 100644 index 000000000..77b95f94e --- /dev/null +++ b/vendor/github.com/digitalocean/go-openvswitch/ovs/portrange.go @@ -0,0 +1,130 @@ +// Copyright 2017 DigitalOcean. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ovs + +import ( + "errors" + "math" +) + +var ( + // ErrInvalidPortRange is returned when there's a port range that invalid. + ErrInvalidPortRange = errors.New("invalid port range") +) + +// An PortRange represents a range of ports expressed in 16 bit integers. The start and +// end values of this range are inclusive. +type PortRange struct { + Start uint16 + End uint16 +} + +// A BitRange is a representation of a range of values from base value with a bitmask +// applied. +type BitRange struct { + Value uint16 + Mask uint16 +} + +// BitwiseMatch returns an array of BitRanges that represent the range of integers +// in the PortRange. +func (r *PortRange) BitwiseMatch() ([]BitRange, error) { + if r.Start <= 0 || r.End <= 0 { + return nil, ErrInvalidPortRange + } + if r.Start > r.End { + return nil, ErrInvalidPortRange + } + + if r.Start == r.End { + return []BitRange{ + {Value: r.Start, Mask: 0xffff}, + }, nil + } + + bitRanges := []BitRange{} + + // Find the largest window we can get on a binary boundary + window := (r.End - r.Start) + 1 + bitLength := uint(math.Floor(math.Log2(float64(window)))) + + rangeStart, rangeEnd := getRange(r.End, bitLength) + + // Decrement our mask until we fit inside the range we want from a binary boundary. + for rangeEnd > r.End { + bitLength-- + rangeStart, rangeEnd = getRange(r.End, bitLength) + } + + current := BitRange{ + Value: rangeStart, + Mask: getMask(bitLength), + } + + // The range we picked out was from the middle of our set, so we'll need to recurse on + // the remaining values for anything less than or greater than the current + // range. + + if r.Start != rangeStart { + leftRemainder := PortRange{ + Start: r.Start, + End: rangeStart - 1, + } + + leftRemainingBitRanges, err := leftRemainder.BitwiseMatch() + if err != nil { + return nil, err + } + + bitRanges = append(bitRanges, leftRemainingBitRanges...) + } + + // We append our current range here, so we're ordered properly. + bitRanges = append(bitRanges, current) + + if r.End != rangeEnd { + rightRemainder := PortRange{ + Start: rangeEnd + 1, + End: r.End, + } + + rightRemainingBitRanges, err := rightRemainder.BitwiseMatch() + if err != nil { + return nil, err + } + + bitRanges = append(bitRanges, rightRemainingBitRanges...) + } + + return bitRanges, nil +} + +func getMask(bitLength uint) uint16 { + // All 1s for everything that doesn't change in the range + return math.MaxUint16 ^ uint16((1< 0 { + s = append(s, fmt.Sprintf("protocols=%s", strings.Join(o.Protocols, ","))) + } + + return s +} + +// Interface sets configuration for an interface using the values from an +// InterfaceOptions struct. +func (v *VSwitchSetService) Interface(ifi string, options InterfaceOptions) error { + // Prepend command line arguments before expanding options slice + // and appending it + args := []string{"set", "interface", ifi} + args = append(args, options.slice()...) + + _, err := v.v.exec(args...) + return err +} + +// An InterfaceOptions struct enables configuration of an Interface. +type InterfaceOptions struct { + // Type specifies the Open vSwitch interface type. + Type InterfaceType + + // Peer specifies an interface to peer with when creating a patch interface. + Peer string + + // Ingress Policing + // + // These settings control ingress policing for packets received on this + // interface. On a physical interface, this limits the rate at which + // traffic is allowed into the system from the outside; on a virtual + // interface (one connected to a virtual machine), this limits the rate + // at which the VM is able to transmit. + + // IngressRatePolicing specifies the maximum rate for data received on + // this interface in kbps. Data received faster than this rate is dropped. + // Set to 0 (the default) to disable policing. + IngressRatePolicing int64 + + // IngressBurstPolicing specifies the maximum burst size for data received on + // this interface in kb. The default burst size if set to 0 is 1000 kb. + // This value has no effect if IngressRatePolicing is set to 0. Specifying + // a larger burst size lets the algorithm be more forgiving, which is important + // for protocols like TCP that react severely to dropped packets. The burst + // size should be at least the size of the interface's MTU. Specifying a + // value that is numerically at least as large as 10% of IngressRatePolicing + // helps TCP come closer to achieving the full rate. + IngressBurstPolicing int64 + + // RemoteIP can be populated when the interface is a tunnel interface type + // for example "stt" or "vxlan". It specifies the remote IP address with which to + // form tunnels when traffic is sent to this port. Optionally it could be set to + // "flow" which expects the flow to set tunnel destination. + RemoteIP string + + // Key can be populated when the interface is a tunnel interface type + // for example "stt" or "vxlan". It specifies the tunnel ID to attach to + // tunneled traffic leaving this interface. Optionally it could be set to + // "flow" which expects the flow to set tunnel ID. + Key string +} + +// slice creates a string slice containing any non-zero option values from the +// struct in the format expected by Open vSwitch. +func (i InterfaceOptions) slice() []string { + var s []string + + if i.Type != "" { + s = append(s, fmt.Sprintf("type=%s", i.Type)) + } + + if i.Peer != "" { + s = append(s, fmt.Sprintf("options:peer=%s", i.Peer)) + } + + if i.IngressRatePolicing == DefaultIngressRatePolicing { + // Set to 0 (the default) to disable policing. + s = append(s, "ingress_policing_rate=0") + } else if i.IngressRatePolicing > 0 { + s = append(s, fmt.Sprintf("ingress_policing_rate=%d", i.IngressRatePolicing)) + } + + if i.IngressBurstPolicing == DefaultIngressBurstPolicing { + // Set to 0 (the default) to the default burst size. + s = append(s, "ingress_policing_burst=0") + } else if i.IngressBurstPolicing > 0 { + s = append(s, fmt.Sprintf("ingress_policing_burst=%d", i.IngressBurstPolicing)) + } + + if i.RemoteIP != "" { + s = append(s, fmt.Sprintf("options:remote_ip=%s", i.RemoteIP)) + } + + if i.Key != "" { + s = append(s, fmt.Sprintf("options:key=%s", i.Key)) + } + + return s +} diff --git a/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md b/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md index a438fe4b4..cc01c08f5 100644 --- a/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md +++ b/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md @@ -7,9 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.5.4] - 2022-04-25 + +* Windows: add missing defer to `Watcher.WatchList` [#447](https://github.com/fsnotify/fsnotify/pull/447) +* go.mod: use latest x/sys [#444](https://github.com/fsnotify/fsnotify/pull/444) +* Fix compilation for OpenBSD [#443](https://github.com/fsnotify/fsnotify/pull/443) + +## [1.5.3] - 2022-04-22 + +* This version is retracted. An incorrect branch is published accidentally [#445](https://github.com/fsnotify/fsnotify/issues/445) + +## [1.5.2] - 2022-04-21 + +* Add a feature to return the directories and files that are being monitored [#374](https://github.com/fsnotify/fsnotify/pull/374) +* Fix potential crash on windows if `raw.FileNameLength` exceeds `syscall.MAX_PATH` [#361](https://github.com/fsnotify/fsnotify/pull/361) +* Allow build on unsupported GOOS [#424](https://github.com/fsnotify/fsnotify/pull/424) +* Don't set `poller.fd` twice in `newFdPoller` [#406](https://github.com/fsnotify/fsnotify/pull/406) +* fix go vet warnings: call to `(*T).Fatalf` from a non-test goroutine [#416](https://github.com/fsnotify/fsnotify/pull/416) + ## [1.5.1] - 2021-08-24 -* Revert Add AddRaw to not follow symlinks +* Revert Add AddRaw to not follow symlinks [#394](https://github.com/fsnotify/fsnotify/pull/394) ## [1.5.0] - 2021-08-20 diff --git a/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md b/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md index 828a60b24..8a642563d 100644 --- a/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md +++ b/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md @@ -48,18 +48,6 @@ fsnotify uses build tags to compile different code on Linux, BSD, macOS, and Win Before doing a pull request, please do your best to test your changes on multiple platforms, and list which platforms you were able/unable to test on. -To aid in cross-platform testing there is a Vagrantfile for Linux and BSD. - -* Install [Vagrant](http://www.vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/) -* Setup [Vagrant Gopher](https://github.com/nathany/vagrant-gopher) in your `src` folder. -* Run `vagrant up` from the project folder. You can also setup just one box with `vagrant up linux` or `vagrant up bsd` (note: the BSD box doesn't support Windows hosts at this time, and NFS may prompt for your host OS password) -* Once setup, you can run the test suite on a given OS with a single command `vagrant ssh linux -c 'cd fsnotify/fsnotify; go test'`. -* When you're done, you will want to halt or destroy the Vagrant boxes. - -Notice: fsnotify file system events won't trigger in shared folders. The tests get around this limitation by using the /tmp directory. - -Right now there is no equivalent solution for Windows and macOS, but there are Windows VMs [freely available from Microsoft](http://www.modern.ie/en-us/virtualization-tools#downloads). - ### Maintainers Help maintaining fsnotify is welcome. To be a maintainer: @@ -67,11 +55,6 @@ Help maintaining fsnotify is welcome. To be a maintainer: * Submit a pull request and sign the CLA as above. * You must be able to run the test suite on Mac, Windows, Linux and BSD. -To keep master clean, the fsnotify project uses the "apply mail" workflow outlined in Nathaniel Talbott's post ["Merge pull request" Considered Harmful][am]. This requires installing [hub][]. - All code changes should be internal pull requests. Releases are tagged using [Semantic Versioning](http://semver.org/). - -[hub]: https://github.com/github/hub -[am]: http://blog.spreedly.com/2014/06/24/merge-pull-request-considered-harmful/#.VGa5yZPF_Zs diff --git a/vendor/github.com/fsnotify/fsnotify/README.md b/vendor/github.com/fsnotify/fsnotify/README.md index df57b1b28..0731c5ef8 100644 --- a/vendor/github.com/fsnotify/fsnotify/README.md +++ b/vendor/github.com/fsnotify/fsnotify/README.md @@ -1,12 +1,8 @@ # File system notifications for Go -[![GoDoc](https://godoc.org/github.com/fsnotify/fsnotify?status.svg)](https://godoc.org/github.com/fsnotify/fsnotify) [![Go Report Card](https://goreportcard.com/badge/github.com/fsnotify/fsnotify)](https://goreportcard.com/report/github.com/fsnotify/fsnotify) +[![Go Reference](https://pkg.go.dev/badge/github.com/fsnotify/fsnotify.svg)](https://pkg.go.dev/github.com/fsnotify/fsnotify) [![Go Report Card](https://goreportcard.com/badge/github.com/fsnotify/fsnotify)](https://goreportcard.com/report/github.com/fsnotify/fsnotify) [![Maintainers Wanted](https://img.shields.io/badge/maintainers-wanted-red.svg)](https://github.com/fsnotify/fsnotify/issues/413) -fsnotify utilizes [golang.org/x/sys](https://godoc.org/golang.org/x/sys) rather than `syscall` from the standard library. Ensure you have the latest version installed by running: - -```console -go get -u golang.org/x/sys/... -``` +fsnotify utilizes [`golang.org/x/sys`](https://pkg.go.dev/golang.org/x/sys) rather than [`syscall`](https://pkg.go.dev/syscall) from the standard library. Cross platform: Windows, Linux, BSD and macOS. @@ -16,22 +12,20 @@ Cross platform: Windows, Linux, BSD and macOS. | kqueue | BSD, macOS, iOS\* | Supported | | ReadDirectoryChangesW | Windows | Supported | | FSEvents | macOS | [Planned](https://github.com/fsnotify/fsnotify/issues/11) | -| FEN | Solaris 11 | [In Progress](https://github.com/fsnotify/fsnotify/issues/12) | -| fanotify | Linux 2.6.37+ | [Planned](https://github.com/fsnotify/fsnotify/issues/114) | +| FEN | Solaris 11 | [In Progress](https://github.com/fsnotify/fsnotify/pull/371) | +| fanotify | Linux 2.6.37+ | [Maybe](https://github.com/fsnotify/fsnotify/issues/114) | | USN Journals | Windows | [Maybe](https://github.com/fsnotify/fsnotify/issues/53) | | Polling | *All* | [Maybe](https://github.com/fsnotify/fsnotify/issues/9) | \* Android and iOS are untested. -Please see [the documentation](https://godoc.org/github.com/fsnotify/fsnotify) and consult the [FAQ](#faq) for usage information. +Please see [the documentation](https://pkg.go.dev/github.com/fsnotify/fsnotify) and consult the [FAQ](#faq) for usage information. ## API stability -fsnotify is a fork of [howeyc/fsnotify](https://godoc.org/github.com/howeyc/fsnotify) with a new API as of v1.0. The API is based on [this design document](http://goo.gl/MrYxyA). - -All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based on [Semantic Versioning](http://semver.org/). Further API changes are [planned](https://github.com/fsnotify/fsnotify/milestones), and will be tagged with a new major revision number. +fsnotify is a fork of [howeyc/fsnotify](https://github.com/howeyc/fsnotify) with a new API as of v1.0. The API is based on [this design document](http://goo.gl/MrYxyA). -Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`. +All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based on [Semantic Versioning](http://semver.org/). ## Usage @@ -84,10 +78,6 @@ func main() { Please refer to [CONTRIBUTING][] before opening an issue or pull request. -## Example - -See [example_test.go](https://github.com/fsnotify/fsnotify/blob/master/example_test.go). - ## FAQ **When a file is moved to another directory is it still being watched?** diff --git a/vendor/github.com/fsnotify/fsnotify/fsnotify_unsupported.go b/vendor/github.com/fsnotify/fsnotify/fsnotify_unsupported.go new file mode 100644 index 000000000..596885598 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/fsnotify_unsupported.go @@ -0,0 +1,36 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows +// +build !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows + +package fsnotify + +import ( + "fmt" + "runtime" +) + +// Watcher watches a set of files, delivering events to a channel. +type Watcher struct{} + +// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. +func NewWatcher() (*Watcher, error) { + return nil, fmt.Errorf("fsnotify not supported on %s", runtime.GOOS) +} + +// Close removes all watches and closes the events channel. +func (w *Watcher) Close() error { + return nil +} + +// Add starts watching the named file or directory (non-recursively). +func (w *Watcher) Add(name string) error { + return nil +} + +// Remove stops watching the the named file or directory (non-recursively). +func (w *Watcher) Remove(name string) error { + return nil +} diff --git a/vendor/github.com/fsnotify/fsnotify/inotify.go b/vendor/github.com/fsnotify/fsnotify/inotify.go index eb87699b5..a6d0e0ec8 100644 --- a/vendor/github.com/fsnotify/fsnotify/inotify.go +++ b/vendor/github.com/fsnotify/fsnotify/inotify.go @@ -163,6 +163,19 @@ func (w *Watcher) Remove(name string) error { return nil } +// WatchList returns the directories and files that are being monitered. +func (w *Watcher) WatchList() []string { + w.mu.Lock() + defer w.mu.Unlock() + + entries := make([]string, 0, len(w.watches)) + for pathname := range w.watches { + entries = append(entries, pathname) + } + + return entries +} + type watch struct { wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall) flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags) diff --git a/vendor/github.com/fsnotify/fsnotify/inotify_poller.go b/vendor/github.com/fsnotify/fsnotify/inotify_poller.go index e9ff9439f..b572a37c3 100644 --- a/vendor/github.com/fsnotify/fsnotify/inotify_poller.go +++ b/vendor/github.com/fsnotify/fsnotify/inotify_poller.go @@ -38,7 +38,6 @@ func newFdPoller(fd int) (*fdPoller, error) { poller.close() } }() - poller.fd = fd // Create epoll fd poller.epfd, errno = unix.EpollCreate1(unix.EPOLL_CLOEXEC) diff --git a/vendor/github.com/fsnotify/fsnotify/kqueue.go b/vendor/github.com/fsnotify/fsnotify/kqueue.go index 368f5b790..6fb8d8532 100644 --- a/vendor/github.com/fsnotify/fsnotify/kqueue.go +++ b/vendor/github.com/fsnotify/fsnotify/kqueue.go @@ -148,6 +148,19 @@ func (w *Watcher) Remove(name string) error { return nil } +// WatchList returns the directories and files that are being monitered. +func (w *Watcher) WatchList() []string { + w.mu.Lock() + defer w.mu.Unlock() + + entries := make([]string, 0, len(w.watches)) + for pathname := range w.watches { + entries = append(entries, pathname) + } + + return entries +} + // Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE) const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME diff --git a/vendor/github.com/fsnotify/fsnotify/windows.go b/vendor/github.com/fsnotify/fsnotify/windows.go index c02b75f7c..02ce7deb0 100644 --- a/vendor/github.com/fsnotify/fsnotify/windows.go +++ b/vendor/github.com/fsnotify/fsnotify/windows.go @@ -12,6 +12,7 @@ import ( "fmt" "os" "path/filepath" + "reflect" "runtime" "sync" "syscall" @@ -96,6 +97,21 @@ func (w *Watcher) Remove(name string) error { return <-in.reply } +// WatchList returns the directories and files that are being monitered. +func (w *Watcher) WatchList() []string { + w.mu.Lock() + defer w.mu.Unlock() + + entries := make([]string, 0, len(w.watches)) + for _, entry := range w.watches { + for _, watchEntry := range entry { + entries = append(entries, watchEntry.path) + } + } + + return entries +} + const ( // Options for AddWatch sysFSONESHOT = 0x80000000 @@ -452,8 +468,16 @@ func (w *Watcher) readEvents() { // Point "raw" to the event in the buffer raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset])) - buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName)) - name := syscall.UTF16ToString(buf[:raw.FileNameLength/2]) + // TODO: Consider using unsafe.Slice that is available from go1.17 + // https://stackoverflow.com/questions/51187973/how-to-create-an-array-or-a-slice-from-an-array-unsafe-pointer-in-golang + // instead of using a fixed syscall.MAX_PATH buf, we create a buf that is the size of the path name + size := int(raw.FileNameLength / 2) + var buf []uint16 + sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) + sh.Data = uintptr(unsafe.Pointer(&raw.FileName)) + sh.Len = size + sh.Cap = size + name := syscall.UTF16ToString(buf) fullname := filepath.Join(watch.path, name) var mask uint64 diff --git a/vendor/modules.txt b/vendor/modules.txt index 0ae8d3f98..310366c01 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -180,6 +180,9 @@ github.com/deislabs/oras/pkg/oras # github.com/dgrijalva/jwt-go v3.2.0+incompatible ## explicit github.com/dgrijalva/jwt-go +# github.com/digitalocean/go-openvswitch v0.0.0-20190515160856-1141932ed5cf => github.com/yousong/go-openvswitch v0.0.0-20200422025222-6b2d502be872 +## explicit +github.com/digitalocean/go-openvswitch/ovs # github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492 ## explicit github.com/docker/cli/cli/config @@ -263,8 +266,8 @@ github.com/exponent-io/jsonpath # github.com/fatih/color v1.13.0 ## explicit; go 1.13 github.com/fatih/color -# github.com/fsnotify/fsnotify v1.5.1 -## explicit; go 1.13 +# github.com/fsnotify/fsnotify v1.5.4 +## explicit; go 1.16 github.com/fsnotify/fsnotify # github.com/ghodss/yaml v1.0.0 ## explicit @@ -1672,7 +1675,7 @@ sigs.k8s.io/structured-merge-diff/v4/value # sigs.k8s.io/yaml v1.2.0 ## explicit; go 1.12 sigs.k8s.io/yaml -# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240304114831-feebc346e513 +# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240308095624-f1aeca8dcd51 ## explicit; go 1.18 yunion.io/x/cloudmux/pkg/apis yunion.io/x/cloudmux/pkg/apis/billing @@ -1691,11 +1694,11 @@ yunion.io/x/executor/client # yunion.io/x/jsonutils v1.0.1-0.20240203102553-4096f103b401 ## explicit; go 1.18 yunion.io/x/jsonutils -# yunion.io/x/log v1.0.1-0.20230411060016-feb3f46ab361 +# yunion.io/x/log v1.0.1-0.20240305175729-7cf2d6cd5a91 ## explicit; go 1.12 yunion.io/x/log yunion.io/x/log/hooks -# yunion.io/x/onecloud v0.0.0-20240305022950-ed2dd1f548c2 +# yunion.io/x/onecloud v0.0.0-20240312053540-299846783d7f ## explicit; go 1.18 yunion.io/x/onecloud/locales yunion.io/x/onecloud/pkg/apis @@ -1808,7 +1811,11 @@ yunion.io/x/pkg/utils # yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e ## explicit; go 1.12 yunion.io/x/s3cli -# yunion.io/x/sqlchemy v1.1.3-0.20240304110946-16faa82225e6 +# yunion.io/x/sdnagent v1.2.10-0.20240129094758-082d26e0e076 +## explicit; go 1.18 +yunion.io/x/sdnagent/pkg/agent +yunion.io/x/sdnagent/pkg/agent/proto +# yunion.io/x/sqlchemy v1.1.3-0.20240309151155-b34f29f02c79 ## explicit; go 1.17 yunion.io/x/sqlchemy yunion.io/x/sqlchemy/backends @@ -1819,6 +1826,7 @@ yunion.io/x/sqlchemy/backends/sqlite # yunion.io/x/structarg v0.0.0-20231017124457-df4d5009457c ## explicit; go 1.12 yunion.io/x/structarg +# github.com/digitalocean/go-openvswitch => github.com/yousong/go-openvswitch v0.0.0-20200422025222-6b2d502be872 # github.com/go-logr/logr => github.com/go-logr/logr v0.4.0 # github.com/go-openapi/analysis => github.com/go-openapi/analysis v0.19.8 # github.com/go-openapi/loads => github.com/go-openapi/loads v0.19.3 diff --git a/vendor/yunion.io/x/log/log.go b/vendor/yunion.io/x/log/log.go index a26d3e439..067f146be 100644 --- a/vendor/yunion.io/x/log/log.go +++ b/vendor/yunion.io/x/log/log.go @@ -17,6 +17,7 @@ package log import ( "runtime" + "runtime/debug" "sync" "github.com/sirupsen/logrus" @@ -134,10 +135,12 @@ func Errorln(args ...interface{}) { } func Fatalf(format string, args ...interface{}) { + debug.PrintStack() logrus.Fatalf(format, args...) } func Fatalln(args ...interface{}) { + debug.PrintStack() logrus.Fatalln(args...) } diff --git a/vendor/yunion.io/x/onecloud/pkg/apis/compute/storage.go b/vendor/yunion.io/x/onecloud/pkg/apis/compute/storage.go index f1c9f291b..6e3ca1a0c 100644 --- a/vendor/yunion.io/x/onecloud/pkg/apis/compute/storage.go +++ b/vendor/yunion.io/x/onecloud/pkg/apis/compute/storage.go @@ -118,6 +118,7 @@ type StorageCreateInput struct { // SLVM VG Name SLVMVgName string MasterHost string + Lvmlockd bool } type RbdTimeoutInput struct { diff --git a/vendor/yunion.io/x/onecloud/pkg/apis/identity/consts.go b/vendor/yunion.io/x/onecloud/pkg/apis/identity/consts.go index edf0da5a9..74d136de0 100644 --- a/vendor/yunion.io/x/onecloud/pkg/apis/identity/consts.go +++ b/vendor/yunion.io/x/onecloud/pkg/apis/identity/consts.go @@ -165,6 +165,7 @@ var ( "db_checksum_skip_init", "db_checksum_tables", "enable_db_checksum_tables", + "db_checksum_hash_algorithm", "auto_sync_table", "exit_after_db_init", "global_virtual_resource_namespace", diff --git a/vendor/yunion.io/x/onecloud/pkg/cloudcommon/consts/db.go b/vendor/yunion.io/x/onecloud/pkg/cloudcommon/consts/db.go index 732893bb7..57aa780b3 100644 --- a/vendor/yunion.io/x/onecloud/pkg/cloudcommon/consts/db.go +++ b/vendor/yunion.io/x/onecloud/pkg/cloudcommon/consts/db.go @@ -14,6 +14,8 @@ package consts +import "yunion.io/x/log" + var ( QueryOffsetOptimization = false @@ -22,6 +24,8 @@ var ( defaultDBDialect string defaultDBConnectionString string + + defaultDBChecksumHashAlgorithm string ) func SetDefaultDB(dialect, connStr string) { @@ -36,3 +40,15 @@ func DefaultDBDialect() string { func DefaultDBConnStr() string { return defaultDBConnectionString } + +func SetDefaultDBChecksumHashAlgorithm(alg string) { + log.Infof("Set default DB checksum hash algorithm: %s", alg) + defaultDBChecksumHashAlgorithm = alg +} + +func DefaultDBChecksumHashAlgorithm() string { + if len(defaultDBChecksumHashAlgorithm) > 0 { + return defaultDBChecksumHashAlgorithm + } + return "sha256" +} diff --git a/vendor/yunion.io/x/onecloud/pkg/cloudcommon/database.go b/vendor/yunion.io/x/onecloud/pkg/cloudcommon/database.go index 80b5f1e70..8f415c1bc 100644 --- a/vendor/yunion.io/x/onecloud/pkg/cloudcommon/database.go +++ b/vendor/yunion.io/x/onecloud/pkg/cloudcommon/database.go @@ -143,6 +143,10 @@ func InitDB(options *common_options.DBOptions) { } // lm := lockman.NewNoopLockManager() + if options.EnableDBChecksumTables && len(options.DBChecksumHashAlgorithm) > 0 { + consts.SetDefaultDBChecksumHashAlgorithm(options.DBChecksumHashAlgorithm) + } + initDBNotifier() startInitInformer(options) } diff --git a/vendor/yunion.io/x/onecloud/pkg/cloudcommon/db/checksum.go b/vendor/yunion.io/x/onecloud/pkg/cloudcommon/db/checksum.go index 3ab75aa87..953a99a4d 100644 --- a/vendor/yunion.io/x/onecloud/pkg/cloudcommon/db/checksum.go +++ b/vendor/yunion.io/x/onecloud/pkg/cloudcommon/db/checksum.go @@ -16,9 +16,11 @@ package db import ( "crypto/md5" + "crypto/sha256" "fmt" "reflect" "sort" + "strings" "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -27,6 +29,7 @@ import ( "yunion.io/x/pkg/utils" "yunion.io/x/sqlchemy" + "yunion.io/x/onecloud/pkg/cloudcommon/consts" "yunion.io/x/onecloud/pkg/util/splitable" ) @@ -126,13 +129,19 @@ func CheckRecordChecksumConsistent(model IModel) error { } func calculateRecordChecksumByValues(vals []interface{}) string { - ss := "" + ss := strings.Builder{} for _, val := range vals { - ss += fmt.Sprintf("\n%v", val) + ss.WriteString(fmt.Sprintf("\n%v", val)) } - hStr := md5.Sum([]byte(ss)) - sum := fmt.Sprintf("%x", hStr) - log.Debugf("calculate values string: %s checksum: %s", ss, sum) + var sum string + algStr := consts.DefaultDBChecksumHashAlgorithm() + switch algStr { + case "md5": + sum = fmt.Sprintf("%x", md5.Sum([]byte(ss.String()))) + default: + sum = fmt.Sprintf("%x", sha256.Sum256([]byte(ss.String()))) + } + // log.Debugf("calculate values string: %s alg: %s, checksum: %s", ss, algStr, sum) return sum } diff --git a/vendor/yunion.io/x/onecloud/pkg/cloudcommon/db/jointbase.go b/vendor/yunion.io/x/onecloud/pkg/cloudcommon/db/jointbase.go index 98beda092..f9baf10f7 100644 --- a/vendor/yunion.io/x/onecloud/pkg/cloudcommon/db/jointbase.go +++ b/vendor/yunion.io/x/onecloud/pkg/cloudcommon/db/jointbase.go @@ -75,6 +75,10 @@ func (manager *SJointResourceBaseManager) GetSlaveManager() IStandaloneModelMana return manager._slave } +func (manager *SJointResourceBaseManager) CreateByInsertOrUpdate() bool { + return false +} + /* func queryField(q *sqlchemy.SQuery, manager IModelManager) sqlchemy.IQueryField { field := q.Field(fmt.Sprintf("%s_id", manager.Keyword())) diff --git a/vendor/yunion.io/x/onecloud/pkg/cloudcommon/options/options.go b/vendor/yunion.io/x/onecloud/pkg/cloudcommon/options/options.go index 0534d2958..d75eb0c3f 100644 --- a/vendor/yunion.io/x/onecloud/pkg/cloudcommon/options/options.go +++ b/vendor/yunion.io/x/onecloud/pkg/cloudcommon/options/options.go @@ -164,6 +164,8 @@ type DBOptions struct { EnableDBChecksumTables bool `help:"Enable DB tables with record checksum for consistency"` DBChecksumSkipInit bool `help:"Skip DB tables with record checksum calculation when init" default:"false"` + DBChecksumHashAlgorithm string `help:"hash algorithm for db checksum hash" choices:"md5|sha256" default:"sha256"` + AutoSyncTable bool `help:"Automatically synchronize table changes if differences are detected"` ExitAfterDBInit bool `help:"Exit program after db initialization" default:"false"` diff --git a/vendor/yunion.io/x/onecloud/pkg/mcclient/auth/auth.go b/vendor/yunion.io/x/onecloud/pkg/mcclient/auth/auth.go index 25aeb1cb7..604284862 100644 --- a/vendor/yunion.io/x/onecloud/pkg/mcclient/auth/auth.go +++ b/vendor/yunion.io/x/onecloud/pkg/mcclient/auth/auth.go @@ -20,6 +20,7 @@ import ( "net" "net/http" "strings" + "sync" "time" "yunion.io/x/jsonutils" @@ -161,16 +162,29 @@ type authManager struct { accessKeyCache *sAccessKeyCache } +var ( + authManagerInstane *authManager + authManagerLock *sync.Mutex = &sync.Mutex{} +) + func newAuthManager(cli *mcclient.Client, info *AuthInfo) *authManager { - authm := &authManager{ + authManagerLock.Lock() + defer authManagerLock.Unlock() + + if authManagerInstane != nil { + authManagerInstane.client = cli + authManagerInstane.info = info + return authManagerInstane + } + authManagerInstane = &authManager{ client: cli, info: info, tokenCacheVerify: NewTokenCacheVerify(), accessKeyCache: newAccessKeyCache(), } - authm.InitSync(authm) - go authm.startRefreshRevokeTokens() - return authm + authManagerInstane.InitSync(authManagerInstane) + go authManagerInstane.startRefreshRevokeTokens() + return authManagerInstane } func (a *authManager) startRefreshRevokeTokens() { diff --git a/vendor/yunion.io/x/onecloud/pkg/util/netutils2/netutils.go b/vendor/yunion.io/x/onecloud/pkg/util/netutils2/netutils.go index 67c43a0bf..3e3be3123 100644 --- a/vendor/yunion.io/x/onecloud/pkg/util/netutils2/netutils.go +++ b/vendor/yunion.io/x/onecloud/pkg/util/netutils2/netutils.go @@ -156,9 +156,10 @@ func isExitAddress(ip string) bool { } func AddNicRoutes(routes [][]string, nicDesc *types.SServerNic, mainIp string, nicCnt int) [][]string { - if mainIp == nicDesc.Ip { - return routes - } + // always add static routes, even if this is the default NIC + // if mainIp == nicDesc.Ip { + // return routes + // } if len(nicDesc.Routes) > 0 { routes = extendRoutes(routes, nicDesc.Routes) } else if len(nicDesc.Gateway) > 0 && !isExitAddress(nicDesc.Ip) && diff --git a/vendor/yunion.io/x/onecloud/pkg/util/splitable/metadata.go b/vendor/yunion.io/x/onecloud/pkg/util/splitable/metadata.go index 896dafdbc..74a7d46df 100644 --- a/vendor/yunion.io/x/onecloud/pkg/util/splitable/metadata.go +++ b/vendor/yunion.io/x/onecloud/pkg/util/splitable/metadata.go @@ -42,7 +42,7 @@ func (spec *SSplitTableSpec) getTableLastMeta() (*STableMetadata, error) { func (spec *SSplitTableSpec) GetTableMetaByTime(recordTime time.Time) (*STableMetadata, error) { q := spec.metaSpec.Query().Desc("id").IsFalse("deleted") if !recordTime.IsZero() { - q = q.LE("start_date", recordTime) + q = q.LE("start_date", recordTime.Add(time.Second)) } meta := new(STableMetadata) err := q.First(meta) diff --git a/vendor/yunion.io/x/sdnagent/LICENSE b/vendor/yunion.io/x/sdnagent/LICENSE new file mode 100644 index 000000000..adcdafcd4 --- /dev/null +++ b/vendor/yunion.io/x/sdnagent/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [2018] [Yunion Technology] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/yunion.io/x/sdnagent/pkg/agent/client.go b/vendor/yunion.io/x/sdnagent/pkg/agent/client.go new file mode 100644 index 000000000..915b30c07 --- /dev/null +++ b/vendor/yunion.io/x/sdnagent/pkg/agent/client.go @@ -0,0 +1,50 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package agent + +import ( + "fmt" + + "google.golang.org/grpc" + + pb "yunion.io/x/sdnagent/pkg/agent/proto" +) + +type AgentClient struct { + VSwitch pb.VSwitchClient + Openflow pb.OpenflowClient +} + +func (c *AgentClient) W(resp pb.CommonResponse, err error) error { + if err != nil { + return err + } + if resp.GetCode() != 0 { + return fmt.Errorf("err %d: %s", resp.GetCode(), resp.GetMesg()) + } + return nil +} + +func NewClient(sockPath string) (*AgentClient, error) { + conn, err := grpc.Dial("unix://"+sockPath, grpc.WithInsecure()) + if err != nil { + return nil, err + } + c := &AgentClient{ + VSwitch: pb.NewVSwitchClient(conn), + Openflow: pb.NewOpenflowClient(conn), + } + return c, nil +} diff --git a/vendor/yunion.io/x/sdnagent/pkg/agent/doc.go b/vendor/yunion.io/x/sdnagent/pkg/agent/doc.go new file mode 100644 index 000000000..bd54941f1 --- /dev/null +++ b/vendor/yunion.io/x/sdnagent/pkg/agent/doc.go @@ -0,0 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package agent // import "yunion.io/x/sdnagent/pkg/agent" diff --git a/vendor/yunion.io/x/sdnagent/pkg/agent/proto/agent.pb.go b/vendor/yunion.io/x/sdnagent/pkg/agent/proto/agent.pb.go new file mode 100644 index 000000000..0a7dd88b8 --- /dev/null +++ b/vendor/yunion.io/x/sdnagent/pkg/agent/proto/agent.pb.go @@ -0,0 +1,975 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: agent.proto + +package pb + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type Response struct { + Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` + Mesg string `protobuf:"bytes,2,opt,name=mesg,proto3" json:"mesg,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Response) Reset() { *m = Response{} } +func (m *Response) String() string { return proto.CompactTextString(m) } +func (*Response) ProtoMessage() {} +func (*Response) Descriptor() ([]byte, []int) { + return fileDescriptor_agent_3f2a378016922cd7, []int{0} +} +func (m *Response) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Response.Unmarshal(m, b) +} +func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Response.Marshal(b, m, deterministic) +} +func (dst *Response) XXX_Merge(src proto.Message) { + xxx_messageInfo_Response.Merge(dst, src) +} +func (m *Response) XXX_Size() int { + return xxx_messageInfo_Response.Size(m) +} +func (m *Response) XXX_DiscardUnknown() { + xxx_messageInfo_Response.DiscardUnknown(m) +} + +var xxx_messageInfo_Response proto.InternalMessageInfo + +func (m *Response) GetCode() uint32 { + if m != nil { + return m.Code + } + return 0 +} + +func (m *Response) GetMesg() string { + if m != nil { + return m.Mesg + } + return "" +} + +type AddBridgeRequest struct { + Bridge string `protobuf:"bytes,1,opt,name=bridge,proto3" json:"bridge,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AddBridgeRequest) Reset() { *m = AddBridgeRequest{} } +func (m *AddBridgeRequest) String() string { return proto.CompactTextString(m) } +func (*AddBridgeRequest) ProtoMessage() {} +func (*AddBridgeRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_agent_3f2a378016922cd7, []int{1} +} +func (m *AddBridgeRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AddBridgeRequest.Unmarshal(m, b) +} +func (m *AddBridgeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AddBridgeRequest.Marshal(b, m, deterministic) +} +func (dst *AddBridgeRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_AddBridgeRequest.Merge(dst, src) +} +func (m *AddBridgeRequest) XXX_Size() int { + return xxx_messageInfo_AddBridgeRequest.Size(m) +} +func (m *AddBridgeRequest) XXX_DiscardUnknown() { + xxx_messageInfo_AddBridgeRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_AddBridgeRequest proto.InternalMessageInfo + +func (m *AddBridgeRequest) GetBridge() string { + if m != nil { + return m.Bridge + } + return "" +} + +type DelBridgeRequest struct { + Bridge string `protobuf:"bytes,1,opt,name=bridge,proto3" json:"bridge,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DelBridgeRequest) Reset() { *m = DelBridgeRequest{} } +func (m *DelBridgeRequest) String() string { return proto.CompactTextString(m) } +func (*DelBridgeRequest) ProtoMessage() {} +func (*DelBridgeRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_agent_3f2a378016922cd7, []int{2} +} +func (m *DelBridgeRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DelBridgeRequest.Unmarshal(m, b) +} +func (m *DelBridgeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DelBridgeRequest.Marshal(b, m, deterministic) +} +func (dst *DelBridgeRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DelBridgeRequest.Merge(dst, src) +} +func (m *DelBridgeRequest) XXX_Size() int { + return xxx_messageInfo_DelBridgeRequest.Size(m) +} +func (m *DelBridgeRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DelBridgeRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_DelBridgeRequest proto.InternalMessageInfo + +func (m *DelBridgeRequest) GetBridge() string { + if m != nil { + return m.Bridge + } + return "" +} + +type AddBridgePortRequest struct { + Bridge string `protobuf:"bytes,1,opt,name=bridge,proto3" json:"bridge,omitempty"` + Port string `protobuf:"bytes,2,opt,name=port,proto3" json:"port,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AddBridgePortRequest) Reset() { *m = AddBridgePortRequest{} } +func (m *AddBridgePortRequest) String() string { return proto.CompactTextString(m) } +func (*AddBridgePortRequest) ProtoMessage() {} +func (*AddBridgePortRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_agent_3f2a378016922cd7, []int{3} +} +func (m *AddBridgePortRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AddBridgePortRequest.Unmarshal(m, b) +} +func (m *AddBridgePortRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AddBridgePortRequest.Marshal(b, m, deterministic) +} +func (dst *AddBridgePortRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_AddBridgePortRequest.Merge(dst, src) +} +func (m *AddBridgePortRequest) XXX_Size() int { + return xxx_messageInfo_AddBridgePortRequest.Size(m) +} +func (m *AddBridgePortRequest) XXX_DiscardUnknown() { + xxx_messageInfo_AddBridgePortRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_AddBridgePortRequest proto.InternalMessageInfo + +func (m *AddBridgePortRequest) GetBridge() string { + if m != nil { + return m.Bridge + } + return "" +} + +func (m *AddBridgePortRequest) GetPort() string { + if m != nil { + return m.Port + } + return "" +} + +type DelBridgePortRequest struct { + Bridge string `protobuf:"bytes,1,opt,name=bridge,proto3" json:"bridge,omitempty"` + Port string `protobuf:"bytes,2,opt,name=port,proto3" json:"port,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DelBridgePortRequest) Reset() { *m = DelBridgePortRequest{} } +func (m *DelBridgePortRequest) String() string { return proto.CompactTextString(m) } +func (*DelBridgePortRequest) ProtoMessage() {} +func (*DelBridgePortRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_agent_3f2a378016922cd7, []int{4} +} +func (m *DelBridgePortRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DelBridgePortRequest.Unmarshal(m, b) +} +func (m *DelBridgePortRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DelBridgePortRequest.Marshal(b, m, deterministic) +} +func (dst *DelBridgePortRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DelBridgePortRequest.Merge(dst, src) +} +func (m *DelBridgePortRequest) XXX_Size() int { + return xxx_messageInfo_DelBridgePortRequest.Size(m) +} +func (m *DelBridgePortRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DelBridgePortRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_DelBridgePortRequest proto.InternalMessageInfo + +func (m *DelBridgePortRequest) GetBridge() string { + if m != nil { + return m.Bridge + } + return "" +} + +func (m *DelBridgePortRequest) GetPort() string { + if m != nil { + return m.Port + } + return "" +} + +type AddFlowRequest struct { + Bridge string `protobuf:"bytes,1,opt,name=bridge,proto3" json:"bridge,omitempty"` + Flow *Flow `protobuf:"bytes,2,opt,name=flow,proto3" json:"flow,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AddFlowRequest) Reset() { *m = AddFlowRequest{} } +func (m *AddFlowRequest) String() string { return proto.CompactTextString(m) } +func (*AddFlowRequest) ProtoMessage() {} +func (*AddFlowRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_agent_3f2a378016922cd7, []int{5} +} +func (m *AddFlowRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AddFlowRequest.Unmarshal(m, b) +} +func (m *AddFlowRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AddFlowRequest.Marshal(b, m, deterministic) +} +func (dst *AddFlowRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_AddFlowRequest.Merge(dst, src) +} +func (m *AddFlowRequest) XXX_Size() int { + return xxx_messageInfo_AddFlowRequest.Size(m) +} +func (m *AddFlowRequest) XXX_DiscardUnknown() { + xxx_messageInfo_AddFlowRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_AddFlowRequest proto.InternalMessageInfo + +func (m *AddFlowRequest) GetBridge() string { + if m != nil { + return m.Bridge + } + return "" +} + +func (m *AddFlowRequest) GetFlow() *Flow { + if m != nil { + return m.Flow + } + return nil +} + +type DelFlowRequest struct { + Bridge string `protobuf:"bytes,1,opt,name=bridge,proto3" json:"bridge,omitempty"` + Flow *Flow `protobuf:"bytes,2,opt,name=flow,proto3" json:"flow,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DelFlowRequest) Reset() { *m = DelFlowRequest{} } +func (m *DelFlowRequest) String() string { return proto.CompactTextString(m) } +func (*DelFlowRequest) ProtoMessage() {} +func (*DelFlowRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_agent_3f2a378016922cd7, []int{6} +} +func (m *DelFlowRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DelFlowRequest.Unmarshal(m, b) +} +func (m *DelFlowRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DelFlowRequest.Marshal(b, m, deterministic) +} +func (dst *DelFlowRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DelFlowRequest.Merge(dst, src) +} +func (m *DelFlowRequest) XXX_Size() int { + return xxx_messageInfo_DelFlowRequest.Size(m) +} +func (m *DelFlowRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DelFlowRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_DelFlowRequest proto.InternalMessageInfo + +func (m *DelFlowRequest) GetBridge() string { + if m != nil { + return m.Bridge + } + return "" +} + +func (m *DelFlowRequest) GetFlow() *Flow { + if m != nil { + return m.Flow + } + return nil +} + +type SyncFlowsRequest struct { + Bridge string `protobuf:"bytes,1,opt,name=bridge,proto3" json:"bridge,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SyncFlowsRequest) Reset() { *m = SyncFlowsRequest{} } +func (m *SyncFlowsRequest) String() string { return proto.CompactTextString(m) } +func (*SyncFlowsRequest) ProtoMessage() {} +func (*SyncFlowsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_agent_3f2a378016922cd7, []int{7} +} +func (m *SyncFlowsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SyncFlowsRequest.Unmarshal(m, b) +} +func (m *SyncFlowsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SyncFlowsRequest.Marshal(b, m, deterministic) +} +func (dst *SyncFlowsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SyncFlowsRequest.Merge(dst, src) +} +func (m *SyncFlowsRequest) XXX_Size() int { + return xxx_messageInfo_SyncFlowsRequest.Size(m) +} +func (m *SyncFlowsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SyncFlowsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SyncFlowsRequest proto.InternalMessageInfo + +func (m *SyncFlowsRequest) GetBridge() string { + if m != nil { + return m.Bridge + } + return "" +} + +type Flow struct { + Cookie uint64 `protobuf:"varint,1,opt,name=cookie,proto3" json:"cookie,omitempty"` + Priority uint32 `protobuf:"varint,2,opt,name=priority,proto3" json:"priority,omitempty"` + Table uint32 `protobuf:"varint,3,opt,name=table,proto3" json:"table,omitempty"` + Matches string `protobuf:"bytes,4,opt,name=matches,proto3" json:"matches,omitempty"` + Actions string `protobuf:"bytes,5,opt,name=actions,proto3" json:"actions,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Flow) Reset() { *m = Flow{} } +func (m *Flow) String() string { return proto.CompactTextString(m) } +func (*Flow) ProtoMessage() {} +func (*Flow) Descriptor() ([]byte, []int) { + return fileDescriptor_agent_3f2a378016922cd7, []int{8} +} +func (m *Flow) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Flow.Unmarshal(m, b) +} +func (m *Flow) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Flow.Marshal(b, m, deterministic) +} +func (dst *Flow) XXX_Merge(src proto.Message) { + xxx_messageInfo_Flow.Merge(dst, src) +} +func (m *Flow) XXX_Size() int { + return xxx_messageInfo_Flow.Size(m) +} +func (m *Flow) XXX_DiscardUnknown() { + xxx_messageInfo_Flow.DiscardUnknown(m) +} + +var xxx_messageInfo_Flow proto.InternalMessageInfo + +func (m *Flow) GetCookie() uint64 { + if m != nil { + return m.Cookie + } + return 0 +} + +func (m *Flow) GetPriority() uint32 { + if m != nil { + return m.Priority + } + return 0 +} + +func (m *Flow) GetTable() uint32 { + if m != nil { + return m.Table + } + return 0 +} + +func (m *Flow) GetMatches() string { + if m != nil { + return m.Matches + } + return "" +} + +func (m *Flow) GetActions() string { + if m != nil { + return m.Actions + } + return "" +} + +type PortStats struct { + PortNo uint32 `protobuf:"varint,1,opt,name=port_no,json=portNo,proto3" json:"port_no,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PortStats) Reset() { *m = PortStats{} } +func (m *PortStats) String() string { return proto.CompactTextString(m) } +func (*PortStats) ProtoMessage() {} +func (*PortStats) Descriptor() ([]byte, []int) { + return fileDescriptor_agent_3f2a378016922cd7, []int{9} +} +func (m *PortStats) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PortStats.Unmarshal(m, b) +} +func (m *PortStats) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PortStats.Marshal(b, m, deterministic) +} +func (dst *PortStats) XXX_Merge(src proto.Message) { + xxx_messageInfo_PortStats.Merge(dst, src) +} +func (m *PortStats) XXX_Size() int { + return xxx_messageInfo_PortStats.Size(m) +} +func (m *PortStats) XXX_DiscardUnknown() { + xxx_messageInfo_PortStats.DiscardUnknown(m) +} + +var xxx_messageInfo_PortStats proto.InternalMessageInfo + +func (m *PortStats) GetPortNo() uint32 { + if m != nil { + return m.PortNo + } + return 0 +} + +type DumpBridgePortRequest struct { + Bridge string `protobuf:"bytes,1,opt,name=bridge,proto3" json:"bridge,omitempty"` + Port string `protobuf:"bytes,2,opt,name=port,proto3" json:"port,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DumpBridgePortRequest) Reset() { *m = DumpBridgePortRequest{} } +func (m *DumpBridgePortRequest) String() string { return proto.CompactTextString(m) } +func (*DumpBridgePortRequest) ProtoMessage() {} +func (*DumpBridgePortRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_agent_3f2a378016922cd7, []int{10} +} +func (m *DumpBridgePortRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DumpBridgePortRequest.Unmarshal(m, b) +} +func (m *DumpBridgePortRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DumpBridgePortRequest.Marshal(b, m, deterministic) +} +func (dst *DumpBridgePortRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DumpBridgePortRequest.Merge(dst, src) +} +func (m *DumpBridgePortRequest) XXX_Size() int { + return xxx_messageInfo_DumpBridgePortRequest.Size(m) +} +func (m *DumpBridgePortRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DumpBridgePortRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_DumpBridgePortRequest proto.InternalMessageInfo + +func (m *DumpBridgePortRequest) GetBridge() string { + if m != nil { + return m.Bridge + } + return "" +} + +func (m *DumpBridgePortRequest) GetPort() string { + if m != nil { + return m.Port + } + return "" +} + +type DumpBridgePortResponse struct { + Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` + Mesg string `protobuf:"bytes,2,opt,name=mesg,proto3" json:"mesg,omitempty"` + PortStats *PortStats `protobuf:"bytes,3,opt,name=port_stats,json=portStats,proto3" json:"port_stats,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DumpBridgePortResponse) Reset() { *m = DumpBridgePortResponse{} } +func (m *DumpBridgePortResponse) String() string { return proto.CompactTextString(m) } +func (*DumpBridgePortResponse) ProtoMessage() {} +func (*DumpBridgePortResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_agent_3f2a378016922cd7, []int{11} +} +func (m *DumpBridgePortResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DumpBridgePortResponse.Unmarshal(m, b) +} +func (m *DumpBridgePortResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DumpBridgePortResponse.Marshal(b, m, deterministic) +} +func (dst *DumpBridgePortResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_DumpBridgePortResponse.Merge(dst, src) +} +func (m *DumpBridgePortResponse) XXX_Size() int { + return xxx_messageInfo_DumpBridgePortResponse.Size(m) +} +func (m *DumpBridgePortResponse) XXX_DiscardUnknown() { + xxx_messageInfo_DumpBridgePortResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_DumpBridgePortResponse proto.InternalMessageInfo + +func (m *DumpBridgePortResponse) GetCode() uint32 { + if m != nil { + return m.Code + } + return 0 +} + +func (m *DumpBridgePortResponse) GetMesg() string { + if m != nil { + return m.Mesg + } + return "" +} + +func (m *DumpBridgePortResponse) GetPortStats() *PortStats { + if m != nil { + return m.PortStats + } + return nil +} + +func init() { + proto.RegisterType((*Response)(nil), "pb.Response") + proto.RegisterType((*AddBridgeRequest)(nil), "pb.AddBridgeRequest") + proto.RegisterType((*DelBridgeRequest)(nil), "pb.DelBridgeRequest") + proto.RegisterType((*AddBridgePortRequest)(nil), "pb.AddBridgePortRequest") + proto.RegisterType((*DelBridgePortRequest)(nil), "pb.DelBridgePortRequest") + proto.RegisterType((*AddFlowRequest)(nil), "pb.AddFlowRequest") + proto.RegisterType((*DelFlowRequest)(nil), "pb.DelFlowRequest") + proto.RegisterType((*SyncFlowsRequest)(nil), "pb.SyncFlowsRequest") + proto.RegisterType((*Flow)(nil), "pb.Flow") + proto.RegisterType((*PortStats)(nil), "pb.PortStats") + proto.RegisterType((*DumpBridgePortRequest)(nil), "pb.DumpBridgePortRequest") + proto.RegisterType((*DumpBridgePortResponse)(nil), "pb.DumpBridgePortResponse") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// VSwitchClient is the client API for VSwitch service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type VSwitchClient interface { + AddBridge(ctx context.Context, in *AddBridgeRequest, opts ...grpc.CallOption) (*Response, error) + DelBridge(ctx context.Context, in *DelBridgeRequest, opts ...grpc.CallOption) (*Response, error) + AddBridgePort(ctx context.Context, in *AddBridgePortRequest, opts ...grpc.CallOption) (*Response, error) + DelBridgePort(ctx context.Context, in *DelBridgePortRequest, opts ...grpc.CallOption) (*Response, error) +} + +type vSwitchClient struct { + cc *grpc.ClientConn +} + +func NewVSwitchClient(cc *grpc.ClientConn) VSwitchClient { + return &vSwitchClient{cc} +} + +func (c *vSwitchClient) AddBridge(ctx context.Context, in *AddBridgeRequest, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/pb.VSwitch/AddBridge", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vSwitchClient) DelBridge(ctx context.Context, in *DelBridgeRequest, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/pb.VSwitch/DelBridge", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vSwitchClient) AddBridgePort(ctx context.Context, in *AddBridgePortRequest, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/pb.VSwitch/AddBridgePort", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vSwitchClient) DelBridgePort(ctx context.Context, in *DelBridgePortRequest, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/pb.VSwitch/DelBridgePort", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// VSwitchServer is the server API for VSwitch service. +type VSwitchServer interface { + AddBridge(context.Context, *AddBridgeRequest) (*Response, error) + DelBridge(context.Context, *DelBridgeRequest) (*Response, error) + AddBridgePort(context.Context, *AddBridgePortRequest) (*Response, error) + DelBridgePort(context.Context, *DelBridgePortRequest) (*Response, error) +} + +func RegisterVSwitchServer(s *grpc.Server, srv VSwitchServer) { + s.RegisterService(&_VSwitch_serviceDesc, srv) +} + +func _VSwitch_AddBridge_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddBridgeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VSwitchServer).AddBridge(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.VSwitch/AddBridge", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VSwitchServer).AddBridge(ctx, req.(*AddBridgeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _VSwitch_DelBridge_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DelBridgeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VSwitchServer).DelBridge(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.VSwitch/DelBridge", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VSwitchServer).DelBridge(ctx, req.(*DelBridgeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _VSwitch_AddBridgePort_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddBridgePortRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VSwitchServer).AddBridgePort(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.VSwitch/AddBridgePort", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VSwitchServer).AddBridgePort(ctx, req.(*AddBridgePortRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _VSwitch_DelBridgePort_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DelBridgePortRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VSwitchServer).DelBridgePort(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.VSwitch/DelBridgePort", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VSwitchServer).DelBridgePort(ctx, req.(*DelBridgePortRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _VSwitch_serviceDesc = grpc.ServiceDesc{ + ServiceName: "pb.VSwitch", + HandlerType: (*VSwitchServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "AddBridge", + Handler: _VSwitch_AddBridge_Handler, + }, + { + MethodName: "DelBridge", + Handler: _VSwitch_DelBridge_Handler, + }, + { + MethodName: "AddBridgePort", + Handler: _VSwitch_AddBridgePort_Handler, + }, + { + MethodName: "DelBridgePort", + Handler: _VSwitch_DelBridgePort_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "agent.proto", +} + +// OpenflowClient is the client API for Openflow service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type OpenflowClient interface { + AddFlow(ctx context.Context, in *AddFlowRequest, opts ...grpc.CallOption) (*Response, error) + DelFlow(ctx context.Context, in *DelFlowRequest, opts ...grpc.CallOption) (*Response, error) + SyncFlows(ctx context.Context, in *SyncFlowsRequest, opts ...grpc.CallOption) (*Response, error) + DumpBridgePort(ctx context.Context, in *DumpBridgePortRequest, opts ...grpc.CallOption) (*DumpBridgePortResponse, error) +} + +type openflowClient struct { + cc *grpc.ClientConn +} + +func NewOpenflowClient(cc *grpc.ClientConn) OpenflowClient { + return &openflowClient{cc} +} + +func (c *openflowClient) AddFlow(ctx context.Context, in *AddFlowRequest, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/pb.Openflow/AddFlow", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *openflowClient) DelFlow(ctx context.Context, in *DelFlowRequest, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/pb.Openflow/DelFlow", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *openflowClient) SyncFlows(ctx context.Context, in *SyncFlowsRequest, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/pb.Openflow/SyncFlows", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *openflowClient) DumpBridgePort(ctx context.Context, in *DumpBridgePortRequest, opts ...grpc.CallOption) (*DumpBridgePortResponse, error) { + out := new(DumpBridgePortResponse) + err := c.cc.Invoke(ctx, "/pb.Openflow/DumpBridgePort", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// OpenflowServer is the server API for Openflow service. +type OpenflowServer interface { + AddFlow(context.Context, *AddFlowRequest) (*Response, error) + DelFlow(context.Context, *DelFlowRequest) (*Response, error) + SyncFlows(context.Context, *SyncFlowsRequest) (*Response, error) + DumpBridgePort(context.Context, *DumpBridgePortRequest) (*DumpBridgePortResponse, error) +} + +func RegisterOpenflowServer(s *grpc.Server, srv OpenflowServer) { + s.RegisterService(&_Openflow_serviceDesc, srv) +} + +func _Openflow_AddFlow_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddFlowRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(OpenflowServer).AddFlow(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.Openflow/AddFlow", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(OpenflowServer).AddFlow(ctx, req.(*AddFlowRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Openflow_DelFlow_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DelFlowRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(OpenflowServer).DelFlow(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.Openflow/DelFlow", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(OpenflowServer).DelFlow(ctx, req.(*DelFlowRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Openflow_SyncFlows_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SyncFlowsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(OpenflowServer).SyncFlows(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.Openflow/SyncFlows", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(OpenflowServer).SyncFlows(ctx, req.(*SyncFlowsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Openflow_DumpBridgePort_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DumpBridgePortRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(OpenflowServer).DumpBridgePort(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.Openflow/DumpBridgePort", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(OpenflowServer).DumpBridgePort(ctx, req.(*DumpBridgePortRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Openflow_serviceDesc = grpc.ServiceDesc{ + ServiceName: "pb.Openflow", + HandlerType: (*OpenflowServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "AddFlow", + Handler: _Openflow_AddFlow_Handler, + }, + { + MethodName: "DelFlow", + Handler: _Openflow_DelFlow_Handler, + }, + { + MethodName: "SyncFlows", + Handler: _Openflow_SyncFlows_Handler, + }, + { + MethodName: "DumpBridgePort", + Handler: _Openflow_DumpBridgePort_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "agent.proto", +} + +func init() { proto.RegisterFile("agent.proto", fileDescriptor_agent_3f2a378016922cd7) } + +var fileDescriptor_agent_3f2a378016922cd7 = []byte{ + // 469 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0x4b, 0x6f, 0xd3, 0x40, + 0x10, 0xc6, 0xc5, 0x8d, 0xe3, 0x09, 0xae, 0xaa, 0x51, 0x28, 0xc6, 0xe2, 0x50, 0x59, 0x1c, 0xaa, + 0x8a, 0x46, 0xc2, 0x9c, 0x38, 0xb6, 0x44, 0x95, 0xb8, 0x00, 0xda, 0x48, 0x5c, 0x91, 0x1f, 0x4b, + 0x6a, 0xe1, 0x78, 0x17, 0xef, 0x56, 0x51, 0xef, 0xfc, 0x55, 0xf8, 0x1d, 0x68, 0xd6, 0x0f, 0xd5, + 0x8e, 0xa5, 0x40, 0x7b, 0x9b, 0xd7, 0x37, 0x33, 0x9e, 0xfd, 0x3e, 0xc3, 0x2c, 0x5e, 0xf3, 0x52, + 0x2f, 0x64, 0x25, 0xb4, 0xc0, 0x03, 0x99, 0x84, 0x11, 0x4c, 0x19, 0x57, 0x52, 0x94, 0x8a, 0x23, + 0x82, 0x9d, 0x8a, 0x8c, 0xfb, 0xd6, 0xa9, 0x75, 0xe6, 0x31, 0x63, 0x53, 0x6c, 0xc3, 0xd5, 0xda, + 0x3f, 0x38, 0xb5, 0xce, 0x5c, 0x66, 0xec, 0xf0, 0x1c, 0x8e, 0x2f, 0xb3, 0xec, 0xaa, 0xca, 0xb3, + 0x35, 0x67, 0xfc, 0xe7, 0x2d, 0x57, 0x1a, 0x4f, 0x60, 0x92, 0x98, 0x80, 0x41, 0xbb, 0xac, 0xf1, + 0xa8, 0x76, 0xc9, 0x8b, 0x7f, 0xab, 0xbd, 0x82, 0x79, 0xd7, 0xf7, 0x8b, 0xa8, 0xf4, 0x9e, 0x7a, + 0xda, 0x4d, 0x8a, 0x4a, 0xb7, 0xbb, 0x91, 0x4d, 0x3d, 0xba, 0x79, 0x0f, 0xed, 0x71, 0x0d, 0x47, + 0x97, 0x59, 0x76, 0x5d, 0x88, 0xed, 0x3e, 0xf4, 0x2b, 0xb0, 0xbf, 0x17, 0x62, 0x6b, 0xd0, 0xb3, + 0x68, 0xba, 0x90, 0xc9, 0xc2, 0xc0, 0x4c, 0x94, 0xfa, 0x2c, 0x79, 0xf1, 0xf8, 0x3e, 0xe7, 0x70, + 0xbc, 0xba, 0x2b, 0x53, 0x8a, 0xa8, 0x7d, 0x37, 0xfc, 0x65, 0x81, 0x4d, 0x85, 0x54, 0x90, 0x0a, + 0xf1, 0x23, 0xaf, 0x0b, 0x6c, 0xd6, 0x78, 0x18, 0xc0, 0x54, 0x56, 0xb9, 0xa8, 0x72, 0x7d, 0x67, + 0xc6, 0x79, 0xac, 0xf3, 0x71, 0x0e, 0x87, 0x3a, 0x4e, 0x0a, 0xee, 0x3f, 0x35, 0x89, 0xda, 0x41, + 0x1f, 0x9c, 0x4d, 0xac, 0xd3, 0x1b, 0xae, 0x7c, 0xdb, 0xcc, 0x6a, 0x5d, 0xca, 0xc4, 0xa9, 0xce, + 0x45, 0xa9, 0xfc, 0xc3, 0x3a, 0xd3, 0xb8, 0xe1, 0x6b, 0x70, 0xe9, 0xfa, 0x2b, 0x1d, 0x6b, 0x85, + 0x2f, 0xc0, 0xa1, 0xbb, 0x7e, 0x2b, 0x45, 0x43, 0xad, 0x09, 0xb9, 0x9f, 0x44, 0xf8, 0x01, 0x9e, + 0x2f, 0x6f, 0x37, 0xf2, 0x71, 0xaf, 0x55, 0xc2, 0xc9, 0xb0, 0xc9, 0xff, 0xf1, 0x19, 0xdf, 0x00, + 0x98, 0xfd, 0x14, 0x6d, 0x6b, 0xbe, 0x7d, 0x16, 0x79, 0xf4, 0x06, 0xdd, 0x27, 0x30, 0x57, 0xb6, + 0x66, 0xf4, 0xdb, 0x02, 0xe7, 0xeb, 0x6a, 0x9b, 0xeb, 0xf4, 0x06, 0xdf, 0x82, 0xdb, 0x31, 0x16, + 0xe7, 0x04, 0x19, 0x0a, 0x23, 0x78, 0x46, 0xd1, 0x76, 0xa5, 0xf0, 0x09, 0x41, 0x3a, 0x82, 0xd6, + 0x90, 0xa1, 0x3e, 0x76, 0x20, 0xef, 0xc1, 0xeb, 0xe9, 0x02, 0xfd, 0xde, 0xa4, 0x7b, 0x87, 0x1b, + 0x83, 0xf6, 0xe4, 0x50, 0x43, 0xc7, 0x14, 0x32, 0x84, 0x46, 0x7f, 0x2c, 0x98, 0x7e, 0x96, 0xbc, + 0x24, 0x0a, 0xe2, 0x05, 0x38, 0x8d, 0x24, 0x10, 0x9b, 0xe1, 0xf7, 0x78, 0xbd, 0x33, 0xf6, 0x02, + 0x9c, 0x86, 0xf9, 0x75, 0x79, 0x5f, 0x06, 0x63, 0x37, 0xe9, 0x08, 0x5e, 0xdf, 0x64, 0xc8, 0xf7, + 0x1d, 0xc8, 0x47, 0x38, 0xea, 0xbf, 0x3a, 0xbe, 0x34, 0x83, 0xc6, 0xe8, 0x14, 0x04, 0x63, 0xa9, + 0xb6, 0x55, 0x32, 0x31, 0x7f, 0xc3, 0x77, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xe3, 0x56, 0xf3, + 0x6a, 0x1c, 0x05, 0x00, 0x00, +} diff --git a/vendor/yunion.io/x/sdnagent/pkg/agent/proto/agent.proto b/vendor/yunion.io/x/sdnagent/pkg/agent/proto/agent.proto new file mode 100644 index 000000000..928f4d35a --- /dev/null +++ b/vendor/yunion.io/x/sdnagent/pkg/agent/proto/agent.proto @@ -0,0 +1,79 @@ +syntax = "proto3"; + +package pb; + +// TODO add comment: idempotency + +service VSwitch { + rpc AddBridge (AddBridgeRequest) returns (Response) {} + rpc DelBridge (DelBridgeRequest) returns (Response) {} + rpc AddBridgePort (AddBridgePortRequest) returns (Response) {} + rpc DelBridgePort (DelBridgePortRequest) returns (Response) {} +} + +service Openflow { + rpc AddFlow (AddFlowRequest) returns (Response) {} + rpc DelFlow (DelFlowRequest) returns (Response) {} + rpc SyncFlows (SyncFlowsRequest) returns (Response) {} + rpc DumpBridgePort (DumpBridgePortRequest) returns (DumpBridgePortResponse) {} +} + +message Response { + uint32 code = 1; + string mesg = 2; +} + +message AddBridgeRequest { + string bridge = 1; +} + +message DelBridgeRequest { + string bridge = 1; +} + +message AddBridgePortRequest { + string bridge = 1; + string port = 2; +} + +message DelBridgePortRequest { + string bridge = 1; + string port = 2; +} + +message AddFlowRequest { + string bridge = 1; + Flow flow = 2; +} + +message DelFlowRequest { + string bridge = 1; + Flow flow = 2; +} + +message SyncFlowsRequest { + string bridge =1; +} + +message Flow { + uint64 cookie = 1; + uint32 priority = 2; + uint32 table = 3; + string matches = 4; + string actions = 5; +} + +message PortStats { + uint32 port_no = 1; +} + +message DumpBridgePortRequest { + string bridge = 1; + string port = 2; +} + +message DumpBridgePortResponse { + uint32 code = 1; + string mesg = 2; + PortStats port_stats = 3; +} diff --git a/vendor/yunion.io/x/sdnagent/pkg/agent/proto/agent_pb2.py b/vendor/yunion.io/x/sdnagent/pkg/agent/proto/agent_pb2.py new file mode 100644 index 000000000..06407f546 --- /dev/null +++ b/vendor/yunion.io/x/sdnagent/pkg/agent/proto/agent_pb2.py @@ -0,0 +1,687 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: agent.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='agent.proto', + package='pb', + syntax='proto3', + serialized_pb=_b('\n\x0b\x61gent.proto\x12\x02pb\"&\n\x08Response\x12\x0c\n\x04\x63ode\x18\x01 \x01(\r\x12\x0c\n\x04mesg\x18\x02 \x01(\t\"\"\n\x10\x41\x64\x64\x42ridgeRequest\x12\x0e\n\x06\x62ridge\x18\x01 \x01(\t\"\"\n\x10\x44\x65lBridgeRequest\x12\x0e\n\x06\x62ridge\x18\x01 \x01(\t\"4\n\x14\x41\x64\x64\x42ridgePortRequest\x12\x0e\n\x06\x62ridge\x18\x01 \x01(\t\x12\x0c\n\x04port\x18\x02 \x01(\t\"4\n\x14\x44\x65lBridgePortRequest\x12\x0e\n\x06\x62ridge\x18\x01 \x01(\t\x12\x0c\n\x04port\x18\x02 \x01(\t\"8\n\x0e\x41\x64\x64\x46lowRequest\x12\x0e\n\x06\x62ridge\x18\x01 \x01(\t\x12\x16\n\x04\x66low\x18\x02 \x01(\x0b\x32\x08.pb.Flow\"8\n\x0e\x44\x65lFlowRequest\x12\x0e\n\x06\x62ridge\x18\x01 \x01(\t\x12\x16\n\x04\x66low\x18\x02 \x01(\x0b\x32\x08.pb.Flow\"\"\n\x10SyncFlowsRequest\x12\x0e\n\x06\x62ridge\x18\x01 \x01(\t\"Y\n\x04\x46low\x12\x0e\n\x06\x63ookie\x18\x01 \x01(\x04\x12\x10\n\x08priority\x18\x02 \x01(\r\x12\r\n\x05table\x18\x03 \x01(\r\x12\x0f\n\x07matches\x18\x04 \x01(\t\x12\x0f\n\x07\x61\x63tions\x18\x05 \x01(\t\"\x1c\n\tPortStats\x12\x0f\n\x07port_no\x18\x01 \x01(\r\"5\n\x15\x44umpBridgePortRequest\x12\x0e\n\x06\x62ridge\x18\x01 \x01(\t\x12\x0c\n\x04port\x18\x02 \x01(\t\"W\n\x16\x44umpBridgePortResponse\x12\x0c\n\x04\x63ode\x18\x01 \x01(\r\x12\x0c\n\x04mesg\x18\x02 \x01(\t\x12!\n\nport_stats\x18\x03 \x01(\x0b\x32\r.pb.PortStats2\xe5\x01\n\x07VSwitch\x12\x31\n\tAddBridge\x12\x14.pb.AddBridgeRequest\x1a\x0c.pb.Response\"\x00\x12\x31\n\tDelBridge\x12\x14.pb.DelBridgeRequest\x1a\x0c.pb.Response\"\x00\x12\x39\n\rAddBridgePort\x12\x18.pb.AddBridgePortRequest\x1a\x0c.pb.Response\"\x00\x12\x39\n\rDelBridgePort\x12\x18.pb.DelBridgePortRequest\x1a\x0c.pb.Response\"\x00\x32\xe6\x01\n\x08Openflow\x12-\n\x07\x41\x64\x64\x46low\x12\x12.pb.AddFlowRequest\x1a\x0c.pb.Response\"\x00\x12-\n\x07\x44\x65lFlow\x12\x12.pb.DelFlowRequest\x1a\x0c.pb.Response\"\x00\x12\x31\n\tSyncFlows\x12\x14.pb.SyncFlowsRequest\x1a\x0c.pb.Response\"\x00\x12I\n\x0e\x44umpBridgePort\x12\x19.pb.DumpBridgePortRequest\x1a\x1a.pb.DumpBridgePortResponse\"\x00\x62\x06proto3') +) + + + + +_RESPONSE = _descriptor.Descriptor( + name='Response', + full_name='pb.Response', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='code', full_name='pb.Response.code', index=0, + number=1, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='mesg', full_name='pb.Response.mesg', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=19, + serialized_end=57, +) + + +_ADDBRIDGEREQUEST = _descriptor.Descriptor( + name='AddBridgeRequest', + full_name='pb.AddBridgeRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='bridge', full_name='pb.AddBridgeRequest.bridge', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=59, + serialized_end=93, +) + + +_DELBRIDGEREQUEST = _descriptor.Descriptor( + name='DelBridgeRequest', + full_name='pb.DelBridgeRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='bridge', full_name='pb.DelBridgeRequest.bridge', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=95, + serialized_end=129, +) + + +_ADDBRIDGEPORTREQUEST = _descriptor.Descriptor( + name='AddBridgePortRequest', + full_name='pb.AddBridgePortRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='bridge', full_name='pb.AddBridgePortRequest.bridge', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='port', full_name='pb.AddBridgePortRequest.port', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=131, + serialized_end=183, +) + + +_DELBRIDGEPORTREQUEST = _descriptor.Descriptor( + name='DelBridgePortRequest', + full_name='pb.DelBridgePortRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='bridge', full_name='pb.DelBridgePortRequest.bridge', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='port', full_name='pb.DelBridgePortRequest.port', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=185, + serialized_end=237, +) + + +_ADDFLOWREQUEST = _descriptor.Descriptor( + name='AddFlowRequest', + full_name='pb.AddFlowRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='bridge', full_name='pb.AddFlowRequest.bridge', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='flow', full_name='pb.AddFlowRequest.flow', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=239, + serialized_end=295, +) + + +_DELFLOWREQUEST = _descriptor.Descriptor( + name='DelFlowRequest', + full_name='pb.DelFlowRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='bridge', full_name='pb.DelFlowRequest.bridge', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='flow', full_name='pb.DelFlowRequest.flow', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=297, + serialized_end=353, +) + + +_SYNCFLOWSREQUEST = _descriptor.Descriptor( + name='SyncFlowsRequest', + full_name='pb.SyncFlowsRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='bridge', full_name='pb.SyncFlowsRequest.bridge', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=355, + serialized_end=389, +) + + +_FLOW = _descriptor.Descriptor( + name='Flow', + full_name='pb.Flow', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='cookie', full_name='pb.Flow.cookie', index=0, + number=1, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='priority', full_name='pb.Flow.priority', index=1, + number=2, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='table', full_name='pb.Flow.table', index=2, + number=3, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='matches', full_name='pb.Flow.matches', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='actions', full_name='pb.Flow.actions', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=391, + serialized_end=480, +) + + +_PORTSTATS = _descriptor.Descriptor( + name='PortStats', + full_name='pb.PortStats', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='port_no', full_name='pb.PortStats.port_no', index=0, + number=1, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=482, + serialized_end=510, +) + + +_DUMPBRIDGEPORTREQUEST = _descriptor.Descriptor( + name='DumpBridgePortRequest', + full_name='pb.DumpBridgePortRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='bridge', full_name='pb.DumpBridgePortRequest.bridge', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='port', full_name='pb.DumpBridgePortRequest.port', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=512, + serialized_end=565, +) + + +_DUMPBRIDGEPORTRESPONSE = _descriptor.Descriptor( + name='DumpBridgePortResponse', + full_name='pb.DumpBridgePortResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='code', full_name='pb.DumpBridgePortResponse.code', index=0, + number=1, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='mesg', full_name='pb.DumpBridgePortResponse.mesg', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='port_stats', full_name='pb.DumpBridgePortResponse.port_stats', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=567, + serialized_end=654, +) + +_ADDFLOWREQUEST.fields_by_name['flow'].message_type = _FLOW +_DELFLOWREQUEST.fields_by_name['flow'].message_type = _FLOW +_DUMPBRIDGEPORTRESPONSE.fields_by_name['port_stats'].message_type = _PORTSTATS +DESCRIPTOR.message_types_by_name['Response'] = _RESPONSE +DESCRIPTOR.message_types_by_name['AddBridgeRequest'] = _ADDBRIDGEREQUEST +DESCRIPTOR.message_types_by_name['DelBridgeRequest'] = _DELBRIDGEREQUEST +DESCRIPTOR.message_types_by_name['AddBridgePortRequest'] = _ADDBRIDGEPORTREQUEST +DESCRIPTOR.message_types_by_name['DelBridgePortRequest'] = _DELBRIDGEPORTREQUEST +DESCRIPTOR.message_types_by_name['AddFlowRequest'] = _ADDFLOWREQUEST +DESCRIPTOR.message_types_by_name['DelFlowRequest'] = _DELFLOWREQUEST +DESCRIPTOR.message_types_by_name['SyncFlowsRequest'] = _SYNCFLOWSREQUEST +DESCRIPTOR.message_types_by_name['Flow'] = _FLOW +DESCRIPTOR.message_types_by_name['PortStats'] = _PORTSTATS +DESCRIPTOR.message_types_by_name['DumpBridgePortRequest'] = _DUMPBRIDGEPORTREQUEST +DESCRIPTOR.message_types_by_name['DumpBridgePortResponse'] = _DUMPBRIDGEPORTRESPONSE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Response = _reflection.GeneratedProtocolMessageType('Response', (_message.Message,), dict( + DESCRIPTOR = _RESPONSE, + __module__ = 'agent_pb2' + # @@protoc_insertion_point(class_scope:pb.Response) + )) +_sym_db.RegisterMessage(Response) + +AddBridgeRequest = _reflection.GeneratedProtocolMessageType('AddBridgeRequest', (_message.Message,), dict( + DESCRIPTOR = _ADDBRIDGEREQUEST, + __module__ = 'agent_pb2' + # @@protoc_insertion_point(class_scope:pb.AddBridgeRequest) + )) +_sym_db.RegisterMessage(AddBridgeRequest) + +DelBridgeRequest = _reflection.GeneratedProtocolMessageType('DelBridgeRequest', (_message.Message,), dict( + DESCRIPTOR = _DELBRIDGEREQUEST, + __module__ = 'agent_pb2' + # @@protoc_insertion_point(class_scope:pb.DelBridgeRequest) + )) +_sym_db.RegisterMessage(DelBridgeRequest) + +AddBridgePortRequest = _reflection.GeneratedProtocolMessageType('AddBridgePortRequest', (_message.Message,), dict( + DESCRIPTOR = _ADDBRIDGEPORTREQUEST, + __module__ = 'agent_pb2' + # @@protoc_insertion_point(class_scope:pb.AddBridgePortRequest) + )) +_sym_db.RegisterMessage(AddBridgePortRequest) + +DelBridgePortRequest = _reflection.GeneratedProtocolMessageType('DelBridgePortRequest', (_message.Message,), dict( + DESCRIPTOR = _DELBRIDGEPORTREQUEST, + __module__ = 'agent_pb2' + # @@protoc_insertion_point(class_scope:pb.DelBridgePortRequest) + )) +_sym_db.RegisterMessage(DelBridgePortRequest) + +AddFlowRequest = _reflection.GeneratedProtocolMessageType('AddFlowRequest', (_message.Message,), dict( + DESCRIPTOR = _ADDFLOWREQUEST, + __module__ = 'agent_pb2' + # @@protoc_insertion_point(class_scope:pb.AddFlowRequest) + )) +_sym_db.RegisterMessage(AddFlowRequest) + +DelFlowRequest = _reflection.GeneratedProtocolMessageType('DelFlowRequest', (_message.Message,), dict( + DESCRIPTOR = _DELFLOWREQUEST, + __module__ = 'agent_pb2' + # @@protoc_insertion_point(class_scope:pb.DelFlowRequest) + )) +_sym_db.RegisterMessage(DelFlowRequest) + +SyncFlowsRequest = _reflection.GeneratedProtocolMessageType('SyncFlowsRequest', (_message.Message,), dict( + DESCRIPTOR = _SYNCFLOWSREQUEST, + __module__ = 'agent_pb2' + # @@protoc_insertion_point(class_scope:pb.SyncFlowsRequest) + )) +_sym_db.RegisterMessage(SyncFlowsRequest) + +Flow = _reflection.GeneratedProtocolMessageType('Flow', (_message.Message,), dict( + DESCRIPTOR = _FLOW, + __module__ = 'agent_pb2' + # @@protoc_insertion_point(class_scope:pb.Flow) + )) +_sym_db.RegisterMessage(Flow) + +PortStats = _reflection.GeneratedProtocolMessageType('PortStats', (_message.Message,), dict( + DESCRIPTOR = _PORTSTATS, + __module__ = 'agent_pb2' + # @@protoc_insertion_point(class_scope:pb.PortStats) + )) +_sym_db.RegisterMessage(PortStats) + +DumpBridgePortRequest = _reflection.GeneratedProtocolMessageType('DumpBridgePortRequest', (_message.Message,), dict( + DESCRIPTOR = _DUMPBRIDGEPORTREQUEST, + __module__ = 'agent_pb2' + # @@protoc_insertion_point(class_scope:pb.DumpBridgePortRequest) + )) +_sym_db.RegisterMessage(DumpBridgePortRequest) + +DumpBridgePortResponse = _reflection.GeneratedProtocolMessageType('DumpBridgePortResponse', (_message.Message,), dict( + DESCRIPTOR = _DUMPBRIDGEPORTRESPONSE, + __module__ = 'agent_pb2' + # @@protoc_insertion_point(class_scope:pb.DumpBridgePortResponse) + )) +_sym_db.RegisterMessage(DumpBridgePortResponse) + + + +_VSWITCH = _descriptor.ServiceDescriptor( + name='VSwitch', + full_name='pb.VSwitch', + file=DESCRIPTOR, + index=0, + options=None, + serialized_start=657, + serialized_end=886, + methods=[ + _descriptor.MethodDescriptor( + name='AddBridge', + full_name='pb.VSwitch.AddBridge', + index=0, + containing_service=None, + input_type=_ADDBRIDGEREQUEST, + output_type=_RESPONSE, + options=None, + ), + _descriptor.MethodDescriptor( + name='DelBridge', + full_name='pb.VSwitch.DelBridge', + index=1, + containing_service=None, + input_type=_DELBRIDGEREQUEST, + output_type=_RESPONSE, + options=None, + ), + _descriptor.MethodDescriptor( + name='AddBridgePort', + full_name='pb.VSwitch.AddBridgePort', + index=2, + containing_service=None, + input_type=_ADDBRIDGEPORTREQUEST, + output_type=_RESPONSE, + options=None, + ), + _descriptor.MethodDescriptor( + name='DelBridgePort', + full_name='pb.VSwitch.DelBridgePort', + index=3, + containing_service=None, + input_type=_DELBRIDGEPORTREQUEST, + output_type=_RESPONSE, + options=None, + ), +]) +_sym_db.RegisterServiceDescriptor(_VSWITCH) + +DESCRIPTOR.services_by_name['VSwitch'] = _VSWITCH + + +_OPENFLOW = _descriptor.ServiceDescriptor( + name='Openflow', + full_name='pb.Openflow', + file=DESCRIPTOR, + index=1, + options=None, + serialized_start=889, + serialized_end=1119, + methods=[ + _descriptor.MethodDescriptor( + name='AddFlow', + full_name='pb.Openflow.AddFlow', + index=0, + containing_service=None, + input_type=_ADDFLOWREQUEST, + output_type=_RESPONSE, + options=None, + ), + _descriptor.MethodDescriptor( + name='DelFlow', + full_name='pb.Openflow.DelFlow', + index=1, + containing_service=None, + input_type=_DELFLOWREQUEST, + output_type=_RESPONSE, + options=None, + ), + _descriptor.MethodDescriptor( + name='SyncFlows', + full_name='pb.Openflow.SyncFlows', + index=2, + containing_service=None, + input_type=_SYNCFLOWSREQUEST, + output_type=_RESPONSE, + options=None, + ), + _descriptor.MethodDescriptor( + name='DumpBridgePort', + full_name='pb.Openflow.DumpBridgePort', + index=3, + containing_service=None, + input_type=_DUMPBRIDGEPORTREQUEST, + output_type=_DUMPBRIDGEPORTRESPONSE, + options=None, + ), +]) +_sym_db.RegisterServiceDescriptor(_OPENFLOW) + +DESCRIPTOR.services_by_name['Openflow'] = _OPENFLOW + +# @@protoc_insertion_point(module_scope) diff --git a/vendor/yunion.io/x/sdnagent/pkg/agent/proto/agent_pb2_grpc.py b/vendor/yunion.io/x/sdnagent/pkg/agent/proto/agent_pb2_grpc.py new file mode 100644 index 000000000..3b78655f3 --- /dev/null +++ b/vendor/yunion.io/x/sdnagent/pkg/agent/proto/agent_pb2_grpc.py @@ -0,0 +1,192 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +import grpc + +import agent_pb2 as agent__pb2 + + +class VSwitchStub(object): + """TODO add comment: idempotency + + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.AddBridge = channel.unary_unary( + '/pb.VSwitch/AddBridge', + request_serializer=agent__pb2.AddBridgeRequest.SerializeToString, + response_deserializer=agent__pb2.Response.FromString, + ) + self.DelBridge = channel.unary_unary( + '/pb.VSwitch/DelBridge', + request_serializer=agent__pb2.DelBridgeRequest.SerializeToString, + response_deserializer=agent__pb2.Response.FromString, + ) + self.AddBridgePort = channel.unary_unary( + '/pb.VSwitch/AddBridgePort', + request_serializer=agent__pb2.AddBridgePortRequest.SerializeToString, + response_deserializer=agent__pb2.Response.FromString, + ) + self.DelBridgePort = channel.unary_unary( + '/pb.VSwitch/DelBridgePort', + request_serializer=agent__pb2.DelBridgePortRequest.SerializeToString, + response_deserializer=agent__pb2.Response.FromString, + ) + + +class VSwitchServicer(object): + """TODO add comment: idempotency + + """ + + def AddBridge(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def DelBridge(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def AddBridgePort(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def DelBridgePort(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_VSwitchServicer_to_server(servicer, server): + rpc_method_handlers = { + 'AddBridge': grpc.unary_unary_rpc_method_handler( + servicer.AddBridge, + request_deserializer=agent__pb2.AddBridgeRequest.FromString, + response_serializer=agent__pb2.Response.SerializeToString, + ), + 'DelBridge': grpc.unary_unary_rpc_method_handler( + servicer.DelBridge, + request_deserializer=agent__pb2.DelBridgeRequest.FromString, + response_serializer=agent__pb2.Response.SerializeToString, + ), + 'AddBridgePort': grpc.unary_unary_rpc_method_handler( + servicer.AddBridgePort, + request_deserializer=agent__pb2.AddBridgePortRequest.FromString, + response_serializer=agent__pb2.Response.SerializeToString, + ), + 'DelBridgePort': grpc.unary_unary_rpc_method_handler( + servicer.DelBridgePort, + request_deserializer=agent__pb2.DelBridgePortRequest.FromString, + response_serializer=agent__pb2.Response.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'pb.VSwitch', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + +class OpenflowStub(object): + # missing associated documentation comment in .proto file + pass + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.AddFlow = channel.unary_unary( + '/pb.Openflow/AddFlow', + request_serializer=agent__pb2.AddFlowRequest.SerializeToString, + response_deserializer=agent__pb2.Response.FromString, + ) + self.DelFlow = channel.unary_unary( + '/pb.Openflow/DelFlow', + request_serializer=agent__pb2.DelFlowRequest.SerializeToString, + response_deserializer=agent__pb2.Response.FromString, + ) + self.SyncFlows = channel.unary_unary( + '/pb.Openflow/SyncFlows', + request_serializer=agent__pb2.SyncFlowsRequest.SerializeToString, + response_deserializer=agent__pb2.Response.FromString, + ) + self.DumpBridgePort = channel.unary_unary( + '/pb.Openflow/DumpBridgePort', + request_serializer=agent__pb2.DumpBridgePortRequest.SerializeToString, + response_deserializer=agent__pb2.DumpBridgePortResponse.FromString, + ) + + +class OpenflowServicer(object): + # missing associated documentation comment in .proto file + pass + + def AddFlow(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def DelFlow(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def SyncFlows(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def DumpBridgePort(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_OpenflowServicer_to_server(servicer, server): + rpc_method_handlers = { + 'AddFlow': grpc.unary_unary_rpc_method_handler( + servicer.AddFlow, + request_deserializer=agent__pb2.AddFlowRequest.FromString, + response_serializer=agent__pb2.Response.SerializeToString, + ), + 'DelFlow': grpc.unary_unary_rpc_method_handler( + servicer.DelFlow, + request_deserializer=agent__pb2.DelFlowRequest.FromString, + response_serializer=agent__pb2.Response.SerializeToString, + ), + 'SyncFlows': grpc.unary_unary_rpc_method_handler( + servicer.SyncFlows, + request_deserializer=agent__pb2.SyncFlowsRequest.FromString, + response_serializer=agent__pb2.Response.SerializeToString, + ), + 'DumpBridgePort': grpc.unary_unary_rpc_method_handler( + servicer.DumpBridgePort, + request_deserializer=agent__pb2.DumpBridgePortRequest.FromString, + response_serializer=agent__pb2.DumpBridgePortResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'pb.Openflow', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) diff --git a/vendor/yunion.io/x/sdnagent/pkg/agent/proto/common.go b/vendor/yunion.io/x/sdnagent/pkg/agent/proto/common.go new file mode 100644 index 000000000..e2ec5ddab --- /dev/null +++ b/vendor/yunion.io/x/sdnagent/pkg/agent/proto/common.go @@ -0,0 +1,20 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pb + +type CommonResponse interface { + GetCode() uint32 + GetMesg() string +} diff --git a/vendor/yunion.io/x/sdnagent/pkg/agent/proto/doc.go b/vendor/yunion.io/x/sdnagent/pkg/agent/proto/doc.go new file mode 100644 index 000000000..175298cd8 --- /dev/null +++ b/vendor/yunion.io/x/sdnagent/pkg/agent/proto/doc.go @@ -0,0 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pb // import "yunion.io/x/sdnagent/pkg/agent/proto" diff --git a/vendor/yunion.io/x/sdnagent/pkg/agent/proto/flow.go b/vendor/yunion.io/x/sdnagent/pkg/agent/proto/flow.go new file mode 100644 index 000000000..7137136e0 --- /dev/null +++ b/vendor/yunion.io/x/sdnagent/pkg/agent/proto/flow.go @@ -0,0 +1,42 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pb + +import ( + "fmt" + "strings" + + "github.com/digitalocean/go-openvswitch/ovs" +) + +func (f *Flow) OvsFlow() (*ovs.Flow, error) { + chunks := []string{} + if f.Cookie != 0 { + chunks = append(chunks, fmt.Sprintf("cookie=0x%x", f.Cookie)) + } + if f.Table != 0 { + chunks = append(chunks, fmt.Sprintf("table=%d", f.Table)) + } + chunks = append(chunks, fmt.Sprintf("priority=%d", f.Priority)) + chunks = append(chunks, f.Matches) + chunks = append(chunks, fmt.Sprintf("actions=%s", f.Actions)) + txt := strings.Join(chunks, ",") + of := &ovs.Flow{} + err := of.UnmarshalText([]byte(txt)) + if err != nil { + return nil, err + } + return of, nil +} diff --git a/vendor/yunion.io/x/sqlchemy/backends.go b/vendor/yunion.io/x/sqlchemy/backends.go index 7f17adda6..6e2065663 100644 --- a/vendor/yunion.io/x/sqlchemy/backends.go +++ b/vendor/yunion.io/x/sqlchemy/backends.go @@ -109,8 +109,14 @@ type IBackend interface { ////////////////// FUNCTIONS ////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// - // cast + // cast field to specified type CAST(field IQueryField, typeStr string, fieldname string) IQueryField + // cast field to string + CASTString(field IQueryField, fieldname string) IQueryField + // cast field to integer + CASTInt(field IQueryField, fieldname string) IQueryField + // cast field to float + CASTFloat(field IQueryField, fieldname string) IQueryField // TIMESTAMPADD TIMESTAMPADD(name string, field IQueryField, offsetSeconds int) IQueryField // DATE_FORMAT @@ -139,6 +145,8 @@ type IBackend interface { MIN(name string, field IQueryField) IQueryField // SUM SUM(name string, field IQueryField) IQueryField + // AVG + AVG(name string, field IQueryField) IQueryField // LENGTH LENGTH(name string, field IQueryField) IQueryField // LOWER diff --git a/vendor/yunion.io/x/sqlchemy/backends/clickhouse/functions.go b/vendor/yunion.io/x/sqlchemy/backends/clickhouse/functions.go index 31f76ce57..766d94bb7 100644 --- a/vendor/yunion.io/x/sqlchemy/backends/clickhouse/functions.go +++ b/vendor/yunion.io/x/sqlchemy/backends/clickhouse/functions.go @@ -24,3 +24,18 @@ import ( func (click *SClickhouseBackend) GROUP_CONCAT2(name string, sep string, field sqlchemy.IQueryField) sqlchemy.IQueryField { return sqlchemy.NewFunctionField(name, true, fmt.Sprintf("arrayStringConcat(groupUniqArray(%%s), '%s')", sep), field) } + +// cast field to string +func (click *SClickhouseBackend) CASTString(field sqlchemy.IQueryField, fieldname string) sqlchemy.IQueryField { + return sqlchemy.NewFunctionField(fieldname, false, `CAST(%s, 'String')`, field) +} + +// cast field to integer +func (click *SClickhouseBackend) CASTInt(field sqlchemy.IQueryField, fieldname string) sqlchemy.IQueryField { + return sqlchemy.NewFunctionField(fieldname, false, `CAST(%s, 'Int64')`, field) +} + +// cast field to float +func (click *SClickhouseBackend) CASTFloat(field sqlchemy.IQueryField, fieldname string) sqlchemy.IQueryField { + return sqlchemy.NewFunctionField(fieldname, false, `CAST(%s, 'Float64')`, field) +} diff --git a/vendor/yunion.io/x/sqlchemy/backends/dameng/column.go b/vendor/yunion.io/x/sqlchemy/backends/dameng/column.go index 3115d3471..225ba3c7c 100644 --- a/vendor/yunion.io/x/sqlchemy/backends/dameng/column.go +++ b/vendor/yunion.io/x/sqlchemy/backends/dameng/column.go @@ -505,12 +505,12 @@ func (c *STimeTypeColumn) ConvertFromString(str string) interface{} { // NewTimeTypeColumn return an instance of STimeTypeColumn func NewTimeTypeColumn(name string, typeStr string, tagmap map[string]string, isPointer bool) STimeTypeColumn { tagmap, v, ok := utils.TagPop(tagmap, sqlchemy.TAG_PRECISION) - prec := 6 + prec := 0 if ok { prec, _ = strconv.Atoi(v) - if prec > 6 || prec <= 0 { + if prec > 6 || prec < 0 { log.Warningf("datetime field %s precision %d change to 6", name, prec) - prec = 6 + prec = 0 } } dc := STimeTypeColumn{ diff --git a/vendor/yunion.io/x/sqlchemy/backends/dameng/dameng.go b/vendor/yunion.io/x/sqlchemy/backends/dameng/dameng.go index 8fd08a991..26dc8714a 100644 --- a/vendor/yunion.io/x/sqlchemy/backends/dameng/dameng.go +++ b/vendor/yunion.io/x/sqlchemy/backends/dameng/dameng.go @@ -16,6 +16,7 @@ package dameng import ( "fmt" + "log" "reflect" "strconv" "strings" @@ -84,11 +85,17 @@ func (dameng *SDamengBackend) PrepareInsertOrUpdateSQL(ts sqlchemy.ITableSpec, i selectValues := make([]string, 0, len(insertColNames)) onConditions := make([]string, 0, len(onPrimaryCols)) + colNameMap := make(map[string]struct{}) for i := range insertColNames { colName := strings.Trim(insertColNames[i], "'\"") + colNameMap[colName] = struct{}{} selectValues = append(selectValues, fmt.Sprintf("%s AS \"%s\"", insertFields[i], colName)) } for _, primary := range onPrimaryCols { + colName := strings.Trim(primary, "'\"") + if _, ok := colNameMap[colName]; !ok { + log.Fatalf("primary colume %s missing from insert columes for table %s", colName, ts.Name()) + } onConditions = append(onConditions, fmt.Sprintf("T1.%s=T2.%s", primary, primary)) } for i := range updateSetCols { diff --git a/vendor/yunion.io/x/sqlchemy/backends/dameng/functions.go b/vendor/yunion.io/x/sqlchemy/backends/dameng/functions.go index 916b1e866..ed534cce2 100644 --- a/vendor/yunion.io/x/sqlchemy/backends/dameng/functions.go +++ b/vendor/yunion.io/x/sqlchemy/backends/dameng/functions.go @@ -21,6 +21,40 @@ import ( ) // GROUP_CONCAT2 represents the SQL function GROUP_CONCAT -func (mysql *SDamengBackend) GROUP_CONCAT2(name string, sep string, field sqlchemy.IQueryField) sqlchemy.IQueryField { - return sqlchemy.NewFunctionField(name, true, fmt.Sprintf("REPLACE(WM_CONCAT(%%s), ',', '%s')", sep), field) +func (dameng *SDamengBackend) GROUP_CONCAT2(name string, sep string, field sqlchemy.IQueryField) sqlchemy.IQueryField { + if sep == "," { + return sqlchemy.NewFunctionField(name, true, `WM_CONCAT(%s)`, field) + } else { + return sqlchemy.NewFunctionField(name, true, fmt.Sprintf("REPLACE(WM_CONCAT(%%s), ',', '%s')", sep), field) + } +} + +// INET_ATON represents the SQL function INET_ATON +func (dameng *SDamengBackend) INET_ATON(field sqlchemy.IQueryField) sqlchemy.IQueryField { + expr := "" + vars := make([]sqlchemy.IQueryField, 0) + expr += `TO_NUMBER(SUBSTR(%s,1,INSTR(%s,'.')-1))*POWER(256,3)+` + vars = append(vars, field, field) + expr += `TO_NUMBER(SUBSTR(%s,INSTR(%s,'.')+1,INSTR(%s,'.',1,2)-INSTR(%s,'.')-1))*POWER(256,2)+` + vars = append(vars, field, field, field, field) + expr += `TO_NUMBER(SUBSTR(%s,INSTR(%s,'.',1,2)+1,INSTR(%s,'.',1,3)-INSTR(%s,'.',1,2)-1))*256+` + vars = append(vars, field, field, field, field) + expr += `TO_NUMBER(SUBSTR(%s,INSTR(%s,'.',1,3)+1))` + vars = append(vars, field, field) + return sqlchemy.NewFunctionField("", false, expr, vars...) +} + +// cast field to string +func (dameng *SDamengBackend) CASTString(field sqlchemy.IQueryField, fieldname string) sqlchemy.IQueryField { + return sqlchemy.NewFunctionField(fieldname, false, `CAST(%s AS VARCHAR)`, field) +} + +// cast field to integer +func (dameng *SDamengBackend) CASTInt(field sqlchemy.IQueryField, fieldname string) sqlchemy.IQueryField { + return sqlchemy.NewFunctionField(fieldname, false, `CAST(%s AS INTEGER)`, field) +} + +// cast field to float +func (dameng *SDamengBackend) CASTFloat(field sqlchemy.IQueryField, fieldname string) sqlchemy.IQueryField { + return sqlchemy.NewFunctionField(fieldname, false, `CAST(%s AS REAL)`, field) } diff --git a/vendor/yunion.io/x/sqlchemy/backends/mysql/functions.go b/vendor/yunion.io/x/sqlchemy/backends/mysql/functions.go index e812849ac..960a9c641 100644 --- a/vendor/yunion.io/x/sqlchemy/backends/mysql/functions.go +++ b/vendor/yunion.io/x/sqlchemy/backends/mysql/functions.go @@ -24,3 +24,18 @@ import ( func (mysql *SMySQLBackend) GROUP_CONCAT2(name string, sep string, field sqlchemy.IQueryField) sqlchemy.IQueryField { return sqlchemy.NewFunctionField(name, true, fmt.Sprintf("GROUP_CONCAT(%%s SEPARATOR '%s')", sep), field) } + +// cast field to string +func (mysql *SMySQLBackend) CASTString(field sqlchemy.IQueryField, fieldname string) sqlchemy.IQueryField { + return sqlchemy.NewFunctionField(fieldname, false, `CAST(%s AS CHAR)`, field) +} + +// cast field to integer +func (mysql *SMySQLBackend) CASTInt(field sqlchemy.IQueryField, fieldname string) sqlchemy.IQueryField { + return sqlchemy.NewFunctionField(fieldname, false, `CAST(%s AS SIGNED)`, field) +} + +// cast field to float +func (mysql *SMySQLBackend) CASTFloat(field sqlchemy.IQueryField, fieldname string) sqlchemy.IQueryField { + return sqlchemy.NewFunctionField(fieldname, false, `CAST(%s AS DECIMAL)`, field) +} diff --git a/vendor/yunion.io/x/sqlchemy/backends/sqlite/functions.go b/vendor/yunion.io/x/sqlchemy/backends/sqlite/functions.go index 89ff52a42..3936c970a 100644 --- a/vendor/yunion.io/x/sqlchemy/backends/sqlite/functions.go +++ b/vendor/yunion.io/x/sqlchemy/backends/sqlite/functions.go @@ -24,3 +24,18 @@ import ( func (sqlite *SSqliteBackend) GROUP_CONCAT2(name string, sep string, field sqlchemy.IQueryField) sqlchemy.IQueryField { return sqlchemy.NewFunctionField(name, true, fmt.Sprintf("GROUP_CONCAT(%%s, '%s')", sep), field) } + +// cast field to string +func (sqlite *SSqliteBackend) CASTString(field sqlchemy.IQueryField, fieldname string) sqlchemy.IQueryField { + return sqlchemy.NewFunctionField(fieldname, false, `CAST(%s AS TEXT)`, field) +} + +// cast field to integer +func (sqlite *SSqliteBackend) CASTInt(field sqlchemy.IQueryField, fieldname string) sqlchemy.IQueryField { + return sqlchemy.NewFunctionField(fieldname, false, `CAST(%s AS INTEGER)`, field) +} + +// cast field to float +func (sqlite *SSqliteBackend) CASTFloat(field sqlchemy.IQueryField, fieldname string) sqlchemy.IQueryField { + return sqlchemy.NewFunctionField(fieldname, false, `CAST(%s AS REAL)`, field) +} diff --git a/vendor/yunion.io/x/sqlchemy/backends_base.go b/vendor/yunion.io/x/sqlchemy/backends_base.go index 2d56117c4..92420c70e 100644 --- a/vendor/yunion.io/x/sqlchemy/backends_base.go +++ b/vendor/yunion.io/x/sqlchemy/backends_base.go @@ -124,6 +124,21 @@ func (bb *SBaseBackend) CAST(field IQueryField, typeStr string, fieldname string return NewFunctionField(fieldname, false, `CAST(%s AS `+typeStr+`)`, field) } +// cast field to string +func (bb *SBaseBackend) CASTString(field IQueryField, fieldname string) IQueryField { + return NewFunctionField(fieldname, false, `CAST(%s AS CHAR)`, field) +} + +// cast field to integer +func (bb *SBaseBackend) CASTInt(field IQueryField, fieldname string) IQueryField { + return NewFunctionField(fieldname, false, `CAST(%s AS SIGNED)`, field) +} + +// cast field to float +func (bb *SBaseBackend) CASTFloat(field IQueryField, fieldname string) IQueryField { + return NewFunctionField(fieldname, false, `CAST(%s AS REAL)`, field) +} + // TimestampAdd represents a SQL function TimestampAdd func (bb *SBaseBackend) TIMESTAMPADD(name string, field IQueryField, offsetSeconds int) IQueryField { return NewFunctionField(name, false, `TIMESTAMPADD(SECOND, `+fmt.Sprintf("%d", offsetSeconds)+`, %s)`, field) @@ -207,6 +222,11 @@ func (bb *SBaseBackend) SUM(name string, field IQueryField) IQueryField { return NewFunctionField(name, true, "SUM(%s)", field) } +// AVG represents the SQL function SUM +func (bb *SBaseBackend) AVG(name string, field IQueryField) IQueryField { + return NewFunctionField(name, true, "AVG(%s)", field) +} + // LENGTH represents SQL function LENGTH func (bb *SBaseBackend) LENGTH(name string, field IQueryField) IQueryField { return NewFunctionField(name, false, "LENGTH(%s)", field) diff --git a/vendor/yunion.io/x/sqlchemy/functions.go b/vendor/yunion.io/x/sqlchemy/functions.go index 8f784a9cf..812e93b93 100644 --- a/vendor/yunion.io/x/sqlchemy/functions.go +++ b/vendor/yunion.io/x/sqlchemy/functions.go @@ -17,8 +17,6 @@ package sqlchemy import ( "fmt" "strings" - - "yunion.io/x/log" ) // IFunctionQueryField is a special type of field that is an aggregate function @@ -74,12 +72,6 @@ func (ff *SFunctionFieldBase) Reference() string { // Expression implementation of SFunctionFieldBase for IQueryField func (ff *SFunctionFieldBase) Expression() string { - if len(ff.alias) > 0 { - qChar := ff.getQuoteChar() - // add alias - return fmt.Sprintf("%s AS %s%s%s", ff.expression(), qChar, ff.alias, qChar) - } - // no alias return ff.expression() } @@ -116,12 +108,7 @@ type sExprFunction struct { func (ff *sExprFunction) expression() string { fieldRefs := make([]interface{}, 0) for _, f := range ff.fields { - switch tf := f.(type) { - case *SFunctionFieldBase: - fieldRefs = append(fieldRefs, tf.expression()) - default: - fieldRefs = append(fieldRefs, tf.Reference()) - } + fieldRefs = append(fieldRefs, f.Expression()) } return fmt.Sprintf(ff.function, fieldRefs...) } @@ -142,9 +129,9 @@ func (ff *sExprFunction) database() *SDatabase { return db } } - if ff.function != "COUNT(*)" { - log.Debugf("no fields function? %s", ff.expression()) - } + // if ff.function != "COUNT(*)" { + // log.Debugf("no fields function? %s", ff.expression()) + // } return nil } @@ -185,6 +172,11 @@ func SUM(name string, field IQueryField) IQueryField { return getFieldBackend(field).SUM(name, field) } +// AVG represents the SQL function SUM +func AVG(name string, field IQueryField) IQueryField { + return getFieldBackend(field).AVG(name, field) +} + // LOWER represents the SQL function SUM func LOWER(name string, field IQueryField) IQueryField { return getFieldBackend(field).LOWER(name, field) @@ -224,12 +216,7 @@ type SConstField struct { // Expression implementation of SConstField for IQueryField func (s *SConstField) Expression() string { - name := s.Name() - if len(name) == 0 { - return s.Reference() - } else { - return fmt.Sprintf("%s AS %s", s.Reference(), name) - } + return s.Reference() } // Name implementation of SConstField for IQueryField @@ -278,12 +265,7 @@ type SStringField struct { // Expression implementation of SStringField for IQueryField func (s *SStringField) Expression() string { - name := s.Name() - if len(name) == 0 { - return s.Reference() - } else { - return fmt.Sprintf("%s AS %s", s.Reference(), name) - } + return s.Reference() } // Name implementation of SStringField for IQueryField @@ -375,6 +357,21 @@ func CAST(field IQueryField, typeStr string, fieldname string) IQueryField { return getFieldBackend(field).CAST(field, typeStr, fieldname) } +// CASTString represents a SQL function cast any type to String +func CASTString(field IQueryField, fieldname string) IQueryField { + return getFieldBackend(field).CASTString(field, fieldname) +} + +// CASTInt represents a SQL function cast any type to Integer +func CASTInt(field IQueryField, fieldname string) IQueryField { + return getFieldBackend(field).CASTInt(field, fieldname) +} + +// CASTFloat represents a SQL function cast any type to Float +func CASTFloat(field IQueryField, fieldname string) IQueryField { + return getFieldBackend(field).CASTFloat(field, fieldname) +} + // LENGTH represents a SQL function of LENGTH func LENGTH(name string, field IQueryField) IQueryField { return getFieldBackend(field).LENGTH(name, field) diff --git a/vendor/yunion.io/x/sqlchemy/querydefs.go b/vendor/yunion.io/x/sqlchemy/querydefs.go index b82a15a88..2251ad7af 100644 --- a/vendor/yunion.io/x/sqlchemy/querydefs.go +++ b/vendor/yunion.io/x/sqlchemy/querydefs.go @@ -169,6 +169,9 @@ func queryString(tq *SQuery, tmpFields ...IQueryField) string { // normal buf.WriteString(f.Expression()) } + if f.Name() != "" { + buf.WriteString(fmt.Sprintf(" AS %s%s%s", qChar, f.Name(), qChar)) + } } buf.WriteString(" FROM ") buf.WriteString(fmt.Sprintf("%s AS %s%s%s", tq.from.Expression(), qChar, tq.from.Alias(), qChar)) diff --git a/vendor/yunion.io/x/sqlchemy/queryfield.go b/vendor/yunion.io/x/sqlchemy/queryfield.go index 95a575c27..ea72a132b 100644 --- a/vendor/yunion.io/x/sqlchemy/queryfield.go +++ b/vendor/yunion.io/x/sqlchemy/queryfield.go @@ -25,12 +25,7 @@ type sQueryField struct { // the string after select func (sqf *sQueryField) Expression() string { qChar := sqf.from.database().backend.QuoteChar() - - alias := sqf.name - if len(sqf.alias) > 0 { - alias = sqf.alias - } - return fmt.Sprintf("%s%s%s.%s%s%s AS %s%s%s", qChar, sqf.from.Alias(), qChar, qChar, sqf.name, qChar, qChar, alias, qChar) + return fmt.Sprintf("%s%s%s.%s%s%s", qChar, sqf.from.Alias(), qChar, qChar, sqf.name, qChar) } // the name of thie field diff --git a/vendor/yunion.io/x/sqlchemy/subquery.go b/vendor/yunion.io/x/sqlchemy/subquery.go index d1da45aba..ee60682b2 100644 --- a/vendor/yunion.io/x/sqlchemy/subquery.go +++ b/vendor/yunion.io/x/sqlchemy/subquery.go @@ -31,12 +31,7 @@ type SSubQueryField struct { // Expression implementation of SSubQueryField for IQueryField func (sqf *SSubQueryField) Expression() string { qChar := sqf.query.database().backend.QuoteChar() - - alias := sqf.field.Name() - if len(sqf.alias) > 0 { - alias = sqf.alias - } - return fmt.Sprintf("%s%s%s.%s%s%s AS %s%s%s", qChar, sqf.query.alias, qChar, qChar, sqf.field.Name(), qChar, qChar, alias, qChar) + return fmt.Sprintf("%s%s%s.%s%s%s", qChar, sqf.query.alias, qChar, qChar, sqf.field.Name(), qChar) } // Name implementation of SSubQueryField for IQueryField @@ -125,7 +120,7 @@ func (sq *SSubQuery) findField(id string) IQueryField { func (sq *SSubQuery) Field(id string, alias ...string) IQueryField { f := sq.field(id, alias...) if f == nil { - log.Errorf("subquery %s AS %s cannot find field %s", sq.query.String(), sq.alias, id) + log.Errorf("subquery %s as %s cannot find field %s", sq.query.String(), sq.alias, id) } return f } diff --git a/vendor/yunion.io/x/sqlchemy/table.go b/vendor/yunion.io/x/sqlchemy/table.go index 3fa121870..574f86ae5 100644 --- a/vendor/yunion.io/x/sqlchemy/table.go +++ b/vendor/yunion.io/x/sqlchemy/table.go @@ -355,12 +355,8 @@ func (tbl *STable) Variables() []interface{} { // Expression implementation of STableField for IQueryField func (c *STableField) Expression() string { - alias := c.spec.Name() - if len(c.alias) > 0 { - alias = c.alias - } qChar := c.database().backend.QuoteChar() - return fmt.Sprintf("%s%s%s.%s%s%s AS %s%s%s", qChar, c.table.Alias(), qChar, qChar, c.spec.Name(), qChar, qChar, alias, qChar) + return fmt.Sprintf("%s%s%s.%s%s%s", qChar, c.table.Alias(), qChar, qChar, c.spec.Name(), qChar) } // Name implementation of STableField for IQueryField diff --git a/vendor/yunion.io/x/sqlchemy/union.go b/vendor/yunion.io/x/sqlchemy/union.go index 171b3f6f0..a8a505331 100644 --- a/vendor/yunion.io/x/sqlchemy/union.go +++ b/vendor/yunion.io/x/sqlchemy/union.go @@ -33,12 +33,7 @@ type SUnionQueryField struct { // Expression implementation of SUnionQueryField for IQueryField func (sqf *SUnionQueryField) Expression() string { qChar := sqf.union.database().backend.QuoteChar() - - alias := sqf.name - if len(sqf.alias) > 0 { - alias = sqf.alias - } - return fmt.Sprintf("%s%s%s.%s%s%s AS %s%s%s", qChar, sqf.union.Alias(), qChar, qChar, sqf.name, qChar, qChar, alias, qChar) + return fmt.Sprintf("%s%s%s.%s%s%s", qChar, sqf.union.Alias(), qChar, qChar, sqf.name, qChar) } // Name implementation of SUnionQueryField for IQueryField From cad1398a5252c9b4b2216f4507b66b5951ec9434 Mon Sep 17 00:00:00 2001 From: Zexi Li Date: Wed, 7 Feb 2024 18:20:15 +0800 Subject: [PATCH 3/3] first cni version --- cmd/ocnet-cni/main.go | 1 - go.mod | 4 +- go.sum | 4 + pkg/cni-plugin/plugin/plugin.go | 124 +++- pkg/cni-plugin/plugin/utils.go | 80 ++- .../cni/pkg/types/020/types.go | 85 +-- .../cni/pkg/types/040/types.go | 306 -------- .../cni/pkg/types/100/types.go | 307 -------- .../containernetworking/cni/pkg/types/args.go | 18 +- .../cni/pkg/types/create/create.go | 56 -- .../cni/pkg/types/current/types.go | 276 +++++++ .../cni/pkg/types/internal/convert.go | 92 --- .../cni/pkg/types/internal/create.go | 66 -- .../cni/pkg/types/types.go | 31 +- .../cni/pkg/version/conf.go | 15 +- .../cni/pkg/version/plugin.go | 8 +- .../cni/pkg/version/version.go | 40 +- .../plugins/pkg/ip/addr_linux.go | 68 ++ .../plugins/pkg/ip/cidr.go | 61 ++ .../plugins/pkg/ip/ipforward_linux.go | 61 ++ .../plugins/pkg/ip/ipmasq_linux.go | 126 ++++ .../plugins/pkg/ip/link_linux.go | 293 ++++++++ .../plugins/pkg/ip/route_linux.go | 47 ++ .../plugins/pkg/ip/utils_linux.go | 120 ++++ .../plugins/pkg/utils/hwaddr/hwaddr.go | 63 ++ .../plugins/pkg/utils/sysctl/sysctl_linux.go | 80 +++ vendor/github.com/coreos/go-iptables/LICENSE | 191 +++++ vendor/github.com/coreos/go-iptables/NOTICE | 5 + .../coreos/go-iptables/iptables/iptables.go | 680 ++++++++++++++++++ .../coreos/go-iptables/iptables/lock.go | 84 +++ vendor/github.com/safchain/ethtool/.gitignore | 27 + .../github.com/safchain/ethtool/.travis.yml | 1 + vendor/github.com/safchain/ethtool/LICENSE | 202 ++++++ vendor/github.com/safchain/ethtool/Makefile | 4 + vendor/github.com/safchain/ethtool/README.md | 60 ++ vendor/github.com/safchain/ethtool/ethtool.go | 541 ++++++++++++++ .../safchain/ethtool/ethtool_cmd.go | 207 ++++++ .../safchain/ethtool/ethtool_msglvl.go | 113 +++ vendor/modules.txt | 18 +- 39 files changed, 3574 insertions(+), 991 deletions(-) delete mode 100644 vendor/github.com/containernetworking/cni/pkg/types/040/types.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/types/100/types.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/types/create/create.go create mode 100644 vendor/github.com/containernetworking/cni/pkg/types/current/types.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/types/internal/convert.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/types/internal/create.go create mode 100644 vendor/github.com/containernetworking/plugins/pkg/ip/addr_linux.go create mode 100644 vendor/github.com/containernetworking/plugins/pkg/ip/cidr.go create mode 100644 vendor/github.com/containernetworking/plugins/pkg/ip/ipforward_linux.go create mode 100644 vendor/github.com/containernetworking/plugins/pkg/ip/ipmasq_linux.go create mode 100644 vendor/github.com/containernetworking/plugins/pkg/ip/link_linux.go create mode 100644 vendor/github.com/containernetworking/plugins/pkg/ip/route_linux.go create mode 100644 vendor/github.com/containernetworking/plugins/pkg/ip/utils_linux.go create mode 100644 vendor/github.com/containernetworking/plugins/pkg/utils/hwaddr/hwaddr.go create mode 100644 vendor/github.com/containernetworking/plugins/pkg/utils/sysctl/sysctl_linux.go create mode 100644 vendor/github.com/coreos/go-iptables/LICENSE create mode 100644 vendor/github.com/coreos/go-iptables/NOTICE create mode 100644 vendor/github.com/coreos/go-iptables/iptables/iptables.go create mode 100644 vendor/github.com/coreos/go-iptables/iptables/lock.go create mode 100644 vendor/github.com/safchain/ethtool/.gitignore create mode 100644 vendor/github.com/safchain/ethtool/.travis.yml create mode 100644 vendor/github.com/safchain/ethtool/LICENSE create mode 100644 vendor/github.com/safchain/ethtool/Makefile create mode 100644 vendor/github.com/safchain/ethtool/README.md create mode 100644 vendor/github.com/safchain/ethtool/ethtool.go create mode 100644 vendor/github.com/safchain/ethtool/ethtool_cmd.go create mode 100644 vendor/github.com/safchain/ethtool/ethtool_msglvl.go diff --git a/cmd/ocnet-cni/main.go b/cmd/ocnet-cni/main.go index 054531b2f..43cdce7f3 100644 --- a/cmd/ocnet-cni/main.go +++ b/cmd/ocnet-cni/main.go @@ -10,7 +10,6 @@ import ( ) func main() { - log.Infof("-------start") // Use the name of the binary to determine which routine to run. _, filename := filepath.Split(os.Args[0]) switch filename { diff --git a/go.mod b/go.mod index 294a5c0d3..619294b30 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Masterminds/semver/v3 v3.1.0 github.com/Masterminds/sprig v2.22.0+incompatible github.com/ceph/go-ceph v0.0.0-20181217221554-e32f9f0f2e94 - github.com/containernetworking/cni v1.0.0 + github.com/containernetworking/cni v0.8.0 github.com/containernetworking/plugins v0.8.7 github.com/fsnotify/fsnotify v1.5.4 github.com/ghodss/yaml v1.0.0 @@ -78,6 +78,7 @@ require ( github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59 // indirect github.com/containerd/containerd v1.3.4 // indirect github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41 // indirect + github.com/coreos/go-iptables v0.6.0 // indirect github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect github.com/coreos/go-systemd/v22 v22.3.2 // indirect @@ -188,6 +189,7 @@ require ( github.com/rs/xid v1.2.1 // indirect github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351 // indirect github.com/russross/blackfriday v1.5.2 // indirect + github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 // indirect github.com/satori/go.uuid v1.2.0 // indirect github.com/shirou/gopsutil/v3 v3.22.10 // indirect github.com/sirupsen/logrus v1.9.0 // indirect diff --git a/go.sum b/go.sum index 03c21769d..f8013dda7 100644 --- a/go.sum +++ b/go.sum @@ -235,6 +235,7 @@ github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDG github.com/containerd/ttrpc v1.0.0/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/containerd/typeurl v1.0.0/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/containernetworking/cni v0.8.0 h1:BT9lpgGoH4jw3lFC7Odz2prU5ruiYKcgAjMCbgybcKI= github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/cni v1.0.0 h1:9VJe1a5uKtdeJIHC/UvbTweracOh6GafT0nfbEGVcQ0= github.com/containernetworking/cni v1.0.0/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtrBI6QcRV0NiNt15Y= @@ -244,6 +245,8 @@ github.com/coredns/corefile-migration v1.0.10/go.mod h1:RMy/mXdeDlYwzt0vdMEJvT2h github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk= +github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= @@ -1047,6 +1050,7 @@ github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNue github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 h1:2c1EFnZHIPCW8qKWgHMH/fX2PkSabFc5mrVzfUNdg5U= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= diff --git a/pkg/cni-plugin/plugin/plugin.go b/pkg/cni-plugin/plugin/plugin.go index 0016f0fe6..e91c65ed6 100644 --- a/pkg/cni-plugin/plugin/plugin.go +++ b/pkg/cni-plugin/plugin/plugin.go @@ -7,10 +7,13 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - current "github.com/containernetworking/cni/pkg/types/040" + "github.com/containernetworking/cni/pkg/types/current" cniversion "github.com/containernetworking/cni/pkg/version" + "github.com/containernetworking/plugins/pkg/ip" "github.com/containernetworking/plugins/pkg/ns" + "github.com/vishvananda/netlink" + "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/log/hooks" "yunion.io/x/pkg/errors" @@ -63,6 +66,7 @@ func cmdAdd(args *skel.CmdArgs) error { if err != nil { return errors.Wrap(err, "NewCloudPodFromCNIArgs") } + log.Infof("=====pod desc: %s", jsonutils.Marshal(pod.GetDesc()).PrettyString()) netns, err := ns.GetNS(args.Netns) if err != nil { @@ -81,15 +85,131 @@ func cmdAdd(args *skel.CmdArgs) error { } for idx, nic := range nics { - if err := setupNic(idx, nic, netns); err != nil { + defaultGw := false + if idx == 0 { + defaultGw = true + } + nicResult, err := GenerateNetworkResultByNic(idx, nic, defaultGw) + if err != nil { + return errors.Wrapf(err, "GenerateNetworkResultByNic: %#v", nic) + } + if err := setupNic(idx, nic, netns, nicResult); err != nil { return errors.Wrap(err, "setupNic") } } + log.Infof("====result: %s", jsonutils.Marshal(result).PrettyString()) return types.PrintResult(result, cniVersion) } +type linkNameToInterfaceReq struct { + linkName string + nsPath string +} + +func linkNamesToInterfaces(reqs []*linkNameToInterfaceReq) ([]*current.Interface, error) { + resps := []*current.Interface{} + for _, req := range reqs { + resp, err := linkNameToInterface(req.linkName, req.nsPath) + if err != nil { + return nil, errors.Wrapf(err, "linkNameToInterface(%s, %s)", req.linkName, req.nsPath) + } + resps = append(resps, resp) + } + return resps, nil +} + +func linkNameToInterface(ifName, nsPath string) (*current.Interface, error) { + var err error + var namespace ns.NetNS + if nsPath == "" { + if namespace, err = ns.GetCurrentNS(); err != nil { + return nil, errors.Wrap(err, "GetCurrentNS") + } + } else { + if namespace, err = ns.GetNS(nsPath); err != nil { + return nil, errors.Wrap(err, "GetNS") + } + } + defer namespace.Close() + + var if_ *current.Interface + if err = namespace.Do(func(_ ns.NetNS) error { + link, err := netlink.LinkByName(ifName) + if err != nil { + return errors.Wrapf(err, "netlink.LinkByName %s", ifName) + } + if_ = ¤t.Interface{ + Name: ifName, + Mac: link.Attrs().HardwareAddr.String(), + Sandbox: nsPath, + } + return nil + }); err != nil { + return nil, errors.Wrap(err, "namespace.Do") + } + return if_, nil +} + func cmdDel(args *skel.CmdArgs) error { + _, _, err := loadNetConf(args.StdinData, args.Args) + if err != nil { + return errors.Wrap(err, "loadNetConf") + } + pod, err := NewCloudPodFromCNIArgs(args.Args) + if err != nil { + return errors.Wrap(err, "NewCloudPodFromCNIArgs") + } + + ovsCli, err := NewOVSClient() + if err != nil { + return errors.Wrap(err, "NewOVSClient") + } + + nics := pod.GetDesc().Nics + for idx, nic := range nics { + ovsPort := nic.Ifname + reqs := []*linkNameToInterfaceReq{ + { + linkName: nic.Bridge, + }, + { + linkName: ovsPort, + }, + } + if args.Netns != "" { + reqs = append(reqs, &linkNameToInterfaceReq{ + linkName: nic.GetInterface(idx), + nsPath: args.Netns, + }) + } + if _, err := linkNamesToInterfaces(reqs); err != nil { + return errors.Wrap(err, "failed to convert link name to ifname") + } + log.Infof("deleting port %s of Bridge %s", ovsPort, nic.Bridge) + if err := ovsCli.DeletePort(nic.Bridge, ovsPort); err != nil { + return errors.Wrapf(err, "delete port %s of %s", ovsPort, nic.Bridge) + } + } + + if args.Netns == "" { + return nil + } + + // There is a netns so try to clean up. Delete can be called multiple times + // so don't return an error if the device is already removed. + if err := ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error { + for idx, nic := range nics { + _, err := ip.DelLinkByNameAddr(nic.GetInterface(idx)) + if err != nil && err == ip.ErrLinkNotFound { + continue + } + return errors.Wrapf(err, "ip.DelLinkByNameAddr(%s, %v)", nic.GetInterface(idx), netlink.FAMILY_ALL) + } + return nil + }); err != nil { + return errors.Wrapf(err, "ns.WithNetNSPath %s", args.Netns) + } return nil } diff --git a/pkg/cni-plugin/plugin/utils.go b/pkg/cni-plugin/plugin/utils.go index 84a47215a..d1521bb33 100644 --- a/pkg/cni-plugin/plugin/utils.go +++ b/pkg/cni-plugin/plugin/utils.go @@ -5,15 +5,16 @@ import ( "fmt" "io/ioutil" "net" + "os" "path/filepath" "strings" "github.com/containernetworking/cni/pkg/types" - current "github.com/containernetworking/cni/pkg/types/040" + "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/plugins/pkg/ip" "github.com/containernetworking/plugins/pkg/ns" "github.com/vishvananda/netlink" - "yunion.io/x/log" "yunion.io/x/pkg/errors" ) @@ -97,6 +98,18 @@ func (p CloudPod) GetDesc() *PodDesc { return p.desc } +func GenerateNetworkResultByNic(index int, nic PodNic, defaultGw bool) (*current.Result, error) { + result := ¤t.Result{} + ctrIf, ipConfigs, routes, err := getNetworkConfig(index, nic, defaultGw) + if err != nil { + return nil, errors.Wrap(err, "getNetworkConfig") + } + result.Interfaces = []*current.Interface{ctrIf} + result.IPs = ipConfigs + result.Routes = routes + return result, nil +} + func GenerateNetworkResultByNics(nics []PodNic) (*current.Result, error) { result := ¤t.Result{} ipConfs := make([]*current.IPConfig, 0) @@ -115,6 +128,9 @@ func GenerateNetworkResultByNics(nics []PodNic) (*current.Result, error) { ifs = append(ifs, ctrIf) ifRoutes = append(ifRoutes, routes...) } + result.Interfaces = ifs + result.IPs = ipConfs + result.Routes = ifRoutes return result, nil } @@ -161,30 +177,29 @@ func getNetworkConfig(idx int, nic PodNic, defaultGw bool) (*current.Interface, return ctrIf, ipConfigs, routes, nil } -func setupNic(index int, nic PodNic, netns ns.NetNS) error { +func setupNic(index int, nic PodNic, netns ns.NetNS, result *current.Result) error { // Create OVS client ovsCli, err := NewOVSClient() if err != nil { return errors.Wrap(err, "NewOVSClient") } - hostIfname := nic.Ifname ctrIfname := nic.GetInterface(index) - ctrMac := nic.Mac - hostInterface, ctrInterface, err := setupVeth(ovsCli, index, netns, nic.Bridge) + //hostInterface, ctrInterface, err := setupVeth(ovsCli, index, netns, nic.Bridge) + _, _, err = setupVeth(ovsCli, index, nic, netns) if err != nil { return errors.Wrap(err, "setupVeth") } // Configure the container hardware address and IP address(es) if err := netns.Do(func(_ ns.NetNS) error { - ctrVeth, err := net.InterfaceByName(ctrIfname) + _, err := net.InterfaceByName(ctrIfname) if err != nil { return errors.Wrapf(err, "net.InterfaceByName %q", ctrIfname) } // Add the IP to the interface - if err := ConfigureIface(ctrIfname, nic); err != nil { + if err := ConfigureIface(ctrIfname, result.IPs, result.Routes); err != nil { return errors.Wrap(err, "ConfigureIface") } return nil @@ -212,7 +227,7 @@ func setupVeth( if err != nil { return errors.Wrap(err, "setupYunionVeth") } - log.Infof("makeVethPair hostVeth: %#v, containerVeth: %#v", hostVeth, ctrVeth) + //log.Infof("makeVethPair hostVeth: %#v, containerVeth: %#v", hostVeth, ctrVeth) ctrIf.Name = ctrVeth.Name ctrIf.Mac = ctrVeth.HardwareAddr.String() @@ -220,7 +235,7 @@ func setupVeth( hostIf.Name = hostVeth.Name // ip link set lo up - if err := setLinkup("lo"); err != nil { + if err := setLinkUp("lo"); err != nil { return errors.Wrap(err, "set loopback nic up") } return nil @@ -238,7 +253,7 @@ func setupVeth( if err := cli.AddPort(nic.Bridge, hostIf.Name); err != nil { return nil, nil, errors.Wrapf(err, "Add port to OVS: %s -> %s", hostIf.Name, nic.Bridge) } - log.Infof("Port %q added to %q", hostIf.Name, nic.Bridge) + //log.Infof("Port %q added to %q", hostIf.Name, nic.Bridge) return hostIf, ctrIf, nil } @@ -251,7 +266,7 @@ func ensureVethDeleted(name string) error { } } if err != nil { - log.Warningf("delete %q peer veth err: %v", name, err) + //log.Warningf("delete %q peer veth err: %v", name, err) } return nil } @@ -282,7 +297,7 @@ func setupYunionVeth(index int, hostVethName string, ctrIfName string, ctrMac st return net.Interface{}, net.Interface{}, errors.Wrapf(err, "failed to move veth %q to host netns %#v", hostVeth, hostNS) } - if err := hostNS.Do(func(_, ns.NetNS) error { + if err := hostNS.Do(func(_ ns.NetNS) error { hostVeth, err := netlink.LinkByName(hostVethName) if err != nil { return errors.Wrapf(err, "failed to lookup host veth after moved: %q", hostVethName) @@ -332,7 +347,7 @@ func setLinkUp(name string) error { return netlink.LinkSetUp(iface) } -func ConfigureIface(ifName string, nic PodNic) error { +func ConfigureIface(ifName string, ipConfigs []*current.IPConfig, routes []*types.Route) error { link, err := netlink.LinkByName(ifName) if err != nil { return errors.Wrap(err, "LinkByName") @@ -343,7 +358,40 @@ func ConfigureIface(ifName string, nic PodNic) error { } var v4gw, v6gw net.IP - addr := &netlink.Addr{ - IPNet: nic.Ip + for _, ipc := range ipConfigs { + addr := &netlink.Addr{IPNet: &ipc.Address, Label: ""} + if err = netlink.AddrAdd(link, addr); err != nil { + return fmt.Errorf("failed to add IP addr %v to %q: %v", ipc, ifName, err) + } + + gwIsV4 := ipc.Gateway.To4() != nil + if gwIsV4 && v4gw == nil { + v4gw = ipc.Gateway + } else if !gwIsV4 && v6gw == nil { + v6gw = ipc.Gateway + } } + + ip.SettleAddresses(ifName, 10) + + for _, r := range routes { + routeIsV4 := r.Dst.IP.To4() != nil + gw := r.GW + if gw == nil { + if routeIsV4 && v4gw != nil { + gw = v4gw + } else if !routeIsV4 && v6gw != nil { + gw = v6gw + } + } + if err = ip.AddRoute(&r.Dst, gw, link); err != nil { + // we skip over duplicate routes as we assume the first one wins + if !os.IsExist(err) { + return fmt.Errorf("failed to add route '%v via %v dev %v': %v", r.Dst, gw, ifName, err) + } + } + } + + return nil + } diff --git a/vendor/github.com/containernetworking/cni/pkg/types/020/types.go b/vendor/github.com/containernetworking/cni/pkg/types/020/types.go index 99b151ff2..36f31678a 100644 --- a/vendor/github.com/containernetworking/cni/pkg/types/020/types.go +++ b/vendor/github.com/containernetworking/cni/pkg/types/020/types.go @@ -22,47 +22,25 @@ import ( "os" "github.com/containernetworking/cni/pkg/types" - convert "github.com/containernetworking/cni/pkg/types/internal" ) const ImplementedSpecVersion string = "0.2.0" -var supportedVersions = []string{"", "0.1.0", ImplementedSpecVersion} - -// Register converters for all versions less than the implemented spec version -func init() { - convert.RegisterConverter("0.1.0", []string{ImplementedSpecVersion}, convertFrom010) - convert.RegisterConverter(ImplementedSpecVersion, []string{"0.1.0"}, convertTo010) - - // Creator - convert.RegisterCreator(supportedVersions, NewResult) -} +var SupportedVersions = []string{"", "0.1.0", ImplementedSpecVersion} // Compatibility types for CNI version 0.1.0 and 0.2.0 -// NewResult creates a new Result object from JSON data. The JSON data -// must be compatible with the CNI versions implemented by this type. func NewResult(data []byte) (types.Result, error) { result := &Result{} if err := json.Unmarshal(data, result); err != nil { return nil, err } - for _, v := range supportedVersions { - if result.CNIVersion == v { - if result.CNIVersion == "" { - result.CNIVersion = "0.1.0" - } - return result, nil - } - } - return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q", - supportedVersions, result.CNIVersion) + return result, nil } -// GetResult converts the given Result object to the ImplementedSpecVersion -// and returns the concrete type or an error func GetResult(r types.Result) (*Result, error) { - result020, err := convert.Convert(r, ImplementedSpecVersion) + // We expect version 0.1.0/0.2.0 results + result020, err := r.GetAsVersion(ImplementedSpecVersion) if err != nil { return nil, err } @@ -73,32 +51,6 @@ func GetResult(r types.Result) (*Result, error) { return result, nil } -func convertFrom010(from types.Result, toVersion string) (types.Result, error) { - if toVersion != "0.2.0" { - panic("only converts to version 0.2.0") - } - fromResult := from.(*Result) - return &Result{ - CNIVersion: ImplementedSpecVersion, - IP4: fromResult.IP4.Copy(), - IP6: fromResult.IP6.Copy(), - DNS: *fromResult.DNS.Copy(), - }, nil -} - -func convertTo010(from types.Result, toVersion string) (types.Result, error) { - if toVersion != "0.1.0" { - panic("only converts to version 0.1.0") - } - fromResult := from.(*Result) - return &Result{ - CNIVersion: "0.1.0", - IP4: fromResult.IP4.Copy(), - IP6: fromResult.IP6.Copy(), - DNS: *fromResult.DNS.Copy(), - }, nil -} - // Result is what gets returned from the plugin (via stdout) to the caller type Result struct { CNIVersion string `json:"cniVersion,omitempty"` @@ -108,16 +60,17 @@ type Result struct { } func (r *Result) Version() string { - return r.CNIVersion + return ImplementedSpecVersion } func (r *Result) GetAsVersion(version string) (types.Result, error) { - // If the creator of the result did not set the CNIVersion, assume it - // should be the highest spec version implemented by this Result - if r.CNIVersion == "" { - r.CNIVersion = ImplementedSpecVersion + for _, supportedVersion := range SupportedVersions { + if version == supportedVersion { + r.CNIVersion = version + return r, nil + } } - return convert.Convert(r, version) + return nil, fmt.Errorf("cannot convert version %q to %s", SupportedVersions, version) } func (r *Result) Print() error { @@ -140,22 +93,6 @@ type IPConfig struct { Routes []types.Route } -func (i *IPConfig) Copy() *IPConfig { - if i == nil { - return nil - } - - var routes []types.Route - for _, fromRoute := range i.Routes { - routes = append(routes, *fromRoute.Copy()) - } - return &IPConfig{ - IP: i.IP, - Gateway: i.Gateway, - Routes: routes, - } -} - // net.IPNet is not JSON (un)marshallable so this duality is needed // for our custom IPNet type diff --git a/vendor/github.com/containernetworking/cni/pkg/types/040/types.go b/vendor/github.com/containernetworking/cni/pkg/types/040/types.go deleted file mode 100644 index 3633b0eaa..000000000 --- a/vendor/github.com/containernetworking/cni/pkg/types/040/types.go +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2016 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package types040 - -import ( - "encoding/json" - "fmt" - "io" - "net" - "os" - - "github.com/containernetworking/cni/pkg/types" - types020 "github.com/containernetworking/cni/pkg/types/020" - convert "github.com/containernetworking/cni/pkg/types/internal" -) - -const ImplementedSpecVersion string = "0.4.0" - -var supportedVersions = []string{"0.3.0", "0.3.1", ImplementedSpecVersion} - -// Register converters for all versions less than the implemented spec version -func init() { - // Up-converters - convert.RegisterConverter("0.1.0", supportedVersions, convertFrom02x) - convert.RegisterConverter("0.2.0", supportedVersions, convertFrom02x) - convert.RegisterConverter("0.3.0", supportedVersions, convertInternal) - convert.RegisterConverter("0.3.1", supportedVersions, convertInternal) - - // Down-converters - convert.RegisterConverter("0.4.0", []string{"0.3.0", "0.3.1"}, convertInternal) - convert.RegisterConverter("0.4.0", []string{"0.1.0", "0.2.0"}, convertTo02x) - convert.RegisterConverter("0.3.1", []string{"0.1.0", "0.2.0"}, convertTo02x) - convert.RegisterConverter("0.3.0", []string{"0.1.0", "0.2.0"}, convertTo02x) - - // Creator - convert.RegisterCreator(supportedVersions, NewResult) -} - -func NewResult(data []byte) (types.Result, error) { - result := &Result{} - if err := json.Unmarshal(data, result); err != nil { - return nil, err - } - for _, v := range supportedVersions { - if result.CNIVersion == v { - return result, nil - } - } - return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q", - supportedVersions, result.CNIVersion) -} - -func GetResult(r types.Result) (*Result, error) { - resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion) - if err != nil { - return nil, err - } - result, ok := resultCurrent.(*Result) - if !ok { - return nil, fmt.Errorf("failed to convert result") - } - return result, nil -} - -func NewResultFromResult(result types.Result) (*Result, error) { - newResult, err := convert.Convert(result, ImplementedSpecVersion) - if err != nil { - return nil, err - } - return newResult.(*Result), nil -} - -// Result is what gets returned from the plugin (via stdout) to the caller -type Result struct { - CNIVersion string `json:"cniVersion,omitempty"` - Interfaces []*Interface `json:"interfaces,omitempty"` - IPs []*IPConfig `json:"ips,omitempty"` - Routes []*types.Route `json:"routes,omitempty"` - DNS types.DNS `json:"dns,omitempty"` -} - -func convert020IPConfig(from *types020.IPConfig, ipVersion string) *IPConfig { - return &IPConfig{ - Version: ipVersion, - Address: from.IP, - Gateway: from.Gateway, - } -} - -func convertFrom02x(from types.Result, toVersion string) (types.Result, error) { - fromResult := from.(*types020.Result) - toResult := &Result{ - CNIVersion: toVersion, - DNS: *fromResult.DNS.Copy(), - Routes: []*types.Route{}, - } - if fromResult.IP4 != nil { - toResult.IPs = append(toResult.IPs, convert020IPConfig(fromResult.IP4, "4")) - for _, fromRoute := range fromResult.IP4.Routes { - toResult.Routes = append(toResult.Routes, fromRoute.Copy()) - } - } - - if fromResult.IP6 != nil { - toResult.IPs = append(toResult.IPs, convert020IPConfig(fromResult.IP6, "6")) - for _, fromRoute := range fromResult.IP6.Routes { - toResult.Routes = append(toResult.Routes, fromRoute.Copy()) - } - } - - return toResult, nil -} - -func convertInternal(from types.Result, toVersion string) (types.Result, error) { - fromResult := from.(*Result) - toResult := &Result{ - CNIVersion: toVersion, - DNS: *fromResult.DNS.Copy(), - Routes: []*types.Route{}, - } - for _, fromIntf := range fromResult.Interfaces { - toResult.Interfaces = append(toResult.Interfaces, fromIntf.Copy()) - } - for _, fromIPC := range fromResult.IPs { - toResult.IPs = append(toResult.IPs, fromIPC.Copy()) - } - for _, fromRoute := range fromResult.Routes { - toResult.Routes = append(toResult.Routes, fromRoute.Copy()) - } - return toResult, nil -} - -func convertTo02x(from types.Result, toVersion string) (types.Result, error) { - fromResult := from.(*Result) - toResult := &types020.Result{ - CNIVersion: toVersion, - DNS: *fromResult.DNS.Copy(), - } - - for _, fromIP := range fromResult.IPs { - // Only convert the first IP address of each version as 0.2.0 - // and earlier cannot handle multiple IP addresses - if fromIP.Version == "4" && toResult.IP4 == nil { - toResult.IP4 = &types020.IPConfig{ - IP: fromIP.Address, - Gateway: fromIP.Gateway, - } - } else if fromIP.Version == "6" && toResult.IP6 == nil { - toResult.IP6 = &types020.IPConfig{ - IP: fromIP.Address, - Gateway: fromIP.Gateway, - } - } - if toResult.IP4 != nil && toResult.IP6 != nil { - break - } - } - - for _, fromRoute := range fromResult.Routes { - is4 := fromRoute.Dst.IP.To4() != nil - if is4 && toResult.IP4 != nil { - toResult.IP4.Routes = append(toResult.IP4.Routes, types.Route{ - Dst: fromRoute.Dst, - GW: fromRoute.GW, - }) - } else if !is4 && toResult.IP6 != nil { - toResult.IP6.Routes = append(toResult.IP6.Routes, types.Route{ - Dst: fromRoute.Dst, - GW: fromRoute.GW, - }) - } - } - - // 0.2.0 and earlier require at least one IP address in the Result - if toResult.IP4 == nil && toResult.IP6 == nil { - return nil, fmt.Errorf("cannot convert: no valid IP addresses") - } - - return toResult, nil -} - -func (r *Result) Version() string { - return r.CNIVersion -} - -func (r *Result) GetAsVersion(version string) (types.Result, error) { - // If the creator of the result did not set the CNIVersion, assume it - // should be the highest spec version implemented by this Result - if r.CNIVersion == "" { - r.CNIVersion = ImplementedSpecVersion - } - return convert.Convert(r, version) -} - -func (r *Result) Print() error { - return r.PrintTo(os.Stdout) -} - -func (r *Result) PrintTo(writer io.Writer) error { - data, err := json.MarshalIndent(r, "", " ") - if err != nil { - return err - } - _, err = writer.Write(data) - return err -} - -// Interface contains values about the created interfaces -type Interface struct { - Name string `json:"name"` - Mac string `json:"mac,omitempty"` - Sandbox string `json:"sandbox,omitempty"` -} - -func (i *Interface) String() string { - return fmt.Sprintf("%+v", *i) -} - -func (i *Interface) Copy() *Interface { - if i == nil { - return nil - } - newIntf := *i - return &newIntf -} - -// Int returns a pointer to the int value passed in. Used to -// set the IPConfig.Interface field. -func Int(v int) *int { - return &v -} - -// IPConfig contains values necessary to configure an IP address on an interface -type IPConfig struct { - // IP version, either "4" or "6" - Version string - // Index into Result structs Interfaces list - Interface *int - Address net.IPNet - Gateway net.IP -} - -func (i *IPConfig) String() string { - return fmt.Sprintf("%+v", *i) -} - -func (i *IPConfig) Copy() *IPConfig { - if i == nil { - return nil - } - - ipc := &IPConfig{ - Version: i.Version, - Address: i.Address, - Gateway: i.Gateway, - } - if i.Interface != nil { - intf := *i.Interface - ipc.Interface = &intf - } - return ipc -} - -// JSON (un)marshallable types -type ipConfig struct { - Version string `json:"version"` - Interface *int `json:"interface,omitempty"` - Address types.IPNet `json:"address"` - Gateway net.IP `json:"gateway,omitempty"` -} - -func (c *IPConfig) MarshalJSON() ([]byte, error) { - ipc := ipConfig{ - Version: c.Version, - Interface: c.Interface, - Address: types.IPNet(c.Address), - Gateway: c.Gateway, - } - - return json.Marshal(ipc) -} - -func (c *IPConfig) UnmarshalJSON(data []byte) error { - ipc := ipConfig{} - if err := json.Unmarshal(data, &ipc); err != nil { - return err - } - - c.Version = ipc.Version - c.Interface = ipc.Interface - c.Address = net.IPNet(ipc.Address) - c.Gateway = ipc.Gateway - return nil -} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/100/types.go b/vendor/github.com/containernetworking/cni/pkg/types/100/types.go deleted file mode 100644 index 0e1e8b857..000000000 --- a/vendor/github.com/containernetworking/cni/pkg/types/100/types.go +++ /dev/null @@ -1,307 +0,0 @@ -// Copyright 2016 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package types100 - -import ( - "encoding/json" - "fmt" - "io" - "net" - "os" - - "github.com/containernetworking/cni/pkg/types" - types040 "github.com/containernetworking/cni/pkg/types/040" - convert "github.com/containernetworking/cni/pkg/types/internal" -) - -const ImplementedSpecVersion string = "1.0.0" - -var supportedVersions = []string{ImplementedSpecVersion} - -// Register converters for all versions less than the implemented spec version -func init() { - // Up-converters - convert.RegisterConverter("0.1.0", supportedVersions, convertFrom02x) - convert.RegisterConverter("0.2.0", supportedVersions, convertFrom02x) - convert.RegisterConverter("0.3.0", supportedVersions, convertFrom04x) - convert.RegisterConverter("0.3.1", supportedVersions, convertFrom04x) - convert.RegisterConverter("0.4.0", supportedVersions, convertFrom04x) - - // Down-converters - convert.RegisterConverter("1.0.0", []string{"0.3.0", "0.3.1", "0.4.0"}, convertTo04x) - convert.RegisterConverter("1.0.0", []string{"0.1.0", "0.2.0"}, convertTo02x) - - // Creator - convert.RegisterCreator(supportedVersions, NewResult) -} - -func NewResult(data []byte) (types.Result, error) { - result := &Result{} - if err := json.Unmarshal(data, result); err != nil { - return nil, err - } - for _, v := range supportedVersions { - if result.CNIVersion == v { - return result, nil - } - } - return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q", - supportedVersions, result.CNIVersion) -} - -func GetResult(r types.Result) (*Result, error) { - resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion) - if err != nil { - return nil, err - } - result, ok := resultCurrent.(*Result) - if !ok { - return nil, fmt.Errorf("failed to convert result") - } - return result, nil -} - -func NewResultFromResult(result types.Result) (*Result, error) { - newResult, err := convert.Convert(result, ImplementedSpecVersion) - if err != nil { - return nil, err - } - return newResult.(*Result), nil -} - -// Result is what gets returned from the plugin (via stdout) to the caller -type Result struct { - CNIVersion string `json:"cniVersion,omitempty"` - Interfaces []*Interface `json:"interfaces,omitempty"` - IPs []*IPConfig `json:"ips,omitempty"` - Routes []*types.Route `json:"routes,omitempty"` - DNS types.DNS `json:"dns,omitempty"` -} - -func convertFrom02x(from types.Result, toVersion string) (types.Result, error) { - result040, err := convert.Convert(from, "0.4.0") - if err != nil { - return nil, err - } - result100, err := convertFrom04x(result040, ImplementedSpecVersion) - if err != nil { - return nil, err - } - return result100, nil -} - -func convertIPConfigFrom040(from *types040.IPConfig) *IPConfig { - to := &IPConfig{ - Address: from.Address, - Gateway: from.Gateway, - } - if from.Interface != nil { - intf := *from.Interface - to.Interface = &intf - } - return to -} - -func convertInterfaceFrom040(from *types040.Interface) *Interface { - return &Interface{ - Name: from.Name, - Mac: from.Mac, - Sandbox: from.Sandbox, - } -} - -func convertFrom04x(from types.Result, toVersion string) (types.Result, error) { - fromResult := from.(*types040.Result) - toResult := &Result{ - CNIVersion: toVersion, - DNS: *fromResult.DNS.Copy(), - Routes: []*types.Route{}, - } - for _, fromIntf := range fromResult.Interfaces { - toResult.Interfaces = append(toResult.Interfaces, convertInterfaceFrom040(fromIntf)) - } - for _, fromIPC := range fromResult.IPs { - toResult.IPs = append(toResult.IPs, convertIPConfigFrom040(fromIPC)) - } - for _, fromRoute := range fromResult.Routes { - toResult.Routes = append(toResult.Routes, fromRoute.Copy()) - } - return toResult, nil -} - -func convertIPConfigTo040(from *IPConfig) *types040.IPConfig { - version := "6" - if from.Address.IP.To4() != nil { - version = "4" - } - to := &types040.IPConfig{ - Version: version, - Address: from.Address, - Gateway: from.Gateway, - } - if from.Interface != nil { - intf := *from.Interface - to.Interface = &intf - } - return to -} - -func convertInterfaceTo040(from *Interface) *types040.Interface { - return &types040.Interface{ - Name: from.Name, - Mac: from.Mac, - Sandbox: from.Sandbox, - } -} - -func convertTo04x(from types.Result, toVersion string) (types.Result, error) { - fromResult := from.(*Result) - toResult := &types040.Result{ - CNIVersion: toVersion, - DNS: *fromResult.DNS.Copy(), - Routes: []*types.Route{}, - } - for _, fromIntf := range fromResult.Interfaces { - toResult.Interfaces = append(toResult.Interfaces, convertInterfaceTo040(fromIntf)) - } - for _, fromIPC := range fromResult.IPs { - toResult.IPs = append(toResult.IPs, convertIPConfigTo040(fromIPC)) - } - for _, fromRoute := range fromResult.Routes { - toResult.Routes = append(toResult.Routes, fromRoute.Copy()) - } - return toResult, nil -} - -func convertTo02x(from types.Result, toVersion string) (types.Result, error) { - // First convert to 0.4.0 - result040, err := convertTo04x(from, "0.4.0") - if err != nil { - return nil, err - } - result02x, err := convert.Convert(result040, toVersion) - if err != nil { - return nil, err - } - return result02x, nil -} - -func (r *Result) Version() string { - return r.CNIVersion -} - -func (r *Result) GetAsVersion(version string) (types.Result, error) { - // If the creator of the result did not set the CNIVersion, assume it - // should be the highest spec version implemented by this Result - if r.CNIVersion == "" { - r.CNIVersion = ImplementedSpecVersion - } - return convert.Convert(r, version) -} - -func (r *Result) Print() error { - return r.PrintTo(os.Stdout) -} - -func (r *Result) PrintTo(writer io.Writer) error { - data, err := json.MarshalIndent(r, "", " ") - if err != nil { - return err - } - _, err = writer.Write(data) - return err -} - -// Interface contains values about the created interfaces -type Interface struct { - Name string `json:"name"` - Mac string `json:"mac,omitempty"` - Sandbox string `json:"sandbox,omitempty"` -} - -func (i *Interface) String() string { - return fmt.Sprintf("%+v", *i) -} - -func (i *Interface) Copy() *Interface { - if i == nil { - return nil - } - newIntf := *i - return &newIntf -} - -// Int returns a pointer to the int value passed in. Used to -// set the IPConfig.Interface field. -func Int(v int) *int { - return &v -} - -// IPConfig contains values necessary to configure an IP address on an interface -type IPConfig struct { - // Index into Result structs Interfaces list - Interface *int - Address net.IPNet - Gateway net.IP -} - -func (i *IPConfig) String() string { - return fmt.Sprintf("%+v", *i) -} - -func (i *IPConfig) Copy() *IPConfig { - if i == nil { - return nil - } - - ipc := &IPConfig{ - Address: i.Address, - Gateway: i.Gateway, - } - if i.Interface != nil { - intf := *i.Interface - ipc.Interface = &intf - } - return ipc -} - -// JSON (un)marshallable types -type ipConfig struct { - Interface *int `json:"interface,omitempty"` - Address types.IPNet `json:"address"` - Gateway net.IP `json:"gateway,omitempty"` -} - -func (c *IPConfig) MarshalJSON() ([]byte, error) { - ipc := ipConfig{ - Interface: c.Interface, - Address: types.IPNet(c.Address), - Gateway: c.Gateway, - } - - return json.Marshal(ipc) -} - -func (c *IPConfig) UnmarshalJSON(data []byte) error { - ipc := ipConfig{} - if err := json.Unmarshal(data, &ipc); err != nil { - return err - } - - c.Interface = ipc.Interface - c.Address = net.IPNet(ipc.Address) - c.Gateway = ipc.Gateway - return nil -} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/args.go b/vendor/github.com/containernetworking/cni/pkg/types/args.go index 7516f03ef..4eac64899 100644 --- a/vendor/github.com/containernetworking/cni/pkg/types/args.go +++ b/vendor/github.com/containernetworking/cni/pkg/types/args.go @@ -91,26 +91,16 @@ func LoadArgs(args string, container interface{}) error { unknownArgs = append(unknownArgs, pair) continue } - - var keyFieldInterface interface{} - switch { - case keyField.Kind() == reflect.Ptr: - keyField.Set(reflect.New(keyField.Type().Elem())) - keyFieldInterface = keyField.Interface() - case keyField.CanAddr() && keyField.Addr().CanInterface(): - keyFieldInterface = keyField.Addr().Interface() - default: - return UnmarshalableArgsError{fmt.Errorf("field '%s' has no valid interface", keyString)} - } - u, ok := keyFieldInterface.(encoding.TextUnmarshaler) + keyFieldIface := keyField.Addr().Interface() + u, ok := keyFieldIface.(encoding.TextUnmarshaler) if !ok { return UnmarshalableArgsError{fmt.Errorf( "ARGS: cannot unmarshal into field '%s' - type '%s' does not implement encoding.TextUnmarshaler", - keyString, reflect.TypeOf(keyFieldInterface))} + keyString, reflect.TypeOf(keyFieldIface))} } err := u.UnmarshalText([]byte(valueString)) if err != nil { - return fmt.Errorf("ARGS: error parsing value of pair %q: %w", pair, err) + return fmt.Errorf("ARGS: error parsing value of pair %q: %v)", pair, err) } } diff --git a/vendor/github.com/containernetworking/cni/pkg/types/create/create.go b/vendor/github.com/containernetworking/cni/pkg/types/create/create.go deleted file mode 100644 index ed28b33e8..000000000 --- a/vendor/github.com/containernetworking/cni/pkg/types/create/create.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2016 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package create - -import ( - "encoding/json" - "fmt" - - "github.com/containernetworking/cni/pkg/types" - convert "github.com/containernetworking/cni/pkg/types/internal" -) - -// DecodeVersion returns the CNI version from CNI configuration or result JSON, -// or an error if the operation could not be performed. -func DecodeVersion(jsonBytes []byte) (string, error) { - var conf struct { - CNIVersion string `json:"cniVersion"` - } - err := json.Unmarshal(jsonBytes, &conf) - if err != nil { - return "", fmt.Errorf("decoding version from network config: %w", err) - } - if conf.CNIVersion == "" { - return "0.1.0", nil - } - return conf.CNIVersion, nil -} - -// Create creates a CNI Result using the given JSON with the expected -// version, or an error if the creation could not be performed -func Create(version string, bytes []byte) (types.Result, error) { - return convert.Create(version, bytes) -} - -// CreateFromBytes creates a CNI Result from the given JSON, automatically -// detecting the CNI spec version of the result. An error is returned if the -// operation could not be performed. -func CreateFromBytes(bytes []byte) (types.Result, error) { - version, err := DecodeVersion(bytes) - if err != nil { - return nil, err - } - return convert.Create(version, bytes) -} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/current/types.go b/vendor/github.com/containernetworking/cni/pkg/types/current/types.go new file mode 100644 index 000000000..754cc6e72 --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/types/current/types.go @@ -0,0 +1,276 @@ +// Copyright 2016 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package current + +import ( + "encoding/json" + "fmt" + "io" + "net" + "os" + + "github.com/containernetworking/cni/pkg/types" + "github.com/containernetworking/cni/pkg/types/020" +) + +const ImplementedSpecVersion string = "0.4.0" + +var SupportedVersions = []string{"0.3.0", "0.3.1", ImplementedSpecVersion} + +func NewResult(data []byte) (types.Result, error) { + result := &Result{} + if err := json.Unmarshal(data, result); err != nil { + return nil, err + } + return result, nil +} + +func GetResult(r types.Result) (*Result, error) { + resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion) + if err != nil { + return nil, err + } + result, ok := resultCurrent.(*Result) + if !ok { + return nil, fmt.Errorf("failed to convert result") + } + return result, nil +} + +var resultConverters = []struct { + versions []string + convert func(types.Result) (*Result, error) +}{ + {types020.SupportedVersions, convertFrom020}, + {SupportedVersions, convertFrom030}, +} + +func convertFrom020(result types.Result) (*Result, error) { + oldResult, err := types020.GetResult(result) + if err != nil { + return nil, err + } + + newResult := &Result{ + CNIVersion: ImplementedSpecVersion, + DNS: oldResult.DNS, + Routes: []*types.Route{}, + } + + if oldResult.IP4 != nil { + newResult.IPs = append(newResult.IPs, &IPConfig{ + Version: "4", + Address: oldResult.IP4.IP, + Gateway: oldResult.IP4.Gateway, + }) + for _, route := range oldResult.IP4.Routes { + newResult.Routes = append(newResult.Routes, &types.Route{ + Dst: route.Dst, + GW: route.GW, + }) + } + } + + if oldResult.IP6 != nil { + newResult.IPs = append(newResult.IPs, &IPConfig{ + Version: "6", + Address: oldResult.IP6.IP, + Gateway: oldResult.IP6.Gateway, + }) + for _, route := range oldResult.IP6.Routes { + newResult.Routes = append(newResult.Routes, &types.Route{ + Dst: route.Dst, + GW: route.GW, + }) + } + } + + return newResult, nil +} + +func convertFrom030(result types.Result) (*Result, error) { + newResult, ok := result.(*Result) + if !ok { + return nil, fmt.Errorf("failed to convert result") + } + newResult.CNIVersion = ImplementedSpecVersion + return newResult, nil +} + +func NewResultFromResult(result types.Result) (*Result, error) { + version := result.Version() + for _, converter := range resultConverters { + for _, supportedVersion := range converter.versions { + if version == supportedVersion { + return converter.convert(result) + } + } + } + return nil, fmt.Errorf("unsupported CNI result22 version %q", version) +} + +// Result is what gets returned from the plugin (via stdout) to the caller +type Result struct { + CNIVersion string `json:"cniVersion,omitempty"` + Interfaces []*Interface `json:"interfaces,omitempty"` + IPs []*IPConfig `json:"ips,omitempty"` + Routes []*types.Route `json:"routes,omitempty"` + DNS types.DNS `json:"dns,omitempty"` +} + +// Convert to the older 0.2.0 CNI spec Result type +func (r *Result) convertTo020() (*types020.Result, error) { + oldResult := &types020.Result{ + CNIVersion: types020.ImplementedSpecVersion, + DNS: r.DNS, + } + + for _, ip := range r.IPs { + // Only convert the first IP address of each version as 0.2.0 + // and earlier cannot handle multiple IP addresses + if ip.Version == "4" && oldResult.IP4 == nil { + oldResult.IP4 = &types020.IPConfig{ + IP: ip.Address, + Gateway: ip.Gateway, + } + } else if ip.Version == "6" && oldResult.IP6 == nil { + oldResult.IP6 = &types020.IPConfig{ + IP: ip.Address, + Gateway: ip.Gateway, + } + } + + if oldResult.IP4 != nil && oldResult.IP6 != nil { + break + } + } + + for _, route := range r.Routes { + is4 := route.Dst.IP.To4() != nil + if is4 && oldResult.IP4 != nil { + oldResult.IP4.Routes = append(oldResult.IP4.Routes, types.Route{ + Dst: route.Dst, + GW: route.GW, + }) + } else if !is4 && oldResult.IP6 != nil { + oldResult.IP6.Routes = append(oldResult.IP6.Routes, types.Route{ + Dst: route.Dst, + GW: route.GW, + }) + } + } + + if oldResult.IP4 == nil && oldResult.IP6 == nil { + return nil, fmt.Errorf("cannot convert: no valid IP addresses") + } + + return oldResult, nil +} + +func (r *Result) Version() string { + return ImplementedSpecVersion +} + +func (r *Result) GetAsVersion(version string) (types.Result, error) { + switch version { + case "0.3.0", "0.3.1", ImplementedSpecVersion: + r.CNIVersion = version + return r, nil + case types020.SupportedVersions[0], types020.SupportedVersions[1], types020.SupportedVersions[2]: + return r.convertTo020() + } + return nil, fmt.Errorf("cannot convert version 0.3.x to %q", version) +} + +func (r *Result) Print() error { + return r.PrintTo(os.Stdout) +} + +func (r *Result) PrintTo(writer io.Writer) error { + data, err := json.MarshalIndent(r, "", " ") + if err != nil { + return err + } + _, err = writer.Write(data) + return err +} + +// Convert this old version result to the current CNI version result +func (r *Result) Convert() (*Result, error) { + return r, nil +} + +// Interface contains values about the created interfaces +type Interface struct { + Name string `json:"name"` + Mac string `json:"mac,omitempty"` + Sandbox string `json:"sandbox,omitempty"` +} + +func (i *Interface) String() string { + return fmt.Sprintf("%+v", *i) +} + +// Int returns a pointer to the int value passed in. Used to +// set the IPConfig.Interface field. +func Int(v int) *int { + return &v +} + +// IPConfig contains values necessary to configure an IP address on an interface +type IPConfig struct { + // IP version, either "4" or "6" + Version string + // Index into Result structs Interfaces list + Interface *int + Address net.IPNet + Gateway net.IP +} + +func (i *IPConfig) String() string { + return fmt.Sprintf("%+v", *i) +} + +// JSON (un)marshallable types +type ipConfig struct { + Version string `json:"version"` + Interface *int `json:"interface,omitempty"` + Address types.IPNet `json:"address"` + Gateway net.IP `json:"gateway,omitempty"` +} + +func (c *IPConfig) MarshalJSON() ([]byte, error) { + ipc := ipConfig{ + Version: c.Version, + Interface: c.Interface, + Address: types.IPNet(c.Address), + Gateway: c.Gateway, + } + + return json.Marshal(ipc) +} + +func (c *IPConfig) UnmarshalJSON(data []byte) error { + ipc := ipConfig{} + if err := json.Unmarshal(data, &ipc); err != nil { + return err + } + + c.Version = ipc.Version + c.Interface = ipc.Interface + c.Address = net.IPNet(ipc.Address) + c.Gateway = ipc.Gateway + return nil +} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/internal/convert.go b/vendor/github.com/containernetworking/cni/pkg/types/internal/convert.go deleted file mode 100644 index bdbe4b0a5..000000000 --- a/vendor/github.com/containernetworking/cni/pkg/types/internal/convert.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2016 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package convert - -import ( - "fmt" - - "github.com/containernetworking/cni/pkg/types" -) - -// ConvertFn should convert from the given arbitrary Result type into a -// Result implementing CNI specification version passed in toVersion. -// The function is guaranteed to be passed a Result type matching the -// fromVersion it was registered with, and is guaranteed to be -// passed a toVersion matching one of the toVersions it was registered with. -type ConvertFn func(from types.Result, toVersion string) (types.Result, error) - -type converter struct { - // fromVersion is the CNI Result spec version that convertFn accepts - fromVersion string - // toVersions is a list of versions that convertFn can convert to - toVersions []string - convertFn ConvertFn -} - -var converters []*converter - -func findConverter(fromVersion, toVersion string) *converter { - for _, c := range converters { - if c.fromVersion == fromVersion { - for _, v := range c.toVersions { - if v == toVersion { - return c - } - } - } - } - return nil -} - -// Convert converts a CNI Result to the requested CNI specification version, -// or returns an error if the conversion could not be performed or failed -func Convert(from types.Result, toVersion string) (types.Result, error) { - if toVersion == "" { - toVersion = "0.1.0" - } - - fromVersion := from.Version() - - // Shortcut for same version - if fromVersion == toVersion { - return from, nil - } - - // Otherwise find the right converter - c := findConverter(fromVersion, toVersion) - if c == nil { - return nil, fmt.Errorf("no converter for CNI result version %s to %s", - fromVersion, toVersion) - } - return c.convertFn(from, toVersion) -} - -// RegisterConverter registers a CNI Result converter. SHOULD NOT BE CALLED -// EXCEPT FROM CNI ITSELF. -func RegisterConverter(fromVersion string, toVersions []string, convertFn ConvertFn) { - // Make sure there is no converter already registered for these - // from and to versions - for _, v := range toVersions { - if findConverter(fromVersion, v) != nil { - panic(fmt.Sprintf("converter already registered for %s to %s", - fromVersion, v)) - } - } - converters = append(converters, &converter{ - fromVersion: fromVersion, - toVersions: toVersions, - convertFn: convertFn, - }) -} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/internal/create.go b/vendor/github.com/containernetworking/cni/pkg/types/internal/create.go deleted file mode 100644 index 963630912..000000000 --- a/vendor/github.com/containernetworking/cni/pkg/types/internal/create.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2016 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package convert - -import ( - "fmt" - - "github.com/containernetworking/cni/pkg/types" -) - -type ResultFactoryFunc func([]byte) (types.Result, error) - -type creator struct { - // CNI Result spec versions that createFn can create a Result for - versions []string - createFn ResultFactoryFunc -} - -var creators []*creator - -func findCreator(version string) *creator { - for _, c := range creators { - for _, v := range c.versions { - if v == version { - return c - } - } - } - return nil -} - -// Create creates a CNI Result using the given JSON, or an error if the creation -// could not be performed -func Create(version string, bytes []byte) (types.Result, error) { - if c := findCreator(version); c != nil { - return c.createFn(bytes) - } - return nil, fmt.Errorf("unsupported CNI result version %q", version) -} - -// RegisterCreator registers a CNI Result creator. SHOULD NOT BE CALLED -// EXCEPT FROM CNI ITSELF. -func RegisterCreator(versions []string, createFn ResultFactoryFunc) { - // Make sure there is no creator already registered for these versions - for _, v := range versions { - if findCreator(v) != nil { - panic(fmt.Sprintf("creator already registered for %s", v)) - } - } - creators = append(creators, &creator{ - versions: versions, - createFn: createFn, - }) -} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/types.go b/vendor/github.com/containernetworking/cni/pkg/types/types.go index fba17dfc0..3fa757a5d 100644 --- a/vendor/github.com/containernetworking/cni/pkg/types/types.go +++ b/vendor/github.com/containernetworking/cni/pkg/types/types.go @@ -83,6 +83,8 @@ type NetConfList struct { Plugins []*NetConf `json:"plugins,omitempty"` } +type ResultFactoryFunc func([]byte) (Result, error) + // Result is an interface that provides the result of plugin execution type Result interface { // The highest CNI specification result version the result supports @@ -116,24 +118,6 @@ type DNS struct { Options []string `json:"options,omitempty"` } -func (d *DNS) Copy() *DNS { - if d == nil { - return nil - } - - to := &DNS{Domain: d.Domain} - for _, ns := range d.Nameservers { - to.Nameservers = append(to.Nameservers, ns) - } - for _, s := range d.Search { - to.Search = append(to.Search, s) - } - for _, o := range d.Options { - to.Options = append(to.Options, o) - } - return to -} - type Route struct { Dst net.IPNet GW net.IP @@ -143,17 +127,6 @@ func (r *Route) String() string { return fmt.Sprintf("%+v", *r) } -func (r *Route) Copy() *Route { - if r == nil { - return nil - } - - return &Route{ - Dst: r.Dst, - GW: r.GW, - } -} - // Well known error codes // see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes const ( diff --git a/vendor/github.com/containernetworking/cni/pkg/version/conf.go b/vendor/github.com/containernetworking/cni/pkg/version/conf.go index 808c33b83..3cca58bbe 100644 --- a/vendor/github.com/containernetworking/cni/pkg/version/conf.go +++ b/vendor/github.com/containernetworking/cni/pkg/version/conf.go @@ -15,12 +15,23 @@ package version import ( - "github.com/containernetworking/cni/pkg/types/create" + "encoding/json" + "fmt" ) // ConfigDecoder can decode the CNI version available in network config data type ConfigDecoder struct{} func (*ConfigDecoder) Decode(jsonBytes []byte) (string, error) { - return create.DecodeVersion(jsonBytes) + var conf struct { + CNIVersion string `json:"cniVersion"` + } + err := json.Unmarshal(jsonBytes, &conf) + if err != nil { + return "", fmt.Errorf("decoding version from network config: %s", err) + } + if conf.CNIVersion == "" { + return "0.1.0", nil + } + return conf.CNIVersion, nil } diff --git a/vendor/github.com/containernetworking/cni/pkg/version/plugin.go b/vendor/github.com/containernetworking/cni/pkg/version/plugin.go index d4bc9d169..1df427243 100644 --- a/vendor/github.com/containernetworking/cni/pkg/version/plugin.go +++ b/vendor/github.com/containernetworking/cni/pkg/version/plugin.go @@ -68,7 +68,7 @@ func (*PluginDecoder) Decode(jsonBytes []byte) (PluginInfo, error) { var info pluginInfo err := json.Unmarshal(jsonBytes, &info) if err != nil { - return nil, fmt.Errorf("decoding version info: %w", err) + return nil, fmt.Errorf("decoding version info: %s", err) } if info.CNIVersion_ == "" { return nil, fmt.Errorf("decoding version info: missing field cniVersion") @@ -97,20 +97,20 @@ func ParseVersion(version string) (int, int, int, error) { major, err := strconv.Atoi(parts[0]) if err != nil { - return -1, -1, -1, fmt.Errorf("failed to convert major version part %q: %w", parts[0], err) + return -1, -1, -1, fmt.Errorf("failed to convert major version part %q: %v", parts[0], err) } if len(parts) >= 2 { minor, err = strconv.Atoi(parts[1]) if err != nil { - return -1, -1, -1, fmt.Errorf("failed to convert minor version part %q: %w", parts[1], err) + return -1, -1, -1, fmt.Errorf("failed to convert minor version part %q: %v", parts[1], err) } } if len(parts) >= 3 { micro, err = strconv.Atoi(parts[2]) if err != nil { - return -1, -1, -1, fmt.Errorf("failed to convert micro version part %q: %w", parts[2], err) + return -1, -1, -1, fmt.Errorf("failed to convert micro version part %q: %v", parts[2], err) } } diff --git a/vendor/github.com/containernetworking/cni/pkg/version/version.go b/vendor/github.com/containernetworking/cni/pkg/version/version.go index 709a1fb3d..8f3508e61 100644 --- a/vendor/github.com/containernetworking/cni/pkg/version/version.go +++ b/vendor/github.com/containernetworking/cni/pkg/version/version.go @@ -19,13 +19,13 @@ import ( "fmt" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/100" - "github.com/containernetworking/cni/pkg/types/create" + "github.com/containernetworking/cni/pkg/types/020" + "github.com/containernetworking/cni/pkg/types/current" ) // Current reports the version of the CNI spec implemented by this library func Current() string { - return types100.ImplementedSpecVersion + return "0.4.0" } // Legacy PluginInfo describes a plugin that is backwards compatible with the @@ -36,12 +36,29 @@ func Current() string { // Any future CNI spec versions which meet this definition should be added to // this list. var Legacy = PluginSupports("0.1.0", "0.2.0") -var All = PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0", "1.0.0") +var All = PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0") + +var resultFactories = []struct { + supportedVersions []string + newResult types.ResultFactoryFunc +}{ + {current.SupportedVersions, current.NewResult}, + {types020.SupportedVersions, types020.NewResult}, +} // Finds a Result object matching the requested version (if any) and asks // that object to parse the plugin result, returning an error if parsing failed. func NewResult(version string, resultBytes []byte) (types.Result, error) { - return create.Create(version, resultBytes) + reconciler := &Reconciler{} + for _, resultFactory := range resultFactories { + err := reconciler.CheckRaw(version, resultFactory.supportedVersions) + if err == nil { + // Result supports this version + return resultFactory.newResult(resultBytes) + } + } + + return nil, fmt.Errorf("unsupported CNI result version %q", version) } // ParsePrevResult parses a prevResult in a NetConf structure and sets @@ -51,22 +68,15 @@ func ParsePrevResult(conf *types.NetConf) error { return nil } - // Prior to 1.0.0, Result types may not marshal a CNIVersion. Since the - // result version must match the config version, if the Result's version - // is empty, inject the config version. - if ver, ok := conf.RawPrevResult["CNIVersion"]; !ok || ver == "" { - conf.RawPrevResult["CNIVersion"] = conf.CNIVersion - } - resultBytes, err := json.Marshal(conf.RawPrevResult) if err != nil { - return fmt.Errorf("could not serialize prevResult: %w", err) + return fmt.Errorf("could not serialize prevResult: %v", err) } conf.RawPrevResult = nil - conf.PrevResult, err = create.Create(conf.CNIVersion, resultBytes) + conf.PrevResult, err = NewResult(conf.CNIVersion, resultBytes) if err != nil { - return fmt.Errorf("could not parse prevResult: %w", err) + return fmt.Errorf("could not parse prevResult: %v", err) } return nil diff --git a/vendor/github.com/containernetworking/plugins/pkg/ip/addr_linux.go b/vendor/github.com/containernetworking/plugins/pkg/ip/addr_linux.go new file mode 100644 index 000000000..b4db50b9a --- /dev/null +++ b/vendor/github.com/containernetworking/plugins/pkg/ip/addr_linux.go @@ -0,0 +1,68 @@ +// Copyright 2017 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ip + +import ( + "fmt" + "syscall" + "time" + + "github.com/vishvananda/netlink" +) + +const SETTLE_INTERVAL = 50 * time.Millisecond + +// SettleAddresses waits for all addresses on a link to leave tentative state. +// This is particularly useful for ipv6, where all addresses need to do DAD. +// There is no easy way to wait for this as an event, so just loop until the +// addresses are no longer tentative. +// If any addresses are still tentative after timeout seconds, then error. +func SettleAddresses(ifName string, timeout int) error { + link, err := netlink.LinkByName(ifName) + if err != nil { + return fmt.Errorf("failed to retrieve link: %v", err) + } + + deadline := time.Now().Add(time.Duration(timeout) * time.Second) + for { + addrs, err := netlink.AddrList(link, netlink.FAMILY_ALL) + if err != nil { + return fmt.Errorf("could not list addresses: %v", err) + } + + if len(addrs) == 0 { + return nil + } + + ok := true + for _, addr := range addrs { + if addr.Flags&(syscall.IFA_F_TENTATIVE|syscall.IFA_F_DADFAILED) > 0 { + ok = false + break // Break out of the `range addrs`, not the `for` + } + } + + if ok { + return nil + } + if time.Now().After(deadline) { + return fmt.Errorf("link %s still has tentative addresses after %d seconds", + ifName, + timeout) + } + + time.Sleep(SETTLE_INTERVAL) + } +} diff --git a/vendor/github.com/containernetworking/plugins/pkg/ip/cidr.go b/vendor/github.com/containernetworking/plugins/pkg/ip/cidr.go new file mode 100644 index 000000000..7acc2d47c --- /dev/null +++ b/vendor/github.com/containernetworking/plugins/pkg/ip/cidr.go @@ -0,0 +1,61 @@ +// Copyright 2015 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ip + +import ( + "math/big" + "net" +) + +// NextIP returns IP incremented by 1 +func NextIP(ip net.IP) net.IP { + i := ipToInt(ip) + return intToIP(i.Add(i, big.NewInt(1))) +} + +// PrevIP returns IP decremented by 1 +func PrevIP(ip net.IP) net.IP { + i := ipToInt(ip) + return intToIP(i.Sub(i, big.NewInt(1))) +} + +// Cmp compares two IPs, returning the usual ordering: +// a < b : -1 +// a == b : 0 +// a > b : 1 +func Cmp(a, b net.IP) int { + aa := ipToInt(a) + bb := ipToInt(b) + return aa.Cmp(bb) +} + +func ipToInt(ip net.IP) *big.Int { + if v := ip.To4(); v != nil { + return big.NewInt(0).SetBytes(v) + } + return big.NewInt(0).SetBytes(ip.To16()) +} + +func intToIP(i *big.Int) net.IP { + return net.IP(i.Bytes()) +} + +// Network masks off the host portion of the IP +func Network(ipn *net.IPNet) *net.IPNet { + return &net.IPNet{ + IP: ipn.IP.Mask(ipn.Mask), + Mask: ipn.Mask, + } +} diff --git a/vendor/github.com/containernetworking/plugins/pkg/ip/ipforward_linux.go b/vendor/github.com/containernetworking/plugins/pkg/ip/ipforward_linux.go new file mode 100644 index 000000000..8216a2c38 --- /dev/null +++ b/vendor/github.com/containernetworking/plugins/pkg/ip/ipforward_linux.go @@ -0,0 +1,61 @@ +// Copyright 2015 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ip + +import ( + "bytes" + "io/ioutil" + + "github.com/containernetworking/cni/pkg/types/current" +) + +func EnableIP4Forward() error { + return echo1("/proc/sys/net/ipv4/ip_forward") +} + +func EnableIP6Forward() error { + return echo1("/proc/sys/net/ipv6/conf/all/forwarding") +} + +// EnableForward will enable forwarding for all configured +// address families +func EnableForward(ips []*current.IPConfig) error { + v4 := false + v6 := false + + for _, ip := range ips { + if ip.Version == "4" && !v4 { + if err := EnableIP4Forward(); err != nil { + return err + } + v4 = true + } else if ip.Version == "6" && !v6 { + if err := EnableIP6Forward(); err != nil { + return err + } + v6 = true + } + } + return nil +} + +func echo1(f string) error { + if content, err := ioutil.ReadFile(f); err == nil { + if bytes.Equal(bytes.TrimSpace(content), []byte("1")) { + return nil + } + } + return ioutil.WriteFile(f, []byte("1"), 0644) +} diff --git a/vendor/github.com/containernetworking/plugins/pkg/ip/ipmasq_linux.go b/vendor/github.com/containernetworking/plugins/pkg/ip/ipmasq_linux.go new file mode 100644 index 000000000..cc640a605 --- /dev/null +++ b/vendor/github.com/containernetworking/plugins/pkg/ip/ipmasq_linux.go @@ -0,0 +1,126 @@ +// Copyright 2015 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ip + +import ( + "fmt" + "net" + + "github.com/coreos/go-iptables/iptables" +) + +// SetupIPMasq installs iptables rules to masquerade traffic +// coming from ip of ipn and going outside of ipn +func SetupIPMasq(ipn *net.IPNet, chain string, comment string) error { + isV6 := ipn.IP.To4() == nil + + var ipt *iptables.IPTables + var err error + var multicastNet string + + if isV6 { + ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv6) + multicastNet = "ff00::/8" + } else { + ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv4) + multicastNet = "224.0.0.0/4" + } + if err != nil { + return fmt.Errorf("failed to locate iptables: %v", err) + } + + // Create chain if doesn't exist + exists := false + chains, err := ipt.ListChains("nat") + if err != nil { + return fmt.Errorf("failed to list chains: %v", err) + } + for _, ch := range chains { + if ch == chain { + exists = true + break + } + } + if !exists { + if err = ipt.NewChain("nat", chain); err != nil { + return err + } + } + + // Packets to this network should not be touched + if err := ipt.AppendUnique("nat", chain, "-d", ipn.String(), "-j", "ACCEPT", "-m", "comment", "--comment", comment); err != nil { + return err + } + + // Don't masquerade multicast - pods should be able to talk to other pods + // on the local network via multicast. + if err := ipt.AppendUnique("nat", chain, "!", "-d", multicastNet, "-j", "MASQUERADE", "-m", "comment", "--comment", comment); err != nil { + return err + } + + // Packets from the specific IP of this network will hit the chain + return ipt.AppendUnique("nat", "POSTROUTING", "-s", ipn.IP.String(), "-j", chain, "-m", "comment", "--comment", comment) +} + +// TeardownIPMasq undoes the effects of SetupIPMasq +func TeardownIPMasq(ipn *net.IPNet, chain string, comment string) error { + isV6 := ipn.IP.To4() == nil + + var ipt *iptables.IPTables + var err error + + if isV6 { + ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv6) + } else { + ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv4) + } + if err != nil { + return fmt.Errorf("failed to locate iptables: %v", err) + } + + err = ipt.Delete("nat", "POSTROUTING", "-s", ipn.IP.String(), "-j", chain, "-m", "comment", "--comment", comment) + if err != nil && !isNotExist(err) { + return err + } + + // for downward compatibility + err = ipt.Delete("nat", "POSTROUTING", "-s", ipn.String(), "-j", chain, "-m", "comment", "--comment", comment) + if err != nil && !isNotExist(err) { + return err + } + + err = ipt.ClearChain("nat", chain) + if err != nil && !isNotExist(err) { + return err + + } + + err = ipt.DeleteChain("nat", chain) + if err != nil && !isNotExist(err) { + return err + } + + return nil +} + +// isNotExist returnst true if the error is from iptables indicating +// that the target does not exist. +func isNotExist(err error) bool { + e, ok := err.(*iptables.Error) + if !ok { + return false + } + return e.IsNotExist() +} diff --git a/vendor/github.com/containernetworking/plugins/pkg/ip/link_linux.go b/vendor/github.com/containernetworking/plugins/pkg/ip/link_linux.go new file mode 100644 index 000000000..f8781cf19 --- /dev/null +++ b/vendor/github.com/containernetworking/plugins/pkg/ip/link_linux.go @@ -0,0 +1,293 @@ +// Copyright 2015 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ip + +import ( + "crypto/rand" + "errors" + "fmt" + "net" + "os" + + "github.com/safchain/ethtool" + "github.com/vishvananda/netlink" + + "github.com/containernetworking/plugins/pkg/ns" + "github.com/containernetworking/plugins/pkg/utils/hwaddr" + "github.com/containernetworking/plugins/pkg/utils/sysctl" +) + +var ( + ErrLinkNotFound = errors.New("link not found") +) + +func makeVethPair(name, peer string, mtu int) (netlink.Link, error) { + veth := &netlink.Veth{ + LinkAttrs: netlink.LinkAttrs{ + Name: name, + Flags: net.FlagUp, + MTU: mtu, + }, + PeerName: peer, + } + if err := netlink.LinkAdd(veth); err != nil { + return nil, err + } + // Re-fetch the link to get its creation-time parameters, e.g. index and mac + veth2, err := netlink.LinkByName(name) + if err != nil { + netlink.LinkDel(veth) // try and clean up the link if possible. + return nil, err + } + + return veth2, nil +} + +func peerExists(name string) bool { + if _, err := netlink.LinkByName(name); err != nil { + return false + } + return true +} + +func makeVeth(name, vethPeerName string, mtu int) (peerName string, veth netlink.Link, err error) { + for i := 0; i < 10; i++ { + if vethPeerName != "" { + peerName = vethPeerName + } else { + peerName, err = RandomVethName() + if err != nil { + return + } + } + + veth, err = makeVethPair(name, peerName, mtu) + switch { + case err == nil: + return + + case os.IsExist(err): + if peerExists(peerName) && vethPeerName == "" { + continue + } + err = fmt.Errorf("container veth name provided (%v) already exists", name) + return + + default: + err = fmt.Errorf("failed to make veth pair: %v", err) + return + } + } + + // should really never be hit + err = fmt.Errorf("failed to find a unique veth name") + return +} + +// RandomVethName returns string "veth" with random prefix (hashed from entropy) +func RandomVethName() (string, error) { + entropy := make([]byte, 4) + _, err := rand.Reader.Read(entropy) + if err != nil { + return "", fmt.Errorf("failed to generate random veth name: %v", err) + } + + // NetworkManager (recent versions) will ignore veth devices that start with "veth" + return fmt.Sprintf("veth%x", entropy), nil +} + +func RenameLink(curName, newName string) error { + link, err := netlink.LinkByName(curName) + if err == nil { + err = netlink.LinkSetName(link, newName) + } + return err +} + +func ifaceFromNetlinkLink(l netlink.Link) net.Interface { + a := l.Attrs() + return net.Interface{ + Index: a.Index, + MTU: a.MTU, + Name: a.Name, + HardwareAddr: a.HardwareAddr, + Flags: a.Flags, + } +} + +// SetupVethWithName sets up a pair of virtual ethernet devices. +// Call SetupVethWithName from inside the container netns. It will create both veth +// devices and move the host-side veth into the provided hostNS namespace. +// hostVethName: If hostVethName is not specified, the host-side veth name will use a random string. +// On success, SetupVethWithName returns (hostVeth, containerVeth, nil) +func SetupVethWithName(contVethName, hostVethName string, mtu int, hostNS ns.NetNS) (net.Interface, net.Interface, error) { + hostVethName, contVeth, err := makeVeth(contVethName, hostVethName, mtu) + if err != nil { + return net.Interface{}, net.Interface{}, err + } + + if err = netlink.LinkSetUp(contVeth); err != nil { + return net.Interface{}, net.Interface{}, fmt.Errorf("failed to set %q up: %v", contVethName, err) + } + + hostVeth, err := netlink.LinkByName(hostVethName) + if err != nil { + return net.Interface{}, net.Interface{}, fmt.Errorf("failed to lookup %q: %v", hostVethName, err) + } + + if err = netlink.LinkSetNsFd(hostVeth, int(hostNS.Fd())); err != nil { + return net.Interface{}, net.Interface{}, fmt.Errorf("failed to move veth to host netns: %v", err) + } + + err = hostNS.Do(func(_ ns.NetNS) error { + hostVeth, err = netlink.LinkByName(hostVethName) + if err != nil { + return fmt.Errorf("failed to lookup %q in %q: %v", hostVethName, hostNS.Path(), err) + } + + if err = netlink.LinkSetUp(hostVeth); err != nil { + return fmt.Errorf("failed to set %q up: %v", hostVethName, err) + } + + // we want to own the routes for this interface + _, _ = sysctl.Sysctl(fmt.Sprintf("net/ipv6/conf/%s/accept_ra", hostVethName), "0") + return nil + }) + if err != nil { + return net.Interface{}, net.Interface{}, err + } + return ifaceFromNetlinkLink(hostVeth), ifaceFromNetlinkLink(contVeth), nil +} + +// SetupVeth sets up a pair of virtual ethernet devices. +// Call SetupVeth from inside the container netns. It will create both veth +// devices and move the host-side veth into the provided hostNS namespace. +// On success, SetupVeth returns (hostVeth, containerVeth, nil) +func SetupVeth(contVethName string, mtu int, hostNS ns.NetNS) (net.Interface, net.Interface, error) { + return SetupVethWithName(contVethName, "", mtu, hostNS) +} + +// DelLinkByName removes an interface link. +func DelLinkByName(ifName string) error { + iface, err := netlink.LinkByName(ifName) + if err != nil { + if _, ok := err.(netlink.LinkNotFoundError); ok { + return ErrLinkNotFound + } + return fmt.Errorf("failed to lookup %q: %v", ifName, err) + } + + if err = netlink.LinkDel(iface); err != nil { + return fmt.Errorf("failed to delete %q: %v", ifName, err) + } + + return nil +} + +// DelLinkByNameAddr remove an interface and returns its addresses +func DelLinkByNameAddr(ifName string) ([]*net.IPNet, error) { + iface, err := netlink.LinkByName(ifName) + if err != nil { + if _, ok := err.(netlink.LinkNotFoundError); ok { + return nil, ErrLinkNotFound + } + return nil, fmt.Errorf("failed to lookup %q: %v", ifName, err) + } + + addrs, err := netlink.AddrList(iface, netlink.FAMILY_ALL) + if err != nil { + return nil, fmt.Errorf("failed to get IP addresses for %q: %v", ifName, err) + } + + if err = netlink.LinkDel(iface); err != nil { + return nil, fmt.Errorf("failed to delete %q: %v", ifName, err) + } + + out := []*net.IPNet{} + for _, addr := range addrs { + if addr.IP.IsGlobalUnicast() { + out = append(out, addr.IPNet) + } + } + + return out, nil +} + +func SetHWAddrByIP(ifName string, ip4 net.IP, ip6 net.IP) error { + iface, err := netlink.LinkByName(ifName) + if err != nil { + return fmt.Errorf("failed to lookup %q: %v", ifName, err) + } + + switch { + case ip4 == nil && ip6 == nil: + return fmt.Errorf("neither ip4 or ip6 specified") + + case ip4 != nil: + { + hwAddr, err := hwaddr.GenerateHardwareAddr4(ip4, hwaddr.PrivateMACPrefix) + if err != nil { + return fmt.Errorf("failed to generate hardware addr: %v", err) + } + if err = netlink.LinkSetHardwareAddr(iface, hwAddr); err != nil { + return fmt.Errorf("failed to add hardware addr to %q: %v", ifName, err) + } + } + case ip6 != nil: + // TODO: IPv6 + } + + return nil +} + +// GetVethPeerIfindex returns the veth link object, the peer ifindex of the +// veth, or an error. This peer ifindex will only be valid in the peer's +// network namespace. +func GetVethPeerIfindex(ifName string) (netlink.Link, int, error) { + link, err := netlink.LinkByName(ifName) + if err != nil { + return nil, -1, fmt.Errorf("could not look up %q: %v", ifName, err) + } + if _, ok := link.(*netlink.Veth); !ok { + return nil, -1, fmt.Errorf("interface %q was not a veth interface", ifName) + } + + // veth supports IFLA_LINK (what vishvananda/netlink calls ParentIndex) + // on 4.1 and higher kernels + peerIndex := link.Attrs().ParentIndex + if peerIndex <= 0 { + // Fall back to ethtool for 4.0 and earlier kernels + e, err := ethtool.NewEthtool() + if err != nil { + return nil, -1, fmt.Errorf("failed to initialize ethtool: %v", err) + } + defer e.Close() + + stats, err := e.Stats(link.Attrs().Name) + if err != nil { + return nil, -1, fmt.Errorf("failed to request ethtool stats: %v", err) + } + n, ok := stats["peer_ifindex"] + if !ok { + return nil, -1, fmt.Errorf("failed to find 'peer_ifindex' in ethtool stats") + } + if n > 32767 || n == 0 { + return nil, -1, fmt.Errorf("invalid 'peer_ifindex' %d", n) + } + peerIndex = int(n) + } + + return link, peerIndex, nil +} diff --git a/vendor/github.com/containernetworking/plugins/pkg/ip/route_linux.go b/vendor/github.com/containernetworking/plugins/pkg/ip/route_linux.go new file mode 100644 index 000000000..f5c0d0803 --- /dev/null +++ b/vendor/github.com/containernetworking/plugins/pkg/ip/route_linux.go @@ -0,0 +1,47 @@ +// Copyright 2015-2017 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ip + +import ( + "net" + + "github.com/vishvananda/netlink" +) + +// AddRoute adds a universally-scoped route to a device. +func AddRoute(ipn *net.IPNet, gw net.IP, dev netlink.Link) error { + return netlink.RouteAdd(&netlink.Route{ + LinkIndex: dev.Attrs().Index, + Scope: netlink.SCOPE_UNIVERSE, + Dst: ipn, + Gw: gw, + }) +} + +// AddHostRoute adds a host-scoped route to a device. +func AddHostRoute(ipn *net.IPNet, gw net.IP, dev netlink.Link) error { + return netlink.RouteAdd(&netlink.Route{ + LinkIndex: dev.Attrs().Index, + Scope: netlink.SCOPE_HOST, + Dst: ipn, + Gw: gw, + }) +} + +// AddDefaultRoute sets the default route on the given gateway. +func AddDefaultRoute(gw net.IP, dev netlink.Link) error { + _, defNet, _ := net.ParseCIDR("0.0.0.0/0") + return AddRoute(defNet, gw, dev) +} diff --git a/vendor/github.com/containernetworking/plugins/pkg/ip/utils_linux.go b/vendor/github.com/containernetworking/plugins/pkg/ip/utils_linux.go new file mode 100644 index 000000000..7623c5e13 --- /dev/null +++ b/vendor/github.com/containernetworking/plugins/pkg/ip/utils_linux.go @@ -0,0 +1,120 @@ +// +build linux + +// Copyright 2016 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ip + +import ( + "fmt" + "net" + + "github.com/containernetworking/cni/pkg/types" + "github.com/containernetworking/cni/pkg/types/current" + "github.com/vishvananda/netlink" +) + +func ValidateExpectedInterfaceIPs(ifName string, resultIPs []*current.IPConfig) error { + + // Ensure ips + for _, ips := range resultIPs { + ourAddr := netlink.Addr{IPNet: &ips.Address} + match := false + + link, err := netlink.LinkByName(ifName) + if err != nil { + return fmt.Errorf("Cannot find container link %v", ifName) + } + + addrList, err := netlink.AddrList(link, netlink.FAMILY_ALL) + if err != nil { + return fmt.Errorf("Cannot obtain List of IP Addresses") + } + + for _, addr := range addrList { + if addr.Equal(ourAddr) { + match = true + break + } + } + if match == false { + return fmt.Errorf("Failed to match addr %v on interface %v", ourAddr, ifName) + } + + // Convert the host/prefixlen to just prefix for route lookup. + _, ourPrefix, err := net.ParseCIDR(ourAddr.String()) + + findGwy := &netlink.Route{Dst: ourPrefix} + routeFilter := netlink.RT_FILTER_DST + var family int + + switch { + case ips.Version == "4": + family = netlink.FAMILY_V4 + case ips.Version == "6": + family = netlink.FAMILY_V6 + default: + return fmt.Errorf("Invalid IP Version %v for interface %v", ips.Version, ifName) + } + + gwy, err := netlink.RouteListFiltered(family, findGwy, routeFilter) + if err != nil { + return fmt.Errorf("Error %v trying to find Gateway %v for interface %v", err, ips.Gateway, ifName) + } + if gwy == nil { + return fmt.Errorf("Failed to find Gateway %v for interface %v", ips.Gateway, ifName) + } + } + + return nil +} + +func ValidateExpectedRoute(resultRoutes []*types.Route) error { + + // Ensure that each static route in prevResults is found in the routing table + for _, route := range resultRoutes { + find := &netlink.Route{Dst: &route.Dst, Gw: route.GW} + routeFilter := netlink.RT_FILTER_DST | netlink.RT_FILTER_GW + var family int + + switch { + case route.Dst.IP.To4() != nil: + family = netlink.FAMILY_V4 + // Default route needs Dst set to nil + if route.Dst.String() == "0.0.0.0/0" { + find = &netlink.Route{Dst: nil, Gw: route.GW} + routeFilter = netlink.RT_FILTER_DST + } + case len(route.Dst.IP) == net.IPv6len: + family = netlink.FAMILY_V6 + // Default route needs Dst set to nil + if route.Dst.String() == "::/0" { + find = &netlink.Route{Dst: nil, Gw: route.GW} + routeFilter = netlink.RT_FILTER_DST + } + default: + return fmt.Errorf("Invalid static route found %v", route) + } + + wasFound, err := netlink.RouteListFiltered(family, find, routeFilter) + if err != nil { + return fmt.Errorf("Expected Route %v not route table lookup error %v", route, err) + } + if wasFound == nil { + return fmt.Errorf("Expected Route %v not found in routing table", route) + } + } + + return nil +} diff --git a/vendor/github.com/containernetworking/plugins/pkg/utils/hwaddr/hwaddr.go b/vendor/github.com/containernetworking/plugins/pkg/utils/hwaddr/hwaddr.go new file mode 100644 index 000000000..aaf3b8a02 --- /dev/null +++ b/vendor/github.com/containernetworking/plugins/pkg/utils/hwaddr/hwaddr.go @@ -0,0 +1,63 @@ +// Copyright 2016 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hwaddr + +import ( + "fmt" + "net" +) + +const ( + ipRelevantByteLen = 4 + PrivateMACPrefixString = "0a:58" +) + +var ( + // private mac prefix safe to use + PrivateMACPrefix = []byte{0x0a, 0x58} +) + +type SupportIp4OnlyErr struct{ msg string } + +func (e SupportIp4OnlyErr) Error() string { return e.msg } + +type MacParseErr struct{ msg string } + +func (e MacParseErr) Error() string { return e.msg } + +type InvalidPrefixLengthErr struct{ msg string } + +func (e InvalidPrefixLengthErr) Error() string { return e.msg } + +// GenerateHardwareAddr4 generates 48 bit virtual mac addresses based on the IP4 input. +func GenerateHardwareAddr4(ip net.IP, prefix []byte) (net.HardwareAddr, error) { + switch { + + case ip.To4() == nil: + return nil, SupportIp4OnlyErr{msg: "GenerateHardwareAddr4 only supports valid IPv4 address as input"} + + case len(prefix) != len(PrivateMACPrefix): + return nil, InvalidPrefixLengthErr{msg: fmt.Sprintf( + "Prefix has length %d instead of %d", len(prefix), len(PrivateMACPrefix)), + } + } + + ipByteLen := len(ip) + return (net.HardwareAddr)( + append( + prefix, + ip[ipByteLen-ipRelevantByteLen:ipByteLen]...), + ), nil +} diff --git a/vendor/github.com/containernetworking/plugins/pkg/utils/sysctl/sysctl_linux.go b/vendor/github.com/containernetworking/plugins/pkg/utils/sysctl/sysctl_linux.go new file mode 100644 index 000000000..7ee47e1ce --- /dev/null +++ b/vendor/github.com/containernetworking/plugins/pkg/utils/sysctl/sysctl_linux.go @@ -0,0 +1,80 @@ +// Copyright 2016 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sysctl + +import ( + "fmt" + "io/ioutil" + "path/filepath" + "strings" +) + +// Sysctl provides a method to set/get values from /proc/sys - in linux systems +// new interface to set/get values of variables formerly handled by sysctl syscall +// If optional `params` have only one string value - this function will +// set this value into corresponding sysctl variable +func Sysctl(name string, params ...string) (string, error) { + if len(params) > 1 { + return "", fmt.Errorf("unexcepted additional parameters") + } else if len(params) == 1 { + return setSysctl(name, params[0]) + } + return getSysctl(name) +} + +func getSysctl(name string) (string, error) { + fullName := filepath.Join("/proc/sys", toNormalName(name)) + fullName = filepath.Clean(fullName) + data, err := ioutil.ReadFile(fullName) + if err != nil { + return "", err + } + + return string(data[:len(data)-1]), nil +} + +func setSysctl(name, value string) (string, error) { + fullName := filepath.Join("/proc/sys", toNormalName(name)) + fullName = filepath.Clean(fullName) + if err := ioutil.WriteFile(fullName, []byte(value), 0644); err != nil { + return "", err + } + + return getSysctl(name) +} + +// Normalize names by using slash as separator +// Sysctl names can use dots or slashes as separator: +// - if dots are used, dots and slashes are interchanged. +// - if slashes are used, slashes and dots are left intact. +// Separator in use is determined by first occurrence. +func toNormalName(name string) string { + interchange := false + for _, c := range name { + if c == '.' { + interchange = true + break + } + if c == '/' { + break + } + } + + if interchange { + r := strings.NewReplacer(".", "/", "/", ".") + return r.Replace(name) + } + return name +} diff --git a/vendor/github.com/coreos/go-iptables/LICENSE b/vendor/github.com/coreos/go-iptables/LICENSE new file mode 100644 index 000000000..37ec93a14 --- /dev/null +++ b/vendor/github.com/coreos/go-iptables/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/coreos/go-iptables/NOTICE b/vendor/github.com/coreos/go-iptables/NOTICE new file mode 100644 index 000000000..23a0ada2f --- /dev/null +++ b/vendor/github.com/coreos/go-iptables/NOTICE @@ -0,0 +1,5 @@ +CoreOS Project +Copyright 2018 CoreOS, Inc + +This product includes software developed at CoreOS, Inc. +(http://www.coreos.com/). diff --git a/vendor/github.com/coreos/go-iptables/iptables/iptables.go b/vendor/github.com/coreos/go-iptables/iptables/iptables.go new file mode 100644 index 000000000..85047e59d --- /dev/null +++ b/vendor/github.com/coreos/go-iptables/iptables/iptables.go @@ -0,0 +1,680 @@ +// Copyright 2015 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package iptables + +import ( + "bytes" + "fmt" + "io" + "net" + "os/exec" + "regexp" + "strconv" + "strings" + "syscall" +) + +// Adds the output of stderr to exec.ExitError +type Error struct { + exec.ExitError + cmd exec.Cmd + msg string + exitStatus *int //for overriding +} + +func (e *Error) ExitStatus() int { + if e.exitStatus != nil { + return *e.exitStatus + } + return e.Sys().(syscall.WaitStatus).ExitStatus() +} + +func (e *Error) Error() string { + return fmt.Sprintf("running %v: exit status %v: %v", e.cmd.Args, e.ExitStatus(), e.msg) +} + +// IsNotExist returns true if the error is due to the chain or rule not existing +func (e *Error) IsNotExist() bool { + if e.ExitStatus() != 1 { + return false + } + msgNoRuleExist := "Bad rule (does a matching rule exist in that chain?).\n" + msgNoChainExist := "No chain/target/match by that name.\n" + return strings.Contains(e.msg, msgNoRuleExist) || strings.Contains(e.msg, msgNoChainExist) +} + +// Protocol to differentiate between IPv4 and IPv6 +type Protocol byte + +const ( + ProtocolIPv4 Protocol = iota + ProtocolIPv6 +) + +type IPTables struct { + path string + proto Protocol + hasCheck bool + hasWait bool + waitSupportSecond bool + hasRandomFully bool + v1 int + v2 int + v3 int + mode string // the underlying iptables operating mode, e.g. nf_tables + timeout int // time to wait for the iptables lock, default waits forever +} + +// Stat represents a structured statistic entry. +type Stat struct { + Packets uint64 `json:"pkts"` + Bytes uint64 `json:"bytes"` + Target string `json:"target"` + Protocol string `json:"prot"` + Opt string `json:"opt"` + Input string `json:"in"` + Output string `json:"out"` + Source *net.IPNet `json:"source"` + Destination *net.IPNet `json:"destination"` + Options string `json:"options"` +} + +type option func(*IPTables) + +func IPFamily(proto Protocol) option { + return func(ipt *IPTables) { + ipt.proto = proto + } +} + +func Timeout(timeout int) option { + return func(ipt *IPTables) { + ipt.timeout = timeout + } +} + +// New creates a new IPTables configured with the options passed as parameter. +// For backwards compatibility, by default always uses IPv4 and timeout 0. +// i.e. you can create an IPv6 IPTables using a timeout of 5 seconds passing +// the IPFamily and Timeout options as follow: +// ip6t := New(IPFamily(ProtocolIPv6), Timeout(5)) +func New(opts ...option) (*IPTables, error) { + + ipt := &IPTables{ + proto: ProtocolIPv4, + timeout: 0, + } + + for _, opt := range opts { + opt(ipt) + } + + path, err := exec.LookPath(getIptablesCommand(ipt.proto)) + if err != nil { + return nil, err + } + ipt.path = path + + vstring, err := getIptablesVersionString(path) + if err != nil { + return nil, fmt.Errorf("could not get iptables version: %v", err) + } + v1, v2, v3, mode, err := extractIptablesVersion(vstring) + if err != nil { + return nil, fmt.Errorf("failed to extract iptables version from [%s]: %v", vstring, err) + } + ipt.v1 = v1 + ipt.v2 = v2 + ipt.v3 = v3 + ipt.mode = mode + + checkPresent, waitPresent, waitSupportSecond, randomFullyPresent := getIptablesCommandSupport(v1, v2, v3) + ipt.hasCheck = checkPresent + ipt.hasWait = waitPresent + ipt.waitSupportSecond = waitSupportSecond + ipt.hasRandomFully = randomFullyPresent + + return ipt, nil +} + +// New creates a new IPTables for the given proto. +// The proto will determine which command is used, either "iptables" or "ip6tables". +func NewWithProtocol(proto Protocol) (*IPTables, error) { + return New(IPFamily(proto), Timeout(0)) +} + +// Proto returns the protocol used by this IPTables. +func (ipt *IPTables) Proto() Protocol { + return ipt.proto +} + +// Exists checks if given rulespec in specified table/chain exists +func (ipt *IPTables) Exists(table, chain string, rulespec ...string) (bool, error) { + if !ipt.hasCheck { + return ipt.existsForOldIptables(table, chain, rulespec) + + } + cmd := append([]string{"-t", table, "-C", chain}, rulespec...) + err := ipt.run(cmd...) + eerr, eok := err.(*Error) + switch { + case err == nil: + return true, nil + case eok && eerr.ExitStatus() == 1: + return false, nil + default: + return false, err + } +} + +// Insert inserts rulespec to specified table/chain (in specified pos) +func (ipt *IPTables) Insert(table, chain string, pos int, rulespec ...string) error { + cmd := append([]string{"-t", table, "-I", chain, strconv.Itoa(pos)}, rulespec...) + return ipt.run(cmd...) +} + +// Append appends rulespec to specified table/chain +func (ipt *IPTables) Append(table, chain string, rulespec ...string) error { + cmd := append([]string{"-t", table, "-A", chain}, rulespec...) + return ipt.run(cmd...) +} + +// AppendUnique acts like Append except that it won't add a duplicate +func (ipt *IPTables) AppendUnique(table, chain string, rulespec ...string) error { + exists, err := ipt.Exists(table, chain, rulespec...) + if err != nil { + return err + } + + if !exists { + return ipt.Append(table, chain, rulespec...) + } + + return nil +} + +// Delete removes rulespec in specified table/chain +func (ipt *IPTables) Delete(table, chain string, rulespec ...string) error { + cmd := append([]string{"-t", table, "-D", chain}, rulespec...) + return ipt.run(cmd...) +} + +func (ipt *IPTables) DeleteIfExists(table, chain string, rulespec ...string) error { + exists, err := ipt.Exists(table, chain, rulespec...) + if err == nil && exists { + err = ipt.Delete(table, chain, rulespec...) + } + return err +} + +// List rules in specified table/chain +func (ipt *IPTables) List(table, chain string) ([]string, error) { + args := []string{"-t", table, "-S", chain} + return ipt.executeList(args) +} + +// List rules (with counters) in specified table/chain +func (ipt *IPTables) ListWithCounters(table, chain string) ([]string, error) { + args := []string{"-t", table, "-v", "-S", chain} + return ipt.executeList(args) +} + +// ListChains returns a slice containing the name of each chain in the specified table. +func (ipt *IPTables) ListChains(table string) ([]string, error) { + args := []string{"-t", table, "-S"} + + result, err := ipt.executeList(args) + if err != nil { + return nil, err + } + + // Iterate over rules to find all default (-P) and user-specified (-N) chains. + // Chains definition always come before rules. + // Format is the following: + // -P OUTPUT ACCEPT + // -N Custom + var chains []string + for _, val := range result { + if strings.HasPrefix(val, "-P") || strings.HasPrefix(val, "-N") { + chains = append(chains, strings.Fields(val)[1]) + } else { + break + } + } + return chains, nil +} + +// '-S' is fine with non existing rule index as long as the chain exists +// therefore pass index 1 to reduce overhead for large chains +func (ipt *IPTables) ChainExists(table, chain string) (bool, error) { + err := ipt.run("-t", table, "-S", chain, "1") + eerr, eok := err.(*Error) + switch { + case err == nil: + return true, nil + case eok && eerr.ExitStatus() == 1: + return false, nil + default: + return false, err + } +} + +// Stats lists rules including the byte and packet counts +func (ipt *IPTables) Stats(table, chain string) ([][]string, error) { + args := []string{"-t", table, "-L", chain, "-n", "-v", "-x"} + lines, err := ipt.executeList(args) + if err != nil { + return nil, err + } + + appendSubnet := func(addr string) string { + if strings.IndexByte(addr, byte('/')) < 0 { + if strings.IndexByte(addr, '.') < 0 { + return addr + "/128" + } + return addr + "/32" + } + return addr + } + + ipv6 := ipt.proto == ProtocolIPv6 + + rows := [][]string{} + for i, line := range lines { + // Skip over chain name and field header + if i < 2 { + continue + } + + // Fields: + // 0=pkts 1=bytes 2=target 3=prot 4=opt 5=in 6=out 7=source 8=destination 9=options + line = strings.TrimSpace(line) + fields := strings.Fields(line) + + // The ip6tables verbose output cannot be naively split due to the default "opt" + // field containing 2 single spaces. + if ipv6 { + // Check if field 6 is "opt" or "source" address + dest := fields[6] + ip, _, _ := net.ParseCIDR(dest) + if ip == nil { + ip = net.ParseIP(dest) + } + + // If we detected a CIDR or IP, the "opt" field is empty.. insert it. + if ip != nil { + f := []string{} + f = append(f, fields[:4]...) + f = append(f, " ") // Empty "opt" field for ip6tables + f = append(f, fields[4:]...) + fields = f + } + } + + // Adjust "source" and "destination" to include netmask, to match regular + // List output + fields[7] = appendSubnet(fields[7]) + fields[8] = appendSubnet(fields[8]) + + // Combine "options" fields 9... into a single space-delimited field. + options := fields[9:] + fields = fields[:9] + fields = append(fields, strings.Join(options, " ")) + rows = append(rows, fields) + } + return rows, nil +} + +// ParseStat parses a single statistic row into a Stat struct. The input should +// be a string slice that is returned from calling the Stat method. +func (ipt *IPTables) ParseStat(stat []string) (parsed Stat, err error) { + // For forward-compatibility, expect at least 10 fields in the stat + if len(stat) < 10 { + return parsed, fmt.Errorf("stat contained fewer fields than expected") + } + + // Convert the fields that are not plain strings + parsed.Packets, err = strconv.ParseUint(stat[0], 0, 64) + if err != nil { + return parsed, fmt.Errorf(err.Error(), "could not parse packets") + } + parsed.Bytes, err = strconv.ParseUint(stat[1], 0, 64) + if err != nil { + return parsed, fmt.Errorf(err.Error(), "could not parse bytes") + } + _, parsed.Source, err = net.ParseCIDR(stat[7]) + if err != nil { + return parsed, fmt.Errorf(err.Error(), "could not parse source") + } + _, parsed.Destination, err = net.ParseCIDR(stat[8]) + if err != nil { + return parsed, fmt.Errorf(err.Error(), "could not parse destination") + } + + // Put the fields that are strings + parsed.Target = stat[2] + parsed.Protocol = stat[3] + parsed.Opt = stat[4] + parsed.Input = stat[5] + parsed.Output = stat[6] + parsed.Options = stat[9] + + return parsed, nil +} + +// StructuredStats returns statistics as structured data which may be further +// parsed and marshaled. +func (ipt *IPTables) StructuredStats(table, chain string) ([]Stat, error) { + rawStats, err := ipt.Stats(table, chain) + if err != nil { + return nil, err + } + + structStats := []Stat{} + for _, rawStat := range rawStats { + stat, err := ipt.ParseStat(rawStat) + if err != nil { + return nil, err + } + structStats = append(structStats, stat) + } + + return structStats, nil +} + +func (ipt *IPTables) executeList(args []string) ([]string, error) { + var stdout bytes.Buffer + if err := ipt.runWithOutput(args, &stdout); err != nil { + return nil, err + } + + rules := strings.Split(stdout.String(), "\n") + + // strip trailing newline + if len(rules) > 0 && rules[len(rules)-1] == "" { + rules = rules[:len(rules)-1] + } + + for i, rule := range rules { + rules[i] = filterRuleOutput(rule) + } + + return rules, nil +} + +// NewChain creates a new chain in the specified table. +// If the chain already exists, it will result in an error. +func (ipt *IPTables) NewChain(table, chain string) error { + return ipt.run("-t", table, "-N", chain) +} + +const existsErr = 1 + +// ClearChain flushed (deletes all rules) in the specified table/chain. +// If the chain does not exist, a new one will be created +func (ipt *IPTables) ClearChain(table, chain string) error { + err := ipt.NewChain(table, chain) + + eerr, eok := err.(*Error) + switch { + case err == nil: + return nil + case eok && eerr.ExitStatus() == existsErr: + // chain already exists. Flush (clear) it. + return ipt.run("-t", table, "-F", chain) + default: + return err + } +} + +// RenameChain renames the old chain to the new one. +func (ipt *IPTables) RenameChain(table, oldChain, newChain string) error { + return ipt.run("-t", table, "-E", oldChain, newChain) +} + +// DeleteChain deletes the chain in the specified table. +// The chain must be empty +func (ipt *IPTables) DeleteChain(table, chain string) error { + return ipt.run("-t", table, "-X", chain) +} + +func (ipt *IPTables) ClearAndDeleteChain(table, chain string) error { + exists, err := ipt.ChainExists(table, chain) + if err != nil || !exists { + return err + } + err = ipt.run("-t", table, "-F", chain) + if err == nil { + err = ipt.run("-t", table, "-X", chain) + } + return err +} + +func (ipt *IPTables) ClearAll() error { + return ipt.run("-F") +} + +func (ipt *IPTables) DeleteAll() error { + return ipt.run("-X") +} + +// ChangePolicy changes policy on chain to target +func (ipt *IPTables) ChangePolicy(table, chain, target string) error { + return ipt.run("-t", table, "-P", chain, target) +} + +// Check if the underlying iptables command supports the --random-fully flag +func (ipt *IPTables) HasRandomFully() bool { + return ipt.hasRandomFully +} + +// Return version components of the underlying iptables command +func (ipt *IPTables) GetIptablesVersion() (int, int, int) { + return ipt.v1, ipt.v2, ipt.v3 +} + +// run runs an iptables command with the given arguments, ignoring +// any stdout output +func (ipt *IPTables) run(args ...string) error { + return ipt.runWithOutput(args, nil) +} + +// runWithOutput runs an iptables command with the given arguments, +// writing any stdout output to the given writer +func (ipt *IPTables) runWithOutput(args []string, stdout io.Writer) error { + args = append([]string{ipt.path}, args...) + if ipt.hasWait { + args = append(args, "--wait") + if ipt.timeout != 0 && ipt.waitSupportSecond { + args = append(args, strconv.Itoa(ipt.timeout)) + } + } else { + fmu, err := newXtablesFileLock() + if err != nil { + return err + } + ul, err := fmu.tryLock() + if err != nil { + syscall.Close(fmu.fd) + return err + } + defer ul.Unlock() + } + + var stderr bytes.Buffer + cmd := exec.Cmd{ + Path: ipt.path, + Args: args, + Stdout: stdout, + Stderr: &stderr, + } + + if err := cmd.Run(); err != nil { + switch e := err.(type) { + case *exec.ExitError: + return &Error{*e, cmd, stderr.String(), nil} + default: + return err + } + } + + return nil +} + +// getIptablesCommand returns the correct command for the given protocol, either "iptables" or "ip6tables". +func getIptablesCommand(proto Protocol) string { + if proto == ProtocolIPv6 { + return "ip6tables" + } else { + return "iptables" + } +} + +// Checks if iptables has the "-C" and "--wait" flag +func getIptablesCommandSupport(v1 int, v2 int, v3 int) (bool, bool, bool, bool) { + return iptablesHasCheckCommand(v1, v2, v3), iptablesHasWaitCommand(v1, v2, v3), iptablesWaitSupportSecond(v1, v2, v3), iptablesHasRandomFully(v1, v2, v3) +} + +// getIptablesVersion returns the first three components of the iptables version +// and the operating mode (e.g. nf_tables or legacy) +// e.g. "iptables v1.3.66" would return (1, 3, 66, legacy, nil) +func extractIptablesVersion(str string) (int, int, int, string, error) { + versionMatcher := regexp.MustCompile(`v([0-9]+)\.([0-9]+)\.([0-9]+)(?:\s+\((\w+))?`) + result := versionMatcher.FindStringSubmatch(str) + if result == nil { + return 0, 0, 0, "", fmt.Errorf("no iptables version found in string: %s", str) + } + + v1, err := strconv.Atoi(result[1]) + if err != nil { + return 0, 0, 0, "", err + } + + v2, err := strconv.Atoi(result[2]) + if err != nil { + return 0, 0, 0, "", err + } + + v3, err := strconv.Atoi(result[3]) + if err != nil { + return 0, 0, 0, "", err + } + + mode := "legacy" + if result[4] != "" { + mode = result[4] + } + return v1, v2, v3, mode, nil +} + +// Runs "iptables --version" to get the version string +func getIptablesVersionString(path string) (string, error) { + cmd := exec.Command(path, "--version") + var out bytes.Buffer + cmd.Stdout = &out + err := cmd.Run() + if err != nil { + return "", err + } + return out.String(), nil +} + +// Checks if an iptables version is after 1.4.11, when --check was added +func iptablesHasCheckCommand(v1 int, v2 int, v3 int) bool { + if v1 > 1 { + return true + } + if v1 == 1 && v2 > 4 { + return true + } + if v1 == 1 && v2 == 4 && v3 >= 11 { + return true + } + return false +} + +// Checks if an iptables version is after 1.4.20, when --wait was added +func iptablesHasWaitCommand(v1 int, v2 int, v3 int) bool { + if v1 > 1 { + return true + } + if v1 == 1 && v2 > 4 { + return true + } + if v1 == 1 && v2 == 4 && v3 >= 20 { + return true + } + return false +} + +//Checks if an iptablse version is after 1.6.0, when --wait support second +func iptablesWaitSupportSecond(v1 int, v2 int, v3 int) bool { + if v1 > 1 { + return true + } + if v1 == 1 && v2 >= 6 { + return true + } + return false +} + +// Checks if an iptables version is after 1.6.2, when --random-fully was added +func iptablesHasRandomFully(v1 int, v2 int, v3 int) bool { + if v1 > 1 { + return true + } + if v1 == 1 && v2 > 6 { + return true + } + if v1 == 1 && v2 == 6 && v3 >= 2 { + return true + } + return false +} + +// Checks if a rule specification exists for a table +func (ipt *IPTables) existsForOldIptables(table, chain string, rulespec []string) (bool, error) { + rs := strings.Join(append([]string{"-A", chain}, rulespec...), " ") + args := []string{"-t", table, "-S"} + var stdout bytes.Buffer + err := ipt.runWithOutput(args, &stdout) + if err != nil { + return false, err + } + return strings.Contains(stdout.String(), rs), nil +} + +// counterRegex is the regex used to detect nftables counter format +var counterRegex = regexp.MustCompile(`^\[([0-9]+):([0-9]+)\] `) + +// filterRuleOutput works around some inconsistencies in output. +// For example, when iptables is in legacy vs. nftables mode, it produces +// different results. +func filterRuleOutput(rule string) string { + out := rule + + // work around an output difference in nftables mode where counters + // are output in iptables-save format, rather than iptables -S format + // The string begins with "[0:0]" + // + // Fixes #49 + if groups := counterRegex.FindStringSubmatch(out); groups != nil { + // drop the brackets + out = out[len(groups[0]):] + out = fmt.Sprintf("%s -c %s %s", out, groups[1], groups[2]) + } + + return out +} diff --git a/vendor/github.com/coreos/go-iptables/iptables/lock.go b/vendor/github.com/coreos/go-iptables/iptables/lock.go new file mode 100644 index 000000000..a88e92b4e --- /dev/null +++ b/vendor/github.com/coreos/go-iptables/iptables/lock.go @@ -0,0 +1,84 @@ +// Copyright 2015 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package iptables + +import ( + "os" + "sync" + "syscall" +) + +const ( + // In earlier versions of iptables, the xtables lock was implemented + // via a Unix socket, but now flock is used via this lockfile: + // http://git.netfilter.org/iptables/commit/?id=aa562a660d1555b13cffbac1e744033e91f82707 + // Note the LSB-conforming "/run" directory does not exist on old + // distributions, so assume "/var" is symlinked + xtablesLockFilePath = "/var/run/xtables.lock" + + defaultFilePerm = 0600 +) + +type Unlocker interface { + Unlock() error +} + +type nopUnlocker struct{} + +func (_ nopUnlocker) Unlock() error { return nil } + +type fileLock struct { + // mu is used to protect against concurrent invocations from within this process + mu sync.Mutex + fd int +} + +// tryLock takes an exclusive lock on the xtables lock file without blocking. +// This is best-effort only: if the exclusive lock would block (i.e. because +// another process already holds it), no error is returned. Otherwise, any +// error encountered during the locking operation is returned. +// The returned Unlocker should be used to release the lock when the caller is +// done invoking iptables commands. +func (l *fileLock) tryLock() (Unlocker, error) { + l.mu.Lock() + err := syscall.Flock(l.fd, syscall.LOCK_EX|syscall.LOCK_NB) + switch err { + case syscall.EWOULDBLOCK: + l.mu.Unlock() + return nopUnlocker{}, nil + case nil: + return l, nil + default: + l.mu.Unlock() + return nil, err + } +} + +// Unlock closes the underlying file, which implicitly unlocks it as well. It +// also unlocks the associated mutex. +func (l *fileLock) Unlock() error { + defer l.mu.Unlock() + return syscall.Close(l.fd) +} + +// newXtablesFileLock opens a new lock on the xtables lockfile without +// acquiring the lock +func newXtablesFileLock() (*fileLock, error) { + fd, err := syscall.Open(xtablesLockFilePath, os.O_CREATE, defaultFilePerm) + if err != nil { + return nil, err + } + return &fileLock{fd: fd}, nil +} diff --git a/vendor/github.com/safchain/ethtool/.gitignore b/vendor/github.com/safchain/ethtool/.gitignore new file mode 100644 index 000000000..db6cadffd --- /dev/null +++ b/vendor/github.com/safchain/ethtool/.gitignore @@ -0,0 +1,27 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +# Skip compiled example binary file +/example/example diff --git a/vendor/github.com/safchain/ethtool/.travis.yml b/vendor/github.com/safchain/ethtool/.travis.yml new file mode 100644 index 000000000..4f2ee4d97 --- /dev/null +++ b/vendor/github.com/safchain/ethtool/.travis.yml @@ -0,0 +1 @@ +language: go diff --git a/vendor/github.com/safchain/ethtool/LICENSE b/vendor/github.com/safchain/ethtool/LICENSE new file mode 100644 index 000000000..8f71f43fe --- /dev/null +++ b/vendor/github.com/safchain/ethtool/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/vendor/github.com/safchain/ethtool/Makefile b/vendor/github.com/safchain/ethtool/Makefile new file mode 100644 index 000000000..67d2da395 --- /dev/null +++ b/vendor/github.com/safchain/ethtool/Makefile @@ -0,0 +1,4 @@ +all: build + +build: + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build diff --git a/vendor/github.com/safchain/ethtool/README.md b/vendor/github.com/safchain/ethtool/README.md new file mode 100644 index 000000000..1f146229c --- /dev/null +++ b/vendor/github.com/safchain/ethtool/README.md @@ -0,0 +1,60 @@ +# ethtool go package # + +[![Build Status](https://travis-ci.org/safchain/ethtool.png?branch=master)](https://travis-ci.org/safchain/ethtool) +[![GoDoc](https://godoc.org/github.com/safchain/ethtool?status.svg)](https://godoc.org/github.com/safchain/ethtool) + +The ethtool package aims to provide a library giving a simple access to the Linux SIOCETHTOOL ioctl operations. It can be used to retrieve informations from a network device like statistics, driver related informations or even the peer of a VETH interface. + +## Build and Test ## + +go get command: + + go get github.com/safchain/ethtool + +Testing + +In order to run te + + go test github.com/safchain/ethtool + +## Examples ## + +```go +package main + +import ( + "fmt" + + "github.com/safchain/ethtool" +) + +func main() { + ethHandle, err := ethtool.NewEthtool() + if err != nil { + panic(err.Error()) + } + defer ethHandle.Close() + + // Retrieve tx from eth0 + stats, err := ethHandle.Stats("eth0") + if err != nil { + panic(err.Error()) + } + fmt.Printf("TX: %d\n", stats["tx_bytes"]) + + // Retrieve peer index of a veth interface + stats, err = ethHandle.Stats("veth0") + if err != nil { + panic(err.Error()) + } + fmt.Printf("Peer Index: %d\n", stats["peer_ifindex"]) +} +``` + +## LICENSE ## + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/vendor/github.com/safchain/ethtool/ethtool.go b/vendor/github.com/safchain/ethtool/ethtool.go new file mode 100644 index 000000000..8dcc78c05 --- /dev/null +++ b/vendor/github.com/safchain/ethtool/ethtool.go @@ -0,0 +1,541 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +// Package ethtool aims to provide a library giving a simple access to the +// Linux SIOCETHTOOL ioctl operations. It can be used to retrieve informations +// from a network device like statistics, driver related informations or +// even the peer of a VETH interface. +package ethtool + +import ( + "bytes" + "encoding/hex" + "fmt" + "strings" + "syscall" + "unsafe" +) + +// Maximum size of an interface name +const ( + IFNAMSIZ = 16 +) + +// ioctl ethtool request +const ( + SIOCETHTOOL = 0x8946 +) + +// ethtool stats related constants. +const ( + ETH_GSTRING_LEN = 32 + ETH_SS_STATS = 1 + ETH_SS_FEATURES = 4 + ETHTOOL_GDRVINFO = 0x00000003 + ETHTOOL_GSTRINGS = 0x0000001b + ETHTOOL_GSTATS = 0x0000001d + // other CMDs from ethtool-copy.h of ethtool-3.5 package + ETHTOOL_GSET = 0x00000001 /* Get settings. */ + ETHTOOL_SSET = 0x00000002 /* Set settings. */ + ETHTOOL_GMSGLVL = 0x00000007 /* Get driver message level */ + ETHTOOL_SMSGLVL = 0x00000008 /* Set driver msg level. */ + /* Get link status for host, i.e. whether the interface *and* the + * physical port (if there is one) are up (ethtool_value). */ + ETHTOOL_GLINK = 0x0000000a + ETHTOOL_GMODULEINFO = 0x00000042 /* Get plug-in module information */ + ETHTOOL_GMODULEEEPROM = 0x00000043 /* Get plug-in module eeprom */ + ETHTOOL_GPERMADDR = 0x00000020 + ETHTOOL_GFEATURES = 0x0000003a /* Get device offload settings */ + ETHTOOL_SFEATURES = 0x0000003b /* Change device offload settings */ + ETHTOOL_GFLAGS = 0x00000025 /* Get flags bitmap(ethtool_value) */ + ETHTOOL_GSSET_INFO = 0x00000037 /* Get string set info */ +) + +// MAX_GSTRINGS maximum number of stats entries that ethtool can +// retrieve currently. +const ( + MAX_GSTRINGS = 1000 + MAX_FEATURE_BLOCKS = (MAX_GSTRINGS + 32 - 1) / 32 + EEPROM_LEN = 640 + PERMADDR_LEN = 32 +) + +type ifreq struct { + ifr_name [IFNAMSIZ]byte + ifr_data uintptr +} + +// following structures comes from uapi/linux/ethtool.h +type ethtoolSsetInfo struct { + cmd uint32 + reserved uint32 + sset_mask uint32 + data uintptr +} + +type ethtoolGetFeaturesBlock struct { + available uint32 + requested uint32 + active uint32 + never_changed uint32 +} + +type ethtoolGfeatures struct { + cmd uint32 + size uint32 + blocks [MAX_FEATURE_BLOCKS]ethtoolGetFeaturesBlock +} + +type ethtoolSetFeaturesBlock struct { + valid uint32 + requested uint32 +} + +type ethtoolSfeatures struct { + cmd uint32 + size uint32 + blocks [MAX_FEATURE_BLOCKS]ethtoolSetFeaturesBlock +} + +type ethtoolDrvInfo struct { + cmd uint32 + driver [32]byte + version [32]byte + fw_version [32]byte + bus_info [32]byte + erom_version [32]byte + reserved2 [12]byte + n_priv_flags uint32 + n_stats uint32 + testinfo_len uint32 + eedump_len uint32 + regdump_len uint32 +} + +type ethtoolGStrings struct { + cmd uint32 + string_set uint32 + len uint32 + data [MAX_GSTRINGS * ETH_GSTRING_LEN]byte +} + +type ethtoolStats struct { + cmd uint32 + n_stats uint32 + data [MAX_GSTRINGS]uint64 +} + +type ethtoolEeprom struct { + cmd uint32 + magic uint32 + offset uint32 + len uint32 + data [EEPROM_LEN]byte +} + +type ethtoolModInfo struct { + cmd uint32 + tpe uint32 + eeprom_len uint32 + reserved [8]uint32 +} + +type ethtoolLink struct { + cmd uint32 + data uint32 +} + +type ethtoolPermAddr struct { + cmd uint32 + size uint32 + data [PERMADDR_LEN]byte +} + +type Ethtool struct { + fd int +} + +// DriverName returns the driver name of the given interface name. +func (e *Ethtool) DriverName(intf string) (string, error) { + info, err := e.getDriverInfo(intf) + if err != nil { + return "", err + } + return string(bytes.Trim(info.driver[:], "\x00")), nil +} + +// BusInfo returns the bus information of the given interface name. +func (e *Ethtool) BusInfo(intf string) (string, error) { + info, err := e.getDriverInfo(intf) + if err != nil { + return "", err + } + return string(bytes.Trim(info.bus_info[:], "\x00")), nil +} + +// ModuleEeprom returns Eeprom information of the given interface name. +func (e *Ethtool) ModuleEeprom(intf string) ([]byte, error) { + eeprom, _, err := e.getModuleEeprom(intf) + if err != nil { + return nil, err + } + + return eeprom.data[:eeprom.len], nil +} + +// ModuleEeprom returns Eeprom information of the given interface name. +func (e *Ethtool) ModuleEepromHex(intf string) (string, error) { + eeprom, _, err := e.getModuleEeprom(intf) + if err != nil { + return "", err + } + + return hex.EncodeToString(eeprom.data[:eeprom.len]), nil +} + +// DriverInfo returns driver information of the given interface name. +func (e *Ethtool) DriverInfo(intf string) (ethtoolDrvInfo, error) { + drvInfo, err := e.getDriverInfo(intf) + if err != nil { + return ethtoolDrvInfo{}, err + } + + return drvInfo, nil +} + +// PermAddr returns permanent address of the given interface name. +func (e *Ethtool) PermAddr(intf string) (string, error) { + permAddr, err := e.getPermAddr(intf) + if err != nil { + return "", err + } + + if permAddr.data[0] == 0 && permAddr.data[1] == 0 && + permAddr.data[2] == 0 && permAddr.data[3] == 0 && + permAddr.data[4] == 0 && permAddr.data[5] == 0 { + return "", nil + } + + return fmt.Sprintf("%x:%x:%x:%x:%x:%x", + permAddr.data[0:1], + permAddr.data[1:2], + permAddr.data[2:3], + permAddr.data[3:4], + permAddr.data[4:5], + permAddr.data[5:6], + ), nil +} + +func (e *Ethtool) ioctl(intf string, data uintptr) error { + var name [IFNAMSIZ]byte + copy(name[:], []byte(intf)) + + ifr := ifreq{ + ifr_name: name, + ifr_data: data, + } + + _, _, ep := syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr))) + if ep != 0 { + return syscall.Errno(ep) + } + + return nil +} + +func (e *Ethtool) getDriverInfo(intf string) (ethtoolDrvInfo, error) { + drvinfo := ethtoolDrvInfo{ + cmd: ETHTOOL_GDRVINFO, + } + + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&drvinfo))); err != nil { + return ethtoolDrvInfo{}, err + } + + return drvinfo, nil +} + +func (e *Ethtool) getPermAddr(intf string) (ethtoolPermAddr, error) { + permAddr := ethtoolPermAddr{ + cmd: ETHTOOL_GPERMADDR, + size: PERMADDR_LEN, + } + + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&permAddr))); err != nil { + return ethtoolPermAddr{}, err + } + + return permAddr, nil +} + +func (e *Ethtool) getModuleEeprom(intf string) (ethtoolEeprom, ethtoolModInfo, error) { + modInfo := ethtoolModInfo{ + cmd: ETHTOOL_GMODULEINFO, + } + + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&modInfo))); err != nil { + return ethtoolEeprom{}, ethtoolModInfo{}, err + } + + eeprom := ethtoolEeprom{ + cmd: ETHTOOL_GMODULEEEPROM, + len: modInfo.eeprom_len, + offset: 0, + } + + if modInfo.eeprom_len > EEPROM_LEN { + return ethtoolEeprom{}, ethtoolModInfo{}, fmt.Errorf("eeprom size: %d is larger than buffer size: %d", modInfo.eeprom_len, EEPROM_LEN) + } + + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&eeprom))); err != nil { + return ethtoolEeprom{}, ethtoolModInfo{}, err + } + + return eeprom, modInfo, nil +} + +func isFeatureBitSet(blocks [MAX_FEATURE_BLOCKS]ethtoolGetFeaturesBlock, index uint) bool { + return (blocks)[index/32].active&(1<<(index%32)) != 0 +} + +func setFeatureBit(blocks *[MAX_FEATURE_BLOCKS]ethtoolSetFeaturesBlock, index uint, value bool) { + blockIndex, bitIndex := index/32, index%32 + + blocks[blockIndex].valid |= 1 << bitIndex + + if value { + blocks[blockIndex].requested |= 1 << bitIndex + } else { + blocks[blockIndex].requested &= ^(1 << bitIndex) + } +} + +// FeatureNames shows supported features by their name. +func (e *Ethtool) FeatureNames(intf string) (map[string]uint, error) { + ssetInfo := ethtoolSsetInfo{ + cmd: ETHTOOL_GSSET_INFO, + sset_mask: 1 << ETH_SS_FEATURES, + } + + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&ssetInfo))); err != nil { + return nil, err + } + + length := uint32(ssetInfo.data) + if length == 0 { + return map[string]uint{}, nil + } else if length > MAX_GSTRINGS { + return nil, fmt.Errorf("ethtool currently doesn't support more than %d entries, received %d", MAX_GSTRINGS, length) + } + + gstrings := ethtoolGStrings{ + cmd: ETHTOOL_GSTRINGS, + string_set: ETH_SS_FEATURES, + len: length, + data: [MAX_GSTRINGS * ETH_GSTRING_LEN]byte{}, + } + + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&gstrings))); err != nil { + return nil, err + } + + var result = make(map[string]uint) + for i := 0; i != int(length); i++ { + b := gstrings.data[i*ETH_GSTRING_LEN : i*ETH_GSTRING_LEN+ETH_GSTRING_LEN] + key := string(bytes.Trim(b, "\x00")) + if key != "" { + result[key] = uint(i) + } + } + + return result, nil +} + +// Features retrieves features of the given interface name. +func (e *Ethtool) Features(intf string) (map[string]bool, error) { + names, err := e.FeatureNames(intf) + if err != nil { + return nil, err + } + + length := uint32(len(names)) + if length == 0 { + return map[string]bool{}, nil + } + + features := ethtoolGfeatures{ + cmd: ETHTOOL_GFEATURES, + size: (length + 32 - 1) / 32, + } + + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&features))); err != nil { + return nil, err + } + + var result = make(map[string]bool, length) + for key, index := range names { + result[key] = isFeatureBitSet(features.blocks, index) + } + + return result, nil +} + +// Change requests a change in the given device's features. +func (e *Ethtool) Change(intf string, config map[string]bool) error { + names, err := e.FeatureNames(intf) + if err != nil { + return err + } + + length := uint32(len(names)) + + features := ethtoolSfeatures{ + cmd: ETHTOOL_SFEATURES, + size: (length + 32 - 1) / 32, + } + + for key, value := range config { + if index, ok := names[key]; ok { + setFeatureBit(&features.blocks, index, value) + } else { + return fmt.Errorf("unsupported feature %q", key) + } + } + + return e.ioctl(intf, uintptr(unsafe.Pointer(&features))) +} + +// Get state of a link. +func (e *Ethtool) LinkState(intf string) (uint32, error) { + x := ethtoolLink{ + cmd: ETHTOOL_GLINK, + } + + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&x))); err != nil { + return 0, err + } + + return x.data, nil +} + +// Stats retrieves stats of the given interface name. +func (e *Ethtool) Stats(intf string) (map[string]uint64, error) { + drvinfo := ethtoolDrvInfo{ + cmd: ETHTOOL_GDRVINFO, + } + + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&drvinfo))); err != nil { + return nil, err + } + + if drvinfo.n_stats*ETH_GSTRING_LEN > MAX_GSTRINGS*ETH_GSTRING_LEN { + return nil, fmt.Errorf("ethtool currently doesn't support more than %d entries, received %d", MAX_GSTRINGS, drvinfo.n_stats) + } + + gstrings := ethtoolGStrings{ + cmd: ETHTOOL_GSTRINGS, + string_set: ETH_SS_STATS, + len: drvinfo.n_stats, + data: [MAX_GSTRINGS * ETH_GSTRING_LEN]byte{}, + } + + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&gstrings))); err != nil { + return nil, err + } + + stats := ethtoolStats{ + cmd: ETHTOOL_GSTATS, + n_stats: drvinfo.n_stats, + data: [MAX_GSTRINGS]uint64{}, + } + + if err := e.ioctl(intf, uintptr(unsafe.Pointer(&stats))); err != nil { + return nil, err + } + + var result = make(map[string]uint64) + for i := 0; i != int(drvinfo.n_stats); i++ { + b := gstrings.data[i*ETH_GSTRING_LEN : i*ETH_GSTRING_LEN+ETH_GSTRING_LEN] + key := string(b[:strings.Index(string(b), "\x00")]) + if len(key) != 0 { + result[key] = stats.data[i] + } + } + + return result, nil +} + +// Close closes the ethool handler +func (e *Ethtool) Close() { + syscall.Close(e.fd) +} + +// NewEthtool returns a new ethtool handler +func NewEthtool() (*Ethtool, error) { + fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_IP) + if err != nil { + return nil, err + } + + return &Ethtool{ + fd: int(fd), + }, nil +} + +// BusInfo returns bus information of the given interface name. +func BusInfo(intf string) (string, error) { + e, err := NewEthtool() + if err != nil { + return "", err + } + defer e.Close() + return e.BusInfo(intf) +} + +// DriverName returns the driver name of the given interface name. +func DriverName(intf string) (string, error) { + e, err := NewEthtool() + if err != nil { + return "", err + } + defer e.Close() + return e.DriverName(intf) +} + +// Stats retrieves stats of the given interface name. +func Stats(intf string) (map[string]uint64, error) { + e, err := NewEthtool() + if err != nil { + return nil, err + } + defer e.Close() + return e.Stats(intf) +} + +// PermAddr returns permanent address of the given interface name. +func PermAddr(intf string) (string, error) { + e, err := NewEthtool() + if err != nil { + return "", err + } + defer e.Close() + return e.PermAddr(intf) +} diff --git a/vendor/github.com/safchain/ethtool/ethtool_cmd.go b/vendor/github.com/safchain/ethtool/ethtool_cmd.go new file mode 100644 index 000000000..d0c35e476 --- /dev/null +++ b/vendor/github.com/safchain/ethtool/ethtool_cmd.go @@ -0,0 +1,207 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +// Package ethtool aims to provide a library giving a simple access to the +// Linux SIOCETHTOOL ioctl operations. It can be used to retrieve informations +// from a network device like statistics, driver related informations or +// even the peer of a VETH interface. +package ethtool + +import ( + "math" + "reflect" + "syscall" + "unsafe" +) + +type EthtoolCmd struct { /* ethtool.c: struct ethtool_cmd */ + Cmd uint32 + Supported uint32 + Advertising uint32 + Speed uint16 + Duplex uint8 + Port uint8 + Phy_address uint8 + Transceiver uint8 + Autoneg uint8 + Mdio_support uint8 + Maxtxpkt uint32 + Maxrxpkt uint32 + Speed_hi uint16 + Eth_tp_mdix uint8 + Reserved2 uint8 + Lp_advertising uint32 + Reserved [2]uint32 +} + +// CmdGet returns the interface settings in the receiver struct +// and returns speed +func (ecmd *EthtoolCmd) CmdGet(intf string) (uint32, error) { + e, err := NewEthtool() + if err != nil { + return 0, err + } + defer e.Close() + return e.CmdGet(ecmd, intf) +} + +// CmdSet sets and returns the settings in the receiver struct +// and returns speed +func (ecmd *EthtoolCmd) CmdSet(intf string) (uint32, error) { + e, err := NewEthtool() + if err != nil { + return 0, err + } + defer e.Close() + return e.CmdSet(ecmd, intf) +} + +func (f *EthtoolCmd) reflect(retv *map[string]uint64) { + val := reflect.ValueOf(f).Elem() + + for i := 0; i < val.NumField(); i++ { + valueField := val.Field(i) + typeField := val.Type().Field(i) + + t := valueField.Interface() + //tt := reflect.TypeOf(t) + //fmt.Printf(" t %T %v tt %T %v\n", t, t, tt, tt) + switch t.(type) { + case uint32: + //fmt.Printf(" t is uint32\n") + (*retv)[typeField.Name] = uint64(t.(uint32)) + case uint16: + (*retv)[typeField.Name] = uint64(t.(uint16)) + case uint8: + (*retv)[typeField.Name] = uint64(t.(uint8)) + case int32: + (*retv)[typeField.Name] = uint64(t.(int32)) + case int16: + (*retv)[typeField.Name] = uint64(t.(int16)) + case int8: + (*retv)[typeField.Name] = uint64(t.(int8)) + default: + (*retv)[typeField.Name+"_unknown_type"] = 0 + } + + //tag := typeField.Tag + //fmt.Printf("Field Name: %s,\t Field Value: %v,\t Tag Value: %s\n", + // typeField.Name, valueField.Interface(), tag.Get("tag_name")) + } +} + +// CmdGet returns the interface settings in the receiver struct +// and returns speed +func (e *Ethtool) CmdGet(ecmd *EthtoolCmd, intf string) (uint32, error) { + ecmd.Cmd = ETHTOOL_GSET + + var name [IFNAMSIZ]byte + copy(name[:], []byte(intf)) + + ifr := ifreq{ + ifr_name: name, + ifr_data: uintptr(unsafe.Pointer(ecmd)), + } + + _, _, ep := syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), + SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr))) + if ep != 0 { + return 0, syscall.Errno(ep) + } + + var speedval uint32 = (uint32(ecmd.Speed_hi) << 16) | + (uint32(ecmd.Speed) & 0xffff) + if speedval == math.MaxUint16 { + speedval = math.MaxUint32 + } + + return speedval, nil +} + +// CmdSet sets and returns the settings in the receiver struct +// and returns speed +func (e *Ethtool) CmdSet(ecmd *EthtoolCmd, intf string) (uint32, error) { + ecmd.Cmd = ETHTOOL_SSET + + var name [IFNAMSIZ]byte + copy(name[:], []byte(intf)) + + ifr := ifreq{ + ifr_name: name, + ifr_data: uintptr(unsafe.Pointer(ecmd)), + } + + _, _, ep := syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), + SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr))) + if ep != 0 { + return 0, syscall.Errno(ep) + } + + var speedval uint32 = (uint32(ecmd.Speed_hi) << 16) | + (uint32(ecmd.Speed) & 0xffff) + if speedval == math.MaxUint16 { + speedval = math.MaxUint32 + } + + return speedval, nil +} + +// CmdGetMapped returns the interface settings in a map +func (e *Ethtool) CmdGetMapped(intf string) (map[string]uint64, error) { + ecmd := EthtoolCmd{ + Cmd: ETHTOOL_GSET, + } + + var name [IFNAMSIZ]byte + copy(name[:], []byte(intf)) + + ifr := ifreq{ + ifr_name: name, + ifr_data: uintptr(unsafe.Pointer(&ecmd)), + } + + _, _, ep := syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), + SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr))) + if ep != 0 { + return nil, syscall.Errno(ep) + } + + var result = make(map[string]uint64) + + // ref https://gist.github.com/drewolson/4771479 + // Golang Reflection Example + ecmd.reflect(&result) + + var speedval uint32 = (uint32(ecmd.Speed_hi) << 16) | + (uint32(ecmd.Speed) & 0xffff) + result["speed"] = uint64(speedval) + + return result, nil +} + +func CmdGetMapped(intf string) (map[string]uint64, error) { + e, err := NewEthtool() + if err != nil { + return nil, err + } + defer e.Close() + return e.CmdGetMapped(intf) +} diff --git a/vendor/github.com/safchain/ethtool/ethtool_msglvl.go b/vendor/github.com/safchain/ethtool/ethtool_msglvl.go new file mode 100644 index 000000000..91836f019 --- /dev/null +++ b/vendor/github.com/safchain/ethtool/ethtool_msglvl.go @@ -0,0 +1,113 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +// Package ethtool aims to provide a library giving a simple access to the +// Linux SIOCETHTOOL ioctl operations. It can be used to retrieve informations +// from a network device like statistics, driver related informations or +// even the peer of a VETH interface. +package ethtool + +import ( + "syscall" + "unsafe" +) + +type ethtoolValue struct { /* ethtool.c: struct ethtool_value */ + cmd uint32 + data uint32 +} + +// MsglvlGet returns the msglvl of the given interface. +func (e *Ethtool) MsglvlGet(intf string) (uint32, error) { + edata := ethtoolValue{ + cmd: ETHTOOL_GMSGLVL, + } + + var name [IFNAMSIZ]byte + copy(name[:], []byte(intf)) + + ifr := ifreq{ + ifr_name: name, + ifr_data: uintptr(unsafe.Pointer(&edata)), + } + + _, _, ep := syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), + SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr))) + if ep != 0 { + return 0, syscall.Errno(ep) + } + + return edata.data, nil +} + +// MsglvlSet returns the read-msglvl, post-set-msglvl of the given interface. +func (e *Ethtool) MsglvlSet(intf string, valset uint32) (uint32, uint32, error) { + edata := ethtoolValue{ + cmd: ETHTOOL_GMSGLVL, + } + + var name [IFNAMSIZ]byte + copy(name[:], []byte(intf)) + + ifr := ifreq{ + ifr_name: name, + ifr_data: uintptr(unsafe.Pointer(&edata)), + } + + _, _, ep := syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), + SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr))) + if ep != 0 { + return 0, 0, syscall.Errno(ep) + } + + readval := edata.data + + edata.cmd = ETHTOOL_SMSGLVL + edata.data = valset + + _, _, ep = syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), + SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr))) + if ep != 0 { + return 0, 0, syscall.Errno(ep) + } + + return readval, edata.data, nil +} + +// MsglvlGet returns the msglvl of the given interface. +func MsglvlGet(intf string) (uint32, error) { + e, err := NewEthtool() + if err != nil { + return 0, err + } + defer e.Close() + return e.MsglvlGet(intf) +} + +// MsglvlSet returns the read-msglvl, post-set-msglvl of the given interface. +func MsglvlSet(intf string, valset uint32) (uint32, uint32, error) { + e, err := NewEthtool() + if err != nil { + return 0, 0, err + } + defer e.Close() + return e.MsglvlSet(intf, valset) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 310366c01..8080443e4 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -137,20 +137,23 @@ github.com/containerd/containerd/version # github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41 ## explicit; go 1.11 github.com/containerd/continuity/pathdriver -# github.com/containernetworking/cni v1.0.0 -## explicit; go 1.14 +# github.com/containernetworking/cni v0.8.0 +## explicit github.com/containernetworking/cni/pkg/skel github.com/containernetworking/cni/pkg/types github.com/containernetworking/cni/pkg/types/020 -github.com/containernetworking/cni/pkg/types/040 -github.com/containernetworking/cni/pkg/types/100 -github.com/containernetworking/cni/pkg/types/create -github.com/containernetworking/cni/pkg/types/internal +github.com/containernetworking/cni/pkg/types/current github.com/containernetworking/cni/pkg/utils github.com/containernetworking/cni/pkg/version # github.com/containernetworking/plugins v0.8.7 ## explicit; go 1.14 +github.com/containernetworking/plugins/pkg/ip github.com/containernetworking/plugins/pkg/ns +github.com/containernetworking/plugins/pkg/utils/hwaddr +github.com/containernetworking/plugins/pkg/utils/sysctl +# github.com/coreos/go-iptables v0.6.0 +## explicit; go 1.16 +github.com/coreos/go-iptables/iptables # github.com/coreos/go-semver v0.3.0 ## explicit github.com/coreos/go-semver/semver @@ -745,6 +748,9 @@ github.com/rubenv/sql-migrate/sqlparse # github.com/russross/blackfriday v1.5.2 ## explicit github.com/russross/blackfriday +# github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 +## explicit +github.com/safchain/ethtool # github.com/satori/go.uuid v1.2.0 ## explicit github.com/satori/go.uuid