Compare commits
214 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c29dc79f96 | ||
|
|
508c94caec | ||
|
|
b96f963c7a | ||
|
|
518bc80c56 | ||
|
|
434e9b9ef7 | ||
|
|
0e87bccf19 | ||
|
|
0259301ae2 | ||
|
|
14cad164ec | ||
|
|
1868e4fe2d | ||
|
|
5c95925a71 | ||
|
|
505f5a7def | ||
|
|
dc8b8289fa | ||
|
|
196ed2d00e | ||
|
|
43139b3375 | ||
|
|
18d129f18b | ||
|
|
9397270f5e | ||
|
|
3656c69d0f | ||
|
|
3af6e6b61d | ||
|
|
427af8636f | ||
|
|
dc704d19d6 | ||
|
|
45a24d5d95 | ||
|
|
c0533466c8 | ||
|
|
ada798a3f7 | ||
|
|
eb49a034c4 | ||
|
|
670139cffa | ||
|
|
717b4337b5 | ||
|
|
ef076afac1 | ||
|
|
ccc1cfaa58 | ||
|
|
78ebd8bfb9 | ||
|
|
c666d1400d | ||
|
|
ab0b386b4e | ||
|
|
52da39d3aa | ||
|
|
597408952e | ||
|
|
6f05dc325a | ||
|
|
8ab3c63c2d | ||
|
|
c464674317 | ||
|
|
c860b78de4 | ||
|
|
9f1bf2a848 | ||
|
|
7567d28a73 | ||
|
|
ba5bdafe5d | ||
|
|
d34720b531 | ||
|
|
8fc26ce7a0 | ||
|
|
c8d165df6d | ||
|
|
1b5811957e | ||
|
|
8a3014f202 | ||
|
|
019727a392 | ||
|
|
352e0512e8 | ||
|
|
9c016b5d12 | ||
|
|
0729398940 | ||
|
|
394ab0d149 | ||
|
|
0144de0fcf | ||
|
|
47373d2612 | ||
|
|
8e8410f726 | ||
|
|
2bae9b67d3 | ||
|
|
976edfe1bc | ||
|
|
cb244060c2 | ||
|
|
d1aada912d | ||
|
|
8b2b1d20d6 | ||
|
|
14bdce598f | ||
|
|
7e131a0076 | ||
|
|
b6a0e0bc96 | ||
|
|
133a764c4d | ||
|
|
e6099fb83d | ||
|
|
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 |
2
.github/actions/retest-action/Dockerfile
vendored
2
.github/actions/retest-action/Dockerfile
vendored
@@ -1,4 +1,4 @@
|
|||||||
FROM alpine:3.17
|
FROM alpine:3.20
|
||||||
|
|
||||||
RUN apk add --no-cache curl jq
|
RUN apk add --no-cache curl jq
|
||||||
|
|
||||||
|
|||||||
2
.github/actions/retest-action/action.yml
vendored
2
.github/actions/retest-action/action.yml
vendored
@@ -8,4 +8,4 @@ runs:
|
|||||||
using: 'docker'
|
using: 'docker'
|
||||||
image: 'Dockerfile'
|
image: 'Dockerfile'
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ inputs.token }}
|
GITHUB_TOKEN: ${{ inputs.token }}
|
||||||
|
|||||||
18
.github/dependabot.yml
vendored
18
.github/dependabot.yml
vendored
@@ -5,15 +5,21 @@
|
|||||||
|
|
||||||
version: 2
|
version: 2
|
||||||
updates:
|
updates:
|
||||||
- package-ecosystem: "docker" # See documentation for possible values
|
- package-ecosystem: "docker" # See documentation for possible values
|
||||||
directory: "/.github/actions/retest-action" # Location of package manifests
|
directory: "/.github/actions/retest-action" # Location of package manifests
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
- package-ecosystem: "github-actions" # See documentation for possible values
|
- package-ecosystem: "github-actions" # See documentation for possible values
|
||||||
directory: "/" # Location of package manifests
|
directory: "/" # Location of package manifests
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
- package-ecosystem: "gomod" # See documentation for possible values
|
- package-ecosystem: "gomod" # See documentation for possible values
|
||||||
directory: "/" # Location of package manifests
|
directory: "/" # Location of package manifests
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
|
groups:
|
||||||
|
golang:
|
||||||
|
patterns:
|
||||||
|
- "*"
|
||||||
|
exclude-patterns:
|
||||||
|
- "github.com/containernetworking/*"
|
||||||
|
|||||||
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@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Re-Test Action
|
- name: Re-Test Action
|
||||||
uses: ./.github/actions/retest-action
|
uses: ./.github/actions/retest-action
|
||||||
|
|||||||
114
.github/workflows/release.yaml
vendored
Normal file
114
.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
---
|
||||||
|
name: Release binaries
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
linux_release:
|
||||||
|
name: Release linux binaries
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
goarch: [amd64, arm, arm64, mips64le, ppc64le, riscv64, s390x]
|
||||||
|
steps:
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: 1.22
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
env:
|
||||||
|
GOARCH: ${{ matrix.goarch }}
|
||||||
|
CGO_ENABLED: 0
|
||||||
|
run: ./build_linux.sh -ldflags '-extldflags -static -X github.com/containernetworking/plugins/pkg/utils/buildversion.BuildVersion=${{ github.ref_name }}'
|
||||||
|
|
||||||
|
- name: COPY files
|
||||||
|
run: cp README.md LICENSE bin/
|
||||||
|
|
||||||
|
- name: Change plugin file ownership
|
||||||
|
working-directory: ./bin
|
||||||
|
run: sudo chown root:root ./*
|
||||||
|
|
||||||
|
- name: Create dist directory
|
||||||
|
run: mkdir dist
|
||||||
|
|
||||||
|
- name: Create archive file
|
||||||
|
working-directory: ./bin
|
||||||
|
run: tar cfzpv ../dist/cni-plugins-linux-${{ matrix.goarch }}-${{ github.ref_name }}.tgz .
|
||||||
|
|
||||||
|
- name: Create sha256 checksum
|
||||||
|
working-directory: ./dist
|
||||||
|
run: sha256sum cni-plugins-linux-${{ matrix.goarch }}-${{ github.ref_name }}.tgz | tee cni-plugins-linux-${{ matrix.goarch }}-${{ github.ref_name }}.tgz.sha256
|
||||||
|
|
||||||
|
- name: Create sha512 checksum
|
||||||
|
working-directory: ./dist
|
||||||
|
run: sha512sum cni-plugins-linux-${{ matrix.goarch }}-${{ github.ref_name }}.tgz | tee cni-plugins-linux-${{ matrix.goarch }}-${{ github.ref_name }}.tgz.sha512
|
||||||
|
|
||||||
|
- name: Upload binaries to release
|
||||||
|
uses: svenstaro/upload-release-action@v2
|
||||||
|
with:
|
||||||
|
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
file: ./dist/*
|
||||||
|
tag: ${{ github.ref }}
|
||||||
|
overwrite: true
|
||||||
|
file_glob: true
|
||||||
|
|
||||||
|
windows_releases:
|
||||||
|
name: Release windows binaries
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
goarch: [amd64]
|
||||||
|
steps:
|
||||||
|
- name: Install dos2unix
|
||||||
|
run: sudo apt-get install dos2unix
|
||||||
|
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: 1.21
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
env:
|
||||||
|
GOARCH: ${{ matrix.goarch }}
|
||||||
|
CGO_ENABLED: 0
|
||||||
|
run: ./build_windows.sh -ldflags '-extldflags -static -X github.com/containernetworking/plugins/pkg/utils/buildversion.BuildVersion=${{ github.ref_name }}'
|
||||||
|
|
||||||
|
- name: COPY files
|
||||||
|
run: cp README.md LICENSE bin/
|
||||||
|
|
||||||
|
- name: Change plugin file ownership
|
||||||
|
working-directory: ./bin
|
||||||
|
run: sudo chown root:root ./*
|
||||||
|
|
||||||
|
- name: Create dist directory
|
||||||
|
run: mkdir dist
|
||||||
|
|
||||||
|
- name: Create archive file
|
||||||
|
working-directory: ./bin
|
||||||
|
run: tar cpfzv ../dist/cni-plugins-windows-${{ matrix.goarch }}-${{ github.ref_name }}.tgz .
|
||||||
|
|
||||||
|
- name: Create sha256 checksum
|
||||||
|
working-directory: ./dist
|
||||||
|
run: sha256sum cni-plugins-windows-${{ matrix.goarch }}-${{ github.ref_name }}.tgz | tee cni-plugins-windows-${{ matrix.goarch }}-${{ github.ref_name }}.tgz.sha256
|
||||||
|
|
||||||
|
- name: Create sha512 checksum
|
||||||
|
working-directory: ./dist
|
||||||
|
run: sha512sum cni-plugins-windows-${{ matrix.goarch }}-${{ github.ref_name }}.tgz | tee cni-plugins-windows-${{ matrix.goarch }}-${{ github.ref_name }}.tgz.sha512
|
||||||
|
|
||||||
|
- name: Upload binaries to release
|
||||||
|
uses: svenstaro/upload-release-action@v2
|
||||||
|
with:
|
||||||
|
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
file: ./dist/*
|
||||||
|
tag: ${{ github.ref }}
|
||||||
|
overwrite: true
|
||||||
|
file_glob: true
|
||||||
13
.github/workflows/stale.yml
vendored
13
.github/workflows/stale.yml
vendored
@@ -1,13 +0,0 @@
|
|||||||
name: 'Close stale issues and PRs'
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '30 1 * * *'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
stale:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/stale@v7
|
|
||||||
with:
|
|
||||||
stale-pr-message: 'This PR has been untouched for too long without an update. It will be closed in 7 days.'
|
|
||||||
exempt-issue-labels: 'keep'
|
|
||||||
56
.github/workflows/test.yaml
vendored
56
.github/workflows/test.yaml
vendored
@@ -4,20 +4,37 @@ name: test
|
|||||||
on: ["push", "pull_request"]
|
on: ["push", "pull_request"]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
GO_VERSION: "1.18"
|
GO_VERSION: "1.22"
|
||||||
LINUX_ARCHES: "amd64 386 arm arm64 s390x mips64le ppc64le riscv64"
|
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@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GO_VERSION }}
|
go-version-file: go.mod
|
||||||
- uses: actions/checkout@v3
|
- uses: ibiqlik/action-yamllint@v3
|
||||||
|
with:
|
||||||
|
format: auto
|
||||||
|
- uses: golangci/golangci-lint-action@v6
|
||||||
|
with:
|
||||||
|
version: v1.55.2
|
||||||
|
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@v5
|
||||||
|
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 +43,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 +54,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@v3
|
uses: actions/setup-go@v5
|
||||||
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@v3
|
|
||||||
|
|
||||||
- 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 +80,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@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GO_VERSION }}
|
go-version-file: go.mod
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- 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
|
||||||
@@ -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")"
|
||||||
|
|
||||||
|
|||||||
51
go.mod
51
go.mod
@@ -1,41 +1,48 @@
|
|||||||
module github.com/containernetworking/plugins
|
module github.com/containernetworking/plugins
|
||||||
|
|
||||||
go 1.17
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Microsoft/hcsshim v0.9.6
|
github.com/Microsoft/hcsshim v0.12.3
|
||||||
github.com/alexflint/go-filemutex v1.2.0
|
github.com/alexflint/go-filemutex v1.3.0
|
||||||
github.com/buger/jsonparser v1.1.1
|
github.com/buger/jsonparser v1.1.1
|
||||||
github.com/containernetworking/cni v1.1.2
|
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.5.0
|
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.1.0
|
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.5
|
github.com/onsi/ginkgo/v2 v2.19.0
|
||||||
github.com/onsi/gomega v1.24.2
|
github.com/onsi/gomega v1.33.1
|
||||||
github.com/safchain/ethtool v0.2.0
|
github.com/opencontainers/selinux v1.11.0
|
||||||
|
github.com/safchain/ethtool v0.4.0
|
||||||
github.com/vishvananda/netlink v1.2.1-beta.2
|
github.com/vishvananda/netlink v1.2.1-beta.2
|
||||||
golang.org/x/sys v0.4.0
|
golang.org/x/sys v0.21.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Microsoft/go-winio v0.4.17 // indirect
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/containerd/cgroups v1.0.1 // indirect
|
github.com/containerd/cgroups/v3 v3.0.2 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
github.com/containerd/errdefs v0.1.0 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
github.com/go-logr/logr v1.4.1 // indirect
|
||||||
github.com/google/go-cmp v0.5.9 // indirect
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
github.com/nxadm/tail v1.4.8 // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
|
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // 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.3 // 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.4.0 // indirect
|
golang.org/x/net v0.25.0 // indirect
|
||||||
golang.org/x/text v0.5.0 // indirect
|
golang.org/x/text v0.15.0 // indirect
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
golang.org/x/tools v0.21.0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
|
||||||
|
google.golang.org/grpc v1.62.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.33.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
// You may obtain a copy of the License at
|
||||||
//
|
//
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
//
|
//
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -14,21 +14,21 @@
|
|||||||
package integration_test
|
package integration_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
"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"
|
||||||
@@ -61,6 +61,13 @@ var _ = Describe("Basic PTP using cnitool", func() {
|
|||||||
netConfPath, err := filepath.Abs("./testdata")
|
netConfPath, err := filepath.Abs("./testdata")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
// Flush ipam stores to avoid conflicts
|
||||||
|
err = os.RemoveAll("/tmp/chained-ptp-bandwidth-test")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
err = os.RemoveAll("/tmp/basic-ptp-test")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
env = TestEnv([]string{
|
env = TestEnv([]string{
|
||||||
"CNI_PATH=" + cniPath,
|
"CNI_PATH=" + cniPath,
|
||||||
"NETCONFPATH=" + netConfPath,
|
"NETCONFPATH=" + netConfPath,
|
||||||
@@ -83,6 +90,7 @@ var _ = Describe("Basic PTP using cnitool", func() {
|
|||||||
env.runInNS(hostNS, cnitoolBin, "add", netName, contNS.LongName())
|
env.runInNS(hostNS, cnitoolBin, "add", netName, contNS.LongName())
|
||||||
|
|
||||||
addrOutput := env.runInNS(contNS, "ip", "addr")
|
addrOutput := env.runInNS(contNS, "ip", "addr")
|
||||||
|
|
||||||
Expect(addrOutput).To(ContainSubstring(expectedIPPrefix))
|
Expect(addrOutput).To(ContainSubstring(expectedIPPrefix))
|
||||||
|
|
||||||
env.runInNS(hostNS, cnitoolBin, "del", netName, contNS.LongName())
|
env.runInNS(hostNS, cnitoolBin, "del", netName, contNS.LongName())
|
||||||
@@ -146,10 +154,14 @@ var _ = Describe("Basic PTP using cnitool", func() {
|
|||||||
|
|
||||||
chainedBridgeBandwidthEnv.runInNS(hostNS, cnitoolBin, "del", "network-chain-test", contNS1.LongName())
|
chainedBridgeBandwidthEnv.runInNS(hostNS, cnitoolBin, "del", "network-chain-test", contNS1.LongName())
|
||||||
basicBridgeEnv.runInNS(hostNS, cnitoolBin, "del", "network-chain-test", contNS2.LongName())
|
basicBridgeEnv.runInNS(hostNS, cnitoolBin, "del", "network-chain-test", contNS2.LongName())
|
||||||
|
|
||||||
|
contNS1.Del()
|
||||||
|
contNS2.Del()
|
||||||
|
hostNS.Del()
|
||||||
})
|
})
|
||||||
|
|
||||||
Measure("limits traffic only on the restricted bandwith veth device", func(b Benchmarker) {
|
It("limits traffic only on the restricted bandwidth veth device", func() {
|
||||||
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,31 +174,30 @@ 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 := 3000
|
||||||
// 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() {
|
start := time.Now()
|
||||||
makeTcpClientInNS(hostNS.ShortName(), chainedBridgeIP, chainedBridgeBandwidthPort, packetInBytes)
|
makeTCPClientInNS(hostNS.ShortName(), chainedBridgeIP, chainedBridgeBandwidthPort, packetInBytes)
|
||||||
})
|
runtimeWithLimit := time.Since(start)
|
||||||
|
|
||||||
|
log.Printf("Runtime with qos limit %.2f seconds", runtimeWithLimit.Seconds())
|
||||||
|
|
||||||
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() {
|
start = time.Now()
|
||||||
makeTcpClientInNS(hostNS.ShortName(), basicBridgeIP, basicBridgePort, packetInBytes)
|
makeTCPClientInNS(hostNS.ShortName(), basicBridgeIP, basicBridgePort, packetInBytes)
|
||||||
})
|
runtimeWithoutLimit := time.Since(start)
|
||||||
|
log.Printf("Runtime without qos limit %.2f seconds", runtimeWithLimit.Seconds())
|
||||||
|
|
||||||
Expect(runtimeWithLimit).To(BeNumerically(">", runtimeWithoutLimit+1000*time.Millisecond))
|
Expect(runtimeWithLimit).To(BeNumerically(">", runtimeWithoutLimit+1000*time.Millisecond))
|
||||||
}, 1)
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -224,7 +235,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 +254,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 +271,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) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
// You may obtain a copy of the License at
|
||||||
//
|
//
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
//
|
//
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -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"
|
||||||
)
|
)
|
||||||
|
|||||||
3
integration/testdata/basic-ptp.json
vendored
3
integration/testdata/basic-ptp.json
vendored
@@ -6,6 +6,7 @@
|
|||||||
"mtu": 512,
|
"mtu": 512,
|
||||||
"ipam": {
|
"ipam": {
|
||||||
"type": "host-local",
|
"type": "host-local",
|
||||||
"subnet": "10.1.2.0/24"
|
"subnet": "10.1.2.0/24",
|
||||||
|
"dataDir": "/tmp/basic-ptp-test"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
"mtu": 512,
|
"mtu": 512,
|
||||||
"ipam": {
|
"ipam": {
|
||||||
"type": "host-local",
|
"type": "host-local",
|
||||||
"subnet": "10.9.2.0/24"
|
"subnet": "10.9.2.0/24",
|
||||||
|
"dataDir": "/tmp/chained-ptp-bandwidth-test"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ package ip
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
11
pkg/ip/ip.go
11
pkg/ip/ip.go
@@ -47,13 +47,12 @@ func ParseIP(s string) *IP {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return newIP(ip, ipNet.Mask)
|
return newIP(ip, ipNet.Mask)
|
||||||
} else {
|
|
||||||
ip := net.ParseIP(s)
|
|
||||||
if ip == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return newIP(ip, nil)
|
|
||||||
}
|
}
|
||||||
|
ip := net.ParseIP(s)
|
||||||
|
if ip == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return newIP(ip, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToIP will return a net.IP in standard form from this IP.
|
// ToIP will return a net.IP in standard form from this IP.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -58,5 +58,5 @@ func echo1(f string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return os.WriteFile(f, []byte("1"), 0644)
|
return os.WriteFile(f, []byte("1"), 0o644)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -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 (%q) peer provided (%q) already exists", name, peerName)
|
||||||
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"
|
||||||
@@ -64,7 +57,7 @@ var _ = Describe("Link", func() {
|
|||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
fakeBytes := make([]byte, 20)
|
fakeBytes := make([]byte, 20)
|
||||||
//to be reset in AfterEach block
|
// to be reset in AfterEach block
|
||||||
rand.Reader = bytes.NewReader(fakeBytes)
|
rand.Reader = bytes.NewReader(fakeBytes)
|
||||||
|
|
||||||
_ = containerNetNS.Do(func(ns.NetNS) error {
|
_ = containerNetNS.Do(func(ns.NetNS) error {
|
||||||
@@ -156,9 +149,9 @@ var _ = Describe("Link", func() {
|
|||||||
It("returns useful error", func() {
|
It("returns useful error", func() {
|
||||||
_ = containerNetNS.Do(func(ns.NetNS) error {
|
_ = containerNetNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
testHostVethName := "test" + hostVethName
|
||||||
_, _, err := ip.SetupVeth(containerVethName, mtu, "", hostNetNS)
|
_, _, err := ip.SetupVethWithName(containerVethName, testHostVethName, mtu, "", hostNetNS)
|
||||||
Expect(err.Error()).To(Equal(fmt.Sprintf("container veth name provided (%s) already exists", containerVethName)))
|
Expect(err.Error()).To(Equal(fmt.Sprintf("container veth name (%q) peer provided (%q) already exists", containerVethName, testHostVethName)))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@@ -181,15 +174,14 @@ var _ = Describe("Link", func() {
|
|||||||
|
|
||||||
Context("when there is no name available for the host-side", func() {
|
Context("when there is no name available for the host-side", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
//adding different interface to container ns
|
// adding different interface to container ns
|
||||||
containerVethName += "0"
|
containerVethName += "0"
|
||||||
})
|
})
|
||||||
It("returns useful error", func() {
|
It("returns useful error", func() {
|
||||||
_ = containerNetNS.Do(func(ns.NetNS) error {
|
_ = containerNetNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
_, _, err := ip.SetupVeth(containerVethName, mtu, "", hostNetNS)
|
_, _, err := ip.SetupVethWithName(containerVethName, hostVethName, mtu, "", hostNetNS)
|
||||||
Expect(err.Error()).To(HavePrefix("container veth name provided"))
|
Expect(err.Error()).To(Equal(fmt.Sprintf("container veth name (%q) peer provided (%q) already exists", containerVethName, hostVethName)))
|
||||||
Expect(err.Error()).To(HaveSuffix("already exists"))
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -197,7 +189,7 @@ var _ = Describe("Link", func() {
|
|||||||
|
|
||||||
Context("when there is no name conflict for the host or container interfaces", func() {
|
Context("when there is no name conflict for the host or container interfaces", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
//adding different interface to container and host ns
|
// adding different interface to container and host ns
|
||||||
containerVethName += "0"
|
containerVethName += "0"
|
||||||
rand.Reader = originalRandReader
|
rand.Reader = originalRandReader
|
||||||
})
|
})
|
||||||
@@ -211,7 +203,7 @@ var _ = Describe("Link", func() {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
//verify veths are in different namespaces
|
// verify veths are in different namespaces
|
||||||
_ = containerNetNS.Do(func(ns.NetNS) error {
|
_ = containerNetNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
@@ -290,7 +282,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
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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,37 +106,51 @@ 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()
|
expectedRuleToFind := sc.matchIfaceJumpToChainRule(preRoutingBaseChainName, ifaceChain.Name)
|
||||||
if ifaceMatchRuleErr == nil {
|
// It is safer to exclude the statement matching, avoiding cases where a current statement includes
|
||||||
expectedRuleToFind := sc.matchIfaceJumpToChainRule(preRoutingBaseChainName, ifaceChain.Name)
|
// additional default entries (e.g. counters).
|
||||||
// It is safer to exclude the statement matching, avoiding cases where a current statement includes
|
ruleToFindExcludingStatements := *expectedRuleToFind
|
||||||
// additional default entries (e.g. counters).
|
ruleToFindExcludingStatements.Expr = nil
|
||||||
ruleToFindExcludingStatements := *expectedRuleToFind
|
|
||||||
ruleToFindExcludingStatements.Expr = nil
|
rules, ifaceMatchRuleErr := sc.findPreroutingRule(&ruleToFindExcludingStatements)
|
||||||
rules := currentConfig.LookupRule(&ruleToFindExcludingStatements)
|
if ifaceMatchRuleErr == nil && len(rules) > 0 {
|
||||||
if 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 {
|
|
||||||
ifaceMatchRuleErr = fmt.Errorf("failed to delete iface match rule: %v", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(os.Stderr, "spoofcheck/teardown: unable to detect iface match rule for deletion: %+v", expectedRuleToFind)
|
|
||||||
}
|
}
|
||||||
|
if _, err := sc.configurer.Apply(c); err != nil {
|
||||||
|
ifaceMatchRuleErr = fmt.Errorf("failed to delete iface match rule: %v", err)
|
||||||
|
}
|
||||||
|
// Drop the cache, it should contain deleted rule(s) now
|
||||||
|
sc.rulestore = nil
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(os.Stderr, "spoofcheck/teardown: unable to detect iface match rule for deletion: %+v", expectedRuleToFind)
|
||||||
}
|
}
|
||||||
|
|
||||||
regularChainsConfig := nft.NewConfig()
|
regularChainsConfig := nft.NewConfig()
|
||||||
@@ -135,7 +158,7 @@ func (sc *SpoofChecker) Teardown() error {
|
|||||||
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,11 +21,12 @@ import (
|
|||||||
"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) {
|
||||||
@@ -181,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))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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()
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"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"
|
||||||
@@ -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,9 +1,10 @@
|
|||||||
// 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
|
//
|
||||||
// A test should wait for this line, parse it
|
// 127.0.0.1:xxxxx
|
||||||
// and may then attempt to connect.
|
// A test should wait for this line, parse it
|
||||||
|
// and may then attempt to connect.
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -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())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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 := os.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() {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const (
|
|||||||
MaxDHCPLen = 576
|
MaxDHCPLen = 576
|
||||||
)
|
)
|
||||||
|
|
||||||
//Send the Discovery Packet to the Broadcast Channel
|
// Send the Discovery Packet to the Broadcast Channel
|
||||||
func DhcpSendDiscoverPacket(c *dhcp4client.Client, options dhcp4.Options) (dhcp4.Packet, error) {
|
func DhcpSendDiscoverPacket(c *dhcp4client.Client, options dhcp4.Options) (dhcp4.Packet, error) {
|
||||||
discoveryPacket := c.DiscoverPacket()
|
discoveryPacket := c.DiscoverPacket()
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ func DhcpSendDiscoverPacket(c *dhcp4client.Client, options dhcp4.Options) (dhcp4
|
|||||||
return discoveryPacket, c.SendPacket(discoveryPacket)
|
return discoveryPacket, c.SendPacket(discoveryPacket)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Send Request Based On the offer Received.
|
// Send Request Based On the offer Received.
|
||||||
func DhcpSendRequest(c *dhcp4client.Client, options dhcp4.Options, offerPacket *dhcp4.Packet) (dhcp4.Packet, error) {
|
func DhcpSendRequest(c *dhcp4client.Client, options dhcp4.Options, offerPacket *dhcp4.Packet) (dhcp4.Packet, error) {
|
||||||
requestPacket := c.RequestPacket(offerPacket)
|
requestPacket := c.RequestPacket(offerPacket)
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ func DhcpSendRequest(c *dhcp4client.Client, options dhcp4.Options, offerPacket *
|
|||||||
return requestPacket, c.SendPacket(requestPacket)
|
return requestPacket, c.SendPacket(requestPacket)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Send Decline to the received acknowledgement.
|
// Send Decline to the received acknowledgement.
|
||||||
func DhcpSendDecline(c *dhcp4client.Client, acknowledgementPacket *dhcp4.Packet, options dhcp4.Options) (dhcp4.Packet, error) {
|
func DhcpSendDecline(c *dhcp4client.Client, acknowledgementPacket *dhcp4.Packet, options dhcp4.Options) (dhcp4.Packet, error) {
|
||||||
declinePacket := c.DeclinePacket(acknowledgementPacket)
|
declinePacket := c.DeclinePacket(acknowledgementPacket)
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ func DhcpSendDecline(c *dhcp4client.Client, acknowledgementPacket *dhcp4.Packet,
|
|||||||
return declinePacket, c.SendPacket(declinePacket)
|
return declinePacket, c.SendPacket(declinePacket)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Lets do a Full DHCP Request.
|
// Lets do a Full DHCP Request.
|
||||||
func DhcpRequest(c *dhcp4client.Client, options dhcp4.Options) (bool, dhcp4.Packet, error) {
|
func DhcpRequest(c *dhcp4client.Client, options dhcp4.Options) (bool, dhcp4.Packet, error) {
|
||||||
discoveryPacket, err := DhcpSendDiscoverPacket(c, options)
|
discoveryPacket, err := DhcpSendDiscoverPacket(c, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -91,8 +91,8 @@ func DhcpRequest(c *dhcp4client.Client, options dhcp4.Options) (bool, dhcp4.Pack
|
|||||||
return true, acknowledgement, nil
|
return true, acknowledgement, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//Renew a lease backed on the Acknowledgement Packet.
|
// Renew a lease backed on the Acknowledgement Packet.
|
||||||
//Returns Successful, The AcknoledgementPacket, Any Errors
|
// Returns Successful, The AcknoledgementPacket, Any Errors
|
||||||
func DhcpRenew(c *dhcp4client.Client, acknowledgement dhcp4.Packet, options dhcp4.Options) (bool, dhcp4.Packet, error) {
|
func DhcpRenew(c *dhcp4client.Client, acknowledgement dhcp4.Packet, options dhcp4.Options) (bool, dhcp4.Packet, error) {
|
||||||
renewRequest := c.RenewalRequestPacket(&acknowledgement)
|
renewRequest := c.RenewalRequestPacket(&acknowledgement)
|
||||||
|
|
||||||
@@ -120,8 +120,8 @@ func DhcpRenew(c *dhcp4client.Client, acknowledgement dhcp4.Packet, options dhcp
|
|||||||
return true, newAcknowledgement, nil
|
return true, newAcknowledgement, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//Release a lease backed on the Acknowledgement Packet.
|
// Release a lease backed on the Acknowledgement Packet.
|
||||||
//Returns Any Errors
|
// Returns Any Errors
|
||||||
func DhcpRelease(c *dhcp4client.Client, acknowledgement dhcp4.Packet, options dhcp4.Options) error {
|
func DhcpRelease(c *dhcp4client.Client, acknowledgement dhcp4.Packet, options dhcp4.Options) error {
|
||||||
release := c.ReleasePacket(&acknowledgement)
|
release := c.ReleasePacket(&acknowledgement)
|
||||||
|
|
||||||
|
|||||||
@@ -30,9 +30,10 @@ import (
|
|||||||
"syscall"
|
"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")
|
||||||
@@ -55,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.
|
||||||
@@ -114,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)
|
||||||
@@ -166,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)
|
||||||
@@ -195,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 := os.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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -25,20 +25,18 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"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"
|
|
||||||
|
|
||||||
"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) {
|
||||||
@@ -50,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{}
|
||||||
|
|
||||||
@@ -120,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())
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -202,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
|
||||||
@@ -273,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
|
||||||
})
|
})
|
||||||
@@ -316,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
|
||||||
})
|
})
|
||||||
@@ -334,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())
|
||||||
@@ -363,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
|
||||||
@@ -384,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()
|
||||||
@@ -483,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() {
|
||||||
@@ -493,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
|
||||||
@@ -517,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
|
||||||
@@ -596,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
|
||||||
@@ -79,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, ";") {
|
||||||
@@ -94,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)
|
||||||
}
|
}
|
||||||
@@ -119,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
|
||||||
}
|
}
|
||||||
@@ -136,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
|
||||||
@@ -206,7 +209,7 @@ func (l *DHCPLease) Check() {
|
|||||||
l.check <- struct{}{}
|
l.check <- struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *DHCPLease) getOptionsWithClientId() dhcp4.Options {
|
func (l *DHCPLease) getOptionsWithClientID() dhcp4.Options {
|
||||||
opts := make(dhcp4.Options)
|
opts := make(dhcp4.Options)
|
||||||
opts[dhcp4.OptionClientIdentifier] = []byte(l.clientID)
|
opts[dhcp4.OptionClientIdentifier] = []byte(l.clientID)
|
||||||
// client identifier's first byte is "type"
|
// client identifier's first byte is "type"
|
||||||
@@ -217,7 +220,7 @@ func (l *DHCPLease) getOptionsWithClientId() dhcp4.Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *DHCPLease) getAllOptions() dhcp4.Options {
|
func (l *DHCPLease) getAllOptions() dhcp4.Options {
|
||||||
opts := l.getOptionsWithClientId()
|
opts := l.getOptionsWithClientID()
|
||||||
|
|
||||||
for k, v := range l.optsProviding {
|
for k, v := range l.optsProviding {
|
||||||
opts[k] = v
|
opts[k] = v
|
||||||
@@ -231,7 +234,7 @@ func (l *DHCPLease) getAllOptions() dhcp4.Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
@@ -302,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
|
||||||
@@ -359,7 +362,7 @@ 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
|
||||||
}
|
}
|
||||||
@@ -388,13 +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 := l.getOptionsWithClientId()
|
opts := l.getOptionsWithClientID()
|
||||||
|
|
||||||
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")
|
||||||
@@ -424,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
|
||||||
@@ -448,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 {
|
||||||
@@ -482,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,27 +118,20 @@ 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 {
|
||||||
// Plugin must return result in same version as specified in netconf
|
// Plugin must return result in same version as specified in netconf
|
||||||
versionDecoder := &version.ConfigDecoder{}
|
versionDecoder := &version.ConfigDecoder{}
|
||||||
//confVersion, err := versionDecoder.Decode(args.StdinData)
|
// confVersion, err := versionDecoder.Decode(args.StdinData)
|
||||||
_, err := versionDecoder.Decode(args.StdinData)
|
_, err := versionDecoder.Decode(args.StdinData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
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,10 +67,8 @@ 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")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -24,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"
|
||||||
|
|
||||||
@@ -44,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,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
|
||||||
}
|
}
|
||||||
@@ -76,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 = os.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
|
||||||
}
|
}
|
||||||
@@ -93,7 +95,7 @@ func (s *Store) LastReservedIP(rangeID string) (net.IP, error) {
|
|||||||
return net.ParseIP(string(data)), nil
|
return net.ParseIP(string(data)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) FindByKey(id string, ifname string, match string) (bool, error) {
|
func (s *Store) FindByKey(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 {
|
||||||
@@ -110,27 +112,25 @@ 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() {
|
||||||
@@ -149,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
|
||||||
}
|
}
|
||||||
@@ -198,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
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ var _ = Describe("Lock Operations", func() {
|
|||||||
|
|
||||||
// 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())
|
||||||
|
|||||||
@@ -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,7 +63,7 @@ func (s *FakeStore) LastReservedIP(rangeID string) (net.IP, error) {
|
|||||||
return ip, nil
|
return ip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FakeStore) ReleaseByID(id string, ifname string) error {
|
func (s *FakeStore) ReleaseByID(id string, _ string) error {
|
||||||
toDelete := []string{}
|
toDelete := []string{}
|
||||||
for k, v := range s.ipMap {
|
for k, v := range s.ipMap {
|
||||||
if v == id {
|
if v == id {
|
||||||
@@ -76,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 {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ func parseResolvConf(filename string) (*types.DNS, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
defer fp.Close()
|
||||||
|
|
||||||
dns := types.DNS{}
|
dns := types.DNS{}
|
||||||
scanner := bufio.NewScanner(fp)
|
scanner := bufio.NewScanner(fp)
|
||||||
|
|||||||
@@ -17,9 +17,10 @@ package main
|
|||||||
import (
|
import (
|
||||||
"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() {
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ import (
|
|||||||
"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"
|
||||||
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/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"
|
||||||
@@ -57,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 := os.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(`{
|
||||||
@@ -114,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},
|
||||||
@@ -166,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 := os.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(`{
|
||||||
@@ -310,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
|
||||||
@@ -330,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
|
||||||
@@ -356,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 := os.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(`{
|
||||||
@@ -397,7 +397,7 @@ var _ = Describe("host-local Operations", func() {
|
|||||||
contents, err := os.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 = os.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 {
|
||||||
@@ -504,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() {
|
||||||
@@ -546,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 := os.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(`{
|
||||||
@@ -594,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 := os.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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +82,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
// Store all requested IPs in a map, so we can easily remove ones we use
|
// Store all requested IPs in a map, so we can easily remove ones we use
|
||||||
// and error if some remain
|
// and error if some remain
|
||||||
requestedIPs := map[string]net.IP{} //net.IP cannot be a key
|
requestedIPs := map[string]net.IP{} // net.IP cannot be a key
|
||||||
|
|
||||||
for _, ip := range ipamConf.IPArgs {
|
for _, ip := range ipamConf.IPArgs {
|
||||||
requestedIPs[ip.String()] = ip
|
requestedIPs[ip.String()] = ip
|
||||||
|
|||||||
@@ -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")},
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"sort"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -46,17 +47,20 @@ const defaultBrName = "cni0"
|
|||||||
|
|
||||||
type NetConf struct {
|
type NetConf struct {
|
||||||
types.NetConf
|
types.NetConf
|
||||||
BrName string `json:"bridge"`
|
BrName string `json:"bridge"`
|
||||||
IsGW bool `json:"isGateway"`
|
IsGW bool `json:"isGateway"`
|
||||||
IsDefaultGW bool `json:"isDefaultGateway"`
|
IsDefaultGW bool `json:"isDefaultGateway"`
|
||||||
ForceAddress bool `json:"forceAddress"`
|
ForceAddress bool `json:"forceAddress"`
|
||||||
IPMasq bool `json:"ipMasq"`
|
IPMasq bool `json:"ipMasq"`
|
||||||
MTU int `json:"mtu"`
|
MTU int `json:"mtu"`
|
||||||
HairpinMode bool `json:"hairpinMode"`
|
HairpinMode bool `json:"hairpinMode"`
|
||||||
PromiscMode bool `json:"promiscMode"`
|
PromiscMode bool `json:"promiscMode"`
|
||||||
Vlan int `json:"vlan"`
|
Vlan int `json:"vlan"`
|
||||||
MacSpoofChk bool `json:"macspoofchk,omitempty"`
|
VlanTrunk []*VlanTrunk `json:"vlanTrunk,omitempty"`
|
||||||
EnableDad bool `json:"enabledad,omitempty"`
|
PreserveDefaultVlan bool `json:"preserveDefaultVlan"`
|
||||||
|
MacSpoofChk bool `json:"macspoofchk,omitempty"`
|
||||||
|
EnableDad bool `json:"enabledad,omitempty"`
|
||||||
|
DisableContainerInterface bool `json:"disableContainerInterface,omitempty"`
|
||||||
|
|
||||||
Args struct {
|
Args struct {
|
||||||
Cni BridgeArgs `json:"cni,omitempty"`
|
Cni BridgeArgs `json:"cni,omitempty"`
|
||||||
@@ -65,7 +69,14 @@ type NetConf struct {
|
|||||||
Mac string `json:"mac,omitempty"`
|
Mac string `json:"mac,omitempty"`
|
||||||
} `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 +105,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 +114,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 +148,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 +378,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 +392,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 +411,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 +448,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,16 +463,45 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupBridge(n *NetConf) (*netlink.Bridge, *current.Interface, error) {
|
func setupBridge(n *NetConf) (*netlink.Bridge, *current.Interface, error) {
|
||||||
vlanFiltering := n.Vlan != 0
|
vlanFiltering := false
|
||||||
|
if n.Vlan != 0 || n.VlanTrunk != nil {
|
||||||
|
vlanFiltering = true
|
||||||
|
}
|
||||||
// create bridge if necessary
|
// create bridge if necessary
|
||||||
br, err := ensureBridge(n.BrName, n.MTU, n.PromiscMode, vlanFiltering)
|
br, err := ensureBridge(n.BrName, n.MTU, n.PromiscMode, vlanFiltering)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -407,7 +522,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 {
|
||||||
@@ -416,12 +531,16 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
isLayer3 := n.IPAM.Type != ""
|
isLayer3 := n.IPAM.Type != ""
|
||||||
|
|
||||||
|
if isLayer3 && n.DisableContainerInterface {
|
||||||
|
return fmt.Errorf("cannot use IPAM when DisableContainerInterface flag is set")
|
||||||
|
}
|
||||||
|
|
||||||
if n.IsDefaultGW {
|
if n.IsDefaultGW {
|
||||||
n.IsGW = true
|
n.IsGW = true
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
@@ -435,7 +554,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
|
||||||
}
|
}
|
||||||
@@ -509,32 +628,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
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,12 +681,13 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if !n.DisableContainerInterface {
|
||||||
if err := netns.Do(func(_ ns.NetNS) error {
|
if err := netns.Do(func(_ ns.NetNS) error {
|
||||||
link, err := netlink.LinkByName(args.IfName)
|
link, err := netlink.LinkByName(args.IfName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to retrieve link: %v", err)
|
return fmt.Errorf("failed to retrieve link: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If layer 2 we still need to set the container veth to up
|
// If layer 2 we still need to set the container veth to up
|
||||||
if err = netlink.LinkSetUp(link); err != nil {
|
if err = netlink.LinkSetUp(link); err != nil {
|
||||||
return fmt.Errorf("failed to set %q up: %v", args.IfName, err)
|
return fmt.Errorf("failed to set %q up: %v", args.IfName, err)
|
||||||
@@ -583,23 +698,28 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var hostVeth netlink.Link
|
hostVeth, err := netlink.LinkByName(hostInterface.Name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// check bridge port state
|
if !n.DisableContainerInterface {
|
||||||
retries := []int{0, 50, 500, 1000, 1000}
|
// check bridge port state
|
||||||
for idx, sleep := range retries {
|
retries := []int{0, 50, 500, 1000, 1000}
|
||||||
time.Sleep(time.Duration(sleep) * time.Millisecond)
|
for idx, sleep := range retries {
|
||||||
|
time.Sleep(time.Duration(sleep) * time.Millisecond)
|
||||||
|
|
||||||
hostVeth, err = netlink.LinkByName(hostInterface.Name)
|
hostVeth, err = netlink.LinkByName(hostInterface.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if hostVeth.Attrs().OperState == netlink.OperUp {
|
if hostVeth.Attrs().OperState == netlink.OperUp {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if idx == len(retries)-1 {
|
if idx == len(retries)-1 {
|
||||||
return fmt.Errorf("bridge port in error state: %s", hostVeth.Attrs().OperState)
|
return fmt.Errorf("bridge port in error state: %s", hostVeth.Attrs().OperState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -620,7 +740,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use incoming DNS settings if provided, otherwise use the
|
// Use incoming DNS settings if provided, otherwise use the
|
||||||
// settings that were already configued by the IPAM plugin
|
// settings that were already configured by the IPAM plugin
|
||||||
if dnsConfSet(n.DNS) {
|
if dnsConfSet(n.DNS) {
|
||||||
result.DNS = n.DNS
|
result.DNS = n.DNS
|
||||||
}
|
}
|
||||||
@@ -670,7 +790,6 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times
|
// 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.
|
// so don't return an error if the device is already removed.
|
||||||
@@ -720,7 +839,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 ")
|
||||||
@@ -745,7 +863,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
|
||||||
@@ -777,7 +894,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
|
||||||
@@ -821,7 +937,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
|
||||||
@@ -850,7 +965,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
|
||||||
@@ -957,7 +1071,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
|
||||||
@@ -968,11 +1082,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)
|
||||||
|
})
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ import (
|
|||||||
|
|
||||||
"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"
|
||||||
@@ -32,12 +35,7 @@ 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 (
|
||||||
@@ -53,10 +51,11 @@ type Net struct {
|
|||||||
Type string `json:"type,omitempty"`
|
Type string `json:"type,omitempty"`
|
||||||
BrName string `json:"bridge"`
|
BrName string `json:"bridge"`
|
||||||
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"`
|
||||||
@@ -66,21 +65,25 @@ type Net struct {
|
|||||||
// testCase defines the CNI network configuration and the expected
|
// testCase defines the CNI network configuration and the expected
|
||||||
// bridge addresses for a test case.
|
// bridge addresses for a test case.
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
cniVersion string // CNI Version
|
cniVersion string // CNI Version
|
||||||
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
|
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
|
||||||
ipMasq bool
|
vlanTrunk []*VlanTrunk
|
||||||
macspoofchk bool
|
removeDefaultVlan bool
|
||||||
AddErr020 string
|
ipMasq bool
|
||||||
DelErr020 string
|
macspoofchk bool
|
||||||
AddErr010 string
|
disableContIface bool
|
||||||
DelErr010 string
|
|
||||||
|
AddErr020 string
|
||||||
|
DelErr020 string
|
||||||
|
AddErr010 string
|
||||||
|
DelErr010 string
|
||||||
|
|
||||||
envArgs string // CNI_ARGS
|
envArgs string // CNI_ARGS
|
||||||
runtimeConfig struct {
|
runtimeConfig struct {
|
||||||
@@ -130,9 +133,32 @@ 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`
|
||||||
|
|
||||||
|
disableContainerInterface = `,
|
||||||
|
"disableContainerInterface": true`
|
||||||
|
|
||||||
ipamStartStr = `,
|
ipamStartStr = `,
|
||||||
"ipam": {
|
"ipam": {
|
||||||
"type": "host-local"`
|
"type": "host-local"`
|
||||||
@@ -192,7 +218,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()
|
||||||
}
|
}
|
||||||
@@ -206,6 +253,10 @@ func (tc testCase) netConfJSON(dataDir string) string {
|
|||||||
conf += fmt.Sprintf(macspoofchkFormat, tc.macspoofchk)
|
conf += fmt.Sprintf(macspoofchkFormat, tc.macspoofchk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tc.disableContIface {
|
||||||
|
conf += disableContainerInterface
|
||||||
|
}
|
||||||
|
|
||||||
if !tc.isLayer2 {
|
if !tc.isLayer2 {
|
||||||
conf += netDefault
|
conf += netDefault
|
||||||
if tc.subnet != "" || tc.ranges != nil {
|
if tc.subnet != "" || tc.ranges != nil {
|
||||||
@@ -285,7 +336,7 @@ var counter uint
|
|||||||
// arguments for a test case.
|
// arguments for a test case.
|
||||||
func (tc testCase) createCmdArgs(targetNS ns.NetNS, dataDir string) *skel.CmdArgs {
|
func (tc testCase) createCmdArgs(targetNS ns.NetNS, dataDir string) *skel.CmdArgs {
|
||||||
conf := tc.netConfJSON(dataDir)
|
conf := tc.netConfJSON(dataDir)
|
||||||
//defer func() { counter += 1 }()
|
// defer func() { counter += 1 }()
|
||||||
return &skel.CmdArgs{
|
return &skel.CmdArgs{
|
||||||
ContainerID: fmt.Sprintf("dummy-%d", counter),
|
ContainerID: fmt.Sprintf("dummy-%d", counter),
|
||||||
Netns: targetNS.Path(),
|
Netns: targetNS.Path(),
|
||||||
@@ -297,18 +348,17 @@ 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())
|
||||||
|
|
||||||
// TODO Don't we need to use the same counter as before?
|
// TODO Don't we need to use the same counter as before?
|
||||||
//defer func() { counter += 1 }()
|
// defer func() { counter += 1 }()
|
||||||
return &skel.CmdArgs{
|
return &skel.CmdArgs{
|
||||||
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,9 +462,9 @@ func countIPAMIPs(path string) (int, error) {
|
|||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -435,10 +485,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 {
|
||||||
@@ -485,9 +537,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))
|
||||||
@@ -527,13 +579,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)
|
||||||
@@ -544,7 +599,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())
|
||||||
@@ -558,16 +613,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)
|
||||||
@@ -582,6 +637,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
|
||||||
@@ -609,14 +686,16 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(link.Attrs().Name).To(Equal(IFNAME))
|
Expect(link.Attrs().Name).To(Equal(IFNAME))
|
||||||
Expect(link).To(BeAssignableToTypeOf(&netlink.Veth{}))
|
Expect(link).To(BeAssignableToTypeOf(&netlink.Veth{}))
|
||||||
|
assertContainerInterfaceLinkState(&tc, link)
|
||||||
|
|
||||||
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(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
assertIPv6Addresses(&tc, addrs, expCIDRsV6)
|
||||||
|
|
||||||
// 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.
|
||||||
var foundAddrs int
|
var foundAddrs int
|
||||||
@@ -650,7 +729,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
|
||||||
@@ -660,9 +739,18 @@ 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 assertContainerInterfaceLinkState(tc *testCase, link netlink.Link) {
|
||||||
|
linkState := int(link.Attrs().OperState)
|
||||||
|
if tc.disableContIface {
|
||||||
|
Expect(linkState).ToNot(Equal(netlink.OperUp))
|
||||||
|
} else {
|
||||||
|
Expect(linkState).To(Equal(netlink.OperUp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
@@ -689,9 +777,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.
|
||||||
@@ -726,7 +814,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
|
||||||
@@ -790,9 +878,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))
|
||||||
@@ -832,13 +920,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)
|
||||||
@@ -849,7 +940,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())
|
||||||
@@ -863,16 +954,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)
|
||||||
@@ -887,6 +978,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
|
||||||
@@ -913,10 +1026,11 @@ 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(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
assertIPv6Addresses(&tc, addrs, expCIDRsV6)
|
||||||
|
|
||||||
// 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.
|
||||||
var foundAddrs int
|
var foundAddrs int
|
||||||
@@ -950,7 +1064,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
|
||||||
@@ -960,9 +1074,17 @@ 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 assertIPv6Addresses(tc *testCase, addrs []netlink.Addr, expCIDRsV6 []*net.IPNet) {
|
||||||
|
if tc.disableContIface {
|
||||||
|
Expect(addrs).To(BeEmpty())
|
||||||
|
} else {
|
||||||
|
Expect(addrs).To(HaveLen(len(expCIDRsV6) + 1)) // add one for the link-local
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
@@ -989,9 +1111,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.
|
||||||
@@ -1026,7 +1148,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
|
||||||
@@ -1090,9 +1212,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))
|
||||||
@@ -1132,13 +1254,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)
|
||||||
@@ -1149,7 +1274,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())
|
||||||
@@ -1163,16 +1288,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)
|
||||||
@@ -1187,6 +1312,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
|
||||||
@@ -1213,7 +1360,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
|
||||||
@@ -1249,7 +1396,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
|
||||||
@@ -1258,11 +1405,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()
|
||||||
|
|
||||||
@@ -1359,13 +1505,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)
|
||||||
@@ -1376,7 +1525,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())
|
||||||
@@ -1390,7 +1539,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
|
||||||
@@ -1399,9 +1548,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
|
||||||
@@ -1432,7 +1581,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
|
||||||
@@ -1468,7 +1617,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
|
||||||
@@ -1481,6 +1630,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
|
||||||
@@ -1489,22 +1641,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
|
||||||
@@ -1577,7 +1729,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) {
|
||||||
@@ -1619,9 +1770,6 @@ var _ = Describe("bridge Operations", func() {
|
|||||||
var originalNS, targetNS ns.NetNS
|
var originalNS, targetNS ns.NetNS
|
||||||
var dataDir string
|
var dataDir string
|
||||||
|
|
||||||
resolvConf, err := newResolvConf()
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
// Create a new NetNS so we don't modify the host
|
// Create a new NetNS so we don't modify the host
|
||||||
var err error
|
var err error
|
||||||
@@ -1645,9 +1793,60 @@ var _ = Describe("bridge Operations", func() {
|
|||||||
Expect(testutils.UnmountNS(targetNS)).To(Succeed())
|
Expect(testutils.UnmountNS(targetNS)).To(Succeed())
|
||||||
})
|
})
|
||||||
|
|
||||||
AfterSuite(func() {
|
var (
|
||||||
deleteResolvConf(resolvConf)
|
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()
|
||||||
@@ -1781,6 +1980,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
|
||||||
@@ -1831,6 +2061,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{
|
||||||
@@ -1938,7 +2173,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 != "" {
|
||||||
@@ -1948,7 +2183,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)
|
||||||
@@ -1996,8 +2231,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{
|
||||||
@@ -2019,9 +2252,9 @@ 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")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
@@ -2257,6 +2490,36 @@ var _ = Describe("bridge Operations", func() {
|
|||||||
return nil
|
return nil
|
||||||
})).To(Succeed())
|
})).To(Succeed())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It(fmt.Sprintf("[%s] should fail when both IPAM and DisableContainerInterface are set", ver), func() {
|
||||||
|
Expect(originalNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
tc := testCase{
|
||||||
|
cniVersion: ver,
|
||||||
|
subnet: "10.1.2.0/24",
|
||||||
|
disableContIface: true,
|
||||||
|
}
|
||||||
|
args := tc.createCmdArgs(targetNS, dataDir)
|
||||||
|
Expect(cmdAdd(args)).To(HaveOccurred())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})).To(Succeed())
|
||||||
|
})
|
||||||
|
|
||||||
|
It(fmt.Sprintf("[%s] should set the container veth peer state down", ver), func() {
|
||||||
|
Expect(originalNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
tc := testCase{
|
||||||
|
cniVersion: ver,
|
||||||
|
disableContIface: true,
|
||||||
|
isLayer2: true,
|
||||||
|
AddErr020: "cannot convert: no valid IP addresses",
|
||||||
|
AddErr010: "cannot convert: no valid IP addresses",
|
||||||
|
}
|
||||||
|
cmdAddDelTest(originalNS, targetNS, tc, dataDir)
|
||||||
|
return nil
|
||||||
|
})).To(Succeed())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
It("check vlan id when loading net conf", func() {
|
It("check vlan id when loading net conf", func() {
|
||||||
@@ -2286,7 +2549,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))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -41,8 +40,7 @@ func parseNetConf(bytes []byte) (*types.NetConf, error) {
|
|||||||
return conf, nil
|
return conf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createDummy(conf *types.NetConf, ifName string, netns ns.NetNS) (*current.Interface, error) {
|
func createDummy(ifName string, netns ns.NetNS) (*current.Interface, error) {
|
||||||
|
|
||||||
dummy := ¤t.Interface{}
|
dummy := ¤t.Interface{}
|
||||||
|
|
||||||
dm := &netlink.Dummy{
|
dm := &netlink.Dummy{
|
||||||
@@ -92,7 +90,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
defer netns.Close()
|
defer netns.Close()
|
||||||
|
|
||||||
dummyInterface, err := createDummy(conf, args.IfName, netns)
|
dummyInterface, err := createDummy(args.IfName, netns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -136,12 +134,8 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
result.Interfaces = []*current.Interface{dummyInterface}
|
result.Interfaces = []*current.Interface{dummyInterface}
|
||||||
|
|
||||||
err = netns.Do(func(_ ns.NetNS) error {
|
err = netns.Do(func(_ ns.NetNS) error {
|
||||||
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
|
||||||
}
|
}
|
||||||
@@ -170,7 +164,6 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times
|
// 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.
|
// so don't return an error if the device is already removed.
|
||||||
@@ -245,7 +238,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 {
|
||||||
@@ -262,11 +254,9 @@ func cmdCheck(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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,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
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"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/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
types020 "github.com/containernetworking/cni/pkg/types/020"
|
types020 "github.com/containernetworking/cni/pkg/types/020"
|
||||||
@@ -31,11 +35,6 @@ import (
|
|||||||
"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/plugins/ipam/host-local/backend/allocator"
|
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const MASTER_NAME = "eth0"
|
const MASTER_NAME = "eth0"
|
||||||
@@ -89,7 +88,6 @@ func buildOneConfig(netName string, cniVersion string, orig *Net, prevResult typ
|
|||||||
}
|
}
|
||||||
|
|
||||||
return conf, nil
|
return conf, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type tester interface {
|
type tester interface {
|
||||||
@@ -99,10 +97,12 @@ type tester interface {
|
|||||||
|
|
||||||
type testerBase struct{}
|
type testerBase struct{}
|
||||||
|
|
||||||
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) tester {
|
func newTesterByVersion(version string) tester {
|
||||||
switch {
|
switch {
|
||||||
@@ -122,9 +122,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
|
||||||
}
|
}
|
||||||
@@ -133,9 +133,9 @@ func verify0403(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
|
||||||
}
|
}
|
||||||
@@ -151,7 +151,7 @@ func (t *testerV03x) verifyResult(result types.Result, name string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 *testerV01xOr02x) verifyResult(result types.Result, name string) string {
|
func (t *testerV01xOr02x) verifyResult(result types.Result, _ string) string {
|
||||||
r, err := types020.GetResult(result)
|
r, err := types020.GetResult(result)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
@@ -210,17 +210,11 @@ var _ = Describe("dummy Operations", func() {
|
|||||||
ver := ver
|
ver := ver
|
||||||
|
|
||||||
It(fmt.Sprintf("[%s] creates an dummy link in a non-default namespace", ver), func() {
|
It(fmt.Sprintf("[%s] creates an dummy link in a non-default namespace", ver), func() {
|
||||||
conf := &types.NetConf{
|
|
||||||
CNIVersion: ver,
|
|
||||||
Name: "testConfig",
|
|
||||||
Type: "dummy",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create dummy in other namespace
|
// Create dummy in other namespace
|
||||||
err := originalNS.Do(func(ns.NetNS) error {
|
err := originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
_, err := createDummy(conf, "foobar0", targetNS)
|
_, err := createDummy("foobar0", targetNS)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@@ -292,7 +286,7 @@ var _ = Describe("dummy Operations", func() {
|
|||||||
|
|
||||||
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())
|
||||||
@@ -377,7 +371,7 @@ var _ = Describe("dummy Operations", func() {
|
|||||||
StdinData: []byte(fmt.Sprintf(confFmt, ver)),
|
StdinData: []byte(fmt.Sprintf(confFmt, ver)),
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = originalNS.Do(func(netNS ns.NetNS) error {
|
_ = originalNS.Do(func(_ ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
_, _, err = testutils.CmdAddWithArgs(args, func() error {
|
_, _, err = testutils.CmdAddWithArgs(args, func() error {
|
||||||
|
|||||||
@@ -31,7 +31,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,7 +38,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
sysBusPCI = "/sys/bus/pci/devices"
|
sysBusPCI = "/sys/bus/pci/devices"
|
||||||
|
sysBusAuxiliary = "/sys/bus/auxiliary/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
|
||||||
@@ -56,6 +56,9 @@ type NetConf struct {
|
|||||||
RuntimeConfig struct {
|
RuntimeConfig struct {
|
||||||
DeviceID string `json:"deviceID,omitempty"`
|
DeviceID string `json:"deviceID,omitempty"`
|
||||||
} `json:"runtimeConfig,omitempty"`
|
} `json:"runtimeConfig,omitempty"`
|
||||||
|
|
||||||
|
// for internal use
|
||||||
|
auxDevice string `json:"-"` // Auxiliary device name as appears on Auxiliary bus (/sys/bus/auxiliary)
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -65,6 +68,31 @@ func init() {
|
|||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleDeviceID updates netconf fields with DeviceID runtime config
|
||||||
|
func handleDeviceID(netconf *NetConf) error {
|
||||||
|
deviceID := netconf.RuntimeConfig.DeviceID
|
||||||
|
if deviceID == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if deviceID is a PCI device
|
||||||
|
pciPath := filepath.Join(sysBusPCI, deviceID)
|
||||||
|
if _, err := os.Stat(pciPath); err == nil {
|
||||||
|
netconf.PCIAddr = deviceID
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if deviceID is an Auxiliary device
|
||||||
|
auxPath := filepath.Join(sysBusAuxiliary, deviceID)
|
||||||
|
if _, err := os.Stat(auxPath); err == nil {
|
||||||
|
netconf.PCIAddr = ""
|
||||||
|
netconf.auxDevice = deviceID
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("runtime config DeviceID %s not found or unsupported", deviceID)
|
||||||
|
}
|
||||||
|
|
||||||
func loadConf(bytes []byte) (*NetConf, error) {
|
func loadConf(bytes []byte) (*NetConf, error) {
|
||||||
n := &NetConf{}
|
n := &NetConf{}
|
||||||
var err error
|
var err error
|
||||||
@@ -72,12 +100,12 @@ func loadConf(bytes []byte) (*NetConf, error) {
|
|||||||
return nil, fmt.Errorf("failed to load netconf: %v", err)
|
return nil, fmt.Errorf("failed to load netconf: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n.RuntimeConfig.DeviceID != "" {
|
// Override device with the standardized DeviceID if provided in Runtime Config.
|
||||||
// Override PCI device with the standardized DeviceID provided in Runtime Config.
|
if err := handleDeviceID(n); err != nil {
|
||||||
n.PCIAddr = n.RuntimeConfig.DeviceID
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if n.Device == "" && n.HWAddr == "" && n.KernelPath == "" && n.PCIAddr == "" {
|
if n.Device == "" && n.HWAddr == "" && n.KernelPath == "" && n.PCIAddr == "" && n.auxDevice == "" {
|
||||||
return nil, fmt.Errorf(`specify either "device", "hwaddr", "kernelpath" or "pciBusID"`)
|
return nil, fmt.Errorf(`specify either "device", "hwaddr", "kernelpath" or "pciBusID"`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +133,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
result := ¤t.Result{}
|
result := ¤t.Result{}
|
||||||
var contDev netlink.Link
|
var contDev netlink.Link
|
||||||
if !cfg.DPDKMode {
|
if !cfg.DPDKMode {
|
||||||
hostDev, err := getLink(cfg.Device, cfg.HWAddr, cfg.KernelPath, cfg.PCIAddr)
|
hostDev, err := getLink(cfg.Device, cfg.HWAddr, cfg.KernelPath, cfg.PCIAddr, cfg.auxDevice)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to find host device: %v", err)
|
return fmt.Errorf("failed to find host device: %v", err)
|
||||||
}
|
}
|
||||||
@@ -161,10 +189,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
|
||||||
@@ -205,34 +230,112 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setTempName sets a temporary name for netdevice, returns updated Link object or error
|
||||||
|
// if occurred.
|
||||||
|
func setTempName(dev netlink.Link) (netlink.Link, error) {
|
||||||
|
tempName := fmt.Sprintf("%s%d", "temp_", dev.Attrs().Index)
|
||||||
|
|
||||||
|
// rename to tempName
|
||||||
|
if err := netlink.LinkSetName(dev, tempName); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to rename device %q to %q: %v", dev.Attrs().Name, tempName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get updated Link obj
|
||||||
|
tempDev, err := netlink.LinkByName(tempName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to find %q after rename to %q: %v", dev.Attrs().Name, tempName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempDev, nil
|
||||||
|
}
|
||||||
|
|
||||||
func moveLinkIn(hostDev netlink.Link, containerNs ns.NetNS, ifName string) (netlink.Link, error) {
|
func moveLinkIn(hostDev netlink.Link, containerNs ns.NetNS, ifName string) (netlink.Link, error) {
|
||||||
if err := netlink.LinkSetNsFd(hostDev, int(containerNs.Fd())); err != nil {
|
origLinkFlags := hostDev.Attrs().Flags
|
||||||
return nil, err
|
hostDevName := hostDev.Attrs().Name
|
||||||
|
defaultNs, err := ns.GetCurrentNS()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get host namespace: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Devices can be renamed only when down
|
||||||
|
if err = netlink.LinkSetDown(hostDev); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to set %q down: %v", hostDev.Attrs().Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore original link state in case of error
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
if origLinkFlags&net.FlagUp == net.FlagUp && hostDev != nil {
|
||||||
|
_ = netlink.LinkSetUp(hostDev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
hostDev, err = setTempName(hostDev)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to rename device %q to temporary name: %v", hostDevName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore original netdev name in case of error
|
||||||
|
defer func() {
|
||||||
|
if err != nil && hostDev != nil {
|
||||||
|
_ = netlink.LinkSetName(hostDev, hostDevName)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err = netlink.LinkSetNsFd(hostDev, int(containerNs.Fd())); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to move %q to container ns: %v", hostDev.Attrs().Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var contDev netlink.Link
|
var contDev netlink.Link
|
||||||
if err := containerNs.Do(func(_ ns.NetNS) error {
|
tempDevName := hostDev.Attrs().Name
|
||||||
|
if err = containerNs.Do(func(_ ns.NetNS) error {
|
||||||
var err error
|
var err error
|
||||||
contDev, err = netlink.LinkByName(hostDev.Attrs().Name)
|
contDev, err = netlink.LinkByName(tempDevName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to find %q: %v", hostDev.Attrs().Name, err)
|
return fmt.Errorf("failed to find %q: %v", tempDevName, err)
|
||||||
}
|
|
||||||
// Devices can be renamed only when down
|
|
||||||
if err = netlink.LinkSetDown(contDev); err != nil {
|
|
||||||
return fmt.Errorf("failed to set %q down: %v", hostDev.Attrs().Name, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// move netdev back to host namespace in case of error
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = netlink.LinkSetNsFd(contDev, int(defaultNs.Fd()))
|
||||||
|
// we need to get updated link object as link was moved back to host namepsace
|
||||||
|
_ = defaultNs.Do(func(_ ns.NetNS) error {
|
||||||
|
hostDev, _ = netlink.LinkByName(tempDevName)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// Save host device name into the container device's alias property
|
// Save host device name into the container device's alias property
|
||||||
if err := netlink.LinkSetAlias(contDev, hostDev.Attrs().Name); err != nil {
|
if err = netlink.LinkSetAlias(contDev, hostDevName); err != nil {
|
||||||
return fmt.Errorf("failed to set alias to %q: %v", hostDev.Attrs().Name, err)
|
return fmt.Errorf("failed to set alias to %q: %v", tempDevName, err)
|
||||||
}
|
}
|
||||||
// Rename container device to respect args.IfName
|
// Rename container device to respect args.IfName
|
||||||
if err := netlink.LinkSetName(contDev, ifName); err != nil {
|
if err = netlink.LinkSetName(contDev, ifName); err != nil {
|
||||||
return fmt.Errorf("failed to rename device %q to %q: %v", hostDev.Attrs().Name, ifName, err)
|
return fmt.Errorf("failed to rename device %q to %q: %v", tempDevName, ifName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// restore tempDevName in case of error
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = netlink.LinkSetName(contDev, tempDevName)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// Bring container device up
|
// Bring container device up
|
||||||
if err = netlink.LinkSetUp(contDev); err != nil {
|
if err = netlink.LinkSetUp(contDev); err != nil {
|
||||||
return fmt.Errorf("failed to set %q up: %v", ifName, err)
|
return fmt.Errorf("failed to set %q up: %v", ifName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bring device down in case of error
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = netlink.LinkSetDown(contDev)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// Retrieve link again to get up-to-date name and attributes
|
// Retrieve link again to get up-to-date name and attributes
|
||||||
contDev, err = netlink.LinkByName(ifName)
|
contDev, err = netlink.LinkByName(ifName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -253,11 +356,14 @@ func moveLinkOut(containerNs ns.NetNS, ifName string) error {
|
|||||||
}
|
}
|
||||||
defer defaultNs.Close()
|
defer defaultNs.Close()
|
||||||
|
|
||||||
return containerNs.Do(func(_ ns.NetNS) error {
|
var tempName string
|
||||||
|
var origDev netlink.Link
|
||||||
|
err = containerNs.Do(func(_ ns.NetNS) error {
|
||||||
dev, err := netlink.LinkByName(ifName)
|
dev, err := netlink.LinkByName(ifName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to find %q: %v", ifName, err)
|
return fmt.Errorf("failed to find %q: %v", ifName, err)
|
||||||
}
|
}
|
||||||
|
origDev = dev
|
||||||
|
|
||||||
// Devices can be renamed only when down
|
// Devices can be renamed only when down
|
||||||
if err = netlink.LinkSetDown(dev); err != nil {
|
if err = netlink.LinkSetDown(dev); err != nil {
|
||||||
@@ -275,16 +381,49 @@ func moveLinkOut(containerNs ns.NetNS, ifName string) error {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Rename the device to its original name from the host namespace
|
newLink, err := setTempName(dev)
|
||||||
if err = netlink.LinkSetName(dev, dev.Attrs().Alias); err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to restore %q to original name %q: %v", ifName, dev.Attrs().Alias, err)
|
return fmt.Errorf("failed to rename device %q to temporary name: %v", ifName, err)
|
||||||
}
|
}
|
||||||
|
dev = newLink
|
||||||
|
tempName = dev.Attrs().Name
|
||||||
|
|
||||||
if err = netlink.LinkSetNsFd(dev, int(defaultNs.Fd())); err != nil {
|
if err = netlink.LinkSetNsFd(dev, int(defaultNs.Fd())); err != nil {
|
||||||
return fmt.Errorf("failed to move %q to host netns: %v", dev.Attrs().Alias, err)
|
return fmt.Errorf("failed to move %q to host netns: %v", tempName, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename the device to its original name from the host namespace
|
||||||
|
tempDev, err := netlink.LinkByName(tempName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to find %q in host namespace: %v", tempName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = netlink.LinkSetName(tempDev, tempDev.Attrs().Alias); err != nil {
|
||||||
|
// move device back to container ns so it may be retired
|
||||||
|
defer func() {
|
||||||
|
_ = netlink.LinkSetNsFd(tempDev, int(containerNs.Fd()))
|
||||||
|
_ = containerNs.Do(func(_ ns.NetNS) error {
|
||||||
|
lnk, err := netlink.LinkByName(tempName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_ = netlink.LinkSetName(lnk, ifName)
|
||||||
|
if origDev.Attrs().Flags&net.FlagUp == net.FlagUp {
|
||||||
|
_ = netlink.LinkSetUp(lnk)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}()
|
||||||
|
return fmt.Errorf("failed to restore %q to original name %q: %v", tempName, tempDev.Attrs().Alias, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasDpdkDriver(pciaddr string) (bool, error) {
|
func hasDpdkDriver(pciaddr string) (bool, error) {
|
||||||
@@ -320,45 +459,46 @@ func printLink(dev netlink.Link, cniVersion string, containerNs ns.NetNS) error
|
|||||||
return types.PrintResult(&result, cniVersion)
|
return types.PrintResult(&result, cniVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLink(devname, hwaddr, kernelpath, pciaddr string) (netlink.Link, error) {
|
func linkFromPath(path string) (netlink.Link, error) {
|
||||||
links, err := netlink.LinkList()
|
entries, err := os.ReadDir(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to list node links: %v", err)
|
return nil, fmt.Errorf("failed to read directory %s: %q", path, err)
|
||||||
}
|
}
|
||||||
|
if len(entries) > 0 {
|
||||||
|
// grab the first net device
|
||||||
|
return netlink.LinkByName(entries[0].Name())
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("failed to find network device in path %s", path)
|
||||||
|
}
|
||||||
|
|
||||||
if len(devname) > 0 {
|
func getLink(devname, hwaddr, kernelpath, pciaddr string, auxDev string) (netlink.Link, error) {
|
||||||
|
switch {
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
links, err := netlink.LinkList()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to list node links: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
for _, link := range links {
|
for _, link := range links {
|
||||||
if bytes.Equal(link.Attrs().HardwareAddr, hwAddr) {
|
if bytes.Equal(link.Attrs().HardwareAddr, hwAddr) {
|
||||||
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")
|
||||||
entries, err := os.ReadDir(netDir)
|
return linkFromPath(netDir)
|
||||||
if err != nil {
|
case len(pciaddr) > 0:
|
||||||
return nil, fmt.Errorf("failed to find network devices at %q", netDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grab the first device from eg /sys/devices/pci0000:00/0000:00:19.0/net
|
|
||||||
for _, entry := range entries {
|
|
||||||
// Make sure it's really an interface
|
|
||||||
for _, l := range links {
|
|
||||||
if entry.Name() == l.Attrs().Name {
|
|
||||||
return l, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if 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")
|
||||||
@@ -368,14 +508,10 @@ func getLink(devname, hwaddr, kernelpath, pciaddr string) (netlink.Link, error)
|
|||||||
}
|
}
|
||||||
netDir = matches[0]
|
netDir = matches[0]
|
||||||
}
|
}
|
||||||
entries, err := os.ReadDir(netDir)
|
return linkFromPath(netDir)
|
||||||
if err != nil {
|
case len(auxDev) > 0:
|
||||||
return nil, fmt.Errorf("failed to read net directory %s: %q", netDir, err)
|
netDir := filepath.Join(sysBusAuxiliary, auxDev, "net")
|
||||||
}
|
return linkFromPath(netDir)
|
||||||
if len(entries) > 0 {
|
|
||||||
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 physical interface")
|
return nil, fmt.Errorf("failed to find physical interface")
|
||||||
@@ -386,7 +522,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
|
||||||
@@ -443,7 +578,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 {
|
||||||
@@ -469,7 +603,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) {
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ import (
|
|||||||
"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"
|
||||||
@@ -30,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 {
|
||||||
@@ -86,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
|
||||||
@@ -103,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 {
|
||||||
@@ -123,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 != "" {
|
||||||
@@ -132,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}}
|
||||||
@@ -149,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 {
|
||||||
@@ -164,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) {
|
||||||
@@ -214,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 {
|
||||||
@@ -224,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 {
|
||||||
@@ -259,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))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,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))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,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))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -664,11 +665,11 @@ var _ = Describe("base functionality", func() {
|
|||||||
Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr))
|
Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr))
|
||||||
Expect(link.Attrs().Flags & net.FlagUp).To(Equal(net.FlagUp))
|
Expect(link.Attrs().Flags & net.FlagUp).To(Equal(net.FlagUp))
|
||||||
|
|
||||||
//get the IP address of the interface in the target namespace
|
// get the IP address of the interface in the target namespace
|
||||||
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
addr := addrs[0].IPNet.String()
|
addr := addrs[0].IPNet.String()
|
||||||
//assert that IP address is what we set
|
// assert that IP address is what we set
|
||||||
Expect(addr).To(Equal(targetIP))
|
Expect(addr).To(Equal(targetIP))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -711,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() {
|
||||||
@@ -868,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) {
|
||||||
@@ -898,6 +898,71 @@ var _ = Describe("base functionality", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It(fmt.Sprintf("Works with a valid %s config on auxiliary device", ver), func() {
|
||||||
|
var origLink netlink.Link
|
||||||
|
ifname := "eth0"
|
||||||
|
|
||||||
|
fs := &fakeFilesystem{
|
||||||
|
dirs: []string{
|
||||||
|
fmt.Sprintf("sys/bus/auxiliary/devices/mlx5_core.sf.4/net/%s", ifname),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
defer fs.use()()
|
||||||
|
|
||||||
|
// prepare ifname in original namespace
|
||||||
|
_ = originalNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
err := netlink.LinkAdd(&netlink.Dummy{
|
||||||
|
LinkAttrs: netlink.LinkAttrs{
|
||||||
|
Name: ifname,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
origLink, err = netlink.LinkByName(ifname)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
err = netlink.LinkSetUp(origLink)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// call CmdAdd
|
||||||
|
cniName := "net1"
|
||||||
|
conf := fmt.Sprintf(`{
|
||||||
|
"cniVersion": "%s",
|
||||||
|
"name": "cni-plugin-host-device-test",
|
||||||
|
"type": "host-device",
|
||||||
|
"runtimeConfig": {"deviceID": %q}
|
||||||
|
}`, ver, "mlx5_core.sf.4")
|
||||||
|
args := &skel.CmdArgs{
|
||||||
|
ContainerID: "dummy",
|
||||||
|
IfName: cniName,
|
||||||
|
Netns: targetNS.Path(),
|
||||||
|
StdinData: []byte(conf),
|
||||||
|
}
|
||||||
|
var resI types.Result
|
||||||
|
err := originalNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
var err error
|
||||||
|
resI, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) })
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
// check that the result was sane
|
||||||
|
t := newTesterByVersion(ver)
|
||||||
|
t.expectInterfaces(resI, cniName, origLink.Attrs().HardwareAddr.String(), targetNS.Path())
|
||||||
|
|
||||||
|
// call CmdDel
|
||||||
|
_ = originalNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
err = testutils.CmdDelWithArgs(args, func() error {
|
||||||
|
return cmdDel(args)
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
It(fmt.Sprintf("Works with a valid %s config with IPAM", ver), func() {
|
It(fmt.Sprintf("Works with a valid %s config with IPAM", ver), func() {
|
||||||
var origLink netlink.Link
|
var origLink netlink.Link
|
||||||
|
|
||||||
@@ -961,11 +1026,11 @@ var _ = Describe("base functionality", func() {
|
|||||||
Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr))
|
Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr))
|
||||||
Expect(link.Attrs().Flags & net.FlagUp).To(Equal(net.FlagUp))
|
Expect(link.Attrs().Flags & net.FlagUp).To(Equal(net.FlagUp))
|
||||||
|
|
||||||
//get the IP address of the interface in the target namespace
|
// get the IP address of the interface in the target namespace
|
||||||
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
addr := addrs[0].IPNet.String()
|
addr := addrs[0].IPNet.String()
|
||||||
//assert that IP address is what we set
|
// assert that IP address is what we set
|
||||||
Expect(addr).To(Equal(targetIP))
|
Expect(addr).To(Equal(targetIP))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -984,7 +1049,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) {
|
||||||
@@ -1149,6 +1214,114 @@ var _ = Describe("base functionality", func() {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It(fmt.Sprintf("Test CmdAdd/Del when additioinal interface alreay exists in container ns with same name. %s config", ver), func() {
|
||||||
|
var (
|
||||||
|
origLink netlink.Link
|
||||||
|
containerLink netlink.Link
|
||||||
|
)
|
||||||
|
|
||||||
|
hostIfname := "eth0"
|
||||||
|
containerAdditionalIfname := "eth0"
|
||||||
|
|
||||||
|
// prepare host device in original namespace
|
||||||
|
_ = originalNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
err := netlink.LinkAdd(&netlink.Dummy{
|
||||||
|
LinkAttrs: netlink.LinkAttrs{
|
||||||
|
Name: hostIfname,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
origLink, err = netlink.LinkByName(hostIfname)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
err = netlink.LinkSetUp(origLink)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// prepare device in container namespace with same name as host device
|
||||||
|
_ = targetNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
err := netlink.LinkAdd(&netlink.Dummy{
|
||||||
|
LinkAttrs: netlink.LinkAttrs{
|
||||||
|
Name: containerAdditionalIfname,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
containerLink, err = netlink.LinkByName(containerAdditionalIfname)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
err = netlink.LinkSetUp(containerLink)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// call CmdAdd
|
||||||
|
cniName := "net1"
|
||||||
|
conf := fmt.Sprintf(`{
|
||||||
|
"cniVersion": "%s",
|
||||||
|
"name": "cni-plugin-host-device-test",
|
||||||
|
"type": "host-device",
|
||||||
|
"device": %q
|
||||||
|
}`, ver, hostIfname)
|
||||||
|
args := &skel.CmdArgs{
|
||||||
|
ContainerID: "dummy",
|
||||||
|
Netns: targetNS.Path(),
|
||||||
|
IfName: cniName,
|
||||||
|
StdinData: []byte(conf),
|
||||||
|
}
|
||||||
|
var resI types.Result
|
||||||
|
err := originalNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
var err error
|
||||||
|
resI, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) })
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
// check that the result was sane
|
||||||
|
t := newTesterByVersion(ver)
|
||||||
|
t.expectInterfaces(resI, cniName, origLink.Attrs().HardwareAddr.String(), targetNS.Path())
|
||||||
|
|
||||||
|
// assert that host device is now in the target namespace and is up
|
||||||
|
_ = targetNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
link, err := netlink.LinkByName(cniName)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr))
|
||||||
|
Expect(link.Attrs().Flags & net.FlagUp).To(Equal(net.FlagUp))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// call CmdDel, expect it to succeed
|
||||||
|
_ = originalNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
err = testutils.CmdDelWithArgs(args, func() error {
|
||||||
|
return cmdDel(args)
|
||||||
|
})
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// assert container interface "eth0" still exists in target namespace and is up
|
||||||
|
err = targetNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
link, err := netlink.LinkByName(containerAdditionalIfname)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(link.Attrs().HardwareAddr).To(Equal(containerLink.Attrs().HardwareAddr))
|
||||||
|
Expect(link.Attrs().Flags & net.FlagUp).To(Equal(net.FlagUp))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
// assert that host device is now back in the original namespace
|
||||||
|
_ = originalNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
_, err := netlink.LinkByName(hostIfname)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1167,7 +1340,7 @@ func (fs *fakeFilesystem) use() func() {
|
|||||||
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()))
|
||||||
}
|
}
|
||||||
@@ -1181,6 +1354,7 @@ func (fs *fakeFilesystem) use() func() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sysBusPCI = path.Join(fs.rootDir, "/sys/bus/pci/devices")
|
sysBusPCI = path.Join(fs.rootDir, "/sys/bus/pci/devices")
|
||||||
|
sysBusAuxiliary = path.Join(fs.rootDir, "/sys/bus/auxiliary/devices")
|
||||||
|
|
||||||
return func() {
|
return func() {
|
||||||
// remove temporary fake fs
|
// remove temporary fake fs
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -36,9 +35,10 @@ import (
|
|||||||
|
|
||||||
type NetConf struct {
|
type NetConf struct {
|
||||||
types.NetConf
|
types.NetConf
|
||||||
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,8 +154,14 @@ func createIpvlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Interf
|
|||||||
Mode: mode,
|
Mode: mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := netlink.LinkAdd(mv); err != nil {
|
if conf.LinkContNs {
|
||||||
return nil, fmt.Errorf("failed to create ipvlan: %v", err)
|
err = netns.Do(func(_ ns.NetNS) error {
|
||||||
|
return netlink.LinkAdd(mv)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if err := netlink.LinkAdd(mv); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create ipvlan: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = netns.Do(func(_ ns.NetNS) error {
|
err = netns.Do(func(_ ns.NetNS) error {
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
@@ -299,7 +334,6 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times
|
// 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.
|
// so don't return an error if the device is already removed.
|
||||||
@@ -319,8 +353,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 +402,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 +440,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 +461,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) {
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"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/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
types020 "github.com/containernetworking/cni/pkg/types/020"
|
types020 "github.com/containernetworking/cni/pkg/types/020"
|
||||||
@@ -29,15 +33,13 @@ import (
|
|||||||
types100 "github.com/containernetworking/cni/pkg/types/100"
|
types100 "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/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"`
|
||||||
@@ -49,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) {
|
||||||
@@ -90,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) {
|
||||||
@@ -139,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())
|
||||||
@@ -204,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 {
|
||||||
@@ -226,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
|
||||||
}
|
}
|
||||||
@@ -238,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())
|
||||||
|
|
||||||
@@ -289,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() {
|
||||||
@@ -301,67 +321,77 @@ 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} {
|
||||||
// Redefine ver inside for scope so real value is picked up by each dynamically defined It()
|
masterInterface := MASTER_NAME
|
||||||
// See Gingkgo's "Patterns for dynamically generating tests" documentation.
|
if inContainer {
|
||||||
ver := ver
|
masterInterface = MASTER_NAME_INCONTAINER
|
||||||
|
}
|
||||||
|
// for _, ver := range testutils.AllSpecVersions {
|
||||||
|
for _, ver := range [...]string{"1.0.0"} {
|
||||||
|
// Redefine ver inside for scope so real value is picked up by each dynamically defined It()
|
||||||
|
// See Gingkgo's "Patterns for dynamically generating tests" documentation.
|
||||||
|
ver := ver
|
||||||
|
isInContainer := inContainer // Tests need a local var with constant value
|
||||||
|
|
||||||
It(fmt.Sprintf("[%s] creates an ipvlan link in a non-default namespace", ver), func() {
|
It(fmt.Sprintf("[%s] creates an ipvlan link in a non-default namespace", ver), func() {
|
||||||
conf := &NetConf{
|
conf := &NetConf{
|
||||||
NetConf: types.NetConf{
|
NetConf: types.NetConf{
|
||||||
CNIVersion: ver,
|
CNIVersion: ver,
|
||||||
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 {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
_, err := createIpvlan(conf, "foobar0", targetNS)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
_, err := createIpvlan(conf, "foobar0", targetNS)
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
return nil
|
|
||||||
|
// Make sure ipvlan link exists in the target namespace
|
||||||
|
err = targetNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
link, err := netlink.LinkByName("foobar0")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(link.Attrs().Name).To(Equal("foobar0"))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
})
|
})
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
It(fmt.Sprintf("[%s] configures and deconfigures an iplvan link with ADD/DEL", ver), func() {
|
||||||
|
conf := fmt.Sprintf(`{
|
||||||
// Make sure ipvlan link exists in the target namespace
|
|
||||||
err = targetNS.Do(func(ns.NetNS) error {
|
|
||||||
defer GinkgoRecover()
|
|
||||||
|
|
||||||
link, err := netlink.LinkByName("foobar0")
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
Expect(link.Attrs().Name).To(Equal("foobar0"))
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
})
|
|
||||||
|
|
||||||
It(fmt.Sprintf("[%s] configures and deconfigures an iplvan link with ADD/DEL", ver), func() {
|
|
||||||
conf := fmt.Sprintf(`{
|
|
||||||
"cniVersion": "%s",
|
"cniVersion": "%s",
|
||||||
"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)
|
||||||
})
|
})
|
||||||
|
|
||||||
if testutils.SpecVersionHasChaining(ver) {
|
if testutils.SpecVersionHasChaining(ver) {
|
||||||
It(fmt.Sprintf("[%s] configures and deconfigures an iplvan link with ADD/DEL when chained", ver), func() {
|
It(fmt.Sprintf("[%s] configures and deconfigures an iplvan link with ADD/DEL when chained", ver), func() {
|
||||||
conf := fmt.Sprintf(`{
|
conf := fmt.Sprintf(`{
|
||||||
"cniVersion": "%s",
|
"cniVersion": "%s",
|
||||||
"name": "mynet",
|
"name": "mynet",
|
||||||
"type": "ipvlan",
|
"type": "ipvlan",
|
||||||
|
"linkInContainer": %t,
|
||||||
"prevResult": {
|
"prevResult": {
|
||||||
"interfaces": [
|
"interfaces": [
|
||||||
{
|
{
|
||||||
@@ -378,84 +408,91 @@ var _ = Describe("ipvlan Operations", func() {
|
|||||||
],
|
],
|
||||||
"routes": []
|
"routes": []
|
||||||
}
|
}
|
||||||
}`, ver, MASTER_NAME)
|
}`, ver, isInContainer, masterInterface)
|
||||||
|
|
||||||
ipvlanAddCheckDelTest(conf, MASTER_NAME, originalNS, targetNS)
|
ipvlanAddCheckDelTest(conf, masterInterface, originalNS, targetNS)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
It(fmt.Sprintf("[%s] deconfigures an unconfigured ipvlan link with DEL", ver), func() {
|
It(fmt.Sprintf("[%s] deconfigures an unconfigured ipvlan link with DEL", ver), func() {
|
||||||
conf := fmt.Sprintf(`{
|
conf := fmt.Sprintf(`{
|
||||||
"cniVersion": "%s",
|
"cniVersion": "%s",
|
||||||
"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",
|
||||||
Netns: targetNS.Path(),
|
Netns: targetNS.Path(),
|
||||||
IfName: "ipvl0",
|
IfName: "ipvl0",
|
||||||
StdinData: []byte(conf),
|
StdinData: []byte(conf),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := originalNS.Do(func(ns.NetNS) error {
|
err := originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
err := testutils.CmdDelWithArgs(args, func() error {
|
err := testutils.CmdDelWithArgs(args, func() error {
|
||||||
return cmdDel(args)
|
return cmdDel(args)
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
})
|
|
||||||
|
|
||||||
It(fmt.Sprintf("[%s] configures and deconfigures a ipvlan link with ADD/DEL, without master config", ver), func() {
|
It(fmt.Sprintf("[%s] configures and deconfigures a ipvlan link with ADD/DEL, without master config", ver), func() {
|
||||||
conf := fmt.Sprintf(`{
|
conf := fmt.Sprintf(`{
|
||||||
"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
|
||||||
defer GinkgoRecover()
|
if isInContainer {
|
||||||
|
currentNs = targetNS
|
||||||
link, err := netlink.LinkByName(MASTER_NAME)
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
err = netlink.LinkSetUp(link)
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
|
|
||||||
var address = &net.IPNet{IP: net.IPv4(192, 0, 0, 1), Mask: net.CIDRMask(24, 32)}
|
|
||||||
var addr = &netlink.Addr{IPNet: address}
|
|
||||||
err = netlink.AddrAdd(link, addr)
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
|
|
||||||
// add default gateway into MASTER
|
|
||||||
dst := &net.IPNet{
|
|
||||||
IP: net.IPv4(0, 0, 0, 0),
|
|
||||||
Mask: net.CIDRMask(0, 0),
|
|
||||||
}
|
}
|
||||||
ip := net.IPv4(192, 0, 0, 254)
|
err := currentNs.Do(func(ns.NetNS) error {
|
||||||
route := netlink.Route{LinkIndex: link.Attrs().Index, Dst: dst, Gw: ip}
|
defer GinkgoRecover()
|
||||||
err = netlink.RouteAdd(&route)
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
|
|
||||||
ipvlanAddCheckDelTest(conf, MASTER_NAME, originalNS, targetNS)
|
link, err := netlink.LinkByName(masterInterface)
|
||||||
})
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
err = netlink.LinkSetUp(link)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
address := &net.IPNet{IP: net.IPv4(192, 0, 0, 1), Mask: net.CIDRMask(24, 32)}
|
||||||
|
addr := &netlink.Addr{IPNet: address}
|
||||||
|
err = netlink.AddrAdd(link, addr)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
// add default gateway into MASTER
|
||||||
|
dst := &net.IPNet{
|
||||||
|
IP: net.IPv4(0, 0, 0, 0),
|
||||||
|
Mask: net.CIDRMask(0, 0),
|
||||||
|
}
|
||||||
|
ip := net.IPv4(192, 0, 0, 254)
|
||||||
|
route := netlink.Route{LinkIndex: link.Attrs().Index, Dst: dst, Gw: ip}
|
||||||
|
err = netlink.RouteAdd(&route)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
ipvlanAddCheckDelTest(conf, masterInterface, originalNS, targetNS)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -27,7 +27,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"
|
||||||
@@ -37,10 +36,11 @@ import (
|
|||||||
|
|
||||||
type NetConf struct {
|
type NetConf struct {
|
||||||
types.NetConf
|
types.NetConf
|
||||||
Master string `json:"master"`
|
Master string `json:"master"`
|
||||||
Mode string `json:"mode"`
|
Mode string `json:"mode"`
|
||||||
MTU int `json:"mtu"`
|
MTU int `json:"mtu"`
|
||||||
Mac string `json:"mac,omitempty"`
|
Mac string `json:"mac,omitempty"`
|
||||||
|
LinkContNs bool `json:"linkInContainer,omitempty"`
|
||||||
|
|
||||||
RuntimeConfig struct {
|
RuntimeConfig struct {
|
||||||
Mac string `json:"mac,omitempty"`
|
Mac string `json:"mac,omitempty"`
|
||||||
@@ -79,13 +79,36 @@ func getDefaultRouteInterfaceName() (string, error) {
|
|||||||
return "", fmt.Errorf("no default route interface found")
|
return "", fmt.Errorf("no default route interface found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadConf(bytes []byte, envArgs string) (*NetConf, string, error) {
|
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 loadConf(args *skel.CmdArgs, envArgs string) (*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)
|
||||||
}
|
}
|
||||||
if n.Master == "" {
|
if n.Master == "" {
|
||||||
defaultRouteInterface, err := getDefaultRouteInterfaceName()
|
defaultRouteInterface, err := getNamespacedDefaultRouteInterfaceName(args.Netns, n.LinkContNs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
@@ -93,7 +116,7 @@ func loadConf(bytes []byte, envArgs string) (*NetConf, string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check existing and MTU of master interface
|
// check existing and MTU of master interface
|
||||||
masterMTU, err := getMTUByName(n.Master)
|
masterMTU, err := getMTUByName(n.Master, args.Netns, n.LinkContNs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
@@ -120,8 +143,24 @@ func loadConf(bytes []byte, envArgs string) (*NetConf, string, error) {
|
|||||||
return n, n.CNIVersion, nil
|
return n, n.CNIVersion, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMTUByName(ifName string) (int, error) {
|
func getMTUByName(ifName string, namespace string, inContainer bool) (int, error) {
|
||||||
link, err := netlink.LinkByName(ifName)
|
var link netlink.Link
|
||||||
|
var err error
|
||||||
|
if inContainer {
|
||||||
|
var netns ns.NetNS
|
||||||
|
netns, err = ns.GetNS(namespace)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("failed to open netns %q: %v", netns, err)
|
||||||
|
}
|
||||||
|
defer netns.Close()
|
||||||
|
|
||||||
|
err = netns.Do(func(_ ns.NetNS) error {
|
||||||
|
link, err = netlink.LinkByName(ifName)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
link, err = netlink.LinkByName(ifName)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@@ -166,7 +205,15 @@ func createMacvlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Inter
|
|||||||
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)
|
||||||
}
|
}
|
||||||
@@ -198,7 +245,16 @@ func createMacvlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Inter
|
|||||||
Mode: mode,
|
Mode: mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := netlink.LinkAdd(mv); err != nil {
|
if conf.LinkContNs {
|
||||||
|
err = netns.Do(func(_ ns.NetNS) error {
|
||||||
|
return netlink.LinkAdd(mv)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if err = netlink.LinkAdd(mv); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create macvlan: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create macvlan: %v", err)
|
return nil, fmt.Errorf("failed to create macvlan: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +284,7 @@ func createMacvlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Inter
|
|||||||
}
|
}
|
||||||
|
|
||||||
func cmdAdd(args *skel.CmdArgs) error {
|
func cmdAdd(args *skel.CmdArgs) error {
|
||||||
n, cniVersion, err := loadConf(args.StdinData, args.Args)
|
n, cniVersion, err := loadConf(args, args.Args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -295,11 +351,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
|
||||||
@@ -329,13 +383,13 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func cmdDel(args *skel.CmdArgs) error {
|
func cmdDel(args *skel.CmdArgs) error {
|
||||||
n, _, err := loadConf(args.StdinData, args.Args)
|
var n NetConf
|
||||||
|
err := json.Unmarshal(args.StdinData, &n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to load netConf: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
isLayer3 := n.IPAM.Type != ""
|
isLayer3 := n.IPAM.Type != ""
|
||||||
|
|
||||||
if isLayer3 {
|
if isLayer3 {
|
||||||
err = ipam.ExecDel(n.IPAM.Type, args.StdinData)
|
err = ipam.ExecDel(n.IPAM.Type, args.StdinData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -357,7 +411,6 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times
|
// 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.
|
// so don't return an error if the device is already removed.
|
||||||
@@ -377,8 +430,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func cmdCheck(args *skel.CmdArgs) error {
|
func cmdCheck(args *skel.CmdArgs) error {
|
||||||
|
n, _, err := loadConf(args, args.Args)
|
||||||
n, _, err := loadConf(args.StdinData, args.Args)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -400,7 +452,7 @@ func cmdCheck(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
// Parse previous result.
|
// Parse previous result.
|
||||||
if n.NetConf.RawPrevResult == nil {
|
if n.NetConf.RawPrevResult == nil {
|
||||||
return fmt.Errorf("Required prevResult missing")
|
return fmt.Errorf("required prevResult missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := version.ParsePrevResult(&n.NetConf); err != nil {
|
if err := version.ParsePrevResult(&n.NetConf); err != nil {
|
||||||
@@ -425,20 +477,26 @@ func cmdCheck(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
// The namespace must be the same as what was configured
|
// The namespace must be the same as what was configured
|
||||||
if args.Netns != contMap.Sandbox {
|
if args.Netns != contMap.Sandbox {
|
||||||
return fmt.Errorf("Sandbox in prevResult %s doesn't match configured netns: %s",
|
return fmt.Errorf("sandbox in prevResult %s doesn't match configured netns: %s",
|
||||||
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
|
||||||
}
|
}
|
||||||
@@ -460,28 +518,30 @@ func cmdCheck(args *skel.CmdArgs) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateCniContainerInterface(intf current.Interface, parentIndex 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
|
||||||
|
|
||||||
if intf.Name == "" {
|
if intf.Name == "" {
|
||||||
return fmt.Errorf("Container interface name missing in prevResult: %v", intf.Name)
|
return fmt.Errorf("container interface name missing in prevResult: %v", intf.Name)
|
||||||
}
|
}
|
||||||
link, err = netlink.LinkByName(intf.Name)
|
link, err = netlink.LinkByName(intf.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Container Interface name in prevResult: %s not found", intf.Name)
|
return fmt.Errorf("container Interface name in prevResult: %s not found", intf.Name)
|
||||||
}
|
}
|
||||||
if intf.Sandbox == "" {
|
if intf.Sandbox == "" {
|
||||||
return fmt.Errorf("Error: Container interface %s should not be in host namespace", link.Attrs().Name)
|
return fmt.Errorf("error: Container interface %s should not be in host namespace", link.Attrs().Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
macv, isMacvlan := link.(*netlink.Macvlan)
|
macv, isMacvlan := link.(*netlink.Macvlan)
|
||||||
if !isMacvlan {
|
if !isMacvlan {
|
||||||
return fmt.Errorf("Error: Container interface %s not of type macvlan", link.Attrs().Name)
|
return fmt.Errorf("error: Container interface %s not of type macvlan", link.Attrs().Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
mode, err := modeFromString(modeExpected)
|
mode, err := modeFromString(modeExpected)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if macv.Mode != mode {
|
if macv.Mode != mode {
|
||||||
currString, err := modeToString(macv.Mode)
|
currString, err := modeToString(macv.Mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -491,12 +551,12 @@ func validateCniContainerInterface(intf current.Interface, parentIndex int, mode
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return fmt.Errorf("Container macvlan mode %s does not match expected value: %s", currString, confString)
|
return fmt.Errorf("container macvlan mode %s does not match expected value: %s", currString, confString)
|
||||||
}
|
}
|
||||||
|
|
||||||
if intf.Mac != "" {
|
if intf.Mac != "" {
|
||||||
if intf.Mac != link.Attrs().HardwareAddr.String() {
|
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)
|
return fmt.Errorf("interface %s Mac %s doesn't match container Mac: %s", intf.Name, intf.Mac, link.Attrs().HardwareAddr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 TestMacvlan(t *testing.T) {
|
func TestMacvlan(t *testing.T) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user