Compare commits
118 Commits
Author | SHA1 | Date | |
---|---|---|---|
6e7fb60738 | |||
7c122fabb4 | |||
e4ca66b414 | |||
abfac4a938 | |||
eded0afca8 | |||
41d548592d | |||
e8c7d9b930 | |||
ba8bc7d0c7 | |||
7f756b411e | |||
3ffc42cdfd | |||
6de8a9853c | |||
9296c5f80a | |||
fec2d62676 | |||
a4fc6f93c7 | |||
d61e7e5e1f | |||
e4950728ce | |||
93d197c455 | |||
c52e02bccf | |||
24b0bf96af | |||
d44bbf28af | |||
8ad0361964 | |||
8324a2e5a4 | |||
a4b80cc634 | |||
3a49cff1f6 | |||
c11ed48733 | |||
fa737f82b2 | |||
e5df283ab3 | |||
cc8b1bd80c | |||
03712a572b | |||
01a94e17c7 | |||
3d1968c152 | |||
a3ccebc6ec | |||
61d078645a | |||
729dd23c40 | |||
a6d6efa5ca | |||
01b3db8e01 | |||
20f31e5e88 | |||
06ba001d84 | |||
deb8ef63f4 | |||
720b1e9811 | |||
bdb6814fe2 | |||
3653221fad | |||
0d2780f0e7 | |||
5def33291f | |||
07bd325095 | |||
7cff5db82a | |||
d924f05e12 | |||
6269f399a5 | |||
5188dc8a19 | |||
675ca92261 | |||
30078e1cfd | |||
acf8ddc8e1 | |||
352c8b7ab5 | |||
11ad58cf20 | |||
d5f9ad99d7 | |||
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 |
2
.github/actions/retest-action/Dockerfile
vendored
2
.github/actions/retest-action/Dockerfile
vendored
@ -1,4 +1,4 @@
|
|||||||
FROM alpine:3.18
|
FROM alpine:3.21
|
||||||
|
|
||||||
RUN apk add --no-cache curl jq
|
RUN apk add --no-cache curl jq
|
||||||
|
|
||||||
|
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@ -21,3 +21,5 @@ updates:
|
|||||||
golang:
|
golang:
|
||||||
patterns:
|
patterns:
|
||||||
- "*"
|
- "*"
|
||||||
|
exclude-patterns:
|
||||||
|
- "github.com/containernetworking/*"
|
||||||
|
1
.github/go-version
vendored
Normal file
1
.github/go-version
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
1.23
|
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: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version-file: .github/go-version
|
||||||
|
|
||||||
|
- 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 -R 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: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version-file: .github/go-version
|
||||||
|
|
||||||
|
- 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 -R 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
|
42
.github/workflows/test.yaml
vendored
42
.github/workflows/test.yaml
vendored
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
name: test
|
name: test
|
||||||
|
|
||||||
on: ["push", "pull_request"]
|
on:
|
||||||
|
pull_request: {}
|
||||||
|
|
||||||
env:
|
env:
|
||||||
GO_VERSION: "1.21"
|
|
||||||
LINUX_ARCHES: "amd64 386 arm arm64 s390x mips64le ppc64le riscv64"
|
LINUX_ARCHES: "amd64 386 arm arm64 s390x mips64le ppc64le riscv64"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@ -14,16 +14,30 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: setup go
|
- name: setup go
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: .github/go-version
|
||||||
- uses: ibiqlik/action-yamllint@v3
|
- uses: ibiqlik/action-yamllint@v3
|
||||||
with:
|
with:
|
||||||
format: auto
|
format: auto
|
||||||
- uses: golangci/golangci-lint-action@v3
|
- uses: golangci/golangci-lint-action@v6
|
||||||
with:
|
with:
|
||||||
|
version: v1.61.0
|
||||||
args: -v
|
args: -v
|
||||||
skip-cache: true
|
verify-vendor:
|
||||||
|
name: Verify vendor directory
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version-file: .github/go-version
|
||||||
|
- name: Check module vendoring
|
||||||
|
run: |
|
||||||
|
go mod tidy
|
||||||
|
go mod vendor
|
||||||
|
test -z "$(git status --porcelain)" || (echo "please run 'go mod tidy && go mod vendor', and submit your changes"; exit 1)
|
||||||
build:
|
build:
|
||||||
name: Build all linux architectures
|
name: Build all linux architectures
|
||||||
needs: lint
|
needs: lint
|
||||||
@ -31,9 +45,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: setup go
|
- name: setup go
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: .github/go-version
|
||||||
- name: Build on all supported architectures
|
- name: Build on all supported architectures
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
@ -53,11 +67,15 @@ 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
|
||||||
|
- name: Install dnsmasq(dhcp server)
|
||||||
|
run: |
|
||||||
|
sudo apt-get install dnsmasq
|
||||||
|
sudo systemctl disable --now dnsmasq
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: setup go
|
- name: setup go
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: .github/go-version
|
||||||
- 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
|
||||||
@ -86,8 +104,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: setup go
|
- name: setup go
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: .github/go-version
|
||||||
- name: test
|
- name: test
|
||||||
run: bash ./test_windows.sh
|
run: bash ./test_windows.sh
|
||||||
|
@ -40,6 +40,5 @@ linters-settings:
|
|||||||
- prefix(github.com/containernetworking)
|
- prefix(github.com/containernetworking)
|
||||||
|
|
||||||
run:
|
run:
|
||||||
skip-dirs:
|
|
||||||
- vendor
|
|
||||||
timeout: 5m
|
timeout: 5m
|
||||||
|
modules-download-mode: vendor
|
||||||
|
64
go.mod
64
go.mod
@ -1,50 +1,54 @@
|
|||||||
module github.com/containernetworking/plugins
|
module github.com/containernetworking/plugins
|
||||||
|
|
||||||
go 1.20
|
go 1.23
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Microsoft/hcsshim v0.11.4
|
github.com/Microsoft/hcsshim v0.12.9
|
||||||
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.2.3
|
||||||
github.com/coreos/go-iptables v0.7.0
|
github.com/coreos/go-iptables v0.8.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/dhcp4client v1.0.0
|
|
||||||
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/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475
|
||||||
github.com/mattn/go-shellwords v1.0.12
|
github.com/mattn/go-shellwords v1.0.12
|
||||||
github.com/networkplumbing/go-nft v0.4.0
|
github.com/networkplumbing/go-nft v0.4.0
|
||||||
github.com/onsi/ginkgo/v2 v2.13.2
|
github.com/onsi/ginkgo/v2 v2.22.2
|
||||||
github.com/onsi/gomega v1.30.0
|
github.com/onsi/gomega v1.36.2
|
||||||
github.com/opencontainers/selinux v1.11.0
|
github.com/opencontainers/selinux v1.11.1
|
||||||
github.com/safchain/ethtool v0.3.0
|
github.com/safchain/ethtool v0.5.9
|
||||||
github.com/vishvananda/netlink v1.2.1-beta.2
|
github.com/vishvananda/netlink v1.3.0
|
||||||
golang.org/x/sys v0.15.0
|
golang.org/x/sys v0.29.0
|
||||||
|
sigs.k8s.io/knftables v0.0.18
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/containerd/cgroups v1.1.0 // indirect
|
github.com/containerd/cgroups/v3 v3.0.3 // indirect
|
||||||
github.com/containerd/containerd v1.6.23 // indirect
|
github.com/containerd/errdefs v0.3.0 // indirect
|
||||||
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4 // indirect
|
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||||
github.com/go-logr/logr v1.3.0 // indirect
|
github.com/containerd/typeurl/v2 v2.2.0 // indirect
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // 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/go-cmp v0.6.0 // indirect
|
||||||
github.com/google/pprof v0.0.0-20230323073829-e72429f035bd // indirect
|
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
|
||||||
|
github.com/josharian/native v1.1.0 // indirect
|
||||||
|
github.com/mdlayher/packet v1.1.2 // indirect
|
||||||
|
github.com/mdlayher/socket v0.5.1 // indirect
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
|
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
|
||||||
github.com/vishvananda/netns v0.0.4 // indirect
|
github.com/vishvananda/netns v0.0.4 // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
golang.org/x/mod v0.13.0 // indirect
|
golang.org/x/net v0.33.0 // indirect
|
||||||
golang.org/x/net v0.17.0 // indirect
|
golang.org/x/sync v0.10.0 // indirect
|
||||||
golang.org/x/text v0.13.0 // indirect
|
golang.org/x/text v0.21.0 // indirect
|
||||||
golang.org/x/tools v0.14.0 // indirect
|
golang.org/x/tools v0.28.0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||||
google.golang.org/grpc v1.56.3 // indirect
|
google.golang.org/grpc v1.67.0 // indirect
|
||||||
google.golang.org/protobuf v1.30.0 // indirect
|
google.golang.org/protobuf v1.36.1 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
185
go.sum
185
go.sum
@ -1,37 +1,30 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8=
|
github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6lLg=
|
||||||
github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w=
|
github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y=
|
||||||
github.com/alexflint/go-filemutex v1.2.0 h1:1v0TJPDtlhgpW4nJ+GvxCLSlUDC3+gW0CQQvlmfDR/s=
|
github.com/alexflint/go-filemutex v1.3.0 h1:LgE+nTUWnQCyRKbpoceKZsPQbs84LivvgwUymZXdOcM=
|
||||||
github.com/alexflint/go-filemutex v1.2.0/go.mod h1:mYyQSWvw9Tx2/H2n9qXPb52tTYfE0pZAWcBq5mK025c=
|
github.com/alexflint/go-filemutex v1.3.0/go.mod h1:U0+VA/i30mGBlLCrFPGtTe9y6wGQfNAWPBTekHQ+c8A=
|
||||||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
||||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
|
github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0=
|
||||||
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
|
github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0=
|
||||||
github.com/containerd/containerd v1.6.23 h1:KYJd6UJhKHzwMhiD70iTtSmU+k4565ac22GOTI3AuTA=
|
github.com/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4=
|
||||||
github.com/containerd/containerd v1.6.23/go.mod h1:UrQOiyzrLi3n4aezYJbQH6Il+YzTvnHFbEuO3yfDrM4=
|
github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||||
github.com/containernetworking/cni v1.1.2 h1:wtRGZVv7olUHMOqouPpn3cXJWpJgM6+EUl31EQbXALQ=
|
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||||
github.com/containernetworking/cni v1.1.2/go.mod h1:sDpYKmGVENF3s6uvMvGgldDWeG8dMxakj/u+i9ht9vw=
|
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||||
github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8=
|
github.com/containerd/typeurl/v2 v2.2.0 h1:6NBDbQzr7I5LHgp34xAXYF5DOTQDn05X58lsPEmzLso=
|
||||||
github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
|
github.com/containerd/typeurl/v2 v2.2.0/go.mod h1:8XOOxnyatxSWuG8OfsZXVnAF4iZfedjS/8UHSPJnX4g=
|
||||||
|
github.com/containernetworking/cni v1.2.3 h1:hhOcjNVUQTnzdRJ6alC5XF+wd9mfGIUaj8FuJbEslXM=
|
||||||
|
github.com/containernetworking/cni v1.2.3/go.mod h1:DuLgF+aPd3DzcTQTtp/Nvl1Kim23oFKdm2okJzBQA5M=
|
||||||
|
github.com/coreos/go-iptables v0.8.0 h1:MPc2P89IhuVpLI7ETL/2tx3XZ61VeICZjYqDEgNsPRc=
|
||||||
|
github.com/coreos/go-iptables v0.8.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c h1:Xo2rK1pzOm0jO6abTPIQwbAmqBIOj132otexc1mmzFc=
|
|
||||||
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
|
|
||||||
github.com/d2g/dhcp4client v1.0.0 h1:suYBsYZIkSlUMEz4TAYCczKf62IA2UWC+O8+KtdOhCo=
|
|
||||||
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
|
|
||||||
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5 h1:+CpLbZIeUn94m02LdEKPcgErLJ347NUwxPKs5u8ieiY=
|
|
||||||
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
|
|
||||||
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4 h1:itqmmf1PFpC4n5JW+j4BU7X4MTfVurhYRTjODoPb2Y8=
|
|
||||||
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@ -39,13 +32,10 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
|
|||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
|
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||||
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
@ -64,71 +54,67 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
|
|||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
|
||||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
|
||||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
|
||||||
github.com/google/pprof v0.0.0-20230323073829-e72429f035bd h1:r8yyd+DJDmsUhGrRBxH5Pj7KeFK5l+Y3FsgT8keqKtk=
|
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||||
github.com/google/pprof v0.0.0-20230323073829-e72429f035bd/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk=
|
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
|
||||||
|
github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475 h1:hxST5pwMBEOWmxpkX20w9oZG+hXdhKmAIPQ3NGGAxas=
|
||||||
|
github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic=
|
||||||
|
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||||
|
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY=
|
||||||
|
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
|
||||||
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
|
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
|
||||||
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||||
|
github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY=
|
||||||
|
github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4=
|
||||||
|
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
|
||||||
|
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
|
||||||
github.com/networkplumbing/go-nft v0.4.0 h1:kExVMwXW48DOAukkBwyI16h4uhE5lN9iMvQd52lpTyU=
|
github.com/networkplumbing/go-nft v0.4.0 h1:kExVMwXW48DOAukkBwyI16h4uhE5lN9iMvQd52lpTyU=
|
||||||
github.com/networkplumbing/go-nft v0.4.0/go.mod h1:HnnM+tYvlGAsMU7yoYwXEVLLiDW9gdMmb5HoGcwpuQs=
|
github.com/networkplumbing/go-nft v0.4.0/go.mod h1:HnnM+tYvlGAsMU7yoYwXEVLLiDW9gdMmb5HoGcwpuQs=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
||||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8=
|
||||||
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
|
||||||
github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs=
|
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
||||||
github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM=
|
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
|
||||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
|
||||||
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
|
|
||||||
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
|
||||||
github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
|
|
||||||
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
|
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0=
|
github.com/safchain/ethtool v0.5.9 h1://6RvaOKFf3nQ0rl5+8zBbE4/72455VC9Jq61pfq67E=
|
||||||
github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
|
github.com/safchain/ethtool v0.5.9/go.mod h1:w8oSsZeowyRaM7xJJBAbubzzrOkwO8TBgPSEqPP/5mg=
|
||||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
|
||||||
|
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
|
||||||
|
github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk=
|
||||||
|
github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs=
|
||||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
@ -146,23 +132,18 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
|
|||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
|
|
||||||
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@ -170,35 +151,27 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
|
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||||
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||||
|
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
@ -206,11 +179,10 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
|
|||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
|
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
|
||||||
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
|
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@ -220,15 +192,15 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
|||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
|
||||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||||
google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=
|
google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw=
|
||||||
google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
|
google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
@ -238,20 +210,15 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
|||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
|
||||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
sigs.k8s.io/knftables v0.0.18 h1:6Duvmu0s/HwGifKrtl6G3AyAPYlWiZqTgS8bkVMiyaE=
|
||||||
|
sigs.k8s.io/knftables v0.0.18/go.mod h1:f/5ZLKYEUPUhVjUCg6l80ACdL7CIIyeL0DxfgojGRTk=
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
@ -60,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,
|
||||||
@ -82,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())
|
||||||
@ -145,9 +154,13 @@ 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 bandwidth 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()))
|
||||||
@ -172,17 +185,19 @@ var _ = Describe("Basic PTP using cnitool", func() {
|
|||||||
// balanced by run time.
|
// balanced by run time.
|
||||||
|
|
||||||
By(fmt.Sprintf("sending tcp traffic to the chained, bridged, traffic shaped container on ip address '%s:%d'\n\n", chainedBridgeIP, chainedBridgeBandwidthPort))
|
By(fmt.Sprintf("sending tcp traffic to the chained, bridged, traffic shaped container on ip address '%s:%d'\n\n", chainedBridgeIP, chainedBridgeBandwidthPort))
|
||||||
runtimeWithLimit := b.Time("with chained bridge and bandwidth plugins", func() {
|
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", runtimeWithoutLimit.Seconds())
|
||||||
|
|
||||||
Expect(runtimeWithLimit).To(BeNumerically(">", runtimeWithoutLimit+1000*time.Millisecond))
|
Expect(runtimeWithLimit).To(BeNumerically(">", runtimeWithoutLimit+1000*time.Millisecond))
|
||||||
}, 1)
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -43,7 +43,7 @@ func TestAnnotate(t *testing.T) {
|
|||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
if !reflect.DeepEqual(Annotatef(test.existingErr, test.contextMessage), test.expectedErr) {
|
if !reflect.DeepEqual(Annotate(test.existingErr, test.contextMessage), test.expectedErr) {
|
||||||
t.Errorf("test case %s fails", test.name)
|
t.Errorf("test case %s fails", test.name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
180
pkg/ip/ipmasq_iptables_linux.go
Normal file
180
pkg/ip/ipmasq_iptables_linux.go
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
// Copyright 2015 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package ip
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/coreos/go-iptables/iptables"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
"github.com/containernetworking/plugins/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// setupIPMasqIPTables is the iptables-based implementation of SetupIPMasqForNetworks
|
||||||
|
func setupIPMasqIPTables(ipns []*net.IPNet, network, _, containerID string) error {
|
||||||
|
// Note: for historical reasons, the iptables implementation ignores ifname.
|
||||||
|
chain := utils.FormatChainName(network, containerID)
|
||||||
|
comment := utils.FormatComment(network, containerID)
|
||||||
|
for _, ip := range ipns {
|
||||||
|
if err := SetupIPMasq(ip, chain, comment); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupIPMasq installs iptables rules to masquerade traffic
|
||||||
|
// coming from ip of ipn and going outside of ipn.
|
||||||
|
// Deprecated: This function only supports iptables. Use SetupIPMasqForNetworks, which
|
||||||
|
// supports both iptables and nftables.
|
||||||
|
func SetupIPMasq(ipn *net.IPNet, chain string, comment string) error {
|
||||||
|
isV6 := ipn.IP.To4() == nil
|
||||||
|
|
||||||
|
var ipt *iptables.IPTables
|
||||||
|
var err error
|
||||||
|
var multicastNet string
|
||||||
|
|
||||||
|
if isV6 {
|
||||||
|
ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv6)
|
||||||
|
multicastNet = "ff00::/8"
|
||||||
|
} else {
|
||||||
|
ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv4)
|
||||||
|
multicastNet = "224.0.0.0/4"
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to locate iptables: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create chain if doesn't exist
|
||||||
|
exists := false
|
||||||
|
chains, err := ipt.ListChains("nat")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to list chains: %v", err)
|
||||||
|
}
|
||||||
|
for _, ch := range chains {
|
||||||
|
if ch == chain {
|
||||||
|
exists = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
if err = ipt.NewChain("nat", chain); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Packets to this network should not be touched
|
||||||
|
if err := ipt.AppendUnique("nat", chain, "-d", ipn.String(), "-j", "ACCEPT", "-m", "comment", "--comment", comment); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't masquerade multicast - pods should be able to talk to other pods
|
||||||
|
// on the local network via multicast.
|
||||||
|
if err := ipt.AppendUnique("nat", chain, "!", "-d", multicastNet, "-j", "MASQUERADE", "-m", "comment", "--comment", comment); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Packets from the specific IP of this network will hit the chain
|
||||||
|
return ipt.AppendUnique("nat", "POSTROUTING", "-s", ipn.IP.String(), "-j", chain, "-m", "comment", "--comment", comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
// teardownIPMasqIPTables is the iptables-based implementation of TeardownIPMasqForNetworks
|
||||||
|
func teardownIPMasqIPTables(ipns []*net.IPNet, network, _, containerID string) error {
|
||||||
|
// Note: for historical reasons, the iptables implementation ignores ifname.
|
||||||
|
chain := utils.FormatChainName(network, containerID)
|
||||||
|
comment := utils.FormatComment(network, containerID)
|
||||||
|
|
||||||
|
var errs []string
|
||||||
|
for _, ipn := range ipns {
|
||||||
|
err := TeardownIPMasq(ipn, chain, comment)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if errs == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New(strings.Join(errs, "\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TeardownIPMasq undoes the effects of SetupIPMasq.
|
||||||
|
// Deprecated: This function only supports iptables. Use TeardownIPMasqForNetworks, which
|
||||||
|
// supports both iptables and nftables.
|
||||||
|
func TeardownIPMasq(ipn *net.IPNet, chain string, comment string) error {
|
||||||
|
isV6 := ipn.IP.To4() == nil
|
||||||
|
|
||||||
|
var ipt *iptables.IPTables
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if isV6 {
|
||||||
|
ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv6)
|
||||||
|
} else {
|
||||||
|
ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv4)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to locate iptables: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ipt.Delete("nat", "POSTROUTING", "-s", ipn.IP.String(), "-j", chain, "-m", "comment", "--comment", comment)
|
||||||
|
if err != nil && !isNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// for downward compatibility
|
||||||
|
err = ipt.Delete("nat", "POSTROUTING", "-s", ipn.String(), "-j", chain, "-m", "comment", "--comment", comment)
|
||||||
|
if err != nil && !isNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ipt.ClearChain("nat", chain)
|
||||||
|
if err != nil && !isNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ipt.DeleteChain("nat", chain)
|
||||||
|
if err != nil && !isNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// gcIPMasqIPTables is the iptables-based implementation of GCIPMasqForNetwork
|
||||||
|
func gcIPMasqIPTables(_ string, _ []types.GCAttachment) error {
|
||||||
|
// FIXME: The iptables implementation does not support GC.
|
||||||
|
//
|
||||||
|
// (In theory, it _could_ backward-compatibly support it, by adding a no-op rule
|
||||||
|
// with a comment indicating the network to each chain it creates, so that it
|
||||||
|
// could later figure out which chains corresponded to which networks; older
|
||||||
|
// implementations would ignore the extra rule but would still correctly delete
|
||||||
|
// the chain on teardown (because they ClearChain() before doing DeleteChain()).
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isNotExist returnst true if the error is from iptables indicating
|
||||||
|
// that the target does not exist.
|
||||||
|
func isNotExist(err error) bool {
|
||||||
|
e, ok := err.(*iptables.Error)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return e.IsNotExist()
|
||||||
|
}
|
@ -15,111 +15,78 @@
|
|||||||
package ip
|
package ip
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/coreos/go-iptables/iptables"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
"github.com/containernetworking/plugins/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetupIPMasq installs iptables rules to masquerade traffic
|
// SetupIPMasqForNetworks installs rules to masquerade traffic coming from ips of ipns and
|
||||||
// coming from ip of ipn and going outside of ipn
|
// going outside of ipns, using a chain name based on network, ifname, and containerID. The
|
||||||
func SetupIPMasq(ipn *net.IPNet, chain string, comment string) error {
|
// backend can be either "iptables" or "nftables"; if it is nil, then a suitable default
|
||||||
isV6 := ipn.IP.To4() == nil
|
// implementation will be used.
|
||||||
|
func SetupIPMasqForNetworks(backend *string, ipns []*net.IPNet, network, ifname, containerID string) error {
|
||||||
var ipt *iptables.IPTables
|
if backend == nil {
|
||||||
var err error
|
// Prefer iptables, unless only nftables is available
|
||||||
var multicastNet string
|
defaultBackend := "iptables"
|
||||||
|
if !utils.SupportsIPTables() && utils.SupportsNFTables() {
|
||||||
if isV6 {
|
defaultBackend = "nftables"
|
||||||
ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv6)
|
|
||||||
multicastNet = "ff00::/8"
|
|
||||||
} else {
|
|
||||||
ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv4)
|
|
||||||
multicastNet = "224.0.0.0/4"
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to locate iptables: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create chain if doesn't exist
|
|
||||||
exists := false
|
|
||||||
chains, err := ipt.ListChains("nat")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to list chains: %v", err)
|
|
||||||
}
|
|
||||||
for _, ch := range chains {
|
|
||||||
if ch == chain {
|
|
||||||
exists = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !exists {
|
|
||||||
if err = ipt.NewChain("nat", chain); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
backend = &defaultBackend
|
||||||
}
|
}
|
||||||
|
|
||||||
// Packets to this network should not be touched
|
switch *backend {
|
||||||
if err := ipt.AppendUnique("nat", chain, "-d", ipn.String(), "-j", "ACCEPT", "-m", "comment", "--comment", comment); err != nil {
|
case "iptables":
|
||||||
return err
|
return setupIPMasqIPTables(ipns, network, ifname, containerID)
|
||||||
|
case "nftables":
|
||||||
|
return setupIPMasqNFTables(ipns, network, ifname, containerID)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown ipmasq backend %q", *backend)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't masquerade multicast - pods should be able to talk to other pods
|
|
||||||
// on the local network via multicast.
|
|
||||||
if err := ipt.AppendUnique("nat", chain, "!", "-d", multicastNet, "-j", "MASQUERADE", "-m", "comment", "--comment", comment); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Packets from the specific IP of this network will hit the chain
|
|
||||||
return ipt.AppendUnique("nat", "POSTROUTING", "-s", ipn.IP.String(), "-j", chain, "-m", "comment", "--comment", comment)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TeardownIPMasq undoes the effects of SetupIPMasq
|
// TeardownIPMasqForNetworks undoes the effects of SetupIPMasqForNetworks
|
||||||
func TeardownIPMasq(ipn *net.IPNet, chain string, comment string) error {
|
func TeardownIPMasqForNetworks(ipns []*net.IPNet, network, ifname, containerID string) error {
|
||||||
isV6 := ipn.IP.To4() == nil
|
var errs []string
|
||||||
|
|
||||||
var ipt *iptables.IPTables
|
// Do both the iptables and the nftables cleanup, since the pod may have been
|
||||||
var err error
|
// created with a different version of this plugin or a different configuration.
|
||||||
|
|
||||||
if isV6 {
|
err := teardownIPMasqIPTables(ipns, network, ifname, containerID)
|
||||||
ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv6)
|
if err != nil && utils.SupportsIPTables() {
|
||||||
} else {
|
errs = append(errs, err.Error())
|
||||||
ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv4)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to locate iptables: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ipt.Delete("nat", "POSTROUTING", "-s", ipn.IP.String(), "-j", chain, "-m", "comment", "--comment", comment)
|
err = teardownIPMasqNFTables(ipns, network, ifname, containerID)
|
||||||
if err != nil && !isNotExist(err) {
|
if err != nil && utils.SupportsNFTables() {
|
||||||
return err
|
errs = append(errs, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// for downward compatibility
|
if errs == nil {
|
||||||
err = ipt.Delete("nat", "POSTROUTING", "-s", ipn.String(), "-j", chain, "-m", "comment", "--comment", comment)
|
return nil
|
||||||
if err != nil && !isNotExist(err) {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
return errors.New(strings.Join(errs, "\n"))
|
||||||
err = ipt.ClearChain("nat", chain)
|
|
||||||
if err != nil && !isNotExist(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ipt.DeleteChain("nat", chain)
|
|
||||||
if err != nil && !isNotExist(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// isNotExist returnst true if the error is from iptables indicating
|
// GCIPMasqForNetwork garbage collects stale IPMasq entries for network
|
||||||
// that the target does not exist.
|
func GCIPMasqForNetwork(network string, attachments []types.GCAttachment) error {
|
||||||
func isNotExist(err error) bool {
|
var errs []string
|
||||||
e, ok := err.(*iptables.Error)
|
|
||||||
if !ok {
|
err := gcIPMasqIPTables(network, attachments)
|
||||||
return false
|
if err != nil && utils.SupportsIPTables() {
|
||||||
|
errs = append(errs, err.Error())
|
||||||
}
|
}
|
||||||
return e.IsNotExist()
|
|
||||||
|
err = gcIPMasqNFTables(network, attachments)
|
||||||
|
if err != nil && utils.SupportsNFTables() {
|
||||||
|
errs = append(errs, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if errs == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New(strings.Join(errs, "\n"))
|
||||||
}
|
}
|
||||||
|
231
pkg/ip/ipmasq_nftables_linux.go
Normal file
231
pkg/ip/ipmasq_nftables_linux.go
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
// Copyright 2023 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package ip
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"sigs.k8s.io/knftables"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
"github.com/containernetworking/plugins/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ipMasqTableName = "cni_plugins_masquerade"
|
||||||
|
ipMasqChainName = "masq_checks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The nftables ipmasq implementation is mostly like the iptables implementation, with
|
||||||
|
// minor updates to fix a bug (adding `ifname`) and to allow future GC support.
|
||||||
|
//
|
||||||
|
// We add a rule for each mapping, with a comment containing a hash of its identifiers,
|
||||||
|
// so that we can later reliably delete the rules we want. (This is important because in
|
||||||
|
// edge cases, it's possible the plugin might see "ADD container A with IP 192.168.1.3",
|
||||||
|
// followed by "ADD container B with IP 192.168.1.3" followed by "DEL container A with IP
|
||||||
|
// 192.168.1.3", and we need to make sure that the DEL causes us to delete the rule for
|
||||||
|
// container A, and not the rule for container B.)
|
||||||
|
//
|
||||||
|
// It would be more nftables-y to have a chain with a single rule doing a lookup against a
|
||||||
|
// set with an element per mapping, rather than having a chain with a rule per mapping.
|
||||||
|
// But there's no easy, non-racy way to say "delete the element 192.168.1.3 from the set,
|
||||||
|
// but only if it was added for container A, not if it was added for container B".
|
||||||
|
|
||||||
|
// hashForNetwork returns a unique hash for this network
|
||||||
|
func hashForNetwork(network string) string {
|
||||||
|
return utils.MustFormatHashWithPrefix(16, "", network)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashForInstance returns a unique hash identifying the rules for this
|
||||||
|
// network/ifname/containerID
|
||||||
|
func hashForInstance(network, ifname, containerID string) string {
|
||||||
|
return hashForNetwork(network) + "-" + utils.MustFormatHashWithPrefix(16, "", ifname+":"+containerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// commentForInstance returns a comment string that begins with a unique hash and
|
||||||
|
// ends with a (possibly-truncated) human-readable description.
|
||||||
|
func commentForInstance(network, ifname, containerID string) string {
|
||||||
|
comment := fmt.Sprintf("%s, net: %s, if: %s, id: %s",
|
||||||
|
hashForInstance(network, ifname, containerID),
|
||||||
|
strings.ReplaceAll(network, `"`, ``),
|
||||||
|
strings.ReplaceAll(ifname, `"`, ``),
|
||||||
|
strings.ReplaceAll(containerID, `"`, ``),
|
||||||
|
)
|
||||||
|
if len(comment) > knftables.CommentLengthMax {
|
||||||
|
comment = comment[:knftables.CommentLengthMax]
|
||||||
|
}
|
||||||
|
return comment
|
||||||
|
}
|
||||||
|
|
||||||
|
// setupIPMasqNFTables is the nftables-based implementation of SetupIPMasqForNetworks
|
||||||
|
func setupIPMasqNFTables(ipns []*net.IPNet, network, ifname, containerID string) error {
|
||||||
|
nft, err := knftables.New(knftables.InetFamily, ipMasqTableName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return setupIPMasqNFTablesWithInterface(nft, ipns, network, ifname, containerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupIPMasqNFTablesWithInterface(nft knftables.Interface, ipns []*net.IPNet, network, ifname, containerID string) error {
|
||||||
|
staleRules, err := findRules(nft, hashForInstance(network, ifname, containerID))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := nft.NewTransaction()
|
||||||
|
|
||||||
|
// Ensure that our table and chains exist.
|
||||||
|
tx.Add(&knftables.Table{
|
||||||
|
Comment: knftables.PtrTo("Masquerading for plugins from github.com/containernetworking/plugins"),
|
||||||
|
})
|
||||||
|
tx.Add(&knftables.Chain{
|
||||||
|
Name: ipMasqChainName,
|
||||||
|
Comment: knftables.PtrTo("Masquerade traffic from certain IPs to any (non-multicast) IP outside their subnet"),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Ensure that the postrouting chain exists and has the correct rules. (Has to be
|
||||||
|
// done after creating ipMasqChainName, so we can jump to it.)
|
||||||
|
tx.Add(&knftables.Chain{
|
||||||
|
Name: "postrouting",
|
||||||
|
Type: knftables.PtrTo(knftables.NATType),
|
||||||
|
Hook: knftables.PtrTo(knftables.PostroutingHook),
|
||||||
|
Priority: knftables.PtrTo(knftables.SNATPriority),
|
||||||
|
})
|
||||||
|
tx.Flush(&knftables.Chain{
|
||||||
|
Name: "postrouting",
|
||||||
|
})
|
||||||
|
tx.Add(&knftables.Rule{
|
||||||
|
Chain: "postrouting",
|
||||||
|
Rule: "ip daddr == 224.0.0.0/4 return",
|
||||||
|
})
|
||||||
|
tx.Add(&knftables.Rule{
|
||||||
|
Chain: "postrouting",
|
||||||
|
Rule: "ip6 daddr == ff00::/8 return",
|
||||||
|
})
|
||||||
|
tx.Add(&knftables.Rule{
|
||||||
|
Chain: "postrouting",
|
||||||
|
Rule: knftables.Concat(
|
||||||
|
"goto", ipMasqChainName,
|
||||||
|
),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Delete stale rules, add new rules to masquerade chain
|
||||||
|
for _, rule := range staleRules {
|
||||||
|
tx.Delete(rule)
|
||||||
|
}
|
||||||
|
for _, ipn := range ipns {
|
||||||
|
ip := "ip"
|
||||||
|
if ipn.IP.To4() == nil {
|
||||||
|
ip = "ip6"
|
||||||
|
}
|
||||||
|
|
||||||
|
// e.g. if ipn is "192.168.1.4/24", then dstNet is "192.168.1.0/24"
|
||||||
|
dstNet := &net.IPNet{IP: ipn.IP.Mask(ipn.Mask), Mask: ipn.Mask}
|
||||||
|
|
||||||
|
tx.Add(&knftables.Rule{
|
||||||
|
Chain: ipMasqChainName,
|
||||||
|
Rule: knftables.Concat(
|
||||||
|
ip, "saddr", "==", ipn.IP,
|
||||||
|
ip, "daddr", "!=", dstNet,
|
||||||
|
"masquerade",
|
||||||
|
),
|
||||||
|
Comment: knftables.PtrTo(commentForInstance(network, ifname, containerID)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nft.Run(context.TODO(), tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// teardownIPMasqNFTables is the nftables-based implementation of TeardownIPMasqForNetworks
|
||||||
|
func teardownIPMasqNFTables(ipns []*net.IPNet, network, ifname, containerID string) error {
|
||||||
|
nft, err := knftables.New(knftables.InetFamily, ipMasqTableName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return teardownIPMasqNFTablesWithInterface(nft, ipns, network, ifname, containerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func teardownIPMasqNFTablesWithInterface(nft knftables.Interface, _ []*net.IPNet, network, ifname, containerID string) error {
|
||||||
|
rules, err := findRules(nft, hashForInstance(network, ifname, containerID))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if len(rules) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := nft.NewTransaction()
|
||||||
|
for _, rule := range rules {
|
||||||
|
tx.Delete(rule)
|
||||||
|
}
|
||||||
|
return nft.Run(context.TODO(), tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gcIPMasqNFTables is the nftables-based implementation of GCIPMasqForNetwork
|
||||||
|
func gcIPMasqNFTables(network string, attachments []types.GCAttachment) error {
|
||||||
|
nft, err := knftables.New(knftables.InetFamily, ipMasqTableName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return gcIPMasqNFTablesWithInterface(nft, network, attachments)
|
||||||
|
}
|
||||||
|
|
||||||
|
func gcIPMasqNFTablesWithInterface(nft knftables.Interface, network string, attachments []types.GCAttachment) error {
|
||||||
|
// Find all rules for the network
|
||||||
|
rules, err := findRules(nft, hashForNetwork(network))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if len(rules) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the comments for all elements of attachments
|
||||||
|
validAttachments := map[string]bool{}
|
||||||
|
for _, attachment := range attachments {
|
||||||
|
validAttachments[commentForInstance(network, attachment.IfName, attachment.ContainerID)] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete anything in rules that isn't in validAttachments
|
||||||
|
tx := nft.NewTransaction()
|
||||||
|
for _, rule := range rules {
|
||||||
|
if !validAttachments[*rule.Comment] {
|
||||||
|
tx.Delete(rule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nft.Run(context.TODO(), tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// findRules finds rules with comments that start with commentPrefix.
|
||||||
|
func findRules(nft knftables.Interface, commentPrefix string) ([]*knftables.Rule, error) {
|
||||||
|
rules, err := nft.ListRules(context.TODO(), ipMasqChainName)
|
||||||
|
if err != nil {
|
||||||
|
if knftables.IsNotFound(err) {
|
||||||
|
// If ipMasqChainName doesn't exist yet, that's fine
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
matchingRules := make([]*knftables.Rule, 0, 1)
|
||||||
|
for _, rule := range rules {
|
||||||
|
if rule.Comment != nil && strings.HasPrefix(*rule.Comment, commentPrefix) {
|
||||||
|
matchingRules = append(matchingRules, rule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchingRules, nil
|
||||||
|
}
|
213
pkg/ip/ipmasq_nftables_linux_test.go
Normal file
213
pkg/ip/ipmasq_nftables_linux_test.go
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
// Copyright 2023 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package ip
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
"sigs.k8s.io/knftables"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_setupIPMasqNFTables(t *testing.T) {
|
||||||
|
nft := knftables.NewFake(knftables.InetFamily, ipMasqTableName)
|
||||||
|
|
||||||
|
containers := []struct {
|
||||||
|
network string
|
||||||
|
ifname string
|
||||||
|
containerID string
|
||||||
|
addrs []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
network: "unit-test",
|
||||||
|
ifname: "eth0",
|
||||||
|
containerID: "one",
|
||||||
|
addrs: []string{"192.168.1.1/24"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
network: "unit-test",
|
||||||
|
ifname: "eth0",
|
||||||
|
containerID: "two",
|
||||||
|
addrs: []string{"192.168.1.2/24", "2001:db8::2/64"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
network: "unit-test",
|
||||||
|
ifname: "eth0",
|
||||||
|
containerID: "three",
|
||||||
|
addrs: []string{"192.168.99.5/24"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
network: "alternate",
|
||||||
|
ifname: "net1",
|
||||||
|
containerID: "three",
|
||||||
|
addrs: []string{
|
||||||
|
"10.0.0.5/24",
|
||||||
|
"10.0.0.6/24",
|
||||||
|
"10.0.1.7/24",
|
||||||
|
"2001:db8::5/64",
|
||||||
|
"2001:db8::6/64",
|
||||||
|
"2001:db8:1::7/64",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range containers {
|
||||||
|
ipns := []*net.IPNet{}
|
||||||
|
for _, addr := range c.addrs {
|
||||||
|
nladdr, err := netlink.ParseAddr(addr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to parse test addr: %v", err)
|
||||||
|
}
|
||||||
|
ipns = append(ipns, nladdr.IPNet)
|
||||||
|
}
|
||||||
|
err := setupIPMasqNFTablesWithInterface(nft, ipns, c.network, c.ifname, c.containerID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error from setupIPMasqNFTables: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := strings.TrimSpace(`
|
||||||
|
add table inet cni_plugins_masquerade { comment "Masquerading for plugins from github.com/containernetworking/plugins" ; }
|
||||||
|
add chain inet cni_plugins_masquerade masq_checks { comment "Masquerade traffic from certain IPs to any (non-multicast) IP outside their subnet" ; }
|
||||||
|
add chain inet cni_plugins_masquerade postrouting { type nat hook postrouting priority 100 ; }
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip saddr == 192.168.1.1 ip daddr != 192.168.1.0/24 masquerade comment "6fd94d501e58f0aa-287fc69eff0574a2, net: unit-test, if: eth0, id: one"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip saddr == 192.168.1.2 ip daddr != 192.168.1.0/24 masquerade comment "6fd94d501e58f0aa-d750b2c8f0f25d5f, net: unit-test, if: eth0, id: two"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip6 saddr == 2001:db8::2 ip6 daddr != 2001:db8::/64 masquerade comment "6fd94d501e58f0aa-d750b2c8f0f25d5f, net: unit-test, if: eth0, id: two"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip saddr == 192.168.99.5 ip daddr != 192.168.99.0/24 masquerade comment "6fd94d501e58f0aa-a4d4adb82b669cfe, net: unit-test, if: eth0, id: three"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip saddr == 10.0.0.5 ip daddr != 10.0.0.0/24 masquerade comment "82783ef24bdc7036-acb19d111858e348, net: alternate, if: net1, id: three"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip saddr == 10.0.0.6 ip daddr != 10.0.0.0/24 masquerade comment "82783ef24bdc7036-acb19d111858e348, net: alternate, if: net1, id: three"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip saddr == 10.0.1.7 ip daddr != 10.0.1.0/24 masquerade comment "82783ef24bdc7036-acb19d111858e348, net: alternate, if: net1, id: three"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip6 saddr == 2001:db8::5 ip6 daddr != 2001:db8::/64 masquerade comment "82783ef24bdc7036-acb19d111858e348, net: alternate, if: net1, id: three"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip6 saddr == 2001:db8::6 ip6 daddr != 2001:db8::/64 masquerade comment "82783ef24bdc7036-acb19d111858e348, net: alternate, if: net1, id: three"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip6 saddr == 2001:db8:1::7 ip6 daddr != 2001:db8:1::/64 masquerade comment "82783ef24bdc7036-acb19d111858e348, net: alternate, if: net1, id: three"
|
||||||
|
add rule inet cni_plugins_masquerade postrouting ip daddr == 224.0.0.0/4 return
|
||||||
|
add rule inet cni_plugins_masquerade postrouting ip6 daddr == ff00::/8 return
|
||||||
|
add rule inet cni_plugins_masquerade postrouting goto masq_checks
|
||||||
|
`)
|
||||||
|
dump := strings.TrimSpace(nft.Dump())
|
||||||
|
if dump != expected {
|
||||||
|
t.Errorf("expected nftables state:\n%s\n\nactual:\n%s\n\n", expected, dump)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a new container reusing "one"'s address, before deleting "one"
|
||||||
|
c := containers[0]
|
||||||
|
addr, err := netlink.ParseAddr(c.addrs[0])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to parse test addr: %v", err)
|
||||||
|
}
|
||||||
|
err = setupIPMasqNFTablesWithInterface(nft, []*net.IPNet{addr.IPNet}, "unit-test", "eth0", "four")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error from setupIPMasqNFTables: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove "one"
|
||||||
|
err = teardownIPMasqNFTablesWithInterface(nft, []*net.IPNet{addr.IPNet}, c.network, c.ifname, c.containerID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error from teardownIPMasqNFTables: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that "one" was deleted (and "four" wasn't)
|
||||||
|
expected = strings.TrimSpace(`
|
||||||
|
add table inet cni_plugins_masquerade { comment "Masquerading for plugins from github.com/containernetworking/plugins" ; }
|
||||||
|
add chain inet cni_plugins_masquerade masq_checks { comment "Masquerade traffic from certain IPs to any (non-multicast) IP outside their subnet" ; }
|
||||||
|
add chain inet cni_plugins_masquerade postrouting { type nat hook postrouting priority 100 ; }
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip saddr == 192.168.1.2 ip daddr != 192.168.1.0/24 masquerade comment "6fd94d501e58f0aa-d750b2c8f0f25d5f, net: unit-test, if: eth0, id: two"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip6 saddr == 2001:db8::2 ip6 daddr != 2001:db8::/64 masquerade comment "6fd94d501e58f0aa-d750b2c8f0f25d5f, net: unit-test, if: eth0, id: two"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip saddr == 192.168.99.5 ip daddr != 192.168.99.0/24 masquerade comment "6fd94d501e58f0aa-a4d4adb82b669cfe, net: unit-test, if: eth0, id: three"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip saddr == 10.0.0.5 ip daddr != 10.0.0.0/24 masquerade comment "82783ef24bdc7036-acb19d111858e348, net: alternate, if: net1, id: three"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip saddr == 10.0.0.6 ip daddr != 10.0.0.0/24 masquerade comment "82783ef24bdc7036-acb19d111858e348, net: alternate, if: net1, id: three"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip saddr == 10.0.1.7 ip daddr != 10.0.1.0/24 masquerade comment "82783ef24bdc7036-acb19d111858e348, net: alternate, if: net1, id: three"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip6 saddr == 2001:db8::5 ip6 daddr != 2001:db8::/64 masquerade comment "82783ef24bdc7036-acb19d111858e348, net: alternate, if: net1, id: three"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip6 saddr == 2001:db8::6 ip6 daddr != 2001:db8::/64 masquerade comment "82783ef24bdc7036-acb19d111858e348, net: alternate, if: net1, id: three"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip6 saddr == 2001:db8:1::7 ip6 daddr != 2001:db8:1::/64 masquerade comment "82783ef24bdc7036-acb19d111858e348, net: alternate, if: net1, id: three"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip saddr == 192.168.1.1 ip daddr != 192.168.1.0/24 masquerade comment "6fd94d501e58f0aa-e766de567ef6c543, net: unit-test, if: eth0, id: four"
|
||||||
|
add rule inet cni_plugins_masquerade postrouting ip daddr == 224.0.0.0/4 return
|
||||||
|
add rule inet cni_plugins_masquerade postrouting ip6 daddr == ff00::/8 return
|
||||||
|
add rule inet cni_plugins_masquerade postrouting goto masq_checks
|
||||||
|
`)
|
||||||
|
dump = strings.TrimSpace(nft.Dump())
|
||||||
|
if dump != expected {
|
||||||
|
t.Errorf("expected nftables state:\n%s\n\nactual:\n%s\n\n", expected, dump)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GC "four" from the "unit-test" network
|
||||||
|
err = gcIPMasqNFTablesWithInterface(nft, "unit-test", []types.GCAttachment{
|
||||||
|
{IfName: "eth0", ContainerID: "two"},
|
||||||
|
{IfName: "eth0", ContainerID: "three"},
|
||||||
|
// (irrelevant extra element)
|
||||||
|
{IfName: "eth0", ContainerID: "one"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error from gcIPMasqNFTables: %v", err)
|
||||||
|
}
|
||||||
|
// GC the "alternate" network without removing anything
|
||||||
|
err = gcIPMasqNFTablesWithInterface(nft, "alternate", []types.GCAttachment{
|
||||||
|
{IfName: "net1", ContainerID: "three"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error from gcIPMasqNFTables: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-dump
|
||||||
|
expected = strings.TrimSpace(`
|
||||||
|
add table inet cni_plugins_masquerade { comment "Masquerading for plugins from github.com/containernetworking/plugins" ; }
|
||||||
|
add chain inet cni_plugins_masquerade masq_checks { comment "Masquerade traffic from certain IPs to any (non-multicast) IP outside their subnet" ; }
|
||||||
|
add chain inet cni_plugins_masquerade postrouting { type nat hook postrouting priority 100 ; }
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip saddr == 192.168.1.2 ip daddr != 192.168.1.0/24 masquerade comment "6fd94d501e58f0aa-d750b2c8f0f25d5f, net: unit-test, if: eth0, id: two"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip6 saddr == 2001:db8::2 ip6 daddr != 2001:db8::/64 masquerade comment "6fd94d501e58f0aa-d750b2c8f0f25d5f, net: unit-test, if: eth0, id: two"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip saddr == 192.168.99.5 ip daddr != 192.168.99.0/24 masquerade comment "6fd94d501e58f0aa-a4d4adb82b669cfe, net: unit-test, if: eth0, id: three"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip saddr == 10.0.0.5 ip daddr != 10.0.0.0/24 masquerade comment "82783ef24bdc7036-acb19d111858e348, net: alternate, if: net1, id: three"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip saddr == 10.0.0.6 ip daddr != 10.0.0.0/24 masquerade comment "82783ef24bdc7036-acb19d111858e348, net: alternate, if: net1, id: three"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip saddr == 10.0.1.7 ip daddr != 10.0.1.0/24 masquerade comment "82783ef24bdc7036-acb19d111858e348, net: alternate, if: net1, id: three"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip6 saddr == 2001:db8::5 ip6 daddr != 2001:db8::/64 masquerade comment "82783ef24bdc7036-acb19d111858e348, net: alternate, if: net1, id: three"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip6 saddr == 2001:db8::6 ip6 daddr != 2001:db8::/64 masquerade comment "82783ef24bdc7036-acb19d111858e348, net: alternate, if: net1, id: three"
|
||||||
|
add rule inet cni_plugins_masquerade masq_checks ip6 saddr == 2001:db8:1::7 ip6 daddr != 2001:db8:1::/64 masquerade comment "82783ef24bdc7036-acb19d111858e348, net: alternate, if: net1, id: three"
|
||||||
|
add rule inet cni_plugins_masquerade postrouting ip daddr == 224.0.0.0/4 return
|
||||||
|
add rule inet cni_plugins_masquerade postrouting ip6 daddr == ff00::/8 return
|
||||||
|
add rule inet cni_plugins_masquerade postrouting goto masq_checks
|
||||||
|
`)
|
||||||
|
dump = strings.TrimSpace(nft.Dump())
|
||||||
|
if dump != expected {
|
||||||
|
t.Errorf("expected nftables state:\n%s\n\nactual:\n%s\n\n", expected, dump)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GC everything
|
||||||
|
err = gcIPMasqNFTablesWithInterface(nft, "unit-test", []types.GCAttachment{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error from gcIPMasqNFTables: %v", err)
|
||||||
|
}
|
||||||
|
err = gcIPMasqNFTablesWithInterface(nft, "alternate", []types.GCAttachment{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error from gcIPMasqNFTables: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected = strings.TrimSpace(`
|
||||||
|
add table inet cni_plugins_masquerade { comment "Masquerading for plugins from github.com/containernetworking/plugins" ; }
|
||||||
|
add chain inet cni_plugins_masquerade masq_checks { comment "Masquerade traffic from certain IPs to any (non-multicast) IP outside their subnet" ; }
|
||||||
|
add chain inet cni_plugins_masquerade postrouting { type nat hook postrouting priority 100 ; }
|
||||||
|
add rule inet cni_plugins_masquerade postrouting ip daddr == 224.0.0.0/4 return
|
||||||
|
add rule inet cni_plugins_masquerade postrouting ip6 daddr == ff00::/8 return
|
||||||
|
add rule inet cni_plugins_masquerade postrouting goto masq_checks
|
||||||
|
`)
|
||||||
|
dump = strings.TrimSpace(nft.Dump())
|
||||||
|
if dump != expected {
|
||||||
|
t.Errorf("expected nftables state:\n%s\n\nactual:\n%s\n\n", expected, dump)
|
||||||
|
}
|
||||||
|
}
|
@ -32,11 +32,12 @@ var 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) {
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = name
|
||||||
|
linkAttrs.MTU = mtu
|
||||||
|
|
||||||
veth := &netlink.Veth{
|
veth := &netlink.Veth{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: name,
|
|
||||||
MTU: mtu,
|
|
||||||
},
|
|
||||||
PeerName: peer,
|
PeerName: peer,
|
||||||
PeerNamespace: netlink.NsFd(int(hostNS.Fd())),
|
PeerNamespace: netlink.NsFd(int(hostNS.Fd())),
|
||||||
}
|
}
|
||||||
@ -90,7 +91,7 @@ func makeVeth(name, vethPeerName string, mtu int, mac string, hostNS ns.NetNS) (
|
|||||||
if peerExists(peerName) && vethPeerName == "" {
|
if peerExists(peerName) && vethPeerName == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return peerName, veth, 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)
|
||||||
default:
|
default:
|
||||||
return peerName, veth, fmt.Errorf("failed to make veth pair: %v", err)
|
return peerName, veth, fmt.Errorf("failed to make veth pair: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -149,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
|
||||||
})
|
})
|
||||||
@ -180,9 +180,8 @@ 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()
|
||||||
_, _, 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
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -50,3 +50,16 @@ func AddDefaultRoute(gw net.IP, dev netlink.Link) error {
|
|||||||
}
|
}
|
||||||
return AddRoute(defNet, gw, dev)
|
return AddRoute(defNet, gw, dev)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsIPNetZero check if the IPNet is "0.0.0.0/0" or "::/0"
|
||||||
|
// This is needed as go-netlink replaces nil Dst with a '0' IPNet since
|
||||||
|
// https://github.com/vishvananda/netlink/commit/acdc658b8613655ddb69f978e9fb4cf413e2b830
|
||||||
|
func IsIPNetZero(ipnet *net.IPNet) bool {
|
||||||
|
if ipnet == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if ones, _ := ipnet.Mask.Size(); ones != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return ipnet.IP.Equal(net.IPv4zero) || ipnet.IP.Equal(net.IPv6zero)
|
||||||
|
}
|
||||||
|
@ -32,3 +32,7 @@ func ExecCheck(plugin string, netconf []byte) error {
|
|||||||
func ExecDel(plugin string, netconf []byte) error {
|
func ExecDel(plugin string, netconf []byte) error {
|
||||||
return invoke.DelegateDel(context.TODO(), plugin, netconf, nil)
|
return invoke.DelegateDel(context.TODO(), plugin, netconf, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExecStatus(plugin string, netconf []byte) error {
|
||||||
|
return invoke.DelegateStatus(context.TODO(), plugin, netconf, nil)
|
||||||
|
}
|
||||||
|
@ -117,10 +117,27 @@ func ConfigureIface(ifName string, res *current.Result) error {
|
|||||||
Dst: &r.Dst,
|
Dst: &r.Dst,
|
||||||
LinkIndex: link.Attrs().Index,
|
LinkIndex: link.Attrs().Index,
|
||||||
Gw: gw,
|
Gw: gw,
|
||||||
|
Priority: r.Priority,
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Table != nil {
|
||||||
|
route.Table = *r.Table
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Scope != nil {
|
||||||
|
route.Scope = netlink.Scope(*r.Scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Table != nil {
|
||||||
|
route.Table = *r.Table
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Scope != nil {
|
||||||
|
route.Scope = netlink.Scope(*r.Scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = netlink.RouteAddEcmp(&route); err != nil {
|
if err = netlink.RouteAddEcmp(&route); err != nil {
|
||||||
return fmt.Errorf("failed to add route '%v via %v dev %v': %v", r.Dst, gw, ifName, err)
|
return fmt.Errorf("failed to add route '%v via %v dev %v metric %d (Scope: %v, Table: %d)': %v", r.Dst, gw, ifName, r.Priority, route.Scope, route.Table, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,9 +41,11 @@ func ipNetEqual(a, b *net.IPNet) bool {
|
|||||||
|
|
||||||
var _ = Describe("ConfigureIface", func() {
|
var _ = Describe("ConfigureIface", func() {
|
||||||
var originalNS ns.NetNS
|
var originalNS ns.NetNS
|
||||||
var ipv4, ipv6, routev4, routev6 *net.IPNet
|
var ipv4, ipv6, routev4, routev6, routev4Scope *net.IPNet
|
||||||
var ipgw4, ipgw6, routegwv4, routegwv6 net.IP
|
var ipgw4, ipgw6, routegwv4, routegwv6 net.IP
|
||||||
|
var routeScope int
|
||||||
var result *current.Result
|
var result *current.Result
|
||||||
|
var routeTable int
|
||||||
|
|
||||||
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
|
||||||
@ -54,11 +56,12 @@ var _ = Describe("ConfigureIface", func() {
|
|||||||
err = originalNS.Do(func(ns.NetNS) error {
|
err = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = LINK_NAME
|
||||||
|
|
||||||
// Add master
|
// Add master
|
||||||
err = netlink.LinkAdd(&netlink.Dummy{
|
err = netlink.LinkAdd(&netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: LINK_NAME,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
_, err = netlink.LinkByName(LINK_NAME)
|
_, err = netlink.LinkByName(LINK_NAME)
|
||||||
@ -77,6 +80,10 @@ var _ = Describe("ConfigureIface", func() {
|
|||||||
routegwv4 = net.ParseIP("1.2.3.5")
|
routegwv4 = net.ParseIP("1.2.3.5")
|
||||||
Expect(routegwv4).NotTo(BeNil())
|
Expect(routegwv4).NotTo(BeNil())
|
||||||
|
|
||||||
|
_, routev4Scope, err = net.ParseCIDR("1.2.3.4/32")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(routev4Scope).NotTo(BeNil())
|
||||||
|
|
||||||
ipgw4 = net.ParseIP("1.2.3.1")
|
ipgw4 = net.ParseIP("1.2.3.1")
|
||||||
Expect(ipgw4).NotTo(BeNil())
|
Expect(ipgw4).NotTo(BeNil())
|
||||||
|
|
||||||
@ -93,6 +100,9 @@ var _ = Describe("ConfigureIface", func() {
|
|||||||
ipgw6 = net.ParseIP("abcd:1234:ffff::1")
|
ipgw6 = net.ParseIP("abcd:1234:ffff::1")
|
||||||
Expect(ipgw6).NotTo(BeNil())
|
Expect(ipgw6).NotTo(BeNil())
|
||||||
|
|
||||||
|
routeTable := 5000
|
||||||
|
routeScope = 200
|
||||||
|
|
||||||
result = ¤t.Result{
|
result = ¤t.Result{
|
||||||
Interfaces: []*current.Interface{
|
Interfaces: []*current.Interface{
|
||||||
{
|
{
|
||||||
@ -121,6 +131,8 @@ var _ = Describe("ConfigureIface", func() {
|
|||||||
Routes: []*types.Route{
|
Routes: []*types.Route{
|
||||||
{Dst: *routev4, GW: routegwv4},
|
{Dst: *routev4, GW: routegwv4},
|
||||||
{Dst: *routev6, GW: routegwv6},
|
{Dst: *routev6, GW: routegwv6},
|
||||||
|
{Dst: *routev4, GW: routegwv4, Table: &routeTable},
|
||||||
|
{Dst: *routev4Scope, Scope: &routeScope},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -162,7 +174,7 @@ var _ = Describe("ConfigureIface", func() {
|
|||||||
routes, err := netlink.RouteList(link, 0)
|
routes, err := netlink.RouteList(link, 0)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
var v4found, v6found bool
|
var v4found, v6found, v4Scopefound bool
|
||||||
for _, route := range routes {
|
for _, route := range routes {
|
||||||
isv4 := route.Dst.IP.To4() != nil
|
isv4 := route.Dst.IP.To4() != nil
|
||||||
if isv4 && ipNetEqual(route.Dst, routev4) && route.Gw.Equal(routegwv4) {
|
if isv4 && ipNetEqual(route.Dst, routev4) && route.Gw.Equal(routegwv4) {
|
||||||
@ -171,13 +183,17 @@ var _ = Describe("ConfigureIface", func() {
|
|||||||
if !isv4 && ipNetEqual(route.Dst, routev6) && route.Gw.Equal(routegwv6) {
|
if !isv4 && ipNetEqual(route.Dst, routev6) && route.Gw.Equal(routegwv6) {
|
||||||
v6found = true
|
v6found = true
|
||||||
}
|
}
|
||||||
|
if isv4 && ipNetEqual(route.Dst, routev4Scope) && int(route.Scope) == routeScope {
|
||||||
|
v4Scopefound = true
|
||||||
|
}
|
||||||
|
|
||||||
if v4found && v6found {
|
if v4found && v6found && v4Scopefound {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expect(v4found).To(BeTrue())
|
Expect(v4found).To(BeTrue())
|
||||||
Expect(v6found).To(BeTrue())
|
Expect(v6found).To(BeTrue())
|
||||||
|
Expect(v4Scopefound).To(BeTrue())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@ -201,7 +217,7 @@ var _ = Describe("ConfigureIface", func() {
|
|||||||
routes, err := netlink.RouteList(link, 0)
|
routes, err := netlink.RouteList(link, 0)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
var v4found, v6found bool
|
var v4found, v6found, v4Tablefound bool
|
||||||
for _, route := range routes {
|
for _, route := range routes {
|
||||||
isv4 := route.Dst.IP.To4() != nil
|
isv4 := route.Dst.IP.To4() != nil
|
||||||
if isv4 && ipNetEqual(route.Dst, routev4) && route.Gw.Equal(ipgw4) {
|
if isv4 && ipNetEqual(route.Dst, routev4) && route.Gw.Equal(ipgw4) {
|
||||||
@ -218,6 +234,29 @@ var _ = Describe("ConfigureIface", func() {
|
|||||||
Expect(v4found).To(BeTrue())
|
Expect(v4found).To(BeTrue())
|
||||||
Expect(v6found).To(BeTrue())
|
Expect(v6found).To(BeTrue())
|
||||||
|
|
||||||
|
// Need to read all tables, so cannot use RouteList
|
||||||
|
routeFilter := &netlink.Route{
|
||||||
|
Table: routeTable,
|
||||||
|
}
|
||||||
|
|
||||||
|
routes, err = netlink.RouteListFiltered(netlink.FAMILY_ALL,
|
||||||
|
routeFilter,
|
||||||
|
netlink.RT_FILTER_TABLE)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
for _, route := range routes {
|
||||||
|
isv4 := route.Dst.IP.To4() != nil
|
||||||
|
if isv4 && ipNetEqual(route.Dst, routev4) && route.Gw.Equal(ipgw4) {
|
||||||
|
v4Tablefound = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if v4Tablefound {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Expect(v4Tablefound).To(BeTrue())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package link_test
|
package link_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/networkplumbing/go-nft/nft"
|
"github.com/networkplumbing/go-nft/nft"
|
||||||
@ -301,10 +302,10 @@ type configurerStub struct {
|
|||||||
func (a *configurerStub) Apply(c *nft.Config) (*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 nil, fmt.Errorf(errorFirstApplyText)
|
return nil, errors.New(errorFirstApplyText)
|
||||||
}
|
}
|
||||||
if a.failSecondApplyConfig && a.applyCounter == 2 {
|
if a.failSecondApplyConfig && a.applyCounter == 2 {
|
||||||
return nil, fmt.Errorf(errorSecondApplyText)
|
return nil, errors.New(errorSecondApplyText)
|
||||||
}
|
}
|
||||||
a.applyConfig = append(a.applyConfig, c)
|
a.applyConfig = append(a.applyConfig, c)
|
||||||
if a.applyReturnNil {
|
if a.applyReturnNil {
|
||||||
@ -316,7 +317,7 @@ func (a *configurerStub) Apply(c *nft.Config) (*nft.Config, error) {
|
|||||||
func (a *configurerStub) Read(_ ...string) (*nft.Config, error) {
|
func (a *configurerStub) Read(_ ...string) (*nft.Config, error) {
|
||||||
a.readCalled = true
|
a.readCalled = true
|
||||||
if a.failReadConfig {
|
if a.failReadConfig {
|
||||||
return nil, fmt.Errorf(errorReadText)
|
return nil, errors.New(errorReadText)
|
||||||
}
|
}
|
||||||
return a.readConfig, nil
|
return a.readConfig, nil
|
||||||
}
|
}
|
||||||
|
@ -13,10 +13,10 @@ The `ns.Do()` method provides **partial** control over network namespaces for yo
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
err = targetNs.Do(func(hostNs ns.NetNS) error {
|
err = targetNs.Do(func(hostNs ns.NetNS) error {
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = "dummy0"
|
||||||
dummy := &netlink.Dummy{
|
dummy := &netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: "dummy0",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
return netlink.LinkAdd(dummy)
|
return netlink.LinkAdd(dummy)
|
||||||
})
|
})
|
||||||
|
@ -31,6 +31,10 @@ func GetCurrentNS() (NetNS, error) {
|
|||||||
// return an unexpected network namespace.
|
// return an unexpected network namespace.
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
return getCurrentNSNoLock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCurrentNSNoLock() (NetNS, error) {
|
||||||
return GetNS(getCurrentThreadNetNSPath())
|
return GetNS(getCurrentThreadNetNSPath())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,6 +156,54 @@ func GetNS(nspath string) (NetNS, error) {
|
|||||||
return &netNS{file: fd}, nil
|
return &netNS{file: fd}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns a new empty NetNS.
|
||||||
|
// Calling Close() let the kernel garbage collect the network namespace.
|
||||||
|
func TempNetNS() (NetNS, error) {
|
||||||
|
var tempNS NetNS
|
||||||
|
var err error
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
|
||||||
|
// Create the new namespace in a new goroutine so that if we later fail
|
||||||
|
// to switch the namespace back to the original one, we can safely
|
||||||
|
// leave the thread locked to die without a risk of the current thread
|
||||||
|
// left lingering with incorrect namespace.
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
runtime.LockOSThread()
|
||||||
|
|
||||||
|
var threadNS NetNS
|
||||||
|
// save a handle to current network namespace
|
||||||
|
threadNS, err = getCurrentNSNoLock()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("failed to open current namespace: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer threadNS.Close()
|
||||||
|
|
||||||
|
// create the temporary network namespace
|
||||||
|
err = unix.Unshare(unix.CLONE_NEWNET)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// get a handle to the temporary network namespace
|
||||||
|
tempNS, err = getCurrentNSNoLock()
|
||||||
|
|
||||||
|
err2 := threadNS.Set()
|
||||||
|
if err2 == nil {
|
||||||
|
// Unlock the current thread only when we successfully switched back
|
||||||
|
// to the original namespace; otherwise leave the thread locked which
|
||||||
|
// will force the runtime to scrap the current thread, that is maybe
|
||||||
|
// not as optimal but at least always safe to do.
|
||||||
|
runtime.UnlockOSThread()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
return tempNS, err
|
||||||
|
}
|
||||||
|
|
||||||
func (ns *netNS) Path() string {
|
func (ns *netNS) Path() string {
|
||||||
return ns.file.Name()
|
return ns.file.Name()
|
||||||
}
|
}
|
||||||
@ -173,7 +225,7 @@ func (ns *netNS) Do(toRun func(NetNS) error) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
containedCall := func(hostNS NetNS) error {
|
containedCall := func(hostNS NetNS) error {
|
||||||
threadNS, err := GetCurrentNS()
|
threadNS, err := getCurrentNSNoLock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to open current netns: %v", err)
|
return fmt.Errorf("failed to open current netns: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -114,3 +114,12 @@ func CmdDel(cniNetns, cniContainerID, cniIfname string, f func() error) error {
|
|||||||
func CmdDelWithArgs(args *skel.CmdArgs, f func() error) error {
|
func CmdDelWithArgs(args *skel.CmdArgs, f func() error) error {
|
||||||
return CmdDel(args.Netns, args.ContainerID, args.IfName, f)
|
return CmdDel(args.Netns, args.ContainerID, args.IfName, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CmdStatus(f func() error) error {
|
||||||
|
os.Setenv("CNI_COMMAND", "STATUS")
|
||||||
|
os.Setenv("CNI_PATH", os.Getenv("PATH"))
|
||||||
|
os.Setenv("CNI_NETNS_OVERRIDE", "1")
|
||||||
|
defer envCleanup()
|
||||||
|
|
||||||
|
return f()
|
||||||
|
}
|
||||||
|
@ -19,7 +19,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// AllSpecVersions contains all CNI spec version numbers
|
// AllSpecVersions contains all CNI spec version numbers
|
||||||
var AllSpecVersions = [...]string{"0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0", "1.0.0"}
|
var AllSpecVersions = [...]string{"0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0", "1.0.0", "1.1.0"}
|
||||||
|
|
||||||
// SpecVersionHasIPVersion returns true if the given CNI specification version
|
// SpecVersionHasIPVersion returns true if the given CNI specification version
|
||||||
// includes the "version" field in the IP address elements
|
// includes the "version" field in the IP address elements
|
||||||
@ -39,6 +39,13 @@ func SpecVersionHasCHECK(ver string) bool {
|
|||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SpecVersionHasSTATUS returns true if the given CNI specification version
|
||||||
|
// supports the STATUS command
|
||||||
|
func SpecVersionHasSTATUS(ver string) bool {
|
||||||
|
ok, _ := version.GreaterThanOrEqualTo(ver, "1.1.0")
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
// SpecVersionHasChaining returns true if the given CNI specification version
|
// SpecVersionHasChaining returns true if the given CNI specification version
|
||||||
// supports plugin chaining
|
// supports plugin chaining
|
||||||
func SpecVersionHasChaining(ver string) bool {
|
func SpecVersionHasChaining(ver string) bool {
|
||||||
|
@ -51,7 +51,7 @@ func DeleteConntrackEntriesForDstIP(dstIP string, protocol uint8) error {
|
|||||||
filter.AddIP(netlink.ConntrackOrigDstIP, ip)
|
filter.AddIP(netlink.ConntrackOrigDstIP, ip)
|
||||||
filter.AddProtocol(protocol)
|
filter.AddProtocol(protocol)
|
||||||
|
|
||||||
_, err := netlink.ConntrackDeleteFilter(netlink.ConntrackTable, family, filter)
|
_, err := netlink.ConntrackDeleteFilters(netlink.ConntrackTable, family, filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error deleting connection tracking state for protocol: %d IP: %s, error: %v", protocol, ip, err)
|
return fmt.Errorf("error deleting connection tracking state for protocol: %d IP: %s, error: %v", protocol, ip, err)
|
||||||
}
|
}
|
||||||
@ -65,7 +65,7 @@ func DeleteConntrackEntriesForDstPort(port uint16, protocol uint8, family netlin
|
|||||||
filter.AddProtocol(protocol)
|
filter.AddProtocol(protocol)
|
||||||
filter.AddPort(netlink.ConntrackOrigDstPort, port)
|
filter.AddPort(netlink.ConntrackOrigDstPort, port)
|
||||||
|
|
||||||
_, err := netlink.ConntrackDeleteFilter(netlink.ConntrackTable, family, filter)
|
_, err := netlink.ConntrackDeleteFilters(netlink.ConntrackTable, family, filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error deleting connection tracking state for protocol: %d Port: %d, error: %v", protocol, port, err)
|
return fmt.Errorf("error deleting connection tracking state for protocol: %d Port: %d, error: %v", protocol, port, err)
|
||||||
}
|
}
|
||||||
|
46
pkg/utils/netfilter.go
Normal file
46
pkg/utils/netfilter.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright 2023 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/coreos/go-iptables/iptables"
|
||||||
|
"sigs.k8s.io/knftables"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SupportsIPTables tests whether the system supports using netfilter via the iptables API
|
||||||
|
// (whether via "iptables-legacy" or "iptables-nft"). (Note that this returns true if it
|
||||||
|
// is *possible* to use iptables; it does not test whether any other components on the
|
||||||
|
// system are *actually* using iptables.)
|
||||||
|
func SupportsIPTables() bool {
|
||||||
|
ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// We don't care whether the chain actually exists, only whether we can *check*
|
||||||
|
// whether it exists.
|
||||||
|
_, err = ipt.ChainExists("filter", "INPUT")
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportsNFTables tests whether the system supports using netfilter via the nftables API
|
||||||
|
// (ie, not via "iptables-nft"). (Note that this returns true if it is *possible* to use
|
||||||
|
// nftables; it does not test whether any other components on the system are *actually*
|
||||||
|
// using nftables.)
|
||||||
|
func SupportsNFTables() bool {
|
||||||
|
// knftables.New() does sanity checks so we don't need any further test like in
|
||||||
|
// the iptables case.
|
||||||
|
_, err := knftables.New(knftables.IPv4Family, "supports_nftables_test")
|
||||||
|
return err == nil
|
||||||
|
}
|
52
pkg/utils/netfilter_test.go
Normal file
52
pkg/utils/netfilter_test.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright 2023 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("netfilter support", func() {
|
||||||
|
When("it is available", func() {
|
||||||
|
It("reports that iptables is supported", func() {
|
||||||
|
Expect(SupportsIPTables()).To(BeTrue(), "This test should only fail if iptables is not available, but the test suite as a whole requires it to be available.")
|
||||||
|
})
|
||||||
|
It("reports that nftables is supported", func() {
|
||||||
|
Expect(SupportsNFTables()).To(BeTrue(), "This test should only fail if nftables is not available, but the test suite as a whole requires it to be available.")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// These are Serial because os.Setenv has process-wide effect
|
||||||
|
When("it is not available", Serial, func() {
|
||||||
|
var origPath string
|
||||||
|
BeforeEach(func() {
|
||||||
|
origPath = os.Getenv("PATH")
|
||||||
|
os.Setenv("PATH", "/does-not-exist")
|
||||||
|
})
|
||||||
|
AfterEach(func() {
|
||||||
|
os.Setenv("PATH", origPath)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("reports that iptables is not supported", func() {
|
||||||
|
Expect(SupportsIPTables()).To(BeFalse(), "found iptables outside of PATH??")
|
||||||
|
})
|
||||||
|
It("reports that nftables is not supported", func() {
|
||||||
|
Expect(SupportsNFTables()).To(BeFalse(), "found nftables outside of PATH??")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@ -48,11 +48,11 @@ var _ = Describe("Sysctl tests", func() {
|
|||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
testIfaceName = fmt.Sprintf("cnitest.%d", rand.Intn(100000))
|
testIfaceName = fmt.Sprintf("cnitest.%d", rand.Intn(100000))
|
||||||
|
testLinkAttrs := netlink.NewLinkAttrs()
|
||||||
|
testLinkAttrs.Name = testIfaceName
|
||||||
|
testLinkAttrs.Namespace = netlink.NsFd(int(testNs.Fd()))
|
||||||
testIface := &netlink.Dummy{
|
testIface := &netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: testLinkAttrs,
|
||||||
Name: testIfaceName,
|
|
||||||
Namespace: netlink.NsFd(int(testNs.Fd())),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = netlink.LinkAdd(testIface)
|
err = netlink.LinkAdd(testIface)
|
||||||
|
@ -1,135 +0,0 @@
|
|||||||
// Copyright 2021 CNI authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/d2g/dhcp4"
|
|
||||||
"github.com/d2g/dhcp4client"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
MaxDHCPLen = 576
|
|
||||||
)
|
|
||||||
|
|
||||||
// Send the Discovery Packet to the Broadcast Channel
|
|
||||||
func DhcpSendDiscoverPacket(c *dhcp4client.Client, options dhcp4.Options) (dhcp4.Packet, error) {
|
|
||||||
discoveryPacket := c.DiscoverPacket()
|
|
||||||
|
|
||||||
for opt, data := range options {
|
|
||||||
discoveryPacket.AddOption(opt, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
discoveryPacket.PadToMinSize()
|
|
||||||
return discoveryPacket, c.SendPacket(discoveryPacket)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send Request Based On the offer Received.
|
|
||||||
func DhcpSendRequest(c *dhcp4client.Client, options dhcp4.Options, offerPacket *dhcp4.Packet) (dhcp4.Packet, error) {
|
|
||||||
requestPacket := c.RequestPacket(offerPacket)
|
|
||||||
|
|
||||||
for opt, data := range options {
|
|
||||||
requestPacket.AddOption(opt, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
requestPacket.PadToMinSize()
|
|
||||||
|
|
||||||
return requestPacket, c.SendPacket(requestPacket)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send Decline to the received acknowledgement.
|
|
||||||
func DhcpSendDecline(c *dhcp4client.Client, acknowledgementPacket *dhcp4.Packet, options dhcp4.Options) (dhcp4.Packet, error) {
|
|
||||||
declinePacket := c.DeclinePacket(acknowledgementPacket)
|
|
||||||
|
|
||||||
for opt, data := range options {
|
|
||||||
declinePacket.AddOption(opt, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
declinePacket.PadToMinSize()
|
|
||||||
|
|
||||||
return declinePacket, c.SendPacket(declinePacket)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lets do a Full DHCP Request.
|
|
||||||
func DhcpRequest(c *dhcp4client.Client, options dhcp4.Options) (bool, dhcp4.Packet, error) {
|
|
||||||
discoveryPacket, err := DhcpSendDiscoverPacket(c, options)
|
|
||||||
if err != nil {
|
|
||||||
return false, discoveryPacket, err
|
|
||||||
}
|
|
||||||
|
|
||||||
offerPacket, err := c.GetOffer(&discoveryPacket)
|
|
||||||
if err != nil {
|
|
||||||
return false, offerPacket, err
|
|
||||||
}
|
|
||||||
|
|
||||||
requestPacket, err := DhcpSendRequest(c, options, &offerPacket)
|
|
||||||
if err != nil {
|
|
||||||
return false, requestPacket, err
|
|
||||||
}
|
|
||||||
|
|
||||||
acknowledgement, err := c.GetAcknowledgement(&requestPacket)
|
|
||||||
if err != nil {
|
|
||||||
return false, acknowledgement, err
|
|
||||||
}
|
|
||||||
|
|
||||||
acknowledgementOptions := acknowledgement.ParseOptions()
|
|
||||||
if dhcp4.MessageType(acknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK {
|
|
||||||
return false, acknowledgement, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, acknowledgement, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Renew a lease backed on the Acknowledgement Packet.
|
|
||||||
// Returns Successful, The AcknoledgementPacket, Any Errors
|
|
||||||
func DhcpRenew(c *dhcp4client.Client, acknowledgement dhcp4.Packet, options dhcp4.Options) (bool, dhcp4.Packet, error) {
|
|
||||||
renewRequest := c.RenewalRequestPacket(&acknowledgement)
|
|
||||||
|
|
||||||
for opt, data := range options {
|
|
||||||
renewRequest.AddOption(opt, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
renewRequest.PadToMinSize()
|
|
||||||
|
|
||||||
err := c.SendPacket(renewRequest)
|
|
||||||
if err != nil {
|
|
||||||
return false, renewRequest, err
|
|
||||||
}
|
|
||||||
|
|
||||||
newAcknowledgement, err := c.GetAcknowledgement(&renewRequest)
|
|
||||||
if err != nil {
|
|
||||||
return false, newAcknowledgement, err
|
|
||||||
}
|
|
||||||
|
|
||||||
newAcknowledgementOptions := newAcknowledgement.ParseOptions()
|
|
||||||
if dhcp4.MessageType(newAcknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK {
|
|
||||||
return false, newAcknowledgement, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, newAcknowledgement, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Release a lease backed on the Acknowledgement Packet.
|
|
||||||
// Returns Any Errors
|
|
||||||
func DhcpRelease(c *dhcp4client.Client, acknowledgement dhcp4.Packet, options dhcp4.Options) error {
|
|
||||||
release := c.ReleasePacket(&acknowledgement)
|
|
||||||
|
|
||||||
for opt, data := range options {
|
|
||||||
release.AddOption(opt, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
release.PadToMinSize()
|
|
||||||
|
|
||||||
return c.SendPacket(release)
|
|
||||||
}
|
|
@ -39,19 +39,21 @@ import (
|
|||||||
var errNoMoreTries = errors.New("no more tries")
|
var errNoMoreTries = errors.New("no more tries")
|
||||||
|
|
||||||
type DHCP struct {
|
type DHCP struct {
|
||||||
mux sync.Mutex
|
mux sync.Mutex
|
||||||
leases map[string]*DHCPLease
|
leases map[string]*DHCPLease
|
||||||
hostNetnsPrefix string
|
hostNetnsPrefix string
|
||||||
clientTimeout time.Duration
|
clientTimeout time.Duration
|
||||||
clientResendMax time.Duration
|
clientResendMax time.Duration
|
||||||
broadcast bool
|
clientResendTimeout time.Duration
|
||||||
|
broadcast bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDHCP(clientTimeout, clientResendMax time.Duration) *DHCP {
|
func newDHCP(clientTimeout, clientResendMax time.Duration, resendTimeout time.Duration) *DHCP {
|
||||||
return &DHCP{
|
return &DHCP{
|
||||||
leases: make(map[string]*DHCPLease),
|
leases: make(map[string]*DHCPLease),
|
||||||
clientTimeout: clientTimeout,
|
clientTimeout: clientTimeout,
|
||||||
clientResendMax: clientResendMax,
|
clientResendMax: clientResendMax,
|
||||||
|
clientResendTimeout: resendTimeout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +76,7 @@ func (d *DHCP) Allocate(args *skel.CmdArgs, result *current.Result) error {
|
|||||||
return fmt.Errorf("error parsing netconf: %v", err)
|
return fmt.Errorf("error parsing netconf: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
optsRequesting, optsProviding, err := prepareOptions(args.Args, conf.IPAM.ProvideOptions, conf.IPAM.RequestOptions)
|
opts, err := prepareOptions(args.Args, conf.IPAM.ProvideOptions, conf.IPAM.RequestOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -89,8 +91,8 @@ func (d *DHCP) Allocate(args *skel.CmdArgs, result *current.Result) error {
|
|||||||
} else {
|
} else {
|
||||||
hostNetns := d.hostNetnsPrefix + args.Netns
|
hostNetns := d.hostNetnsPrefix + args.Netns
|
||||||
l, err = AcquireLease(clientID, hostNetns, args.IfName,
|
l, err = AcquireLease(clientID, hostNetns, args.IfName,
|
||||||
optsRequesting, optsProviding,
|
opts,
|
||||||
d.clientTimeout, d.clientResendMax, d.broadcast)
|
d.clientTimeout, d.clientResendMax, d.clientResendTimeout, d.broadcast)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -109,6 +111,11 @@ func (d *DHCP) Allocate(args *skel.CmdArgs, result *current.Result) error {
|
|||||||
Gateway: l.Gateway(),
|
Gateway: l.Gateway(),
|
||||||
}}
|
}}
|
||||||
result.Routes = l.Routes()
|
result.Routes = l.Routes()
|
||||||
|
if conf.IPAM.Priority != 0 {
|
||||||
|
for _, r := range result.Routes {
|
||||||
|
r.Priority = conf.IPAM.Priority
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -185,7 +192,8 @@ func getListener(socketPath string) (net.Listener, error) {
|
|||||||
|
|
||||||
func runDaemon(
|
func runDaemon(
|
||||||
pidfilePath, hostPrefix, socketPath string,
|
pidfilePath, hostPrefix, socketPath string,
|
||||||
dhcpClientTimeout time.Duration, resendMax time.Duration, broadcast bool,
|
dhcpClientTimeout time.Duration, resendMax time.Duration, resendTimeout time.Duration,
|
||||||
|
broadcast bool,
|
||||||
) error {
|
) error {
|
||||||
// since other goroutines (on separate threads) will change namespaces,
|
// since other goroutines (on separate threads) will change namespaces,
|
||||||
// ensure the RPC server does not get scheduled onto those
|
// ensure the RPC server does not get scheduled onto those
|
||||||
@ -220,7 +228,7 @@ func runDaemon(
|
|||||||
done <- true
|
done <- true
|
||||||
}()
|
}()
|
||||||
|
|
||||||
dhcp := newDHCP(dhcpClientTimeout, resendMax)
|
dhcp := newDHCP(dhcpClientTimeout, resendMax, resendTimeout)
|
||||||
dhcp.hostNetnsPrefix = hostPrefix
|
dhcp.hostNetnsPrefix = hostPrefix
|
||||||
dhcp.broadcast = broadcast
|
dhcp.broadcast = broadcast
|
||||||
rpc.Register(dhcp)
|
rpc.Register(dhcp)
|
||||||
|
@ -61,13 +61,12 @@ var _ = Describe("DHCP Multiple Lease Operations", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Start the DHCP server
|
// Start the DHCP server
|
||||||
dhcpServerDone, err = dhcpServerStart(originalNS, 2, dhcpServerStopCh)
|
dhcpServerDone = dhcpServerStart(originalNS, 2, dhcpServerStopCh)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
|
|
||||||
// Start the DHCP client daemon
|
// Start the DHCP client daemon
|
||||||
dhcpPluginPath, err := exec.LookPath("dhcp")
|
dhcpPluginPath, err := exec.LookPath("dhcp")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
clientCmd = exec.Command(dhcpPluginPath, "daemon", "-socketpath", socketPath)
|
clientCmd = exec.Command(dhcpPluginPath, "daemon", "-socketpath", socketPath, "--timeout", "2s", "--resendtimeout", "8s")
|
||||||
err = clientCmd.Start()
|
err = clientCmd.Start()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(clientCmd.Process).NotTo(BeNil())
|
Expect(clientCmd.Process).NotTo(BeNil())
|
||||||
|
@ -25,10 +25,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/d2g/dhcp4"
|
|
||||||
"github.com/d2g/dhcp4server"
|
|
||||||
"github.com/d2g/dhcp4server/leasepool"
|
|
||||||
"github.com/d2g/dhcp4server/leasepool/memorypool"
|
|
||||||
. "github.com/onsi/ginkgo/v2"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
@ -48,31 +44,52 @@ func getTmpDir() (string, error) {
|
|||||||
return tmpDir, err
|
return tmpDir, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func dhcpServerStart(netns ns.NetNS, numLeases int, stopCh <-chan bool) (*sync.WaitGroup, error) {
|
type DhcpServer struct {
|
||||||
// Add the expected IP to the pool
|
cmd *exec.Cmd
|
||||||
lp := memorypool.MemoryPool{}
|
lock sync.Mutex
|
||||||
|
|
||||||
Expect(numLeases).To(BeNumerically(">", 0))
|
startAddr net.IP
|
||||||
// Currently tests only need at most 2
|
endAddr net.IP
|
||||||
Expect(numLeases).To(BeNumerically("<=", 2))
|
leaseTime time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
// tests expect first lease to be at address 192.168.1.5
|
func (s *DhcpServer) Serve() error {
|
||||||
for i := 5; i < numLeases+5; i++ {
|
if err := s.Start(); err != nil {
|
||||||
err := lp.AddLease(leasepool.Lease{IP: dhcp4.IPAdd(net.IPv4(192, 168, 1, byte(i)), 0)})
|
return err
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error adding IP to DHCP pool: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return s.cmd.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
dhcpServer, err := dhcp4server.New(
|
func (s *DhcpServer) Start() error {
|
||||||
net.IPv4(192, 168, 1, 1),
|
s.lock.Lock()
|
||||||
&lp,
|
defer s.lock.Unlock()
|
||||||
dhcp4server.SetLocalAddr(net.UDPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 67}),
|
|
||||||
dhcp4server.SetRemoteAddr(net.UDPAddr{IP: net.IPv4bcast, Port: 68}),
|
s.cmd = exec.Command(
|
||||||
dhcp4server.LeaseDuration(time.Minute*15),
|
"dnsmasq",
|
||||||
|
"--no-daemon",
|
||||||
|
"--dhcp-sequential-ip", // allocate IPs sequentially
|
||||||
|
"--port=0", // disable DNS
|
||||||
|
"--conf-file=-", // Do not read /etc/dnsmasq.conf
|
||||||
|
fmt.Sprintf("--dhcp-range=%s,%s,%d", s.startAddr, s.endAddr, int(s.leaseTime.Seconds())),
|
||||||
)
|
)
|
||||||
if err != nil {
|
s.cmd.Stdin = bytes.NewBufferString("")
|
||||||
return nil, fmt.Errorf("failed to create DHCP server: %v", err)
|
s.cmd.Stdout = os.Stdout
|
||||||
|
s.cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
return s.cmd.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DhcpServer) Stop() error {
|
||||||
|
s.lock.Lock()
|
||||||
|
defer s.lock.Unlock()
|
||||||
|
return s.cmd.Process.Kill()
|
||||||
|
}
|
||||||
|
|
||||||
|
func dhcpServerStart(netns ns.NetNS, numLeases int, stopCh <-chan bool) *sync.WaitGroup {
|
||||||
|
dhcpServer := &DhcpServer{
|
||||||
|
startAddr: net.IPv4(192, 168, 1, 5),
|
||||||
|
endAddr: net.IPv4(192, 168, 1, 5+uint8(numLeases)-1),
|
||||||
|
leaseTime: 5 * time.Minute,
|
||||||
}
|
}
|
||||||
|
|
||||||
stopWg := sync.WaitGroup{}
|
stopWg := sync.WaitGroup{}
|
||||||
@ -84,9 +101,10 @@ func dhcpServerStart(netns ns.NetNS, numLeases int, stopCh <-chan bool) (*sync.W
|
|||||||
go func() {
|
go func() {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
err = netns.Do(func(ns.NetNS) error {
|
err := netns.Do(func(ns.NetNS) error {
|
||||||
startWg.Done()
|
startWg.Done()
|
||||||
if err := dhcpServer.ListenAndServe(); err != nil {
|
|
||||||
|
if err := dhcpServer.Serve(); err != nil {
|
||||||
// Log, but don't trap errors; the server will
|
// Log, but don't trap errors; the server will
|
||||||
// always report an error when stopped
|
// always report an error when stopped
|
||||||
GinkgoT().Logf("DHCP server finished with error: %v", err)
|
GinkgoT().Logf("DHCP server finished with error: %v", err)
|
||||||
@ -103,12 +121,12 @@ func dhcpServerStart(netns ns.NetNS, numLeases int, stopCh <-chan bool) (*sync.W
|
|||||||
go func() {
|
go func() {
|
||||||
startWg.Done()
|
startWg.Done()
|
||||||
<-stopCh
|
<-stopCh
|
||||||
dhcpServer.Shutdown()
|
dhcpServer.Stop()
|
||||||
stopWg.Done()
|
stopWg.Done()
|
||||||
}()
|
}()
|
||||||
startWg.Wait()
|
startWg.Wait()
|
||||||
|
|
||||||
return &stopWg, nil
|
return &stopWg
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -155,11 +173,11 @@ var _ = Describe("DHCP Operations", func() {
|
|||||||
err = originalNS.Do(func(ns.NetNS) error {
|
err = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = hostVethName
|
||||||
err = netlink.LinkAdd(&netlink.Veth{
|
err = netlink.LinkAdd(&netlink.Veth{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: hostVethName,
|
PeerName: contVethName,
|
||||||
},
|
|
||||||
PeerName: contVethName,
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
@ -200,8 +218,7 @@ var _ = Describe("DHCP Operations", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Start the DHCP server
|
// Start the DHCP server
|
||||||
dhcpServerDone, err = dhcpServerStart(originalNS, 1, dhcpServerStopCh)
|
dhcpServerDone = dhcpServerStart(originalNS, 1, dhcpServerStopCh)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
|
|
||||||
// Start the DHCP client daemon
|
// Start the DHCP client daemon
|
||||||
dhcpPluginPath, err := exec.LookPath("dhcp")
|
dhcpPluginPath, err := exec.LookPath("dhcp")
|
||||||
@ -394,11 +411,11 @@ func dhcpSetupOriginalNS() (chan bool, string, ns.NetNS, ns.NetNS, error) {
|
|||||||
err = originalNS.Do(func(ns.NetNS) error {
|
err = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = hostBridgeName
|
||||||
// Create bridge in the "host" (original) NS
|
// Create bridge in the "host" (original) NS
|
||||||
br = &netlink.Bridge{
|
br = &netlink.Bridge{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: hostBridgeName,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = netlink.LinkAdd(br)
|
err = netlink.LinkAdd(br)
|
||||||
@ -517,8 +534,7 @@ var _ = Describe("DHCP Lease Unavailable Operations", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Start the DHCP server
|
// Start the DHCP server
|
||||||
dhcpServerDone, err = dhcpServerStart(originalNS, 1, dhcpServerStopCh)
|
dhcpServerDone = dhcpServerStart(originalNS, 1, dhcpServerStopCh)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
|
|
||||||
// Start the DHCP client daemon
|
// Start the DHCP client daemon
|
||||||
dhcpPluginPath, err := exec.LookPath("dhcp")
|
dhcpPluginPath, err := exec.LookPath("dhcp")
|
||||||
@ -528,7 +544,7 @@ var _ = Describe("DHCP Lease Unavailable Operations", func() {
|
|||||||
// `go test` timeout with default delays. Since our DHCP server
|
// `go test` timeout with default delays. Since our DHCP server
|
||||||
// and client daemon are local processes anyway, we can depend on
|
// and client daemon are local processes anyway, we can depend on
|
||||||
// them to respond very quickly.
|
// them to respond very quickly.
|
||||||
clientCmd = exec.Command(dhcpPluginPath, "daemon", "-socketpath", socketPath, "-timeout", "2s", "-resendmax", "8s")
|
clientCmd = exec.Command(dhcpPluginPath, "daemon", "-socketpath", socketPath, "-timeout", "2s", "-resendmax", "8s", "--resendtimeout", "10s")
|
||||||
|
|
||||||
// copy dhcp client's stdout/stderr to test stdout
|
// copy dhcp client's stdout/stderr to test stdout
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
@ -24,8 +25,8 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/d2g/dhcp4"
|
dhcp4 "github.com/insomniacslk/dhcp/dhcpv4"
|
||||||
"github.com/d2g/dhcp4client"
|
"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
@ -35,8 +36,10 @@ 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 (
|
const (
|
||||||
resendDelay0 = 4 * time.Second
|
resendDelay0 = 4 * time.Second
|
||||||
resendDelayMax = 62 * time.Second
|
resendDelayMax = 62 * time.Second
|
||||||
|
defaultLeaseTime = 60 * time.Minute
|
||||||
|
defaultResendTimeout = 208 * time.Second // fast resend + backoff resend
|
||||||
)
|
)
|
||||||
|
|
||||||
// To speed up the retry for first few failures, we retry without
|
// To speed up the retry for first few failures, we retry without
|
||||||
@ -60,34 +63,35 @@ const (
|
|||||||
|
|
||||||
type DHCPLease struct {
|
type DHCPLease struct {
|
||||||
clientID string
|
clientID string
|
||||||
ack *dhcp4.Packet
|
latestLease *nclient4.Lease
|
||||||
opts dhcp4.Options
|
|
||||||
link netlink.Link
|
link netlink.Link
|
||||||
renewalTime time.Time
|
renewalTime time.Time
|
||||||
rebindingTime time.Time
|
rebindingTime time.Time
|
||||||
expireTime time.Time
|
expireTime time.Time
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
resendMax time.Duration
|
resendMax time.Duration
|
||||||
|
resendTimeout time.Duration
|
||||||
broadcast bool
|
broadcast bool
|
||||||
stopping uint32
|
stopping uint32
|
||||||
stop chan struct{}
|
stop chan struct{}
|
||||||
check chan struct{}
|
check chan struct{}
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
cancelFunc context.CancelFunc
|
||||||
|
ctx context.Context
|
||||||
// list of requesting and providing options and if they are necessary / their value
|
// list of requesting and providing options and if they are necessary / their value
|
||||||
optsRequesting map[dhcp4.OptionCode]bool
|
opts []dhcp4.Option
|
||||||
optsProviding map[dhcp4.OptionCode][]byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var requestOptionsDefault = map[dhcp4.OptionCode]bool{
|
var requestOptionsDefault = []dhcp4.OptionCode{
|
||||||
dhcp4.OptionRouter: true,
|
dhcp4.OptionRouter,
|
||||||
dhcp4.OptionSubnetMask: true,
|
dhcp4.OptionSubnetMask,
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareOptions(cniArgs string, provideOptions []ProvideOption, requestOptions []RequestOption) (
|
func prepareOptions(cniArgs string, provideOptions []ProvideOption, requestOptions []RequestOption) (
|
||||||
map[dhcp4.OptionCode]bool, map[dhcp4.OptionCode][]byte, error,
|
[]dhcp4.Option, error,
|
||||||
) {
|
) {
|
||||||
var optsRequesting map[dhcp4.OptionCode]bool
|
var opts []dhcp4.Option
|
||||||
var optsProviding map[dhcp4.OptionCode][]byte
|
|
||||||
var err error
|
var err error
|
||||||
// parse CNI args
|
// parse CNI args
|
||||||
cniArgsParsed := map[string]string{}
|
cniArgsParsed := map[string]string{}
|
||||||
@ -100,46 +104,51 @@ 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)
|
|
||||||
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 {
|
||||||
return nil, nil, fmt.Errorf("Can not parse option %q: %w", opt.Option, err)
|
return nil, fmt.Errorf("Can not parse option %q: %w", opt.Option, err)
|
||||||
}
|
}
|
||||||
if len(opt.Value) > 0 {
|
if len(opt.Value) > 0 {
|
||||||
if len(opt.Value) > 255 {
|
if len(opt.Value) > 255 {
|
||||||
return nil, nil, fmt.Errorf("value too long for option %q: %q", opt.Option, opt.Value)
|
return nil, fmt.Errorf("value too long for option %q: %q", opt.Option, opt.Value)
|
||||||
}
|
}
|
||||||
optsProviding[optParsed] = []byte(opt.Value)
|
opts = append(opts, dhcp4.Option{Code: optParsed, Value: dhcp4.String(opt.Value)})
|
||||||
}
|
}
|
||||||
if value, ok := cniArgsParsed[opt.ValueFromCNIArg]; ok {
|
if value, ok := cniArgsParsed[opt.ValueFromCNIArg]; ok {
|
||||||
if len(value) > 255 {
|
if len(value) > 255 {
|
||||||
return nil, nil, fmt.Errorf("value too long for option %q from CNI_ARGS %q: %q", opt.Option, opt.ValueFromCNIArg, opt.Value)
|
return nil, fmt.Errorf("value too long for option %q from CNI_ARGS %q: %q", opt.Option, opt.ValueFromCNIArg, opt.Value)
|
||||||
}
|
}
|
||||||
optsProviding[optParsed] = []byte(value)
|
opts = append(opts, dhcp4.Option{Code: optParsed, Value: dhcp4.String(value)})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse necessary options map
|
// parse necessary options map
|
||||||
optsRequesting = make(map[dhcp4.OptionCode]bool)
|
var optsRequesting dhcp4.OptionCodeList
|
||||||
skipRequireDefault := false
|
skipRequireDefault := false
|
||||||
for _, opt := range requestOptions {
|
for _, opt := range requestOptions {
|
||||||
if opt.SkipDefault {
|
if opt.SkipDefault {
|
||||||
skipRequireDefault = true
|
skipRequireDefault = true
|
||||||
}
|
}
|
||||||
|
if opt.Option == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
optParsed, err = parseOptionName(string(opt.Option))
|
optParsed, err = parseOptionName(string(opt.Option))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("Can not parse option %q: %w", opt.Option, err)
|
return nil, fmt.Errorf("Can not parse option %q: %w", opt.Option, err)
|
||||||
}
|
}
|
||||||
optsRequesting[optParsed] = true
|
optsRequesting.Add(optParsed)
|
||||||
}
|
}
|
||||||
for k, v := range requestOptionsDefault {
|
if !skipRequireDefault {
|
||||||
// only set if not skipping default and this value does not exists
|
for _, opt := range requestOptionsDefault {
|
||||||
if _, ok := optsRequesting[k]; !ok && !skipRequireDefault {
|
optsRequesting.Add(opt)
|
||||||
optsRequesting[k] = v
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return optsRequesting, optsProviding, err
|
if len(optsRequesting) > 0 {
|
||||||
|
opts = append(opts, dhcp4.Option{Code: dhcp4.OptionParameterRequestList, Value: optsRequesting})
|
||||||
|
}
|
||||||
|
|
||||||
|
return opts, 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
|
||||||
@ -147,19 +156,25 @@ func prepareOptions(cniArgs string, provideOptions []ProvideOption, requestOptio
|
|||||||
// calling DHCPLease.Stop()
|
// calling DHCPLease.Stop()
|
||||||
func AcquireLease(
|
func AcquireLease(
|
||||||
clientID, netns, ifName string,
|
clientID, netns, ifName string,
|
||||||
optsRequesting map[dhcp4.OptionCode]bool, optsProviding map[dhcp4.OptionCode][]byte,
|
opts []dhcp4.Option,
|
||||||
timeout, resendMax time.Duration, broadcast bool,
|
timeout, resendMax time.Duration, resendTimeout time.Duration, broadcast bool,
|
||||||
) (*DHCPLease, error) {
|
) (*DHCPLease, error) {
|
||||||
errCh := make(chan error, 1)
|
errCh := make(chan error, 1)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
l := &DHCPLease{
|
l := &DHCPLease{
|
||||||
clientID: clientID,
|
clientID: clientID,
|
||||||
stop: make(chan struct{}),
|
stop: make(chan struct{}),
|
||||||
check: make(chan struct{}),
|
check: make(chan struct{}),
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
resendMax: resendMax,
|
resendMax: resendMax,
|
||||||
broadcast: broadcast,
|
resendTimeout: resendTimeout,
|
||||||
optsRequesting: optsRequesting,
|
broadcast: broadcast,
|
||||||
optsProviding: optsProviding,
|
opts: opts,
|
||||||
|
cancelFunc: cancel,
|
||||||
|
ctx: ctx,
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("%v: acquiring lease", clientID)
|
log.Printf("%v: acquiring lease", clientID)
|
||||||
@ -201,6 +216,7 @@ func AcquireLease(
|
|||||||
func (l *DHCPLease) Stop() {
|
func (l *DHCPLease) Stop() {
|
||||||
if atomic.CompareAndSwapUint32(&l.stopping, 0, 1) {
|
if atomic.CompareAndSwapUint32(&l.stopping, 0, 1) {
|
||||||
close(l.stop)
|
close(l.stop)
|
||||||
|
l.cancelFunc()
|
||||||
}
|
}
|
||||||
l.wg.Wait()
|
l.wg.Wait()
|
||||||
}
|
}
|
||||||
@ -209,92 +225,65 @@ func (l *DHCPLease) Check() {
|
|||||||
l.check <- struct{}{}
|
l.check <- struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *DHCPLease) getOptionsWithClientID() dhcp4.Options {
|
func withClientID(clientID string) dhcp4.Modifier {
|
||||||
opts := make(dhcp4.Options)
|
return func(d *dhcp4.DHCPv4) {
|
||||||
opts[dhcp4.OptionClientIdentifier] = []byte(l.clientID)
|
optClientID := []byte{0}
|
||||||
// client identifier's first byte is "type"
|
optClientID = append(optClientID, []byte(clientID)...)
|
||||||
newClientID := []byte{0}
|
d.Options.Update(dhcp4.OptClientIdentifier(optClientID))
|
||||||
newClientID = append(newClientID, opts[dhcp4.OptionClientIdentifier]...)
|
}
|
||||||
opts[dhcp4.OptionClientIdentifier] = newClientID
|
|
||||||
return opts
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *DHCPLease) getAllOptions() dhcp4.Options {
|
func withAllOptions(l *DHCPLease) dhcp4.Modifier {
|
||||||
opts := l.getOptionsWithClientID()
|
return func(d *dhcp4.DHCPv4) {
|
||||||
|
for _, opt := range l.opts {
|
||||||
for k, v := range l.optsProviding {
|
d.Options.Update(opt)
|
||||||
opts[k] = v
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
opts[dhcp4.OptionParameterRequestList] = []byte{}
|
|
||||||
for k := range l.optsRequesting {
|
|
||||||
opts[dhcp4.OptionParameterRequestList] = append(opts[dhcp4.OptionParameterRequestList], byte(k))
|
|
||||||
}
|
|
||||||
return opts
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *DHCPLease) acquire() error {
|
func (l *DHCPLease) acquire() error {
|
||||||
c, err := newDHCPClient(l.link, l.timeout, l.broadcast)
|
if (l.link.Attrs().Flags & net.FlagUp) != net.FlagUp {
|
||||||
|
log.Printf("Link %q down. Attempting to set up", l.link.Attrs().Name)
|
||||||
|
if err := netlink.LinkSetUp(l.link); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := newDHCPClient(l.link, l.timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
if (l.link.Attrs().Flags & net.FlagUp) != net.FlagUp {
|
timeoutCtx, cancel := context.WithTimeoutCause(l.ctx, l.resendTimeout, errNoMoreTries)
|
||||||
log.Printf("Link %q down. Attempting to set up", l.link.Attrs().Name)
|
defer cancel()
|
||||||
if err = netlink.LinkSetUp(l.link); err != nil {
|
pkt, err := backoffRetry(timeoutCtx, l.resendMax, func() (*nclient4.Lease, error) {
|
||||||
return err
|
return c.Request(
|
||||||
}
|
timeoutCtx,
|
||||||
}
|
withClientID(l.clientID),
|
||||||
|
withAllOptions(l),
|
||||||
opts := l.getAllOptions()
|
)
|
||||||
|
|
||||||
pkt, err := backoffRetry(l.resendMax, func() (*dhcp4.Packet, error) {
|
|
||||||
ok, ack, err := DhcpRequest(c, opts)
|
|
||||||
switch {
|
|
||||||
case err != nil:
|
|
||||||
return nil, err
|
|
||||||
case !ok:
|
|
||||||
return nil, fmt.Errorf("DHCP server NACK'd own offer")
|
|
||||||
default:
|
|
||||||
return &ack, nil
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return l.commit(pkt)
|
l.commit(pkt)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *DHCPLease) commit(ack *dhcp4.Packet) error {
|
func (l *DHCPLease) commit(lease *nclient4.Lease) {
|
||||||
opts := ack.ParseOptions()
|
l.latestLease = lease
|
||||||
|
ack := lease.ACK
|
||||||
|
|
||||||
leaseTime, err := parseLeaseTime(opts)
|
leaseTime := ack.IPAddressLeaseTime(defaultLeaseTime)
|
||||||
if err != nil {
|
rebindingTime := ack.IPAddressRebindingTime(leaseTime * 85 / 100)
|
||||||
return err
|
renewalTime := ack.IPAddressRenewalTime(leaseTime / 2)
|
||||||
}
|
|
||||||
|
|
||||||
rebindingTime, err := parseRebindingTime(opts)
|
|
||||||
if err != nil || rebindingTime > leaseTime {
|
|
||||||
// Per RFC 2131 Section 4.4.5, it should default to 85% of lease time
|
|
||||||
rebindingTime = leaseTime * 85 / 100
|
|
||||||
}
|
|
||||||
|
|
||||||
renewalTime, err := parseRenewalTime(opts)
|
|
||||||
if err != nil || renewalTime > rebindingTime {
|
|
||||||
// Per RFC 2131 Section 4.4.5, it should default to 50% of lease time
|
|
||||||
renewalTime = leaseTime / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
l.expireTime = now.Add(leaseTime)
|
l.expireTime = now.Add(leaseTime)
|
||||||
l.renewalTime = now.Add(renewalTime)
|
l.renewalTime = now.Add(renewalTime)
|
||||||
l.rebindingTime = now.Add(rebindingTime)
|
l.rebindingTime = now.Add(rebindingTime)
|
||||||
l.ack = ack
|
|
||||||
l.opts = opts
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *DHCPLease) maintain() {
|
func (l *DHCPLease) maintain() {
|
||||||
@ -362,44 +351,40 @@ func (l *DHCPLease) downIface() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *DHCPLease) renew() error {
|
func (l *DHCPLease) renew() error {
|
||||||
c, err := newDHCPClient(l.link, l.timeout, l.broadcast)
|
c, err := newDHCPClient(l.link, l.timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
opts := l.getAllOptions()
|
timeoutCtx, cancel := context.WithTimeoutCause(l.ctx, l.resendTimeout, errNoMoreTries)
|
||||||
pkt, err := backoffRetry(l.resendMax, func() (*dhcp4.Packet, error) {
|
defer cancel()
|
||||||
ok, ack, err := DhcpRenew(c, *l.ack, opts)
|
lease, err := backoffRetry(timeoutCtx, l.resendMax, func() (*nclient4.Lease, error) {
|
||||||
switch {
|
return c.Renew(
|
||||||
case err != nil:
|
timeoutCtx,
|
||||||
return nil, err
|
l.latestLease,
|
||||||
case !ok:
|
withClientID(l.clientID),
|
||||||
return nil, fmt.Errorf("DHCP server did not renew lease")
|
withAllOptions(l),
|
||||||
default:
|
)
|
||||||
return &ack, nil
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
l.commit(pkt)
|
l.commit(lease)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
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.timeout, l.broadcast)
|
c, err := newDHCPClient(l.link, l.timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
opts := l.getOptionsWithClientID()
|
if err = c.Release(l.latestLease, withClientID(l.clientID)); 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")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,33 +392,47 @@ func (l *DHCPLease) release() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *DHCPLease) IPNet() (*net.IPNet, error) {
|
func (l *DHCPLease) IPNet() (*net.IPNet, error) {
|
||||||
mask := parseSubnetMask(l.opts)
|
ack := l.latestLease.ACK
|
||||||
|
|
||||||
|
mask := ack.SubnetMask()
|
||||||
if mask == nil {
|
if mask == nil {
|
||||||
return nil, fmt.Errorf("DHCP option Subnet Mask not found in DHCPACK")
|
return nil, fmt.Errorf("DHCP option Subnet Mask not found in DHCPACK")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &net.IPNet{
|
return &net.IPNet{
|
||||||
IP: l.ack.YIAddr(),
|
IP: ack.YourIPAddr,
|
||||||
Mask: mask,
|
Mask: mask,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *DHCPLease) Gateway() net.IP {
|
func (l *DHCPLease) Gateway() net.IP {
|
||||||
return parseRouter(l.opts)
|
ack := l.latestLease.ACK
|
||||||
|
gws := ack.Router()
|
||||||
|
if len(gws) > 0 {
|
||||||
|
return gws[0]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *DHCPLease) Routes() []*types.Route {
|
func (l *DHCPLease) Routes() []*types.Route {
|
||||||
routes := []*types.Route{}
|
routes := []*types.Route{}
|
||||||
|
|
||||||
|
ack := l.latestLease.ACK
|
||||||
|
|
||||||
// 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.
|
||||||
opt121Routes := parseCIDRRoutes(l.opts)
|
opt121Routes := ack.ClasslessStaticRoute()
|
||||||
if len(opt121Routes) > 0 {
|
if len(opt121Routes) > 0 {
|
||||||
return append(routes, opt121Routes...)
|
for _, r := range opt121Routes {
|
||||||
|
routes = append(routes, &types.Route{Dst: *r.Dest, GW: r.Router})
|
||||||
|
}
|
||||||
|
return routes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append Static Routes
|
// Append Static Routes
|
||||||
routes = append(routes, parseRoutes(l.opts)...)
|
if ack.Options.Has(dhcp4.OptionStaticRoutingTable) {
|
||||||
|
routes = append(routes, parseRoutes(ack.Options.Get(dhcp4.OptionStaticRoutingTable))...)
|
||||||
|
}
|
||||||
|
|
||||||
// The CNI spec says even if there is a gateway specified, we must
|
// The CNI spec says even if there is a gateway specified, we must
|
||||||
// add a default route in the routes section.
|
// add a default route in the routes section.
|
||||||
@ -450,7 +449,7 @@ func jitter(span time.Duration) time.Duration {
|
|||||||
return time.Duration(float64(span) * (2.0*rand.Float64() - 1.0))
|
return time.Duration(float64(span) * (2.0*rand.Float64() - 1.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func backoffRetry(resendMax time.Duration, f func() (*dhcp4.Packet, error)) (*dhcp4.Packet, error) {
|
func backoffRetry(ctx context.Context, resendMax time.Duration, f func() (*nclient4.Lease, error)) (*nclient4.Lease, error) {
|
||||||
baseDelay := resendDelay0
|
baseDelay := resendDelay0
|
||||||
var sleepTime time.Duration
|
var sleepTime time.Duration
|
||||||
fastRetryLimit := resendFastMax
|
fastRetryLimit := resendFastMax
|
||||||
@ -471,33 +470,23 @@ func backoffRetry(resendMax time.Duration, f func() (*dhcp4.Packet, error)) (*dh
|
|||||||
|
|
||||||
log.Printf("retrying in %f seconds", sleepTime.Seconds())
|
log.Printf("retrying in %f seconds", sleepTime.Seconds())
|
||||||
|
|
||||||
time.Sleep(sleepTime)
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
// only adjust delay time if we are in normal backoff stage
|
return nil, context.Cause(ctx)
|
||||||
if baseDelay < resendMax && fastRetryLimit == 0 {
|
case <-time.After(sleepTime):
|
||||||
baseDelay *= 2
|
// only adjust delay time if we are in normal backoff stage
|
||||||
} else if fastRetryLimit == 0 { // only break if we are at normal delay
|
if baseDelay < resendMax && fastRetryLimit == 0 {
|
||||||
break
|
baseDelay *= 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, errNoMoreTries
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDHCPClient(
|
func newDHCPClient(
|
||||||
link netlink.Link,
|
link netlink.Link,
|
||||||
timeout time.Duration,
|
timeout time.Duration,
|
||||||
broadcast bool,
|
clientOpts ...nclient4.ClientOpt,
|
||||||
) (*dhcp4client.Client, error) {
|
) (*nclient4.Client, error) {
|
||||||
pktsock, err := dhcp4client.NewPacketSock(link.Attrs().Index)
|
clientOpts = append(clientOpts, nclient4.WithTimeout(timeout))
|
||||||
if err != nil {
|
return nclient4.New(link.Attrs().Name, clientOpts...)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return dhcp4client.New(
|
|
||||||
dhcp4client.HardwareAddr(link.Attrs().HardwareAddr),
|
|
||||||
dhcp4client.Timeout(timeout),
|
|
||||||
dhcp4client.Broadcast(broadcast),
|
|
||||||
dhcp4client.Connection(pktsock),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,8 @@ type IPAMConfig struct {
|
|||||||
// To override default requesting fields, set `skipDefault` to `false`.
|
// To override default requesting fields, set `skipDefault` to `false`.
|
||||||
// If an field is not optional, but the server failed to provide it, error will be raised.
|
// If an field is not optional, but the server failed to provide it, error will be raised.
|
||||||
RequestOptions []RequestOption `json:"request"`
|
RequestOptions []RequestOption `json:"request"`
|
||||||
|
// The metric of routes
|
||||||
|
Priority int `json:"priority,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DHCPOption represents a DHCP option. It can be a number, or a string defined in manual dhcp-options(5).
|
// DHCPOption represents a DHCP option. It can be a number, or a string defined in manual dhcp-options(5).
|
||||||
@ -78,25 +80,33 @@ func main() {
|
|||||||
var broadcast bool
|
var broadcast bool
|
||||||
var timeout time.Duration
|
var timeout time.Duration
|
||||||
var resendMax time.Duration
|
var resendMax time.Duration
|
||||||
|
var resendTimeout time.Duration
|
||||||
daemonFlags := flag.NewFlagSet("daemon", flag.ExitOnError)
|
daemonFlags := flag.NewFlagSet("daemon", flag.ExitOnError)
|
||||||
daemonFlags.StringVar(&pidfilePath, "pidfile", "", "optional path to write daemon PID to")
|
daemonFlags.StringVar(&pidfilePath, "pidfile", "", "optional path to write daemon PID to")
|
||||||
daemonFlags.StringVar(&hostPrefix, "hostprefix", "", "optional prefix to host root")
|
daemonFlags.StringVar(&hostPrefix, "hostprefix", "", "optional prefix to host root")
|
||||||
daemonFlags.StringVar(&socketPath, "socketpath", "", "optional dhcp server socketpath")
|
daemonFlags.StringVar(&socketPath, "socketpath", "", "optional dhcp server socketpath")
|
||||||
daemonFlags.BoolVar(&broadcast, "broadcast", false, "broadcast DHCP leases")
|
daemonFlags.BoolVar(&broadcast, "broadcast", false, "broadcast DHCP leases")
|
||||||
daemonFlags.DurationVar(&timeout, "timeout", 10*time.Second, "optional dhcp client timeout duration")
|
daemonFlags.DurationVar(&timeout, "timeout", 10*time.Second, "optional dhcp client timeout duration for each request")
|
||||||
daemonFlags.DurationVar(&resendMax, "resendmax", resendDelayMax, "optional dhcp client resend max duration")
|
daemonFlags.DurationVar(&resendMax, "resendmax", resendDelayMax, "optional dhcp client max resend delay between requests")
|
||||||
|
daemonFlags.DurationVar(&resendTimeout, "resendtimeout", defaultResendTimeout, "optional dhcp client resend timeout, no more retries after this timeout")
|
||||||
daemonFlags.Parse(os.Args[2:])
|
daemonFlags.Parse(os.Args[2:])
|
||||||
|
|
||||||
if socketPath == "" {
|
if socketPath == "" {
|
||||||
socketPath = defaultSocketPath
|
socketPath = defaultSocketPath
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := runDaemon(pidfilePath, hostPrefix, socketPath, timeout, resendMax, broadcast); err != nil {
|
if err := runDaemon(pidfilePath, hostPrefix, socketPath, timeout, resendMax, resendTimeout, broadcast); err != nil {
|
||||||
log.Print(err.Error())
|
log.Print(err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("dhcp"))
|
skel.PluginMainFuncs(skel.CNIFuncs{
|
||||||
|
Add: cmdAdd,
|
||||||
|
Check: cmdCheck,
|
||||||
|
Del: cmdDel,
|
||||||
|
/* FIXME GC */
|
||||||
|
/* FIXME Status */
|
||||||
|
}, version.All, bv.BuildString("dhcp"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,13 +15,11 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/d2g/dhcp4"
|
dhcp4 "github.com/insomniacslk/dhcp/dhcpv4"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
)
|
)
|
||||||
@ -31,8 +29,8 @@ var optionNameToID = map[string]dhcp4.OptionCode{
|
|||||||
"subnet-mask": dhcp4.OptionSubnetMask,
|
"subnet-mask": dhcp4.OptionSubnetMask,
|
||||||
"routers": dhcp4.OptionRouter,
|
"routers": dhcp4.OptionRouter,
|
||||||
"host-name": dhcp4.OptionHostName,
|
"host-name": dhcp4.OptionHostName,
|
||||||
"user-class": dhcp4.OptionUserClass,
|
"user-class": dhcp4.OptionUserClassInformation,
|
||||||
"vendor-class-identifier": dhcp4.OptionVendorClassIdentifier,
|
"vendor-class-identifier": dhcp4.OptionClassIdentifier,
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseOptionName(option string) (dhcp4.OptionCode, error) {
|
func parseOptionName(option string) (dhcp4.OptionCode, error) {
|
||||||
@ -41,18 +39,9 @@ func parseOptionName(option string) (dhcp4.OptionCode, error) {
|
|||||||
}
|
}
|
||||||
i, err := strconv.ParseUint(option, 10, 8)
|
i, err := strconv.ParseUint(option, 10, 8)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("Can not parse option: %w", err)
|
return dhcp4.OptionPad, fmt.Errorf("Can not parse option: %w", err)
|
||||||
}
|
}
|
||||||
return dhcp4.OptionCode(i), nil
|
return dhcp4.GenericOptionCode(i), nil
|
||||||
}
|
|
||||||
|
|
||||||
func parseRouter(opts dhcp4.Options) net.IP {
|
|
||||||
if opts, ok := opts[dhcp4.OptionRouter]; ok {
|
|
||||||
if len(opts) == 4 {
|
|
||||||
return net.IP(opts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func classfulSubnet(sn net.IP) net.IPNet {
|
func classfulSubnet(sn net.IP) net.IPNet {
|
||||||
@ -62,100 +51,22 @@ func classfulSubnet(sn net.IP) net.IPNet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseRoutes(opts dhcp4.Options) []*types.Route {
|
func parseRoutes(opt []byte) []*types.Route {
|
||||||
// StaticRoutes format: pairs of:
|
// StaticRoutes format: pairs of:
|
||||||
// Dest = 4 bytes; Classful IP subnet
|
// Dest = 4 bytes; Classful IP subnet
|
||||||
// Router = 4 bytes; IP address of router
|
// Router = 4 bytes; IP address of router
|
||||||
|
|
||||||
routes := []*types.Route{}
|
routes := []*types.Route{}
|
||||||
if opt, ok := opts[dhcp4.OptionStaticRoute]; ok {
|
for len(opt) >= 8 {
|
||||||
for len(opt) >= 8 {
|
sn := opt[0:4]
|
||||||
sn := opt[0:4]
|
r := opt[4:8]
|
||||||
r := opt[4:8]
|
rt := &types.Route{
|
||||||
rt := &types.Route{
|
Dst: classfulSubnet(sn),
|
||||||
Dst: classfulSubnet(sn),
|
GW: r,
|
||||||
GW: r,
|
|
||||||
}
|
|
||||||
routes = append(routes, rt)
|
|
||||||
opt = opt[8:]
|
|
||||||
}
|
}
|
||||||
|
routes = append(routes, rt)
|
||||||
|
opt = opt[8:]
|
||||||
}
|
}
|
||||||
|
|
||||||
return routes
|
return routes
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseCIDRRoutes(opts dhcp4.Options) []*types.Route {
|
|
||||||
// See RFC4332 for format (http://tools.ietf.org/html/rfc3442)
|
|
||||||
|
|
||||||
routes := []*types.Route{}
|
|
||||||
if opt, ok := opts[dhcp4.OptionClasslessRouteFormat]; ok {
|
|
||||||
for len(opt) >= 5 {
|
|
||||||
width := int(opt[0])
|
|
||||||
if width > 32 {
|
|
||||||
// error: can't have more than /32
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// network bits are compacted to avoid zeros
|
|
||||||
octets := 0
|
|
||||||
if width > 0 {
|
|
||||||
octets = (width-1)/8 + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(opt) < 1+octets+4 {
|
|
||||||
// error: too short
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
sn := make([]byte, 4)
|
|
||||||
copy(sn, opt[1:octets+1])
|
|
||||||
|
|
||||||
gw := net.IP(opt[octets+1 : octets+5])
|
|
||||||
|
|
||||||
rt := &types.Route{
|
|
||||||
Dst: net.IPNet{
|
|
||||||
IP: net.IP(sn),
|
|
||||||
Mask: net.CIDRMask(width, 32),
|
|
||||||
},
|
|
||||||
GW: gw,
|
|
||||||
}
|
|
||||||
routes = append(routes, rt)
|
|
||||||
|
|
||||||
opt = opt[octets+5:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return routes
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseSubnetMask(opts dhcp4.Options) net.IPMask {
|
|
||||||
mask, ok := opts[dhcp4.OptionSubnetMask]
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return net.IPMask(mask)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseDuration(opts dhcp4.Options, code dhcp4.OptionCode, optName string) (time.Duration, error) {
|
|
||||||
val, ok := opts[code]
|
|
||||||
if !ok {
|
|
||||||
return 0, fmt.Errorf("option %v not found", optName)
|
|
||||||
}
|
|
||||||
if len(val) != 4 {
|
|
||||||
return 0, fmt.Errorf("option %v is not 4 bytes", optName)
|
|
||||||
}
|
|
||||||
|
|
||||||
secs := binary.BigEndian.Uint32(val)
|
|
||||||
return time.Duration(secs) * time.Second, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseLeaseTime(opts dhcp4.Options) (time.Duration, error) {
|
|
||||||
return parseDuration(opts, dhcp4.OptionIPAddressLeaseTime, "LeaseTime")
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseRenewalTime(opts dhcp4.Options) (time.Duration, error) {
|
|
||||||
return parseDuration(opts, dhcp4.OptionRenewalTimeValue, "RenewalTime")
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseRebindingTime(opts dhcp4.Options) (time.Duration, error) {
|
|
||||||
return parseDuration(opts, dhcp4.OptionRebindingTimeValue, "RebindingTime")
|
|
||||||
}
|
|
||||||
|
@ -19,7 +19,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/d2g/dhcp4"
|
dhcp4 "github.com/insomniacslk/dhcp/dhcpv4"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
)
|
)
|
||||||
@ -61,17 +61,8 @@ func validateRoutes(t *testing.T, routes []*types.Route) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseRoutes(t *testing.T) {
|
func TestParseRoutes(t *testing.T) {
|
||||||
opts := make(dhcp4.Options)
|
data := []byte{10, 0, 0, 0, 10, 1, 2, 3, 192, 168, 1, 0, 192, 168, 2, 3}
|
||||||
opts[dhcp4.OptionStaticRoute] = []byte{10, 0, 0, 0, 10, 1, 2, 3, 192, 168, 1, 0, 192, 168, 2, 3}
|
routes := parseRoutes(data)
|
||||||
routes := parseRoutes(opts)
|
|
||||||
|
|
||||||
validateRoutes(t, routes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseCIDRRoutes(t *testing.T) {
|
|
||||||
opts := make(dhcp4.Options)
|
|
||||||
opts[dhcp4.OptionClasslessRouteFormat] = []byte{8, 10, 10, 1, 2, 3, 24, 192, 168, 1, 192, 168, 2, 3}
|
|
||||||
routes := parseCIDRRoutes(opts)
|
|
||||||
|
|
||||||
validateRoutes(t, routes)
|
validateRoutes(t, routes)
|
||||||
}
|
}
|
||||||
@ -87,10 +78,10 @@ func TestParseOptionName(t *testing.T) {
|
|||||||
"hostname", "host-name", dhcp4.OptionHostName, false,
|
"hostname", "host-name", dhcp4.OptionHostName, false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"hostname in number", "12", dhcp4.OptionHostName, false,
|
"hostname in number", "12", dhcp4.GenericOptionCode(12), false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"random string", "doNotparseMe", 0, true,
|
"random string", "doNotparseMe", dhcp4.OptionPad, true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
@ -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)
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
@ -29,7 +30,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("host-local"))
|
skel.PluginMainFuncs(skel.CNIFuncs{
|
||||||
|
Add: cmdAdd,
|
||||||
|
Check: cmdCheck,
|
||||||
|
Del: cmdDel,
|
||||||
|
/* FIXME GC */
|
||||||
|
/* FIXME Status */
|
||||||
|
}, version.All, bv.BuildString("host-local"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdCheck(args *skel.CmdArgs) error {
|
func cmdCheck(args *skel.CmdArgs) error {
|
||||||
@ -124,7 +131,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
for _, ip := range requestedIPs {
|
for _, ip := range requestedIPs {
|
||||||
errstr = errstr + " " + ip.String()
|
errstr = errstr + " " + ip.String()
|
||||||
}
|
}
|
||||||
return fmt.Errorf(errstr)
|
return errors.New(errstr)
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Routes = ipamConf.Routes
|
result.Routes = ipamConf.Routes
|
||||||
@ -145,18 +152,18 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
defer store.Close()
|
defer store.Close()
|
||||||
|
|
||||||
// Loop through all ranges, releasing all IPs, even if an error occurs
|
// Loop through all ranges, releasing all IPs, even if an error occurs
|
||||||
var errors []string
|
var errs []string
|
||||||
for idx, rangeset := range ipamConf.Ranges {
|
for idx, rangeset := range ipamConf.Ranges {
|
||||||
ipAllocator := allocator.NewIPAllocator(&rangeset, store, idx)
|
ipAllocator := allocator.NewIPAllocator(&rangeset, store, idx)
|
||||||
|
|
||||||
err := ipAllocator.Release(args.ContainerID, args.IfName)
|
err := ipAllocator.Release(args.ContainerID, args.IfName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors = append(errors, err.Error())
|
errs = append(errs, err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if errors != nil {
|
if errs != nil {
|
||||||
return fmt.Errorf(strings.Join(errors, ";"))
|
return errors.New(strings.Join(errs, ";"))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,13 @@ type Address struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("static"))
|
skel.PluginMainFuncs(skel.CNIFuncs{
|
||||||
|
Add: cmdAdd,
|
||||||
|
Check: cmdCheck,
|
||||||
|
Del: cmdDel,
|
||||||
|
/* FIXME GC */
|
||||||
|
/* FIXME Status */
|
||||||
|
}, version.All, bv.BuildString("static"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadNetConf(bytes []byte) (*types.NetConf, string, error) {
|
func loadNetConf(bytes []byte) (*types.NetConf, string, error) {
|
||||||
|
@ -35,7 +35,6 @@ import (
|
|||||||
"github.com/containernetworking/plugins/pkg/ipam"
|
"github.com/containernetworking/plugins/pkg/ipam"
|
||||||
"github.com/containernetworking/plugins/pkg/link"
|
"github.com/containernetworking/plugins/pkg/link"
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
"github.com/containernetworking/plugins/pkg/utils"
|
|
||||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
||||||
"github.com/containernetworking/plugins/pkg/utils/sysctl"
|
"github.com/containernetworking/plugins/pkg/utils/sysctl"
|
||||||
)
|
)
|
||||||
@ -47,19 +46,22 @@ 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"`
|
IPMasqBackend *string `json:"ipMasqBackend,omitempty"`
|
||||||
HairpinMode bool `json:"hairpinMode"`
|
MTU int `json:"mtu"`
|
||||||
PromiscMode bool `json:"promiscMode"`
|
HairpinMode bool `json:"hairpinMode"`
|
||||||
Vlan int `json:"vlan"`
|
PromiscMode bool `json:"promiscMode"`
|
||||||
VlanTrunk []*VlanTrunk `json:"vlanTrunk,omitempty"`
|
Vlan int `json:"vlan"`
|
||||||
PreserveDefaultVlan bool `json:"preserveDefaultVlan"`
|
VlanTrunk []*VlanTrunk `json:"vlanTrunk,omitempty"`
|
||||||
MacSpoofChk bool `json:"macspoofchk,omitempty"`
|
PreserveDefaultVlan bool `json:"preserveDefaultVlan"`
|
||||||
EnableDad bool `json:"enabledad,omitempty"`
|
MacSpoofChk bool `json:"macspoofchk,omitempty"`
|
||||||
|
EnableDad bool `json:"enabledad,omitempty"`
|
||||||
|
DisableContainerInterface bool `json:"disableContainerInterface,omitempty"`
|
||||||
|
PortIsolation bool `json:"portIsolation,omitempty"`
|
||||||
|
|
||||||
Args struct {
|
Args struct {
|
||||||
Cni BridgeArgs `json:"cni,omitempty"`
|
Cni BridgeArgs `json:"cni,omitempty"`
|
||||||
@ -334,16 +336,11 @@ func bridgeByName(name string) (*netlink.Bridge, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ensureBridge(brName string, mtu int, promiscMode, vlanFiltering bool) (*netlink.Bridge, error) {
|
func ensureBridge(brName string, mtu int, promiscMode, vlanFiltering bool) (*netlink.Bridge, error) {
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = brName
|
||||||
|
linkAttrs.MTU = mtu
|
||||||
br := &netlink.Bridge{
|
br := &netlink.Bridge{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: brName,
|
|
||||||
MTU: mtu,
|
|
||||||
// Let kernel use default txqueuelen; leaving it unset
|
|
||||||
// means 0, and a zero-length TX queue messes up FIFO
|
|
||||||
// traffic shapers which use TX queue length as the
|
|
||||||
// default packet limit
|
|
||||||
TxQLen: -1,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
if vlanFiltering {
|
if vlanFiltering {
|
||||||
br.VlanFiltering = &vlanFiltering
|
br.VlanFiltering = &vlanFiltering
|
||||||
@ -391,7 +388,7 @@ func ensureVlanInterface(br *netlink.Bridge, vlanID int, preserveDefaultVlan boo
|
|||||||
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, nil, preserveDefaultVlan, "")
|
_, brGatewayIface, err := setupVeth(hostNS, br, name, br.MTU, false, vlanID, nil, preserveDefaultVlan, "", false)
|
||||||
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)
|
||||||
}
|
}
|
||||||
@ -410,7 +407,18 @@ func ensureVlanInterface(br *netlink.Bridge, vlanID int, preserveDefaultVlan boo
|
|||||||
return brGatewayVeth, nil
|
return brGatewayVeth, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
func setupVeth(
|
||||||
|
netns ns.NetNS,
|
||||||
|
br *netlink.Bridge,
|
||||||
|
ifName string,
|
||||||
|
mtu int,
|
||||||
|
hairpinMode bool,
|
||||||
|
vlanID int,
|
||||||
|
vlans []int,
|
||||||
|
preserveDefaultVlan bool,
|
||||||
|
mac string,
|
||||||
|
portIsolation bool,
|
||||||
|
) (*current.Interface, *current.Interface, error) {
|
||||||
contIface := ¤t.Interface{}
|
contIface := ¤t.Interface{}
|
||||||
hostIface := ¤t.Interface{}
|
hostIface := ¤t.Interface{}
|
||||||
|
|
||||||
@ -447,6 +455,11 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set isolation mode
|
||||||
|
if err = netlink.LinkSetIsolated(hostVeth, portIsolation); err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to set isolated on for %v: %v", hostVeth.Attrs().Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
if (vlanID != 0 || len(vlans) > 0) && !preserveDefaultVlan {
|
if (vlanID != 0 || len(vlans) > 0) && !preserveDefaultVlan {
|
||||||
err = removeDefaultVlan(hostVeth)
|
err = removeDefaultVlan(hostVeth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -530,6 +543,10 @@ 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
|
||||||
}
|
}
|
||||||
@ -549,7 +566,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.vlans, n.PreserveDefaultVlan, n.mac)
|
hostInterface, containerInterface, err := setupVeth(netns, br, args.IfName, n.MTU, n.HairpinMode, n.Vlan, n.vlans, n.PreserveDefaultVlan, n.mac, n.PortIsolation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -668,20 +685,21 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if n.IPMasq {
|
if n.IPMasq {
|
||||||
chain := utils.FormatChainName(n.Name, args.ContainerID)
|
ipns := []*net.IPNet{}
|
||||||
comment := utils.FormatComment(n.Name, args.ContainerID)
|
|
||||||
for _, ipc := range result.IPs {
|
for _, ipc := range result.IPs {
|
||||||
if err = ip.SetupIPMasq(&ipc.Address, chain, comment); err != nil {
|
ipns = append(ipns, &ipc.Address)
|
||||||
return err
|
}
|
||||||
}
|
if err = ip.SetupIPMasqForNetworks(n.IPMasqBackend, ipns, n.Name, args.IfName, args.ContainerID); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} 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)
|
||||||
@ -692,23 +710,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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -779,7 +802,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.
|
||||||
@ -804,12 +826,8 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if isLayer3 && n.IPMasq {
|
if isLayer3 && n.IPMasq {
|
||||||
chain := utils.FormatChainName(n.Name, args.ContainerID)
|
if err := ip.TeardownIPMasqForNetworks(ipnets, n.Name, args.IfName, args.ContainerID); err != nil {
|
||||||
comment := utils.FormatComment(n.Name, args.ContainerID)
|
return err
|
||||||
for _, ipn := range ipnets {
|
|
||||||
if err := ip.TeardownIPMasq(ipn, chain, comment); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -817,7 +835,13 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("bridge"))
|
skel.PluginMainFuncs(skel.CNIFuncs{
|
||||||
|
Add: cmdAdd,
|
||||||
|
Check: cmdCheck,
|
||||||
|
Del: cmdDel,
|
||||||
|
Status: cmdStatus,
|
||||||
|
/* FIXME GC */
|
||||||
|
}, version.All, bv.BuildString("bridge"))
|
||||||
}
|
}
|
||||||
|
|
||||||
type cniBridgeIf struct {
|
type cniBridgeIf struct {
|
||||||
@ -1078,3 +1102,18 @@ func cmdCheck(args *skel.CmdArgs) error {
|
|||||||
func uniqueID(containerID, cniIface string) string {
|
func uniqueID(containerID, cniIface string) string {
|
||||||
return containerID + "-" + cniIface
|
return containerID + "-" + cniIface
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cmdStatus(args *skel.CmdArgs) error {
|
||||||
|
conf := NetConf{}
|
||||||
|
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
|
||||||
|
return fmt.Errorf("failed to load netconf: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.IPAM.Type != "" {
|
||||||
|
if err := ipam.ExecStatus(conf.IPAM.Type, args.StdinData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
@ -27,6 +28,7 @@ import (
|
|||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
"github.com/vishvananda/netlink/nl"
|
"github.com/vishvananda/netlink/nl"
|
||||||
|
"sigs.k8s.io/knftables"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
@ -77,11 +79,15 @@ type testCase struct {
|
|||||||
vlanTrunk []*VlanTrunk
|
vlanTrunk []*VlanTrunk
|
||||||
removeDefaultVlan bool
|
removeDefaultVlan bool
|
||||||
ipMasq bool
|
ipMasq bool
|
||||||
|
ipMasqBackend string
|
||||||
macspoofchk bool
|
macspoofchk bool
|
||||||
AddErr020 string
|
disableContIface bool
|
||||||
DelErr020 string
|
portIsolation bool
|
||||||
AddErr010 string
|
|
||||||
DelErr010 string
|
AddErr020 string
|
||||||
|
DelErr020 string
|
||||||
|
AddErr010 string
|
||||||
|
DelErr010 string
|
||||||
|
|
||||||
envArgs string // CNI_ARGS
|
envArgs string // CNI_ARGS
|
||||||
runtimeConfig struct {
|
runtimeConfig struct {
|
||||||
@ -154,6 +160,12 @@ const (
|
|||||||
netDefault = `,
|
netDefault = `,
|
||||||
"isDefaultGateway": true`
|
"isDefaultGateway": true`
|
||||||
|
|
||||||
|
disableContainerInterface = `,
|
||||||
|
"disableContainerInterface": true`
|
||||||
|
|
||||||
|
portIsolation = `,
|
||||||
|
"portIsolation": true`
|
||||||
|
|
||||||
ipamStartStr = `,
|
ipamStartStr = `,
|
||||||
"ipam": {
|
"ipam": {
|
||||||
"type": "host-local"`
|
"type": "host-local"`
|
||||||
@ -167,6 +179,9 @@ const (
|
|||||||
ipMasqConfStr = `,
|
ipMasqConfStr = `,
|
||||||
"ipMasq": %t`
|
"ipMasq": %t`
|
||||||
|
|
||||||
|
ipMasqBackendConfStr = `,
|
||||||
|
"ipMasqBackend": "%s"`
|
||||||
|
|
||||||
// Single subnet configuration (legacy)
|
// Single subnet configuration (legacy)
|
||||||
subnetConfStr = `,
|
subnetConfStr = `,
|
||||||
"subnet": "%s"`
|
"subnet": "%s"`
|
||||||
@ -238,6 +253,9 @@ func (tc testCase) netConfJSON(dataDir string) string {
|
|||||||
if tc.ipMasq {
|
if tc.ipMasq {
|
||||||
conf += tc.ipMasqConfig()
|
conf += tc.ipMasqConfig()
|
||||||
}
|
}
|
||||||
|
if tc.ipMasqBackend != "" {
|
||||||
|
conf += tc.ipMasqBackendConfig()
|
||||||
|
}
|
||||||
if tc.args.cni.mac != "" {
|
if tc.args.cni.mac != "" {
|
||||||
conf += fmt.Sprintf(argsFormat, tc.args.cni.mac)
|
conf += fmt.Sprintf(argsFormat, tc.args.cni.mac)
|
||||||
}
|
}
|
||||||
@ -248,6 +266,14 @@ 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.portIsolation {
|
||||||
|
conf += portIsolation
|
||||||
|
}
|
||||||
|
|
||||||
if !tc.isLayer2 {
|
if !tc.isLayer2 {
|
||||||
conf += netDefault
|
conf += netDefault
|
||||||
if tc.subnet != "" || tc.ranges != nil {
|
if tc.subnet != "" || tc.ranges != nil {
|
||||||
@ -286,6 +312,11 @@ func (tc testCase) ipMasqConfig() string {
|
|||||||
return conf
|
return conf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tc testCase) ipMasqBackendConfig() string {
|
||||||
|
conf := fmt.Sprintf(ipMasqBackendConfStr, tc.ipMasqBackend)
|
||||||
|
return conf
|
||||||
|
}
|
||||||
|
|
||||||
func (tc testCase) rangesConfig() string {
|
func (tc testCase) rangesConfig() string {
|
||||||
conf := rangesStartStr
|
conf := rangesStartStr
|
||||||
for i, tcRange := range tc.ranges {
|
for i, tcRange := range tc.ranges {
|
||||||
@ -485,6 +516,11 @@ type (
|
|||||||
|
|
||||||
func newTesterByVersion(version string, testNS, targetNS ns.NetNS) cmdAddDelTester {
|
func newTesterByVersion(version string, testNS, targetNS ns.NetNS) cmdAddDelTester {
|
||||||
switch {
|
switch {
|
||||||
|
case strings.HasPrefix(version, "1.1."):
|
||||||
|
return &testerV10x{
|
||||||
|
testNS: testNS,
|
||||||
|
targetNS: targetNS,
|
||||||
|
}
|
||||||
case strings.HasPrefix(version, "1.0."):
|
case strings.HasPrefix(version, "1.0."):
|
||||||
return &testerV10x{
|
return &testerV10x{
|
||||||
testNS: testNS,
|
testNS: testNS,
|
||||||
@ -621,6 +657,10 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
Expect(link).To(BeAssignableToTypeOf(&netlink.Veth{}))
|
Expect(link).To(BeAssignableToTypeOf(&netlink.Veth{}))
|
||||||
tester.vethName = result.Interfaces[1].Name
|
tester.vethName = result.Interfaces[1].Name
|
||||||
|
|
||||||
|
protInfo, err := netlink.LinkGetProtinfo(link)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(protInfo.Isolated).To(Equal(tc.portIsolation), "link isolation should be on when portIsolation is set")
|
||||||
|
|
||||||
// check vlan exist on the veth interface
|
// check vlan exist on the veth interface
|
||||||
if tc.vlan != 0 {
|
if tc.vlan != 0 {
|
||||||
interfaceMap, err := netlink.BridgeVlanList()
|
interfaceMap, err := netlink.BridgeVlanList()
|
||||||
@ -677,14 +717,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(addrs).To(HaveLen(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(addrs).To(HaveLen(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
|
||||||
@ -713,7 +755,7 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, route := range routes {
|
for _, route := range routes {
|
||||||
*found = (route.Dst == nil && route.Src == nil && route.Gw.Equal(gwIP))
|
*found = (ip.IsIPNetZero(route.Dst) && route.Src == nil && route.Gw.Equal(gwIP))
|
||||||
if *found {
|
if *found {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -728,6 +770,15 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
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)
|
tester.args = tc.createCheckCmdArgs(tester.targetNS, conf)
|
||||||
@ -789,7 +840,7 @@ func (tester *testerV10x) cmdCheckTest(tc testCase, conf *Net, _ string) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, route := range routes {
|
for _, route := range routes {
|
||||||
*found = (route.Dst == nil && route.Src == nil && route.Gw.Equal(gwIP))
|
*found = (ip.IsIPNetZero(route.Dst) && route.Src == nil && route.Gw.Equal(gwIP))
|
||||||
if *found {
|
if *found {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1008,8 +1059,9 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(addrs).To(HaveLen(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(addrs).To(HaveLen(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
|
||||||
@ -1038,7 +1090,7 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, route := range routes {
|
for _, route := range routes {
|
||||||
*found = (route.Dst == nil && route.Src == nil && route.Gw.Equal(gwIP))
|
*found = (ip.IsIPNetZero(route.Dst) && route.Src == nil && route.Gw.Equal(gwIP))
|
||||||
if *found {
|
if *found {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1053,6 +1105,14 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
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)
|
tester.args = tc.createCheckCmdArgs(tester.targetNS, conf)
|
||||||
@ -1114,7 +1174,7 @@ func (tester *testerV04x) cmdCheckTest(tc testCase, conf *Net, _ string) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, route := range routes {
|
for _, route := range routes {
|
||||||
*found = (route.Dst == nil && route.Src == nil && route.Gw.Equal(gwIP))
|
*found = (ip.IsIPNetZero(route.Dst) && route.Src == nil && route.Gw.Equal(gwIP))
|
||||||
if *found {
|
if *found {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1362,7 +1422,7 @@ func (tester *testerV03x) cmdAddTest(tc testCase, dataDir string) (types.Result,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, route := range routes {
|
for _, route := range routes {
|
||||||
*found = (route.Dst == nil && route.Src == nil && route.Gw.Equal(gwIP))
|
*found = (ip.IsIPNetZero(route.Dst) && route.Src == nil && route.Gw.Equal(gwIP))
|
||||||
if *found {
|
if *found {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1440,6 +1500,14 @@ func (tester *testerV01xOr02x) cmdAddTest(tc testCase, dataDir string) (types.Re
|
|||||||
err := tester.testNS.Do(func(ns.NetNS) error {
|
err := tester.testNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
// check that STATUS is
|
||||||
|
if testutils.SpecVersionHasSTATUS(tc.cniVersion) {
|
||||||
|
err := testutils.CmdStatus(func() error {
|
||||||
|
return cmdStatus(&skel.CmdArgs{StdinData: []byte(tc.netConfJSON(dataDir))})
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
r, raw, err := testutils.CmdAddWithArgs(tester.args, func() error {
|
r, raw, err := testutils.CmdAddWithArgs(tester.args, func() error {
|
||||||
return cmdAdd(tester.args)
|
return cmdAdd(tester.args)
|
||||||
})
|
})
|
||||||
@ -1583,7 +1651,7 @@ func (tester *testerV01xOr02x) cmdAddTest(tc testCase, dataDir string) (types.Re
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, route := range routes {
|
for _, route := range routes {
|
||||||
*found = (route.Dst == nil && route.Src == nil && route.Gw.Equal(gwIP))
|
*found = (ip.IsIPNetZero(route.Dst) && route.Src == nil && route.Gw.Equal(gwIP))
|
||||||
if *found {
|
if *found {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1847,10 +1915,10 @@ var _ = Describe("bridge Operations", func() {
|
|||||||
err := originalNS.Do(func(ns.NetNS) error {
|
err := originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = BRNAME
|
||||||
err := netlink.LinkAdd(&netlink.Bridge{
|
err := netlink.LinkAdd(&netlink.Bridge{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: BRNAME,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
link, err := netlink.LinkByName(BRNAME)
|
link, err := netlink.LinkByName(BRNAME)
|
||||||
@ -2361,41 +2429,82 @@ var _ = Describe("bridge Operations", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if testutils.SpecVersionHasChaining(ver) {
|
if testutils.SpecVersionHasChaining(ver) {
|
||||||
It(fmt.Sprintf("[%s] configures a bridge and ipMasq rules", ver), func() {
|
for _, tc := range []testCase{
|
||||||
err := originalNS.Do(func(ns.NetNS) error {
|
{
|
||||||
defer GinkgoRecover()
|
ranges: []rangeInfo{{
|
||||||
tc := testCase{
|
subnet: "10.1.2.0/24",
|
||||||
ranges: []rangeInfo{{
|
}},
|
||||||
subnet: "10.1.2.0/24",
|
ipMasq: true,
|
||||||
}},
|
cniVersion: ver,
|
||||||
ipMasq: true,
|
},
|
||||||
cniVersion: ver,
|
{
|
||||||
}
|
ranges: []rangeInfo{{
|
||||||
|
subnet: "10.1.2.0/24",
|
||||||
|
}},
|
||||||
|
ipMasq: true,
|
||||||
|
ipMasqBackend: "iptables",
|
||||||
|
cniVersion: ver,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ranges: []rangeInfo{{
|
||||||
|
subnet: "10.1.2.0/24",
|
||||||
|
}},
|
||||||
|
ipMasq: true,
|
||||||
|
ipMasqBackend: "nftables",
|
||||||
|
cniVersion: ver,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
tc := tc
|
||||||
|
It(fmt.Sprintf("[%s] configures a bridge and ipMasq rules with ipMasqBackend %q", ver, tc.ipMasqBackend), func() {
|
||||||
|
err := originalNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
|
||||||
args := tc.createCmdArgs(originalNS, dataDir)
|
args := tc.createCmdArgs(originalNS, dataDir)
|
||||||
r, _, err := testutils.CmdAddWithArgs(args, func() error {
|
r, _, err := testutils.CmdAddWithArgs(args, func() error {
|
||||||
return cmdAdd(args)
|
return cmdAdd(args)
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
result, err := types100.GetResult(r)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(result.IPs).Should(HaveLen(1))
|
||||||
|
|
||||||
|
ip := result.IPs[0].Address.IP.String()
|
||||||
|
|
||||||
|
// Update this if the default ipmasq backend changes
|
||||||
|
switch tc.ipMasqBackend {
|
||||||
|
case "iptables", "":
|
||||||
|
ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
rules, err := ipt.List("nat", "POSTROUTING")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(rules).Should(ContainElement(ContainSubstring(ip)))
|
||||||
|
case "nftables":
|
||||||
|
nft, err := knftables.New(knftables.InetFamily, "cni_plugins_masquerade")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
rules, err := nft.ListRules(context.TODO(), "masq_checks")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
// FIXME: ListRules() doesn't return the actual rule strings,
|
||||||
|
// and we can't easily compute the ipmasq plugin's comment.
|
||||||
|
comments := 0
|
||||||
|
for _, r := range rules {
|
||||||
|
if r.Comment != nil {
|
||||||
|
comments++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expect(comments).To(Equal(1), "expected to find exactly one Rule with a comment")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = testutils.CmdDelWithArgs(args, func() error {
|
||||||
|
return cmdDel(args)
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
result, err := types100.GetResult(r)
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
Expect(result.IPs).Should(HaveLen(1))
|
|
||||||
|
|
||||||
ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
|
|
||||||
rules, err := ipt.List("nat", "POSTROUTING")
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
Expect(rules).Should(ContainElement(ContainSubstring(result.IPs[0].Address.IP.String())))
|
|
||||||
|
|
||||||
err = testutils.CmdDelWithArgs(args, func() error {
|
|
||||||
return cmdDel(args)
|
|
||||||
})
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
}
|
||||||
})
|
|
||||||
|
|
||||||
for i, tc := range []testCase{
|
for i, tc := range []testCase{
|
||||||
{
|
{
|
||||||
@ -2461,6 +2570,66 @@ 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(fmt.Sprintf("[%s] when port-isolation is off, should set the veth peer on node with isolation off", ver), func() {
|
||||||
|
Expect(originalNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
tc := testCase{
|
||||||
|
cniVersion: ver,
|
||||||
|
portIsolation: false,
|
||||||
|
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(fmt.Sprintf("[%s] when port-isolation is on, should set the veth peer on node with isolation on", ver), func() {
|
||||||
|
Expect(originalNS.Do(func(ns.NetNS) error {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
tc := testCase{
|
||||||
|
cniVersion: ver,
|
||||||
|
portIsolation: 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() {
|
||||||
|
@ -43,11 +43,12 @@ func parseNetConf(bytes []byte) (*types.NetConf, error) {
|
|||||||
func createDummy(ifName string, netns ns.NetNS) (*current.Interface, error) {
|
func createDummy(ifName string, netns ns.NetNS) (*current.Interface, error) {
|
||||||
dummy := ¤t.Interface{}
|
dummy := ¤t.Interface{}
|
||||||
|
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = ifName
|
||||||
|
linkAttrs.Namespace = netlink.NsFd(int(netns.Fd()))
|
||||||
|
|
||||||
dm := &netlink.Dummy{
|
dm := &netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: ifName,
|
|
||||||
Namespace: netlink.NsFd(int(netns.Fd())),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := netlink.LinkAdd(dm); err != nil {
|
if err := netlink.LinkAdd(dm); err != nil {
|
||||||
@ -136,7 +137,6 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
err = netns.Do(func(_ ns.NetNS) error {
|
err = netns.Do(func(_ ns.NetNS) error {
|
||||||
return ipam.ConfigureIface(args.IfName, result)
|
return ipam.ConfigureIface(args.IfName, result)
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -165,7 +165,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.
|
||||||
@ -181,7 +180,13 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("dummy"))
|
skel.PluginMainFuncs(skel.CNIFuncs{
|
||||||
|
Add: cmdAdd,
|
||||||
|
Check: cmdCheck,
|
||||||
|
Del: cmdDel,
|
||||||
|
Status: cmdStatus,
|
||||||
|
/* FIXME GC */
|
||||||
|
}, version.All, bv.BuildString("dummy"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdCheck(args *skel.CmdArgs) error {
|
func cmdCheck(args *skel.CmdArgs) error {
|
||||||
@ -290,3 +295,16 @@ func validateCniContainerInterface(intf current.Interface) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cmdStatus(args *skel.CmdArgs) error {
|
||||||
|
conf := types.NetConf{}
|
||||||
|
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
|
||||||
|
return fmt.Errorf("failed to load netconf: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ipam.ExecStatus(conf.IPAM.Type, args.StdinData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -106,7 +106,7 @@ type (
|
|||||||
|
|
||||||
func newTesterByVersion(version string) tester {
|
func newTesterByVersion(version string) tester {
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(version, "1.0."):
|
case strings.HasPrefix(version, "1."):
|
||||||
return &testerV10x{}
|
return &testerV10x{}
|
||||||
case strings.HasPrefix(version, "0.4."):
|
case strings.HasPrefix(version, "0.4."):
|
||||||
return &testerV04x{}
|
return &testerV04x{}
|
||||||
@ -180,11 +180,11 @@ var _ = Describe("dummy Operations", func() {
|
|||||||
err = originalNS.Do(func(ns.NetNS) error {
|
err = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = MASTER_NAME
|
||||||
// Add master
|
// Add master
|
||||||
err = netlink.LinkAdd(&netlink.Dummy{
|
err = netlink.LinkAdd(&netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: MASTER_NAME,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
m, err := netlink.LinkByName(MASTER_NAME)
|
m, err := netlink.LinkByName(MASTER_NAME)
|
||||||
@ -261,6 +261,13 @@ var _ = Describe("dummy Operations", func() {
|
|||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
if testutils.SpecVersionHasSTATUS(ver) {
|
||||||
|
err = testutils.CmdStatus(func() error {
|
||||||
|
return cmdStatus(args)
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
result, _, err = testutils.CmdAddWithArgs(args, func() error {
|
result, _, err = testutils.CmdAddWithArgs(args, func() error {
|
||||||
return cmdAdd(args)
|
return cmdAdd(args)
|
||||||
})
|
})
|
||||||
@ -371,7 +378,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 {
|
||||||
|
@ -37,7 +37,10 @@ import (
|
|||||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
||||||
)
|
)
|
||||||
|
|
||||||
var sysBusPCI = "/sys/bus/pci/devices"
|
var (
|
||||||
|
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
|
||||||
var userspaceDrivers = []string{"vfio-pci", "uio_pci_generic", "igb_uio"}
|
var userspaceDrivers = []string{"vfio-pci", "uio_pci_generic", "igb_uio"}
|
||||||
@ -53,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() {
|
||||||
@ -62,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
|
||||||
@ -69,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"`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,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)
|
||||||
}
|
}
|
||||||
@ -199,40 +230,121 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func moveLinkIn(hostDev netlink.Link, containerNs ns.NetNS, ifName string) (netlink.Link, error) {
|
func moveLinkIn(hostDev netlink.Link, containerNs ns.NetNS, containerIfName string) (netlink.Link, error) {
|
||||||
if err := netlink.LinkSetNsFd(hostDev, int(containerNs.Fd())); err != nil {
|
hostDevName := hostDev.Attrs().Name
|
||||||
return nil, err
|
|
||||||
|
// With recent kernels we could do all changes in a single netlink call,
|
||||||
|
// but on failure the device is left in a partially modified state.
|
||||||
|
// Doing changes one by one allow us to (try to) rollback to the initial state.
|
||||||
|
|
||||||
|
// Create a temporary namespace to rename (and modify) the device in.
|
||||||
|
// We were previously using a temporary name, but rapid rename leads to
|
||||||
|
// race condition with udev and NetworkManager.
|
||||||
|
tempNS, err := ns.TempNetNS()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create tempNS: %v", err)
|
||||||
|
}
|
||||||
|
defer tempNS.Close()
|
||||||
|
|
||||||
|
// Restore original up state in case of error
|
||||||
|
// This must be done in the hostNS as moving
|
||||||
|
// device between namespaces sets the link down
|
||||||
|
if hostDev.Attrs().Flags&net.FlagUp == net.FlagUp {
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
// lookup the device again (index might have changed)
|
||||||
|
if hostDev, err := netlink.LinkByName(hostDevName); err == nil {
|
||||||
|
_ = netlink.LinkSetUp(hostDev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the host device into tempNS
|
||||||
|
if err = netlink.LinkSetNsFd(hostDev, int(tempNS.Fd())); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to move %q to tempNS: %v", hostDevName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var contDev netlink.Link
|
var contDev netlink.Link
|
||||||
if err := containerNs.Do(func(_ ns.NetNS) error {
|
|
||||||
var err error
|
// In a container in container scenario, hostNS is not the initial net namespace,
|
||||||
contDev, err = netlink.LinkByName(hostDev.Attrs().Name)
|
// but host / container naming is easier to follow.
|
||||||
|
if err = tempNS.Do(func(hostNS ns.NetNS) error {
|
||||||
|
// lookup the device in tempNS (index might have changed)
|
||||||
|
tempNSDev, err := netlink.LinkByName(hostDevName)
|
||||||
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 in tempNS: %v", hostDevName, err)
|
||||||
}
|
}
|
||||||
// Devices can be renamed only when down
|
|
||||||
if err = netlink.LinkSetDown(contDev); err != nil {
|
// detroying a non empty tempNS would move physical devices back to the initial net namespace,
|
||||||
return fmt.Errorf("failed to set %q down: %v", hostDev.Attrs().Name, err)
|
// not the namespace of the "parent" process, and virtual devices would be destroyed,
|
||||||
|
// so we need to actively move the device back to hostNS on error
|
||||||
|
defer func() {
|
||||||
|
if err != nil && tempNSDev != nil {
|
||||||
|
_ = netlink.LinkSetNsFd(tempNSDev, int(hostNS.Fd()))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Rename the device to the wanted name
|
||||||
|
if err = netlink.LinkSetName(tempNSDev, containerIfName); err != nil {
|
||||||
|
return fmt.Errorf("failed to rename host device %q to %q: %v", hostDevName, containerIfName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restore the original device name in case of error
|
||||||
|
defer func() {
|
||||||
|
if err != nil && tempNSDev != nil {
|
||||||
|
_ = netlink.LinkSetName(tempNSDev, hostDevName)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// 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(tempNSDev, 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", hostDevName, err)
|
||||||
}
|
}
|
||||||
// Rename container device to respect args.IfName
|
|
||||||
if err := netlink.LinkSetName(contDev, ifName); err != nil {
|
// Remove the alias on error
|
||||||
return fmt.Errorf("failed to rename device %q to %q: %v", hostDev.Attrs().Name, ifName, err)
|
defer func() {
|
||||||
|
if err != nil && tempNSDev != nil {
|
||||||
|
_ = netlink.LinkSetAlias(tempNSDev, "")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Move the device to the containerNS
|
||||||
|
if err = netlink.LinkSetNsFd(tempNSDev, int(containerNs.Fd())); err != nil {
|
||||||
|
return fmt.Errorf("failed to move %q (host: %q) to container NS: %v", containerIfName, hostDevName, err)
|
||||||
}
|
}
|
||||||
// Bring container device up
|
|
||||||
if err = netlink.LinkSetUp(contDev); err != nil {
|
// Lookup the device again on error, the index might have changed
|
||||||
return fmt.Errorf("failed to set %q up: %v", ifName, err)
|
defer func() {
|
||||||
}
|
if err != nil {
|
||||||
// Retrieve link again to get up-to-date name and attributes
|
tempNSDev, _ = netlink.LinkByName(containerIfName)
|
||||||
contDev, err = netlink.LinkByName(ifName)
|
}
|
||||||
if err != nil {
|
}()
|
||||||
return fmt.Errorf("failed to find %q: %v", ifName, err)
|
|
||||||
}
|
err = containerNs.Do(func(_ ns.NetNS) error {
|
||||||
return nil
|
var err error
|
||||||
|
contDev, err = netlink.LinkByName(containerIfName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to find %q in container NS: %v", containerIfName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the interface back to tempNS on error
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = netlink.LinkSetNsFd(contDev, int(tempNS.Fd()))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Bring the device up
|
||||||
|
// This must be done in the containerNS
|
||||||
|
if err = netlink.LinkSetUp(contDev); err != nil {
|
||||||
|
return fmt.Errorf("failed to set %q up: %v", containerIfName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -240,45 +352,110 @@ func moveLinkIn(hostDev netlink.Link, containerNs ns.NetNS, ifName string) (netl
|
|||||||
return contDev, nil
|
return contDev, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func moveLinkOut(containerNs ns.NetNS, ifName string) error {
|
func moveLinkOut(containerNs ns.NetNS, containerIfName string) error {
|
||||||
defaultNs, err := ns.GetCurrentNS()
|
// Create a temporary namespace to rename (and modify) the device in.
|
||||||
|
// We were previously using a temporary name, but multiple rapid renames
|
||||||
|
// leads to race condition with udev and NetworkManager.
|
||||||
|
tempNS, err := ns.TempNetNS()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to create tempNS: %v", err)
|
||||||
}
|
}
|
||||||
defer defaultNs.Close()
|
defer tempNS.Close()
|
||||||
|
|
||||||
return containerNs.Do(func(_ ns.NetNS) error {
|
var contDev netlink.Link
|
||||||
dev, err := netlink.LinkByName(ifName)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to find %q: %v", ifName, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Devices can be renamed only when down
|
// Restore original up state in case of error
|
||||||
if err = netlink.LinkSetDown(dev); err != nil {
|
// This must be done in the containerNS as moving
|
||||||
return fmt.Errorf("failed to set %q down: %v", ifName, err)
|
// device between namespaces sets the link down
|
||||||
}
|
defer func() {
|
||||||
|
if err != nil && contDev != nil && contDev.Attrs().Flags&net.FlagUp == net.FlagUp {
|
||||||
defer func() {
|
containerNs.Do(func(_ ns.NetNS) error {
|
||||||
// If moving the device to the host namespace fails, set its name back to ifName so that this
|
// lookup the device again (index might have changed)
|
||||||
// function can be retried. Also bring the device back up, unless it was already down before.
|
if contDev, err := netlink.LinkByName(containerIfName); err == nil {
|
||||||
if err != nil {
|
_ = netlink.LinkSetUp(contDev)
|
||||||
_ = netlink.LinkSetName(dev, ifName)
|
|
||||||
if dev.Attrs().Flags&net.FlagUp == net.FlagUp {
|
|
||||||
_ = netlink.LinkSetUp(dev)
|
|
||||||
}
|
}
|
||||||
}
|
return nil
|
||||||
}()
|
})
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// Rename the device to its original name from the host namespace
|
err = containerNs.Do(func(_ ns.NetNS) error {
|
||||||
if err = netlink.LinkSetName(dev, dev.Attrs().Alias); err != nil {
|
var err error
|
||||||
return fmt.Errorf("failed to restore %q to original name %q: %v", ifName, dev.Attrs().Alias, err)
|
// Lookup the device in the containerNS
|
||||||
|
contDev, err = netlink.LinkByName(containerIfName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to find %q in containerNS: %v", containerIfName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = netlink.LinkSetNsFd(dev, int(defaultNs.Fd())); err != nil {
|
// Verify we have the original name
|
||||||
return fmt.Errorf("failed to move %q to host netns: %v", dev.Attrs().Alias, err)
|
if contDev.Attrs().Alias == "" {
|
||||||
|
return fmt.Errorf("failed to find original ifname for %q (alias is not set)", containerIfName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the device to the tempNS
|
||||||
|
if err = netlink.LinkSetNsFd(contDev, int(tempNS.Fd())); err != nil {
|
||||||
|
return fmt.Errorf("failed to move %q to tempNS: %v", containerIfName, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tempNS.Do(func(hostNS ns.NetNS) error {
|
||||||
|
// Lookup the device in tempNS (index might have changed)
|
||||||
|
tempNSDev, err := netlink.LinkByName(containerIfName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to find %q in tempNS: %v", containerIfName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the device back to containerNS on error
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = netlink.LinkSetNsFd(tempNSDev, int(containerNs.Fd()))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
hostDevName := tempNSDev.Attrs().Alias
|
||||||
|
|
||||||
|
// Rename container device to hostDevName
|
||||||
|
if err = netlink.LinkSetName(tempNSDev, hostDevName); err != nil {
|
||||||
|
return fmt.Errorf("failed to rename device %q to %q: %v", containerIfName, hostDevName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename the device back to containerIfName on error
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = netlink.LinkSetName(tempNSDev, containerIfName)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Unset device's alias property
|
||||||
|
if err = netlink.LinkSetAlias(tempNSDev, ""); err != nil {
|
||||||
|
return fmt.Errorf("failed to unset alias of %q: %v", hostDevName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set back the device alias to hostDevName on error
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = netlink.LinkSetAlias(tempNSDev, hostDevName)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Finally move the device to the hostNS
|
||||||
|
if err = netlink.LinkSetNsFd(tempNSDev, int(hostNS.Fd())); err != nil {
|
||||||
|
return fmt.Errorf("failed to move %q to hostNS: %v", hostDevName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// As we don't know the previous state, leave the link down
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasDpdkDriver(pciaddr string) (bool, error) {
|
func hasDpdkDriver(pciaddr string) (bool, error) {
|
||||||
@ -314,11 +491,19 @@ 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLink(devname, hwaddr, kernelpath, pciaddr string, auxDev string) (netlink.Link, error) {
|
||||||
switch {
|
switch {
|
||||||
|
|
||||||
case len(devname) > 0:
|
case len(devname) > 0:
|
||||||
@ -329,6 +514,11 @@ func getLink(devname, hwaddr, kernelpath, pciaddr string) (netlink.Link, error)
|
|||||||
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
|
||||||
@ -339,20 +529,7 @@ func getLink(devname, hwaddr, kernelpath, pciaddr string) (netlink.Link, error)
|
|||||||
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 {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case len(pciaddr) > 0:
|
case len(pciaddr) > 0:
|
||||||
netDir := filepath.Join(sysBusPCI, pciaddr, "net")
|
netDir := filepath.Join(sysBusPCI, pciaddr, "net")
|
||||||
if _, err := os.Lstat(netDir); err != nil {
|
if _, err := os.Lstat(netDir); err != nil {
|
||||||
@ -363,21 +540,23 @@ 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")
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("host-device"))
|
skel.PluginMainFuncs(skel.CNIFuncs{
|
||||||
|
Add: cmdAdd,
|
||||||
|
Check: cmdCheck,
|
||||||
|
Del: cmdDel,
|
||||||
|
Status: cmdStatus,
|
||||||
|
/* FIXME GC */
|
||||||
|
}, version.All, bv.BuildString("host-device"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdCheck(args *skel.CmdArgs) error {
|
func cmdCheck(args *skel.CmdArgs) error {
|
||||||
@ -484,3 +663,20 @@ func validateCniContainerInterface(intf current.Interface) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cmdStatus(args *skel.CmdArgs) error {
|
||||||
|
conf := NetConf{}
|
||||||
|
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
|
||||||
|
return fmt.Errorf("failed to load netconf: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.IPAM.Type != "" {
|
||||||
|
if err := ipam.ExecStatus(conf.IPAM.Type, args.StdinData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Check if host device exists.
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -231,7 +231,7 @@ type (
|
|||||||
|
|
||||||
func newTesterByVersion(version string) tester {
|
func newTesterByVersion(version string) tester {
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(version, "1.0."):
|
case strings.HasPrefix(version, "1."):
|
||||||
return &testerV10x{}
|
return &testerV10x{}
|
||||||
case strings.HasPrefix(version, "0.4."):
|
case strings.HasPrefix(version, "0.4."):
|
||||||
return &testerV04x{}
|
return &testerV04x{}
|
||||||
@ -341,10 +341,10 @@ var _ = Describe("base functionality", func() {
|
|||||||
// prepare ifname in original namespace
|
// prepare ifname in original namespace
|
||||||
_ = originalNS.Do(func(ns.NetNS) error {
|
_ = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = ifname
|
||||||
err := netlink.LinkAdd(&netlink.Dummy{
|
err := netlink.LinkAdd(&netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: ifname,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
origLink, err = netlink.LinkByName(ifname)
|
origLink, err = netlink.LinkByName(ifname)
|
||||||
@ -362,6 +362,15 @@ var _ = Describe("base functionality", func() {
|
|||||||
"type": "host-device",
|
"type": "host-device",
|
||||||
"device": %q
|
"device": %q
|
||||||
}`, ver, ifname)
|
}`, ver, ifname)
|
||||||
|
|
||||||
|
// if v1.1 or greater, call CmdStatus
|
||||||
|
if testutils.SpecVersionHasSTATUS(ver) {
|
||||||
|
err := testutils.CmdStatus(func() error {
|
||||||
|
return cmdStatus(&skel.CmdArgs{StdinData: []byte(conf)})
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
args := &skel.CmdArgs{
|
args := &skel.CmdArgs{
|
||||||
ContainerID: "dummy",
|
ContainerID: "dummy",
|
||||||
Netns: targetNS.Path(),
|
Netns: targetNS.Path(),
|
||||||
@ -422,10 +431,10 @@ var _ = Describe("base functionality", func() {
|
|||||||
// prepare host device in original namespace
|
// prepare host device in original namespace
|
||||||
_ = originalNS.Do(func(ns.NetNS) error {
|
_ = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = ifname
|
||||||
err := netlink.LinkAdd(&netlink.Dummy{
|
err := netlink.LinkAdd(&netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: ifname,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
origLink, err = netlink.LinkByName(ifname)
|
origLink, err = netlink.LinkByName(ifname)
|
||||||
@ -483,10 +492,10 @@ var _ = Describe("base functionality", func() {
|
|||||||
// create another conflict host device with same name "dummy0"
|
// create another conflict host device with same name "dummy0"
|
||||||
_ = originalNS.Do(func(ns.NetNS) error {
|
_ = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = ifname
|
||||||
err := netlink.LinkAdd(&netlink.Dummy{
|
err := netlink.LinkAdd(&netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: ifname,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
conflictLink, err = netlink.LinkByName(ifname)
|
conflictLink, err = netlink.LinkByName(ifname)
|
||||||
@ -608,10 +617,10 @@ var _ = Describe("base functionality", func() {
|
|||||||
// prepare ifname in original namespace
|
// prepare ifname in original namespace
|
||||||
_ = originalNS.Do(func(ns.NetNS) error {
|
_ = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = ifname
|
||||||
err := netlink.LinkAdd(&netlink.Dummy{
|
err := netlink.LinkAdd(&netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: ifname,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
origLink, err = netlink.LinkByName(ifname)
|
origLink, err = netlink.LinkByName(ifname)
|
||||||
@ -720,10 +729,10 @@ var _ = Describe("base functionality", func() {
|
|||||||
// prepare ifname in original namespace
|
// prepare ifname in original namespace
|
||||||
_ = originalNS.Do(func(ns.NetNS) error {
|
_ = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = ifname
|
||||||
err := netlink.LinkAdd(&netlink.Dummy{
|
err := netlink.LinkAdd(&netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: ifname,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
origLink, err = netlink.LinkByName(ifname)
|
origLink, err = netlink.LinkByName(ifname)
|
||||||
@ -898,16 +907,81 @@ 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()
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = ifname
|
||||||
|
err := netlink.LinkAdd(&netlink.Dummy{
|
||||||
|
LinkAttrs: linkAttrs,
|
||||||
|
})
|
||||||
|
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
|
||||||
|
|
||||||
// prepare ifname in original namespace
|
// prepare ifname in original namespace
|
||||||
_ = originalNS.Do(func(ns.NetNS) error {
|
_ = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = ifname
|
||||||
err := netlink.LinkAdd(&netlink.Dummy{
|
err := netlink.LinkAdd(&netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: ifname,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
origLink, err = netlink.LinkByName(ifname)
|
origLink, err = netlink.LinkByName(ifname)
|
||||||
@ -1028,10 +1102,10 @@ var _ = Describe("base functionality", func() {
|
|||||||
// prepare host device in original namespace
|
// prepare host device in original namespace
|
||||||
_ = originalNS.Do(func(ns.NetNS) error {
|
_ = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = ifname
|
||||||
err := netlink.LinkAdd(&netlink.Dummy{
|
err := netlink.LinkAdd(&netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: ifname,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
origLink, err = netlink.LinkByName(ifname)
|
origLink, err = netlink.LinkByName(ifname)
|
||||||
@ -1089,10 +1163,10 @@ var _ = Describe("base functionality", func() {
|
|||||||
// create another conflict host device with same name "dummy0"
|
// create another conflict host device with same name "dummy0"
|
||||||
_ = originalNS.Do(func(ns.NetNS) error {
|
_ = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = ifname
|
||||||
err := netlink.LinkAdd(&netlink.Dummy{
|
err := netlink.LinkAdd(&netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: ifname,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
conflictLink, err = netlink.LinkByName(ifname)
|
conflictLink, err = netlink.LinkByName(ifname)
|
||||||
@ -1149,6 +1223,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()
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = hostIfname
|
||||||
|
err := netlink.LinkAdd(&netlink.Dummy{
|
||||||
|
LinkAttrs: linkAttrs,
|
||||||
|
})
|
||||||
|
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()
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = containerAdditionalIfname
|
||||||
|
err := netlink.LinkAdd(&netlink.Dummy{
|
||||||
|
LinkAttrs: linkAttrs,
|
||||||
|
})
|
||||||
|
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
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1181,6 +1363,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
|
||||||
|
@ -144,14 +144,15 @@ func createIpvlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Interf
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.MTU = conf.MTU
|
||||||
|
linkAttrs.Name = tmpName
|
||||||
|
linkAttrs.ParentIndex = m.Attrs().Index
|
||||||
|
linkAttrs.Namespace = netlink.NsFd(int(netns.Fd()))
|
||||||
|
|
||||||
mv := &netlink.IPVlan{
|
mv := &netlink.IPVlan{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
MTU: conf.MTU,
|
Mode: mode,
|
||||||
Name: tmpName,
|
|
||||||
ParentIndex: m.Attrs().Index,
|
|
||||||
Namespace: netlink.NsFd(int(netns.Fd())),
|
|
||||||
},
|
|
||||||
Mode: mode,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if conf.LinkContNs {
|
if conf.LinkContNs {
|
||||||
@ -195,7 +196,7 @@ func getDefaultRouteInterfaceName() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range routeToDstIP {
|
for _, v := range routeToDstIP {
|
||||||
if v.Dst == nil {
|
if ip.IsIPNetZero(v.Dst) {
|
||||||
l, err := netlink.LinkByIndex(v.LinkIndex)
|
l, err := netlink.LinkByIndex(v.LinkIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -334,7 +335,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.
|
||||||
@ -350,7 +350,13 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("ipvlan"))
|
skel.PluginMainFuncs(skel.CNIFuncs{
|
||||||
|
Add: cmdAdd,
|
||||||
|
Check: cmdCheck,
|
||||||
|
Del: cmdDel,
|
||||||
|
Status: cmdStatus,
|
||||||
|
/* FIXME GC */
|
||||||
|
}, version.All, bv.BuildString("ipvlan"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdCheck(args *skel.CmdArgs) error {
|
func cmdCheck(args *skel.CmdArgs) error {
|
||||||
@ -485,3 +491,19 @@ func validateCniContainerInterface(intf current.Interface, modeExpected string)
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cmdStatus(args *skel.CmdArgs) error {
|
||||||
|
conf := NetConf{}
|
||||||
|
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
|
||||||
|
return fmt.Errorf("failed to load netconf: %w", err)
|
||||||
|
}
|
||||||
|
if conf.IPAM.Type != "" {
|
||||||
|
if err := ipam.ExecStatus(conf.IPAM.Type, args.StdinData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Check if master interface exists.
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -114,6 +114,13 @@ func ipvlanAddCheckDelTest(conf, masterName string, originalNS, targetNS ns.NetN
|
|||||||
err = originalNS.Do(func(ns.NetNS) error {
|
err = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
if testutils.SpecVersionHasSTATUS(cniVersion) {
|
||||||
|
err = testutils.CmdStatus(func() error {
|
||||||
|
return cmdStatus(args)
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
result, _, err = testutils.CmdAddWithArgs(args, func() error {
|
result, _, err = testutils.CmdAddWithArgs(args, func() error {
|
||||||
return cmdAdd(args)
|
return cmdAdd(args)
|
||||||
})
|
})
|
||||||
@ -214,7 +221,7 @@ type (
|
|||||||
|
|
||||||
func newTesterByVersion(version string) tester {
|
func newTesterByVersion(version string) tester {
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(version, "1.0."):
|
case strings.HasPrefix(version, "1."):
|
||||||
return &testerV10x{}
|
return &testerV10x{}
|
||||||
case strings.HasPrefix(version, "0.4.") || strings.HasPrefix(version, "0.3."):
|
case strings.HasPrefix(version, "0.4.") || strings.HasPrefix(version, "0.3."):
|
||||||
return &testerV04x{}
|
return &testerV04x{}
|
||||||
@ -281,11 +288,11 @@ var _ = Describe("ipvlan Operations", func() {
|
|||||||
err = originalNS.Do(func(ns.NetNS) error {
|
err = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = MASTER_NAME
|
||||||
// Add master
|
// Add master
|
||||||
err = netlink.LinkAdd(&netlink.Dummy{
|
err = netlink.LinkAdd(&netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: MASTER_NAME,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
_, err = netlink.LinkByName(MASTER_NAME)
|
_, err = netlink.LinkByName(MASTER_NAME)
|
||||||
@ -297,11 +304,11 @@ var _ = Describe("ipvlan Operations", func() {
|
|||||||
err = targetNS.Do(func(ns.NetNS) error {
|
err = targetNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = MASTER_NAME_INCONTAINER
|
||||||
// Add master
|
// Add master
|
||||||
err = netlink.LinkAdd(&netlink.Dummy{
|
err = netlink.LinkAdd(&netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: MASTER_NAME_INCONTAINER,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
_, err = netlink.LinkByName(MASTER_NAME_INCONTAINER)
|
_, err = netlink.LinkByName(MASTER_NAME_INCONTAINER)
|
||||||
|
@ -172,7 +172,13 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("loopback"))
|
skel.PluginMainFuncs(skel.CNIFuncs{
|
||||||
|
Add: cmdAdd,
|
||||||
|
Check: cmdCheck,
|
||||||
|
Del: cmdDel,
|
||||||
|
/* FIXME GC */
|
||||||
|
/* FIXME Status */
|
||||||
|
}, version.All, bv.BuildString("loopback"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdCheck(args *skel.CmdArgs) error {
|
func cmdCheck(args *skel.CmdArgs) error {
|
||||||
|
@ -74,7 +74,8 @@ var _ = Describe("Loopback", func() {
|
|||||||
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
|
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
Eventually(session).Should(gbytes.Say(`{.*}`))
|
// "(?s)" turns on the "s" flag, making "." match newlines too.
|
||||||
|
Eventually(session).Should(gbytes.Say(`(?s){.*}`))
|
||||||
Eventually(session).Should(gexec.Exit(0))
|
Eventually(session).Should(gexec.Exit(0))
|
||||||
|
|
||||||
var lo *net.Interface
|
var lo *net.Interface
|
||||||
|
@ -41,6 +41,7 @@ type NetConf struct {
|
|||||||
MTU int `json:"mtu"`
|
MTU int `json:"mtu"`
|
||||||
Mac string `json:"mac,omitempty"`
|
Mac string `json:"mac,omitempty"`
|
||||||
LinkContNs bool `json:"linkInContainer,omitempty"`
|
LinkContNs bool `json:"linkInContainer,omitempty"`
|
||||||
|
BcQueueLen uint32 `json:"bcqueuelen,omitempty"`
|
||||||
|
|
||||||
RuntimeConfig struct {
|
RuntimeConfig struct {
|
||||||
Mac string `json:"mac,omitempty"`
|
Mac string `json:"mac,omitempty"`
|
||||||
@ -67,7 +68,7 @@ func getDefaultRouteInterfaceName() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range routeToDstIP {
|
for _, v := range routeToDstIP {
|
||||||
if v.Dst == nil {
|
if ip.IsIPNetZero(v.Dst) {
|
||||||
l, err := netlink.LinkByIndex(v.LinkIndex)
|
l, err := netlink.LinkByIndex(v.LinkIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -225,12 +226,11 @@ func createMacvlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Inter
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
linkAttrs := netlink.LinkAttrs{
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
MTU: conf.MTU,
|
linkAttrs.MTU = conf.MTU
|
||||||
Name: tmpName,
|
linkAttrs.Name = tmpName
|
||||||
ParentIndex: m.Attrs().Index,
|
linkAttrs.ParentIndex = m.Attrs().Index
|
||||||
Namespace: netlink.NsFd(int(netns.Fd())),
|
linkAttrs.Namespace = netlink.NsFd(int(netns.Fd()))
|
||||||
}
|
|
||||||
|
|
||||||
if conf.Mac != "" {
|
if conf.Mac != "" {
|
||||||
addr, err := net.ParseMAC(conf.Mac)
|
addr, err := net.ParseMAC(conf.Mac)
|
||||||
@ -245,6 +245,8 @@ func createMacvlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Inter
|
|||||||
Mode: mode,
|
Mode: mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mv.BCQueueLen = conf.BcQueueLen
|
||||||
|
|
||||||
if conf.LinkContNs {
|
if conf.LinkContNs {
|
||||||
err = netns.Do(func(_ ns.NetNS) error {
|
err = netns.Do(func(_ ns.NetNS) error {
|
||||||
return netlink.LinkAdd(mv)
|
return netlink.LinkAdd(mv)
|
||||||
@ -411,7 +413,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.
|
||||||
@ -427,7 +428,13 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("macvlan"))
|
skel.PluginMainFuncs(skel.CNIFuncs{
|
||||||
|
Add: cmdAdd,
|
||||||
|
Check: cmdCheck,
|
||||||
|
Del: cmdDel,
|
||||||
|
Status: cmdStatus,
|
||||||
|
/* FIXME GC */
|
||||||
|
}, version.All, bv.BuildString("macvlan"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdCheck(args *skel.CmdArgs) error {
|
func cmdCheck(args *skel.CmdArgs) error {
|
||||||
@ -563,3 +570,20 @@ func validateCniContainerInterface(intf current.Interface, modeExpected string)
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cmdStatus(args *skel.CmdArgs) error {
|
||||||
|
conf := NetConf{}
|
||||||
|
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
|
||||||
|
return fmt.Errorf("failed to load netconf: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.IPAM.Type != "" {
|
||||||
|
if err := ipam.ExecStatus(conf.IPAM.Type, args.StdinData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Check if master interface exists.
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -117,7 +117,7 @@ type (
|
|||||||
|
|
||||||
func newTesterByVersion(version string) tester {
|
func newTesterByVersion(version string) tester {
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(version, "1.0."):
|
case strings.HasPrefix(version, "1."):
|
||||||
return &testerV10x{}
|
return &testerV10x{}
|
||||||
case strings.HasPrefix(version, "0.4."):
|
case strings.HasPrefix(version, "0.4."):
|
||||||
return &testerV04x{}
|
return &testerV04x{}
|
||||||
@ -208,11 +208,11 @@ var _ = Describe("macvlan Operations", func() {
|
|||||||
err = originalNS.Do(func(ns.NetNS) error {
|
err = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = MASTER_NAME
|
||||||
// Add master
|
// Add master
|
||||||
err = netlink.LinkAdd(&netlink.Dummy{
|
err = netlink.LinkAdd(&netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: MASTER_NAME,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
_, err = netlink.LinkByName(MASTER_NAME)
|
_, err = netlink.LinkByName(MASTER_NAME)
|
||||||
@ -224,11 +224,11 @@ var _ = Describe("macvlan Operations", func() {
|
|||||||
err = targetNS.Do(func(ns.NetNS) error {
|
err = targetNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = MASTER_NAME_INCONTAINER
|
||||||
// Add master
|
// Add master
|
||||||
err = netlink.LinkAdd(&netlink.Dummy{
|
err = netlink.LinkAdd(&netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: MASTER_NAME_INCONTAINER,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
_, err = netlink.LinkByName(MASTER_NAME_INCONTAINER)
|
_, err = netlink.LinkByName(MASTER_NAME_INCONTAINER)
|
||||||
@ -322,6 +322,13 @@ var _ = Describe("macvlan Operations", func() {
|
|||||||
err := originalNS.Do(func(ns.NetNS) error {
|
err := originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
if testutils.SpecVersionHasSTATUS(ver) {
|
||||||
|
err := testutils.CmdStatus(func() error {
|
||||||
|
return cmdStatus(args)
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
result, _, err := testutils.CmdAddWithArgs(args, func() error {
|
result, _, err := testutils.CmdAddWithArgs(args, func() error {
|
||||||
return cmdAdd(args)
|
return cmdAdd(args)
|
||||||
})
|
})
|
||||||
@ -434,6 +441,13 @@ var _ = Describe("macvlan Operations", func() {
|
|||||||
err := originalNS.Do(func(ns.NetNS) error {
|
err := originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
if testutils.SpecVersionHasSTATUS(ver) {
|
||||||
|
err := testutils.CmdStatus(func() error {
|
||||||
|
return cmdStatus(args)
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
result, _, err := testutils.CmdAddWithArgs(args, func() error {
|
result, _, err := testutils.CmdAddWithArgs(args, func() error {
|
||||||
return cmdAdd(args)
|
return cmdAdd(args)
|
||||||
})
|
})
|
||||||
@ -520,6 +534,13 @@ var _ = Describe("macvlan Operations", func() {
|
|||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
if testutils.SpecVersionHasSTATUS(ver) {
|
||||||
|
err := testutils.CmdStatus(func() error {
|
||||||
|
return cmdStatus(args)
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
result, _, err = testutils.CmdAddWithArgs(args, func() error {
|
result, _, err = testutils.CmdAddWithArgs(args, func() error {
|
||||||
return cmdAdd(args)
|
return cmdAdd(args)
|
||||||
})
|
})
|
||||||
@ -660,6 +681,13 @@ var _ = Describe("macvlan Operations", func() {
|
|||||||
err = originalNS.Do(func(ns.NetNS) error {
|
err = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
if testutils.SpecVersionHasSTATUS(ver) {
|
||||||
|
err := testutils.CmdStatus(func() error {
|
||||||
|
return cmdStatus(args)
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
result, _, err := testutils.CmdAddWithArgs(args, func() error {
|
result, _, err := testutils.CmdAddWithArgs(args, func() error {
|
||||||
return cmdAdd(args)
|
return cmdAdd(args)
|
||||||
})
|
})
|
||||||
|
@ -31,7 +31,6 @@ import (
|
|||||||
"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"
|
||||||
"github.com/containernetworking/plugins/pkg/utils"
|
|
||||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -44,8 +43,9 @@ func init() {
|
|||||||
|
|
||||||
type NetConf struct {
|
type NetConf struct {
|
||||||
types.NetConf
|
types.NetConf
|
||||||
IPMasq bool `json:"ipMasq"`
|
IPMasq bool `json:"ipMasq"`
|
||||||
MTU int `json:"mtu"`
|
IPMasqBackend *string `json:"ipMasqBackend,omitempty"`
|
||||||
|
MTU int `json:"mtu"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupContainerVeth(netns ns.NetNS, ifName string, mtu int, pr *current.Result) (*current.Interface, *current.Interface, error) {
|
func setupContainerVeth(netns ns.NetNS, ifName string, mtu int, pr *current.Result) (*current.Interface, *current.Interface, error) {
|
||||||
@ -229,12 +229,12 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if conf.IPMasq {
|
if conf.IPMasq {
|
||||||
chain := utils.FormatChainName(conf.Name, args.ContainerID)
|
ipns := []*net.IPNet{}
|
||||||
comment := utils.FormatComment(conf.Name, args.ContainerID)
|
|
||||||
for _, ipc := range result.IPs {
|
for _, ipc := range result.IPs {
|
||||||
if err = ip.SetupIPMasq(&ipc.Address, chain, comment); err != nil {
|
ipns = append(ipns, &ipc.Address)
|
||||||
return err
|
}
|
||||||
}
|
if err = ip.SetupIPMasqForNetworks(conf.IPMasqBackend, ipns, conf.Name, args.IfName, args.ContainerID); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,10 +293,8 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(ipnets) != 0 && conf.IPMasq {
|
if len(ipnets) != 0 && conf.IPMasq {
|
||||||
chain := utils.FormatChainName(conf.Name, args.ContainerID)
|
if err := ip.TeardownIPMasqForNetworks(ipnets, conf.Name, args.IfName, args.ContainerID); err != nil {
|
||||||
comment := utils.FormatComment(conf.Name, args.ContainerID)
|
return err
|
||||||
for _, ipn := range ipnets {
|
|
||||||
err = ip.TeardownIPMasq(ipn, chain, comment)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,7 +302,13 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("ptp"))
|
skel.PluginMainFuncs(skel.CNIFuncs{
|
||||||
|
Add: cmdAdd,
|
||||||
|
Check: cmdCheck,
|
||||||
|
Del: cmdDel,
|
||||||
|
Status: cmdStatus,
|
||||||
|
/* FIXME GC */
|
||||||
|
}, version.All, bv.BuildString("ptp"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdCheck(args *skel.CmdArgs) error {
|
func cmdCheck(args *skel.CmdArgs) error {
|
||||||
@ -407,3 +411,16 @@ func validateCniContainerInterface(intf current.Interface) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cmdStatus(args *skel.CmdArgs) error {
|
||||||
|
conf := NetConf{}
|
||||||
|
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
|
||||||
|
return fmt.Errorf("failed to load netconf: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ipam.ExecStatus(conf.IPAM.Type, args.StdinData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -39,6 +39,7 @@ type Net struct {
|
|||||||
CNIVersion string `json:"cniVersion"`
|
CNIVersion string `json:"cniVersion"`
|
||||||
Type string `json:"type,omitempty"`
|
Type string `json:"type,omitempty"`
|
||||||
IPMasq bool `json:"ipMasq"`
|
IPMasq bool `json:"ipMasq"`
|
||||||
|
IPMasqBackend *string `json:"ipMasqBackend,omitempty"`
|
||||||
MTU int `json:"mtu"`
|
MTU int `json:"mtu"`
|
||||||
IPAM *allocator.IPAMConfig `json:"ipam"`
|
IPAM *allocator.IPAMConfig `json:"ipam"`
|
||||||
DNS types.DNS `json:"dns"`
|
DNS types.DNS `json:"dns"`
|
||||||
@ -104,7 +105,7 @@ type (
|
|||||||
|
|
||||||
func newTesterByVersion(version string) tester {
|
func newTesterByVersion(version string) tester {
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(version, "1.0."):
|
case strings.HasPrefix(version, "1."):
|
||||||
return &testerV10x{}
|
return &testerV10x{}
|
||||||
case strings.HasPrefix(version, "0.4."):
|
case strings.HasPrefix(version, "0.4."):
|
||||||
return &testerV04x{}
|
return &testerV04x{}
|
||||||
@ -249,6 +250,14 @@ var _ = Describe("ptp Operations", func() {
|
|||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
if testutils.SpecVersionHasSTATUS(cniVersion) {
|
||||||
|
By("Doing a cni STATUS")
|
||||||
|
err = testutils.CmdStatus(func() error {
|
||||||
|
return cmdStatus(args)
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
result, _, err = testutils.CmdAddWithArgs(args, func() error {
|
result, _, err = testutils.CmdAddWithArgs(args, func() error {
|
||||||
return cmdAdd(args)
|
return cmdAdd(args)
|
||||||
})
|
})
|
||||||
@ -368,6 +377,62 @@ var _ = Describe("ptp Operations", func() {
|
|||||||
doTest(conf, ver, 1, dnsConf, targetNS)
|
doTest(conf, ver, 1, dnsConf, targetNS)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It(fmt.Sprintf("[%s] configures and deconfigures a ptp link when specifying ipMasqBackend: iptables", ver), func() {
|
||||||
|
dnsConf := types.DNS{
|
||||||
|
Nameservers: []string{"10.1.2.123"},
|
||||||
|
Domain: "some.domain.test",
|
||||||
|
Search: []string{"search.test"},
|
||||||
|
Options: []string{"option1:foo"},
|
||||||
|
}
|
||||||
|
dnsConfBytes, err := json.Marshal(dnsConf)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
conf := fmt.Sprintf(`{
|
||||||
|
"cniVersion": "%s",
|
||||||
|
"name": "mynet",
|
||||||
|
"type": "ptp",
|
||||||
|
"ipMasq": true,
|
||||||
|
"ipMasqBackend": "iptables",
|
||||||
|
"mtu": 5000,
|
||||||
|
"ipam": {
|
||||||
|
"type": "host-local",
|
||||||
|
"subnet": "10.1.2.0/24",
|
||||||
|
"dataDir": "%s"
|
||||||
|
},
|
||||||
|
"dns": %s
|
||||||
|
}`, ver, dataDir, string(dnsConfBytes))
|
||||||
|
|
||||||
|
doTest(conf, ver, 1, dnsConf, targetNS)
|
||||||
|
})
|
||||||
|
|
||||||
|
It(fmt.Sprintf("[%s] configures and deconfigures a ptp link when specifying ipMasqBackend: nftables", ver), func() {
|
||||||
|
dnsConf := types.DNS{
|
||||||
|
Nameservers: []string{"10.1.2.123"},
|
||||||
|
Domain: "some.domain.test",
|
||||||
|
Search: []string{"search.test"},
|
||||||
|
Options: []string{"option1:foo"},
|
||||||
|
}
|
||||||
|
dnsConfBytes, err := json.Marshal(dnsConf)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
conf := fmt.Sprintf(`{
|
||||||
|
"cniVersion": "%s",
|
||||||
|
"name": "mynet",
|
||||||
|
"type": "ptp",
|
||||||
|
"ipMasq": true,
|
||||||
|
"ipMasqBackend": "nftables",
|
||||||
|
"mtu": 5000,
|
||||||
|
"ipam": {
|
||||||
|
"type": "host-local",
|
||||||
|
"subnet": "10.1.2.0/24",
|
||||||
|
"dataDir": "%s"
|
||||||
|
},
|
||||||
|
"dns": %s
|
||||||
|
}`, ver, dataDir, string(dnsConfBytes))
|
||||||
|
|
||||||
|
doTest(conf, ver, 1, dnsConf, targetNS)
|
||||||
|
})
|
||||||
|
|
||||||
It(fmt.Sprintf("[%s] configures and deconfigures a dual-stack ptp link + routes with ADD/DEL", ver), func() {
|
It(fmt.Sprintf("[%s] configures and deconfigures a dual-stack ptp link + routes with ADD/DEL", ver), func() {
|
||||||
conf := fmt.Sprintf(`{
|
conf := fmt.Sprintf(`{
|
||||||
"cniVersion": "%s",
|
"cniVersion": "%s",
|
||||||
|
@ -137,10 +137,9 @@ func createTapWithIptool(tmpName string, mtu int, multiqueue bool, mac string, o
|
|||||||
}
|
}
|
||||||
|
|
||||||
func createLinkWithNetlink(tmpName string, mtu int, nsFd int, multiqueue bool, mac string, owner *uint32, group *uint32) error {
|
func createLinkWithNetlink(tmpName string, mtu int, nsFd int, multiqueue bool, mac string, owner *uint32, group *uint32) error {
|
||||||
linkAttrs := netlink.LinkAttrs{
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
Name: tmpName,
|
linkAttrs.Name = tmpName
|
||||||
Namespace: netlink.NsFd(nsFd),
|
linkAttrs.Namespace = netlink.NsFd(nsFd)
|
||||||
}
|
|
||||||
if mtu != 0 {
|
if mtu != 0 {
|
||||||
linkAttrs.MTU = mtu
|
linkAttrs.MTU = mtu
|
||||||
}
|
}
|
||||||
@ -371,7 +370,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.
|
||||||
@ -387,7 +385,13 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("tap"))
|
skel.PluginMainFuncs(skel.CNIFuncs{
|
||||||
|
Add: cmdAdd,
|
||||||
|
Check: cmdCheck,
|
||||||
|
Del: cmdDel,
|
||||||
|
Status: cmdStatus,
|
||||||
|
/* FIXME GC */
|
||||||
|
}, version.All, bv.BuildString("tap"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdCheck(args *skel.CmdArgs) error {
|
func cmdCheck(args *skel.CmdArgs) error {
|
||||||
@ -456,3 +460,18 @@ func cmdCheck(args *skel.CmdArgs) error {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cmdStatus(args *skel.CmdArgs) error {
|
||||||
|
conf := NetConf{}
|
||||||
|
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
|
||||||
|
return fmt.Errorf("failed to load netconf: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.IPAM.Type != "" {
|
||||||
|
if err := ipam.ExecStatus(conf.IPAM.Type, args.StdinData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -108,7 +108,7 @@ type (
|
|||||||
|
|
||||||
func newTesterByVersion(version string) tester {
|
func newTesterByVersion(version string) tester {
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(version, "1.0."):
|
case strings.HasPrefix(version, "1."):
|
||||||
return &testerV10x{}
|
return &testerV10x{}
|
||||||
case strings.HasPrefix(version, "0.4."):
|
case strings.HasPrefix(version, "0.4."):
|
||||||
return &testerV04x{}
|
return &testerV04x{}
|
||||||
@ -223,6 +223,13 @@ var _ = Describe("Add, check, remove tap plugin", func() {
|
|||||||
err = originalNS.Do(func(ns.NetNS) error {
|
err = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
if testutils.SpecVersionHasSTATUS(ver) {
|
||||||
|
err := testutils.CmdStatus(func() error {
|
||||||
|
return cmdStatus(args)
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
result, _, err = testutils.CmdAddWithArgs(args, func() error {
|
result, _, err = testutils.CmdAddWithArgs(args, func() error {
|
||||||
return cmdAdd(args)
|
return cmdAdd(args)
|
||||||
})
|
})
|
||||||
@ -346,10 +353,10 @@ var _ = Describe("Add, check, remove tap plugin", func() {
|
|||||||
|
|
||||||
Expect(
|
Expect(
|
||||||
targetNS.Do(func(ns.NetNS) error {
|
targetNS.Do(func(ns.NetNS) error {
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = bridgeName
|
||||||
if err := netlink.LinkAdd(&netlink.Bridge{
|
if err := netlink.LinkAdd(&netlink.Bridge{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: bridgeName,
|
|
||||||
},
|
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -364,6 +371,13 @@ var _ = Describe("Add, check, remove tap plugin", func() {
|
|||||||
err = originalNS.Do(func(ns.NetNS) error {
|
err = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
if testutils.SpecVersionHasSTATUS(ver) {
|
||||||
|
err := testutils.CmdStatus(func() error {
|
||||||
|
return cmdStatus(args)
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
result, _, err = testutils.CmdAddWithArgs(args, func() error {
|
result, _, err = testutils.CmdAddWithArgs(args, func() error {
|
||||||
return cmdAdd(args)
|
return cmdAdd(args)
|
||||||
})
|
})
|
||||||
|
@ -119,14 +119,15 @@ func createVlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Interfac
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.MTU = conf.MTU
|
||||||
|
linkAttrs.Name = tmpName
|
||||||
|
linkAttrs.ParentIndex = m.Attrs().Index
|
||||||
|
linkAttrs.Namespace = netlink.NsFd(int(netns.Fd()))
|
||||||
|
|
||||||
v := &netlink.Vlan{
|
v := &netlink.Vlan{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
MTU: conf.MTU,
|
VlanId: conf.VlanID,
|
||||||
Name: tmpName,
|
|
||||||
ParentIndex: m.Attrs().Index,
|
|
||||||
Namespace: netlink.NsFd(int(netns.Fd())),
|
|
||||||
},
|
|
||||||
VlanId: conf.VlanID,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if conf.LinkContNs {
|
if conf.LinkContNs {
|
||||||
@ -244,7 +245,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.
|
||||||
@ -260,7 +260,13 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("vlan"))
|
skel.PluginMainFuncs(skel.CNIFuncs{
|
||||||
|
Add: cmdAdd,
|
||||||
|
Check: cmdCheck,
|
||||||
|
Del: cmdDel,
|
||||||
|
Status: cmdStatus,
|
||||||
|
/* FIXME GC */
|
||||||
|
}, version.All, bv.BuildString("vlan"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdCheck(args *skel.CmdArgs) error {
|
func cmdCheck(args *skel.CmdArgs) error {
|
||||||
@ -393,3 +399,18 @@ func validateCniContainerInterface(intf current.Interface, vlanID int, mtu int)
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cmdStatus(args *skel.CmdArgs) error {
|
||||||
|
conf := NetConf{}
|
||||||
|
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
|
||||||
|
return fmt.Errorf("failed to load netconf: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ipam.ExecStatus(conf.IPAM.Type, args.StdinData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Check if master interface exists.
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -113,7 +113,7 @@ type (
|
|||||||
|
|
||||||
func newTesterByVersion(version string) tester {
|
func newTesterByVersion(version string) tester {
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(version, "1.0."):
|
case strings.HasPrefix(version, "1."):
|
||||||
return &testerV10x{}
|
return &testerV10x{}
|
||||||
case strings.HasPrefix(version, "0.4."):
|
case strings.HasPrefix(version, "0.4."):
|
||||||
return &testerV04x{}
|
return &testerV04x{}
|
||||||
@ -187,11 +187,11 @@ var _ = Describe("vlan Operations", func() {
|
|||||||
err = originalNS.Do(func(ns.NetNS) error {
|
err = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = MASTER_NAME
|
||||||
// Add master
|
// Add master
|
||||||
err = netlink.LinkAdd(&netlink.Dummy{
|
err = netlink.LinkAdd(&netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: MASTER_NAME,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
m, err := netlink.LinkByName(MASTER_NAME)
|
m, err := netlink.LinkByName(MASTER_NAME)
|
||||||
@ -205,11 +205,11 @@ var _ = Describe("vlan Operations", func() {
|
|||||||
err = targetNS.Do(func(ns.NetNS) error {
|
err = targetNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = MASTER_NAME_INCONTAINER
|
||||||
// Add master
|
// Add master
|
||||||
err = netlink.LinkAdd(&netlink.Dummy{
|
err = netlink.LinkAdd(&netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: MASTER_NAME_INCONTAINER,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
m, err := netlink.LinkByName(MASTER_NAME_INCONTAINER)
|
m, err := netlink.LinkByName(MASTER_NAME_INCONTAINER)
|
||||||
@ -354,6 +354,13 @@ var _ = Describe("vlan Operations", func() {
|
|||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
if testutils.SpecVersionHasSTATUS(ver) {
|
||||||
|
err := testutils.CmdStatus(func() error {
|
||||||
|
return cmdStatus(args)
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
result, _, err = testutils.CmdAddWithArgs(args, func() error {
|
result, _, err = testutils.CmdAddWithArgs(args, func() error {
|
||||||
return cmdAdd(args)
|
return cmdAdd(args)
|
||||||
})
|
})
|
||||||
@ -499,7 +506,7 @@ var _ = Describe("vlan Operations", func() {
|
|||||||
StdinData: []byte(fmt.Sprintf(confFmt, ver, masterInterface, 1600, isInContainer, dataDir)),
|
StdinData: []byte(fmt.Sprintf(confFmt, ver, masterInterface, 1600, isInContainer, dataDir)),
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = 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 {
|
||||||
@ -520,7 +527,7 @@ var _ = Describe("vlan Operations", func() {
|
|||||||
StdinData: []byte(fmt.Sprintf(confFmt, ver, masterInterface, -100, isInContainer, dataDir)),
|
StdinData: []byte(fmt.Sprintf(confFmt, ver, masterInterface, -100, isInContainer, dataDir)),
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = 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 {
|
||||||
|
@ -215,5 +215,26 @@ func cmdCheck(_ *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("win-bridge"))
|
skel.PluginMainFuncs(skel.CNIFuncs{
|
||||||
|
Add: cmdAdd,
|
||||||
|
Check: cmdCheck,
|
||||||
|
Del: cmdDel,
|
||||||
|
Status: cmdStatus,
|
||||||
|
/* FIXME GC */
|
||||||
|
}, version.All, bv.BuildString("win-bridge"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdStatus(args *skel.CmdArgs) error {
|
||||||
|
conf := NetConf{}
|
||||||
|
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
|
||||||
|
return fmt.Errorf("failed to load netconf: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.IPAM.Type != "" {
|
||||||
|
if err := ipam.ExecStatus(conf.IPAM.Type, args.StdinData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -106,13 +106,13 @@ func cmdHcnAdd(args *skel.CmdArgs, n *NetConf) (*current.Result, error) {
|
|||||||
return nil, errors.Annotatef(err, "error while hcn.GetNetworkByName(%s)", networkName)
|
return nil, errors.Annotatef(err, "error while hcn.GetNetworkByName(%s)", networkName)
|
||||||
}
|
}
|
||||||
if hcnNetwork == nil {
|
if hcnNetwork == nil {
|
||||||
return nil, fmt.Errorf("network %v is not found", networkName)
|
return nil, fmt.Errorf("network %v is not found", networkName)
|
||||||
}
|
}
|
||||||
if hnsNetwork == nil {
|
if hnsNetwork == nil {
|
||||||
return nil, fmt.Errorf("network %v not found", networkName)
|
return nil, fmt.Errorf("network %v not found", networkName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.EqualFold(string (hcnNetwork.Type), "Overlay") {
|
if !strings.EqualFold(string(hcnNetwork.Type), "Overlay") {
|
||||||
return nil, fmt.Errorf("network %v is of an unexpected type: %v", networkName, hcnNetwork.Type)
|
return nil, fmt.Errorf("network %v is of an unexpected type: %v", networkName, hcnNetwork.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +131,6 @@ func cmdHcnAdd(args *skel.CmdArgs, n *NetConf) (*current.Result, error) {
|
|||||||
n.ApplyOutboundNatPolicy(hnsNetwork.Subnets[0].AddressPrefix)
|
n.ApplyOutboundNatPolicy(hnsNetwork.Subnets[0].AddressPrefix)
|
||||||
}
|
}
|
||||||
hcnEndpoint, err := hns.GenerateHcnEndpoint(epInfo, &n.NetConf)
|
hcnEndpoint, err := hns.GenerateHcnEndpoint(epInfo, &n.NetConf)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Annotate(err, "error while generating HostComputeEndpoint")
|
return nil, errors.Annotate(err, "error while generating HostComputeEndpoint")
|
||||||
}
|
}
|
||||||
@ -142,15 +141,14 @@ func cmdHcnAdd(args *skel.CmdArgs, n *NetConf) (*current.Result, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result, err := hns.ConstructHcnResult(hcnNetwork, hcnEndpoint)
|
result, err := hns.ConstructHcnResult(hcnNetwork, hcnEndpoint)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ipam.ExecDel(n.IPAM.Type, args.StdinData)
|
ipam.ExecDel(n.IPAM.Type, args.StdinData)
|
||||||
return nil, errors.Annotate(err, "error while constructing HostComputeEndpoint addition result")
|
return nil, errors.Annotate(err, "error while constructing HostComputeEndpoint addition result")
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdHnsAdd(args *skel.CmdArgs, n *NetConf) (*current.Result, error) {
|
func cmdHnsAdd(args *skel.CmdArgs, n *NetConf) (*current.Result, error) {
|
||||||
success := false
|
success := false
|
||||||
|
|
||||||
@ -243,6 +241,7 @@ func cmdHnsAdd(args *skel.CmdArgs, n *NetConf) (*current.Result, error) {
|
|||||||
success = true
|
success = true
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdAdd(args *skel.CmdArgs) error {
|
func cmdAdd(args *skel.CmdArgs) error {
|
||||||
n, cniVersion, err := loadNetConf(args.StdinData)
|
n, cniVersion, err := loadNetConf(args.StdinData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -288,5 +287,24 @@ func cmdCheck(_ *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("win-overlay"))
|
skel.PluginMainFuncs(skel.CNIFuncs{
|
||||||
|
Add: cmdAdd,
|
||||||
|
Check: cmdCheck,
|
||||||
|
Del: cmdDel,
|
||||||
|
Status: cmdStatus,
|
||||||
|
/* FIXME GC */
|
||||||
|
}, version.All, bv.BuildString("win-overlay"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdStatus(args *skel.CmdArgs) error {
|
||||||
|
conf := NetConf{}
|
||||||
|
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
|
||||||
|
return fmt.Errorf("failed to load netconf: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ipam.ExecStatus(conf.IPAM.Type, args.StdinData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -165,7 +165,7 @@ var _ = Describe("bandwidth test", func() {
|
|||||||
StdinData: []byte(conf),
|
StdinData: []byte(conf),
|
||||||
}
|
}
|
||||||
|
|
||||||
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
||||||
Expect(err).NotTo(HaveOccurred(), string(out))
|
Expect(err).NotTo(HaveOccurred(), string(out))
|
||||||
@ -202,7 +202,7 @@ var _ = Describe("bandwidth test", func() {
|
|||||||
return nil
|
return nil
|
||||||
})).To(Succeed())
|
})).To(Succeed())
|
||||||
|
|
||||||
Expect(hostNs.Do(func(n ns.NetNS) error {
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
ifbLink, err := netlink.LinkByName(hostIfname)
|
ifbLink, err := netlink.LinkByName(hostIfname)
|
||||||
@ -260,7 +260,7 @@ var _ = Describe("bandwidth test", func() {
|
|||||||
StdinData: []byte(conf),
|
StdinData: []byte(conf),
|
||||||
}
|
}
|
||||||
|
|
||||||
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
_, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, ifbDeviceName, []byte(conf), func() error { return cmdAdd(args) })
|
_, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, ifbDeviceName, []byte(conf), func() error { return cmdAdd(args) })
|
||||||
@ -271,7 +271,7 @@ var _ = Describe("bandwidth test", func() {
|
|||||||
return nil
|
return nil
|
||||||
})).To(Succeed())
|
})).To(Succeed())
|
||||||
|
|
||||||
Expect(hostNs.Do(func(n ns.NetNS) error {
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
containerIfLink, err := netlink.LinkByName(hostIfname)
|
containerIfLink, err := netlink.LinkByName(hostIfname)
|
||||||
@ -327,7 +327,7 @@ var _ = Describe("bandwidth test", func() {
|
|||||||
StdinData: []byte(conf),
|
StdinData: []byte(conf),
|
||||||
}
|
}
|
||||||
|
|
||||||
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
_, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, ifbDeviceName, []byte(conf), func() error { return cmdAdd(args) })
|
_, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, ifbDeviceName, []byte(conf), func() error { return cmdAdd(args) })
|
||||||
@ -338,7 +338,7 @@ var _ = Describe("bandwidth test", func() {
|
|||||||
return nil
|
return nil
|
||||||
})).To(Succeed())
|
})).To(Succeed())
|
||||||
|
|
||||||
Expect(hostNs.Do(func(n ns.NetNS) error {
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
containerIfLink, err := netlink.LinkByName(hostIfname)
|
containerIfLink, err := netlink.LinkByName(hostIfname)
|
||||||
@ -396,7 +396,7 @@ var _ = Describe("bandwidth test", func() {
|
|||||||
StdinData: []byte(conf),
|
StdinData: []byte(conf),
|
||||||
}
|
}
|
||||||
|
|
||||||
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
||||||
@ -448,7 +448,7 @@ var _ = Describe("bandwidth test", func() {
|
|||||||
StdinData: []byte(conf),
|
StdinData: []byte(conf),
|
||||||
}
|
}
|
||||||
|
|
||||||
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
||||||
Expect(err).NotTo(HaveOccurred(), string(out))
|
Expect(err).NotTo(HaveOccurred(), string(out))
|
||||||
@ -485,7 +485,7 @@ var _ = Describe("bandwidth test", func() {
|
|||||||
return nil
|
return nil
|
||||||
})).To(Succeed())
|
})).To(Succeed())
|
||||||
|
|
||||||
Expect(hostNs.Do(func(n ns.NetNS) error {
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
ifbLink, err := netlink.LinkByName(hostIfname)
|
ifbLink, err := netlink.LinkByName(hostIfname)
|
||||||
@ -551,7 +551,7 @@ var _ = Describe("bandwidth test", func() {
|
|||||||
StdinData: []byte(conf),
|
StdinData: []byte(conf),
|
||||||
}
|
}
|
||||||
|
|
||||||
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
||||||
@ -601,7 +601,7 @@ var _ = Describe("bandwidth test", func() {
|
|||||||
StdinData: []byte(conf),
|
StdinData: []byte(conf),
|
||||||
}
|
}
|
||||||
|
|
||||||
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
_, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
_, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
||||||
Expect(err).NotTo(HaveOccurred(), string(out))
|
Expect(err).NotTo(HaveOccurred(), string(out))
|
||||||
@ -669,7 +669,7 @@ var _ = Describe("bandwidth test", func() {
|
|||||||
StdinData: []byte(conf),
|
StdinData: []byte(conf),
|
||||||
}
|
}
|
||||||
|
|
||||||
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
||||||
Expect(err).NotTo(HaveOccurred(), string(out))
|
Expect(err).NotTo(HaveOccurred(), string(out))
|
||||||
@ -706,7 +706,7 @@ var _ = Describe("bandwidth test", func() {
|
|||||||
return nil
|
return nil
|
||||||
})).To(Succeed())
|
})).To(Succeed())
|
||||||
|
|
||||||
Expect(hostNs.Do(func(n ns.NetNS) error {
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
ifbLink, err := netlink.LinkByName(hostIfname)
|
ifbLink, err := netlink.LinkByName(hostIfname)
|
||||||
@ -768,7 +768,7 @@ var _ = Describe("bandwidth test", func() {
|
|||||||
StdinData: []byte(conf),
|
StdinData: []byte(conf),
|
||||||
}
|
}
|
||||||
|
|
||||||
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
||||||
@ -801,7 +801,7 @@ var _ = Describe("bandwidth test", func() {
|
|||||||
StdinData: []byte(conf),
|
StdinData: []byte(conf),
|
||||||
}
|
}
|
||||||
|
|
||||||
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
||||||
@ -853,7 +853,7 @@ var _ = Describe("bandwidth test", func() {
|
|||||||
StdinData: []byte(conf),
|
StdinData: []byte(conf),
|
||||||
}
|
}
|
||||||
|
|
||||||
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
||||||
|
@ -106,13 +106,13 @@ func makeTCPClientInNS(netns string, address string, port int, numBytes int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func createVeth(hostNs ns.NetNS, hostVethIfName string, containerNs ns.NetNS, containerVethIfName string, hostIP []byte, containerIP []byte, hostIfaceMTU int) {
|
func createVeth(hostNs ns.NetNS, hostVethIfName string, containerNs ns.NetNS, containerVethIfName string, hostIP []byte, containerIP []byte, hostIfaceMTU int) {
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = hostVethIfName
|
||||||
|
linkAttrs.Flags = net.FlagUp
|
||||||
|
linkAttrs.MTU = hostIfaceMTU
|
||||||
vethDeviceRequest := &netlink.Veth{
|
vethDeviceRequest := &netlink.Veth{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: hostVethIfName,
|
PeerName: containerVethIfName,
|
||||||
Flags: net.FlagUp,
|
|
||||||
MTU: hostIfaceMTU,
|
|
||||||
},
|
|
||||||
PeerName: containerVethIfName,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := hostNs.Do(func(_ ns.NetNS) error {
|
err := hostNs.Do(func(_ ns.NetNS) error {
|
||||||
@ -193,12 +193,12 @@ func createVeth(hostNs ns.NetNS, hostVethIfName string, containerNs ns.NetNS, co
|
|||||||
}
|
}
|
||||||
|
|
||||||
func createVethInOneNs(netNS ns.NetNS, vethName, peerName string) {
|
func createVethInOneNs(netNS ns.NetNS, vethName, peerName string) {
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = vethName
|
||||||
|
linkAttrs.Flags = net.FlagUp
|
||||||
vethDeviceRequest := &netlink.Veth{
|
vethDeviceRequest := &netlink.Veth{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: vethName,
|
PeerName: peerName,
|
||||||
Flags: net.FlagUp,
|
|
||||||
},
|
|
||||||
PeerName: peerName,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := netNS.Do(func(_ ns.NetNS) error {
|
err := netNS.Do(func(_ ns.NetNS) error {
|
||||||
@ -222,13 +222,13 @@ func createMacvlan(netNS ns.NetNS, master, macvlanName string) {
|
|||||||
return fmt.Errorf("failed to lookup master %q: %v", master, err)
|
return fmt.Errorf("failed to lookup master %q: %v", master, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.MTU = m.Attrs().MTU
|
||||||
|
linkAttrs.Name = macvlanName
|
||||||
|
linkAttrs.ParentIndex = m.Attrs().Index
|
||||||
macvlanDeviceRequest := &netlink.Macvlan{
|
macvlanDeviceRequest := &netlink.Macvlan{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
MTU: m.Attrs().MTU,
|
Mode: netlink.MACVLAN_MODE_BRIDGE,
|
||||||
Name: macvlanName,
|
|
||||||
ParentIndex: m.Attrs().Index,
|
|
||||||
},
|
|
||||||
Mode: netlink.MACVLAN_MODE_BRIDGE,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = netlink.LinkAdd(macvlanDeviceRequest); err != nil {
|
if err = netlink.LinkAdd(macvlanDeviceRequest); err != nil {
|
||||||
|
@ -27,11 +27,14 @@ import (
|
|||||||
const latencyInMillis = 25
|
const latencyInMillis = 25
|
||||||
|
|
||||||
func CreateIfb(ifbDeviceName string, mtu int) error {
|
func CreateIfb(ifbDeviceName string, mtu int) error {
|
||||||
|
// do not set TxQLen > 0 nor TxQLen == -1 until issues have been fixed with numrxqueues / numtxqueues across interfaces
|
||||||
|
// which needs to get set on IFB devices via upstream library: see hint https://github.com/containernetworking/plugins/pull/1097
|
||||||
err := netlink.LinkAdd(&netlink.Ifb{
|
err := netlink.LinkAdd(&netlink.Ifb{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: netlink.LinkAttrs{
|
||||||
Name: ifbDeviceName,
|
Name: ifbDeviceName,
|
||||||
Flags: net.FlagUp,
|
Flags: net.FlagUp,
|
||||||
MTU: mtu,
|
MTU: mtu,
|
||||||
|
TxQLen: 0,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -240,7 +240,13 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.VersionsStartingFrom("0.3.0"), bv.BuildString("bandwidth"))
|
skel.PluginMainFuncs(skel.CNIFuncs{
|
||||||
|
Add: cmdAdd,
|
||||||
|
Check: cmdCheck,
|
||||||
|
Del: cmdDel,
|
||||||
|
/* FIXME GC */
|
||||||
|
/* FIXME Status */
|
||||||
|
}, version.VersionsStartingFrom("0.3.0"), bv.BuildString("bandwidth"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func SafeQdiscList(link netlink.Link) ([]netlink.Qdisc, error) {
|
func SafeQdiscList(link netlink.Link) ([]netlink.Qdisc, error) {
|
||||||
|
@ -179,7 +179,13 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.VersionsStartingFrom("0.4.0"), bv.BuildString("firewall"))
|
skel.PluginMainFuncs(skel.CNIFuncs{
|
||||||
|
Add: cmdAdd,
|
||||||
|
Check: cmdCheck,
|
||||||
|
Del: cmdDel,
|
||||||
|
/* FIXME GC */
|
||||||
|
/* FIXME Status */
|
||||||
|
}, version.VersionsStartingFrom("0.4.0"), bv.BuildString("firewall"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdCheck(args *skel.CmdArgs) error {
|
func cmdCheck(args *skel.CmdArgs) error {
|
||||||
|
@ -205,10 +205,10 @@ var _ = Describe("firewall plugin iptables backend", func() {
|
|||||||
err = originalNS.Do(func(ns.NetNS) error {
|
err = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = IFNAME
|
||||||
err = netlink.LinkAdd(&netlink.Dummy{
|
err = netlink.LinkAdd(&netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: IFNAME,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
_, err = netlink.LinkByName(IFNAME)
|
_, err = netlink.LinkByName(IFNAME)
|
||||||
|
@ -37,9 +37,22 @@ 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/utils"
|
||||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type PortMapper interface {
|
||||||
|
forwardPorts(config *PortMapConf, containerNet net.IPNet) error
|
||||||
|
checkPorts(config *PortMapConf, containerNet net.IPNet) error
|
||||||
|
unforwardPorts(config *PortMapConf) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are vars rather than consts so we can "&" them
|
||||||
|
var (
|
||||||
|
iptablesBackend = "iptables"
|
||||||
|
nftablesBackend = "nftables"
|
||||||
|
)
|
||||||
|
|
||||||
// PortMapEntry corresponds to a single entry in the port_mappings argument,
|
// PortMapEntry corresponds to a single entry in the port_mappings argument,
|
||||||
// see CONVENTIONS.md
|
// see CONVENTIONS.md
|
||||||
type PortMapEntry struct {
|
type PortMapEntry struct {
|
||||||
@ -51,16 +64,23 @@ type PortMapEntry struct {
|
|||||||
|
|
||||||
type PortMapConf struct {
|
type PortMapConf struct {
|
||||||
types.NetConf
|
types.NetConf
|
||||||
SNAT *bool `json:"snat,omitempty"`
|
|
||||||
ConditionsV4 *[]string `json:"conditionsV4"`
|
mapper PortMapper
|
||||||
ConditionsV6 *[]string `json:"conditionsV6"`
|
|
||||||
MasqAll bool `json:"masqAll,omitempty"`
|
// Generic config
|
||||||
MarkMasqBit *int `json:"markMasqBit"`
|
Backend *string `json:"backend,omitempty"`
|
||||||
ExternalSetMarkChain *string `json:"externalSetMarkChain"`
|
SNAT *bool `json:"snat,omitempty"`
|
||||||
RuntimeConfig struct {
|
ConditionsV4 *[]string `json:"conditionsV4"`
|
||||||
|
ConditionsV6 *[]string `json:"conditionsV6"`
|
||||||
|
MasqAll bool `json:"masqAll,omitempty"`
|
||||||
|
MarkMasqBit *int `json:"markMasqBit"`
|
||||||
|
RuntimeConfig struct {
|
||||||
PortMaps []PortMapEntry `json:"portMappings,omitempty"`
|
PortMaps []PortMapEntry `json:"portMappings,omitempty"`
|
||||||
} `json:"runtimeConfig,omitempty"`
|
} `json:"runtimeConfig,omitempty"`
|
||||||
|
|
||||||
|
// iptables-backend-specific config
|
||||||
|
ExternalSetMarkChain *string `json:"externalSetMarkChain"`
|
||||||
|
|
||||||
// These are fields parsed out of the config or the environment;
|
// These are fields parsed out of the config or the environment;
|
||||||
// included here for convenience
|
// included here for convenience
|
||||||
ContainerID string `json:"-"`
|
ContainerID string `json:"-"`
|
||||||
@ -89,7 +109,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
netConf.ContainerID = args.ContainerID
|
netConf.ContainerID = args.ContainerID
|
||||||
|
|
||||||
if netConf.ContIPv4.IP != nil {
|
if netConf.ContIPv4.IP != nil {
|
||||||
if err := forwardPorts(netConf, netConf.ContIPv4); err != nil {
|
if err := netConf.mapper.forwardPorts(netConf, netConf.ContIPv4); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Delete conntrack entries for UDP to avoid conntrack blackholing traffic
|
// Delete conntrack entries for UDP to avoid conntrack blackholing traffic
|
||||||
@ -98,10 +118,21 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
if err := deletePortmapStaleConnections(netConf.RuntimeConfig.PortMaps, unix.AF_INET); err != nil {
|
if err := deletePortmapStaleConnections(netConf.RuntimeConfig.PortMaps, unix.AF_INET); err != nil {
|
||||||
log.Printf("failed to delete stale UDP conntrack entries for %s: %v", netConf.ContIPv4.IP, err)
|
log.Printf("failed to delete stale UDP conntrack entries for %s: %v", netConf.ContIPv4.IP, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *netConf.SNAT {
|
||||||
|
// Set the route_localnet bit on the host interface, so that
|
||||||
|
// 127/8 can cross a routing boundary.
|
||||||
|
hostIfName := getRoutableHostIF(netConf.ContIPv4.IP)
|
||||||
|
if hostIfName != "" {
|
||||||
|
if err := enableLocalnetRouting(hostIfName); err != nil {
|
||||||
|
return fmt.Errorf("unable to enable route_localnet: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if netConf.ContIPv6.IP != nil {
|
if netConf.ContIPv6.IP != nil {
|
||||||
if err := forwardPorts(netConf, netConf.ContIPv6); err != nil {
|
if err := netConf.mapper.forwardPorts(netConf, netConf.ContIPv6); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Delete conntrack entries for UDP to avoid conntrack blackholing traffic
|
// Delete conntrack entries for UDP to avoid conntrack blackholing traffic
|
||||||
@ -130,11 +161,17 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
// We don't need to parse out whether or not we're using v6 or snat,
|
// We don't need to parse out whether or not we're using v6 or snat,
|
||||||
// deletion is idempotent
|
// deletion is idempotent
|
||||||
return unforwardPorts(netConf)
|
return netConf.mapper.unforwardPorts(netConf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("portmap"))
|
skel.PluginMainFuncs(skel.CNIFuncs{
|
||||||
|
Add: cmdAdd,
|
||||||
|
Check: cmdCheck,
|
||||||
|
Del: cmdDel,
|
||||||
|
/* FIXME GC */
|
||||||
|
/* FIXME Status */
|
||||||
|
}, version.All, bv.BuildString("portmap"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdCheck(args *skel.CmdArgs) error {
|
func cmdCheck(args *skel.CmdArgs) error {
|
||||||
@ -155,13 +192,13 @@ func cmdCheck(args *skel.CmdArgs) error {
|
|||||||
conf.ContainerID = args.ContainerID
|
conf.ContainerID = args.ContainerID
|
||||||
|
|
||||||
if conf.ContIPv4.IP != nil {
|
if conf.ContIPv4.IP != nil {
|
||||||
if err := checkPorts(conf, conf.ContIPv4); err != nil {
|
if err := conf.mapper.checkPorts(conf, conf.ContIPv4); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if conf.ContIPv6.IP != nil {
|
if conf.ContIPv6.IP != nil {
|
||||||
if err := checkPorts(conf, conf.ContIPv6); err != nil {
|
if err := conf.mapper.checkPorts(conf, conf.ContIPv6); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -191,6 +228,8 @@ func parseConfig(stdin []byte, ifName string) (*PortMapConf, *current.Result, er
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conf.mapper = &portMapperIPTables{}
|
||||||
|
|
||||||
if conf.SNAT == nil {
|
if conf.SNAT == nil {
|
||||||
tvar := true
|
tvar := true
|
||||||
conf.SNAT = &tvar
|
conf.SNAT = &tvar
|
||||||
@ -209,6 +248,21 @@ func parseConfig(stdin []byte, ifName string) (*PortMapConf, *current.Result, er
|
|||||||
return nil, nil, fmt.Errorf("MasqMarkBit must be between 0 and 31")
|
return nil, nil, fmt.Errorf("MasqMarkBit must be between 0 and 31")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := ensureBackend(&conf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
switch *conf.Backend {
|
||||||
|
case iptablesBackend:
|
||||||
|
conf.mapper = &portMapperIPTables{}
|
||||||
|
|
||||||
|
case nftablesBackend:
|
||||||
|
conf.mapper = &portMapperNFTables{}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, nil, fmt.Errorf("unrecognized backend %q", *conf.Backend)
|
||||||
|
}
|
||||||
|
|
||||||
// Reject invalid port numbers
|
// Reject invalid port numbers
|
||||||
for _, pm := range conf.RuntimeConfig.PortMaps {
|
for _, pm := range conf.RuntimeConfig.PortMaps {
|
||||||
if pm.ContainerPort <= 0 {
|
if pm.ContainerPort <= 0 {
|
||||||
@ -248,3 +302,59 @@ func parseConfig(stdin []byte, ifName string) (*PortMapConf, *current.Result, er
|
|||||||
|
|
||||||
return &conf, result, nil
|
return &conf, result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ensureBackend validates and/or sets conf.Backend
|
||||||
|
func ensureBackend(conf *PortMapConf) error {
|
||||||
|
backendConfig := make(map[string][]string)
|
||||||
|
|
||||||
|
if conf.ExternalSetMarkChain != nil {
|
||||||
|
backendConfig[iptablesBackend] = append(backendConfig[iptablesBackend], "externalSetMarkChain")
|
||||||
|
}
|
||||||
|
if conditionsBackend := detectBackendOfConditions(conf.ConditionsV4); conditionsBackend != "" {
|
||||||
|
backendConfig[conditionsBackend] = append(backendConfig[conditionsBackend], "conditionsV4")
|
||||||
|
}
|
||||||
|
if conditionsBackend := detectBackendOfConditions(conf.ConditionsV6); conditionsBackend != "" {
|
||||||
|
backendConfig[conditionsBackend] = append(backendConfig[conditionsBackend], "conditionsV6")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If backend wasn't requested explicitly, default to iptables, unless it is not
|
||||||
|
// available (and nftables is). FIXME: flip this default at some point.
|
||||||
|
if conf.Backend == nil {
|
||||||
|
if !utils.SupportsIPTables() && utils.SupportsNFTables() {
|
||||||
|
conf.Backend = &nftablesBackend
|
||||||
|
} else {
|
||||||
|
conf.Backend = &iptablesBackend
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we dont have config for the wrong backend
|
||||||
|
var wrongBackend string
|
||||||
|
if *conf.Backend == iptablesBackend {
|
||||||
|
wrongBackend = nftablesBackend
|
||||||
|
} else {
|
||||||
|
wrongBackend = iptablesBackend
|
||||||
|
}
|
||||||
|
if len(backendConfig[wrongBackend]) > 0 {
|
||||||
|
return fmt.Errorf("%s backend was requested but configuration contains %s-specific options %v", *conf.Backend, wrongBackend, backendConfig[wrongBackend])
|
||||||
|
}
|
||||||
|
|
||||||
|
// OK
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// detectBackendOfConditions returns "iptables" if conditions contains iptables
|
||||||
|
// conditions, "nftables" if it contains nftables conditions, and "" if it is empty.
|
||||||
|
func detectBackendOfConditions(conditions *[]string) string {
|
||||||
|
if conditions == nil || len(*conditions) == 0 || (*conditions)[0] == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first character of any iptables condition would either be an hyphen
|
||||||
|
// (e.g. "-d", "--sport", "-m") or an exclamation mark.
|
||||||
|
// No nftables condition would start that way. (An nftables condition might
|
||||||
|
// include a negative number, but not as the first token.)
|
||||||
|
if (*conditions)[0][0] == '-' || (*conditions)[0][0] == '!' {
|
||||||
|
return iptablesBackend
|
||||||
|
}
|
||||||
|
return nftablesBackend
|
||||||
|
}
|
||||||
|
@ -25,7 +25,6 @@ import (
|
|||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
"github.com/containernetworking/plugins/pkg/utils"
|
"github.com/containernetworking/plugins/pkg/utils"
|
||||||
"github.com/containernetworking/plugins/pkg/utils/sysctl"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// This creates the chains to be added to iptables. The basic structure is
|
// This creates the chains to be added to iptables. The basic structure is
|
||||||
@ -52,9 +51,11 @@ const (
|
|||||||
OldTopLevelSNATChainName = "CNI-HOSTPORT-SNAT"
|
OldTopLevelSNATChainName = "CNI-HOSTPORT-SNAT"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type portMapperIPTables struct{}
|
||||||
|
|
||||||
// forwardPorts establishes port forwarding to a given container IP.
|
// forwardPorts establishes port forwarding to a given container IP.
|
||||||
// containerNet.IP can be either v4 or v6.
|
// containerNet.IP can be either v4 or v6.
|
||||||
func forwardPorts(config *PortMapConf, containerNet net.IPNet) error {
|
func (*portMapperIPTables) forwardPorts(config *PortMapConf, containerNet net.IPNet) error {
|
||||||
isV6 := (containerNet.IP.To4() == nil)
|
isV6 := (containerNet.IP.To4() == nil)
|
||||||
|
|
||||||
var ipt *iptables.IPTables
|
var ipt *iptables.IPTables
|
||||||
@ -87,17 +88,6 @@ func forwardPorts(config *PortMapConf, containerNet net.IPNet) error {
|
|||||||
return fmt.Errorf("unable to create chain %s: %v", setMarkChain.name, err)
|
return fmt.Errorf("unable to create chain %s: %v", setMarkChain.name, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isV6 {
|
|
||||||
// Set the route_localnet bit on the host interface, so that
|
|
||||||
// 127/8 can cross a routing boundary.
|
|
||||||
hostIfName := getRoutableHostIF(containerNet.IP)
|
|
||||||
if hostIfName != "" {
|
|
||||||
if err := enableLocalnetRouting(hostIfName); err != nil {
|
|
||||||
return fmt.Errorf("unable to enable route_localnet: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate the DNAT (actual port forwarding) rules
|
// Generate the DNAT (actual port forwarding) rules
|
||||||
@ -117,7 +107,7 @@ func forwardPorts(config *PortMapConf, containerNet net.IPNet) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkPorts(config *PortMapConf, containerNet net.IPNet) error {
|
func (*portMapperIPTables) checkPorts(config *PortMapConf, containerNet net.IPNet) error {
|
||||||
isV6 := (containerNet.IP.To4() == nil)
|
isV6 := (containerNet.IP.To4() == nil)
|
||||||
dnatChain := genDnatChain(config.Name, config.ContainerID)
|
dnatChain := genDnatChain(config.Name, config.ContainerID)
|
||||||
fillDnatRules(&dnatChain, config, containerNet)
|
fillDnatRules(&dnatChain, config, containerNet)
|
||||||
@ -344,14 +334,6 @@ func genMarkMasqChain(markBit int) chain {
|
|||||||
return ch
|
return ch
|
||||||
}
|
}
|
||||||
|
|
||||||
// enableLocalnetRouting tells the kernel not to treat 127/8 as a martian,
|
|
||||||
// so that connections with a source ip of 127/8 can cross a routing boundary.
|
|
||||||
func enableLocalnetRouting(ifName string) error {
|
|
||||||
routeLocalnetPath := "net/ipv4/conf/" + ifName + "/route_localnet"
|
|
||||||
_, err := sysctl.Sysctl(routeLocalnetPath, "1")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// genOldSnatChain is no longer used, but used to be created. We'll try and
|
// genOldSnatChain is no longer used, but used to be created. We'll try and
|
||||||
// tear it down in case the plugin version changed between ADD and DEL
|
// tear it down in case the plugin version changed between ADD and DEL
|
||||||
func genOldSnatChain(netName, containerID string) chain {
|
func genOldSnatChain(netName, containerID string) chain {
|
||||||
@ -372,7 +354,7 @@ func genOldSnatChain(netName, containerID string) chain {
|
|||||||
// don't know which protocols were used.
|
// don't know which protocols were used.
|
||||||
// So, we first check that iptables is "generally OK" by doing a check. If
|
// So, we first check that iptables is "generally OK" by doing a check. If
|
||||||
// not, we ignore the error, unless neither v4 nor v6 are OK.
|
// not, we ignore the error, unless neither v4 nor v6 are OK.
|
||||||
func unforwardPorts(config *PortMapConf) error {
|
func (*portMapperIPTables) unforwardPorts(config *PortMapConf) error {
|
||||||
dnatChain := genDnatChain(config.Name, config.ContainerID)
|
dnatChain := genDnatChain(config.Name, config.ContainerID)
|
||||||
|
|
||||||
// Might be lying around from old versions
|
// Might be lying around from old versions
|
252
plugins/meta/portmap/portmap_iptables_test.go
Normal file
252
plugins/meta/portmap/portmap_iptables_test.go
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
// Copyright 2017 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("portmapping configuration (iptables)", func() {
|
||||||
|
netName := "testNetName"
|
||||||
|
containerID := "icee6giejonei6sohng6ahngee7laquohquee9shiGo7fohferakah3Feiyoolu2pei7ciPhoh7shaoX6vai3vuf0ahfaeng8yohb9ceu0daez5hashee8ooYai5wa3y"
|
||||||
|
|
||||||
|
for _, ver := range []string{"0.3.0", "0.3.1", "0.4.0", "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
|
||||||
|
|
||||||
|
Describe("Generating iptables chains", func() {
|
||||||
|
Context("for DNAT", func() {
|
||||||
|
It(fmt.Sprintf("[%s] generates a correct standard container chain", ver), func() {
|
||||||
|
ch := genDnatChain(netName, containerID)
|
||||||
|
|
||||||
|
Expect(ch).To(Equal(chain{
|
||||||
|
table: "nat",
|
||||||
|
name: "CNI-DN-bfd599665540dd91d5d28",
|
||||||
|
entryChains: []string{TopLevelDNATChainName},
|
||||||
|
}))
|
||||||
|
configBytes := []byte(fmt.Sprintf(`{
|
||||||
|
"name": "test",
|
||||||
|
"type": "portmap",
|
||||||
|
"cniVersion": "%s",
|
||||||
|
"runtimeConfig": {
|
||||||
|
"portMappings": [
|
||||||
|
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"},
|
||||||
|
{ "hostPort": 8081, "containerPort": 80, "protocol": "tcp"},
|
||||||
|
{ "hostPort": 8080, "containerPort": 81, "protocol": "udp"},
|
||||||
|
{ "hostPort": 8082, "containerPort": 82, "protocol": "udp"},
|
||||||
|
{ "hostPort": 8083, "containerPort": 83, "protocol": "tcp", "hostIP": "192.168.0.2"},
|
||||||
|
{ "hostPort": 8084, "containerPort": 84, "protocol": "tcp", "hostIP": "0.0.0.0"},
|
||||||
|
{ "hostPort": 8085, "containerPort": 85, "protocol": "tcp", "hostIP": "2001:db8:a::1"},
|
||||||
|
{ "hostPort": 8086, "containerPort": 86, "protocol": "tcp", "hostIP": "::"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"snat": true,
|
||||||
|
"conditionsV4": ["-a", "b"],
|
||||||
|
"conditionsV6": ["-c", "d"]
|
||||||
|
}`, ver))
|
||||||
|
|
||||||
|
conf, _, err := parseConfig(configBytes, "foo")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
conf.ContainerID = containerID
|
||||||
|
|
||||||
|
ch = genDnatChain(conf.Name, containerID)
|
||||||
|
Expect(ch).To(Equal(chain{
|
||||||
|
table: "nat",
|
||||||
|
name: "CNI-DN-67e92b96e692a494b6b85",
|
||||||
|
entryChains: []string{"CNI-HOSTPORT-DNAT"},
|
||||||
|
}))
|
||||||
|
|
||||||
|
n, err := types.ParseCIDR("10.0.0.2/24")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
fillDnatRules(&ch, conf, *n)
|
||||||
|
|
||||||
|
Expect(ch.entryRules).To(Equal([][]string{
|
||||||
|
{
|
||||||
|
"-m", "comment", "--comment",
|
||||||
|
fmt.Sprintf("dnat name: \"test\" id: \"%s\"", containerID),
|
||||||
|
"-m", "multiport",
|
||||||
|
"-p", "tcp",
|
||||||
|
"--destination-ports", "8080,8081,8083,8084,8085,8086",
|
||||||
|
"-a", "b",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"-m", "comment", "--comment",
|
||||||
|
fmt.Sprintf("dnat name: \"test\" id: \"%s\"", containerID),
|
||||||
|
"-m", "multiport",
|
||||||
|
"-p", "udp",
|
||||||
|
"--destination-ports", "8080,8082",
|
||||||
|
"-a", "b",
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
Expect(ch.rules).To(Equal([][]string{
|
||||||
|
// tcp rules and not hostIP
|
||||||
|
{"-p", "tcp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||||
|
{"-p", "tcp", "--dport", "8080", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||||
|
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
|
||||||
|
{"-p", "tcp", "--dport", "8081", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||||
|
{"-p", "tcp", "--dport", "8081", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||||
|
{"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
|
||||||
|
// udp rules and not hostIP
|
||||||
|
{"-p", "udp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||||
|
{"-p", "udp", "--dport", "8080", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||||
|
{"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:81"},
|
||||||
|
{"-p", "udp", "--dport", "8082", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||||
|
{"-p", "udp", "--dport", "8082", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||||
|
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "10.0.0.2:82"},
|
||||||
|
// tcp rules and hostIP
|
||||||
|
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||||
|
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||||
|
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-j", "DNAT", "--to-destination", "10.0.0.2:83"},
|
||||||
|
// tcp rules and hostIP = "0.0.0.0"
|
||||||
|
{"-p", "tcp", "--dport", "8084", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||||
|
{"-p", "tcp", "--dport", "8084", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||||
|
{"-p", "tcp", "--dport", "8084", "-j", "DNAT", "--to-destination", "10.0.0.2:84"},
|
||||||
|
}))
|
||||||
|
|
||||||
|
ch.rules = nil
|
||||||
|
ch.entryRules = nil
|
||||||
|
|
||||||
|
n, err = types.ParseCIDR("2001:db8::2/64")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
fillDnatRules(&ch, conf, *n)
|
||||||
|
|
||||||
|
Expect(ch.rules).To(Equal([][]string{
|
||||||
|
// tcp rules and not hostIP
|
||||||
|
{"-p", "tcp", "--dport", "8080", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||||
|
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "[2001:db8::2]:80"},
|
||||||
|
{"-p", "tcp", "--dport", "8081", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||||
|
{"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "[2001:db8::2]:80"},
|
||||||
|
// udp rules and not hostIP
|
||||||
|
{"-p", "udp", "--dport", "8080", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||||
|
{"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "[2001:db8::2]:81"},
|
||||||
|
{"-p", "udp", "--dport", "8082", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||||
|
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "[2001:db8::2]:82"},
|
||||||
|
// tcp rules and hostIP
|
||||||
|
{"-p", "tcp", "--dport", "8085", "-d", "2001:db8:a::1", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||||
|
{"-p", "tcp", "--dport", "8085", "-d", "2001:db8:a::1", "-j", "DNAT", "--to-destination", "[2001:db8::2]:85"},
|
||||||
|
// tcp rules and hostIP = "::"
|
||||||
|
{"-p", "tcp", "--dport", "8086", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||||
|
{"-p", "tcp", "--dport", "8086", "-j", "DNAT", "--to-destination", "[2001:db8::2]:86"},
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Disable snat, generate rules
|
||||||
|
ch.rules = nil
|
||||||
|
ch.entryRules = nil
|
||||||
|
fvar := false
|
||||||
|
conf.SNAT = &fvar
|
||||||
|
|
||||||
|
n, err = types.ParseCIDR("10.0.0.2/24")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
fillDnatRules(&ch, conf, *n)
|
||||||
|
Expect(ch.rules).To(Equal([][]string{
|
||||||
|
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
|
||||||
|
{"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
|
||||||
|
{"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:81"},
|
||||||
|
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "10.0.0.2:82"},
|
||||||
|
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-j", "DNAT", "--to-destination", "10.0.0.2:83"},
|
||||||
|
{"-p", "tcp", "--dport", "8084", "-j", "DNAT", "--to-destination", "10.0.0.2:84"},
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
It(fmt.Sprintf("[%s] generates a correct chain with external mark", ver), func() {
|
||||||
|
ch := genDnatChain(netName, containerID)
|
||||||
|
|
||||||
|
Expect(ch).To(Equal(chain{
|
||||||
|
table: "nat",
|
||||||
|
name: "CNI-DN-bfd599665540dd91d5d28",
|
||||||
|
entryChains: []string{TopLevelDNATChainName},
|
||||||
|
}))
|
||||||
|
configBytes := []byte(fmt.Sprintf(`{
|
||||||
|
"name": "test",
|
||||||
|
"type": "portmap",
|
||||||
|
"cniVersion": "%s",
|
||||||
|
"runtimeConfig": {
|
||||||
|
"portMappings": [
|
||||||
|
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"externalSetMarkChain": "PLZ-SET-MARK",
|
||||||
|
"conditionsV4": ["-a", "b"],
|
||||||
|
"conditionsV6": ["-c", "d"]
|
||||||
|
}`, ver))
|
||||||
|
|
||||||
|
conf, _, err := parseConfig(configBytes, "foo")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
conf.ContainerID = containerID
|
||||||
|
|
||||||
|
ch = genDnatChain(conf.Name, containerID)
|
||||||
|
n, err := types.ParseCIDR("10.0.0.2/24")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
fillDnatRules(&ch, conf, *n)
|
||||||
|
Expect(ch.rules).To(Equal([][]string{
|
||||||
|
{"-p", "tcp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "PLZ-SET-MARK"},
|
||||||
|
{"-p", "tcp", "--dport", "8080", "-s", "127.0.0.1", "-j", "PLZ-SET-MARK"},
|
||||||
|
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
It(fmt.Sprintf("[%s] generates a correct top-level chain", ver), func() {
|
||||||
|
ch := genToplevelDnatChain()
|
||||||
|
|
||||||
|
Expect(ch).To(Equal(chain{
|
||||||
|
table: "nat",
|
||||||
|
name: "CNI-HOSTPORT-DNAT",
|
||||||
|
entryChains: []string{"PREROUTING", "OUTPUT"},
|
||||||
|
entryRules: [][]string{{"-m", "addrtype", "--dst-type", "LOCAL"}},
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
It(fmt.Sprintf("[%s] generates the correct mark chains", ver), func() {
|
||||||
|
masqBit := 5
|
||||||
|
ch := genSetMarkChain(masqBit)
|
||||||
|
Expect(ch).To(Equal(chain{
|
||||||
|
table: "nat",
|
||||||
|
name: "CNI-HOSTPORT-SETMARK",
|
||||||
|
rules: [][]string{{
|
||||||
|
"-m", "comment",
|
||||||
|
"--comment", "CNI portfwd masquerade mark",
|
||||||
|
"-j", "MARK",
|
||||||
|
"--set-xmark", "0x20/0x20",
|
||||||
|
}},
|
||||||
|
}))
|
||||||
|
|
||||||
|
ch = genMarkMasqChain(masqBit)
|
||||||
|
Expect(ch).To(Equal(chain{
|
||||||
|
table: "nat",
|
||||||
|
name: "CNI-HOSTPORT-MASQ",
|
||||||
|
entryChains: []string{"POSTROUTING"},
|
||||||
|
entryRules: [][]string{{
|
||||||
|
"-m", "comment",
|
||||||
|
"--comment", "CNI portfwd requiring masquerade",
|
||||||
|
}},
|
||||||
|
rules: [][]string{{
|
||||||
|
"-m", "mark",
|
||||||
|
"--mark", "0x20/0x20",
|
||||||
|
"-j", "MASQUERADE",
|
||||||
|
}},
|
||||||
|
prependEntry: true,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
340
plugins/meta/portmap/portmap_nftables.go
Normal file
340
plugins/meta/portmap/portmap_nftables.go
Normal file
@ -0,0 +1,340 @@
|
|||||||
|
// Copyright 2023 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"sigs.k8s.io/knftables"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tableName = "cni_hostport"
|
||||||
|
|
||||||
|
hostIPHostPortsChain = "hostip_hostports"
|
||||||
|
hostPortsChain = "hostports"
|
||||||
|
masqueradingChain = "masquerading"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The nftables portmap implementation is fairly similar to the iptables implementation:
|
||||||
|
// we add a rule for each mapping, with a comment containing a hash of the container ID,
|
||||||
|
// so that we can later reliably delete the rules we want. (This is important because in
|
||||||
|
// edge cases, it's possible the plugin might see "ADD container A with IP 192.168.1.3",
|
||||||
|
// followed by "ADD container B with IP 192.168.1.3" followed by "DEL container A with IP
|
||||||
|
// 192.168.1.3", and we need to make sure that the DEL causes us to delete the rule for
|
||||||
|
// container A, and not the rule for container B.) This iptables implementation actually
|
||||||
|
// uses a separate chain per container but there's not really any need for that...
|
||||||
|
//
|
||||||
|
// As with pkg/ip/ipmasq_nftables_linux.go, it would be more nftables-y to have a chain
|
||||||
|
// with a single rule doing a lookup against a map with an element per mapping, rather
|
||||||
|
// than having a chain with a rule per mapping. But there's no easy, non-racy way to say
|
||||||
|
// "delete the element 192.168.1.3 from the map, but only if it was added for container A,
|
||||||
|
// not if it was added for container B".
|
||||||
|
|
||||||
|
type portMapperNFTables struct {
|
||||||
|
ipv4 knftables.Interface
|
||||||
|
ipv6 knftables.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPortMapNFT creates an nftables.Interface for port mapping for the IP family of ipn
|
||||||
|
func (pmNFT *portMapperNFTables) getPortMapNFT(ipv6 bool) (knftables.Interface, error) {
|
||||||
|
var err error
|
||||||
|
if ipv6 {
|
||||||
|
if pmNFT.ipv6 == nil {
|
||||||
|
pmNFT.ipv6, err = knftables.New(knftables.IPv6Family, tableName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pmNFT.ipv6, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if pmNFT.ipv4 == nil {
|
||||||
|
pmNFT.ipv4, err = knftables.New(knftables.IPv4Family, tableName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pmNFT.ipv4, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// forwardPorts establishes port forwarding to a given container IP.
|
||||||
|
// containerNet.IP can be either v4 or v6.
|
||||||
|
func (pmNFT *portMapperNFTables) forwardPorts(config *PortMapConf, containerNet net.IPNet) error {
|
||||||
|
isV6 := (containerNet.IP.To4() == nil)
|
||||||
|
nft, err := pmNFT.getPortMapNFT(isV6)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ipX string
|
||||||
|
var conditions []string
|
||||||
|
if isV6 {
|
||||||
|
ipX = "ip6"
|
||||||
|
if config.ConditionsV6 != nil {
|
||||||
|
conditions = *config.ConditionsV6
|
||||||
|
}
|
||||||
|
} else if !isV6 {
|
||||||
|
ipX = "ip"
|
||||||
|
if config.ConditionsV4 != nil {
|
||||||
|
conditions = *config.ConditionsV4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := nft.NewTransaction()
|
||||||
|
|
||||||
|
// Ensure basic rule structure
|
||||||
|
tx.Add(&knftables.Table{
|
||||||
|
Comment: knftables.PtrTo("CNI portmap plugin"),
|
||||||
|
})
|
||||||
|
|
||||||
|
tx.Add(&knftables.Chain{
|
||||||
|
Name: "hostports",
|
||||||
|
})
|
||||||
|
tx.Add(&knftables.Chain{
|
||||||
|
Name: "hostip_hostports",
|
||||||
|
})
|
||||||
|
|
||||||
|
tx.Add(&knftables.Chain{
|
||||||
|
Name: "prerouting",
|
||||||
|
Type: knftables.PtrTo(knftables.NATType),
|
||||||
|
Hook: knftables.PtrTo(knftables.PreroutingHook),
|
||||||
|
Priority: knftables.PtrTo(knftables.DNATPriority),
|
||||||
|
})
|
||||||
|
tx.Flush(&knftables.Chain{
|
||||||
|
Name: "prerouting",
|
||||||
|
})
|
||||||
|
tx.Add(&knftables.Rule{
|
||||||
|
Chain: "prerouting",
|
||||||
|
Rule: knftables.Concat(
|
||||||
|
conditions,
|
||||||
|
"jump", hostIPHostPortsChain,
|
||||||
|
),
|
||||||
|
})
|
||||||
|
tx.Add(&knftables.Rule{
|
||||||
|
Chain: "prerouting",
|
||||||
|
Rule: knftables.Concat(
|
||||||
|
conditions,
|
||||||
|
"jump", hostPortsChain,
|
||||||
|
),
|
||||||
|
})
|
||||||
|
|
||||||
|
tx.Add(&knftables.Chain{
|
||||||
|
Name: "output",
|
||||||
|
Type: knftables.PtrTo(knftables.NATType),
|
||||||
|
Hook: knftables.PtrTo(knftables.OutputHook),
|
||||||
|
Priority: knftables.PtrTo(knftables.DNATPriority),
|
||||||
|
})
|
||||||
|
tx.Flush(&knftables.Chain{
|
||||||
|
Name: "output",
|
||||||
|
})
|
||||||
|
tx.Add(&knftables.Rule{
|
||||||
|
Chain: "output",
|
||||||
|
Rule: knftables.Concat(
|
||||||
|
conditions,
|
||||||
|
"jump", hostIPHostPortsChain,
|
||||||
|
),
|
||||||
|
})
|
||||||
|
tx.Add(&knftables.Rule{
|
||||||
|
Chain: "output",
|
||||||
|
Rule: knftables.Concat(
|
||||||
|
conditions,
|
||||||
|
"fib daddr type local",
|
||||||
|
"jump", hostPortsChain,
|
||||||
|
),
|
||||||
|
})
|
||||||
|
|
||||||
|
if *config.SNAT {
|
||||||
|
tx.Add(&knftables.Chain{
|
||||||
|
Name: masqueradingChain,
|
||||||
|
Type: knftables.PtrTo(knftables.NATType),
|
||||||
|
Hook: knftables.PtrTo(knftables.PostroutingHook),
|
||||||
|
Priority: knftables.PtrTo(knftables.SNATPriority),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up this container
|
||||||
|
for _, e := range config.RuntimeConfig.PortMaps {
|
||||||
|
useHostIP := false
|
||||||
|
if e.HostIP != "" {
|
||||||
|
hostIP := net.ParseIP(e.HostIP)
|
||||||
|
isHostV6 := (hostIP.To4() == nil)
|
||||||
|
// Ignore wrong-IP-family HostIPs
|
||||||
|
if isV6 != isHostV6 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unspecified addresses cannot be used as destination
|
||||||
|
useHostIP = !hostIP.IsUnspecified()
|
||||||
|
}
|
||||||
|
|
||||||
|
if useHostIP {
|
||||||
|
tx.Add(&knftables.Rule{
|
||||||
|
Chain: hostIPHostPortsChain,
|
||||||
|
Rule: knftables.Concat(
|
||||||
|
ipX, "daddr", e.HostIP,
|
||||||
|
e.Protocol, "dport", e.HostPort,
|
||||||
|
"dnat to", net.JoinHostPort(containerNet.IP.String(), strconv.Itoa(e.ContainerPort)),
|
||||||
|
),
|
||||||
|
Comment: &config.ContainerID,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
tx.Add(&knftables.Rule{
|
||||||
|
Chain: hostPortsChain,
|
||||||
|
Rule: knftables.Concat(
|
||||||
|
e.Protocol, "dport", e.HostPort,
|
||||||
|
"dnat to", net.JoinHostPort(containerNet.IP.String(), strconv.Itoa(e.ContainerPort)),
|
||||||
|
),
|
||||||
|
Comment: &config.ContainerID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if *config.SNAT {
|
||||||
|
// Add mark-to-masquerade rules for hairpin and localhost
|
||||||
|
// In theory we should validate that the original dst IP and port are as
|
||||||
|
// expected, but *any* traffic matching one of these patterns would need
|
||||||
|
// to be masqueraded to be able to work correctly anyway.
|
||||||
|
tx.Add(&knftables.Rule{
|
||||||
|
Chain: masqueradingChain,
|
||||||
|
Rule: knftables.Concat(
|
||||||
|
ipX, "saddr", containerNet.IP,
|
||||||
|
ipX, "daddr", containerNet.IP,
|
||||||
|
"masquerade",
|
||||||
|
),
|
||||||
|
Comment: &config.ContainerID,
|
||||||
|
})
|
||||||
|
if !isV6 {
|
||||||
|
tx.Add(&knftables.Rule{
|
||||||
|
Chain: masqueradingChain,
|
||||||
|
Rule: knftables.Concat(
|
||||||
|
ipX, "saddr 127.0.0.1",
|
||||||
|
ipX, "daddr", containerNet.IP,
|
||||||
|
"masquerade",
|
||||||
|
),
|
||||||
|
Comment: &config.ContainerID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = nft.Run(context.TODO(), tx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to set up nftables rules for port mappings: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pmNFT *portMapperNFTables) checkPorts(config *PortMapConf, containerNet net.IPNet) error {
|
||||||
|
isV6 := (containerNet.IP.To4() == nil)
|
||||||
|
|
||||||
|
var hostPorts, hostIPHostPorts, masqueradings int
|
||||||
|
for _, e := range config.RuntimeConfig.PortMaps {
|
||||||
|
if e.HostIP != "" {
|
||||||
|
hostIPHostPorts++
|
||||||
|
} else {
|
||||||
|
hostPorts++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if *config.SNAT {
|
||||||
|
masqueradings = len(config.RuntimeConfig.PortMaps)
|
||||||
|
if isV6 {
|
||||||
|
masqueradings *= 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nft, err := pmNFT.getPortMapNFT(isV6)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if hostPorts > 0 {
|
||||||
|
err := checkPortsAgainstRules(nft, hostPortsChain, config.ContainerID, hostPorts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hostIPHostPorts > 0 {
|
||||||
|
err := checkPortsAgainstRules(nft, hostIPHostPortsChain, config.ContainerID, hostIPHostPorts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if masqueradings > 0 {
|
||||||
|
err := checkPortsAgainstRules(nft, masqueradingChain, config.ContainerID, masqueradings)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPortsAgainstRules(nft knftables.Interface, chain, comment string, nPorts int) error {
|
||||||
|
rules, err := nft.ListRules(context.TODO(), chain)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
found := 0
|
||||||
|
for _, r := range rules {
|
||||||
|
if r.Comment != nil && *r.Comment == comment {
|
||||||
|
found++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found < nPorts {
|
||||||
|
return fmt.Errorf("missing hostport rules in %q chain", chain)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// unforwardPorts deletes any nftables rules created by this plugin.
|
||||||
|
// It should be idempotent - it will not error if the chain does not exist.
|
||||||
|
func (pmNFT *portMapperNFTables) unforwardPorts(config *PortMapConf) error {
|
||||||
|
// Always clear both IPv4 and IPv6, just to be sure
|
||||||
|
for _, family := range []knftables.Family{knftables.IPv4Family, knftables.IPv6Family} {
|
||||||
|
nft, err := pmNFT.getPortMapNFT(family == knftables.IPv6Family)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := nft.NewTransaction()
|
||||||
|
for _, chain := range []string{hostPortsChain, hostIPHostPortsChain, masqueradingChain} {
|
||||||
|
rules, err := nft.ListRules(context.TODO(), chain)
|
||||||
|
if err != nil {
|
||||||
|
if knftables.IsNotFound(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return fmt.Errorf("could not list rules in table %s: %w", tableName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, r := range rules {
|
||||||
|
if r.Comment != nil && *r.Comment == config.ContainerID {
|
||||||
|
tx.Delete(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = nft.Run(context.TODO(), tx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error deleting nftables rules: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
134
plugins/meta/portmap/portmap_nftables_test.go
Normal file
134
plugins/meta/portmap/portmap_nftables_test.go
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
// Copyright 2023 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
"sigs.k8s.io/knftables"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("portmapping configuration (nftables)", func() {
|
||||||
|
containerID := "icee6giejonei6so"
|
||||||
|
|
||||||
|
for _, ver := range []string{"0.3.0", "0.3.1", "0.4.0", "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
|
||||||
|
|
||||||
|
Describe("nftables rules", func() {
|
||||||
|
var pmNFT *portMapperNFTables
|
||||||
|
var ipv4Fake, ipv6Fake *knftables.Fake
|
||||||
|
BeforeEach(func() {
|
||||||
|
ipv4Fake = knftables.NewFake(knftables.IPv4Family, tableName)
|
||||||
|
ipv6Fake = knftables.NewFake(knftables.IPv6Family, tableName)
|
||||||
|
pmNFT = &portMapperNFTables{
|
||||||
|
ipv4: ipv4Fake,
|
||||||
|
ipv6: ipv6Fake,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
It(fmt.Sprintf("[%s] generates correct rules on ADD", ver), func() {
|
||||||
|
configBytes := []byte(fmt.Sprintf(`{
|
||||||
|
"name": "test",
|
||||||
|
"type": "portmap",
|
||||||
|
"cniVersion": "%s",
|
||||||
|
"backend": "nftables",
|
||||||
|
"runtimeConfig": {
|
||||||
|
"portMappings": [
|
||||||
|
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"},
|
||||||
|
{ "hostPort": 8081, "containerPort": 80, "protocol": "tcp"},
|
||||||
|
{ "hostPort": 8080, "containerPort": 81, "protocol": "udp"},
|
||||||
|
{ "hostPort": 8082, "containerPort": 82, "protocol": "udp"},
|
||||||
|
{ "hostPort": 8083, "containerPort": 83, "protocol": "tcp", "hostIP": "192.168.0.2"},
|
||||||
|
{ "hostPort": 8084, "containerPort": 84, "protocol": "tcp", "hostIP": "0.0.0.0"},
|
||||||
|
{ "hostPort": 8085, "containerPort": 85, "protocol": "tcp", "hostIP": "2001:db8:a::1"},
|
||||||
|
{ "hostPort": 8086, "containerPort": 86, "protocol": "tcp", "hostIP": "::"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"snat": true,
|
||||||
|
"conditionsV4": ["a", "b"],
|
||||||
|
"conditionsV6": ["c", "d"]
|
||||||
|
}`, ver))
|
||||||
|
|
||||||
|
conf, _, err := parseConfig(configBytes, "foo")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
conf.ContainerID = containerID
|
||||||
|
|
||||||
|
containerNet, err := types.ParseCIDR("10.0.0.2/24")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
err = pmNFT.forwardPorts(conf, *containerNet)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
expectedRules := strings.TrimSpace(`
|
||||||
|
add table ip cni_hostport { comment "CNI portmap plugin" ; }
|
||||||
|
add chain ip cni_hostport hostip_hostports
|
||||||
|
add chain ip cni_hostport hostports
|
||||||
|
add chain ip cni_hostport masquerading { type nat hook postrouting priority 100 ; }
|
||||||
|
add chain ip cni_hostport output { type nat hook output priority -100 ; }
|
||||||
|
add chain ip cni_hostport prerouting { type nat hook prerouting priority -100 ; }
|
||||||
|
add rule ip cni_hostport hostip_hostports ip daddr 192.168.0.2 tcp dport 8083 dnat to 10.0.0.2:83 comment "icee6giejonei6so"
|
||||||
|
add rule ip cni_hostport hostports tcp dport 8080 dnat to 10.0.0.2:80 comment "icee6giejonei6so"
|
||||||
|
add rule ip cni_hostport hostports tcp dport 8081 dnat to 10.0.0.2:80 comment "icee6giejonei6so"
|
||||||
|
add rule ip cni_hostport hostports udp dport 8080 dnat to 10.0.0.2:81 comment "icee6giejonei6so"
|
||||||
|
add rule ip cni_hostport hostports udp dport 8082 dnat to 10.0.0.2:82 comment "icee6giejonei6so"
|
||||||
|
add rule ip cni_hostport hostports tcp dport 8084 dnat to 10.0.0.2:84 comment "icee6giejonei6so"
|
||||||
|
add rule ip cni_hostport masquerading ip saddr 10.0.0.2 ip daddr 10.0.0.2 masquerade comment "icee6giejonei6so"
|
||||||
|
add rule ip cni_hostport masquerading ip saddr 127.0.0.1 ip daddr 10.0.0.2 masquerade comment "icee6giejonei6so"
|
||||||
|
add rule ip cni_hostport output a b jump hostip_hostports
|
||||||
|
add rule ip cni_hostport output a b fib daddr type local jump hostports
|
||||||
|
add rule ip cni_hostport prerouting a b jump hostip_hostports
|
||||||
|
add rule ip cni_hostport prerouting a b jump hostports
|
||||||
|
`)
|
||||||
|
actualRules := strings.TrimSpace(ipv4Fake.Dump())
|
||||||
|
Expect(actualRules).To(Equal(expectedRules))
|
||||||
|
|
||||||
|
// Disable snat, generate IPv6 rules
|
||||||
|
*conf.SNAT = false
|
||||||
|
containerNet, err = types.ParseCIDR("2001:db8::2/64")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
err = pmNFT.forwardPorts(conf, *containerNet)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
expectedRules = strings.TrimSpace(`
|
||||||
|
add table ip6 cni_hostport { comment "CNI portmap plugin" ; }
|
||||||
|
add chain ip6 cni_hostport hostip_hostports
|
||||||
|
add chain ip6 cni_hostport hostports
|
||||||
|
add chain ip6 cni_hostport output { type nat hook output priority -100 ; }
|
||||||
|
add chain ip6 cni_hostport prerouting { type nat hook prerouting priority -100 ; }
|
||||||
|
add rule ip6 cni_hostport hostip_hostports ip6 daddr 2001:db8:a::1 tcp dport 8085 dnat to [2001:db8::2]:85 comment "icee6giejonei6so"
|
||||||
|
add rule ip6 cni_hostport hostports tcp dport 8080 dnat to [2001:db8::2]:80 comment "icee6giejonei6so"
|
||||||
|
add rule ip6 cni_hostport hostports tcp dport 8081 dnat to [2001:db8::2]:80 comment "icee6giejonei6so"
|
||||||
|
add rule ip6 cni_hostport hostports udp dport 8080 dnat to [2001:db8::2]:81 comment "icee6giejonei6so"
|
||||||
|
add rule ip6 cni_hostport hostports udp dport 8082 dnat to [2001:db8::2]:82 comment "icee6giejonei6so"
|
||||||
|
add rule ip6 cni_hostport hostports tcp dport 8086 dnat to [2001:db8::2]:86 comment "icee6giejonei6so"
|
||||||
|
add rule ip6 cni_hostport output c d jump hostip_hostports
|
||||||
|
add rule ip6 cni_hostport output c d fib daddr type local jump hostports
|
||||||
|
add rule ip6 cni_hostport prerouting c d jump hostip_hostports
|
||||||
|
add rule ip6 cni_hostport prerouting c d jump hostports
|
||||||
|
`)
|
||||||
|
actualRules = strings.TrimSpace(ipv6Fake.Dump())
|
||||||
|
Expect(actualRules).To(Equal(expectedRules))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
@ -24,9 +24,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("portmapping configuration", func() {
|
var _ = Describe("portmapping configuration", func() {
|
||||||
netName := "testNetName"
|
|
||||||
containerID := "icee6giejonei6sohng6ahngee7laquohquee9shiGo7fohferakah3Feiyoolu2pei7ciPhoh7shaoX6vai3vuf0ahfaeng8yohb9ceu0daez5hashee8ooYai5wa3y"
|
|
||||||
|
|
||||||
for _, ver := range []string{"0.3.0", "0.3.1", "0.4.0", "1.0.0"} {
|
for _, ver := range []string{"0.3.0", "0.3.1", "0.4.0", "1.0.0"} {
|
||||||
// Redefine ver inside for scope so real value is picked up by each dynamically defined It()
|
// Redefine ver inside for scope so real value is picked up by each dynamically defined It()
|
||||||
// See Gingkgo's "Patterns for dynamically generating tests" documentation.
|
// See Gingkgo's "Patterns for dynamically generating tests" documentation.
|
||||||
@ -38,6 +35,7 @@ var _ = Describe("portmapping configuration", func() {
|
|||||||
"name": "test",
|
"name": "test",
|
||||||
"type": "portmap",
|
"type": "portmap",
|
||||||
"cniVersion": "%s",
|
"cniVersion": "%s",
|
||||||
|
"backend": "iptables",
|
||||||
"runtimeConfig": {
|
"runtimeConfig": {
|
||||||
"portMappings": [
|
"portMappings": [
|
||||||
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"},
|
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"},
|
||||||
@ -45,8 +43,8 @@ var _ = Describe("portmapping configuration", func() {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"snat": false,
|
"snat": false,
|
||||||
"conditionsV4": ["a", "b"],
|
"conditionsV4": ["-s", "1.2.3.4"],
|
||||||
"conditionsV6": ["c", "d"],
|
"conditionsV6": ["!", "-s", "12::34"],
|
||||||
"prevResult": {
|
"prevResult": {
|
||||||
"interfaces": [
|
"interfaces": [
|
||||||
{"name": "host"},
|
{"name": "host"},
|
||||||
@ -77,8 +75,8 @@ var _ = Describe("portmapping configuration", func() {
|
|||||||
c, _, err := parseConfig(configBytes, "container")
|
c, _, err := parseConfig(configBytes, "container")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(c.CNIVersion).To(Equal(ver))
|
Expect(c.CNIVersion).To(Equal(ver))
|
||||||
Expect(c.ConditionsV4).To(Equal(&[]string{"a", "b"}))
|
Expect(c.ConditionsV4).To(Equal(&[]string{"-s", "1.2.3.4"}))
|
||||||
Expect(c.ConditionsV6).To(Equal(&[]string{"c", "d"}))
|
Expect(c.ConditionsV6).To(Equal(&[]string{"!", "-s", "12::34"}))
|
||||||
fvar := false
|
fvar := false
|
||||||
Expect(c.SNAT).To(Equal(&fvar))
|
Expect(c.SNAT).To(Equal(&fvar))
|
||||||
Expect(c.Name).To(Equal("test"))
|
Expect(c.Name).To(Equal("test"))
|
||||||
@ -97,15 +95,16 @@ var _ = Describe("portmapping configuration", func() {
|
|||||||
"name": "test",
|
"name": "test",
|
||||||
"type": "portmap",
|
"type": "portmap",
|
||||||
"cniVersion": "%s",
|
"cniVersion": "%s",
|
||||||
|
"backend": "iptables",
|
||||||
"snat": false,
|
"snat": false,
|
||||||
"conditionsV4": ["a", "b"],
|
"conditionsV4": ["-s", "1.2.3.4"],
|
||||||
"conditionsV6": ["c", "d"]
|
"conditionsV6": ["-s", "12::34"]
|
||||||
}`, ver))
|
}`, ver))
|
||||||
c, _, err := parseConfig(configBytes, "container")
|
c, _, err := parseConfig(configBytes, "container")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(c.CNIVersion).To(Equal(ver))
|
Expect(c.CNIVersion).To(Equal(ver))
|
||||||
Expect(c.ConditionsV4).To(Equal(&[]string{"a", "b"}))
|
Expect(c.ConditionsV4).To(Equal(&[]string{"-s", "1.2.3.4"}))
|
||||||
Expect(c.ConditionsV6).To(Equal(&[]string{"c", "d"}))
|
Expect(c.ConditionsV6).To(Equal(&[]string{"-s", "12::34"}))
|
||||||
fvar := false
|
fvar := false
|
||||||
Expect(c.SNAT).To(Equal(&fvar))
|
Expect(c.SNAT).To(Equal(&fvar))
|
||||||
Expect(c.Name).To(Equal("test"))
|
Expect(c.Name).To(Equal("test"))
|
||||||
@ -116,9 +115,10 @@ var _ = Describe("portmapping configuration", func() {
|
|||||||
"name": "test",
|
"name": "test",
|
||||||
"type": "portmap",
|
"type": "portmap",
|
||||||
"cniVersion": "%s",
|
"cniVersion": "%s",
|
||||||
|
"backend": "iptables",
|
||||||
"snat": false,
|
"snat": false,
|
||||||
"conditionsV4": ["a", "b"],
|
"conditionsV4": ["-s", "1.2.3.4"],
|
||||||
"conditionsV6": ["c", "d"],
|
"conditionsV6": ["-s", "12::34"],
|
||||||
"runtimeConfig": {
|
"runtimeConfig": {
|
||||||
"portMappings": [
|
"portMappings": [
|
||||||
{ "hostPort": 0, "containerPort": 80, "protocol": "tcp"}
|
{ "hostPort": 0, "containerPort": 80, "protocol": "tcp"}
|
||||||
@ -129,6 +129,82 @@ var _ = Describe("portmapping configuration", func() {
|
|||||||
Expect(err).To(MatchError("Invalid host port number: 0"))
|
Expect(err).To(MatchError("Invalid host port number: 0"))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It(fmt.Sprintf("[%s] defaults to iptables when backend is not specified", ver), func() {
|
||||||
|
// "defaults to iptables" is only true if iptables is installed
|
||||||
|
// (or if neither iptables nor nftables is installed), but the
|
||||||
|
// other unit tests would fail if iptables wasn't installed, so
|
||||||
|
// we know it must be.
|
||||||
|
configBytes := []byte(fmt.Sprintf(`{
|
||||||
|
"name": "test",
|
||||||
|
"type": "portmap",
|
||||||
|
"cniVersion": "%s"
|
||||||
|
}`, ver))
|
||||||
|
c, _, err := parseConfig(configBytes, "container")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(c.CNIVersion).To(Equal(ver))
|
||||||
|
Expect(c.Backend).To(Equal(&iptablesBackend))
|
||||||
|
Expect(c.Name).To(Equal("test"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It(fmt.Sprintf("[%s] uses nftables if requested", ver), func() {
|
||||||
|
configBytes := []byte(fmt.Sprintf(`{
|
||||||
|
"name": "test",
|
||||||
|
"type": "portmap",
|
||||||
|
"cniVersion": "%s",
|
||||||
|
"backend": "nftables"
|
||||||
|
}`, ver))
|
||||||
|
c, _, err := parseConfig(configBytes, "container")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(c.CNIVersion).To(Equal(ver))
|
||||||
|
Expect(c.Backend).To(Equal(&nftablesBackend))
|
||||||
|
Expect(c.Name).To(Equal("test"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It(fmt.Sprintf("[%s] allows nftables conditions if nftables is requested", ver), func() {
|
||||||
|
configBytes := []byte(fmt.Sprintf(`{
|
||||||
|
"name": "test",
|
||||||
|
"type": "portmap",
|
||||||
|
"cniVersion": "%s",
|
||||||
|
"backend": "nftables",
|
||||||
|
"conditionsV4": ["ip", "saddr", "1.2.3.4"],
|
||||||
|
"conditionsV6": ["ip6", "saddr", "12::34"]
|
||||||
|
}`, ver))
|
||||||
|
c, _, err := parseConfig(configBytes, "container")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(c.CNIVersion).To(Equal(ver))
|
||||||
|
Expect(c.Backend).To(Equal(&nftablesBackend))
|
||||||
|
Expect(c.ConditionsV4).To(Equal(&[]string{"ip", "saddr", "1.2.3.4"}))
|
||||||
|
Expect(c.ConditionsV6).To(Equal(&[]string{"ip6", "saddr", "12::34"}))
|
||||||
|
Expect(c.Name).To(Equal("test"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It(fmt.Sprintf("[%s] rejects nftables options with 'backend: iptables'", ver), func() {
|
||||||
|
configBytes := []byte(fmt.Sprintf(`{
|
||||||
|
"name": "test",
|
||||||
|
"type": "portmap",
|
||||||
|
"cniVersion": "%s",
|
||||||
|
"backend": "iptables",
|
||||||
|
"conditionsV4": ["ip", "saddr", "1.2.3.4"],
|
||||||
|
"conditionsV6": ["ip6", "saddr", "12::34"]
|
||||||
|
}`, ver))
|
||||||
|
_, _, err := parseConfig(configBytes, "container")
|
||||||
|
Expect(err).To(MatchError("iptables backend was requested but configuration contains nftables-specific options [conditionsV4 conditionsV6]"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It(fmt.Sprintf("[%s] rejects iptables options with 'backend: nftables'", ver), func() {
|
||||||
|
configBytes := []byte(fmt.Sprintf(`{
|
||||||
|
"name": "test",
|
||||||
|
"type": "portmap",
|
||||||
|
"cniVersion": "%s",
|
||||||
|
"backend": "nftables",
|
||||||
|
"externalSetMarkChain": "KUBE-MARK-MASQ",
|
||||||
|
"conditionsV4": ["-s", "1.2.3.4"],
|
||||||
|
"conditionsV6": ["-s", "12::34"]
|
||||||
|
}`, ver))
|
||||||
|
_, _, err := parseConfig(configBytes, "container")
|
||||||
|
Expect(err).To(MatchError("nftables backend was requested but configuration contains iptables-specific options [externalSetMarkChain conditionsV4 conditionsV6]"))
|
||||||
|
})
|
||||||
|
|
||||||
It(fmt.Sprintf("[%s] does not fail on missing prevResult interface index", ver), func() {
|
It(fmt.Sprintf("[%s] does not fail on missing prevResult interface index", ver), func() {
|
||||||
configBytes := []byte(fmt.Sprintf(`{
|
configBytes := []byte(fmt.Sprintf(`{
|
||||||
"name": "test",
|
"name": "test",
|
||||||
@ -139,7 +215,7 @@ var _ = Describe("portmapping configuration", func() {
|
|||||||
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"}
|
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"conditionsV4": ["a", "b"],
|
"conditionsV4": ["-s", "1.2.3.4"],
|
||||||
"prevResult": {
|
"prevResult": {
|
||||||
"interfaces": [
|
"interfaces": [
|
||||||
{"name": "host"}
|
{"name": "host"}
|
||||||
@ -157,222 +233,5 @@ var _ = Describe("portmapping configuration", func() {
|
|||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("Generating chains", func() {
|
|
||||||
Context("for DNAT", func() {
|
|
||||||
It(fmt.Sprintf("[%s] generates a correct standard container chain", ver), func() {
|
|
||||||
ch := genDnatChain(netName, containerID)
|
|
||||||
|
|
||||||
Expect(ch).To(Equal(chain{
|
|
||||||
table: "nat",
|
|
||||||
name: "CNI-DN-bfd599665540dd91d5d28",
|
|
||||||
entryChains: []string{TopLevelDNATChainName},
|
|
||||||
}))
|
|
||||||
configBytes := []byte(fmt.Sprintf(`{
|
|
||||||
"name": "test",
|
|
||||||
"type": "portmap",
|
|
||||||
"cniVersion": "%s",
|
|
||||||
"runtimeConfig": {
|
|
||||||
"portMappings": [
|
|
||||||
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"},
|
|
||||||
{ "hostPort": 8081, "containerPort": 80, "protocol": "tcp"},
|
|
||||||
{ "hostPort": 8080, "containerPort": 81, "protocol": "udp"},
|
|
||||||
{ "hostPort": 8082, "containerPort": 82, "protocol": "udp"},
|
|
||||||
{ "hostPort": 8083, "containerPort": 83, "protocol": "tcp", "hostIP": "192.168.0.2"},
|
|
||||||
{ "hostPort": 8084, "containerPort": 84, "protocol": "tcp", "hostIP": "0.0.0.0"},
|
|
||||||
{ "hostPort": 8085, "containerPort": 85, "protocol": "tcp", "hostIP": "2001:db8:a::1"},
|
|
||||||
{ "hostPort": 8086, "containerPort": 86, "protocol": "tcp", "hostIP": "::"}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"snat": true,
|
|
||||||
"conditionsV4": ["a", "b"],
|
|
||||||
"conditionsV6": ["c", "d"]
|
|
||||||
}`, ver))
|
|
||||||
|
|
||||||
conf, _, err := parseConfig(configBytes, "foo")
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
conf.ContainerID = containerID
|
|
||||||
|
|
||||||
ch = genDnatChain(conf.Name, containerID)
|
|
||||||
Expect(ch).To(Equal(chain{
|
|
||||||
table: "nat",
|
|
||||||
name: "CNI-DN-67e92b96e692a494b6b85",
|
|
||||||
entryChains: []string{"CNI-HOSTPORT-DNAT"},
|
|
||||||
}))
|
|
||||||
|
|
||||||
n, err := types.ParseCIDR("10.0.0.2/24")
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
fillDnatRules(&ch, conf, *n)
|
|
||||||
|
|
||||||
Expect(ch.entryRules).To(Equal([][]string{
|
|
||||||
{
|
|
||||||
"-m", "comment", "--comment",
|
|
||||||
fmt.Sprintf("dnat name: \"test\" id: \"%s\"", containerID),
|
|
||||||
"-m", "multiport",
|
|
||||||
"-p", "tcp",
|
|
||||||
"--destination-ports", "8080,8081,8083,8084,8085,8086",
|
|
||||||
"a", "b",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"-m", "comment", "--comment",
|
|
||||||
fmt.Sprintf("dnat name: \"test\" id: \"%s\"", containerID),
|
|
||||||
"-m", "multiport",
|
|
||||||
"-p", "udp",
|
|
||||||
"--destination-ports", "8080,8082",
|
|
||||||
"a", "b",
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
|
|
||||||
Expect(ch.rules).To(Equal([][]string{
|
|
||||||
// tcp rules and not hostIP
|
|
||||||
{"-p", "tcp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
|
|
||||||
{"-p", "tcp", "--dport", "8080", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
|
|
||||||
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
|
|
||||||
{"-p", "tcp", "--dport", "8081", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
|
|
||||||
{"-p", "tcp", "--dport", "8081", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
|
|
||||||
{"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
|
|
||||||
// udp rules and not hostIP
|
|
||||||
{"-p", "udp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
|
|
||||||
{"-p", "udp", "--dport", "8080", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
|
|
||||||
{"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:81"},
|
|
||||||
{"-p", "udp", "--dport", "8082", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
|
|
||||||
{"-p", "udp", "--dport", "8082", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
|
|
||||||
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "10.0.0.2:82"},
|
|
||||||
// tcp rules and hostIP
|
|
||||||
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
|
|
||||||
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
|
|
||||||
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-j", "DNAT", "--to-destination", "10.0.0.2:83"},
|
|
||||||
// tcp rules and hostIP = "0.0.0.0"
|
|
||||||
{"-p", "tcp", "--dport", "8084", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
|
|
||||||
{"-p", "tcp", "--dport", "8084", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
|
|
||||||
{"-p", "tcp", "--dport", "8084", "-j", "DNAT", "--to-destination", "10.0.0.2:84"},
|
|
||||||
}))
|
|
||||||
|
|
||||||
ch.rules = nil
|
|
||||||
ch.entryRules = nil
|
|
||||||
|
|
||||||
n, err = types.ParseCIDR("2001:db8::2/64")
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
fillDnatRules(&ch, conf, *n)
|
|
||||||
|
|
||||||
Expect(ch.rules).To(Equal([][]string{
|
|
||||||
// tcp rules and not hostIP
|
|
||||||
{"-p", "tcp", "--dport", "8080", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
|
|
||||||
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "[2001:db8::2]:80"},
|
|
||||||
{"-p", "tcp", "--dport", "8081", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
|
|
||||||
{"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "[2001:db8::2]:80"},
|
|
||||||
// udp rules and not hostIP
|
|
||||||
{"-p", "udp", "--dport", "8080", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
|
|
||||||
{"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "[2001:db8::2]:81"},
|
|
||||||
{"-p", "udp", "--dport", "8082", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
|
|
||||||
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "[2001:db8::2]:82"},
|
|
||||||
// tcp rules and hostIP
|
|
||||||
{"-p", "tcp", "--dport", "8085", "-d", "2001:db8:a::1", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
|
|
||||||
{"-p", "tcp", "--dport", "8085", "-d", "2001:db8:a::1", "-j", "DNAT", "--to-destination", "[2001:db8::2]:85"},
|
|
||||||
// tcp rules and hostIP = "::"
|
|
||||||
{"-p", "tcp", "--dport", "8086", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
|
|
||||||
{"-p", "tcp", "--dport", "8086", "-j", "DNAT", "--to-destination", "[2001:db8::2]:86"},
|
|
||||||
}))
|
|
||||||
|
|
||||||
// Disable snat, generate rules
|
|
||||||
ch.rules = nil
|
|
||||||
ch.entryRules = nil
|
|
||||||
fvar := false
|
|
||||||
conf.SNAT = &fvar
|
|
||||||
|
|
||||||
n, err = types.ParseCIDR("10.0.0.2/24")
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
fillDnatRules(&ch, conf, *n)
|
|
||||||
Expect(ch.rules).To(Equal([][]string{
|
|
||||||
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
|
|
||||||
{"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
|
|
||||||
{"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:81"},
|
|
||||||
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "10.0.0.2:82"},
|
|
||||||
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-j", "DNAT", "--to-destination", "10.0.0.2:83"},
|
|
||||||
{"-p", "tcp", "--dport", "8084", "-j", "DNAT", "--to-destination", "10.0.0.2:84"},
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
|
|
||||||
It(fmt.Sprintf("[%s] generates a correct chain with external mark", ver), func() {
|
|
||||||
ch := genDnatChain(netName, containerID)
|
|
||||||
|
|
||||||
Expect(ch).To(Equal(chain{
|
|
||||||
table: "nat",
|
|
||||||
name: "CNI-DN-bfd599665540dd91d5d28",
|
|
||||||
entryChains: []string{TopLevelDNATChainName},
|
|
||||||
}))
|
|
||||||
configBytes := []byte(fmt.Sprintf(`{
|
|
||||||
"name": "test",
|
|
||||||
"type": "portmap",
|
|
||||||
"cniVersion": "%s",
|
|
||||||
"runtimeConfig": {
|
|
||||||
"portMappings": [
|
|
||||||
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"externalSetMarkChain": "PLZ-SET-MARK",
|
|
||||||
"conditionsV4": ["a", "b"],
|
|
||||||
"conditionsV6": ["c", "d"]
|
|
||||||
}`, ver))
|
|
||||||
|
|
||||||
conf, _, err := parseConfig(configBytes, "foo")
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
conf.ContainerID = containerID
|
|
||||||
|
|
||||||
ch = genDnatChain(conf.Name, containerID)
|
|
||||||
n, err := types.ParseCIDR("10.0.0.2/24")
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
fillDnatRules(&ch, conf, *n)
|
|
||||||
Expect(ch.rules).To(Equal([][]string{
|
|
||||||
{"-p", "tcp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "PLZ-SET-MARK"},
|
|
||||||
{"-p", "tcp", "--dport", "8080", "-s", "127.0.0.1", "-j", "PLZ-SET-MARK"},
|
|
||||||
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
|
|
||||||
It(fmt.Sprintf("[%s] generates a correct top-level chain", ver), func() {
|
|
||||||
ch := genToplevelDnatChain()
|
|
||||||
|
|
||||||
Expect(ch).To(Equal(chain{
|
|
||||||
table: "nat",
|
|
||||||
name: "CNI-HOSTPORT-DNAT",
|
|
||||||
entryChains: []string{"PREROUTING", "OUTPUT"},
|
|
||||||
entryRules: [][]string{{"-m", "addrtype", "--dst-type", "LOCAL"}},
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
|
|
||||||
It(fmt.Sprintf("[%s] generates the correct mark chains", ver), func() {
|
|
||||||
masqBit := 5
|
|
||||||
ch := genSetMarkChain(masqBit)
|
|
||||||
Expect(ch).To(Equal(chain{
|
|
||||||
table: "nat",
|
|
||||||
name: "CNI-HOSTPORT-SETMARK",
|
|
||||||
rules: [][]string{{
|
|
||||||
"-m", "comment",
|
|
||||||
"--comment", "CNI portfwd masquerade mark",
|
|
||||||
"-j", "MARK",
|
|
||||||
"--set-xmark", "0x20/0x20",
|
|
||||||
}},
|
|
||||||
}))
|
|
||||||
|
|
||||||
ch = genMarkMasqChain(masqBit)
|
|
||||||
Expect(ch).To(Equal(chain{
|
|
||||||
table: "nat",
|
|
||||||
name: "CNI-HOSTPORT-MASQ",
|
|
||||||
entryChains: []string{"POSTROUTING"},
|
|
||||||
entryRules: [][]string{{
|
|
||||||
"-m", "comment",
|
|
||||||
"--comment", "CNI portfwd requiring masquerade",
|
|
||||||
}},
|
|
||||||
rules: [][]string{{
|
|
||||||
"-m", "mark",
|
|
||||||
"--mark", "0x20/0x20",
|
|
||||||
"-j", "MASQUERADE",
|
|
||||||
}},
|
|
||||||
prependEntry: true,
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -21,6 +21,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
|
"github.com/containernetworking/plugins/pkg/utils/sysctl"
|
||||||
)
|
)
|
||||||
|
|
||||||
// fmtIpPort correctly formats ip:port literals for iptables and ip6tables -
|
// fmtIpPort correctly formats ip:port literals for iptables and ip6tables -
|
||||||
@ -52,6 +54,14 @@ func getRoutableHostIF(containerIP net.IP) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// enableLocalnetRouting tells the kernel not to treat 127/8 as a martian,
|
||||||
|
// so that connections with a source ip of 127/8 can cross a routing boundary.
|
||||||
|
func enableLocalnetRouting(ifName string) error {
|
||||||
|
routeLocalnetPath := "net/ipv4/conf/" + ifName + "/route_localnet"
|
||||||
|
_, err := sysctl.Sysctl(routeLocalnetPath, "1")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// groupByProto groups port numbers by protocol
|
// groupByProto groups port numbers by protocol
|
||||||
func groupByProto(entries []PortMapEntry) map[string][]int {
|
func groupByProto(entries []PortMapEntry) map[string][]int {
|
||||||
if len(entries) == 0 {
|
if len(entries) == 0 {
|
||||||
|
@ -47,6 +47,7 @@ type PluginConf struct {
|
|||||||
PrevResult *current.Result `json:"-"`
|
PrevResult *current.Result `json:"-"`
|
||||||
|
|
||||||
// Add plugin-specific flags here
|
// Add plugin-specific flags here
|
||||||
|
Table *int `json:"table,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper that does a lock before and unlock after operations to serialise
|
// Wrapper that does a lock before and unlock after operations to serialise
|
||||||
@ -66,7 +67,6 @@ func withLockAndNetNS(nspath string, toRun func(_ ns.NetNS) error) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = ns.WithNetNSPath(nspath, toRun)
|
err = ns.WithNetNSPath(nspath, toRun)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -164,6 +164,9 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
// Do the actual work.
|
// Do the actual work.
|
||||||
err = withLockAndNetNS(args.Netns, func(_ ns.NetNS) error {
|
err = withLockAndNetNS(args.Netns, func(_ ns.NetNS) error {
|
||||||
|
if conf.Table != nil {
|
||||||
|
return doRoutesWithTable(ipCfgs, *conf.Table)
|
||||||
|
}
|
||||||
return doRoutes(ipCfgs, args.IfName)
|
return doRoutes(ipCfgs, args.IfName)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -331,31 +334,73 @@ func doRoutes(ipCfgs []*current.IPConfig, iface string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func doRoutesWithTable(ipCfgs []*current.IPConfig, table int) error {
|
||||||
|
for _, ipCfg := range ipCfgs {
|
||||||
|
log.Printf("Set rule for source %s", ipCfg.String())
|
||||||
|
rule := netlink.NewRule()
|
||||||
|
rule.Table = table
|
||||||
|
|
||||||
|
// Source must be restricted to a single IP, not a full subnet
|
||||||
|
var src net.IPNet
|
||||||
|
src.IP = ipCfg.Address.IP
|
||||||
|
if src.IP.To4() != nil {
|
||||||
|
src.Mask = net.CIDRMask(32, 32)
|
||||||
|
} else {
|
||||||
|
src.Mask = net.CIDRMask(128, 128)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Source to use %s", src.String())
|
||||||
|
rule.Src = &src
|
||||||
|
|
||||||
|
if err := netlink.RuleAdd(rule); err != nil {
|
||||||
|
return fmt.Errorf("failed to add rule: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// cmdDel is called for DELETE requests
|
// cmdDel is called for DELETE requests
|
||||||
func cmdDel(args *skel.CmdArgs) error {
|
func cmdDel(args *skel.CmdArgs) error {
|
||||||
// We care a bit about config because it sets log level.
|
// We care a bit about config because it sets log level.
|
||||||
_, err := parseConfig(args.StdinData)
|
conf, err := parseConfig(args.StdinData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Cleaning up SBR for %s", args.IfName)
|
log.Printf("Cleaning up SBR for %s", args.IfName)
|
||||||
err = withLockAndNetNS(args.Netns, func(_ ns.NetNS) error {
|
err = withLockAndNetNS(args.Netns, func(_ ns.NetNS) error {
|
||||||
return tidyRules(args.IfName)
|
return tidyRules(args.IfName, conf.Table)
|
||||||
})
|
})
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tidy up the rules for the deleted interface
|
// Tidy up the rules for the deleted interface
|
||||||
func tidyRules(iface string) error {
|
func tidyRules(iface string, table *int) error {
|
||||||
// We keep on going on rule deletion error, but return the last failure.
|
// We keep on going on rule deletion error, but return the last failure.
|
||||||
var errReturn error
|
var errReturn error
|
||||||
|
var err error
|
||||||
|
var rules []netlink.Rule
|
||||||
|
|
||||||
rules, err := netlink.RuleList(netlink.FAMILY_ALL)
|
if table != nil {
|
||||||
if err != nil {
|
rules, err = netlink.RuleListFiltered(
|
||||||
log.Printf("Failed to list all rules to tidy: %v", err)
|
netlink.FAMILY_ALL,
|
||||||
return fmt.Errorf("Failed to list all rules to tidy: %v", err)
|
&netlink.Rule{
|
||||||
|
Table: *table,
|
||||||
|
},
|
||||||
|
netlink.RT_FILTER_TABLE,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to list rules of table %d to tidy: %v", *table, err)
|
||||||
|
return fmt.Errorf("failed to list rules of table %d to tidy: %v", *table, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rules, err = netlink.RuleList(netlink.FAMILY_ALL)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to list all rules to tidy: %v", err)
|
||||||
|
return fmt.Errorf("Failed to list all rules to tidy: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
link, err := netlink.LinkByName(iface)
|
link, err := netlink.LinkByName(iface)
|
||||||
@ -402,7 +447,13 @@ RULE_LOOP:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("sbr"))
|
skel.PluginMainFuncs(skel.CNIFuncs{
|
||||||
|
Add: cmdAdd,
|
||||||
|
Check: cmdCheck,
|
||||||
|
Del: cmdDel,
|
||||||
|
/* FIXME GC */
|
||||||
|
/* FIXME Status */
|
||||||
|
}, version.All, bv.BuildString("sbr"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdCheck(_ *skel.CmdArgs) error {
|
func cmdCheck(_ *skel.CmdArgs) error {
|
||||||
|
@ -53,7 +53,9 @@ func setup(targetNs ns.NetNS, status netStatus) error {
|
|||||||
err := targetNs.Do(func(_ ns.NetNS) error {
|
err := targetNs.Do(func(_ ns.NetNS) error {
|
||||||
for _, dev := range status.Devices {
|
for _, dev := range status.Devices {
|
||||||
log.Printf("Adding dev %s\n", dev.Name)
|
log.Printf("Adding dev %s\n", dev.Name)
|
||||||
link := &netlink.Dummy{LinkAttrs: netlink.LinkAttrs{Name: dev.Name}}
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = dev.Name
|
||||||
|
link := &netlink.Dummy{LinkAttrs: linkAttrs}
|
||||||
err := netlink.LinkAdd(link)
|
err := netlink.LinkAdd(link)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -117,11 +119,16 @@ func readback(targetNs ns.NetNS, devNames []string) (netStatus, error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
routesNoLinkLocal := []netlink.Route{}
|
||||||
for _, route := range routes {
|
for _, route := range routes {
|
||||||
|
if route.Dst.IP.IsLinkLocalMulticast() || route.Dst.IP.IsLinkLocalUnicast() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
log.Printf("Got %s route %v", name, route)
|
log.Printf("Got %s route %v", name, route)
|
||||||
|
routesNoLinkLocal = append(routesNoLinkLocal, route)
|
||||||
}
|
}
|
||||||
|
|
||||||
retVal.Devices[i].Routes = routes
|
retVal.Devices[i].Routes = routesNoLinkLocal
|
||||||
}
|
}
|
||||||
|
|
||||||
rules, err := netlink.RuleList(netlink.FAMILY_ALL)
|
rules, err := netlink.RuleList(netlink.FAMILY_ALL)
|
||||||
@ -291,6 +298,7 @@ var _ = Describe("sbr test", func() {
|
|||||||
expNet1.Routes = append(expNet1.Routes,
|
expNet1.Routes = append(expNet1.Routes,
|
||||||
netlink.Route{
|
netlink.Route{
|
||||||
Gw: net.IPv4(192, 168, 1, 1),
|
Gw: net.IPv4(192, 168, 1, 1),
|
||||||
|
Dst: &net.IPNet{IP: net.IPv4zero, Mask: net.IPMask(net.IPv4zero)},
|
||||||
Table: 100,
|
Table: 100,
|
||||||
LinkIndex: expNet1.Routes[0].LinkIndex,
|
LinkIndex: expNet1.Routes[0].LinkIndex,
|
||||||
})
|
})
|
||||||
@ -491,6 +499,7 @@ var _ = Describe("sbr test", func() {
|
|||||||
}
|
}
|
||||||
expNet1.Routes = append(expNet1.Routes,
|
expNet1.Routes = append(expNet1.Routes,
|
||||||
netlink.Route{
|
netlink.Route{
|
||||||
|
Dst: &net.IPNet{IP: net.IPv4zero, Mask: net.IPMask(net.IPv4zero)},
|
||||||
Gw: net.IPv4(192, 168, 1, 1),
|
Gw: net.IPv4(192, 168, 1, 1),
|
||||||
Table: 100,
|
Table: 100,
|
||||||
LinkIndex: expNet1.Routes[0].LinkIndex,
|
LinkIndex: expNet1.Routes[0].LinkIndex,
|
||||||
@ -498,6 +507,7 @@ var _ = Describe("sbr test", func() {
|
|||||||
|
|
||||||
expNet1.Routes = append(expNet1.Routes,
|
expNet1.Routes = append(expNet1.Routes,
|
||||||
netlink.Route{
|
netlink.Route{
|
||||||
|
Dst: &net.IPNet{IP: net.IPv4zero, Mask: net.IPMask(net.IPv4zero)},
|
||||||
Gw: net.IPv4(192, 168, 101, 1),
|
Gw: net.IPv4(192, 168, 101, 1),
|
||||||
Table: 101,
|
Table: 101,
|
||||||
LinkIndex: expNet1.Routes[0].LinkIndex,
|
LinkIndex: expNet1.Routes[0].LinkIndex,
|
||||||
@ -540,4 +550,81 @@ var _ = Describe("sbr test", func() {
|
|||||||
_, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) })
|
_, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) })
|
||||||
Expect(err).To(MatchError("This plugin must be called as chained plugin"))
|
Expect(err).To(MatchError("This plugin must be called as chained plugin"))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("Works with Table ID", func() {
|
||||||
|
ifname := "net1"
|
||||||
|
tableID := 5000
|
||||||
|
conf := `{
|
||||||
|
"cniVersion": "0.3.0",
|
||||||
|
"name": "cni-plugin-sbr-test",
|
||||||
|
"type": "sbr",
|
||||||
|
"table": %d,
|
||||||
|
"prevResult": {
|
||||||
|
"cniVersion": "0.3.0",
|
||||||
|
"interfaces": [
|
||||||
|
{
|
||||||
|
"name": "%s",
|
||||||
|
"sandbox": "%s"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ips": [
|
||||||
|
{
|
||||||
|
"address": "192.168.1.209/24",
|
||||||
|
"interface": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "192.168.101.209/24",
|
||||||
|
"interface": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"routes": []
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
conf = fmt.Sprintf(conf, tableID, ifname, targetNs.Path())
|
||||||
|
args := &skel.CmdArgs{
|
||||||
|
ContainerID: "dummy",
|
||||||
|
Netns: targetNs.Path(),
|
||||||
|
IfName: ifname,
|
||||||
|
StdinData: []byte(conf),
|
||||||
|
}
|
||||||
|
|
||||||
|
preStatus := createDefaultStatus()
|
||||||
|
|
||||||
|
err := setup(targetNs, preStatus)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
oldStatus, err := readback(targetNs, []string{"net1", "eth0"})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
_, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) })
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
newStatus, err := readback(targetNs, []string{"net1", "eth0"})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
// Routes have not been moved.
|
||||||
|
Expect(newStatus).To(Equal(oldStatus))
|
||||||
|
|
||||||
|
// Fetch all rules for the requested table ID.
|
||||||
|
var rules []netlink.Rule
|
||||||
|
err = targetNs.Do(func(_ ns.NetNS) error {
|
||||||
|
var err error
|
||||||
|
rules, err = netlink.RuleListFiltered(
|
||||||
|
netlink.FAMILY_ALL, &netlink.Rule{
|
||||||
|
Table: tableID,
|
||||||
|
},
|
||||||
|
netlink.RT_FILTER_TABLE,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(rules).To(HaveLen(2))
|
||||||
|
|
||||||
|
// Both IPs have been added as source based routes with requested table ID.
|
||||||
|
Expect(rules[0].Table).To(Equal(tableID))
|
||||||
|
Expect(rules[0].Src.String()).To(Equal("192.168.101.209/32"))
|
||||||
|
Expect(rules[1].Table).To(Equal(tableID))
|
||||||
|
Expect(rules[1].Src.String()).To(Equal("192.168.1.209/32"))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -319,7 +319,7 @@ func restoreBackup(ifName, containerID, backupPath string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(errStr) > 0 {
|
if len(errStr) > 0 {
|
||||||
return fmt.Errorf(strings.Join(errStr, "; "))
|
return errors.New(strings.Join(errStr, "; "))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = os.Remove(filePath); err != nil {
|
if err = os.Remove(filePath); err != nil {
|
||||||
@ -433,7 +433,13 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("tuning"))
|
skel.PluginMainFuncs(skel.CNIFuncs{
|
||||||
|
Add: cmdAdd,
|
||||||
|
Check: cmdCheck,
|
||||||
|
Del: cmdDel,
|
||||||
|
/* FIXME GC */
|
||||||
|
/* FIXME Status */
|
||||||
|
}, version.All, bv.BuildString("tuning"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdCheck(args *skel.CmdArgs) error {
|
func cmdCheck(args *skel.CmdArgs) error {
|
||||||
|
@ -110,10 +110,10 @@ var _ = Describe("tuning plugin", func() {
|
|||||||
err = originalNS.Do(func(ns.NetNS) error {
|
err = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = IFNAME
|
||||||
err = netlink.LinkAdd(&netlink.Dummy{
|
err = netlink.LinkAdd(&netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: IFNAME,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
link, err := netlink.LinkByName(IFNAME)
|
link, err := netlink.LinkByName(IFNAME)
|
||||||
|
@ -39,7 +39,13 @@ type VRFNetConf struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.VersionsStartingFrom("0.3.1"), bv.BuildString("vrf"))
|
skel.PluginMainFuncs(skel.CNIFuncs{
|
||||||
|
Add: cmdAdd,
|
||||||
|
Check: cmdCheck,
|
||||||
|
Del: cmdDel,
|
||||||
|
/* FIXME GC */
|
||||||
|
/* FIXME Status */
|
||||||
|
}, version.VersionsStartingFrom("0.3.1"), bv.BuildString("vrf"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdAdd(args *skel.CmdArgs) error {
|
func cmdAdd(args *skel.CmdArgs) error {
|
||||||
@ -75,7 +81,6 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cmdAdd failed: %v", err)
|
return fmt.Errorf("cmdAdd failed: %v", err)
|
||||||
}
|
}
|
||||||
@ -121,7 +126,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.
|
||||||
|
@ -17,6 +17,8 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
)
|
)
|
||||||
@ -48,11 +50,11 @@ func createVRF(name string, tableID uint32) (*netlink.Vrf, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = name
|
||||||
vrf := &netlink.Vrf{
|
vrf := &netlink.Vrf{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: name,
|
Table: tableID,
|
||||||
},
|
|
||||||
Table: tableID,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = netlink.LinkAdd(vrf)
|
err = netlink.LinkAdd(vrf)
|
||||||
@ -124,7 +126,7 @@ func addInterface(vrf *netlink.Vrf, intf string) error {
|
|||||||
|
|
||||||
afterAddresses, err := netlink.AddrList(i, netlink.FAMILY_V6)
|
afterAddresses, err := netlink.AddrList(i, netlink.FAMILY_V6)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed getting ipv6 new addresses for %s", intf)
|
return fmt.Errorf("failed getting ipv6 new addresses for %s: %v", intf, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since keeping the ipv6 address depends on net.ipv6.conf.all.keep_addr_on_down ,
|
// Since keeping the ipv6 address depends on net.ipv6.conf.all.keep_addr_on_down ,
|
||||||
@ -141,6 +143,37 @@ CONTINUE:
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not restore address %s to %s @ %s: %v", toFind, intf, vrf.Name, err)
|
return fmt.Errorf("could not restore address %s to %s @ %s: %v", toFind, intf, vrf.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Waits for local/host routes to be added by the kernel.
|
||||||
|
maxRetry := 10
|
||||||
|
for {
|
||||||
|
routesVRFTable, err := netlink.RouteListFiltered(
|
||||||
|
netlink.FAMILY_ALL,
|
||||||
|
&netlink.Route{
|
||||||
|
Dst: &net.IPNet{
|
||||||
|
IP: toFind.IP,
|
||||||
|
Mask: net.IPMask{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||||
|
},
|
||||||
|
Table: int(vrf.Table),
|
||||||
|
LinkIndex: i.Attrs().Index,
|
||||||
|
},
|
||||||
|
netlink.RT_FILTER_OIF|netlink.RT_FILTER_TABLE|netlink.RT_FILTER_DST,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed getting routes for %s table %d for dst %s: %v", intf, vrf.Table, toFind.IPNet.String(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(routesVRFTable) >= 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
maxRetry--
|
||||||
|
if maxRetry <= 0 {
|
||||||
|
return fmt.Errorf("failed getting local/host addresses for %s in table %d with dst %s", intf, vrf.Table, toFind.IPNet.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply all saved routes for the interface that was moved to the VRF
|
// Apply all saved routes for the interface that was moved to the VRF
|
||||||
|
@ -19,6 +19,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo/v2"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
@ -94,19 +95,19 @@ var _ = Describe("vrf plugin", func() {
|
|||||||
err = targetNS.Do(func(ns.NetNS) error {
|
err = targetNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
la0 := netlink.NewLinkAttrs()
|
||||||
|
la0.Name = IF0Name
|
||||||
err = netlink.LinkAdd(&netlink.Dummy{
|
err = netlink.LinkAdd(&netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: la0,
|
||||||
Name: IF0Name,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
_, err = netlink.LinkByName(IF0Name)
|
_, err = netlink.LinkByName(IF0Name)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
la1 := netlink.NewLinkAttrs()
|
||||||
|
la1.Name = IF1Name
|
||||||
err = netlink.LinkAdd(&netlink.Dummy{
|
err = netlink.LinkAdd(&netlink.Dummy{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: la1,
|
||||||
Name: IF1Name,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
_, err = netlink.LinkByName(IF1Name)
|
_, err = netlink.LinkByName(IF1Name)
|
||||||
@ -207,6 +208,18 @@ var _ = Describe("vrf plugin", func() {
|
|||||||
// Add IP addresses for network reachability
|
// Add IP addresses for network reachability
|
||||||
netlink.AddrAdd(link, &netlink.Addr{IPNet: ipv4})
|
netlink.AddrAdd(link, &netlink.Addr{IPNet: ipv4})
|
||||||
netlink.AddrAdd(link, &netlink.Addr{IPNet: ipv6})
|
netlink.AddrAdd(link, &netlink.Addr{IPNet: ipv6})
|
||||||
|
// Wait for the corresponding route to be addeded
|
||||||
|
Eventually(func() bool {
|
||||||
|
ipv6RouteDst := &net.IPNet{
|
||||||
|
IP: ipv6.IP,
|
||||||
|
Mask: net.IPMask{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||||
|
}
|
||||||
|
routes, _ := netlink.RouteListFiltered(netlink.FAMILY_ALL, &netlink.Route{
|
||||||
|
Dst: ipv6RouteDst,
|
||||||
|
Table: 0,
|
||||||
|
}, netlink.RT_FILTER_DST|netlink.RT_FILTER_TABLE)
|
||||||
|
return err == nil && len(routes) >= 1
|
||||||
|
}, time.Second, 500*time.Millisecond).Should(BeTrue())
|
||||||
|
|
||||||
ipAddrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
ipAddrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
@ -304,6 +317,18 @@ var _ = Describe("vrf plugin", func() {
|
|||||||
// Add IP addresses for network reachability
|
// Add IP addresses for network reachability
|
||||||
netlink.AddrAdd(link, &netlink.Addr{IPNet: ipv4})
|
netlink.AddrAdd(link, &netlink.Addr{IPNet: ipv4})
|
||||||
netlink.AddrAdd(link, &netlink.Addr{IPNet: ipv6})
|
netlink.AddrAdd(link, &netlink.Addr{IPNet: ipv6})
|
||||||
|
// Wait for the corresponding route to be addeded
|
||||||
|
Eventually(func() bool {
|
||||||
|
ipv6RouteDst := &net.IPNet{
|
||||||
|
IP: ipv6.IP,
|
||||||
|
Mask: net.IPMask{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||||
|
}
|
||||||
|
routes, _ := netlink.RouteListFiltered(netlink.FAMILY_ALL, &netlink.Route{
|
||||||
|
Dst: ipv6RouteDst,
|
||||||
|
Table: 0,
|
||||||
|
}, netlink.RT_FILTER_DST|netlink.RT_FILTER_TABLE)
|
||||||
|
return err == nil && len(routes) >= 1
|
||||||
|
}, time.Second, 500*time.Millisecond).Should(BeTrue())
|
||||||
|
|
||||||
ipAddrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
ipAddrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
@ -362,6 +387,18 @@ var _ = Describe("vrf plugin", func() {
|
|||||||
// Add IP addresses for network reachability
|
// Add IP addresses for network reachability
|
||||||
netlink.AddrAdd(link, &netlink.Addr{IPNet: ipv4})
|
netlink.AddrAdd(link, &netlink.Addr{IPNet: ipv4})
|
||||||
netlink.AddrAdd(link, &netlink.Addr{IPNet: ipv6})
|
netlink.AddrAdd(link, &netlink.Addr{IPNet: ipv6})
|
||||||
|
// Wait for the corresponding route to be addeded
|
||||||
|
Eventually(func() bool {
|
||||||
|
ipv6RouteDst := &net.IPNet{
|
||||||
|
IP: ipv6.IP,
|
||||||
|
Mask: net.IPMask{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||||
|
}
|
||||||
|
routes, _ := netlink.RouteListFiltered(netlink.FAMILY_ALL, &netlink.Route{
|
||||||
|
Dst: ipv6RouteDst,
|
||||||
|
Table: 0,
|
||||||
|
}, netlink.RT_FILTER_DST|netlink.RT_FILTER_TABLE)
|
||||||
|
return err == nil && len(routes) >= 1
|
||||||
|
}, time.Second, 500*time.Millisecond).Should(BeTrue())
|
||||||
|
|
||||||
ipAddrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
ipAddrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
@ -437,10 +474,10 @@ var _ = Describe("vrf plugin", func() {
|
|||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
l, err := netlink.LinkByName(IF0Name)
|
l, err := netlink.LinkByName(IF0Name)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
|
linkAttrs.Name = "testrbridge"
|
||||||
br := &netlink.Bridge{
|
br := &netlink.Bridge{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: linkAttrs,
|
||||||
Name: "testrbridge",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
err = netlink.LinkAdd(br)
|
err = netlink.LinkAdd(br)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
@ -24,6 +24,7 @@ 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/ipam"
|
||||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -150,10 +151,40 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// replace TODO with your plugin name
|
// replace TODO with your plugin name
|
||||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("TODO"))
|
skel.PluginMainFuncs(skel.CNIFuncs{
|
||||||
|
Add: cmdAdd,
|
||||||
|
Check: cmdCheck,
|
||||||
|
Del: cmdDel,
|
||||||
|
Status: cmdStatus,
|
||||||
|
/* FIXME GC */
|
||||||
|
}, version.All, bv.BuildString("TODO"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdCheck(_ *skel.CmdArgs) error {
|
func cmdCheck(_ *skel.CmdArgs) error {
|
||||||
// TODO: implement
|
// TODO: implement
|
||||||
return fmt.Errorf("not implemented")
|
return fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cmdStatus implements the STATUS command, which indicates whether or not
|
||||||
|
// this plugin is able to accept ADD requests.
|
||||||
|
//
|
||||||
|
// If the plugin has external dependencies, such as a daemon
|
||||||
|
// or chained ipam plugin, it should determine their status. If all is well,
|
||||||
|
// and an ADD can be successfully processed, return nil
|
||||||
|
func cmdStatus(args *skel.CmdArgs) error {
|
||||||
|
conf, err := parseConfig(args.StdinData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_ = conf
|
||||||
|
|
||||||
|
// If this plugins delegates IPAM, ensure that IPAM is also running
|
||||||
|
if err := ipam.ExecStatus(conf.IPAM.Type, args.StdinData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: implement STATUS here
|
||||||
|
// e.g. querying an external deamon, or delegating STATUS to an IPAM plugin
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
set -xe
|
|
||||||
|
|
||||||
SRC_DIR="${SRC_DIR:-$PWD}"
|
|
||||||
DOCKER="${DOCKER:-docker}"
|
|
||||||
GOLANG="${GOLANG:-golang:1.21-alpine}"
|
|
||||||
|
|
||||||
TAG=$(git describe --tags --dirty)
|
|
||||||
RELEASE_DIR=release-${TAG}
|
|
||||||
|
|
||||||
BUILDFLAGS="-ldflags '-extldflags -static -X github.com/containernetworking/plugins/pkg/utils/buildversion.BuildVersion=${TAG}'"
|
|
||||||
|
|
||||||
OUTPUT_DIR=bin
|
|
||||||
|
|
||||||
# Always clean first
|
|
||||||
rm -Rf ${SRC_DIR}/${RELEASE_DIR}
|
|
||||||
mkdir -p ${SRC_DIR}/${RELEASE_DIR}
|
|
||||||
mkdir -p ${OUTPUT_DIR}
|
|
||||||
|
|
||||||
$DOCKER run -ti -v ${SRC_DIR}:/go/src/github.com/containernetworking/plugins:z --rm "${GOLANG}" \
|
|
||||||
/bin/sh -xe -c "\
|
|
||||||
apk --no-cache add bash tar;
|
|
||||||
cd /go/src/github.com/containernetworking/plugins; umask 0022;
|
|
||||||
|
|
||||||
for arch in amd64 arm arm64 ppc64le s390x mips64le riscv64; do \
|
|
||||||
rm -f ${OUTPUT_DIR}/*; \
|
|
||||||
CGO_ENABLED=0 GOARCH=\$arch ./build_linux.sh ${BUILDFLAGS}; \
|
|
||||||
for format in tgz; do \
|
|
||||||
FILENAME=cni-plugins-linux-\$arch-${TAG}.\$format; \
|
|
||||||
FILEPATH=${RELEASE_DIR}/\$FILENAME; \
|
|
||||||
tar -C ${OUTPUT_DIR} --owner=0 --group=0 -caf \$FILEPATH .; \
|
|
||||||
done; \
|
|
||||||
done;
|
|
||||||
|
|
||||||
rm -rf ${OUTPUT_DIR}/*; \
|
|
||||||
CGO_ENABLED=0 GOARCH=amd64 ./build_windows.sh ${BUILDFLAGS}; \
|
|
||||||
for format in tgz; do \
|
|
||||||
FILENAME=cni-plugins-windows-amd64-${TAG}.\$format; \
|
|
||||||
FILEPATH=${RELEASE_DIR}/\$FILENAME; \
|
|
||||||
tar -C ${OUTPUT_DIR} --owner=0 --group=0 -caf \$FILEPATH .; \
|
|
||||||
done;
|
|
||||||
|
|
||||||
|
|
||||||
cd ${RELEASE_DIR};
|
|
||||||
for f in *.tgz; do sha1sum \$f > \$f.sha1; done;
|
|
||||||
for f in *.tgz; do sha256sum \$f > \$f.sha256; done;
|
|
||||||
for f in *.tgz; do sha512sum \$f > \$f.sha512; done;
|
|
||||||
cd ..
|
|
||||||
chown -R ${UID} ${OUTPUT_DIR} ${RELEASE_DIR}"
|
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env bash
|
||||||
#
|
#
|
||||||
# Run CNI plugin tests.
|
# Run CNI plugin tests.
|
||||||
#
|
#
|
||||||
@ -18,6 +18,23 @@ testrun() {
|
|||||||
sudo -E sh -c "umask 0; PATH=${GOPATH}/bin:$(pwd)/bin:${PATH} go test -race $*"
|
sudo -E sh -c "umask 0; PATH=${GOPATH}/bin:$(pwd)/bin:${PATH} go test -race $*"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ensure_sysctl() {
|
||||||
|
local key
|
||||||
|
local val
|
||||||
|
local existing
|
||||||
|
|
||||||
|
key="$1"
|
||||||
|
val="$2"
|
||||||
|
existing="$(sysctl -ben "$key")"
|
||||||
|
|
||||||
|
sysctl -r
|
||||||
|
|
||||||
|
if [ "$val" -ne "$existing" ]; then
|
||||||
|
echo "sudo sysctl -we '$key'='$val'"
|
||||||
|
sudo sysctl -we "$key"="$val"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
COVERALLS=${COVERALLS:-""}
|
COVERALLS=${COVERALLS:-""}
|
||||||
|
|
||||||
if [ -n "${COVERALLS}" ]; then
|
if [ -n "${COVERALLS}" ]; then
|
||||||
@ -40,4 +57,7 @@ done
|
|||||||
|
|
||||||
# Run the pkg/ns tests as non root user
|
# Run the pkg/ns tests as non root user
|
||||||
mkdir -p /tmp/cni-rootless
|
mkdir -p /tmp/cni-rootless
|
||||||
|
ensure_sysctl kernel.unprivileged_userns_clone 1
|
||||||
|
ensure_sysctl kernel.apparmor_restrict_unprivileged_userns 0
|
||||||
|
|
||||||
(export XDG_RUNTIME_DIR=/tmp/cni-rootless; cd pkg/ns/; unshare -rmn go test)
|
(export XDG_RUNTIME_DIR=/tmp/cni-rootless; cd pkg/ns/; unshare -rmn go test)
|
||||||
|
10
vendor/github.com/Microsoft/go-winio/.golangci.yml
generated
vendored
10
vendor/github.com/Microsoft/go-winio/.golangci.yml
generated
vendored
@ -1,7 +1,3 @@
|
|||||||
run:
|
|
||||||
skip-dirs:
|
|
||||||
- pkg/etw/sample
|
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
enable:
|
enable:
|
||||||
# style
|
# style
|
||||||
@ -20,9 +16,13 @@ linters:
|
|||||||
- gofmt # files are gofmt'ed
|
- gofmt # files are gofmt'ed
|
||||||
- gosec # security
|
- gosec # security
|
||||||
- nilerr # returns nil even with non-nil error
|
- nilerr # returns nil even with non-nil error
|
||||||
|
- thelper # test helpers without t.Helper()
|
||||||
- unparam # unused function params
|
- unparam # unused function params
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
|
exclude-dirs:
|
||||||
|
- pkg/etw/sample
|
||||||
|
|
||||||
exclude-rules:
|
exclude-rules:
|
||||||
# err is very often shadowed in nested scopes
|
# err is very often shadowed in nested scopes
|
||||||
- linters:
|
- linters:
|
||||||
@ -69,9 +69,7 @@ linters-settings:
|
|||||||
# struct order is often for Win32 compat
|
# struct order is often for Win32 compat
|
||||||
# also, ignore pointer bytes/GC issues for now until performance becomes an issue
|
# also, ignore pointer bytes/GC issues for now until performance becomes an issue
|
||||||
- fieldalignment
|
- fieldalignment
|
||||||
check-shadowing: true
|
|
||||||
nolintlint:
|
nolintlint:
|
||||||
allow-leading-space: false
|
|
||||||
require-explanation: true
|
require-explanation: true
|
||||||
require-specific: true
|
require-specific: true
|
||||||
revive:
|
revive:
|
||||||
|
33
vendor/github.com/Microsoft/go-winio/backup.go
generated
vendored
33
vendor/github.com/Microsoft/go-winio/backup.go
generated
vendored
@ -10,14 +10,14 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
|
||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio/internal/fs"
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
//sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
|
//sys backupRead(h windows.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
|
||||||
//sys backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
|
//sys backupWrite(h windows.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
|
||||||
|
|
||||||
const (
|
const (
|
||||||
BackupData = uint32(iota + 1)
|
BackupData = uint32(iota + 1)
|
||||||
@ -104,7 +104,7 @@ func (r *BackupStreamReader) Next() (*BackupHeader, error) {
|
|||||||
if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
|
if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
hdr.Name = syscall.UTF16ToString(name)
|
hdr.Name = windows.UTF16ToString(name)
|
||||||
}
|
}
|
||||||
if wsi.StreamID == BackupSparseBlock {
|
if wsi.StreamID == BackupSparseBlock {
|
||||||
if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
|
if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
|
||||||
@ -205,7 +205,7 @@ func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader {
|
|||||||
// Read reads a backup stream from the file by calling the Win32 API BackupRead().
|
// Read reads a backup stream from the file by calling the Win32 API BackupRead().
|
||||||
func (r *BackupFileReader) Read(b []byte) (int, error) {
|
func (r *BackupFileReader) Read(b []byte) (int, error) {
|
||||||
var bytesRead uint32
|
var bytesRead uint32
|
||||||
err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
|
err := backupRead(windows.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, &os.PathError{Op: "BackupRead", Path: r.f.Name(), Err: err}
|
return 0, &os.PathError{Op: "BackupRead", Path: r.f.Name(), Err: err}
|
||||||
}
|
}
|
||||||
@ -220,7 +220,7 @@ func (r *BackupFileReader) Read(b []byte) (int, error) {
|
|||||||
// the underlying file.
|
// the underlying file.
|
||||||
func (r *BackupFileReader) Close() error {
|
func (r *BackupFileReader) Close() error {
|
||||||
if r.ctx != 0 {
|
if r.ctx != 0 {
|
||||||
_ = backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
|
_ = backupRead(windows.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
|
||||||
runtime.KeepAlive(r.f)
|
runtime.KeepAlive(r.f)
|
||||||
r.ctx = 0
|
r.ctx = 0
|
||||||
}
|
}
|
||||||
@ -244,7 +244,7 @@ func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter {
|
|||||||
// Write restores a portion of the file using the provided backup stream.
|
// Write restores a portion of the file using the provided backup stream.
|
||||||
func (w *BackupFileWriter) Write(b []byte) (int, error) {
|
func (w *BackupFileWriter) Write(b []byte) (int, error) {
|
||||||
var bytesWritten uint32
|
var bytesWritten uint32
|
||||||
err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
|
err := backupWrite(windows.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, &os.PathError{Op: "BackupWrite", Path: w.f.Name(), Err: err}
|
return 0, &os.PathError{Op: "BackupWrite", Path: w.f.Name(), Err: err}
|
||||||
}
|
}
|
||||||
@ -259,7 +259,7 @@ func (w *BackupFileWriter) Write(b []byte) (int, error) {
|
|||||||
// close the underlying file.
|
// close the underlying file.
|
||||||
func (w *BackupFileWriter) Close() error {
|
func (w *BackupFileWriter) Close() error {
|
||||||
if w.ctx != 0 {
|
if w.ctx != 0 {
|
||||||
_ = backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
|
_ = backupWrite(windows.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
|
||||||
runtime.KeepAlive(w.f)
|
runtime.KeepAlive(w.f)
|
||||||
w.ctx = 0
|
w.ctx = 0
|
||||||
}
|
}
|
||||||
@ -271,17 +271,14 @@ func (w *BackupFileWriter) Close() error {
|
|||||||
//
|
//
|
||||||
// If the file opened was a directory, it cannot be used with Readdir().
|
// If the file opened was a directory, it cannot be used with Readdir().
|
||||||
func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) {
|
func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) {
|
||||||
winPath, err := syscall.UTF16FromString(path)
|
h, err := fs.CreateFile(path,
|
||||||
if err != nil {
|
fs.AccessMask(access),
|
||||||
return nil, err
|
fs.FileShareMode(share),
|
||||||
}
|
|
||||||
h, err := syscall.CreateFile(&winPath[0],
|
|
||||||
access,
|
|
||||||
share,
|
|
||||||
nil,
|
nil,
|
||||||
createmode,
|
fs.FileCreationDisposition(createmode),
|
||||||
syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT,
|
fs.FILE_FLAG_BACKUP_SEMANTICS|fs.FILE_FLAG_OPEN_REPARSE_POINT,
|
||||||
0)
|
0,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = &os.PathError{Op: "open", Path: path, Err: err}
|
err = &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
return nil, err
|
return nil, err
|
||||||
|
85
vendor/github.com/Microsoft/go-winio/file.go
generated
vendored
85
vendor/github.com/Microsoft/go-winio/file.go
generated
vendored
@ -15,26 +15,11 @@ import (
|
|||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
//sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx
|
//sys cancelIoEx(file windows.Handle, o *windows.Overlapped) (err error) = CancelIoEx
|
||||||
//sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort
|
//sys createIoCompletionPort(file windows.Handle, port windows.Handle, key uintptr, threadCount uint32) (newport windows.Handle, err error) = CreateIoCompletionPort
|
||||||
//sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
|
//sys getQueuedCompletionStatus(port windows.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
|
||||||
//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
|
//sys setFileCompletionNotificationModes(h windows.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
|
||||||
//sys wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
|
//sys wsaGetOverlappedResult(h windows.Handle, o *windows.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
|
||||||
|
|
||||||
type atomicBool int32
|
|
||||||
|
|
||||||
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
|
|
||||||
func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) }
|
|
||||||
func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) }
|
|
||||||
|
|
||||||
//revive:disable-next-line:predeclared Keep "new" to maintain consistency with "atomic" pkg
|
|
||||||
func (b *atomicBool) swap(new bool) bool {
|
|
||||||
var newInt int32
|
|
||||||
if new {
|
|
||||||
newInt = 1
|
|
||||||
}
|
|
||||||
return atomic.SwapInt32((*int32)(b), newInt) == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrFileClosed = errors.New("file has already been closed")
|
ErrFileClosed = errors.New("file has already been closed")
|
||||||
@ -50,7 +35,7 @@ func (*timeoutError) Temporary() bool { return true }
|
|||||||
type timeoutChan chan struct{}
|
type timeoutChan chan struct{}
|
||||||
|
|
||||||
var ioInitOnce sync.Once
|
var ioInitOnce sync.Once
|
||||||
var ioCompletionPort syscall.Handle
|
var ioCompletionPort windows.Handle
|
||||||
|
|
||||||
// ioResult contains the result of an asynchronous IO operation.
|
// ioResult contains the result of an asynchronous IO operation.
|
||||||
type ioResult struct {
|
type ioResult struct {
|
||||||
@ -60,12 +45,12 @@ type ioResult struct {
|
|||||||
|
|
||||||
// ioOperation represents an outstanding asynchronous Win32 IO.
|
// ioOperation represents an outstanding asynchronous Win32 IO.
|
||||||
type ioOperation struct {
|
type ioOperation struct {
|
||||||
o syscall.Overlapped
|
o windows.Overlapped
|
||||||
ch chan ioResult
|
ch chan ioResult
|
||||||
}
|
}
|
||||||
|
|
||||||
func initIO() {
|
func initIO() {
|
||||||
h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff)
|
h, err := createIoCompletionPort(windows.InvalidHandle, 0, 0, 0xffffffff)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -76,10 +61,10 @@ func initIO() {
|
|||||||
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
|
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
|
||||||
// It takes ownership of this handle and will close it if it is garbage collected.
|
// It takes ownership of this handle and will close it if it is garbage collected.
|
||||||
type win32File struct {
|
type win32File struct {
|
||||||
handle syscall.Handle
|
handle windows.Handle
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
wgLock sync.RWMutex
|
wgLock sync.RWMutex
|
||||||
closing atomicBool
|
closing atomic.Bool
|
||||||
socket bool
|
socket bool
|
||||||
readDeadline deadlineHandler
|
readDeadline deadlineHandler
|
||||||
writeDeadline deadlineHandler
|
writeDeadline deadlineHandler
|
||||||
@ -90,11 +75,11 @@ type deadlineHandler struct {
|
|||||||
channel timeoutChan
|
channel timeoutChan
|
||||||
channelLock sync.RWMutex
|
channelLock sync.RWMutex
|
||||||
timer *time.Timer
|
timer *time.Timer
|
||||||
timedout atomicBool
|
timedout atomic.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeWin32File makes a new win32File from an existing file handle.
|
// makeWin32File makes a new win32File from an existing file handle.
|
||||||
func makeWin32File(h syscall.Handle) (*win32File, error) {
|
func makeWin32File(h windows.Handle) (*win32File, error) {
|
||||||
f := &win32File{handle: h}
|
f := &win32File{handle: h}
|
||||||
ioInitOnce.Do(initIO)
|
ioInitOnce.Do(initIO)
|
||||||
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
|
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
|
||||||
@ -110,7 +95,12 @@ func makeWin32File(h syscall.Handle) (*win32File, error) {
|
|||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: use NewOpenFile instead.
|
||||||
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
||||||
|
return NewOpenFile(windows.Handle(h))
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOpenFile(h windows.Handle) (io.ReadWriteCloser, error) {
|
||||||
// If we return the result of makeWin32File directly, it can result in an
|
// If we return the result of makeWin32File directly, it can result in an
|
||||||
// interface-wrapped nil, rather than a nil interface value.
|
// interface-wrapped nil, rather than a nil interface value.
|
||||||
f, err := makeWin32File(h)
|
f, err := makeWin32File(h)
|
||||||
@ -124,13 +114,13 @@ func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
|||||||
func (f *win32File) closeHandle() {
|
func (f *win32File) closeHandle() {
|
||||||
f.wgLock.Lock()
|
f.wgLock.Lock()
|
||||||
// Atomically set that we are closing, releasing the resources only once.
|
// Atomically set that we are closing, releasing the resources only once.
|
||||||
if !f.closing.swap(true) {
|
if !f.closing.Swap(true) {
|
||||||
f.wgLock.Unlock()
|
f.wgLock.Unlock()
|
||||||
// cancel all IO and wait for it to complete
|
// cancel all IO and wait for it to complete
|
||||||
_ = cancelIoEx(f.handle, nil)
|
_ = cancelIoEx(f.handle, nil)
|
||||||
f.wg.Wait()
|
f.wg.Wait()
|
||||||
// at this point, no new IO can start
|
// at this point, no new IO can start
|
||||||
syscall.Close(f.handle)
|
windows.Close(f.handle)
|
||||||
f.handle = 0
|
f.handle = 0
|
||||||
} else {
|
} else {
|
||||||
f.wgLock.Unlock()
|
f.wgLock.Unlock()
|
||||||
@ -145,14 +135,14 @@ func (f *win32File) Close() error {
|
|||||||
|
|
||||||
// IsClosed checks if the file has been closed.
|
// IsClosed checks if the file has been closed.
|
||||||
func (f *win32File) IsClosed() bool {
|
func (f *win32File) IsClosed() bool {
|
||||||
return f.closing.isSet()
|
return f.closing.Load()
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepareIO prepares for a new IO operation.
|
// prepareIO prepares for a new IO operation.
|
||||||
// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
|
// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
|
||||||
func (f *win32File) prepareIO() (*ioOperation, error) {
|
func (f *win32File) prepareIO() (*ioOperation, error) {
|
||||||
f.wgLock.RLock()
|
f.wgLock.RLock()
|
||||||
if f.closing.isSet() {
|
if f.closing.Load() {
|
||||||
f.wgLock.RUnlock()
|
f.wgLock.RUnlock()
|
||||||
return nil, ErrFileClosed
|
return nil, ErrFileClosed
|
||||||
}
|
}
|
||||||
@ -164,12 +154,12 @@ func (f *win32File) prepareIO() (*ioOperation, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ioCompletionProcessor processes completed async IOs forever.
|
// ioCompletionProcessor processes completed async IOs forever.
|
||||||
func ioCompletionProcessor(h syscall.Handle) {
|
func ioCompletionProcessor(h windows.Handle) {
|
||||||
for {
|
for {
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
var key uintptr
|
var key uintptr
|
||||||
var op *ioOperation
|
var op *ioOperation
|
||||||
err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE)
|
err := getQueuedCompletionStatus(h, &bytes, &key, &op, windows.INFINITE)
|
||||||
if op == nil {
|
if op == nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -182,11 +172,11 @@ func ioCompletionProcessor(h syscall.Handle) {
|
|||||||
// asyncIO processes the return value from ReadFile or WriteFile, blocking until
|
// asyncIO processes the return value from ReadFile or WriteFile, blocking until
|
||||||
// the operation has actually completed.
|
// the operation has actually completed.
|
||||||
func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) {
|
func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) {
|
||||||
if err != syscall.ERROR_IO_PENDING { //nolint:errorlint // err is Errno
|
if err != windows.ERROR_IO_PENDING { //nolint:errorlint // err is Errno
|
||||||
return int(bytes), err
|
return int(bytes), err
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.closing.isSet() {
|
if f.closing.Load() {
|
||||||
_ = cancelIoEx(f.handle, &c.o)
|
_ = cancelIoEx(f.handle, &c.o)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,8 +191,8 @@ func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, er
|
|||||||
select {
|
select {
|
||||||
case r = <-c.ch:
|
case r = <-c.ch:
|
||||||
err = r.err
|
err = r.err
|
||||||
if err == syscall.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
|
if err == windows.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
|
||||||
if f.closing.isSet() {
|
if f.closing.Load() {
|
||||||
err = ErrFileClosed
|
err = ErrFileClosed
|
||||||
}
|
}
|
||||||
} else if err != nil && f.socket {
|
} else if err != nil && f.socket {
|
||||||
@ -214,7 +204,7 @@ func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, er
|
|||||||
_ = cancelIoEx(f.handle, &c.o)
|
_ = cancelIoEx(f.handle, &c.o)
|
||||||
r = <-c.ch
|
r = <-c.ch
|
||||||
err = r.err
|
err = r.err
|
||||||
if err == syscall.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
|
if err == windows.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
|
||||||
err = ErrTimeout
|
err = ErrTimeout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -235,23 +225,22 @@ func (f *win32File) Read(b []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
defer f.wg.Done()
|
defer f.wg.Done()
|
||||||
|
|
||||||
if f.readDeadline.timedout.isSet() {
|
if f.readDeadline.timedout.Load() {
|
||||||
return 0, ErrTimeout
|
return 0, ErrTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
err = syscall.ReadFile(f.handle, b, &bytes, &c.o)
|
err = windows.ReadFile(f.handle, b, &bytes, &c.o)
|
||||||
n, err := f.asyncIO(c, &f.readDeadline, bytes, err)
|
n, err := f.asyncIO(c, &f.readDeadline, bytes, err)
|
||||||
runtime.KeepAlive(b)
|
runtime.KeepAlive(b)
|
||||||
|
|
||||||
// Handle EOF conditions.
|
// Handle EOF conditions.
|
||||||
if err == nil && n == 0 && len(b) != 0 {
|
if err == nil && n == 0 && len(b) != 0 {
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
} else if err == syscall.ERROR_BROKEN_PIPE { //nolint:errorlint // err is Errno
|
} else if err == windows.ERROR_BROKEN_PIPE { //nolint:errorlint // err is Errno
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
} else {
|
|
||||||
return n, err
|
|
||||||
}
|
}
|
||||||
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write writes to a file handle.
|
// Write writes to a file handle.
|
||||||
@ -262,12 +251,12 @@ func (f *win32File) Write(b []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
defer f.wg.Done()
|
defer f.wg.Done()
|
||||||
|
|
||||||
if f.writeDeadline.timedout.isSet() {
|
if f.writeDeadline.timedout.Load() {
|
||||||
return 0, ErrTimeout
|
return 0, ErrTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
err = syscall.WriteFile(f.handle, b, &bytes, &c.o)
|
err = windows.WriteFile(f.handle, b, &bytes, &c.o)
|
||||||
n, err := f.asyncIO(c, &f.writeDeadline, bytes, err)
|
n, err := f.asyncIO(c, &f.writeDeadline, bytes, err)
|
||||||
runtime.KeepAlive(b)
|
runtime.KeepAlive(b)
|
||||||
return n, err
|
return n, err
|
||||||
@ -282,7 +271,7 @@ func (f *win32File) SetWriteDeadline(deadline time.Time) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *win32File) Flush() error {
|
func (f *win32File) Flush() error {
|
||||||
return syscall.FlushFileBuffers(f.handle)
|
return windows.FlushFileBuffers(f.handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *win32File) Fd() uintptr {
|
func (f *win32File) Fd() uintptr {
|
||||||
@ -299,7 +288,7 @@ func (d *deadlineHandler) set(deadline time.Time) error {
|
|||||||
}
|
}
|
||||||
d.timer = nil
|
d.timer = nil
|
||||||
}
|
}
|
||||||
d.timedout.setFalse()
|
d.timedout.Store(false)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-d.channel:
|
case <-d.channel:
|
||||||
@ -314,7 +303,7 @@ func (d *deadlineHandler) set(deadline time.Time) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
timeoutIO := func() {
|
timeoutIO := func() {
|
||||||
d.timedout.setTrue()
|
d.timedout.Store(true)
|
||||||
close(d.channel)
|
close(d.channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
22
vendor/github.com/Microsoft/go-winio/fileinfo.go
generated
vendored
22
vendor/github.com/Microsoft/go-winio/fileinfo.go
generated
vendored
@ -18,9 +18,18 @@ type FileBasicInfo struct {
|
|||||||
_ uint32 // padding
|
_ uint32 // padding
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// alignedFileBasicInfo is a FileBasicInfo, but aligned to uint64 by containing
|
||||||
|
// uint64 rather than windows.Filetime. Filetime contains two uint32s. uint64
|
||||||
|
// alignment is necessary to pass this as FILE_BASIC_INFO.
|
||||||
|
type alignedFileBasicInfo struct {
|
||||||
|
CreationTime, LastAccessTime, LastWriteTime, ChangeTime uint64
|
||||||
|
FileAttributes uint32
|
||||||
|
_ uint32 // padding
|
||||||
|
}
|
||||||
|
|
||||||
// GetFileBasicInfo retrieves times and attributes for a file.
|
// GetFileBasicInfo retrieves times and attributes for a file.
|
||||||
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
|
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
|
||||||
bi := &FileBasicInfo{}
|
bi := &alignedFileBasicInfo{}
|
||||||
if err := windows.GetFileInformationByHandleEx(
|
if err := windows.GetFileInformationByHandleEx(
|
||||||
windows.Handle(f.Fd()),
|
windows.Handle(f.Fd()),
|
||||||
windows.FileBasicInfo,
|
windows.FileBasicInfo,
|
||||||
@ -30,16 +39,21 @@ func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
|
|||||||
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||||
}
|
}
|
||||||
runtime.KeepAlive(f)
|
runtime.KeepAlive(f)
|
||||||
return bi, nil
|
// Reinterpret the alignedFileBasicInfo as a FileBasicInfo so it matches the
|
||||||
|
// public API of this module. The data may be unnecessarily aligned.
|
||||||
|
return (*FileBasicInfo)(unsafe.Pointer(bi)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetFileBasicInfo sets times and attributes for a file.
|
// SetFileBasicInfo sets times and attributes for a file.
|
||||||
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
|
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
|
||||||
|
// Create an alignedFileBasicInfo based on a FileBasicInfo. The copy is
|
||||||
|
// suitable to pass to GetFileInformationByHandleEx.
|
||||||
|
biAligned := *(*alignedFileBasicInfo)(unsafe.Pointer(bi))
|
||||||
if err := windows.SetFileInformationByHandle(
|
if err := windows.SetFileInformationByHandle(
|
||||||
windows.Handle(f.Fd()),
|
windows.Handle(f.Fd()),
|
||||||
windows.FileBasicInfo,
|
windows.FileBasicInfo,
|
||||||
(*byte)(unsafe.Pointer(bi)),
|
(*byte)(unsafe.Pointer(&biAligned)),
|
||||||
uint32(unsafe.Sizeof(*bi)),
|
uint32(unsafe.Sizeof(biAligned)),
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
|
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
|
||||||
}
|
}
|
||||||
|
47
vendor/github.com/Microsoft/go-winio/hvsock.go
generated
vendored
47
vendor/github.com/Microsoft/go-winio/hvsock.go
generated
vendored
@ -10,7 +10,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
@ -181,13 +180,13 @@ type HvsockConn struct {
|
|||||||
var _ net.Conn = &HvsockConn{}
|
var _ net.Conn = &HvsockConn{}
|
||||||
|
|
||||||
func newHVSocket() (*win32File, error) {
|
func newHVSocket() (*win32File, error) {
|
||||||
fd, err := syscall.Socket(afHVSock, syscall.SOCK_STREAM, 1)
|
fd, err := windows.Socket(afHVSock, windows.SOCK_STREAM, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, os.NewSyscallError("socket", err)
|
return nil, os.NewSyscallError("socket", err)
|
||||||
}
|
}
|
||||||
f, err := makeWin32File(fd)
|
f, err := makeWin32File(fd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
syscall.Close(fd)
|
windows.Close(fd)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
f.socket = true
|
f.socket = true
|
||||||
@ -197,16 +196,24 @@ func newHVSocket() (*win32File, error) {
|
|||||||
// ListenHvsock listens for connections on the specified hvsock address.
|
// ListenHvsock listens for connections on the specified hvsock address.
|
||||||
func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) {
|
func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) {
|
||||||
l := &HvsockListener{addr: *addr}
|
l := &HvsockListener{addr: *addr}
|
||||||
sock, err := newHVSocket()
|
|
||||||
|
var sock *win32File
|
||||||
|
sock, err = newHVSocket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, l.opErr("listen", err)
|
return nil, l.opErr("listen", err)
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = sock.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
sa := addr.raw()
|
sa := addr.raw()
|
||||||
err = socket.Bind(windows.Handle(sock.handle), &sa)
|
err = socket.Bind(sock.handle, &sa)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, l.opErr("listen", os.NewSyscallError("socket", err))
|
return nil, l.opErr("listen", os.NewSyscallError("socket", err))
|
||||||
}
|
}
|
||||||
err = syscall.Listen(sock.handle, 16)
|
err = windows.Listen(sock.handle, 16)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, l.opErr("listen", os.NewSyscallError("listen", err))
|
return nil, l.opErr("listen", os.NewSyscallError("listen", err))
|
||||||
}
|
}
|
||||||
@ -246,7 +253,7 @@ func (l *HvsockListener) Accept() (_ net.Conn, err error) {
|
|||||||
var addrbuf [addrlen * 2]byte
|
var addrbuf [addrlen * 2]byte
|
||||||
|
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0 /* rxdatalen */, addrlen, addrlen, &bytes, &c.o)
|
err = windows.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0 /* rxdatalen */, addrlen, addrlen, &bytes, &c.o)
|
||||||
if _, err = l.sock.asyncIO(c, nil, bytes, err); err != nil {
|
if _, err = l.sock.asyncIO(c, nil, bytes, err); err != nil {
|
||||||
return nil, l.opErr("accept", os.NewSyscallError("acceptex", err))
|
return nil, l.opErr("accept", os.NewSyscallError("acceptex", err))
|
||||||
}
|
}
|
||||||
@ -263,7 +270,7 @@ func (l *HvsockListener) Accept() (_ net.Conn, err error) {
|
|||||||
conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen])))
|
conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen])))
|
||||||
|
|
||||||
// initialize the accepted socket and update its properties with those of the listening socket
|
// initialize the accepted socket and update its properties with those of the listening socket
|
||||||
if err = windows.Setsockopt(windows.Handle(sock.handle),
|
if err = windows.Setsockopt(sock.handle,
|
||||||
windows.SOL_SOCKET, windows.SO_UPDATE_ACCEPT_CONTEXT,
|
windows.SOL_SOCKET, windows.SO_UPDATE_ACCEPT_CONTEXT,
|
||||||
(*byte)(unsafe.Pointer(&l.sock.handle)), int32(unsafe.Sizeof(l.sock.handle))); err != nil {
|
(*byte)(unsafe.Pointer(&l.sock.handle)), int32(unsafe.Sizeof(l.sock.handle))); err != nil {
|
||||||
return nil, conn.opErr("accept", os.NewSyscallError("setsockopt", err))
|
return nil, conn.opErr("accept", os.NewSyscallError("setsockopt", err))
|
||||||
@ -334,7 +341,7 @@ func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *Hvsock
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
sa := addr.raw()
|
sa := addr.raw()
|
||||||
err = socket.Bind(windows.Handle(sock.handle), &sa)
|
err = socket.Bind(sock.handle, &sa)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, conn.opErr(op, os.NewSyscallError("bind", err))
|
return nil, conn.opErr(op, os.NewSyscallError("bind", err))
|
||||||
}
|
}
|
||||||
@ -347,7 +354,7 @@ func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *Hvsock
|
|||||||
var bytes uint32
|
var bytes uint32
|
||||||
for i := uint(0); i <= d.Retries; i++ {
|
for i := uint(0); i <= d.Retries; i++ {
|
||||||
err = socket.ConnectEx(
|
err = socket.ConnectEx(
|
||||||
windows.Handle(sock.handle),
|
sock.handle,
|
||||||
&sa,
|
&sa,
|
||||||
nil, // sendBuf
|
nil, // sendBuf
|
||||||
0, // sendDataLen
|
0, // sendDataLen
|
||||||
@ -367,7 +374,7 @@ func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *Hvsock
|
|||||||
|
|
||||||
// update the connection properties, so shutdown can be used
|
// update the connection properties, so shutdown can be used
|
||||||
if err = windows.Setsockopt(
|
if err = windows.Setsockopt(
|
||||||
windows.Handle(sock.handle),
|
sock.handle,
|
||||||
windows.SOL_SOCKET,
|
windows.SOL_SOCKET,
|
||||||
windows.SO_UPDATE_CONNECT_CONTEXT,
|
windows.SO_UPDATE_CONNECT_CONTEXT,
|
||||||
nil, // optvalue
|
nil, // optvalue
|
||||||
@ -378,7 +385,7 @@ func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *Hvsock
|
|||||||
|
|
||||||
// get the local name
|
// get the local name
|
||||||
var sal rawHvsockAddr
|
var sal rawHvsockAddr
|
||||||
err = socket.GetSockName(windows.Handle(sock.handle), &sal)
|
err = socket.GetSockName(sock.handle, &sal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, conn.opErr(op, os.NewSyscallError("getsockname", err))
|
return nil, conn.opErr(op, os.NewSyscallError("getsockname", err))
|
||||||
}
|
}
|
||||||
@ -421,7 +428,7 @@ func (d *HvsockDialer) redialWait(ctx context.Context) (err error) {
|
|||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// assumes error is a plain, unwrapped syscall.Errno provided by direct syscall.
|
// assumes error is a plain, unwrapped windows.Errno provided by direct syscall.
|
||||||
func canRedial(err error) bool {
|
func canRedial(err error) bool {
|
||||||
//nolint:errorlint // guaranteed to be an Errno
|
//nolint:errorlint // guaranteed to be an Errno
|
||||||
switch err {
|
switch err {
|
||||||
@ -447,9 +454,9 @@ func (conn *HvsockConn) Read(b []byte) (int, error) {
|
|||||||
return 0, conn.opErr("read", err)
|
return 0, conn.opErr("read", err)
|
||||||
}
|
}
|
||||||
defer conn.sock.wg.Done()
|
defer conn.sock.wg.Done()
|
||||||
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
buf := windows.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
||||||
var flags, bytes uint32
|
var flags, bytes uint32
|
||||||
err = syscall.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil)
|
err = windows.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil)
|
||||||
n, err := conn.sock.asyncIO(c, &conn.sock.readDeadline, bytes, err)
|
n, err := conn.sock.asyncIO(c, &conn.sock.readDeadline, bytes, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var eno windows.Errno
|
var eno windows.Errno
|
||||||
@ -482,9 +489,9 @@ func (conn *HvsockConn) write(b []byte) (int, error) {
|
|||||||
return 0, conn.opErr("write", err)
|
return 0, conn.opErr("write", err)
|
||||||
}
|
}
|
||||||
defer conn.sock.wg.Done()
|
defer conn.sock.wg.Done()
|
||||||
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
buf := windows.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
||||||
var bytes uint32
|
var bytes uint32
|
||||||
err = syscall.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil)
|
err = windows.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil)
|
||||||
n, err := conn.sock.asyncIO(c, &conn.sock.writeDeadline, bytes, err)
|
n, err := conn.sock.asyncIO(c, &conn.sock.writeDeadline, bytes, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var eno windows.Errno
|
var eno windows.Errno
|
||||||
@ -511,7 +518,7 @@ func (conn *HvsockConn) shutdown(how int) error {
|
|||||||
return socket.ErrSocketClosed
|
return socket.ErrSocketClosed
|
||||||
}
|
}
|
||||||
|
|
||||||
err := syscall.Shutdown(conn.sock.handle, how)
|
err := windows.Shutdown(conn.sock.handle, how)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If the connection was closed, shutdowns fail with "not connected"
|
// If the connection was closed, shutdowns fail with "not connected"
|
||||||
if errors.Is(err, windows.WSAENOTCONN) ||
|
if errors.Is(err, windows.WSAENOTCONN) ||
|
||||||
@ -525,7 +532,7 @@ func (conn *HvsockConn) shutdown(how int) error {
|
|||||||
|
|
||||||
// CloseRead shuts down the read end of the socket, preventing future read operations.
|
// CloseRead shuts down the read end of the socket, preventing future read operations.
|
||||||
func (conn *HvsockConn) CloseRead() error {
|
func (conn *HvsockConn) CloseRead() error {
|
||||||
err := conn.shutdown(syscall.SHUT_RD)
|
err := conn.shutdown(windows.SHUT_RD)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return conn.opErr("closeread", err)
|
return conn.opErr("closeread", err)
|
||||||
}
|
}
|
||||||
@ -535,7 +542,7 @@ func (conn *HvsockConn) CloseRead() error {
|
|||||||
// CloseWrite shuts down the write end of the socket, preventing future write operations and
|
// CloseWrite shuts down the write end of the socket, preventing future write operations and
|
||||||
// notifying the other endpoint that no more data will be written.
|
// notifying the other endpoint that no more data will be written.
|
||||||
func (conn *HvsockConn) CloseWrite() error {
|
func (conn *HvsockConn) CloseWrite() error {
|
||||||
err := conn.shutdown(syscall.SHUT_WR)
|
err := conn.shutdown(windows.SHUT_WR)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return conn.opErr("closewrite", err)
|
return conn.opErr("closewrite", err)
|
||||||
}
|
}
|
||||||
|
72
vendor/github.com/Microsoft/go-winio/internal/fs/fs.go
generated
vendored
72
vendor/github.com/Microsoft/go-winio/internal/fs/fs.go
generated
vendored
@ -11,12 +11,14 @@ import (
|
|||||||
//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go fs.go
|
//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go fs.go
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
|
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
|
||||||
//sys CreateFile(name string, access AccessMask, mode FileShareMode, sa *syscall.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateFileW
|
//sys CreateFile(name string, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateFileW
|
||||||
|
|
||||||
const NullHandle windows.Handle = 0
|
const NullHandle windows.Handle = 0
|
||||||
|
|
||||||
// AccessMask defines standard, specific, and generic rights.
|
// AccessMask defines standard, specific, and generic rights.
|
||||||
//
|
//
|
||||||
|
// Used with CreateFile and NtCreateFile (and co.).
|
||||||
|
//
|
||||||
// Bitmask:
|
// Bitmask:
|
||||||
// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
|
// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
|
||||||
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
||||||
@ -47,6 +49,12 @@ const (
|
|||||||
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew#parameters
|
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew#parameters
|
||||||
FILE_ANY_ACCESS AccessMask = 0
|
FILE_ANY_ACCESS AccessMask = 0
|
||||||
|
|
||||||
|
GENERIC_READ AccessMask = 0x8000_0000
|
||||||
|
GENERIC_WRITE AccessMask = 0x4000_0000
|
||||||
|
GENERIC_EXECUTE AccessMask = 0x2000_0000
|
||||||
|
GENERIC_ALL AccessMask = 0x1000_0000
|
||||||
|
ACCESS_SYSTEM_SECURITY AccessMask = 0x0100_0000
|
||||||
|
|
||||||
// Specific Object Access
|
// Specific Object Access
|
||||||
// from ntioapi.h
|
// from ntioapi.h
|
||||||
|
|
||||||
@ -124,14 +132,32 @@ const (
|
|||||||
TRUNCATE_EXISTING FileCreationDisposition = 0x05
|
TRUNCATE_EXISTING FileCreationDisposition = 0x05
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Create disposition values for NtCreate*
|
||||||
|
type NTFileCreationDisposition uint32
|
||||||
|
|
||||||
|
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||||
|
const (
|
||||||
|
// From ntioapi.h
|
||||||
|
|
||||||
|
FILE_SUPERSEDE NTFileCreationDisposition = 0x00
|
||||||
|
FILE_OPEN NTFileCreationDisposition = 0x01
|
||||||
|
FILE_CREATE NTFileCreationDisposition = 0x02
|
||||||
|
FILE_OPEN_IF NTFileCreationDisposition = 0x03
|
||||||
|
FILE_OVERWRITE NTFileCreationDisposition = 0x04
|
||||||
|
FILE_OVERWRITE_IF NTFileCreationDisposition = 0x05
|
||||||
|
FILE_MAXIMUM_DISPOSITION NTFileCreationDisposition = 0x05
|
||||||
|
)
|
||||||
|
|
||||||
// CreateFile and co. take flags or attributes together as one parameter.
|
// CreateFile and co. take flags or attributes together as one parameter.
|
||||||
// Define alias until we can use generics to allow both
|
// Define alias until we can use generics to allow both
|
||||||
|
//
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
|
// https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
|
||||||
type FileFlagOrAttribute uint32
|
type FileFlagOrAttribute uint32
|
||||||
|
|
||||||
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||||
const ( // from winnt.h
|
const (
|
||||||
|
// from winnt.h
|
||||||
|
|
||||||
FILE_FLAG_WRITE_THROUGH FileFlagOrAttribute = 0x8000_0000
|
FILE_FLAG_WRITE_THROUGH FileFlagOrAttribute = 0x8000_0000
|
||||||
FILE_FLAG_OVERLAPPED FileFlagOrAttribute = 0x4000_0000
|
FILE_FLAG_OVERLAPPED FileFlagOrAttribute = 0x4000_0000
|
||||||
FILE_FLAG_NO_BUFFERING FileFlagOrAttribute = 0x2000_0000
|
FILE_FLAG_NO_BUFFERING FileFlagOrAttribute = 0x2000_0000
|
||||||
@ -145,17 +171,51 @@ const ( // from winnt.h
|
|||||||
FILE_FLAG_FIRST_PIPE_INSTANCE FileFlagOrAttribute = 0x0008_0000
|
FILE_FLAG_FIRST_PIPE_INSTANCE FileFlagOrAttribute = 0x0008_0000
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NtCreate* functions take a dedicated CreateOptions parameter.
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/Winternl/nf-winternl-ntcreatefile
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/devnotes/nt-create-named-pipe-file
|
||||||
|
type NTCreateOptions uint32
|
||||||
|
|
||||||
|
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||||
|
const (
|
||||||
|
// From ntioapi.h
|
||||||
|
|
||||||
|
FILE_DIRECTORY_FILE NTCreateOptions = 0x0000_0001
|
||||||
|
FILE_WRITE_THROUGH NTCreateOptions = 0x0000_0002
|
||||||
|
FILE_SEQUENTIAL_ONLY NTCreateOptions = 0x0000_0004
|
||||||
|
FILE_NO_INTERMEDIATE_BUFFERING NTCreateOptions = 0x0000_0008
|
||||||
|
|
||||||
|
FILE_SYNCHRONOUS_IO_ALERT NTCreateOptions = 0x0000_0010
|
||||||
|
FILE_SYNCHRONOUS_IO_NONALERT NTCreateOptions = 0x0000_0020
|
||||||
|
FILE_NON_DIRECTORY_FILE NTCreateOptions = 0x0000_0040
|
||||||
|
FILE_CREATE_TREE_CONNECTION NTCreateOptions = 0x0000_0080
|
||||||
|
|
||||||
|
FILE_COMPLETE_IF_OPLOCKED NTCreateOptions = 0x0000_0100
|
||||||
|
FILE_NO_EA_KNOWLEDGE NTCreateOptions = 0x0000_0200
|
||||||
|
FILE_DISABLE_TUNNELING NTCreateOptions = 0x0000_0400
|
||||||
|
FILE_RANDOM_ACCESS NTCreateOptions = 0x0000_0800
|
||||||
|
|
||||||
|
FILE_DELETE_ON_CLOSE NTCreateOptions = 0x0000_1000
|
||||||
|
FILE_OPEN_BY_FILE_ID NTCreateOptions = 0x0000_2000
|
||||||
|
FILE_OPEN_FOR_BACKUP_INTENT NTCreateOptions = 0x0000_4000
|
||||||
|
FILE_NO_COMPRESSION NTCreateOptions = 0x0000_8000
|
||||||
|
)
|
||||||
|
|
||||||
type FileSQSFlag = FileFlagOrAttribute
|
type FileSQSFlag = FileFlagOrAttribute
|
||||||
|
|
||||||
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||||
const ( // from winbase.h
|
const (
|
||||||
|
// from winbase.h
|
||||||
|
|
||||||
SECURITY_ANONYMOUS FileSQSFlag = FileSQSFlag(SecurityAnonymous << 16)
|
SECURITY_ANONYMOUS FileSQSFlag = FileSQSFlag(SecurityAnonymous << 16)
|
||||||
SECURITY_IDENTIFICATION FileSQSFlag = FileSQSFlag(SecurityIdentification << 16)
|
SECURITY_IDENTIFICATION FileSQSFlag = FileSQSFlag(SecurityIdentification << 16)
|
||||||
SECURITY_IMPERSONATION FileSQSFlag = FileSQSFlag(SecurityImpersonation << 16)
|
SECURITY_IMPERSONATION FileSQSFlag = FileSQSFlag(SecurityImpersonation << 16)
|
||||||
SECURITY_DELEGATION FileSQSFlag = FileSQSFlag(SecurityDelegation << 16)
|
SECURITY_DELEGATION FileSQSFlag = FileSQSFlag(SecurityDelegation << 16)
|
||||||
|
|
||||||
SECURITY_SQOS_PRESENT FileSQSFlag = 0x00100000
|
SECURITY_SQOS_PRESENT FileSQSFlag = 0x0010_0000
|
||||||
SECURITY_VALID_SQOS_FLAGS FileSQSFlag = 0x001F0000
|
SECURITY_VALID_SQOS_FLAGS FileSQSFlag = 0x001F_0000
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetFinalPathNameByHandle flags
|
// GetFinalPathNameByHandle flags
|
||||||
|
9
vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go
generated
vendored
9
vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go
generated
vendored
@ -33,9 +33,6 @@ func errnoErr(e syscall.Errno) error {
|
|||||||
case errnoERROR_IO_PENDING:
|
case errnoERROR_IO_PENDING:
|
||||||
return errERROR_IO_PENDING
|
return errERROR_IO_PENDING
|
||||||
}
|
}
|
||||||
// TODO: add more here, after collecting data on the common
|
|
||||||
// error values see on Windows. (perhaps when running
|
|
||||||
// all.bat?)
|
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +42,7 @@ var (
|
|||||||
procCreateFileW = modkernel32.NewProc("CreateFileW")
|
procCreateFileW = modkernel32.NewProc("CreateFileW")
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateFile(name string, access AccessMask, mode FileShareMode, sa *syscall.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) {
|
func CreateFile(name string, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) {
|
||||||
var _p0 *uint16
|
var _p0 *uint16
|
||||||
_p0, err = syscall.UTF16PtrFromString(name)
|
_p0, err = syscall.UTF16PtrFromString(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -54,8 +51,8 @@ func CreateFile(name string, access AccessMask, mode FileShareMode, sa *syscall.
|
|||||||
return _CreateFile(_p0, access, mode, sa, createmode, attrs, templatefile)
|
return _CreateFile(_p0, access, mode, sa, createmode, attrs, templatefile)
|
||||||
}
|
}
|
||||||
|
|
||||||
func _CreateFile(name *uint16, access AccessMask, mode FileShareMode, sa *syscall.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) {
|
func _CreateFile(name *uint16, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) {
|
||||||
r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
|
r0, _, e1 := syscall.SyscallN(procCreateFileW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile))
|
||||||
handle = windows.Handle(r0)
|
handle = windows.Handle(r0)
|
||||||
if handle == windows.InvalidHandle {
|
if handle == windows.InvalidHandle {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
|
8
vendor/github.com/Microsoft/go-winio/internal/socket/socket.go
generated
vendored
8
vendor/github.com/Microsoft/go-winio/internal/socket/socket.go
generated
vendored
@ -156,9 +156,7 @@ func connectEx(
|
|||||||
bytesSent *uint32,
|
bytesSent *uint32,
|
||||||
overlapped *windows.Overlapped,
|
overlapped *windows.Overlapped,
|
||||||
) (err error) {
|
) (err error) {
|
||||||
// todo: after upgrading to 1.18, switch from syscall.Syscall9 to syscall.SyscallN
|
r1, _, e1 := syscall.SyscallN(connectExFunc.addr,
|
||||||
r1, _, e1 := syscall.Syscall9(connectExFunc.addr,
|
|
||||||
7,
|
|
||||||
uintptr(s),
|
uintptr(s),
|
||||||
uintptr(name),
|
uintptr(name),
|
||||||
uintptr(namelen),
|
uintptr(namelen),
|
||||||
@ -166,8 +164,8 @@ func connectEx(
|
|||||||
uintptr(sendDataLen),
|
uintptr(sendDataLen),
|
||||||
uintptr(unsafe.Pointer(bytesSent)),
|
uintptr(unsafe.Pointer(bytesSent)),
|
||||||
uintptr(unsafe.Pointer(overlapped)),
|
uintptr(unsafe.Pointer(overlapped)),
|
||||||
0,
|
)
|
||||||
0)
|
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
if e1 != 0 {
|
if e1 != 0 {
|
||||||
err = error(e1)
|
err = error(e1)
|
||||||
|
9
vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go
generated
vendored
9
vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go
generated
vendored
@ -33,9 +33,6 @@ func errnoErr(e syscall.Errno) error {
|
|||||||
case errnoERROR_IO_PENDING:
|
case errnoERROR_IO_PENDING:
|
||||||
return errERROR_IO_PENDING
|
return errERROR_IO_PENDING
|
||||||
}
|
}
|
||||||
// TODO: add more here, after collecting data on the common
|
|
||||||
// error values see on Windows. (perhaps when running
|
|
||||||
// all.bat?)
|
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +45,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) {
|
func bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
|
r1, _, e1 := syscall.SyscallN(procbind.Addr(), uintptr(s), uintptr(name), uintptr(namelen))
|
||||||
if r1 == socketError {
|
if r1 == socketError {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
@ -56,7 +53,7 @@ func bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
|
func getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procgetpeername.Addr(), 3, uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
|
r1, _, e1 := syscall.SyscallN(procgetpeername.Addr(), uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
|
||||||
if r1 == socketError {
|
if r1 == socketError {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
@ -64,7 +61,7 @@ func getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
|
func getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procgetsockname.Addr(), 3, uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
|
r1, _, e1 := syscall.SyscallN(procgetsockname.Addr(), uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
|
||||||
if r1 == socketError {
|
if r1 == socketError {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
|
2
vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go
generated
vendored
2
vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go
generated
vendored
@ -62,7 +62,7 @@ func (b *WString) Free() {
|
|||||||
// ResizeTo grows the buffer to at least c and returns the new capacity, freeing the
|
// ResizeTo grows the buffer to at least c and returns the new capacity, freeing the
|
||||||
// previous buffer back into pool.
|
// previous buffer back into pool.
|
||||||
func (b *WString) ResizeTo(c uint32) uint32 {
|
func (b *WString) ResizeTo(c uint32) uint32 {
|
||||||
// allready sufficient (or n is 0)
|
// already sufficient (or n is 0)
|
||||||
if c <= b.Cap() {
|
if c <= b.Cap() {
|
||||||
return b.Cap()
|
return b.Cap()
|
||||||
}
|
}
|
||||||
|
125
vendor/github.com/Microsoft/go-winio/pipe.go
generated
vendored
125
vendor/github.com/Microsoft/go-winio/pipe.go
generated
vendored
@ -11,7 +11,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
@ -20,20 +19,44 @@ import (
|
|||||||
"github.com/Microsoft/go-winio/internal/fs"
|
"github.com/Microsoft/go-winio/internal/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
//sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe
|
//sys connectNamedPipe(pipe windows.Handle, o *windows.Overlapped) (err error) = ConnectNamedPipe
|
||||||
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
|
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateNamedPipeW
|
||||||
//sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
|
//sys disconnectNamedPipe(pipe windows.Handle) (err error) = DisconnectNamedPipe
|
||||||
//sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
//sys getNamedPipeInfo(pipe windows.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
|
||||||
//sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc
|
//sys getNamedPipeHandleState(pipe windows.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
||||||
//sys ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) = ntdll.NtCreateNamedPipeFile
|
//sys ntCreateNamedPipeFile(pipe *windows.Handle, access ntAccessMask, oa *objectAttributes, iosb *ioStatusBlock, share ntFileShareMode, disposition ntFileCreationDisposition, options ntFileOptions, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) = ntdll.NtCreateNamedPipeFile
|
||||||
//sys rtlNtStatusToDosError(status ntStatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
|
//sys rtlNtStatusToDosError(status ntStatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
|
||||||
//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) = ntdll.RtlDosPathNameToNtPathName_U
|
//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) = ntdll.RtlDosPathNameToNtPathName_U
|
||||||
//sys rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) = ntdll.RtlDefaultNpAcl
|
//sys rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) = ntdll.RtlDefaultNpAcl
|
||||||
|
|
||||||
|
type PipeConn interface {
|
||||||
|
net.Conn
|
||||||
|
Disconnect() error
|
||||||
|
Flush() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// type aliases for mkwinsyscall code
|
||||||
|
type (
|
||||||
|
ntAccessMask = fs.AccessMask
|
||||||
|
ntFileShareMode = fs.FileShareMode
|
||||||
|
ntFileCreationDisposition = fs.NTFileCreationDisposition
|
||||||
|
ntFileOptions = fs.NTCreateOptions
|
||||||
|
)
|
||||||
|
|
||||||
type ioStatusBlock struct {
|
type ioStatusBlock struct {
|
||||||
Status, Information uintptr
|
Status, Information uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// typedef struct _OBJECT_ATTRIBUTES {
|
||||||
|
// ULONG Length;
|
||||||
|
// HANDLE RootDirectory;
|
||||||
|
// PUNICODE_STRING ObjectName;
|
||||||
|
// ULONG Attributes;
|
||||||
|
// PVOID SecurityDescriptor;
|
||||||
|
// PVOID SecurityQualityOfService;
|
||||||
|
// } OBJECT_ATTRIBUTES;
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_object_attributes
|
||||||
type objectAttributes struct {
|
type objectAttributes struct {
|
||||||
Length uintptr
|
Length uintptr
|
||||||
RootDirectory uintptr
|
RootDirectory uintptr
|
||||||
@ -49,6 +72,17 @@ type unicodeString struct {
|
|||||||
Buffer uintptr
|
Buffer uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// typedef struct _SECURITY_DESCRIPTOR {
|
||||||
|
// BYTE Revision;
|
||||||
|
// BYTE Sbz1;
|
||||||
|
// SECURITY_DESCRIPTOR_CONTROL Control;
|
||||||
|
// PSID Owner;
|
||||||
|
// PSID Group;
|
||||||
|
// PACL Sacl;
|
||||||
|
// PACL Dacl;
|
||||||
|
// } SECURITY_DESCRIPTOR, *PISECURITY_DESCRIPTOR;
|
||||||
|
//
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-security_descriptor
|
||||||
type securityDescriptor struct {
|
type securityDescriptor struct {
|
||||||
Revision byte
|
Revision byte
|
||||||
Sbz1 byte
|
Sbz1 byte
|
||||||
@ -80,6 +114,8 @@ type win32Pipe struct {
|
|||||||
path string
|
path string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ PipeConn = (*win32Pipe)(nil)
|
||||||
|
|
||||||
type win32MessageBytePipe struct {
|
type win32MessageBytePipe struct {
|
||||||
win32Pipe
|
win32Pipe
|
||||||
writeClosed bool
|
writeClosed bool
|
||||||
@ -103,6 +139,10 @@ func (f *win32Pipe) SetDeadline(t time.Time) error {
|
|||||||
return f.SetWriteDeadline(t)
|
return f.SetWriteDeadline(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *win32Pipe) Disconnect() error {
|
||||||
|
return disconnectNamedPipe(f.win32File.handle)
|
||||||
|
}
|
||||||
|
|
||||||
// CloseWrite closes the write side of a message pipe in byte mode.
|
// CloseWrite closes the write side of a message pipe in byte mode.
|
||||||
func (f *win32MessageBytePipe) CloseWrite() error {
|
func (f *win32MessageBytePipe) CloseWrite() error {
|
||||||
if f.writeClosed {
|
if f.writeClosed {
|
||||||
@ -146,7 +186,7 @@ func (f *win32MessageBytePipe) Read(b []byte) (int, error) {
|
|||||||
// zero-byte message, ensure that all future Read() calls
|
// zero-byte message, ensure that all future Read() calls
|
||||||
// also return EOF.
|
// also return EOF.
|
||||||
f.readEOF = true
|
f.readEOF = true
|
||||||
} else if err == syscall.ERROR_MORE_DATA { //nolint:errorlint // err is Errno
|
} else if err == windows.ERROR_MORE_DATA { //nolint:errorlint // err is Errno
|
||||||
// ERROR_MORE_DATA indicates that the pipe's read mode is message mode
|
// ERROR_MORE_DATA indicates that the pipe's read mode is message mode
|
||||||
// and the message still has more bytes. Treat this as a success, since
|
// and the message still has more bytes. Treat this as a success, since
|
||||||
// this package presents all named pipes as byte streams.
|
// this package presents all named pipes as byte streams.
|
||||||
@ -164,21 +204,20 @@ func (s pipeAddress) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
|
// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
|
||||||
func tryDialPipe(ctx context.Context, path *string, access fs.AccessMask) (syscall.Handle, error) {
|
func tryDialPipe(ctx context.Context, path *string, access fs.AccessMask, impLevel PipeImpLevel) (windows.Handle, error) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return syscall.Handle(0), ctx.Err()
|
return windows.Handle(0), ctx.Err()
|
||||||
default:
|
default:
|
||||||
wh, err := fs.CreateFile(*path,
|
h, err := fs.CreateFile(*path,
|
||||||
access,
|
access,
|
||||||
0, // mode
|
0, // mode
|
||||||
nil, // security attributes
|
nil, // security attributes
|
||||||
fs.OPEN_EXISTING,
|
fs.OPEN_EXISTING,
|
||||||
fs.FILE_FLAG_OVERLAPPED|fs.SECURITY_SQOS_PRESENT|fs.SECURITY_ANONYMOUS,
|
fs.FILE_FLAG_OVERLAPPED|fs.SECURITY_SQOS_PRESENT|fs.FileSQSFlag(impLevel),
|
||||||
0, // template file handle
|
0, // template file handle
|
||||||
)
|
)
|
||||||
h := syscall.Handle(wh)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
@ -214,15 +253,33 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
|
|||||||
// DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
|
// DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
|
||||||
// cancellation or timeout.
|
// cancellation or timeout.
|
||||||
func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
|
func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
|
||||||
return DialPipeAccess(ctx, path, syscall.GENERIC_READ|syscall.GENERIC_WRITE)
|
return DialPipeAccess(ctx, path, uint32(fs.GENERIC_READ|fs.GENERIC_WRITE))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PipeImpLevel is an enumeration of impersonation levels that may be set
|
||||||
|
// when calling DialPipeAccessImpersonation.
|
||||||
|
type PipeImpLevel uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
PipeImpLevelAnonymous = PipeImpLevel(fs.SECURITY_ANONYMOUS)
|
||||||
|
PipeImpLevelIdentification = PipeImpLevel(fs.SECURITY_IDENTIFICATION)
|
||||||
|
PipeImpLevelImpersonation = PipeImpLevel(fs.SECURITY_IMPERSONATION)
|
||||||
|
PipeImpLevelDelegation = PipeImpLevel(fs.SECURITY_DELEGATION)
|
||||||
|
)
|
||||||
|
|
||||||
// DialPipeAccess attempts to connect to a named pipe by `path` with `access` until `ctx`
|
// DialPipeAccess attempts to connect to a named pipe by `path` with `access` until `ctx`
|
||||||
// cancellation or timeout.
|
// cancellation or timeout.
|
||||||
func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) {
|
func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) {
|
||||||
|
return DialPipeAccessImpLevel(ctx, path, access, PipeImpLevelAnonymous)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialPipeAccessImpLevel attempts to connect to a named pipe by `path` with
|
||||||
|
// `access` at `impLevel` until `ctx` cancellation or timeout. The other
|
||||||
|
// DialPipe* implementations use PipeImpLevelAnonymous.
|
||||||
|
func DialPipeAccessImpLevel(ctx context.Context, path string, access uint32, impLevel PipeImpLevel) (net.Conn, error) {
|
||||||
var err error
|
var err error
|
||||||
var h syscall.Handle
|
var h windows.Handle
|
||||||
h, err = tryDialPipe(ctx, &path, fs.AccessMask(access))
|
h, err = tryDialPipe(ctx, &path, fs.AccessMask(access), impLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -235,7 +292,7 @@ func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn,
|
|||||||
|
|
||||||
f, err := makeWin32File(h)
|
f, err := makeWin32File(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
syscall.Close(h)
|
windows.Close(h)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,7 +312,7 @@ type acceptResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type win32PipeListener struct {
|
type win32PipeListener struct {
|
||||||
firstHandle syscall.Handle
|
firstHandle windows.Handle
|
||||||
path string
|
path string
|
||||||
config PipeConfig
|
config PipeConfig
|
||||||
acceptCh chan (chan acceptResponse)
|
acceptCh chan (chan acceptResponse)
|
||||||
@ -263,8 +320,8 @@ type win32PipeListener struct {
|
|||||||
doneCh chan int
|
doneCh chan int
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (syscall.Handle, error) {
|
func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (windows.Handle, error) {
|
||||||
path16, err := syscall.UTF16FromString(path)
|
path16, err := windows.UTF16FromString(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
}
|
}
|
||||||
@ -280,16 +337,20 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy
|
|||||||
).Err(); err != nil {
|
).Err(); err != nil {
|
||||||
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
}
|
}
|
||||||
defer localFree(ntPath.Buffer)
|
defer windows.LocalFree(windows.Handle(ntPath.Buffer)) //nolint:errcheck
|
||||||
oa.ObjectName = &ntPath
|
oa.ObjectName = &ntPath
|
||||||
oa.Attributes = windows.OBJ_CASE_INSENSITIVE
|
oa.Attributes = windows.OBJ_CASE_INSENSITIVE
|
||||||
|
|
||||||
// The security descriptor is only needed for the first pipe.
|
// The security descriptor is only needed for the first pipe.
|
||||||
if first {
|
if first {
|
||||||
if sd != nil {
|
if sd != nil {
|
||||||
|
//todo: does `sdb` need to be allocated on the heap, or can go allocate it?
|
||||||
l := uint32(len(sd))
|
l := uint32(len(sd))
|
||||||
sdb := localAlloc(0, l)
|
sdb, err := windows.LocalAlloc(0, l)
|
||||||
defer localFree(sdb)
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("LocalAlloc for security descriptor with of length %d: %w", l, err)
|
||||||
|
}
|
||||||
|
defer windows.LocalFree(windows.Handle(sdb)) //nolint:errcheck
|
||||||
copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd)
|
copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd)
|
||||||
oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb))
|
oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb))
|
||||||
} else {
|
} else {
|
||||||
@ -298,7 +359,7 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy
|
|||||||
if err := rtlDefaultNpAcl(&dacl).Err(); err != nil {
|
if err := rtlDefaultNpAcl(&dacl).Err(); err != nil {
|
||||||
return 0, fmt.Errorf("getting default named pipe ACL: %w", err)
|
return 0, fmt.Errorf("getting default named pipe ACL: %w", err)
|
||||||
}
|
}
|
||||||
defer localFree(dacl)
|
defer windows.LocalFree(windows.Handle(dacl)) //nolint:errcheck
|
||||||
|
|
||||||
sdb := &securityDescriptor{
|
sdb := &securityDescriptor{
|
||||||
Revision: 1,
|
Revision: 1,
|
||||||
@ -314,27 +375,27 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy
|
|||||||
typ |= windows.FILE_PIPE_MESSAGE_TYPE
|
typ |= windows.FILE_PIPE_MESSAGE_TYPE
|
||||||
}
|
}
|
||||||
|
|
||||||
disposition := uint32(windows.FILE_OPEN)
|
disposition := fs.FILE_OPEN
|
||||||
access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | syscall.SYNCHRONIZE)
|
access := fs.GENERIC_READ | fs.GENERIC_WRITE | fs.SYNCHRONIZE
|
||||||
if first {
|
if first {
|
||||||
disposition = windows.FILE_CREATE
|
disposition = fs.FILE_CREATE
|
||||||
// By not asking for read or write access, the named pipe file system
|
// By not asking for read or write access, the named pipe file system
|
||||||
// will put this pipe into an initially disconnected state, blocking
|
// will put this pipe into an initially disconnected state, blocking
|
||||||
// client connections until the next call with first == false.
|
// client connections until the next call with first == false.
|
||||||
access = syscall.SYNCHRONIZE
|
access = fs.SYNCHRONIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
timeout := int64(-50 * 10000) // 50ms
|
timeout := int64(-50 * 10000) // 50ms
|
||||||
|
|
||||||
var (
|
var (
|
||||||
h syscall.Handle
|
h windows.Handle
|
||||||
iosb ioStatusBlock
|
iosb ioStatusBlock
|
||||||
)
|
)
|
||||||
err = ntCreateNamedPipeFile(&h,
|
err = ntCreateNamedPipeFile(&h,
|
||||||
access,
|
access,
|
||||||
&oa,
|
&oa,
|
||||||
&iosb,
|
&iosb,
|
||||||
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE,
|
fs.FILE_SHARE_READ|fs.FILE_SHARE_WRITE,
|
||||||
disposition,
|
disposition,
|
||||||
0,
|
0,
|
||||||
typ,
|
typ,
|
||||||
@ -359,7 +420,7 @@ func (l *win32PipeListener) makeServerPipe() (*win32File, error) {
|
|||||||
}
|
}
|
||||||
f, err := makeWin32File(h)
|
f, err := makeWin32File(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
syscall.Close(h)
|
windows.Close(h)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return f, nil
|
return f, nil
|
||||||
@ -418,7 +479,7 @@ func (l *win32PipeListener) listenerRoutine() {
|
|||||||
closed = err == ErrPipeListenerClosed //nolint:errorlint // err is Errno
|
closed = err == ErrPipeListenerClosed //nolint:errorlint // err is Errno
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
syscall.Close(l.firstHandle)
|
windows.Close(l.firstHandle)
|
||||||
l.firstHandle = 0
|
l.firstHandle = 0
|
||||||
// Notify Close() and Accept() callers that the handle has been closed.
|
// Notify Close() and Accept() callers that the handle has been closed.
|
||||||
close(l.doneCh)
|
close(l.doneCh)
|
||||||
|
9
vendor/github.com/Microsoft/go-winio/privilege.go
generated
vendored
9
vendor/github.com/Microsoft/go-winio/privilege.go
generated
vendored
@ -9,7 +9,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
|
||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
@ -18,8 +17,8 @@ import (
|
|||||||
//sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
|
//sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
|
||||||
//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
|
//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
|
||||||
//sys revertToSelf() (err error) = advapi32.RevertToSelf
|
//sys revertToSelf() (err error) = advapi32.RevertToSelf
|
||||||
//sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken
|
//sys openThreadToken(thread windows.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken
|
||||||
//sys getCurrentThread() (h syscall.Handle) = GetCurrentThread
|
//sys getCurrentThread() (h windows.Handle) = GetCurrentThread
|
||||||
//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
|
//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
|
||||||
//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
|
//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
|
||||||
//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
|
//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
|
||||||
@ -29,7 +28,7 @@ const (
|
|||||||
SE_PRIVILEGE_ENABLED = windows.SE_PRIVILEGE_ENABLED
|
SE_PRIVILEGE_ENABLED = windows.SE_PRIVILEGE_ENABLED
|
||||||
|
|
||||||
//revive:disable-next-line:var-naming ALL_CAPS
|
//revive:disable-next-line:var-naming ALL_CAPS
|
||||||
ERROR_NOT_ALL_ASSIGNED syscall.Errno = windows.ERROR_NOT_ALL_ASSIGNED
|
ERROR_NOT_ALL_ASSIGNED windows.Errno = windows.ERROR_NOT_ALL_ASSIGNED
|
||||||
|
|
||||||
SeBackupPrivilege = "SeBackupPrivilege"
|
SeBackupPrivilege = "SeBackupPrivilege"
|
||||||
SeRestorePrivilege = "SeRestorePrivilege"
|
SeRestorePrivilege = "SeRestorePrivilege"
|
||||||
@ -177,7 +176,7 @@ func newThreadToken() (windows.Token, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var token windows.Token
|
var token windows.Token
|
||||||
err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token)
|
err = openThreadToken(getCurrentThread(), windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, false, &token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rerr := revertToSelf()
|
rerr := revertToSelf()
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
|
37
vendor/github.com/Microsoft/go-winio/sd.go
generated
vendored
37
vendor/github.com/Microsoft/go-winio/sd.go
generated
vendored
@ -5,7 +5,7 @@ package winio
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"syscall"
|
"fmt"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
@ -15,10 +15,6 @@ import (
|
|||||||
//sys lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountSidW
|
//sys lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountSidW
|
||||||
//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
|
//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
|
||||||
//sys convertStringSidToSid(str *uint16, sid **byte) (err error) = advapi32.ConvertStringSidToSidW
|
//sys convertStringSidToSid(str *uint16, sid **byte) (err error) = advapi32.ConvertStringSidToSidW
|
||||||
//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
|
|
||||||
//sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW
|
|
||||||
//sys localFree(mem uintptr) = LocalFree
|
|
||||||
//sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength
|
|
||||||
|
|
||||||
type AccountLookupError struct {
|
type AccountLookupError struct {
|
||||||
Name string
|
Name string
|
||||||
@ -64,7 +60,7 @@ func LookupSidByName(name string) (sid string, err error) {
|
|||||||
|
|
||||||
var sidSize, sidNameUse, refDomainSize uint32
|
var sidSize, sidNameUse, refDomainSize uint32
|
||||||
err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse)
|
err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse)
|
||||||
if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno
|
if err != nil && err != windows.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno
|
||||||
return "", &AccountLookupError{name, err}
|
return "", &AccountLookupError{name, err}
|
||||||
}
|
}
|
||||||
sidBuffer := make([]byte, sidSize)
|
sidBuffer := make([]byte, sidSize)
|
||||||
@ -78,8 +74,8 @@ func LookupSidByName(name string) (sid string, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", &AccountLookupError{name, err}
|
return "", &AccountLookupError{name, err}
|
||||||
}
|
}
|
||||||
sid = syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
|
sid = windows.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
|
||||||
localFree(uintptr(unsafe.Pointer(strBuffer)))
|
_, _ = windows.LocalFree(windows.Handle(unsafe.Pointer(strBuffer)))
|
||||||
return sid, nil
|
return sid, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,7 +96,7 @@ func LookupNameBySid(sid string) (name string, err error) {
|
|||||||
if err = convertStringSidToSid(sidBuffer, &sidPtr); err != nil {
|
if err = convertStringSidToSid(sidBuffer, &sidPtr); err != nil {
|
||||||
return "", &AccountLookupError{sid, err}
|
return "", &AccountLookupError{sid, err}
|
||||||
}
|
}
|
||||||
defer localFree(uintptr(unsafe.Pointer(sidPtr)))
|
defer windows.LocalFree(windows.Handle(unsafe.Pointer(sidPtr))) //nolint:errcheck
|
||||||
|
|
||||||
var nameSize, refDomainSize, sidNameUse uint32
|
var nameSize, refDomainSize, sidNameUse uint32
|
||||||
err = lookupAccountSid(nil, sidPtr, nil, &nameSize, nil, &refDomainSize, &sidNameUse)
|
err = lookupAccountSid(nil, sidPtr, nil, &nameSize, nil, &refDomainSize, &sidNameUse)
|
||||||
@ -120,25 +116,18 @@ func LookupNameBySid(sid string) (name string, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
|
func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
|
||||||
var sdBuffer uintptr
|
sd, err := windows.SecurityDescriptorFromString(sddl)
|
||||||
err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &SddlConversionError{sddl, err}
|
return nil, &SddlConversionError{Sddl: sddl, Err: err}
|
||||||
}
|
}
|
||||||
defer localFree(sdBuffer)
|
b := unsafe.Slice((*byte)(unsafe.Pointer(sd)), sd.Length())
|
||||||
sd := make([]byte, getSecurityDescriptorLength(sdBuffer))
|
return b, nil
|
||||||
copy(sd, (*[0xffff]byte)(unsafe.Pointer(sdBuffer))[:len(sd)])
|
|
||||||
return sd, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func SecurityDescriptorToSddl(sd []byte) (string, error) {
|
func SecurityDescriptorToSddl(sd []byte) (string, error) {
|
||||||
var sddl *uint16
|
if l := int(unsafe.Sizeof(windows.SECURITY_DESCRIPTOR{})); len(sd) < l {
|
||||||
// The returned string length seems to include an arbitrary number of terminating NULs.
|
return "", fmt.Errorf("SecurityDescriptor (%d) smaller than expected (%d): %w", len(sd), l, windows.ERROR_INCORRECT_SIZE)
|
||||||
// Don't use it.
|
|
||||||
err := convertSecurityDescriptorToStringSecurityDescriptor(&sd[0], 1, 0xff, &sddl, nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
defer localFree(uintptr(unsafe.Pointer(sddl)))
|
s := (*windows.SECURITY_DESCRIPTOR)(unsafe.Pointer(&sd[0]))
|
||||||
return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(sddl))[:]), nil
|
return s.String(), nil
|
||||||
}
|
}
|
||||||
|
5
vendor/github.com/Microsoft/go-winio/tools.go
generated
vendored
5
vendor/github.com/Microsoft/go-winio/tools.go
generated
vendored
@ -1,5 +0,0 @@
|
|||||||
//go:build tools
|
|
||||||
|
|
||||||
package winio
|
|
||||||
|
|
||||||
import _ "golang.org/x/tools/cmd/stringer"
|
|
13
vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go
generated
vendored
13
vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go
generated
vendored
@ -33,9 +33,6 @@ func errnoErr(e syscall.Errno) error {
|
|||||||
case errnoERROR_IO_PENDING:
|
case errnoERROR_IO_PENDING:
|
||||||
return errERROR_IO_PENDING
|
return errERROR_IO_PENDING
|
||||||
}
|
}
|
||||||
// TODO: add more here, after collecting data on the common
|
|
||||||
// error values see on Windows. (perhaps when running
|
|
||||||
// all.bat?)
|
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +47,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func attachVirtualDisk(handle syscall.Handle, securityDescriptor *uintptr, attachVirtualDiskFlag uint32, providerSpecificFlags uint32, parameters *AttachVirtualDiskParameters, overlapped *syscall.Overlapped) (win32err error) {
|
func attachVirtualDisk(handle syscall.Handle, securityDescriptor *uintptr, attachVirtualDiskFlag uint32, providerSpecificFlags uint32, parameters *AttachVirtualDiskParameters, overlapped *syscall.Overlapped) (win32err error) {
|
||||||
r0, _, _ := syscall.Syscall6(procAttachVirtualDisk.Addr(), 6, uintptr(handle), uintptr(unsafe.Pointer(securityDescriptor)), uintptr(attachVirtualDiskFlag), uintptr(providerSpecificFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(overlapped)))
|
r0, _, _ := syscall.SyscallN(procAttachVirtualDisk.Addr(), uintptr(handle), uintptr(unsafe.Pointer(securityDescriptor)), uintptr(attachVirtualDiskFlag), uintptr(providerSpecificFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(overlapped)))
|
||||||
if r0 != 0 {
|
if r0 != 0 {
|
||||||
win32err = syscall.Errno(r0)
|
win32err = syscall.Errno(r0)
|
||||||
}
|
}
|
||||||
@ -67,7 +64,7 @@ func createVirtualDisk(virtualStorageType *VirtualStorageType, path string, virt
|
|||||||
}
|
}
|
||||||
|
|
||||||
func _createVirtualDisk(virtualStorageType *VirtualStorageType, path *uint16, virtualDiskAccessMask uint32, securityDescriptor *uintptr, createVirtualDiskFlags uint32, providerSpecificFlags uint32, parameters *CreateVirtualDiskParameters, overlapped *syscall.Overlapped, handle *syscall.Handle) (win32err error) {
|
func _createVirtualDisk(virtualStorageType *VirtualStorageType, path *uint16, virtualDiskAccessMask uint32, securityDescriptor *uintptr, createVirtualDiskFlags uint32, providerSpecificFlags uint32, parameters *CreateVirtualDiskParameters, overlapped *syscall.Overlapped, handle *syscall.Handle) (win32err error) {
|
||||||
r0, _, _ := syscall.Syscall9(procCreateVirtualDisk.Addr(), 9, uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(unsafe.Pointer(securityDescriptor)), uintptr(createVirtualDiskFlags), uintptr(providerSpecificFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(handle)))
|
r0, _, _ := syscall.SyscallN(procCreateVirtualDisk.Addr(), uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(unsafe.Pointer(securityDescriptor)), uintptr(createVirtualDiskFlags), uintptr(providerSpecificFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(handle)))
|
||||||
if r0 != 0 {
|
if r0 != 0 {
|
||||||
win32err = syscall.Errno(r0)
|
win32err = syscall.Errno(r0)
|
||||||
}
|
}
|
||||||
@ -75,7 +72,7 @@ func _createVirtualDisk(virtualStorageType *VirtualStorageType, path *uint16, vi
|
|||||||
}
|
}
|
||||||
|
|
||||||
func detachVirtualDisk(handle syscall.Handle, detachVirtualDiskFlags uint32, providerSpecificFlags uint32) (win32err error) {
|
func detachVirtualDisk(handle syscall.Handle, detachVirtualDiskFlags uint32, providerSpecificFlags uint32) (win32err error) {
|
||||||
r0, _, _ := syscall.Syscall(procDetachVirtualDisk.Addr(), 3, uintptr(handle), uintptr(detachVirtualDiskFlags), uintptr(providerSpecificFlags))
|
r0, _, _ := syscall.SyscallN(procDetachVirtualDisk.Addr(), uintptr(handle), uintptr(detachVirtualDiskFlags), uintptr(providerSpecificFlags))
|
||||||
if r0 != 0 {
|
if r0 != 0 {
|
||||||
win32err = syscall.Errno(r0)
|
win32err = syscall.Errno(r0)
|
||||||
}
|
}
|
||||||
@ -83,7 +80,7 @@ func detachVirtualDisk(handle syscall.Handle, detachVirtualDiskFlags uint32, pro
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getVirtualDiskPhysicalPath(handle syscall.Handle, diskPathSizeInBytes *uint32, buffer *uint16) (win32err error) {
|
func getVirtualDiskPhysicalPath(handle syscall.Handle, diskPathSizeInBytes *uint32, buffer *uint16) (win32err error) {
|
||||||
r0, _, _ := syscall.Syscall(procGetVirtualDiskPhysicalPath.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(diskPathSizeInBytes)), uintptr(unsafe.Pointer(buffer)))
|
r0, _, _ := syscall.SyscallN(procGetVirtualDiskPhysicalPath.Addr(), uintptr(handle), uintptr(unsafe.Pointer(diskPathSizeInBytes)), uintptr(unsafe.Pointer(buffer)))
|
||||||
if r0 != 0 {
|
if r0 != 0 {
|
||||||
win32err = syscall.Errno(r0)
|
win32err = syscall.Errno(r0)
|
||||||
}
|
}
|
||||||
@ -100,7 +97,7 @@ func openVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtua
|
|||||||
}
|
}
|
||||||
|
|
||||||
func _openVirtualDisk(virtualStorageType *VirtualStorageType, path *uint16, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *openVirtualDiskParameters, handle *syscall.Handle) (win32err error) {
|
func _openVirtualDisk(virtualStorageType *VirtualStorageType, path *uint16, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *openVirtualDiskParameters, handle *syscall.Handle) (win32err error) {
|
||||||
r0, _, _ := syscall.Syscall6(procOpenVirtualDisk.Addr(), 6, uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(openVirtualDiskFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(handle)))
|
r0, _, _ := syscall.SyscallN(procOpenVirtualDisk.Addr(), uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(openVirtualDiskFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(handle)))
|
||||||
if r0 != 0 {
|
if r0 != 0 {
|
||||||
win32err = syscall.Errno(r0)
|
win32err = syscall.Errno(r0)
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user