Compare commits

..

230 Commits

Author SHA1 Message Date
1fb5bf669e Merge pull request #982 from jingyuanliang/patch-1
Bump to golang:1.21-alpine in release.sh
2023-12-04 10:37:33 +01:00
3712c1cfcb Merge pull request #988 from containernetworking/dependabot/go_modules/golang-2d6cee5bad
build(deps): bump the golang group with 2 updates
2023-12-04 10:36:51 +01:00
825421709e build(deps): bump the golang group with 2 updates
Bumps the golang group with 2 updates: [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) and [golang.org/x/sys](https://github.com/golang/sys).


Updates `github.com/onsi/ginkgo/v2` from 2.13.1 to 2.13.2
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.13.1...v2.13.2)

Updates `golang.org/x/sys` from 0.14.0 to 0.15.0
- [Commits](https://github.com/golang/sys/compare/v0.14.0...v0.15.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: golang
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: golang
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-04 02:47:46 +00:00
d708217503 Bump to golang:1.21-alpine in release.sh
Also make this overridable in env vars.

Signed-off-by: Jingyuan Liang <jingyuanliang@google.com>
2023-11-17 09:01:30 +00:00
abee8ccc0d Merge pull request #954 from cyclinder/improve_cmd_del
macvlan cmdDel: replace the loadConf function with json.unmarshal
2023-11-16 19:06:11 +01:00
e1474463ef Merge pull request #978 from containernetworking/dependabot/go_modules/golang-439c5fc513
build(deps): bump the golang group with 3 updates
2023-11-16 19:05:16 +01:00
11ee4b61d9 Merge pull request #969 from s1061123/fix-netns-override
Add CNI_NETNS_OVERRIDE for upcoming CNI change
2023-11-16 19:04:53 +01:00
a4cbf13a9b Add CNI_NETNS_OVERRIDE for upcoming CNI change
containernetwork/cni#890 introduces CNI_NETNS_OVERRIDE and plugin
testing requires this flag. This change enables CNI_NETNS_OVERRIDE
for further containernetwork/cni vendor update.

Signed-off-by: Tomofumi Hayashi <tohayash@redhat.com>
2023-11-16 23:18:00 +09:00
6cac5d603b build(deps): bump the golang group with 3 updates
Bumps the golang group with 3 updates: [github.com/Microsoft/hcsshim](https://github.com/Microsoft/hcsshim), [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) and [github.com/onsi/gomega](https://github.com/onsi/gomega).


Updates `github.com/Microsoft/hcsshim` from 0.11.2 to 0.11.4
- [Release notes](https://github.com/Microsoft/hcsshim/releases)
- [Commits](https://github.com/Microsoft/hcsshim/compare/v0.11.2...v0.11.4)

Updates `github.com/onsi/ginkgo/v2` from 2.13.0 to 2.13.1
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.13.0...v2.13.1)

Updates `github.com/onsi/gomega` from 1.29.0 to 1.30.0
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.29.0...v1.30.0)

---
updated-dependencies:
- dependency-name: github.com/Microsoft/hcsshim
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: golang
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: golang
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: golang
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-16 12:41:14 +00:00
333fc9a0d7 Merge pull request #981 from arthur-zhang/dev-revert
revert some code in pr 962
2023-11-16 13:39:59 +01:00
f90ac41ae4 revert some code in pr 962
Signed-off-by: arthur-zhang <zhangya_no1@qq.com>
2023-11-14 10:04:18 +08:00
93a1b3d0e7 Merge pull request #979 from s1061123/fix/ndisc_ipvlan
Add ndisc_notify in ipvlan for ipv6 ndp
2023-11-14 00:27:01 +01:00
00406f9d1e Merge branch 'main' into fix/ndisc_ipvlan 2023-11-14 08:18:07 +09:00
e82848a9cb Merge pull request #962 from arthur-zhang/dev-pr-bridge
bridge: remove useless code
2023-11-13 18:01:02 +01:00
5280b4d582 bridge: fix spelling
Signed-off-by: arthur-zhang <zhangya_no1@qq.com>
2023-11-13 17:11:21 +01:00
495a2cbb0c bridge: remove useless firstV4Addr
Signed-off-by: arthur-zhang <zhangya_no1@qq.com>
2023-11-13 17:11:21 +01:00
8c59fc1eea bridge: remove useless check
gws.defaultRouteFound here is always false.

Signed-off-by: arthur-zhang <zhangya_no1@qq.com>
2023-11-13 17:11:21 +01:00
2eee7cef35 Merge pull request #974 from zshi-redhat/macvlan-ipv6-ndisc
macvlan: enable ipv6 ndisc_notify
2023-11-13 17:08:56 +01:00
1079e113fe Add ndisc_notify in ipvlan for ipv6 ndp
Signed-off-by: Tomofumi Hayashi <tohayash@redhat.com>
2023-11-14 01:07:59 +09:00
999ca15763 macvlan: enable ipv6 ndisc_notify
Signed-off-by: Zenghui Shi <zshi@redhat.com>
2023-11-07 19:43:50 +08:00
dad27e9f72 Merge pull request #963 from containernetworking/dependabot/go_modules/google.golang.org/grpc-1.56.3
build(deps): bump google.golang.org/grpc from 1.50.1 to 1.56.3
2023-11-01 10:21:47 +01:00
0b1a96ff30 build(deps): bump google.golang.org/grpc from 1.50.1 to 1.56.3
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.50.1 to 1.56.3.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.50.1...v1.56.3)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-31 19:09:22 +00:00
c8c26897ba Merge pull request #970 from containernetworking/dependabot/go_modules/golang-1eaf3aa819
build(deps): bump the golang group with 3 updates
2023-10-31 20:08:15 +01:00
28c5faee75 build(deps): bump the golang group with 3 updates
Bumps the golang group with 3 updates: [github.com/Microsoft/hcsshim](https://github.com/Microsoft/hcsshim), [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) and [github.com/onsi/gomega](https://github.com/onsi/gomega).


Updates `github.com/Microsoft/hcsshim` from 0.11.1 to 0.11.2
- [Release notes](https://github.com/Microsoft/hcsshim/releases)
- [Commits](https://github.com/Microsoft/hcsshim/compare/v0.11.1...v0.11.2)

Updates `github.com/onsi/ginkgo/v2` from 2.12.0 to 2.13.0
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.12.0...v2.13.0)

Updates `github.com/onsi/gomega` from 1.28.0 to 1.29.0
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.28.0...v1.29.0)

---
updated-dependencies:
- dependency-name: github.com/Microsoft/hcsshim
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: golang
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: golang
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: golang
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-31 15:06:19 +00:00
d0d9e36662 Merge pull request #961 from squeed/dependabot-batch
dependabot: batch updates
2023-10-31 16:04:45 +01:00
f7662a2435 Merge pull request #968 from mmorel-35/patch-1
fix workflow warnings
2023-10-31 11:47:42 +01:00
aacae5c053 dependabot: batch updates
Rather than endless rebases, just batch all go updates to once a week.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2023-10-31 11:46:29 +01:00
6b7876125d fix workflow warnings
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-10-31 09:07:15 +01:00
2f0faf6721 Merge pull request #967 from squeed/fix-lint
fix lint errors
2023-10-31 08:40:30 +01:00
37531cdaf5 fix lint errors
Fix a small ginkgo compare issue, and ignore dot imports.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2023-10-30 17:55:55 +01:00
a8d4e0a7dd Merge pull request #949 from containernetworking/dependabot/go_modules/github.com/onsi/gomega-1.28.0
build(deps): bump github.com/onsi/gomega from 1.27.8 to 1.28.0
2023-10-17 12:31:08 +02:00
845ef62b74 macvlan cmdDel: replace the loadConf function with json.unmarshal
When the master interface on the node has been deleted, and loadConf tries
to get the MTU, This causes cmdDel to return a linkNotFound error to the
runtime. The cmdDel only needs to unmarshal the netConf. No need to
get the MTU. So we just replaced the loadConf function with
json.unmarshal in cmdDel.

Signed-off-by: cyclinder <qifeng.guo@daocloud.io>
2023-10-17 10:26:18 +08:00
691186ca7f build(deps): bump github.com/onsi/gomega from 1.27.8 to 1.28.0
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.27.8 to 1.28.0.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.27.8...v1.28.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-16 15:44:53 +00:00
adaeedd6af Merge pull request #955 from containernetworking/dependabot/go_modules/golang.org/x/net-0.17.0
build(deps): bump golang.org/x/net from 0.10.0 to 0.17.0
2023-10-16 17:43:46 +02:00
19e5747a8c build(deps): bump golang.org/x/net from 0.10.0 to 0.17.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.10.0 to 0.17.0.
- [Commits](https://github.com/golang/net/compare/v0.10.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-16 15:26:26 +00:00
4cf3da4ae3 Merge pull request #948 from twz123/posix-sh
build: Use POSIX sh for shell scripts
2023-10-16 17:25:04 +02:00
c20da1521f Merge pull request #952 from containernetworking/dependabot/go_modules/golang.org/x/sys-0.13.0
build(deps): bump golang.org/x/sys from 0.10.0 to 0.13.0
2023-10-16 17:24:51 +02:00
b66b5dd85f Merge pull request #945 from containernetworking/dependabot/github_actions/actions/checkout-4
build(deps): bump actions/checkout from 3 to 4
2023-10-16 17:23:45 +02:00
e727ad6697 Merge pull request #946 from containernetworking/dependabot/go_modules/github.com/Microsoft/hcsshim-0.11.1
build(deps): bump github.com/Microsoft/hcsshim from 0.9.9 to 0.11.1
2023-10-16 17:23:25 +02:00
18172539d8 build(deps): bump github.com/Microsoft/hcsshim from 0.9.9 to 0.11.1
Bumps [github.com/Microsoft/hcsshim](https://github.com/Microsoft/hcsshim) from 0.9.9 to 0.11.1.
- [Release notes](https://github.com/Microsoft/hcsshim/releases)
- [Commits](https://github.com/Microsoft/hcsshim/compare/v0.9.9...v0.11.1)

---
updated-dependencies:
- dependency-name: github.com/Microsoft/hcsshim
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-16 15:11:01 +00:00
f20b8408a4 Merge pull request #937 from containernetworking/dependabot/go_modules/github.com/coreos/go-iptables-0.7.0
build(deps): bump github.com/coreos/go-iptables from 0.6.0 to 0.7.0
2023-10-16 17:10:05 +02:00
6ff8e5eb86 Merge pull request #950 from ricky-rav/OCPBUGS-16788
Create IPAM files with 0600 permissions
2023-10-12 09:13:05 -05:00
61fa963636 build(deps): bump golang.org/x/sys from 0.10.0 to 0.13.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.10.0 to 0.13.0.
- [Commits](https://github.com/golang/sys/compare/v0.10.0...v0.13.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-09 02:20:37 +00:00
33ccedc66f Create IPAM files with 0600 permissions
Conform to CIS Benchmarks "1.1.9 Ensure that the Container Network Interface file permissions are set to 600 or more restrictive"
https://www.tenable.com/audits/items/CIS_Kubernetes_v1.20_v1.0.1_Level_1_Master.audit:f1717a5dd65d498074dd41c4a639e47d

Signed-off-by: Riccardo Ravaioli <rravaiol@redhat.com>
2023-10-02 11:59:31 +02:00
853b82d19f build: Use POSIX sh for shell scripts
The scripts didn't really use any bash specific features. Convert
them to POSIX shell scripts, so that the plugins can be built without
requiring bash.

Signed-off-by: Tom Wieczorek <twieczorek@mirantis.com>
2023-09-29 16:57:19 +02:00
d216b0c39b build(deps): bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-11 02:11:46 +00:00
f95505231a Merge pull request #942 from mmorel-35/patch-1
ci(lint) extend timeout to 5 min
2023-09-04 17:18:17 +02:00
5f25a93a47 ci(lint) extend timeout to 5 min
This extends the timeout or golangci-lint execution to 5 min as it is only one minute now it fails for several PR.

Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-09-01 20:45:00 +00:00
7c11d48630 build(deps): bump github.com/coreos/go-iptables from 0.6.0 to 0.7.0
Bumps [github.com/coreos/go-iptables](https://github.com/coreos/go-iptables) from 0.6.0 to 0.7.0.
- [Release notes](https://github.com/coreos/go-iptables/releases)
- [Commits](https://github.com/coreos/go-iptables/compare/v0.6.0...v0.7.0)

---
updated-dependencies:
- dependency-name: github.com/coreos/go-iptables
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-07 02:47:30 +00:00
9d9ec6e3e1 Merge pull request #927 from sockmister/vrf_filter_fix
vrf: fix route filter to use output iface
2023-07-21 13:49:33 +02:00
8fd63065a6 Merge pull request #913 from AlinaSecret/dhcp/fix-race-test
Fix race conditions in DHCP test
2023-07-21 12:55:01 +02:00
c1a7948b19 vrf: fix route filter to use output iface
current route filter uses RT_FILTER_IIF in conjunction with LinkIndex.
This combination is ignored by netlink, rendering the filter
ineffective

Signed-off-by: Poh Chiat Koh <poh@inter.link>
2023-07-21 12:50:21 +02:00
1561794ae9 Merge pull request #924 from SirPhuttel/go-iptables_review
Review code using go-iptables module
2023-07-21 12:23:08 +02:00
fb8ca5d31e Merge pull request #918 from SirPhuttel/rawhide_testing
Two minor testsuite fixes
2023-07-21 12:22:46 +02:00
f2574a7cb1 Merge pull request #926 from containernetworking/dependabot/go_modules/golang.org/x/sys-0.10.0
build(deps): bump golang.org/x/sys from 0.9.0 to 0.10.0
2023-07-21 11:56:30 +02:00
438548a9dd Merge pull request #902 from SirPhuttel/applyconfigecho
spoofcheck: Make use of go-nft's ApplyConfigEcho()
2023-07-20 12:01:18 +02:00
8e69e38d51 test_linux.sh: Do not fail if called twice
The script is set to exit on error, so mkdir failing because
/tmp/cni-rootless already exists aborts the test run. Call 'mkdir -p' to
avoid the spurious error.

Signed-off-by: Phil Sutter <psutter@redhat.com>
2023-07-20 11:34:34 +02:00
0a100e5d8f meta: firewall: Fix firewalld test with non-abstract sockets
On a recent Fedora Rawhide, dbus-daemon-1.14.8-1 prints a string
prefixed by 'unix:path' instead of the expected 'unix:abstract', thereby
failing the test. Allowing this alternate prefix fixes the test, so for
communication with the daemon it is not relevant.

Signed-off-by: Phil Sutter <psutter@redhat.com>
2023-07-20 11:34:34 +02:00
3eb775c5e6 plugins: meta: portmap: Implement a teardown() fast path
Just attempt to delete the known rules referring to the custom chain,
then flush and delete it. If the latter succeeds, no referencing rules
are left and the job is done.

If the final flush'n'delete fails, fall back to the referencing rule
search which is slow with large rulesets.

Signed-off-by: Phil Sutter <psutter@redhat.com>
2023-07-20 11:34:02 +02:00
719f60bb91 utils: iptables: Use go-iptables' ChainExists()
Starting with v0.5.0, go-iptables exports a fast ChainExists() which
does not rely upon listing all chains and searching the results but
probes chain existence by listing its first rule. This should make a
significant difference in rulesets with thousands of chains.

Signed-off-by: Phil Sutter <psutter@redhat.com>
2023-07-20 11:34:02 +02:00
2ba7f1608f spoofcheck: Make use of go-nft's ApplyConfigEcho()
Store the relevant applied config part for later to extract the rule to
delete from there instead of having to list the ruleset. This is much
faster especially with large rulesets.

Signed-off-by: Phil Sutter <psutter@redhat.com>
2023-07-20 11:32:09 +02:00
bf79945c70 Merge pull request #929 from squeed/fix-ci-cnitool-version
test: install binaries using `go install`
2023-07-20 11:26:31 +02:00
ba41448fe6 test: install binaries using go install
We were getting tip- versions of all the tools, and this was unstable.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2023-07-20 11:15:41 +02:00
13fd3de77f build(deps): bump golang.org/x/sys from 0.9.0 to 0.10.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.9.0 to 0.10.0.
- [Commits](https://github.com/golang/sys/compare/v0.9.0...v0.10.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-10 02:29:05 +00:00
283f200489 Merge pull request #897 from containernetworking/dependabot/docker/dot-github/actions/retest-action/alpine-3.18
build(deps): bump alpine from 3.17 to 3.18 in /.github/actions/retest-action
2023-07-03 17:05:49 +02:00
a7e8db00cb Merge pull request #911 from containernetworking/dependabot/go_modules/golang.org/x/sys-0.9.0
build(deps): bump golang.org/x/sys from 0.7.0 to 0.9.0
2023-07-03 17:05:17 +02:00
ffb2e2d7d1 Merge pull request #912 from containernetworking/dependabot/go_modules/github.com/onsi/ginkgo/v2-2.11.0
build(deps): bump github.com/onsi/ginkgo/v2 from 2.9.2 to 2.11.0
2023-07-03 17:05:02 +02:00
d03b84d8f2 Merge pull request #874 from travelping/vrf-add-routes
Add routes propagation for VRF plugin
2023-06-27 12:20:45 +02:00
1512d727cb Merge pull request #914 from tariq1890/tx-qlen
[tuning] add ability to set tx queue len
2023-06-26 17:47:01 +02:00
470eee1385 [tuning]add ability to set tx queue len
Signed-off-by: Tariq Ibrahim <tibrahim@nvidia.com>
2023-06-25 23:28:48 -07:00
2216cff9e8 build(deps): bump github.com/onsi/ginkgo/v2 from 2.9.2 to 2.11.0
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.9.2 to 2.11.0.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.9.2...v2.11.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 02:58:59 +00:00
83029befef build(deps): bump golang.org/x/sys from 0.7.0 to 0.9.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.7.0 to 0.9.0.
- [Commits](https://github.com/golang/sys/compare/v0.7.0...v0.9.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 02:58:45 +00:00
48aa2f4eef Fix race conditions in DHCP test
The test named "correctly handles multiple DELs for the same container" in the ipam/dhcp package experiences race conditions when multiple goroutines concurrently access and modify the Args struct (of type CmdArgs).
To address these issues, a copy of the CmdArgs struct is now created in each function to eliminate data races.

Also, the test-linux.sh and test-windows.sh scripts have been updated to include the '-race' flag, enabling race detection during testing. This change helps prevent future race conditions by activating the Go race detector.

Signed-off-by: Alina Sudakov <asudakov@redhat.com>
2023-06-14 17:57:46 +03:00
ca12d49b41 Add routes propagation for VRF plugin
Up until now, if previous plugin assigned routes to interface, movement of
this interface to new VRF cause routes to be deleted.

This patch adds funtionality to VRF plugin to save the routes before
interface is assgined to VRF, and then re-apply all saved routes to new VRF.

Signed-off-by: Artur Korzeniewski <artur.korzeniewski@travelping.com>
2023-06-02 14:21:28 +02:00
2b097c5a62 Merge pull request #900 from squeed/disable-stale
github: remove stale issue cleanup
2023-05-25 10:32:04 +02:00
0389a29052 github: remove stale issue cleanup
In retrospect, this was a bad idea. It's closing too many valid issues.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2023-05-25 10:31:15 +02:00
6265f4e4ca Merge pull request #832 from maiqueb/tap-plugin-set-as-bridge-port
tap: allow for a tap device to be created as a bridge port
2023-05-22 10:59:28 -05:00
edab9efdea tap: allow for a tap device to be created as a bridge port
This extends the tap plugin API enabling the user to instruct the CNI
plugin the created tap device must be set as a port of an *existing*
linux bridge on the pod network namespace.

This is helpful for KubeVirt, allowing network connectivity to be
extended from the pod's interface into the Virtual Machine running
inside the pod.

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2023-05-19 16:26:14 +02:00
1b2dc7c2a4 build(deps): bump alpine in /.github/actions/retest-action
Bumps alpine from 3.17 to 3.18.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-15 03:01:50 +00:00
38f18d26ec Merge pull request #892 from e0ne/ignore-not-found
[sbr]: Ignore LinkNotFoundError during cmdDel
2023-05-03 21:53:21 +02:00
e51301765c Merge pull request #891 from containernetworking/dependabot/go_modules/github.com/Microsoft/hcsshim-0.9.9
build(deps): bump github.com/Microsoft/hcsshim from 0.9.8 to 0.9.9
2023-05-03 17:26:09 +02:00
7e918412d5 [sbr]: Ignore LinkNotFoundError during cmdDel
Signed-off-by: Ivan Kolodyazhny <e0ne@e0ne.info>
2023-05-02 14:08:11 +03:00
99b475ab1a build(deps): bump github.com/Microsoft/hcsshim from 0.9.8 to 0.9.9
Bumps [github.com/Microsoft/hcsshim](https://github.com/Microsoft/hcsshim) from 0.9.8 to 0.9.9.
- [Release notes](https://github.com/Microsoft/hcsshim/releases)
- [Commits](https://github.com/Microsoft/hcsshim/compare/v0.9.8...v0.9.9)

---
updated-dependencies:
- dependency-name: github.com/Microsoft/hcsshim
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-01 03:02:09 +00:00
10b5639361 Merge pull request #885 from champtar/tuning-CHECK
tuning: fix cmdCheck when using IFNAME
2023-04-24 10:46:37 -05:00
65fe256058 Merge pull request #883 from mmorel-35/linter-2
enable govet and unparam linters
2023-04-24 17:42:55 +02:00
00b82fb666 Merge pull request #887 from champtar/route-CHECK
Fix ValidateExpectedRoute with non default routes and nil GW
2023-04-24 10:41:24 -05:00
c795a3c6b1 Merge pull request #888 from jingyuanliang/go120
Bump to golang 1.20 to pick up go1.19.6 / go1.20.1 CVE fixes
2023-04-24 17:37:28 +02:00
c10af01dfb Merge pull request #880 from maiqueb/mac-spoof-improv-read-only-required-chain-on-cni-del
bridge: read only required chain on cni del instead of the entire ruleset
2023-04-24 17:32:32 +02:00
9cf1a09835 Merge pull request #829 from tjjh89017/bridge_vlan_trunk
bridge: add vlan trunk support
2023-04-24 10:25:16 -05:00
d8fc886bf0 Bump to golang 1.20 to pick up go1.19.6 / go1.20.1 CVE fixes
Go 1.18 is already EOL and doesn't have fixes available.

Signed-off-by: Jingyuan Liang <jingyuanliang@google.com>
2023-04-21 05:21:43 +00:00
c347755f87 Fix ValidateExpectedRoute with non default routes and nil GW
Using ptp plugin with non default routes, we get the following error
when cri-o call CheckNetworkList():
```
Expected Route {Dst:{IP:198.18.128.0 Mask:ffff8000} GW:<nil>} not found in routing table
```
Using cniVersion 0.3.1 to bypass the check, we can see that the
route is added with a gateway
```
$ ip r
198.18.0.0/17 via 198.18.0.1 dev eth0 src 198.18.3.102
198.18.0.1 dev eth0 scope link src 198.18.3.102
198.18.128.0/17 via 198.18.0.1 dev eth0
```

If GW is nil only check if we have a route with a DST that matches, and
ignore the GW.

Fixes #886
Signed-off-by: Etienne Champetier <e.champetier@ateme.com>
2023-04-20 15:24:20 -04:00
5b7a263e8f tuning: fix cmdCheck when using IFNAME
Fixes: c16cff9805
Signed-off-by: Etienne Champetier <e.champetier@ateme.com>
2023-04-20 11:19:23 -04:00
135292e050 bridge, del: timeout after 55 secs of trying to list rules
Making sure the exec'ed nft command is executed in 55 secs allows for
CNI to fail early, thus preventing CRI from sending another CNI DEL
while the previous NFT call is still being processed.

This fix prevents part of the behavior described in [0], in which:
> cnv-bridge and nft comes pile up in a loop, increasing every 60, never
completes

The timeout had to be less than 60 seconds (otherwise CRI would still
trigger CNI DEL again) but large enough for this feature to have a
chance of working on older kernels (e.g. centOS 8), where it takes
longer to access even a specific chain/table.

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2023-04-20 11:19:07 +02:00
7dcd738d34 bridge, spoofcheck: only read the prerouting chain on CNI delete
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2023-04-20 10:35:42 +02:00
83fe87c5b0 build: consume specific tables/chains via go-nft
This go-nft version allows its users to only read particular
tables/chains when invoking `ReadConfig`, instead of the entire ruleset.

This will make deleting rules from a large ruleset faster, thus speeding
up CNI DELs.

Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=2175041

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2023-04-20 10:08:18 +02:00
090af7db9a bridge: add vlan trunk support
add vlan trunk support for veth
vlan trunk only support L2 only mode without any IPAM
refer ovs-cni design
https://github.com/k8snetworkplumbingwg/ovs-cni/blob/main/pkg/plugin/plugin.go

design:
origin "vlan" option will be PVID or untagged vlan for the network.
"vlanTrunk" will setup tagged vlan for veth.

entry type:
`{ "id": 100 }` will specify only tagged vlan 100
`{ "minID": 100, "maxID": 120 }` will specify tagged vlan from 100 to
120 (include 100 and 120)
vlanTrunk is a list of above entry type, so you can use this to add
tagged vlan
`[
  { "id": 100 },
  {
    "minID": 1000,
    "maxID": 2000
  }
]`

complete config will be like this
{
  "cniVersion": "0.3.1",
  "name": "mynet",
  "type": "bridge",
  "bridge": "mynet0",
  "vlan": 100,
  "vlanTrunk": [
    { "id": 101 },
    { "minID": 1000, "maxID": 2000 },
    { "minID": 3000, "maxID": 4000 }
  ],
  "ipam": {}
}

Signed-off-by: Date Huang <date.huang@suse.com>
2023-04-19 22:55:14 +08:00
9f1f9a588b Merge pull request #875 from mlguerrero12/adddefaultvlanparam
Add parameter to disable default vlan
2023-04-17 17:47:34 +02:00
71aa710196 Merge pull request #873 from maiqueb/mac-spoof-remove-index-when-adding-rules
bridge, spoof check: remove drop rule index
2023-04-17 17:07:11 +02:00
10ddd9e454 enable govet and unparam linters
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-04-11 12:07:04 +02:00
4a6147a155 Merge pull request #881 from containernetworking/dependabot/go_modules/golang.org/x/sys-0.7.0 2023-04-05 19:24:31 +00:00
435ef2235d build(deps): bump golang.org/x/sys from 0.6.0 to 0.7.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.6.0 to 0.7.0.
- [Release notes](https://github.com/golang/sys/releases)
- [Commits](https://github.com/golang/sys/compare/v0.6.0...v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-05 19:14:01 +00:00
43db9cc063 Merge pull request #879 from squeed/bump-deps
go.mod: bump all deps
2023-04-05 21:13:05 +02:00
821982da1c Add parameter to disable default vlan
This new parameter allows users to remove the default vlan

Fixes: #667
Signed-off-by: Marcelo Guerrero Viveros <marguerr@redhat.com>
2023-04-05 18:20:40 +02:00
cac8230e7c bridge, spoof check: remove drop rule index
Rules are appendend by default, thus using an index is redundant.
Using an index also requires the full NFT cache, which causes a CNI ADD
to be extremely slow.

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2023-04-04 17:10:08 +02:00
bc5f3defe7 go.mod: bump all deps
Bump all transitive and direct dependencies.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
2023-04-04 16:31:14 +02:00
47a4319462 Merge pull request #861 from containernetworking/dependabot/github_actions/actions/setup-go-4
build(deps): bump actions/setup-go from 3 to 4
2023-04-04 16:27:44 +02:00
68a661999a Merge pull request #870 from containernetworking/dependabot/github_actions/actions/stale-8
build(deps): bump actions/stale from 7 to 8
2023-04-04 16:27:10 +02:00
63235a2531 Merge pull request #878 from maiqueb/fix-ginkgo-linter-warnings
linter: fix ginkgolinter errors
2023-04-04 16:23:41 +02:00
7bbd4d19e9 linter: fix ginkgolinter errors
Use:
- `BeEmpty` instead of `HaveLen(0)`
- `Expect(x).To(BeZero())` instead of `Expect(x == 0).To(BeTrue())`

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2023-04-04 16:09:01 +02:00
deec68747e Merge pull request #853 from mmorel-35/ginkgolinter
enable ginkgolinter linter
2023-04-04 15:24:20 +02:00
6f6345ca05 Merge pull request #871 from mlguerrero12/fixwastedassignlinter
Fix wastedassign linter errors
2023-04-04 15:23:28 +02:00
6c0d73ecc0 Fix wastedassign linter errors
Signed-off-by: Marcelo Guerrero Viveros <marguerr@redhat.com>
2023-03-27 18:42:49 +02:00
8813bfea7b Merge pull request #855 from mmorel-35/linters
enable durationcheck,  predeclared, unconvert, unused and wastedassign linters
2023-03-27 10:53:34 -05:00
16d05ec100 Merge pull request #867 from mlguerrero12/fixlinters
Fix revive linter errors
2023-03-27 10:49:33 -05:00
086f7eb7a1 build(deps): bump actions/stale from 7 to 8
Bumps [actions/stale](https://github.com/actions/stale) from 7 to 8.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v7...v8)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-27 03:04:07 +00:00
d71d0f2da1 Fix revive linter errors
Golangci-lint is now running version 1.52.1. This introduced some errors.

Signed-off-by: Marcelo Guerrero Viveros <marguerr@redhat.com>
2023-03-24 21:04:39 +01:00
00e0d3b758 build(deps): bump actions/setup-go from 3 to 4
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-20 03:01:39 +00:00
2fb0efe8a3 enable durationcheck, predeclared, unconvert, unused and wastedassign linters
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-03-16 07:29:37 +01:00
3bc00017e3 Merge pull request #854 from mmorel-35/clean-linters
remove govet and gofmt from test_linux.sh
2023-03-14 11:49:37 +01:00
c0fe3b7bde remove govet and gofmt from test_linux.sh
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-03-13 22:47:17 +00:00
09f36a295d enable ginkgolinter linter
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-03-13 22:27:21 +00:00
d3ee71f240 Merge pull request #843 from mmorel-35/golangci-lint
ci(lint): setup golangci-lint
2023-03-13 22:26:32 +01:00
a02bf4b463 enable revive linter
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-03-13 17:59:41 +01:00
79f524689c enable gocritic linter
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-03-13 17:59:33 +01:00
5a7619c019 enable gosimple linter
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-03-13 17:59:31 +01:00
709e775b13 enable nonamedreturns linter
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-03-13 17:59:28 +01:00
3a04eb00bb enable ineffassign linter
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-03-06 11:51:40 +01:00
16ba4222bc enable contextcheck linter
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-03-06 11:23:07 +01:00
177e0bf2d9 enable staticcheck linter
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-03-02 11:06:22 +01:00
d12b81dec5 ci(lint): setup golangci-lint
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-03-01 06:55:40 +00:00
86e39cfe3c Merge pull request #836 from mmorel-35/yamllint
ci(lint): setup yamllint linter
2023-02-28 16:35:10 +01:00
6223674f25 ci(lint): setup yamllint linter
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-02-25 12:10:11 +00:00
36e1e162fa Merge pull request #812 from liornoy/ginkgov2
Update ginkgo to v2
2023-02-20 10:58:24 -06:00
286064b9ec Merge pull request #831 from mlguerrero12/fixerrorignored
Fix overwritten error var in getMTUByName
2023-02-20 10:13:34 -06:00
9ee4d3225d Fix overwritten error var in getMTUByName
this prevents the error to be lost which was causing the
panic while accesing a nil var.

Fix #830

Signed-off-by: Marcelo Guerrero Viveros <marguerr@redhat.com>
2023-02-16 12:28:17 +01:00
2d1005ec02 Update tests to utilize ginkgo/v2
This commit updates the import of ginkgo to v2 in
all of the tests.

Signed-off-by: liornoy <lnoy@redhat.com>
Co-authored-by: Sascha Grunert <sgrunert@redhat.com>
2023-02-13 21:15:18 +02:00
23c2134110 Update ginkgo to v2 in go.mod, go.sum, vendor
This commit updates ginkgo to v2.
Note that because ginkgo/v2 requires go1.18, it was
updated as well.

Signed-off-by: liornoy <lnoy@redhat.com>
Co-authored-by: Sascha Grunert <sgrunert@redhat.com>
2023-02-13 21:15:18 +02:00
fb92605570 Merge pull request #784 from mmirecki/tap-plugin
Tap plugin
2023-02-13 17:26:06 +01:00
01d0031487 Tap plugin
This PR adds a plugin to create tap devices.
The plugin adds a tap device to the container.

The plugin has a workaround for a golang netlink library
which does not allow for tap devices with no owner/group
to be created. When no tap owner/group is requested, the
plugin will fall back to using the ip tool for creating
the tap device. A fix to the golang netlink lib is pending.

Signed-off-by: mmirecki <mmirecki@redhat.com>
2023-02-13 17:14:46 +01:00
98e01b7c80 Merge pull request #824 from containernetworking/dependabot/go_modules/github.com/onsi/gomega-1.26.0
build(deps): bump github.com/onsi/gomega from 1.24.2 to 1.26.0
2023-02-13 10:09:15 -06:00
9a2f763345 build(deps): bump github.com/onsi/gomega from 1.24.2 to 1.26.0
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.24.2 to 1.26.0.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.24.2...v1.26.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-13 10:23:59 +00:00
98359ff8b4 Merge pull request #828 from containernetworking/dependabot/go_modules/golang.org/x/sys-0.5.0
build(deps): bump golang.org/x/sys from 0.4.0 to 0.5.0
2023-02-13 11:23:00 +01:00
29e6486154 build(deps): bump golang.org/x/sys from 0.4.0 to 0.5.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.4.0 to 0.5.0.
- [Release notes](https://github.com/golang/sys/releases)
- [Commits](https://github.com/golang/sys/compare/v0.4.0...v0.5.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-13 03:22:30 +00:00
bbf47c1083 Merge pull request #814 from mmirecki/macvlan_incontainermaster
Add support for in-container master for macvlans
2023-02-08 12:55:44 +01:00
5d02d91c96 Merge pull request #813 from mmirecki/ipvlan_incontainermaster
Add support for in-container master for ipvlan
2023-02-06 09:49:06 -06:00
755714d716 Merge pull request #781 from mmirecki/vlan_incontainermaster
Add support for in-container master for vlans
2023-02-06 09:47:58 -06:00
e2e14ee46f Merge pull request #821 from mccv1r0/portmapCheck
Only check or del ipv6 when an IPv6 is configured
2023-02-01 14:02:07 -06:00
fb5d195fc5 Only check ipv6 when an IPv6 is configured
Signed-off-by: Michael Cambria <mccv1r0@gmail.com>
2023-01-27 20:22:11 -05:00
ac7cf82531 Add support for in-container master for macvlans
Signed-off-by: mmirecki <mmirecki@redhat.com>
2023-01-25 12:58:28 +01:00
c798f80912 Add support for in-container master for ipvlan
Signed-off-by: mmirecki <mmirecki@redhat.com>
2023-01-25 12:11:06 +01:00
9fa80036d3 Add support for in-container master for vlans
Signed-off-by: mmirecki <mmirecki@redhat.com>
2023-01-25 11:50:12 +01:00
c4d24e80d6 Merge pull request #809 from squeed/bridge-refresh-mac
bridge: refresh host-veth mac after port add
2023-01-16 10:56:47 -06:00
2c4c27eb17 bridge: re-fetch mac address
It was noticed that, sometimes, the mac of the host-side of the veth
changes after setting up the bridge. So, just refresh it.

Fixes: #805
Signed-off-by: Casey Callendrello <c1@caseyc.net>
2023-01-16 17:36:40 +01:00
0924b71fc8 Merge pull request #790 from austinvazquez/remove-ioutil-references
Remove references to io/ioutil package
2023-01-16 10:45:56 +01:00
0af8153e9b Merge pull request #804 from mmorel-35/main
build(deps): bump github.com/safchain/ethtool to v0.2.0
2023-01-11 11:22:53 +01:00
83fe27748c Merge pull request #803 from containernetworking/dependabot/go_modules/golang.org/x/sys-0.4.0
build(deps): bump golang.org/x/sys from 0.3.0 to 0.4.0
2023-01-11 11:22:19 +01:00
bf9c25887a Merge pull request #792 from EmilyShepherd/check-dhcp
Update Allocate method to reuse lease if present
2023-01-10 14:47:54 +01:00
0fc229df5e Update Allocate method to reuse lease if present
Previously, the Allocate method of the daemon always created a new Lease
object. However, as both the CNI ADD and CHECK commands call Allocate,
and CHECK can be called multiple times, this resulted in multiple Lease
objects being created per pod.

Each of these leases was long lived with its own maintain() loop -
however the daemon only kept track of the most recent one, meaning any
old lease objects remained running forever (and held open their NetNS
files). After a long enough period, this resulted in the system crashing
out with "too many files" or a similar error limits-related error.

This commit updates the behaviour of Allocate() to first check if a
Lease already exists for the given clientID. If none is found, one is
created as before. If a Lease is found, a new Check() mechanism is
called, which simply wakes up the maintain() loop to cause it to check
the status of the lease.

This may fix #329.

Signed-off-by: Emily Shepherd <emily@redcoat.dev>
2023-01-10 13:10:21 +00:00
ec924a4be2 build(deps): bump github.com/safchain/ethtool to v0.2.0
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-01-09 18:17:27 +00:00
d27fabcd83 Merge pull request #782 from mars1024/bugfix/next-ip
fix bug on getting NextIP of addresses with first byte 0
2023-01-09 18:02:45 +01:00
20a92ff382 build(deps): bump golang.org/x/sys from 0.3.0 to 0.4.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.3.0 to 0.4.0.
- [Release notes](https://github.com/golang/sys/releases)
- [Commits](https://github.com/golang/sys/compare/v0.3.0...v0.4.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-09 16:59:28 +00:00
5c29eb7fb5 Merge pull request #801 from mmorel-35/main
ci(deps): setup dependabot
2023-01-09 17:58:48 +01:00
f48a5ea512 Merge pull request #798 from mmirecki/pathissue_fix
Fix tuning path validation
2023-01-09 17:48:00 +01:00
a3b678ee03 Merge pull request #772 from BSWANG/master
`portmap` support masquerade all
2023-01-09 17:41:52 +01:00
87ccb8918b Merge pull request #802 from Y7n05h/main
Add IPv6 support for AddDefaultRoute
2023-01-09 17:38:37 +01:00
fa2ed0fd6e Add IPv6 support for AddDefaultRoute
Signed-off-by: Y7n05h <Y7n05h@protonmail.com>
2022-12-28 15:37:29 +08:00
b769956cf4 build(deps): bump github.com/containernetworking/cni from 1.0.1 to 1.1.2
Bumps [github.com/containernetworking/cni](https://github.com/containernetworking/cni) from 1.0.1 to 1.1.2.
- [Release notes](https://github.com/containernetworking/cni/releases)
- [Commits](https://github.com/containernetworking/cni/compare/v1.0.1...v1.1.2)

---
updated-dependencies:
- dependency-name: github.com/containernetworking/cni
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:36:15 +00:00
df141fc722 build(deps): bump github.com/coreos/go-systemd/v22 from 22.3.2 to 22.5.0
Bumps [github.com/coreos/go-systemd/v22](https://github.com/coreos/go-systemd) from 22.3.2 to 22.5.0.
- [Release notes](https://github.com/coreos/go-systemd/releases)
- [Commits](https://github.com/coreos/go-systemd/compare/v22.3.2...v22.5.0)

---
updated-dependencies:
- dependency-name: github.com/coreos/go-systemd/v22
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:36:15 +00:00
35047644a8 build(deps): bump github.com/onsi/ginkgo from 1.16.4 to 1.16.5
Bumps [github.com/onsi/ginkgo](https://github.com/onsi/ginkgo) from 1.16.4 to 1.16.5.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v1.16.4...v1.16.5)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:36:15 +00:00
352f181ff1 build(deps): bump alpine in /.github/actions/retest-action
Bumps alpine from 3.10 to 3.17.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:36:15 +00:00
91b1a0e385 build(deps): bump github.com/godbus/dbus/v5 from 5.0.4 to 5.1.0
Bumps [github.com/godbus/dbus/v5](https://github.com/godbus/dbus) from 5.0.4 to 5.1.0.
- [Release notes](https://github.com/godbus/dbus/releases)
- [Commits](https://github.com/godbus/dbus/compare/v5.0.4...v5.1.0)

---
updated-dependencies:
- dependency-name: github.com/godbus/dbus/v5
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:36:15 +00:00
648dd2e14c build(deps): bump github.com/vishvananda/netlink
Bumps [github.com/vishvananda/netlink](https://github.com/vishvananda/netlink) from 1.2.0-beta to 1.2.1-beta.2.
- [Release notes](https://github.com/vishvananda/netlink/releases)
- [Commits](https://github.com/vishvananda/netlink/compare/v1.2.0-beta...v1.2.1-beta.2)

---
updated-dependencies:
- dependency-name: github.com/vishvananda/netlink
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:36:15 +00:00
615420fa9f build(deps): bump github.com/alexflint/go-filemutex from 1.1.0 to 1.2.0
Bumps [github.com/alexflint/go-filemutex](https://github.com/alexflint/go-filemutex) from 1.1.0 to 1.2.0.
- [Release notes](https://github.com/alexflint/go-filemutex/releases)
- [Commits](https://github.com/alexflint/go-filemutex/compare/v1.1...v1.2.0)

---
updated-dependencies:
- dependency-name: github.com/alexflint/go-filemutex
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:36:15 +00:00
90ed30a55a build(deps): bump github.com/Microsoft/hcsshim from 0.8.20 to 0.9.6
Bumps [github.com/Microsoft/hcsshim](https://github.com/Microsoft/hcsshim) from 0.8.20 to 0.9.6.
- [Release notes](https://github.com/Microsoft/hcsshim/releases)
- [Commits](https://github.com/Microsoft/hcsshim/compare/v0.8.20...v0.9.6)

---
updated-dependencies:
- dependency-name: github.com/Microsoft/hcsshim
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:36:15 +00:00
020b8db6ab build(deps): bump github.com/onsi/gomega from 1.15.0 to 1.24.2
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.15.0 to 1.24.2.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.15.0...v1.24.2)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:36:15 +00:00
c5e81e3c05 Update dependabot.yml
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:36:15 +00:00
3be17f4af7 build(deps): bump actions/checkout from 2 to 3
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:36:15 +00:00
f64652faf8 build(deps): bump actions/stale from 4 to 7
Bumps [actions/stale](https://github.com/actions/stale) from 4 to 7.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v4...v7)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:36:15 +00:00
edfd2274a5 build(deps): bump actions/setup-go from 2 to 3
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2 to 3.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:36:15 +00:00
d7efab18c0 Update dependabot.yml
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:36:15 +00:00
12471312e1 Update dependabot.yml
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:36:15 +00:00
c1e2be2765 ci(deps): setup dependabot
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2022-12-26 21:36:15 +00:00
fd9408bc97 Fix tuning path validation
Signed-off-by: mmirecki <mmirecki@redhat.com>
2022-12-16 11:55:33 +01:00
ec76e3c35c Merge pull request #793 from EmilyShepherd/fix-ci
Fix Tests: Update portmap test's iptables error check
2022-12-12 10:48:32 -06:00
c30b47a712 Merge pull request #795 from MikeZappa87/updateemailtogmail
Update email to gmail
2022-12-12 10:47:48 -06:00
9dc2ed2c0a Update email to gmail
Signed-off-by: Michael Zappa <michael.zappa@gmail.com>
2022-12-07 11:57:16 -07:00
6b30e290d2 Update portmap test's iptables error check
GitHub Actions recently updated ubuntu-latest to 22.04 [1], which now
defaults to nfttables (rather than iptables-legacy) [2]. The portmap
tests in this project are written with the expectation that expected
error message for one test is in the iptables-legacy format.

This commit updates the check to make it work for both the
iptables-legecy and iptables-nftables variants.

References:
[1]: 4aba37bd3b
[2]: https://ubuntu.com/blog/whats-new-in-security-for-ubuntu-22-04-lts

Signed-off-by: Emily Shepherd <emily@redcoat.dev>
2022-12-06 18:56:59 +00:00
1a6f478913 Remove references to io/ioutil package
Signed-off-by: Austin Vazquez <macedonv@amazon.com>
2022-12-01 22:27:05 +00:00
63a6dbcfd6 fix bug on getting NextIP of addresses with first byte 0
1. get the right next IP of addresses of first byte 0
2. refactor some methods to handle illegal IPs or IPNets
3. add some unit tests

Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2022-11-17 17:54:16 +08:00
7e9ada51e7 Merge pull request #779 from mmirecki/sysctl_on_vlan
Fix path substitution to enable setting sysctls on vlan interfaces
2022-11-14 10:51:25 -06:00
198ab129a1 Fix path substitution to enable setting sysctls on vlan interfaces
This commit changes the order of substituting sysctl path to first handle
. to / change, before substituting the interface name.
This is needed as vlan interfaces have a . in the name, which should not
be changed.

Signed-off-by: mmirecki <mmirecki@redhat.com>
2022-11-09 11:31:58 +01:00
0463fd19af support masquerade all config
Signed-off-by: bingshen.wbs <bingshen.wbs@alibaba-inc.com>
2022-11-07 13:50:21 +08:00
ac8673105a Merge pull request #743 from arista-eosplus/dummy-plugin
dummy: Create a Dummy CNI plugin that creates a virtual interface.
2022-10-10 17:51:00 +02:00
fcf14d39fd Merge pull request #769 from jingyuanliang/main
host-local: remove unused Release(ip) from type Store interface
2022-09-19 10:55:33 -05:00
54f69e0a59 Merge pull request #768 from EmilyShepherd/cleanup-socket-on-exit
Cleanup Socket and Pidfile on exit
2022-09-12 10:34:39 -05:00
87c3643d3c host-local: remove unused Release(ip) from type Store interface
Signed-off-by: Jingyuan Liang <jingyuanliang@google.com>
2022-09-06 21:39:54 +00:00
f89a005740 Cleanup Socket and Pidfile on exit
These were previously left intact, even when exiting gracefully. As the
daemon also fails if the socket already exists, it became the caller's
responsibilityto check for and cleanup old socket files when performing
graceful / deliberate restarts.

Signed-off-by: Emily Shepherd <emily@redcoat.dev>
2022-09-06 15:11:57 +01:00
7fcf8e4860 dummy: Create a Dummy CNI plugin that creates a virtual interface.
Leverages the Linux dummy interface type to create network interfaces
that permists routing packets through the Linux kernel without
them being transmitted.

This solution allows use of arbitrary non-loopback IP addresses within
the container.

Related to #466

Signed-off-by: Mircea Iordache-Sica <mircea@arista.com>
2022-08-11 13:50:37 +01:00
8c3664b2b1 Merge pull request #742 from lx1036/patch-1
bridge: update vlanFiltering variable to make code more readable
2022-07-28 09:13:29 -05:00
e2a71387ab Merge pull request #755 from akhilles/fix/dhcp-renew-hostname
Use the same options for acquiring, renewing lease
2022-07-27 10:37:31 -05:00
c78e1e4656 Use the same options for acquiring, renewing lease
Currently, hostname is set in the original DHCPREQUEST but not the
renewal. With some DHCP server implementations (such as FreeBSD dhcpd),
this leads to the hostname being cleared in the lease table.

This behavior is inconsistent with other DHCP clients such as dhclient
which set the hostname on the renewal request as well. To fix, use the
same options for acquire and renew.

This is compatible with RFC 2131 (see table 5).

Signed-off-by: Akhil Velagapudi <4@4khil.com>
2022-06-23 17:47:37 -07:00
9f4090dabf bridge: update vlanFiltering variable to make code more readable
Signed-off-by: Xiang Liu <lx1036@126.com>
2022-05-21 14:37:36 +08:00
9c59728d39 Merge pull request #730 from mmirecki/tuning_duplicate_check
Check for duplicated sysctl keys
2022-05-04 11:21:35 -05:00
6a94696205 Merge pull request #712 from mesosphere/joe/add_error_output
bug: return errors when iptables and ip6tables are unusable
2022-05-04 11:01:41 -05:00
356db54531 Merge pull request #740 from jpsim/patch-1
ci: only rerun failed jobs on `/retest`
2022-05-04 11:00:26 -05:00
f7dfa0f600 Merge pull request #739 from AkihiroSuda/riscv64
build: support riscv64
2022-05-04 17:44:44 +02:00
f1aa6c2622 ci: only rerun failed jobs on /retest
Unlike `rerun_url`, this endpoint isn't in the run API response,
but we can construct it manually easily.

Documentation for it is here:
https://docs.github.com/en/rest/actions/workflow-runs#re-run-failed-jobs-from-a-workflow-run

Signed-off-by: JP Simard <jp@jpsim.com>
2022-04-28 22:13:02 -04:00
bf4068e1cd build: support riscv64
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2022-04-29 01:53:59 +09:00
7c452c77cd Check for duplicated sysctl keys
Signed-off-by: mmirecki <mmirecki@redhat.com>
2022-04-27 22:13:17 +02:00
6264f7bff9 Merge pull request #735 from martinetd/crash-LTE
Update github.com/vishvananda/netlink to v1.2.0-beta
2022-04-27 11:02:30 -05:00
6bcc877722 Merge pull request #732 from squeed/go-1.18
Bump to go 1.18
2022-04-27 10:59:28 -05:00
9114aa6d37 Merge pull request #733 from Kern--/bridge-ipam-dns
bridge: support IPAM DNS settings
2022-04-27 17:59:06 +02:00
f891722833 Update github.com/vishvananda/netlink to v1.2.0-beta
Latest version fixes a segfault when used on some ppp setup

Signed-off-by: Dominique Martinet <dominique.martinet@atmark-techno.com>
2022-04-27 10:58:34 +09:00
a70e87c3aa bridge: support IPAM DNS settings
Previously, the bridge plugin ignored DNS settings returned
from an IPAM plugin (e.g. the host-local plugin parsing
resolv.conf to configure DNS). With this change, the bridge plugin
uses IPAM DNS settings.

Similarly to #388, this change will use incoming DNS settings if set,
otherwise IPAM plugin returned DNS settings

Signed-off-by: Kern Walster <walster@amazon.com>
2022-04-21 05:12:01 +00:00
0a0853a756 Bump to go 1.18
Signed-off-by: Casey Callendrello <cdc@redhat.com>
2022-04-20 18:06:58 +02:00
0c39335765 Merge pull request #725 from selansen/v2api-suuport-win-overlay
V2 API support for win-overlay CNI
2022-04-14 13:13:37 -05:00
8b8825bcd8 V2 API support for win-overlay CNI
This PR bring V2 API support into win-overlay CNI. With the current V1
API, only docker runtime works for win-overlay. By bringing new changes, we
should be able to use containerd as the runtime.Below are the key
points regarding this implementation.
	1. Clear seperation for V1 & V2 API support
	2. New cni.conf sample that works for win-overlay

Signed-off-by: selansen <esiva@redhat.com>
Signed-off-by: mansikulkarni96 <mankulka@redhat.com>
2022-04-14 12:44:49 -04:00
93604ec20a Merge pull request #724 from squeed/stale-keep
github: ignore issues with "keep" label from stale closing
2022-04-13 11:14:47 -05:00
e3d563b0f0 bug: return errors when iptables and ip6tables are unusable
Signed-off-by: Joe Julian <me@joejulian.name>
2022-04-01 11:03:54 -07:00
16e4a82b32 Merge pull request #719 from benmcmahon100/patch-1
Make description for `static` plugin more exact
2022-03-30 18:03:34 +02:00
e952f16c75 github: ignore issues with "keep" label from stale closing
Signed-off-by: Casey Callendrello <cdc@redhat.com>
2022-03-30 18:02:45 +02:00
5ad4fcf85a Make description for static plugin more exact
Signed-off-by: Ben McMahon <benmcmahon100@gmail.com>
2022-03-18 23:18:57 +00:00
292f188e4e Merge pull request #717 from squeed/auto-close-stale
workflow: add something to auto-close stale PRs
2022-03-16 16:58:48 +01:00
57c1cb5058 workflow: add something to auto-close stale PRs
Signed-off-by: Casey Callendrello <cdc@redhat.com>
2022-03-09 18:05:17 +01:00
b8a10bbe11 Merge pull request #702 from gojoy/master
call ipam.ExecDel after clean up device in netns
2022-03-02 10:50:25 -06:00
600c58a54f Merge pull request #709 from fwiesel/ipam_dhcp_client_id
ipam/dhcp: Fix client id in renew/release
2022-03-02 17:49:12 +01:00
3512b10ff0 Merge pull request #693 from mmirecki/POC_sysctl_whitelist
Add sysctl allowList
2022-03-02 17:40:46 +01:00
7a98979487 ipam/dhcp: Fix client id in renew/release
The client id was constructed differently in the acquire
function compared to the release and renew functions,
which caused the dhcp-server to consider it a different client.
This is now encapsulated in a common function.

Signed-off-by: Fabian Wiesel <fabian.wiesel@sap.com>
2022-02-27 22:19:50 +01:00
7aa07efe29 call ipam.ExceDel after clean up device in netns
fix #666

Signed-off-by: gojoy <729324352@qq.com>
2022-02-27 10:55:42 +08:00
96c3af81e2 Add sysctl allowlist
Signed-off-by: mmirecki <mmirecki@redhat.com>
2022-02-24 15:41:04 +01:00
1440 changed files with 172191 additions and 38255 deletions

View File

@ -1,4 +1,4 @@
FROM alpine:3.10
FROM alpine:3.18
RUN apk add --no-cache curl jq

View File

@ -8,4 +8,4 @@ runs:
using: 'docker'
image: 'Dockerfile'
env:
GITHUB_TOKEN: ${{ inputs.token }}
GITHUB_TOKEN: ${{ inputs.token }}

View File

@ -27,10 +27,10 @@ curl --request GET \
--header "authorization: Bearer ${GITHUB_TOKEN}" \
--header "content-type: application/json" | jq '.workflow_runs | max_by(.run_number)' > run.json
RERUN_URL=$(jq -r '.rerun_url' run.json)
RUN_URL=$(jq -r '.rerun_url' run.json)
curl --request POST \
--url "${RERUN_URL}" \
--url "${RUN_URL}/rerun-failed-jobs" \
--header "authorization: Bearer ${GITHUB_TOKEN}" \
--header "content-type: application/json"
@ -42,4 +42,4 @@ curl --request POST \
--header "authorization: Bearer ${GITHUB_TOKEN}" \
--header "accept: application/vnd.github.squirrel-girl-preview+json" \
--header "content-type: application/json" \
--data '{ "content" : "rocket" }'
--data '{ "content" : "rocket" }'

23
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,23 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "docker" # See documentation for possible values
directory: "/.github/actions/retest-action" # Location of package manifests
schedule:
interval: "weekly"
- package-ecosystem: "github-actions" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
- package-ecosystem: "gomod" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
groups:
golang:
patterns:
- "*"

View File

@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Re-Test Action
uses: ./.github/actions/retest-action

View File

@ -4,20 +4,36 @@ name: test
on: ["push", "pull_request"]
env:
GO_VERSION: "1.17"
LINUX_ARCHES: "amd64 386 arm arm64 s390x mips64le ppc64le"
GO_VERSION: "1.21"
LINUX_ARCHES: "amd64 386 arm arm64 s390x mips64le ppc64le riscv64"
jobs:
build:
name: Build all linux architectures
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: setup go
uses: actions/setup-go@v2
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
- uses: actions/checkout@v2
go-version-file: go.mod
- uses: ibiqlik/action-yamllint@v3
with:
format: auto
- uses: golangci/golangci-lint-action@v3
with:
args: -v
skip-cache: true
build:
name: Build all linux architectures
needs: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: setup go
uses: actions/setup-go@v4
with:
go-version-file: go.mod
- name: Build on all supported architectures
run: |
set -e
@ -26,9 +42,9 @@ jobs:
GOARCH=$arch ./build_linux.sh
rm bin/*
done
test-linux:
name: Run tests on Linux amd64
needs: build
runs-on: ubuntu-latest
steps:
- name: Install kernel module
@ -37,24 +53,21 @@ jobs:
sudo apt-get install linux-modules-extra-$(uname -r)
- name: Install nftables
run: sudo apt-get install nftables
- uses: actions/checkout@v4
- name: setup go
uses: actions/setup-go@v2
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
go-version-file: go.mod
- name: Set up Go for root
run: |
sudo ln -sf `which go` `sudo which go` || true
sudo go version
- uses: actions/checkout@v2
- name: Install test binaries
env:
GO111MODULE: off
run: |
go get github.com/containernetworking/cni/cnitool
go get github.com/mattn/goveralls
go get github.com/modocache/gover
go install github.com/containernetworking/cni/cnitool@latest
go install github.com/mattn/goveralls@latest
go install github.com/modocache/gover@latest
- name: test
run: PATH=$PATH:$(go env GOPATH)/bin COVERALLS=1 ./test_linux.sh
@ -66,15 +79,15 @@ jobs:
PATH=$PATH:$(go env GOPATH)/bin
gover
goveralls -coverprofile=gover.coverprofile -service=github
test-win:
name: Build and run tests on Windows
needs: build
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: setup go
uses: actions/setup-go@v2
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
- uses: actions/checkout@v2
go-version-file: go.mod
- name: test
run: bash ./test_windows.sh

45
.golangci.yml Normal file
View File

@ -0,0 +1,45 @@
issues:
exclude-rules:
- linters:
- revive
text: "don't use ALL_CAPS in Go names; use CamelCase"
- linters:
- revive
text: " and that stutters;"
- path: '(.+)_test\.go'
text: "dot-imports: should not use dot imports"
linters:
disable:
- errcheck
enable:
- contextcheck
- durationcheck
- gci
- ginkgolinter
- gocritic
- gofumpt
- gosimple
- govet
- ineffassign
- misspell
- nonamedreturns
- predeclared
- revive
- staticcheck
- unconvert
- unparam
- unused
- wastedassign
linters-settings:
gci:
sections:
- standard
- default
- prefix(github.com/containernetworking)
run:
skip-dirs:
- vendor
timeout: 5m

12
.yamllint.yml Normal file
View File

@ -0,0 +1,12 @@
extends: default
ignore: |
vendor
rules:
document-start: disable
line-length: disable
truthy:
ignore: |
.github/workflows/*.yml
.github/workflows/*.yaml

View File

@ -7,3 +7,4 @@ This is the official list of the CNI network plugins owners:
- Matt Dupre <matt@tigera.io> (@matthewdupre)
- Michael Cambria <mcambria@redhat.com> (@mccv1r0)
- Piotr Skarmuk <piotr.skarmuk@gmail.com> (@jellonek)
- Michael Zappa <michael.zappa@gmail.com> (@MikeZappa87)

View File

@ -14,13 +14,14 @@ Read [CONTRIBUTING](CONTRIBUTING.md) for build and test instructions.
* `ptp`: Creates a veth pair.
* `vlan`: Allocates a vlan device.
* `host-device`: Move an already-existing device into a container.
* `dummy`: Creates a new Dummy device in the container.
#### Windows: Windows specific
* `win-bridge`: Creates a bridge, adds the host and the container to it.
* `win-overlay`: Creates an overlay interface to the container.
### IPAM: IP address allocation
* `dhcp`: Runs a daemon on the host to make DHCP requests on behalf of the container
* `host-local`: Maintains a local database of allocated IPs
* `static`: Allocate a static IPv4/IPv6 addresses to container and it's useful in debugging purpose.
* `static`: Allocate a single static IPv4/IPv6 address to container. It's useful in debugging purpose.
### Meta: other plugins
* `tuning`: Tweaks sysctl parameters of an existing interface

View File

@ -1,8 +1,8 @@
#!/usr/bin/env bash
#!/usr/bin/env sh
set -e
cd "$(dirname "$0")"
if [ "$(uname)" == "Darwin" ]; then
if [ "$(uname)" = "Darwin" ]; then
export GOOS="${GOOS:-linux}"
fi

View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/usr/bin/env sh
set -e
cd "$(dirname "$0")"

60
go.mod
View File

@ -1,40 +1,50 @@
module github.com/containernetworking/plugins
go 1.17
go 1.20
require (
github.com/Microsoft/hcsshim v0.8.20
github.com/alexflint/go-filemutex v1.1.0
github.com/Microsoft/hcsshim v0.11.4
github.com/alexflint/go-filemutex v1.2.0
github.com/buger/jsonparser v1.1.1
github.com/containernetworking/cni v1.0.1
github.com/coreos/go-iptables v0.6.0
github.com/coreos/go-systemd/v22 v22.3.2
github.com/containernetworking/cni v1.1.2
github.com/coreos/go-iptables v0.7.0
github.com/coreos/go-systemd/v22 v22.5.0
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c
github.com/d2g/dhcp4client v1.0.0
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5
github.com/godbus/dbus/v5 v5.0.4
github.com/godbus/dbus/v5 v5.1.0
github.com/mattn/go-shellwords v1.0.12
github.com/networkplumbing/go-nft v0.2.0
github.com/onsi/ginkgo v1.16.4
github.com/onsi/gomega v1.15.0
github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1
github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e
github.com/networkplumbing/go-nft v0.4.0
github.com/onsi/ginkgo/v2 v2.13.2
github.com/onsi/gomega v1.30.0
github.com/opencontainers/selinux v1.11.0
github.com/safchain/ethtool v0.3.0
github.com/vishvananda/netlink v1.2.1-beta.2
golang.org/x/sys v0.15.0
)
require (
github.com/Microsoft/go-winio v0.4.17 // indirect
github.com/containerd/cgroups v1.0.1 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
github.com/containerd/containerd v1.6.23 // indirect
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4 // indirect
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20230323073829-e72429f035bd // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
go.opencensus.io v0.22.3 // indirect
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 // indirect
golang.org/x/text v0.3.6 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/vishvananda/netns v0.0.4 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/mod v0.13.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/tools v0.14.0 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/grpc v1.56.3 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

884
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@
// 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
// 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,
@ -14,21 +14,20 @@
package integration_test
import (
"bytes"
"fmt"
"io"
"math/rand"
"net"
"os"
"os/exec"
"path/filepath"
"bytes"
"io"
"net"
"regexp"
"strconv"
"strings"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gbytes"
"github.com/onsi/gomega/gexec"
@ -148,8 +147,8 @@ var _ = Describe("Basic PTP using cnitool", func() {
basicBridgeEnv.runInNS(hostNS, cnitoolBin, "del", "network-chain-test", contNS2.LongName())
})
Measure("limits traffic only on the restricted bandwith veth device", func(b Benchmarker) {
ipRegexp := regexp.MustCompile("10\\.1[12]\\.2\\.\\d{1,3}")
Measure("limits traffic only on the restricted bandwidth veth device", func(b Benchmarker) {
ipRegexp := regexp.MustCompile(`10\.1[12]\.2\.\d{1,3}`)
By(fmt.Sprintf("adding %s to %s\n\n", "chained-bridge-bandwidth", contNS1.ShortName()))
chainedBridgeBandwidthEnv.runInNS(hostNS, cnitoolBin, "add", "network-chain-test", contNS1.LongName())
@ -162,27 +161,24 @@ var _ = Describe("Basic PTP using cnitool", func() {
Expect(basicBridgeIP).To(ContainSubstring("10.11.2."))
var chainedBridgeBandwidthPort, basicBridgePort int
var err error
By(fmt.Sprintf("starting echo server in %s\n\n", contNS1.ShortName()))
chainedBridgeBandwidthPort, chainedBridgeBandwidthSession, err = startEchoServerInNamespace(contNS1)
Expect(err).ToNot(HaveOccurred())
chainedBridgeBandwidthPort, chainedBridgeBandwidthSession = startEchoServerInNamespace(contNS1)
By(fmt.Sprintf("starting echo server in %s\n\n", contNS2.ShortName()))
basicBridgePort, basicBridgeSession, err = startEchoServerInNamespace(contNS2)
Expect(err).ToNot(HaveOccurred())
basicBridgePort, basicBridgeSession = startEchoServerInNamespace(contNS2)
packetInBytes := 20000 // The shaper needs to 'warm'. Send enough to cause it to throttle,
// balanced by run time.
By(fmt.Sprintf("sending tcp traffic to the chained, bridged, traffic shaped container on ip address '%s:%d'\n\n", chainedBridgeIP, chainedBridgeBandwidthPort))
runtimeWithLimit := b.Time("with chained bridge and bandwidth plugins", func() {
makeTcpClientInNS(hostNS.ShortName(), chainedBridgeIP, chainedBridgeBandwidthPort, packetInBytes)
makeTCPClientInNS(hostNS.ShortName(), chainedBridgeIP, chainedBridgeBandwidthPort, packetInBytes)
})
By(fmt.Sprintf("sending tcp traffic to the basic bridged container on ip address '%s:%d'\n\n", basicBridgeIP, basicBridgePort))
runtimeWithoutLimit := b.Time("with basic bridged plugin", func() {
makeTcpClientInNS(hostNS.ShortName(), basicBridgeIP, basicBridgePort, packetInBytes)
makeTCPClientInNS(hostNS.ShortName(), basicBridgeIP, basicBridgePort, packetInBytes)
})
Expect(runtimeWithLimit).To(BeNumerically(">", runtimeWithoutLimit+1000*time.Millisecond))
@ -224,7 +220,7 @@ func (n Namespace) Del() {
(TestEnv{}).run("ip", "netns", "del", string(n))
}
func makeTcpClientInNS(netns string, address string, port int, numBytes int) {
func makeTCPClientInNS(netns string, address string, port int, numBytes int) {
payload := bytes.Repeat([]byte{'a'}, numBytes)
message := string(payload)
@ -243,7 +239,7 @@ func makeTcpClientInNS(netns string, address string, port int, numBytes int) {
Expect(string(out)).To(Equal(message))
}
func startEchoServerInNamespace(netNS Namespace) (int, *gexec.Session, error) {
func startEchoServerInNamespace(netNS Namespace) (int, *gexec.Session) {
session, err := startInNetNS(echoServerBinaryPath, netNS)
Expect(err).NotTo(HaveOccurred())
@ -260,7 +256,7 @@ func startEchoServerInNamespace(netNS Namespace) (int, *gexec.Session, error) {
io.Copy(GinkgoWriter, io.MultiReader(session.Out, session.Err))
}()
return port, session, nil
return port, session
}
func startInNetNS(binPath string, namespace Namespace) (*gexec.Session, error) {

View File

@ -4,7 +4,7 @@
// 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
// 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,
@ -17,7 +17,7 @@ import (
"strings"
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
)

View File

@ -39,6 +39,7 @@ type EndpointInfo struct {
NetworkId string
Gateway net.IP
IpAddress net.IP
MacAddress string
}
// GetSandboxContainerID returns the sandbox ID of this pod.
@ -248,6 +249,7 @@ func GenerateHcnEndpoint(epInfo *EndpointInfo, n *NetConf) (*hcn.HostComputeEndp
Minor: 0,
},
Name: epInfo.EndpointName,
MacAddress: epInfo.MacAddress,
HostComputeNetwork: epInfo.NetworkId,
Dns: hcn.Dns{
Domain: epInfo.DNS.Domain,
@ -280,6 +282,16 @@ func RemoveHcnEndpoint(epName string) error {
}
return errors.Annotatef(err, "failed to find HostComputeEndpoint %s", epName)
}
epNamespace, err := hcn.GetNamespaceByID(hcnEndpoint.HostComputeNamespace)
if err != nil && !hcn.IsNotFoundError(err) {
return errors.Annotatef(err, "failed to get HostComputeNamespace %s", epName)
}
if epNamespace != nil {
err = hcn.RemoveNamespaceEndpoint(hcnEndpoint.HostComputeNamespace, hcnEndpoint.Id)
if err != nil && !hcn.IsNotFoundError(err) {
return errors.Annotatef(err,"error removing endpoint: %s from namespace", epName)
}
}
err = hcnEndpoint.Delete()
if err != nil {

View File

@ -14,7 +14,7 @@
package hns
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"testing"

View File

@ -18,7 +18,7 @@ import (
"net"
"github.com/Microsoft/hcsshim/hcn"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

View File

@ -19,43 +19,87 @@ import (
"net"
)
// NextIP returns IP incremented by 1
// NextIP returns IP incremented by 1, if IP is invalid, return nil
func NextIP(ip net.IP) net.IP {
i := ipToInt(ip)
return intToIP(i.Add(i, big.NewInt(1)))
normalizedIP := normalizeIP(ip)
if normalizedIP == nil {
return nil
}
i := ipToInt(normalizedIP)
return intToIP(i.Add(i, big.NewInt(1)), len(normalizedIP) == net.IPv6len)
}
// PrevIP returns IP decremented by 1
// PrevIP returns IP decremented by 1, if IP is invalid, return nil
func PrevIP(ip net.IP) net.IP {
i := ipToInt(ip)
return intToIP(i.Sub(i, big.NewInt(1)))
normalizedIP := normalizeIP(ip)
if normalizedIP == nil {
return nil
}
i := ipToInt(normalizedIP)
return intToIP(i.Sub(i, big.NewInt(1)), len(normalizedIP) == net.IPv6len)
}
// Cmp compares two IPs, returning the usual ordering:
// a < b : -1
// a == b : 0
// a > b : 1
// incomparable : -2
func Cmp(a, b net.IP) int {
aa := ipToInt(a)
bb := ipToInt(b)
return aa.Cmp(bb)
normalizedA := normalizeIP(a)
normalizedB := normalizeIP(b)
if len(normalizedA) == len(normalizedB) && len(normalizedA) != 0 {
return ipToInt(normalizedA).Cmp(ipToInt(normalizedB))
}
return -2
}
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)
}
func intToIP(i *big.Int, isIPv6 bool) net.IP {
intBytes := i.Bytes()
if len(intBytes) == net.IPv4len || len(intBytes) == net.IPv6len {
return intBytes
}
return big.NewInt(0).SetBytes(ip.To16())
if isIPv6 {
return append(make([]byte, net.IPv6len-len(intBytes)), intBytes...)
}
return append(make([]byte, net.IPv4len-len(intBytes)), intBytes...)
}
func intToIP(i *big.Int) net.IP {
return net.IP(i.Bytes())
// normalizeIP will normalize IP by family,
// IPv4 : 4-byte form
// IPv6 : 16-byte form
// others : nil
func normalizeIP(ip net.IP) net.IP {
if ipTo4 := ip.To4(); ipTo4 != nil {
return ipTo4
}
return ip.To16()
}
// Network masks off the host portion of the IP
// Network masks off the host portion of the IP, if IPNet is invalid,
// return nil
func Network(ipn *net.IPNet) *net.IPNet {
if ipn == nil {
return nil
}
maskedIP := ipn.IP.Mask(ipn.Mask)
if maskedIP == nil {
return nil
}
return &net.IPNet{
IP: ipn.IP.Mask(ipn.Mask),
IP: maskedIP,
Mask: ipn.Mask,
}
}

247
pkg/ip/cidr_test.go Normal file
View File

@ -0,0 +1,247 @@
// Copyright 2022 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/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("CIDR functions", func() {
It("NextIP", func() {
testCases := []struct {
ip net.IP
nextIP net.IP
}{
{
[]byte{192, 0, 2},
nil,
},
{
net.ParseIP("192.168.0.1"),
net.IPv4(192, 168, 0, 2).To4(),
},
{
net.ParseIP("192.168.0.255"),
net.IPv4(192, 168, 1, 0).To4(),
},
{
net.ParseIP("0.1.0.5"),
net.IPv4(0, 1, 0, 6).To4(),
},
{
net.ParseIP("AB12::123"),
net.ParseIP("AB12::124"),
},
{
net.ParseIP("AB12::FFFF"),
net.ParseIP("AB12::1:0"),
},
{
net.ParseIP("0::123"),
net.ParseIP("0::124"),
},
}
for _, test := range testCases {
ip := NextIP(test.ip)
Expect(ip).To(Equal(test.nextIP))
}
})
It("PrevIP", func() {
testCases := []struct {
ip net.IP
prevIP net.IP
}{
{
[]byte{192, 0, 2},
nil,
},
{
net.ParseIP("192.168.0.2"),
net.IPv4(192, 168, 0, 1).To4(),
},
{
net.ParseIP("192.168.1.0"),
net.IPv4(192, 168, 0, 255).To4(),
},
{
net.ParseIP("0.1.0.5"),
net.IPv4(0, 1, 0, 4).To4(),
},
{
net.ParseIP("AB12::123"),
net.ParseIP("AB12::122"),
},
{
net.ParseIP("AB12::1:0"),
net.ParseIP("AB12::FFFF"),
},
{
net.ParseIP("0::124"),
net.ParseIP("0::123"),
},
}
for _, test := range testCases {
ip := PrevIP(test.ip)
Expect(ip).To(Equal(test.prevIP))
}
})
It("Cmp", func() {
testCases := []struct {
a net.IP
b net.IP
result int
}{
{
net.ParseIP("192.168.0.2"),
nil,
-2,
},
{
net.ParseIP("192.168.0.2"),
[]byte{192, 168, 5},
-2,
},
{
net.ParseIP("192.168.0.2"),
net.ParseIP("AB12::123"),
-2,
},
{
net.ParseIP("192.168.0.2"),
net.ParseIP("192.168.0.5"),
-1,
},
{
net.ParseIP("192.168.0.2"),
net.ParseIP("192.168.0.5").To4(),
-1,
},
{
net.ParseIP("192.168.0.10"),
net.ParseIP("192.168.0.5"),
1,
},
{
net.ParseIP("192.168.0.10"),
net.ParseIP("192.168.0.10"),
0,
},
{
net.ParseIP("192.168.0.10"),
net.ParseIP("192.168.0.10").To4(),
0,
},
{
net.ParseIP("AB12::122"),
net.ParseIP("AB12::123"),
-1,
},
{
net.ParseIP("AB12::210"),
net.ParseIP("AB12::123"),
1,
},
{
net.ParseIP("AB12::210"),
net.ParseIP("AB12::210"),
0,
},
}
for _, test := range testCases {
result := Cmp(test.a, test.b)
Expect(result).To(Equal(test.result))
}
})
It("Network", func() {
testCases := []struct {
ipNet *net.IPNet
result *net.IPNet
}{
{
nil,
nil,
},
{
&net.IPNet{
IP: nil,
Mask: net.IPv4Mask(255, 255, 255, 0),
},
nil,
},
{
&net.IPNet{
IP: net.IPv4(192, 168, 0, 1),
Mask: nil,
},
nil,
},
{
&net.IPNet{
IP: net.ParseIP("AB12::123"),
Mask: net.IPv4Mask(255, 255, 255, 0),
},
nil,
},
{
&net.IPNet{
IP: net.IPv4(192, 168, 0, 100).To4(),
Mask: net.CIDRMask(120, 128),
},
&net.IPNet{
IP: net.IPv4(192, 168, 0, 0).To4(),
Mask: net.CIDRMask(120, 128),
},
},
{
&net.IPNet{
IP: net.IPv4(192, 168, 0, 100),
Mask: net.CIDRMask(24, 32),
},
&net.IPNet{
IP: net.IPv4(192, 168, 0, 0).To4(),
Mask: net.CIDRMask(24, 32),
},
},
{
&net.IPNet{
IP: net.ParseIP("AB12::123"),
Mask: net.CIDRMask(120, 128),
},
&net.IPNet{
IP: net.ParseIP("AB12::100"),
Mask: net.CIDRMask(120, 128),
},
},
}
for _, test := range testCases {
result := Network(test.ipNet)
Expect(result).To(Equal(test.result))
}
})
})

View File

@ -47,13 +47,12 @@ func ParseIP(s string) *IP {
return nil
}
return newIP(ip, ipNet.Mask)
} else {
ip := net.ParseIP(s)
if ip == nil {
return nil
}
return newIP(ip, nil)
}
ip := net.ParseIP(s)
if ip == nil {
return nil
}
return newIP(ip, nil)
}
// ToIP will return a net.IP in standard form from this IP.

View File

@ -15,10 +15,10 @@
package ip_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestIp(t *testing.T) {

View File

@ -19,7 +19,7 @@ import (
"fmt"
"net"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
@ -124,7 +124,7 @@ var _ = Describe("IP Operations", func() {
}
for _, test := range testCases {
Expect(len(test.ip.ToIP())).To(Equal(test.expectedLen))
Expect(test.ip.ToIP()).To(HaveLen(test.expectedLen))
Expect(test.ip.ToIP()).To(Equal(test.expectedIP))
}
})
@ -174,8 +174,8 @@ var _ = Describe("IP Operations", func() {
}
})
It("Decode", func() {
Context("valid IP", func() {
Context("Decode", func() {
It("valid IP", func() {
testCases := []struct {
text string
expected *IP
@ -205,10 +205,9 @@ var _ = Describe("IP Operations", func() {
Expect(err).NotTo(HaveOccurred())
Expect(ip).To(Equal(test.expected))
}
})
Context("empty text", func() {
It("empty text", func() {
ip := &IP{}
err := json.Unmarshal([]byte(`""`), ip)
@ -216,7 +215,7 @@ var _ = Describe("IP Operations", func() {
Expect(ip).To(Equal(newIP(nil, nil)))
})
Context("invalid IP", func() {
It("invalid IP", func() {
testCases := []struct {
text string
expectedErr error
@ -243,7 +242,7 @@ var _ = Describe("IP Operations", func() {
}
})
Context("IP slice", func() {
It("IP slice", func() {
testCases := []struct {
text string
expected []*IP

View File

@ -16,7 +16,7 @@ package ip
import (
"bytes"
"io/ioutil"
"os"
current "github.com/containernetworking/cni/pkg/types/100"
)
@ -53,10 +53,10 @@ func EnableForward(ips []*current.IPConfig) error {
}
func echo1(f string) error {
if content, err := ioutil.ReadFile(f); err == nil {
if content, err := os.ReadFile(f); err == nil {
if bytes.Equal(bytes.TrimSpace(content), []byte("1")) {
return nil
}
}
return ioutil.WriteFile(f, []byte("1"), 0644)
return os.WriteFile(f, []byte("1"), 0o644)
}

View File

@ -1,17 +1,16 @@
package ip
import (
"io/ioutil"
"os"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("IpforwardLinux", func() {
It("echo1 must not write the file if content is 1", func() {
file, err := ioutil.TempFile(os.TempDir(), "containernetworking")
file, err := os.CreateTemp("", "containernetworking")
Expect(err).NotTo(HaveOccurred())
defer os.Remove(file.Name())
err = echo1(file.Name())

View File

@ -104,7 +104,6 @@ func TeardownIPMasq(ipn *net.IPNet, chain string, comment string) error {
err = ipt.ClearChain("nat", chain)
if err != nil && !isNotExist(err) {
return err
}
err = ipt.DeleteChain("nat", chain)

View File

@ -28,9 +28,7 @@ import (
"github.com/containernetworking/plugins/pkg/utils/sysctl"
)
var (
ErrLinkNotFound = errors.New("link not found")
)
var ErrLinkNotFound = errors.New("link not found")
// makeVethPair is called from within the container's network namespace
func makeVethPair(name, peer string, mtu int, mac string, hostNS ns.NetNS) (netlink.Link, error) {
@ -69,38 +67,37 @@ func peerExists(name string) bool {
return true
}
func makeVeth(name, vethPeerName string, mtu int, mac string, hostNS ns.NetNS) (peerName string, veth netlink.Link, err error) {
func makeVeth(name, vethPeerName string, mtu int, mac string, hostNS ns.NetNS) (string, netlink.Link, error) {
var peerName string
var veth netlink.Link
var err error
for i := 0; i < 10; i++ {
if vethPeerName != "" {
peerName = vethPeerName
} else {
peerName, err = RandomVethName()
if err != nil {
return
return peerName, nil, err
}
}
veth, err = makeVethPair(name, peerName, mtu, mac, hostNS)
switch {
case err == nil:
return
return peerName, veth, nil
case os.IsExist(err):
if peerExists(peerName) && vethPeerName == "" {
continue
}
err = fmt.Errorf("container veth name provided (%v) already exists", name)
return
return peerName, veth, fmt.Errorf("container veth name provided (%v) already exists", name)
default:
err = fmt.Errorf("failed to make veth pair: %v", err)
return
return peerName, veth, fmt.Errorf("failed to make veth pair: %v", err)
}
}
// should really never be hit
err = fmt.Errorf("failed to find a unique veth name")
return
return peerName, nil, fmt.Errorf("failed to find a unique veth name")
}
// RandomVethName returns string "veth" with random prefix (hashed from entropy)

View File

@ -20,22 +20,15 @@ import (
"fmt"
"net"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/vishvananda/netlink"
"github.com/containernetworking/plugins/pkg/ip"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
"github.com/vishvananda/netlink"
)
func getHwAddr(linkname string) string {
veth, err := netlink.LinkByName(linkname)
Expect(err).NotTo(HaveOccurred())
return fmt.Sprintf("%s", veth.Attrs().HardwareAddr)
}
var _ = Describe("Link", func() {
const (
ifaceFormatString string = "i%d"
@ -64,7 +57,7 @@ var _ = Describe("Link", func() {
Expect(err).NotTo(HaveOccurred())
fakeBytes := make([]byte, 20)
//to be reset in AfterEach block
// to be reset in AfterEach block
rand.Reader = bytes.NewReader(fakeBytes)
_ = containerNetNS.Do(func(ns.NetNS) error {
@ -181,7 +174,7 @@ var _ = Describe("Link", func() {
Context("when there is no name available for the host-side", func() {
BeforeEach(func() {
//adding different interface to container ns
// adding different interface to container ns
containerVethName += "0"
})
It("returns useful error", func() {
@ -197,7 +190,7 @@ var _ = Describe("Link", func() {
Context("when there is no name conflict for the host or container interfaces", func() {
BeforeEach(func() {
//adding different interface to container and host ns
// adding different interface to container and host ns
containerVethName += "0"
rand.Reader = originalRandReader
})
@ -211,7 +204,7 @@ var _ = Describe("Link", func() {
return nil
})
//verify veths are in different namespaces
// verify veths are in different namespaces
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
@ -290,7 +283,7 @@ var _ = Describe("Link", func() {
// this will delete the host endpoint too
addr, err := ip.DelLinkByNameAddr(containerVethName)
Expect(err).NotTo(HaveOccurred())
Expect(addr).To(HaveLen(0))
Expect(addr).To(BeEmpty())
return nil
})
})

View File

@ -42,6 +42,11 @@ func AddHostRoute(ipn *net.IPNet, gw net.IP, dev netlink.Link) error {
// 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")
var defNet *net.IPNet
if gw.To4() != nil {
_, defNet, _ = net.ParseCIDR("0.0.0.0/0")
} else {
_, defNet, _ = net.ParseCIDR("::/0")
}
return AddRoute(defNet, gw, dev)
}

View File

@ -21,13 +21,13 @@ import (
"fmt"
"net"
"github.com/vishvananda/netlink"
"github.com/containernetworking/cni/pkg/types"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/vishvananda/netlink"
)
func ValidateExpectedInterfaceIPs(ifName string, resultIPs []*current.IPConfig) error {
// Ensure ips
for _, ips := range resultIPs {
ourAddr := netlink.Addr{IPNet: &ips.Address}
@ -49,12 +49,15 @@ func ValidateExpectedInterfaceIPs(ifName string, resultIPs []*current.IPConfig)
break
}
}
if match == false {
if !match {
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())
if err != nil {
return err
}
findGwy := &netlink.Route{Dst: ourPrefix}
routeFilter := netlink.RT_FILTER_DST
@ -77,11 +80,13 @@ func ValidateExpectedInterfaceIPs(ifName string, resultIPs []*current.IPConfig)
}
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
routeFilter := netlink.RT_FILTER_DST
if route.GW != nil {
routeFilter |= netlink.RT_FILTER_GW
}
var family int
switch {

View File

@ -16,6 +16,7 @@ package ipam
import (
"context"
"github.com/containernetworking/cni/pkg/invoke"
"github.com/containernetworking/cni/pkg/types"
)

View File

@ -19,11 +19,11 @@ import (
"net"
"os"
"github.com/vishvananda/netlink"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ip"
"github.com/containernetworking/plugins/pkg/utils/sysctl"
"github.com/vishvananda/netlink"
)
const (
@ -44,7 +44,7 @@ func ConfigureIface(ifName string, res *current.Result) error {
}
var v4gw, v6gw net.IP
var has_enabled_ipv6 bool = false
hasEnabledIpv6 := false
for _, ipc := range res.IPs {
if ipc.Interface == nil {
continue
@ -57,7 +57,7 @@ func ConfigureIface(ifName string, res *current.Result) error {
// Make sure sysctl "disable_ipv6" is 0 if we are about to add
// an IPv6 address to the interface
if !has_enabled_ipv6 && ipc.Address.IP.To4() == nil {
if !hasEnabledIpv6 && ipc.Address.IP.To4() == nil {
// Enabled IPv6 for loopback "lo" and the interface
// being configured
for _, iface := range [2]string{"lo", ifName} {
@ -79,7 +79,7 @@ func ConfigureIface(ifName string, res *current.Result) error {
return fmt.Errorf("failed to enable IPv6 for interface %q (%s=%s): %v", iface, ipv6SysctlValueName, value, err)
}
}
has_enabled_ipv6 = true
hasEnabledIpv6 = true
}
addr := &netlink.Addr{IPNet: &ipc.Address, Label: ""}

View File

@ -18,15 +18,14 @@ import (
"net"
"syscall"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/vishvananda/netlink"
"github.com/containernetworking/cni/pkg/types"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
"github.com/vishvananda/netlink"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
const LINK_NAME = "eth0"
@ -143,12 +142,12 @@ var _ = Describe("ConfigureIface", func() {
v4addrs, err := netlink.AddrList(link, syscall.AF_INET)
Expect(err).NotTo(HaveOccurred())
Expect(len(v4addrs)).To(Equal(1))
Expect(ipNetEqual(v4addrs[0].IPNet, ipv4)).To(Equal(true))
Expect(v4addrs).To(HaveLen(1))
Expect(ipNetEqual(v4addrs[0].IPNet, ipv4)).To(BeTrue())
v6addrs, err := netlink.AddrList(link, syscall.AF_INET6)
Expect(err).NotTo(HaveOccurred())
Expect(len(v6addrs)).To(Equal(2))
Expect(v6addrs).To(HaveLen(2))
var found bool
for _, a := range v6addrs {
@ -157,7 +156,7 @@ var _ = Describe("ConfigureIface", func() {
break
}
}
Expect(found).To(Equal(true))
Expect(found).To(BeTrue())
// Ensure the v4 route, v6 route, and subnet route
routes, err := netlink.RouteList(link, 0)
@ -177,8 +176,8 @@ var _ = Describe("ConfigureIface", func() {
break
}
}
Expect(v4found).To(Equal(true))
Expect(v6found).To(Equal(true))
Expect(v4found).To(BeTrue())
Expect(v6found).To(BeTrue())
return nil
})
@ -216,8 +215,8 @@ var _ = Describe("ConfigureIface", func() {
break
}
}
Expect(v4found).To(Equal(true))
Expect(v6found).To(Equal(true))
Expect(v4found).To(BeTrue())
Expect(v6found).To(BeTrue())
return nil
})

View File

@ -15,10 +15,10 @@
package ipam_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestIpam(t *testing.T) {

View File

@ -17,7 +17,7 @@ package link_test
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

View File

@ -15,8 +15,10 @@
package link
import (
"context"
"fmt"
"os"
"time"
"github.com/networkplumbing/go-nft/nft"
"github.com/networkplumbing/go-nft/nft/schema"
@ -28,8 +30,8 @@ const (
)
type NftConfigurer interface {
Apply(*nft.Config) error
Read() (*nft.Config, error)
Apply(*nft.Config) (*nft.Config, error)
Read(filterCommands ...string) (*nft.Config, error)
}
type SpoofChecker struct {
@ -37,16 +39,23 @@ type SpoofChecker struct {
macAddress string
refID string
configurer NftConfigurer
rulestore *nft.Config
}
type defaultNftConfigurer struct{}
func (_ defaultNftConfigurer) Apply(cfg *nft.Config) error {
return nft.ApplyConfig(cfg)
func (dnc defaultNftConfigurer) Apply(cfg *nft.Config) (*nft.Config, error) {
const timeout = 55 * time.Second
ctxWithTimeout, cancelFunc := context.WithTimeout(context.Background(), timeout)
defer cancelFunc()
return nft.ApplyConfigEcho(ctxWithTimeout, cfg)
}
func (_ defaultNftConfigurer) Read() (*nft.Config, error) {
return nft.ReadConfig()
func (dnc defaultNftConfigurer) Read(filterCommands ...string) (*nft.Config, error) {
const timeout = 55 * time.Second
ctxWithTimeout, cancelFunc := context.WithTimeout(context.Background(), timeout)
defer cancelFunc()
return nft.ReadConfigContext(ctxWithTimeout, filterCommands...)
}
func NewSpoofChecker(iface, macAddress, refID string) *SpoofChecker {
@ -54,7 +63,7 @@ func NewSpoofChecker(iface, macAddress, refID string) *SpoofChecker {
}
func NewSpoofCheckerWithConfigurer(iface, macAddress, refID string, configurer NftConfigurer) *SpoofChecker {
return &SpoofChecker{iface, macAddress, refID, configurer}
return &SpoofChecker{iface, macAddress, refID, configurer, nil}
}
// Setup applies nftables configuration to restrict traffic
@ -83,7 +92,7 @@ func (sc *SpoofChecker) Setup() error {
macChain := sc.macChain(ifaceChain.Name)
baseConfig.AddChain(macChain)
if err := sc.configurer.Apply(baseConfig); err != nil {
if _, err := sc.configurer.Apply(baseConfig); err != nil {
return fmt.Errorf("failed to setup spoof-check: %v", err)
}
@ -97,37 +106,51 @@ func (sc *SpoofChecker) Setup() error {
rulesConfig.AddRule(sc.matchMacRule(macChain.Name))
rulesConfig.AddRule(sc.dropRule(macChain.Name))
if err := sc.configurer.Apply(rulesConfig); err != nil {
rulestore, err := sc.configurer.Apply(rulesConfig)
if err != nil {
return fmt.Errorf("failed to setup spoof-check: %v", err)
}
sc.rulestore = rulestore
return nil
}
func (sc *SpoofChecker) findPreroutingRule(ruleToFind *schema.Rule) ([]*schema.Rule, error) {
ruleset := sc.rulestore
if ruleset == nil {
chain, err := sc.configurer.Read(listChainBridgeNatPrerouting()...)
if err != nil {
return nil, err
}
ruleset = chain
}
return ruleset.LookupRule(ruleToFind), nil
}
// Teardown removes the interface and mac-address specific chains and their rules.
// The table and base-chain are expected to survive while the base-chain rule that matches the
// interface is removed.
func (sc *SpoofChecker) Teardown() error {
ifaceChain := sc.ifaceChain()
currentConfig, ifaceMatchRuleErr := sc.configurer.Read()
if ifaceMatchRuleErr == nil {
expectedRuleToFind := sc.matchIfaceJumpToChainRule(preRoutingBaseChainName, ifaceChain.Name)
// It is safer to exclude the statement matching, avoiding cases where a current statement includes
// additional default entries (e.g. counters).
ruleToFindExcludingStatements := *expectedRuleToFind
ruleToFindExcludingStatements.Expr = nil
rules := currentConfig.LookupRule(&ruleToFindExcludingStatements)
if len(rules) > 0 {
c := nft.NewConfig()
for _, rule := range rules {
c.DeleteRule(rule)
}
if err := sc.configurer.Apply(c); err != nil {
ifaceMatchRuleErr = fmt.Errorf("failed to delete iface match rule: %v", err)
}
} else {
fmt.Fprintf(os.Stderr, "spoofcheck/teardown: unable to detect iface match rule for deletion: %+v", expectedRuleToFind)
expectedRuleToFind := sc.matchIfaceJumpToChainRule(preRoutingBaseChainName, ifaceChain.Name)
// It is safer to exclude the statement matching, avoiding cases where a current statement includes
// additional default entries (e.g. counters).
ruleToFindExcludingStatements := *expectedRuleToFind
ruleToFindExcludingStatements.Expr = nil
rules, ifaceMatchRuleErr := sc.findPreroutingRule(&ruleToFindExcludingStatements)
if ifaceMatchRuleErr == nil && len(rules) > 0 {
c := nft.NewConfig()
for _, rule := range rules {
c.DeleteRule(rule)
}
if _, err := sc.configurer.Apply(c); err != nil {
ifaceMatchRuleErr = fmt.Errorf("failed to delete iface match rule: %v", err)
}
// Drop the cache, it should contain deleted rule(s) now
sc.rulestore = nil
} else {
fmt.Fprintf(os.Stderr, "spoofcheck/teardown: unable to detect iface match rule for deletion: %+v", expectedRuleToFind)
}
regularChainsConfig := nft.NewConfig()
@ -135,7 +158,7 @@ func (sc *SpoofChecker) Teardown() error {
regularChainsConfig.DeleteChain(sc.macChain(ifaceChain.Name))
var regularChainsErr error
if err := sc.configurer.Apply(regularChainsConfig); err != nil {
if _, err := sc.configurer.Apply(regularChainsConfig); err != nil {
regularChainsErr = fmt.Errorf("failed to delete regular chains: %v", err)
}
@ -195,12 +218,10 @@ func (sc *SpoofChecker) matchMacRule(chain string) *schema.Rule {
}
func (sc *SpoofChecker) dropRule(chain string) *schema.Rule {
macRulesIndex := nft.NewRuleIndex()
return &schema.Rule{
Family: schema.FamilyBridge,
Table: natTableName,
Chain: chain,
Index: macRulesIndex.Next(),
Expr: []schema.Statement{
{Verdict: schema.Verdict{SimpleVerdict: schema.SimpleVerdict{Drop: true}}},
},
@ -208,7 +229,7 @@ func (sc *SpoofChecker) dropRule(chain string) *schema.Rule {
}
}
func (_ *SpoofChecker) baseChain() *schema.Chain {
func (sc *SpoofChecker) baseChain() *schema.Chain {
chainPriority := -300
return &schema.Chain{
Family: schema.FamilyBridge,
@ -230,7 +251,7 @@ func (sc *SpoofChecker) ifaceChain() *schema.Chain {
}
}
func (_ *SpoofChecker) macChain(ifaceChainName string) *schema.Chain {
func (sc *SpoofChecker) macChain(ifaceChainName string) *schema.Chain {
macChainName := ifaceChainName + "-mac"
return &schema.Chain{
Family: schema.FamilyBridge,
@ -243,3 +264,7 @@ func ruleComment(id string) string {
const refIDPrefix = "macspoofchk-"
return refIDPrefix + id
}
func listChainBridgeNatPrerouting() []string {
return []string{"chain", "bridge", natTableName, preRoutingBaseChainName}
}

View File

@ -16,9 +16,9 @@ package link_test
import (
"fmt"
"github.com/networkplumbing/go-nft/nft"
. "github.com/onsi/ginkgo"
"github.com/networkplumbing/go-nft/nft"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/containernetworking/plugins/pkg/link"
@ -113,13 +113,32 @@ var _ = Describe("spoofcheck", func() {
)))
})
})
Context("echo", func() {
It("succeeds, no read called", func() {
c := configurerStub{}
sc := link.NewSpoofCheckerWithConfigurer(iface, mac, id, &c)
Expect(sc.Setup()).To(Succeed())
Expect(sc.Teardown()).To(Succeed())
Expect(c.readCalled).To(BeFalse())
})
It("succeeds, fall back to config read", func() {
c := configurerStub{applyReturnNil: true}
sc := link.NewSpoofCheckerWithConfigurer(iface, mac, id, &c)
Expect(sc.Setup()).To(Succeed())
c.readConfig = c.applyConfig[0]
Expect(sc.Teardown()).To(Succeed())
Expect(c.readCalled).To(BeTrue())
})
})
})
func assertExpectedRegularChainsDeletionInTeardownConfig(action configurerStub) {
deleteRegularChainRulesJsonConfig, err := action.applyConfig[1].ToJSON()
deleteRegularChainRulesJSONConfig, err := action.applyConfig[1].ToJSON()
ExpectWithOffset(1, err).NotTo(HaveOccurred())
expectedDeleteRegularChainRulesJsonConfig := `
expectedDeleteRegularChainRulesJSONConfig := `
{"nftables": [
{"delete": {"chain": {
"family": "bridge",
@ -133,14 +152,14 @@ func assertExpectedRegularChainsDeletionInTeardownConfig(action configurerStub)
}}}
]}`
ExpectWithOffset(1, string(deleteRegularChainRulesJsonConfig)).To(MatchJSON(expectedDeleteRegularChainRulesJsonConfig))
ExpectWithOffset(1, string(deleteRegularChainRulesJSONConfig)).To(MatchJSON(expectedDeleteRegularChainRulesJSONConfig))
}
func assertExpectedBaseChainRuleDeletionInTeardownConfig(action configurerStub) {
deleteBaseChainRuleJsonConfig, err := action.applyConfig[0].ToJSON()
deleteBaseChainRuleJSONConfig, err := action.applyConfig[0].ToJSON()
Expect(err).NotTo(HaveOccurred())
expectedDeleteIfaceMatchRuleJsonConfig := `
expectedDeleteIfaceMatchRuleJSONConfig := `
{"nftables": [
{"delete": {"rule": {
"family": "bridge",
@ -157,7 +176,7 @@ func assertExpectedBaseChainRuleDeletionInTeardownConfig(action configurerStub)
"comment": "macspoofchk-container99-net1"
}}}
]}`
Expect(string(deleteBaseChainRuleJsonConfig)).To(MatchJSON(expectedDeleteIfaceMatchRuleJsonConfig))
Expect(string(deleteBaseChainRuleJSONConfig)).To(MatchJSON(expectedDeleteIfaceMatchRuleJSONConfig))
}
func rowConfigWithRulesOnly() string {
@ -254,7 +273,6 @@ func assertExpectedRulesInSetupConfig(c configurerStub) {
"comment":"macspoofchk-container99-net1"}},
{"rule":{"family":"bridge","table":"nat","chain":"cni-br-iface-container99-net1-mac",
"expr":[{"drop":null}],
"index":0,
"comment":"macspoofchk-container99-net1"}}
]}`
ExpectWithOffset(1, string(jsonConfig)).To(MatchJSON(expectedConfig))
@ -275,21 +293,28 @@ type configurerStub struct {
failFirstApplyConfig bool
failSecondApplyConfig bool
failReadConfig bool
applyReturnNil bool
readCalled bool
}
func (a *configurerStub) Apply(c *nft.Config) error {
func (a *configurerStub) Apply(c *nft.Config) (*nft.Config, error) {
a.applyCounter++
if a.failFirstApplyConfig && a.applyCounter == 1 {
return fmt.Errorf(errorFirstApplyText)
return nil, fmt.Errorf(errorFirstApplyText)
}
if a.failSecondApplyConfig && a.applyCounter == 2 {
return fmt.Errorf(errorSecondApplyText)
return nil, fmt.Errorf(errorSecondApplyText)
}
a.applyConfig = append(a.applyConfig, c)
return nil
if a.applyReturnNil {
return nil, nil
}
return c, nil
}
func (a *configurerStub) Read() (*nft.Config, error) {
func (a *configurerStub) Read(_ ...string) (*nft.Config, error) {
a.readCalled = true
if a.failReadConfig {
return nil, fmt.Errorf(errorReadText)
}

View File

@ -17,16 +17,16 @@ package ns_test
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sync"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"golang.org/x/sys/unix"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
)
func getInodeCurNetNS() (uint64, error) {
@ -182,7 +182,7 @@ var _ = Describe("Linux namespace operations", func() {
testNsInode, err := getInodeNS(targetNetNS)
Expect(err).NotTo(HaveOccurred())
Expect(testNsInode).NotTo(Equal(0))
Expect(testNsInode).NotTo(Equal(uint64(0)))
Expect(testNsInode).NotTo(Equal(origNSInode))
})
@ -208,7 +208,7 @@ var _ = Describe("Linux namespace operations", func() {
})
It("fails when the path is not a namespace", func() {
tempFile, err := ioutil.TempFile("", "nstest")
tempFile, err := os.CreateTemp("", "nstest")
Expect(err).NotTo(HaveOccurred())
defer tempFile.Close()
@ -262,7 +262,7 @@ var _ = Describe("Linux namespace operations", func() {
})
It("should refuse other paths", func() {
tempFile, err := ioutil.TempFile("", "nstest")
tempFile, err := os.CreateTemp("", "nstest")
Expect(err).NotTo(HaveOccurred())
defer tempFile.Close()

View File

@ -15,18 +15,14 @@
package ns_test
import (
"math/rand"
"runtime"
. "github.com/onsi/ginkgo"
"github.com/onsi/ginkgo/config"
. "github.com/onsi/gomega"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestNs(t *testing.T) {
rand.Seed(config.GinkgoConfig.RandomSeed)
runtime.LockOSThread()
RegisterFailHandler(Fail)

View File

@ -21,7 +21,7 @@ type BadReader struct {
Error error
}
func (r *BadReader) Read(buffer []byte) (int, error) {
func (r *BadReader) Read(_ []byte) (int, error) {
if r.Error != nil {
return 0, r.Error
}

View File

@ -15,7 +15,7 @@
package testutils
import (
"io/ioutil"
"io"
"os"
"github.com/containernetworking/cni/pkg/skel"
@ -29,6 +29,7 @@ func envCleanup() {
os.Unsetenv("CNI_NETNS")
os.Unsetenv("CNI_IFNAME")
os.Unsetenv("CNI_CONTAINERID")
os.Unsetenv("CNI_NETNS_OVERRIDE")
}
func CmdAdd(cniNetns, cniContainerID, cniIfname string, conf []byte, f func() error) (types.Result, []byte, error) {
@ -37,6 +38,7 @@ func CmdAdd(cniNetns, cniContainerID, cniIfname string, conf []byte, f func() er
os.Setenv("CNI_NETNS", cniNetns)
os.Setenv("CNI_IFNAME", cniIfname)
os.Setenv("CNI_CONTAINERID", cniContainerID)
os.Setenv("CNI_NETNS_OVERRIDE", "1")
defer envCleanup()
// Redirect stdout to capture plugin result
@ -52,7 +54,7 @@ func CmdAdd(cniNetns, cniContainerID, cniIfname string, conf []byte, f func() er
var out []byte
if err == nil {
out, err = ioutil.ReadAll(r)
out, err = io.ReadAll(r)
}
os.Stdout = oldStdout
@ -81,19 +83,20 @@ func CmdAddWithArgs(args *skel.CmdArgs, f func() error) (types.Result, []byte, e
return CmdAdd(args.Netns, args.ContainerID, args.IfName, args.StdinData, f)
}
func CmdCheck(cniNetns, cniContainerID, cniIfname string, conf []byte, f func() error) error {
func CmdCheck(cniNetns, cniContainerID, cniIfname string, f func() error) error {
os.Setenv("CNI_COMMAND", "CHECK")
os.Setenv("CNI_PATH", os.Getenv("PATH"))
os.Setenv("CNI_NETNS", cniNetns)
os.Setenv("CNI_IFNAME", cniIfname)
os.Setenv("CNI_CONTAINERID", cniContainerID)
os.Setenv("CNI_NETNS_OVERRIDE", "1")
defer envCleanup()
return f()
}
func CmdCheckWithArgs(args *skel.CmdArgs, f func() error) error {
return CmdCheck(args.Netns, args.ContainerID, args.IfName, args.StdinData, f)
return CmdCheck(args.Netns, args.ContainerID, args.IfName, f)
}
func CmdDel(cniNetns, cniContainerID, cniIfname string, f func() error) error {
@ -102,6 +105,7 @@ func CmdDel(cniNetns, cniContainerID, cniIfname string, f func() error) error {
os.Setenv("CNI_NETNS", cniNetns)
os.Setenv("CNI_IFNAME", cniIfname)
os.Setenv("CNI_CONTAINERID", cniContainerID)
os.Setenv("CNI_NETNS_OVERRIDE", "1")
defer envCleanup()
return f()

View File

@ -16,7 +16,6 @@ package testutils
import (
"fmt"
"io/ioutil"
"os"
"strings"
@ -28,7 +27,7 @@ import (
// an error if any occurs while creating/writing the file. It is the caller's
// responsibility to remove the file.
func TmpResolvConf(dnsConf types.DNS) (string, error) {
f, err := ioutil.TempFile("", "cni_test_resolv.conf")
f, err := os.CreateTemp("", "cni_test_resolv.conf")
if err != nil {
return "", fmt.Errorf("failed to get temp file for CNI test resolv.conf: %v", err)
}

View File

@ -2,12 +2,12 @@ package main_test
import (
"fmt"
"io/ioutil"
"io"
"net"
"os/exec"
"strings"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gbytes"
"github.com/onsi/gomega/gexec"
@ -74,7 +74,7 @@ var _ = Describe("Echosvr", func() {
defer conn.Close()
fmt.Fprintf(conn, "hello\n")
Expect(ioutil.ReadAll(conn)).To(Equal([]byte("hello")))
Expect(io.ReadAll(conn)).To(Equal([]byte("hello")))
})
})
@ -86,7 +86,7 @@ var _ = Describe("Echosvr", func() {
It("connects successfully using echo client", func() {
Eventually(session.Out).Should(gbytes.Say("\n"))
serverAddress := strings.TrimSpace(string(session.Out.Contents()))
fmt.Println("Server address", string(serverAddress))
fmt.Println("Server address", serverAddress)
cmd := exec.Command(clientBinaryPath, "-target", serverAddress, "-message", "hello")
clientSession, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)

View File

@ -1,10 +1,10 @@
package main_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestEchosvr(t *testing.T) {

View File

@ -1,9 +1,10 @@
// Echosvr is a simple TCP echo server
//
// It prints its listen address on stdout
// 127.0.0.1:xxxxx
// A test should wait for this line, parse it
// and may then attempt to connect.
//
// 127.0.0.1:xxxxx
// A test should wait for this line, parse it
// and may then attempt to connect.
package main
import (
@ -43,11 +44,13 @@ func main() {
// Start UDP server
addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%s", port))
if err != nil {
log.Fatalf("Error from net.ResolveUDPAddr(): %s", err)
log.Printf("Error from net.ResolveUDPAddr(): %s", err)
return
}
sock, err := net.ListenUDP("udp", addr)
if err != nil {
log.Fatalf("Error from ListenUDP(): %s", err)
log.Printf("Error from ListenUDP(): %s", err)
return
}
defer sock.Close()
@ -55,10 +58,11 @@ func main() {
for {
n, addr, err := sock.ReadFrom(buffer)
if err != nil {
log.Fatalf("Error from ReadFrom(): %s", err)
log.Printf("Error from ReadFrom(): %s", err)
return
}
sock.SetWriteDeadline(time.Now().Add(1 * time.Minute))
n, err = sock.WriteTo(buffer[0:n], addr)
_, err = sock.WriteTo(buffer[0:n], addr)
if err != nil {
return
}

View File

@ -24,8 +24,9 @@ import (
"sync"
"syscall"
"github.com/containernetworking/plugins/pkg/ns"
"golang.org/x/sys/unix"
"github.com/containernetworking/plugins/pkg/ns"
)
func getNsRunDir() string {
@ -49,7 +50,6 @@ func getNsRunDir() string {
// Creates a new persistent (bind-mounted) network namespace and returns an object
// representing that namespace, without switching to it.
func NewNS() (ns.NetNS, error) {
nsRunDir := getNsRunDir()
b := make([]byte, 16)
@ -61,7 +61,7 @@ func NewNS() (ns.NetNS, error) {
// Create the directory for mounting network namespaces
// This needs to be a shared mountpoint in case it is mounted in to
// other namespaces (containers)
err = os.MkdirAll(nsRunDir, 0755)
err = os.MkdirAll(nsRunDir, 0o755)
if err != nil {
return nil, err
}

View File

@ -29,9 +29,9 @@ func EnsureChain(ipt *iptables.IPTables, table, chain string) error {
if ipt == nil {
return errors.New("failed to ensure iptable chain: IPTables was nil")
}
exists, err := ChainExists(ipt, table, chain)
exists, err := ipt.ChainExists(table, chain)
if err != nil {
return fmt.Errorf("failed to list iptables chains: %v", err)
return fmt.Errorf("failed to check iptables chain existence: %v", err)
}
if !exists {
err = ipt.NewChain(table, chain)
@ -45,24 +45,6 @@ func EnsureChain(ipt *iptables.IPTables, table, chain string) error {
return nil
}
// ChainExists checks whether an iptables chain exists.
func ChainExists(ipt *iptables.IPTables, table, chain string) (bool, error) {
if ipt == nil {
return false, errors.New("failed to check iptable chain: IPTables was nil")
}
chains, err := ipt.ListChains(table)
if err != nil {
return false, err
}
for _, ch := range chains {
if ch == chain {
return true, nil
}
}
return false, nil
}
// DeleteRule idempotently delete the iptables rule in the specified table/chain.
// It does not return an error if the referring chain doesn't exist
func DeleteRule(ipt *iptables.IPTables, table, chain string, rulespec ...string) error {
@ -133,7 +115,6 @@ func InsertUnique(ipt *iptables.IPTables, table, chain string, prepend bool, rul
if prepend {
return ipt.Insert(table, chain, 1, rule...)
} else {
return ipt.Append(table, chain, rule...)
}
return ipt.Append(table, chain, rule...)
}

View File

@ -19,11 +19,12 @@ import (
"math/rand"
"runtime"
"github.com/coreos/go-iptables/iptables"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
"github.com/coreos/go-iptables/iptables"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
const TABLE = "filter" // We'll monkey around here
@ -34,7 +35,6 @@ var _ = Describe("chain tests", func() {
var cleanup func()
BeforeEach(func() {
// Save a reference to the original namespace,
// Add a new NS
currNs, err := ns.GetCurrentNS()
@ -60,7 +60,6 @@ var _ = Describe("chain tests", func() {
ipt.DeleteChain(TABLE, testChain)
currNs.Set()
}
})
AfterEach(func() {
@ -93,5 +92,4 @@ var _ = Describe("chain tests", func() {
Expect(err).NotTo(HaveOccurred())
})
})
})

View File

@ -16,7 +16,7 @@ package sysctl
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
@ -36,7 +36,7 @@ func Sysctl(name string, params ...string) (string, error) {
func getSysctl(name string) (string, error) {
fullName := filepath.Join("/proc/sys", toNormalName(name))
data, err := ioutil.ReadFile(fullName)
data, err := os.ReadFile(fullName)
if err != nil {
return "", err
}
@ -46,7 +46,7 @@ func getSysctl(name string) (string, error) {
func setSysctl(name, value string) (string, error) {
fullName := filepath.Join("/proc/sys", toNormalName(name))
if err := ioutil.WriteFile(fullName, []byte(value), 0644); err != nil {
if err := os.WriteFile(fullName, []byte(value), 0o644); err != nil {
return "", err
}

View File

@ -20,12 +20,13 @@ import (
"runtime"
"strings"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/vishvananda/netlink"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
"github.com/containernetworking/plugins/pkg/utils/sysctl"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vishvananda/netlink"
)
const (
@ -37,8 +38,7 @@ var _ = Describe("Sysctl tests", func() {
var testIfaceName string
var cleanup func()
BeforeEach(func() {
beforeEach := func() {
// Save a reference to the original namespace,
// Add a new NS
currNs, err := ns.GetCurrentNS()
@ -66,8 +66,7 @@ var _ = Describe("Sysctl tests", func() {
netlink.LinkDel(testIface)
currNs.Set()
}
})
}
AfterEach(func() {
cleanup()
@ -75,7 +74,8 @@ var _ = Describe("Sysctl tests", func() {
Describe("Sysctl", func() {
It("reads keys with dot separators", func() {
sysctlIfaceName := strings.Replace(testIfaceName, ".", "/", -1)
beforeEach()
sysctlIfaceName := strings.ReplaceAll(testIfaceName, ".", "/")
sysctlKey := fmt.Sprintf(sysctlDotKeyTemplate, sysctlIfaceName)
_, err := sysctl.Sysctl(sysctlKey)
@ -85,6 +85,7 @@ var _ = Describe("Sysctl tests", func() {
Describe("Sysctl", func() {
It("reads keys with slash separators", func() {
beforeEach()
sysctlKey := fmt.Sprintf(sysctlSlashKeyTemplate, testIfaceName)
_, err := sysctl.Sysctl(sysctlKey)
@ -94,7 +95,8 @@ var _ = Describe("Sysctl tests", func() {
Describe("Sysctl", func() {
It("writes keys with dot separators", func() {
sysctlIfaceName := strings.Replace(testIfaceName, ".", "/", -1)
beforeEach()
sysctlIfaceName := strings.ReplaceAll(testIfaceName, ".", "/")
sysctlKey := fmt.Sprintf(sysctlDotKeyTemplate, sysctlIfaceName)
_, err := sysctl.Sysctl(sysctlKey, "1")
@ -104,11 +106,11 @@ var _ = Describe("Sysctl tests", func() {
Describe("Sysctl", func() {
It("writes keys with slash separators", func() {
beforeEach()
sysctlKey := fmt.Sprintf(sysctlSlashKeyTemplate, testIfaceName)
_, err := sysctl.Sysctl(sysctlKey, "1")
Expect(err).NotTo(HaveOccurred())
})
})
})

View File

@ -17,7 +17,7 @@ package sysctl_test
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

View File

@ -15,10 +15,10 @@
package utils_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestUtils(t *testing.T) {

View File

@ -18,7 +18,7 @@ import (
"fmt"
"strings"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
@ -26,29 +26,29 @@ var _ = Describe("Utils", func() {
Describe("FormatChainName", func() {
It("must format a short name", func() {
chain := FormatChainName("test", "1234")
Expect(len(chain)).To(Equal(maxChainLength))
Expect(chain).To(HaveLen(maxChainLength))
Expect(chain).To(Equal("CNI-2bbe0c48b91a7d1b8a6753a8"))
})
It("must truncate a long name", func() {
chain := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
Expect(len(chain)).To(Equal(maxChainLength))
Expect(chain).To(HaveLen(maxChainLength))
Expect(chain).To(Equal("CNI-374f33fe84ab0ed84dcdebe3"))
})
It("must be predictable", func() {
chain1 := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
chain2 := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
Expect(len(chain1)).To(Equal(maxChainLength))
Expect(len(chain2)).To(Equal(maxChainLength))
Expect(chain1).To(HaveLen(maxChainLength))
Expect(chain2).To(HaveLen(maxChainLength))
Expect(chain1).To(Equal(chain2))
})
It("must change when a character changes", func() {
chain1 := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
chain2 := FormatChainName("testalongnamethatdoesnotmakesense", "1235")
Expect(len(chain1)).To(Equal(maxChainLength))
Expect(len(chain2)).To(Equal(maxChainLength))
Expect(chain1).To(HaveLen(maxChainLength))
Expect(chain2).To(HaveLen(maxChainLength))
Expect(chain1).To(Equal("CNI-374f33fe84ab0ed84dcdebe3"))
Expect(chain1).NotTo(Equal(chain2))
})
@ -57,35 +57,35 @@ var _ = Describe("Utils", func() {
Describe("MustFormatChainNameWithPrefix", func() {
It("generates a chain name with a prefix", func() {
chain := MustFormatChainNameWithPrefix("test", "1234", "PREFIX-")
Expect(len(chain)).To(Equal(maxChainLength))
Expect(chain).To(HaveLen(maxChainLength))
Expect(chain).To(Equal("CNI-PREFIX-2bbe0c48b91a7d1b8"))
})
It("must format a short name", func() {
chain := MustFormatChainNameWithPrefix("test", "1234", "PREFIX-")
Expect(len(chain)).To(Equal(maxChainLength))
Expect(chain).To(HaveLen(maxChainLength))
Expect(chain).To(Equal("CNI-PREFIX-2bbe0c48b91a7d1b8"))
})
It("must truncate a long name", func() {
chain := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-")
Expect(len(chain)).To(Equal(maxChainLength))
Expect(chain).To(HaveLen(maxChainLength))
Expect(chain).To(Equal("CNI-PREFIX-374f33fe84ab0ed84"))
})
It("must be predictable", func() {
chain1 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-")
chain2 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-")
Expect(len(chain1)).To(Equal(maxChainLength))
Expect(len(chain2)).To(Equal(maxChainLength))
Expect(chain1).To(HaveLen(maxChainLength))
Expect(chain2).To(HaveLen(maxChainLength))
Expect(chain1).To(Equal(chain2))
})
It("must change when a character changes", func() {
chain1 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-")
chain2 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1235", "PREFIX-")
Expect(len(chain1)).To(Equal(maxChainLength))
Expect(len(chain2)).To(Equal(maxChainLength))
Expect(chain1).To(HaveLen(maxChainLength))
Expect(chain2).To(HaveLen(maxChainLength))
Expect(chain1).To(Equal("CNI-PREFIX-374f33fe84ab0ed84"))
Expect(chain1).NotTo(Equal(chain2))
})
@ -161,5 +161,4 @@ var _ = Describe("Utils", func() {
)
})
})
})

View File

@ -23,7 +23,7 @@ const (
MaxDHCPLen = 576
)
//Send the Discovery Packet to the Broadcast Channel
// Send the Discovery Packet to the Broadcast Channel
func DhcpSendDiscoverPacket(c *dhcp4client.Client, options dhcp4.Options) (dhcp4.Packet, error) {
discoveryPacket := c.DiscoverPacket()
@ -35,7 +35,7 @@ func DhcpSendDiscoverPacket(c *dhcp4client.Client, options dhcp4.Options) (dhcp4
return discoveryPacket, c.SendPacket(discoveryPacket)
}
//Send Request Based On the offer Received.
// Send Request Based On the offer Received.
func DhcpSendRequest(c *dhcp4client.Client, options dhcp4.Options, offerPacket *dhcp4.Packet) (dhcp4.Packet, error) {
requestPacket := c.RequestPacket(offerPacket)
@ -48,7 +48,7 @@ func DhcpSendRequest(c *dhcp4client.Client, options dhcp4.Options, offerPacket *
return requestPacket, c.SendPacket(requestPacket)
}
//Send Decline to the received acknowledgement.
// Send Decline to the received acknowledgement.
func DhcpSendDecline(c *dhcp4client.Client, acknowledgementPacket *dhcp4.Packet, options dhcp4.Options) (dhcp4.Packet, error) {
declinePacket := c.DeclinePacket(acknowledgementPacket)
@ -61,7 +61,7 @@ func DhcpSendDecline(c *dhcp4client.Client, acknowledgementPacket *dhcp4.Packet,
return declinePacket, c.SendPacket(declinePacket)
}
//Lets do a Full DHCP Request.
// Lets do a Full DHCP Request.
func DhcpRequest(c *dhcp4client.Client, options dhcp4.Options) (bool, dhcp4.Packet, error) {
discoveryPacket, err := DhcpSendDiscoverPacket(c, options)
if err != nil {
@ -91,8 +91,8 @@ func DhcpRequest(c *dhcp4client.Client, options dhcp4.Options) (bool, dhcp4.Pack
return true, acknowledgement, nil
}
//Renew a lease backed on the Acknowledgement Packet.
//Returns Successful, The AcknoledgementPacket, Any Errors
// Renew a lease backed on the Acknowledgement Packet.
// Returns Successful, The AcknoledgementPacket, Any Errors
func DhcpRenew(c *dhcp4client.Client, acknowledgement dhcp4.Packet, options dhcp4.Options) (bool, dhcp4.Packet, error) {
renewRequest := c.RenewalRequestPacket(&acknowledgement)
@ -120,8 +120,8 @@ func DhcpRenew(c *dhcp4client.Client, acknowledgement dhcp4.Packet, options dhcp
return true, newAcknowledgement, nil
}
//Release a lease backed on the Acknowledgement Packet.
//Returns Any Errors
// Release a lease backed on the Acknowledgement Packet.
// Returns Any Errors
func DhcpRelease(c *dhcp4client.Client, acknowledgement dhcp4.Packet, options dhcp4.Options) error {
release := c.ReleasePacket(&acknowledgement)

View File

@ -15,22 +15,25 @@
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/rpc"
"os"
"os/signal"
"path/filepath"
"runtime"
"sync"
"syscall"
"time"
"github.com/coreos/go-systemd/v22/activation"
"github.com/containernetworking/cni/pkg/skel"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/coreos/go-systemd/v22/activation"
)
var errNoMoreTries = errors.New("no more tries")
@ -53,7 +56,7 @@ func newDHCP(clientTimeout, clientResendMax time.Duration) *DHCP {
}
// TODO: current client ID is too long. At least the container ID should not be used directly.
// A seperate issue is necessary to ensure no breaking change is affecting other users.
// A separate issue is necessary to ensure no breaking change is affecting other users.
func generateClientID(containerID string, netName string, ifName string) string {
clientID := containerID + "/" + netName + "/" + ifName
// defined in RFC 2132, length size can not be larger than 1 octet. So we truncate 254 to make everyone happy.
@ -77,12 +80,20 @@ func (d *DHCP) Allocate(args *skel.CmdArgs, result *current.Result) error {
}
clientID := generateClientID(args.ContainerID, conf.Name, args.IfName)
hostNetns := d.hostNetnsPrefix + args.Netns
l, err := AcquireLease(clientID, hostNetns, args.IfName,
optsRequesting, optsProviding,
d.clientTimeout, d.clientResendMax, d.broadcast)
if err != nil {
return err
// If we already have an active lease for this clientID, do not create
// another one
l := d.getLease(clientID)
if l != nil {
l.Check()
} else {
hostNetns := d.hostNetnsPrefix + args.Netns
l, err = AcquireLease(clientID, hostNetns, args.IfName,
optsRequesting, optsProviding,
d.clientTimeout, d.clientResendMax, d.broadcast)
if err != nil {
return err
}
}
ipn, err := l.IPNet()
@ -104,7 +115,7 @@ func (d *DHCP) Allocate(args *skel.CmdArgs, result *current.Result) error {
// Release stops maintenance of the lease acquired in Allocate()
// and sends a release msg to the DHCP server.
func (d *DHCP) Release(args *skel.CmdArgs, reply *struct{}) error {
func (d *DHCP) Release(args *skel.CmdArgs, _ *struct{}) error {
conf := NetConf{}
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
return fmt.Errorf("error parsing netconf: %v", err)
@ -139,7 +150,7 @@ func (d *DHCP) setLease(clientID string, l *DHCPLease) {
d.leases[clientID] = l
}
//func (d *DHCP) clearLease(contID, netName, ifName string) {
// func (d *DHCP) clearLease(contID, netName, ifName string) {
func (d *DHCP) clearLease(clientID string) {
d.mux.Lock()
defer d.mux.Unlock()
@ -156,7 +167,7 @@ func getListener(socketPath string) (net.Listener, error) {
switch {
case len(l) == 0:
if err := os.MkdirAll(filepath.Dir(socketPath), 0700); err != nil {
if err := os.MkdirAll(filepath.Dir(socketPath), 0o700); err != nil {
return nil, err
}
return net.Listen("unix", socketPath)
@ -185,7 +196,7 @@ func runDaemon(
if !filepath.IsAbs(pidfilePath) {
return fmt.Errorf("Error writing pidfile %q: path not absolute", pidfilePath)
}
if err := ioutil.WriteFile(pidfilePath, []byte(fmt.Sprintf("%d", os.Getpid())), 0644); err != nil {
if err := os.WriteFile(pidfilePath, []byte(fmt.Sprintf("%d", os.Getpid())), 0o644); err != nil {
return fmt.Errorf("Error writing pidfile %q: %v", pidfilePath, err)
}
}
@ -195,11 +206,27 @@ func runDaemon(
return fmt.Errorf("Error getting listener: %v", err)
}
srv := http.Server{}
exit := make(chan os.Signal, 1)
done := make(chan bool, 1)
signal.Notify(exit, os.Interrupt, syscall.SIGTERM)
go func() {
<-exit
srv.Shutdown(context.TODO())
os.Remove(hostPrefix + socketPath)
os.Remove(pidfilePath)
done <- true
}()
dhcp := newDHCP(dhcpClientTimeout, resendMax)
dhcp.hostNetnsPrefix = hostPrefix
dhcp.broadcast = broadcast
rpc.Register(dhcp)
rpc.HandleHTTP()
http.Serve(l, nil)
srv.Serve(l)
<-done
return nil
}

View File

@ -16,21 +16,19 @@ package main
import (
"fmt"
"net"
"os"
"os/exec"
"sync"
"time"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/vishvananda/netlink"
"github.com/containernetworking/cni/pkg/skel"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
"github.com/vishvananda/netlink"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("DHCP Multiple Lease Operations", func() {
@ -40,11 +38,10 @@ var _ = Describe("DHCP Multiple Lease Operations", func() {
var clientCmd *exec.Cmd
var socketPath string
var tmpDir string
var serverIP net.IPNet
var err error
BeforeEach(func() {
dhcpServerStopCh, serverIP, socketPath, originalNS, targetNS, err = dhcpSetupOriginalNS()
dhcpServerStopCh, socketPath, originalNS, targetNS, err = dhcpSetupOriginalNS()
Expect(err).NotTo(HaveOccurred())
// Move the container side to the container's NS
@ -64,7 +61,7 @@ var _ = Describe("DHCP Multiple Lease Operations", func() {
})
// Start the DHCP server
dhcpServerDone, err = dhcpServerStart(originalNS, net.IPv4(192, 168, 1, 5), serverIP.IP, 2, dhcpServerStopCh)
dhcpServerDone, err = dhcpServerStart(originalNS, 2, dhcpServerStopCh)
Expect(err).NotTo(HaveOccurred())
// Start the DHCP client daemon
@ -123,7 +120,7 @@ var _ = Describe("DHCP Multiple Lease Operations", func() {
addResult, err = current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(len(addResult.IPs)).To(Equal(1))
Expect(addResult.IPs).To(HaveLen(1))
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24"))
return nil
})
@ -146,7 +143,7 @@ var _ = Describe("DHCP Multiple Lease Operations", func() {
addResult, err = current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(len(addResult.IPs)).To(Equal(1))
Expect(addResult.IPs).To(HaveLen(1))
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.6/24"))
return nil
})

View File

@ -15,10 +15,10 @@
package main
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestDHCP(t *testing.T) {

View File

@ -18,7 +18,6 @@ import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net"
"os"
"os/exec"
@ -26,24 +25,22 @@ import (
"sync"
"time"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
"github.com/vishvananda/netlink"
"github.com/d2g/dhcp4"
"github.com/d2g/dhcp4server"
"github.com/d2g/dhcp4server/leasepool"
"github.com/d2g/dhcp4server/leasepool/memorypool"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/vishvananda/netlink"
"github.com/containernetworking/cni/pkg/skel"
types100 "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
)
func getTmpDir() (string, error) {
tmpDir, err := ioutil.TempDir(cniDirPrefix, "dhcp")
tmpDir, err := os.MkdirTemp(cniDirPrefix, "dhcp")
if err == nil {
tmpDir = filepath.ToSlash(tmpDir)
}
@ -51,7 +48,7 @@ func getTmpDir() (string, error) {
return tmpDir, err
}
func dhcpServerStart(netns ns.NetNS, leaseIP, serverIP net.IP, numLeases int, stopCh <-chan bool) (*sync.WaitGroup, error) {
func dhcpServerStart(netns ns.NetNS, numLeases int, stopCh <-chan bool) (*sync.WaitGroup, error) {
// Add the expected IP to the pool
lp := memorypool.MemoryPool{}
@ -121,7 +118,7 @@ const (
)
var _ = BeforeSuite(func() {
err := os.MkdirAll(cniDirPrefix, 0700)
err := os.MkdirAll(cniDirPrefix, 0o700)
Expect(err).NotTo(HaveOccurred())
})
@ -203,7 +200,7 @@ var _ = Describe("DHCP Operations", func() {
})
// Start the DHCP server
dhcpServerDone, err = dhcpServerStart(originalNS, net.IPv4(192, 168, 1, 5), serverIP.IP, 1, dhcpServerStopCh)
dhcpServerDone, err = dhcpServerStart(originalNS, 1, dhcpServerStopCh)
Expect(err).NotTo(HaveOccurred())
// Start the DHCP client daemon
@ -274,7 +271,7 @@ var _ = Describe("DHCP Operations", func() {
addResult, err = types100.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(len(addResult.IPs)).To(Equal(1))
Expect(addResult.IPs).To(HaveLen(1))
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24"))
return nil
})
@ -317,7 +314,7 @@ var _ = Describe("DHCP Operations", func() {
addResult, err = types100.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(len(addResult.IPs)).To(Equal(1))
Expect(addResult.IPs).To(HaveLen(1))
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24"))
return nil
})
@ -335,9 +332,17 @@ var _ = Describe("DHCP Operations", func() {
started.Done()
started.Wait()
err = originalNS.Do(func(ns.NetNS) error {
err := originalNS.Do(func(ns.NetNS) error {
return testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
copiedArgs := &skel.CmdArgs{
ContainerID: args.ContainerID,
Netns: args.Netns,
IfName: args.IfName,
StdinData: args.StdinData,
Path: args.Path,
Args: args.Args,
}
return cmdDel(copiedArgs)
})
})
Expect(err).NotTo(HaveOccurred())
@ -364,7 +369,7 @@ const (
contVethName1 string = "eth1"
)
func dhcpSetupOriginalNS() (chan bool, net.IPNet, string, ns.NetNS, ns.NetNS, error) {
func dhcpSetupOriginalNS() (chan bool, string, ns.NetNS, ns.NetNS, error) {
var originalNS, targetNS ns.NetNS
var dhcpServerStopCh chan bool
var socketPath string
@ -385,11 +390,6 @@ func dhcpSetupOriginalNS() (chan bool, net.IPNet, string, ns.NetNS, ns.NetNS, er
targetNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
serverIP := net.IPNet{
IP: net.IPv4(192, 168, 1, 1),
Mask: net.IPv4Mask(255, 255, 255, 0),
}
// Use (original) NS
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
@ -484,7 +484,7 @@ func dhcpSetupOriginalNS() (chan bool, net.IPNet, string, ns.NetNS, ns.NetNS, er
return nil
})
return dhcpServerStopCh, serverIP, socketPath, originalNS, targetNS, err
return dhcpServerStopCh, socketPath, originalNS, targetNS, err
}
var _ = Describe("DHCP Lease Unavailable Operations", func() {
@ -494,11 +494,10 @@ var _ = Describe("DHCP Lease Unavailable Operations", func() {
var clientCmd *exec.Cmd
var socketPath string
var tmpDir string
var serverIP net.IPNet
var err error
BeforeEach(func() {
dhcpServerStopCh, serverIP, socketPath, originalNS, targetNS, err = dhcpSetupOriginalNS()
dhcpServerStopCh, socketPath, originalNS, targetNS, err = dhcpSetupOriginalNS()
Expect(err).NotTo(HaveOccurred())
// Move the container side to the container's NS
@ -518,7 +517,7 @@ var _ = Describe("DHCP Lease Unavailable Operations", func() {
})
// Start the DHCP server
dhcpServerDone, err = dhcpServerStart(originalNS, net.IPv4(192, 168, 1, 5), serverIP.IP, 1, dhcpServerStopCh)
dhcpServerDone, err = dhcpServerStart(originalNS, 1, dhcpServerStopCh)
Expect(err).NotTo(HaveOccurred())
// Start the DHCP client daemon
@ -597,7 +596,7 @@ var _ = Describe("DHCP Lease Unavailable Operations", func() {
addResult, err = types100.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(len(addResult.IPs)).To(Equal(1))
Expect(addResult.IPs).To(HaveLen(1))
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24"))
return nil
})

View File

@ -34,13 +34,17 @@ import (
// RFC 2131 suggests using exponential backoff, starting with 4sec
// and randomized to +/- 1sec
const resendDelay0 = 4 * time.Second
const resendDelayMax = 62 * time.Second
const (
resendDelay0 = 4 * time.Second
resendDelayMax = 62 * time.Second
)
// To speed up the retry for first few failures, we retry without
// backoff for a few times
const resendFastDelay = 2 * time.Second
const resendFastMax = 4
const (
resendFastDelay = 2 * time.Second
resendFastMax = 4
)
const (
leaseStateBound = iota
@ -67,6 +71,7 @@ type DHCPLease struct {
broadcast bool
stopping uint32
stop chan struct{}
check chan struct{}
wg sync.WaitGroup
// list of requesting and providing options and if they are necessary / their value
optsRequesting map[dhcp4.OptionCode]bool
@ -78,9 +83,12 @@ var requestOptionsDefault = map[dhcp4.OptionCode]bool{
dhcp4.OptionSubnetMask: true,
}
func prepareOptions(cniArgs string, ProvideOptions []ProvideOption, RequestOptions []RequestOption) (
optsRequesting map[dhcp4.OptionCode]bool, optsProviding map[dhcp4.OptionCode][]byte, err error) {
func prepareOptions(cniArgs string, provideOptions []ProvideOption, requestOptions []RequestOption) (
map[dhcp4.OptionCode]bool, map[dhcp4.OptionCode][]byte, error,
) {
var optsRequesting map[dhcp4.OptionCode]bool
var optsProviding map[dhcp4.OptionCode][]byte
var err error
// parse CNI args
cniArgsParsed := map[string]string{}
for _, argPair := range strings.Split(cniArgs, ";") {
@ -93,23 +101,20 @@ func prepareOptions(cniArgs string, ProvideOptions []ProvideOption, RequestOptio
// parse providing options map
var optParsed dhcp4.OptionCode
optsProviding = make(map[dhcp4.OptionCode][]byte)
for _, opt := range ProvideOptions {
for _, opt := range provideOptions {
optParsed, err = parseOptionName(string(opt.Option))
if err != nil {
err = fmt.Errorf("Can not parse option %q: %w", opt.Option, err)
return
return nil, nil, fmt.Errorf("Can not parse option %q: %w", opt.Option, err)
}
if len(opt.Value) > 0 {
if len(opt.Value) > 255 {
err = fmt.Errorf("value too long for option %q: %q", opt.Option, opt.Value)
return
return nil, nil, fmt.Errorf("value too long for option %q: %q", opt.Option, opt.Value)
}
optsProviding[optParsed] = []byte(opt.Value)
}
if value, ok := cniArgsParsed[opt.ValueFromCNIArg]; ok {
if len(value) > 255 {
err = fmt.Errorf("value too long for option %q from CNI_ARGS %q: %q", opt.Option, opt.ValueFromCNIArg, opt.Value)
return
return nil, nil, fmt.Errorf("value too long for option %q from CNI_ARGS %q: %q", opt.Option, opt.ValueFromCNIArg, opt.Value)
}
optsProviding[optParsed] = []byte(value)
}
@ -118,14 +123,13 @@ func prepareOptions(cniArgs string, ProvideOptions []ProvideOption, RequestOptio
// parse necessary options map
optsRequesting = make(map[dhcp4.OptionCode]bool)
skipRequireDefault := false
for _, opt := range RequestOptions {
for _, opt := range requestOptions {
if opt.SkipDefault {
skipRequireDefault = true
}
optParsed, err = parseOptionName(string(opt.Option))
if err != nil {
err = fmt.Errorf("Can not parse option %q: %w", opt.Option, err)
return
return nil, nil, fmt.Errorf("Can not parse option %q: %w", opt.Option, err)
}
optsRequesting[optParsed] = true
}
@ -135,7 +139,7 @@ func prepareOptions(cniArgs string, ProvideOptions []ProvideOption, RequestOptio
optsRequesting[k] = v
}
}
return
return optsRequesting, optsProviding, err
}
// AcquireLease gets an DHCP lease and then maintains it in the background
@ -150,6 +154,7 @@ func AcquireLease(
l := &DHCPLease{
clientID: clientID,
stop: make(chan struct{}),
check: make(chan struct{}),
timeout: timeout,
resendMax: resendMax,
broadcast: broadcast,
@ -200,8 +205,36 @@ func (l *DHCPLease) Stop() {
l.wg.Wait()
}
func (l *DHCPLease) Check() {
l.check <- struct{}{}
}
func (l *DHCPLease) getOptionsWithClientID() dhcp4.Options {
opts := make(dhcp4.Options)
opts[dhcp4.OptionClientIdentifier] = []byte(l.clientID)
// client identifier's first byte is "type"
newClientID := []byte{0}
newClientID = append(newClientID, opts[dhcp4.OptionClientIdentifier]...)
opts[dhcp4.OptionClientIdentifier] = newClientID
return opts
}
func (l *DHCPLease) getAllOptions() dhcp4.Options {
opts := l.getOptionsWithClientID()
for k, v := range l.optsProviding {
opts[k] = v
}
opts[dhcp4.OptionParameterRequestList] = []byte{}
for k := range l.optsRequesting {
opts[dhcp4.OptionParameterRequestList] = append(opts[dhcp4.OptionParameterRequestList], byte(k))
}
return opts
}
func (l *DHCPLease) acquire() error {
c, err := newDHCPClient(l.link, l.clientID, l.timeout, l.broadcast)
c, err := newDHCPClient(l.link, l.timeout, l.broadcast)
if err != nil {
return err
}
@ -214,19 +247,7 @@ func (l *DHCPLease) acquire() error {
}
}
opts := make(dhcp4.Options)
opts[dhcp4.OptionClientIdentifier] = []byte(l.clientID)
opts[dhcp4.OptionParameterRequestList] = []byte{}
for k := range l.optsRequesting {
opts[dhcp4.OptionParameterRequestList] = append(opts[dhcp4.OptionParameterRequestList], byte(k))
}
for k, v := range l.optsProviding {
opts[k] = v
}
// client identifier's first byte is "type"
newClientID := []byte{0}
newClientID = append(newClientID, opts[dhcp4.OptionClientIdentifier]...)
opts[dhcp4.OptionClientIdentifier] = newClientID
opts := l.getAllOptions()
pkt, err := backoffRetry(l.resendMax, func() (*dhcp4.Packet, error) {
ok, ack, err := DhcpRequest(c, opts)
@ -284,7 +305,7 @@ func (l *DHCPLease) maintain() {
switch state {
case leaseStateBound:
sleepDur = l.renewalTime.Sub(time.Now())
sleepDur = time.Until(l.renewalTime)
if sleepDur <= 0 {
log.Printf("%v: renewing lease", l.clientID)
state = leaseStateRenewing
@ -296,7 +317,7 @@ func (l *DHCPLease) maintain() {
log.Printf("%v: %v", l.clientID, err)
if time.Now().After(l.rebindingTime) {
log.Printf("%v: renawal time expired, rebinding", l.clientID)
log.Printf("%v: renewal time expired, rebinding", l.clientID)
state = leaseStateRebinding
}
} else {
@ -322,6 +343,9 @@ func (l *DHCPLease) maintain() {
select {
case <-time.After(sleepDur):
case <-l.check:
log.Printf("%v: Checking lease", l.clientID)
case <-l.stop:
if err := l.release(); err != nil {
log.Printf("%v: failed to release DHCP lease: %v", l.clientID, err)
@ -338,15 +362,13 @@ func (l *DHCPLease) downIface() {
}
func (l *DHCPLease) renew() error {
c, err := newDHCPClient(l.link, l.clientID, l.timeout, l.broadcast)
c, err := newDHCPClient(l.link, l.timeout, l.broadcast)
if err != nil {
return err
}
defer c.Close()
opts := make(dhcp4.Options)
opts[dhcp4.OptionClientIdentifier] = []byte(l.clientID)
opts := l.getAllOptions()
pkt, err := backoffRetry(l.resendMax, func() (*dhcp4.Packet, error) {
ok, ack, err := DhcpRenew(c, *l.ack, opts)
switch {
@ -369,14 +391,13 @@ func (l *DHCPLease) renew() error {
func (l *DHCPLease) release() error {
log.Printf("%v: releasing lease", l.clientID)
c, err := newDHCPClient(l.link, l.clientID, l.timeout, l.broadcast)
c, err := newDHCPClient(l.link, l.timeout, l.broadcast)
if err != nil {
return err
}
defer c.Close()
opts := make(dhcp4.Options)
opts[dhcp4.OptionClientIdentifier] = []byte(l.clientID)
opts := l.getOptionsWithClientID()
if err = DhcpRelease(c, *l.ack, opts); err != nil {
return fmt.Errorf("failed to send DHCPRELEASE")
@ -406,9 +427,9 @@ func (l *DHCPLease) Routes() []*types.Route {
// RFC 3442 states that if Classless Static Routes (option 121)
// exist, we ignore Static Routes (option 33) and the Router/Gateway.
opt121_routes := parseCIDRRoutes(l.opts)
if len(opt121_routes) > 0 {
return append(routes, opt121_routes...)
opt121Routes := parseCIDRRoutes(l.opts)
if len(opt121Routes) > 0 {
return append(routes, opt121Routes...)
}
// Append Static Routes
@ -430,9 +451,9 @@ func jitter(span time.Duration) time.Duration {
}
func backoffRetry(resendMax time.Duration, f func() (*dhcp4.Packet, error)) (*dhcp4.Packet, error) {
var baseDelay time.Duration = resendDelay0
baseDelay := resendDelay0
var sleepTime time.Duration
var fastRetryLimit = resendFastMax
fastRetryLimit := resendFastMax
for {
pkt, err := f()
if err == nil {
@ -464,7 +485,7 @@ func backoffRetry(resendMax time.Duration, f func() (*dhcp4.Packet, error)) (*dh
}
func newDHCPClient(
link netlink.Link, clientID string,
link netlink.Link,
timeout time.Duration,
broadcast bool,
) (*dhcp4client.Client, error) {

View File

@ -118,27 +118,20 @@ func cmdAdd(args *skel.CmdArgs) error {
func cmdDel(args *skel.CmdArgs) error {
result := struct{}{}
if err := rpcCall("DHCP.Release", args, &result); err != nil {
return err
}
return nil
return rpcCall("DHCP.Release", args, &result)
}
func cmdCheck(args *skel.CmdArgs) error {
// Plugin must return result in same version as specified in netconf
versionDecoder := &version.ConfigDecoder{}
//confVersion, err := versionDecoder.Decode(args.StdinData)
// confVersion, err := versionDecoder.Decode(args.StdinData)
_, err := versionDecoder.Decode(args.StdinData)
if err != nil {
return err
}
result := &current.Result{CNIVersion: current.ImplementedSpecVersion}
if err := rpcCall("DHCP.Allocate", args, result); err != nil {
return err
}
return nil
return rpcCall("DHCP.Allocate", args, result)
}
func getSocketPath(stdinData []byte) (string, error) {

View File

@ -21,8 +21,9 @@ import (
"strconv"
"time"
"github.com/containernetworking/cni/pkg/types"
"github.com/d2g/dhcp4"
"github.com/containernetworking/cni/pkg/types"
)
var optionNameToID = map[string]dhcp4.OptionCode{

View File

@ -19,8 +19,9 @@ import (
"reflect"
"testing"
"github.com/containernetworking/cni/pkg/types"
"github.com/d2g/dhcp4"
"github.com/containernetworking/cni/pkg/types"
)
func validateRoutes(t *testing.T, routes []*types.Route) {

View File

@ -22,7 +22,6 @@ import (
"strconv"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ip"
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend"
)
@ -197,7 +196,7 @@ func (i *RangeIter) Next() (*net.IPNet, net.IP) {
// If we've reached the end of this range, we need to advance the range
// RangeEnd is inclusive as well
if i.cur.Equal(r.RangeEnd) {
i.rangeIdx += 1
i.rangeIdx++
i.rangeIdx %= len(*i.rangeset)
r = (*i.rangeset)[i.rangeIdx]

View File

@ -15,10 +15,10 @@
package allocator_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestAllocator(t *testing.T) {

View File

@ -18,12 +18,12 @@ import (
"fmt"
"net"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/containernetworking/cni/pkg/types"
current "github.com/containernetworking/cni/pkg/types/100"
fakestore "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
type AllocatorTestCase struct {
@ -77,7 +77,7 @@ func (t AllocatorTestCase) run(idx int) (*current.IPConfig, error) {
p = append(p, Range{Subnet: types.IPNet(*subnet)})
}
Expect(p.Canonicalize()).To(BeNil())
Expect(p.Canonicalize()).To(Succeed())
store := fakestore.NewFakeStore(t.ipmap, map[string]net.IP{"rangeid": net.ParseIP(t.lastIP)})
@ -262,7 +262,6 @@ var _ = Describe("host-local ip allocator", func() {
res, err = alloc.Get("ID", "eth0", nil)
Expect(err).ToNot(HaveOccurred())
Expect(res.Address.String()).To(Equal("192.168.1.3/29"))
})
Context("when requesting a specific IP", func() {
@ -301,7 +300,6 @@ var _ = Describe("host-local ip allocator", func() {
Expect(err).To(HaveOccurred())
})
})
})
Context("when out of ips", func() {
It("returns a meaningful error", func() {
@ -332,7 +330,7 @@ var _ = Describe("host-local ip allocator", func() {
}
for idx, tc := range testCases {
_, err := tc.run(idx)
Expect(err).NotTo(BeNil())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(HavePrefix("no IP addresses available in range set"))
}
})

View File

@ -21,7 +21,6 @@ import (
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/ip"
)
@ -43,7 +42,7 @@ type Net struct {
// IPAMConfig represents the IP related network configuration.
// This nests Range because we initially only supported a single
// range directly, and wish to preserve backwards compatability
// range directly, and wish to preserve backwards compatibility
type IPAMConfig struct {
*Range
Name string

View File

@ -17,9 +17,10 @@ package allocator
import (
"net"
"github.com/containernetworking/cni/pkg/types"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/containernetworking/cni/pkg/types"
)
var _ = Describe("IPAM config", func() {
@ -415,7 +416,6 @@ var _ = Describe("IPAM config", func() {
}`
_, _, err := LoadIPAMConfig([]byte(input), "")
Expect(err).To(MatchError("invalid range set 0: mixed address families"))
})
It("Should should error on too many ranges", func() {

View File

@ -125,7 +125,7 @@ func (r *Range) Contains(addr net.IP) bool {
// Overlaps returns true if there is any overlap between ranges
func (r *Range) Overlaps(r1 *Range) bool {
// different familes
// different families
if len(r.RangeStart) != len(r1.RangeStart) {
return false
}

View File

@ -67,10 +67,8 @@ func (s *RangeSet) Canonicalize() error {
}
if i == 0 {
fam = len((*s)[i].RangeStart)
} else {
if fam != len((*s)[i].RangeStart) {
return fmt.Errorf("mixed address families")
}
} else if fam != len((*s)[i].RangeStart) {
return fmt.Errorf("mixed address families")
}
}

View File

@ -17,7 +17,7 @@ package allocator
import (
"net"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
@ -40,7 +40,6 @@ var _ = Describe("range sets", func() {
r, err = p.RangeFor(net.IP{192, 168, 99, 99})
Expect(r).To(BeNil())
Expect(err).To(MatchError("192.168.99.99 not in range set 192.168.0.1-192.168.0.254,172.16.1.1-172.16.1.254"))
})
It("should discover overlaps within a set", func() {

View File

@ -17,11 +17,10 @@ package allocator
import (
"net"
"github.com/containernetworking/cni/pkg/types"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/containernetworking/cni/pkg/types"
)
var _ = Describe("IP ranges", func() {

View File

@ -15,7 +15,6 @@
package disk
import (
"io/ioutil"
"net"
"os"
"path/filepath"
@ -25,8 +24,10 @@ import (
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend"
)
const lastIPFilePrefix = "last_reserved_ip."
const LineBreak = "\r\n"
const (
lastIPFilePrefix = "last_reserved_ip."
LineBreak = "\r\n"
)
var defaultDataDir = "/var/lib/cni/networks"
@ -45,7 +46,7 @@ func New(network, dataDir string) (*Store, error) {
dataDir = defaultDataDir
}
dir := filepath.Join(dataDir, network)
if err := os.MkdirAll(dir, 0755); err != nil {
if err := os.MkdirAll(dir, 0o755); err != nil {
return nil, err
}
@ -59,7 +60,7 @@ func New(network, dataDir string) (*Store, error) {
func (s *Store) Reserve(id string, ifname string, ip net.IP, rangeID string) (bool, error) {
fname := GetEscapedPath(s.dataDir, ip.String())
f, err := os.OpenFile(fname, os.O_RDWR|os.O_EXCL|os.O_CREATE, 0644)
f, err := os.OpenFile(fname, os.O_RDWR|os.O_EXCL|os.O_CREATE, 0o600)
if os.IsExist(err) {
return false, nil
}
@ -77,7 +78,7 @@ func (s *Store) Reserve(id string, ifname string, ip net.IP, rangeID string) (bo
}
// store the reserved ip in lastIPFile
ipfile := GetEscapedPath(s.dataDir, lastIPFilePrefix+rangeID)
err = ioutil.WriteFile(ipfile, []byte(ip.String()), 0644)
err = os.WriteFile(ipfile, []byte(ip.String()), 0o600)
if err != nil {
return false, err
}
@ -87,25 +88,21 @@ func (s *Store) Reserve(id string, ifname string, ip net.IP, rangeID string) (bo
// LastReservedIP returns the last reserved IP if exists
func (s *Store) LastReservedIP(rangeID string) (net.IP, error) {
ipfile := GetEscapedPath(s.dataDir, lastIPFilePrefix+rangeID)
data, err := ioutil.ReadFile(ipfile)
data, err := os.ReadFile(ipfile)
if err != nil {
return nil, err
}
return net.ParseIP(string(data)), nil
}
func (s *Store) Release(ip net.IP) error {
return os.Remove(GetEscapedPath(s.dataDir, ip.String()))
}
func (s *Store) FindByKey(id string, ifname string, match string) (bool, error) {
func (s *Store) FindByKey(match string) (bool, error) {
found := false
err := filepath.Walk(s.dataDir, func(path string, info os.FileInfo, err error) error {
if err != nil || info.IsDir() {
return nil
}
data, err := ioutil.ReadFile(path)
data, err := os.ReadFile(path)
if err != nil {
return nil
}
@ -115,33 +112,31 @@ func (s *Store) FindByKey(id string, ifname string, match string) (bool, error)
return nil
})
return found, err
}
func (s *Store) FindByID(id string, ifname string) bool {
s.Lock()
defer s.Unlock()
found := false
match := strings.TrimSpace(id) + LineBreak + ifname
found, err := s.FindByKey(id, ifname, match)
found, err := s.FindByKey(match)
// Match anything created by this id
if !found && err == nil {
match := strings.TrimSpace(id)
found, err = s.FindByKey(id, ifname, match)
found, _ = s.FindByKey(match)
}
return found
}
func (s *Store) ReleaseByKey(id string, ifname string, match string) (bool, error) {
func (s *Store) ReleaseByKey(match string) (bool, error) {
found := false
err := filepath.Walk(s.dataDir, func(path string, info os.FileInfo, err error) error {
if err != nil || info.IsDir() {
return nil
}
data, err := ioutil.ReadFile(path)
data, err := os.ReadFile(path)
if err != nil {
return nil
}
@ -154,20 +149,18 @@ func (s *Store) ReleaseByKey(id string, ifname string, match string) (bool, erro
return nil
})
return found, err
}
// N.B. This function eats errors to be tolerant and
// release as much as possible
func (s *Store) ReleaseByID(id string, ifname string) error {
found := false
match := strings.TrimSpace(id) + LineBreak + ifname
found, err := s.ReleaseByKey(id, ifname, match)
found, err := s.ReleaseByKey(match)
// For backwards compatibility, look for files written by a previous version
if !found && err == nil {
match := strings.TrimSpace(id)
found, err = s.ReleaseByKey(id, ifname, match)
_, err = s.ReleaseByKey(match)
}
return err
}
@ -185,7 +178,7 @@ func (s *Store) GetByID(id string, ifname string) []net.IP {
if err != nil || info.IsDir() {
return nil
}
data, err := ioutil.ReadFile(path)
data, err := os.ReadFile(path)
if err != nil {
return nil
}
@ -203,7 +196,7 @@ func (s *Store) GetByID(id string, ifname string) []net.IP {
func GetEscapedPath(dataDir string, fname string) string {
if runtime.GOOS == "windows" {
fname = strings.Replace(fname, ":", "_", -1)
fname = strings.ReplaceAll(fname, ":", "_")
}
return filepath.Join(dataDir, fname)
}

View File

@ -15,10 +15,10 @@
package disk
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestLock(t *testing.T) {

View File

@ -15,9 +15,10 @@
package disk
import (
"github.com/alexflint/go-filemutex"
"os"
"path"
"github.com/alexflint/go-filemutex"
)
// FileLock wraps os.File to be used as a lock using flock

View File

@ -15,23 +15,22 @@
package disk
import (
"io/ioutil"
"os"
"path/filepath"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Lock Operations", func() {
It("locks a file path", func() {
dir, err := ioutil.TempDir("", "")
dir, err := os.MkdirTemp("", "")
Expect(err).ToNot(HaveOccurred())
defer os.RemoveAll(dir)
// create a dummy file to lock
path := filepath.Join(dir, "x")
f, err := os.OpenFile(path, os.O_RDONLY|os.O_CREATE, 0666)
f, err := os.OpenFile(path, os.O_RDONLY|os.O_CREATE, 0o666)
Expect(err).ToNot(HaveOccurred())
err = f.Close()
Expect(err).ToNot(HaveOccurred())
@ -47,7 +46,7 @@ var _ = Describe("Lock Operations", func() {
})
It("locks a folder path", func() {
dir, err := ioutil.TempDir("", "")
dir, err := os.MkdirTemp("", "")
Expect(err).ToNot(HaveOccurred())
defer os.RemoveAll(dir)

View File

@ -22,7 +22,6 @@ type Store interface {
Close() error
Reserve(id string, ifname string, ip net.IP, rangeID string) (bool, error)
LastReservedIP(rangeID string) (net.IP, error)
Release(ip net.IP) error
ReleaseByID(id string, ifname string) error
GetByID(id string, ifname string) []net.IP
}

View File

@ -45,7 +45,7 @@ func (s *FakeStore) Close() error {
return nil
}
func (s *FakeStore) Reserve(id string, ifname string, ip net.IP, rangeID string) (bool, error) {
func (s *FakeStore) Reserve(id string, _ string, ip net.IP, rangeID string) (bool, error) {
key := ip.String()
if _, ok := s.ipMap[key]; !ok {
s.ipMap[key] = id
@ -63,12 +63,7 @@ func (s *FakeStore) LastReservedIP(rangeID string) (net.IP, error) {
return ip, nil
}
func (s *FakeStore) Release(ip net.IP) error {
delete(s.ipMap, ip.String())
return nil
}
func (s *FakeStore) ReleaseByID(id string, ifname string) error {
func (s *FakeStore) ReleaseByID(id string, _ string) error {
toDelete := []string{}
for k, v := range s.ipMap {
if v == id {
@ -81,7 +76,7 @@ func (s *FakeStore) ReleaseByID(id string, ifname string) error {
return nil
}
func (s *FakeStore) GetByID(id string, ifname string) []net.IP {
func (s *FakeStore) GetByID(id string, _ string) []net.IP {
var ips []net.IP
for k, v := range s.ipMap {
if v == id {

View File

@ -15,12 +15,12 @@
package main
import (
"io/ioutil"
"os"
"github.com/containernetworking/cni/pkg/types"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/containernetworking/cni/pkg/types"
)
var _ = Describe("parsing resolv.conf", func() {
@ -64,7 +64,7 @@ options four
})
func parse(contents string) (*types.DNS, error) {
f, err := ioutil.TempFile("", "host_local_resolv")
f, err := os.CreateTemp("", "host_local_resolv")
if err != nil {
return nil, err
}

View File

@ -15,10 +15,10 @@
package main
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestHostLocal(t *testing.T) {

View File

@ -16,20 +16,19 @@ package main
import (
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
"strings"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/100"
types100 "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/testutils"
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/disk"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
const LineBreak = "\r\n"
@ -43,7 +42,7 @@ var _ = Describe("host-local Operations", func() {
BeforeEach(func() {
var err error
tmpDir, err = ioutil.TempDir("", "host-local_test")
tmpDir, err = os.MkdirTemp("", "host-local_test")
Expect(err).NotTo(HaveOccurred())
tmpDir = filepath.ToSlash(tmpDir)
})
@ -58,7 +57,7 @@ var _ = Describe("host-local Operations", func() {
ver := ver
It(fmt.Sprintf("[%s] allocates and releases addresses with ADD/DEL", ver), func() {
err := ioutil.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0644)
err := os.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0o644)
Expect(err).NotTo(HaveOccurred())
conf := fmt.Sprintf(`{
@ -115,7 +114,7 @@ var _ = Describe("host-local Operations", func() {
Gateway: net.ParseIP("2001:db8:1::1"),
},
))
Expect(len(result.IPs)).To(Equal(2))
Expect(result.IPs).To(HaveLen(2))
for _, expectedRoute := range []*types.Route{
{Dst: mustCIDR("0.0.0.0/0"), GW: nil},
@ -134,22 +133,22 @@ var _ = Describe("host-local Operations", func() {
}
ipFilePath1 := filepath.Join(tmpDir, "mynet", "10.1.2.2")
contents, err := ioutil.ReadFile(ipFilePath1)
contents, err := os.ReadFile(ipFilePath1)
Expect(err).NotTo(HaveOccurred())
Expect(string(contents)).To(Equal(args.ContainerID + LineBreak + ifname))
ipFilePath2 := filepath.Join(tmpDir, disk.GetEscapedPath("mynet", "2001:db8:1::2"))
contents, err = ioutil.ReadFile(ipFilePath2)
contents, err = os.ReadFile(ipFilePath2)
Expect(err).NotTo(HaveOccurred())
Expect(string(contents)).To(Equal(args.ContainerID + LineBreak + ifname))
lastFilePath1 := filepath.Join(tmpDir, "mynet", "last_reserved_ip.0")
contents, err = ioutil.ReadFile(lastFilePath1)
contents, err = os.ReadFile(lastFilePath1)
Expect(err).NotTo(HaveOccurred())
Expect(string(contents)).To(Equal("10.1.2.2"))
lastFilePath2 := filepath.Join(tmpDir, "mynet", "last_reserved_ip.1")
contents, err = ioutil.ReadFile(lastFilePath2)
contents, err = os.ReadFile(lastFilePath2)
Expect(err).NotTo(HaveOccurred())
Expect(string(contents)).To(Equal("2001:db8:1::2"))
// Release the IP
@ -167,7 +166,7 @@ var _ = Describe("host-local Operations", func() {
It(fmt.Sprintf("[%s] allocates and releases addresses on specific interface with ADD/DEL", ver), func() {
const ifname1 string = "eth1"
err := ioutil.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0644)
err := os.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0o644)
Expect(err).NotTo(HaveOccurred())
conf0 := fmt.Sprintf(`{
@ -239,12 +238,12 @@ var _ = Describe("host-local Operations", func() {
Expect(err).NotTo(HaveOccurred())
ipFilePath0 := filepath.Join(tmpDir, "mynet0", "10.1.2.2")
contents, err := ioutil.ReadFile(ipFilePath0)
contents, err := os.ReadFile(ipFilePath0)
Expect(err).NotTo(HaveOccurred())
Expect(string(contents)).To(Equal(args0.ContainerID + LineBreak + ifname))
ipFilePath1 := filepath.Join(tmpDir, "mynet1", "10.2.2.2")
contents, err = ioutil.ReadFile(ipFilePath1)
contents, err = os.ReadFile(ipFilePath1)
Expect(err).NotTo(HaveOccurred())
Expect(string(contents)).To(Equal(args1.ContainerID + LineBreak + ifname1))
@ -257,7 +256,7 @@ var _ = Describe("host-local Operations", func() {
Expect(err).To(HaveOccurred())
// reread ipFilePath1, ensure that ifname1 didn't get deleted
contents, err = ioutil.ReadFile(ipFilePath1)
contents, err = os.ReadFile(ipFilePath1)
Expect(err).NotTo(HaveOccurred())
Expect(string(contents)).To(Equal(args1.ContainerID + LineBreak + ifname1))
@ -311,7 +310,7 @@ var _ = Describe("host-local Operations", func() {
result0, err := types100.GetResult(r0)
Expect(err).NotTo(HaveOccurred())
Expect(len(result0.IPs)).Should(Equal(1))
Expect(result0.IPs).Should(HaveLen(1))
Expect(result0.IPs[0].Address.String()).Should(Equal("10.1.2.2/24"))
// Allocate the IP with the same container ID
@ -331,7 +330,7 @@ var _ = Describe("host-local Operations", func() {
result1, err := types100.GetResult(r1)
Expect(err).NotTo(HaveOccurred())
Expect(len(result1.IPs)).Should(Equal(1))
Expect(result1.IPs).Should(HaveLen(1))
Expect(result1.IPs[0].Address.String()).Should(Equal("10.1.2.3/24"))
// Allocate the IP with the same container ID again
@ -357,7 +356,7 @@ var _ = Describe("host-local Operations", func() {
})
It(fmt.Sprintf("[%s] verify DEL works on backwards compatible allocate", ver), func() {
err := ioutil.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0644)
err := os.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0o644)
Expect(err).NotTo(HaveOccurred())
conf := fmt.Sprintf(`{
@ -395,10 +394,10 @@ var _ = Describe("host-local Operations", func() {
Expect(err).NotTo(HaveOccurred())
ipFilePath := filepath.Join(tmpDir, "mynet", "10.1.2.2")
contents, err := ioutil.ReadFile(ipFilePath)
contents, err := os.ReadFile(ipFilePath)
Expect(err).NotTo(HaveOccurred())
Expect(string(contents)).To(Equal(args.ContainerID + LineBreak + ifname))
err = ioutil.WriteFile(ipFilePath, []byte(strings.TrimSpace(args.ContainerID)), 0644)
err = os.WriteFile(ipFilePath, []byte(strings.TrimSpace(args.ContainerID)), 0o644)
Expect(err).NotTo(HaveOccurred())
err = testutils.CmdDelWithArgs(args, func() error {
@ -466,7 +465,7 @@ var _ = Describe("host-local Operations", func() {
Expect(err).NotTo(HaveOccurred())
ipFilePath := filepath.Join(tmpDir, "mynet", result.IPs[0].Address.IP.String())
contents, err := ioutil.ReadFile(ipFilePath)
contents, err := os.ReadFile(ipFilePath)
Expect(err).NotTo(HaveOccurred())
Expect(string(contents)).To(Equal("dummy" + LineBreak + ifname))
@ -505,7 +504,7 @@ var _ = Describe("host-local Operations", func() {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
Expect(strings.Index(string(out), "Error retriving last reserved ip")).To(Equal(-1))
Expect(strings.Index(string(out), "Error retrieving last reserved ip")).To(Equal(-1))
})
It(fmt.Sprintf("[%s] allocates a custom IP when requested by config args", ver), func() {
@ -547,7 +546,7 @@ var _ = Describe("host-local Operations", func() {
})
It(fmt.Sprintf("[%s] allocates custom IPs from multiple ranges", ver), func() {
err := ioutil.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0644)
err := os.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0o644)
Expect(err).NotTo(HaveOccurred())
conf := fmt.Sprintf(`{
@ -595,7 +594,7 @@ var _ = Describe("host-local Operations", func() {
})
It(fmt.Sprintf("[%s] allocates custom IPs from multiple protocols", ver), func() {
err := ioutil.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0644)
err := os.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0o644)
Expect(err).NotTo(HaveOccurred())
conf := fmt.Sprintf(`{

View File

@ -19,14 +19,13 @@ import (
"net"
"strings"
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/disk"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version"
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/disk"
)
func main() {
@ -34,7 +33,6 @@ func main() {
}
func cmdCheck(args *skel.CmdArgs) error {
ipamConf, _, err := allocator.LoadIPAMConfig(args.StdinData, args.Args)
if err != nil {
return err
@ -48,8 +46,8 @@ func cmdCheck(args *skel.CmdArgs) error {
}
defer store.Close()
containerIpFound := store.FindByID(args.ContainerID, args.IfName)
if containerIpFound == false {
containerIPFound := store.FindByID(args.ContainerID, args.IfName)
if !containerIPFound {
return fmt.Errorf("host-local: Failed to find address added by container %v", args.ContainerID)
}
@ -84,7 +82,7 @@ func cmdAdd(args *skel.CmdArgs) error {
// Store all requested IPs in a map, so we can easily remove ones we use
// and error if some remain
requestedIPs := map[string]net.IP{} //net.IP cannot be a key
requestedIPs := map[string]net.IP{} // net.IP cannot be a key
for _, ip := range ipamConf.IPArgs {
requestedIPs[ip.String()] = ip

View File

@ -276,7 +276,7 @@ func cmdAdd(args *skel.CmdArgs) error {
return types.PrintResult(result, confVersion)
}
func cmdDel(args *skel.CmdArgs) error {
func cmdDel(_ *skel.CmdArgs) error {
// Nothing required because of no resource allocation in static plugin.
return nil
}

View File

@ -17,7 +17,7 @@ package main_test
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

View File

@ -19,13 +19,13 @@ import (
"net"
"strings"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
types100 "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/testutils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("static Operations", func() {
@ -97,7 +97,7 @@ var _ = Describe("static Operations", func() {
Gateway: net.ParseIP("3ffe:ffff:0::1"),
},
))
Expect(len(result.IPs)).To(Equal(2))
Expect(result.IPs).To(HaveLen(2))
Expect(result.Routes).To(Equal([]*types.Route{
{Dst: mustCIDR("0.0.0.0/0")},
@ -206,7 +206,7 @@ var _ = Describe("static Operations", func() {
Gateway: net.ParseIP("10.10.0.254"),
}))
Expect(len(result.IPs)).To(Equal(1))
Expect(result.IPs).To(HaveLen(1))
Expect(result.Routes).To(Equal([]*types.Route{
{Dst: mustCIDR("0.0.0.0/0")},
@ -272,7 +272,7 @@ var _ = Describe("static Operations", func() {
Gateway: nil,
}))
Expect(len(result.IPs)).To(Equal(2))
Expect(result.IPs).To(HaveLen(2))
// Release the IP
err = testutils.CmdDelWithArgs(args, func() error {
@ -337,7 +337,7 @@ var _ = Describe("static Operations", func() {
Address: mustCIDR("3ffe:ffff:0:01ff::1/64"),
},
))
Expect(len(result.IPs)).To(Equal(2))
Expect(result.IPs).To(HaveLen(2))
Expect(result.Routes).To(Equal([]*types.Route{
{Dst: mustCIDR("0.0.0.0/0"), GW: net.ParseIP("10.10.0.254")},
{Dst: mustCIDR("3ffe:ffff:0:01ff::1/64"), GW: net.ParseIP("3ffe:ffff:0::1")},
@ -407,7 +407,7 @@ var _ = Describe("static Operations", func() {
Address: mustCIDR("3ffe:ffff:0:01ff::1/64"),
},
))
Expect(len(result.IPs)).To(Equal(2))
Expect(result.IPs).To(HaveLen(2))
Expect(result.Routes).To(Equal([]*types.Route{
{Dst: mustCIDR("0.0.0.0/0"), GW: net.ParseIP("10.10.0.254")},
{Dst: mustCIDR("3ffe:ffff:0:01ff::1/64"), GW: net.ParseIP("3ffe:ffff:0::1")},
@ -482,7 +482,7 @@ var _ = Describe("static Operations", func() {
Address: mustCIDR("3ffe:ffff:0:01ff::1/64"),
},
))
Expect(len(result.IPs)).To(Equal(2))
Expect(result.IPs).To(HaveLen(2))
Expect(result.Routes).To(Equal([]*types.Route{
{Dst: mustCIDR("0.0.0.0/0"), GW: net.ParseIP("10.10.0.254")},
{Dst: mustCIDR("3ffe:ffff:0:01ff::1/64"), GW: net.ParseIP("3ffe:ffff:0::1")},

View File

@ -6,6 +6,7 @@ plugins/main/loopback
plugins/main/macvlan
plugins/main/ptp
plugins/main/vlan
plugins/main/dummy
plugins/meta/portmap
plugins/meta/tuning
plugins/meta/bandwidth

View File

@ -21,6 +21,7 @@ import (
"net"
"os"
"runtime"
"sort"
"syscall"
"time"
@ -46,17 +47,19 @@ const defaultBrName = "cni0"
type NetConf struct {
types.NetConf
BrName string `json:"bridge"`
IsGW bool `json:"isGateway"`
IsDefaultGW bool `json:"isDefaultGateway"`
ForceAddress bool `json:"forceAddress"`
IPMasq bool `json:"ipMasq"`
MTU int `json:"mtu"`
HairpinMode bool `json:"hairpinMode"`
PromiscMode bool `json:"promiscMode"`
Vlan int `json:"vlan"`
MacSpoofChk bool `json:"macspoofchk,omitempty"`
EnableDad bool `json:"enabledad,omitempty"`
BrName string `json:"bridge"`
IsGW bool `json:"isGateway"`
IsDefaultGW bool `json:"isDefaultGateway"`
ForceAddress bool `json:"forceAddress"`
IPMasq bool `json:"ipMasq"`
MTU int `json:"mtu"`
HairpinMode bool `json:"hairpinMode"`
PromiscMode bool `json:"promiscMode"`
Vlan int `json:"vlan"`
VlanTrunk []*VlanTrunk `json:"vlanTrunk,omitempty"`
PreserveDefaultVlan bool `json:"preserveDefaultVlan"`
MacSpoofChk bool `json:"macspoofchk,omitempty"`
EnableDad bool `json:"enabledad,omitempty"`
Args struct {
Cni BridgeArgs `json:"cni,omitempty"`
@ -65,7 +68,14 @@ type NetConf struct {
Mac string `json:"mac,omitempty"`
} `json:"runtimeConfig,omitempty"`
mac string
mac string
vlans []int
}
type VlanTrunk struct {
MinID *int `json:"minID,omitempty"`
MaxID *int `json:"maxID,omitempty"`
ID *int `json:"id,omitempty"`
}
type BridgeArgs struct {
@ -94,6 +104,8 @@ func init() {
func loadNetConf(bytes []byte, envArgs string) (*NetConf, string, error) {
n := &NetConf{
BrName: defaultBrName,
// Set default value equal to true to maintain existing behavior.
PreserveDefaultVlan: true,
}
if err := json.Unmarshal(bytes, n); err != nil {
return nil, "", fmt.Errorf("failed to load netconf: %v", err)
@ -101,6 +113,17 @@ func loadNetConf(bytes []byte, envArgs string) (*NetConf, string, error) {
if n.Vlan < 0 || n.Vlan > 4094 {
return nil, "", fmt.Errorf("invalid VLAN ID %d (must be between 0 and 4094)", n.Vlan)
}
var err error
n.vlans, err = collectVlanTrunk(n.VlanTrunk)
if err != nil {
// fail to parsing
return nil, "", err
}
// Currently bridge CNI only support access port(untagged only) or trunk port(tagged only)
if n.Vlan > 0 && n.vlans != nil {
return nil, "", errors.New("cannot set vlan and vlanTrunk at the same time")
}
if envArgs != "" {
e := MacEnvArgs{}
@ -124,12 +147,66 @@ func loadNetConf(bytes []byte, envArgs string) (*NetConf, string, error) {
return n, n.CNIVersion, nil
}
// This method is copied from https://github.com/k8snetworkplumbingwg/ovs-cni/blob/v0.27.2/pkg/plugin/plugin.go
func collectVlanTrunk(vlanTrunk []*VlanTrunk) ([]int, error) {
if vlanTrunk == nil {
return nil, nil
}
vlanMap := make(map[int]struct{})
for _, item := range vlanTrunk {
var minID int
var maxID int
var ID int
switch {
case item.MinID != nil && item.MaxID != nil:
minID = *item.MinID
if minID <= 0 || minID > 4094 {
return nil, errors.New("incorrect trunk minID parameter")
}
maxID = *item.MaxID
if maxID <= 0 || maxID > 4094 {
return nil, errors.New("incorrect trunk maxID parameter")
}
if maxID < minID {
return nil, errors.New("minID is greater than maxID in trunk parameter")
}
for v := minID; v <= maxID; v++ {
vlanMap[v] = struct{}{}
}
case item.MinID == nil && item.MaxID != nil:
return nil, errors.New("minID and maxID should be configured simultaneously, minID is missing")
case item.MinID != nil && item.MaxID == nil:
return nil, errors.New("minID and maxID should be configured simultaneously, maxID is missing")
}
// single vid
if item.ID != nil {
ID = *item.ID
if ID <= 0 || ID > 4094 {
return nil, errors.New("incorrect trunk id parameter")
}
vlanMap[ID] = struct{}{}
}
}
if len(vlanMap) == 0 {
return nil, nil
}
vlans := make([]int, 0, len(vlanMap))
for k := range vlanMap {
vlans = append(vlans, k)
}
sort.Slice(vlans, func(i int, j int) bool { return vlans[i] < vlans[j] })
return vlans, nil
}
// calcGateways processes the results from the IPAM plugin and does the
// following for each IP family:
// - Calculates and compiles a list of gateway addresses
// - Adds a default route if needed
// - Calculates and compiles a list of gateway addresses
// - Adds a default route if needed
func calcGateways(result *current.Result, n *NetConf) (*gwInfo, *gwInfo, error) {
gwsV4 := &gwInfo{}
gwsV6 := &gwInfo{}
@ -300,8 +377,8 @@ func ensureBridge(brName string, mtu int, promiscMode, vlanFiltering bool) (*net
return br, nil
}
func ensureVlanInterface(br *netlink.Bridge, vlanId int) (netlink.Link, error) {
name := fmt.Sprintf("%s.%d", br.Name, vlanId)
func ensureVlanInterface(br *netlink.Bridge, vlanID int, preserveDefaultVlan bool) (netlink.Link, error) {
name := fmt.Sprintf("%s.%d", br.Name, vlanID)
brGatewayVeth, err := netlink.LinkByName(name)
if err != nil {
@ -314,7 +391,7 @@ func ensureVlanInterface(br *netlink.Bridge, vlanId int) (netlink.Link, error) {
return nil, fmt.Errorf("faild to find host namespace: %v", err)
}
_, brGatewayIface, err := setupVeth(hostNS, br, name, br.MTU, false, vlanId, "")
_, brGatewayIface, err := setupVeth(hostNS, br, name, br.MTU, false, vlanID, nil, preserveDefaultVlan, "")
if err != nil {
return nil, fmt.Errorf("faild to create vlan gateway %q: %v", name, err)
}
@ -333,7 +410,7 @@ func ensureVlanInterface(br *netlink.Bridge, vlanId int) (netlink.Link, error) {
return brGatewayVeth, nil
}
func setupVeth(netns ns.NetNS, br *netlink.Bridge, ifName string, mtu int, hairpinMode bool, vlanID int, mac string) (*current.Interface, *current.Interface, error) {
func setupVeth(netns ns.NetNS, br *netlink.Bridge, ifName string, mtu int, hairpinMode bool, vlanID int, vlans []int, preserveDefaultVlan bool, mac string) (*current.Interface, *current.Interface, error) {
contIface := &current.Interface{}
hostIface := &current.Interface{}
@ -370,6 +447,14 @@ func setupVeth(netns ns.NetNS, br *netlink.Bridge, ifName string, mtu int, hairp
return nil, nil, fmt.Errorf("failed to setup hairpin mode for %v: %v", hostVeth.Attrs().Name, err)
}
if (vlanID != 0 || len(vlans) > 0) && !preserveDefaultVlan {
err = removeDefaultVlan(hostVeth)
if err != nil {
return nil, nil, fmt.Errorf("failed to remove default vlan on interface %q: %v", hostIface.Name, err)
}
}
// Currently bridge CNI only support access port(untagged only) or trunk port(tagged only)
if vlanID != 0 {
err = netlink.BridgeVlanAdd(hostVeth, uint16(vlanID), true, true, false, true)
if err != nil {
@ -377,9 +462,35 @@ func setupVeth(netns ns.NetNS, br *netlink.Bridge, ifName string, mtu int, hairp
}
}
for _, v := range vlans {
err = netlink.BridgeVlanAdd(hostVeth, uint16(v), false, false, false, true)
if err != nil {
return nil, nil, fmt.Errorf("failed to setup vlan tag on interface %q: %w", hostIface.Name, err)
}
}
return hostIface, contIface, nil
}
func removeDefaultVlan(hostVeth netlink.Link) error {
vlanInfo, err := netlink.BridgeVlanList()
if err != nil {
return err
}
brVlanInfo, ok := vlanInfo[int32(hostVeth.Attrs().Index)]
if ok {
for _, info := range brVlanInfo {
err = netlink.BridgeVlanDel(hostVeth, info.Vid, false, false, false, true)
if err != nil {
return err
}
}
}
return nil
}
func calcGatewayIP(ipn *net.IPNet) net.IP {
nid := ipn.IP.Mask(ipn.Mask)
return ip.NextIP(nid)
@ -387,7 +498,7 @@ func calcGatewayIP(ipn *net.IPNet) net.IP {
func setupBridge(n *NetConf) (*netlink.Bridge, *current.Interface, error) {
vlanFiltering := false
if n.Vlan != 0 {
if n.Vlan != 0 || n.VlanTrunk != nil {
vlanFiltering = true
}
// create bridge if necessary
@ -410,7 +521,7 @@ func enableIPForward(family int) error {
}
func cmdAdd(args *skel.CmdArgs) error {
var success bool = false
success := false
n, cniVersion, err := loadNetConf(args.StdinData, args.Args)
if err != nil {
@ -424,7 +535,7 @@ func cmdAdd(args *skel.CmdArgs) error {
}
if n.HairpinMode && n.PromiscMode {
return fmt.Errorf("cannot set hairpin mode and promiscuous mode at the same time.")
return fmt.Errorf("cannot set hairpin mode and promiscuous mode at the same time")
}
br, brInterface, err := setupBridge(n)
@ -438,7 +549,7 @@ func cmdAdd(args *skel.CmdArgs) error {
}
defer netns.Close()
hostInterface, containerInterface, err := setupVeth(netns, br, args.IfName, n.MTU, n.HairpinMode, n.Vlan, n.mac)
hostInterface, containerInterface, err := setupVeth(netns, br, args.IfName, n.MTU, n.HairpinMode, n.Vlan, n.vlans, n.PreserveDefaultVlan, n.mac)
if err != nil {
return err
}
@ -489,6 +600,7 @@ func cmdAdd(args *skel.CmdArgs) error {
result.IPs = ipamResult.IPs
result.Routes = ipamResult.Routes
result.DNS = ipamResult.DNS
if len(result.IPs) == 0 {
return errors.New("IPAM plugin returned missing IP config")
@ -511,50 +623,27 @@ func cmdAdd(args *skel.CmdArgs) error {
_, _ = sysctl.Sysctl(fmt.Sprintf("net/ipv4/conf/%s/arp_notify", args.IfName), "1")
// Add the IP to the interface
if err := ipam.ConfigureIface(args.IfName, result); err != nil {
return err
}
return nil
return ipam.ConfigureIface(args.IfName, result)
}); err != nil {
return err
}
// check bridge port state
retries := []int{0, 50, 500, 1000, 1000}
for idx, sleep := range retries {
time.Sleep(time.Duration(sleep) * time.Millisecond)
hostVeth, err := netlink.LinkByName(hostInterface.Name)
if err != nil {
return err
}
if hostVeth.Attrs().OperState == netlink.OperUp {
break
}
if idx == len(retries)-1 {
return fmt.Errorf("bridge port in error state: %s", hostVeth.Attrs().OperState)
}
}
if n.IsGW {
var firstV4Addr net.IP
var vlanInterface *current.Interface
// Set the IP address(es) on the bridge and enable forwarding
for _, gws := range []*gwInfo{gwsV4, gwsV6} {
for _, gw := range gws.gws {
if gw.IP.To4() != nil && firstV4Addr == nil {
firstV4Addr = gw.IP
}
if n.Vlan != 0 {
vlanIface, err := ensureVlanInterface(br, n.Vlan)
vlanIface, err := ensureVlanInterface(br, n.Vlan, n.PreserveDefaultVlan)
if err != nil {
return fmt.Errorf("failed to create vlan interface: %v", err)
}
if vlanInterface == nil {
vlanInterface = &current.Interface{Name: vlanIface.Attrs().Name,
Mac: vlanIface.Attrs().HardwareAddr.String()}
vlanInterface = &current.Interface{
Name: vlanIface.Attrs().Name,
Mac: vlanIface.Attrs().HardwareAddr.String(),
}
result.Interfaces = append(result.Interfaces, vlanInterface)
}
@ -603,6 +692,29 @@ func cmdAdd(args *skel.CmdArgs) error {
}
}
var hostVeth netlink.Link
// check bridge port state
retries := []int{0, 50, 500, 1000, 1000}
for idx, sleep := range retries {
time.Sleep(time.Duration(sleep) * time.Millisecond)
hostVeth, err = netlink.LinkByName(hostInterface.Name)
if err != nil {
return err
}
if hostVeth.Attrs().OperState == netlink.OperUp {
break
}
if idx == len(retries)-1 {
return fmt.Errorf("bridge port in error state: %s", hostVeth.Attrs().OperState)
}
}
// In certain circumstances, the host-side of the veth may change addrs
hostInterface.Mac = hostVeth.Attrs().HardwareAddr.String()
// Refetch the bridge since its MAC address may change when the first
// veth is added or after its IP address is set
br, err = bridgeByName(n.BrName)
@ -611,18 +723,29 @@ func cmdAdd(args *skel.CmdArgs) error {
}
brInterface.Mac = br.Attrs().HardwareAddr.String()
result.DNS = n.DNS
// Return an error requested by testcases, if any
if debugPostIPAMError != nil {
return debugPostIPAMError
}
// Use incoming DNS settings if provided, otherwise use the
// settings that were already configured by the IPAM plugin
if dnsConfSet(n.DNS) {
result.DNS = n.DNS
}
success = true
return types.PrintResult(result, cniVersion)
}
func dnsConfSet(dnsConf types.DNS) bool {
return dnsConf.Nameservers != nil ||
dnsConf.Search != nil ||
dnsConf.Options != nil ||
dnsConf.Domain != ""
}
func cmdDel(args *skel.CmdArgs) error {
n, _, err := loadNetConf(args.StdinData, args.Args)
if err != nil {
@ -631,14 +754,17 @@ func cmdDel(args *skel.CmdArgs) error {
isLayer3 := n.IPAM.Type != ""
if isLayer3 {
if err := ipam.ExecDel(n.IPAM.Type, args.StdinData); err != nil {
return err
ipamDel := func() error {
if isLayer3 {
if err := ipam.ExecDel(n.IPAM.Type, args.StdinData); err != nil {
return err
}
}
return nil
}
if args.Netns == "" {
return nil
return ipamDel()
}
// There is a netns so try to clean up. Delete can be called multiple times
@ -660,11 +786,16 @@ func cmdDel(args *skel.CmdArgs) error {
// https://github.com/kubernetes/kubernetes/issues/43014#issuecomment-287164444
_, ok := err.(ns.NSPathNotExistErr)
if ok {
return nil
return ipamDel()
}
return err
}
// call ipam.ExecDel after clean up device in netns
if err := ipamDel(); err != nil {
return err
}
if n.MacSpoofChk {
sc := link.NewSpoofChecker("", "", uniqueID(args.ContainerID, args.IfName))
if err := sc.Teardown(); err != nil {
@ -698,7 +829,6 @@ type cniBridgeIf struct {
}
func validateInterface(intf current.Interface, expectInSb bool) (cniBridgeIf, netlink.Link, error) {
ifFound := cniBridgeIf{found: false}
if intf.Name == "" {
return ifFound, nil, fmt.Errorf("Interface name missing ")
@ -723,7 +853,6 @@ func validateInterface(intf current.Interface, expectInSb bool) (cniBridgeIf, ne
}
func validateCniBrInterface(intf current.Interface, n *NetConf) (cniBridgeIf, error) {
brFound, link, err := validateInterface(intf, false)
if err != nil {
return brFound, err
@ -755,7 +884,6 @@ func validateCniBrInterface(intf current.Interface, n *NetConf) (cniBridgeIf, er
}
func validateCniVethInterface(intf *current.Interface, brIf cniBridgeIf, contIf cniBridgeIf) (cniBridgeIf, error) {
vethFound, link, err := validateInterface(*intf, false)
if err != nil {
return vethFound, err
@ -799,7 +927,6 @@ func validateCniVethInterface(intf *current.Interface, brIf cniBridgeIf, contIf
}
func validateCniContainerInterface(intf current.Interface) (cniBridgeIf, error) {
vethFound, link, err := validateInterface(intf, true)
if err != nil {
return vethFound, err
@ -828,7 +955,6 @@ func validateCniContainerInterface(intf current.Interface) (cniBridgeIf, error)
}
func cmdCheck(args *skel.CmdArgs) error {
n, _, err := loadNetConf(args.StdinData, args.Args)
if err != nil {
return err
@ -935,7 +1061,7 @@ func cmdCheck(args *skel.CmdArgs) error {
}
// Check prevResults for ips, routes and dns against values found in the container
if err := netns.Do(func(_ ns.NetNS) error {
return netns.Do(func(_ ns.NetNS) error {
err = ip.ValidateExpectedInterfaceIPs(args.IfName, result.IPs)
if err != nil {
return err
@ -946,11 +1072,7 @@ func cmdCheck(args *skel.CmdArgs) error {
return err
}
return nil
}); err != nil {
return err
}
return nil
})
}
func uniqueID(containerID, cniIface string) string {

View File

@ -15,13 +15,26 @@
package main
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var (
resolvConf string
err error
)
func TestBridge(t *testing.T) {
RegisterFailHandler(Fail)
resolvConf, err = newResolvConf()
Expect(err).NotTo(HaveOccurred())
RunSpecs(t, "plugins/main/bridge")
}
var _ = AfterSuite(func() {
deleteResolvConf(resolvConf)
})

View File

@ -17,13 +17,15 @@ package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net"
"os"
"strings"
"github.com/coreos/go-iptables/iptables"
"github.com/networkplumbing/go-nft/nft"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netlink/nl"
"github.com/containernetworking/cni/pkg/skel"
@ -33,18 +35,14 @@ import (
"github.com/containernetworking/plugins/pkg/ip"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
"github.com/vishvananda/netlink"
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
const (
BRNAME = "bridge0"
BRNAMEVLAN = "bridge0.100"
IFNAME = "eth0"
NAMESERVER = "192.0.2.0"
)
type Net struct {
@ -53,10 +51,11 @@ type Net struct {
Type string `json:"type,omitempty"`
BrName string `json:"bridge"`
IPAM *allocator.IPAMConfig `json:"ipam"`
//RuntimeConfig struct { // The capability arg
// RuntimeConfig struct { // The capability arg
// IPRanges []RangeSet `json:"ipRanges,omitempty"`
//} `json:"runtimeConfig,omitempty"`
//Args *struct {
// Args *struct {
// } `json:"runtimeConfig,omitempty"`
// A *IPAMArgs `json:"cni"`
DNS types.DNS `json:"dns"`
RawPrevResult map[string]interface{} `json:"prevResult,omitempty"`
@ -66,20 +65,23 @@ type Net struct {
// testCase defines the CNI network configuration and the expected
// bridge addresses for a test case.
type testCase struct {
cniVersion string // CNI Version
subnet string // Single subnet config: Subnet CIDR
gateway string // Single subnet config: Gateway
ranges []rangeInfo // Ranges list (multiple subnets config)
isGW bool
isLayer2 bool
expGWCIDRs []string // Expected gateway addresses in CIDR form
vlan int
ipMasq bool
macspoofchk bool
AddErr020 string
DelErr020 string
AddErr010 string
DelErr010 string
cniVersion string // CNI Version
subnet string // Single subnet config: Subnet CIDR
gateway string // Single subnet config: Gateway
ranges []rangeInfo // Ranges list (multiple subnets config)
resolvConf string // host-local resolvConf file path
isGW bool
isLayer2 bool
expGWCIDRs []string // Expected gateway addresses in CIDR form
vlan int
vlanTrunk []*VlanTrunk
removeDefaultVlan bool
ipMasq bool
macspoofchk bool
AddErr020 string
DelErr020 string
AddErr010 string
DelErr010 string
envArgs string // CNI_ARGS
runtimeConfig struct {
@ -129,6 +131,26 @@ const (
vlan = `,
"vlan": %d`
vlanTrunkStartStr = `,
"vlanTrunk": [`
vlanTrunk = `
{
"id": %d
}`
vlanTrunkRange = `
{
"minID": %d,
"maxID": %d
}`
vlanTrunkEndStr = `
]`
preserveDefaultVlan = `,
"preserveDefaultVlan": false`
netDefault = `,
"isDefaultGateway": true`
@ -139,6 +161,9 @@ const (
ipamDataDirStr = `,
"dataDir": "%s"`
ipamResolvConfStr = `,
"resolvConf": "%s"`
ipMasqConfStr = `,
"ipMasq": %t`
@ -188,7 +213,28 @@ func (tc testCase) netConfJSON(dataDir string) string {
conf := fmt.Sprintf(netConfStr, tc.cniVersion, BRNAME)
if tc.vlan != 0 {
conf += fmt.Sprintf(vlan, tc.vlan)
if tc.removeDefaultVlan {
conf += preserveDefaultVlan
}
}
if tc.isLayer2 && tc.vlanTrunk != nil {
conf += vlanTrunkStartStr
for i, vlan := range tc.vlanTrunk {
if i > 0 {
conf += ","
}
if vlan.ID != nil {
conf += fmt.Sprintf(vlanTrunk, *vlan.ID)
}
if vlan.MinID != nil && vlan.MaxID != nil {
conf += fmt.Sprintf(vlanTrunkRange, *vlan.MinID, *vlan.MaxID)
}
}
conf += vlanTrunkEndStr
}
if tc.ipMasq {
conf += tc.ipMasqConfig()
}
@ -215,6 +261,9 @@ func (tc testCase) netConfJSON(dataDir string) string {
if tc.ranges != nil {
conf += tc.rangesConfig()
}
if tc.resolvConf != "" {
conf += tc.resolvConfConfig()
}
conf += ipamEndStr
}
} else {
@ -252,13 +301,33 @@ func (tc testCase) rangesConfig() string {
return conf + rangesEndStr
}
func (tc testCase) resolvConfConfig() string {
conf := fmt.Sprintf(ipamResolvConfStr, tc.resolvConf)
return conf
}
func newResolvConf() (string, error) {
f, err := os.CreateTemp("", "host_local_resolv")
if err != nil {
return "", err
}
defer f.Close()
name := f.Name()
_, err = f.WriteString(fmt.Sprintf("nameserver %s", NAMESERVER))
return name, err
}
func deleteResolvConf(path string) error {
return os.Remove(path)
}
var counter uint
// createCmdArgs generates network configuration and creates command
// arguments for a test case.
func (tc testCase) createCmdArgs(targetNS ns.NetNS, dataDir string) *skel.CmdArgs {
conf := tc.netConfJSON(dataDir)
//defer func() { counter += 1 }()
// defer func() { counter += 1 }()
return &skel.CmdArgs{
ContainerID: fmt.Sprintf("dummy-%d", counter),
Netns: targetNS.Path(),
@ -270,18 +339,17 @@ func (tc testCase) createCmdArgs(targetNS ns.NetNS, dataDir string) *skel.CmdArg
// createCheckCmdArgs generates network configuration and creates command
// arguments for a Check test case.
func (tc testCase) createCheckCmdArgs(targetNS ns.NetNS, config *Net, dataDir string) *skel.CmdArgs {
func (tc testCase) createCheckCmdArgs(targetNS ns.NetNS, config *Net) *skel.CmdArgs {
conf, err := json.Marshal(config)
Expect(err).NotTo(HaveOccurred())
// TODO Don't we need to use the same counter as before?
//defer func() { counter += 1 }()
// defer func() { counter += 1 }()
return &skel.CmdArgs{
ContainerID: fmt.Sprintf("dummy-%d", counter),
Netns: targetNS.Path(),
IfName: IFNAME,
StdinData: []byte(conf),
StdinData: conf,
}
}
@ -369,25 +437,25 @@ func ipVersion(ip net.IP) string {
func countIPAMIPs(path string) (int, error) {
count := 0
files, err := ioutil.ReadDir(path)
entries, err := os.ReadDir(path)
if err != nil {
return -1, err
}
for _, file := range files {
if file.IsDir() {
for _, entry := range entries {
if entry.IsDir() {
continue
}
if net.ParseIP(file.Name()) != nil {
if net.ParseIP(entry.Name()) != nil {
count++
}
}
return count, nil
}
func checkVlan(vlanId int, bridgeVlanInfo []*nl.BridgeVlanInfo) bool {
func checkVlan(vlanID int, bridgeVlanInfo []*nl.BridgeVlanInfo) bool {
for _, vlan := range bridgeVlanInfo {
if vlan.Vid == uint16(vlanId) {
if vlan.Vid == uint16(vlanID) {
return true
}
}
@ -408,10 +476,12 @@ type testerBase struct {
vethName string
}
type testerV10x testerBase
type testerV04x testerBase
type testerV03x testerBase
type testerV01xOr02x testerBase
type (
testerV10x testerBase
testerV04x testerBase
testerV03x testerBase
testerV01xOr02x testerBase
)
func newTesterByVersion(version string, testNS, targetNS ns.NetNS) cmdAddDelTester {
switch {
@ -458,9 +528,9 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
result = resultType.(*types100.Result)
if !tc.isLayer2 && tc.vlan != 0 {
Expect(len(result.Interfaces)).To(Equal(4))
Expect(result.Interfaces).To(HaveLen(4))
} else {
Expect(len(result.Interfaces)).To(Equal(3))
Expect(result.Interfaces).To(HaveLen(3))
}
Expect(result.Interfaces[0].Name).To(Equal(BRNAME))
@ -500,13 +570,16 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
vlans, isExist := interfaceMap[int32(peerLink.Attrs().Index)]
Expect(isExist).To(BeTrue())
Expect(checkVlan(tc.vlan, vlans)).To(BeTrue())
if tc.removeDefaultVlan {
Expect(vlans).To(HaveLen(1))
}
}
// Check the bridge vlan filtering equals true
if tc.vlan != 0 {
Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(true))
if tc.vlan != 0 || tc.vlanTrunk != nil {
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeTrue())
} else {
Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(false))
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeFalse())
}
// Ensure bridge has expected gateway address(es)
@ -517,7 +590,7 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
addrs, err = netlink.AddrList(vlanLink, netlink.FAMILY_ALL)
}
Expect(err).NotTo(HaveOccurred())
Expect(len(addrs)).To(BeNumerically(">", 0))
Expect(addrs).ToNot(BeEmpty())
for _, cidr := range tc.expGWCIDRs {
ip, subnet, err := net.ParseCIDR(cidr)
Expect(err).NotTo(HaveOccurred())
@ -531,16 +604,16 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
break
}
}
Expect(found).To(Equal(true), fmt.Sprintf("failed to find %s", cidr))
Expect(found).To(BeTrue(), fmt.Sprintf("failed to find %s", cidr))
}
// Check for the veth link in the main namespace
links, err := netlink.LinkList()
Expect(err).NotTo(HaveOccurred())
if !tc.isLayer2 && tc.vlan != 0 {
Expect(len(links)).To(Equal(5)) // Bridge, Bridge vlan veth, veth, and loopback
Expect(links).To(HaveLen(5)) // Bridge, Bridge vlan veth, veth, and loopback
} else {
Expect(len(links)).To(Equal(3)) // Bridge, veth, and loopback
Expect(links).To(HaveLen(3)) // Bridge, veth, and loopback
}
link, err = netlink.LinkByName(result.Interfaces[1].Name)
@ -555,6 +628,28 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
vlans, isExist := interfaceMap[int32(link.Attrs().Index)]
Expect(isExist).To(BeTrue())
Expect(checkVlan(tc.vlan, vlans)).To(BeTrue())
if tc.removeDefaultVlan {
Expect(vlans).To(HaveLen(1))
}
}
// check VlanTrunks exist on the veth interface
if tc.vlanTrunk != nil {
interfaceMap, err := netlink.BridgeVlanList()
Expect(err).NotTo(HaveOccurred())
vlans, isExist := interfaceMap[int32(link.Attrs().Index)]
Expect(isExist).To(BeTrue())
for _, vlanEntry := range tc.vlanTrunk {
if vlanEntry.ID != nil {
Expect(checkVlan(*vlanEntry.ID, vlans)).To(BeTrue())
}
if vlanEntry.MinID != nil && vlanEntry.MaxID != nil {
for vid := *vlanEntry.MinID; vid <= *vlanEntry.MaxID; vid++ {
Expect(checkVlan(vid, vlans)).To(BeTrue())
}
}
}
}
// Check that the bridge has a different mac from the veth
@ -565,6 +660,11 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
Expect(link.Attrs().HardwareAddr.String()).NotTo(Equal(bridgeMAC))
}
// Check that resolvConf was used properly
if !tc.isLayer2 && tc.resolvConf != "" {
Expect(result.DNS.Nameservers).To(Equal([]string{NAMESERVER}))
}
return nil
})
Expect(err).NotTo(HaveOccurred())
@ -581,9 +681,9 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
expCIDRsV4, expCIDRsV6 := tc.expectedCIDRs()
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
Expect(err).NotTo(HaveOccurred())
Expect(len(addrs)).To(Equal(len(expCIDRsV4)))
Expect(addrs).To(HaveLen(len(expCIDRsV4)))
addrs, err = netlink.AddrList(link, netlink.FAMILY_V6)
Expect(len(addrs)).To(Equal(len(expCIDRsV6) + 1)) //add one for the link-local
Expect(addrs).To(HaveLen(len(expCIDRsV6) + 1)) // add one for the link-local
Expect(err).NotTo(HaveOccurred())
// Ignore link local address which may or may not be
// ready when we read addresses.
@ -618,7 +718,7 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
break
}
}
Expect(*found).To(Equal(true))
Expect(*found).To(BeTrue())
}
return nil
@ -628,9 +728,9 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
return result, nil
}
func (tester *testerV10x) cmdCheckTest(tc testCase, conf *Net, dataDir string) {
func (tester *testerV10x) cmdCheckTest(tc testCase, conf *Net, _ string) {
// Generate network config and command arguments
tester.args = tc.createCheckCmdArgs(tester.targetNS, conf, dataDir)
tester.args = tc.createCheckCmdArgs(tester.targetNS, conf)
// Execute cmdCHECK on the plugin
err := tester.testNS.Do(func(ns.NetNS) error {
@ -657,9 +757,9 @@ func (tester *testerV10x) cmdCheckTest(tc testCase, conf *Net, dataDir string) {
expCIDRsV4, expCIDRsV6 := tc.expectedCIDRs()
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
Expect(err).NotTo(HaveOccurred())
Expect(len(addrs)).To(Equal(len(expCIDRsV4)))
Expect(addrs).To(HaveLen(len(expCIDRsV4)))
addrs, err = netlink.AddrList(link, netlink.FAMILY_V6)
Expect(len(addrs)).To(Equal(len(expCIDRsV6) + 1)) //add one for the link-local
Expect(addrs).To(HaveLen(len(expCIDRsV6) + 1)) // add one for the link-local
Expect(err).NotTo(HaveOccurred())
// Ignore link local address which may or may not be
// ready when we read addresses.
@ -694,7 +794,7 @@ func (tester *testerV10x) cmdCheckTest(tc testCase, conf *Net, dataDir string) {
break
}
}
Expect(*found).To(Equal(true))
Expect(*found).To(BeTrue())
}
return nil
@ -758,9 +858,9 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
result = resultType.(*types040.Result)
if !tc.isLayer2 && tc.vlan != 0 {
Expect(len(result.Interfaces)).To(Equal(4))
Expect(result.Interfaces).To(HaveLen(4))
} else {
Expect(len(result.Interfaces)).To(Equal(3))
Expect(result.Interfaces).To(HaveLen(3))
}
Expect(result.Interfaces[0].Name).To(Equal(BRNAME))
@ -800,13 +900,16 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
vlans, isExist := interfaceMap[int32(peerLink.Attrs().Index)]
Expect(isExist).To(BeTrue())
Expect(checkVlan(tc.vlan, vlans)).To(BeTrue())
if tc.removeDefaultVlan {
Expect(vlans).To(HaveLen(1))
}
}
// Check the bridge vlan filtering equals true
if tc.vlan != 0 {
Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(true))
if tc.vlan != 0 || tc.vlanTrunk != nil {
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeTrue())
} else {
Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(false))
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeFalse())
}
// Ensure bridge has expected gateway address(es)
@ -817,7 +920,7 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
addrs, err = netlink.AddrList(vlanLink, netlink.FAMILY_ALL)
}
Expect(err).NotTo(HaveOccurred())
Expect(len(addrs)).To(BeNumerically(">", 0))
Expect(addrs).ToNot(BeEmpty())
for _, cidr := range tc.expGWCIDRs {
ip, subnet, err := net.ParseCIDR(cidr)
Expect(err).NotTo(HaveOccurred())
@ -831,16 +934,16 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
break
}
}
Expect(found).To(Equal(true))
Expect(found).To(BeTrue())
}
// Check for the veth link in the main namespace
links, err := netlink.LinkList()
Expect(err).NotTo(HaveOccurred())
if !tc.isLayer2 && tc.vlan != 0 {
Expect(len(links)).To(Equal(5)) // Bridge, Bridge vlan veth, veth, and loopback
Expect(links).To(HaveLen(5)) // Bridge, Bridge vlan veth, veth, and loopback
} else {
Expect(len(links)).To(Equal(3)) // Bridge, veth, and loopback
Expect(links).To(HaveLen(3)) // Bridge, veth, and loopback
}
link, err = netlink.LinkByName(result.Interfaces[1].Name)
@ -855,6 +958,28 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
vlans, isExist := interfaceMap[int32(link.Attrs().Index)]
Expect(isExist).To(BeTrue())
Expect(checkVlan(tc.vlan, vlans)).To(BeTrue())
if tc.removeDefaultVlan {
Expect(vlans).To(HaveLen(1))
}
}
// check VlanTrunks exist on the veth interface
if tc.vlanTrunk != nil {
interfaceMap, err := netlink.BridgeVlanList()
Expect(err).NotTo(HaveOccurred())
vlans, isExist := interfaceMap[int32(link.Attrs().Index)]
Expect(isExist).To(BeTrue())
for _, vlanEntry := range tc.vlanTrunk {
if vlanEntry.ID != nil {
Expect(checkVlan(*vlanEntry.ID, vlans)).To(BeTrue())
}
if vlanEntry.MinID != nil && vlanEntry.MaxID != nil {
for vid := *vlanEntry.MinID; vid <= *vlanEntry.MaxID; vid++ {
Expect(checkVlan(vid, vlans)).To(BeTrue())
}
}
}
}
// Check that the bridge has a different mac from the veth
@ -881,9 +1006,9 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
expCIDRsV4, expCIDRsV6 := tc.expectedCIDRs()
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
Expect(err).NotTo(HaveOccurred())
Expect(len(addrs)).To(Equal(len(expCIDRsV4)))
Expect(addrs).To(HaveLen(len(expCIDRsV4)))
addrs, err = netlink.AddrList(link, netlink.FAMILY_V6)
Expect(len(addrs)).To(Equal(len(expCIDRsV6) + 1)) //add one for the link-local
Expect(addrs).To(HaveLen(len(expCIDRsV6) + 1)) // add one for the link-local
Expect(err).NotTo(HaveOccurred())
// Ignore link local address which may or may not be
// ready when we read addresses.
@ -918,7 +1043,7 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
break
}
}
Expect(*found).To(Equal(true))
Expect(*found).To(BeTrue())
}
return nil
@ -928,9 +1053,9 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
return result, nil
}
func (tester *testerV04x) cmdCheckTest(tc testCase, conf *Net, dataDir string) {
func (tester *testerV04x) cmdCheckTest(tc testCase, conf *Net, _ string) {
// Generate network config and command arguments
tester.args = tc.createCheckCmdArgs(tester.targetNS, conf, dataDir)
tester.args = tc.createCheckCmdArgs(tester.targetNS, conf)
// Execute cmdCHECK on the plugin
err := tester.testNS.Do(func(ns.NetNS) error {
@ -957,9 +1082,9 @@ func (tester *testerV04x) cmdCheckTest(tc testCase, conf *Net, dataDir string) {
expCIDRsV4, expCIDRsV6 := tc.expectedCIDRs()
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
Expect(err).NotTo(HaveOccurred())
Expect(len(addrs)).To(Equal(len(expCIDRsV4)))
Expect(addrs).To(HaveLen(len(expCIDRsV4)))
addrs, err = netlink.AddrList(link, netlink.FAMILY_V6)
Expect(len(addrs)).To(Equal(len(expCIDRsV6) + 1)) //add one for the link-local
Expect(addrs).To(HaveLen(len(expCIDRsV6) + 1)) // add one for the link-local
Expect(err).NotTo(HaveOccurred())
// Ignore link local address which may or may not be
// ready when we read addresses.
@ -994,7 +1119,7 @@ func (tester *testerV04x) cmdCheckTest(tc testCase, conf *Net, dataDir string) {
break
}
}
Expect(*found).To(Equal(true))
Expect(*found).To(BeTrue())
}
return nil
@ -1058,9 +1183,9 @@ func (tester *testerV03x) cmdAddTest(tc testCase, dataDir string) (types.Result,
result = resultType.(*types040.Result)
if !tc.isLayer2 && tc.vlan != 0 {
Expect(len(result.Interfaces)).To(Equal(4))
Expect(result.Interfaces).To(HaveLen(4))
} else {
Expect(len(result.Interfaces)).To(Equal(3))
Expect(result.Interfaces).To(HaveLen(3))
}
Expect(result.Interfaces[0].Name).To(Equal(BRNAME))
@ -1100,13 +1225,16 @@ func (tester *testerV03x) cmdAddTest(tc testCase, dataDir string) (types.Result,
vlans, isExist := interfaceMap[int32(peerLink.Attrs().Index)]
Expect(isExist).To(BeTrue())
Expect(checkVlan(tc.vlan, vlans)).To(BeTrue())
if tc.removeDefaultVlan {
Expect(vlans).To(HaveLen(1))
}
}
// Check the bridge vlan filtering equals true
if tc.vlan != 0 {
Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(true))
if tc.vlan != 0 || tc.vlanTrunk != nil {
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeTrue())
} else {
Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(false))
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeFalse())
}
// Ensure bridge has expected gateway address(es)
@ -1117,7 +1245,7 @@ func (tester *testerV03x) cmdAddTest(tc testCase, dataDir string) (types.Result,
addrs, err = netlink.AddrList(vlanLink, netlink.FAMILY_ALL)
}
Expect(err).NotTo(HaveOccurred())
Expect(len(addrs)).To(BeNumerically(">", 0))
Expect(addrs).ToNot(BeEmpty())
for _, cidr := range tc.expGWCIDRs {
ip, subnet, err := net.ParseCIDR(cidr)
Expect(err).NotTo(HaveOccurred())
@ -1131,16 +1259,16 @@ func (tester *testerV03x) cmdAddTest(tc testCase, dataDir string) (types.Result,
break
}
}
Expect(found).To(Equal(true))
Expect(found).To(BeTrue())
}
// Check for the veth link in the main namespace
links, err := netlink.LinkList()
Expect(err).NotTo(HaveOccurred())
if !tc.isLayer2 && tc.vlan != 0 {
Expect(len(links)).To(Equal(5)) // Bridge, Bridge vlan veth, veth, and loopback
Expect(links).To(HaveLen(5)) // Bridge, Bridge vlan veth, veth, and loopback
} else {
Expect(len(links)).To(Equal(3)) // Bridge, veth, and loopback
Expect(links).To(HaveLen(3)) // Bridge, veth, and loopback
}
link, err = netlink.LinkByName(result.Interfaces[1].Name)
@ -1155,6 +1283,28 @@ func (tester *testerV03x) cmdAddTest(tc testCase, dataDir string) (types.Result,
vlans, isExist := interfaceMap[int32(link.Attrs().Index)]
Expect(isExist).To(BeTrue())
Expect(checkVlan(tc.vlan, vlans)).To(BeTrue())
if tc.removeDefaultVlan {
Expect(vlans).To(HaveLen(1))
}
}
// check VlanTrunks exist on the veth interface
if tc.vlanTrunk != nil {
interfaceMap, err := netlink.BridgeVlanList()
Expect(err).NotTo(HaveOccurred())
vlans, isExist := interfaceMap[int32(link.Attrs().Index)]
Expect(isExist).To(BeTrue())
for _, vlanEntry := range tc.vlanTrunk {
if vlanEntry.ID != nil {
Expect(checkVlan(*vlanEntry.ID, vlans)).To(BeTrue())
}
if vlanEntry.MinID != nil && vlanEntry.MaxID != nil {
for vid := *vlanEntry.MinID; vid <= *vlanEntry.MaxID; vid++ {
Expect(checkVlan(vid, vlans)).To(BeTrue())
}
}
}
}
// Check that the bridge has a different mac from the veth
@ -1181,7 +1331,7 @@ func (tester *testerV03x) cmdAddTest(tc testCase, dataDir string) (types.Result,
expCIDRsV4, expCIDRsV6 := tc.expectedCIDRs()
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
Expect(err).NotTo(HaveOccurred())
Expect(len(addrs)).To(Equal(len(expCIDRsV4)))
Expect(addrs).To(HaveLen(len(expCIDRsV4)))
addrs, err = netlink.AddrList(link, netlink.FAMILY_V6)
Expect(err).NotTo(HaveOccurred())
// Ignore link local address which may or may not be
@ -1217,7 +1367,7 @@ func (tester *testerV03x) cmdAddTest(tc testCase, dataDir string) (types.Result,
break
}
}
Expect(*found).To(Equal(true))
Expect(*found).To(BeTrue())
}
return nil
@ -1226,11 +1376,10 @@ func (tester *testerV03x) cmdAddTest(tc testCase, dataDir string) (types.Result,
return result, nil
}
func (tester *testerV03x) cmdCheckTest(tc testCase, conf *Net, dataDir string) {
return
func (tester *testerV03x) cmdCheckTest(_ testCase, _ *Net, _ string) {
}
func (tester *testerV03x) cmdDelTest(tc testCase, dataDir string) {
func (tester *testerV03x) cmdDelTest(_ testCase, _ string) {
err := tester.testNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
@ -1327,13 +1476,16 @@ func (tester *testerV01xOr02x) cmdAddTest(tc testCase, dataDir string) (types.Re
vlans, isExist := interfaceMap[int32(peerLink.Attrs().Index)]
Expect(isExist).To(BeTrue())
Expect(checkVlan(tc.vlan, vlans)).To(BeTrue())
if tc.removeDefaultVlan {
Expect(vlans).To(HaveLen(1))
}
}
// Check the bridge vlan filtering equals true
if tc.vlan != 0 {
Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(true))
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeTrue())
} else {
Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(false))
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeFalse())
}
// Ensure bridge has expected gateway address(es)
@ -1344,7 +1496,7 @@ func (tester *testerV01xOr02x) cmdAddTest(tc testCase, dataDir string) (types.Re
addrs, err = netlink.AddrList(vlanLink, netlink.FAMILY_ALL)
}
Expect(err).NotTo(HaveOccurred())
Expect(len(addrs)).To(BeNumerically(">", 0))
Expect(addrs).ToNot(BeEmpty())
for _, cidr := range tc.expGWCIDRs {
ip, subnet, err := net.ParseCIDR(cidr)
Expect(err).NotTo(HaveOccurred())
@ -1358,7 +1510,7 @@ func (tester *testerV01xOr02x) cmdAddTest(tc testCase, dataDir string) (types.Re
break
}
}
Expect(found).To(Equal(true))
Expect(found).To(BeTrue())
}
// Check for the veth link in the main namespace; can't
@ -1367,9 +1519,9 @@ func (tester *testerV01xOr02x) cmdAddTest(tc testCase, dataDir string) (types.Re
links, err := netlink.LinkList()
Expect(err).NotTo(HaveOccurred())
if !tc.isLayer2 && tc.vlan != 0 {
Expect(len(links)).To(Equal(5)) // Bridge, Bridge vlan veth, veth, and loopback
Expect(links).To(HaveLen(5)) // Bridge, Bridge vlan veth, veth, and loopback
} else {
Expect(len(links)).To(Equal(3)) // Bridge, veth, and loopback
Expect(links).To(HaveLen(3)) // Bridge, veth, and loopback
}
// Grab the vlan map in the host NS for checking later
@ -1400,7 +1552,7 @@ func (tester *testerV01xOr02x) cmdAddTest(tc testCase, dataDir string) (types.Re
expCIDRsV4, expCIDRsV6 := tc.expectedCIDRs()
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
Expect(err).NotTo(HaveOccurred())
Expect(len(addrs)).To(Equal(len(expCIDRsV4)))
Expect(addrs).To(HaveLen(len(expCIDRsV4)))
addrs, err = netlink.AddrList(link, netlink.FAMILY_V6)
Expect(err).NotTo(HaveOccurred())
// Ignore link local address which may or may not be
@ -1436,7 +1588,7 @@ func (tester *testerV01xOr02x) cmdAddTest(tc testCase, dataDir string) (types.Re
break
}
}
Expect(*found).To(Equal(true))
Expect(*found).To(BeTrue())
}
// Validate VLAN in the host NS. Since 0.1.0/0.2.0 don't return
@ -1449,6 +1601,9 @@ func (tester *testerV01xOr02x) cmdAddTest(tc testCase, dataDir string) (types.Re
vlans, isExist := hostNSVlanMap[int32(peerIndex)]
Expect(isExist).To(BeTrue())
Expect(checkVlan(tc.vlan, vlans)).To(BeTrue())
if tc.removeDefaultVlan {
Expect(vlans).To(HaveLen(1))
}
}
return nil
@ -1457,22 +1612,22 @@ func (tester *testerV01xOr02x) cmdAddTest(tc testCase, dataDir string) (types.Re
return nil, nil
}
func (tester *testerV01xOr02x) cmdCheckTest(tc testCase, conf *Net, dataDir string) {
return
func (tester *testerV01xOr02x) cmdCheckTest(_ testCase, _ *Net, _ string) {
}
func (tester *testerV01xOr02x) cmdDelTest(tc testCase, dataDir string) {
func (tester *testerV01xOr02x) cmdDelTest(tc testCase, _ string) {
err := tester.testNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
err := testutils.CmdDelWithArgs(tester.args, func() error {
return cmdDel(tester.args)
})
if expect020DelError(tc) {
switch {
case expect020DelError(tc):
Expect(err).To(MatchError(tc.DelErr020))
} else if expect010DelError(tc) {
case expect010DelError(tc):
Expect(err).To(MatchError(tc.DelErr010))
} else {
default:
Expect(err).NotTo(HaveOccurred())
}
return nil
@ -1545,7 +1700,6 @@ func buildOneConfig(name, cniVersion string, orig *Net, prevResult types.Result)
}
return conf, nil
}
func cmdAddDelCheckTest(origNS, targetNS ns.NetNS, tc testCase, dataDir string) {
@ -1595,7 +1749,7 @@ var _ = Describe("bridge Operations", func() {
targetNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
dataDir, err = ioutil.TempDir("", "bridge_test")
dataDir, err = os.MkdirTemp("", "bridge_test")
Expect(err).NotTo(HaveOccurred())
// Do not emulate an error, each test will set this if needed
@ -1610,6 +1764,61 @@ var _ = Describe("bridge Operations", func() {
Expect(testutils.UnmountNS(targetNS)).To(Succeed())
})
var (
correctID int = 10
correctMinID int = 100
correctMaxID int = 105
incorrectMinID int = 1000
incorrectMaxID int = 100
overID int = 5000
negativeID int = -1
)
DescribeTable(
"collectVlanTrunk succeeds",
func(vlanTrunks []*VlanTrunk, expectedVIDs []int) {
Expect(collectVlanTrunk(vlanTrunks)).To(ConsistOf(expectedVIDs))
},
Entry("when provided an empty VLAN trunk configuration", []*VlanTrunk{}, nil),
Entry("when provided a VLAN trunk configuration with both min / max range", []*VlanTrunk{
{
MinID: &correctMinID,
MaxID: &correctMaxID,
},
}, []int{100, 101, 102, 103, 104, 105}),
Entry("when provided a VLAN trunk configuration with id only", []*VlanTrunk{
{
ID: &correctID,
},
}, []int{10}),
Entry("when provided a VLAN trunk configuration with id and range", []*VlanTrunk{
{
ID: &correctID,
},
{
MinID: &correctMinID,
MaxID: &correctMaxID,
},
}, []int{10, 100, 101, 102, 103, 104, 105}),
)
DescribeTable(
"collectVlanTrunk failed",
func(vlanTrunks []*VlanTrunk, expectedError error) {
_, err := collectVlanTrunk(vlanTrunks)
Expect(err).To(MatchError(expectedError))
},
Entry("when not passed the maxID", []*VlanTrunk{{MinID: &correctMinID}}, fmt.Errorf("minID and maxID should be configured simultaneously, maxID is missing")),
Entry("when not passed the minID", []*VlanTrunk{{MaxID: &correctMaxID}}, fmt.Errorf("minID and maxID should be configured simultaneously, minID is missing")),
Entry("when the minID is negative", []*VlanTrunk{{MinID: &negativeID, MaxID: &correctMaxID}}, fmt.Errorf("incorrect trunk minID parameter")),
Entry("when the minID is larger than 4094", []*VlanTrunk{{MinID: &overID, MaxID: &correctMaxID}}, fmt.Errorf("incorrect trunk minID parameter")),
Entry("when the maxID is larger than 4094", []*VlanTrunk{{MinID: &correctMinID, MaxID: &overID}}, fmt.Errorf("incorrect trunk maxID parameter")),
Entry("when the maxID is negative", []*VlanTrunk{{MinID: &correctMinID, MaxID: &overID}}, fmt.Errorf("incorrect trunk maxID parameter")),
Entry("when the ID is larger than 4094", []*VlanTrunk{{ID: &overID}}, fmt.Errorf("incorrect trunk id parameter")),
Entry("when the ID is negative", []*VlanTrunk{{ID: &negativeID}}, fmt.Errorf("incorrect trunk id parameter")),
Entry("when the maxID is smaller than minID", []*VlanTrunk{{MinID: &incorrectMinID, MaxID: &incorrectMaxID}}, fmt.Errorf("minID is greater than maxID in trunk parameter")),
)
for _, ver := range testutils.AllSpecVersions {
// Redefine ver inside for scope so real value is picked up by each dynamically defined It()
// See Gingkgo's "Patterns for dynamically generating tests" documentation.
@ -1706,6 +1915,12 @@ var _ = Describe("bridge Operations", func() {
AddErr010: "CNI version 0.1.0 does not support more than 1 address per family",
DelErr010: "CNI version 0.1.0 does not support more than 1 address per family",
},
{
// with resolvConf DNS settings
subnet: "10.1.2.0/24",
expGWCIDRs: []string{"10.1.2.1/24"},
resolvConf: resolvConf,
},
} {
tc := tc
i := i
@ -1736,6 +1951,37 @@ var _ = Describe("bridge Operations", func() {
cmdAddDelTest(originalNS, targetNS, tc, dataDir)
})
// TODO find some way to put pointer
It(fmt.Sprintf("[%s] configures and deconfigures a l2 bridge with vlan id 100, vlanTrunk 101,200~210 using ADD/DEL", ver), func() {
id, minID, maxID := 101, 200, 210
tc := testCase{
cniVersion: ver,
isLayer2: true,
vlanTrunk: []*VlanTrunk{
{ID: &id},
{
MinID: &minID,
MaxID: &maxID,
},
},
AddErr020: "cannot convert: no valid IP addresses",
AddErr010: "cannot convert: no valid IP addresses",
}
cmdAddDelTest(originalNS, targetNS, tc, dataDir)
})
It(fmt.Sprintf("[%s] configures and deconfigures a l2 bridge with vlan id 100 and no default vlan using ADD/DEL", ver), func() {
tc := testCase{
cniVersion: ver,
isLayer2: true,
vlan: 100,
removeDefaultVlan: true,
AddErr020: "cannot convert: no valid IP addresses",
AddErr010: "cannot convert: no valid IP addresses",
}
cmdAddDelTest(originalNS, targetNS, tc, dataDir)
})
for i, tc := range []testCase{
{
// IPv4 only
@ -1786,6 +2032,11 @@ var _ = Describe("bridge Operations", func() {
tc.cniVersion = ver
cmdAddDelTest(originalNS, targetNS, tc, dataDir)
})
It(fmt.Sprintf("[%s] (%d) configures and deconfigures a bridge, veth with default route and vlanID 100 and no default vlan with ADD/DEL", ver, i), func() {
tc.cniVersion = ver
tc.removeDefaultVlan = true
cmdAddDelTest(originalNS, targetNS, tc, dataDir)
})
}
for i, tc := range []testCase{
@ -1893,7 +2144,7 @@ var _ = Describe("bridge Operations", func() {
checkBridgeIPs := func(cidr0, cidr1 string) {
addrs, err := netlink.AddrList(bridge, family)
Expect(err).NotTo(HaveOccurred())
Expect(len(addrs)).To(Equal(expNumAddrs))
Expect(addrs).To(HaveLen(expNumAddrs))
addr := addrs[0].IPNet.String()
Expect(addr).To(Equal(cidr0))
if cidr1 != "" {
@ -1903,7 +2154,7 @@ var _ = Describe("bridge Operations", func() {
}
// Check if ForceAddress has default value
Expect(conf.ForceAddress).To(Equal(false))
Expect(conf.ForceAddress).To(BeFalse())
// Set first address on bridge
err = ensureAddr(bridge, family, &gwnFirst, conf.ForceAddress)
@ -1951,8 +2202,6 @@ var _ = Describe("bridge Operations", func() {
It(fmt.Sprintf("[%s] ensure promiscuous mode on bridge", ver), func() {
const IFNAME = "bridge0"
const EXPECTED_IP = "10.0.0.0/8"
const CHANGED_EXPECTED_IP = "10.1.2.3/16"
conf := &NetConf{
NetConf: types.NetConf{
@ -1974,9 +2223,9 @@ var _ = Describe("bridge Operations", func() {
_, _, err := setupBridge(conf)
Expect(err).NotTo(HaveOccurred())
// Check if ForceAddress has default value
Expect(conf.ForceAddress).To(Equal(false))
Expect(conf.ForceAddress).To(BeFalse())
//Check if promiscuous mode is set correctly
// Check if promiscuous mode is set correctly
link, err := netlink.LinkByName("bridge0")
Expect(err).NotTo(HaveOccurred())
@ -2241,7 +2490,7 @@ var _ = Describe("bridge Operations", func() {
for _, test := range tests {
_, _, err := loadNetConf([]byte(test.netConfJSON("")), "")
if test.err == nil {
Expect(err).To(BeNil())
Expect(err).ToNot(HaveOccurred())
} else {
Expect(err).To(Equal(test.err))
}

View File

@ -0,0 +1,39 @@
---
title: dummy plugin
description: "plugins/main/dummy/README.md"
date: 2022-05-12
toc: true
draft: true
weight: 200
---
## Overview
dummy is a useful feature for routing packets through the Linux kernel without transmitting.
Like loopback, it is a purely virtual interface that allows packets to be routed to a designated IP address. Unlike loopback, the IP address can be arbitrary and is not restricted to the `127.0.0.0/8` range.
## Example configuration
```json
{
"name": "mynet",
"type": "dummy",
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24"
}
}
```
## Network configuration reference
* `name` (string, required): the name of the network.
* `type` (string, required): "dummy".
* `ipam` (dictionary, required): IPAM configuration to be used for this network.
## Notes
* `dummy` does not transmit packets.
Therefore the container will not be able to reach any external network.
This solution is designed to be used in conjunction with other CNI plugins (e.g., `bridge`) to provide an internal non-loopback address for applications to use.

292
plugins/main/dummy/dummy.go Normal file
View File

@ -0,0 +1,292 @@
// Copyright 2022 Arista Networks
//
// 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 main
import (
"encoding/json"
"errors"
"fmt"
"net"
"github.com/vishvananda/netlink"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/ip"
"github.com/containernetworking/plugins/pkg/ipam"
"github.com/containernetworking/plugins/pkg/ns"
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
)
func parseNetConf(bytes []byte) (*types.NetConf, error) {
conf := &types.NetConf{}
if err := json.Unmarshal(bytes, conf); err != nil {
return nil, fmt.Errorf("failed to parse network config: %v", err)
}
return conf, nil
}
func createDummy(ifName string, netns ns.NetNS) (*current.Interface, error) {
dummy := &current.Interface{}
dm := &netlink.Dummy{
LinkAttrs: netlink.LinkAttrs{
Name: ifName,
Namespace: netlink.NsFd(int(netns.Fd())),
},
}
if err := netlink.LinkAdd(dm); err != nil {
return nil, fmt.Errorf("failed to create dummy: %v", err)
}
dummy.Name = ifName
err := netns.Do(func(_ ns.NetNS) error {
// Re-fetch interface to get all properties/attributes
contDummy, err := netlink.LinkByName(ifName)
if err != nil {
return fmt.Errorf("failed to fetch dummy%q: %v", ifName, err)
}
dummy.Mac = contDummy.Attrs().HardwareAddr.String()
dummy.Sandbox = netns.Path()
return nil
})
if err != nil {
return nil, err
}
return dummy, nil
}
func cmdAdd(args *skel.CmdArgs) error {
conf, err := parseNetConf(args.StdinData)
if err != nil {
return err
}
if conf.IPAM.Type == "" {
return errors.New("dummy interface requires an IPAM configuration")
}
netns, err := ns.GetNS(args.Netns)
if err != nil {
return fmt.Errorf("failed to open netns %q: %v", netns, err)
}
defer netns.Close()
dummyInterface, err := createDummy(args.IfName, netns)
if err != nil {
return err
}
// Delete link if err to avoid link leak in this ns
defer func() {
if err != nil {
netns.Do(func(_ ns.NetNS) error {
return ip.DelLinkByName(args.IfName)
})
}
}()
r, err := ipam.ExecAdd(conf.IPAM.Type, args.StdinData)
if err != nil {
return err
}
// defer ipam deletion to avoid ip leak
defer func() {
if err != nil {
ipam.ExecDel(conf.IPAM.Type, args.StdinData)
}
}()
// convert IPAMResult to current Result type
result, err := current.NewResultFromResult(r)
if err != nil {
return err
}
if len(result.IPs) == 0 {
return errors.New("IPAM plugin returned missing IP config")
}
for _, ipc := range result.IPs {
// all addresses apply to the container dummy interface
ipc.Interface = current.Int(0)
}
result.Interfaces = []*current.Interface{dummyInterface}
err = netns.Do(func(_ ns.NetNS) error {
return ipam.ConfigureIface(args.IfName, result)
})
if err != nil {
return err
}
return types.PrintResult(result, conf.CNIVersion)
}
func cmdDel(args *skel.CmdArgs) error {
conf, err := parseNetConf(args.StdinData)
if err != nil {
return err
}
if err = ipam.ExecDel(conf.IPAM.Type, args.StdinData); err != nil {
return err
}
if args.Netns == "" {
return nil
}
err = ns.WithNetNSPath(args.Netns, func(ns.NetNS) error {
err = ip.DelLinkByName(args.IfName)
if err != nil && err == ip.ErrLinkNotFound {
return nil
}
return err
})
if err != nil {
// if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times
// so don't return an error if the device is already removed.
// https://github.com/kubernetes/kubernetes/issues/43014#issuecomment-287164444
_, ok := err.(ns.NSPathNotExistErr)
if ok {
return nil
}
return err
}
return nil
}
func main() {
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("dummy"))
}
func cmdCheck(args *skel.CmdArgs) error {
conf, err := parseNetConf(args.StdinData)
if err != nil {
return err
}
if conf.IPAM.Type == "" {
return errors.New("dummy interface requires an IPAM configuration")
}
netns, err := ns.GetNS(args.Netns)
if err != nil {
return fmt.Errorf("failed to open netns %q: %v", args.Netns, err)
}
defer netns.Close()
// run the IPAM plugin and get back the config to apply
err = ipam.ExecCheck(conf.IPAM.Type, args.StdinData)
if err != nil {
return err
}
if conf.RawPrevResult == nil {
return fmt.Errorf("dummy: Required prevResult missing")
}
if err := version.ParsePrevResult(conf); err != nil {
return err
}
// Convert whatever the IPAM result was into the current Result type
result, err := current.NewResultFromResult(conf.PrevResult)
if err != nil {
return err
}
var contMap current.Interface
// Find interfaces for name whe know, that of dummy device inside container
for _, intf := range result.Interfaces {
if args.IfName == intf.Name {
if args.Netns == intf.Sandbox {
contMap = *intf
continue
}
}
}
// The namespace must be the same as what was configured
if args.Netns != contMap.Sandbox {
return fmt.Errorf("Sandbox in prevResult %s doesn't match configured netns: %s",
contMap.Sandbox, args.Netns)
}
//
// Check prevResults for ips, routes and dns against values found in the container
if err := netns.Do(func(_ ns.NetNS) error {
// Check interface against values found in the container
err := validateCniContainerInterface(contMap)
if err != nil {
return err
}
err = ip.ValidateExpectedInterfaceIPs(args.IfName, result.IPs)
if err != nil {
return err
}
return nil
}); err != nil {
return err
}
return nil
}
func validateCniContainerInterface(intf current.Interface) error {
var link netlink.Link
var err error
if intf.Name == "" {
return fmt.Errorf("Container interface name missing in prevResult: %v", intf.Name)
}
link, err = netlink.LinkByName(intf.Name)
if err != nil {
return fmt.Errorf("Container Interface name in prevResult: %s not found", intf.Name)
}
if intf.Sandbox == "" {
return fmt.Errorf("Error: Container interface %s should not be in host namespace", link.Attrs().Name)
}
_, isDummy := link.(*netlink.Dummy)
if !isDummy {
return fmt.Errorf("Error: Container interface %s not of type dummy", link.Attrs().Name)
}
if intf.Mac != "" {
if intf.Mac != link.Attrs().HardwareAddr.String() {
return fmt.Errorf("Interface %s Mac %s doesn't match container Mac: %s", intf.Name, intf.Mac, link.Attrs().HardwareAddr)
}
}
if link.Attrs().Flags&net.FlagUp != net.FlagUp {
return fmt.Errorf("Interface %s is down", intf.Name)
}
return nil
}

View File

@ -0,0 +1,40 @@
// Copyright 2022 Arista Networks
//
// 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 main_test
import (
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
)
var pathToLoPlugin string
func TestLoopback(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "plugins/main/dummy")
}
var _ = BeforeSuite(func() {
var err error
pathToLoPlugin, err = gexec.Build("github.com/containernetworking/plugins/plugins/main/dummy")
Expect(err).NotTo(HaveOccurred())
})
var _ = AfterSuite(func() {
gexec.CleanupBuildArtifacts()
})

View File

@ -0,0 +1,385 @@
// Copyright 2022 Arista Networks
//
// 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 main
import (
"encoding/json"
"errors"
"fmt"
"net"
"os"
"strings"
"syscall"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/vishvananda/netlink"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
types020 "github.com/containernetworking/cni/pkg/types/020"
types040 "github.com/containernetworking/cni/pkg/types/040"
types100 "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
)
const MASTER_NAME = "eth0"
type Net struct {
Name string `json:"name"`
CNIVersion string `json:"cniVersion"`
Type string `json:"type,omitempty"`
IPAM *allocator.IPAMConfig `json:"ipam"`
RawPrevResult map[string]interface{} `json:"prevResult,omitempty"`
PrevResult types100.Result `json:"-"`
}
func buildOneConfig(netName string, cniVersion string, orig *Net, prevResult types.Result) (*Net, error) {
var err error
inject := map[string]interface{}{
"name": netName,
"cniVersion": cniVersion,
}
// Add previous plugin result
if prevResult != nil {
inject["prevResult"] = prevResult
}
// Ensure every config uses the same name and version
config := make(map[string]interface{})
confBytes, err := json.Marshal(orig)
if err != nil {
return nil, err
}
err = json.Unmarshal(confBytes, &config)
if err != nil {
return nil, fmt.Errorf("unmarshal existing network bytes: %s", err)
}
for key, value := range inject {
config[key] = value
}
newBytes, err := json.Marshal(config)
if err != nil {
return nil, err
}
conf := &Net{}
if err := json.Unmarshal(newBytes, &conf); err != nil {
return nil, fmt.Errorf("error parsing configuration: %s", err)
}
return conf, nil
}
type tester interface {
// verifyResult minimally verifies the Result and returns the interface's MAC address
verifyResult(result types.Result, name string) string
}
type testerBase struct{}
type (
testerV10x testerBase
testerV04x testerBase
testerV03x testerBase
testerV01xOr02x testerBase
)
func newTesterByVersion(version string) tester {
switch {
case strings.HasPrefix(version, "1.0."):
return &testerV10x{}
case strings.HasPrefix(version, "0.4."):
return &testerV04x{}
case strings.HasPrefix(version, "0.3."):
return &testerV03x{}
default:
return &testerV01xOr02x{}
}
}
// verifyResult minimally verifies the Result and returns the interface's MAC address
func (t *testerV10x) verifyResult(result types.Result, name string) string {
r, err := types100.GetResult(result)
Expect(err).NotTo(HaveOccurred())
Expect(r.Interfaces).To(HaveLen(1))
Expect(r.Interfaces[0].Name).To(Equal(name))
Expect(r.IPs).To(HaveLen(1))
return r.Interfaces[0].Mac
}
func verify0403(result types.Result, name string) string {
r, err := types040.GetResult(result)
Expect(err).NotTo(HaveOccurred())
Expect(r.Interfaces).To(HaveLen(1))
Expect(r.Interfaces[0].Name).To(Equal(name))
Expect(r.IPs).To(HaveLen(1))
return r.Interfaces[0].Mac
}
// verifyResult minimally verifies the Result and returns the interface's MAC address
func (t *testerV04x) verifyResult(result types.Result, name string) string {
return verify0403(result, name)
}
// verifyResult minimally verifies the Result and returns the interface's MAC address
func (t *testerV03x) verifyResult(result types.Result, name string) string {
return verify0403(result, name)
}
// verifyResult minimally verifies the Result and returns the interface's MAC address
func (t *testerV01xOr02x) verifyResult(result types.Result, _ string) string {
r, err := types020.GetResult(result)
Expect(err).NotTo(HaveOccurred())
Expect(r.IP4.IP.IP).NotTo(BeNil())
Expect(r.IP6).To(BeNil())
// 0.2 and earlier don't return MAC address
return ""
}
var _ = Describe("dummy Operations", func() {
var originalNS, targetNS ns.NetNS
var dataDir string
BeforeEach(func() {
// Create a new NetNS so we don't modify the host
var err error
originalNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
targetNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
dataDir, err = os.MkdirTemp("", "dummy_test")
Expect(err).NotTo(HaveOccurred())
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
// Add master
err = netlink.LinkAdd(&netlink.Dummy{
LinkAttrs: netlink.LinkAttrs{
Name: MASTER_NAME,
},
})
Expect(err).NotTo(HaveOccurred())
m, err := netlink.LinkByName(MASTER_NAME)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetUp(m)
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
})
AfterEach(func() {
Expect(os.RemoveAll(dataDir)).To(Succeed())
Expect(originalNS.Close()).To(Succeed())
Expect(testutils.UnmountNS(originalNS)).To(Succeed())
Expect(targetNS.Close()).To(Succeed())
Expect(testutils.UnmountNS(targetNS)).To(Succeed())
})
for _, ver := range testutils.AllSpecVersions {
// Redefine ver inside for scope so real value is picked up by each dynamically defined It()
// See Gingkgo's "Patterns for dynamically generating tests" documentation.
ver := ver
It(fmt.Sprintf("[%s] creates an dummy link in a non-default namespace", ver), func() {
// Create dummy in other namespace
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
_, err := createDummy("foobar0", targetNS)
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
// Make sure dummy link exists in the target namespace
err = targetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName("foobar0")
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().Name).To(Equal("foobar0"))
return nil
})
Expect(err).NotTo(HaveOccurred())
})
It(fmt.Sprintf("[%s] configures and deconfigures a dummy link with ADD/CHECK/DEL", ver), func() {
const IFNAME = "dummy0"
conf := fmt.Sprintf(`{
"cniVersion": "%s",
"name": "dummyTestv4",
"type": "dummy",
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24",
"dataDir": "%s"
}
}`, ver, dataDir)
args := &skel.CmdArgs{
ContainerID: "contDummy",
Netns: targetNS.Path(),
IfName: IFNAME,
StdinData: []byte(conf),
}
t := newTesterByVersion(ver)
var result types.Result
var macAddress string
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
var err error
result, _, err = testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
macAddress = t.verifyResult(result, IFNAME)
return nil
})
Expect(err).NotTo(HaveOccurred())
// Make sure dummy link exists in the target namespace
err = targetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName(IFNAME)
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().Name).To(Equal(IFNAME))
if macAddress != "" {
hwaddr, err := net.ParseMAC(macAddress)
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr))
}
addrs, err := netlink.AddrList(link, syscall.AF_INET)
Expect(err).NotTo(HaveOccurred())
Expect(addrs).To(HaveLen(1))
return nil
})
Expect(err).NotTo(HaveOccurred())
// call CmdCheck
n := &Net{}
err = json.Unmarshal([]byte(conf), &n)
Expect(err).NotTo(HaveOccurred())
n.IPAM, _, err = allocator.LoadIPAMConfig([]byte(conf), "")
Expect(err).NotTo(HaveOccurred())
newConf, err := buildOneConfig("dummyTestv4", ver, n, result)
Expect(err).NotTo(HaveOccurred())
confString, err := json.Marshal(newConf)
Expect(err).NotTo(HaveOccurred())
args.StdinData = confString
// CNI Check dummy in the target namespace
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
return testutils.CmdCheckWithArgs(args, func() error { return cmdCheck(args) })
})
if testutils.SpecVersionHasCHECK(ver) {
Expect(err).NotTo(HaveOccurred())
} else {
Expect(err).To(MatchError("config version does not allow CHECK"))
}
args.StdinData = []byte(conf)
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
// Make sure dummy link has been deleted
err = targetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName(IFNAME)
Expect(err).To(HaveOccurred())
Expect(link).To(BeNil())
return nil
})
Expect(err).NotTo(HaveOccurred())
// DEL can be called multiple times, make sure no error is returned
// if the device is already removed.
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
})
It(fmt.Sprintf("[%s] fails to create dummy link with no ipam", ver), func() {
var err error
const confFmt = `{
"cniVersion": "%s",
"name": "mynet",
"type": "dummy"
}`
args := &skel.CmdArgs{
ContainerID: "dummyCont",
Netns: "/var/run/netns/test",
IfName: "dummy0",
StdinData: []byte(fmt.Sprintf(confFmt, ver)),
}
_ = originalNS.Do(func(netNS ns.NetNS) error {
defer GinkgoRecover()
_, _, err = testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).To(Equal(errors.New("dummy interface requires an IPAM configuration")))
return nil
})
})
}
})

View File

@ -19,7 +19,6 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
@ -32,21 +31,18 @@ import (
"github.com/containernetworking/cni/pkg/types"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/ip"
"github.com/containernetworking/plugins/pkg/ipam"
"github.com/containernetworking/plugins/pkg/ns"
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
)
var (
sysBusPCI = "/sys/bus/pci/devices"
)
var sysBusPCI = "/sys/bus/pci/devices"
// Array of different linux drivers bound to network device needed for DPDK
var userspaceDrivers = []string{"vfio-pci", "uio_pci_generic", "igb_uio"}
//NetConf for host-device config, look the README to learn how to use those parameters
// NetConf for host-device config, look the README to learn how to use those parameters
type NetConf struct {
types.NetConf
Device string `json:"device"` // Device-Name, something like eth0 or can0 etc.
@ -162,10 +158,7 @@ func cmdAdd(args *skel.CmdArgs) error {
if !cfg.DPDKMode {
err = containerNs.Do(func(_ ns.NetNS) error {
if err := ipam.ConfigureIface(args.IfName, newResult); err != nil {
return err
}
return nil
return ipam.ConfigureIface(args.IfName, newResult)
})
if err != nil {
return err
@ -326,10 +319,11 @@ func getLink(devname, hwaddr, kernelpath, pciaddr string) (netlink.Link, error)
if err != nil {
return nil, fmt.Errorf("failed to list node links: %v", err)
}
switch {
if len(devname) > 0 {
case len(devname) > 0:
return netlink.LinkByName(devname)
} else if len(hwaddr) > 0 {
case len(hwaddr) > 0:
hwAddr, err := net.ParseMAC(hwaddr)
if err != nil {
return nil, fmt.Errorf("failed to parse MAC address %q: %v", hwaddr, err)
@ -340,26 +334,26 @@ func getLink(devname, hwaddr, kernelpath, pciaddr string) (netlink.Link, error)
return link, nil
}
}
} else if len(kernelpath) > 0 {
case len(kernelpath) > 0:
if !filepath.IsAbs(kernelpath) || !strings.HasPrefix(kernelpath, "/sys/devices/") {
return nil, fmt.Errorf("kernel device path %q must be absolute and begin with /sys/devices/", kernelpath)
}
netDir := filepath.Join(kernelpath, "net")
files, err := ioutil.ReadDir(netDir)
entries, err := os.ReadDir(netDir)
if err != nil {
return nil, fmt.Errorf("failed to find network devices at %q", netDir)
}
// Grab the first device from eg /sys/devices/pci0000:00/0000:00:19.0/net
for _, file := range files {
for _, entry := range entries {
// Make sure it's really an interface
for _, l := range links {
if file.Name() == l.Attrs().Name {
if entry.Name() == l.Attrs().Name {
return l, nil
}
}
}
} else if len(pciaddr) > 0 {
case len(pciaddr) > 0:
netDir := filepath.Join(sysBusPCI, pciaddr, "net")
if _, err := os.Lstat(netDir); err != nil {
virtioNetDir := filepath.Join(sysBusPCI, pciaddr, "virtio*", "net")
@ -369,12 +363,12 @@ func getLink(devname, hwaddr, kernelpath, pciaddr string) (netlink.Link, error)
}
netDir = matches[0]
}
fInfo, err := ioutil.ReadDir(netDir)
entries, err := os.ReadDir(netDir)
if err != nil {
return nil, fmt.Errorf("failed to read net directory %s: %q", netDir, err)
}
if len(fInfo) > 0 {
return netlink.LinkByName(fInfo[0].Name())
if len(entries) > 0 {
return netlink.LinkByName(entries[0].Name())
}
return nil, fmt.Errorf("failed to find device name for pci address %s", pciaddr)
}
@ -387,7 +381,6 @@ func main() {
}
func cmdCheck(args *skel.CmdArgs) error {
cfg, err := loadConf(args.StdinData)
if err != nil {
return err
@ -444,7 +437,6 @@ func cmdCheck(args *skel.CmdArgs) error {
//
// Check prevResults for ips, routes and dns against values found in the container
if err := netns.Do(func(_ ns.NetNS) error {
// Check interface against values found in the container
err := validateCniContainerInterface(contMap)
if err != nil {
@ -470,7 +462,6 @@ func cmdCheck(args *skel.CmdArgs) error {
}
func validateCniContainerInterface(intf current.Interface) error {
var link netlink.Link
var err error

View File

@ -15,10 +15,10 @@
package main
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestVlan(t *testing.T) {

View File

@ -17,13 +17,16 @@ package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"math/rand"
"net"
"os"
"path"
"strings"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/vishvananda/netlink"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
types040 "github.com/containernetworking/cni/pkg/types/040"
@ -31,10 +34,6 @@ import (
"github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vishvananda/netlink"
)
type Net struct {
@ -87,14 +86,14 @@ func canonicalizeIP(ip *net.IP) error {
// LoadIPAMConfig creates IPAMConfig using json encoded configuration provided
// as `bytes`. At the moment values provided in envArgs are ignored so there
// is no possibility to overload the json configuration using envArgs
func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) {
func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, error) {
n := Net{}
if err := json.Unmarshal(bytes, &n); err != nil {
return nil, "", err
return nil, err
}
if n.IPAM == nil {
return nil, "", fmt.Errorf("IPAM config missing 'ipam' key")
return nil, fmt.Errorf("IPAM config missing 'ipam' key")
}
// Validate all ranges
@ -104,13 +103,13 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) {
for i := range n.IPAM.Addresses {
ip, addr, err := net.ParseCIDR(n.IPAM.Addresses[i].AddressStr)
if err != nil {
return nil, "", fmt.Errorf("invalid CIDR %s: %s", n.IPAM.Addresses[i].AddressStr, err)
return nil, fmt.Errorf("invalid CIDR %s: %s", n.IPAM.Addresses[i].AddressStr, err)
}
n.IPAM.Addresses[i].Address = *addr
n.IPAM.Addresses[i].Address.IP = ip
if err := canonicalizeIP(&n.IPAM.Addresses[i].Address.IP); err != nil {
return nil, "", fmt.Errorf("invalid address %d: %s", i, err)
return nil, fmt.Errorf("invalid address %d: %s", i, err)
}
if n.IPAM.Addresses[i].Address.IP.To4() != nil {
@ -124,7 +123,7 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) {
e := IPAMEnvArgs{}
err := types.LoadArgs(envArgs, &e)
if err != nil {
return nil, "", err
return nil, err
}
if e.IP != "" {
@ -133,7 +132,7 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) {
ip, subnet, err := net.ParseCIDR(ipstr)
if err != nil {
return nil, "", fmt.Errorf("invalid CIDR %s: %s", ipstr, err)
return nil, fmt.Errorf("invalid CIDR %s: %s", ipstr, err)
}
addr := Address{Address: net.IPNet{IP: ip, Mask: subnet.Mask}}
@ -150,7 +149,7 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) {
for _, item := range strings.Split(string(e.GATEWAY), ",") {
gwip := net.ParseIP(strings.TrimSpace(item))
if gwip == nil {
return nil, "", fmt.Errorf("invalid gateway address: %s", item)
return nil, fmt.Errorf("invalid gateway address: %s", item)
}
for i := range n.IPAM.Addresses {
@ -165,14 +164,14 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) {
// CNI spec 0.2.0 and below supported only one v4 and v6 address
if numV4 > 1 || numV6 > 1 {
if ok, _ := version.GreaterThanOrEqualTo(n.CNIVersion, "0.3.0"); !ok {
return nil, "", fmt.Errorf("CNI version %v does not support more than 1 address per family", n.CNIVersion)
return nil, fmt.Errorf("CNI version %v does not support more than 1 address per family", n.CNIVersion)
}
}
// Copy net name into IPAM so not to drag Net struct around
n.IPAM.Name = n.Name
return n.IPAM, n.CNIVersion, nil
return n.IPAM, nil
}
func buildOneConfig(name, cniVersion string, orig *Net, prevResult types.Result) (*Net, error) {
@ -215,7 +214,6 @@ func buildOneConfig(name, cniVersion string, orig *Net, prevResult types.Result)
}
return conf, nil
}
type tester interface {
@ -225,9 +223,11 @@ type tester interface {
type testerBase struct{}
type testerV10x testerBase
type testerV04x testerBase
type testerV03x testerBase
type (
testerV10x testerBase
testerV04x testerBase
testerV03x testerBase
)
func newTesterByVersion(version string) tester {
switch {
@ -260,8 +260,8 @@ func (t *testerV10x) expectDpdkInterfaceIP(result types.Result, ipAddress string
// check that the result was sane
res, err := types100.NewResultFromResult(result)
Expect(err).NotTo(HaveOccurred())
Expect(len(res.Interfaces)).To(Equal(0))
Expect(len(res.IPs)).To(Equal(1))
Expect(res.Interfaces).To(BeEmpty())
Expect(res.IPs).To(HaveLen(1))
Expect(res.IPs[0].Address.String()).To(Equal(ipAddress))
}
@ -282,8 +282,8 @@ func (t *testerV04x) expectDpdkInterfaceIP(result types.Result, ipAddress string
// check that the result was sane
res, err := types040.NewResultFromResult(result)
Expect(err).NotTo(HaveOccurred())
Expect(len(res.Interfaces)).To(Equal(0))
Expect(len(res.IPs)).To(Equal(1))
Expect(res.Interfaces).To(BeEmpty())
Expect(res.IPs).To(HaveLen(1))
Expect(res.IPs[0].Address.String()).To(Equal(ipAddress))
}
@ -304,8 +304,8 @@ func (t *testerV03x) expectDpdkInterfaceIP(result types.Result, ipAddress string
// check that the result was sane
res, err := types040.NewResultFromResult(result)
Expect(err).NotTo(HaveOccurred())
Expect(len(res.Interfaces)).To(Equal(0))
Expect(len(res.IPs)).To(Equal(1))
Expect(res.Interfaces).To(BeEmpty())
Expect(res.IPs).To(HaveLen(1))
Expect(res.IPs[0].Address.String()).To(Equal(ipAddress))
}
@ -665,11 +665,11 @@ var _ = Describe("base functionality", func() {
Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr))
Expect(link.Attrs().Flags & net.FlagUp).To(Equal(net.FlagUp))
//get the IP address of the interface in the target namespace
// get the IP address of the interface in the target namespace
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
Expect(err).NotTo(HaveOccurred())
addr := addrs[0].IPNet.String()
//assert that IP address is what we set
// assert that IP address is what we set
Expect(addr).To(Equal(targetIP))
return nil
@ -712,7 +712,6 @@ var _ = Describe("base functionality", func() {
}
_, _, err := testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) })
Expect(err).To(MatchError(`specify either "device", "hwaddr", "kernelpath" or "pciBusID"`))
})
It(fmt.Sprintf("[%s] works with a valid config without IPAM", ver), func() {
@ -869,7 +868,7 @@ var _ = Describe("base functionality", func() {
err = json.Unmarshal([]byte(conf), &n)
Expect(err).NotTo(HaveOccurred())
n.IPAM, _, err = LoadIPAMConfig([]byte(conf), "")
n.IPAM, err = LoadIPAMConfig([]byte(conf), "")
Expect(err).NotTo(HaveOccurred())
if testutils.SpecVersionHasCHECK(ver) {
@ -962,11 +961,11 @@ var _ = Describe("base functionality", func() {
Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr))
Expect(link.Attrs().Flags & net.FlagUp).To(Equal(net.FlagUp))
//get the IP address of the interface in the target namespace
// get the IP address of the interface in the target namespace
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
Expect(err).NotTo(HaveOccurred())
addr := addrs[0].IPNet.String()
//assert that IP address is what we set
// assert that IP address is what we set
Expect(addr).To(Equal(targetIP))
return nil
@ -985,7 +984,7 @@ var _ = Describe("base functionality", func() {
err = json.Unmarshal([]byte(conf), &n)
Expect(err).NotTo(HaveOccurred())
n.IPAM, _, err = LoadIPAMConfig([]byte(conf), "")
n.IPAM, err = LoadIPAMConfig([]byte(conf), "")
Expect(err).NotTo(HaveOccurred())
if testutils.SpecVersionHasCHECK(ver) {
@ -1161,14 +1160,14 @@ type fakeFilesystem struct {
func (fs *fakeFilesystem) use() func() {
// create the new fake fs root dir in /tmp/sriov...
tmpDir, err := ioutil.TempDir("", "sriov")
tmpDir, err := os.MkdirTemp("", "sriov")
if err != nil {
panic(fmt.Errorf("error creating fake root dir: %s", err.Error()))
}
fs.rootDir = tmpDir
for _, dir := range fs.dirs {
err := os.MkdirAll(path.Join(fs.rootDir, dir), 0755)
err := os.MkdirAll(path.Join(fs.rootDir, dir), 0o755)
if err != nil {
panic(fmt.Errorf("error creating fake directory: %s", err.Error()))
}

View File

@ -26,7 +26,6 @@ import (
"github.com/containernetworking/cni/pkg/types"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/ip"
"github.com/containernetworking/plugins/pkg/ipam"
"github.com/containernetworking/plugins/pkg/ns"
@ -36,9 +35,10 @@ import (
type NetConf struct {
types.NetConf
Master string `json:"master"`
Mode string `json:"mode"`
MTU int `json:"mtu"`
Master string `json:"master"`
Mode string `json:"mode"`
MTU int `json:"mtu"`
LinkContNs bool `json:"linkInContainer,omitempty"`
}
func init() {
@ -48,9 +48,9 @@ func init() {
runtime.LockOSThread()
}
func loadConf(bytes []byte, cmdCheck bool) (*NetConf, string, error) {
func loadConf(args *skel.CmdArgs, cmdCheck bool) (*NetConf, string, error) {
n := &NetConf{}
if err := json.Unmarshal(bytes, n); err != nil {
if err := json.Unmarshal(args.StdinData, n); err != nil {
return nil, "", fmt.Errorf("failed to load netconf: %v", err)
}
@ -73,8 +73,8 @@ func loadConf(bytes []byte, cmdCheck bool) (*NetConf, string, error) {
}
if n.Master == "" {
if result == nil {
defaultRouteInterface, err := getDefaultRouteInterfaceName()
var defaultRouteInterface string
defaultRouteInterface, err = getNamespacedDefaultRouteInterfaceName(args.Netns, n.LinkContNs)
if err != nil {
return nil, "", err
}
@ -124,7 +124,15 @@ func createIpvlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Interf
return nil, err
}
m, err := netlink.LinkByName(conf.Master)
var m netlink.Link
if conf.LinkContNs {
err = netns.Do(func(_ ns.NetNS) error {
m, err = netlink.LinkByName(conf.Master)
return err
})
} else {
m, err = netlink.LinkByName(conf.Master)
}
if err != nil {
return nil, fmt.Errorf("failed to lookup master %q: %v", conf.Master, err)
}
@ -146,8 +154,14 @@ func createIpvlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Interf
Mode: mode,
}
if err := netlink.LinkAdd(mv); err != nil {
return nil, fmt.Errorf("failed to create ipvlan: %v", err)
if conf.LinkContNs {
err = netns.Do(func(_ ns.NetNS) error {
return netlink.LinkAdd(mv)
})
} else {
if err := netlink.LinkAdd(mv); err != nil {
return nil, fmt.Errorf("failed to create ipvlan: %v", err)
}
}
err = netns.Do(func(_ ns.NetNS) error {
@ -193,8 +207,31 @@ func getDefaultRouteInterfaceName() (string, error) {
return "", fmt.Errorf("no default route interface found")
}
func getNamespacedDefaultRouteInterfaceName(namespace string, inContainer bool) (string, error) {
if !inContainer {
return getDefaultRouteInterfaceName()
}
netns, err := ns.GetNS(namespace)
if err != nil {
return "", fmt.Errorf("failed to open netns %q: %v", netns, err)
}
defer netns.Close()
var defaultRouteInterface string
err = netns.Do(func(_ ns.NetNS) error {
defaultRouteInterface, err = getDefaultRouteInterfaceName()
if err != nil {
return err
}
return nil
})
if err != nil {
return "", err
}
return defaultRouteInterface, nil
}
func cmdAdd(args *skel.CmdArgs) error {
n, cniVersion, err := loadConf(args.StdinData, false)
n, cniVersion, err := loadConf(args, false)
if err != nil {
return err
}
@ -256,11 +293,9 @@ func cmdAdd(args *skel.CmdArgs) error {
err = netns.Do(func(_ ns.NetNS) error {
_, _ = sysctl.Sysctl(fmt.Sprintf("net/ipv4/conf/%s/arp_notify", args.IfName), "1")
_, _ = sysctl.Sysctl(fmt.Sprintf("net/ipv6/conf/%s/ndisc_notify", args.IfName), "1")
if err := ipam.ConfigureIface(args.IfName, result); err != nil {
return err
}
return nil
return ipam.ConfigureIface(args.IfName, result)
})
if err != nil {
return err
@ -272,7 +307,7 @@ func cmdAdd(args *skel.CmdArgs) error {
}
func cmdDel(args *skel.CmdArgs) error {
n, _, err := loadConf(args.StdinData, false)
n, _, err := loadConf(args, false)
if err != nil {
return err
}
@ -319,8 +354,7 @@ func main() {
}
func cmdCheck(args *skel.CmdArgs) error {
n, _, err := loadConf(args.StdinData, true)
n, _, err := loadConf(args, true)
if err != nil {
return err
}
@ -369,16 +403,23 @@ func cmdCheck(args *skel.CmdArgs) error {
contMap.Sandbox, args.Netns)
}
m, err := netlink.LinkByName(n.Master)
if n.LinkContNs {
err = netns.Do(func(_ ns.NetNS) error {
_, err = netlink.LinkByName(n.Master)
return err
})
} else {
_, err = netlink.LinkByName(n.Master)
}
if err != nil {
return fmt.Errorf("failed to lookup master %q: %v", n.Master, err)
}
// Check prevResults for ips, routes and dns against values found in the container
if err := netns.Do(func(_ ns.NetNS) error {
// Check interface against values found in the container
err := validateCniContainerInterface(contMap, m.Attrs().Index, n.Mode)
err := validateCniContainerInterface(contMap, n.Mode)
if err != nil {
return err
}
@ -400,8 +441,7 @@ func cmdCheck(args *skel.CmdArgs) error {
return nil
}
func validateCniContainerInterface(intf current.Interface, masterIndex int, modeExpected string) error {
func validateCniContainerInterface(intf current.Interface, modeExpected string) error {
var link netlink.Link
var err error
@ -422,6 +462,9 @@ func validateCniContainerInterface(intf current.Interface, masterIndex int, mode
}
mode, err := modeFromString(modeExpected)
if err != nil {
return err
}
if ipv.Mode != mode {
currString, err := modeToString(ipv.Mode)
if err != nil {

View File

@ -15,10 +15,10 @@
package main
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestIpvlan(t *testing.T) {

View File

@ -17,28 +17,29 @@ package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net"
"os"
"strings"
"syscall"
"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/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/vishvananda/netlink"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
types020 "github.com/containernetworking/cni/pkg/types/020"
types040 "github.com/containernetworking/cni/pkg/types/040"
types100 "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
const MASTER_NAME = "eth0"
const (
MASTER_NAME = "eth0"
MASTER_NAME_INCONTAINER = "eth1"
)
type Net struct {
Name string `json:"name"`
@ -50,6 +51,7 @@ type Net struct {
DNS types.DNS `json:"dns"`
RawPrevResult map[string]interface{} `json:"prevResult,omitempty"`
PrevResult types100.Result `json:"-"`
LinkContNs bool `json:"linkInContainer"`
}
func buildOneConfig(cniVersion string, master string, orig *Net, prevResult types.Result) (*Net, error) {
@ -91,7 +93,6 @@ func buildOneConfig(cniVersion string, master string, orig *Net, prevResult type
}
return conf, nil
}
func ipvlanAddCheckDelTest(conf, masterName string, originalNS, targetNS ns.NetNS) {
@ -140,7 +141,7 @@ func ipvlanAddCheckDelTest(conf, masterName string, originalNS, targetNS ns.NetN
addrs, err := netlink.AddrList(link, syscall.AF_INET)
Expect(err).NotTo(HaveOccurred())
Expect(len(addrs)).To(Equal(1))
Expect(addrs).To(HaveLen(1))
return nil
})
Expect(err).NotTo(HaveOccurred())
@ -205,9 +206,11 @@ type tester interface {
type testerBase struct{}
type testerV10x testerBase
type testerV04x testerBase
type testerV02x testerBase
type (
testerV10x testerBase
testerV04x testerBase
testerV02x testerBase
)
func newTesterByVersion(version string) tester {
switch {
@ -227,9 +230,9 @@ func (t *testerV10x) verifyResult(result types.Result, name string) string {
r, err := types100.GetResult(result)
Expect(err).NotTo(HaveOccurred())
Expect(len(r.Interfaces)).To(Equal(1))
Expect(r.Interfaces).To(HaveLen(1))
Expect(r.Interfaces[0].Name).To(Equal(name))
Expect(len(r.IPs)).To(Equal(1))
Expect(r.IPs).To(HaveLen(1))
return r.Interfaces[0].Mac
}
@ -239,15 +242,15 @@ func (t *testerV04x) verifyResult(result types.Result, name string) string {
r, err := types040.GetResult(result)
Expect(err).NotTo(HaveOccurred())
Expect(len(r.Interfaces)).To(Equal(1))
Expect(r.Interfaces).To(HaveLen(1))
Expect(r.Interfaces[0].Name).To(Equal(name))
Expect(len(r.IPs)).To(Equal(1))
Expect(r.IPs).To(HaveLen(1))
return r.Interfaces[0].Mac
}
// verifyResult minimally verifies the Result and returns the interface's MAC address
func (t *testerV02x) verifyResult(result types.Result, name string) string {
func (t *testerV02x) verifyResult(result types.Result, _ string) string {
r, err := types020.GetResult(result)
Expect(err).NotTo(HaveOccurred())
@ -272,7 +275,7 @@ var _ = Describe("ipvlan Operations", func() {
targetNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
dataDir, err = ioutil.TempDir("", "ipvlan_test")
dataDir, err = os.MkdirTemp("", "ipvlan_test")
Expect(err).NotTo(HaveOccurred())
err = originalNS.Do(func(ns.NetNS) error {
@ -290,6 +293,22 @@ var _ = Describe("ipvlan Operations", func() {
return nil
})
Expect(err).NotTo(HaveOccurred())
err = targetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
// Add master
err = netlink.LinkAdd(&netlink.Dummy{
LinkAttrs: netlink.LinkAttrs{
Name: MASTER_NAME_INCONTAINER,
},
})
Expect(err).NotTo(HaveOccurred())
_, err = netlink.LinkByName(MASTER_NAME_INCONTAINER)
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
})
AfterEach(func() {
@ -302,67 +321,77 @@ var _ = Describe("ipvlan Operations", func() {
Expect(testutils.UnmountNS(targetNS)).To(Succeed())
})
for _, ver := range testutils.AllSpecVersions {
// Redefine ver inside for scope so real value is picked up by each dynamically defined It()
// See Gingkgo's "Patterns for dynamically generating tests" documentation.
ver := ver
for _, inContainer := range []bool{false, true} {
masterInterface := MASTER_NAME
if inContainer {
masterInterface = MASTER_NAME_INCONTAINER
}
// for _, ver := range testutils.AllSpecVersions {
for _, ver := range [...]string{"1.0.0"} {
// Redefine ver inside for scope so real value is picked up by each dynamically defined It()
// See Gingkgo's "Patterns for dynamically generating tests" documentation.
ver := ver
isInContainer := inContainer // Tests need a local var with constant value
It(fmt.Sprintf("[%s] creates an ipvlan link in a non-default namespace", ver), func() {
conf := &NetConf{
NetConf: types.NetConf{
CNIVersion: ver,
Name: "testConfig",
Type: "ipvlan",
},
Master: MASTER_NAME,
Mode: "l2",
MTU: 1500,
}
It(fmt.Sprintf("[%s] creates an ipvlan link in a non-default namespace", ver), func() {
conf := &NetConf{
NetConf: types.NetConf{
CNIVersion: ver,
Name: "testConfig",
Type: "ipvlan",
},
Master: masterInterface,
Mode: "l2",
MTU: 1500,
LinkContNs: isInContainer,
}
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
_, err := createIpvlan(conf, "foobar0", targetNS)
Expect(err).NotTo(HaveOccurred())
return nil
})
_, err := createIpvlan(conf, "foobar0", targetNS)
Expect(err).NotTo(HaveOccurred())
return nil
// Make sure ipvlan link exists in the target namespace
err = targetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName("foobar0")
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().Name).To(Equal("foobar0"))
return nil
})
Expect(err).NotTo(HaveOccurred())
})
Expect(err).NotTo(HaveOccurred())
// Make sure ipvlan link exists in the target namespace
err = targetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName("foobar0")
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().Name).To(Equal("foobar0"))
return nil
})
Expect(err).NotTo(HaveOccurred())
})
It(fmt.Sprintf("[%s] configures and deconfigures an iplvan link with ADD/DEL", ver), func() {
conf := fmt.Sprintf(`{
It(fmt.Sprintf("[%s] configures and deconfigures an iplvan link with ADD/DEL", ver), func() {
conf := fmt.Sprintf(`{
"cniVersion": "%s",
"name": "mynet",
"type": "ipvlan",
"master": "%s",
"linkInContainer": %t,
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24",
"dataDir": "%s"
}
}`, ver, MASTER_NAME, dataDir)
}`, ver, masterInterface, isInContainer, dataDir)
ipvlanAddCheckDelTest(conf, "", originalNS, targetNS)
})
ipvlanAddCheckDelTest(conf, "", originalNS, targetNS)
})
if testutils.SpecVersionHasChaining(ver) {
It(fmt.Sprintf("[%s] configures and deconfigures an iplvan link with ADD/DEL when chained", ver), func() {
conf := fmt.Sprintf(`{
if testutils.SpecVersionHasChaining(ver) {
It(fmt.Sprintf("[%s] configures and deconfigures an iplvan link with ADD/DEL when chained", ver), func() {
conf := fmt.Sprintf(`{
"cniVersion": "%s",
"name": "mynet",
"type": "ipvlan",
"linkInContainer": %t,
"prevResult": {
"interfaces": [
{
@ -379,84 +408,91 @@ var _ = Describe("ipvlan Operations", func() {
],
"routes": []
}
}`, ver, MASTER_NAME)
}`, ver, isInContainer, masterInterface)
ipvlanAddCheckDelTest(conf, MASTER_NAME, originalNS, targetNS)
})
}
ipvlanAddCheckDelTest(conf, masterInterface, originalNS, targetNS)
})
}
It(fmt.Sprintf("[%s] deconfigures an unconfigured ipvlan link with DEL", ver), func() {
conf := fmt.Sprintf(`{
It(fmt.Sprintf("[%s] deconfigures an unconfigured ipvlan link with DEL", ver), func() {
conf := fmt.Sprintf(`{
"cniVersion": "%s",
"name": "mynet",
"type": "ipvlan",
"master": "%s",
"linkInContainer": %t,
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24",
"dataDir": "%s"
}
}`, ver, MASTER_NAME, dataDir)
}`, ver, masterInterface, isInContainer, dataDir)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: "ipvl0",
StdinData: []byte(conf),
}
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: "ipvl0",
StdinData: []byte(conf),
}
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
err := testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
err := testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
})
It(fmt.Sprintf("[%s] configures and deconfigures a ipvlan link with ADD/DEL, without master config", ver), func() {
conf := fmt.Sprintf(`{
It(fmt.Sprintf("[%s] configures and deconfigures a ipvlan link with ADD/DEL, without master config", ver), func() {
conf := fmt.Sprintf(`{
"cniVersion": "%s",
"name": "mynet",
"type": "ipvlan",
"linkInContainer": %t,
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24",
"dataDir": "%s"
}
}`, ver, dataDir)
}`, ver, isInContainer, dataDir)
// Make MASTER_NAME as default route interface
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName(MASTER_NAME)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetUp(link)
Expect(err).NotTo(HaveOccurred())
var address = &net.IPNet{IP: net.IPv4(192, 0, 0, 1), Mask: net.CIDRMask(24, 32)}
var addr = &netlink.Addr{IPNet: address}
err = netlink.AddrAdd(link, addr)
Expect(err).NotTo(HaveOccurred())
// add default gateway into MASTER
dst := &net.IPNet{
IP: net.IPv4(0, 0, 0, 0),
Mask: net.CIDRMask(0, 0),
// Make MASTER_NAME as default route interface
currentNs := originalNS
if isInContainer {
currentNs = targetNS
}
ip := net.IPv4(192, 0, 0, 254)
route := netlink.Route{LinkIndex: link.Attrs().Index, Dst: dst, Gw: ip}
err = netlink.RouteAdd(&route)
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
err := currentNs.Do(func(ns.NetNS) error {
defer GinkgoRecover()
ipvlanAddCheckDelTest(conf, MASTER_NAME, originalNS, targetNS)
})
link, err := netlink.LinkByName(masterInterface)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetUp(link)
Expect(err).NotTo(HaveOccurred())
address := &net.IPNet{IP: net.IPv4(192, 0, 0, 1), Mask: net.CIDRMask(24, 32)}
addr := &netlink.Addr{IPNet: address}
err = netlink.AddrAdd(link, addr)
Expect(err).NotTo(HaveOccurred())
// add default gateway into MASTER
dst := &net.IPNet{
IP: net.IPv4(0, 0, 0, 0),
Mask: net.CIDRMask(0, 0),
}
ip := net.IPv4(192, 0, 0, 254)
route := netlink.Route{LinkIndex: link.Attrs().Index, Dst: dst, Gw: ip}
err = netlink.RouteAdd(&route)
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
ipvlanAddCheckDelTest(conf, masterInterface, originalNS, targetNS)
})
}
}
})

View File

@ -26,7 +26,6 @@ import (
"github.com/containernetworking/cni/pkg/types"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/ns"
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
)
@ -112,7 +111,7 @@ func cmdAdd(args *skel.CmdArgs) error {
r := &current.Result{
CNIVersion: conf.CNIVersion,
Interfaces: []*current.Interface{
&current.Interface{
{
Name: args.IfName,
Mac: "00:00:00:00:00:00",
Sandbox: args.Netns,

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