Compare commits
230 Commits
Author | SHA1 | Date | |
---|---|---|---|
1fb5bf669e | |||
3712c1cfcb | |||
825421709e | |||
d708217503 | |||
abee8ccc0d | |||
e1474463ef | |||
11ee4b61d9 | |||
a4cbf13a9b | |||
6cac5d603b | |||
333fc9a0d7 | |||
f90ac41ae4 | |||
93a1b3d0e7 | |||
00406f9d1e | |||
e82848a9cb | |||
5280b4d582 | |||
495a2cbb0c | |||
8c59fc1eea | |||
2eee7cef35 | |||
1079e113fe | |||
999ca15763 | |||
dad27e9f72 | |||
0b1a96ff30 | |||
c8c26897ba | |||
28c5faee75 | |||
d0d9e36662 | |||
f7662a2435 | |||
aacae5c053 | |||
6b7876125d | |||
2f0faf6721 | |||
37531cdaf5 | |||
a8d4e0a7dd | |||
845ef62b74 | |||
691186ca7f | |||
adaeedd6af | |||
19e5747a8c | |||
4cf3da4ae3 | |||
c20da1521f | |||
b66b5dd85f | |||
e727ad6697 | |||
18172539d8 | |||
f20b8408a4 | |||
6ff8e5eb86 | |||
61fa963636 | |||
33ccedc66f | |||
853b82d19f | |||
d216b0c39b | |||
f95505231a | |||
5f25a93a47 | |||
7c11d48630 | |||
9d9ec6e3e1 | |||
8fd63065a6 | |||
c1a7948b19 | |||
1561794ae9 | |||
fb8ca5d31e | |||
f2574a7cb1 | |||
438548a9dd | |||
8e69e38d51 | |||
0a100e5d8f | |||
3eb775c5e6 | |||
719f60bb91 | |||
2ba7f1608f | |||
bf79945c70 | |||
ba41448fe6 | |||
13fd3de77f | |||
283f200489 | |||
a7e8db00cb | |||
ffb2e2d7d1 | |||
d03b84d8f2 | |||
1512d727cb | |||
470eee1385 | |||
2216cff9e8 | |||
83029befef | |||
48aa2f4eef | |||
ca12d49b41 | |||
2b097c5a62 | |||
0389a29052 | |||
6265f4e4ca | |||
edab9efdea | |||
1b2dc7c2a4 | |||
38f18d26ec | |||
e51301765c | |||
7e918412d5 | |||
99b475ab1a | |||
10b5639361 | |||
65fe256058 | |||
00b82fb666 | |||
c795a3c6b1 | |||
c10af01dfb | |||
9cf1a09835 | |||
d8fc886bf0 | |||
c347755f87 | |||
5b7a263e8f | |||
135292e050 | |||
7dcd738d34 | |||
83fe87c5b0 | |||
090af7db9a | |||
9f1f9a588b | |||
71aa710196 | |||
10ddd9e454 | |||
4a6147a155 | |||
435ef2235d | |||
43db9cc063 | |||
821982da1c | |||
cac8230e7c | |||
bc5f3defe7 | |||
47a4319462 | |||
68a661999a | |||
63235a2531 | |||
7bbd4d19e9 | |||
deec68747e | |||
6f6345ca05 | |||
6c0d73ecc0 | |||
8813bfea7b | |||
16d05ec100 | |||
086f7eb7a1 | |||
d71d0f2da1 | |||
00e0d3b758 | |||
2fb0efe8a3 | |||
3bc00017e3 | |||
c0fe3b7bde | |||
09f36a295d | |||
d3ee71f240 | |||
a02bf4b463 | |||
79f524689c | |||
5a7619c019 | |||
709e775b13 | |||
3a04eb00bb | |||
16ba4222bc | |||
177e0bf2d9 | |||
d12b81dec5 | |||
86e39cfe3c | |||
6223674f25 | |||
36e1e162fa | |||
286064b9ec | |||
9ee4d3225d | |||
2d1005ec02 | |||
23c2134110 | |||
fb92605570 | |||
01d0031487 | |||
98e01b7c80 | |||
9a2f763345 | |||
98359ff8b4 | |||
29e6486154 | |||
bbf47c1083 | |||
5d02d91c96 | |||
755714d716 | |||
e2e14ee46f | |||
fb5d195fc5 | |||
ac7cf82531 | |||
c798f80912 | |||
9fa80036d3 | |||
c4d24e80d6 | |||
2c4c27eb17 | |||
0924b71fc8 | |||
0af8153e9b | |||
83fe27748c | |||
bf9c25887a | |||
0fc229df5e | |||
ec924a4be2 | |||
d27fabcd83 | |||
20a92ff382 | |||
5c29eb7fb5 | |||
f48a5ea512 | |||
a3b678ee03 | |||
87ccb8918b | |||
fa2ed0fd6e | |||
b769956cf4 | |||
df141fc722 | |||
35047644a8 | |||
352f181ff1 | |||
91b1a0e385 | |||
648dd2e14c | |||
615420fa9f | |||
90ed30a55a | |||
020b8db6ab | |||
c5e81e3c05 | |||
3be17f4af7 | |||
f64652faf8 | |||
edfd2274a5 | |||
d7efab18c0 | |||
12471312e1 | |||
c1e2be2765 | |||
fd9408bc97 | |||
ec76e3c35c | |||
c30b47a712 | |||
9dc2ed2c0a | |||
6b30e290d2 | |||
1a6f478913 | |||
63a6dbcfd6 | |||
7e9ada51e7 | |||
198ab129a1 | |||
0463fd19af | |||
ac8673105a | |||
fcf14d39fd | |||
54f69e0a59 | |||
87c3643d3c | |||
f89a005740 | |||
7fcf8e4860 | |||
8c3664b2b1 | |||
e2a71387ab | |||
c78e1e4656 | |||
9f4090dabf | |||
9c59728d39 | |||
6a94696205 | |||
356db54531 | |||
f7dfa0f600 | |||
f1aa6c2622 | |||
bf4068e1cd | |||
7c452c77cd | |||
6264f7bff9 | |||
6bcc877722 | |||
9114aa6d37 | |||
f891722833 | |||
a70e87c3aa | |||
0a0853a756 | |||
0c39335765 | |||
8b8825bcd8 | |||
93604ec20a | |||
e3d563b0f0 | |||
16e4a82b32 | |||
e952f16c75 | |||
5ad4fcf85a | |||
292f188e4e | |||
57c1cb5058 | |||
b8a10bbe11 | |||
600c58a54f | |||
3512b10ff0 | |||
7a98979487 | |||
7aa07efe29 | |||
96c3af81e2 |
2
.github/actions/retest-action/Dockerfile
vendored
2
.github/actions/retest-action/Dockerfile
vendored
@ -1,4 +1,4 @@
|
|||||||
FROM alpine:3.10
|
FROM alpine:3.18
|
||||||
|
|
||||||
RUN apk add --no-cache curl jq
|
RUN apk add --no-cache curl jq
|
||||||
|
|
||||||
|
4
.github/actions/retest-action/entrypoint.sh
vendored
4
.github/actions/retest-action/entrypoint.sh
vendored
@ -27,10 +27,10 @@ curl --request GET \
|
|||||||
--header "authorization: Bearer ${GITHUB_TOKEN}" \
|
--header "authorization: Bearer ${GITHUB_TOKEN}" \
|
||||||
--header "content-type: application/json" | jq '.workflow_runs | max_by(.run_number)' > run.json
|
--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 \
|
curl --request POST \
|
||||||
--url "${RERUN_URL}" \
|
--url "${RUN_URL}/rerun-failed-jobs" \
|
||||||
--header "authorization: Bearer ${GITHUB_TOKEN}" \
|
--header "authorization: Bearer ${GITHUB_TOKEN}" \
|
||||||
--header "content-type: application/json"
|
--header "content-type: application/json"
|
||||||
|
|
||||||
|
23
.github/dependabot.yml
vendored
Normal file
23
.github/dependabot.yml
vendored
Normal 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:
|
||||||
|
- "*"
|
2
.github/workflows/commands.yml
vendored
2
.github/workflows/commands.yml
vendored
@ -9,7 +9,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Re-Test Action
|
- name: Re-Test Action
|
||||||
uses: ./.github/actions/retest-action
|
uses: ./.github/actions/retest-action
|
||||||
|
57
.github/workflows/test.yaml
vendored
57
.github/workflows/test.yaml
vendored
@ -4,20 +4,36 @@ name: test
|
|||||||
on: ["push", "pull_request"]
|
on: ["push", "pull_request"]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
GO_VERSION: "1.17"
|
GO_VERSION: "1.21"
|
||||||
LINUX_ARCHES: "amd64 386 arm arm64 s390x mips64le ppc64le"
|
LINUX_ARCHES: "amd64 386 arm arm64 s390x mips64le ppc64le riscv64"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
lint:
|
||||||
name: Build all linux architectures
|
name: Lint
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
- name: setup go
|
- name: setup go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GO_VERSION }}
|
go-version-file: go.mod
|
||||||
- uses: actions/checkout@v2
|
- 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
|
- name: Build on all supported architectures
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
@ -26,9 +42,9 @@ jobs:
|
|||||||
GOARCH=$arch ./build_linux.sh
|
GOARCH=$arch ./build_linux.sh
|
||||||
rm bin/*
|
rm bin/*
|
||||||
done
|
done
|
||||||
|
|
||||||
test-linux:
|
test-linux:
|
||||||
name: Run tests on Linux amd64
|
name: Run tests on Linux amd64
|
||||||
|
needs: build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Install kernel module
|
- name: Install kernel module
|
||||||
@ -37,24 +53,21 @@ jobs:
|
|||||||
sudo apt-get install linux-modules-extra-$(uname -r)
|
sudo apt-get install linux-modules-extra-$(uname -r)
|
||||||
- name: Install nftables
|
- name: Install nftables
|
||||||
run: sudo apt-get install nftables
|
run: sudo apt-get install nftables
|
||||||
|
- uses: actions/checkout@v4
|
||||||
- name: setup go
|
- name: setup go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GO_VERSION }}
|
go-version-file: go.mod
|
||||||
- name: Set up Go for root
|
- name: Set up Go for root
|
||||||
run: |
|
run: |
|
||||||
sudo ln -sf `which go` `sudo which go` || true
|
sudo ln -sf `which go` `sudo which go` || true
|
||||||
sudo go version
|
sudo go version
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Install test binaries
|
- name: Install test binaries
|
||||||
env:
|
|
||||||
GO111MODULE: off
|
|
||||||
run: |
|
run: |
|
||||||
go get github.com/containernetworking/cni/cnitool
|
go install github.com/containernetworking/cni/cnitool@latest
|
||||||
go get github.com/mattn/goveralls
|
go install github.com/mattn/goveralls@latest
|
||||||
go get github.com/modocache/gover
|
go install github.com/modocache/gover@latest
|
||||||
|
|
||||||
- name: test
|
- name: test
|
||||||
run: PATH=$PATH:$(go env GOPATH)/bin COVERALLS=1 ./test_linux.sh
|
run: PATH=$PATH:$(go env GOPATH)/bin COVERALLS=1 ./test_linux.sh
|
||||||
@ -66,15 +79,15 @@ jobs:
|
|||||||
PATH=$PATH:$(go env GOPATH)/bin
|
PATH=$PATH:$(go env GOPATH)/bin
|
||||||
gover
|
gover
|
||||||
goveralls -coverprofile=gover.coverprofile -service=github
|
goveralls -coverprofile=gover.coverprofile -service=github
|
||||||
|
|
||||||
test-win:
|
test-win:
|
||||||
name: Build and run tests on Windows
|
name: Build and run tests on Windows
|
||||||
|
needs: build
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
- name: setup go
|
- name: setup go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GO_VERSION }}
|
go-version-file: go.mod
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: test
|
- name: test
|
||||||
run: bash ./test_windows.sh
|
run: bash ./test_windows.sh
|
||||||
|
45
.golangci.yml
Normal file
45
.golangci.yml
Normal 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
12
.yamllint.yml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
extends: default
|
||||||
|
|
||||||
|
ignore: |
|
||||||
|
vendor
|
||||||
|
|
||||||
|
rules:
|
||||||
|
document-start: disable
|
||||||
|
line-length: disable
|
||||||
|
truthy:
|
||||||
|
ignore: |
|
||||||
|
.github/workflows/*.yml
|
||||||
|
.github/workflows/*.yaml
|
@ -7,3 +7,4 @@ This is the official list of the CNI network plugins owners:
|
|||||||
- Matt Dupre <matt@tigera.io> (@matthewdupre)
|
- Matt Dupre <matt@tigera.io> (@matthewdupre)
|
||||||
- Michael Cambria <mcambria@redhat.com> (@mccv1r0)
|
- Michael Cambria <mcambria@redhat.com> (@mccv1r0)
|
||||||
- Piotr Skarmuk <piotr.skarmuk@gmail.com> (@jellonek)
|
- Piotr Skarmuk <piotr.skarmuk@gmail.com> (@jellonek)
|
||||||
|
- Michael Zappa <michael.zappa@gmail.com> (@MikeZappa87)
|
||||||
|
@ -14,13 +14,14 @@ Read [CONTRIBUTING](CONTRIBUTING.md) for build and test instructions.
|
|||||||
* `ptp`: Creates a veth pair.
|
* `ptp`: Creates a veth pair.
|
||||||
* `vlan`: Allocates a vlan device.
|
* `vlan`: Allocates a vlan device.
|
||||||
* `host-device`: Move an already-existing device into a container.
|
* `host-device`: Move an already-existing device into a container.
|
||||||
|
* `dummy`: Creates a new Dummy device in the container.
|
||||||
#### Windows: Windows specific
|
#### Windows: Windows specific
|
||||||
* `win-bridge`: Creates a bridge, adds the host and the container to it.
|
* `win-bridge`: Creates a bridge, adds the host and the container to it.
|
||||||
* `win-overlay`: Creates an overlay interface to the container.
|
* `win-overlay`: Creates an overlay interface to the container.
|
||||||
### IPAM: IP address allocation
|
### IPAM: IP address allocation
|
||||||
* `dhcp`: Runs a daemon on the host to make DHCP requests on behalf of the container
|
* `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
|
* `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
|
### Meta: other plugins
|
||||||
* `tuning`: Tweaks sysctl parameters of an existing interface
|
* `tuning`: Tweaks sysctl parameters of an existing interface
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env sh
|
||||||
set -e
|
set -e
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
if [ "$(uname)" == "Darwin" ]; then
|
if [ "$(uname)" = "Darwin" ]; then
|
||||||
export GOOS="${GOOS:-linux}"
|
export GOOS="${GOOS:-linux}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env sh
|
||||||
set -e
|
set -e
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
60
go.mod
60
go.mod
@ -1,40 +1,50 @@
|
|||||||
module github.com/containernetworking/plugins
|
module github.com/containernetworking/plugins
|
||||||
|
|
||||||
go 1.17
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Microsoft/hcsshim v0.8.20
|
github.com/Microsoft/hcsshim v0.11.4
|
||||||
github.com/alexflint/go-filemutex v1.1.0
|
github.com/alexflint/go-filemutex v1.2.0
|
||||||
github.com/buger/jsonparser v1.1.1
|
github.com/buger/jsonparser v1.1.1
|
||||||
github.com/containernetworking/cni v1.0.1
|
github.com/containernetworking/cni v1.1.2
|
||||||
github.com/coreos/go-iptables v0.6.0
|
github.com/coreos/go-iptables v0.7.0
|
||||||
github.com/coreos/go-systemd/v22 v22.3.2
|
github.com/coreos/go-systemd/v22 v22.5.0
|
||||||
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c
|
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c
|
||||||
github.com/d2g/dhcp4client v1.0.0
|
github.com/d2g/dhcp4client v1.0.0
|
||||||
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5
|
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/mattn/go-shellwords v1.0.12
|
||||||
github.com/networkplumbing/go-nft v0.2.0
|
github.com/networkplumbing/go-nft v0.4.0
|
||||||
github.com/onsi/ginkgo v1.16.4
|
github.com/onsi/ginkgo/v2 v2.13.2
|
||||||
github.com/onsi/gomega v1.15.0
|
github.com/onsi/gomega v1.30.0
|
||||||
github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1
|
github.com/opencontainers/selinux v1.11.0
|
||||||
github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5
|
github.com/safchain/ethtool v0.3.0
|
||||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e
|
github.com/vishvananda/netlink v1.2.1-beta.2
|
||||||
|
golang.org/x/sys v0.15.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Microsoft/go-winio v0.4.17 // indirect
|
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||||
github.com/containerd/cgroups v1.0.1 // indirect
|
github.com/containerd/cgroups v1.1.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.4.9 // 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/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
github.com/nxadm/tail v1.4.8 // 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/pkg/errors v0.9.1 // indirect
|
||||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
|
github.com/vishvananda/netns v0.0.4 // indirect
|
||||||
go.opencensus.io v0.22.3 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 // indirect
|
golang.org/x/mod v0.13.0 // indirect
|
||||||
golang.org/x/text v0.3.6 // indirect
|
golang.org/x/net v0.17.0 // indirect
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
golang.org/x/text v0.13.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.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
|
||||||
)
|
)
|
||||||
|
@ -14,21 +14,20 @@
|
|||||||
package integration_test
|
package integration_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"github.com/onsi/gomega/gbytes"
|
"github.com/onsi/gomega/gbytes"
|
||||||
"github.com/onsi/gomega/gexec"
|
"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())
|
basicBridgeEnv.runInNS(hostNS, cnitoolBin, "del", "network-chain-test", contNS2.LongName())
|
||||||
})
|
})
|
||||||
|
|
||||||
Measure("limits traffic only on the restricted bandwith veth device", func(b Benchmarker) {
|
Measure("limits traffic only on the restricted bandwidth veth device", func(b Benchmarker) {
|
||||||
ipRegexp := regexp.MustCompile("10\\.1[12]\\.2\\.\\d{1,3}")
|
ipRegexp := regexp.MustCompile(`10\.1[12]\.2\.\d{1,3}`)
|
||||||
|
|
||||||
By(fmt.Sprintf("adding %s to %s\n\n", "chained-bridge-bandwidth", contNS1.ShortName()))
|
By(fmt.Sprintf("adding %s to %s\n\n", "chained-bridge-bandwidth", contNS1.ShortName()))
|
||||||
chainedBridgeBandwidthEnv.runInNS(hostNS, cnitoolBin, "add", "network-chain-test", contNS1.LongName())
|
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."))
|
Expect(basicBridgeIP).To(ContainSubstring("10.11.2."))
|
||||||
|
|
||||||
var chainedBridgeBandwidthPort, basicBridgePort int
|
var chainedBridgeBandwidthPort, basicBridgePort int
|
||||||
var err error
|
|
||||||
|
|
||||||
By(fmt.Sprintf("starting echo server in %s\n\n", contNS1.ShortName()))
|
By(fmt.Sprintf("starting echo server in %s\n\n", contNS1.ShortName()))
|
||||||
chainedBridgeBandwidthPort, chainedBridgeBandwidthSession, err = startEchoServerInNamespace(contNS1)
|
chainedBridgeBandwidthPort, chainedBridgeBandwidthSession = startEchoServerInNamespace(contNS1)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
|
|
||||||
By(fmt.Sprintf("starting echo server in %s\n\n", contNS2.ShortName()))
|
By(fmt.Sprintf("starting echo server in %s\n\n", contNS2.ShortName()))
|
||||||
basicBridgePort, basicBridgeSession, err = startEchoServerInNamespace(contNS2)
|
basicBridgePort, basicBridgeSession = startEchoServerInNamespace(contNS2)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
|
|
||||||
packetInBytes := 20000 // The shaper needs to 'warm'. Send enough to cause it to throttle,
|
packetInBytes := 20000 // The shaper needs to 'warm'. Send enough to cause it to throttle,
|
||||||
// balanced by run time.
|
// 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))
|
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() {
|
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))
|
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() {
|
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))
|
Expect(runtimeWithLimit).To(BeNumerically(">", runtimeWithoutLimit+1000*time.Millisecond))
|
||||||
@ -224,7 +220,7 @@ func (n Namespace) Del() {
|
|||||||
(TestEnv{}).run("ip", "netns", "del", string(n))
|
(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)
|
payload := bytes.Repeat([]byte{'a'}, numBytes)
|
||||||
message := string(payload)
|
message := string(payload)
|
||||||
|
|
||||||
@ -243,7 +239,7 @@ func makeTcpClientInNS(netns string, address string, port int, numBytes int) {
|
|||||||
Expect(string(out)).To(Equal(message))
|
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)
|
session, err := startInNetNS(echoServerBinaryPath, netNS)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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))
|
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) {
|
func startInNetNS(binPath string, namespace Namespace) (*gexec.Session, error) {
|
||||||
|
@ -17,7 +17,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"github.com/onsi/gomega/gexec"
|
"github.com/onsi/gomega/gexec"
|
||||||
)
|
)
|
||||||
|
@ -39,6 +39,7 @@ type EndpointInfo struct {
|
|||||||
NetworkId string
|
NetworkId string
|
||||||
Gateway net.IP
|
Gateway net.IP
|
||||||
IpAddress net.IP
|
IpAddress net.IP
|
||||||
|
MacAddress string
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSandboxContainerID returns the sandbox ID of this pod.
|
// GetSandboxContainerID returns the sandbox ID of this pod.
|
||||||
@ -248,6 +249,7 @@ func GenerateHcnEndpoint(epInfo *EndpointInfo, n *NetConf) (*hcn.HostComputeEndp
|
|||||||
Minor: 0,
|
Minor: 0,
|
||||||
},
|
},
|
||||||
Name: epInfo.EndpointName,
|
Name: epInfo.EndpointName,
|
||||||
|
MacAddress: epInfo.MacAddress,
|
||||||
HostComputeNetwork: epInfo.NetworkId,
|
HostComputeNetwork: epInfo.NetworkId,
|
||||||
Dns: hcn.Dns{
|
Dns: hcn.Dns{
|
||||||
Domain: epInfo.DNS.Domain,
|
Domain: epInfo.DNS.Domain,
|
||||||
@ -280,6 +282,16 @@ func RemoveHcnEndpoint(epName string) error {
|
|||||||
}
|
}
|
||||||
return errors.Annotatef(err, "failed to find HostComputeEndpoint %s", epName)
|
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()
|
err = hcnEndpoint.Delete()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
package hns
|
package hns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -18,7 +18,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/Microsoft/hcsshim/hcn"
|
"github.com/Microsoft/hcsshim/hcn"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,43 +19,87 @@ import (
|
|||||||
"net"
|
"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 {
|
func NextIP(ip net.IP) net.IP {
|
||||||
i := ipToInt(ip)
|
normalizedIP := normalizeIP(ip)
|
||||||
return intToIP(i.Add(i, big.NewInt(1)))
|
if normalizedIP == nil {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrevIP returns IP decremented by 1
|
i := ipToInt(normalizedIP)
|
||||||
|
return intToIP(i.Add(i, big.NewInt(1)), len(normalizedIP) == net.IPv6len)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrevIP returns IP decremented by 1, if IP is invalid, return nil
|
||||||
func PrevIP(ip net.IP) net.IP {
|
func PrevIP(ip net.IP) net.IP {
|
||||||
i := ipToInt(ip)
|
normalizedIP := normalizeIP(ip)
|
||||||
return intToIP(i.Sub(i, big.NewInt(1)))
|
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:
|
// Cmp compares two IPs, returning the usual ordering:
|
||||||
// a < b : -1
|
// a < b : -1
|
||||||
// a == b : 0
|
// a == b : 0
|
||||||
// a > b : 1
|
// a > b : 1
|
||||||
|
// incomparable : -2
|
||||||
func Cmp(a, b net.IP) int {
|
func Cmp(a, b net.IP) int {
|
||||||
aa := ipToInt(a)
|
normalizedA := normalizeIP(a)
|
||||||
bb := ipToInt(b)
|
normalizedB := normalizeIP(b)
|
||||||
return aa.Cmp(bb)
|
|
||||||
|
if len(normalizedA) == len(normalizedB) && len(normalizedA) != 0 {
|
||||||
|
return ipToInt(normalizedA).Cmp(ipToInt(normalizedB))
|
||||||
|
}
|
||||||
|
|
||||||
|
return -2
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipToInt(ip net.IP) *big.Int {
|
func ipToInt(ip net.IP) *big.Int {
|
||||||
if v := ip.To4(); v != nil {
|
return big.NewInt(0).SetBytes(ip)
|
||||||
return big.NewInt(0).SetBytes(v)
|
|
||||||
}
|
|
||||||
return big.NewInt(0).SetBytes(ip.To16())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func intToIP(i *big.Int) net.IP {
|
func intToIP(i *big.Int, isIPv6 bool) net.IP {
|
||||||
return net.IP(i.Bytes())
|
intBytes := i.Bytes()
|
||||||
|
|
||||||
|
if len(intBytes) == net.IPv4len || len(intBytes) == net.IPv6len {
|
||||||
|
return intBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Network masks off the host portion of the IP
|
if isIPv6 {
|
||||||
|
return append(make([]byte, net.IPv6len-len(intBytes)), intBytes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(make([]byte, net.IPv4len-len(intBytes)), intBytes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, if IPNet is invalid,
|
||||||
|
// return nil
|
||||||
func Network(ipn *net.IPNet) *net.IPNet {
|
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{
|
return &net.IPNet{
|
||||||
IP: ipn.IP.Mask(ipn.Mask),
|
IP: maskedIP,
|
||||||
Mask: ipn.Mask,
|
Mask: ipn.Mask,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
247
pkg/ip/cidr_test.go
Normal file
247
pkg/ip/cidr_test.go
Normal 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))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
@ -47,14 +47,13 @@ func ParseIP(s string) *IP {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return newIP(ip, ipNet.Mask)
|
return newIP(ip, ipNet.Mask)
|
||||||
} else {
|
}
|
||||||
ip := net.ParseIP(s)
|
ip := net.ParseIP(s)
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return newIP(ip, nil)
|
return newIP(ip, nil)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// ToIP will return a net.IP in standard form from this IP.
|
// ToIP will return a net.IP in standard form from this IP.
|
||||||
// If this IP can not be converted to a valid net.IP, will return nil.
|
// If this IP can not be converted to a valid net.IP, will return nil.
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
package ip_test
|
package ip_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIp(t *testing.T) {
|
func TestIp(t *testing.T) {
|
||||||
|
@ -19,7 +19,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ var _ = Describe("IP Operations", func() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
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))
|
Expect(test.ip.ToIP()).To(Equal(test.expectedIP))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -174,8 +174,8 @@ var _ = Describe("IP Operations", func() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
It("Decode", func() {
|
Context("Decode", func() {
|
||||||
Context("valid IP", func() {
|
It("valid IP", func() {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
text string
|
text string
|
||||||
expected *IP
|
expected *IP
|
||||||
@ -205,10 +205,9 @@ var _ = Describe("IP Operations", func() {
|
|||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(ip).To(Equal(test.expected))
|
Expect(ip).To(Equal(test.expected))
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("empty text", func() {
|
It("empty text", func() {
|
||||||
ip := &IP{}
|
ip := &IP{}
|
||||||
err := json.Unmarshal([]byte(`""`), ip)
|
err := json.Unmarshal([]byte(`""`), ip)
|
||||||
|
|
||||||
@ -216,7 +215,7 @@ var _ = Describe("IP Operations", func() {
|
|||||||
Expect(ip).To(Equal(newIP(nil, nil)))
|
Expect(ip).To(Equal(newIP(nil, nil)))
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("invalid IP", func() {
|
It("invalid IP", func() {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
text string
|
text string
|
||||||
expectedErr error
|
expectedErr error
|
||||||
@ -243,7 +242,7 @@ var _ = Describe("IP Operations", func() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("IP slice", func() {
|
It("IP slice", func() {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
text string
|
text string
|
||||||
expected []*IP
|
expected []*IP
|
||||||
|
@ -16,7 +16,7 @@ package ip
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io/ioutil"
|
"os"
|
||||||
|
|
||||||
current "github.com/containernetworking/cni/pkg/types/100"
|
current "github.com/containernetworking/cni/pkg/types/100"
|
||||||
)
|
)
|
||||||
@ -53,10 +53,10 @@ func EnableForward(ips []*current.IPConfig) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func echo1(f string) 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")) {
|
if bytes.Equal(bytes.TrimSpace(content), []byte("1")) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ioutil.WriteFile(f, []byte("1"), 0644)
|
return os.WriteFile(f, []byte("1"), 0o644)
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
package ip
|
package ip
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("IpforwardLinux", func() {
|
var _ = Describe("IpforwardLinux", func() {
|
||||||
It("echo1 must not write the file if content is 1", 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())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
defer os.Remove(file.Name())
|
defer os.Remove(file.Name())
|
||||||
err = echo1(file.Name())
|
err = echo1(file.Name())
|
||||||
|
@ -104,7 +104,6 @@ func TeardownIPMasq(ipn *net.IPNet, chain string, comment string) error {
|
|||||||
err = ipt.ClearChain("nat", chain)
|
err = ipt.ClearChain("nat", chain)
|
||||||
if err != nil && !isNotExist(err) {
|
if err != nil && !isNotExist(err) {
|
||||||
return err
|
return err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ipt.DeleteChain("nat", chain)
|
err = ipt.DeleteChain("nat", chain)
|
||||||
|
@ -28,9 +28,7 @@ import (
|
|||||||
"github.com/containernetworking/plugins/pkg/utils/sysctl"
|
"github.com/containernetworking/plugins/pkg/utils/sysctl"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var ErrLinkNotFound = errors.New("link not found")
|
||||||
ErrLinkNotFound = errors.New("link not found")
|
|
||||||
)
|
|
||||||
|
|
||||||
// makeVethPair is called from within the container's network namespace
|
// 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) {
|
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
|
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++ {
|
for i := 0; i < 10; i++ {
|
||||||
if vethPeerName != "" {
|
if vethPeerName != "" {
|
||||||
peerName = vethPeerName
|
peerName = vethPeerName
|
||||||
} else {
|
} else {
|
||||||
peerName, err = RandomVethName()
|
peerName, err = RandomVethName()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return peerName, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
veth, err = makeVethPair(name, peerName, mtu, mac, hostNS)
|
veth, err = makeVethPair(name, peerName, mtu, mac, hostNS)
|
||||||
switch {
|
switch {
|
||||||
case err == nil:
|
case err == nil:
|
||||||
return
|
return peerName, veth, nil
|
||||||
|
|
||||||
case os.IsExist(err):
|
case os.IsExist(err):
|
||||||
if peerExists(peerName) && vethPeerName == "" {
|
if peerExists(peerName) && vethPeerName == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
err = fmt.Errorf("container veth name provided (%v) already exists", name)
|
return peerName, veth, fmt.Errorf("container veth name provided (%v) already exists", name)
|
||||||
return
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("failed to make veth pair: %v", err)
|
return peerName, veth, fmt.Errorf("failed to make veth pair: %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// should really never be hit
|
// should really never be hit
|
||||||
err = fmt.Errorf("failed to find a unique veth name")
|
return peerName, nil, fmt.Errorf("failed to find a unique veth name")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RandomVethName returns string "veth" with random prefix (hashed from entropy)
|
// RandomVethName returns string "veth" with random prefix (hashed from entropy)
|
||||||
|
@ -20,22 +20,15 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
"github.com/containernetworking/plugins/pkg/ip"
|
"github.com/containernetworking/plugins/pkg/ip"
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
"github.com/containernetworking/plugins/pkg/testutils"
|
"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() {
|
var _ = Describe("Link", func() {
|
||||||
const (
|
const (
|
||||||
ifaceFormatString string = "i%d"
|
ifaceFormatString string = "i%d"
|
||||||
@ -290,7 +283,7 @@ var _ = Describe("Link", func() {
|
|||||||
// this will delete the host endpoint too
|
// this will delete the host endpoint too
|
||||||
addr, err := ip.DelLinkByNameAddr(containerVethName)
|
addr, err := ip.DelLinkByNameAddr(containerVethName)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(addr).To(HaveLen(0))
|
Expect(addr).To(BeEmpty())
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -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.
|
// AddDefaultRoute sets the default route on the given gateway.
|
||||||
func AddDefaultRoute(gw net.IP, dev netlink.Link) error {
|
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)
|
return AddRoute(defNet, gw, dev)
|
||||||
}
|
}
|
||||||
|
@ -21,13 +21,13 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
current "github.com/containernetworking/cni/pkg/types/100"
|
current "github.com/containernetworking/cni/pkg/types/100"
|
||||||
"github.com/vishvananda/netlink"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ValidateExpectedInterfaceIPs(ifName string, resultIPs []*current.IPConfig) error {
|
func ValidateExpectedInterfaceIPs(ifName string, resultIPs []*current.IPConfig) error {
|
||||||
|
|
||||||
// Ensure ips
|
// Ensure ips
|
||||||
for _, ips := range resultIPs {
|
for _, ips := range resultIPs {
|
||||||
ourAddr := netlink.Addr{IPNet: &ips.Address}
|
ourAddr := netlink.Addr{IPNet: &ips.Address}
|
||||||
@ -49,12 +49,15 @@ func ValidateExpectedInterfaceIPs(ifName string, resultIPs []*current.IPConfig)
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if match == false {
|
if !match {
|
||||||
return fmt.Errorf("Failed to match addr %v on interface %v", ourAddr, ifName)
|
return fmt.Errorf("Failed to match addr %v on interface %v", ourAddr, ifName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the host/prefixlen to just prefix for route lookup.
|
// Convert the host/prefixlen to just prefix for route lookup.
|
||||||
_, ourPrefix, err := net.ParseCIDR(ourAddr.String())
|
_, ourPrefix, err := net.ParseCIDR(ourAddr.String())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
findGwy := &netlink.Route{Dst: ourPrefix}
|
findGwy := &netlink.Route{Dst: ourPrefix}
|
||||||
routeFilter := netlink.RT_FILTER_DST
|
routeFilter := netlink.RT_FILTER_DST
|
||||||
@ -77,11 +80,13 @@ func ValidateExpectedInterfaceIPs(ifName string, resultIPs []*current.IPConfig)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ValidateExpectedRoute(resultRoutes []*types.Route) error {
|
func ValidateExpectedRoute(resultRoutes []*types.Route) error {
|
||||||
|
|
||||||
// Ensure that each static route in prevResults is found in the routing table
|
// Ensure that each static route in prevResults is found in the routing table
|
||||||
for _, route := range resultRoutes {
|
for _, route := range resultRoutes {
|
||||||
find := &netlink.Route{Dst: &route.Dst, Gw: route.GW}
|
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
|
var family int
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
@ -16,6 +16,7 @@ package ipam
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/invoke"
|
"github.com/containernetworking/cni/pkg/invoke"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
)
|
)
|
||||||
|
@ -19,11 +19,11 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
current "github.com/containernetworking/cni/pkg/types/100"
|
current "github.com/containernetworking/cni/pkg/types/100"
|
||||||
"github.com/containernetworking/plugins/pkg/ip"
|
"github.com/containernetworking/plugins/pkg/ip"
|
||||||
"github.com/containernetworking/plugins/pkg/utils/sysctl"
|
"github.com/containernetworking/plugins/pkg/utils/sysctl"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -44,7 +44,7 @@ func ConfigureIface(ifName string, res *current.Result) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var v4gw, v6gw net.IP
|
var v4gw, v6gw net.IP
|
||||||
var has_enabled_ipv6 bool = false
|
hasEnabledIpv6 := false
|
||||||
for _, ipc := range res.IPs {
|
for _, ipc := range res.IPs {
|
||||||
if ipc.Interface == nil {
|
if ipc.Interface == nil {
|
||||||
continue
|
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
|
// Make sure sysctl "disable_ipv6" is 0 if we are about to add
|
||||||
// an IPv6 address to the interface
|
// 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
|
// Enabled IPv6 for loopback "lo" and the interface
|
||||||
// being configured
|
// being configured
|
||||||
for _, iface := range [2]string{"lo", ifName} {
|
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)
|
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: ""}
|
addr := &netlink.Addr{IPNet: &ipc.Address, Label: ""}
|
||||||
|
@ -18,15 +18,14 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
current "github.com/containernetworking/cni/pkg/types/100"
|
current "github.com/containernetworking/cni/pkg/types/100"
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
"github.com/containernetworking/plugins/pkg/testutils"
|
"github.com/containernetworking/plugins/pkg/testutils"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const LINK_NAME = "eth0"
|
const LINK_NAME = "eth0"
|
||||||
@ -143,12 +142,12 @@ var _ = Describe("ConfigureIface", func() {
|
|||||||
|
|
||||||
v4addrs, err := netlink.AddrList(link, syscall.AF_INET)
|
v4addrs, err := netlink.AddrList(link, syscall.AF_INET)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(len(v4addrs)).To(Equal(1))
|
Expect(v4addrs).To(HaveLen(1))
|
||||||
Expect(ipNetEqual(v4addrs[0].IPNet, ipv4)).To(Equal(true))
|
Expect(ipNetEqual(v4addrs[0].IPNet, ipv4)).To(BeTrue())
|
||||||
|
|
||||||
v6addrs, err := netlink.AddrList(link, syscall.AF_INET6)
|
v6addrs, err := netlink.AddrList(link, syscall.AF_INET6)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(len(v6addrs)).To(Equal(2))
|
Expect(v6addrs).To(HaveLen(2))
|
||||||
|
|
||||||
var found bool
|
var found bool
|
||||||
for _, a := range v6addrs {
|
for _, a := range v6addrs {
|
||||||
@ -157,7 +156,7 @@ var _ = Describe("ConfigureIface", func() {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expect(found).To(Equal(true))
|
Expect(found).To(BeTrue())
|
||||||
|
|
||||||
// Ensure the v4 route, v6 route, and subnet route
|
// Ensure the v4 route, v6 route, and subnet route
|
||||||
routes, err := netlink.RouteList(link, 0)
|
routes, err := netlink.RouteList(link, 0)
|
||||||
@ -177,8 +176,8 @@ var _ = Describe("ConfigureIface", func() {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expect(v4found).To(Equal(true))
|
Expect(v4found).To(BeTrue())
|
||||||
Expect(v6found).To(Equal(true))
|
Expect(v6found).To(BeTrue())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@ -216,8 +215,8 @@ var _ = Describe("ConfigureIface", func() {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expect(v4found).To(Equal(true))
|
Expect(v4found).To(BeTrue())
|
||||||
Expect(v6found).To(Equal(true))
|
Expect(v6found).To(BeTrue())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
package ipam_test
|
package ipam_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIpam(t *testing.T) {
|
func TestIpam(t *testing.T) {
|
||||||
|
@ -17,7 +17,7 @@ package link_test
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,8 +15,10 @@
|
|||||||
package link
|
package link
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/networkplumbing/go-nft/nft"
|
"github.com/networkplumbing/go-nft/nft"
|
||||||
"github.com/networkplumbing/go-nft/nft/schema"
|
"github.com/networkplumbing/go-nft/nft/schema"
|
||||||
@ -28,8 +30,8 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type NftConfigurer interface {
|
type NftConfigurer interface {
|
||||||
Apply(*nft.Config) error
|
Apply(*nft.Config) (*nft.Config, error)
|
||||||
Read() (*nft.Config, error)
|
Read(filterCommands ...string) (*nft.Config, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SpoofChecker struct {
|
type SpoofChecker struct {
|
||||||
@ -37,16 +39,23 @@ type SpoofChecker struct {
|
|||||||
macAddress string
|
macAddress string
|
||||||
refID string
|
refID string
|
||||||
configurer NftConfigurer
|
configurer NftConfigurer
|
||||||
|
rulestore *nft.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
type defaultNftConfigurer struct{}
|
type defaultNftConfigurer struct{}
|
||||||
|
|
||||||
func (_ defaultNftConfigurer) Apply(cfg *nft.Config) error {
|
func (dnc defaultNftConfigurer) Apply(cfg *nft.Config) (*nft.Config, error) {
|
||||||
return nft.ApplyConfig(cfg)
|
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) {
|
func (dnc defaultNftConfigurer) Read(filterCommands ...string) (*nft.Config, error) {
|
||||||
return nft.ReadConfig()
|
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 {
|
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 {
|
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
|
// Setup applies nftables configuration to restrict traffic
|
||||||
@ -83,7 +92,7 @@ func (sc *SpoofChecker) Setup() error {
|
|||||||
macChain := sc.macChain(ifaceChain.Name)
|
macChain := sc.macChain(ifaceChain.Name)
|
||||||
baseConfig.AddChain(macChain)
|
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)
|
return fmt.Errorf("failed to setup spoof-check: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,45 +106,59 @@ func (sc *SpoofChecker) Setup() error {
|
|||||||
rulesConfig.AddRule(sc.matchMacRule(macChain.Name))
|
rulesConfig.AddRule(sc.matchMacRule(macChain.Name))
|
||||||
rulesConfig.AddRule(sc.dropRule(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)
|
return fmt.Errorf("failed to setup spoof-check: %v", err)
|
||||||
}
|
}
|
||||||
|
sc.rulestore = rulestore
|
||||||
|
|
||||||
return nil
|
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.
|
// 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
|
// The table and base-chain are expected to survive while the base-chain rule that matches the
|
||||||
// interface is removed.
|
// interface is removed.
|
||||||
func (sc *SpoofChecker) Teardown() error {
|
func (sc *SpoofChecker) Teardown() error {
|
||||||
ifaceChain := sc.ifaceChain()
|
ifaceChain := sc.ifaceChain()
|
||||||
currentConfig, ifaceMatchRuleErr := sc.configurer.Read()
|
|
||||||
if ifaceMatchRuleErr == nil {
|
|
||||||
expectedRuleToFind := sc.matchIfaceJumpToChainRule(preRoutingBaseChainName, ifaceChain.Name)
|
expectedRuleToFind := sc.matchIfaceJumpToChainRule(preRoutingBaseChainName, ifaceChain.Name)
|
||||||
// It is safer to exclude the statement matching, avoiding cases where a current statement includes
|
// It is safer to exclude the statement matching, avoiding cases where a current statement includes
|
||||||
// additional default entries (e.g. counters).
|
// additional default entries (e.g. counters).
|
||||||
ruleToFindExcludingStatements := *expectedRuleToFind
|
ruleToFindExcludingStatements := *expectedRuleToFind
|
||||||
ruleToFindExcludingStatements.Expr = nil
|
ruleToFindExcludingStatements.Expr = nil
|
||||||
rules := currentConfig.LookupRule(&ruleToFindExcludingStatements)
|
|
||||||
if len(rules) > 0 {
|
rules, ifaceMatchRuleErr := sc.findPreroutingRule(&ruleToFindExcludingStatements)
|
||||||
|
if ifaceMatchRuleErr == nil && len(rules) > 0 {
|
||||||
c := nft.NewConfig()
|
c := nft.NewConfig()
|
||||||
for _, rule := range rules {
|
for _, rule := range rules {
|
||||||
c.DeleteRule(rule)
|
c.DeleteRule(rule)
|
||||||
}
|
}
|
||||||
if err := sc.configurer.Apply(c); err != nil {
|
if _, err := sc.configurer.Apply(c); err != nil {
|
||||||
ifaceMatchRuleErr = fmt.Errorf("failed to delete iface match rule: %v", err)
|
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 {
|
} else {
|
||||||
fmt.Fprintf(os.Stderr, "spoofcheck/teardown: unable to detect iface match rule for deletion: %+v", expectedRuleToFind)
|
fmt.Fprintf(os.Stderr, "spoofcheck/teardown: unable to detect iface match rule for deletion: %+v", expectedRuleToFind)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
regularChainsConfig := nft.NewConfig()
|
regularChainsConfig := nft.NewConfig()
|
||||||
regularChainsConfig.DeleteChain(ifaceChain)
|
regularChainsConfig.DeleteChain(ifaceChain)
|
||||||
regularChainsConfig.DeleteChain(sc.macChain(ifaceChain.Name))
|
regularChainsConfig.DeleteChain(sc.macChain(ifaceChain.Name))
|
||||||
|
|
||||||
var regularChainsErr error
|
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)
|
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 {
|
func (sc *SpoofChecker) dropRule(chain string) *schema.Rule {
|
||||||
macRulesIndex := nft.NewRuleIndex()
|
|
||||||
return &schema.Rule{
|
return &schema.Rule{
|
||||||
Family: schema.FamilyBridge,
|
Family: schema.FamilyBridge,
|
||||||
Table: natTableName,
|
Table: natTableName,
|
||||||
Chain: chain,
|
Chain: chain,
|
||||||
Index: macRulesIndex.Next(),
|
|
||||||
Expr: []schema.Statement{
|
Expr: []schema.Statement{
|
||||||
{Verdict: schema.Verdict{SimpleVerdict: schema.SimpleVerdict{Drop: true}}},
|
{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
|
chainPriority := -300
|
||||||
return &schema.Chain{
|
return &schema.Chain{
|
||||||
Family: schema.FamilyBridge,
|
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"
|
macChainName := ifaceChainName + "-mac"
|
||||||
return &schema.Chain{
|
return &schema.Chain{
|
||||||
Family: schema.FamilyBridge,
|
Family: schema.FamilyBridge,
|
||||||
@ -243,3 +264,7 @@ func ruleComment(id string) string {
|
|||||||
const refIDPrefix = "macspoofchk-"
|
const refIDPrefix = "macspoofchk-"
|
||||||
return refIDPrefix + id
|
return refIDPrefix + id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func listChainBridgeNatPrerouting() []string {
|
||||||
|
return []string{"chain", "bridge", natTableName, preRoutingBaseChainName}
|
||||||
|
}
|
||||||
|
@ -16,9 +16,9 @@ package link_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"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/onsi/gomega"
|
||||||
|
|
||||||
"github.com/containernetworking/plugins/pkg/link"
|
"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) {
|
func assertExpectedRegularChainsDeletionInTeardownConfig(action configurerStub) {
|
||||||
deleteRegularChainRulesJsonConfig, err := action.applyConfig[1].ToJSON()
|
deleteRegularChainRulesJSONConfig, err := action.applyConfig[1].ToJSON()
|
||||||
ExpectWithOffset(1, err).NotTo(HaveOccurred())
|
ExpectWithOffset(1, err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
expectedDeleteRegularChainRulesJsonConfig := `
|
expectedDeleteRegularChainRulesJSONConfig := `
|
||||||
{"nftables": [
|
{"nftables": [
|
||||||
{"delete": {"chain": {
|
{"delete": {"chain": {
|
||||||
"family": "bridge",
|
"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) {
|
func assertExpectedBaseChainRuleDeletionInTeardownConfig(action configurerStub) {
|
||||||
deleteBaseChainRuleJsonConfig, err := action.applyConfig[0].ToJSON()
|
deleteBaseChainRuleJSONConfig, err := action.applyConfig[0].ToJSON()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
expectedDeleteIfaceMatchRuleJsonConfig := `
|
expectedDeleteIfaceMatchRuleJSONConfig := `
|
||||||
{"nftables": [
|
{"nftables": [
|
||||||
{"delete": {"rule": {
|
{"delete": {"rule": {
|
||||||
"family": "bridge",
|
"family": "bridge",
|
||||||
@ -157,7 +176,7 @@ func assertExpectedBaseChainRuleDeletionInTeardownConfig(action configurerStub)
|
|||||||
"comment": "macspoofchk-container99-net1"
|
"comment": "macspoofchk-container99-net1"
|
||||||
}}}
|
}}}
|
||||||
]}`
|
]}`
|
||||||
Expect(string(deleteBaseChainRuleJsonConfig)).To(MatchJSON(expectedDeleteIfaceMatchRuleJsonConfig))
|
Expect(string(deleteBaseChainRuleJSONConfig)).To(MatchJSON(expectedDeleteIfaceMatchRuleJSONConfig))
|
||||||
}
|
}
|
||||||
|
|
||||||
func rowConfigWithRulesOnly() string {
|
func rowConfigWithRulesOnly() string {
|
||||||
@ -254,7 +273,6 @@ func assertExpectedRulesInSetupConfig(c configurerStub) {
|
|||||||
"comment":"macspoofchk-container99-net1"}},
|
"comment":"macspoofchk-container99-net1"}},
|
||||||
{"rule":{"family":"bridge","table":"nat","chain":"cni-br-iface-container99-net1-mac",
|
{"rule":{"family":"bridge","table":"nat","chain":"cni-br-iface-container99-net1-mac",
|
||||||
"expr":[{"drop":null}],
|
"expr":[{"drop":null}],
|
||||||
"index":0,
|
|
||||||
"comment":"macspoofchk-container99-net1"}}
|
"comment":"macspoofchk-container99-net1"}}
|
||||||
]}`
|
]}`
|
||||||
ExpectWithOffset(1, string(jsonConfig)).To(MatchJSON(expectedConfig))
|
ExpectWithOffset(1, string(jsonConfig)).To(MatchJSON(expectedConfig))
|
||||||
@ -275,21 +293,28 @@ type configurerStub struct {
|
|||||||
failFirstApplyConfig bool
|
failFirstApplyConfig bool
|
||||||
failSecondApplyConfig bool
|
failSecondApplyConfig bool
|
||||||
failReadConfig 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++
|
a.applyCounter++
|
||||||
if a.failFirstApplyConfig && a.applyCounter == 1 {
|
if a.failFirstApplyConfig && a.applyCounter == 1 {
|
||||||
return fmt.Errorf(errorFirstApplyText)
|
return nil, fmt.Errorf(errorFirstApplyText)
|
||||||
}
|
}
|
||||||
if a.failSecondApplyConfig && a.applyCounter == 2 {
|
if a.failSecondApplyConfig && a.applyCounter == 2 {
|
||||||
return fmt.Errorf(errorSecondApplyText)
|
return nil, fmt.Errorf(errorSecondApplyText)
|
||||||
}
|
}
|
||||||
a.applyConfig = append(a.applyConfig, c)
|
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 {
|
if a.failReadConfig {
|
||||||
return nil, fmt.Errorf(errorReadText)
|
return nil, fmt.Errorf(errorReadText)
|
||||||
}
|
}
|
||||||
|
@ -17,16 +17,16 @@ package ns_test
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
"github.com/containernetworking/plugins/pkg/testutils"
|
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
|
"github.com/containernetworking/plugins/pkg/testutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getInodeCurNetNS() (uint64, error) {
|
func getInodeCurNetNS() (uint64, error) {
|
||||||
@ -182,7 +182,7 @@ var _ = Describe("Linux namespace operations", func() {
|
|||||||
testNsInode, err := getInodeNS(targetNetNS)
|
testNsInode, err := getInodeNS(targetNetNS)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
Expect(testNsInode).NotTo(Equal(0))
|
Expect(testNsInode).NotTo(Equal(uint64(0)))
|
||||||
Expect(testNsInode).NotTo(Equal(origNSInode))
|
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() {
|
It("fails when the path is not a namespace", func() {
|
||||||
tempFile, err := ioutil.TempFile("", "nstest")
|
tempFile, err := os.CreateTemp("", "nstest")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
defer tempFile.Close()
|
defer tempFile.Close()
|
||||||
|
|
||||||
@ -262,7 +262,7 @@ var _ = Describe("Linux namespace operations", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("should refuse other paths", func() {
|
It("should refuse other paths", func() {
|
||||||
tempFile, err := ioutil.TempFile("", "nstest")
|
tempFile, err := os.CreateTemp("", "nstest")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
defer tempFile.Close()
|
defer tempFile.Close()
|
||||||
|
|
||||||
|
@ -15,18 +15,14 @@
|
|||||||
package ns_test
|
package ns_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
"github.com/onsi/ginkgo/config"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNs(t *testing.T) {
|
func TestNs(t *testing.T) {
|
||||||
rand.Seed(config.GinkgoConfig.RandomSeed)
|
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
|
|
||||||
RegisterFailHandler(Fail)
|
RegisterFailHandler(Fail)
|
||||||
|
@ -21,7 +21,7 @@ type BadReader struct {
|
|||||||
Error error
|
Error error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *BadReader) Read(buffer []byte) (int, error) {
|
func (r *BadReader) Read(_ []byte) (int, error) {
|
||||||
if r.Error != nil {
|
if r.Error != nil {
|
||||||
return 0, r.Error
|
return 0, r.Error
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
package testutils
|
package testutils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
@ -29,6 +29,7 @@ func envCleanup() {
|
|||||||
os.Unsetenv("CNI_NETNS")
|
os.Unsetenv("CNI_NETNS")
|
||||||
os.Unsetenv("CNI_IFNAME")
|
os.Unsetenv("CNI_IFNAME")
|
||||||
os.Unsetenv("CNI_CONTAINERID")
|
os.Unsetenv("CNI_CONTAINERID")
|
||||||
|
os.Unsetenv("CNI_NETNS_OVERRIDE")
|
||||||
}
|
}
|
||||||
|
|
||||||
func CmdAdd(cniNetns, cniContainerID, cniIfname string, conf []byte, f func() error) (types.Result, []byte, error) {
|
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_NETNS", cniNetns)
|
||||||
os.Setenv("CNI_IFNAME", cniIfname)
|
os.Setenv("CNI_IFNAME", cniIfname)
|
||||||
os.Setenv("CNI_CONTAINERID", cniContainerID)
|
os.Setenv("CNI_CONTAINERID", cniContainerID)
|
||||||
|
os.Setenv("CNI_NETNS_OVERRIDE", "1")
|
||||||
defer envCleanup()
|
defer envCleanup()
|
||||||
|
|
||||||
// Redirect stdout to capture plugin result
|
// Redirect stdout to capture plugin result
|
||||||
@ -52,7 +54,7 @@ func CmdAdd(cniNetns, cniContainerID, cniIfname string, conf []byte, f func() er
|
|||||||
|
|
||||||
var out []byte
|
var out []byte
|
||||||
if err == nil {
|
if err == nil {
|
||||||
out, err = ioutil.ReadAll(r)
|
out, err = io.ReadAll(r)
|
||||||
}
|
}
|
||||||
os.Stdout = oldStdout
|
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)
|
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_COMMAND", "CHECK")
|
||||||
os.Setenv("CNI_PATH", os.Getenv("PATH"))
|
os.Setenv("CNI_PATH", os.Getenv("PATH"))
|
||||||
os.Setenv("CNI_NETNS", cniNetns)
|
os.Setenv("CNI_NETNS", cniNetns)
|
||||||
os.Setenv("CNI_IFNAME", cniIfname)
|
os.Setenv("CNI_IFNAME", cniIfname)
|
||||||
os.Setenv("CNI_CONTAINERID", cniContainerID)
|
os.Setenv("CNI_CONTAINERID", cniContainerID)
|
||||||
|
os.Setenv("CNI_NETNS_OVERRIDE", "1")
|
||||||
defer envCleanup()
|
defer envCleanup()
|
||||||
|
|
||||||
return f()
|
return f()
|
||||||
}
|
}
|
||||||
|
|
||||||
func CmdCheckWithArgs(args *skel.CmdArgs, f func() error) error {
|
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 {
|
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_NETNS", cniNetns)
|
||||||
os.Setenv("CNI_IFNAME", cniIfname)
|
os.Setenv("CNI_IFNAME", cniIfname)
|
||||||
os.Setenv("CNI_CONTAINERID", cniContainerID)
|
os.Setenv("CNI_CONTAINERID", cniContainerID)
|
||||||
|
os.Setenv("CNI_NETNS_OVERRIDE", "1")
|
||||||
defer envCleanup()
|
defer envCleanup()
|
||||||
|
|
||||||
return f()
|
return f()
|
||||||
|
@ -16,7 +16,6 @@ package testutils
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -28,7 +27,7 @@ import (
|
|||||||
// an error if any occurs while creating/writing the file. It is the caller's
|
// an error if any occurs while creating/writing the file. It is the caller's
|
||||||
// responsibility to remove the file.
|
// responsibility to remove the file.
|
||||||
func TmpResolvConf(dnsConf types.DNS) (string, error) {
|
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 {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to get temp file for CNI test resolv.conf: %v", err)
|
return "", fmt.Errorf("failed to get temp file for CNI test resolv.conf: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,12 @@ package main_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"github.com/onsi/gomega/gbytes"
|
"github.com/onsi/gomega/gbytes"
|
||||||
"github.com/onsi/gomega/gexec"
|
"github.com/onsi/gomega/gexec"
|
||||||
@ -74,7 +74,7 @@ var _ = Describe("Echosvr", func() {
|
|||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
fmt.Fprintf(conn, "hello\n")
|
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() {
|
It("connects successfully using echo client", func() {
|
||||||
Eventually(session.Out).Should(gbytes.Say("\n"))
|
Eventually(session.Out).Should(gbytes.Say("\n"))
|
||||||
serverAddress := strings.TrimSpace(string(session.Out.Contents()))
|
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")
|
cmd := exec.Command(clientBinaryPath, "-target", serverAddress, "-message", "hello")
|
||||||
clientSession, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
|
clientSession, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package main_test
|
package main_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEchosvr(t *testing.T) {
|
func TestEchosvr(t *testing.T) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Echosvr is a simple TCP echo server
|
// Echosvr is a simple TCP echo server
|
||||||
//
|
//
|
||||||
// It prints its listen address on stdout
|
// It prints its listen address on stdout
|
||||||
|
//
|
||||||
// 127.0.0.1:xxxxx
|
// 127.0.0.1:xxxxx
|
||||||
// A test should wait for this line, parse it
|
// A test should wait for this line, parse it
|
||||||
// and may then attempt to connect.
|
// and may then attempt to connect.
|
||||||
@ -43,11 +44,13 @@ func main() {
|
|||||||
// Start UDP server
|
// Start UDP server
|
||||||
addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%s", port))
|
addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%s", port))
|
||||||
if err != nil {
|
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)
|
sock, err := net.ListenUDP("udp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error from ListenUDP(): %s", err)
|
log.Printf("Error from ListenUDP(): %s", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
defer sock.Close()
|
defer sock.Close()
|
||||||
|
|
||||||
@ -55,10 +58,11 @@ func main() {
|
|||||||
for {
|
for {
|
||||||
n, addr, err := sock.ReadFrom(buffer)
|
n, addr, err := sock.ReadFrom(buffer)
|
||||||
if err != nil {
|
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))
|
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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,9 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getNsRunDir() string {
|
func getNsRunDir() string {
|
||||||
@ -49,7 +50,6 @@ func getNsRunDir() string {
|
|||||||
// Creates a new persistent (bind-mounted) network namespace and returns an object
|
// Creates a new persistent (bind-mounted) network namespace and returns an object
|
||||||
// representing that namespace, without switching to it.
|
// representing that namespace, without switching to it.
|
||||||
func NewNS() (ns.NetNS, error) {
|
func NewNS() (ns.NetNS, error) {
|
||||||
|
|
||||||
nsRunDir := getNsRunDir()
|
nsRunDir := getNsRunDir()
|
||||||
|
|
||||||
b := make([]byte, 16)
|
b := make([]byte, 16)
|
||||||
@ -61,7 +61,7 @@ func NewNS() (ns.NetNS, error) {
|
|||||||
// Create the directory for mounting network namespaces
|
// Create the directory for mounting network namespaces
|
||||||
// This needs to be a shared mountpoint in case it is mounted in to
|
// This needs to be a shared mountpoint in case it is mounted in to
|
||||||
// other namespaces (containers)
|
// other namespaces (containers)
|
||||||
err = os.MkdirAll(nsRunDir, 0755)
|
err = os.MkdirAll(nsRunDir, 0o755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -29,9 +29,9 @@ func EnsureChain(ipt *iptables.IPTables, table, chain string) error {
|
|||||||
if ipt == nil {
|
if ipt == nil {
|
||||||
return errors.New("failed to ensure iptable chain: IPTables was 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 {
|
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 {
|
if !exists {
|
||||||
err = ipt.NewChain(table, chain)
|
err = ipt.NewChain(table, chain)
|
||||||
@ -45,24 +45,6 @@ func EnsureChain(ipt *iptables.IPTables, table, chain string) error {
|
|||||||
return nil
|
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.
|
// DeleteRule idempotently delete the iptables rule in the specified table/chain.
|
||||||
// It does not return an error if the referring chain doesn't exist
|
// It does not return an error if the referring chain doesn't exist
|
||||||
func DeleteRule(ipt *iptables.IPTables, table, chain string, rulespec ...string) error {
|
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 {
|
if prepend {
|
||||||
return ipt.Insert(table, chain, 1, rule...)
|
return ipt.Insert(table, chain, 1, rule...)
|
||||||
} else {
|
}
|
||||||
return ipt.Append(table, chain, rule...)
|
return ipt.Append(table, chain, rule...)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -19,11 +19,12 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"runtime"
|
"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/ns"
|
||||||
"github.com/containernetworking/plugins/pkg/testutils"
|
"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
|
const TABLE = "filter" // We'll monkey around here
|
||||||
@ -34,7 +35,6 @@ var _ = Describe("chain tests", func() {
|
|||||||
var cleanup func()
|
var cleanup func()
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
|
|
||||||
// Save a reference to the original namespace,
|
// Save a reference to the original namespace,
|
||||||
// Add a new NS
|
// Add a new NS
|
||||||
currNs, err := ns.GetCurrentNS()
|
currNs, err := ns.GetCurrentNS()
|
||||||
@ -60,7 +60,6 @@ var _ = Describe("chain tests", func() {
|
|||||||
ipt.DeleteChain(TABLE, testChain)
|
ipt.DeleteChain(TABLE, testChain)
|
||||||
currNs.Set()
|
currNs.Set()
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
AfterEach(func() {
|
AfterEach(func() {
|
||||||
@ -93,5 +92,4 @@ var _ = Describe("chain tests", func() {
|
|||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@ -16,7 +16,7 @@ package sysctl
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -36,7 +36,7 @@ func Sysctl(name string, params ...string) (string, error) {
|
|||||||
|
|
||||||
func getSysctl(name string) (string, error) {
|
func getSysctl(name string) (string, error) {
|
||||||
fullName := filepath.Join("/proc/sys", toNormalName(name))
|
fullName := filepath.Join("/proc/sys", toNormalName(name))
|
||||||
data, err := ioutil.ReadFile(fullName)
|
data, err := os.ReadFile(fullName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -46,7 +46,7 @@ func getSysctl(name string) (string, error) {
|
|||||||
|
|
||||||
func setSysctl(name, value string) (string, error) {
|
func setSysctl(name, value string) (string, error) {
|
||||||
fullName := filepath.Join("/proc/sys", toNormalName(name))
|
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
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,12 +20,13 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"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/ns"
|
||||||
"github.com/containernetworking/plugins/pkg/testutils"
|
"github.com/containernetworking/plugins/pkg/testutils"
|
||||||
"github.com/containernetworking/plugins/pkg/utils/sysctl"
|
"github.com/containernetworking/plugins/pkg/utils/sysctl"
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
"github.com/vishvananda/netlink"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -37,8 +38,7 @@ var _ = Describe("Sysctl tests", func() {
|
|||||||
var testIfaceName string
|
var testIfaceName string
|
||||||
var cleanup func()
|
var cleanup func()
|
||||||
|
|
||||||
BeforeEach(func() {
|
beforeEach := func() {
|
||||||
|
|
||||||
// Save a reference to the original namespace,
|
// Save a reference to the original namespace,
|
||||||
// Add a new NS
|
// Add a new NS
|
||||||
currNs, err := ns.GetCurrentNS()
|
currNs, err := ns.GetCurrentNS()
|
||||||
@ -66,8 +66,7 @@ var _ = Describe("Sysctl tests", func() {
|
|||||||
netlink.LinkDel(testIface)
|
netlink.LinkDel(testIface)
|
||||||
currNs.Set()
|
currNs.Set()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
|
||||||
|
|
||||||
AfterEach(func() {
|
AfterEach(func() {
|
||||||
cleanup()
|
cleanup()
|
||||||
@ -75,7 +74,8 @@ var _ = Describe("Sysctl tests", func() {
|
|||||||
|
|
||||||
Describe("Sysctl", func() {
|
Describe("Sysctl", func() {
|
||||||
It("reads keys with dot separators", func() {
|
It("reads keys with dot separators", func() {
|
||||||
sysctlIfaceName := strings.Replace(testIfaceName, ".", "/", -1)
|
beforeEach()
|
||||||
|
sysctlIfaceName := strings.ReplaceAll(testIfaceName, ".", "/")
|
||||||
sysctlKey := fmt.Sprintf(sysctlDotKeyTemplate, sysctlIfaceName)
|
sysctlKey := fmt.Sprintf(sysctlDotKeyTemplate, sysctlIfaceName)
|
||||||
|
|
||||||
_, err := sysctl.Sysctl(sysctlKey)
|
_, err := sysctl.Sysctl(sysctlKey)
|
||||||
@ -85,6 +85,7 @@ var _ = Describe("Sysctl tests", func() {
|
|||||||
|
|
||||||
Describe("Sysctl", func() {
|
Describe("Sysctl", func() {
|
||||||
It("reads keys with slash separators", func() {
|
It("reads keys with slash separators", func() {
|
||||||
|
beforeEach()
|
||||||
sysctlKey := fmt.Sprintf(sysctlSlashKeyTemplate, testIfaceName)
|
sysctlKey := fmt.Sprintf(sysctlSlashKeyTemplate, testIfaceName)
|
||||||
|
|
||||||
_, err := sysctl.Sysctl(sysctlKey)
|
_, err := sysctl.Sysctl(sysctlKey)
|
||||||
@ -94,7 +95,8 @@ var _ = Describe("Sysctl tests", func() {
|
|||||||
|
|
||||||
Describe("Sysctl", func() {
|
Describe("Sysctl", func() {
|
||||||
It("writes keys with dot separators", func() {
|
It("writes keys with dot separators", func() {
|
||||||
sysctlIfaceName := strings.Replace(testIfaceName, ".", "/", -1)
|
beforeEach()
|
||||||
|
sysctlIfaceName := strings.ReplaceAll(testIfaceName, ".", "/")
|
||||||
sysctlKey := fmt.Sprintf(sysctlDotKeyTemplate, sysctlIfaceName)
|
sysctlKey := fmt.Sprintf(sysctlDotKeyTemplate, sysctlIfaceName)
|
||||||
|
|
||||||
_, err := sysctl.Sysctl(sysctlKey, "1")
|
_, err := sysctl.Sysctl(sysctlKey, "1")
|
||||||
@ -104,11 +106,11 @@ var _ = Describe("Sysctl tests", func() {
|
|||||||
|
|
||||||
Describe("Sysctl", func() {
|
Describe("Sysctl", func() {
|
||||||
It("writes keys with slash separators", func() {
|
It("writes keys with slash separators", func() {
|
||||||
|
beforeEach()
|
||||||
sysctlKey := fmt.Sprintf(sysctlSlashKeyTemplate, testIfaceName)
|
sysctlKey := fmt.Sprintf(sysctlSlashKeyTemplate, testIfaceName)
|
||||||
|
|
||||||
_, err := sysctl.Sysctl(sysctlKey, "1")
|
_, err := sysctl.Sysctl(sysctlKey, "1")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@ -17,7 +17,7 @@ package sysctl_test
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
package utils_test
|
package utils_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUtils(t *testing.T) {
|
func TestUtils(t *testing.T) {
|
||||||
|
@ -18,7 +18,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -26,29 +26,29 @@ var _ = Describe("Utils", func() {
|
|||||||
Describe("FormatChainName", func() {
|
Describe("FormatChainName", func() {
|
||||||
It("must format a short name", func() {
|
It("must format a short name", func() {
|
||||||
chain := FormatChainName("test", "1234")
|
chain := FormatChainName("test", "1234")
|
||||||
Expect(len(chain)).To(Equal(maxChainLength))
|
Expect(chain).To(HaveLen(maxChainLength))
|
||||||
Expect(chain).To(Equal("CNI-2bbe0c48b91a7d1b8a6753a8"))
|
Expect(chain).To(Equal("CNI-2bbe0c48b91a7d1b8a6753a8"))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("must truncate a long name", func() {
|
It("must truncate a long name", func() {
|
||||||
chain := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
|
chain := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
|
||||||
Expect(len(chain)).To(Equal(maxChainLength))
|
Expect(chain).To(HaveLen(maxChainLength))
|
||||||
Expect(chain).To(Equal("CNI-374f33fe84ab0ed84dcdebe3"))
|
Expect(chain).To(Equal("CNI-374f33fe84ab0ed84dcdebe3"))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("must be predictable", func() {
|
It("must be predictable", func() {
|
||||||
chain1 := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
|
chain1 := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
|
||||||
chain2 := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
|
chain2 := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
|
||||||
Expect(len(chain1)).To(Equal(maxChainLength))
|
Expect(chain1).To(HaveLen(maxChainLength))
|
||||||
Expect(len(chain2)).To(Equal(maxChainLength))
|
Expect(chain2).To(HaveLen(maxChainLength))
|
||||||
Expect(chain1).To(Equal(chain2))
|
Expect(chain1).To(Equal(chain2))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("must change when a character changes", func() {
|
It("must change when a character changes", func() {
|
||||||
chain1 := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
|
chain1 := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
|
||||||
chain2 := FormatChainName("testalongnamethatdoesnotmakesense", "1235")
|
chain2 := FormatChainName("testalongnamethatdoesnotmakesense", "1235")
|
||||||
Expect(len(chain1)).To(Equal(maxChainLength))
|
Expect(chain1).To(HaveLen(maxChainLength))
|
||||||
Expect(len(chain2)).To(Equal(maxChainLength))
|
Expect(chain2).To(HaveLen(maxChainLength))
|
||||||
Expect(chain1).To(Equal("CNI-374f33fe84ab0ed84dcdebe3"))
|
Expect(chain1).To(Equal("CNI-374f33fe84ab0ed84dcdebe3"))
|
||||||
Expect(chain1).NotTo(Equal(chain2))
|
Expect(chain1).NotTo(Equal(chain2))
|
||||||
})
|
})
|
||||||
@ -57,35 +57,35 @@ var _ = Describe("Utils", func() {
|
|||||||
Describe("MustFormatChainNameWithPrefix", func() {
|
Describe("MustFormatChainNameWithPrefix", func() {
|
||||||
It("generates a chain name with a prefix", func() {
|
It("generates a chain name with a prefix", func() {
|
||||||
chain := MustFormatChainNameWithPrefix("test", "1234", "PREFIX-")
|
chain := MustFormatChainNameWithPrefix("test", "1234", "PREFIX-")
|
||||||
Expect(len(chain)).To(Equal(maxChainLength))
|
Expect(chain).To(HaveLen(maxChainLength))
|
||||||
Expect(chain).To(Equal("CNI-PREFIX-2bbe0c48b91a7d1b8"))
|
Expect(chain).To(Equal("CNI-PREFIX-2bbe0c48b91a7d1b8"))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("must format a short name", func() {
|
It("must format a short name", func() {
|
||||||
chain := MustFormatChainNameWithPrefix("test", "1234", "PREFIX-")
|
chain := MustFormatChainNameWithPrefix("test", "1234", "PREFIX-")
|
||||||
Expect(len(chain)).To(Equal(maxChainLength))
|
Expect(chain).To(HaveLen(maxChainLength))
|
||||||
Expect(chain).To(Equal("CNI-PREFIX-2bbe0c48b91a7d1b8"))
|
Expect(chain).To(Equal("CNI-PREFIX-2bbe0c48b91a7d1b8"))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("must truncate a long name", func() {
|
It("must truncate a long name", func() {
|
||||||
chain := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-")
|
chain := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-")
|
||||||
Expect(len(chain)).To(Equal(maxChainLength))
|
Expect(chain).To(HaveLen(maxChainLength))
|
||||||
Expect(chain).To(Equal("CNI-PREFIX-374f33fe84ab0ed84"))
|
Expect(chain).To(Equal("CNI-PREFIX-374f33fe84ab0ed84"))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("must be predictable", func() {
|
It("must be predictable", func() {
|
||||||
chain1 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-")
|
chain1 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-")
|
||||||
chain2 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-")
|
chain2 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-")
|
||||||
Expect(len(chain1)).To(Equal(maxChainLength))
|
Expect(chain1).To(HaveLen(maxChainLength))
|
||||||
Expect(len(chain2)).To(Equal(maxChainLength))
|
Expect(chain2).To(HaveLen(maxChainLength))
|
||||||
Expect(chain1).To(Equal(chain2))
|
Expect(chain1).To(Equal(chain2))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("must change when a character changes", func() {
|
It("must change when a character changes", func() {
|
||||||
chain1 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-")
|
chain1 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-")
|
||||||
chain2 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1235", "PREFIX-")
|
chain2 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1235", "PREFIX-")
|
||||||
Expect(len(chain1)).To(Equal(maxChainLength))
|
Expect(chain1).To(HaveLen(maxChainLength))
|
||||||
Expect(len(chain2)).To(Equal(maxChainLength))
|
Expect(chain2).To(HaveLen(maxChainLength))
|
||||||
Expect(chain1).To(Equal("CNI-PREFIX-374f33fe84ab0ed84"))
|
Expect(chain1).To(Equal("CNI-PREFIX-374f33fe84ab0ed84"))
|
||||||
Expect(chain1).NotTo(Equal(chain2))
|
Expect(chain1).NotTo(Equal(chain2))
|
||||||
})
|
})
|
||||||
@ -161,5 +161,4 @@ var _ = Describe("Utils", func() {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@ -15,22 +15,25 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/rpc"
|
"net/rpc"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/go-systemd/v22/activation"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
current "github.com/containernetworking/cni/pkg/types/100"
|
current "github.com/containernetworking/cni/pkg/types/100"
|
||||||
"github.com/coreos/go-systemd/v22/activation"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var errNoMoreTries = errors.New("no more tries")
|
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.
|
// 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 {
|
func generateClientID(containerID string, netName string, ifName string) string {
|
||||||
clientID := containerID + "/" + netName + "/" + ifName
|
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.
|
// defined in RFC 2132, length size can not be larger than 1 octet. So we truncate 254 to make everyone happy.
|
||||||
@ -77,13 +80,21 @@ func (d *DHCP) Allocate(args *skel.CmdArgs, result *current.Result) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
clientID := generateClientID(args.ContainerID, conf.Name, args.IfName)
|
clientID := generateClientID(args.ContainerID, conf.Name, args.IfName)
|
||||||
|
|
||||||
|
// 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
|
hostNetns := d.hostNetnsPrefix + args.Netns
|
||||||
l, err := AcquireLease(clientID, hostNetns, args.IfName,
|
l, err = AcquireLease(clientID, hostNetns, args.IfName,
|
||||||
optsRequesting, optsProviding,
|
optsRequesting, optsProviding,
|
||||||
d.clientTimeout, d.clientResendMax, d.broadcast)
|
d.clientTimeout, d.clientResendMax, d.broadcast)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ipn, err := l.IPNet()
|
ipn, err := l.IPNet()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -104,7 +115,7 @@ func (d *DHCP) Allocate(args *skel.CmdArgs, result *current.Result) error {
|
|||||||
|
|
||||||
// Release stops maintenance of the lease acquired in Allocate()
|
// Release stops maintenance of the lease acquired in Allocate()
|
||||||
// and sends a release msg to the DHCP server.
|
// 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{}
|
conf := NetConf{}
|
||||||
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
|
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
|
||||||
return fmt.Errorf("error parsing netconf: %v", err)
|
return fmt.Errorf("error parsing netconf: %v", err)
|
||||||
@ -156,7 +167,7 @@ func getListener(socketPath string) (net.Listener, error) {
|
|||||||
|
|
||||||
switch {
|
switch {
|
||||||
case len(l) == 0:
|
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 nil, err
|
||||||
}
|
}
|
||||||
return net.Listen("unix", socketPath)
|
return net.Listen("unix", socketPath)
|
||||||
@ -185,7 +196,7 @@ func runDaemon(
|
|||||||
if !filepath.IsAbs(pidfilePath) {
|
if !filepath.IsAbs(pidfilePath) {
|
||||||
return fmt.Errorf("Error writing pidfile %q: path not absolute", 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)
|
return fmt.Errorf("Error writing pidfile %q: %v", pidfilePath, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -195,11 +206,27 @@ func runDaemon(
|
|||||||
return fmt.Errorf("Error getting listener: %v", err)
|
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 := newDHCP(dhcpClientTimeout, resendMax)
|
||||||
dhcp.hostNetnsPrefix = hostPrefix
|
dhcp.hostNetnsPrefix = hostPrefix
|
||||||
dhcp.broadcast = broadcast
|
dhcp.broadcast = broadcast
|
||||||
rpc.Register(dhcp)
|
rpc.Register(dhcp)
|
||||||
rpc.HandleHTTP()
|
rpc.HandleHTTP()
|
||||||
http.Serve(l, nil)
|
srv.Serve(l)
|
||||||
|
|
||||||
|
<-done
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -16,21 +16,19 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
current "github.com/containernetworking/cni/pkg/types/100"
|
current "github.com/containernetworking/cni/pkg/types/100"
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
"github.com/containernetworking/plugins/pkg/testutils"
|
"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() {
|
var _ = Describe("DHCP Multiple Lease Operations", func() {
|
||||||
@ -40,11 +38,10 @@ var _ = Describe("DHCP Multiple Lease Operations", func() {
|
|||||||
var clientCmd *exec.Cmd
|
var clientCmd *exec.Cmd
|
||||||
var socketPath string
|
var socketPath string
|
||||||
var tmpDir string
|
var tmpDir string
|
||||||
var serverIP net.IPNet
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
dhcpServerStopCh, serverIP, socketPath, originalNS, targetNS, err = dhcpSetupOriginalNS()
|
dhcpServerStopCh, socketPath, originalNS, targetNS, err = dhcpSetupOriginalNS()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
// Move the container side to the container's NS
|
// Move the container side to the container's NS
|
||||||
@ -64,7 +61,7 @@ var _ = Describe("DHCP Multiple Lease Operations", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Start the DHCP server
|
// 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())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
// Start the DHCP client daemon
|
// Start the DHCP client daemon
|
||||||
@ -123,7 +120,7 @@ var _ = Describe("DHCP Multiple Lease Operations", func() {
|
|||||||
|
|
||||||
addResult, err = current.GetResult(r)
|
addResult, err = current.GetResult(r)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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"))
|
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24"))
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@ -146,7 +143,7 @@ var _ = Describe("DHCP Multiple Lease Operations", func() {
|
|||||||
|
|
||||||
addResult, err = current.GetResult(r)
|
addResult, err = current.GetResult(r)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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"))
|
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.6/24"))
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDHCP(t *testing.T) {
|
func TestDHCP(t *testing.T) {
|
||||||
|
@ -18,7 +18,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@ -26,24 +25,22 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"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/dhcp4"
|
||||||
"github.com/d2g/dhcp4server"
|
"github.com/d2g/dhcp4server"
|
||||||
"github.com/d2g/dhcp4server/leasepool"
|
"github.com/d2g/dhcp4server/leasepool"
|
||||||
"github.com/d2g/dhcp4server/leasepool/memorypool"
|
"github.com/d2g/dhcp4server/leasepool/memorypool"
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
. "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) {
|
func getTmpDir() (string, error) {
|
||||||
tmpDir, err := ioutil.TempDir(cniDirPrefix, "dhcp")
|
tmpDir, err := os.MkdirTemp(cniDirPrefix, "dhcp")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
tmpDir = filepath.ToSlash(tmpDir)
|
tmpDir = filepath.ToSlash(tmpDir)
|
||||||
}
|
}
|
||||||
@ -51,7 +48,7 @@ func getTmpDir() (string, error) {
|
|||||||
return tmpDir, err
|
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
|
// Add the expected IP to the pool
|
||||||
lp := memorypool.MemoryPool{}
|
lp := memorypool.MemoryPool{}
|
||||||
|
|
||||||
@ -121,7 +118,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var _ = BeforeSuite(func() {
|
var _ = BeforeSuite(func() {
|
||||||
err := os.MkdirAll(cniDirPrefix, 0700)
|
err := os.MkdirAll(cniDirPrefix, 0o700)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -203,7 +200,7 @@ var _ = Describe("DHCP Operations", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Start the DHCP server
|
// 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())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
// Start the DHCP client daemon
|
// Start the DHCP client daemon
|
||||||
@ -274,7 +271,7 @@ var _ = Describe("DHCP Operations", func() {
|
|||||||
|
|
||||||
addResult, err = types100.GetResult(r)
|
addResult, err = types100.GetResult(r)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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"))
|
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24"))
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@ -317,7 +314,7 @@ var _ = Describe("DHCP Operations", func() {
|
|||||||
|
|
||||||
addResult, err = types100.GetResult(r)
|
addResult, err = types100.GetResult(r)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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"))
|
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24"))
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@ -335,9 +332,17 @@ var _ = Describe("DHCP Operations", func() {
|
|||||||
started.Done()
|
started.Done()
|
||||||
started.Wait()
|
started.Wait()
|
||||||
|
|
||||||
err = originalNS.Do(func(ns.NetNS) error {
|
err := originalNS.Do(func(ns.NetNS) error {
|
||||||
return testutils.CmdDelWithArgs(args, func() 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())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
@ -364,7 +369,7 @@ const (
|
|||||||
contVethName1 string = "eth1"
|
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 originalNS, targetNS ns.NetNS
|
||||||
var dhcpServerStopCh chan bool
|
var dhcpServerStopCh chan bool
|
||||||
var socketPath string
|
var socketPath string
|
||||||
@ -385,11 +390,6 @@ func dhcpSetupOriginalNS() (chan bool, net.IPNet, string, ns.NetNS, ns.NetNS, er
|
|||||||
targetNS, err = testutils.NewNS()
|
targetNS, err = testutils.NewNS()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
serverIP := net.IPNet{
|
|
||||||
IP: net.IPv4(192, 168, 1, 1),
|
|
||||||
Mask: net.IPv4Mask(255, 255, 255, 0),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use (original) NS
|
// Use (original) NS
|
||||||
err = originalNS.Do(func(ns.NetNS) error {
|
err = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
@ -484,7 +484,7 @@ func dhcpSetupOriginalNS() (chan bool, net.IPNet, string, ns.NetNS, ns.NetNS, er
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
return dhcpServerStopCh, serverIP, socketPath, originalNS, targetNS, err
|
return dhcpServerStopCh, socketPath, originalNS, targetNS, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ = Describe("DHCP Lease Unavailable Operations", func() {
|
var _ = Describe("DHCP Lease Unavailable Operations", func() {
|
||||||
@ -494,11 +494,10 @@ var _ = Describe("DHCP Lease Unavailable Operations", func() {
|
|||||||
var clientCmd *exec.Cmd
|
var clientCmd *exec.Cmd
|
||||||
var socketPath string
|
var socketPath string
|
||||||
var tmpDir string
|
var tmpDir string
|
||||||
var serverIP net.IPNet
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
dhcpServerStopCh, serverIP, socketPath, originalNS, targetNS, err = dhcpSetupOriginalNS()
|
dhcpServerStopCh, socketPath, originalNS, targetNS, err = dhcpSetupOriginalNS()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
// Move the container side to the container's NS
|
// Move the container side to the container's NS
|
||||||
@ -518,7 +517,7 @@ var _ = Describe("DHCP Lease Unavailable Operations", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Start the DHCP server
|
// 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())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
// Start the DHCP client daemon
|
// Start the DHCP client daemon
|
||||||
@ -597,7 +596,7 @@ var _ = Describe("DHCP Lease Unavailable Operations", func() {
|
|||||||
|
|
||||||
addResult, err = types100.GetResult(r)
|
addResult, err = types100.GetResult(r)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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"))
|
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24"))
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
@ -34,13 +34,17 @@ import (
|
|||||||
|
|
||||||
// RFC 2131 suggests using exponential backoff, starting with 4sec
|
// RFC 2131 suggests using exponential backoff, starting with 4sec
|
||||||
// and randomized to +/- 1sec
|
// and randomized to +/- 1sec
|
||||||
const resendDelay0 = 4 * time.Second
|
const (
|
||||||
const resendDelayMax = 62 * time.Second
|
resendDelay0 = 4 * time.Second
|
||||||
|
resendDelayMax = 62 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
// To speed up the retry for first few failures, we retry without
|
// To speed up the retry for first few failures, we retry without
|
||||||
// backoff for a few times
|
// backoff for a few times
|
||||||
const resendFastDelay = 2 * time.Second
|
const (
|
||||||
const resendFastMax = 4
|
resendFastDelay = 2 * time.Second
|
||||||
|
resendFastMax = 4
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
leaseStateBound = iota
|
leaseStateBound = iota
|
||||||
@ -67,6 +71,7 @@ type DHCPLease struct {
|
|||||||
broadcast bool
|
broadcast bool
|
||||||
stopping uint32
|
stopping uint32
|
||||||
stop chan struct{}
|
stop chan struct{}
|
||||||
|
check chan struct{}
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
// list of requesting and providing options and if they are necessary / their value
|
// list of requesting and providing options and if they are necessary / their value
|
||||||
optsRequesting map[dhcp4.OptionCode]bool
|
optsRequesting map[dhcp4.OptionCode]bool
|
||||||
@ -78,9 +83,12 @@ var requestOptionsDefault = map[dhcp4.OptionCode]bool{
|
|||||||
dhcp4.OptionSubnetMask: true,
|
dhcp4.OptionSubnetMask: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareOptions(cniArgs string, ProvideOptions []ProvideOption, RequestOptions []RequestOption) (
|
func prepareOptions(cniArgs string, provideOptions []ProvideOption, requestOptions []RequestOption) (
|
||||||
optsRequesting map[dhcp4.OptionCode]bool, optsProviding map[dhcp4.OptionCode][]byte, err error) {
|
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
|
// parse CNI args
|
||||||
cniArgsParsed := map[string]string{}
|
cniArgsParsed := map[string]string{}
|
||||||
for _, argPair := range strings.Split(cniArgs, ";") {
|
for _, argPair := range strings.Split(cniArgs, ";") {
|
||||||
@ -93,23 +101,20 @@ func prepareOptions(cniArgs string, ProvideOptions []ProvideOption, RequestOptio
|
|||||||
// parse providing options map
|
// parse providing options map
|
||||||
var optParsed dhcp4.OptionCode
|
var optParsed dhcp4.OptionCode
|
||||||
optsProviding = make(map[dhcp4.OptionCode][]byte)
|
optsProviding = make(map[dhcp4.OptionCode][]byte)
|
||||||
for _, opt := range ProvideOptions {
|
for _, opt := range provideOptions {
|
||||||
optParsed, err = parseOptionName(string(opt.Option))
|
optParsed, err = parseOptionName(string(opt.Option))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("Can not parse option %q: %w", opt.Option, err)
|
return nil, nil, fmt.Errorf("Can not parse option %q: %w", opt.Option, err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if len(opt.Value) > 0 {
|
if len(opt.Value) > 0 {
|
||||||
if len(opt.Value) > 255 {
|
if len(opt.Value) > 255 {
|
||||||
err = fmt.Errorf("value too long for option %q: %q", opt.Option, opt.Value)
|
return nil, nil, fmt.Errorf("value too long for option %q: %q", opt.Option, opt.Value)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
optsProviding[optParsed] = []byte(opt.Value)
|
optsProviding[optParsed] = []byte(opt.Value)
|
||||||
}
|
}
|
||||||
if value, ok := cniArgsParsed[opt.ValueFromCNIArg]; ok {
|
if value, ok := cniArgsParsed[opt.ValueFromCNIArg]; ok {
|
||||||
if len(value) > 255 {
|
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 nil, nil, fmt.Errorf("value too long for option %q from CNI_ARGS %q: %q", opt.Option, opt.ValueFromCNIArg, opt.Value)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
optsProviding[optParsed] = []byte(value)
|
optsProviding[optParsed] = []byte(value)
|
||||||
}
|
}
|
||||||
@ -118,14 +123,13 @@ func prepareOptions(cniArgs string, ProvideOptions []ProvideOption, RequestOptio
|
|||||||
// parse necessary options map
|
// parse necessary options map
|
||||||
optsRequesting = make(map[dhcp4.OptionCode]bool)
|
optsRequesting = make(map[dhcp4.OptionCode]bool)
|
||||||
skipRequireDefault := false
|
skipRequireDefault := false
|
||||||
for _, opt := range RequestOptions {
|
for _, opt := range requestOptions {
|
||||||
if opt.SkipDefault {
|
if opt.SkipDefault {
|
||||||
skipRequireDefault = true
|
skipRequireDefault = true
|
||||||
}
|
}
|
||||||
optParsed, err = parseOptionName(string(opt.Option))
|
optParsed, err = parseOptionName(string(opt.Option))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("Can not parse option %q: %w", opt.Option, err)
|
return nil, nil, fmt.Errorf("Can not parse option %q: %w", opt.Option, err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
optsRequesting[optParsed] = true
|
optsRequesting[optParsed] = true
|
||||||
}
|
}
|
||||||
@ -135,7 +139,7 @@ func prepareOptions(cniArgs string, ProvideOptions []ProvideOption, RequestOptio
|
|||||||
optsRequesting[k] = v
|
optsRequesting[k] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return optsRequesting, optsProviding, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// AcquireLease gets an DHCP lease and then maintains it in the background
|
// AcquireLease gets an DHCP lease and then maintains it in the background
|
||||||
@ -150,6 +154,7 @@ func AcquireLease(
|
|||||||
l := &DHCPLease{
|
l := &DHCPLease{
|
||||||
clientID: clientID,
|
clientID: clientID,
|
||||||
stop: make(chan struct{}),
|
stop: make(chan struct{}),
|
||||||
|
check: make(chan struct{}),
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
resendMax: resendMax,
|
resendMax: resendMax,
|
||||||
broadcast: broadcast,
|
broadcast: broadcast,
|
||||||
@ -200,8 +205,36 @@ func (l *DHCPLease) Stop() {
|
|||||||
l.wg.Wait()
|
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 {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -214,19 +247,7 @@ func (l *DHCPLease) acquire() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := make(dhcp4.Options)
|
opts := l.getAllOptions()
|
||||||
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
|
|
||||||
|
|
||||||
pkt, err := backoffRetry(l.resendMax, func() (*dhcp4.Packet, error) {
|
pkt, err := backoffRetry(l.resendMax, func() (*dhcp4.Packet, error) {
|
||||||
ok, ack, err := DhcpRequest(c, opts)
|
ok, ack, err := DhcpRequest(c, opts)
|
||||||
@ -284,7 +305,7 @@ func (l *DHCPLease) maintain() {
|
|||||||
|
|
||||||
switch state {
|
switch state {
|
||||||
case leaseStateBound:
|
case leaseStateBound:
|
||||||
sleepDur = l.renewalTime.Sub(time.Now())
|
sleepDur = time.Until(l.renewalTime)
|
||||||
if sleepDur <= 0 {
|
if sleepDur <= 0 {
|
||||||
log.Printf("%v: renewing lease", l.clientID)
|
log.Printf("%v: renewing lease", l.clientID)
|
||||||
state = leaseStateRenewing
|
state = leaseStateRenewing
|
||||||
@ -296,7 +317,7 @@ func (l *DHCPLease) maintain() {
|
|||||||
log.Printf("%v: %v", l.clientID, err)
|
log.Printf("%v: %v", l.clientID, err)
|
||||||
|
|
||||||
if time.Now().After(l.rebindingTime) {
|
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
|
state = leaseStateRebinding
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -322,6 +343,9 @@ func (l *DHCPLease) maintain() {
|
|||||||
select {
|
select {
|
||||||
case <-time.After(sleepDur):
|
case <-time.After(sleepDur):
|
||||||
|
|
||||||
|
case <-l.check:
|
||||||
|
log.Printf("%v: Checking lease", l.clientID)
|
||||||
|
|
||||||
case <-l.stop:
|
case <-l.stop:
|
||||||
if err := l.release(); err != nil {
|
if err := l.release(); err != nil {
|
||||||
log.Printf("%v: failed to release DHCP lease: %v", l.clientID, err)
|
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 {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
opts := make(dhcp4.Options)
|
opts := l.getAllOptions()
|
||||||
opts[dhcp4.OptionClientIdentifier] = []byte(l.clientID)
|
|
||||||
|
|
||||||
pkt, err := backoffRetry(l.resendMax, func() (*dhcp4.Packet, error) {
|
pkt, err := backoffRetry(l.resendMax, func() (*dhcp4.Packet, error) {
|
||||||
ok, ack, err := DhcpRenew(c, *l.ack, opts)
|
ok, ack, err := DhcpRenew(c, *l.ack, opts)
|
||||||
switch {
|
switch {
|
||||||
@ -369,14 +391,13 @@ func (l *DHCPLease) renew() error {
|
|||||||
func (l *DHCPLease) release() error {
|
func (l *DHCPLease) release() error {
|
||||||
log.Printf("%v: releasing lease", l.clientID)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
opts := make(dhcp4.Options)
|
opts := l.getOptionsWithClientID()
|
||||||
opts[dhcp4.OptionClientIdentifier] = []byte(l.clientID)
|
|
||||||
|
|
||||||
if err = DhcpRelease(c, *l.ack, opts); err != nil {
|
if err = DhcpRelease(c, *l.ack, opts); err != nil {
|
||||||
return fmt.Errorf("failed to send DHCPRELEASE")
|
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)
|
// RFC 3442 states that if Classless Static Routes (option 121)
|
||||||
// exist, we ignore Static Routes (option 33) and the Router/Gateway.
|
// exist, we ignore Static Routes (option 33) and the Router/Gateway.
|
||||||
opt121_routes := parseCIDRRoutes(l.opts)
|
opt121Routes := parseCIDRRoutes(l.opts)
|
||||||
if len(opt121_routes) > 0 {
|
if len(opt121Routes) > 0 {
|
||||||
return append(routes, opt121_routes...)
|
return append(routes, opt121Routes...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append Static Routes
|
// 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) {
|
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 sleepTime time.Duration
|
||||||
var fastRetryLimit = resendFastMax
|
fastRetryLimit := resendFastMax
|
||||||
for {
|
for {
|
||||||
pkt, err := f()
|
pkt, err := f()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -464,7 +485,7 @@ func backoffRetry(resendMax time.Duration, f func() (*dhcp4.Packet, error)) (*dh
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newDHCPClient(
|
func newDHCPClient(
|
||||||
link netlink.Link, clientID string,
|
link netlink.Link,
|
||||||
timeout time.Duration,
|
timeout time.Duration,
|
||||||
broadcast bool,
|
broadcast bool,
|
||||||
) (*dhcp4client.Client, error) {
|
) (*dhcp4client.Client, error) {
|
||||||
|
@ -118,10 +118,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
func cmdDel(args *skel.CmdArgs) error {
|
func cmdDel(args *skel.CmdArgs) error {
|
||||||
result := struct{}{}
|
result := struct{}{}
|
||||||
if err := rpcCall("DHCP.Release", args, &result); err != nil {
|
return rpcCall("DHCP.Release", args, &result)
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdCheck(args *skel.CmdArgs) error {
|
func cmdCheck(args *skel.CmdArgs) error {
|
||||||
@ -134,11 +131,7 @@ func cmdCheck(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := ¤t.Result{CNIVersion: current.ImplementedSpecVersion}
|
result := ¤t.Result{CNIVersion: current.ImplementedSpecVersion}
|
||||||
if err := rpcCall("DHCP.Allocate", args, result); err != nil {
|
return rpcCall("DHCP.Allocate", args, result)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSocketPath(stdinData []byte) (string, error) {
|
func getSocketPath(stdinData []byte) (string, error) {
|
||||||
|
@ -21,8 +21,9 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
|
||||||
"github.com/d2g/dhcp4"
|
"github.com/d2g/dhcp4"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
var optionNameToID = map[string]dhcp4.OptionCode{
|
var optionNameToID = map[string]dhcp4.OptionCode{
|
||||||
|
@ -19,8 +19,9 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
|
||||||
"github.com/d2g/dhcp4"
|
"github.com/d2g/dhcp4"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func validateRoutes(t *testing.T, routes []*types.Route) {
|
func validateRoutes(t *testing.T, routes []*types.Route) {
|
||||||
|
@ -22,7 +22,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
current "github.com/containernetworking/cni/pkg/types/100"
|
current "github.com/containernetworking/cni/pkg/types/100"
|
||||||
|
|
||||||
"github.com/containernetworking/plugins/pkg/ip"
|
"github.com/containernetworking/plugins/pkg/ip"
|
||||||
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend"
|
"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
|
// If we've reached the end of this range, we need to advance the range
|
||||||
// RangeEnd is inclusive as well
|
// RangeEnd is inclusive as well
|
||||||
if i.cur.Equal(r.RangeEnd) {
|
if i.cur.Equal(r.RangeEnd) {
|
||||||
i.rangeIdx += 1
|
i.rangeIdx++
|
||||||
i.rangeIdx %= len(*i.rangeset)
|
i.rangeIdx %= len(*i.rangeset)
|
||||||
r = (*i.rangeset)[i.rangeIdx]
|
r = (*i.rangeset)[i.rangeIdx]
|
||||||
|
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
package allocator_test
|
package allocator_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAllocator(t *testing.T) {
|
func TestAllocator(t *testing.T) {
|
||||||
|
@ -18,12 +18,12 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
current "github.com/containernetworking/cni/pkg/types/100"
|
current "github.com/containernetworking/cni/pkg/types/100"
|
||||||
fakestore "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/testing"
|
fakestore "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/testing"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type AllocatorTestCase struct {
|
type AllocatorTestCase struct {
|
||||||
@ -77,7 +77,7 @@ func (t AllocatorTestCase) run(idx int) (*current.IPConfig, error) {
|
|||||||
p = append(p, Range{Subnet: types.IPNet(*subnet)})
|
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)})
|
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)
|
res, err = alloc.Get("ID", "eth0", nil)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(res.Address.String()).To(Equal("192.168.1.3/29"))
|
Expect(res.Address.String()).To(Equal("192.168.1.3/29"))
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("when requesting a specific IP", func() {
|
Context("when requesting a specific IP", func() {
|
||||||
@ -301,7 +300,6 @@ var _ = Describe("host-local ip allocator", func() {
|
|||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
Context("when out of ips", func() {
|
Context("when out of ips", func() {
|
||||||
It("returns a meaningful error", func() {
|
It("returns a meaningful error", func() {
|
||||||
@ -332,7 +330,7 @@ var _ = Describe("host-local ip allocator", func() {
|
|||||||
}
|
}
|
||||||
for idx, tc := range testCases {
|
for idx, tc := range testCases {
|
||||||
_, err := tc.run(idx)
|
_, err := tc.run(idx)
|
||||||
Expect(err).NotTo(BeNil())
|
Expect(err).To(HaveOccurred())
|
||||||
Expect(err.Error()).To(HavePrefix("no IP addresses available in range set"))
|
Expect(err.Error()).To(HavePrefix("no IP addresses available in range set"))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -21,7 +21,6 @@ import (
|
|||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/version"
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
|
|
||||||
"github.com/containernetworking/plugins/pkg/ip"
|
"github.com/containernetworking/plugins/pkg/ip"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -43,7 +42,7 @@ type Net struct {
|
|||||||
|
|
||||||
// IPAMConfig represents the IP related network configuration.
|
// IPAMConfig represents the IP related network configuration.
|
||||||
// This nests Range because we initially only supported a single
|
// 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 {
|
type IPAMConfig struct {
|
||||||
*Range
|
*Range
|
||||||
Name string
|
Name string
|
||||||
|
@ -17,9 +17,10 @@ package allocator
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("IPAM config", func() {
|
var _ = Describe("IPAM config", func() {
|
||||||
@ -415,7 +416,6 @@ var _ = Describe("IPAM config", func() {
|
|||||||
}`
|
}`
|
||||||
_, _, err := LoadIPAMConfig([]byte(input), "")
|
_, _, err := LoadIPAMConfig([]byte(input), "")
|
||||||
Expect(err).To(MatchError("invalid range set 0: mixed address families"))
|
Expect(err).To(MatchError("invalid range set 0: mixed address families"))
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
It("Should should error on too many ranges", func() {
|
It("Should should error on too many ranges", func() {
|
||||||
|
@ -125,7 +125,7 @@ func (r *Range) Contains(addr net.IP) bool {
|
|||||||
|
|
||||||
// Overlaps returns true if there is any overlap between ranges
|
// Overlaps returns true if there is any overlap between ranges
|
||||||
func (r *Range) Overlaps(r1 *Range) bool {
|
func (r *Range) Overlaps(r1 *Range) bool {
|
||||||
// different familes
|
// different families
|
||||||
if len(r.RangeStart) != len(r1.RangeStart) {
|
if len(r.RangeStart) != len(r1.RangeStart) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -67,12 +67,10 @@ func (s *RangeSet) Canonicalize() error {
|
|||||||
}
|
}
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
fam = len((*s)[i].RangeStart)
|
fam = len((*s)[i].RangeStart)
|
||||||
} else {
|
} else if fam != len((*s)[i].RangeStart) {
|
||||||
if fam != len((*s)[i].RangeStart) {
|
|
||||||
return fmt.Errorf("mixed address families")
|
return fmt.Errorf("mixed address families")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure none of the ranges in the set overlap
|
// Make sure none of the ranges in the set overlap
|
||||||
l := len(*s)
|
l := len(*s)
|
||||||
|
@ -17,7 +17,7 @@ package allocator
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -40,7 +40,6 @@ var _ = Describe("range sets", func() {
|
|||||||
r, err = p.RangeFor(net.IP{192, 168, 99, 99})
|
r, err = p.RangeFor(net.IP{192, 168, 99, 99})
|
||||||
Expect(r).To(BeNil())
|
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"))
|
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() {
|
It("should discover overlaps within a set", func() {
|
||||||
|
@ -17,11 +17,10 @@ package allocator
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/ginkgo/extensions/table"
|
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("IP ranges", func() {
|
var _ = Describe("IP ranges", func() {
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
package disk
|
package disk
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -25,8 +24,10 @@ import (
|
|||||||
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend"
|
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend"
|
||||||
)
|
)
|
||||||
|
|
||||||
const lastIPFilePrefix = "last_reserved_ip."
|
const (
|
||||||
const LineBreak = "\r\n"
|
lastIPFilePrefix = "last_reserved_ip."
|
||||||
|
LineBreak = "\r\n"
|
||||||
|
)
|
||||||
|
|
||||||
var defaultDataDir = "/var/lib/cni/networks"
|
var defaultDataDir = "/var/lib/cni/networks"
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ func New(network, dataDir string) (*Store, error) {
|
|||||||
dataDir = defaultDataDir
|
dataDir = defaultDataDir
|
||||||
}
|
}
|
||||||
dir := filepath.Join(dataDir, network)
|
dir := filepath.Join(dataDir, network)
|
||||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||||
return nil, err
|
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) {
|
func (s *Store) Reserve(id string, ifname string, ip net.IP, rangeID string) (bool, error) {
|
||||||
fname := GetEscapedPath(s.dataDir, ip.String())
|
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) {
|
if os.IsExist(err) {
|
||||||
return false, nil
|
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
|
// store the reserved ip in lastIPFile
|
||||||
ipfile := GetEscapedPath(s.dataDir, lastIPFilePrefix+rangeID)
|
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 {
|
if err != nil {
|
||||||
return false, err
|
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
|
// LastReservedIP returns the last reserved IP if exists
|
||||||
func (s *Store) LastReservedIP(rangeID string) (net.IP, error) {
|
func (s *Store) LastReservedIP(rangeID string) (net.IP, error) {
|
||||||
ipfile := GetEscapedPath(s.dataDir, lastIPFilePrefix+rangeID)
|
ipfile := GetEscapedPath(s.dataDir, lastIPFilePrefix+rangeID)
|
||||||
data, err := ioutil.ReadFile(ipfile)
|
data, err := os.ReadFile(ipfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return net.ParseIP(string(data)), nil
|
return net.ParseIP(string(data)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) Release(ip net.IP) error {
|
func (s *Store) FindByKey(match string) (bool, error) {
|
||||||
return os.Remove(GetEscapedPath(s.dataDir, ip.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) FindByKey(id string, ifname string, match string) (bool, error) {
|
|
||||||
found := false
|
found := false
|
||||||
|
|
||||||
err := filepath.Walk(s.dataDir, func(path string, info os.FileInfo, err error) error {
|
err := filepath.Walk(s.dataDir, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil || info.IsDir() {
|
if err != nil || info.IsDir() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
data, err := ioutil.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -115,33 +112,31 @@ func (s *Store) FindByKey(id string, ifname string, match string) (bool, error)
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
return found, err
|
return found, err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) FindByID(id string, ifname string) bool {
|
func (s *Store) FindByID(id string, ifname string) bool {
|
||||||
s.Lock()
|
s.Lock()
|
||||||
defer s.Unlock()
|
defer s.Unlock()
|
||||||
|
|
||||||
found := false
|
|
||||||
match := strings.TrimSpace(id) + LineBreak + ifname
|
match := strings.TrimSpace(id) + LineBreak + ifname
|
||||||
found, err := s.FindByKey(id, ifname, match)
|
found, err := s.FindByKey(match)
|
||||||
|
|
||||||
// Match anything created by this id
|
// Match anything created by this id
|
||||||
if !found && err == nil {
|
if !found && err == nil {
|
||||||
match := strings.TrimSpace(id)
|
match := strings.TrimSpace(id)
|
||||||
found, err = s.FindByKey(id, ifname, match)
|
found, _ = s.FindByKey(match)
|
||||||
}
|
}
|
||||||
|
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) ReleaseByKey(id string, ifname string, match string) (bool, error) {
|
func (s *Store) ReleaseByKey(match string) (bool, error) {
|
||||||
found := false
|
found := false
|
||||||
err := filepath.Walk(s.dataDir, func(path string, info os.FileInfo, err error) error {
|
err := filepath.Walk(s.dataDir, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil || info.IsDir() {
|
if err != nil || info.IsDir() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
data, err := ioutil.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -154,20 +149,18 @@ func (s *Store) ReleaseByKey(id string, ifname string, match string) (bool, erro
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
return found, err
|
return found, err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// N.B. This function eats errors to be tolerant and
|
// N.B. This function eats errors to be tolerant and
|
||||||
// release as much as possible
|
// release as much as possible
|
||||||
func (s *Store) ReleaseByID(id string, ifname string) error {
|
func (s *Store) ReleaseByID(id string, ifname string) error {
|
||||||
found := false
|
|
||||||
match := strings.TrimSpace(id) + LineBreak + ifname
|
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
|
// For backwards compatibility, look for files written by a previous version
|
||||||
if !found && err == nil {
|
if !found && err == nil {
|
||||||
match := strings.TrimSpace(id)
|
match := strings.TrimSpace(id)
|
||||||
found, err = s.ReleaseByKey(id, ifname, match)
|
_, err = s.ReleaseByKey(match)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -185,7 +178,7 @@ func (s *Store) GetByID(id string, ifname string) []net.IP {
|
|||||||
if err != nil || info.IsDir() {
|
if err != nil || info.IsDir() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
data, err := ioutil.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -203,7 +196,7 @@ func (s *Store) GetByID(id string, ifname string) []net.IP {
|
|||||||
|
|
||||||
func GetEscapedPath(dataDir string, fname string) string {
|
func GetEscapedPath(dataDir string, fname string) string {
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
fname = strings.Replace(fname, ":", "_", -1)
|
fname = strings.ReplaceAll(fname, ":", "_")
|
||||||
}
|
}
|
||||||
return filepath.Join(dataDir, fname)
|
return filepath.Join(dataDir, fname)
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
package disk
|
package disk
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLock(t *testing.T) {
|
func TestLock(t *testing.T) {
|
||||||
|
@ -15,9 +15,10 @@
|
|||||||
package disk
|
package disk
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/alexflint/go-filemutex"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
|
"github.com/alexflint/go-filemutex"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FileLock wraps os.File to be used as a lock using flock
|
// FileLock wraps os.File to be used as a lock using flock
|
||||||
|
@ -15,23 +15,22 @@
|
|||||||
package disk
|
package disk
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("Lock Operations", func() {
|
var _ = Describe("Lock Operations", func() {
|
||||||
It("locks a file path", func() {
|
It("locks a file path", func() {
|
||||||
dir, err := ioutil.TempDir("", "")
|
dir, err := os.MkdirTemp("", "")
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
// create a dummy file to lock
|
// create a dummy file to lock
|
||||||
path := filepath.Join(dir, "x")
|
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())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
err = f.Close()
|
err = f.Close()
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
@ -47,7 +46,7 @@ var _ = Describe("Lock Operations", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("locks a folder path", func() {
|
It("locks a folder path", func() {
|
||||||
dir, err := ioutil.TempDir("", "")
|
dir, err := os.MkdirTemp("", "")
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ type Store interface {
|
|||||||
Close() error
|
Close() error
|
||||||
Reserve(id string, ifname string, ip net.IP, rangeID string) (bool, error)
|
Reserve(id string, ifname string, ip net.IP, rangeID string) (bool, error)
|
||||||
LastReservedIP(rangeID string) (net.IP, error)
|
LastReservedIP(rangeID string) (net.IP, error)
|
||||||
Release(ip net.IP) error
|
|
||||||
ReleaseByID(id string, ifname string) error
|
ReleaseByID(id string, ifname string) error
|
||||||
GetByID(id string, ifname string) []net.IP
|
GetByID(id string, ifname string) []net.IP
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ func (s *FakeStore) Close() error {
|
|||||||
return nil
|
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()
|
key := ip.String()
|
||||||
if _, ok := s.ipMap[key]; !ok {
|
if _, ok := s.ipMap[key]; !ok {
|
||||||
s.ipMap[key] = id
|
s.ipMap[key] = id
|
||||||
@ -63,12 +63,7 @@ func (s *FakeStore) LastReservedIP(rangeID string) (net.IP, error) {
|
|||||||
return ip, nil
|
return ip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FakeStore) Release(ip net.IP) error {
|
func (s *FakeStore) ReleaseByID(id string, _ string) error {
|
||||||
delete(s.ipMap, ip.String())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *FakeStore) ReleaseByID(id string, ifname string) error {
|
|
||||||
toDelete := []string{}
|
toDelete := []string{}
|
||||||
for k, v := range s.ipMap {
|
for k, v := range s.ipMap {
|
||||||
if v == id {
|
if v == id {
|
||||||
@ -81,7 +76,7 @@ func (s *FakeStore) ReleaseByID(id string, ifname string) error {
|
|||||||
return nil
|
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
|
var ips []net.IP
|
||||||
for k, v := range s.ipMap {
|
for k, v := range s.ipMap {
|
||||||
if v == id {
|
if v == id {
|
||||||
|
@ -15,12 +15,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("parsing resolv.conf", func() {
|
var _ = Describe("parsing resolv.conf", func() {
|
||||||
@ -64,7 +64,7 @@ options four
|
|||||||
})
|
})
|
||||||
|
|
||||||
func parse(contents string) (*types.DNS, error) {
|
func parse(contents string) (*types.DNS, error) {
|
||||||
f, err := ioutil.TempFile("", "host_local_resolv")
|
f, err := os.CreateTemp("", "host_local_resolv")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHostLocal(t *testing.T) {
|
func TestHostLocal(t *testing.T) {
|
||||||
|
@ -16,20 +16,19 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"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/pkg/testutils"
|
||||||
|
|
||||||
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/disk"
|
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/disk"
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const LineBreak = "\r\n"
|
const LineBreak = "\r\n"
|
||||||
@ -43,7 +42,7 @@ var _ = Describe("host-local Operations", func() {
|
|||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
var err error
|
var err error
|
||||||
tmpDir, err = ioutil.TempDir("", "host-local_test")
|
tmpDir, err = os.MkdirTemp("", "host-local_test")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
tmpDir = filepath.ToSlash(tmpDir)
|
tmpDir = filepath.ToSlash(tmpDir)
|
||||||
})
|
})
|
||||||
@ -58,7 +57,7 @@ var _ = Describe("host-local Operations", func() {
|
|||||||
ver := ver
|
ver := ver
|
||||||
|
|
||||||
It(fmt.Sprintf("[%s] allocates and releases addresses with ADD/DEL", ver), func() {
|
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())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
conf := fmt.Sprintf(`{
|
conf := fmt.Sprintf(`{
|
||||||
@ -115,7 +114,7 @@ var _ = Describe("host-local Operations", func() {
|
|||||||
Gateway: net.ParseIP("2001:db8:1::1"),
|
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{
|
for _, expectedRoute := range []*types.Route{
|
||||||
{Dst: mustCIDR("0.0.0.0/0"), GW: nil},
|
{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")
|
ipFilePath1 := filepath.Join(tmpDir, "mynet", "10.1.2.2")
|
||||||
contents, err := ioutil.ReadFile(ipFilePath1)
|
contents, err := os.ReadFile(ipFilePath1)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(string(contents)).To(Equal(args.ContainerID + LineBreak + ifname))
|
Expect(string(contents)).To(Equal(args.ContainerID + LineBreak + ifname))
|
||||||
|
|
||||||
ipFilePath2 := filepath.Join(tmpDir, disk.GetEscapedPath("mynet", "2001:db8:1::2"))
|
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(err).NotTo(HaveOccurred())
|
||||||
Expect(string(contents)).To(Equal(args.ContainerID + LineBreak + ifname))
|
Expect(string(contents)).To(Equal(args.ContainerID + LineBreak + ifname))
|
||||||
|
|
||||||
lastFilePath1 := filepath.Join(tmpDir, "mynet", "last_reserved_ip.0")
|
lastFilePath1 := filepath.Join(tmpDir, "mynet", "last_reserved_ip.0")
|
||||||
contents, err = ioutil.ReadFile(lastFilePath1)
|
contents, err = os.ReadFile(lastFilePath1)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(string(contents)).To(Equal("10.1.2.2"))
|
Expect(string(contents)).To(Equal("10.1.2.2"))
|
||||||
|
|
||||||
lastFilePath2 := filepath.Join(tmpDir, "mynet", "last_reserved_ip.1")
|
lastFilePath2 := filepath.Join(tmpDir, "mynet", "last_reserved_ip.1")
|
||||||
contents, err = ioutil.ReadFile(lastFilePath2)
|
contents, err = os.ReadFile(lastFilePath2)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(string(contents)).To(Equal("2001:db8:1::2"))
|
Expect(string(contents)).To(Equal("2001:db8:1::2"))
|
||||||
// Release the IP
|
// 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() {
|
It(fmt.Sprintf("[%s] allocates and releases addresses on specific interface with ADD/DEL", ver), func() {
|
||||||
const ifname1 string = "eth1"
|
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())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
conf0 := fmt.Sprintf(`{
|
conf0 := fmt.Sprintf(`{
|
||||||
@ -239,12 +238,12 @@ var _ = Describe("host-local Operations", func() {
|
|||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
ipFilePath0 := filepath.Join(tmpDir, "mynet0", "10.1.2.2")
|
ipFilePath0 := filepath.Join(tmpDir, "mynet0", "10.1.2.2")
|
||||||
contents, err := ioutil.ReadFile(ipFilePath0)
|
contents, err := os.ReadFile(ipFilePath0)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(string(contents)).To(Equal(args0.ContainerID + LineBreak + ifname))
|
Expect(string(contents)).To(Equal(args0.ContainerID + LineBreak + ifname))
|
||||||
|
|
||||||
ipFilePath1 := filepath.Join(tmpDir, "mynet1", "10.2.2.2")
|
ipFilePath1 := filepath.Join(tmpDir, "mynet1", "10.2.2.2")
|
||||||
contents, err = ioutil.ReadFile(ipFilePath1)
|
contents, err = os.ReadFile(ipFilePath1)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(string(contents)).To(Equal(args1.ContainerID + LineBreak + ifname1))
|
Expect(string(contents)).To(Equal(args1.ContainerID + LineBreak + ifname1))
|
||||||
|
|
||||||
@ -257,7 +256,7 @@ var _ = Describe("host-local Operations", func() {
|
|||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
|
|
||||||
// reread ipFilePath1, ensure that ifname1 didn't get deleted
|
// reread ipFilePath1, ensure that ifname1 didn't get deleted
|
||||||
contents, err = ioutil.ReadFile(ipFilePath1)
|
contents, err = os.ReadFile(ipFilePath1)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(string(contents)).To(Equal(args1.ContainerID + LineBreak + ifname1))
|
Expect(string(contents)).To(Equal(args1.ContainerID + LineBreak + ifname1))
|
||||||
|
|
||||||
@ -311,7 +310,7 @@ var _ = Describe("host-local Operations", func() {
|
|||||||
|
|
||||||
result0, err := types100.GetResult(r0)
|
result0, err := types100.GetResult(r0)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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"))
|
Expect(result0.IPs[0].Address.String()).Should(Equal("10.1.2.2/24"))
|
||||||
|
|
||||||
// Allocate the IP with the same container ID
|
// Allocate the IP with the same container ID
|
||||||
@ -331,7 +330,7 @@ var _ = Describe("host-local Operations", func() {
|
|||||||
|
|
||||||
result1, err := types100.GetResult(r1)
|
result1, err := types100.GetResult(r1)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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"))
|
Expect(result1.IPs[0].Address.String()).Should(Equal("10.1.2.3/24"))
|
||||||
|
|
||||||
// Allocate the IP with the same container ID again
|
// 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() {
|
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())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
conf := fmt.Sprintf(`{
|
conf := fmt.Sprintf(`{
|
||||||
@ -395,10 +394,10 @@ var _ = Describe("host-local Operations", func() {
|
|||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
ipFilePath := filepath.Join(tmpDir, "mynet", "10.1.2.2")
|
ipFilePath := filepath.Join(tmpDir, "mynet", "10.1.2.2")
|
||||||
contents, err := ioutil.ReadFile(ipFilePath)
|
contents, err := os.ReadFile(ipFilePath)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(string(contents)).To(Equal(args.ContainerID + LineBreak + ifname))
|
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())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
err = testutils.CmdDelWithArgs(args, func() error {
|
err = testutils.CmdDelWithArgs(args, func() error {
|
||||||
@ -466,7 +465,7 @@ var _ = Describe("host-local Operations", func() {
|
|||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
ipFilePath := filepath.Join(tmpDir, "mynet", result.IPs[0].Address.IP.String())
|
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(err).NotTo(HaveOccurred())
|
||||||
Expect(string(contents)).To(Equal("dummy" + LineBreak + ifname))
|
Expect(string(contents)).To(Equal("dummy" + LineBreak + ifname))
|
||||||
|
|
||||||
@ -505,7 +504,7 @@ var _ = Describe("host-local Operations", func() {
|
|||||||
return cmdAdd(args)
|
return cmdAdd(args)
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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() {
|
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() {
|
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())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
conf := fmt.Sprintf(`{
|
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() {
|
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())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
conf := fmt.Sprintf(`{
|
conf := fmt.Sprintf(`{
|
||||||
|
@ -19,14 +19,13 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"strings"
|
"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/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
current "github.com/containernetworking/cni/pkg/types/100"
|
current "github.com/containernetworking/cni/pkg/types/100"
|
||||||
"github.com/containernetworking/cni/pkg/version"
|
"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() {
|
func main() {
|
||||||
@ -34,7 +33,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func cmdCheck(args *skel.CmdArgs) error {
|
func cmdCheck(args *skel.CmdArgs) error {
|
||||||
|
|
||||||
ipamConf, _, err := allocator.LoadIPAMConfig(args.StdinData, args.Args)
|
ipamConf, _, err := allocator.LoadIPAMConfig(args.StdinData, args.Args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -48,8 +46,8 @@ func cmdCheck(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
defer store.Close()
|
defer store.Close()
|
||||||
|
|
||||||
containerIpFound := store.FindByID(args.ContainerID, args.IfName)
|
containerIPFound := store.FindByID(args.ContainerID, args.IfName)
|
||||||
if containerIpFound == false {
|
if !containerIPFound {
|
||||||
return fmt.Errorf("host-local: Failed to find address added by container %v", args.ContainerID)
|
return fmt.Errorf("host-local: Failed to find address added by container %v", args.ContainerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,7 +276,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
return types.PrintResult(result, confVersion)
|
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.
|
// Nothing required because of no resource allocation in static plugin.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ package main_test
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,13 +19,13 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
types100 "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/pkg/testutils"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("static Operations", func() {
|
var _ = Describe("static Operations", func() {
|
||||||
@ -97,7 +97,7 @@ var _ = Describe("static Operations", func() {
|
|||||||
Gateway: net.ParseIP("3ffe:ffff:0::1"),
|
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{
|
Expect(result.Routes).To(Equal([]*types.Route{
|
||||||
{Dst: mustCIDR("0.0.0.0/0")},
|
{Dst: mustCIDR("0.0.0.0/0")},
|
||||||
@ -206,7 +206,7 @@ var _ = Describe("static Operations", func() {
|
|||||||
Gateway: net.ParseIP("10.10.0.254"),
|
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{
|
Expect(result.Routes).To(Equal([]*types.Route{
|
||||||
{Dst: mustCIDR("0.0.0.0/0")},
|
{Dst: mustCIDR("0.0.0.0/0")},
|
||||||
@ -272,7 +272,7 @@ var _ = Describe("static Operations", func() {
|
|||||||
Gateway: nil,
|
Gateway: nil,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
Expect(len(result.IPs)).To(Equal(2))
|
Expect(result.IPs).To(HaveLen(2))
|
||||||
|
|
||||||
// Release the IP
|
// Release the IP
|
||||||
err = testutils.CmdDelWithArgs(args, func() error {
|
err = testutils.CmdDelWithArgs(args, func() error {
|
||||||
@ -337,7 +337,7 @@ var _ = Describe("static Operations", func() {
|
|||||||
Address: mustCIDR("3ffe:ffff:0:01ff::1/64"),
|
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{
|
Expect(result.Routes).To(Equal([]*types.Route{
|
||||||
{Dst: mustCIDR("0.0.0.0/0"), GW: net.ParseIP("10.10.0.254")},
|
{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")},
|
{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"),
|
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{
|
Expect(result.Routes).To(Equal([]*types.Route{
|
||||||
{Dst: mustCIDR("0.0.0.0/0"), GW: net.ParseIP("10.10.0.254")},
|
{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")},
|
{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"),
|
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{
|
Expect(result.Routes).To(Equal([]*types.Route{
|
||||||
{Dst: mustCIDR("0.0.0.0/0"), GW: net.ParseIP("10.10.0.254")},
|
{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")},
|
{Dst: mustCIDR("3ffe:ffff:0:01ff::1/64"), GW: net.ParseIP("3ffe:ffff:0::1")},
|
||||||
|
@ -6,6 +6,7 @@ plugins/main/loopback
|
|||||||
plugins/main/macvlan
|
plugins/main/macvlan
|
||||||
plugins/main/ptp
|
plugins/main/ptp
|
||||||
plugins/main/vlan
|
plugins/main/vlan
|
||||||
|
plugins/main/dummy
|
||||||
plugins/meta/portmap
|
plugins/meta/portmap
|
||||||
plugins/meta/tuning
|
plugins/meta/tuning
|
||||||
plugins/meta/bandwidth
|
plugins/meta/bandwidth
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"sort"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -55,6 +56,8 @@ type NetConf struct {
|
|||||||
HairpinMode bool `json:"hairpinMode"`
|
HairpinMode bool `json:"hairpinMode"`
|
||||||
PromiscMode bool `json:"promiscMode"`
|
PromiscMode bool `json:"promiscMode"`
|
||||||
Vlan int `json:"vlan"`
|
Vlan int `json:"vlan"`
|
||||||
|
VlanTrunk []*VlanTrunk `json:"vlanTrunk,omitempty"`
|
||||||
|
PreserveDefaultVlan bool `json:"preserveDefaultVlan"`
|
||||||
MacSpoofChk bool `json:"macspoofchk,omitempty"`
|
MacSpoofChk bool `json:"macspoofchk,omitempty"`
|
||||||
EnableDad bool `json:"enabledad,omitempty"`
|
EnableDad bool `json:"enabledad,omitempty"`
|
||||||
|
|
||||||
@ -66,6 +69,13 @@ type NetConf struct {
|
|||||||
} `json:"runtimeConfig,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 {
|
type BridgeArgs struct {
|
||||||
@ -94,6 +104,8 @@ func init() {
|
|||||||
func loadNetConf(bytes []byte, envArgs string) (*NetConf, string, error) {
|
func loadNetConf(bytes []byte, envArgs string) (*NetConf, string, error) {
|
||||||
n := &NetConf{
|
n := &NetConf{
|
||||||
BrName: defaultBrName,
|
BrName: defaultBrName,
|
||||||
|
// Set default value equal to true to maintain existing behavior.
|
||||||
|
PreserveDefaultVlan: true,
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal(bytes, n); err != nil {
|
if err := json.Unmarshal(bytes, n); err != nil {
|
||||||
return nil, "", fmt.Errorf("failed to load netconf: %v", err)
|
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 {
|
if n.Vlan < 0 || n.Vlan > 4094 {
|
||||||
return nil, "", fmt.Errorf("invalid VLAN ID %d (must be between 0 and 4094)", n.Vlan)
|
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 != "" {
|
if envArgs != "" {
|
||||||
e := MacEnvArgs{}
|
e := MacEnvArgs{}
|
||||||
@ -124,12 +147,66 @@ func loadNetConf(bytes []byte, envArgs string) (*NetConf, string, error) {
|
|||||||
return n, n.CNIVersion, nil
|
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
|
// calcGateways processes the results from the IPAM plugin and does the
|
||||||
// following for each IP family:
|
// following for each IP family:
|
||||||
// - Calculates and compiles a list of gateway addresses
|
// - Calculates and compiles a list of gateway addresses
|
||||||
// - Adds a default route if needed
|
// - Adds a default route if needed
|
||||||
func calcGateways(result *current.Result, n *NetConf) (*gwInfo, *gwInfo, error) {
|
func calcGateways(result *current.Result, n *NetConf) (*gwInfo, *gwInfo, error) {
|
||||||
|
|
||||||
gwsV4 := &gwInfo{}
|
gwsV4 := &gwInfo{}
|
||||||
gwsV6 := &gwInfo{}
|
gwsV6 := &gwInfo{}
|
||||||
|
|
||||||
@ -300,8 +377,8 @@ func ensureBridge(brName string, mtu int, promiscMode, vlanFiltering bool) (*net
|
|||||||
return br, nil
|
return br, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ensureVlanInterface(br *netlink.Bridge, vlanId int) (netlink.Link, error) {
|
func ensureVlanInterface(br *netlink.Bridge, vlanID int, preserveDefaultVlan bool) (netlink.Link, error) {
|
||||||
name := fmt.Sprintf("%s.%d", br.Name, vlanId)
|
name := fmt.Sprintf("%s.%d", br.Name, vlanID)
|
||||||
|
|
||||||
brGatewayVeth, err := netlink.LinkByName(name)
|
brGatewayVeth, err := netlink.LinkByName(name)
|
||||||
if err != nil {
|
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)
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("faild to create vlan gateway %q: %v", name, err)
|
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
|
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 := ¤t.Interface{}
|
contIface := ¤t.Interface{}
|
||||||
hostIface := ¤t.Interface{}
|
hostIface := ¤t.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)
|
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 {
|
if vlanID != 0 {
|
||||||
err = netlink.BridgeVlanAdd(hostVeth, uint16(vlanID), true, true, false, true)
|
err = netlink.BridgeVlanAdd(hostVeth, uint16(vlanID), true, true, false, true)
|
||||||
if err != nil {
|
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
|
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 {
|
func calcGatewayIP(ipn *net.IPNet) net.IP {
|
||||||
nid := ipn.IP.Mask(ipn.Mask)
|
nid := ipn.IP.Mask(ipn.Mask)
|
||||||
return ip.NextIP(nid)
|
return ip.NextIP(nid)
|
||||||
@ -387,7 +498,7 @@ func calcGatewayIP(ipn *net.IPNet) net.IP {
|
|||||||
|
|
||||||
func setupBridge(n *NetConf) (*netlink.Bridge, *current.Interface, error) {
|
func setupBridge(n *NetConf) (*netlink.Bridge, *current.Interface, error) {
|
||||||
vlanFiltering := false
|
vlanFiltering := false
|
||||||
if n.Vlan != 0 {
|
if n.Vlan != 0 || n.VlanTrunk != nil {
|
||||||
vlanFiltering = true
|
vlanFiltering = true
|
||||||
}
|
}
|
||||||
// create bridge if necessary
|
// create bridge if necessary
|
||||||
@ -410,7 +521,7 @@ func enableIPForward(family int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func cmdAdd(args *skel.CmdArgs) error {
|
func cmdAdd(args *skel.CmdArgs) error {
|
||||||
var success bool = false
|
success := false
|
||||||
|
|
||||||
n, cniVersion, err := loadNetConf(args.StdinData, args.Args)
|
n, cniVersion, err := loadNetConf(args.StdinData, args.Args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -424,7 +535,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if n.HairpinMode && n.PromiscMode {
|
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)
|
br, brInterface, err := setupBridge(n)
|
||||||
@ -438,7 +549,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
defer netns.Close()
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -489,6 +600,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
result.IPs = ipamResult.IPs
|
result.IPs = ipamResult.IPs
|
||||||
result.Routes = ipamResult.Routes
|
result.Routes = ipamResult.Routes
|
||||||
|
result.DNS = ipamResult.DNS
|
||||||
|
|
||||||
if len(result.IPs) == 0 {
|
if len(result.IPs) == 0 {
|
||||||
return errors.New("IPAM plugin returned missing IP config")
|
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")
|
_, _ = sysctl.Sysctl(fmt.Sprintf("net/ipv4/conf/%s/arp_notify", args.IfName), "1")
|
||||||
|
|
||||||
// Add the IP to the interface
|
// Add the IP to the interface
|
||||||
if err := ipam.ConfigureIface(args.IfName, result); err != nil {
|
return ipam.ConfigureIface(args.IfName, result)
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
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 {
|
if n.IsGW {
|
||||||
var firstV4Addr net.IP
|
|
||||||
var vlanInterface *current.Interface
|
var vlanInterface *current.Interface
|
||||||
// Set the IP address(es) on the bridge and enable forwarding
|
// Set the IP address(es) on the bridge and enable forwarding
|
||||||
for _, gws := range []*gwInfo{gwsV4, gwsV6} {
|
for _, gws := range []*gwInfo{gwsV4, gwsV6} {
|
||||||
for _, gw := range gws.gws {
|
for _, gw := range gws.gws {
|
||||||
if gw.IP.To4() != nil && firstV4Addr == nil {
|
|
||||||
firstV4Addr = gw.IP
|
|
||||||
}
|
|
||||||
if n.Vlan != 0 {
|
if n.Vlan != 0 {
|
||||||
vlanIface, err := ensureVlanInterface(br, n.Vlan)
|
vlanIface, err := ensureVlanInterface(br, n.Vlan, n.PreserveDefaultVlan)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create vlan interface: %v", err)
|
return fmt.Errorf("failed to create vlan interface: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if vlanInterface == nil {
|
if vlanInterface == nil {
|
||||||
vlanInterface = ¤t.Interface{Name: vlanIface.Attrs().Name,
|
vlanInterface = ¤t.Interface{
|
||||||
Mac: vlanIface.Attrs().HardwareAddr.String()}
|
Name: vlanIface.Attrs().Name,
|
||||||
|
Mac: vlanIface.Attrs().HardwareAddr.String(),
|
||||||
|
}
|
||||||
result.Interfaces = append(result.Interfaces, vlanInterface)
|
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
|
// Refetch the bridge since its MAC address may change when the first
|
||||||
// veth is added or after its IP address is set
|
// veth is added or after its IP address is set
|
||||||
br, err = bridgeByName(n.BrName)
|
br, err = bridgeByName(n.BrName)
|
||||||
@ -611,18 +723,29 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
brInterface.Mac = br.Attrs().HardwareAddr.String()
|
brInterface.Mac = br.Attrs().HardwareAddr.String()
|
||||||
|
|
||||||
result.DNS = n.DNS
|
|
||||||
|
|
||||||
// Return an error requested by testcases, if any
|
// Return an error requested by testcases, if any
|
||||||
if debugPostIPAMError != nil {
|
if debugPostIPAMError != nil {
|
||||||
return debugPostIPAMError
|
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
|
success = true
|
||||||
|
|
||||||
return types.PrintResult(result, cniVersion)
|
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 {
|
func cmdDel(args *skel.CmdArgs) error {
|
||||||
n, _, err := loadNetConf(args.StdinData, args.Args)
|
n, _, err := loadNetConf(args.StdinData, args.Args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -631,14 +754,17 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
isLayer3 := n.IPAM.Type != ""
|
isLayer3 := n.IPAM.Type != ""
|
||||||
|
|
||||||
|
ipamDel := func() error {
|
||||||
if isLayer3 {
|
if isLayer3 {
|
||||||
if err := ipam.ExecDel(n.IPAM.Type, args.StdinData); err != nil {
|
if err := ipam.ExecDel(n.IPAM.Type, args.StdinData); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if args.Netns == "" {
|
if args.Netns == "" {
|
||||||
return nil
|
return ipamDel()
|
||||||
}
|
}
|
||||||
|
|
||||||
// There is a netns so try to clean up. Delete can be called multiple times
|
// 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
|
// https://github.com/kubernetes/kubernetes/issues/43014#issuecomment-287164444
|
||||||
_, ok := err.(ns.NSPathNotExistErr)
|
_, ok := err.(ns.NSPathNotExistErr)
|
||||||
if ok {
|
if ok {
|
||||||
return nil
|
return ipamDel()
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// call ipam.ExecDel after clean up device in netns
|
||||||
|
if err := ipamDel(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if n.MacSpoofChk {
|
if n.MacSpoofChk {
|
||||||
sc := link.NewSpoofChecker("", "", uniqueID(args.ContainerID, args.IfName))
|
sc := link.NewSpoofChecker("", "", uniqueID(args.ContainerID, args.IfName))
|
||||||
if err := sc.Teardown(); err != nil {
|
if err := sc.Teardown(); err != nil {
|
||||||
@ -698,7 +829,6 @@ type cniBridgeIf struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func validateInterface(intf current.Interface, expectInSb bool) (cniBridgeIf, netlink.Link, error) {
|
func validateInterface(intf current.Interface, expectInSb bool) (cniBridgeIf, netlink.Link, error) {
|
||||||
|
|
||||||
ifFound := cniBridgeIf{found: false}
|
ifFound := cniBridgeIf{found: false}
|
||||||
if intf.Name == "" {
|
if intf.Name == "" {
|
||||||
return ifFound, nil, fmt.Errorf("Interface name missing ")
|
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) {
|
func validateCniBrInterface(intf current.Interface, n *NetConf) (cniBridgeIf, error) {
|
||||||
|
|
||||||
brFound, link, err := validateInterface(intf, false)
|
brFound, link, err := validateInterface(intf, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return brFound, err
|
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) {
|
func validateCniVethInterface(intf *current.Interface, brIf cniBridgeIf, contIf cniBridgeIf) (cniBridgeIf, error) {
|
||||||
|
|
||||||
vethFound, link, err := validateInterface(*intf, false)
|
vethFound, link, err := validateInterface(*intf, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return vethFound, err
|
return vethFound, err
|
||||||
@ -799,7 +927,6 @@ func validateCniVethInterface(intf *current.Interface, brIf cniBridgeIf, contIf
|
|||||||
}
|
}
|
||||||
|
|
||||||
func validateCniContainerInterface(intf current.Interface) (cniBridgeIf, error) {
|
func validateCniContainerInterface(intf current.Interface) (cniBridgeIf, error) {
|
||||||
|
|
||||||
vethFound, link, err := validateInterface(intf, true)
|
vethFound, link, err := validateInterface(intf, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return vethFound, err
|
return vethFound, err
|
||||||
@ -828,7 +955,6 @@ func validateCniContainerInterface(intf current.Interface) (cniBridgeIf, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func cmdCheck(args *skel.CmdArgs) error {
|
func cmdCheck(args *skel.CmdArgs) error {
|
||||||
|
|
||||||
n, _, err := loadNetConf(args.StdinData, args.Args)
|
n, _, err := loadNetConf(args.StdinData, args.Args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
// 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)
|
err = ip.ValidateExpectedInterfaceIPs(args.IfName, result.IPs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -946,11 +1072,7 @@ func cmdCheck(args *skel.CmdArgs) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}); err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func uniqueID(containerID, cniIface string) string {
|
func uniqueID(containerID, cniIface string) string {
|
||||||
|
@ -15,13 +15,26 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
resolvConf string
|
||||||
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBridge(t *testing.T) {
|
func TestBridge(t *testing.T) {
|
||||||
RegisterFailHandler(Fail)
|
RegisterFailHandler(Fail)
|
||||||
|
|
||||||
|
resolvConf, err = newResolvConf()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
RunSpecs(t, "plugins/main/bridge")
|
RunSpecs(t, "plugins/main/bridge")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ = AfterSuite(func() {
|
||||||
|
deleteResolvConf(resolvConf)
|
||||||
|
})
|
||||||
|
@ -17,13 +17,15 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/coreos/go-iptables/iptables"
|
"github.com/coreos/go-iptables/iptables"
|
||||||
"github.com/networkplumbing/go-nft/nft"
|
"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/vishvananda/netlink/nl"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
@ -33,18 +35,14 @@ import (
|
|||||||
"github.com/containernetworking/plugins/pkg/ip"
|
"github.com/containernetworking/plugins/pkg/ip"
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
"github.com/containernetworking/plugins/pkg/testutils"
|
"github.com/containernetworking/plugins/pkg/testutils"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
|
||||||
|
|
||||||
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
|
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
BRNAME = "bridge0"
|
BRNAME = "bridge0"
|
||||||
BRNAMEVLAN = "bridge0.100"
|
BRNAMEVLAN = "bridge0.100"
|
||||||
IFNAME = "eth0"
|
IFNAME = "eth0"
|
||||||
|
NAMESERVER = "192.0.2.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Net struct {
|
type Net struct {
|
||||||
@ -55,8 +53,9 @@ type Net struct {
|
|||||||
IPAM *allocator.IPAMConfig `json:"ipam"`
|
IPAM *allocator.IPAMConfig `json:"ipam"`
|
||||||
// RuntimeConfig struct { // The capability arg
|
// RuntimeConfig struct { // The capability arg
|
||||||
// IPRanges []RangeSet `json:"ipRanges,omitempty"`
|
// IPRanges []RangeSet `json:"ipRanges,omitempty"`
|
||||||
//} `json:"runtimeConfig,omitempty"`
|
|
||||||
// Args *struct {
|
// Args *struct {
|
||||||
|
// } `json:"runtimeConfig,omitempty"`
|
||||||
|
|
||||||
// A *IPAMArgs `json:"cni"`
|
// A *IPAMArgs `json:"cni"`
|
||||||
DNS types.DNS `json:"dns"`
|
DNS types.DNS `json:"dns"`
|
||||||
RawPrevResult map[string]interface{} `json:"prevResult,omitempty"`
|
RawPrevResult map[string]interface{} `json:"prevResult,omitempty"`
|
||||||
@ -70,10 +69,13 @@ type testCase struct {
|
|||||||
subnet string // Single subnet config: Subnet CIDR
|
subnet string // Single subnet config: Subnet CIDR
|
||||||
gateway string // Single subnet config: Gateway
|
gateway string // Single subnet config: Gateway
|
||||||
ranges []rangeInfo // Ranges list (multiple subnets config)
|
ranges []rangeInfo // Ranges list (multiple subnets config)
|
||||||
|
resolvConf string // host-local resolvConf file path
|
||||||
isGW bool
|
isGW bool
|
||||||
isLayer2 bool
|
isLayer2 bool
|
||||||
expGWCIDRs []string // Expected gateway addresses in CIDR form
|
expGWCIDRs []string // Expected gateway addresses in CIDR form
|
||||||
vlan int
|
vlan int
|
||||||
|
vlanTrunk []*VlanTrunk
|
||||||
|
removeDefaultVlan bool
|
||||||
ipMasq bool
|
ipMasq bool
|
||||||
macspoofchk bool
|
macspoofchk bool
|
||||||
AddErr020 string
|
AddErr020 string
|
||||||
@ -129,6 +131,26 @@ const (
|
|||||||
vlan = `,
|
vlan = `,
|
||||||
"vlan": %d`
|
"vlan": %d`
|
||||||
|
|
||||||
|
vlanTrunkStartStr = `,
|
||||||
|
"vlanTrunk": [`
|
||||||
|
|
||||||
|
vlanTrunk = `
|
||||||
|
{
|
||||||
|
"id": %d
|
||||||
|
}`
|
||||||
|
|
||||||
|
vlanTrunkRange = `
|
||||||
|
{
|
||||||
|
"minID": %d,
|
||||||
|
"maxID": %d
|
||||||
|
}`
|
||||||
|
|
||||||
|
vlanTrunkEndStr = `
|
||||||
|
]`
|
||||||
|
|
||||||
|
preserveDefaultVlan = `,
|
||||||
|
"preserveDefaultVlan": false`
|
||||||
|
|
||||||
netDefault = `,
|
netDefault = `,
|
||||||
"isDefaultGateway": true`
|
"isDefaultGateway": true`
|
||||||
|
|
||||||
@ -139,6 +161,9 @@ const (
|
|||||||
ipamDataDirStr = `,
|
ipamDataDirStr = `,
|
||||||
"dataDir": "%s"`
|
"dataDir": "%s"`
|
||||||
|
|
||||||
|
ipamResolvConfStr = `,
|
||||||
|
"resolvConf": "%s"`
|
||||||
|
|
||||||
ipMasqConfStr = `,
|
ipMasqConfStr = `,
|
||||||
"ipMasq": %t`
|
"ipMasq": %t`
|
||||||
|
|
||||||
@ -188,7 +213,28 @@ func (tc testCase) netConfJSON(dataDir string) string {
|
|||||||
conf := fmt.Sprintf(netConfStr, tc.cniVersion, BRNAME)
|
conf := fmt.Sprintf(netConfStr, tc.cniVersion, BRNAME)
|
||||||
if tc.vlan != 0 {
|
if tc.vlan != 0 {
|
||||||
conf += fmt.Sprintf(vlan, tc.vlan)
|
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 {
|
if tc.ipMasq {
|
||||||
conf += tc.ipMasqConfig()
|
conf += tc.ipMasqConfig()
|
||||||
}
|
}
|
||||||
@ -215,6 +261,9 @@ func (tc testCase) netConfJSON(dataDir string) string {
|
|||||||
if tc.ranges != nil {
|
if tc.ranges != nil {
|
||||||
conf += tc.rangesConfig()
|
conf += tc.rangesConfig()
|
||||||
}
|
}
|
||||||
|
if tc.resolvConf != "" {
|
||||||
|
conf += tc.resolvConfConfig()
|
||||||
|
}
|
||||||
conf += ipamEndStr
|
conf += ipamEndStr
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -252,6 +301,26 @@ func (tc testCase) rangesConfig() string {
|
|||||||
return conf + rangesEndStr
|
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
|
var counter uint
|
||||||
|
|
||||||
// createCmdArgs generates network configuration and creates command
|
// createCmdArgs generates network configuration and creates command
|
||||||
@ -270,8 +339,7 @@ func (tc testCase) createCmdArgs(targetNS ns.NetNS, dataDir string) *skel.CmdArg
|
|||||||
|
|
||||||
// createCheckCmdArgs generates network configuration and creates command
|
// createCheckCmdArgs generates network configuration and creates command
|
||||||
// arguments for a Check test case.
|
// 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)
|
conf, err := json.Marshal(config)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
@ -281,7 +349,7 @@ func (tc testCase) createCheckCmdArgs(targetNS ns.NetNS, config *Net, dataDir st
|
|||||||
ContainerID: fmt.Sprintf("dummy-%d", counter),
|
ContainerID: fmt.Sprintf("dummy-%d", counter),
|
||||||
Netns: targetNS.Path(),
|
Netns: targetNS.Path(),
|
||||||
IfName: IFNAME,
|
IfName: IFNAME,
|
||||||
StdinData: []byte(conf),
|
StdinData: conf,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,25 +437,25 @@ func ipVersion(ip net.IP) string {
|
|||||||
|
|
||||||
func countIPAMIPs(path string) (int, error) {
|
func countIPAMIPs(path string) (int, error) {
|
||||||
count := 0
|
count := 0
|
||||||
files, err := ioutil.ReadDir(path)
|
entries, err := os.ReadDir(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
for _, file := range files {
|
for _, entry := range entries {
|
||||||
if file.IsDir() {
|
if entry.IsDir() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if net.ParseIP(file.Name()) != nil {
|
if net.ParseIP(entry.Name()) != nil {
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return count, nil
|
return count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkVlan(vlanId int, bridgeVlanInfo []*nl.BridgeVlanInfo) bool {
|
func checkVlan(vlanID int, bridgeVlanInfo []*nl.BridgeVlanInfo) bool {
|
||||||
for _, vlan := range bridgeVlanInfo {
|
for _, vlan := range bridgeVlanInfo {
|
||||||
if vlan.Vid == uint16(vlanId) {
|
if vlan.Vid == uint16(vlanID) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -408,10 +476,12 @@ type testerBase struct {
|
|||||||
vethName string
|
vethName string
|
||||||
}
|
}
|
||||||
|
|
||||||
type testerV10x testerBase
|
type (
|
||||||
type testerV04x testerBase
|
testerV10x testerBase
|
||||||
type testerV03x testerBase
|
testerV04x testerBase
|
||||||
type testerV01xOr02x testerBase
|
testerV03x testerBase
|
||||||
|
testerV01xOr02x testerBase
|
||||||
|
)
|
||||||
|
|
||||||
func newTesterByVersion(version string, testNS, targetNS ns.NetNS) cmdAddDelTester {
|
func newTesterByVersion(version string, testNS, targetNS ns.NetNS) cmdAddDelTester {
|
||||||
switch {
|
switch {
|
||||||
@ -458,9 +528,9 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
result = resultType.(*types100.Result)
|
result = resultType.(*types100.Result)
|
||||||
|
|
||||||
if !tc.isLayer2 && tc.vlan != 0 {
|
if !tc.isLayer2 && tc.vlan != 0 {
|
||||||
Expect(len(result.Interfaces)).To(Equal(4))
|
Expect(result.Interfaces).To(HaveLen(4))
|
||||||
} else {
|
} else {
|
||||||
Expect(len(result.Interfaces)).To(Equal(3))
|
Expect(result.Interfaces).To(HaveLen(3))
|
||||||
}
|
}
|
||||||
|
|
||||||
Expect(result.Interfaces[0].Name).To(Equal(BRNAME))
|
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)]
|
vlans, isExist := interfaceMap[int32(peerLink.Attrs().Index)]
|
||||||
Expect(isExist).To(BeTrue())
|
Expect(isExist).To(BeTrue())
|
||||||
Expect(checkVlan(tc.vlan, vlans)).To(BeTrue())
|
Expect(checkVlan(tc.vlan, vlans)).To(BeTrue())
|
||||||
|
if tc.removeDefaultVlan {
|
||||||
|
Expect(vlans).To(HaveLen(1))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the bridge vlan filtering equals true
|
// Check the bridge vlan filtering equals true
|
||||||
if tc.vlan != 0 {
|
if tc.vlan != 0 || tc.vlanTrunk != nil {
|
||||||
Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(true))
|
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeTrue())
|
||||||
} else {
|
} else {
|
||||||
Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(false))
|
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeFalse())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure bridge has expected gateway address(es)
|
// 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)
|
addrs, err = netlink.AddrList(vlanLink, netlink.FAMILY_ALL)
|
||||||
}
|
}
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(len(addrs)).To(BeNumerically(">", 0))
|
Expect(addrs).ToNot(BeEmpty())
|
||||||
for _, cidr := range tc.expGWCIDRs {
|
for _, cidr := range tc.expGWCIDRs {
|
||||||
ip, subnet, err := net.ParseCIDR(cidr)
|
ip, subnet, err := net.ParseCIDR(cidr)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
@ -531,16 +604,16 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
break
|
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
|
// Check for the veth link in the main namespace
|
||||||
links, err := netlink.LinkList()
|
links, err := netlink.LinkList()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
if !tc.isLayer2 && tc.vlan != 0 {
|
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 {
|
} 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)
|
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)]
|
vlans, isExist := interfaceMap[int32(link.Attrs().Index)]
|
||||||
Expect(isExist).To(BeTrue())
|
Expect(isExist).To(BeTrue())
|
||||||
Expect(checkVlan(tc.vlan, vlans)).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
|
// 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))
|
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
|
return nil
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
@ -581,9 +681,9 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
expCIDRsV4, expCIDRsV6 := tc.expectedCIDRs()
|
expCIDRsV4, expCIDRsV6 := tc.expectedCIDRs()
|
||||||
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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)
|
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())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
// Ignore link local address which may or may not be
|
// Ignore link local address which may or may not be
|
||||||
// ready when we read addresses.
|
// ready when we read addresses.
|
||||||
@ -618,7 +718,7 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expect(*found).To(Equal(true))
|
Expect(*found).To(BeTrue())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -628,9 +728,9 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
return result, nil
|
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
|
// 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
|
// Execute cmdCHECK on the plugin
|
||||||
err := tester.testNS.Do(func(ns.NetNS) error {
|
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()
|
expCIDRsV4, expCIDRsV6 := tc.expectedCIDRs()
|
||||||
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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)
|
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())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
// Ignore link local address which may or may not be
|
// Ignore link local address which may or may not be
|
||||||
// ready when we read addresses.
|
// ready when we read addresses.
|
||||||
@ -694,7 +794,7 @@ func (tester *testerV10x) cmdCheckTest(tc testCase, conf *Net, dataDir string) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expect(*found).To(Equal(true))
|
Expect(*found).To(BeTrue())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -758,9 +858,9 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
result = resultType.(*types040.Result)
|
result = resultType.(*types040.Result)
|
||||||
|
|
||||||
if !tc.isLayer2 && tc.vlan != 0 {
|
if !tc.isLayer2 && tc.vlan != 0 {
|
||||||
Expect(len(result.Interfaces)).To(Equal(4))
|
Expect(result.Interfaces).To(HaveLen(4))
|
||||||
} else {
|
} else {
|
||||||
Expect(len(result.Interfaces)).To(Equal(3))
|
Expect(result.Interfaces).To(HaveLen(3))
|
||||||
}
|
}
|
||||||
|
|
||||||
Expect(result.Interfaces[0].Name).To(Equal(BRNAME))
|
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)]
|
vlans, isExist := interfaceMap[int32(peerLink.Attrs().Index)]
|
||||||
Expect(isExist).To(BeTrue())
|
Expect(isExist).To(BeTrue())
|
||||||
Expect(checkVlan(tc.vlan, vlans)).To(BeTrue())
|
Expect(checkVlan(tc.vlan, vlans)).To(BeTrue())
|
||||||
|
if tc.removeDefaultVlan {
|
||||||
|
Expect(vlans).To(HaveLen(1))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the bridge vlan filtering equals true
|
// Check the bridge vlan filtering equals true
|
||||||
if tc.vlan != 0 {
|
if tc.vlan != 0 || tc.vlanTrunk != nil {
|
||||||
Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(true))
|
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeTrue())
|
||||||
} else {
|
} else {
|
||||||
Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(false))
|
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeFalse())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure bridge has expected gateway address(es)
|
// 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)
|
addrs, err = netlink.AddrList(vlanLink, netlink.FAMILY_ALL)
|
||||||
}
|
}
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(len(addrs)).To(BeNumerically(">", 0))
|
Expect(addrs).ToNot(BeEmpty())
|
||||||
for _, cidr := range tc.expGWCIDRs {
|
for _, cidr := range tc.expGWCIDRs {
|
||||||
ip, subnet, err := net.ParseCIDR(cidr)
|
ip, subnet, err := net.ParseCIDR(cidr)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
@ -831,16 +934,16 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expect(found).To(Equal(true))
|
Expect(found).To(BeTrue())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for the veth link in the main namespace
|
// Check for the veth link in the main namespace
|
||||||
links, err := netlink.LinkList()
|
links, err := netlink.LinkList()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
if !tc.isLayer2 && tc.vlan != 0 {
|
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 {
|
} 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)
|
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)]
|
vlans, isExist := interfaceMap[int32(link.Attrs().Index)]
|
||||||
Expect(isExist).To(BeTrue())
|
Expect(isExist).To(BeTrue())
|
||||||
Expect(checkVlan(tc.vlan, vlans)).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
|
// 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()
|
expCIDRsV4, expCIDRsV6 := tc.expectedCIDRs()
|
||||||
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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)
|
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())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
// Ignore link local address which may or may not be
|
// Ignore link local address which may or may not be
|
||||||
// ready when we read addresses.
|
// ready when we read addresses.
|
||||||
@ -918,7 +1043,7 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expect(*found).To(Equal(true))
|
Expect(*found).To(BeTrue())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -928,9 +1053,9 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
return result, nil
|
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
|
// 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
|
// Execute cmdCHECK on the plugin
|
||||||
err := tester.testNS.Do(func(ns.NetNS) error {
|
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()
|
expCIDRsV4, expCIDRsV6 := tc.expectedCIDRs()
|
||||||
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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)
|
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())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
// Ignore link local address which may or may not be
|
// Ignore link local address which may or may not be
|
||||||
// ready when we read addresses.
|
// ready when we read addresses.
|
||||||
@ -994,7 +1119,7 @@ func (tester *testerV04x) cmdCheckTest(tc testCase, conf *Net, dataDir string) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expect(*found).To(Equal(true))
|
Expect(*found).To(BeTrue())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -1058,9 +1183,9 @@ func (tester *testerV03x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
result = resultType.(*types040.Result)
|
result = resultType.(*types040.Result)
|
||||||
|
|
||||||
if !tc.isLayer2 && tc.vlan != 0 {
|
if !tc.isLayer2 && tc.vlan != 0 {
|
||||||
Expect(len(result.Interfaces)).To(Equal(4))
|
Expect(result.Interfaces).To(HaveLen(4))
|
||||||
} else {
|
} else {
|
||||||
Expect(len(result.Interfaces)).To(Equal(3))
|
Expect(result.Interfaces).To(HaveLen(3))
|
||||||
}
|
}
|
||||||
|
|
||||||
Expect(result.Interfaces[0].Name).To(Equal(BRNAME))
|
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)]
|
vlans, isExist := interfaceMap[int32(peerLink.Attrs().Index)]
|
||||||
Expect(isExist).To(BeTrue())
|
Expect(isExist).To(BeTrue())
|
||||||
Expect(checkVlan(tc.vlan, vlans)).To(BeTrue())
|
Expect(checkVlan(tc.vlan, vlans)).To(BeTrue())
|
||||||
|
if tc.removeDefaultVlan {
|
||||||
|
Expect(vlans).To(HaveLen(1))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the bridge vlan filtering equals true
|
// Check the bridge vlan filtering equals true
|
||||||
if tc.vlan != 0 {
|
if tc.vlan != 0 || tc.vlanTrunk != nil {
|
||||||
Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(true))
|
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeTrue())
|
||||||
} else {
|
} else {
|
||||||
Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(false))
|
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeFalse())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure bridge has expected gateway address(es)
|
// 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)
|
addrs, err = netlink.AddrList(vlanLink, netlink.FAMILY_ALL)
|
||||||
}
|
}
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(len(addrs)).To(BeNumerically(">", 0))
|
Expect(addrs).ToNot(BeEmpty())
|
||||||
for _, cidr := range tc.expGWCIDRs {
|
for _, cidr := range tc.expGWCIDRs {
|
||||||
ip, subnet, err := net.ParseCIDR(cidr)
|
ip, subnet, err := net.ParseCIDR(cidr)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
@ -1131,16 +1259,16 @@ func (tester *testerV03x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expect(found).To(Equal(true))
|
Expect(found).To(BeTrue())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for the veth link in the main namespace
|
// Check for the veth link in the main namespace
|
||||||
links, err := netlink.LinkList()
|
links, err := netlink.LinkList()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
if !tc.isLayer2 && tc.vlan != 0 {
|
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 {
|
} 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)
|
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)]
|
vlans, isExist := interfaceMap[int32(link.Attrs().Index)]
|
||||||
Expect(isExist).To(BeTrue())
|
Expect(isExist).To(BeTrue())
|
||||||
Expect(checkVlan(tc.vlan, vlans)).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
|
// 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()
|
expCIDRsV4, expCIDRsV6 := tc.expectedCIDRs()
|
||||||
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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)
|
addrs, err = netlink.AddrList(link, netlink.FAMILY_V6)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
// Ignore link local address which may or may not be
|
// 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
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expect(*found).To(Equal(true))
|
Expect(*found).To(BeTrue())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -1226,11 +1376,10 @@ func (tester *testerV03x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tester *testerV03x) cmdCheckTest(tc testCase, conf *Net, dataDir string) {
|
func (tester *testerV03x) cmdCheckTest(_ testCase, _ *Net, _ string) {
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tester *testerV03x) cmdDelTest(tc testCase, dataDir string) {
|
func (tester *testerV03x) cmdDelTest(_ testCase, _ string) {
|
||||||
err := tester.testNS.Do(func(ns.NetNS) error {
|
err := tester.testNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
@ -1327,13 +1476,16 @@ func (tester *testerV01xOr02x) cmdAddTest(tc testCase, dataDir string) (types.Re
|
|||||||
vlans, isExist := interfaceMap[int32(peerLink.Attrs().Index)]
|
vlans, isExist := interfaceMap[int32(peerLink.Attrs().Index)]
|
||||||
Expect(isExist).To(BeTrue())
|
Expect(isExist).To(BeTrue())
|
||||||
Expect(checkVlan(tc.vlan, vlans)).To(BeTrue())
|
Expect(checkVlan(tc.vlan, vlans)).To(BeTrue())
|
||||||
|
if tc.removeDefaultVlan {
|
||||||
|
Expect(vlans).To(HaveLen(1))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the bridge vlan filtering equals true
|
// Check the bridge vlan filtering equals true
|
||||||
if tc.vlan != 0 {
|
if tc.vlan != 0 {
|
||||||
Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(true))
|
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeTrue())
|
||||||
} else {
|
} else {
|
||||||
Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(false))
|
Expect(*link.(*netlink.Bridge).VlanFiltering).To(BeFalse())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure bridge has expected gateway address(es)
|
// 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)
|
addrs, err = netlink.AddrList(vlanLink, netlink.FAMILY_ALL)
|
||||||
}
|
}
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(len(addrs)).To(BeNumerically(">", 0))
|
Expect(addrs).ToNot(BeEmpty())
|
||||||
for _, cidr := range tc.expGWCIDRs {
|
for _, cidr := range tc.expGWCIDRs {
|
||||||
ip, subnet, err := net.ParseCIDR(cidr)
|
ip, subnet, err := net.ParseCIDR(cidr)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
@ -1358,7 +1510,7 @@ func (tester *testerV01xOr02x) cmdAddTest(tc testCase, dataDir string) (types.Re
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expect(found).To(Equal(true))
|
Expect(found).To(BeTrue())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for the veth link in the main namespace; can't
|
// 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()
|
links, err := netlink.LinkList()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
if !tc.isLayer2 && tc.vlan != 0 {
|
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 {
|
} 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
|
// 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()
|
expCIDRsV4, expCIDRsV6 := tc.expectedCIDRs()
|
||||||
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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)
|
addrs, err = netlink.AddrList(link, netlink.FAMILY_V6)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
// Ignore link local address which may or may not be
|
// 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
|
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
|
// 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)]
|
vlans, isExist := hostNSVlanMap[int32(peerIndex)]
|
||||||
Expect(isExist).To(BeTrue())
|
Expect(isExist).To(BeTrue())
|
||||||
Expect(checkVlan(tc.vlan, vlans)).To(BeTrue())
|
Expect(checkVlan(tc.vlan, vlans)).To(BeTrue())
|
||||||
|
if tc.removeDefaultVlan {
|
||||||
|
Expect(vlans).To(HaveLen(1))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -1457,22 +1612,22 @@ func (tester *testerV01xOr02x) cmdAddTest(tc testCase, dataDir string) (types.Re
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tester *testerV01xOr02x) cmdCheckTest(tc testCase, conf *Net, dataDir string) {
|
func (tester *testerV01xOr02x) cmdCheckTest(_ testCase, _ *Net, _ string) {
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tester *testerV01xOr02x) cmdDelTest(tc testCase, dataDir string) {
|
func (tester *testerV01xOr02x) cmdDelTest(tc testCase, _ string) {
|
||||||
err := tester.testNS.Do(func(ns.NetNS) error {
|
err := tester.testNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
err := testutils.CmdDelWithArgs(tester.args, func() error {
|
err := testutils.CmdDelWithArgs(tester.args, func() error {
|
||||||
return cmdDel(tester.args)
|
return cmdDel(tester.args)
|
||||||
})
|
})
|
||||||
if expect020DelError(tc) {
|
switch {
|
||||||
|
case expect020DelError(tc):
|
||||||
Expect(err).To(MatchError(tc.DelErr020))
|
Expect(err).To(MatchError(tc.DelErr020))
|
||||||
} else if expect010DelError(tc) {
|
case expect010DelError(tc):
|
||||||
Expect(err).To(MatchError(tc.DelErr010))
|
Expect(err).To(MatchError(tc.DelErr010))
|
||||||
} else {
|
default:
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -1545,7 +1700,6 @@ func buildOneConfig(name, cniVersion string, orig *Net, prevResult types.Result)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return conf, nil
|
return conf, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdAddDelCheckTest(origNS, targetNS ns.NetNS, tc testCase, dataDir string) {
|
func cmdAddDelCheckTest(origNS, targetNS ns.NetNS, tc testCase, dataDir string) {
|
||||||
@ -1595,7 +1749,7 @@ var _ = Describe("bridge Operations", func() {
|
|||||||
targetNS, err = testutils.NewNS()
|
targetNS, err = testutils.NewNS()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
dataDir, err = ioutil.TempDir("", "bridge_test")
|
dataDir, err = os.MkdirTemp("", "bridge_test")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
// Do not emulate an error, each test will set this if needed
|
// 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())
|
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 {
|
for _, ver := range testutils.AllSpecVersions {
|
||||||
// Redefine ver inside for scope so real value is picked up by each dynamically defined It()
|
// 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.
|
// 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",
|
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",
|
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
|
tc := tc
|
||||||
i := i
|
i := i
|
||||||
@ -1736,6 +1951,37 @@ var _ = Describe("bridge Operations", func() {
|
|||||||
cmdAddDelTest(originalNS, targetNS, tc, dataDir)
|
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{
|
for i, tc := range []testCase{
|
||||||
{
|
{
|
||||||
// IPv4 only
|
// IPv4 only
|
||||||
@ -1786,6 +2032,11 @@ var _ = Describe("bridge Operations", func() {
|
|||||||
tc.cniVersion = ver
|
tc.cniVersion = ver
|
||||||
cmdAddDelTest(originalNS, targetNS, tc, dataDir)
|
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{
|
for i, tc := range []testCase{
|
||||||
@ -1893,7 +2144,7 @@ var _ = Describe("bridge Operations", func() {
|
|||||||
checkBridgeIPs := func(cidr0, cidr1 string) {
|
checkBridgeIPs := func(cidr0, cidr1 string) {
|
||||||
addrs, err := netlink.AddrList(bridge, family)
|
addrs, err := netlink.AddrList(bridge, family)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(len(addrs)).To(Equal(expNumAddrs))
|
Expect(addrs).To(HaveLen(expNumAddrs))
|
||||||
addr := addrs[0].IPNet.String()
|
addr := addrs[0].IPNet.String()
|
||||||
Expect(addr).To(Equal(cidr0))
|
Expect(addr).To(Equal(cidr0))
|
||||||
if cidr1 != "" {
|
if cidr1 != "" {
|
||||||
@ -1903,7 +2154,7 @@ var _ = Describe("bridge Operations", func() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if ForceAddress has default value
|
// Check if ForceAddress has default value
|
||||||
Expect(conf.ForceAddress).To(Equal(false))
|
Expect(conf.ForceAddress).To(BeFalse())
|
||||||
|
|
||||||
// Set first address on bridge
|
// Set first address on bridge
|
||||||
err = ensureAddr(bridge, family, &gwnFirst, conf.ForceAddress)
|
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() {
|
It(fmt.Sprintf("[%s] ensure promiscuous mode on bridge", ver), func() {
|
||||||
const IFNAME = "bridge0"
|
const IFNAME = "bridge0"
|
||||||
const EXPECTED_IP = "10.0.0.0/8"
|
|
||||||
const CHANGED_EXPECTED_IP = "10.1.2.3/16"
|
|
||||||
|
|
||||||
conf := &NetConf{
|
conf := &NetConf{
|
||||||
NetConf: types.NetConf{
|
NetConf: types.NetConf{
|
||||||
@ -1974,7 +2223,7 @@ var _ = Describe("bridge Operations", func() {
|
|||||||
_, _, err := setupBridge(conf)
|
_, _, err := setupBridge(conf)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
// Check if ForceAddress has default value
|
// 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")
|
link, err := netlink.LinkByName("bridge0")
|
||||||
@ -2241,7 +2490,7 @@ var _ = Describe("bridge Operations", func() {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
_, _, err := loadNetConf([]byte(test.netConfJSON("")), "")
|
_, _, err := loadNetConf([]byte(test.netConfJSON("")), "")
|
||||||
if test.err == nil {
|
if test.err == nil {
|
||||||
Expect(err).To(BeNil())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
} else {
|
} else {
|
||||||
Expect(err).To(Equal(test.err))
|
Expect(err).To(Equal(test.err))
|
||||||
}
|
}
|
||||||
|
39
plugins/main/dummy/README.md
Normal file
39
plugins/main/dummy/README.md
Normal 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
292
plugins/main/dummy/dummy.go
Normal 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 := ¤t.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
|
||||||
|
}
|
40
plugins/main/dummy/dummy_suite_test.go
Normal file
40
plugins/main/dummy/dummy_suite_test.go
Normal 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()
|
||||||
|
})
|
385
plugins/main/dummy/dummy_test.go
Normal file
385
plugins/main/dummy/dummy_test.go
Normal 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
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
@ -19,7 +19,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -32,16 +31,13 @@ import (
|
|||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
current "github.com/containernetworking/cni/pkg/types/100"
|
current "github.com/containernetworking/cni/pkg/types/100"
|
||||||
"github.com/containernetworking/cni/pkg/version"
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
|
|
||||||
"github.com/containernetworking/plugins/pkg/ip"
|
"github.com/containernetworking/plugins/pkg/ip"
|
||||||
"github.com/containernetworking/plugins/pkg/ipam"
|
"github.com/containernetworking/plugins/pkg/ipam"
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var sysBusPCI = "/sys/bus/pci/devices"
|
||||||
sysBusPCI = "/sys/bus/pci/devices"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Array of different linux drivers bound to network device needed for DPDK
|
// Array of different linux drivers bound to network device needed for DPDK
|
||||||
var userspaceDrivers = []string{"vfio-pci", "uio_pci_generic", "igb_uio"}
|
var userspaceDrivers = []string{"vfio-pci", "uio_pci_generic", "igb_uio"}
|
||||||
@ -162,10 +158,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
if !cfg.DPDKMode {
|
if !cfg.DPDKMode {
|
||||||
err = containerNs.Do(func(_ ns.NetNS) error {
|
err = containerNs.Do(func(_ ns.NetNS) error {
|
||||||
if err := ipam.ConfigureIface(args.IfName, newResult); err != nil {
|
return ipam.ConfigureIface(args.IfName, newResult)
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -326,10 +319,11 @@ func getLink(devname, hwaddr, kernelpath, pciaddr string) (netlink.Link, error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to list node links: %v", err)
|
return nil, fmt.Errorf("failed to list node links: %v", err)
|
||||||
}
|
}
|
||||||
|
switch {
|
||||||
|
|
||||||
if len(devname) > 0 {
|
case len(devname) > 0:
|
||||||
return netlink.LinkByName(devname)
|
return netlink.LinkByName(devname)
|
||||||
} else if len(hwaddr) > 0 {
|
case len(hwaddr) > 0:
|
||||||
hwAddr, err := net.ParseMAC(hwaddr)
|
hwAddr, err := net.ParseMAC(hwaddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse MAC address %q: %v", hwaddr, err)
|
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
|
return link, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if len(kernelpath) > 0 {
|
case len(kernelpath) > 0:
|
||||||
if !filepath.IsAbs(kernelpath) || !strings.HasPrefix(kernelpath, "/sys/devices/") {
|
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)
|
return nil, fmt.Errorf("kernel device path %q must be absolute and begin with /sys/devices/", kernelpath)
|
||||||
}
|
}
|
||||||
netDir := filepath.Join(kernelpath, "net")
|
netDir := filepath.Join(kernelpath, "net")
|
||||||
files, err := ioutil.ReadDir(netDir)
|
entries, err := os.ReadDir(netDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to find network devices at %q", netDir)
|
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
|
// 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
|
// Make sure it's really an interface
|
||||||
for _, l := range links {
|
for _, l := range links {
|
||||||
if file.Name() == l.Attrs().Name {
|
if entry.Name() == l.Attrs().Name {
|
||||||
return l, nil
|
return l, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if len(pciaddr) > 0 {
|
case len(pciaddr) > 0:
|
||||||
netDir := filepath.Join(sysBusPCI, pciaddr, "net")
|
netDir := filepath.Join(sysBusPCI, pciaddr, "net")
|
||||||
if _, err := os.Lstat(netDir); err != nil {
|
if _, err := os.Lstat(netDir); err != nil {
|
||||||
virtioNetDir := filepath.Join(sysBusPCI, pciaddr, "virtio*", "net")
|
virtioNetDir := filepath.Join(sysBusPCI, pciaddr, "virtio*", "net")
|
||||||
@ -369,12 +363,12 @@ func getLink(devname, hwaddr, kernelpath, pciaddr string) (netlink.Link, error)
|
|||||||
}
|
}
|
||||||
netDir = matches[0]
|
netDir = matches[0]
|
||||||
}
|
}
|
||||||
fInfo, err := ioutil.ReadDir(netDir)
|
entries, err := os.ReadDir(netDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read net directory %s: %q", netDir, err)
|
return nil, fmt.Errorf("failed to read net directory %s: %q", netDir, err)
|
||||||
}
|
}
|
||||||
if len(fInfo) > 0 {
|
if len(entries) > 0 {
|
||||||
return netlink.LinkByName(fInfo[0].Name())
|
return netlink.LinkByName(entries[0].Name())
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("failed to find device name for pci address %s", pciaddr)
|
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 {
|
func cmdCheck(args *skel.CmdArgs) error {
|
||||||
|
|
||||||
cfg, err := loadConf(args.StdinData)
|
cfg, err := loadConf(args.StdinData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
// Check prevResults for ips, routes and dns against values found in the container
|
||||||
if err := netns.Do(func(_ ns.NetNS) error {
|
if err := netns.Do(func(_ ns.NetNS) error {
|
||||||
|
|
||||||
// Check interface against values found in the container
|
// Check interface against values found in the container
|
||||||
err := validateCniContainerInterface(contMap)
|
err := validateCniContainerInterface(contMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -470,7 +462,6 @@ func cmdCheck(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func validateCniContainerInterface(intf current.Interface) error {
|
func validateCniContainerInterface(intf current.Interface) error {
|
||||||
|
|
||||||
var link netlink.Link
|
var link netlink.Link
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestVlan(t *testing.T) {
|
func TestVlan(t *testing.T) {
|
||||||
|
@ -17,13 +17,16 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"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/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
types040 "github.com/containernetworking/cni/pkg/types/040"
|
types040 "github.com/containernetworking/cni/pkg/types/040"
|
||||||
@ -31,10 +34,6 @@ import (
|
|||||||
"github.com/containernetworking/cni/pkg/version"
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
"github.com/containernetworking/plugins/pkg/testutils"
|
"github.com/containernetworking/plugins/pkg/testutils"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
"github.com/vishvananda/netlink"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Net struct {
|
type Net struct {
|
||||||
@ -87,14 +86,14 @@ func canonicalizeIP(ip *net.IP) error {
|
|||||||
// LoadIPAMConfig creates IPAMConfig using json encoded configuration provided
|
// LoadIPAMConfig creates IPAMConfig using json encoded configuration provided
|
||||||
// as `bytes`. At the moment values provided in envArgs are ignored so there
|
// as `bytes`. At the moment values provided in envArgs are ignored so there
|
||||||
// is no possibility to overload the json configuration using envArgs
|
// 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{}
|
n := Net{}
|
||||||
if err := json.Unmarshal(bytes, &n); err != nil {
|
if err := json.Unmarshal(bytes, &n); err != nil {
|
||||||
return nil, "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if n.IPAM == nil {
|
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
|
// Validate all ranges
|
||||||
@ -104,13 +103,13 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) {
|
|||||||
for i := range n.IPAM.Addresses {
|
for i := range n.IPAM.Addresses {
|
||||||
ip, addr, err := net.ParseCIDR(n.IPAM.Addresses[i].AddressStr)
|
ip, addr, err := net.ParseCIDR(n.IPAM.Addresses[i].AddressStr)
|
||||||
if err != nil {
|
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 = *addr
|
||||||
n.IPAM.Addresses[i].Address.IP = ip
|
n.IPAM.Addresses[i].Address.IP = ip
|
||||||
|
|
||||||
if err := canonicalizeIP(&n.IPAM.Addresses[i].Address.IP); err != nil {
|
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 {
|
if n.IPAM.Addresses[i].Address.IP.To4() != nil {
|
||||||
@ -124,7 +123,7 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) {
|
|||||||
e := IPAMEnvArgs{}
|
e := IPAMEnvArgs{}
|
||||||
err := types.LoadArgs(envArgs, &e)
|
err := types.LoadArgs(envArgs, &e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.IP != "" {
|
if e.IP != "" {
|
||||||
@ -133,7 +132,7 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) {
|
|||||||
|
|
||||||
ip, subnet, err := net.ParseCIDR(ipstr)
|
ip, subnet, err := net.ParseCIDR(ipstr)
|
||||||
if err != nil {
|
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}}
|
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), ",") {
|
for _, item := range strings.Split(string(e.GATEWAY), ",") {
|
||||||
gwip := net.ParseIP(strings.TrimSpace(item))
|
gwip := net.ParseIP(strings.TrimSpace(item))
|
||||||
if gwip == nil {
|
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 {
|
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
|
// CNI spec 0.2.0 and below supported only one v4 and v6 address
|
||||||
if numV4 > 1 || numV6 > 1 {
|
if numV4 > 1 || numV6 > 1 {
|
||||||
if ok, _ := version.GreaterThanOrEqualTo(n.CNIVersion, "0.3.0"); !ok {
|
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
|
// Copy net name into IPAM so not to drag Net struct around
|
||||||
n.IPAM.Name = n.Name
|
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) {
|
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
|
return conf, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type tester interface {
|
type tester interface {
|
||||||
@ -225,9 +223,11 @@ type tester interface {
|
|||||||
|
|
||||||
type testerBase struct{}
|
type testerBase struct{}
|
||||||
|
|
||||||
type testerV10x testerBase
|
type (
|
||||||
type testerV04x testerBase
|
testerV10x testerBase
|
||||||
type testerV03x testerBase
|
testerV04x testerBase
|
||||||
|
testerV03x testerBase
|
||||||
|
)
|
||||||
|
|
||||||
func newTesterByVersion(version string) tester {
|
func newTesterByVersion(version string) tester {
|
||||||
switch {
|
switch {
|
||||||
@ -260,8 +260,8 @@ func (t *testerV10x) expectDpdkInterfaceIP(result types.Result, ipAddress string
|
|||||||
// check that the result was sane
|
// check that the result was sane
|
||||||
res, err := types100.NewResultFromResult(result)
|
res, err := types100.NewResultFromResult(result)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(len(res.Interfaces)).To(Equal(0))
|
Expect(res.Interfaces).To(BeEmpty())
|
||||||
Expect(len(res.IPs)).To(Equal(1))
|
Expect(res.IPs).To(HaveLen(1))
|
||||||
Expect(res.IPs[0].Address.String()).To(Equal(ipAddress))
|
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
|
// check that the result was sane
|
||||||
res, err := types040.NewResultFromResult(result)
|
res, err := types040.NewResultFromResult(result)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(len(res.Interfaces)).To(Equal(0))
|
Expect(res.Interfaces).To(BeEmpty())
|
||||||
Expect(len(res.IPs)).To(Equal(1))
|
Expect(res.IPs).To(HaveLen(1))
|
||||||
Expect(res.IPs[0].Address.String()).To(Equal(ipAddress))
|
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
|
// check that the result was sane
|
||||||
res, err := types040.NewResultFromResult(result)
|
res, err := types040.NewResultFromResult(result)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(len(res.Interfaces)).To(Equal(0))
|
Expect(res.Interfaces).To(BeEmpty())
|
||||||
Expect(len(res.IPs)).To(Equal(1))
|
Expect(res.IPs).To(HaveLen(1))
|
||||||
Expect(res.IPs[0].Address.String()).To(Equal(ipAddress))
|
Expect(res.IPs[0].Address.String()).To(Equal(ipAddress))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -712,7 +712,6 @@ var _ = Describe("base functionality", func() {
|
|||||||
}
|
}
|
||||||
_, _, err := testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) })
|
_, _, err := testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) })
|
||||||
Expect(err).To(MatchError(`specify either "device", "hwaddr", "kernelpath" or "pciBusID"`))
|
Expect(err).To(MatchError(`specify either "device", "hwaddr", "kernelpath" or "pciBusID"`))
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
It(fmt.Sprintf("[%s] works with a valid config without IPAM", ver), func() {
|
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)
|
err = json.Unmarshal([]byte(conf), &n)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
n.IPAM, _, err = LoadIPAMConfig([]byte(conf), "")
|
n.IPAM, err = LoadIPAMConfig([]byte(conf), "")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
if testutils.SpecVersionHasCHECK(ver) {
|
if testutils.SpecVersionHasCHECK(ver) {
|
||||||
@ -985,7 +984,7 @@ var _ = Describe("base functionality", func() {
|
|||||||
err = json.Unmarshal([]byte(conf), &n)
|
err = json.Unmarshal([]byte(conf), &n)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
n.IPAM, _, err = LoadIPAMConfig([]byte(conf), "")
|
n.IPAM, err = LoadIPAMConfig([]byte(conf), "")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
if testutils.SpecVersionHasCHECK(ver) {
|
if testutils.SpecVersionHasCHECK(ver) {
|
||||||
@ -1161,14 +1160,14 @@ type fakeFilesystem struct {
|
|||||||
|
|
||||||
func (fs *fakeFilesystem) use() func() {
|
func (fs *fakeFilesystem) use() func() {
|
||||||
// create the new fake fs root dir in /tmp/sriov...
|
// create the new fake fs root dir in /tmp/sriov...
|
||||||
tmpDir, err := ioutil.TempDir("", "sriov")
|
tmpDir, err := os.MkdirTemp("", "sriov")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("error creating fake root dir: %s", err.Error()))
|
panic(fmt.Errorf("error creating fake root dir: %s", err.Error()))
|
||||||
}
|
}
|
||||||
fs.rootDir = tmpDir
|
fs.rootDir = tmpDir
|
||||||
|
|
||||||
for _, dir := range fs.dirs {
|
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 {
|
if err != nil {
|
||||||
panic(fmt.Errorf("error creating fake directory: %s", err.Error()))
|
panic(fmt.Errorf("error creating fake directory: %s", err.Error()))
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ import (
|
|||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
current "github.com/containernetworking/cni/pkg/types/100"
|
current "github.com/containernetworking/cni/pkg/types/100"
|
||||||
"github.com/containernetworking/cni/pkg/version"
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
|
|
||||||
"github.com/containernetworking/plugins/pkg/ip"
|
"github.com/containernetworking/plugins/pkg/ip"
|
||||||
"github.com/containernetworking/plugins/pkg/ipam"
|
"github.com/containernetworking/plugins/pkg/ipam"
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
@ -39,6 +38,7 @@ type NetConf struct {
|
|||||||
Master string `json:"master"`
|
Master string `json:"master"`
|
||||||
Mode string `json:"mode"`
|
Mode string `json:"mode"`
|
||||||
MTU int `json:"mtu"`
|
MTU int `json:"mtu"`
|
||||||
|
LinkContNs bool `json:"linkInContainer,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -48,9 +48,9 @@ func init() {
|
|||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadConf(bytes []byte, cmdCheck bool) (*NetConf, string, error) {
|
func loadConf(args *skel.CmdArgs, cmdCheck bool) (*NetConf, string, error) {
|
||||||
n := &NetConf{}
|
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)
|
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 n.Master == "" {
|
||||||
if result == nil {
|
if result == nil {
|
||||||
defaultRouteInterface, err := getDefaultRouteInterfaceName()
|
var defaultRouteInterface string
|
||||||
|
defaultRouteInterface, err = getNamespacedDefaultRouteInterfaceName(args.Netns, n.LinkContNs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
@ -124,7 +124,15 @@ func createIpvlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Interf
|
|||||||
return nil, err
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to lookup master %q: %v", conf.Master, err)
|
return nil, fmt.Errorf("failed to lookup master %q: %v", conf.Master, err)
|
||||||
}
|
}
|
||||||
@ -146,9 +154,15 @@ func createIpvlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Interf
|
|||||||
Mode: mode,
|
Mode: mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if conf.LinkContNs {
|
||||||
|
err = netns.Do(func(_ ns.NetNS) error {
|
||||||
|
return netlink.LinkAdd(mv)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
if err := netlink.LinkAdd(mv); err != nil {
|
if err := netlink.LinkAdd(mv); err != nil {
|
||||||
return nil, fmt.Errorf("failed to create ipvlan: %v", err)
|
return nil, fmt.Errorf("failed to create ipvlan: %v", err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = netns.Do(func(_ ns.NetNS) error {
|
err = netns.Do(func(_ ns.NetNS) error {
|
||||||
err := ip.RenameLink(tmpName, ifName)
|
err := ip.RenameLink(tmpName, ifName)
|
||||||
@ -193,8 +207,31 @@ func getDefaultRouteInterfaceName() (string, error) {
|
|||||||
return "", fmt.Errorf("no default route interface found")
|
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 {
|
func cmdAdd(args *skel.CmdArgs) error {
|
||||||
n, cniVersion, err := loadConf(args.StdinData, false)
|
n, cniVersion, err := loadConf(args, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -256,11 +293,9 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
err = netns.Do(func(_ ns.NetNS) 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/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 ipam.ConfigureIface(args.IfName, result)
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -272,7 +307,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func cmdDel(args *skel.CmdArgs) error {
|
func cmdDel(args *skel.CmdArgs) error {
|
||||||
n, _, err := loadConf(args.StdinData, false)
|
n, _, err := loadConf(args, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -319,8 +354,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func cmdCheck(args *skel.CmdArgs) error {
|
func cmdCheck(args *skel.CmdArgs) error {
|
||||||
|
n, _, err := loadConf(args, true)
|
||||||
n, _, err := loadConf(args.StdinData, true)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -369,16 +403,23 @@ func cmdCheck(args *skel.CmdArgs) error {
|
|||||||
contMap.Sandbox, args.Netns)
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to lookup master %q: %v", n.Master, err)
|
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
|
// Check prevResults for ips, routes and dns against values found in the container
|
||||||
if err := netns.Do(func(_ ns.NetNS) error {
|
if err := netns.Do(func(_ ns.NetNS) error {
|
||||||
|
|
||||||
// Check interface against values found in the container
|
// Check interface against values found in the container
|
||||||
err := validateCniContainerInterface(contMap, m.Attrs().Index, n.Mode)
|
err := validateCniContainerInterface(contMap, n.Mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -400,8 +441,7 @@ func cmdCheck(args *skel.CmdArgs) error {
|
|||||||
return nil
|
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 link netlink.Link
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@ -422,6 +462,9 @@ func validateCniContainerInterface(intf current.Interface, masterIndex int, mode
|
|||||||
}
|
}
|
||||||
|
|
||||||
mode, err := modeFromString(modeExpected)
|
mode, err := modeFromString(modeExpected)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if ipv.Mode != mode {
|
if ipv.Mode != mode {
|
||||||
currString, err := modeToString(ipv.Mode)
|
currString, err := modeToString(ipv.Mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIpvlan(t *testing.T) {
|
func TestIpvlan(t *testing.T) {
|
||||||
|
@ -17,28 +17,29 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
. "github.com/onsi/gomega"
|
||||||
"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/vishvananda/netlink"
|
"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/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 {
|
type Net struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@ -50,6 +51,7 @@ type Net struct {
|
|||||||
DNS types.DNS `json:"dns"`
|
DNS types.DNS `json:"dns"`
|
||||||
RawPrevResult map[string]interface{} `json:"prevResult,omitempty"`
|
RawPrevResult map[string]interface{} `json:"prevResult,omitempty"`
|
||||||
PrevResult types100.Result `json:"-"`
|
PrevResult types100.Result `json:"-"`
|
||||||
|
LinkContNs bool `json:"linkInContainer"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildOneConfig(cniVersion string, master string, orig *Net, prevResult types.Result) (*Net, error) {
|
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
|
return conf, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipvlanAddCheckDelTest(conf, masterName string, originalNS, targetNS ns.NetNS) {
|
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)
|
addrs, err := netlink.AddrList(link, syscall.AF_INET)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(len(addrs)).To(Equal(1))
|
Expect(addrs).To(HaveLen(1))
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
@ -205,9 +206,11 @@ type tester interface {
|
|||||||
|
|
||||||
type testerBase struct{}
|
type testerBase struct{}
|
||||||
|
|
||||||
type testerV10x testerBase
|
type (
|
||||||
type testerV04x testerBase
|
testerV10x testerBase
|
||||||
type testerV02x testerBase
|
testerV04x testerBase
|
||||||
|
testerV02x testerBase
|
||||||
|
)
|
||||||
|
|
||||||
func newTesterByVersion(version string) tester {
|
func newTesterByVersion(version string) tester {
|
||||||
switch {
|
switch {
|
||||||
@ -227,9 +230,9 @@ func (t *testerV10x) verifyResult(result types.Result, name string) string {
|
|||||||
r, err := types100.GetResult(result)
|
r, err := types100.GetResult(result)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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(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
|
return r.Interfaces[0].Mac
|
||||||
}
|
}
|
||||||
@ -239,15 +242,15 @@ func (t *testerV04x) verifyResult(result types.Result, name string) string {
|
|||||||
r, err := types040.GetResult(result)
|
r, err := types040.GetResult(result)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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(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
|
return r.Interfaces[0].Mac
|
||||||
}
|
}
|
||||||
|
|
||||||
// verifyResult minimally verifies the Result and returns the interface's MAC address
|
// 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)
|
r, err := types020.GetResult(result)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
@ -272,7 +275,7 @@ var _ = Describe("ipvlan Operations", func() {
|
|||||||
targetNS, err = testutils.NewNS()
|
targetNS, err = testutils.NewNS()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
dataDir, err = ioutil.TempDir("", "ipvlan_test")
|
dataDir, err = os.MkdirTemp("", "ipvlan_test")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
err = originalNS.Do(func(ns.NetNS) error {
|
err = originalNS.Do(func(ns.NetNS) error {
|
||||||
@ -290,6 +293,22 @@ var _ = Describe("ipvlan Operations", func() {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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() {
|
AfterEach(func() {
|
||||||
@ -302,10 +321,17 @@ var _ = Describe("ipvlan Operations", func() {
|
|||||||
Expect(testutils.UnmountNS(targetNS)).To(Succeed())
|
Expect(testutils.UnmountNS(targetNS)).To(Succeed())
|
||||||
})
|
})
|
||||||
|
|
||||||
for _, ver := range testutils.AllSpecVersions {
|
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()
|
// 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.
|
// See Gingkgo's "Patterns for dynamically generating tests" documentation.
|
||||||
ver := ver
|
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() {
|
It(fmt.Sprintf("[%s] creates an ipvlan link in a non-default namespace", ver), func() {
|
||||||
conf := &NetConf{
|
conf := &NetConf{
|
||||||
@ -314,9 +340,10 @@ var _ = Describe("ipvlan Operations", func() {
|
|||||||
Name: "testConfig",
|
Name: "testConfig",
|
||||||
Type: "ipvlan",
|
Type: "ipvlan",
|
||||||
},
|
},
|
||||||
Master: MASTER_NAME,
|
Master: masterInterface,
|
||||||
Mode: "l2",
|
Mode: "l2",
|
||||||
MTU: 1500,
|
MTU: 1500,
|
||||||
|
LinkContNs: isInContainer,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := originalNS.Do(func(ns.NetNS) error {
|
err := originalNS.Do(func(ns.NetNS) error {
|
||||||
@ -347,12 +374,13 @@ var _ = Describe("ipvlan Operations", func() {
|
|||||||
"name": "mynet",
|
"name": "mynet",
|
||||||
"type": "ipvlan",
|
"type": "ipvlan",
|
||||||
"master": "%s",
|
"master": "%s",
|
||||||
|
"linkInContainer": %t,
|
||||||
"ipam": {
|
"ipam": {
|
||||||
"type": "host-local",
|
"type": "host-local",
|
||||||
"subnet": "10.1.2.0/24",
|
"subnet": "10.1.2.0/24",
|
||||||
"dataDir": "%s"
|
"dataDir": "%s"
|
||||||
}
|
}
|
||||||
}`, ver, MASTER_NAME, dataDir)
|
}`, ver, masterInterface, isInContainer, dataDir)
|
||||||
|
|
||||||
ipvlanAddCheckDelTest(conf, "", originalNS, targetNS)
|
ipvlanAddCheckDelTest(conf, "", originalNS, targetNS)
|
||||||
})
|
})
|
||||||
@ -363,6 +391,7 @@ var _ = Describe("ipvlan Operations", func() {
|
|||||||
"cniVersion": "%s",
|
"cniVersion": "%s",
|
||||||
"name": "mynet",
|
"name": "mynet",
|
||||||
"type": "ipvlan",
|
"type": "ipvlan",
|
||||||
|
"linkInContainer": %t,
|
||||||
"prevResult": {
|
"prevResult": {
|
||||||
"interfaces": [
|
"interfaces": [
|
||||||
{
|
{
|
||||||
@ -379,9 +408,9 @@ var _ = Describe("ipvlan Operations", func() {
|
|||||||
],
|
],
|
||||||
"routes": []
|
"routes": []
|
||||||
}
|
}
|
||||||
}`, ver, MASTER_NAME)
|
}`, ver, isInContainer, masterInterface)
|
||||||
|
|
||||||
ipvlanAddCheckDelTest(conf, MASTER_NAME, originalNS, targetNS)
|
ipvlanAddCheckDelTest(conf, masterInterface, originalNS, targetNS)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,12 +420,13 @@ var _ = Describe("ipvlan Operations", func() {
|
|||||||
"name": "mynet",
|
"name": "mynet",
|
||||||
"type": "ipvlan",
|
"type": "ipvlan",
|
||||||
"master": "%s",
|
"master": "%s",
|
||||||
|
"linkInContainer": %t,
|
||||||
"ipam": {
|
"ipam": {
|
||||||
"type": "host-local",
|
"type": "host-local",
|
||||||
"subnet": "10.1.2.0/24",
|
"subnet": "10.1.2.0/24",
|
||||||
"dataDir": "%s"
|
"dataDir": "%s"
|
||||||
}
|
}
|
||||||
}`, ver, MASTER_NAME, dataDir)
|
}`, ver, masterInterface, isInContainer, dataDir)
|
||||||
|
|
||||||
args := &skel.CmdArgs{
|
args := &skel.CmdArgs{
|
||||||
ContainerID: "dummy",
|
ContainerID: "dummy",
|
||||||
@ -422,24 +452,29 @@ var _ = Describe("ipvlan Operations", func() {
|
|||||||
"cniVersion": "%s",
|
"cniVersion": "%s",
|
||||||
"name": "mynet",
|
"name": "mynet",
|
||||||
"type": "ipvlan",
|
"type": "ipvlan",
|
||||||
|
"linkInContainer": %t,
|
||||||
"ipam": {
|
"ipam": {
|
||||||
"type": "host-local",
|
"type": "host-local",
|
||||||
"subnet": "10.1.2.0/24",
|
"subnet": "10.1.2.0/24",
|
||||||
"dataDir": "%s"
|
"dataDir": "%s"
|
||||||
}
|
}
|
||||||
}`, ver, dataDir)
|
}`, ver, isInContainer, dataDir)
|
||||||
|
|
||||||
// Make MASTER_NAME as default route interface
|
// Make MASTER_NAME as default route interface
|
||||||
err := originalNS.Do(func(ns.NetNS) error {
|
currentNs := originalNS
|
||||||
|
if isInContainer {
|
||||||
|
currentNs = targetNS
|
||||||
|
}
|
||||||
|
err := currentNs.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
link, err := netlink.LinkByName(MASTER_NAME)
|
link, err := netlink.LinkByName(masterInterface)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
err = netlink.LinkSetUp(link)
|
err = netlink.LinkSetUp(link)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
var address = &net.IPNet{IP: net.IPv4(192, 0, 0, 1), Mask: net.CIDRMask(24, 32)}
|
address := &net.IPNet{IP: net.IPv4(192, 0, 0, 1), Mask: net.CIDRMask(24, 32)}
|
||||||
var addr = &netlink.Addr{IPNet: address}
|
addr := &netlink.Addr{IPNet: address}
|
||||||
err = netlink.AddrAdd(link, addr)
|
err = netlink.AddrAdd(link, addr)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
@ -456,7 +491,8 @@ var _ = Describe("ipvlan Operations", func() {
|
|||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
ipvlanAddCheckDelTest(conf, MASTER_NAME, originalNS, targetNS)
|
ipvlanAddCheckDelTest(conf, masterInterface, originalNS, targetNS)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
@ -26,7 +26,6 @@ import (
|
|||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
current "github.com/containernetworking/cni/pkg/types/100"
|
current "github.com/containernetworking/cni/pkg/types/100"
|
||||||
"github.com/containernetworking/cni/pkg/version"
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
|
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
||||||
)
|
)
|
||||||
@ -112,7 +111,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
r := ¤t.Result{
|
r := ¤t.Result{
|
||||||
CNIVersion: conf.CNIVersion,
|
CNIVersion: conf.CNIVersion,
|
||||||
Interfaces: []*current.Interface{
|
Interfaces: []*current.Interface{
|
||||||
¤t.Interface{
|
{
|
||||||
Name: args.IfName,
|
Name: args.IfName,
|
||||||
Mac: "00:00:00:00:00:00",
|
Mac: "00:00:00:00:00:00",
|
||||||
Sandbox: args.Netns,
|
Sandbox: args.Netns,
|
||||||
|
@ -15,12 +15,11 @@
|
|||||||
package main_test
|
package main_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/onsi/gomega/gexec"
|
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
"github.com/onsi/gomega/gexec"
|
||||||
)
|
)
|
||||||
|
|
||||||
var pathToLoPlugin string
|
var pathToLoPlugin string
|
||||||
|
@ -20,12 +20,13 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
"github.com/containernetworking/plugins/pkg/testutils"
|
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"github.com/onsi/gomega/gbytes"
|
"github.com/onsi/gomega/gbytes"
|
||||||
"github.com/onsi/gomega/gexec"
|
"github.com/onsi/gomega/gexec"
|
||||||
|
|
||||||
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
|
"github.com/containernetworking/plugins/pkg/testutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func generateConfig(cniVersion string) *strings.Reader {
|
func generateConfig(cniVersion string) *strings.Reader {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user