Compare commits

..

3 Commits

Author SHA1 Message Date
4744ec27b8 Merge pull request #716 from squeed/cp-bugfixes
[release-1.1] Cherry-pick some bugfixes
2022-03-09 09:06:40 -08:00
b1782e50d7 ipam/dhcp: Fix client id in renew/release
The client id was constructed differently in the acquire
function compared to the release and renew functions,
which caused the dhcp-server to consider it a different client.
This is now encapsulated in a common function.

Signed-off-by: Fabian Wiesel <fabian.wiesel@sap.com>
2022-03-09 17:47:10 +01:00
b03deb63a9 call ipam.ExceDel after clean up device in netns
fix #666

Signed-off-by: gojoy <729324352@qq.com>
2022-03-09 17:46:59 +01:00
1574 changed files with 66940 additions and 180660 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,106 +0,0 @@
---
name: Release binaries
on:
push:
tags:
- 'v*'
jobs:
linux_release:
name: Release linux binaries
runs-on: ubuntu-latest
strategy:
matrix:
goarch: [amd64, arm, arm64, mips64le, ppc64le, riscv64, s390x]
steps:
- name: Install Go
uses: actions/setup-go@v5
with:
go-version: 1.21
- name: Checkout code
uses: actions/checkout@v4
- name: Build
env:
GOARCH: ${{ matrix.goarch }}
CGO_ENABLED: 0
run: ./build_linux.sh
- name: COPY files
run: cp README.md LICENSE bin/
- name: Create dist directory
run: mkdir dist
- name: Create archive file
working-directory: ./bin
run: tar cfzv ../dist/cni-plugins-linux-${{ matrix.goarch }}-${{ github.ref_name }}.tgz .
- name: Create sha256 checksum
working-directory: ./dist
run: sha256sum cni-plugins-linux-${{ matrix.goarch }}-${{ github.ref_name }}.tgz | tee cni-plugins-linux-${{ matrix.goarch }}-${{ github.ref_name }}.tgz.sha256
- name: Create sha512 checksum
working-directory: ./dist
run: sha512sum cni-plugins-linux-${{ matrix.goarch }}-${{ github.ref_name }}.tgz | tee cni-plugins-linux-${{ matrix.goarch }}-${{ github.ref_name }}.tgz.sha512
- name: Upload binaries to release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ./dist/*
tag: ${{ github.ref }}
overwrite: true
file_glob: true
windows_releases:
name: Release windows binaries
runs-on: ubuntu-latest
strategy:
matrix:
goarch: [amd64]
steps:
- name: Install dos2unix
run: sudo apt-get install dos2unix
- name: Install Go
uses: actions/setup-go@v5
with:
go-version: 1.21
- name: Checkout code
uses: actions/checkout@v4
- name: Build
env:
GOARCH: ${{ matrix.goarch }}
CGO_ENABLED: 0
run: ./build_windows.sh
- name: COPY files
run: cp README.md LICENSE bin/
- name: Create dist directory
run: mkdir dist
- name: Create archive file
working-directory: ./bin
run: tar cfzv ../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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

62
go.mod
View File

@ -1,50 +1,40 @@
module github.com/containernetworking/plugins
go 1.20
go 1.17
require (
github.com/Microsoft/hcsshim v0.12.0
github.com/alexflint/go-filemutex v1.3.0
github.com/Microsoft/hcsshim v0.8.20
github.com/alexflint/go-filemutex v1.1.0
github.com/buger/jsonparser v1.1.1
github.com/containernetworking/cni v1.1.2
github.com/coreos/go-iptables v0.7.0
github.com/coreos/go-systemd/v22 v22.5.0
github.com/containernetworking/cni v1.0.1
github.com/coreos/go-iptables v0.6.0
github.com/coreos/go-systemd/v22 v22.3.2
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.0.4
github.com/mattn/go-shellwords v1.0.12
github.com/networkplumbing/go-nft v0.4.0
github.com/onsi/ginkgo/v2 v2.16.0
github.com/onsi/gomega v1.31.1
github.com/opencontainers/selinux v1.11.0
github.com/safchain/ethtool v0.3.0
github.com/vishvananda/netlink v1.2.1-beta.2
golang.org/x/sys v0.17.0
github.com/networkplumbing/go-nft v0.2.0
github.com/onsi/ginkgo v1.16.4
github.com/onsi/gomega v1.15.0
github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1
github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e
)
require (
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/containerd/cgroups/v3 v3.0.2 // indirect
github.com/containerd/errdefs v0.1.0 // indirect
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20230323073829-e72429f035bd // indirect
github.com/Microsoft/go-winio v0.4.17 // indirect
github.com/containerd/cgroups v1.0.1 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/stretchr/testify v1.8.2 // indirect
github.com/vishvananda/netns v0.0.4 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.17.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
google.golang.org/grpc v1.62.0 // indirect
google.golang.org/protobuf v1.32.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
go.opencensus.io v0.22.3 // indirect
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 // indirect
golang.org/x/text v0.3.6 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

895
go.sum

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -17,7 +17,7 @@ import (
"strings"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
)

View File

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

View File

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

View File

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

View File

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

View File

@ -1,247 +0,0 @@
// Copyright 2022 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ip
import (
"net"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("CIDR functions", func() {
It("NextIP", func() {
testCases := []struct {
ip net.IP
nextIP net.IP
}{
{
[]byte{192, 0, 2},
nil,
},
{
net.ParseIP("192.168.0.1"),
net.IPv4(192, 168, 0, 2).To4(),
},
{
net.ParseIP("192.168.0.255"),
net.IPv4(192, 168, 1, 0).To4(),
},
{
net.ParseIP("0.1.0.5"),
net.IPv4(0, 1, 0, 6).To4(),
},
{
net.ParseIP("AB12::123"),
net.ParseIP("AB12::124"),
},
{
net.ParseIP("AB12::FFFF"),
net.ParseIP("AB12::1:0"),
},
{
net.ParseIP("0::123"),
net.ParseIP("0::124"),
},
}
for _, test := range testCases {
ip := NextIP(test.ip)
Expect(ip).To(Equal(test.nextIP))
}
})
It("PrevIP", func() {
testCases := []struct {
ip net.IP
prevIP net.IP
}{
{
[]byte{192, 0, 2},
nil,
},
{
net.ParseIP("192.168.0.2"),
net.IPv4(192, 168, 0, 1).To4(),
},
{
net.ParseIP("192.168.1.0"),
net.IPv4(192, 168, 0, 255).To4(),
},
{
net.ParseIP("0.1.0.5"),
net.IPv4(0, 1, 0, 4).To4(),
},
{
net.ParseIP("AB12::123"),
net.ParseIP("AB12::122"),
},
{
net.ParseIP("AB12::1:0"),
net.ParseIP("AB12::FFFF"),
},
{
net.ParseIP("0::124"),
net.ParseIP("0::123"),
},
}
for _, test := range testCases {
ip := PrevIP(test.ip)
Expect(ip).To(Equal(test.prevIP))
}
})
It("Cmp", func() {
testCases := []struct {
a net.IP
b net.IP
result int
}{
{
net.ParseIP("192.168.0.2"),
nil,
-2,
},
{
net.ParseIP("192.168.0.2"),
[]byte{192, 168, 5},
-2,
},
{
net.ParseIP("192.168.0.2"),
net.ParseIP("AB12::123"),
-2,
},
{
net.ParseIP("192.168.0.2"),
net.ParseIP("192.168.0.5"),
-1,
},
{
net.ParseIP("192.168.0.2"),
net.ParseIP("192.168.0.5").To4(),
-1,
},
{
net.ParseIP("192.168.0.10"),
net.ParseIP("192.168.0.5"),
1,
},
{
net.ParseIP("192.168.0.10"),
net.ParseIP("192.168.0.10"),
0,
},
{
net.ParseIP("192.168.0.10"),
net.ParseIP("192.168.0.10").To4(),
0,
},
{
net.ParseIP("AB12::122"),
net.ParseIP("AB12::123"),
-1,
},
{
net.ParseIP("AB12::210"),
net.ParseIP("AB12::123"),
1,
},
{
net.ParseIP("AB12::210"),
net.ParseIP("AB12::210"),
0,
},
}
for _, test := range testCases {
result := Cmp(test.a, test.b)
Expect(result).To(Equal(test.result))
}
})
It("Network", func() {
testCases := []struct {
ipNet *net.IPNet
result *net.IPNet
}{
{
nil,
nil,
},
{
&net.IPNet{
IP: nil,
Mask: net.IPv4Mask(255, 255, 255, 0),
},
nil,
},
{
&net.IPNet{
IP: net.IPv4(192, 168, 0, 1),
Mask: nil,
},
nil,
},
{
&net.IPNet{
IP: net.ParseIP("AB12::123"),
Mask: net.IPv4Mask(255, 255, 255, 0),
},
nil,
},
{
&net.IPNet{
IP: net.IPv4(192, 168, 0, 100).To4(),
Mask: net.CIDRMask(120, 128),
},
&net.IPNet{
IP: net.IPv4(192, 168, 0, 0).To4(),
Mask: net.CIDRMask(120, 128),
},
},
{
&net.IPNet{
IP: net.IPv4(192, 168, 0, 100),
Mask: net.CIDRMask(24, 32),
},
&net.IPNet{
IP: net.IPv4(192, 168, 0, 0).To4(),
Mask: net.CIDRMask(24, 32),
},
},
{
&net.IPNet{
IP: net.ParseIP("AB12::123"),
Mask: net.CIDRMask(120, 128),
},
&net.IPNet{
IP: net.ParseIP("AB12::100"),
Mask: net.CIDRMask(120, 128),
},
},
}
for _, test := range testCases {
result := Network(test.ipNet)
Expect(result).To(Equal(test.result))
}
})
})

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -42,11 +42,6 @@ func AddHostRoute(ipn *net.IPNet, gw net.IP, dev netlink.Link) error {
// AddDefaultRoute sets the default route on the given gateway.
func AddDefaultRoute(gw net.IP, dev netlink.Link) error {
var defNet *net.IPNet
if gw.To4() != nil {
_, defNet, _ = net.ParseCIDR("0.0.0.0/0")
} else {
_, defNet, _ = net.ParseCIDR("::/0")
}
_, defNet, _ := net.ParseCIDR("0.0.0.0/0")
return AddRoute(defNet, gw, dev)
}

View File

@ -21,13 +21,13 @@ import (
"fmt"
"net"
"github.com/vishvananda/netlink"
"github.com/containernetworking/cni/pkg/types"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/vishvananda/netlink"
)
func ValidateExpectedInterfaceIPs(ifName string, resultIPs []*current.IPConfig) error {
// Ensure ips
for _, ips := range resultIPs {
ourAddr := netlink.Addr{IPNet: &ips.Address}
@ -49,15 +49,12 @@ func ValidateExpectedInterfaceIPs(ifName string, resultIPs []*current.IPConfig)
break
}
}
if !match {
if match == false {
return fmt.Errorf("Failed to match addr %v on interface %v", ourAddr, ifName)
}
// Convert the host/prefixlen to just prefix for route lookup.
_, ourPrefix, err := net.ParseCIDR(ourAddr.String())
if err != nil {
return err
}
findGwy := &netlink.Route{Dst: ourPrefix}
routeFilter := netlink.RT_FILTER_DST
@ -80,13 +77,11 @@ func ValidateExpectedInterfaceIPs(ifName string, resultIPs []*current.IPConfig)
}
func ValidateExpectedRoute(resultRoutes []*types.Route) error {
// Ensure that each static route in prevResults is found in the routing table
for _, route := range resultRoutes {
find := &netlink.Route{Dst: &route.Dst, Gw: route.GW}
routeFilter := netlink.RT_FILTER_DST
if route.GW != nil {
routeFilter |= netlink.RT_FILTER_GW
}
routeFilter := netlink.RT_FILTER_DST | netlink.RT_FILTER_GW
var family int
switch {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -21,7 +21,6 @@ import (
"net"
"os"
"runtime"
"sort"
"syscall"
"time"
@ -47,20 +46,17 @@ const defaultBrName = "cni0"
type NetConf struct {
types.NetConf
BrName string `json:"bridge"`
IsGW bool `json:"isGateway"`
IsDefaultGW bool `json:"isDefaultGateway"`
ForceAddress bool `json:"forceAddress"`
IPMasq bool `json:"ipMasq"`
MTU int `json:"mtu"`
HairpinMode bool `json:"hairpinMode"`
PromiscMode bool `json:"promiscMode"`
Vlan int `json:"vlan"`
VlanTrunk []*VlanTrunk `json:"vlanTrunk,omitempty"`
PreserveDefaultVlan bool `json:"preserveDefaultVlan"`
MacSpoofChk bool `json:"macspoofchk,omitempty"`
EnableDad bool `json:"enabledad,omitempty"`
DisableContainerInterface bool `json:"disableContainerInterface,omitempty"`
BrName string `json:"bridge"`
IsGW bool `json:"isGateway"`
IsDefaultGW bool `json:"isDefaultGateway"`
ForceAddress bool `json:"forceAddress"`
IPMasq bool `json:"ipMasq"`
MTU int `json:"mtu"`
HairpinMode bool `json:"hairpinMode"`
PromiscMode bool `json:"promiscMode"`
Vlan int `json:"vlan"`
MacSpoofChk bool `json:"macspoofchk,omitempty"`
EnableDad bool `json:"enabledad,omitempty"`
Args struct {
Cni BridgeArgs `json:"cni,omitempty"`
@ -69,14 +65,7 @@ type NetConf struct {
Mac string `json:"mac,omitempty"`
} `json:"runtimeConfig,omitempty"`
mac string
vlans []int
}
type VlanTrunk struct {
MinID *int `json:"minID,omitempty"`
MaxID *int `json:"maxID,omitempty"`
ID *int `json:"id,omitempty"`
mac string
}
type BridgeArgs struct {
@ -105,8 +94,6 @@ func init() {
func loadNetConf(bytes []byte, envArgs string) (*NetConf, string, error) {
n := &NetConf{
BrName: defaultBrName,
// Set default value equal to true to maintain existing behavior.
PreserveDefaultVlan: true,
}
if err := json.Unmarshal(bytes, n); err != nil {
return nil, "", fmt.Errorf("failed to load netconf: %v", err)
@ -114,17 +101,6 @@ func loadNetConf(bytes []byte, envArgs string) (*NetConf, string, error) {
if n.Vlan < 0 || n.Vlan > 4094 {
return nil, "", fmt.Errorf("invalid VLAN ID %d (must be between 0 and 4094)", n.Vlan)
}
var err error
n.vlans, err = collectVlanTrunk(n.VlanTrunk)
if err != nil {
// fail to parsing
return nil, "", err
}
// Currently bridge CNI only support access port(untagged only) or trunk port(tagged only)
if n.Vlan > 0 && n.vlans != nil {
return nil, "", errors.New("cannot set vlan and vlanTrunk at the same time")
}
if envArgs != "" {
e := MacEnvArgs{}
@ -148,66 +124,12 @@ func loadNetConf(bytes []byte, envArgs string) (*NetConf, string, error) {
return n, n.CNIVersion, nil
}
// This method is copied from https://github.com/k8snetworkplumbingwg/ovs-cni/blob/v0.27.2/pkg/plugin/plugin.go
func collectVlanTrunk(vlanTrunk []*VlanTrunk) ([]int, error) {
if vlanTrunk == nil {
return nil, nil
}
vlanMap := make(map[int]struct{})
for _, item := range vlanTrunk {
var minID int
var maxID int
var ID int
switch {
case item.MinID != nil && item.MaxID != nil:
minID = *item.MinID
if minID <= 0 || minID > 4094 {
return nil, errors.New("incorrect trunk minID parameter")
}
maxID = *item.MaxID
if maxID <= 0 || maxID > 4094 {
return nil, errors.New("incorrect trunk maxID parameter")
}
if maxID < minID {
return nil, errors.New("minID is greater than maxID in trunk parameter")
}
for v := minID; v <= maxID; v++ {
vlanMap[v] = struct{}{}
}
case item.MinID == nil && item.MaxID != nil:
return nil, errors.New("minID and maxID should be configured simultaneously, minID is missing")
case item.MinID != nil && item.MaxID == nil:
return nil, errors.New("minID and maxID should be configured simultaneously, maxID is missing")
}
// single vid
if item.ID != nil {
ID = *item.ID
if ID <= 0 || ID > 4094 {
return nil, errors.New("incorrect trunk id parameter")
}
vlanMap[ID] = struct{}{}
}
}
if len(vlanMap) == 0 {
return nil, nil
}
vlans := make([]int, 0, len(vlanMap))
for k := range vlanMap {
vlans = append(vlans, k)
}
sort.Slice(vlans, func(i int, j int) bool { return vlans[i] < vlans[j] })
return vlans, nil
}
// calcGateways processes the results from the IPAM plugin and does the
// following for each IP family:
// - Calculates and compiles a list of gateway addresses
// - Adds a default route if needed
// - Calculates and compiles a list of gateway addresses
// - Adds a default route if needed
func calcGateways(result *current.Result, n *NetConf) (*gwInfo, *gwInfo, error) {
gwsV4 := &gwInfo{}
gwsV6 := &gwInfo{}
@ -378,8 +300,8 @@ func ensureBridge(brName string, mtu int, promiscMode, vlanFiltering bool) (*net
return br, nil
}
func ensureVlanInterface(br *netlink.Bridge, vlanID int, preserveDefaultVlan bool) (netlink.Link, error) {
name := fmt.Sprintf("%s.%d", br.Name, vlanID)
func ensureVlanInterface(br *netlink.Bridge, vlanId int) (netlink.Link, error) {
name := fmt.Sprintf("%s.%d", br.Name, vlanId)
brGatewayVeth, err := netlink.LinkByName(name)
if err != nil {
@ -392,7 +314,7 @@ func ensureVlanInterface(br *netlink.Bridge, vlanID int, preserveDefaultVlan boo
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, "")
if err != nil {
return nil, fmt.Errorf("faild to create vlan gateway %q: %v", name, err)
}
@ -411,7 +333,7 @@ func ensureVlanInterface(br *netlink.Bridge, vlanID int, preserveDefaultVlan boo
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, mac string) (*current.Interface, *current.Interface, error) {
contIface := &current.Interface{}
hostIface := &current.Interface{}
@ -448,14 +370,6 @@ func setupVeth(netns ns.NetNS, br *netlink.Bridge, ifName string, mtu int, hairp
return nil, nil, fmt.Errorf("failed to setup hairpin mode for %v: %v", hostVeth.Attrs().Name, err)
}
if (vlanID != 0 || len(vlans) > 0) && !preserveDefaultVlan {
err = removeDefaultVlan(hostVeth)
if err != nil {
return nil, nil, fmt.Errorf("failed to remove default vlan on interface %q: %v", hostIface.Name, err)
}
}
// Currently bridge CNI only support access port(untagged only) or trunk port(tagged only)
if vlanID != 0 {
err = netlink.BridgeVlanAdd(hostVeth, uint16(vlanID), true, true, false, true)
if err != nil {
@ -463,35 +377,9 @@ func setupVeth(netns ns.NetNS, br *netlink.Bridge, ifName string, mtu int, hairp
}
}
for _, v := range vlans {
err = netlink.BridgeVlanAdd(hostVeth, uint16(v), false, false, false, true)
if err != nil {
return nil, nil, fmt.Errorf("failed to setup vlan tag on interface %q: %w", hostIface.Name, err)
}
}
return hostIface, contIface, nil
}
func removeDefaultVlan(hostVeth netlink.Link) error {
vlanInfo, err := netlink.BridgeVlanList()
if err != nil {
return err
}
brVlanInfo, ok := vlanInfo[int32(hostVeth.Attrs().Index)]
if ok {
for _, info := range brVlanInfo {
err = netlink.BridgeVlanDel(hostVeth, info.Vid, false, false, false, true)
if err != nil {
return err
}
}
}
return nil
}
func calcGatewayIP(ipn *net.IPNet) net.IP {
nid := ipn.IP.Mask(ipn.Mask)
return ip.NextIP(nid)
@ -499,7 +387,7 @@ func calcGatewayIP(ipn *net.IPNet) net.IP {
func setupBridge(n *NetConf) (*netlink.Bridge, *current.Interface, error) {
vlanFiltering := false
if n.Vlan != 0 || n.VlanTrunk != nil {
if n.Vlan != 0 {
vlanFiltering = true
}
// create bridge if necessary
@ -522,7 +410,7 @@ func enableIPForward(family int) error {
}
func cmdAdd(args *skel.CmdArgs) error {
success := false
var success bool = false
n, cniVersion, err := loadNetConf(args.StdinData, args.Args)
if err != nil {
@ -531,16 +419,12 @@ func cmdAdd(args *skel.CmdArgs) error {
isLayer3 := n.IPAM.Type != ""
if isLayer3 && n.DisableContainerInterface {
return fmt.Errorf("cannot use IPAM when DisableContainerInterface flag is set")
}
if n.IsDefaultGW {
n.IsGW = true
}
if n.HairpinMode && n.PromiscMode {
return fmt.Errorf("cannot set hairpin mode and promiscuous mode at the same time")
return fmt.Errorf("cannot set hairpin mode and promiscuous mode at the same time.")
}
br, brInterface, err := setupBridge(n)
@ -554,7 +438,7 @@ func cmdAdd(args *skel.CmdArgs) error {
}
defer netns.Close()
hostInterface, containerInterface, err := setupVeth(netns, br, args.IfName, n.MTU, n.HairpinMode, n.Vlan, n.vlans, n.PreserveDefaultVlan, n.mac)
hostInterface, containerInterface, err := setupVeth(netns, br, args.IfName, n.MTU, n.HairpinMode, n.Vlan, n.mac)
if err != nil {
return err
}
@ -605,7 +489,6 @@ func cmdAdd(args *skel.CmdArgs) error {
result.IPs = ipamResult.IPs
result.Routes = ipamResult.Routes
result.DNS = ipamResult.DNS
if len(result.IPs) == 0 {
return errors.New("IPAM plugin returned missing IP config")
@ -628,27 +511,50 @@ func cmdAdd(args *skel.CmdArgs) error {
_, _ = sysctl.Sysctl(fmt.Sprintf("net/ipv4/conf/%s/arp_notify", args.IfName), "1")
// Add the IP to the interface
return ipam.ConfigureIface(args.IfName, result)
if err := ipam.ConfigureIface(args.IfName, result); err != nil {
return err
}
return nil
}); err != nil {
return err
}
// check bridge port state
retries := []int{0, 50, 500, 1000, 1000}
for idx, sleep := range retries {
time.Sleep(time.Duration(sleep) * time.Millisecond)
hostVeth, err := netlink.LinkByName(hostInterface.Name)
if err != nil {
return err
}
if hostVeth.Attrs().OperState == netlink.OperUp {
break
}
if idx == len(retries)-1 {
return fmt.Errorf("bridge port in error state: %s", hostVeth.Attrs().OperState)
}
}
if n.IsGW {
var firstV4Addr net.IP
var vlanInterface *current.Interface
// Set the IP address(es) on the bridge and enable forwarding
for _, gws := range []*gwInfo{gwsV4, gwsV6} {
for _, gw := range gws.gws {
if gw.IP.To4() != nil && firstV4Addr == nil {
firstV4Addr = gw.IP
}
if n.Vlan != 0 {
vlanIface, err := ensureVlanInterface(br, n.Vlan, n.PreserveDefaultVlan)
vlanIface, err := ensureVlanInterface(br, n.Vlan)
if err != nil {
return fmt.Errorf("failed to create vlan interface: %v", err)
}
if vlanInterface == nil {
vlanInterface = &current.Interface{
Name: vlanIface.Attrs().Name,
Mac: vlanIface.Attrs().HardwareAddr.String(),
}
vlanInterface = &current.Interface{Name: vlanIface.Attrs().Name,
Mac: vlanIface.Attrs().HardwareAddr.String()}
result.Interfaces = append(result.Interfaces, vlanInterface)
}
@ -681,13 +587,12 @@ func cmdAdd(args *skel.CmdArgs) error {
}
}
}
} else if !n.DisableContainerInterface {
} else {
if err := netns.Do(func(_ ns.NetNS) error {
link, err := netlink.LinkByName(args.IfName)
if err != nil {
return fmt.Errorf("failed to retrieve link: %v", err)
}
// If layer 2 we still need to set the container veth to up
if err = netlink.LinkSetUp(link); err != nil {
return fmt.Errorf("failed to set %q up: %v", args.IfName, err)
@ -698,34 +603,6 @@ func cmdAdd(args *skel.CmdArgs) error {
}
}
hostVeth, err := netlink.LinkByName(hostInterface.Name)
if err != nil {
return err
}
if !n.DisableContainerInterface {
// check bridge port state
retries := []int{0, 50, 500, 1000, 1000}
for idx, sleep := range retries {
time.Sleep(time.Duration(sleep) * time.Millisecond)
hostVeth, err = netlink.LinkByName(hostInterface.Name)
if err != nil {
return err
}
if hostVeth.Attrs().OperState == netlink.OperUp {
break
}
if idx == len(retries)-1 {
return fmt.Errorf("bridge port in error state: %s", hostVeth.Attrs().OperState)
}
}
}
// In certain circumstances, the host-side of the veth may change addrs
hostInterface.Mac = hostVeth.Attrs().HardwareAddr.String()
// Refetch the bridge since its MAC address may change when the first
// veth is added or after its IP address is set
br, err = bridgeByName(n.BrName)
@ -734,29 +611,18 @@ func cmdAdd(args *skel.CmdArgs) error {
}
brInterface.Mac = br.Attrs().HardwareAddr.String()
result.DNS = n.DNS
// Return an error requested by testcases, if any
if debugPostIPAMError != nil {
return debugPostIPAMError
}
// Use incoming DNS settings if provided, otherwise use the
// settings that were already configured by the IPAM plugin
if dnsConfSet(n.DNS) {
result.DNS = n.DNS
}
success = true
return types.PrintResult(result, cniVersion)
}
func dnsConfSet(dnsConf types.DNS) bool {
return dnsConf.Nameservers != nil ||
dnsConf.Search != nil ||
dnsConf.Options != nil ||
dnsConf.Domain != ""
}
func cmdDel(args *skel.CmdArgs) error {
n, _, err := loadNetConf(args.StdinData, args.Args)
if err != nil {
@ -790,6 +656,7 @@ func cmdDel(args *skel.CmdArgs) error {
}
return err
})
if err != nil {
// if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times
// so don't return an error if the device is already removed.
@ -839,6 +706,7 @@ type cniBridgeIf struct {
}
func validateInterface(intf current.Interface, expectInSb bool) (cniBridgeIf, netlink.Link, error) {
ifFound := cniBridgeIf{found: false}
if intf.Name == "" {
return ifFound, nil, fmt.Errorf("Interface name missing ")
@ -863,6 +731,7 @@ func validateInterface(intf current.Interface, expectInSb bool) (cniBridgeIf, ne
}
func validateCniBrInterface(intf current.Interface, n *NetConf) (cniBridgeIf, error) {
brFound, link, err := validateInterface(intf, false)
if err != nil {
return brFound, err
@ -894,6 +763,7 @@ func validateCniBrInterface(intf current.Interface, n *NetConf) (cniBridgeIf, er
}
func validateCniVethInterface(intf *current.Interface, brIf cniBridgeIf, contIf cniBridgeIf) (cniBridgeIf, error) {
vethFound, link, err := validateInterface(*intf, false)
if err != nil {
return vethFound, err
@ -937,6 +807,7 @@ func validateCniVethInterface(intf *current.Interface, brIf cniBridgeIf, contIf
}
func validateCniContainerInterface(intf current.Interface) (cniBridgeIf, error) {
vethFound, link, err := validateInterface(intf, true)
if err != nil {
return vethFound, err
@ -965,6 +836,7 @@ func validateCniContainerInterface(intf current.Interface) (cniBridgeIf, error)
}
func cmdCheck(args *skel.CmdArgs) error {
n, _, err := loadNetConf(args.StdinData, args.Args)
if err != nil {
return err
@ -1071,7 +943,7 @@ func cmdCheck(args *skel.CmdArgs) error {
}
// Check prevResults for ips, routes and dns against values found in the container
return netns.Do(func(_ ns.NetNS) error {
if err := netns.Do(func(_ ns.NetNS) error {
err = ip.ValidateExpectedInterfaceIPs(args.IfName, result.IPs)
if err != nil {
return err
@ -1082,7 +954,11 @@ func cmdCheck(args *skel.CmdArgs) error {
return err
}
return nil
})
}); err != nil {
return err
}
return nil
}
func uniqueID(containerID, cniIface string) string {

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -1,40 +0,0 @@
// Copyright 2022 Arista Networks
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main_test
import (
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
)
var pathToLoPlugin string
func TestLoopback(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "plugins/main/dummy")
}
var _ = BeforeSuite(func() {
var err error
pathToLoPlugin, err = gexec.Build("github.com/containernetworking/plugins/plugins/main/dummy")
Expect(err).NotTo(HaveOccurred())
})
var _ = AfterSuite(func() {
gexec.CleanupBuildArtifacts()
})

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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