From 2a6e9cfeb158e29df821a0364d7923c5f0c489b6 Mon Sep 17 00:00:00 2001 From: Xu Liu Date: Wed, 23 Jun 2021 09:45:39 +0800 Subject: [PATCH] Add WireGuard client for tunnel traffic encryption. This PR is a part of #2243. Currently WireGuard only support with antrea encap mode. Signed-off-by: Xu Liu --- build/images/scripts/install_cni | 3 + build/images/wireguard-go/Dockerfile | 15 + build/yamls/antrea-aks.yml | 15 +- build/yamls/antrea-eks.yml | 15 +- build/yamls/antrea-gke.yml | 15 +- build/yamls/antrea-ipsec.yml | 15 +- build/yamls/antrea.yml | 15 +- build/yamls/base/conf/antrea-agent.conf | 7 + build/yamls/patches/kind/wireguardGo.yml | 41 +++ ci/kind/kind-setup.sh | 9 + ci/kind/test-e2e-kind.sh | 4 +- cmd/antrea-agent/agent.go | 8 +- cmd/antrea-agent/config.go | 6 + cmd/antrea-agent/options_windows.go | 4 +- cmd/antrea-agent/options_windows_test.go | 5 + go.mod | 9 +- go.sum | 62 +++- hack/generate-manifest.sh | 14 + pkg/agent/agent.go | 65 +++- pkg/agent/config/node_config.go | 9 +- .../noderoute/node_route_controller.go | 75 +++- .../noderoute/node_route_controller_test.go | 2 +- pkg/agent/openflow/client.go | 26 +- pkg/agent/openflow/client_test.go | 7 +- pkg/agent/openflow/network_policy_test.go | 3 + pkg/agent/openflow/pipeline.go | 32 +- pkg/agent/openflow/pipeline_windows.go | 4 +- pkg/agent/openflow/testing/mock_openflow.go | 2 +- pkg/agent/types/annotations.go | 2 + pkg/agent/wireguard/client_linux.go | 332 ++++++++++++++++++ pkg/agent/wireguard/client_test.go | 273 ++++++++++++++ pkg/agent/wireguard/client_windows.go | 27 ++ pkg/agent/wireguard/interface.go | 26 ++ plugins/octant/go.sum | 48 ++- test/e2e/connectivity_test.go | 17 +- test/e2e/fixtures.go | 2 +- test/e2e/framework.go | 40 ++- test/e2e/ipsec_test.go | 8 +- test/e2e/performance_test.go | 2 +- test/e2e/wireguard_test.go | 65 ++++ test/integration/agent/openflow_test.go | 10 +- 41 files changed, 1228 insertions(+), 111 deletions(-) create mode 100644 build/images/wireguard-go/Dockerfile create mode 100644 build/yamls/patches/kind/wireguardGo.yml create mode 100644 pkg/agent/wireguard/client_linux.go create mode 100644 pkg/agent/wireguard/client_test.go create mode 100644 pkg/agent/wireguard/client_windows.go create mode 100644 pkg/agent/wireguard/interface.go create mode 100644 test/e2e/wireguard_test.go diff --git a/build/images/scripts/install_cni b/build/images/scripts/install_cni index 336e53cb031..09680e8e878 100755 --- a/build/images/scripts/install_cni +++ b/build/images/scripts/install_cni @@ -33,5 +33,8 @@ install -m 755 /opt/cni/bin/whereabouts /host/opt/cni/bin/whereabouts # Load the OVS kernel module modprobe openvswitch || (echo "Failed to load the OVS kernel module from the container, try running 'modprobe openvswitch' on your Nodes"; exit 1) +# Load the WireGuard kernel module +modprobe wireguard || (echo "Failed to load the WireGuard kernel module from the container.") + # Change the default permissions of the run directory. chmod 0750 /var/run/antrea diff --git a/build/images/wireguard-go/Dockerfile b/build/images/wireguard-go/Dockerfile new file mode 100644 index 00000000000..6d086f877c6 --- /dev/null +++ b/build/images/wireguard-go/Dockerfile @@ -0,0 +1,15 @@ +FROM golang:1.16 as builder + +RUN git clone https://git.zx2c4.com/wireguard-go && \ + cd wireguard-go && \ + make && \ + make install + +RUN git clone https://git.zx2c4.com/wireguard-tools && \ + cd wireguard-tools && \ + cd src && \ + make && \ + make install + +FROM ubuntu:20.04 +COPY --from=builder /usr/bin/wireguard-go /usr/bin/wg /usr/bin/ diff --git a/build/yamls/antrea-aks.yml b/build/yamls/antrea-aks.yml index 5c8c59ef4c4..e53c637b417 100644 --- a/build/yamls/antrea-aks.yml +++ b/build/yamls/antrea-aks.yml @@ -3718,6 +3718,13 @@ data: # for the GRE tunnel type. #enableIPSecTunnel: false + # Whether or not to enable WireGuard encryption of tunnel traffic. WireGuard encryption is only supported + # for encap mode. + #enableWireGuardTunnel: false + + # The port for WireGuard to receive traffic. + #wireGuardPort: 51850 + # ClusterIP CIDR range for IPv6 Services. It's required when using kube-proxy to provide IPv6 Service in a Dual-Stack # cluster or an IPv6 only cluster. The value should be the same as the configuration for kube-apiserver specified by # --service-cluster-ip-range. When AntreaProxy is enabled, this parameter is not needed. @@ -3861,7 +3868,7 @@ metadata: annotations: {} labels: app: antrea - name: antrea-config-t9hc8tf75d + name: antrea-config-5t7m5kt44b namespace: kube-system --- apiVersion: v1 @@ -3932,7 +3939,7 @@ spec: fieldRef: fieldPath: spec.serviceAccountName - name: ANTREA_CONFIG_MAP_NAME - value: antrea-config-t9hc8tf75d + value: antrea-config-5t7m5kt44b image: projects.registry.vmware.com/antrea/antrea-ubuntu:latest imagePullPolicy: IfNotPresent livenessProbe: @@ -3983,7 +3990,7 @@ spec: key: node-role.kubernetes.io/master volumes: - configMap: - name: antrea-config-t9hc8tf75d + name: antrea-config-5t7m5kt44b name: antrea-config - name: antrea-controller-tls secret: @@ -4279,7 +4286,7 @@ spec: operator: Exists volumes: - configMap: - name: antrea-config-t9hc8tf75d + name: antrea-config-5t7m5kt44b name: antrea-config - hostPath: path: /etc/cni/net.d diff --git a/build/yamls/antrea-eks.yml b/build/yamls/antrea-eks.yml index 6b0a25e656c..07f4ebcfebd 100644 --- a/build/yamls/antrea-eks.yml +++ b/build/yamls/antrea-eks.yml @@ -3718,6 +3718,13 @@ data: # for the GRE tunnel type. #enableIPSecTunnel: false + # Whether or not to enable WireGuard encryption of tunnel traffic. WireGuard encryption is only supported + # for encap mode. + #enableWireGuardTunnel: false + + # The port for WireGuard to receive traffic. + #wireGuardPort: 51850 + # ClusterIP CIDR range for IPv6 Services. It's required when using kube-proxy to provide IPv6 Service in a Dual-Stack # cluster or an IPv6 only cluster. The value should be the same as the configuration for kube-apiserver specified by # --service-cluster-ip-range. When AntreaProxy is enabled, this parameter is not needed. @@ -3861,7 +3868,7 @@ metadata: annotations: {} labels: app: antrea - name: antrea-config-t9hc8tf75d + name: antrea-config-5t7m5kt44b namespace: kube-system --- apiVersion: v1 @@ -3932,7 +3939,7 @@ spec: fieldRef: fieldPath: spec.serviceAccountName - name: ANTREA_CONFIG_MAP_NAME - value: antrea-config-t9hc8tf75d + value: antrea-config-5t7m5kt44b image: projects.registry.vmware.com/antrea/antrea-ubuntu:latest imagePullPolicy: IfNotPresent livenessProbe: @@ -3983,7 +3990,7 @@ spec: key: node-role.kubernetes.io/master volumes: - configMap: - name: antrea-config-t9hc8tf75d + name: antrea-config-5t7m5kt44b name: antrea-config - name: antrea-controller-tls secret: @@ -4281,7 +4288,7 @@ spec: operator: Exists volumes: - configMap: - name: antrea-config-t9hc8tf75d + name: antrea-config-5t7m5kt44b name: antrea-config - hostPath: path: /etc/cni/net.d diff --git a/build/yamls/antrea-gke.yml b/build/yamls/antrea-gke.yml index 21e82a208eb..8491031d1d6 100644 --- a/build/yamls/antrea-gke.yml +++ b/build/yamls/antrea-gke.yml @@ -3718,6 +3718,13 @@ data: # for the GRE tunnel type. #enableIPSecTunnel: false + # Whether or not to enable WireGuard encryption of tunnel traffic. WireGuard encryption is only supported + # for encap mode. + #enableWireGuardTunnel: false + + # The port for WireGuard to receive traffic. + #wireGuardPort: 51850 + # ClusterIP CIDR range for IPv6 Services. It's required when using kube-proxy to provide IPv6 Service in a Dual-Stack # cluster or an IPv6 only cluster. The value should be the same as the configuration for kube-apiserver specified by # --service-cluster-ip-range. When AntreaProxy is enabled, this parameter is not needed. @@ -3861,7 +3868,7 @@ metadata: annotations: {} labels: app: antrea - name: antrea-config-9g829tktd6 + name: antrea-config-d8b7t88629 namespace: kube-system --- apiVersion: v1 @@ -3932,7 +3939,7 @@ spec: fieldRef: fieldPath: spec.serviceAccountName - name: ANTREA_CONFIG_MAP_NAME - value: antrea-config-9g829tktd6 + value: antrea-config-d8b7t88629 image: projects.registry.vmware.com/antrea/antrea-ubuntu:latest imagePullPolicy: IfNotPresent livenessProbe: @@ -3983,7 +3990,7 @@ spec: key: node-role.kubernetes.io/master volumes: - configMap: - name: antrea-config-9g829tktd6 + name: antrea-config-d8b7t88629 name: antrea-config - name: antrea-controller-tls secret: @@ -4282,7 +4289,7 @@ spec: path: /home/kubernetes/bin name: host-cni-bin - configMap: - name: antrea-config-9g829tktd6 + name: antrea-config-d8b7t88629 name: antrea-config - hostPath: path: /etc/cni/net.d diff --git a/build/yamls/antrea-ipsec.yml b/build/yamls/antrea-ipsec.yml index c8958d062fc..1b554e3d7c0 100644 --- a/build/yamls/antrea-ipsec.yml +++ b/build/yamls/antrea-ipsec.yml @@ -3718,6 +3718,13 @@ data: # for the GRE tunnel type. enableIPSecTunnel: true + # Whether or not to enable WireGuard encryption of tunnel traffic. WireGuard encryption is only supported + # for encap mode. + #enableWireGuardTunnel: false + + # The port for WireGuard to receive traffic. + #wireGuardPort: 51850 + # ClusterIP CIDR range for Services. It's required when AntreaProxy is not enabled, and should be # set to the same value as the one specified by --service-cluster-ip-range for kube-apiserver. When # AntreaProxy is enabled, this parameter is not needed and will be ignored if provided. @@ -3866,7 +3873,7 @@ metadata: annotations: {} labels: app: antrea - name: antrea-config-h5kbhh859d + name: antrea-config-fk8d988bd9 namespace: kube-system --- apiVersion: v1 @@ -3946,7 +3953,7 @@ spec: fieldRef: fieldPath: spec.serviceAccountName - name: ANTREA_CONFIG_MAP_NAME - value: antrea-config-h5kbhh859d + value: antrea-config-fk8d988bd9 image: projects.registry.vmware.com/antrea/antrea-ubuntu:latest imagePullPolicy: IfNotPresent livenessProbe: @@ -3997,7 +4004,7 @@ spec: key: node-role.kubernetes.io/master volumes: - configMap: - name: antrea-config-h5kbhh859d + name: antrea-config-fk8d988bd9 name: antrea-config - name: antrea-controller-tls secret: @@ -4328,7 +4335,7 @@ spec: operator: Exists volumes: - configMap: - name: antrea-config-h5kbhh859d + name: antrea-config-fk8d988bd9 name: antrea-config - hostPath: path: /etc/cni/net.d diff --git a/build/yamls/antrea.yml b/build/yamls/antrea.yml index b75e88e6f1b..f7da39187ed 100644 --- a/build/yamls/antrea.yml +++ b/build/yamls/antrea.yml @@ -3718,6 +3718,13 @@ data: # for the GRE tunnel type. #enableIPSecTunnel: false + # Whether or not to enable WireGuard encryption of tunnel traffic. WireGuard encryption is only supported + # for encap mode. + #enableWireGuardTunnel: false + + # The port for WireGuard to receive traffic. + #wireGuardPort: 51850 + # ClusterIP CIDR range for Services. It's required when AntreaProxy is not enabled, and should be # set to the same value as the one specified by --service-cluster-ip-range for kube-apiserver. When # AntreaProxy is enabled, this parameter is not needed and will be ignored if provided. @@ -3866,7 +3873,7 @@ metadata: annotations: {} labels: app: antrea - name: antrea-config-cbfh568k9m + name: antrea-config-m8b78df98b namespace: kube-system --- apiVersion: v1 @@ -3937,7 +3944,7 @@ spec: fieldRef: fieldPath: spec.serviceAccountName - name: ANTREA_CONFIG_MAP_NAME - value: antrea-config-cbfh568k9m + value: antrea-config-m8b78df98b image: projects.registry.vmware.com/antrea/antrea-ubuntu:latest imagePullPolicy: IfNotPresent livenessProbe: @@ -3988,7 +3995,7 @@ spec: key: node-role.kubernetes.io/master volumes: - configMap: - name: antrea-config-cbfh568k9m + name: antrea-config-m8b78df98b name: antrea-config - name: antrea-controller-tls secret: @@ -4284,7 +4291,7 @@ spec: operator: Exists volumes: - configMap: - name: antrea-config-cbfh568k9m + name: antrea-config-m8b78df98b name: antrea-config - hostPath: path: /etc/cni/net.d diff --git a/build/yamls/base/conf/antrea-agent.conf b/build/yamls/base/conf/antrea-agent.conf index 01591e9837f..351a103e3c8 100644 --- a/build/yamls/base/conf/antrea-agent.conf +++ b/build/yamls/base/conf/antrea-agent.conf @@ -81,6 +81,13 @@ featureGates: # for the GRE tunnel type. #enableIPSecTunnel: false +# Whether or not to enable WireGuard encryption of tunnel traffic. WireGuard encryption is only supported +# for encap mode. +#enableWireGuardTunnel: false + +# The port for WireGuard to receive traffic. +#wireGuardPort: 51850 + # ClusterIP CIDR range for Services. It's required when AntreaProxy is not enabled, and should be # set to the same value as the one specified by --service-cluster-ip-range for kube-apiserver. When # AntreaProxy is enabled, this parameter is not needed and will be ignored if provided. diff --git a/build/yamls/patches/kind/wireguardGo.yml b/build/yamls/patches/kind/wireguardGo.yml new file mode 100644 index 00000000000..a6eb33b0026 --- /dev/null +++ b/build/yamls/patches/kind/wireguardGo.yml @@ -0,0 +1,41 @@ +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: antrea-agent +spec: + template: + spec: + containers: + - name: wireguard + command: + - wireguard-go + args: + - -f + - antrea-wg + image: xliuxu/wireguard-go:latest + imagePullPolicy: IfNotPresent + resources: + requests: + cpu: 100m + securityContext: + capabilities: + add: + - NET_ADMIN + volumeMounts: + - name: host-var-run-wireguard + mountPath: /var/run/wireguard + - name: dev-tun + mountPath: /dev/net/tun + - name: antrea-agent + volumeMounts: + - name: host-var-run-wireguard + mountPath: /var/run/wireguard + volumes: + - name: host-var-run-wireguard + hostPath: + path: /var/run/wireguard + type: DirectoryOrCreate + - name: dev-tun + hostPath: + path: /dev/net/tun + type: CharDevice diff --git a/ci/kind/kind-setup.sh b/ci/kind/kind-setup.sh index c0d38d72e20..4d71862fc5a 100755 --- a/ci/kind/kind-setup.sh +++ b/ci/kind/kind-setup.sh @@ -28,6 +28,7 @@ SUBNETS="" ENCAP_MODE="" PROXY=true PROMETHEUS=false +WIREGUARDGO=false THIS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" @@ -54,6 +55,7 @@ where: --images: specifies images loaded to kind cluster, default is $IMAGES --subnets: a subnet creates a separate docker bridge network (named 'antrea-') with assigned subnet that worker nodes may connect to. Default is empty: all worker Node connected to default docker bridge network created by Kind. + --wireguard: enable wireguard encryption in userspace. " function print_usage { @@ -274,6 +276,9 @@ EOF if [[ $PROXY == false ]]; then cmd+=" --no-proxy" fi + if [[ $WIREGUARDGO == true ]]; then + cmd+=" --wireguard-go" + fi echo "$cmd --kind $(get_encap_mode) | kubectl apply --context kind-$CLUSTER_NAME -f -" eval "$cmd --kind $(get_encap_mode) | kubectl apply --context kind-$CLUSTER_NAME -f -" @@ -348,6 +353,10 @@ while [[ $# -gt 0 ]] NUM_WORKERS="$2" shift 2 ;; + --wireguard-go) + WIREGUARDGO=true + shift + ;; help) print_usage exit 0 diff --git a/ci/kind/test-e2e-kind.sh b/ci/kind/test-e2e-kind.sh index 2dc362c64f8..50493790a7f 100755 --- a/ci/kind/test-e2e-kind.sh +++ b/ci/kind/test-e2e-kind.sh @@ -103,7 +103,7 @@ else manifest_args="$manifest_args --no-np" fi -COMMON_IMAGES_LIST=("gcr.io/kubernetes-e2e-test-images/agnhost:2.8" "projects.registry.vmware.com/library/busybox" "projects.registry.vmware.com/antrea/nginx" "projects.registry.vmware.com/antrea/perftool" "projects.registry.vmware.com/antrea/ipfix-collector:v0.5.3") +COMMON_IMAGES_LIST=("gcr.io/kubernetes-e2e-test-images/agnhost:2.8" "projects.registry.vmware.com/library/busybox" "projects.registry.vmware.com/antrea/nginx" "projects.registry.vmware.com/antrea/perftool" "projects.registry.vmware.com/antrea/ipfix-collector:v0.5.2" "xliuxu/wireguard-go:latest") for image in "${COMMON_IMAGES_LIST[@]}"; do for i in `seq 3`; do docker pull $image && break @@ -130,9 +130,11 @@ function run_test { if $coverage; then $YML_CMD --kind --encap-mode $current_mode $manifest_args | docker exec -i kind-control-plane dd of=/root/antrea-coverage.yml + $YML_CMD --kind --encap-mode $current_mode $manifest_args --wireguard-go | docker exec -i kind-control-plane dd of=/root/antrea-wireguard-coverage.yml $FLOWAGGREGATOR_YML_CMD --coverage | docker exec -i kind-control-plane dd of=/root/flow-aggregator-coverage.yml else $YML_CMD --kind --encap-mode $current_mode $manifest_args | docker exec -i kind-control-plane dd of=/root/antrea.yml + $YML_CMD --kind --encap-mode $current_mode $manifest_args --wireguard-go | docker exec -i kind-control-plane dd of=/root/antrea-wireguard.yml $FLOWAGGREGATOR_YML_CMD | docker exec -i kind-control-plane dd of=/root/flow-aggregator.yml fi sleep 1 diff --git a/cmd/antrea-agent/agent.go b/cmd/antrea-agent/agent.go index 904185587da..3afa5189039 100644 --- a/cmd/antrea-agent/agent.go +++ b/cmd/antrea-agent/agent.go @@ -115,7 +115,10 @@ func run(o *Options) error { networkConfig := &config.NetworkConfig{ TunnelType: ovsconfig.TunnelType(o.config.TunnelType), TrafficEncapMode: encapMode, - EnableIPSecTunnel: o.config.EnableIPSecTunnel} + EnableIPSecTunnel: o.config.EnableIPSecTunnel, + EnableWireGuard: o.config.EnableWireGuard, + WireGuardPort: o.config.WireGuardPort, + } routeClient, err := route.NewClient(serviceCIDRNet, networkConfig, o.config.NoSNAT) if err != nil { @@ -162,7 +165,8 @@ func run(o *Options) error { routeClient, ifaceStore, networkConfig, - nodeConfig) + nodeConfig, + agentInitializer.GetWireGuardClient()) var proxier proxy.Proxier if features.DefaultFeatureGate.Enabled(features.AntreaProxy) { diff --git a/cmd/antrea-agent/config.go b/cmd/antrea-agent/config.go index 9e3f13725ba..7e4917a8f41 100644 --- a/cmd/antrea-agent/config.go +++ b/cmd/antrea-agent/config.go @@ -95,6 +95,12 @@ type AgentConfig struct { // through an environment variable: ANTREA_IPSEC_PSK. // Defaults to false. EnableIPSecTunnel bool `yaml:"enableIPSecTunnel,omitempty"` + // Whether or not to enable WireGuard encryption for Pod traffic across Nodes. Currently wireguard + // is supported only with encap mode. + EnableWireGuard bool `yaml:"enableWireGuardTunnel,omitempty"` + // WireGuardPort s the port for the WireGuard to receive traffic. + // Defaults to 51850 + WireGuardPort int `yaml:"wireGuardPort,omitempty"` // APIPort is the port for the antrea-agent APIServer to serve on. // Defaults to 10350. APIPort int `yaml:"apiPort,omitempty"` diff --git a/cmd/antrea-agent/options_windows.go b/cmd/antrea-agent/options_windows.go index 1e72a509201..d34c9236193 100644 --- a/cmd/antrea-agent/options_windows.go +++ b/cmd/antrea-agent/options_windows.go @@ -51,7 +51,9 @@ func (o *Options) checkUnsupportedFeatures() error { if o.config.EnableIPSecTunnel { unsupported = append(unsupported, "IPsecTunnel") } - + if o.config.EnableWireGuard { + unsupported = append(unsupported, "WireGuard") + } if unsupported != nil { return fmt.Errorf("unsupported features on Windows: {%s}", strings.Join(unsupported, ", ")) } diff --git a/cmd/antrea-agent/options_windows_test.go b/cmd/antrea-agent/options_windows_test.go index f27632b281b..920431e8a67 100644 --- a/cmd/antrea-agent/options_windows_test.go +++ b/cmd/antrea-agent/options_windows_test.go @@ -69,6 +69,11 @@ func TestCheckUnsupportedFeatures(t *testing.T) { AgentConfig{EnableIPSecTunnel: true}, false, }, + { + "WireGuard tunnel", + AgentConfig{EnableWireGuard: true}, + false, + }, { "hybrid mode and GRE tunnel", AgentConfig{TrafficEncapMode: config.TrafficEncapModeHybrid.String(), TunnelType: ovsconfig.GRETunnel}, diff --git a/go.mod b/go.mod index 6c6b1d438ca..a7de248e553 100644 --- a/go.mod +++ b/go.mod @@ -41,15 +41,16 @@ require ( github.com/streamrail/concurrent-map v0.0.0-20160823150647-8bf1e9bacbf6 // indirect github.com/stretchr/testify v1.6.1 github.com/ti-mo/conntrack v0.3.0 - github.com/vishvananda/netlink v1.1.0 + github.com/vishvananda/netlink v1.1.1-0.20210510164352-d17758a128bf github.com/vmware/go-ipfix v0.5.3 - golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 + golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 golang.org/x/mod v0.4.0 - golang.org/x/net v0.0.0-20210224082022-3d97a244fca7 + golang.org/x/net v0.0.0-20210504132125-bbd867fde50d golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 - golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073 + golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6 golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba + golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210506160403-92e472f520a5 google.golang.org/grpc v1.27.1 gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index b9472712de5..567c920b315 100644 --- a/go.sum +++ b/go.sum @@ -309,6 +309,7 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -379,9 +380,16 @@ github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA= +github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= -github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4 h1:nwOc1YaOrYJ37sEBrtWZrdqzK22hiJs3GpDmP3sR2Yw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= +github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= +github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw= +github.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs= +github.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA= +github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b h1:c3NTyLNozICy8B4mlMXemD3z/gXgQzVXZS/HqT+i3do= +github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= @@ -439,11 +447,23 @@ github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vq github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43 h1:WgyLFv10Ov49JAQI/ZLUkCZ7VJS3r74hwFIGXJsgZlY= +github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo= +github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0= +github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= -github.com/mdlayher/netlink v1.1.0 h1:mpdLgm+brq10nI9zM1BpX1kpDbh3NLl3RSnVq6ZSkfg= github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= +github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= +github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8= +github.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= +github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= +github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys= +github.com/mdlayher/netlink v1.4.0 h1:n3ARR+Fm0dDv37dj5wSWZXDKcy+U0zwcXS3zKMnSiT0= +github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws= +github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -608,11 +628,13 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= -github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netlink v1.1.1-0.20210510164352-d17758a128bf h1:JMdq3oWN6LQKfRpwVfjwuaZcPN4vGdVBzvOU2mzUXd8= +github.com/vishvananda/netlink v1.1.1-0.20210510164352-d17758a128bf/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vmware/go-ipfix v0.5.3 h1:ZJTn5vQd6W0WWt05gm+nNFjjgbgVfUkvywdxKmWj4uM= github.com/vmware/go-ipfix v0.5.3/go.mod h1:SF6BrZTPvoVdzgmjJvshoegBVbicn4xWlkoCNADab6E= github.com/wenyingd/ofnet v0.0.0-20210526054554-3e71e19fd0cf h1:EEGpnM6W07pq2nKdqk+lig1Qit5f8eUe+Vt1ditTLgk= @@ -661,8 +683,9 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e h1:8foAy0aoO5GkqCvAEJ4VC4P3zksTg4X4aJCDpZzmgQI= +golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -732,10 +755,16 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210224082022-3d97a244fca7 h1:OgUuv8lsRpBibGNbSizVwKWlysjaNzmC9gYMhPVfqFM= +golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210504132125-bbd867fde50d h1:nTDGCTeAu2LhcsHTRzjyIUbZHCJ4QePArsm27Hka0UM= +golang.org/x/net v0.0.0-20210504132125-bbd867fde50d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -788,18 +817,30 @@ golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073 h1:8qxJSnu+7dRq6upnbntrmriWByIakBuct5OM/MdQC1M= +golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6 h1:cdsMqa2nXzqlgs183pHxtvoVwU7CyzaCTAUOg94af4c= +golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= @@ -809,8 +850,9 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -864,6 +906,10 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.zx2c4.com/wireguard v0.0.0-20210427022245-097af6e1351b h1:XDLXhn7ryprJVo+Lpkiib6CIuXE2031GDwtfEm7vLjI= +golang.zx2c4.com/wireguard v0.0.0-20210427022245-097af6e1351b/go.mod h1:a057zjmoc00UN7gVkaJt2sXVK523kMJcogDTEvPIasg= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210506160403-92e472f520a5 h1:LpEwXnbN4q2EIPkqbG9KHBUrducJYDOOdL+eMcJAlFo= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210506160403-92e472f520a5/go.mod h1:+1XihzyZUBJcSc5WO9SwNA7v26puQwOEDwanaxfNXPQ= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= diff --git a/hack/generate-manifest.sh b/hack/generate-manifest.sh index 1ecdf687406..c4b590159bf 100755 --- a/hack/generate-manifest.sh +++ b/hack/generate-manifest.sh @@ -27,6 +27,7 @@ Generate a YAML manifest for Antrea using Kustomize and print it to stdout. --kind Generate a manifest appropriate for running Antrea in a Kind cluster --cloud Generate a manifest appropriate for running Antrea in Public Cloud --ipsec Generate a manifest with IPSec encryption of tunnel traffic enabled + --wireguard-go Generate a manifest with userspace wireguard-go encryption for tunnel traffic --all-features Generate a manifest with all alpha features enabled --no-proxy Generate a manifest with Antrea proxy disabled --no-legacy-crd Generate a manifest without legacy CRD mirroring support enabled @@ -67,6 +68,7 @@ function print_help { MODE="dev" KIND=false IPSEC=false +WIREGUARDGO=false ALLFEATURES=false PROXY=true LEGACY_CRD=true @@ -109,6 +111,10 @@ case $key in --ipsec) IPSEC=true shift + ;; + --wireguard-go) + WIREGUARDGO=true + shift ;; --all-features) ALLFEATURES=true @@ -265,6 +271,10 @@ if $IPSEC; then sed -i.bak -E "s/^[[:space:]]*#[[:space:]]*tunnelType[[:space:]]*:[[:space:]]*[a-z]+[[:space:]]*$/tunnelType: gre/" antrea-agent.conf fi +if $WIREGUARDGO; then + sed -i.bak -E "s/^[[:space:]]*#[[:space:]]*enableWireGuardTunnel[[:space:]]*:[[:space:]]*[a-z]+[[:space:]]*$/enableWireGuardTunnel: true/" antrea-agent.conf +fi + if $ALLFEATURES; then sed -i.bak -E "s/^[[:space:]]*#[[:space:]]*AntreaPolicy[[:space:]]*:[[:space:]]*[a-z]+[[:space:]]*$/ AntreaPolicy: true/" antrea-agent.conf sed -i.bak -E "s/^[[:space:]]*#[[:space:]]*FlowExporter[[:space:]]*:[[:space:]]*[a-z]+[[:space:]]*$/ FlowExporter: true/" antrea-agent.conf @@ -407,6 +417,10 @@ if $KIND; then else $KUSTOMIZE edit add patch --path startAgent.yml fi + # add wireguard sidecar container to run wireguard in userspace + if $WIREGUARDGO; then + $KUSTOMIZE edit add patch --path wireguardGo.yml + fi # change initContainer script and remove SYS_MODULE capability $KUSTOMIZE edit add patch --path installCni.yml diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go index d059c9b30f1..7b0eabe8dad 100644 --- a/pkg/agent/agent.go +++ b/pkg/agent/agent.go @@ -41,6 +41,7 @@ import ( "antrea.io/antrea/pkg/agent/route" "antrea.io/antrea/pkg/agent/types" "antrea.io/antrea/pkg/agent/util" + "antrea.io/antrea/pkg/agent/wireguard" "antrea.io/antrea/pkg/ovs/ovsconfig" "antrea.io/antrea/pkg/util/env" "antrea.io/antrea/pkg/util/k8s" @@ -66,6 +67,7 @@ type Initializer struct { ovsBridgeClient ovsconfig.OVSBridgeClient ofClient openflow.Client routeClient route.Interface + wireguardClient wireguard.Interface ifaceStore interfacestore.InterfaceStore ovsBridge string hostGateway string // name of gateway port on the OVS bridge @@ -119,6 +121,11 @@ func (i *Initializer) GetNodeConfig() *config.NodeConfig { return i.nodeConfig } +// GetNodeConfig returns the NodeConfig. +func (i *Initializer) GetWireGuardClient() wireguard.Interface { + return i.wireguardClient +} + // setupOVSBridge sets up the OVS bridge and create host gateway interface and tunnel port func (i *Initializer) setupOVSBridge() error { if err := i.ovsBridgeClient.Create(); err != nil { @@ -224,6 +231,10 @@ func (i *Initializer) Initialize() error { return err } + if err := i.initializeWireGuard(); err != nil { + return err + } + if err := i.prepareHostNetwork(); err != nil { return err } @@ -298,7 +309,7 @@ func (i *Initializer) initOpenFlowPipeline() error { roundInfo := getRoundInfo(i.ovsBridgeClient) // Set up all basic flows. - ofConnCh, err := i.ofClient.Initialize(roundInfo, i.nodeConfig, i.networkConfig.TrafficEncapMode) + ofConnCh, err := i.ofClient.Initialize(roundInfo, i.nodeConfig, i.networkConfig) if err != nil { klog.Errorf("Failed to initialize openflow client: %v", err) return err @@ -769,6 +780,54 @@ func (i *Initializer) initializeIPSec() error { return nil } +// initializeWireguard checks if preconditions are met for using WireGuard and initialize WireGuard client. +func (i *Initializer) initializeWireGuard() error { + if !i.networkConfig.EnableWireGuard { + return nil + } + nodeName, err := env.GetNodeName() + if err != nil { + return err + } + node, err := i.client.CoreV1().Nodes().Get(context.TODO(), nodeName, metav1.GetOptions{}) + if err != nil { + klog.Errorf("Failed to get node from K8s with name %s: %v", nodeName, err) + return err + } + + ipAddr, err := k8s.GetNodeAddr(node) + if err != nil { + return fmt.Errorf("failed to obtain local IP address from K8s: %w", err) + } + _, localIntf, err := getIPNetDeviceFromIP(ipAddr) + if err != nil { + return err + } + mtu := localIntf.MTU - config.WireGuardOverhead + //create and initialize wireguard client + v4Enabled := config.IsIPv4Enabled(i.nodeConfig, i.networkConfig.TrafficEncapMode) + v6Enabled := config.IsIPv6Enabled(i.nodeConfig, i.networkConfig.TrafficEncapMode) + + var nodeIPv4, nodeIPv6 net.IP + nodeIPv4 = i.nodeConfig.NodeIPAddr.IP.To4() + + if nodeIPv4 == nil { + nodeIPv6 = i.nodeConfig.NodeIPAddr.IP.To16() + } + + wgClient, err := wireguard.New(nodeName, i.client, mtu, nodeIPv4, nodeIPv6, + v4Enabled, v6Enabled, i.networkConfig.WireGuardPort) + if err != nil { + return err + } + i.wireguardClient = wgClient + if err := i.wireguardClient.Init(); err != nil { + return err + } + return nil + +} + // readIPSecPSK reads the IPsec PSK value from environment variable ANTREA_IPSEC_PSK func (i *Initializer) readIPSecPSK() error { i.networkConfig.IPSecPSK = os.Getenv(ipsecPSKEnvKey) @@ -849,10 +908,14 @@ func (i *Initializer) getNodeMTU(localIntf *net.Interface) (int, error) { } else if i.networkConfig.TunnelType == ovsconfig.GRETunnel { mtu -= config.GREOverhead } + if i.nodeConfig.NodeIPAddr.IP.To4() == nil { mtu -= config.IPv6ExtraOverhead } } + if i.networkConfig.EnableWireGuard { + mtu -= config.WireGuardOverhead + } if i.networkConfig.EnableIPSecTunnel { mtu -= config.IPSecESPOverhead } diff --git a/pkg/agent/config/node_config.go b/pkg/agent/config/node_config.go index 786d325c9f1..345c6e5cac8 100644 --- a/pkg/agent/config/node_config.go +++ b/pkg/agent/config/node_config.go @@ -34,9 +34,10 @@ const ( ) const ( - VXLANOverhead = 50 - GeneveOverhead = 50 - GREOverhead = 38 + VXLANOverhead = 50 + GeneveOverhead = 50 + GREOverhead = 38 + WireGuardOverhead = 80 // IPsec ESP can add a maximum of 38 bytes to the packet including the ESP // header and trailer. IPSecESPOverhead = 38 @@ -105,6 +106,8 @@ type NetworkConfig struct { TrafficEncapMode TrafficEncapModeType TunnelType ovsconfig.TunnelType EnableIPSecTunnel bool + EnableWireGuard bool + WireGuardPort int IPSecPSK string } diff --git a/pkg/agent/controller/noderoute/node_route_controller.go b/pkg/agent/controller/noderoute/node_route_controller.go index 7d2dd79dd9a..300cca4f699 100644 --- a/pkg/agent/controller/noderoute/node_route_controller.go +++ b/pkg/agent/controller/noderoute/node_route_controller.go @@ -37,6 +37,7 @@ import ( "antrea.io/antrea/pkg/agent/route" "antrea.io/antrea/pkg/agent/types" "antrea.io/antrea/pkg/agent/util" + "antrea.io/antrea/pkg/agent/wireguard" "antrea.io/antrea/pkg/ovs/ovsconfig" utilip "antrea.io/antrea/pkg/util/ip" "antrea.io/antrea/pkg/util/k8s" @@ -73,7 +74,8 @@ type Controller struct { // installedNodes records routes and flows installation states of Nodes. // The key is the host name of the Node, the value is the nodeRouteInfo of the Node. // A node will be in the map after its flows and routes are installed successfully. - installedNodes cache.Indexer + installedNodes cache.Indexer + wireguardClient wireguard.Interface } // NewNodeRouteController instantiates a new Controller object which will process Node events @@ -86,7 +88,9 @@ func NewNodeRouteController( routeClient route.Interface, interfaceStore interfacestore.InterfaceStore, networkConfig *config.NetworkConfig, - nodeConfig *config.NodeConfig) *Controller { + nodeConfig *config.NodeConfig, + wireguardClient wireguard.Interface, +) *Controller { nodeInformer := informerFactory.Core().V1().Nodes() controller := &Controller{ kubeClient: kubeClient, @@ -101,6 +105,7 @@ func NewNodeRouteController( nodeListerSynced: nodeInformer.Informer().HasSynced, queue: workqueue.NewNamedRateLimitingQueue(workqueue.NewItemExponentialFailureRateLimiter(minRetryDelay, maxRetryDelay), "noderoute"), installedNodes: cache.NewIndexer(nodeRouteInfoKeyFunc, cache.Indexers{nodeRouteInfoPodCIDRIndexName: nodeRouteInfoPodCIDRIndexFunc}), + wireguardClient: wireguardClient, } nodeInformer.Informer().AddEventHandlerWithResyncPeriod( cache.ResourceEventHandlerFuncs{ @@ -133,11 +138,12 @@ func nodeRouteInfoPodCIDRIndexFunc(obj interface{}) ([]string, error) { // nodeRouteInfo is the route related information extracted from corev1.Node. type nodeRouteInfo struct { - nodeName string - podCIDRs []*net.IPNet - nodeIP net.IP - gatewayIP []net.IP - nodeMAC net.HardwareAddr + nodeName string + podCIDRs []*net.IPNet + nodeIP net.IP + gatewayIP []net.IP + nodeMAC net.HardwareAddr + NodeWireGuardPublicKey string } // enqueueNode adds an object to the controller work queue @@ -269,9 +275,30 @@ func (c *Controller) reconcile() error { if err := c.removeStaleTunnelPorts(); err != nil { return fmt.Errorf("error when removing stale tunnel ports: %v", err) } + if err := c.removeStaleWireGuardPeers(); err != nil { + return fmt.Errorf("error when removing stale wireguard peers: %v", err) + } return nil } +func (c *Controller) removeStaleWireGuardPeers() error { + if !c.networkConfig.EnableWireGuard { + return nil + } + nodes, err := c.nodeLister.List(labels.Everything()) + if err != nil { + return fmt.Errorf("error when listing Nodes: %v", err) + } + var currentPeerPublicKeys []string + for _, n := range nodes { + if pubkey, ok := n.Annotations[types.NodeWireGuardPublicKey]; ok { + currentPeerPublicKeys = append(currentPeerPublicKeys, pubkey) + } + } + return c.wireguardClient.RemoveStalePeers(currentPeerPublicKeys) + +} + // Run will create defaultWorkers workers (go routines) which will process the Node events from the // workqueue. func (c *Controller) Run(stopCh <-chan struct{}) { @@ -293,7 +320,7 @@ func (c *Controller) Run(stopCh <-chan struct{}) { } if err := c.reconcile(); err != nil { - klog.Errorf("Error during %s reconciliation", controllerName) + klog.Errorf("Error during %s reconciliation: %w", controllerName, err) } for i := 0; i < defaultWorkers; i++ { @@ -405,6 +432,13 @@ func (c *Controller) deleteNodeRoute(nodeName string) error { } c.interfaceStore.DeleteInterface(interfaceConfig) } + + if c.networkConfig.EnableWireGuard { + if err := c.wireguardClient.DeletePeer(nodeName); err != nil { + klog.Errorf("Failed to delete wireguard peer %s: %v", nodeName, err) + return fmt.Errorf("failed to delete wireguard peer %s", nodeName) + } + } return nil } @@ -415,8 +449,25 @@ func (c *Controller) addNodeRoute(nodeName string, node *corev1.Node) error { return fmt.Errorf("error when retrieving MAC of Node %s: %v", nodeName, err) } - nrInfo, installed, _ := c.installedNodes.GetByKey(nodeName) + peerNodeIP, err := k8s.GetNodeAddr(node) + if err != nil { + klog.Errorf("Failed to retrieve IP address of Node %s: %v", nodeName, err) + return nil + } + + if c.networkConfig.EnableWireGuard { + publickey, ok := node.Annotations[types.NodeWireGuardPublicKey] + if ok { + var ipv4, ipv6 net.IP + ipv4 = peerNodeIP.To4() + if ipv4 == nil { + ipv6 = peerNodeIP + } + c.wireguardClient.UpdatePeer(nodeName, publickey, ipv4, ipv6) + } + } + nrInfo, installed, _ := c.installedNodes.GetByKey(nodeName) if installed && nrInfo.(*nodeRouteInfo).nodeMAC.String() == peerNodeMAC.String() { // Route is already added for this Node and Node MAC isn't changed. return nil @@ -470,12 +521,6 @@ func (c *Controller) addNodeRoute(nodeName string, node *corev1.Node) error { podCIDRs = append(podCIDRs, peerPodCIDR) } - peerNodeIP, err := k8s.GetNodeAddr(node) - if err != nil { - klog.Errorf("Failed to retrieve IP address of Node %s: %v", nodeName, err) - return nil - } - ipsecTunOFPort := int32(0) if c.networkConfig.EnableIPSecTunnel { // Create a separate tunnel port for the Node, as OVS IPSec monitor needs to diff --git a/pkg/agent/controller/noderoute/node_route_controller_test.go b/pkg/agent/controller/noderoute/node_route_controller_test.go index 162055cbe7f..6def7243354 100644 --- a/pkg/agent/controller/noderoute/node_route_controller_test.go +++ b/pkg/agent/controller/noderoute/node_route_controller_test.go @@ -66,7 +66,7 @@ func newController(t *testing.T) (*fakeController, func()) { c := NewNodeRouteController(clientset, informerFactory, ofClient, ovsClient, routeClient, interfaceStore, &config.NetworkConfig{}, &config.NodeConfig{GatewayConfig: &config.GatewayConfig{ IPv4: nil, MAC: gatewayMAC, - }}) + }}, nil) return &fakeController{ Controller: c, clientset: clientset, diff --git a/pkg/agent/openflow/client.go b/pkg/agent/openflow/client.go index 1400a290e6f..db4536c785c 100644 --- a/pkg/agent/openflow/client.go +++ b/pkg/agent/openflow/client.go @@ -39,7 +39,7 @@ type Client interface { // be called to ensure that the set of OVS flows is correct. All flows programmed in the // switch which match the current round number will be deleted before any new flow is // installed. - Initialize(roundInfo types.RoundInfo, config *config.NodeConfig, encapMode config.TrafficEncapModeType) (<-chan struct{}, error) + Initialize(roundInfo types.RoundInfo, config *config.NodeConfig, networkconfig *config.NetworkConfig) (<-chan struct{}, error) // InstallGatewayFlows sets up flows related to an OVS gateway port, the gateway must exist. InstallGatewayFlows() error @@ -390,10 +390,14 @@ func (c *client) InstallNodeFlows(hostname string, // only work for IPv4 addresses. flows = append(flows, c.arpResponderFlow(peerGatewayIP, cookie.Node)) } - if c.encapMode.NeedsEncapToPeer(tunnelPeerIP, c.nodeConfig.NodeIPAddr) { + if c.networkConfig.TrafficEncapMode.NeedsEncapToPeer(tunnelPeerIP, c.nodeConfig.NodeIPAddr) { // tunnelPeerIP is the Node Internal Address. In a dual-stack setup, whether this address is an IPv4 address or an // IPv6 one is decided by the address family of Node Internal Address. - flows = append(flows, c.l3FwdFlowToRemote(localGatewayMAC, *peerPodCIDR, tunnelPeerIP, cookie.Node)) + if c.networkConfig.EnableWireGuard { + flows = append(flows, c.l3FwdFlowToRemoteViaWireguard(localGatewayMAC, *peerPodCIDR, tunnelPeerIP, cookie.Node)) + } else { + flows = append(flows, c.l3FwdFlowToRemote(localGatewayMAC, *peerPodCIDR, tunnelPeerIP, cookie.Node)) + } } else { flows = append(flows, c.l3FwdFlowToRemoteViaRouting(localGatewayMAC, remoteGatewayMAC, cookie.Node, tunnelPeerIP, peerPodCIDR)...) } @@ -437,7 +441,7 @@ func (c *client) InstallPodFlows(interfaceName string, podInterfaceIPs []net.IP, // Add L3 Routing flows to rewrite Pod's dst MAC for all validate IPs. flows = append(flows, c.l3FwdFlowToPod(localGatewayMAC, podInterfaceIPs, podInterfaceMAC, cookie.Pod)...) - if c.encapMode.IsNetworkPolicyOnly() { + if c.networkConfig.TrafficEncapMode.IsNetworkPolicyOnly() { // In policy-only mode, traffic to local Pod is routed based on destination IP. flows = append(flows, c.l3FwdFlowRouteToPod(podInterfaceIPs, podInterfaceMAC, cookie.Pod)..., @@ -663,7 +667,7 @@ func (c *client) initialize() error { if err := c.ofEntryOperations.AddAll(c.establishedConnectionFlows(cookie.Default)); err != nil { return fmt.Errorf("failed to install flows to skip established connections: %v", err) } - if c.encapMode.IsNetworkPolicyOnly() { + if c.networkConfig.TrafficEncapMode.IsNetworkPolicyOnly() { if err := c.setupPolicyOnlyFlows(); err != nil { return fmt.Errorf("failed to setup policy only flows: %w", err) } @@ -679,14 +683,14 @@ func (c *client) initialize() error { return nil } -func (c *client) Initialize(roundInfo types.RoundInfo, nodeConfig *config.NodeConfig, encapMode config.TrafficEncapModeType) (<-chan struct{}, error) { +func (c *client) Initialize(roundInfo types.RoundInfo, nodeConfig *config.NodeConfig, networkConfig *config.NetworkConfig) (<-chan struct{}, error) { c.nodeConfig = nodeConfig - c.encapMode = encapMode + c.networkConfig = networkConfig - if config.IsIPv4Enabled(nodeConfig, encapMode) { + if config.IsIPv4Enabled(nodeConfig, c.networkConfig.TrafficEncapMode) { c.ipProtocols = append(c.ipProtocols, binding.ProtocolIP) } - if config.IsIPv6Enabled(nodeConfig, encapMode) { + if config.IsIPv6Enabled(nodeConfig, c.networkConfig.TrafficEncapMode) { c.ipProtocols = append(c.ipProtocols, binding.ProtocolIPv6) } @@ -945,11 +949,11 @@ func (c *client) InitialTLVMap() error { } func (c *client) IsIPv4Enabled() bool { - return config.IsIPv4Enabled(c.nodeConfig, c.encapMode) + return config.IsIPv4Enabled(c.nodeConfig, c.networkConfig.TrafficEncapMode) } func (c *client) IsIPv6Enabled() bool { - return config.IsIPv6Enabled(c.nodeConfig, c.encapMode) + return config.IsIPv6Enabled(c.nodeConfig, c.networkConfig.TrafficEncapMode) } // setBasePacketOutBuilder sets base IP properties of a packetOutBuilder which can have more packet data added. diff --git a/pkg/agent/openflow/client_test.go b/pkg/agent/openflow/client_test.go index 7df57c19047..a648e1bbc32 100644 --- a/pkg/agent/openflow/client_test.go +++ b/pkg/agent/openflow/client_test.go @@ -48,7 +48,8 @@ var ( IPv6: gwIPv6, MAC: gwMAC, } - nodeConfig = &config.NodeConfig{GatewayConfig: gatewayConfig} + nodeConfig = &config.NodeConfig{GatewayConfig: gatewayConfig} + networkConig = &config.NetworkConfig{} ) func installNodeFlows(ofClient Client, cacheKey string) (int, error) { @@ -105,6 +106,7 @@ func TestIdempotentFlowInstallation(t *testing.T) { client.cookieAllocator = cookie.NewAllocator(0) client.ofEntryOperations = m client.nodeConfig = nodeConfig + client.networkConfig = networkConig m.EXPECT().AddAll(gomock.Any()).Return(nil).Times(1) // Installing the flows should succeed, and all the flows should be added into the cache. @@ -133,6 +135,7 @@ func TestIdempotentFlowInstallation(t *testing.T) { client.cookieAllocator = cookie.NewAllocator(0) client.ofEntryOperations = m client.nodeConfig = nodeConfig + client.networkConfig = networkConig errorCall := m.EXPECT().AddAll(gomock.Any()).Return(errors.New("Bundle error")).Times(1) m.EXPECT().AddAll(gomock.Any()).Return(nil).After(errorCall) @@ -174,6 +177,7 @@ func TestFlowInstallationFailed(t *testing.T) { client.cookieAllocator = cookie.NewAllocator(0) client.ofEntryOperations = m client.nodeConfig = nodeConfig + client.networkConfig = networkConig // We generate an error for AddAll call. m.EXPECT().AddAll(gomock.Any()).Return(errors.New("Bundle error")) @@ -208,6 +212,7 @@ func TestConcurrentFlowInstallation(t *testing.T) { client.cookieAllocator = cookie.NewAllocator(0) client.ofEntryOperations = m client.nodeConfig = nodeConfig + client.networkConfig = networkConig var concurrentCalls atomic.Value // set to true if we observe concurrent calls timeoutCh := make(chan struct{}) diff --git a/pkg/agent/openflow/network_policy_test.go b/pkg/agent/openflow/network_policy_test.go index 468f75c3ffe..450605259c3 100644 --- a/pkg/agent/openflow/network_policy_test.go +++ b/pkg/agent/openflow/network_policy_test.go @@ -146,6 +146,7 @@ func TestInstallPolicyRuleFlows(t *testing.T) { c = prepareClient(ctrl) c.nodeConfig = &config.NodeConfig{PodIPv4CIDR: podIPv4CIDR, PodIPv6CIDR: nil} + c.networkConfig = &config.NetworkConfig{} c.ipProtocols = []binding.Protocol{binding.ProtocolIP} defaultAction := crdv1alpha1.RuleActionAllow ruleID1 := uint32(101) @@ -287,6 +288,7 @@ func TestBatchInstallPolicyRuleFlows(t *testing.T) { c = prepareClient(ctrl) c.nodeConfig = &config.NodeConfig{PodIPv4CIDR: podIPv4CIDR, PodIPv6CIDR: nil} + c.networkConfig = &config.NetworkConfig{} c.ipProtocols = []binding.Protocol{binding.ProtocolIP} defaultAction := crdv1alpha1.RuleActionAllow priorityRule2 := uint16(10000) @@ -416,6 +418,7 @@ func TestInstallPolicyRuleFlowsInDualStackCluster(t *testing.T) { c = prepareClient(ctrl) c.nodeConfig = &config.NodeConfig{PodIPv4CIDR: podIPv4CIDR, PodIPv6CIDR: podIPv6CIDR} + c.networkConfig = &config.NetworkConfig{} c.ipProtocols = []binding.Protocol{binding.ProtocolIP, binding.ProtocolIPv6} defaultAction := crdv1alpha1.RuleActionAllow ruleID1 := uint32(101) diff --git a/pkg/agent/openflow/pipeline.go b/pkg/agent/openflow/pipeline.go index c64cfb35941..5cc5d00ed72 100644 --- a/pkg/agent/openflow/pipeline.go +++ b/pkg/agent/openflow/pipeline.go @@ -256,6 +256,8 @@ const ( macRewriteMark = 0b1 // cnpDenyMark indicates the packet is denied(Drop/Reject). cnpDenyMark = 0b1 + //wireGuardPktMark indicates the packet should be routed through wireguard tunnel + wireguardPktMark = 0b1 // gatewayCTMark is used to to mark connections initiated through the host gateway interface // (i.e. for which the first packet of the connection was received through the gateway). @@ -349,6 +351,10 @@ var ( // a SNAT IP. The bit range must match SNATIPMarkMask. snatPktMarkRange = binding.Range{0, 7} + // wireguardPktMarkRange take one byte to indicate the packet need to be routed through + // the WireGuard tunnel + wireguardPktMarkRange = binding.Range{8, 8} + globalVirtualMAC, _ = net.ParseMAC("aa:bb:cc:dd:ee:ff") hairpinIP = net.ParseIP("169.254.169.252").To4() hairpinIPv6 = net.ParseIP("fc00::aabb:ccdd:eeff").To16() @@ -385,6 +391,7 @@ type client struct { enableAntreaPolicy bool enableDenyTracking bool enableEgress bool + enableWireGuard bool roundInfo types.RoundInfo cookieAllocator cookie.Allocator bridge binding.Bridge @@ -410,7 +417,7 @@ type client struct { // replayMutex provides exclusive access to the OFSwitch to the ReplayFlows method. replayMutex sync.RWMutex nodeConfig *config.NodeConfig - encapMode config.TrafficEncapModeType + networkConfig *config.NetworkConfig gatewayOFPort uint32 // ovsDatapathType is the type of the datapath used by the bridge. ovsDatapathType ovsconfig.OVSDatapathType @@ -995,7 +1002,7 @@ func (c *client) traceflowL2ForwardOutputFlows(dataplaneTag uint8, liveTraffic, flows := []binding.Flow{} l2FwdOutTable := c.pipeline[L2ForwardingOutTable] for _, ipProtocol := range c.ipProtocols { - if c.encapMode.SupportsEncap() { + if c.networkConfig.TrafficEncapMode.SupportsEncap() { // SendToController and Output if output port is tunnel port. fb1 := l2FwdOutTable.BuildFlow(priorityNormal+3). MatchReg(int(PortCacheReg), config.DefaultTunOFPort). @@ -1246,6 +1253,27 @@ func (c *client) l3FwdFlowToRemote( Done() } +// l3FwdFlowToRemoteViaWireguard generates the L3 forward flow for traffic to a remote Node +// (Pods or gateway) through the tunnel. +func (c *client) l3FwdFlowToRemoteViaWireguard( + localGatewayMAC net.HardwareAddr, + peerSubnet net.IPNet, + tunnelPeer net.IP, + category cookie.Category) binding.Flow { + ipProto := getIPProtocol(peerSubnet.IP) + return c.pipeline[l3ForwardingTable].BuildFlow(priorityNormal).MatchProtocol(ipProto). + MatchDstIPNet(peerSubnet). + // Rewrite src MAC to local gateway MAC and rewrite dst MAC to virtual MAC. + Action().SetSrcMAC(localGatewayMAC). + Action().SetDstMAC(globalVirtualMAC). + Action().LoadPktMarkRange(wireguardPktMark, wireguardPktMarkRange). + // Flow based tunnel. Set tunnel destination. + Action().SetTunnelDst(tunnelPeer). + Action().GotoTable(l3DecTTLTable). + Cookie(c.cookieAllocator.Request(category).Raw()). + Done() +} + // l3FwdFlowToRemoteViaGW generates the L3 forward flow to support traffic to // remote via gateway. It is used when the cross-Node traffic does not require // encapsulation (in noEncap, networkPolicyOnly, or hybrid mode). diff --git a/pkg/agent/openflow/pipeline_windows.go b/pkg/agent/openflow/pipeline_windows.go index 395268196b4..53bfb4b1325 100644 --- a/pkg/agent/openflow/pipeline_windows.go +++ b/pkg/agent/openflow/pipeline_windows.go @@ -281,7 +281,7 @@ func (c *client) hostBridgeUplinkFlows(localSubnet net.IPNet, category cookie.Ca Cookie(c.cookieAllocator.Request(category).Raw()). Done(), } - if c.encapMode.SupportsNoEncap() { + if c.networkConfig.TrafficEncapMode.SupportsNoEncap() { // If NoEncap is enabled, the reply packets from remote Pod can be forwarded to local Pod directly. // by explicitly resubmitting them to endpointDNATTable and marking "macRewriteMark" at same time. flows = append(flows, c.pipeline[conntrackStateTable].BuildFlow(priorityHigh).MatchProtocol(binding.ProtocolIP). @@ -297,7 +297,7 @@ func (c *client) hostBridgeUplinkFlows(localSubnet net.IPNet, category cookie.Ca func (c *client) l3FwdFlowToRemoteViaRouting(localGatewayMAC net.HardwareAddr, remoteGatewayMAC net.HardwareAddr, category cookie.Category, peerIP net.IP, peerPodCIDR *net.IPNet) []binding.Flow { - if c.encapMode.NeedsDirectRoutingToPeer(peerIP, c.nodeConfig.NodeIPAddr) && remoteGatewayMAC != nil { + if c.networkConfig.TrafficEncapMode.NeedsDirectRoutingToPeer(peerIP, c.nodeConfig.NodeIPAddr) && remoteGatewayMAC != nil { // It enhances Windows Noencap mode performance by bypassing host network. flows := []binding.Flow{c.pipeline[l2ForwardingCalcTable].BuildFlow(priorityNormal). MatchDstMAC(remoteGatewayMAC). diff --git a/pkg/agent/openflow/testing/mock_openflow.go b/pkg/agent/openflow/testing/mock_openflow.go index 917fca88551..ad3c2c897ce 100644 --- a/pkg/agent/openflow/testing/mock_openflow.go +++ b/pkg/agent/openflow/testing/mock_openflow.go @@ -222,7 +222,7 @@ func (mr *MockClientMockRecorder) InitialTLVMap() *gomock.Call { } // Initialize mocks base method -func (m *MockClient) Initialize(arg0 types.RoundInfo, arg1 *config.NodeConfig, arg2 config.TrafficEncapModeType) (<-chan struct{}, error) { +func (m *MockClient) Initialize(arg0 types.RoundInfo, arg1 *config.NodeConfig, arg2 *config.NetworkConfig) (<-chan struct{}, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Initialize", arg0, arg1, arg2) ret0, _ := ret[0].(<-chan struct{}) diff --git a/pkg/agent/types/annotations.go b/pkg/agent/types/annotations.go index c835cb34e10..ba7635bc125 100644 --- a/pkg/agent/types/annotations.go +++ b/pkg/agent/types/annotations.go @@ -17,4 +17,6 @@ package types const ( // NodeMACAddressAnnotationKey represents the key of the Node's MAC address in the Annotations of the Node. NodeMACAddressAnnotationKey string = "node.antrea.io/mac-address" + // NodeWireGuardPublicKey represents the key of the Node's WireGuard public key in the Annotations of the Node. + NodeWireGuardPublicKey string = "node.antrea.io/wireguard-publickey" ) diff --git a/pkg/agent/wireguard/client_linux.go b/pkg/agent/wireguard/client_linux.go new file mode 100644 index 00000000000..7946e5de1bf --- /dev/null +++ b/pkg/agent/wireguard/client_linux.go @@ -0,0 +1,332 @@ +// +build linux + +// Copyright 2021 Antrea 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 wireguard + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net" + "strconv" + "sync" + + "github.com/vishvananda/netlink" + "golang.org/x/sys/unix" + "golang.zx2c4.com/wireguard/wgctrl" + "golang.zx2c4.com/wireguard/wgctrl/wgtypes" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + apitypes "k8s.io/apimachinery/pkg/types" + clientset "k8s.io/client-go/kubernetes" + "k8s.io/client-go/util/retry" + "k8s.io/klog/v2" + + "antrea.io/antrea/pkg/agent/types" + "antrea.io/antrea/pkg/agent/util/sysctl" +) + +const ( + ifname = "antrea-wg" + listenPort = 51850 + routeMask = 0x100 + routeMark = 0x100 + routeRulePriority = 10 + routeTable = 10 +) + +var _ Interface = (*client)(nil) + +// wireguardClient is an interface to mock wgctrl.Client +type wireguardClient interface { + io.Closer + Devices() ([]*wgtypes.Device, error) + Device(name string) (*wgtypes.Device, error) + ConfigureDevice(name string, config wgtypes.Config) error +} + +type client struct { + sync.Mutex + wgClient wireguardClient + nodeName string + k8sclient clientset.Interface + mtu int + listenPort int + privateKey wgtypes.Key + peerByNodeName map[string]wgtypes.PeerConfig + enabledIPv4, enabledIPv6 bool + nodeIPv4, nodeIPv6 net.IP +} + +func New(nodeName string, k8sclient clientset.Interface, mtu int, nodeIPv4, nodeIPv6 net.IP, enabledIPv4, enabledIPv6 bool, port int) (Interface, error) { + if port == 0 { + port = listenPort + } + wgClient, err := wgctrl.New() + if err != nil { + return nil, err + } + return &client{ + wgClient: wgClient, + nodeName: nodeName, + k8sclient: k8sclient, + mtu: mtu, + listenPort: port, + nodeIPv4: nodeIPv4, + nodeIPv6: nodeIPv6, + enabledIPv4: enabledIPv4, + enabledIPv6: enabledIPv6, + peerByNodeName: make(map[string]wgtypes.PeerConfig), + }, nil +} + +func (client *client) Init() error { + link := &netlink.Wireguard{LinkAttrs: netlink.LinkAttrs{Name: ifname}} + err := netlink.LinkAdd(link) + // ignore existing link as it might be managed by userspace process + if err != nil && !errors.Is(err, unix.EEXIST) { + if errors.Is(err, unix.EOPNOTSUPP) { + return fmt.Errorf("wireguard not supported by the Linux kernel (netlink: %w)", err) + } + return err + } + if err := netlink.LinkSetUp(link); err != nil { + return err + } + if err := netlink.LinkSetMTU(link, client.mtu); err != nil { + return err + } + if client.enabledIPv4 { + if sysctl.EnsureSysctlNetValue("ipv4/conf/all/rp_filter", 0) != nil { + return fmt.Errorf("setting net.ipv4.conf.all.rp_filter failed: %w", err) + } + if sysctl.EnsureSysctlNetValue(fmt.Sprintf("ipv4/conf/%s/rp_filter", ifname), 0) != nil { + return fmt.Errorf("setting net.ipv4.conf.%s.rp_filter failed: %w", ifname, err) + } + if err := client.ensureRouting(netlink.FAMILY_V4); err != nil { + return err + } + } + if client.enabledIPv6 { + if err := client.ensureRouting(netlink.FAMILY_V6); err != nil { + return err + } + } + + wgdev, err := client.wgClient.Device(ifname) + if err != nil { + return err + } + client.privateKey = wgdev.PrivateKey + var privateKeyExist bool + for _, b := range client.privateKey { + if b != 0 { + privateKeyExist = true + break + } + } + if !privateKeyExist { + newPkey, err := wgtypes.GeneratePrivateKey() + if err != nil { + return err + } + client.privateKey = newPkey + } + + cfg := wgtypes.Config{ + PrivateKey: &client.privateKey, + ListenPort: &client.listenPort, + ReplacePeers: false, + } + + klog.Infof("Updating node public key annotation") + patch, _ := json.Marshal(map[string]interface{}{ + "metadata": map[string]interface{}{ + "annotations": map[string]string{ + types.NodeWireGuardPublicKey: client.privateKey.PublicKey().String(), + }, + }, + }) + if err := retry.RetryOnConflict(retry.DefaultRetry, func() error { + _, err := client.k8sclient.CoreV1().Nodes().Patch(context.TODO(), client.nodeName, apitypes.MergePatchType, patch, metav1.PatchOptions{}) + return err + }); err != nil { + return err + } + + return client.wgClient.ConfigureDevice(ifname, cfg) +} + +func (client *client) RemoveStalePeers(existingPeerPublickeys []string) error { + wgdev, err := client.wgClient.Device(ifname) + if err != nil { + klog.Errorf("failed to get wireguard device %w", err) + return err + } + restoredPeerPublicKeys := make(map[wgtypes.Key]struct{}) + for _, peer := range wgdev.Peers { + restoredPeerPublicKeys[peer.PublicKey] = struct{}{} + } + for _, e := range existingPeerPublickeys { + pubKey, err := wgtypes.ParseKey(e) + if err != nil { + klog.Warning("Parse wireguard public key error %w", err) + continue + } + delete(restoredPeerPublicKeys, pubKey) + } + for k := range restoredPeerPublicKeys { + if err := client.deletePeerByPublicKey(k); err != nil { + klog.Warning("delete wireguard peer error", err) + } + } + return nil +} + +func (client *client) UpdatePeer(nodeName, publicKeyString string, nodeIPv4, nodeIPv6 net.IP) error { + client.Lock() + defer client.Unlock() + pubKey, err := wgtypes.ParseKey(publicKeyString) + if err != nil { + return err + } + var endpoint string + // We can only set one endpoint for each peer. Shoud we prioritize using IPv6? + if client.enabledIPv4 && nodeIPv4 != nil { + endpoint = net.JoinHostPort(nodeIPv4.String(), strconv.Itoa(listenPort)) + } else if client.enabledIPv6 && nodeIPv6 != nil { + endpoint = net.JoinHostPort(nodeIPv6.String(), strconv.Itoa(listenPort)) + } else { + return fmt.Errorf("node IP missing: %s", nodeName) + } + if peer, exist := client.peerByNodeName[nodeName]; exist { + if peer.PublicKey.String() != publicKeyString { + klog.Infof("peer %q public key changed to %s", nodeName, publicKeyString) + // delete old peer + if err := client.deletePeerByPublicKey(peer.PublicKey); err != nil { + return err + } + } + } + var allowedIPs []net.IPNet + if nodeIPv4 != nil { + allowedIPs = append(allowedIPs, net.IPNet{ + IP: nodeIPv4, + Mask: net.CIDRMask(net.IPv4len*8, net.IPv4len*8), + }) + } + if nodeIPv6 != nil { + allowedIPs = append(allowedIPs, net.IPNet{ + IP: nodeIPv6, + Mask: net.CIDRMask(net.IPv6len*8, net.IPv6len*8), + }) + } + endpointUDP, err := net.ResolveUDPAddr("udp", endpoint) + if err != nil { + return err + } + peerConfig := wgtypes.PeerConfig{ + PublicKey: pubKey, + Endpoint: endpointUDP, + AllowedIPs: allowedIPs, + ReplaceAllowedIPs: true, + } + client.peerByNodeName[nodeName] = peerConfig + cfg := wgtypes.Config{ + ReplacePeers: false, + Peers: []wgtypes.PeerConfig{peerConfig}, + } + return client.wgClient.ConfigureDevice(ifname, cfg) +} + +func (client *client) deletePeerByPublicKey(pubKey wgtypes.Key) error { + cfg := wgtypes.Config{Peers: []wgtypes.PeerConfig{ + {PublicKey: pubKey, Remove: true}, + }} + return client.wgClient.ConfigureDevice(ifname, cfg) +} + +func (client *client) DeletePeer(nodeName string) error { + peer, exist := client.peerByNodeName[nodeName] + if !exist { + return nil + } + if err := client.deletePeerByPublicKey(peer.PublicKey); err != nil { + return err + } + delete(client.peerByNodeName, nodeName) + return nil +} + +// ensureRouting set route rule, route table and default route for marked traffic +func (client *client) ensureRouting(ipFamily int) error { + rule := netlink.NewRule() + rule.Mark = routeMark + rule.Mask = routeMask + rule.Priority = routeRulePriority + rule.Table = routeTable + rule.Family = ipFamily + exist, err := lookupRule(rule, ipFamily) + if err != nil { + return err + } + if !exist { + if err := netlink.RuleAdd(rule); err != nil { + klog.Errorf("failed to add IP rule for WireGuard: %v", err) + return err + } + } + link, err := netlink.LinkByName(ifname) + if err != nil { + return err + } + rt := netlink.Route{ + LinkIndex: link.Attrs().Index, + Table: routeTable, + Scope: netlink.SCOPE_LINK, + } + if ipFamily == netlink.FAMILY_V4 { + rt.Dst = &net.IPNet{ + IP: net.IPv4zero, + Mask: net.CIDRMask(0, net.IPv4len), + } + rt.Src = client.nodeIPv4 + } else if ipFamily == netlink.FAMILY_V6 { + rt.Dst = &net.IPNet{ + IP: net.IPv6zero, + Mask: net.CIDRMask(0, net.IPv6len), + } + rt.Src = client.nodeIPv6 + } + return netlink.RouteReplace(&rt) +} + +// lookupRule checks whether existing rule matches. We only care the +func lookupRule(rule *netlink.Rule, family int) (bool, error) { + rules, err := netlink.RuleList(family) + if err != nil { + return false, err + } + for _, r := range rules { + if rule.Priority != r.Priority || r.Mask != rule.Mask || + r.Mark != rule.Mark || rule.Table != r.Table { + continue + } + return true, nil + } + return false, nil +} diff --git a/pkg/agent/wireguard/client_test.go b/pkg/agent/wireguard/client_test.go new file mode 100644 index 00000000000..fd97e649af2 --- /dev/null +++ b/pkg/agent/wireguard/client_test.go @@ -0,0 +1,273 @@ +// +build linux + +// Copyright 2021 Antrea 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 wireguard + +import ( + "net" + "testing" + + "github.com/stretchr/testify/assert" + "golang.zx2c4.com/wireguard/wgctrl/wgtypes" +) + +type fakeWireGuardClient struct { + peers map[wgtypes.Key]wgtypes.Peer +} + +func (f *fakeWireGuardClient) Close() error { + return nil +} + +func (f *fakeWireGuardClient) Devices() ([]*wgtypes.Device, error) { + return nil, nil +} + +func (f *fakeWireGuardClient) Device(name string) (*wgtypes.Device, error) { + var res []wgtypes.Peer + for _, p := range f.peers { + res = append(res, p) + } + return &wgtypes.Device{ + Peers: res, + }, nil +} + +func (f *fakeWireGuardClient) ConfigureDevice(name string, cfg wgtypes.Config) error { + for _, c := range cfg.Peers { + if c.Remove { + delete(f.peers, c.PublicKey) + } else { + peer := wgtypes.Peer{ + PublicKey: c.PublicKey, + Endpoint: c.Endpoint, + } + if c.Endpoint != nil { + ep := &net.UDPAddr{} + ip := c.Endpoint.IP + ep.IP = append(ip[:0:0], ip...) + ep.Port = c.Endpoint.Port + ep.Zone = c.Endpoint.Zone + peer.Endpoint = ep + } + for _, n := range c.AllowedIPs { + peer.AllowedIPs = append(peer.AllowedIPs, net.IPNet{ + IP: append(n.IP[:0:0], n.IP...), + Mask: append(n.Mask[:0:0], n.Mask...), + }) + } + f.peers[c.PublicKey] = peer + } + } + return nil +} +func getFakeClient() *client { + return &client{ + wgClient: &fakeWireGuardClient{}, + nodeName: "fake-node-1", + mtu: 1420, + listenPort: listenPort, + peerByNodeName: make(map[string]wgtypes.PeerConfig), + enabledIPv4: true, + enabledIPv6: false, + nodeIPv4: net.ParseIP("10.20.30.41"), + } +} + +func assertWgPeerAndPeerConfigIdentical(t *testing.T, peer wgtypes.Peer, peercfg wgtypes.PeerConfig) { + assert.EqualValues(t, peer.AllowedIPs, peercfg.AllowedIPs) + assert.EqualValues(t, peer.Endpoint, peercfg.Endpoint) + assert.EqualValues(t, peer.PublicKey, peercfg.PublicKey) +} + +func deepCopyForWireGuardPeer(peer wgtypes.Peer) wgtypes.Peer { + c := wgtypes.Peer{} + c.AllowedIPs = make([]net.IPNet, len(peer.AllowedIPs)) + for idx := range peer.AllowedIPs { + ip := peer.AllowedIPs[idx].IP + mask := peer.AllowedIPs[idx].Mask + c.AllowedIPs[idx].IP = append(ip[:0:0], ip...) + c.AllowedIPs[idx].Mask = append(mask[:0:0], mask...) + } + if peer.Endpoint != nil { + ep := &net.UDPAddr{} + ip := peer.Endpoint.IP + ep.IP = append(ip[:0:0], ip...) + ep.Port = peer.Endpoint.Port + ep.Zone = peer.Endpoint.Zone + c.Endpoint = ep + } + c.PublicKey = peer.PublicKey + return c +} + +func Test_RemoveStalePeers(t *testing.T) { + client := getFakeClient() + pk1, _ := wgtypes.GeneratePrivateKey() + pk2, _ := wgtypes.GeneratePrivateKey() + pk3, _ := wgtypes.GeneratePrivateKey() + t.Run("pass empty/nil slice should remove all existing peers", func(tt *testing.T) { + fc := &fakeWireGuardClient{ + peers: map[wgtypes.Key]wgtypes.Peer{ + pk1.PublicKey(): {PublicKey: pk1.PublicKey()}, + pk2.PublicKey(): {PublicKey: pk2.PublicKey()}, + }, + } + client.wgClient = fc + err := client.RemoveStalePeers(nil) + assert.Nil(tt, err) + assert.Empty(tt, fc.peers) + }) + + t.Run("args has no intersection with existing peers", func(tt *testing.T) { + fc := &fakeWireGuardClient{ + peers: map[wgtypes.Key]wgtypes.Peer{ + pk1.PublicKey(): {PublicKey: pk1.PublicKey()}, + pk2.PublicKey(): {PublicKey: pk2.PublicKey()}, + }, + } + client.wgClient = fc + err := client.RemoveStalePeers([]string{pk3.PublicKey().String()}) + assert.Nil(tt, err) + assert.Empty(tt, fc.peers) + }) + + t.Run("should only keep peer passed by args", func(tt *testing.T) { + fc := &fakeWireGuardClient{ + peers: map[wgtypes.Key]wgtypes.Peer{ + pk1.PublicKey(): {PublicKey: pk1.PublicKey()}, + pk2.PublicKey(): {PublicKey: pk2.PublicKey()}, + pk3.PublicKey(): {PublicKey: pk3.PublicKey()}, + }, + } + client.wgClient = fc + err := client.RemoveStalePeers([]string{pk3.PublicKey().String()}) + assert.Nil(tt, err) + assert.Len(tt, fc.peers, 1) + _, exist := fc.peers[pk3.PublicKey()] + assert.True(tt, exist) + }) +} + +func Test_UpdatePeer(t *testing.T) { + client := getFakeClient() + fc := &fakeWireGuardClient{ + peers: map[wgtypes.Key]wgtypes.Peer{}, + } + client.wgClient = fc + pk1, _ := wgtypes.GeneratePrivateKey() + pk2, _ := wgtypes.GeneratePrivateKey() + // pk3, _ := wgtypes.GeneratePrivateKey() + + ip1, ipnet1, _ := net.ParseCIDR("10.20.30.42/32") + ip2, ipnet2, _ := net.ParseCIDR("10.20.30.43/32") + peer1 := wgtypes.Peer{ + PublicKey: pk1.PublicKey(), + Endpoint: &net.UDPAddr{ + IP: ip1, + Port: listenPort, + }, + AllowedIPs: []net.IPNet{*ipnet1}, + } + + t.Run("call update peer to add new peers", func(tt *testing.T) { + err := client.UpdatePeer("fake-node-2", pk1.PublicKey().String(), ip1.To4(), nil) + assert.Nil(tt, err) + assert.Len(tt, fc.peers, 1) + assert.Len(tt, client.peerByNodeName, 1) + peerFromFakeClient, ok := fc.peers[pk1.PublicKey()] + assert.True(tt, ok) + assert.EqualValues(tt, peer1, peerFromFakeClient) + peerCfgFromClient, ok := client.peerByNodeName["fake-node-2"] + assert.True(tt, ok) + assertWgPeerAndPeerConfigIdentical(tt, peerFromFakeClient, peerCfgFromClient) + }) + + t.Run("call update peer to update existing peers", func(tt *testing.T) { + peer1Updated := wgtypes.Peer{ + PublicKey: pk2.PublicKey(), + Endpoint: &net.UDPAddr{ + IP: ip2, + Port: listenPort, + }, + AllowedIPs: []net.IPNet{*ipnet2}, + } + err := client.UpdatePeer("fake-node-2", pk2.PublicKey().String(), ip2.To4(), nil) + assert.Nil(tt, err) + assert.Len(tt, fc.peers, 1) + assert.Len(tt, client.peerByNodeName, 1) + peerFromFakeClient, ok := fc.peers[pk2.PublicKey()] + assert.True(tt, ok) + assert.EqualValues(tt, peer1Updated, peerFromFakeClient) + peerCfgFromClient, ok := client.peerByNodeName["fake-node-2"] + assert.True(tt, ok) + assertWgPeerAndPeerConfigIdentical(tt, peerFromFakeClient, peerCfgFromClient) + }) + + t.Run("call update peer with invalid public key", func(tt *testing.T) { + peerFromFakeClient, ok := fc.peers[pk2.PublicKey()] + assert.True(tt, ok) + peerFromFakeClientCopy := deepCopyForWireGuardPeer(peerFromFakeClient) + err := client.UpdatePeer("fake-node-2", "invalid key", ip1.To4(), nil) + assert.Error(tt, err) + assert.Len(tt, fc.peers, 1) + assert.Len(tt, client.peerByNodeName, 1) + peerFromFakeClientAfterUpdate, ok := fc.peers[pk2.PublicKey()] + assert.True(tt, ok) + assert.EqualValues(tt, peerFromFakeClientCopy, peerFromFakeClientAfterUpdate) + }) + + t.Run("call update peer with nil IP", func(tt *testing.T) { + peerFromFakeClient, ok := fc.peers[pk2.PublicKey()] + assert.True(tt, ok) + peerFromFakeClientCopy := deepCopyForWireGuardPeer(peerFromFakeClient) + err := client.UpdatePeer("fake-node-2", pk2.PublicKey().String(), nil, nil) + assert.Error(tt, err) + assert.Len(tt, fc.peers, 1) + assert.Len(tt, client.peerByNodeName, 1) + peerFromFakeClientAfterUpdate, ok := fc.peers[pk2.PublicKey()] + assert.True(tt, ok) + assert.EqualValues(tt, peerFromFakeClientCopy, peerFromFakeClientAfterUpdate) + }) +} + +func Test_DeletePeer(t *testing.T) { + client := getFakeClient() + fc := &fakeWireGuardClient{ + peers: map[wgtypes.Key]wgtypes.Peer{}, + } + client.wgClient = fc + pk1, _ := wgtypes.GeneratePrivateKey() + ip1, _, _ := net.ParseCIDR("10.20.30.42/32") + t.Run("delete non-existing peer", func(tt *testing.T) { + err := client.UpdatePeer("fake-node-1", pk1.String(), ip1.To4(), nil) + assert.NoError(tt, err) + assert.Len(tt, fc.peers, 1) + assert.Len(tt, client.peerByNodeName, 1) + err = client.DeletePeer("fake-node-2") + assert.NoError(tt, err) + assert.Len(tt, fc.peers, 1) + assert.Len(tt, client.peerByNodeName, 1) + }) + + t.Run("delete existing peer", func(tt *testing.T) { + err := client.DeletePeer("fake-node-1") + assert.NoError(tt, err) + assert.Len(tt, fc.peers, 0) + assert.Len(tt, client.peerByNodeName, 0) + }) + +} diff --git a/pkg/agent/wireguard/client_windows.go b/pkg/agent/wireguard/client_windows.go new file mode 100644 index 00000000000..67081d3ad8c --- /dev/null +++ b/pkg/agent/wireguard/client_windows.go @@ -0,0 +1,27 @@ +// +build windows + +// Copyright 2021 Antrea 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 wireguard + +import ( + "fmt" + "net" + + clientset "k8s.io/client-go/kubernetes" +) + +func New(nodeName string, k8sclient clientset.Interface, mtu int, nodeIPv4, nodeIPv6 net.IP, enabledIPv4, enabledIPv6 bool, port int) (Interface, error) { + return nil, fmt.Errorf("WireGuard is not implemented for windows") +} diff --git a/pkg/agent/wireguard/interface.go b/pkg/agent/wireguard/interface.go new file mode 100644 index 00000000000..b90eeb62d7a --- /dev/null +++ b/pkg/agent/wireguard/interface.go @@ -0,0 +1,26 @@ +// Copyright 2021 Antrea 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 wireguard + +import ( + "net" +) + +type Interface interface { + Init() error + UpdatePeer(nodeName, publicKeyString string, nodeIPv4, nodeIPv6 net.IP) error + RemoveStalePeers(currentPeerPublickeys []string) error + DeletePeer(nodeName string) error +} diff --git a/plugins/octant/go.sum b/plugins/octant/go.sum index 4c8ca2023d9..b879a708cf5 100644 --- a/plugins/octant/go.sum +++ b/plugins/octant/go.sum @@ -331,6 +331,7 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -420,8 +421,14 @@ github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6t github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= +github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= +github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw= +github.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs= +github.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA= +github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= @@ -476,10 +483,19 @@ github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo= +github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= +github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= +github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8= +github.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= +github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= +github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys= +github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -645,8 +661,10 @@ github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPU github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netlink v1.1.1-0.20210510164352-d17758a128bf/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vmware-tanzu/octant v0.17.0 h1:2H2AiQU5C1RiHxxYrrOosDHHI9eV51nP+e9PjRP+c48= github.com/vmware-tanzu/octant v0.17.0/go.mod h1:lA32xKa6icUclg+DjAX/E/Id1cTqwCXZUem3RGEp/2A= github.com/vmware/go-ipfix v0.5.3/go.mod h1:SF6BrZTPvoVdzgmjJvshoegBVbicn4xWlkoCNADab6E= @@ -704,8 +722,9 @@ golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e h1:8foAy0aoO5GkqCvAEJ4VC4P3zksTg4X4aJCDpZzmgQI= +golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -781,11 +800,17 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201024042810-be3efd7ff127/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210224082022-3d97a244fca7 h1:OgUuv8lsRpBibGNbSizVwKWlysjaNzmC9gYMhPVfqFM= +golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210504132125-bbd867fde50d h1:nTDGCTeAu2LhcsHTRzjyIUbZHCJ4QePArsm27Hka0UM= +golang.org/x/net v0.0.0-20210504132125-bbd867fde50d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -841,6 +866,7 @@ golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -851,15 +877,26 @@ golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073 h1:8qxJSnu+7dRq6upnbntrmriWByIakBuct5OM/MdQC1M= +golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6 h1:cdsMqa2nXzqlgs183pHxtvoVwU7CyzaCTAUOg94af4c= +golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -868,8 +905,9 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -939,6 +977,8 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.zx2c4.com/wireguard v0.0.0-20210427022245-097af6e1351b/go.mod h1:a057zjmoc00UN7gVkaJt2sXVK523kMJcogDTEvPIasg= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210506160403-92e472f520a5/go.mod h1:+1XihzyZUBJcSc5WO9SwNA7v26puQwOEDwanaxfNXPQ= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= diff --git a/test/e2e/connectivity_test.go b/test/e2e/connectivity_test.go index d327b03d534..561a60ef0ae 100644 --- a/test/e2e/connectivity_test.go +++ b/test/e2e/connectivity_test.go @@ -223,25 +223,18 @@ func TestPodConnectivityDifferentNodes(t *testing.T) { data.testPodConnectivityDifferentNodes(t) } -func (data *TestData) redeployAntrea(t *testing.T, enableIPSec bool) { +func (data *TestData) redeployAntrea(t *testing.T, option deployAntreaOptions) { var err error // export logs before deleting Antrea - if enableIPSec { - exportLogs(t, data, "beforeRedeployWithIPsec", false) - } else { - exportLogs(t, data, "beforeRedploy", false) - } + exportLogs(t, data, "beforeRedploy", false) t.Logf("Deleting Antrea Agent DaemonSet") if err = data.deleteAntrea(defaultTimeout); err != nil { t.Fatalf("Error when deleting Antrea DaemonSet: %v", err) } t.Logf("Applying Antrea YAML") - if enableIPSec { - err = data.deployAntreaIPSec() - } else { - err = data.deployAntrea() - } + err = data.deployAntrea(option) + if err != nil { t.Fatalf("Error when applying Antrea YAML: %v", err) } @@ -291,7 +284,7 @@ func TestPodConnectivityAfterAntreaRestart(t *testing.T) { data.runPingMesh(t, podInfos[:numPods], agnhostContainerName) - data.redeployAntrea(t, false) + data.redeployAntrea(t, deployOptionsCommon) data.runPingMesh(t, podInfos[:numPods], agnhostContainerName) } diff --git a/test/e2e/fixtures.go b/test/e2e/fixtures.go index 29217af8045..9db9bcfd2f9 100644 --- a/test/e2e/fixtures.go +++ b/test/e2e/fixtures.go @@ -107,7 +107,7 @@ func skipIfNoWindowsNodes(tb testing.TB) { func ensureAntreaRunning(tb testing.TB, data *TestData) error { tb.Logf("Applying Antrea YAML") - if err := data.deployAntrea(); err != nil { + if err := data.deployAntrea(deployOptionsCommon); err != nil { return err } tb.Logf("Waiting for all Antrea DaemonSet Pods") diff --git a/test/e2e/framework.go b/test/e2e/framework.go index 952cc119987..9f7ad2cae76 100644 --- a/test/e2e/framework.go +++ b/test/e2e/framework.go @@ -77,8 +77,10 @@ const ( agentContainerName string = "antrea-agent" antreaYML string = "antrea.yml" antreaIPSecYML string = "antrea-ipsec.yml" + antreaWireGuardYML string = "antrea-wireguard.yml" antreaCovYML string = "antrea-coverage.yml" antreaIPSecCovYML string = "antrea-ipsec-coverage.yml" + antreaWireGuardCovYML string = "antrea-wireguard-coverage.yml" flowAggregatorYML string = "flow-aggregator.yml" flowAggregatorCovYML string = "flow-aggregator-coverage.yml" defaultBridgeName string = "br-int" @@ -112,6 +114,27 @@ const ( aggregatorInactiveFlowRecordTimeout = 6 * time.Second ) +type deployAntreaOptions int + +const ( + deployOptionsCommon deployAntreaOptions = iota + deployOptionsIPsec + deployOptionsWireGuardGo + deployOptionsUnknown +) + +var deployYamlMapping = [...]string{ + deployOptionsCommon: antreaYML, + deployOptionsIPsec: antreaIPSecYML, + deployOptionsWireGuardGo: antreaWireGuardYML, +} + +var deployYamlCovMapping = [...]string{ + deployOptionsCommon: antreaCovYML, + deployOptionsIPsec: antreaIPSecCovYML, + deployOptionsWireGuardGo: antreaWireGuardCovYML, +} + type ClusterNode struct { idx int // 0 for control-plane Node name string @@ -546,20 +569,15 @@ func (data *TestData) deployAntreaCommon(yamlFile string, extraOptions string) e return nil } -// deployAntrea deploys Antrea with the standard manifest. -func (data *TestData) deployAntrea() error { - if testOptions.enableCoverage { - return data.deployAntreaCommon(antreaCovYML, "") +// deployAntrea deploys Antrea with different manifests mapped from deploy options +func (data *TestData) deployAntrea(option deployAntreaOptions) error { + if option < 0 || option >= deployOptionsUnknown { + return fmt.Errorf("unknown deploy option %d", option) } - return data.deployAntreaCommon(antreaYML, "") -} - -// deployAntreaIPSec deploys Antrea with IPSec tunnel enabled. -func (data *TestData) deployAntreaIPSec() error { if testOptions.enableCoverage { - return data.deployAntreaCommon(antreaIPSecCovYML, "") + return data.deployAntreaCommon(deployYamlCovMapping[option], "") } - return data.deployAntreaCommon(antreaIPSecYML, "") + return data.deployAntreaCommon(deployYamlMapping[option], "") } // deployAntreaFlowExporter deploys Antrea with flow exporter config params enabled. diff --git a/test/e2e/ipsec_test.go b/test/e2e/ipsec_test.go index 864049f47af..21bde07fd1e 100644 --- a/test/e2e/ipsec_test.go +++ b/test/e2e/ipsec_test.go @@ -69,7 +69,7 @@ func TestIPSecTunnelConnectivity(t *testing.T) { defer teardownTest(t, data) t.Logf("Redeploy Antrea with IPSec tunnel enabled") - data.redeployAntrea(t, true) + data.redeployAntrea(t, deployOptionsIPsec) data.testPodConnectivityDifferentNodes(t) @@ -85,7 +85,7 @@ func TestIPSecTunnelConnectivity(t *testing.T) { } // Restore normal Antrea deployment with IPSec disabled. - data.redeployAntrea(t, false) + data.redeployAntrea(t, deployOptionsCommon) } // TestIPSecDeleteStaleTunnelPorts checks that when switching from IPsec mode to @@ -104,7 +104,7 @@ func TestIPSecDeleteStaleTunnelPorts(t *testing.T) { defer teardownTest(t, data) t.Logf("Redeploy Antrea with IPSec tunnel enabled") - data.redeployAntrea(t, true) + data.redeployAntrea(t, deployOptionsIPsec) nodeName0 := nodeName(0) nodeName1 := nodeName(1) @@ -136,7 +136,7 @@ func TestIPSecDeleteStaleTunnelPorts(t *testing.T) { } t.Logf("Redeploy Antrea with IPSec tunnel disabled") - data.redeployAntrea(t, false) + data.redeployAntrea(t, deployOptionsCommon) t.Logf("Checking that tunnel port has been deleted") if err := wait.PollImmediate(defaultInterval, defaultTimeout, func() (found bool, err error) { diff --git a/test/e2e/performance_test.go b/test/e2e/performance_test.go index 9182a383a9e..ee8942b08f6 100644 --- a/test/e2e/performance_test.go +++ b/test/e2e/performance_test.go @@ -320,7 +320,7 @@ func withPerfTestSetup(fn func(data *TestData), b *testing.B) { b.Fatalf("Error when deleting Antrea DaemonSet: %v", err) } b.Logf("Applying Antrea YAML") - if err := data.deployAntrea(); err != nil { + if err := data.deployAntrea(deployOptionsCommon); err != nil { b.Fatalf("Error when restarting Antrea: %v", err) } b.Logf("Waiting for all Antrea DaemonSet Pods") diff --git a/test/e2e/wireguard_test.go b/test/e2e/wireguard_test.go new file mode 100644 index 00000000000..c3f31c2b0a2 --- /dev/null +++ b/test/e2e/wireguard_test.go @@ -0,0 +1,65 @@ +// Copyright 2019 Antrea 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 e2e + +import ( + "testing" + + "antrea.io/antrea/pkg/agent/config" +) + +// TestWireGuardTunnelConnectivity checks that Pod traffic across two Nodes over +// the WireGuard tunnel, by creating multiple Pods across distinct Nodes and having +// them ping each other. +func TestWireGuardTunnelConnectivity(t *testing.T) { + // skipIfIPv6Cluster(t) + skipIfNumNodesLessThan(t, 2) + skipIfHasWindowsNodes(t) + data, err := setupTest(t) + if err != nil { + t.Fatalf("Error when setting up test: %v", err) + } + defer teardownTest(t, data) + // WireGuard currently only works with encap mode + skipIfEncapModeIsNot(t, data, config.TrafficEncapModeEncap) + + ac := []configChange{ + {"enableWireGuardTunnel", "true", false}, + } + if err := data.mutateAntreaConfigMap(nil, ac, false, true); err != nil { + t.Fatalf("Failed to enable WireGuard tunnel: %v", err) + } + defer func() { + ac = []configChange{ + {"enableWireGuardTunnel", "false", false}, + } + if err := data.mutateAntreaConfigMap(nil, ac, false, true); err != nil { + t.Fatalf("Failed to disable WireGuard tunnel: %v", err) + } + }() + // if privider is kind we can test using user-space WireGuard for testing. Otherwise we need + // to ensure the wireguard kernel module is present. + if testOptions.providerName != "kind" { + for _, node := range clusterInfo.nodes { + skipIfMissingKernelModule(t, node.name, []string{"wireguard"}) + } + } else { + t.Logf("Redeploy Antrea with userspace wireguard-go ") + data.redeployAntrea(t, deployOptionsWireGuardGo) + defer data.redeployAntrea(t, deployOptionsCommon) + } + + data.testPodConnectivityDifferentNodes(t) +} diff --git a/test/integration/agent/openflow_test.go b/test/integration/agent/openflow_test.go index 74ff7e16a02..d7634151a6d 100644 --- a/test/integration/agent/openflow_test.go +++ b/test/integration/agent/openflow_test.go @@ -168,7 +168,7 @@ func TestReplayFlowsNetworkPolicyFlows(t *testing.T) { err := ofTestUtils.PrepareOVSBridge(br) require.Nil(t, err, fmt.Sprintf("Failed to prepare OVS bridge: %v", err)) - _, err = c.Initialize(roundInfo, &config1.NodeConfig{}, config1.TrafficEncapModeEncap) + _, err = c.Initialize(roundInfo, &config1.NodeConfig{}, &config1.NetworkConfig{TrafficEncapMode: config1.TrafficEncapModeEncap}) require.Nil(t, err, "Failed to initialize OFClient") defer func() { @@ -255,7 +255,7 @@ func testReplayFlows(t *testing.T) { } func testInitialize(t *testing.T, config *testConfig) { - if _, err := c.Initialize(roundInfo, config.nodeConfig, config1.TrafficEncapModeEncap); err != nil { + if _, err := c.Initialize(roundInfo, config.nodeConfig, &config1.NetworkConfig{TrafficEncapMode: config1.TrafficEncapModeEncap}); err != nil { t.Errorf("Failed to initialize openflow client: %v", err) } for _, tableFlow := range prepareDefaultFlows(config) { @@ -347,7 +347,7 @@ func TestNetworkPolicyFlows(t *testing.T) { err := ofTestUtils.PrepareOVSBridge(br) require.Nil(t, err, fmt.Sprintf("Failed to prepare OVS bridge %s", br)) - _, err = c.Initialize(roundInfo, &config1.NodeConfig{PodIPv4CIDR: podIPv4CIDR, PodIPv6CIDR: podIPv6CIDR}, config1.TrafficEncapModeEncap) + _, err = c.Initialize(roundInfo, &config1.NodeConfig{PodIPv4CIDR: podIPv4CIDR, PodIPv6CIDR: podIPv6CIDR}, &config1.NetworkConfig{TrafficEncapMode: config1.TrafficEncapModeEncap}) require.Nil(t, err, "Failed to initialize OFClient") defer func() { @@ -489,7 +489,7 @@ func TestProxyServiceFlows(t *testing.T) { err := ofTestUtils.PrepareOVSBridge(br) require.Nil(t, err, fmt.Sprintf("Failed to prepare OVS bridge %s", br)) - _, err = c.Initialize(roundInfo, &config1.NodeConfig{}, config1.TrafficEncapModeEncap) + _, err = c.Initialize(roundInfo, &config1.NodeConfig{}, &config1.NetworkConfig{TrafficEncapMode: config1.TrafficEncapModeEncap}) require.Nil(t, err, "Failed to initialize OFClient") defer func() { @@ -1370,7 +1370,7 @@ func TestSNATFlows(t *testing.T) { require.Nil(t, err, fmt.Sprintf("Failed to prepare OVS bridge %s", br)) config := prepareConfiguration() - _, err = c.Initialize(roundInfo, config.nodeConfig, config1.TrafficEncapModeEncap) + _, err = c.Initialize(roundInfo, config.nodeConfig, &config1.NetworkConfig{TrafficEncapMode: config1.TrafficEncapModeEncap}) require.Nil(t, err, "Failed to initialize OFClient") defer func() {