diff --git a/.github/workflows/lint-openapi.yaml b/.github/workflows/lint-openapi.yaml
index 9722970d0a..6ec2e1c0b1 100644
--- a/.github/workflows/lint-openapi.yaml
+++ b/.github/workflows/lint-openapi.yaml
@@ -26,7 +26,7 @@ on:
jobs:
link-check:
name: Openapi Lint
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
strategy:
matrix:
include:
@@ -46,12 +46,12 @@ jobs:
- name: Validate OpenAPI definition By PR
if: ${{ github.event_name != 'workflow_dispatch' }}
- uses: char0n/swagger-editor-validate@v1
+ uses: char0n/swagger-editor-validate@v1.4.0
with:
definition-file: ${{ matrix.openapifilepath }}
- name: Validate OpenAPI definition By Command
if: ${{ github.event_name == 'workflow_dispatch' }}
- uses: char0n/swagger-editor-validate@v1
+ uses: char0n/swagger-editor-validate@v1.4.0
with:
definition-file: ${{ github.event.inputs.path }}
diff --git a/Makefile b/Makefile
index 53dd5683b1..eb7c833461 100644
--- a/Makefile
+++ b/Makefile
@@ -275,7 +275,7 @@ check_test_label:
.PHONY: unittest-tests
unittest-tests: check_test_label
@echo "run unittest-tests"
- $(QUIET) $(ROOT_DIR)/tools/scripts/ginkgo.sh \
+ sudo $(QUIET) $(ROOT_DIR)/tools/scripts/ginkgo.sh \
--cover --coverprofile=./coverage.out --covermode set \
--json-report unittestreport.json \
-randomize-suites -randomize-all --keep-going --timeout=1h -p \
diff --git a/README-zh_CN.md b/README-zh_CN.md
index 22d1846598..76a923e3d6 100644
--- a/README-zh_CN.md
+++ b/README-zh_CN.md
@@ -173,6 +173,6 @@ We are a [Cloud Native Computing Foundation](https://www.cncf.io) [sandbox proje
The Linux Foundation® (TLF) has registered trademarks and uses trademarks. For a list of TLF trademarks, see [Trademark Usage](https://www.linuxfoundation.org/legal/trademark-usage).
-
 
+
 
diff --git a/README.md b/README.md
index af2ddf3c1c..4131a41837 100644
--- a/README.md
+++ b/README.md
@@ -179,6 +179,6 @@ We are a [Cloud Native Computing Foundation](https://www.cncf.io) [sandbox proje
The Linux Foundation® (TLF) has registered trademarks and uses trademarks. For a list of TLF trademarks, see [Trademark Usage](https://www.linuxfoundation.org/legal/trademark-usage).
-
 
+
 
diff --git a/api/v1/agent/models/coordinator_config.go b/api/v1/agent/models/coordinator_config.go
index 3654a25c81..041c56ecad 100644
--- a/api/v1/agent/models/coordinator_config.go
+++ b/api/v1/agent/models/coordinator_config.go
@@ -22,12 +22,6 @@ import (
// swagger:model CoordinatorConfig
type CoordinatorConfig struct {
- // detect gateway
- DetectGateway bool `json:"detectGateway,omitempty"`
-
- // detect IP conflict
- DetectIPConflict bool `json:"detectIPConflict,omitempty"`
-
// hijack c ID r
HijackCIDR []string `json:"hijackCIDR"`
diff --git a/api/v1/agent/models/ip_config.go b/api/v1/agent/models/ip_config.go
index 0e06e0c506..938d1c0e95 100644
--- a/api/v1/agent/models/ip_config.go
+++ b/api/v1/agent/models/ip_config.go
@@ -27,6 +27,12 @@ type IPConfig struct {
// Required: true
Address *string `json:"address"`
+ // enable gateway detection
+ EnableGatewayDetection bool `json:"enableGatewayDetection,omitempty"`
+
+ // enable IP conflict detection
+ EnableIPConflictDetection bool `json:"enableIPConflictDetection,omitempty"`
+
// gateway
Gateway string `json:"gateway,omitempty"`
diff --git a/api/v1/agent/openapi.yaml b/api/v1/agent/openapi.yaml
index ea32dcd9da..9580c29f9e 100644
--- a/api/v1/agent/openapi.yaml
+++ b/api/v1/agent/openapi.yaml
@@ -306,6 +306,10 @@ definitions:
type: string
vlan:
type: integer
+ enableGatewayDetection:
+ type: boolean
+ enableIPConflictDetection:
+ type: boolean
required:
- version
- address
@@ -340,10 +344,6 @@ definitions:
type: integer
txQueueLen:
type: integer
- detectIPConflict:
- type: boolean
- detectGateway:
- type: boolean
vethLinkAddress:
type: string
required:
diff --git a/api/v1/agent/server/embedded_spec.go b/api/v1/agent/server/embedded_spec.go
index dd718cb184..23a0fe9112 100644
--- a/api/v1/agent/server/embedded_spec.go
+++ b/api/v1/agent/server/embedded_spec.go
@@ -291,12 +291,6 @@ func init() {
"tunePodRoutes"
],
"properties": {
- "detectGateway": {
- "type": "boolean"
- },
- "detectIPConflict": {
- "type": "boolean"
- },
"hijackCIDR": {
"type": "array",
"items": {
@@ -396,6 +390,12 @@ func init() {
"address": {
"type": "string"
},
+ "enableGatewayDetection": {
+ "type": "boolean"
+ },
+ "enableIPConflictDetection": {
+ "type": "boolean"
+ },
"gateway": {
"type": "string"
},
@@ -846,12 +846,6 @@ func init() {
"tunePodRoutes"
],
"properties": {
- "detectGateway": {
- "type": "boolean"
- },
- "detectIPConflict": {
- "type": "boolean"
- },
"hijackCIDR": {
"type": "array",
"items": {
@@ -951,6 +945,12 @@ func init() {
"address": {
"type": "string"
},
+ "enableGatewayDetection": {
+ "type": "boolean"
+ },
+ "enableIPConflictDetection": {
+ "type": "boolean"
+ },
"gateway": {
"type": "string"
},
diff --git a/charts/spiderpool/README.md b/charts/spiderpool/README.md
index b260051275..372e9cb1c5 100644
--- a/charts/spiderpool/README.md
+++ b/charts/spiderpool/README.md
@@ -134,6 +134,8 @@ helm install spiderpool spiderpool/spiderpool --wait --namespace kube-system \
| `ipam.enableStatefulSet` | the network mode | `true` |
| `ipam.enableKubevirtStaticIP` | the feature to keep kubevirt vm pod static IP | `true` |
| `ipam.enableSpiderSubnet` | SpiderSubnet feature gate. | `true` |
+| `ipam.enableIPConflictDetection` | enable IP conflict detection | `false` |
+| `ipam.enableGatewayDetection` | enable gateway detection | `false` |
| `ipam.subnetDefaultFlexibleIPNumber` | the default flexible IP number of SpiderSubnet feature auto-created IPPools | `1` |
| `ipam.gc.enabled` | enable retrieve IP in spiderippool CR | `true` |
| `ipam.gc.gcAll.intervalInSecond` | the gc all interval duration | `600` |
diff --git a/charts/spiderpool/crds/spiderpool.spidernet.io_spidercoordinators.yaml b/charts/spiderpool/crds/spiderpool.spidernet.io_spidercoordinators.yaml
index 00dc8d9239..c683faf689 100644
--- a/charts/spiderpool/crds/spiderpool.spidernet.io_spidercoordinators.yaml
+++ b/charts/spiderpool/crds/spiderpool.spidernet.io_spidercoordinators.yaml
@@ -40,11 +40,15 @@ spec:
properties:
detectGateway:
default: false
- description: DetectGateway to detect the gateway for the pod
+ description: 'DetectGateway to detect the gateway for the pod Deprecated:
+ gateway detection is now done by IPAM, setting this value has no
+ effect,this will be removed in the future.'
type: boolean
detectIPConflict:
default: false
- description: DetectIPConflict to detect the ip conflict for the pod
+ description: 'DetectIPConflict to detect the ip conflict for the pod
+ Deprecated: IP conflict detection is now done by IPAM, setting this
+ value has no effect,this will be removed in the future.'
type: boolean
hijackCIDR:
description: HijackCIDR configure static routing tables in the pod
diff --git a/charts/spiderpool/crds/spiderpool.spidernet.io_spidermultusconfigs.yaml b/charts/spiderpool/crds/spiderpool.spidernet.io_spidermultusconfigs.yaml
index 01daa9e897..1e491dec74 100644
--- a/charts/spiderpool/crds/spiderpool.spidernet.io_spidermultusconfigs.yaml
+++ b/charts/spiderpool/crds/spiderpool.spidernet.io_spidermultusconfigs.yaml
@@ -59,12 +59,15 @@ spec:
properties:
detectGateway:
default: false
- description: DetectGateway to detect the gateway for the pod
+ description: 'DetectGateway to detect the gateway for the pod
+ Deprecated: gateway detection is now done by IPAM, setting this
+ value has no effect,this will be removed in the future.'
type: boolean
detectIPConflict:
default: false
- description: DetectIPConflict to detect the ip conflict for the
- pod
+ description: 'DetectIPConflict to detect the ip conflict for the
+ pod Deprecated: IP conflict detection is now done by IPAM, setting
+ this value has no effect,this will be removed in the future.'
type: boolean
hijackCIDR:
description: HijackCIDR configure static routing tables in the
diff --git a/charts/spiderpool/templates/configmap.yaml b/charts/spiderpool/templates/configmap.yaml
index ff3cd269ec..80f665a1c6 100644
--- a/charts/spiderpool/templates/configmap.yaml
+++ b/charts/spiderpool/templates/configmap.yaml
@@ -21,6 +21,8 @@ data:
enableStatefulSet: {{ .Values.ipam.enableStatefulSet }}
enableKubevirtStaticIP: {{ .Values.ipam.enableKubevirtStaticIP }}
enableSpiderSubnet: {{ .Values.ipam.enableSpiderSubnet }}
+ enableIPConflictDetection: {{ .Values.ipam.enableIPConflictDetection }}
+ enableGatewayDetection: {{ .Values.ipam.enableGatewayDetection }}
{{- if .Values.ipam.enableSpiderSubnet }}
clusterSubnetDefaultFlexibleIPNumber: {{ .Values.ipam.subnetDefaultFlexibleIPNumber }}
{{- else}}
diff --git a/charts/spiderpool/values.yaml b/charts/spiderpool/values.yaml
index 85951e6e28..175f3f212d 100644
--- a/charts/spiderpool/values.yaml
+++ b/charts/spiderpool/values.yaml
@@ -56,6 +56,12 @@ ipam:
## @param ipam.enableSpiderSubnet SpiderSubnet feature gate.
enableSpiderSubnet: true
+ ## @param ipam.enableIPConflictDetection enable IP conflict detection
+ enableIPConflictDetection: false
+
+ ## @param ipam.enableGatewayDetection enable gateway detection
+ enableGatewayDetection: false
+
## @param ipam.subnetDefaultFlexibleIPNumber the default flexible IP number of SpiderSubnet feature auto-created IPPools
subnetDefaultFlexibleIPNumber: 1
diff --git a/cmd/coordinator/cmd/cni_types.go b/cmd/coordinator/cmd/cni_types.go
index 430a5dd4b9..5ea005d359 100644
--- a/cmd/coordinator/cmd/cni_types.go
+++ b/cmd/coordinator/cmd/cni_types.go
@@ -11,7 +11,6 @@ import (
"path/filepath"
"regexp"
"strings"
- "time"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/version"
@@ -44,24 +43,21 @@ const (
type Config struct {
types.NetConf
- DetectGateway *bool `json:"detectGateway,omitempty"`
- VethLinkAddress string `json:"vethLinkAddress,omitempty"`
- MacPrefix string `json:"podMACPrefix,omitempty"`
- MultusNicPrefix string `json:"multusNicPrefix,omitempty"`
- PodDefaultCniNic string `json:"podDefaultCniNic,omitempty"`
- OverlayPodCIDR []string `json:"overlayPodCIDR,omitempty"`
- ServiceCIDR []string `json:"serviceCIDR,omitempty"`
- HijackCIDR []string `json:"hijackCIDR,omitempty"`
- TunePodRoutes *bool `json:"tunePodRoutes,omitempty"`
- PodDefaultRouteNIC string `json:"podDefaultRouteNic,omitempty"`
- Mode Mode `json:"mode,omitempty"`
- HostRuleTable *int64 `json:"hostRuleTable,omitempty"`
- HostRPFilter *int32 `json:"hostRPFilter,omitempty" `
- PodRPFilter *int32 `json:"podRPFilter,omitempty" `
- TxQueueLen *int64 `json:"txQueueLen,omitempty"`
- IPConflict *bool `json:"detectIPConflict,omitempty"`
- DetectOptions *DetectOptions `json:"detectOptions,omitempty"`
- LogOptions *LogOptions `json:"logOptions,omitempty"`
+ VethLinkAddress string `json:"vethLinkAddress,omitempty"`
+ MacPrefix string `json:"podMACPrefix,omitempty"`
+ MultusNicPrefix string `json:"multusNicPrefix,omitempty"`
+ PodDefaultCniNic string `json:"podDefaultCniNic,omitempty"`
+ OverlayPodCIDR []string `json:"overlayPodCIDR,omitempty"`
+ ServiceCIDR []string `json:"serviceCIDR,omitempty"`
+ HijackCIDR []string `json:"hijackCIDR,omitempty"`
+ TunePodRoutes *bool `json:"tunePodRoutes,omitempty"`
+ PodDefaultRouteNIC string `json:"podDefaultRouteNic,omitempty"`
+ Mode Mode `json:"mode,omitempty"`
+ HostRuleTable *int64 `json:"hostRuleTable,omitempty"`
+ HostRPFilter *int32 `json:"hostRPFilter,omitempty" `
+ PodRPFilter *int32 `json:"podRPFilter,omitempty" `
+ TxQueueLen *int64 `json:"txQueueLen,omitempty"`
+ LogOptions *LogOptions `json:"logOptions,omitempty"`
}
// DetectOptions enable ip conflicting check for pod's ip
@@ -142,15 +138,6 @@ func ParseConfig(stdin []byte, coordinatorConfig *models.CoordinatorConfig) (*Co
return nil, err
}
- if conf.IPConflict == nil && coordinatorConfig.DetectIPConflict {
- conf.IPConflict = ptr.To(true)
- }
-
- conf.DetectOptions, err = ValidateDelectOptions(conf.DetectOptions)
- if err != nil {
- return nil, err
- }
-
if conf.HostRuleTable == nil && coordinatorConfig.HostRuleTable > 0 {
conf.HostRuleTable = ptr.To(coordinatorConfig.HostRuleTable)
}
@@ -163,10 +150,6 @@ func ParseConfig(stdin []byte, coordinatorConfig *models.CoordinatorConfig) (*Co
conf.HostRuleTable = ptr.To(int64(500))
}
- if conf.DetectGateway == nil {
- conf.DetectGateway = ptr.To(coordinatorConfig.DetectGateway)
- }
-
if conf.TunePodRoutes == nil {
conf.TunePodRoutes = coordinatorConfig.TunePodRoutes
}
@@ -270,37 +253,3 @@ func validateRPFilterConfig(rpfilter *int32, coordinatorConfig int64) (*int32, e
}
return rpfilter, nil
}
-
-func ValidateDelectOptions(config *DetectOptions) (*DetectOptions, error) {
- if config == nil {
- return &DetectOptions{
- Interval: "10ms",
- TimeOut: "100ms",
- Retry: 3,
- }, nil
- }
-
- if config.Retry == 0 {
- config.Retry = 3
- }
-
- if config.Interval == "" {
- config.Interval = "10ms"
- }
-
- if config.TimeOut == "" {
- config.TimeOut = "500ms"
- }
-
- _, err := time.ParseDuration(config.Interval)
- if err != nil {
- return nil, fmt.Errorf("invalid detectOptions.interval %s: %v, input like: 1s or 1m", config.Interval, err)
- }
-
- _, err = time.ParseDuration(config.TimeOut)
- if err != nil {
- return nil, fmt.Errorf("invalid detectOptions.timeout %s: %v, input like: 1s or 1m", config.TimeOut, err)
- }
-
- return config, nil
-}
diff --git a/cmd/coordinator/cmd/command_add.go b/cmd/coordinator/cmd/command_add.go
index 1d0057c75c..75dace53e1 100644
--- a/cmd/coordinator/cmd/command_add.go
+++ b/cmd/coordinator/cmd/command_add.go
@@ -4,10 +4,7 @@
package cmd
import (
- "context"
- "errors"
"fmt"
- "net"
"time"
"github.com/containernetworking/cni/pkg/skel"
@@ -15,17 +12,13 @@ import (
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/vishvananda/netlink"
- "go.uber.org/multierr"
"go.uber.org/zap"
"github.com/spidernet-io/spiderpool/api/v1/agent/client/daemonset"
"github.com/spidernet-io/spiderpool/api/v1/agent/models"
plugincmd "github.com/spidernet-io/spiderpool/cmd/spiderpool/cmd"
"github.com/spidernet-io/spiderpool/pkg/constant"
- "github.com/spidernet-io/spiderpool/pkg/errgroup"
"github.com/spidernet-io/spiderpool/pkg/logutils"
- "github.com/spidernet-io/spiderpool/pkg/networking/gwconnection"
- "github.com/spidernet-io/spiderpool/pkg/networking/ipchecking"
"github.com/spidernet-io/spiderpool/pkg/networking/networking"
"github.com/spidernet-io/spiderpool/pkg/networking/sysctl"
"github.com/spidernet-io/spiderpool/pkg/openapi"
@@ -118,6 +111,7 @@ func CmdAdd(args *skel.CmdArgs) (err error) {
if err != nil {
return fmt.Errorf("failed to get current netns: %v", err)
}
+ defer c.hostNs.Close()
logger.Sugar().Debugf("Get current host netns: %v", c.hostNs.Path())
// checking if the nic is in up state
@@ -189,43 +183,14 @@ func CmdAdd(args *skel.CmdArgs) (err error) {
logger.Sugar().Infof("Get coordinator config: %+v", c)
- errgConflict := errgroup.Group{}
- // IP conflict detection must precede gateway detection, which avoids the
- // possibility that gateway detection may update arp table entries first and cause
- // communication problems when IP conflict detection fails
- // see https://github.com/spidernet-io/spiderpool/issues/4475
- var ipc *ipchecking.IPChecker
- if conf.IPConflict != nil && *conf.IPConflict {
- logger.Debug("Try to detect ip conflict")
- ipc, err = ipchecking.NewIPChecker(conf.DetectOptions.Retry, conf.DetectOptions.Interval, conf.DetectOptions.TimeOut, c.hostNs, c.netns, logger)
- if err != nil {
- return fmt.Errorf("failed to run NewIPChecker: %w", err)
- }
- ipc.DoIPConflictChecking(prevResult.IPs, c.currentInterface, &errgConflict)
- } else {
- logger.Debug("disable detect ip conflict")
+ // get ips of this interface(preInterfaceName) from, including ipv4 and ipv6
+ c.currentAddress, err = networking.IPAddressByName(c.netns, args.IfName, ipFamily)
+ if err != nil {
+ logger.Error(err.Error())
+ return fmt.Errorf("failed to IPAddressByName for pod %s : %v", args.IfName, err)
}
- if err = errgConflict.Wait(); err != nil {
- logger.Error("failed to detect ip conflict", zap.Error(err))
- if errors.Is(err, constant.ErrIPConflict) {
- logger.Info("ip conflict detected, clean up conflict IPs")
- _, innerErr := client.Daemonset.DeleteIpamIps(daemonset.NewDeleteIpamIpsParams().WithContext(context.TODO()).WithIpamBatchDelArgs(
- &models.IpamBatchDelArgs{
- ContainerID: &args.ContainerID,
- NetNamespace: args.Netns,
- PodName: (*string)(&k8sArgs.K8S_POD_NAME),
- PodNamespace: (*string)(&k8sArgs.K8S_POD_NAMESPACE),
- PodUID: (*string)(&k8sArgs.K8S_POD_UID),
- },
- ))
- if innerErr != nil {
- logger.Sugar().Errorf("failed to clean up conflict IPs, error: %v", innerErr)
- return multierr.Append(err, innerErr)
- }
- }
- return err
- }
+ logger.Debug("Get currentAddress", zap.Any("currentAddress", c.currentAddress))
// Fixed Mac addresses must come after IP conflict detection, otherwise the switch learns to communicate
// with the wrong Mac address when IP conflict detection fails
@@ -235,65 +200,12 @@ func CmdAdd(args *skel.CmdArgs) (err error) {
return fmt.Errorf("failed to update hardware address for interface %s, maybe hardware_prefix(%s) is invalid: %v", args.IfName, conf.MacPrefix, err)
}
logger.Info("Fix mac address successfully", zap.String("interface", args.IfName), zap.String("macAddress", hwAddr))
- }
-
- // we do detect gateway connection lastly
- // Finally, there is gateway detection, which updates the correct arp table entries
- // once there are no IP address conflicts and fixed Mac addresses
- errgGateway := errgroup.Group{}
- if conf.DetectGateway != nil && *conf.DetectGateway {
- logger.Debug("Try to detect gateway")
- var gws []net.IP
- err = c.netns.Do(func(netNS ns.NetNS) error {
- gws, err = networking.GetDefaultGatewayByName(c.currentInterface, c.ipFamily)
- if err != nil {
- logger.Error("failed to GetDefaultGatewayByName", zap.Error(err))
- return fmt.Errorf("failed to GetDefaultGatewayByName: %v", err)
- }
- logger.Debug("Get GetDefaultGatewayByName", zap.Any("Gws", gws))
- p, err := gwconnection.New(conf.DetectOptions.Retry, conf.DetectOptions.Interval, conf.DetectOptions.TimeOut, c.currentInterface, logger)
- if err != nil {
- return fmt.Errorf("failed to init the gateway client: %v", err)
- }
- p.ParseAddrFromPreresult(prevResult.IPs)
- for _, gw := range gws {
- if gw.To4() != nil {
- p.V4Gw = gw
- errgGateway.Go(c.hostNs, c.netns, p.ArpingOverIface)
- } else {
- p.V6Gw = gw
- errgGateway.Go(c.hostNs, c.netns, p.NDPingOverIface)
- }
- }
- return nil
- })
- if err != nil {
- return err
+ if err = c.netns.Do(func(_ ns.NetNS) error {
+ return c.AnnounceIPs(logger)
+ }); err != nil {
+ logger.Error("failed to AnnounceIPs", zap.Error(err))
}
- } else {
- logger.Debug("disable detect gateway")
- }
-
- if err = errgGateway.Wait(); err != nil {
- logger.Error("failed to detect gateway reachable", zap.Error(err))
- if errors.Is(err, constant.ErrGatewayUnreachable) {
- logger.Info("gateway unreachable detected, clean up conflict IPs")
- _, innerErr := client.Daemonset.DeleteIpamIps(daemonset.NewDeleteIpamIpsParams().WithContext(context.TODO()).WithIpamBatchDelArgs(
- &models.IpamBatchDelArgs{
- ContainerID: &args.ContainerID,
- NetNamespace: args.Netns,
- PodName: (*string)(&k8sArgs.K8S_POD_NAME),
- PodNamespace: (*string)(&k8sArgs.K8S_POD_NAMESPACE),
- PodUID: (*string)(&k8sArgs.K8S_POD_UID),
- },
- ))
- if innerErr != nil {
- logger.Sugar().Errorf("failed to clean up conflict IPs, error: %v", innerErr)
- return multierr.Append(err, innerErr)
- }
- }
- return err
}
// set txqueuelen
@@ -305,18 +217,11 @@ func CmdAdd(args *skel.CmdArgs) (err error) {
// =================================
- // get ips of this interface(preInterfaceName) from, including ipv4 and ipv6
- c.currentAddress, err = networking.IPAddressByName(c.netns, args.IfName, ipFamily)
- if err != nil {
- logger.Error(err.Error())
- return fmt.Errorf("failed to IPAddressByName for pod %s : %v", args.IfName, err)
- }
-
- logger.Debug("Get currentAddress", zap.Any("currentAddress", c.currentAddress))
-
if ipFamily != netlink.FAMILY_V4 {
// ensure ipv6 is enable
- if err := sysctl.EnableIpv6Sysctl(c.netns, 0); err != nil {
+ if err = c.netns.Do(func(nn ns.NetNS) error {
+ return sysctl.EnableIpv6Sysctl(0)
+ }); err != nil {
logger.Error(err.Error())
return err
}
diff --git a/cmd/coordinator/cmd/utils.go b/cmd/coordinator/cmd/utils.go
index c7696ca15c..461e4ad26f 100644
--- a/cmd/coordinator/cmd/utils.go
+++ b/cmd/coordinator/cmd/utils.go
@@ -11,6 +11,7 @@ import (
"github.com/cilium/cilium/pkg/mac"
"github.com/containernetworking/plugins/pkg/ip"
"github.com/containernetworking/plugins/pkg/ns"
+ "github.com/mdlayher/ndp"
"github.com/vishvananda/netlink"
"go.uber.org/zap"
"golang.org/x/sys/unix"
@@ -703,3 +704,38 @@ OUTER2:
return finalNodeIpList, nil
}
+
+func (c *coordinator) AnnounceIPs(logger *zap.Logger) error {
+ l, err := netlink.LinkByName(c.currentInterface)
+ if err != nil {
+ return err
+ }
+
+ for _, addr := range c.currentAddress {
+ if addr.IP.To4() != nil {
+ // send an gratuitous arp to announce the new mac address
+ if err = networking.SendARPReuqest(l, addr.IP, addr.IP); err != nil {
+ logger.Error("failed to send gratuitous arps", zap.Error(err))
+ } else {
+ logger.Debug("Send gratuitous arps successfully", zap.String("interface", c.currentInterface))
+ }
+ } else {
+ ifi, err := net.InterfaceByName(c.currentInterface)
+ if err != nil {
+ return fmt.Errorf("failed to InterfaceByName %s: %w", c.currentInterface, err)
+ }
+
+ ndpClient, _, err := ndp.Listen(ifi, ndp.LinkLocal)
+ if err != nil {
+ return fmt.Errorf("failed to init ndp client: %w", err)
+ }
+ defer ndpClient.Close()
+ if err = networking.SendUnsolicitedNeighborAdvertisement(addr.IP, ifi, ndpClient); err != nil {
+ logger.Error("failed to send unsolicited neighbor advertisements", zap.Error(err))
+ } else {
+ logger.Debug("Send unsolicited neighbor advertisements successfully", zap.String("interface", c.currentInterface))
+ }
+ }
+ }
+ return nil
+}
diff --git a/cmd/spiderpool-agent/cmd/config.go b/cmd/spiderpool-agent/cmd/config.go
index 18987cea6c..f3211befe6 100644
--- a/cmd/spiderpool-agent/cmd/config.go
+++ b/cmd/spiderpool-agent/cmd/config.go
@@ -101,6 +101,8 @@ type Config struct {
EnableKubevirtStaticIP bool `yaml:"enableKubevirtStaticIP"`
EnableSpiderSubnet bool `yaml:"enableSpiderSubnet"`
TuneSysctlConfig bool `yaml:"tuneSysctlConfig"`
+ EnableIPConflictDetection bool `yaml:"enableIPConflictDetection"`
+ EnableGatewayDetection bool `yaml:"enableGatewayDetection"`
ClusterSubnetDefaultFlexibleIPNum int `yaml:"clusterSubnetDefaultFlexibleIPNumber"`
}
diff --git a/cmd/spiderpool-agent/cmd/coordinator.go b/cmd/spiderpool-agent/cmd/coordinator.go
index 1697d2a8ca..a90a4d0857 100644
--- a/cmd/spiderpool-agent/cmd/coordinator.go
+++ b/cmd/spiderpool-agent/cmd/coordinator.go
@@ -8,9 +8,6 @@ import (
"github.com/go-openapi/runtime/middleware"
corev1 "k8s.io/api/core/v1"
- apierrors "k8s.io/apimachinery/pkg/api/errors"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- kubevirtv1 "kubevirt.io/api/core/v1"
"github.com/spidernet-io/spiderpool/api/v1/agent/models"
"github.com/spidernet-io/spiderpool/api/v1/agent/server/restapi/daemonset"
@@ -28,7 +25,6 @@ func (g *_unixGetCoordinatorConfig) Handle(params daemonset.GetCoordinatorConfig
ctx := params.HTTPRequest.Context()
crdClient := agentContext.CRDManager.GetClient()
podClient := agentContext.PodManager
- kubevirtMgr := agentContext.KubevirtManager
var coordList spiderpoolv2beta1.SpiderCoordinatorList
if err := crdClient.List(ctx, &coordList); err != nil {
@@ -52,38 +48,6 @@ func (g *_unixGetCoordinatorConfig) Handle(params daemonset.GetCoordinatorConfig
return daemonset.NewGetCoordinatorConfigFailure().WithPayload(models.Error(fmt.Sprintf("failed to get pod %s/%s", params.GetCoordinatorConfig.PodNamespace, params.GetCoordinatorConfig.PodName)))
}
- isVMPod := false
- // kubevirt vm pod corresponding SpiderEndpoint uses kubevirt VM/VMI name
- ownerReference := metav1.GetControllerOf(pod)
- if ownerReference != nil && agentContext.Cfg.EnableKubevirtStaticIP && ownerReference.APIVersion == kubevirtv1.SchemeGroupVersion.String() && ownerReference.Kind == constant.KindKubevirtVMI {
- isVMPod = true
- }
-
- // cancel IP conflict detection for the kubevirt vm live migration new pod
- detectIPConflict := *coord.Spec.DetectIPConflict
- if detectIPConflict && isVMPod {
- // the live migration new pod has the annotation "kubevirt.io/migrationJobName"
- // we just only cancel IP conflict detection for the live migration new pod.
- podAnnos := pod.GetAnnotations()
- vmimName, ok := podAnnos[kubevirtv1.MigrationJobNameAnnotation]
- if ok {
- _, err := kubevirtMgr.GetVMIMByName(ctx, pod.Namespace, vmimName, false)
- if nil != err {
- if apierrors.IsNotFound(err) {
- logger.Sugar().Warnf("no kubevirt vm pod '%s/%s' corresponding VirtualMachineInstanceMigration '%s/%s' found, still execute IP conflict detection",
- pod.Namespace, pod.Name, pod.Namespace, vmimName)
- } else {
- return daemonset.NewGetCoordinatorConfigFailure().WithPayload(models.Error(fmt.Sprintf("failed to get kubevirt vm pod '%s/%s' corresponding VirtualMachineInstanceMigration '%s/%s', error: %v",
- pod.Namespace, pod.Name, pod.Namespace, vmimName, err)))
- }
- } else {
- // cancel IP conflict detection because there's a moment the old vm pod still running during the vm live migration phase
- logger.Sugar().Infof("cancel IP conflict detection for live migration new pod '%s/%s'", pod.Namespace, pod.Name)
- detectIPConflict = false
- }
- }
- }
-
var prefix string
if coord.Spec.PodMACPrefix != nil {
prefix = *coord.Spec.PodMACPrefix
@@ -116,8 +80,6 @@ func (g *_unixGetCoordinatorConfig) Handle(params daemonset.GetCoordinatorConfig
HostRuleTable: int64(*coord.Spec.HostRuleTable),
PodRPFilter: int64(*coord.Spec.PodRPFilter),
TxQueueLen: int64(*coord.Spec.TxQueueLen),
- DetectGateway: *coord.Spec.DetectGateway,
- DetectIPConflict: detectIPConflict,
}
if config.OverlayPodCIDR == nil {
diff --git a/cmd/spiderpool-agent/cmd/daemon.go b/cmd/spiderpool-agent/cmd/daemon.go
index ec1c11fbf5..1f4195b3c1 100644
--- a/cmd/spiderpool-agent/cmd/daemon.go
+++ b/cmd/spiderpool-agent/cmd/daemon.go
@@ -161,6 +161,8 @@ func DaemonMain() {
EnableStatefulSet: agentContext.Cfg.EnableStatefulSet,
EnableKubevirtStaticIP: agentContext.Cfg.EnableKubevirtStaticIP,
EnableReleaseConflictIPsForStateless: agentContext.Cfg.EnableReleaseConflictIPsForStateless,
+ EnableIPConflictDetection: agentContext.Cfg.EnableIPConflictDetection,
+ EnableGatewayDetection: agentContext.Cfg.EnableGatewayDetection,
OperationRetries: agentContext.Cfg.WaitSubnetPoolMaxRetries,
OperationGapDuration: time.Duration(agentContext.Cfg.WaitSubnetPoolTime) * time.Second,
AgentNamespace: agentContext.Cfg.AgentPodNamespace,
@@ -398,8 +400,10 @@ func initAgentServiceManagers(ctx context.Context) {
logger.Debug("Begin to initialize IPPool manager")
ipPoolManager, err := ippoolmanager.NewIPPoolManager(
ippoolmanager.IPPoolManagerConfig{
- MaxAllocatedIPs: &agentContext.Cfg.IPPoolMaxAllocatedIPs,
- EnableKubevirtStaticIP: agentContext.Cfg.EnableKubevirtStaticIP,
+ MaxAllocatedIPs: &agentContext.Cfg.IPPoolMaxAllocatedIPs,
+ EnableKubevirtStaticIP: agentContext.Cfg.EnableKubevirtStaticIP,
+ EnableIPConflictDetection: agentContext.Cfg.EnableIPConflictDetection,
+ EnableGatewayDetection: agentContext.Cfg.EnableGatewayDetection,
},
agentContext.CRDManager.GetClient(),
agentContext.CRDManager.GetAPIReader(),
diff --git a/cmd/spiderpool-agent/cmd/ipam.go b/cmd/spiderpool-agent/cmd/ipam.go
index 21cadca475..18f2ff4e0d 100644
--- a/cmd/spiderpool-agent/cmd/ipam.go
+++ b/cmd/spiderpool-agent/cmd/ipam.go
@@ -123,7 +123,7 @@ type _unixDeleteAgentIpamIps struct{}
// Handle handles DELETE requests for /ipam/ips.
func (g *_unixDeleteAgentIpamIps) Handle(params daemonset.DeleteIpamIpsParams) middleware.Responder {
err := params.IpamBatchDelArgs.Validate(strfmt.Default)
- if nil != err {
+ if err != nil {
return daemonset.NewDeleteIpamIpsFailure().WithPayload(models.Error(err.Error()))
}
diff --git a/cmd/spiderpool-init/cmd/config.go b/cmd/spiderpool-init/cmd/config.go
index c45c3587b1..1e7c677e4c 100644
--- a/cmd/spiderpool-init/cmd/config.go
+++ b/cmd/spiderpool-init/cmd/config.go
@@ -71,8 +71,6 @@ type InitDefaultConfig struct {
CoordinatorPodDefaultRouteNic string
CoordinatorPodMACPrefix string
CoordinatorVethLinkAddress string
- CoordinatorDetectGateway bool
- CoordinatorDetectIPConflict bool
CoordinatorTunePodRoutes bool
CoordinatorHijackCIDR []string
@@ -127,20 +125,6 @@ func parseENVAsDefault() InitDefaultConfig {
}
config.CoordinatorPodCIDRType = strings.ReplaceAll(os.Getenv(ENVDefaultCoordinatorPodCIDRType), "\"", "")
- edg := strings.ReplaceAll(os.Getenv(ENVDefaultCoordinatorDetectGateway), "\"", "")
- dg, err := strconv.ParseBool(edg)
- if err != nil {
- logger.Sugar().Fatalf("ENV %s %s: %v", ENVDefaultCoordinatorDetectGateway, edg, err)
- }
- config.CoordinatorDetectGateway = dg
-
- edic := strings.ReplaceAll(os.Getenv(ENVDefaultCoordinatorDetectIPConflict), "\"", "")
- dic, err := strconv.ParseBool(edic)
- if err != nil {
- logger.Sugar().Fatalf("ENV %s %s: %v", ENVDefaultCoordinatorDetectIPConflict, edic, err)
- }
- config.CoordinatorDetectIPConflict = dic
-
etpr := strings.ReplaceAll(os.Getenv(ENVDefaultCoordinatorTunePodRoutes), "\"", "")
tpr, err := strconv.ParseBool(etpr)
if err != nil {
diff --git a/cmd/spiderpool-init/cmd/root.go b/cmd/spiderpool-init/cmd/root.go
index 672faa4603..a023c4771e 100644
--- a/cmd/spiderpool-init/cmd/root.go
+++ b/cmd/spiderpool-init/cmd/root.go
@@ -44,8 +44,6 @@ func Execute() {
Mode: &config.CoordinatorMode,
PodCIDRType: &config.CoordinatorPodCIDRType,
TunePodRoutes: &config.CoordinatorTunePodRoutes,
- DetectIPConflict: &config.CoordinatorDetectIPConflict,
- DetectGateway: &config.CoordinatorDetectGateway,
PodDefaultRouteNIC: &config.CoordinatorPodDefaultRouteNic,
PodMACPrefix: &config.CoordinatorPodMACPrefix,
VethLinkAddress: &config.CoordinatorVethLinkAddress,
diff --git a/cmd/spiderpool/cmd/command_add.go b/cmd/spiderpool/cmd/command_add.go
index 079a747183..7525f9b84d 100644
--- a/cmd/spiderpool/cmd/command_add.go
+++ b/cmd/spiderpool/cmd/command_add.go
@@ -5,6 +5,7 @@ package cmd
import (
"context"
+ "errors"
"fmt"
"net"
"runtime/debug"
@@ -13,12 +14,16 @@ import (
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
current "github.com/containernetworking/cni/pkg/types/100"
+ "github.com/containernetworking/plugins/pkg/ns"
"github.com/go-openapi/strfmt"
+ "github.com/vishvananda/netlink"
+ "go.uber.org/multierr"
"go.uber.org/zap"
"github.com/spidernet-io/spiderpool/api/v1/agent/client/connectivity"
"github.com/spidernet-io/spiderpool/api/v1/agent/client/daemonset"
"github.com/spidernet-io/spiderpool/api/v1/agent/models"
+ "github.com/spidernet-io/spiderpool/pkg/constant"
spiderpoolip "github.com/spidernet-io/spiderpool/pkg/ip"
"github.com/spidernet-io/spiderpool/pkg/openapi"
)
@@ -26,7 +31,6 @@ import (
// CmdAdd follows CNI SPEC cmdAdd.
func CmdAdd(args *skel.CmdArgs) (err error) {
var logger *zap.Logger
-
// Defer a panic recover, so that in case we panic we can still return
// a proper error to the runtime.
defer func() {
@@ -39,7 +43,7 @@ func CmdAdd(args *skel.CmdArgs) (err error) {
msg = fmt.Sprintf("%s: error=%v", msg, err.Error())
}
- if nil != logger {
+ if logger != nil {
logger.Sugar().Errorf("%s\n\n%s", msg, debug.Stack())
}
}
@@ -50,11 +54,43 @@ func CmdAdd(args *skel.CmdArgs) (err error) {
return fmt.Errorf("failed to load CNI network configuration: %v", err)
}
- logger, err = setupFileLogging(conf)
- if nil != err {
+ netns, err := ns.GetNS(args.Netns)
+ if err != nil {
+ return fmt.Errorf("failed to GetNS %q for pod: %v", args.Netns, err)
+ }
+ defer netns.Close()
+
+ logger, err = SetupFileLogging(conf)
+ if err != nil {
return fmt.Errorf("failed to setup file logging: %v", err)
}
+ // When IPAM is invoked, the NIC is down and must be set it up in order to detect IP conflicts and
+ // gateway reachability.
+ err = netns.Do(func(netNS ns.NetNS) error {
+ l, err := netlink.LinkByName(args.IfName)
+ if err != nil {
+ return fmt.Errorf("failed to get link: %w", err)
+ }
+
+ if err = netlink.LinkSetUp(l); err != nil {
+ return fmt.Errorf("failed to set link up: %w", err)
+ }
+
+ logger.Sugar().Debugf("Set link %s to up for IP conflict and gateway detection", args.IfName)
+ return nil
+ })
+
+ if err != nil {
+ return fmt.Errorf("failed to set link up: %w", err)
+ }
+
+ hostNs, err := ns.GetCurrentNS()
+ if err != nil {
+ return fmt.Errorf("failed to get current netns: %v", err)
+ }
+ defer hostNs.Close()
+
logger = logger.Named(BinNamePlugin).With(
zap.String("Action", "ADD"),
zap.String("ContainerID", args.ContainerID),
@@ -112,7 +148,7 @@ func CmdAdd(args *skel.CmdArgs) (err error) {
logger.Debug("Send IPAM request")
ipamResponse, err := spiderpoolAgentAPI.Daemonset.PostIpamIP(params)
- if nil != err {
+ if err != nil {
err := fmt.Errorf("%w: %v", ErrPostIPAM, err)
logger.Error(err.Error())
return err
@@ -125,6 +161,24 @@ func CmdAdd(args *skel.CmdArgs) (err error) {
return err
}
+ // do ip conflict and gateway detection
+ logger.Sugar().Info("postIpam response",
+ zap.Any("DNS", ipamResponse.Payload.DNS),
+ zap.Any("Routes", ipamResponse.Payload.Routes),
+ zap.Any("Ips", ipamResponse.Payload.Ips))
+
+ if err = DetectIPConflictAndGatewayReachable(logger, args.IfName, hostNs, netns, ipamResponse.Payload.Ips); err != nil {
+ if errors.Is(err, constant.ErrIPConflict) || errors.Is(err, constant.ErrGatewayUnreachable) {
+ logger.Info("failed to detect IP conflict or gateway unreachable, clean up IPs")
+ if e := deleteIpamIps(spiderpoolAgentAPI, args, k8sArgs); e != nil {
+ logger.Sugar().Errorf("failed to clean up conflict IPs, error: %v", e)
+ return multierr.Append(err, e)
+ }
+ logger.Info("Successfully cleaned up IPs")
+ }
+ return err
+ }
+
// Assemble the result of IPAM request response.
result, err := assembleResult(conf.CNIVersion, args.IfName, ipamResponse)
if err != nil {
diff --git a/cmd/spiderpool/cmd/command_delete.go b/cmd/spiderpool/cmd/command_delete.go
index 47d740a82a..e3d417b51a 100644
--- a/cmd/spiderpool/cmd/command_delete.go
+++ b/cmd/spiderpool/cmd/command_delete.go
@@ -46,7 +46,7 @@ func CmdDel(args *skel.CmdArgs) (err error) {
return fmt.Errorf("failed to load CNI network configuration: %v", err)
}
- logger, err = setupFileLogging(conf)
+ logger, err = SetupFileLogging(conf)
if nil != err {
return fmt.Errorf("failed to setup file logging: %v", err)
}
diff --git a/cmd/spiderpool/cmd/command_test.go b/cmd/spiderpool/cmd/command_test.go
index 6e784989ca..4a979049e1 100644
--- a/cmd/spiderpool/cmd/command_test.go
+++ b/cmd/spiderpool/cmd/command_test.go
@@ -16,10 +16,13 @@ import (
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
current "github.com/containernetworking/cni/pkg/types/100"
+ "github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/ghttp"
+ "github.com/vishvananda/netlink"
+ "go.uber.org/zap"
"k8s.io/utils/ptr"
"github.com/spidernet-io/spiderpool/api/v1/agent/models"
@@ -32,7 +35,6 @@ import (
)
const ifName string = "eth0"
-const nsPath string = "/some/where"
const containerID string = "dummy"
const CNITimeoutSec = 120
const CNILogFilePath = "/tmp/spiderpool.log"
@@ -49,6 +51,7 @@ var cniVersion string
var args *skel.CmdArgs
var netConf cmd.NetConf
var sockPath string
+var nsPath string
var addChan, delChan chan struct{}
@@ -64,17 +67,32 @@ type ConfigWorkableSets struct {
}
var _ = Describe("spiderpool plugin", Label("unittest", "ipam_plugin_test"), func() {
+ var fakeNs ns.NetNS
BeforeEach(func() {
// generate one temp unix file.
tempDir := GinkgoT().TempDir()
sockPath = tempDir + "/tmp.sock"
+ var err error
+ fakeNs, err = testutils.NewNS()
+ Expect(err).NotTo(HaveOccurred())
+ nsPath = fakeNs.Path()
+
+ err = fakeNs.Do(func(nn ns.NetNS) error {
+ return netlink.LinkAdd(&netlink.Dummy{
+ LinkAttrs: netlink.LinkAttrs{
+ Name: ifName,
+ }})
+ })
+ Expect(err).NotTo(HaveOccurred())
+
// cleanup the temp unix file at the end.
DeferCleanup(func() {
err := os.RemoveAll(sockPath)
Expect(err).NotTo(HaveOccurred())
err = os.RemoveAll(CNILogFilePath)
Expect(err).NotTo(HaveOccurred())
+ defer fakeNs.Close()
})
args = &skel.CmdArgs{
@@ -101,6 +119,7 @@ var _ = Describe("spiderpool plugin", Label("unittest", "ipam_plugin_test"), fun
Context("mock ipam plugin interacts with agent through unix socket", func() {
var server *ghttp.Server
BeforeEach(func() {
+ var err error
listener, err := net.Listen("unix", sockPath)
Expect(err).NotTo(HaveOccurred())
server = ghttp.NewUnstartedServer()
@@ -183,6 +202,7 @@ var _ = Describe("spiderpool plugin", Label("unittest", "ipam_plugin_test"), fun
netConfBytes, err := json.Marshal(netConf)
Expect(err).NotTo(HaveOccurred())
args.StdinData = netConfBytes
+ args.IfName = ifName
return args
}, nil, nil),
Entry("returning an error on POST IPAM with ADD", ConfigWorkableSets{isPreConfigGood: true, isHealthy: true, isPostIPAM: false}, func() *skel.CmdArgs {
@@ -251,6 +271,7 @@ var _ = Describe("spiderpool plugin", Label("unittest", "ipam_plugin_test"), fun
netConfBytes, err := json.Marshal(netConf)
Expect(err).NotTo(HaveOccurred())
args.StdinData = netConfBytes
+ Expect(err).NotTo(HaveOccurred())
return args
}, func() *models.IpamAddResponse {
ipamAddResp := &models.IpamAddResponse{
@@ -560,6 +581,110 @@ var _ = Describe("spiderpool plugin", Label("unittest", "ipam_plugin_test"), fun
})
})
+ Context("when detecting IP conflict and gateway reachability", func() {
+ var err error
+ var hostNs, containerNs ns.NetNS
+ var logger *zap.Logger
+ BeforeEach(func() {
+ logger, err = cmd.SetupFileLogging(&cmd.NetConf{
+ IPAM: cmd.IPAMConfig{
+ LogLevel: cmd.DefaultLogLevelStr,
+ },
+ })
+ Expect(err).NotTo(HaveOccurred())
+
+ hostNs, err = testutils.NewNS()
+ Expect(err).NotTo(HaveOccurred())
+
+ containerNs, err = testutils.NewNS()
+ Expect(err).NotTo(HaveOccurred())
+ DeferCleanup(func() {
+ hostNs.Close()
+ containerNs.Close()
+ })
+ })
+ It("Detection is disabled, just returni nil", func() {
+ // Set up test data and mocks
+ args := &skel.CmdArgs{
+ Netns: "test-netns",
+ IfName: "eth0",
+ }
+ ipamResponse := []*models.IPConfig{
+ {
+ Address: ptr.To("192.168.1.0/24"),
+ Gateway: "192.168.1.1",
+ Nic: ptr.To("eth0"),
+ },
+ }
+
+ // Mock ns.GetNS and ns.GetCurrentNS
+ // Mock DetectIPConflictAndGatewayReachable to return specific errors
+ // Mock deleteIpamIps to simulate cleanup
+
+ // Call the function under test
+ err = cmd.DetectIPConflictAndGatewayReachable(logger, args.IfName, hostNs, containerNs, ipamResponse)
+
+ // Assert the expected behavior
+ Expect(err).NotTo(HaveOccurred())
+ // gomega.Expect(err).To(gomega.SatisfyAny(
+ // gomega.MatchError(constant.ErrIPConflict),
+ // gomega.MatchError(constant.ErrGatewayUnreachable),
+ // ))
+ })
+
+ It("ipam allocated record is not for the interface, just return", func() {
+ // Set up test data and mocks
+ args := &skel.CmdArgs{
+ Netns: "test-netns",
+ IfName: "eth0",
+ }
+ ipamResponse := []*models.IPConfig{
+ {
+ Address: ptr.To("192.168.1.0/24"),
+ Gateway: "192.168.1.1",
+ EnableGatewayDetection: true,
+ Nic: ptr.To("net1"),
+ },
+ }
+
+ // Mock ns.GetNS and ns.GetCurrentNS
+ // Mock DetectIPConflictAndGatewayReachable to return specific errors
+ // Mock deleteIpamIps to simulate cleanup
+
+ // Call the function under test
+ err := cmd.DetectIPConflictAndGatewayReachable(logger, args.IfName, hostNs, containerNs, ipamResponse)
+
+ // Assert the expected behavior
+ Expect(err).NotTo(HaveOccurred())
+ })
+
+ It("ipam allocated record is not for the interface, just return", func() {
+ // Set up test data and mocks
+ args := &skel.CmdArgs{
+ Netns: "test-netns",
+ IfName: "eth0",
+ }
+ ipamResponse := []*models.IPConfig{
+ {
+ Address: ptr.To("192.168.1.0/24"),
+ Gateway: "192.168.1.1",
+ EnableGatewayDetection: true,
+ Nic: ptr.To("net1"),
+ },
+ }
+
+ // Mock ns.GetNS and ns.GetCurrentNS
+ // Mock DetectIPConflictAndGatewayReachable to return specific errors
+ // Mock deleteIpamIps to simulate cleanup
+
+ // Call the function under test
+ err := cmd.DetectIPConflictAndGatewayReachable(logger, args.IfName, hostNs, containerNs, ipamResponse)
+
+ // Assert the expected behavior
+ Expect(err).NotTo(HaveOccurred())
+ })
+ })
+
})
func getHealthHandleFunc(isHealthy bool) http.HandlerFunc {
diff --git a/cmd/spiderpool/cmd/ipam_detection.go b/cmd/spiderpool/cmd/ipam_detection.go
new file mode 100644
index 0000000000..68d6d786ac
--- /dev/null
+++ b/cmd/spiderpool/cmd/ipam_detection.go
@@ -0,0 +1,446 @@
+// Copyright 2025 Authors of spidernet-io
+// SPDX-License-Identifier: Apache-2.0
+
+package cmd
+
+import (
+ "context"
+ "fmt"
+ "net"
+ "syscall"
+ "time"
+
+ "github.com/containernetworking/plugins/pkg/ns"
+ "github.com/mdlayher/ndp"
+ "github.com/vishvananda/netlink"
+ "go.uber.org/zap"
+
+ "github.com/spidernet-io/spiderpool/api/v1/agent/models"
+ "github.com/spidernet-io/spiderpool/pkg/constant"
+ "github.com/spidernet-io/spiderpool/pkg/errgroup"
+ "github.com/spidernet-io/spiderpool/pkg/networking/networking"
+)
+
+var (
+ retryNum = 3
+ timeOut = 100 * time.Millisecond
+)
+
+type Detector struct {
+ logger *zap.Logger
+ enableIPv4ConflictDetection, enableIPv6ConflictDetection bool
+ enableIPv4GatewayReachableDetection, enableIPv6GatewayReachableDetection bool
+ retries int
+ iface string
+ timeout time.Duration
+ ip4, ip6, v4Gw, v6Gw net.IP
+}
+
+func DetectIPConflictAndGatewayReachable(logger *zap.Logger, iface string, hostNs ns.NetNS, netns ns.NetNS, ipconfigs []*models.IPConfig) error {
+ d := &Detector{
+ retries: retryNum,
+ timeout: timeOut,
+ iface: iface,
+ logger: logger,
+ }
+
+ var dectectIPs []*models.IPConfig
+ for _, ipa := range ipconfigs {
+ logger.Debug("IPAM Allocated Result", zap.Any("Result", ipa))
+ if *ipa.Nic != iface {
+ // spiderpool assigns IPs to all NICs in advance of the first call to ipam.
+ // different NICs come from different pools, so we only need to focus on the current NIC's ipconfig.
+ logger.Debug("In multi-cni mode, only the current CNI-assigned NIC will be detected for IPAM detection once", zap.String("nic", *ipa.Nic))
+ continue
+ }
+
+ if !ipa.EnableGatewayDetection && !ipa.EnableIPConflictDetection {
+ // IP conflict detection and gateway detection are disabled
+ logger.Debug("IP and Gateway detection is disabled")
+ continue
+ }
+ dectectIPs = append(dectectIPs, ipa)
+ }
+
+ if len(dectectIPs) == 0 {
+ logger.Debug("IP conflict detection and gateway detection are disabled")
+ return nil
+ }
+
+ errg := errgroup.Group{}
+ err := netns.Do(func(_ ns.NetNS) error {
+ for _, ipa := range dectectIPs {
+ if ipa.Version == nil {
+ return nil
+ }
+ ipaddress, _, err := net.ParseCIDR(*ipa.Address)
+ if err != nil {
+ return fmt.Errorf("failed to parse ipaddress %s: %w", *ipa.Address, err)
+ }
+
+ if *ipa.Version == int64(4) {
+ d.ip4 = ipaddress
+ d.enableIPv4ConflictDetection = ipa.EnableIPConflictDetection
+ if ipa.Gateway != "" {
+ d.enableIPv4GatewayReachableDetection = ipa.EnableGatewayDetection
+ d.v4Gw = net.ParseIP(ipa.Gateway)
+ }
+ logger.Info("IPv4 Detection Configs",
+ zap.String("iface", d.iface),
+ zap.Any("IP", ipaddress.String()),
+ zap.Any("Gateway", d.v4Gw),
+ zap.Bool("IPConflictDetection", d.enableIPv4ConflictDetection),
+ zap.Bool("GatewayDetection", d.enableIPv4GatewayReachableDetection),
+ )
+ if d.enableIPv4ConflictDetection || d.enableIPv4GatewayReachableDetection {
+ errg.Go(hostNs, netns, d.ARPDetect)
+ }
+ } else if *ipa.Version == int64(6) {
+ d.ip6 = ipaddress
+ d.enableIPv6ConflictDetection = ipa.EnableIPConflictDetection
+ if ipa.Gateway != "" {
+ d.enableIPv6GatewayReachableDetection = ipa.EnableGatewayDetection
+ d.v6Gw = net.ParseIP(ipa.Gateway)
+ }
+
+ logger.Info("IPv6 Detection Configs",
+ zap.String("Interface", d.iface),
+ zap.Any("IP", d.ip6),
+ zap.Any("Gateway", d.v6Gw),
+ zap.Bool("IPv6ConflictDetection", d.enableIPv6ConflictDetection),
+ zap.Bool("IPv6GatewayDetection", d.enableIPv6GatewayReachableDetection),
+ )
+ if d.enableIPv6ConflictDetection || d.enableIPv6GatewayReachableDetection {
+ errg.Go(hostNs, netns, d.NDPDetect)
+ }
+ }
+ }
+ return nil
+ })
+
+ if err != nil {
+ return fmt.Errorf("failed to init IP conflict and gateway detection: %w", err)
+ }
+
+ return errg.Wait()
+}
+
+func (d *Detector) ARPDetect() error {
+ l, err := netlink.LinkByName(d.iface)
+ if err != nil {
+ d.logger.Error("failed to get link", zap.Error(err))
+ return err
+ }
+
+ sock, err := networking.NewARPSockRAW(l)
+ if err != nil {
+ d.logger.Error("failed to create raw socket", zap.Error(err))
+ return err
+ }
+ defer syscall.Close(sock)
+
+ // IP conflict detection must precede gateway detection, which avoids the
+ // possibility that gateway detection may update arp table entries first and cause
+ // communication problems when IP conflict detection fails
+ // see https://github.com/spidernet-io/spiderpool/issues/4475
+ // call ip conflict detection
+ if d.enableIPv4ConflictDetection {
+ d.logger.Info("Detect IPAddress If Conflicts for IPv4", zap.String("IPAddress", d.ip4.String()))
+ err = d.detectIP4Conflicting(sock, l)
+ if err != nil {
+ return err
+ }
+ } else {
+ d.logger.Info("IPConflitingDetection is disabled for IPv4", zap.String("IPAddress", d.ip4.String()))
+ }
+
+ // we do detect gateway connection lastly
+ // Finally, there is gateway detection, which updates the correct arp table entries
+ // once there are no IP address conflicts and fixed Mac addresses
+ // call gateway detection
+ if d.enableIPv4GatewayReachableDetection {
+ d.logger.Info("Detect Gateway If reachable for IPv4", zap.String("IPAddress", d.ip4.String()), zap.String("Gateway", d.v4Gw.String()))
+ if err = d.detectGateway4Reachable(sock, l); err != nil {
+ return err
+ }
+ } else {
+ d.logger.Info("GatewayDetection is disabled for IPv4", zap.String("IPAddress", d.ip4.String()), zap.String("Gateway", d.v4Gw.String()))
+ }
+ return nil
+}
+
+func (d *Detector) NDPDetect() error {
+ ifi, err := net.InterfaceByName(d.iface)
+ if err != nil {
+ d.logger.Error("failed to InterfaceByName", zap.Error(err))
+ return fmt.Errorf("failed to InterfaceByName %s: %w", d.iface, err)
+ }
+
+ var ndpClient *ndp.Conn
+ // wait for ndp ready
+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ defer cancel()
+
+ // When DAD(Duplicate Address Detection) is enanled, the kernel will check if this local link address is in conflict,
+ // this may take a while, set the maximum timeout to 10s
+ ndpReady := false
+ for !ndpReady {
+ select {
+ case <-ctx.Done():
+ d.logger.Error("Waiting for the maximum timeout of 10s, the state of the local link address is still not READY.")
+ return fmt.Errorf("Waiting for the maximum timeout of 10s, the state of the local link address is still not READY.")
+ default:
+ ndpClient, _, err = ndp.Listen(ifi, ndp.LinkLocal)
+ if err == nil {
+ d.logger.Debug("ndp client is ready")
+ ndpReady = true
+ }
+ }
+ }
+ defer ndpClient.Close()
+
+ // IP conflict detection must precede gateway detection, which avoids the
+ // possibility that gateway detection may update arp table entries first and cause
+ // communication problems when IP conflict detection fails
+ // see https://github.com/spidernet-io/spiderpool/issues/4475
+ // call ip conflict detection
+ if d.enableIPv6ConflictDetection {
+ d.logger.Info("Detect IPAddress If conflict for IPv6", zap.String("IPAddress", d.ip6.String()))
+ err = d.detectIP6Conflicting(ifi, ndpClient)
+ if err != nil {
+ return err
+ }
+ } else {
+ d.logger.Info("IPConflitingDetection is disabled for IPv6", zap.String("IPAddress", d.ip6.String()))
+ }
+
+ // we do detect gateway connection lastly
+ // Finally, there is gateway detection, which updates the correct arp table entries
+ // once there are no IP address conflicts and fixed Mac addresses
+ // call gateway detection
+ if d.enableIPv6GatewayReachableDetection {
+ d.logger.Info("Detecting Gateway if reachable for IPv6", zap.String("IPAddress", d.ip6.String()), zap.String("Gateway", d.v6Gw.String()))
+ if err = d.detectGateway6Reachable(ifi, ndpClient); err != nil {
+ return err
+ }
+ } else {
+ d.logger.Info("GatewayDetection is disabled for IPv6", zap.String("IPAddress", d.ip6.String()), zap.String("Gateway", d.v6Gw.String()))
+ }
+ return nil
+}
+
+func (d *Detector) detectIP4Conflicting(sock int, l netlink.Link) error {
+ var err error
+ // Set a timeout of d.timeout for receiving packets
+ if err = networking.SetSocketTimeout(sock, d.timeout); err != nil {
+ return fmt.Errorf("failed to set socket timeout: %v", err)
+ }
+
+ // Buffer to hold incoming packets.
+ buf := make([]byte, 65536)
+
+ for i := 0; i < d.retries; i++ {
+ // we send a gratuitous arp to checking if ip is conflict
+ // we use dad mode(duplicate address detection mode), so
+ // we set source ip to 0.0.0.0
+ err = networking.SendARPReuqest(l, net.ParseIP("0.0.0.0"), d.ip4)
+ if err != nil {
+ d.logger.Error("failed to send ARP request, retrying...", zap.Error(err))
+ continue
+ }
+
+ d.logger.Debug("success to send ARP request to detecting if the IPv4 address conflicts")
+
+ for {
+ // Read a packet from the socket.
+ n, _, err := syscall.Recvfrom(sock, buf, 0)
+ if err == nil {
+ if n < 28 || buf[20] != 0x00 || buf[21] != 0x02 {
+ // Check if the packet is an ARP response (operation code 2).
+ continue
+ }
+
+ // Now we catch an ARP response
+ // Extract sender's IP and MAC addresses from the ARP response.
+ senderMAC := net.HardwareAddr(buf[22:28])
+ senderIP := net.IP(buf[28:32])
+
+ d.logger.Debug("Received packet from sender", zap.String("senderIP", senderIP.String()), zap.String("senderMAC", senderMAC.String()))
+ // Check if the sender's MAC address is the same as the interface's address
+ if senderIP.Equal(d.ip4) {
+ // found ip conflicting
+ d.logger.Error("IPv4 IPAddress Conflicts", zap.String("Conflicting IP", d.ip4.String()), zap.String("Host", senderMAC.String()))
+ return fmt.Errorf("%w: pod's interface %s with an conflicting ip %s, %s is located at %s",
+ constant.ErrIPConflict, d.iface, d.ip4.String(), d.ip4.String(), senderMAC.String())
+ }
+ continue
+ }
+
+ if err.(net.Error).Timeout() {
+ // If an arp reply is not received within the timeout period or is not
+ // a expected arp reply
+ d.logger.Info("No IPv4 address conflicts")
+ return nil
+ }
+
+ d.logger.Error("failed to receive packet, retrying...", zap.Error(err))
+ }
+ }
+
+ if err != nil {
+ d.logger.Error("after failed to send three ARP request packages, can't detect IPv4 address conflicting", zap.Error(err))
+ return fmt.Errorf("after failed to send three ARP request packages, can't detect IPv4 address conflicting: %w", err)
+ }
+ return nil
+}
+
+func (d *Detector) detectGateway4Reachable(sock int, l netlink.Link) error {
+ var err error
+ // Buffer to hold incoming packets.
+ buf := make([]byte, 65536)
+
+ // Set a timeout of d.timeout for receiving packets
+ if err := networking.SetSocketTimeout(sock, d.timeout); err != nil {
+ return fmt.Errorf("failed to set socket timeout: %v", err)
+ }
+
+ for i := 0; i < d.retries; i++ {
+ if err = networking.SendARPReuqest(l, d.ip4, d.v4Gw); err != nil {
+ d.logger.Error("failed to send ARP request, retrying...", zap.Error(err))
+ continue
+ }
+ d.logger.Debug("success to send ARP request to detect gateway reachable")
+
+ for {
+ // Read a packet from the socket.
+ n, _, err := syscall.Recvfrom(sock, buf, 0)
+ if err == nil {
+ if n < 28 || buf[20] != 0x00 || buf[21] != 0x02 {
+ // Check if the packet is an ARP response (operation code 2).
+ continue
+ }
+
+ // Now we catch an ARP response
+ // Extract sender's IP and MAC addresses from the ARP response.
+ senderMAC := net.HardwareAddr(buf[22:28])
+ senderIP := net.IP(buf[28:32])
+
+ d.logger.Debug("Received packet from sender", zap.String("senderIP", senderIP.String()), zap.String("senderMAC", senderMAC.String()))
+
+ // Check if the sender's MAC address is the same as the interface's address
+ if senderIP.Equal(d.v4Gw) {
+ d.logger.Sugar().Infof("Gateway %s is reachable, gateway is located at %v", d.v4Gw, senderMAC.String())
+ return nil
+ }
+ continue
+ }
+
+ if err.(net.Error).Timeout() {
+ // If an arp reply is not received within the timeout period or is not
+ // sent from the gateway IP, it is assumed that the gateway is not reachable.
+ d.logger.Sugar().Errorf("gateway %s is %v, reason: %v", d.v4Gw.String(), constant.ErrGatewayUnreachable, err)
+ return fmt.Errorf("gateway %s is %w", d.v4Gw.String(), constant.ErrGatewayUnreachable)
+ }
+ d.logger.Error("failed to receive packet, retring", zap.Error(err))
+ }
+ }
+
+ if err != nil {
+ d.logger.Error("after failed to send three ARP request packages, can't detect gateway reachable", zap.Error(err))
+ return fmt.Errorf("after failed to send three ARP request packages, can't detect gateway reachable: %w", err)
+ }
+
+ return nil
+}
+
+func (d *Detector) detectIP6Conflicting(ifi *net.Interface, ndpClient *ndp.Conn) error {
+ if err := ndpClient.SetReadDeadline(time.Now().Add(d.timeout)); err != nil {
+ d.logger.Error("failed to set read deadline", zap.Error(err))
+ }
+ var err error
+ for i := 0; i < d.retries; i++ {
+ err = networking.SendUnsolicitedNeighborAdvertisement(d.ip6, ifi, ndpClient)
+ if err != nil {
+ d.logger.Error("failed to send unsolicited neighbor advertisement, retrying...", zap.Error(err))
+ continue
+ }
+ d.logger.Info("success to send unsolicited neighbor advertisement")
+ for {
+ msg, _, _, err := ndpClient.ReadFrom()
+ if err == nil {
+ na, ok := msg.(*ndp.NeighborAdvertisement)
+ if !ok || na.TargetAddress.String() != d.ip6.String() || len(na.Options) != 1 {
+ continue
+ }
+
+ option, ok := na.Options[0].(*ndp.LinkLayerAddress)
+ if ok {
+ d.logger.Error("IPv6 address conflicts", zap.String("Conflicting IP", d.ip6.String()), zap.String("Host", option.Addr.String()))
+ return fmt.Errorf("%w: pod's interface %s with an conflicting ip %s, %s is located at %s", constant.ErrIPConflict, d.iface, d.ip6.String(), d.ip6.String(), option.Addr.String())
+ }
+ continue
+ }
+
+ // no ndp response unitil timeout, indicates gateway unreachable
+ if err.(net.Error).Timeout() {
+ d.logger.Info("No IPv6 address conflicts")
+ return nil
+ }
+ // retry it if is other error
+ d.logger.Error("failed to receive unsolicited neighbor advertisement message, retrying...", zap.Error(err))
+ }
+ }
+
+ if err != nil {
+ d.logger.Error("after failed to send three unsolicited neighbor advertisement packages, can't detect IPv6 address conflicting", zap.Error(err))
+ return fmt.Errorf("after failed to send three unsolicited neighbor advertisement packages, can't detect IPv6 address conflicting: %w", err)
+ }
+
+ return nil
+}
+
+func (d *Detector) detectGateway6Reachable(ifi *net.Interface, ndpClient *ndp.Conn) error {
+ err := ndpClient.SetReadDeadline(time.Now().Add(d.timeout))
+ if err != nil {
+ d.logger.Error("failed to set read deadline", zap.Error(err))
+ }
+ for i := 0; i < d.retries; i++ {
+ err = networking.SendUnsolicitedNeighborAdvertisement(d.v6Gw, ifi, ndpClient)
+ if err != nil {
+ d.logger.Error("failed to send unsolicited neighbor advertisement, retrying...", zap.Error(err))
+ continue
+ }
+
+ d.logger.Info("success to send unsolicited neighbor advertisement")
+ for {
+ msg, _, _, err := ndpClient.ReadFrom()
+ if err == nil {
+ na, ok := msg.(*ndp.NeighborAdvertisement)
+ if !ok || na.TargetAddress.String() != d.v6Gw.String() || len(na.Options) != 1 {
+ continue
+ }
+
+ option, ok := na.Options[0].(*ndp.LinkLayerAddress)
+ if ok {
+ d.logger.Sugar().Infof("gateway %s is located at %s", d.v6Gw.String(), option.Addr.String())
+ return nil
+ }
+ continue
+ }
+
+ // no ndp response unitil timeout, indicates gateway unreachable
+ if err.(net.Error).Timeout() {
+ d.logger.Sugar().Errorf("gateway %s is %s, reason: %v", d.v6Gw.String(), constant.ErrGatewayUnreachable, err)
+ return fmt.Errorf("gateway %s is %w", d.v6Gw.String(), constant.ErrGatewayUnreachable)
+ }
+ // retry it if is other error
+ d.logger.Error("failed to receive unsolicited neighbor advertisement message, retrying...", zap.Error(err))
+ }
+ }
+
+ if err != nil {
+ d.logger.Error("after failed to send three unsolicited neighbor advertisement packages, can't detect IPv6 Gateway if reachable", zap.Error(err))
+ return fmt.Errorf("after failed to send three unsolicited neighbor advertisement packages, can't detect IPv6 Gateway if reachable: %w", err)
+ }
+ return nil
+}
diff --git a/cmd/spiderpool/cmd/utils.go b/cmd/spiderpool/cmd/utils.go
index 5720ace474..bba1c5ee11 100644
--- a/cmd/spiderpool/cmd/utils.go
+++ b/cmd/spiderpool/cmd/utils.go
@@ -1,18 +1,23 @@
-// Copyright 2022 Authors of spidernet-io
+// Copyright 2025 Authors of spidernet-io
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
+ "context"
"fmt"
+ "github.com/containernetworking/cni/pkg/skel"
"go.uber.org/zap"
+ agentOpenAPIClient "github.com/spidernet-io/spiderpool/api/v1/agent/client"
+ "github.com/spidernet-io/spiderpool/api/v1/agent/client/daemonset"
+ "github.com/spidernet-io/spiderpool/api/v1/agent/models"
"github.com/spidernet-io/spiderpool/pkg/logutils"
)
// Set up file logging for spiderpool bin.
-func setupFileLogging(conf *NetConf) (*zap.Logger, error) {
+func SetupFileLogging(conf *NetConf) (*zap.Logger, error) {
v := logutils.ConvertLogLevel(conf.IPAM.LogLevel)
if v == nil {
return nil, fmt.Errorf("unsupported log level %s", conf.IPAM.LogLevel)
@@ -26,3 +31,16 @@ func setupFileLogging(conf *NetConf) (*zap.Logger, error) {
conf.IPAM.LogFileMaxCount,
)
}
+
+func deleteIpamIps(spiderpoolAgentAPI *agentOpenAPIClient.SpiderpoolAgentAPI, args *skel.CmdArgs, k8sArgs K8sArgs) error {
+ _, err := spiderpoolAgentAPI.Daemonset.DeleteIpamIps(daemonset.NewDeleteIpamIpsParams().WithContext(context.TODO()).WithIpamBatchDelArgs(
+ &models.IpamBatchDelArgs{
+ ContainerID: &args.ContainerID,
+ NetNamespace: args.Netns,
+ PodName: (*string)(&k8sArgs.K8S_POD_NAME),
+ PodNamespace: (*string)(&k8sArgs.K8S_POD_NAMESPACE),
+ PodUID: (*string)(&k8sArgs.K8S_POD_UID),
+ },
+ ))
+ return err
+}
diff --git a/cmd/spiderpool/main.go b/cmd/spiderpool/main.go
index b493042057..a7dd09fa28 100644
--- a/cmd/spiderpool/main.go
+++ b/cmd/spiderpool/main.go
@@ -4,6 +4,8 @@
package main
import (
+ "runtime"
+
"github.com/containernetworking/cni/pkg/skel"
cniSpecVersion "github.com/containernetworking/cni/pkg/version"
"github.com/spidernet-io/spiderpool/cmd/spiderpool/cmd"
@@ -12,6 +14,13 @@ import (
// version means spiderpool released version.
var version string
+func init() {
+ // this ensures that main runs only on main thread (thread group leader).
+ // since namespace ops (unshare, setns) are done for a single thread, we
+ // must ensure that the goroutine does not jump from OS thread to thread
+ runtime.LockOSThread()
+}
+
func main() {
skel.PluginMain(cmd.CmdAdd, cmdCheck, cmd.CmdDel,
cniSpecVersion.PluginSupports(cmd.SupportCNIVersions...),
diff --git a/docs/README-zh_CN.md b/docs/README-zh_CN.md
index 3c27e7a97d..0dd056115f 100644
--- a/docs/README-zh_CN.md
+++ b/docs/README-zh_CN.md
@@ -1,4 +1,4 @@
-#
+# Spiderpool

@@ -20,9 +20,7 @@ Spiderpool 提供了一个 Kubernetes 的 underlay 和 RDMA 网络解决方案,
## Spiderpool 介绍
-Spiderpool 是一个 kubernetes 的 underlay 和 RDMA 网络解决方案,它增强了 [Macvlan CNI](https://github.com/containernetworking/plugins/tree/main/plugins/main/macvlan)、
-[ipvlan CNI](https://github.com/containernetworking/plugins/tree/main/plugins/main/ipvlan) 和
-[SR-IOV CNI](https://github.com/k8snetworkplumbingwg/sriov-cni) 的功能,满足了各种网络需求,使得 underlay 网络方案可应用在**裸金属、虚拟机和公有云环境**中,可为网络 I/O 密集性、低延时应用带来优秀的网络性能,包括**存储、中间件、AI 等应用**。详细的文档可参考[文档站](https://spidernet-io.github.io/spiderpool/)。
+Spiderpool 是一个 kubernetes 的 underlay 和 RDMA 网络解决方案,它增强了 [Macvlan CNI](https://github.com/containernetworking/plugins/tree/main/plugins/main/macvlan)、[ipvlan CNI](https://github.com/containernetworking/plugins/tree/main/plugins/main/ipvlan) 和 [SR-IOV CNI](https://github.com/k8snetworkplumbingwg/sriov-cni) 的功能,满足了各种网络需求,使得 underlay 网络方案可应用在**裸金属、虚拟机和公有云环境**中,可为网络 I/O 密集性、低延时应用带来优秀的网络性能,包括**存储、中间件、AI 等应用**。详细的文档可参考[文档站](https://spidernet-io.github.io/spiderpool/)。
## Underlay CNI 的优势
@@ -160,6 +158,6 @@ We are a [Cloud Native Computing Foundation](https://www.cncf.io) [sandbox proje
The Linux Foundation® (TLF) has registered trademarks and uses trademarks. For a list of TLF trademarks, see [Trademark Usage](https://www.linuxfoundation.org/legal/trademark-usage).
-
 
+
 
diff --git a/docs/README.md b/docs/README.md
index 342bc4576f..7081f8f3d6 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -166,6 +166,6 @@ We are a [Cloud Native Computing Foundation](https://www.cncf.io) [sandbox proje
The Linux Foundation® (TLF) has registered trademarks and uses trademarks. For a list of TLF trademarks, see [Trademark Usage](https://www.linuxfoundation.org/legal/trademark-usage).
-
 
+
 
diff --git a/docs/concepts/coordinator-zh_CN.md b/docs/concepts/coordinator-zh_CN.md
index 1b01c1cf23..2454392f20 100644
--- a/docs/concepts/coordinator-zh_CN.md
+++ b/docs/concepts/coordinator-zh_CN.md
@@ -6,8 +6,8 @@ Spiderpool 内置一个叫 `coordinator` 的 CNI meta-plugin, 它在 Main CNI
- 解决 underlay Pod 无法访问 ClusterIP 的问题
- 在 Pod 多网卡时,调谐 Pod 的路由,确保数据包来回路径一致
-- 支持检测 Pod 的 IP 是否冲突
-- 支持检测 Pod 的网关是否可达
+- 支持检测 Pod 的 IP 是否冲突(遗弃,现在由 IPAM 完成)
+- 支持检测 Pod 的网关是否可达 (遗弃,现在由 IPAM 完成)
- 支持固定 Pod 的 Mac 地址前缀
注意: 如果您的操作系统是使用 NetworkManager 的 OS,比如 Fedora、Centos等,强烈建议配置 NetworkManager 的配置文件(/etc/NetworkManager/conf.d/spidernet.conf),避免 NetworkManager 干扰 `coordinator` 创建的 Veth 虚拟接口,影响通信:
@@ -31,15 +31,14 @@ EOF
| tunePodRoutes | Pod 多网卡模式下,是否调协 Pod 的路由,解决访问来回路径不一致的问题 | 布尔型 | optional | true |
| podDefaultRouteNic | Pod 多网卡时,配置 Pod 的默认路由网卡。默认为 "", 其 value 实际为 Pod 第一张拥有默认路由的网卡 | 字符串 | optional | "" |
| podDefaultCniNic | K8s 中 Pod 默认的第一张网卡 | 布尔型 | optional | eth0 |
-| detectGateway | 创建 Pod 时是否检查网关是否可达 | 布尔型 | optional | false |
-| detectIPConflict | 创建 Pod 时是否检查 Pod 的 IP 是否冲突 | 布尔型 | optional | false |
+| detectGateway | 遗弃,创建 Pod 时是否检查网关是否可达 | 布尔型 | optional | false |
+| detectIPConflict | 遗弃,创建 Pod 时是否检查 Pod 的 IP 是否冲突 | 布尔型 | optional | false |
| podMACPrefix | 是否固定 Pod 的 Mac 地址前缀, 前缀长度为两个字节, 由":"拼接。注意:首字节的最低位必须是 "0"。比如 "0a:1b"。 | 字符串 | optional | "" |
| overlayPodCIDR | 默认的集群 Pod 的子网,会注入到 Pod 中。不需要配置,自动从 Spidercoordinator default 中获取 | []stirng | optional | 默认从 Spidercoordinator default 中获取 |
| serviceCIDR | 默认的集群 Service 子网, 会注入到 Pod 中。不需要配置,自动从 Spidercoordinator default 中获取 | []stirng | optional | 默认从 Spidercoordinator default 中获取 |
| hijackCIDR | 额外的需要从主机转发的子网路由。比如nodelocaldns 的地址: 169.254.20.10/32 | []stirng | optional | 空 |
| hostRuleTable | 策略路由表号,同主机与 Pod 通信的路由将会存放于这个表号 | 整数型 | optional | 500 |
| podRPFilter | 设置 Pod 的 sysctl 参数 rp_filter | 整数型 | optional | 0 |
-| hostRPFilter | (遗弃)设置节点 的 sysctl 参数 rp_filter | 整数型 | optional | 0 |
| txQueueLen | 设置 Pod 的网卡传输队列 | 整数型 | optional | 0 |
| detectOptions | 检测地址冲突和网关可达性的高级配置项: 包括发送探测报文次数(retries: 默认为 3 次), 和响应的超时时间(timeout: 默认为 100ms),还有发送报文的间隔(interval:默认为 10ms, 将会在未来版本中移除) | 对象类型 | optional | 空 |
| logOptions | 日志配置,包括 logLevel(默认为 debug) 和 logFile(默认为 /var/log/spidernet/coordinator.log) | 对象类型 | optional | - |
@@ -79,30 +78,6 @@ spec:
> 若 IP 冲突检查发现某 IP 已被占用,请检查是否被集群中其他处于 `Terminating` 阶段的 **无状态** Pod 所占用,并配合 [IP 回收机制](./ipam-des-zh_CN.md#ip-回收机制) 相关参数进行配置。
-## 支持检测 Pod 的网关是否可达(alpha)
-
-在 Underlay 网络下,Pod 访问外部需要通过网关转发。如果网关不可达,那么在外界看来,这个 Pod 实际是失联的。有时候我们希望创建 Pod 时,其网关是可达的。 我们可借助 `coordinator` 检测 Pod 的网关是否可达,
-支持检测 IPv4 和 IPv6 的网关地址。我们通过发送 ARP 探测报文,探测网关地址是否可达。如果网关不可达,将会阻止 Pod 创建:
-
-我们可以通过 Spidermultusconfig 配置它:
-
-```yaml
-apiVersion: spiderpool.spidernet.io/v2beta1
-kind: SpiderMultusConfig
-metadata:
- name: detect-gateway
- namespace: default
-spec:
- cniType: macvlan
- macvlan:
- master: ["eth0"]
- enableCoordinator: true
- coordinator:
- detectGateway: true # Enable detectGateway
-```
-
-> 注意: 有一些交换机不允许被 arp 探测,否则会发出告警,在这种情况下,我们需要设置 detectGateway 为 false
-
## 支持固定 Pod 的 Mac 地址前缀(alpha)
有一些传统应用可能需要通过固定的 Mac 地址或者 IP 地址来耦合应用的行为。比如 License Server 可能需要应用固定的 Mac 地址或 IP 地址为应用颁发 License。如果 Pod 的 Mac 地址发生改变,已颁发的 License 可能无效。
@@ -113,6 +88,8 @@ spec:
> 目前支持修改 Macvlan 和 SR-IOV 作为 CNI 的 Pod。 IPVlan L2 模式下主接口与子接口 Mac 地址一致,不支持修改
>
> 固定的规则是配置 Mac 地址前缀(2字节) + 转化 Pod 的 IP(4字节) 组成。一个 IPv4 地址长度 4 字节,可以完全转换为2 个 16 进制数。对于 IPv6 地址,只取最后 4 个字节。
+>
+> 固定 Mac 地址后,为避免过时的 ARP 缓存表导致访问失败,Coordinator 插件会发送一个免费 ARP,通告新的 Mac 地址到局域网。
我们可以通过 Spidermultusconfig 配置它:
diff --git a/docs/concepts/coordinator.md b/docs/concepts/coordinator.md
index 6ef30b4948..2f939dc638 100644
--- a/docs/concepts/coordinator.md
+++ b/docs/concepts/coordinator.md
@@ -32,15 +32,14 @@ Let's delve into how coordinator implements these features.
| tunePodRoutes | Tune the pod's routing tables while a pod is in multi-NIC mode | bool | optional | true |
| podDefaultRouteNic | Configure the default routed NIC for the pod while a pod is in multi-NIC mode, The default value is 0, indicate that the first network interface of the pod has the default route. | string | optional | "" |
| podDefaultCniNic | The name of the pod's first NIC defaults to eth0 in kubernetes | bool | optional | eth0 |
-| detectGateway | Enable gateway detection while creating pods, which prevent pod creation if the gateway is unreachable | bool | optional | false |
-| detectIPConflict | Enable IP conflicting checking for pods, which prevent pod creation if the pod's ip is conflicting | bool | optional | false |
+| detectGateway | DEPRECATED: Enable gateway detection while creating pods, which prevent pod creation if the gateway is unreachable | bool | optional | false |
+| detectIPConflict | DEPRECATED: Enable IP conflicting checking for pods, which prevent pod creation if the pod's ip is conflicting | bool | optional | false |
| podMACPrefix | Enable fixing MAC address prefixes for pods. empty value is mean to disable. the length of prefix is two bytes. and the lowest bit of the first byte must be 0, example: "0a:1b". | string | optional | "" |
| overlayPodCIDR | The default cluster CIDR for the cluster. It doesn't need to be configured, and it collected automatically by SpiderCoordinator | []stirng | optional | []string{} |
| serviceCIDR | The default service CIDR for the cluster. It doesn't need to be configured, and it collected automatically by SpiderCoordinator | []stirng | optional | []string{} |
| hijackCIDR | The CIDR that need to be forwarded via the host network, For example, the address of nodelocaldns(169.254.20.10/32 by default) | []stirng | optional | []string{} |
| hostRuleTable | The routes on the host that communicates with the pod's underlay IPs will belong to this routing table number | int | optional | 500 |
| podRPFilter | Set the rp_filter sysctl parameter on the pod, which is recommended to be set to 0 | int | optional | 0 |
-| hostRPFilter | (deprecated)Set the rp_filter sysctl parameter on the node, which is recommended to be set to 0 | int | optional | 0 |
| txQueueLen | set txqueuelen(Transmit Queue Length) of the pod's interface | int | optional | 0 |
| detectOptions | The advanced configuration of detectGateway and detectIPConflict, including the number of the send packets(retries: default is 3) and the response timeout(timeout: default is 100ms) and the packet sending interval(interval: default is 10ms, which will be removed in the future version). | obejct | optional | nil |
| logOptions | The configuration of logging, including logLevel(default is debug) and logFile(default is /var/log/spidernet/coordinator.log) | obejct | optional | nil |
@@ -55,52 +54,6 @@ When using underlay CNIs like Macvlan, IPvlan, SR-IOV, and others, a common chal
For more information about the Underlay Pod not being able to access the ClusterIP, please refer to [Underlay CNI Access Service](../usage/underlay_cni_service.md)
-## Detect Pod IP conflicts(alpha)
-
-IP conflicts are unacceptable for underlay networks, which can cause serious problems. When creating a pod, we can use the `coordinator` to detect whether the IP of the pod conflicts, and support both IPv4 and IPv6 addresses. By sending an ARP or NDP probe message,
-If the MAC address of the reply packet does not belong to the Pod NIC, we consider the IP to be in conflict and reject the creation of the pod with conflicting IP addresses.
-Additionally, we will default to release the whole allocated IPs for the **stateless** Pod to make it try to reallocate those no-conflict IPs in the next CNI call for the Pod. For the **stable** Pod with conflict IPs, we would not release its IPs to keep the IPs own the stable feature either. You can use spiderpool-agent [ENV](../reference/spiderpool-agent.md#env) `SPIDERPOOL_ENABLED_RELEASE_CONFLICT_IPS` to control this feature.
-
-```yaml
-apiVersion: spiderpool.spidernet.io/v2beta1
-kind: SpiderMultusConfig
-metadata:
- name: detect-ip
- namespace: default
-spec:
- cniType: macvlan
- macvlan:
- master: ["eth0"]
- coordinator:
- detectIPConflict: true # Enable detectIPConflict
-```
-
-> If the IP address conflict check indicates that an IP address is occupied, please check it whether is occupied by another **stateless** Pod in `Terminating` phase in the cluster, please refer to [IP garbage collection](./ipam-des.md#ip-garbage-collection).
-
-## Detect Pod gateway reachability(alpha)
-
-Under the underlay network, pod access to the outside needs to be forwarded through the gateway. If the gateway is unreachable, then the pod is actually lost. Sometimes we want to create a pod with a gateway reachable. We can use the 'coordinator' to check if the pod's gateway is reachable.
-Gateway addresses for IPv4 and IPv6 can be detected. We send an ARP probe packet to check whether the gateway address is reachable. If the gateway is unreachable, pods will be prevented from creating:
-
-We can configure it via Spidermultusconfig:
-
-```yaml
-apiVersion: spiderpool.spidernet.io/v2beta1
-kind: SpiderMultusConfig
-metadata:
- name: detect-gateway
- namespace: default
-spec:
- cniType: macvlan
- macvlan:
- master: ["eth0"]
- enableCoordinator: true
- coordinator:
- detectGateway: true # Enable detectGateway
-```
-
-> Note: There are some switches that are not allowed to be probed by arp, otherwise an alarm will be issued, in this case, we need to set detectGateway to false
-
## Fix MAC address prefix for Pods(alpha)
Some traditional applications may require a fixed MAC address or IP address to couple the behavior of the application. For example, the License Server may need to apply a fixed Mac address
@@ -112,6 +65,8 @@ Note:
> currently supports updating Macvlan and SR-IOV as pods for CNI. In IPVlan L2 mode, the MAC addresses of the primary interface and the sub-interface are the same and cannot be modified.
>
> The fixed rule is to configure the MAC address prefix (2 bytes) + the IP of the converted pod (4 bytes). An IPv4 address is 4 bytes long and can be fully converted to 2 hexadecimal numbers. For IPv6 addresses, only the last 4 bytes are taken.
+>
+> After fixing the MAC address, to prevent access failure due to outdated ARP cache tables, the Coordinator plugin will send a gratuitous ARP to announce the new MAC address to the local area network.
We can configure it via Spidermultusconfig:
diff --git a/docs/concepts/ipam-des-zh_CN.md b/docs/concepts/ipam-des-zh_CN.md
index 18856b4f8e..d105463fa7 100644
--- a/docs/concepts/ipam-des-zh_CN.md
+++ b/docs/concepts/ipam-des-zh_CN.md
@@ -185,3 +185,32 @@ NOTE:
- 节点意外宕机后,集群中的 Pod 永久处于 `Terminating` 阶段,Pod 占用的 IP 地址无法被释放。
- 对于处于 `Terminating` 阶段的 **无状态** Pod,Spiderpool 将在 Pod 的 `spec.terminationGracePeriodSecond` + [spiderpool-controller ENV](./../reference/spiderpool-controller.md#env) `SPIDERPOOL_GC_ADDITIONAL_GRACE_DELAY` 时间后,自动释放其 IP 地址。该功能可通过环境变量 `SPIDERPOOL_GC_STATELESS_TERMINATING_POD_ON_NOT_READY_NODE_ENABLED` 控制,解决 `节点意外宕机` 的故障场景。
+
+### IP 冲突检测和网关可达性检测
+
+对于 Underlay 网络,IP 冲突是无法接受的,这可能会造成严重的问题。Spiderpool 支持 IP 冲突检测和网关可达性检测,该功能以前由 coordinator 插件实现,由于可能会导致一些潜在的通信问题。现在由 IPAM 完成。
+
+可通过 `spiderpool-conf` configMap 开启或关闭:
+
+ apiVersion: v1
+ kind: ConfigMap
+ metadata:
+ name: spiderpool-conf
+ namespace: spiderpool
+ data:
+ conf.yml: |
+ ...
+ enableIPConflictDetection: true
+ enableGatewayDetection: true
+ ...
+
+- 开启 IP 冲突检测后,Spiderpool 将会通过发送 ARP 或 NDP 报文检测分配的 IP 地址是否冲突。当检测到该 IP 与网段内其他 IP 冲突,将会阻止 Pod 创建。支持 IPv4 和 IPv6
+
+ - 当发送 ARP 或 NDP 探测报文失败,将会重试 3 次,如果都失败,则返回错误。
+ - 当成功发送探测报文,如果在 100ms 内收到答复,说明存在 IP 冲突。如果接收错误并且为 Network Timeout 类的错误,则判断为不冲突。
+
+- 开启网关可达性检测后,Spiderpool 将会通过发送 ARP 或 NDP 报文检测 Pod 的 网关地址是否可达。如果发现网关地址不可达,将会阻止 Pod 创建。
+
+ - 当发送 ARP 或 NDP 探测报文失败,将会重试 3 次,如果都失败,则返回错误。
+ - 当成功发送探测报文,如果在 100ms 内收到答复,说明网关地址可达。如果未收到答复,则说明网关地址不可达。
+ - 注意: 有一些交换机不允许被 arp 探测,否则会发出告警,在这种情况下,我们需要设置 enableGatewayDetection 为 false。
diff --git a/docs/concepts/ipam-des.md b/docs/concepts/ipam-des.md
index 18fa98c19d..c08c2738c3 100644
--- a/docs/concepts/ipam-des.md
+++ b/docs/concepts/ipam-des.md
@@ -209,3 +209,33 @@ The above complete IP recovery algorithm can ensure the correct recovery of IP a
- After a node goes down unexpectedly, the Pod in the cluster is permanently in the `Terminating` phase, and the IP address occupied by the Pod cannot be released.
- For the **stateless** Pod in the `Terminating` phase, Spiderpool will automatically release its IP address after the Pod's `spec.terminationGracePeriodSecond`. This feature can be controlled by the environment variable `SPIDERPOOL_GC_STATELESS_TERMINATING_POD_ON_NOT_READY_NODE_ENABLED`. This capability can be used to solve the failure scenario of `unexpected node downtime`.
+
+### IP Conflict Detection and Gateway Reachability Detection
+
+For Underlay networks, IP conflicts are unacceptable as they can cause serious issues. Spiderpool supports IP conflict detection and gateway reachability detection, which were previously implemented by the coordinator plugin but could cause some potential communication problems. Now, this is handled by IPAM.
+
+You can enable or disable this feature through the spiderpool-conf ConfigMap:
+
+ apiVersion: v1
+ kind: ConfigMap
+ metadata:
+ name: spiderpool-conf
+ namespace: spiderpool
+ data:
+ conf.yml: |
+ ...
+ enableIPConflictDetection: true
+ enableGatewayDetection: true
+ ...
+
+- When IP conflict detection is enabled, Spiderpool will detect if the assigned IP address conflicts with others in the subnet by sending ARP or NDP packets. If a conflict is detected, Pod creation will be blocked. This supports both IPv4 and IPv6.
+
+ - If sending ARP or NDP probe packets fails, it will retry 3 times, and if all attempts fail, an error will be returned.
+ - If the probe packet is successfully sent and a response is received within 100ms, it indicates an IP conflict.
+ - If a network timeout error is received, it is considered non-conflicting.
+- When gateway reachability detection is enabled, Spiderpool will detect if the Pod's gateway address is reachable by sending ARP or NDP packets. If the gateway address is unreachable, Pod creation will be blocked.
+
+ - If sending ARP or NDP probe packets fails, it will retry 3 times, and if all attempts fail, an error will be returned.
+ - If the probe packet is successfully sent and a response is received within 100ms, it indicates the gateway address is reachable.
+ - If no response is received, it indicates the gateway address is unreachable.
+ - Note: Some switches do not allow ARP probing and will issue alerts. In such cases, you need to set enableGatewayDetection to false.
diff --git a/docs/reference/configmap.md b/docs/reference/configmap.md
index 724756d0a9..9ea0b0f5ea 100644
--- a/docs/reference/configmap.md
+++ b/docs/reference/configmap.md
@@ -20,7 +20,16 @@ data:
enableStatefulSet: true
enableKubevirtStaticIP: true
enableSpiderSubnet: true
+ enableIPConflictDetection: true
+ enableGatewayDetection: true
clusterSubnetDefaultFlexibleIPNumber: 1
+ tuneSysctlConfig: {{ .Values.spiderpoolAgent.tuneSysctlConfig }}
+ podResourceInject:
+ enabled: false
+ namespacesExclude:
+ - kube-system
+ - spiderpool
+ namespacesInclude: []
```
- `ipamUnixSocketPath` (string): Spiderpool agent listens to this UNIX socket file and handles IPAM requests from IPAM plugin.
@@ -39,4 +48,16 @@ data:
- `enableSpiderSubnet` (bool):
- `true`: Enable SpiderSubnet capability of Spiderpool.
- `false`: Disable SpiderSubnet capability of Spiderpool.
+- `enableIPConflictDetection` (bool):
+ - `true`: Enable IP conflict detection capability of Spiderpool.
+ - `false`: Disable IP conflict detection capability of Spiderpool.
+- `enableGatewayDetection` (bool):
+ - `true`: Enable gateway detection capability of Spiderpool.
+ - `false`: Disable gateway detection capability of Spiderpool.
- `clusterSubnetDefaultFlexibleIPNumber` (int): Global SpiderSubnet default flexible IP number. It takes effect across the cluster.
+- `podResourceInject` (object): Pod resource inject capability of Spiderpool.
+ - `enabled` (bool):
+ - `true`: Enable pod resource inject capability of Spiderpool.
+ - `false`: Disable pod resource inject capability of Spiderpool.
+ - `namespacesExclude` (array): Exclude the namespaces of the pod resource inject.
+ - `namespacesInclude` (array): Include the namespaces of the pod resource inject.
diff --git a/docs/reference/crd-spidercoordinator.md b/docs/reference/crd-spidercoordinator.md
index 23ba528b46..1bfeb13829 100644
--- a/docs/reference/crd-spidercoordinator.md
+++ b/docs/reference/crd-spidercoordinator.md
@@ -12,8 +12,6 @@ kind: SpiderCoordinator
metadata:
name: default
spec:
- detectGateway: false
- detectIPConflict: false
enableVethLinkLocalAddress: false
hostRPFilter: 0
podRPFilter: 0
@@ -51,15 +49,13 @@ This is the Spidercoordinators spec for users to configure.
| mode | The mode in which the coordinator. auto: automatically determine if it's overlay or underlay. underlay: coordinator creates veth devices to solve the problem that CNIs such as macvlan cannot communicate with clusterIP. overlay: fix the problem that CNIs such as Macvlan cannot access ClusterIP through the Calico network card attached to the pod,coordinate policy route between interfaces to ensure consistence data path of request and reply packets | string | require | auto,underlay,overlay | auto |
| podCIDRType | The ways to fetch the CIDR of the cluster. auto(default), This means that it will automatically switch podCIDRType to cluster or calico or cilium. based on cluster CNI. calico: auto fetch the subnet of the pod from the ip pools of calico, This only works if the cluster CNI is calico; cilium: Auto fetch the pod's subnet from cilium's configMap or ip pools. Supported IPAM modes: ["cluster-pool","kubernetes","multi-pool"]; cluster: auto fetch the subnet of the pod from the kubeadm-config configmap, This is useful if there is only a globally unique default pod's subnet; none: don't get the subnet of the pod, which is useful for some special cases. In this case,you can manually configure the hijackCIDR field | string | require | auto,cluster,calico,cilium,none | auto |
| tunePodRoutes | tune pod's route while the pod is attached to multiple NICs | bool | optional | true,false | true |
-| podDefaultRouteNIC | The NIC where the pod's default route resides | string | optional | "",eth0,net1... | underlay: eth0,overlay: net1 |
-| detectGateway | enable detect gateway while launching pod, If the gateway is unreachable, pod will be failed to created; Note: We use ARP probes to detect if the gateway is reachable, and some gateway routers may warn about this | boolean | optional | true,false | false |
-| detectIPConflict | enable the pod's ip if is conflicting while launching pod. If an IP conflict of the pod is detected, pod will be failed to created | boolean | optional | true,false | false |
+| podDefaultRouteNIC | The NIC where the pod's default route resides | string | optional | "",eth0,net1... | underlay: eth0,overlay: net1 |
| vethLinkAddress | configure an link-local address for veth0 device, fix the istio case | boolean | optional | true,false | false |
| podMACPrefix | fix the pod's mac address with this prefix + 4 bytes IP | string | optional | a invalid mac address prefix | "" |
| podRPFilter | set rp_filter sysctl for the pod | int | required | 0,1,2;suggest to be 0 | 0 |
| hostRPFilter | (deprecated) set rp_filter sysctl for the node | int | required | 0,1,2;suggest to be 0 | 0 |
| hostRuleTable | The directly routing table of the host accessing the pod's underlay IP will be placed in this policy routing table | int | required | int | 500 |
-| txQueueLen | The Transmit Queue Length (txqueuelen) is a TCP/IP stack network interface value that sets the number of packets allowed per kernel transmit queue of a network interface device | int | optional | >= 0, default to 0, it's mean to don't set it |
+| txQueueLen | The Transmit Queue Length (txqueuelen) is a TCP/IP stack network interface value that sets the number of packets allowed per kernel transmit queue of a network interface device | int | optional | >= 0, default to 0, it's mean to don't set it | 0 |
### Status (subresource)
diff --git a/docs/reference/spiderpool-agent.md b/docs/reference/spiderpool-agent.md
index 1aebc5dd89..eb17588d05 100644
--- a/docs/reference/spiderpool-agent.md
+++ b/docs/reference/spiderpool-agent.md
@@ -52,6 +52,42 @@ helm install spiderpool -n kube-system --set global.tuneSysctlConfig=false
Or configure the spiderpool-conf configMap, set tuneSysctlConfig to false and restart the spiderpool-agent pods.
+## spiderpool-agent helps detect Pod's IPs if conflicts and Detect the gateway if reachable
+
+For Underlay networks, IP conflicts are unacceptable as they can cause serious issues. Spiderpool supports IP conflict detection and gateway reachability detection, which were previously implemented by the coordinator plugin but could cause some potential communication problems. Now, this is handled by IPAM.
+
+You can enable or disable this feature through the spiderpool-conf ConfigMap:
+
+```yaml
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: spiderpool-conf
+ namespace: spiderpool
+data:
+ conf.yml: |
+ ...
+ enableIPConflictDetection: true
+ enableGatewayDetection: true
+ ...
+```
+
+After applying the configMap, restart the spiderpool-agent pods.
+
+- When IP conflict detection is enabled, Spiderpool will detect if the assigned IP address conflicts with others in the subnet by sending ARP or NDP packets. If a conflict is detected, Pod creation will be blocked. This supports both IPv4 and IPv6.
+
+ - If sending ARP or NDP probe packets fails, it will retry 3 times, and if all attempts fail, an error will be returned.
+ - If the probe packet is successfully sent and a response is received within 100ms, it indicates an IP conflict.
+ - If a network timeout error is received, it is considered non-conflicting.
+- When gateway reachability detection is enabled, Spiderpool will detect if the Pod's gateway address is reachable by sending ARP or NDP packets. If the gateway address is unreachable, Pod creation will be blocked.
+
+ - If sending ARP or NDP probe packets fails, it will retry 3 times, and if all attempts fail, an error will be returned.
+ - If the probe packet is successfully sent and a response is received within 100ms, it indicates the gateway address is reachable.
+ - If no response is received, it indicates the gateway address is unreachable.
+ - Note: Some switches do not allow ARP probing and will issue alerts. In such cases, you need to set enableGatewayDetection to false.
+
+> NOTE: Enabling IP conflict detection or gateway detection may increase the time required for IPAM calls and Pod startup, depending on the network. Particularly, when IPv6 Duplicate Address Detection (DAD) is enabled, the kernel will check for conflicts with local link addresses, which may consume additional time.
+
## spiderpool-agent shutdown
Notify of stopping the spiderpool-agent daemon.
diff --git a/docs/usage/install/underlay/get-started-sriov-zh_CN.md b/docs/usage/install/underlay/get-started-sriov-zh_CN.md
index dfcf4a4fef..5bdba2e795 100644
--- a/docs/usage/install/underlay/get-started-sriov-zh_CN.md
+++ b/docs/usage/install/underlay/get-started-sriov-zh_CN.md
@@ -120,8 +120,8 @@ Spiderpool 可用作 underlay 网络场景下提供固定 IP 的一种解决方
EOF
```
- > 下发如上命令后, 因为需要配置节点启用 SR-IOV 功能,可能会重启节点。如有需要,指定工作节点而非 Master 节点。
- > resourceName 不能为特殊字符,支持的字符: [0-9],[a-zA-Z] 和 "_"。
+ > 下发如上命令后,因为需要配置节点启用 SR-IOV 功能,可能会重启节点。如有需要,指定工作节点而非 Master 节点。
+ > resourceName 不能为特殊字符,支持的字符: [0-9],[a-zA-Z] 和 "_".
在下发 SriovNetworkNodePolicy CRs 之后,再次查看 SriovNetworkNodeState CRs 的状态, 可以看见 status 中 VF 已经得到配置:
@@ -339,4 +339,4 @@ Spiderpool 可用作 underlay 网络场景下提供固定 IP 的一种解决方
Connection: keep-alive
ETag: "6398a011-267"
Accept-Ranges: bytes
- ```
+
diff --git a/docs/usage/install/underlay/get-started-sriov.md b/docs/usage/install/underlay/get-started-sriov.md
index f16899812a..5e1a42762d 100644
--- a/docs/usage/install/underlay/get-started-sriov.md
+++ b/docs/usage/install/underlay/get-started-sriov.md
@@ -120,8 +120,8 @@ Spiderpool provides a solution for assigning static IP addresses in underlay net
EOF
```
- > After executing the above command, please note that configuring nodes to enable SR-IOV functionality may require a node restart. If needed, specify worker nodes instead of master nodes for this configuration.
- > The resourceName should not contain special characters and is limited to [0-9], [a-zA-Z], and "_".
+ > After executing the above command, please note that configuring nodes to enable SR-IOV functionality may require a node restart. If needed, specify worker nodes instead of master nodes for this configuration.
+ > The resourceName should not contain special characters and is limited to [0-9], [a-zA-Z], and "_".
After applying the SriovNetworkNodePolicy CRs, you can check the status of the SriovNetworkNodeState CRs again to verify that the VFs have been successfully configured:
@@ -167,14 +167,14 @@ Spiderpool provides a solution for assigning static IP addresses in underlay net
```
> The sriov-network-config-daemon Pod is responsible for configuring VF on nodes, and it will sequentially complete the work on each node. When configuring VF on each node, the SR-IOV network configuration daemon will evict all Pods on the node, configure VF, and possibly restart the node. When SR-IOV network configuration daemon fails to evict a Pod, it will cause all processes to stop, resulting in the vf number of nodes remaining at 0. In this case, the SR-IOV network configuration daemon Pod will see logs similar to the following:
- >
+ >
> `error when evicting pods/calico-kube-controllers-865d498fd9-245c4 -n kube-system (will retry after 5s) ...`
>
> This issue can be referred to similar topics in the sriov-network-operator community [issue](https://github.com/k8snetworkplumbingwg/sriov-network-operator/issues/463)
>
> The reason why the designated Pod cannot be expelled can be investigated, which may include the following:
>
- > 1. The Pod that failed the eviction may have been configured with a PodDisruptionBudget, resulting in a
+ > 1. The Pod that failed the eviction may have been configured with a PodDisruptionBudget, resulting in a
> shortage of available replicas. Please adjust the PodDisruptionBudget
>
> 2. Insufficient available nodes in the cluster, resulting in no nodes available for scheduling
diff --git a/docs/usage/kubevirt-zh_CN.md b/docs/usage/kubevirt-zh_CN.md
index 87632d8a33..78e0a0cd4f 100644
--- a/docs/usage/kubevirt-zh_CN.md
+++ b/docs/usage/kubevirt-zh_CN.md
@@ -14,6 +14,8 @@ Spiderpool underlay 网络解决方案可给 KubeVirt 赋予介入 underlay 的
2. 对于 KubeVirt 的 bridge 网络模式,可搭配 OVS CNI 使用。在该网络模式下,**不支持** Service Mesh 功能,可使用**多网卡**,不支持热迁移。
+3. Spiderpool 支持对 KubeVirt Pod 进行 IP 冲突检测,避免出现 IP 冲突。但对于 KubeVirt 热迁移应用,当开启 IP 冲突检测,会导致热迁移虚拟机无法启动。所以在这个场景下,即使开启了 IP 冲突检测功能,Spiderpool 也不会对 KubeVirt 进行 IP 冲突检测。
+
## KubeVirt VM 固定地址
KubeVirt VM 会在以下一些场景中会出现固定地址的使用:
diff --git a/docs/usage/kubevirt.md b/docs/usage/kubevirt.md
index f31343fee3..679527a0cf 100644
--- a/docs/usage/kubevirt.md
+++ b/docs/usage/kubevirt.md
@@ -14,6 +14,8 @@ The Spiderpool underlay networking solution provides the ability to integrate wi
2. For KubeVirt's bridge network mode, it can be used in conjunction with OVS CNI. In this network mode, Service Mesh functionalities are **not supported**, but **multiple NICs** can be used, and live migration is not available.
+3. Spiderpool supports IP conflict detection for KubeVirt Pods to prevent IP conflicts. However, for KubeVirt live migration applications, enabling IP conflict detection will prevent the live migration virtual machine from starting. Therefore, in this scenario, even if the IP conflict detection feature is enabled, Spiderpool will not perform IP conflict detection for KubeVirt.
+
## Fix IP Address for KubeVirt VMs
KubeVirt VMs may require fixed IP addresses in the following scenarios:
diff --git a/go.mod b/go.mod
index 7ac02a29b9..e07f507fa8 100644
--- a/go.mod
+++ b/go.mod
@@ -23,8 +23,6 @@ require (
github.com/jessevdk/go-flags v1.5.0
github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.4.0
github.com/kdoctor-io/kdoctor v0.2.0
- github.com/mdlayher/arp v0.0.0-20220512170110-6706a2966875
- github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118
github.com/mdlayher/ndp v1.0.1
github.com/onsi/ginkgo/v2 v2.13.0
github.com/onsi/gomega v1.29.0
@@ -120,7 +118,6 @@ require (
github.com/imdario/mergo v0.3.13 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
- github.com/josharian/native v1.1.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
@@ -130,8 +127,6 @@ require (
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
- github.com/mdlayher/packet v1.1.2 // indirect
- github.com/mdlayher/socket v0.4.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
diff --git a/go.sum b/go.sum
index 6f22359f6e..efbc0ed4d4 100644
--- a/go.sum
+++ b/go.sum
@@ -259,7 +259,6 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@@ -334,9 +333,6 @@ github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LF
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
-github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
-github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
-github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
@@ -379,18 +375,8 @@ github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
-github.com/mdlayher/arp v0.0.0-20220512170110-6706a2966875 h1:ql8x//rJsHMjS+qqEag8n3i4azw1QneKh5PieH9UEbY=
-github.com/mdlayher/arp v0.0.0-20220512170110-6706a2966875/go.mod h1:kfOoFJuHWp76v1RgZCb9/gVUc7XdY877S2uVYbNliGc=
-github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 h1:2oDp6OOhLxQ9JBoUuysVz9UZ9uI6oLUbvAZu0x8o+vE=
-github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118/go.mod h1:ZFUnHIVchZ9lJoWoEGUg8Q3M4U8aNNWA3CVSUTkW4og=
github.com/mdlayher/ndp v1.0.1 h1:+yAD79/BWyFlvAoeG5ncPS0ItlHP/eVbH7bQ6/+LVA4=
github.com/mdlayher/ndp v1.0.1/go.mod h1:rf3wKaWhAYJEXFKpgF8kQ2AxypxVbfNcZbqoAo6fVzk=
-github.com/mdlayher/packet v1.0.0/go.mod h1:eE7/ctqDhoiRhQ44ko5JZU2zxB88g+JH/6jmnjzPjOU=
-github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY=
-github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4=
-github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E=
-github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
-github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
diff --git a/pkg/coordinatormanager/coordinator_mutate.go b/pkg/coordinatormanager/coordinator_mutate.go
index e26ca0e37b..ee07ada867 100644
--- a/pkg/coordinatormanager/coordinator_mutate.go
+++ b/pkg/coordinatormanager/coordinator_mutate.go
@@ -38,6 +38,11 @@ func mutateCoordinator(ctx context.Context, coord *spiderpoolv2beta1.SpiderCoord
if coord.Spec.PodRPFilter == nil {
coord.Spec.PodRPFilter = ptr.To(0)
}
+
+ if coord.Spec.TxQueueLen == nil {
+ coord.Spec.TxQueueLen = ptr.To(0)
+ }
+
if coord.Spec.DetectIPConflict == nil {
coord.Spec.DetectIPConflict = ptr.To(false)
}
@@ -45,10 +50,6 @@ func mutateCoordinator(ctx context.Context, coord *spiderpoolv2beta1.SpiderCoord
coord.Spec.DetectGateway = ptr.To(false)
}
- if coord.Spec.TxQueueLen == nil {
- coord.Spec.TxQueueLen = ptr.To(0)
- }
-
if coord.Spec.VethLinkAddress == nil {
coord.Spec.VethLinkAddress = ptr.To("")
}
diff --git a/pkg/ipam/allocate.go b/pkg/ipam/allocate.go
index 313e09a3dc..741cc11d04 100644
--- a/pkg/ipam/allocate.go
+++ b/pkg/ipam/allocate.go
@@ -14,6 +14,7 @@ import (
"go.uber.org/zap"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
@@ -49,7 +50,7 @@ func (i *ipam) Allocate(ctx context.Context, addArgs *models.IpamAddArgs) (*mode
}
podTopController, err := i.podManager.GetPodTopController(ctx, pod)
- if nil != err {
+ if err != nil {
return nil, fmt.Errorf("failed to get the top controller of the Pod %s/%s: %v", pod.Namespace, pod.Name, err)
}
logger.Sugar().Debugf("%s %s/%s is the top controller of the Pod", podTopController.Kind, podTopController.Namespace, podTopController.Name)
@@ -248,7 +249,12 @@ func (i *ipam) retrieveStaticIPAllocation(ctx context.Context, nic string, pod *
return nil, fmt.Errorf("failed to refresh the current IP allocation of %s: %w", endpoint.Status.OwnerControllerType, err)
}
- ips, routes := convert.ConvertIPDetailsToIPConfigsAndAllRoutes(endpoint.Status.Current.IPs)
+ enableIPConflictDetection, err := i.IsDetectGatewayReachableForKubeVirtPod(ctx, pod)
+ if err != nil {
+ return nil, err
+ }
+
+ ips, routes := convert.ConvertIPDetailsToIPConfigsAndAllRoutes(endpoint.Status.Current.IPs, enableIPConflictDetection, i.config.EnableGatewayDetection)
addResp := &models.IpamAddResponse{
Ips: ips,
Routes: routes,
@@ -338,7 +344,7 @@ func (i *ipam) retrieveExistingIPAllocation(ctx context.Context, uid, nic string
}
}
- ips, routes := convert.ConvertIPDetailsToIPConfigsAndAllRoutes(allocation.IPs)
+ ips, routes := convert.ConvertIPDetailsToIPConfigsAndAllRoutes(allocation.IPs, i.config.EnableIPConflictDetection, i.config.EnableGatewayDetection)
addResp := &models.IpamAddResponse{
Ips: ips,
Routes: routes,
@@ -822,6 +828,47 @@ func (i *ipam) verifyPoolCandidates(tt ToBeAllocateds) error {
return nil
}
+// IsDetectGatewayReachableForKubeVirtPod disable IP conflict detection for the kubevirt vm live migration pod,
+// If we don't do this, it will cause the migration pod never be started.
+func (i *ipam) IsDetectGatewayReachableForKubeVirtPod(ctx context.Context, pod *corev1.Pod) (enableIPConflictDetection bool, err error) {
+ if !i.config.EnableIPConflictDetection {
+ return false, nil
+ }
+
+ // disable IP conflict detection for the kubevirt vm live migration pod
+ // return directly if not a kubevirt vm pod
+ ownerReference := metav1.GetControllerOf(pod)
+ if ownerReference == nil || !i.config.EnableKubevirtStaticIP || ownerReference.APIVersion != kubevirtv1.SchemeGroupVersion.String() || ownerReference.Kind != constant.KindKubevirtVMI {
+ return true, nil
+ }
+
+ logger := logutils.FromContext(ctx)
+ // the live migration new pod has the annotation "kubevirt.io/migrationJobName"
+ // we just only cancel IP conflict detection for the live migration new pod.
+ podAnnos := pod.GetAnnotations()
+ vmimName, ok := podAnnos[kubevirtv1.MigrationJobNameAnnotation]
+ if ok {
+ // kubevirt vm pod corresponding SpiderEndpoint uses kubevirt VM/VMI name
+ _, err := i.kubevirtManager.GetVMIMByName(ctx, pod.Namespace, vmimName, false)
+ if err == nil {
+ // cancel IP conflict detection because there's a moment the old vm pod still running during the vm live migration phase
+ logger.Sugar().Infof("cancel IP conflict detection for live migration new pod '%s/%s'", pod.Namespace, pod.Name)
+ return false, nil
+ }
+
+ if apierrors.IsNotFound(err) {
+ // if we don't found the kubevirt migrated vm pod, still execute IP conflict detection
+ logger.Sugar().Warnf("no kubevirt vm pod '%s/%s' corresponding VirtualMachineInstanceMigration '%s/%s' found, still execute IP conflict detection",
+ pod.Namespace, pod.Name, pod.Namespace, vmimName)
+ return true, nil
+ }
+
+ return false, fmt.Errorf("failed to get kubevirt vm pod '%s/%s' corresponding VirtualMachineInstanceMigration '%s/%s', error: %v",
+ pod.Namespace, pod.Name, pod.Namespace, vmimName, err)
+ }
+ return true, nil
+}
+
// sortPoolCandidates would sort IPPool candidates sequence depends on the IPPool multiple affinities.
func sortPoolCandidates(preliminary ToBeAllocateds) {
for _, toBeAllocate := range preliminary {
diff --git a/pkg/ipam/config.go b/pkg/ipam/config.go
index 02d950589c..dc86451ffc 100644
--- a/pkg/ipam/config.go
+++ b/pkg/ipam/config.go
@@ -22,6 +22,8 @@ type IPAMConfig struct {
EnableStatefulSet bool
EnableKubevirtStaticIP bool
EnableReleaseConflictIPsForStateless bool
+ EnableIPConflictDetection bool
+ EnableGatewayDetection bool
OperationRetries int
OperationGapDuration time.Duration
diff --git a/pkg/ippoolmanager/config.go b/pkg/ippoolmanager/config.go
index 56a4a3d139..3a19928b21 100644
--- a/pkg/ippoolmanager/config.go
+++ b/pkg/ippoolmanager/config.go
@@ -8,8 +8,10 @@ const (
)
type IPPoolManagerConfig struct {
- MaxAllocatedIPs *int
- EnableKubevirtStaticIP bool
+ MaxAllocatedIPs *int
+ EnableKubevirtStaticIP bool
+ EnableGatewayDetection bool
+ EnableIPConflictDetection bool
}
func setDefaultsForIPPoolManagerConfig(config IPPoolManagerConfig) IPPoolManagerConfig {
diff --git a/pkg/ippoolmanager/ippool_manager.go b/pkg/ippoolmanager/ippool_manager.go
index c436daf440..7f6d5a5a4c 100644
--- a/pkg/ippoolmanager/ippool_manager.go
+++ b/pkg/ippoolmanager/ippool_manager.go
@@ -135,6 +135,9 @@ func (im *ipPoolManager) AllocateIP(ctx context.Context, poolName, nic string, p
return nil, err
}
+ // TODO(@cyclinder): set these values from ippool.spec
+ ipConfig.EnableGatewayDetection = im.config.EnableGatewayDetection
+ ipConfig.EnableIPConflictDetection = im.config.EnableIPConflictDetection
return ipConfig, nil
}
diff --git a/pkg/k8s/apis/spiderpool.spidernet.io/v2beta1/spidercoordinator_types.go b/pkg/k8s/apis/spiderpool.spidernet.io/v2beta1/spidercoordinator_types.go
index 4176b649ec..371be16ddc 100644
--- a/pkg/k8s/apis/spiderpool.spidernet.io/v2beta1/spidercoordinator_types.go
+++ b/pkg/k8s/apis/spiderpool.spidernet.io/v2beta1/spidercoordinator_types.go
@@ -79,20 +79,26 @@ type CoordinatorSpec struct {
// +kubebuilder:default=0
TxQueueLen *int `json:"txQueueLen,omitempty"`
+ // VethLinkAddress configure a ipv4 link-local address
+ // for veth0 device. empty means disable. default is empty.
+ // Format is like 169.254.100.1
+ VethLinkAddress *string `json:"vethLinkAddress,omitempty"`
+
// DetectIPConflict to detect the ip conflict for the pod
+ // Deprecated: IP conflict detection is now done by IPAM,
+ // setting this value has no effect,this will be removed
+ // in the future.
// +kubebuilder:validation:Optional
// +kubebuilder:default=false
DetectIPConflict *bool `json:"detectIPConflict,omitempty"`
// DetectGateway to detect the gateway for the pod
+ // Deprecated: gateway detection is now done by IPAM,
+ // setting this value has no effect,this will be removed
+ // in the future.
// +kubebuilder:validation:Optional
// +kubebuilder:default=false
DetectGateway *bool `json:"detectGateway,omitempty"`
-
- // VethLinkAddress configure a ipv4 link-local address
- // for veth0 device. empty means disable. default is empty.
- // Format is like 169.254.100.1
- VethLinkAddress *string `json:"vethLinkAddress,omitempty"`
}
// CoordinationStatus defines the observed state of SpiderCoordinator.
diff --git a/pkg/k8s/apis/spiderpool.spidernet.io/v2beta1/zz_generated.deepcopy.go b/pkg/k8s/apis/spiderpool.spidernet.io/v2beta1/zz_generated.deepcopy.go
index 62d5b5be3f..638352948a 100644
--- a/pkg/k8s/apis/spiderpool.spidernet.io/v2beta1/zz_generated.deepcopy.go
+++ b/pkg/k8s/apis/spiderpool.spidernet.io/v2beta1/zz_generated.deepcopy.go
@@ -86,16 +86,6 @@ func (in *CoordinatorSpec) DeepCopyInto(out *CoordinatorSpec) {
*out = new(int)
**out = **in
}
- if in.DetectIPConflict != nil {
- in, out := &in.DetectIPConflict, &out.DetectIPConflict
- *out = new(bool)
- **out = **in
- }
- if in.DetectGateway != nil {
- in, out := &in.DetectGateway, &out.DetectGateway
- *out = new(bool)
- **out = **in
- }
if in.VethLinkAddress != nil {
in, out := &in.VethLinkAddress, &out.VethLinkAddress
*out = new(string)
diff --git a/pkg/multuscniconfig/multusconfig_informer.go b/pkg/multuscniconfig/multusconfig_informer.go
index c254182bd2..d390e5d655 100644
--- a/pkg/multuscniconfig/multusconfig_informer.go
+++ b/pkg/multuscniconfig/multusconfig_informer.go
@@ -731,9 +731,6 @@ func generateCoordinatorCNIConf(coordinatorSpec *spiderpoolv2beta1.CoordinatorSp
if coordinatorSpec.DetectGateway != nil {
coordinatorNetConf.DetectGateway = coordinatorSpec.DetectGateway
}
- if coordinatorSpec.VethLinkAddress != nil {
- coordinatorNetConf.VethLinkAddress = *coordinatorSpec.VethLinkAddress
- }
if coordinatorSpec.TunePodRoutes != nil {
coordinatorNetConf.TunePodRoutes = coordinatorSpec.TunePodRoutes
}
diff --git a/pkg/multuscniconfig/multusconfig_mutate.go b/pkg/multuscniconfig/multusconfig_mutate.go
index 95a24661fe..3a85cad519 100644
--- a/pkg/multuscniconfig/multusconfig_mutate.go
+++ b/pkg/multuscniconfig/multusconfig_mutate.go
@@ -190,12 +190,12 @@ func setCoordinatorDefaultConfig(coordinator *spiderpoolv2beta1.CoordinatorSpec)
return &spiderpoolv2beta1.CoordinatorSpec{
Mode: ptr.To(string(coordinator_cmd.ModeAuto)),
HijackCIDR: []string{},
- DetectGateway: ptr.To(false),
- DetectIPConflict: ptr.To(false),
VethLinkAddress: ptr.To(""),
PodMACPrefix: ptr.To(""),
PodDefaultRouteNIC: ptr.To(""),
HostRPFilter: ptr.To(0),
+ DetectGateway: ptr.To(false),
+ DetectIPConflict: ptr.To(false),
PodRPFilter: ptr.To(0),
TunePodRoutes: ptr.To(true),
}
@@ -209,16 +209,16 @@ func setCoordinatorDefaultConfig(coordinator *spiderpoolv2beta1.CoordinatorSpec)
coordinator.HijackCIDR = []string{}
}
- if coordinator.DetectGateway == nil {
- coordinator.DetectGateway = ptr.To(false)
+ if coordinator.PodMACPrefix == nil {
+ coordinator.PodMACPrefix = ptr.To("")
}
if coordinator.DetectIPConflict == nil {
coordinator.DetectIPConflict = ptr.To(false)
}
- if coordinator.PodMACPrefix == nil {
- coordinator.PodMACPrefix = ptr.To("")
+ if coordinator.DetectGateway == nil {
+ coordinator.DetectGateway = ptr.To(false)
}
if coordinator.PodDefaultRouteNIC == nil {
diff --git a/pkg/networking/gwconnection/connection.go b/pkg/networking/gwconnection/connection.go
deleted file mode 100644
index 21f9690cc6..0000000000
--- a/pkg/networking/gwconnection/connection.go
+++ /dev/null
@@ -1,185 +0,0 @@
-// Copyright 2023 Authors of spidernet-io
-// SPDX-License-Identifier: Apache-2.0
-
-package gwconnection
-
-import (
- "fmt"
- "net"
- "net/netip"
- "time"
-
- "go.uber.org/zap"
-
- types100 "github.com/containernetworking/cni/pkg/types/100"
- "github.com/mdlayher/arp"
- _ "github.com/mdlayher/ethernet"
- "github.com/mdlayher/ndp"
- "github.com/spidernet-io/spiderpool/pkg/constant"
-)
-
-type DetectGateway struct {
- retries int
- iface string
- interval time.Duration
- timeout time.Duration
- v4Addr, v6Addr, V4Gw, V6Gw net.IP
- logger *zap.Logger
-}
-
-func New(retries int, interval, timeout, iface string, logger *zap.Logger) (*DetectGateway, error) {
- var err error
- dg := &DetectGateway{
- retries: retries,
- iface: iface,
- }
-
- dg.interval, err = time.ParseDuration(interval)
- if err != nil {
- return nil, err
- }
-
- dg.timeout, err = time.ParseDuration(timeout)
- if err != nil {
- return nil, err
- }
- dg.logger = logger
-
- return dg, nil
-}
-
-func (dg *DetectGateway) ParseAddrFromPreresult(ipconfigs []*types100.IPConfig) {
- for _, ipconfig := range ipconfigs {
- if ipconfig.Address.IP.To4() != nil {
- dg.v4Addr = ipconfig.Address.IP
- } else {
- dg.v6Addr = ipconfig.Address.IP
- }
- }
-}
-
-// PingOverIface sends an arp ping over interface 'iface' to 'dstIP'
-func (dg *DetectGateway) ArpingOverIface() error {
- ifi, err := net.InterfaceByName(dg.iface)
- if err != nil {
- return err
- }
-
- client, err := arp.Dial(ifi)
- if err != nil {
- return err
- }
- defer client.Close()
-
- gwNetIP := netip.MustParseAddr(dg.V4Gw.String())
- if err = client.SetDeadline(time.Now().Add(dg.timeout)); err != nil {
- dg.logger.Sugar().Errorf("failed to set deadline: %v", err)
- return err
- }
-
- for i := 0; i < dg.retries; i++ {
- dg.logger.Sugar().Debugf("[Retry: %v]try to send the arp request", i+1)
- err := client.Request(gwNetIP)
- if err != nil {
- dg.logger.Sugar().Errorf("[Retry: %v]failed to send the arp request: %v", i+1, err)
- continue
- }
-
- }
-
- // Loop and wait for replies
- for {
- res, _, err := client.Read()
- if err != nil {
- dg.logger.Sugar().Errorf("gateway %s is %v, reason: %v", dg.V4Gw.String(), constant.ErrGatewayUnreachable, err)
- return fmt.Errorf("gateway %s is %v", dg.V4Gw.String(), constant.ErrGatewayUnreachable)
- }
-
- if res.Operation != arp.OperationReply || res.SenderIP != gwNetIP {
- continue
- }
-
- dg.logger.Sugar().Infof("Gateway %s is reachable, gateway is located at %v", gwNetIP, res.SenderHardwareAddr.String())
- return nil
- }
-}
-
-func (dg *DetectGateway) NDPingOverIface() error {
- ifi, err := net.InterfaceByName(dg.iface)
- if err != nil {
- return err
- }
-
- client, _, err := ndp.Listen(ifi, ndp.LinkLocal)
- if err != nil {
- return err
- }
- defer client.Close()
-
- msg := &ndp.NeighborSolicitation{
- TargetAddress: netip.MustParseAddr(dg.V6Gw.String()),
- Options: []ndp.Option{
- &ndp.LinkLayerAddress{
- Direction: ndp.Source,
- Addr: ifi.HardwareAddr,
- },
- },
- }
-
- var gwHwAddr string
- for i := 0; i < dg.retries && gwHwAddr == ""; i++ {
- gwHwAddr, err = dg.sendReceive(client, msg)
- if err != nil {
- dg.logger.Sugar().Errorf("[retry number: %v]error detect if gateway is reachable: %v", i+1, err)
- } else if gwHwAddr != "" {
- dg.logger.Sugar().Infof("gateway %s is reachable, it is located at %s", dg.V6Gw.String(), gwHwAddr)
- return nil
- }
- }
-
- if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
- dg.logger.Sugar().Errorf("gateway %s is unreachable, reason: %v", dg.V6Gw.String(), err)
- return fmt.Errorf("gateway %s is %w", dg.V6Gw.String(), constant.ErrGatewayUnreachable)
- }
- return fmt.Errorf("error detect the gateway %s if is reachable: %v", dg.V6Gw.String(), err)
-}
-
-func (dg *DetectGateway) sendReceive(client *ndp.Conn, m ndp.Message) (string, error) {
- gwNetIP := netip.MustParseAddr(dg.V6Gw.String())
- // Always multicast the message to the target's solicited-node multicast
- // group as if we have no knowledge of its MAC address.
- snm, err := ndp.SolicitedNodeMulticast(gwNetIP)
- if err != nil {
- dg.logger.Error("[NDP]failed to determine solicited-node multicast address", zap.Error(err))
- return "", fmt.Errorf("failed to determine solicited-node multicast address: %v", err)
- }
-
- if err := client.SetDeadline(time.Now().Add(dg.timeout)); err != nil {
- dg.logger.Error("[NDP]failed to set deadline", zap.Error(err))
- return "", fmt.Errorf("failed to set deadline: %v", err)
- }
-
- // we send a gratuitous neighbor solicitation to checking if ip is conflict
- err = client.WriteTo(m, nil, snm)
- if err != nil {
- dg.logger.Error("[NDP]failed to send message", zap.Error(err))
- return "", fmt.Errorf("failed to send message: %v", err)
- }
-
- msg, _, _, err := client.ReadFrom()
- if err != nil {
- return "", err
- }
-
- gwAddr := netip.MustParseAddr(dg.V6Gw.String())
- na, ok := msg.(*ndp.NeighborAdvertisement)
- if ok && na.TargetAddress.Compare(gwAddr) == 0 && len(na.Options) == 1 {
- dg.logger.Debug("Detect gateway: found the response", zap.String("TargetAddress", na.TargetAddress.String()))
- // found ndp reply what we want
- option, ok := na.Options[0].(*ndp.LinkLayerAddress)
- if ok {
- return option.Addr.String(), nil
- }
- }
- return "", nil
-}
diff --git a/pkg/networking/ipchecking/ipchecking.go b/pkg/networking/ipchecking/ipchecking.go
deleted file mode 100644
index 1ac5c44409..0000000000
--- a/pkg/networking/ipchecking/ipchecking.go
+++ /dev/null
@@ -1,249 +0,0 @@
-// Copyright 2023 Authors of spidernet-io
-// SPDX-License-Identifier: Apache-2.0
-
-package ipchecking
-
-import (
- "errors"
- "fmt"
- "net"
- "net/netip"
- "time"
-
- types100 "github.com/containernetworking/cni/pkg/types/100"
- "github.com/containernetworking/plugins/pkg/ns"
- "github.com/mdlayher/arp"
- "github.com/mdlayher/ethernet"
- "github.com/mdlayher/ndp"
- "go.uber.org/zap"
-
- "github.com/spidernet-io/spiderpool/pkg/constant"
- "github.com/spidernet-io/spiderpool/pkg/errgroup"
-)
-
-type IPChecker struct {
- retries int
- interval time.Duration
- timeout time.Duration
- netns, hostNs ns.NetNS
- ip4, ip6 netip.Addr
- ifi *net.Interface
- arpClient *arp.Client
- ndpClient *ndp.Conn
- logger *zap.Logger
-}
-
-func NewIPChecker(retries int, interval, timeout string, hostNs, netns ns.NetNS, logger *zap.Logger) (*IPChecker, error) {
- var err error
-
- ipc := new(IPChecker)
- ipc.retries = retries
- ipc.interval, err = time.ParseDuration(interval)
- if err != nil {
- return nil, fmt.Errorf("failed to parse interval %v: %v", interval, err)
- }
-
- ipc.timeout, err = time.ParseDuration(timeout)
- if err != nil {
- return nil, fmt.Errorf("failed to parse timeoute %v: %v", timeout, err)
- }
-
- if err != nil {
- return nil, err
- }
-
- ipc.hostNs = hostNs
- ipc.netns = netns
- ipc.logger = logger
- return ipc, nil
-}
-
-func (ipc *IPChecker) DoIPConflictChecking(ipconfigs []*types100.IPConfig, iface string, errg *errgroup.Group) {
- ipc.logger.Debug("DoIPConflictChecking", zap.String("interval", ipc.interval.String()), zap.Int("retries", ipc.retries), zap.String("timeout", ipc.timeout.String()))
- if len(ipconfigs) == 0 {
- ipc.logger.Info("No ips found in pod, ignore pod ip's conflict checking")
- return
- }
-
- var err error
- _ = ipc.netns.Do(func(netNS ns.NetNS) error {
- ipc.ifi, err = net.InterfaceByName(iface)
- if err != nil {
- return fmt.Errorf("failed to InterfaceByName %s: %w", iface, err)
- }
-
- for idx := range ipconfigs {
- target := netip.MustParseAddr(ipconfigs[idx].Address.IP.String())
- if target.Is4() {
- ipc.logger.Debug("IPCheckingByARP", zap.String("ipv4 address", target.String()))
- ipc.ip4 = target
- ipc.arpClient, err = arp.Dial(ipc.ifi)
- if err != nil {
- return fmt.Errorf("failed to init arp client: %w", err)
- }
- errg.Go(ipc.hostNs, ipc.netns, ipc.ipCheckingByARP)
- } else {
- ipc.logger.Debug("IPCheckingByNDP", zap.String("ipv6 address", target.String()))
- ipc.ip6 = target
- ipc.ndpClient, _, err = ndp.Listen(ipc.ifi, ndp.LinkLocal)
- if err != nil {
- return fmt.Errorf("failed to init ndp client: %w", err)
- }
- errg.Go(ipc.hostNs, ipc.netns, ipc.ipCheckingByNDP)
- }
- }
- return nil
- })
-}
-
-func (ipc *IPChecker) ipCheckingByARP() error {
- defer ipc.arpClient.Close()
-
- var err error
- for i := 0; i < ipc.retries; i++ {
- ipc.logger.Sugar().Debugf("[Retry: %v]try to arping the ip", i+1)
- if err = ipc.arpClient.SetDeadline(time.Now().Add(ipc.timeout)); err != nil {
- ipc.logger.Error("[ARP]failed to set deadline", zap.Error(err))
- continue
- }
-
- // we send a gratuitous arp to checking if ip is conflict
- // we use dad mode(duplicate address detection mode), so
- // we set source ip to 0.0.0.0
- packet, err := arp.NewPacket(arp.OperationRequest, ipc.ifi.HardwareAddr, netip.MustParseAddr("0.0.0.0"), ethernet.Broadcast, ipc.ip4)
- if err != nil {
- return err
- }
-
- err = ipc.arpClient.WriteTo(packet, ethernet.Broadcast)
- if err != nil {
- ipc.logger.Error("[ARP]failed to send message", zap.Error(err))
- continue
- }
-
- packet, _, err = ipc.arpClient.Read()
- if err != nil {
- ipc.logger.Error("[ARP]failed to receive message", zap.Error(err))
- continue
- }
-
- if packet.Operation != arp.OperationReply || packet.SenderIP.Compare(ipc.ip4) != 0 {
- continue
- }
-
- // found ip conflicting
- ipc.logger.Error("Found IPv4 address conflicting", zap.String("Conflicting IP", ipc.ip4.String()), zap.String("Host", packet.SenderHardwareAddr.String()))
- return fmt.Errorf("%w: pod's interface %s with an conflicting ip %s, %s is located at %s",
- constant.ErrIPConflict, ipc.ifi.Name, ipc.ip4.String(), ipc.ip4.String(), packet.SenderHardwareAddr.String())
- }
-
- if err != nil {
- if neterr, ok := err.(net.Error); ok && !neterr.Timeout() {
- return fmt.Errorf("failed to checking ip %s if it's conflicting: %v", ipc.ip4.String(), err)
- }
- }
-
- ipc.logger.Debug("No ipv4 address conflict", zap.String("IPv4 address", ipc.ip4.String()))
- return nil
-}
-
-var errRetry = errors.New("retry")
-var NDPFoundReply error = errors.New("found ndp reply")
-
-func (ipc *IPChecker) ipCheckingByNDP() error {
- var err error
- defer ipc.ndpClient.Close()
-
- m := &ndp.NeighborSolicitation{
- TargetAddress: ipc.ip6,
- Options: []ndp.Option{
- &ndp.LinkLayerAddress{
- Direction: ndp.Source,
- Addr: ipc.ifi.HardwareAddr,
- },
- },
- }
-
- var replyMac string
- replyMac, err = ipc.sendReceiveLoop(m)
- if err != nil {
- if err.Error() == NDPFoundReply.Error() {
- if replyMac != ipc.ifi.HardwareAddr.String() {
- ipc.logger.Error("Found IPv6 address conflicting", zap.String("Conflicting IP", ipc.ip6.String()), zap.String("Host", replyMac))
- return fmt.Errorf("%w: pod's interface %s with an conflicting ip %s, %s is located at %s",
- constant.ErrIPConflict, ipc.ifi.Name, ipc.ip6.String(), ipc.ip6.String(), replyMac)
- }
- }
- }
-
- // no ipv6 conflicting
- ipc.logger.Debug("No ipv6 address conflicting", zap.String("ipv6 address", ipc.ip6.String()))
- return nil
-}
-
-// sendReceiveLoop send ndp message and waiting for receive.
-// Copyright Authors of mdlayher/ndp: https://github.com/mdlayher/ndp/
-func (ipc *IPChecker) sendReceiveLoop(msg ndp.Message) (string, error) {
- var hwAddr string
- var err error
-
- for i := 0; i < ipc.retries; i++ {
- ipc.logger.Sugar().Debugf("[Retry: %v]try to ndping the ip", i+1)
- hwAddr, err = ipc.sendReceive(msg)
- switch err {
- case errRetry:
- continue
- case nil:
- return hwAddr, NDPFoundReply
- default:
- // Was the error caused by a read timeout, and should the loop continue?
- if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
- ipc.logger.Error(err.Error())
- continue
- }
- return "", err
- }
- }
-
- return "", nil
-}
-
-// sendReceive send and receive ndp message,return error if error occurred.
-// if the returned string isn't empty, it indicates that there are an
-// IPv6 address conflict.
-// Copyright Authors of mdlayher/ndp: https://github.com/mdlayher/ndp/
-func (ipc *IPChecker) sendReceive(m ndp.Message) (string, error) {
- // Always multicast the message to the target's solicited-node multicast
- // group as if we have no knowledge of its MAC address.
- snm, err := ndp.SolicitedNodeMulticast(ipc.ip6)
- if err != nil {
- ipc.logger.Error("[NDP]failed to determine solicited-node multicast address", zap.Error(err))
- return "", fmt.Errorf("failed to determine solicited-node multicast address: %v", err)
- }
-
- // we send a gratuitous neighbor solicitation to checking if ip is conflict
- err = ipc.ndpClient.WriteTo(m, nil, snm)
- if err != nil {
- ipc.logger.Error("[NDP]failed to send message", zap.Error(err))
- return "", fmt.Errorf("failed to send message: %v", err)
- }
-
- if err := ipc.ndpClient.SetReadDeadline(time.Now().Add(ipc.timeout)); err != nil {
- ipc.logger.Error("[NDP]failed to set deadline", zap.Error(err))
- return "", fmt.Errorf("failed to set deadline: %v", err)
- }
-
- msg, _, _, err := ipc.ndpClient.ReadFrom()
- if err == nil {
- na, ok := msg.(*ndp.NeighborAdvertisement)
- if ok && na.TargetAddress.Compare(ipc.ip6) == 0 && len(na.Options) == 1 {
- // found ndp reply what we want
- option, ok := na.Options[0].(*ndp.LinkLayerAddress)
- if ok {
- return option.Addr.String(), nil
- }
- }
- return "", errRetry
- }
- return "", err
-}
diff --git a/pkg/networking/networking/packet.go b/pkg/networking/networking/packet.go
new file mode 100644
index 0000000000..bdc3de2dd0
--- /dev/null
+++ b/pkg/networking/networking/packet.go
@@ -0,0 +1,315 @@
+// Copyright 2025 Authors of spidernet-io
+// SPDX-License-Identifier: Apache-2.0
+
+// Note: The following source files are come from the latest version of https://github.com/k8snetworkplumbingwg/sriov-cni/blob/master/pkg/utils/packet.go.
+// We can't directly go mod import package, it reports error:
+// "require github.com/k8snetworkplumbingwg/sriov-cni: version “v2.8.0” invalid: should be v0 or v1, not v2.""
+
+// So we copied the source files here and made some code changes.
+
+package networking
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "net"
+ "net/netip"
+ "syscall"
+ "time"
+
+ "golang.org/x/net/icmp"
+ "golang.org/x/net/ipv6"
+ "golang.org/x/sys/unix"
+
+ "github.com/vishvananda/netlink"
+
+ "github.com/mdlayher/ndp"
+)
+
+var (
+ arpPacketName = "ARP"
+ icmpV6PacketName = "ICMPv6"
+)
+
+// SetSocketTimeout sets the timeout for a socket in nanoseconds.
+func SetSocketTimeout(sock int, timeout time.Duration) error {
+ tv := syscall.NsecToTimeval(timeout.Nanoseconds())
+ return syscall.SetsockoptTimeval(sock, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &tv)
+}
+
+// SendARPReuqest sends a gratuitous ARP packet with the provided source IP over the provided interface.
+// UPDATE: the golang arp library requires an IPv4 address to exist for the NIC to send ARP request packets.
+func SendARPReuqest(l netlink.Link, srcIP, dstIP net.IP) error {
+ /* As per RFC 5944 section 4.6, a gratuitous ARP packet can be sent by a node in order to spontaneously cause other nodes to update
+ * an entry in their ARP cache. In the case of SRIOV-CNI, an address can be reused for different pods. Each pod could likely have a
+ * different link-layer address in this scenario, which makes the ARP cache entries residing in the other nodes to be an invalid.
+ * The gratuitous ARP packet should update the link-layer address accordingly for the invalid ARP cache.
+ */
+
+ // Construct the ARP packet following RFC 5944 section 4.6.
+ arpPacket := new(bytes.Buffer)
+ if writeErr := binary.Write(arpPacket, binary.BigEndian, uint16(1)); writeErr != nil { // Hardware Type: 1 is Ethernet
+ return formatPacketFieldWriteError("Hardware Type", arpPacketName, writeErr)
+ }
+ if writeErr := binary.Write(arpPacket, binary.BigEndian, uint16(syscall.ETH_P_IP)); writeErr != nil { // Protocol Type: 0x0800 is IPv4
+ return formatPacketFieldWriteError("Protocol Type", arpPacketName, writeErr)
+ }
+ if writeErr := binary.Write(arpPacket, binary.BigEndian, uint8(6)); writeErr != nil { // Hardware address Length: 6 bytes for MAC address
+ return formatPacketFieldWriteError("Hardware address Length", arpPacketName, writeErr)
+ }
+ if writeErr := binary.Write(arpPacket, binary.BigEndian, uint8(4)); writeErr != nil { // Protocol address length: 4 bytes for IPv4 address
+ return formatPacketFieldWriteError("Protocol address length", arpPacketName, writeErr)
+ }
+ if writeErr := binary.Write(arpPacket, binary.BigEndian, uint16(1)); writeErr != nil { // Operation: 1 is request, 2 is response
+ return formatPacketFieldWriteError("Operation", arpPacketName, writeErr)
+ }
+ if _, writeErr := arpPacket.Write(l.Attrs().HardwareAddr); writeErr != nil { // Sender hardware address
+ return formatPacketFieldWriteError("Sender hardware address", arpPacketName, writeErr)
+ }
+ if _, writeErr := arpPacket.Write(srcIP.To4()); writeErr != nil { // Sender protocol address
+ return formatPacketFieldWriteError("Sender protocol address", arpPacketName, writeErr)
+ }
+ if _, writeErr := arpPacket.Write([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}); writeErr != nil { // Target hardware address is the Broadcast MAC.
+ return formatPacketFieldWriteError("Target hardware address", arpPacketName, writeErr)
+ }
+ if _, writeErr := arpPacket.Write(dstIP.To4()); writeErr != nil { // Target protocol address
+ return formatPacketFieldWriteError("Target protocol address", arpPacketName, writeErr)
+ }
+
+ sockAddr := syscall.SockaddrLinklayer{
+ Protocol: htons(syscall.ETH_P_ARP), // Ethertype of ARP (0x0806)
+ Ifindex: l.Attrs().Index, // Interface Index
+ Hatype: 1, // Hardware Type: 1 is Ethernet
+ Pkttype: 0, // Packet Type.
+ Halen: 6, // Hardware address Length: 6 bytes for MAC address
+ Addr: [8]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, // Address is the broadcast MAC address.
+ }
+
+ // Create a socket such that the Ethernet header would constructed by the OS. The arpPacket only contains the ARP payload.
+ soc, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_DGRAM, int(htons(syscall.ETH_P_ARP)))
+ if err != nil {
+ return fmt.Errorf("failed to create AF_PACKET datagram socket: %v", err)
+ }
+ defer syscall.Close(soc)
+
+ if err := syscall.Sendto(soc, arpPacket.Bytes(), 0, &sockAddr); err != nil {
+ return fmt.Errorf("failed to send ARP request for IPv4 %s on Interface %s: %v", srcIP.String(), l.Attrs().Name, err)
+ }
+
+ return nil
+}
+
+func SendUnsolicitedNeighborAdvertisement(dstIP net.IP, ifi *net.Interface, ndpClient *ndp.Conn) error {
+ nDstIP := netip.MustParseAddr(dstIP.String())
+ m := &ndp.NeighborSolicitation{
+ TargetAddress: nDstIP,
+ Options: []ndp.Option{
+ &ndp.LinkLayerAddress{
+ Direction: ndp.Source,
+ Addr: ifi.HardwareAddr,
+ },
+ },
+ }
+
+ // Always multicast the message to the target's solicited-node multicast
+ // group as if we have no knowledge of its MAC address.
+ snm, err := ndp.SolicitedNodeMulticast(nDstIP)
+ if err != nil {
+ return err
+ }
+
+ // we send a gratuitous neighbor solicitation to checking if ip is conflict
+ err = ndpClient.WriteTo(m, nil, snm)
+ if err != nil {
+ return fmt.Errorf("failed to send ndp message: %v", err)
+ }
+
+ return nil
+}
+
+// SendUnsolicitedNeighborAdvertisement sends an unsolicited neighbor advertisement packet with the provided source IP over the provided interface.
+func SendUnsolicitedNeighborAdvertisement1(dstIP net.IP, l netlink.Link) error {
+ /* As per RFC 4861, a link-layer address change can multicast a few unsolicited neighbor advertisements to all nodes to quickly
+ * update the cached link-layer addresses that have become invalid. In the case of SRIOV-CNI, an address can be reused for
+ * different pods. Each pod could likely have a different link-layer address in this scenario, which makes the Neighbor Cache
+ * entries residing in the neighbors to be an invalid. The unsolicited neighbor advertisement should update the link-layer address
+ * accordingly for the IPv6 entry.
+ * However if any of these conditions are true:
+ * - The IPv6 address was not reused for the new pod.
+ * - No prior established communication with the neighbor.
+ * Then the neighbor receiving this unsolicited neighbor advertisement would be silently discard. This behavior is described
+ * in RFC 4861 section 7.2.5. This is acceptable behavior since the purpose of sending an unsolicited neighbor advertisement
+ * is not to create a new entry but rather update already existing invalid entries.
+ */
+
+ // Construct the ICMPv6 Neighbor Advertisement packet following RFC 4861.
+ //payload := new(bytes.Buffer)
+ // ICMPv6 Flags: As per RFC 4861, the solicited flag must not be set and the override flag should be set (to
+ // override existing cache entry) for unsolicited advertisements.
+ // if writeErr := binary.Write(payload, binary.BigEndian, uint32(0x20000000)); writeErr != nil {
+ // return formatPacketFieldWriteError("Flags", icmpV6PacketName, writeErr)
+ // }
+ // if _, writeErr := payload.Write(dstIP.To16()); writeErr != nil { // ICMPv6 Target IPv6 Address.
+ // return formatPacketFieldWriteError("Target IPv6 Address", icmpV6PacketName, writeErr)
+ // }
+ // if writeErr := binary.Write(payload, binary.BigEndian, uint8(2)); writeErr != nil { // ICMPv6 Option Type: 2 is target link-layer address.
+ // return formatPacketFieldWriteError("Option Type", icmpV6PacketName, writeErr)
+ // }
+ // if writeErr := binary.Write(payload, binary.BigEndian, uint8(1)); writeErr != nil { // ICMPv6 Option Length. Units of 8 bytes.
+ // return formatPacketFieldWriteError("Option Length", icmpV6PacketName, writeErr)
+ // }
+ // if _, writeErr := payload.Write(l.Attrs().HardwareAddr); writeErr != nil { // ICMPv6 Option Link-layer Address.
+ // return formatPacketFieldWriteError("Option Link-layer Address", icmpV6PacketName, writeErr)
+ // }
+ // Construct ICMPv6 Neighbor Solicitation message
+ payload := new(bytes.Buffer)
+ if _, writeErr := payload.Write(dstIP.To16()); writeErr != nil { // Target IPv6 address
+ return formatPacketFieldWriteError("Target IPv6 Address", icmpV6PacketName, writeErr) //"failed to write target IPv6 address: %v", writeErr)
+ }
+ if writeErr := binary.Write(payload, binary.BigEndian, uint8(1)); writeErr != nil { // Source link-layer address option type
+ return formatPacketFieldWriteError("Option Type", icmpV6PacketName, writeErr)
+ }
+ if writeErr := binary.Write(payload, binary.BigEndian, uint8(1)); writeErr != nil { // Option length
+ return formatPacketFieldWriteError("Option length", icmpV6PacketName, writeErr)
+ }
+ if _, writeErr := payload.Write(l.Attrs().HardwareAddr); writeErr != nil { // Source link-layer address
+ return formatPacketFieldWriteError("Source link-layer address", icmpV6PacketName, writeErr)
+ }
+
+ icmpv6Msg := icmp.Message{
+ Type: ipv6.ICMPTypeNeighborSolicitation, // ICMPv6 type is neighbor advertisement.
+ Code: 0, // ICMPv6 Code: As per RFC 4861 section 7.1.2, the code is always 0.
+ Checksum: 0, // Checksum is calculated later.
+ Body: &icmp.RawBody{
+ Data: payload.Bytes(),
+ },
+ }
+
+ // Get the byte array of the ICMPv6 Message.
+ icmpv6Bytes, err := icmpv6Msg.Marshal(nil)
+ if err != nil {
+ return fmt.Errorf("failed to Marshal ICMPv6 Message: %v", err)
+ }
+
+ // Create a socket such that the Ethernet header and IPv6 header would constructed by the OS.
+ soc, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_RAW, syscall.IPPROTO_ICMPV6)
+ if err != nil {
+ return fmt.Errorf("failed to create AF_INET6 raw socket: %v", err)
+ }
+ defer syscall.Close(soc)
+
+ // As per RFC 4861 section 7.1.2, the IPv6 hop limit is always 255.
+ if err := syscall.SetsockoptInt(soc, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_HOPS, 255); err != nil {
+ return fmt.Errorf("failed to set IPv6 multicast hops to 255: %v", err)
+ }
+
+ // Set the destination IPv6 address to the IPv6 link-local all nodes multicast address (ff02::1).
+ var r [16]byte
+ copy(r[:], net.IPv6linklocalallnodes.To16())
+ sockAddr := syscall.SockaddrInet6{Addr: r}
+ if err := syscall.Sendto(soc, icmpv6Bytes, 0, &sockAddr); err != nil {
+ return fmt.Errorf("failed to send Unsolicited Neighbor Advertisement for IPv6 %s on Interface %s: %v", dstIP.String(), l.Attrs().Name, err)
+ }
+
+ return nil
+}
+
+// Blocking wait for interface ifName to have carrier (!NO_CARRIER flag).
+func WaitForCarrier(l netlink.Link, waitTime time.Duration) bool {
+ var nextSleepDuration time.Duration
+
+ start := time.Now()
+
+ for nextSleepDuration == 0 || time.Since(start) < waitTime {
+ if nextSleepDuration == 0 {
+ nextSleepDuration = 2 * time.Millisecond
+ } else {
+ time.Sleep(nextSleepDuration)
+ /* Grow wait time exponentionally (factor 1.5). */
+ nextSleepDuration += nextSleepDuration / 2
+ }
+
+ /* Wait for carrier, i.e. IFF_UP|IFF_RUNNING. Note that there is also
+ * IFF_LOWER_UP, but we follow iproute2 ([1]).
+ *
+ * [1] https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/ipaddress.c?id=f9601b10c21145f76c3d46c163bac39515ed2061#n86
+ */
+
+ if l.Attrs().RawFlags&(unix.IFF_UP|unix.IFF_RUNNING) == (unix.IFF_UP | unix.IFF_RUNNING) {
+ return true
+ }
+ }
+
+ return false
+}
+
+// htons converts an uint16 from host to network byte order.
+func htons(i uint16) uint16 {
+ return (i<<8)&0xff00 | i>>8
+}
+
+// formatPacketFieldWriteError builds an error string for the cases when writing to a field of a packet fails.
+func formatPacketFieldWriteError(field string, packetType string, writeErr error) error {
+ return fmt.Errorf("failed to write the %s field in the %s packet: %v", field, packetType, writeErr)
+}
+
+// NewSock returns a new raw socket to listen for ARP packets on the specified network interface.
+func NewARPSockRAW(l netlink.Link) (fd int, err error) {
+ // Create a raw socket to listen for ARP packets.
+ sock, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, int(htons(syscall.ETH_P_ARP)))
+ if err != nil {
+ return fd, fmt.Errorf("failed to create raw socket: %v", err)
+ }
+ //defer syscall.Close(sock)
+
+ // Bind the socket to the network interface.
+ if err := syscall.Bind(sock, &syscall.SockaddrLinklayer{Ifindex: l.Attrs().Index}); err != nil {
+ return fd, fmt.Errorf("failed to bind socket to interface: %v", err)
+ }
+ return sock, nil
+}
+
+func NewNDPSockRaw(iface string) (int, error) {
+ // Create a raw socket for ICMPv6
+ sock, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_RAW, syscall.IPPROTO_ICMPV6)
+ if err != nil {
+ return sock, fmt.Errorf("failed to create raw socket: %v", err)
+ }
+
+ // Bind the socket to the network interface
+ if err := syscall.BindToDevice(sock, iface); err != nil {
+ return sock, fmt.Errorf("failed to bind socket to interface: %v", err)
+ }
+ return sock, nil
+}
+
+func ParseIPv6NeighborAdvertisementMsg(n int, buf []byte) (srcIP net.IP, mac net.HardwareAddr, err error) {
+ if n != 32 && buf[0] != 136 {
+ // this isn't a ICMPv6 NA message
+ return nil, nil, fmt.Errorf("this not a ICMPv6 neighbor advertisement message")
+ }
+
+ // Extract the source IP address (offset 8-24 in the IPv6 header)
+ srcIP = net.IP(buf[8:24])
+
+ // Options start after the 24-byte ICMPv6 header
+ options := buf[24:n]
+
+ // Check if the target link-layer address option is present
+ // Iterate over options to find the target link-layer address option
+ for i := 0; i < len(options); {
+ optionType := options[i]
+ optionLength := options[i+1] * 8 // Length is in units of 8 octets
+
+ if optionType == 2 { // Type 2 is the target link-layer address
+ if optionLength < 8 {
+ return nil, nil, fmt.Errorf("")
+ }
+ mac = net.HardwareAddr(options[i+2 : i+8])
+ }
+
+ i += int(optionLength)
+ }
+ return
+}
diff --git a/pkg/networking/sysctl/sysctl.go b/pkg/networking/sysctl/sysctl.go
index 509da487cc..0794df01ff 100644
--- a/pkg/networking/sysctl/sysctl.go
+++ b/pkg/networking/sysctl/sysctl.go
@@ -11,6 +11,7 @@ import (
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/utils/sysctl"
+ cnisysctl "github.com/containernetworking/plugins/pkg/utils/sysctl"
)
var (
@@ -74,10 +75,8 @@ func SetSysctlRPFilter(netns ns.NetNS, value int32) error {
}
// EnableIpv6Sysctl enable ipv6 for specify netns
-func EnableIpv6Sysctl(netns ns.NetNS, value int32) error {
- return netns.Do(func(_ ns.NetNS) error {
- return SetSysctl(SysctlEnableIPv6, fmt.Sprintf("%v", value))
- })
+func EnableIpv6Sysctl(value int32) error {
+ return SetSysctl(SysctlEnableIPv6, fmt.Sprintf("%v", value))
}
func SetSysctl(sysConfig string, value string) error {
@@ -96,3 +95,26 @@ func SetSysctl(sysConfig string, value string) error {
return nil
}
+
+func EnableIPv6ForInterfaces(ifaces []string) error {
+ disableIPv6SysctlTemplate := "net/ipv6/conf/%s/disable_ipv6"
+ for _, iface := range ifaces {
+ ipv6SysctlValueName := fmt.Sprintf(disableIPv6SysctlTemplate, iface)
+
+ // Read current sysctl value
+ value, err := cnisysctl.Sysctl(ipv6SysctlValueName)
+ if err != nil {
+ continue
+ }
+ if value == "0" {
+ continue
+ }
+
+ // Write sysctl to enable IPv6
+ _, err = cnisysctl.Sysctl(ipv6SysctlValueName, "0")
+ if err != nil {
+ return fmt.Errorf("failed to enable IPv6 for interface %q (%s=%s): %v", iface, ipv6SysctlValueName, value, err)
+ }
+ }
+ return nil
+}
diff --git a/pkg/utils/convert/convert.go b/pkg/utils/convert/convert.go
index ad565437e2..d47627315e 100644
--- a/pkg/utils/convert/convert.go
+++ b/pkg/utils/convert/convert.go
@@ -20,7 +20,7 @@ import (
"github.com/spidernet-io/spiderpool/pkg/types"
)
-func ConvertIPDetailsToIPConfigsAndAllRoutes(details []spiderpoolv2beta1.IPAllocationDetail) ([]*models.IPConfig, []*models.Route) {
+func ConvertIPDetailsToIPConfigsAndAllRoutes(details []spiderpoolv2beta1.IPAllocationDetail, enableIPConflictDetection, enableGatewayDetection bool) ([]*models.IPConfig, []*models.Route) {
var ips []*models.IPConfig
var routes []*models.Route
for _, d := range details {
@@ -36,12 +36,14 @@ func ConvertIPDetailsToIPConfigsAndAllRoutes(details []spiderpoolv2beta1.IPAlloc
}
}
ips = append(ips, &models.IPConfig{
- Address: d.IPv4,
- Gateway: ipv4Gateway,
- IPPool: *d.IPv4Pool,
- Nic: &nic,
- Version: &version,
- Vlan: *d.Vlan,
+ Address: d.IPv4,
+ Gateway: ipv4Gateway,
+ IPPool: *d.IPv4Pool,
+ Nic: &nic,
+ Version: &version,
+ Vlan: *d.Vlan,
+ EnableGatewayDetection: enableGatewayDetection,
+ EnableIPConflictDetection: enableIPConflictDetection,
})
}
@@ -56,12 +58,14 @@ func ConvertIPDetailsToIPConfigsAndAllRoutes(details []spiderpoolv2beta1.IPAlloc
}
}
ips = append(ips, &models.IPConfig{
- Address: d.IPv6,
- Gateway: ipv6Gateway,
- IPPool: *d.IPv6Pool,
- Nic: &nic,
- Version: &version,
- Vlan: *d.Vlan,
+ Address: d.IPv6,
+ Gateway: ipv6Gateway,
+ IPPool: *d.IPv6Pool,
+ Nic: &nic,
+ Version: &version,
+ Vlan: *d.Vlan,
+ EnableGatewayDetection: enableGatewayDetection,
+ EnableIPConflictDetection: enableIPConflictDetection,
})
}
diff --git a/test/Makefile b/test/Makefile
index 9b9ae8f0a0..5bf600f04c 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -248,6 +248,10 @@ setup_spiderpool:
else \
HELM_OPTION+=" --set ipam.enableSpiderSubnet=false " ; \
fi ; \
+ if [ "$(E2E_SPIDERPOOL_ENABLE_IPAM_DETECTION)" == "true" ] ; then \
+ HELM_OPTION+=" --set ipam.enableIPConflictDetection=true " ; \
+ HELM_OPTION+=" --set ipam.enableGatewayDetection=true " ; \
+ fi ; \
if [ "$(INSTALL_SRIOV)" == "true" ] ; then \
HELM_OPTION+=" --set sriov.install=true " ; \
else \
diff --git a/test/e2e/coordinator/macvlan-overlay-one/macvlan_overlay_one_test.go b/test/e2e/coordinator/macvlan-overlay-one/macvlan_overlay_one_test.go
index ba8e360acc..d0fbc5b83e 100644
--- a/test/e2e/coordinator/macvlan-overlay-one/macvlan_overlay_one_test.go
+++ b/test/e2e/coordinator/macvlan-overlay-one/macvlan_overlay_one_test.go
@@ -497,9 +497,12 @@ var _ = Describe("MacvlanOverlayOne", Label("overlay", "one-nic", "coordinator")
}
})
- It("gateway connection detection", Label("C00008"), func() {
+ // Add case V00007: spidercoordinator has the lowest priority here.
+ // Gateway detection is turned off in the default spidercoodinator:default,
+ // turned on in the new multus configuration and takes effect.
+ // Therefore, verifying spidercoodinator has the lowest priority.
+ PIt("gateway connection detection", Label("V00007", "C00009"), func() {
detectGatewayMultusName := "test-gateway-multus-" + common.GenerateString(10, true)
- detectGateway := true
// Define multus cni NetworkAttachmentDefinition and set DetectGateway to true
nad := &spiderpoolv2beta1.SpiderMultusConfig{
@@ -513,9 +516,8 @@ var _ = Describe("MacvlanOverlayOne", Label("overlay", "one-nic", "coordinator")
Master: []string{common.NIC1},
},
CoordinatorConfig: &spiderpoolv2beta1.CoordinatorSpec{
- Mode: &mode,
- DetectGateway: &detectGateway,
- PodCIDRType: &podCidrType,
+ Mode: &mode,
+ PodCIDRType: &podCidrType,
},
},
}
@@ -588,14 +590,12 @@ var _ = Describe("MacvlanOverlayOne", Label("overlay", "one-nic", "coordinator")
var v4IpConflict, v6IpConflict string
var depName, namespace, v4PoolName, v6PoolName, mode, podCidrType string
var v4PoolObj, v6PoolObj *spiderpoolv2beta1.SpiderIPPool
- var ipConflict bool
var multusNadName = "test-multus-" + common.GenerateString(10, true)
BeforeEach(func() {
depName = "dep-name-" + common.GenerateString(10, true)
namespace = "ns-" + common.GenerateString(10, true)
mode = "overlay"
- ipConflict = true
podCidrType = "cluster"
err := frame.CreateNamespaceUntilDefaultServiceAccountReady(namespace, common.ServiceAccountReadyTimeout)
@@ -614,9 +614,8 @@ var _ = Describe("MacvlanOverlayOne", Label("overlay", "one-nic", "coordinator")
VlanID: ptr.To(int32(200)),
},
CoordinatorConfig: &spiderpoolv2beta1.CoordinatorSpec{
- Mode: &mode,
- DetectIPConflict: &ipConflict,
- PodCIDRType: &podCidrType,
+ Mode: &mode,
+ PodCIDRType: &podCidrType,
},
},
}
@@ -635,7 +634,11 @@ var _ = Describe("MacvlanOverlayOne", Label("overlay", "one-nic", "coordinator")
})
})
- It("It should be possible to detect ip conflicts and log output", Label("C00006"), func() {
+ // Add case V00007: spidercoordinator has the lowest priority here.
+ // ip conflict detection is turned off in the default spidercoodinator:default,
+ // turned on in the new multus configuration and takes effect.
+ // Therefore, verifying spidercoodinator has the lowest priority.
+ PIt("It should be possible to detect ip conflicts and log output", Label("C00007", "V00007"), func() {
podAnno := types.AnnoPodIPPoolValue{}
if frame.Info.IpV4Enabled {
diff --git a/vendor/github.com/josharian/native/doc.go b/vendor/github.com/josharian/native/doc.go
deleted file mode 100644
index 2ca7ddc8a2..0000000000
--- a/vendor/github.com/josharian/native/doc.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// Package native provides easy access to native byte order.
-//
-// Usage: use native.Endian where you need the native binary.ByteOrder.
-//
-// Please think twice before using this package.
-// It can break program portability.
-// Native byte order is usually not the right answer.
-package native
diff --git a/vendor/github.com/josharian/native/endian_big.go b/vendor/github.com/josharian/native/endian_big.go
deleted file mode 100644
index 77744fdd40..0000000000
--- a/vendor/github.com/josharian/native/endian_big.go
+++ /dev/null
@@ -1,14 +0,0 @@
-//go:build mips || mips64 || ppc64 || s390x
-// +build mips mips64 ppc64 s390x
-
-package native
-
-import "encoding/binary"
-
-// Endian is the encoding/binary.ByteOrder implementation for the
-// current CPU's native byte order.
-var Endian = binary.BigEndian
-
-// IsBigEndian is whether the current CPU's native byte order is big
-// endian.
-const IsBigEndian = true
diff --git a/vendor/github.com/josharian/native/endian_generic.go b/vendor/github.com/josharian/native/endian_generic.go
deleted file mode 100644
index c15228f312..0000000000
--- a/vendor/github.com/josharian/native/endian_generic.go
+++ /dev/null
@@ -1,31 +0,0 @@
-//go:build !mips && !mips64 && !ppc64 && !s390x && !amd64 && !386 && !arm && !arm64 && !loong64 && !mipsle && !mips64le && !ppc64le && !riscv64 && !wasm
-// +build !mips,!mips64,!ppc64,!s390x,!amd64,!386,!arm,!arm64,!loong64,!mipsle,!mips64le,!ppc64le,!riscv64,!wasm
-
-// This file is a fallback, so that package native doesn't break
-// the instant the Go project adds support for a new architecture.
-//
-
-package native
-
-import (
- "encoding/binary"
- "log"
- "runtime"
- "unsafe"
-)
-
-var Endian binary.ByteOrder
-
-var IsBigEndian bool
-
-func init() {
- b := uint16(0xff) // one byte
- if *(*byte)(unsafe.Pointer(&b)) == 0 {
- Endian = binary.BigEndian
- IsBigEndian = true
- } else {
- Endian = binary.LittleEndian
- IsBigEndian = false
- }
- log.Printf("github.com/josharian/native: unrecognized arch %v (%v), please file an issue", runtime.GOARCH, Endian)
-}
diff --git a/vendor/github.com/josharian/native/endian_little.go b/vendor/github.com/josharian/native/endian_little.go
deleted file mode 100644
index 5098fec26b..0000000000
--- a/vendor/github.com/josharian/native/endian_little.go
+++ /dev/null
@@ -1,14 +0,0 @@
-//go:build amd64 || 386 || arm || arm64 || loong64 || mipsle || mips64le || ppc64le || riscv64 || wasm
-// +build amd64 386 arm arm64 loong64 mipsle mips64le ppc64le riscv64 wasm
-
-package native
-
-import "encoding/binary"
-
-// Endian is the encoding/binary.ByteOrder implementation for the
-// current CPU's native byte order.
-var Endian = binary.LittleEndian
-
-// IsBigEndian is whether the current CPU's native byte order is big
-// endian.
-const IsBigEndian = false
diff --git a/vendor/github.com/josharian/native/license b/vendor/github.com/josharian/native/license
deleted file mode 100644
index 6e617a9c7f..0000000000
--- a/vendor/github.com/josharian/native/license
+++ /dev/null
@@ -1,7 +0,0 @@
-Copyright 2020 Josh Bleecher Snyder
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/josharian/native/readme.md b/vendor/github.com/josharian/native/readme.md
deleted file mode 100644
index 1fc5a01b86..0000000000
--- a/vendor/github.com/josharian/native/readme.md
+++ /dev/null
@@ -1,10 +0,0 @@
-Package native provides easy access to native byte order.
-
-`go get github.com/josharian/native`
-
-Usage: Use `native.Endian` where you need the native binary.ByteOrder.
-
-Please think twice before using this package.
-It can break program portability.
-Native byte order is usually not the right answer.
-
diff --git a/vendor/github.com/mdlayher/arp/.travis.yml b/vendor/github.com/mdlayher/arp/.travis.yml
deleted file mode 100644
index 32a3387d21..0000000000
--- a/vendor/github.com/mdlayher/arp/.travis.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-language: go
-go:
- - 1.x
-os:
- - linux
-before_install:
- - go get golang.org/x/lint/golint
- - go get honnef.co/go/tools/cmd/staticcheck
- - go get -d ./...
-script:
- - go build -tags=gofuzz ./...
- - go vet ./...
- - staticcheck ./...
- - golint -set_exit_status ./...
- - go test -v -race ./...
\ No newline at end of file
diff --git a/vendor/github.com/mdlayher/arp/LICENSE.md b/vendor/github.com/mdlayher/arp/LICENSE.md
deleted file mode 100644
index 75ed9de174..0000000000
--- a/vendor/github.com/mdlayher/arp/LICENSE.md
+++ /dev/null
@@ -1,10 +0,0 @@
-MIT License
-===========
-
-Copyright (C) 2015 Matt Layher
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/mdlayher/arp/README.md b/vendor/github.com/mdlayher/arp/README.md
deleted file mode 100644
index 1b7ffba6b6..0000000000
--- a/vendor/github.com/mdlayher/arp/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-arp [](https://travis-ci.org/mdlayher/arp) [](https://godoc.org/github.com/mdlayher/arp) [](https://goreportcard.com/report/github.com/mdlayher/arp)
-===
-
-Package `arp` implements the ARP protocol, as described in RFC 826.
-MIT Licensed.
-
-Portions of this code are taken from the Go standard library. The Go
-standard library is Copyright (c) 2012 The Go Authors. All rights reserved.
-The Go license can be found at https://golang.org/LICENSE.
diff --git a/vendor/github.com/mdlayher/arp/client.go b/vendor/github.com/mdlayher/arp/client.go
deleted file mode 100644
index bd16f3a99c..0000000000
--- a/vendor/github.com/mdlayher/arp/client.go
+++ /dev/null
@@ -1,243 +0,0 @@
-package arp
-
-import (
- "errors"
- "net"
- "net/netip"
- "time"
-
- "github.com/mdlayher/ethernet"
- "github.com/mdlayher/packet"
-)
-
-// errNoIPv4Addr is returned when an interface does not have an IPv4
-// address.
-var errNoIPv4Addr = errors.New("no IPv4 address available for interface")
-
-// protocolARP is the uint16 EtherType representation of ARP (Address
-// Resolution Protocol, RFC 826).
-const protocolARP = 0x0806
-
-// A Client is an ARP client, which can be used to send and receive
-// ARP packets.
-type Client struct {
- ifi *net.Interface
- ip netip.Addr
- p net.PacketConn
-}
-
-// Dial creates a new Client using the specified network interface.
-// Dial retrieves the IPv4 address of the interface and binds a raw socket
-// to send and receive ARP packets.
-func Dial(ifi *net.Interface) (*Client, error) {
- // Open raw socket to send and receive ARP packets using ethernet frames
- // we build ourselves.
- p, err := packet.Listen(ifi, packet.Raw, protocolARP, nil)
- if err != nil {
- return nil, err
- }
- return New(ifi, p)
-}
-
-// New creates a new Client using the specified network interface
-// and net.PacketConn. This allows the caller to define exactly how they bind to the
-// net.PacketConn. This is most useful to define what protocol to pass to socket(7).
-//
-// In most cases, callers would be better off calling Dial.
-func New(ifi *net.Interface, p net.PacketConn) (*Client, error) {
- // Check for usable IPv4 addresses for the Client
- addrs, err := ifi.Addrs()
- if err != nil {
- return nil, err
- }
-
- ipaddrs := make([]netip.Addr, len(addrs))
- for i, a := range addrs {
- ipPrefix, err := netip.ParsePrefix(a.String())
- if err != nil {
- return nil, err
- }
- ipaddrs[i] = ipPrefix.Addr()
- }
-
- return newClient(ifi, p, ipaddrs)
-}
-
-// newClient is the internal, generic implementation of newClient. It is used
-// to allow an arbitrary net.PacketConn to be used in a Client, so testing
-// is easier to accomplish.
-func newClient(ifi *net.Interface, p net.PacketConn, addrs []netip.Addr) (*Client, error) {
- ip, err := firstIPv4Addr(addrs)
- if err != nil {
- return nil, err
- }
-
- return &Client{
- ifi: ifi,
- ip: ip,
- p: p,
- }, nil
-}
-
-// Close closes the Client's raw socket and stops sending and receiving
-// ARP packets.
-func (c *Client) Close() error {
- return c.p.Close()
-}
-
-// Request sends an ARP request, asking for the hardware address
-// associated with an IPv4 address. The response, if any, can be read
-// with the Read method.
-//
-// Unlike Resolve, which provides an easier interface for getting the
-// hardware address, Request allows sending many requests in a row,
-// retrieving the responses afterwards.
-func (c *Client) Request(ip netip.Addr) error {
- if !c.ip.IsValid() {
- return errNoIPv4Addr
- }
-
- // Create ARP packet for broadcast address to attempt to find the
- // hardware address of the input IP address
- arp, err := NewPacket(OperationRequest, c.ifi.HardwareAddr, c.ip, ethernet.Broadcast, ip)
- if err != nil {
- return err
- }
- return c.WriteTo(arp, ethernet.Broadcast)
-}
-
-// Resolve performs an ARP request, attempting to retrieve the
-// hardware address of a machine using its IPv4 address. Resolve must not
-// be used concurrently with Read. If you're using Read (usually in a
-// loop), you need to use Request instead. Resolve may read more than
-// one message if it receives messages unrelated to the request.
-func (c *Client) Resolve(ip netip.Addr) (net.HardwareAddr, error) {
- err := c.Request(ip)
- if err != nil {
- return nil, err
- }
-
- // Loop and wait for replies
- for {
- arp, _, err := c.Read()
- if err != nil {
- return nil, err
- }
-
- if arp.Operation != OperationReply || arp.SenderIP != ip {
- continue
- }
-
- return arp.SenderHardwareAddr, nil
- }
-}
-
-// Read reads a single ARP packet and returns it, together with its
-// ethernet frame.
-func (c *Client) Read() (*Packet, *ethernet.Frame, error) {
- buf := make([]byte, 128)
- for {
- n, _, err := c.p.ReadFrom(buf)
- if err != nil {
- return nil, nil, err
- }
-
- p, eth, err := parsePacket(buf[:n])
- if err != nil {
- if err == errInvalidARPPacket {
- continue
- }
- return nil, nil, err
- }
- return p, eth, nil
- }
-}
-
-// WriteTo writes a single ARP packet to addr. Note that addr should,
-// but doesn't have to, match the target hardware address of the ARP
-// packet.
-func (c *Client) WriteTo(p *Packet, addr net.HardwareAddr) error {
- pb, err := p.MarshalBinary()
- if err != nil {
- return err
- }
-
- f := ðernet.Frame{
- Destination: addr,
- Source: p.SenderHardwareAddr,
- EtherType: ethernet.EtherTypeARP,
- Payload: pb,
- }
-
- fb, err := f.MarshalBinary()
- if err != nil {
- return err
- }
-
- _, err = c.p.WriteTo(fb, &packet.Addr{HardwareAddr: addr})
- return err
-}
-
-// Reply constructs and sends a reply to an ARP request. On the ARP
-// layer, it will be addressed to the sender address of the packet. On
-// the ethernet layer, it will be sent to the actual remote address
-// from which the request was received.
-//
-// For more fine-grained control, use WriteTo to write a custom
-// response.
-func (c *Client) Reply(req *Packet, hwAddr net.HardwareAddr, ip netip.Addr) error {
- p, err := NewPacket(OperationReply, hwAddr, ip, req.SenderHardwareAddr, req.SenderIP)
- if err != nil {
- return err
- }
- return c.WriteTo(p, req.SenderHardwareAddr)
-}
-
-// Copyright (c) 2012 The Go Authors. All rights reserved.
-// Source code in this file is based on src/net/interface_linux.go,
-// from the Go standard library. The Go license can be found here:
-// https://golang.org/LICENSE.
-
-// Documentation taken from net.PacketConn interface. Thanks:
-// http://golang.org/pkg/net/#PacketConn.
-
-// SetDeadline sets the read and write deadlines associated with the
-// connection.
-func (c *Client) SetDeadline(t time.Time) error {
- return c.p.SetDeadline(t)
-}
-
-// SetReadDeadline sets the deadline for future raw socket read calls.
-// If the deadline is reached, a raw socket read will fail with a timeout
-// (see type net.Error) instead of blocking.
-// A zero value for t means a raw socket read will not time out.
-func (c *Client) SetReadDeadline(t time.Time) error {
- return c.p.SetReadDeadline(t)
-}
-
-// SetWriteDeadline sets the deadline for future raw socket write calls.
-// If the deadline is reached, a raw socket write will fail with a timeout
-// (see type net.Error) instead of blocking.
-// A zero value for t means a raw socket write will not time out.
-// Even if a write times out, it may return n > 0, indicating that
-// some of the data was successfully written.
-func (c *Client) SetWriteDeadline(t time.Time) error {
- return c.p.SetWriteDeadline(t)
-}
-
-// HardwareAddr fetches the hardware address for the interface associated
-// with the connection.
-func (c Client) HardwareAddr() net.HardwareAddr {
- return c.ifi.HardwareAddr
-}
-
-// firstIPv4Addr attempts to retrieve the first detected IPv4 address from an
-// input slice of network addresses.
-func firstIPv4Addr(addrs []netip.Addr) (netip.Addr, error) {
- for _, a := range addrs {
- if a.Is4() {
- return a, nil
- }
- }
- return netip.Addr{}, errNoIPv4Addr
-}
diff --git a/vendor/github.com/mdlayher/arp/doc.go b/vendor/github.com/mdlayher/arp/doc.go
deleted file mode 100644
index 7769f92faf..0000000000
--- a/vendor/github.com/mdlayher/arp/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// Package arp implements the ARP protocol, as described in RFC 826.
-package arp
diff --git a/vendor/github.com/mdlayher/arp/fuzz.go b/vendor/github.com/mdlayher/arp/fuzz.go
deleted file mode 100644
index 710119d137..0000000000
--- a/vendor/github.com/mdlayher/arp/fuzz.go
+++ /dev/null
@@ -1,17 +0,0 @@
-//go:build gofuzz
-// +build gofuzz
-
-package arp
-
-func Fuzz(data []byte) int {
- p := new(Packet)
- if err := p.UnmarshalBinary(data); err != nil {
- return 0
- }
-
- if _, err := p.MarshalBinary(); err != nil {
- panic(err)
- }
-
- return 1
-}
diff --git a/vendor/github.com/mdlayher/arp/packet.go b/vendor/github.com/mdlayher/arp/packet.go
deleted file mode 100644
index 8c619129e8..0000000000
--- a/vendor/github.com/mdlayher/arp/packet.go
+++ /dev/null
@@ -1,261 +0,0 @@
-package arp
-
-import (
- "bytes"
- "encoding/binary"
- "errors"
- "io"
- "net"
- "net/netip"
-
- "github.com/mdlayher/ethernet"
-)
-
-var (
- // ErrInvalidHardwareAddr is returned when one or more invalid hardware
- // addresses are passed to NewPacket.
- ErrInvalidHardwareAddr = errors.New("invalid hardware address")
-
- // ErrInvalidIP is returned when one or more invalid IPv4 addresses are
- // passed to NewPacket.
- ErrInvalidIP = errors.New("invalid IPv4 address")
-
- // errInvalidARPPacket is returned when an ethernet frame does not
- // indicate that an ARP packet is contained in its payload.
- errInvalidARPPacket = errors.New("invalid ARP packet")
-)
-
-//go:generate stringer -output=string.go -type=Operation
-
-// An Operation is an ARP operation, such as request or reply.
-type Operation uint16
-
-// Operation constants which indicate an ARP request or reply.
-const (
- OperationRequest Operation = 1
- OperationReply Operation = 2
-)
-
-// A Packet is a raw ARP packet, as described in RFC 826.
-type Packet struct {
- // HardwareType specifies an IANA-assigned hardware type, as described
- // in RFC 826.
- HardwareType uint16
-
- // ProtocolType specifies the internetwork protocol for which the ARP
- // request is intended. Typically, this is the IPv4 EtherType.
- ProtocolType uint16
-
- // HardwareAddrLength specifies the length of the sender and target
- // hardware addresses included in a Packet.
- HardwareAddrLength uint8
-
- // IPLength specifies the length of the sender and target IPv4 addresses
- // included in a Packet.
- IPLength uint8
-
- // Operation specifies the ARP operation being performed, such as request
- // or reply.
- Operation Operation
-
- // SenderHardwareAddr specifies the hardware address of the sender of this
- // Packet.
- SenderHardwareAddr net.HardwareAddr
-
- // SenderIP specifies the IPv4 address of the sender of this Packet.
- SenderIP netip.Addr
-
- // TargetHardwareAddr specifies the hardware address of the target of this
- // Packet.
- TargetHardwareAddr net.HardwareAddr
-
- // TargetIP specifies the IPv4 address of the target of this Packet.
- TargetIP netip.Addr
-}
-
-// NewPacket creates a new Packet from an input Operation and hardware/IPv4
-// address values for both a sender and target.
-//
-// If either hardware address is less than 6 bytes in length, or there is a
-// length mismatch between the two, ErrInvalidHardwareAddr is returned.
-//
-// If either IP address is not an IPv4 address, or there is a length mismatch
-// between the two, ErrInvalidIP is returned.
-func NewPacket(op Operation, srcHW net.HardwareAddr, srcIP netip.Addr, dstHW net.HardwareAddr, dstIP netip.Addr) (*Packet, error) {
- // Validate hardware addresses for minimum length, and matching length
- if len(srcHW) < 6 {
- return nil, ErrInvalidHardwareAddr
- }
- if len(dstHW) < 6 {
- return nil, ErrInvalidHardwareAddr
- }
- if !bytes.Equal(ethernet.Broadcast, dstHW) && len(srcHW) != len(dstHW) {
- return nil, ErrInvalidHardwareAddr
- }
-
- // Validate IP addresses to ensure they are IPv4 addresses, and
- // correct length
- var invalidIP netip.Addr
- if !srcIP.IsValid() || !srcIP.Is4() {
- return nil, ErrInvalidIP
- }
- if !dstIP.Is4() || dstIP == invalidIP {
- return nil, ErrInvalidIP
- }
-
- return &Packet{
- // There is no Go-native way to detect hardware type of a network
- // interface, so default to 1 (ethernet 10Mb) for now
- HardwareType: 1,
-
- // Default to EtherType for IPv4
- ProtocolType: uint16(ethernet.EtherTypeIPv4),
-
- // Populate other fields using input data
- HardwareAddrLength: uint8(len(srcHW)),
- IPLength: uint8(4),
- Operation: op,
- SenderHardwareAddr: srcHW,
- SenderIP: srcIP,
- TargetHardwareAddr: dstHW,
- TargetIP: dstIP,
- }, nil
-}
-
-// MarshalBinary allocates a byte slice containing the data from a Packet.
-//
-// MarshalBinary never returns an error.
-func (p *Packet) MarshalBinary() ([]byte, error) {
- // 2 bytes: hardware type
- // 2 bytes: protocol type
- // 1 byte : hardware address length
- // 1 byte : protocol length
- // 2 bytes: operation
- // N bytes: source hardware address
- // N bytes: source protocol address
- // N bytes: target hardware address
- // N bytes: target protocol address
-
- // Though an IPv4 address should always 4 bytes, go-fuzz
- // very quickly created several crasher scenarios which
- // indicated that these values can lie.
- b := make([]byte, 2+2+1+1+2+(p.IPLength*2)+(p.HardwareAddrLength*2))
-
- // Marshal fixed length data
-
- binary.BigEndian.PutUint16(b[0:2], p.HardwareType)
- binary.BigEndian.PutUint16(b[2:4], p.ProtocolType)
-
- b[4] = p.HardwareAddrLength
- b[5] = p.IPLength
-
- binary.BigEndian.PutUint16(b[6:8], uint16(p.Operation))
-
- // Marshal variable length data at correct offset using lengths
- // defined in p
-
- n := 8
- hal := int(p.HardwareAddrLength)
- pl := int(p.IPLength)
-
- copy(b[n:n+hal], p.SenderHardwareAddr)
- n += hal
-
- sender4 := p.SenderIP.As4()
- copy(b[n:n+pl], sender4[:])
- n += pl
-
- copy(b[n:n+hal], p.TargetHardwareAddr)
- n += hal
-
- target4 := p.TargetIP.As4()
- copy(b[n:n+pl], target4[:])
-
- return b, nil
-}
-
-// UnmarshalBinary unmarshals a raw byte slice into a Packet.
-func (p *Packet) UnmarshalBinary(b []byte) error {
- // Must have enough room to retrieve hardware address and IP lengths
- if len(b) < 8 {
- return io.ErrUnexpectedEOF
- }
-
- // Retrieve fixed length data
-
- p.HardwareType = binary.BigEndian.Uint16(b[0:2])
- p.ProtocolType = binary.BigEndian.Uint16(b[2:4])
-
- p.HardwareAddrLength = b[4]
- p.IPLength = b[5]
-
- p.Operation = Operation(binary.BigEndian.Uint16(b[6:8]))
-
- // Unmarshal variable length data at correct offset using lengths
- // defined by ml and il
- //
- // These variables are meant to improve readability of offset calculations
- // for the code below
- n := 8
- ml := int(p.HardwareAddrLength)
- ml2 := ml * 2
- il := int(p.IPLength)
- il2 := il * 2
-
- // Must have enough room to retrieve both hardware address and IP addresses
- addrl := n + ml2 + il2
- if len(b) < addrl {
- return io.ErrUnexpectedEOF
- }
-
- // Allocate single byte slice to store address information, which
- // is resliced into fields
- bb := make([]byte, addrl-n)
-
- // Sender hardware address
- copy(bb[0:ml], b[n:n+ml])
- p.SenderHardwareAddr = bb[0:ml]
- n += ml
-
- // Sender IP address
- copy(bb[ml:ml+il], b[n:n+il])
- senderIP, ok := netip.AddrFromSlice(bb[ml : ml+il])
- if !ok {
- return errors.New("Invalid Sender IP address")
- }
- p.SenderIP = senderIP
- n += il
-
- // Target hardware address
- copy(bb[ml+il:ml2+il], b[n:n+ml])
- p.TargetHardwareAddr = bb[ml+il : ml2+il]
- n += ml
-
- // Target IP address
- copy(bb[ml2+il:ml2+il2], b[n:n+il])
- targetIP, ok := netip.AddrFromSlice(bb[ml2+il : ml2+il2])
- if !ok {
- return errors.New("Invalid Target IP address")
- }
- p.TargetIP = targetIP
-
- return nil
-}
-
-func parsePacket(buf []byte) (*Packet, *ethernet.Frame, error) {
- f := new(ethernet.Frame)
- if err := f.UnmarshalBinary(buf); err != nil {
- return nil, nil, err
- }
-
- // Ignore frames which do not have ARP EtherType
- if f.EtherType != ethernet.EtherTypeARP {
- return nil, nil, errInvalidARPPacket
- }
-
- p := new(Packet)
- if err := p.UnmarshalBinary(f.Payload); err != nil {
- return nil, nil, err
- }
- return p, f, nil
-}
diff --git a/vendor/github.com/mdlayher/arp/string.go b/vendor/github.com/mdlayher/arp/string.go
deleted file mode 100644
index 1003f06d40..0000000000
--- a/vendor/github.com/mdlayher/arp/string.go
+++ /dev/null
@@ -1,17 +0,0 @@
-// Code generated by "stringer -output=string.go -type=Operation"; DO NOT EDIT.
-
-package arp
-
-import "strconv"
-
-const _Operation_name = "OperationRequestOperationReply"
-
-var _Operation_index = [...]uint8{0, 16, 30}
-
-func (i Operation) String() string {
- i -= 1
- if i >= Operation(len(_Operation_index)-1) {
- return "Operation(" + strconv.FormatInt(int64(i+1), 10) + ")"
- }
- return _Operation_name[_Operation_index[i]:_Operation_index[i+1]]
-}
diff --git a/vendor/github.com/mdlayher/ethernet/.travis.yml b/vendor/github.com/mdlayher/ethernet/.travis.yml
deleted file mode 100644
index cc215992aa..0000000000
--- a/vendor/github.com/mdlayher/ethernet/.travis.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-language: go
-go:
- - 1.x
-os:
- - linux
-before_install:
- - go get golang.org/x/lint/golint
- - go get honnef.co/go/tools/cmd/staticcheck
- - go get -d ./...
-script:
- - go build -tags=gofuzz ./...
- - go vet ./...
- - staticcheck ./...
- - golint -set_exit_status ./...
- - go test -v -race ./...
diff --git a/vendor/github.com/mdlayher/ethernet/LICENSE.md b/vendor/github.com/mdlayher/ethernet/LICENSE.md
deleted file mode 100644
index 75ed9de174..0000000000
--- a/vendor/github.com/mdlayher/ethernet/LICENSE.md
+++ /dev/null
@@ -1,10 +0,0 @@
-MIT License
-===========
-
-Copyright (C) 2015 Matt Layher
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/mdlayher/ethernet/README.md b/vendor/github.com/mdlayher/ethernet/README.md
deleted file mode 100644
index ec6f4fe4b1..0000000000
--- a/vendor/github.com/mdlayher/ethernet/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-ethernet [](https://travis-ci.org/mdlayher/ethernet) [](https://godoc.org/github.com/mdlayher/ethernet) [](https://goreportcard.com/report/github.com/mdlayher/ethernet)
-========
-
-Package `ethernet` implements marshaling and unmarshaling of IEEE 802.3
-Ethernet II frames and IEEE 802.1Q VLAN tags. MIT Licensed.
-
-For more information about using Ethernet frames in Go, check out my blog
-post: [Network Protocol Breakdown: Ethernet and Go](https://medium.com/@mdlayher/network-protocol-breakdown-ethernet-and-go-de985d726cc1).
\ No newline at end of file
diff --git a/vendor/github.com/mdlayher/ethernet/ethernet.go b/vendor/github.com/mdlayher/ethernet/ethernet.go
deleted file mode 100644
index a462413f3d..0000000000
--- a/vendor/github.com/mdlayher/ethernet/ethernet.go
+++ /dev/null
@@ -1,297 +0,0 @@
-// Package ethernet implements marshaling and unmarshaling of IEEE 802.3
-// Ethernet II frames and IEEE 802.1Q VLAN tags.
-package ethernet
-
-import (
- "encoding/binary"
- "errors"
- "fmt"
- "hash/crc32"
- "io"
- "net"
-)
-
-//go:generate stringer -output=string.go -type=EtherType
-
-const (
- // minPayload is the minimum payload size for an Ethernet frame, assuming
- // that no 802.1Q VLAN tags are present.
- minPayload = 46
-)
-
-// Broadcast is a special hardware address which indicates a Frame should
-// be sent to every device on a given LAN segment.
-var Broadcast = net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
-
-// ErrInvalidFCS is returned when Frame.UnmarshalFCS detects an incorrect
-// Ethernet frame check sequence in a byte slice for a Frame.
-var ErrInvalidFCS = errors.New("invalid frame check sequence")
-
-// An EtherType is a value used to identify an upper layer protocol
-// encapsulated in a Frame.
-//
-// A list of IANA-assigned EtherType values may be found here:
-// http://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml.
-type EtherType uint16
-
-// Common EtherType values frequently used in a Frame.
-const (
- EtherTypeIPv4 EtherType = 0x0800
- EtherTypeARP EtherType = 0x0806
- EtherTypeIPv6 EtherType = 0x86DD
-
- // EtherTypeVLAN and EtherTypeServiceVLAN are used as 802.1Q Tag Protocol
- // Identifiers (TPIDs).
- EtherTypeVLAN EtherType = 0x8100
- EtherTypeServiceVLAN EtherType = 0x88a8
-)
-
-// A Frame is an IEEE 802.3 Ethernet II frame. A Frame contains information
-// such as source and destination hardware addresses, zero or more optional
-// 802.1Q VLAN tags, an EtherType, and payload data.
-type Frame struct {
- // Destination specifies the destination hardware address for this Frame.
- //
- // If this address is set to Broadcast, the Frame will be sent to every
- // device on a given LAN segment.
- Destination net.HardwareAddr
-
- // Source specifies the source hardware address for this Frame.
- //
- // Typically, this is the hardware address of the network interface used to
- // send this Frame.
- Source net.HardwareAddr
-
- // ServiceVLAN specifies an optional 802.1Q service VLAN tag, for use with
- // 802.1ad double tagging, or "Q-in-Q". If ServiceVLAN is not nil, VLAN must
- // not be nil as well.
- //
- // Most users should leave this field set to nil and use VLAN instead.
- ServiceVLAN *VLAN
-
- // VLAN specifies an optional 802.1Q customer VLAN tag, which may or may
- // not be present in a Frame. It is important to note that the operating
- // system may automatically strip VLAN tags before they can be parsed.
- VLAN *VLAN
-
- // EtherType is a value used to identify an upper layer protocol
- // encapsulated in this Frame.
- EtherType EtherType
-
- // Payload is a variable length data payload encapsulated by this Frame.
- Payload []byte
-}
-
-// MarshalBinary allocates a byte slice and marshals a Frame into binary form.
-func (f *Frame) MarshalBinary() ([]byte, error) {
- b := make([]byte, f.length())
- _, err := f.read(b)
- return b, err
-}
-
-// MarshalFCS allocates a byte slice, marshals a Frame into binary form, and
-// finally calculates and places a 4-byte IEEE CRC32 frame check sequence at
-// the end of the slice.
-//
-// Most users should use MarshalBinary instead. MarshalFCS is provided as a
-// convenience for rare occasions when the operating system cannot
-// automatically generate a frame check sequence for an Ethernet frame.
-func (f *Frame) MarshalFCS() ([]byte, error) {
- // Frame length with 4 extra bytes for frame check sequence
- b := make([]byte, f.length()+4)
- if _, err := f.read(b); err != nil {
- return nil, err
- }
-
- // Compute IEEE CRC32 checksum of frame bytes and place it directly
- // in the last four bytes of the slice
- binary.BigEndian.PutUint32(b[len(b)-4:], crc32.ChecksumIEEE(b[0:len(b)-4]))
- return b, nil
-}
-
-// read reads data from a Frame into b. read is used to marshal a Frame
-// into binary form, but does not allocate on its own.
-func (f *Frame) read(b []byte) (int, error) {
- // S-VLAN must also have accompanying C-VLAN.
- if f.ServiceVLAN != nil && f.VLAN == nil {
- return 0, ErrInvalidVLAN
- }
-
- copy(b[0:6], f.Destination)
- copy(b[6:12], f.Source)
-
- // Marshal each non-nil VLAN tag into bytes, inserting the appropriate
- // EtherType/TPID before each, so devices know that one or more VLANs
- // are present.
- vlans := []struct {
- vlan *VLAN
- tpid EtherType
- }{
- {vlan: f.ServiceVLAN, tpid: EtherTypeServiceVLAN},
- {vlan: f.VLAN, tpid: EtherTypeVLAN},
- }
-
- n := 12
- for _, vt := range vlans {
- if vt.vlan == nil {
- continue
- }
-
- // Add VLAN EtherType and VLAN bytes.
- binary.BigEndian.PutUint16(b[n:n+2], uint16(vt.tpid))
- if _, err := vt.vlan.read(b[n+2 : n+4]); err != nil {
- return 0, err
- }
- n += 4
- }
-
- // Marshal actual EtherType after any VLANs, copy payload into
- // output bytes.
- binary.BigEndian.PutUint16(b[n:n+2], uint16(f.EtherType))
- copy(b[n+2:], f.Payload)
-
- return len(b), nil
-}
-
-// UnmarshalBinary unmarshals a byte slice into a Frame.
-func (f *Frame) UnmarshalBinary(b []byte) error {
- // Verify that both hardware addresses and a single EtherType are present
- if len(b) < 14 {
- return io.ErrUnexpectedEOF
- }
-
- // Track offset in packet for reading data
- n := 14
-
- // Continue looping and parsing VLAN tags until no more VLAN EtherType
- // values are detected
- et := EtherType(binary.BigEndian.Uint16(b[n-2 : n]))
- switch et {
- case EtherTypeServiceVLAN, EtherTypeVLAN:
- // VLAN type is hinted for further parsing. An index is returned which
- // indicates how many bytes were consumed by VLAN tags.
- nn, err := f.unmarshalVLANs(et, b[n:])
- if err != nil {
- return err
- }
-
- n += nn
- default:
- // No VLANs detected.
- f.EtherType = et
- }
-
- // Allocate single byte slice to store destination and source hardware
- // addresses, and payload
- bb := make([]byte, 6+6+len(b[n:]))
- copy(bb[0:6], b[0:6])
- f.Destination = bb[0:6]
- copy(bb[6:12], b[6:12])
- f.Source = bb[6:12]
-
- // There used to be a minimum payload length restriction here, but as
- // long as two hardware addresses and an EtherType are present, it
- // doesn't really matter what is contained in the payload. We will
- // follow the "robustness principle".
- copy(bb[12:], b[n:])
- f.Payload = bb[12:]
-
- return nil
-}
-
-// UnmarshalFCS computes the IEEE CRC32 frame check sequence of a Frame,
-// verifies it against the checksum present in the byte slice, and finally,
-// unmarshals a byte slice into a Frame.
-//
-// Most users should use UnmarshalBinary instead. UnmarshalFCS is provided as
-// a convenience for rare occasions when the operating system cannot
-// automatically verify a frame check sequence for an Ethernet frame.
-func (f *Frame) UnmarshalFCS(b []byte) error {
- // Must contain enough data for FCS, to avoid panics
- if len(b) < 4 {
- return io.ErrUnexpectedEOF
- }
-
- // Verify checksum in slice versus newly computed checksum
- want := binary.BigEndian.Uint32(b[len(b)-4:])
- got := crc32.ChecksumIEEE(b[0 : len(b)-4])
- if want != got {
- return ErrInvalidFCS
- }
-
- return f.UnmarshalBinary(b[0 : len(b)-4])
-}
-
-// length calculates the number of bytes required to store a Frame.
-func (f *Frame) length() int {
- // If payload is less than the required minimum length, we zero-pad up to
- // the required minimum length
- pl := len(f.Payload)
- if pl < minPayload {
- pl = minPayload
- }
-
- // Add additional length if VLAN tags are needed.
- var vlanLen int
- switch {
- case f.ServiceVLAN != nil && f.VLAN != nil:
- vlanLen = 8
- case f.VLAN != nil:
- vlanLen = 4
- }
-
- // 6 bytes: destination hardware address
- // 6 bytes: source hardware address
- // N bytes: VLAN tags (if present)
- // 2 bytes: EtherType
- // N bytes: payload length (may be padded)
- return 6 + 6 + vlanLen + 2 + pl
-}
-
-// unmarshalVLANs unmarshals S/C-VLAN tags. It is assumed that tpid
-// is a valid S/C-VLAN TPID.
-func (f *Frame) unmarshalVLANs(tpid EtherType, b []byte) (int, error) {
- // 4 or more bytes must remain for valid S/C-VLAN tag and EtherType.
- if len(b) < 4 {
- return 0, io.ErrUnexpectedEOF
- }
-
- // Track how many bytes are consumed by VLAN tags.
- var n int
-
- switch tpid {
- case EtherTypeServiceVLAN:
- vlan := new(VLAN)
- if err := vlan.UnmarshalBinary(b[n : n+2]); err != nil {
- return 0, err
- }
- f.ServiceVLAN = vlan
-
- // Assume that a C-VLAN immediately trails an S-VLAN.
- if EtherType(binary.BigEndian.Uint16(b[n+2:n+4])) != EtherTypeVLAN {
- return 0, ErrInvalidVLAN
- }
-
- // 4 or more bytes must remain for valid C-VLAN tag and EtherType.
- n += 4
- if len(b[n:]) < 4 {
- return 0, io.ErrUnexpectedEOF
- }
-
- // Continue to parse the C-VLAN.
- fallthrough
- case EtherTypeVLAN:
- vlan := new(VLAN)
- if err := vlan.UnmarshalBinary(b[n : n+2]); err != nil {
- return 0, err
- }
-
- f.VLAN = vlan
- f.EtherType = EtherType(binary.BigEndian.Uint16(b[n+2 : n+4]))
- n += 4
- default:
- panic(fmt.Sprintf("unknown VLAN TPID: %04x", tpid))
- }
-
- return n, nil
-}
diff --git a/vendor/github.com/mdlayher/ethernet/fuzz.go b/vendor/github.com/mdlayher/ethernet/fuzz.go
deleted file mode 100644
index 2698b3bd9a..0000000000
--- a/vendor/github.com/mdlayher/ethernet/fuzz.go
+++ /dev/null
@@ -1,25 +0,0 @@
-//go:build gofuzz
-// +build gofuzz
-
-package ethernet
-
-func Fuzz(data []byte) int {
- f := new(Frame)
- if err := f.UnmarshalBinary(data); err != nil {
- return 0
- }
-
- if _, err := f.MarshalBinary(); err != nil {
- panic(err)
- }
-
- if err := f.UnmarshalFCS(data); err != nil {
- return 0
- }
-
- if _, err := f.MarshalFCS(); err != nil {
- panic(err)
- }
-
- return 1
-}
diff --git a/vendor/github.com/mdlayher/ethernet/string.go b/vendor/github.com/mdlayher/ethernet/string.go
deleted file mode 100644
index 89a3e010a1..0000000000
--- a/vendor/github.com/mdlayher/ethernet/string.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// Code generated by "stringer -output=string.go -type=EtherType"; DO NOT EDIT.
-
-package ethernet
-
-import "fmt"
-
-const (
- _EtherType_name_0 = "EtherTypeIPv4"
- _EtherType_name_1 = "EtherTypeARP"
- _EtherType_name_2 = "EtherTypeVLAN"
- _EtherType_name_3 = "EtherTypeIPv6"
- _EtherType_name_4 = "EtherTypeServiceVLAN"
-)
-
-var (
- _EtherType_index_0 = [...]uint8{0, 13}
- _EtherType_index_1 = [...]uint8{0, 12}
- _EtherType_index_2 = [...]uint8{0, 13}
- _EtherType_index_3 = [...]uint8{0, 13}
- _EtherType_index_4 = [...]uint8{0, 20}
-)
-
-func (i EtherType) String() string {
- switch {
- case i == 2048:
- return _EtherType_name_0
- case i == 2054:
- return _EtherType_name_1
- case i == 33024:
- return _EtherType_name_2
- case i == 34525:
- return _EtherType_name_3
- case i == 34984:
- return _EtherType_name_4
- default:
- return fmt.Sprintf("EtherType(%d)", i)
- }
-}
diff --git a/vendor/github.com/mdlayher/ethernet/vlan.go b/vendor/github.com/mdlayher/ethernet/vlan.go
deleted file mode 100644
index 87b7ca2daf..0000000000
--- a/vendor/github.com/mdlayher/ethernet/vlan.go
+++ /dev/null
@@ -1,127 +0,0 @@
-package ethernet
-
-import (
- "encoding/binary"
- "errors"
- "io"
-)
-
-const (
- // VLANNone is a special VLAN ID which indicates that no VLAN is being
- // used in a Frame. In this case, the VLAN's other fields may be used
- // to indicate a Frame's priority.
- VLANNone = 0x000
-
- // VLANMax is a reserved VLAN ID which may indicate a wildcard in some
- // management systems, but may not be configured or transmitted in a
- // VLAN tag.
- VLANMax = 0xfff
-)
-
-// ErrInvalidVLAN is returned when a VLAN tag is invalid due to one of the
-// following reasons:
-// - Priority of greater than 7 is detected
-// - ID of greater than 4094 (0xffe) is detected
-// - A customer VLAN does not follow a service VLAN (when using Q-in-Q)
-var ErrInvalidVLAN = errors.New("invalid VLAN")
-
-// Priority is an IEEE P802.1p priority level. Priority can be any value from
-// 0 to 7.
-//
-// It is important to note that priority 1 (PriorityBackground) actually has
-// a lower priority than 0 (PriorityBestEffort). All other Priority constants
-// indicate higher priority as the integer values increase.
-type Priority uint8
-
-// IEEE P802.1p recommended priority levels. Note that PriorityBackground has
-// a lower priority than PriorityBestEffort.
-const (
- PriorityBackground Priority = 1
- PriorityBestEffort Priority = 0
- PriorityExcellentEffort Priority = 2
- PriorityCriticalApplications Priority = 3
- PriorityVideo Priority = 4
- PriorityVoice Priority = 5
- PriorityInternetworkControl Priority = 6
- PriorityNetworkControl Priority = 7
-)
-
-// A VLAN is an IEEE 802.1Q Virtual LAN (VLAN) tag. A VLAN contains
-// information regarding traffic priority and a VLAN identifier for
-// a given Frame.
-type VLAN struct {
- // Priority specifies a IEEE P802.1p priority level. Priority can be any
- // value from 0 to 7.
- Priority Priority
-
- // DropEligible indicates if a Frame is eligible to be dropped in the
- // presence of network congestion.
- DropEligible bool
-
- // ID specifies the VLAN ID for a Frame. ID can be any value from 0 to
- // 4094 (0x000 to 0xffe), allowing up to 4094 VLANs.
- //
- // If ID is 0 (0x000, VLANNone), no VLAN is specified, and the other fields
- // simply indicate a Frame's priority.
- ID uint16
-}
-
-// MarshalBinary allocates a byte slice and marshals a VLAN into binary form.
-func (v *VLAN) MarshalBinary() ([]byte, error) {
- b := make([]byte, 2)
- _, err := v.read(b)
- return b, err
-}
-
-// read reads data from a VLAN into b. read is used to marshal a VLAN into
-// binary form, but does not allocate on its own.
-func (v *VLAN) read(b []byte) (int, error) {
- // Check for VLAN priority in valid range
- if v.Priority > PriorityNetworkControl {
- return 0, ErrInvalidVLAN
- }
-
- // Check for VLAN ID in valid range
- if v.ID >= VLANMax {
- return 0, ErrInvalidVLAN
- }
-
- // 3 bits: priority
- ub := uint16(v.Priority) << 13
-
- // 1 bit: drop eligible
- var drop uint16
- if v.DropEligible {
- drop = 1
- }
- ub |= drop << 12
-
- // 12 bits: VLAN ID
- ub |= v.ID
-
- binary.BigEndian.PutUint16(b, ub)
- return 2, nil
-}
-
-// UnmarshalBinary unmarshals a byte slice into a VLAN.
-func (v *VLAN) UnmarshalBinary(b []byte) error {
- // VLAN tag is always 2 bytes
- if len(b) != 2 {
- return io.ErrUnexpectedEOF
- }
-
- // 3 bits: priority
- // 1 bit : drop eligible
- // 12 bits: VLAN ID
- ub := binary.BigEndian.Uint16(b[0:2])
- v.Priority = Priority(uint8(ub >> 13))
- v.DropEligible = ub&0x1000 != 0
- v.ID = ub & 0x0fff
-
- // Check for VLAN ID in valid range
- if v.ID >= VLANMax {
- return ErrInvalidVLAN
- }
-
- return nil
-}
diff --git a/vendor/github.com/mdlayher/packet/.gitignore b/vendor/github.com/mdlayher/packet/.gitignore
deleted file mode 100644
index 945907274e..0000000000
--- a/vendor/github.com/mdlayher/packet/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-cmd/packet
diff --git a/vendor/github.com/mdlayher/packet/CHANGELOG.md b/vendor/github.com/mdlayher/packet/CHANGELOG.md
deleted file mode 100644
index 4124bd9b6a..0000000000
--- a/vendor/github.com/mdlayher/packet/CHANGELOG.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# CHANGELOG
-
-# v1.1.2
-
-- [Improvement]: updated dependencies, test with Go 1.20.
-
-# v1.1.1
-
-- [Bug Fix]: fix test compilation on big endian machines.
-
-# v1.1.0
-
-**This is the first release of package packet that only supports Go 1.18+. Users
-on older versions of Go must use v1.0.0.**
-
-- [Improvement]: drop support for older versions of Go so we can begin using
- modern versions of `x/sys` and other dependencies.
-
-## v1.0.0
-
-**This is the last release of package vsock that supports Go 1.17 and below.**
-
-- Initial stable commit! The API is mostly a direct translation of the previous
- `github.com/mdlayher/raw` package APIs, with some updates to make everything
- focused explicitly on Linux and `AF_PACKET` sockets. Functionally, the two
- packages are equivalent, and `*raw.Conn` is now backed by `*packet.Conn` in
- the latest version of the `raw` package.
diff --git a/vendor/github.com/mdlayher/packet/LICENSE.md b/vendor/github.com/mdlayher/packet/LICENSE.md
deleted file mode 100644
index 98382a3d9f..0000000000
--- a/vendor/github.com/mdlayher/packet/LICENSE.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# MIT License
-
-Copyright (C) 2022 Matt Layher
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/mdlayher/packet/README.md b/vendor/github.com/mdlayher/packet/README.md
deleted file mode 100644
index ddb90074a7..0000000000
--- a/vendor/github.com/mdlayher/packet/README.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# packet [](https://github.com/mdlayher/packet/actions) [](https://pkg.go.dev/github.com/mdlayher/packet) [](https://goreportcard.com/report/github.com/mdlayher/packet)
-
-Package `packet` provides access to Linux packet sockets (`AF_PACKET`). MIT
-Licensed.
-
-## Stability
-
-See the [CHANGELOG](./CHANGELOG.md) file for a description of changes between
-releases.
-
-This package has a stable v1 API and any future breaking changes will prompt
-the release of a new major version. Features and bug fixes will continue to
-occur in the v1.x.x series.
-
-This package only supports the two most recent major versions of Go, mirroring
-Go's own release policy. Older versions of Go may lack critical features and bug
-fixes which are necessary for this package to function correctly.
-
-## History
-
-One of my first major Go networking projects was
-[`github.com/mdlayher/raw`](https://github.com/mdlayher/raw), which provided
-access to Linux `AF_PACKET` sockets and *BSD equivalent mechanisms for sending
-and receiving Ethernet frames. However, the *BSD support languished and I lack
-the expertise and time to properly maintain code for operating systems I do not
-use on a daily basis.
-
-Package `packet` is a successor to package `raw`, but exclusively focused on
-Linux and `AF_PACKET` sockets. The APIs are nearly identical, but with a few
-changes which take into account some of the lessons learned while working on
-`raw`.
-
-Users are highly encouraged to migrate any existing Linux uses of `raw` to
-package `packet` instead. This package will be supported for the foreseeable
-future and will receive continued updates as necessary.
diff --git a/vendor/github.com/mdlayher/packet/doc.go b/vendor/github.com/mdlayher/packet/doc.go
deleted file mode 100644
index 4f555f038e..0000000000
--- a/vendor/github.com/mdlayher/packet/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// Package packet provides access to Linux packet sockets (AF_PACKET).
-package packet
diff --git a/vendor/github.com/mdlayher/packet/packet.go b/vendor/github.com/mdlayher/packet/packet.go
deleted file mode 100644
index a05117c78f..0000000000
--- a/vendor/github.com/mdlayher/packet/packet.go
+++ /dev/null
@@ -1,241 +0,0 @@
-package packet
-
-import (
- "net"
- "syscall"
- "time"
-
- "golang.org/x/net/bpf"
-)
-
-const (
- // network is the network reported in net.OpError.
- network = "packet"
-
- // Operation names which may be returned in net.OpError.
- opClose = "close"
- opGetsockopt = "getsockopt"
- opListen = "listen"
- opRawControl = "raw-control"
- opRawRead = "raw-read"
- opRawWrite = "raw-write"
- opRead = "read"
- opSet = "set"
- opSetsockopt = "setsockopt"
- opSyscallConn = "syscall-conn"
- opWrite = "write"
-)
-
-// Config contains options for a Conn.
-type Config struct {
- // Filter is an optional assembled BPF filter which can be applied to the
- // Conn before bind(2) is called.
- //
- // The Conn.SetBPF method serves the same purpose once a Conn has already
- // been opened, but setting Filter applies the BPF filter before the Conn is
- // bound. This ensures that unexpected packets will not be captured before
- // the Conn is opened.
- Filter []bpf.RawInstruction
-}
-
-// Type is a socket type used when creating a Conn with Listen.
-//enumcheck:exhaustive
-type Type int
-
-// Possible Type values. Note that the zero value is not valid: callers must
-// always specify one of Raw or Datagram when calling Listen.
-const (
- _ Type = iota
- Raw
- Datagram
-)
-
-// Listen opens a packet sockets connection on the specified interface, using
-// the given socket type and protocol values.
-//
-// The socket type must be one of the Type constants: Raw or Datagram.
-//
-// The Config specifies optional configuration for the Conn. A nil *Config
-// applies the default configuration.
-func Listen(ifi *net.Interface, socketType Type, protocol int, cfg *Config) (*Conn, error) {
- l, err := listen(ifi, socketType, protocol, cfg)
- if err != nil {
- return nil, opError(opListen, err, &Addr{HardwareAddr: ifi.HardwareAddr})
- }
-
- return l, nil
-}
-
-// TODO(mdlayher): we want to support FileConn for advanced use cases, but this
-// library would also need a big endian protocol value and an interface index.
-// For now we won't bother, but reconsider in the future.
-
-var (
- _ net.PacketConn = &Conn{}
- _ syscall.Conn = &Conn{}
- _ bpf.Setter = &Conn{}
-)
-
-// A Conn is an Linux packet sockets (AF_PACKET) implementation of a
-// net.PacketConn.
-type Conn struct {
- c *conn
-
- // Metadata about the local connection.
- addr *Addr
- ifIndex int
- protocol uint16
-}
-
-// Close closes the connection.
-func (c *Conn) Close() error {
- return c.opError(opClose, c.c.Close())
-}
-
-// LocalAddr returns the local network address. The Addr returned is shared by
-// all invocations of LocalAddr, so do not modify it.
-func (c *Conn) LocalAddr() net.Addr { return c.addr }
-
-// ReadFrom implements the net.PacketConn ReadFrom method.
-func (c *Conn) ReadFrom(b []byte) (int, net.Addr, error) {
- return c.readFrom(b)
-}
-
-// WriteTo implements the net.PacketConn WriteTo method.
-func (c *Conn) WriteTo(b []byte, addr net.Addr) (int, error) {
- return c.writeTo(b, addr)
-}
-
-// SetDeadline implements the net.PacketConn SetDeadline method.
-func (c *Conn) SetDeadline(t time.Time) error {
- return c.opError(opSet, c.c.SetDeadline(t))
-}
-
-// SetReadDeadline implements the net.PacketConn SetReadDeadline method.
-func (c *Conn) SetReadDeadline(t time.Time) error {
- return c.opError(opSet, c.c.SetReadDeadline(t))
-}
-
-// SetWriteDeadline implements the net.PacketConn SetWriteDeadline method.
-func (c *Conn) SetWriteDeadline(t time.Time) error {
- return c.opError(opSet, c.c.SetWriteDeadline(t))
-}
-
-// SetBPF attaches an assembled BPF program to the Conn.
-func (c *Conn) SetBPF(filter []bpf.RawInstruction) error {
- return c.opError(opSetsockopt, c.c.SetBPF(filter))
-}
-
-// SetPromiscuous enables or disables promiscuous mode on the Conn, allowing it
-// to receive traffic that is not addressed to the Conn's network interface.
-func (c *Conn) SetPromiscuous(enable bool) error {
- return c.setPromiscuous(enable)
-}
-
-// Stats contains statistics about a Conn reported by the Linux kernel.
-type Stats struct {
- // The total number of packets received.
- Packets uint32
-
- // The number of packets dropped.
- Drops uint32
-
- // The total number of times that a receive queue is frozen. May be zero if
- // the Linux kernel is not new enough to support TPACKET_V3 statistics.
- FreezeQueueCount uint32
-}
-
-// Stats retrieves statistics about the Conn from the Linux kernel.
-//
-// Note that calling Stats will reset the kernel's internal counters for this
-// Conn. If you want to maintain cumulative statistics by polling Stats over
-// time, you must do so in your calling code.
-func (c *Conn) Stats() (*Stats, error) { return c.stats() }
-
-// SyscallConn returns a raw network connection. This implements the
-// syscall.Conn interface.
-func (c *Conn) SyscallConn() (syscall.RawConn, error) {
- rc, err := c.c.SyscallConn()
- if err != nil {
- return nil, c.opError(opSyscallConn, err)
- }
-
- return &rawConn{
- rc: rc,
- addr: c.addr,
- }, nil
-}
-
-// opError is a convenience for the function opError that also passes the local
-// and remote addresses of the Conn.
-func (c *Conn) opError(op string, err error) error {
- return opError(op, err, c.addr)
-}
-
-// TODO(mdlayher): see if we can port smarter net.OpError logic into
-// socket.Conn's SyscallConn type to avoid the need for this wrapper.
-
-var _ syscall.RawConn = &rawConn{}
-
-// A rawConn is a syscall.RawConn that wraps an internal syscall.RawConn in order
-// to produce net.OpError error values.
-type rawConn struct {
- rc syscall.RawConn
- addr *Addr
-}
-
-// Control implements the syscall.RawConn Control method.
-func (rc *rawConn) Control(fn func(fd uintptr)) error {
- return rc.opError(opRawControl, rc.rc.Control(fn))
-}
-
-// Control implements the syscall.RawConn Read method.
-func (rc *rawConn) Read(fn func(fd uintptr) (done bool)) error {
- return rc.opError(opRawRead, rc.rc.Read(fn))
-}
-
-// Control implements the syscall.RawConn Write method.
-func (rc *rawConn) Write(fn func(fd uintptr) (done bool)) error {
- return rc.opError(opRawWrite, rc.rc.Write(fn))
-}
-
-// opError is a convenience for the function opError that also passes the
-// address of the rawConn.
-func (rc *rawConn) opError(op string, err error) error {
- return opError(op, err, rc.addr)
-}
-
-var _ net.Addr = &Addr{}
-
-// TODO(mdlayher): expose sll_hatype and sll_pkttype on receive Addr only.
-
-// An Addr is a physical-layer address.
-type Addr struct {
- HardwareAddr net.HardwareAddr
-}
-
-// Network returns the address's network name, "packet".
-func (a *Addr) Network() string { return network }
-
-// String returns the string representation of an Addr.
-func (a *Addr) String() string {
- return a.HardwareAddr.String()
-}
-
-// opError unpacks err if possible, producing a net.OpError with the input
-// parameters in order to implement net.PacketConn. As a convenience, opError
-// returns nil if the input error is nil.
-func opError(op string, err error, local net.Addr) error {
- if err == nil {
- return nil
- }
-
- // TODO(mdlayher): try to comply with net.PacketConn as best as we can; land
- // a nettest.TestPacketConn API upstream.
- return &net.OpError{
- Op: op,
- Net: network,
- Addr: local,
- Err: err,
- }
-}
diff --git a/vendor/github.com/mdlayher/packet/packet_linux.go b/vendor/github.com/mdlayher/packet/packet_linux.go
deleted file mode 100644
index 919e39d51c..0000000000
--- a/vendor/github.com/mdlayher/packet/packet_linux.go
+++ /dev/null
@@ -1,248 +0,0 @@
-//go:build linux
-// +build linux
-
-package packet
-
-import (
- "context"
- "encoding/binary"
- "errors"
- "math"
- "net"
- "os"
-
- "github.com/josharian/native"
- "github.com/mdlayher/socket"
- "golang.org/x/sys/unix"
-)
-
-// A conn is the net.PacketConn implementation for packet sockets. We can use
-// socket.Conn directly on Linux to implement most of the necessary methods.
-type conn = socket.Conn
-
-// readFrom implements the net.PacketConn ReadFrom method using recvfrom(2).
-func (c *Conn) readFrom(b []byte) (int, net.Addr, error) {
- // From net.PacketConn documentation:
- //
- // "[ReadFrom] returns the number of bytes read (0 <= n <= len(p)) and any
- // error encountered. Callers should always process the n > 0 bytes returned
- // before considering the error err."
- //
- // c.opError will return nil if no error, but either way we return all the
- // information that we have.
- n, sa, err := c.c.Recvfrom(context.Background(), b, 0)
- return n, fromSockaddr(sa), c.opError(opRead, err)
-}
-
-// writeTo implements the net.PacketConn WriteTo method.
-func (c *Conn) writeTo(b []byte, addr net.Addr) (int, error) {
- sa, err := c.toSockaddr("sendto", addr)
- if err != nil {
- return 0, c.opError(opWrite, err)
- }
-
- // TODO(mdlayher): it's curious that unix.Sendto does not return the number
- // of bytes actually sent. Fake it for now, but investigate upstream.
- if err := c.c.Sendto(context.Background(), b, 0, sa); err != nil {
- return 0, c.opError(opWrite, err)
- }
-
- return len(b), nil
-}
-
-// setPromiscuous wraps setsockopt(2) for the unix.PACKET_MR_PROMISC option.
-func (c *Conn) setPromiscuous(enable bool) error {
- mreq := unix.PacketMreq{
- Ifindex: int32(c.ifIndex),
- Type: unix.PACKET_MR_PROMISC,
- }
-
- membership := unix.PACKET_DROP_MEMBERSHIP
- if enable {
- membership = unix.PACKET_ADD_MEMBERSHIP
- }
-
- return c.opError(
- opSetsockopt,
- c.c.SetsockoptPacketMreq(unix.SOL_PACKET, membership, &mreq),
- )
-}
-
-// stats wraps getsockopt(2) for tpacket_stats* types.
-func (c *Conn) stats() (*Stats, error) {
- const (
- level = unix.SOL_PACKET
- name = unix.PACKET_STATISTICS
- )
-
- // Try to fetch V3 statistics first, they contain more detailed information.
- if stats, err := c.c.GetsockoptTpacketStatsV3(level, name); err == nil {
- return &Stats{
- Packets: stats.Packets,
- Drops: stats.Drops,
- FreezeQueueCount: stats.Freeze_q_cnt,
- }, nil
- }
-
- // There was an error fetching v3 stats, try to fall back.
- stats, err := c.c.GetsockoptTpacketStats(level, name)
- if err != nil {
- return nil, c.opError(opGetsockopt, err)
- }
-
- return &Stats{
- Packets: stats.Packets,
- Drops: stats.Drops,
- // FreezeQueueCount is not present.
- }, nil
-}
-
-// listen is the entry point for Listen on Linux.
-func listen(ifi *net.Interface, socketType Type, protocol int, cfg *Config) (*Conn, error) {
- if cfg == nil {
- // Default configuration.
- cfg = &Config{}
- }
-
- // Convert Type to the matching SOCK_* constant.
- var typ int
- switch socketType {
- case Raw:
- typ = unix.SOCK_RAW
- case Datagram:
- typ = unix.SOCK_DGRAM
- default:
- return nil, errors.New("packet: invalid Type value")
- }
-
- // Protocol is intentionally zero in call to socket(2); we can set it on
- // bind(2) instead. Package raw notes: "Do not specify a protocol to avoid
- // capturing packets which to not match cfg.Filter."
- c, err := socket.Socket(unix.AF_PACKET, typ, 0, network, nil)
- if err != nil {
- return nil, err
- }
-
- conn, err := bind(c, ifi.Index, protocol, cfg)
- if err != nil {
- _ = c.Close()
- return nil, err
- }
-
- return conn, nil
-}
-
-// bind binds the *socket.Conn to finalize *Conn setup.
-func bind(c *socket.Conn, ifIndex, protocol int, cfg *Config) (*Conn, error) {
- if len(cfg.Filter) > 0 {
- // The caller wants to apply a BPF filter before bind(2).
- if err := c.SetBPF(cfg.Filter); err != nil {
- return nil, err
- }
- }
-
- // packet(7) says we sll_protocol must be in network byte order.
- pnet, err := htons(protocol)
- if err != nil {
- return nil, err
- }
-
- // TODO(mdlayher): investigate the possibility of sll_ifindex = 0 because we
- // could bind to any interface.
- err = c.Bind(&unix.SockaddrLinklayer{
- Protocol: pnet,
- Ifindex: ifIndex,
- })
- if err != nil {
- return nil, err
- }
-
- lsa, err := c.Getsockname()
- if err != nil {
- return nil, err
- }
-
- // Parse the physical layer address; sll_halen tells us how many bytes of
- // sll_addr we should treat as valid.
- lsall := lsa.(*unix.SockaddrLinklayer)
- addr := make(net.HardwareAddr, lsall.Halen)
- copy(addr, lsall.Addr[:])
-
- return &Conn{
- c: c,
-
- addr: &Addr{HardwareAddr: addr},
- ifIndex: ifIndex,
- protocol: pnet,
- }, nil
-}
-
-// fromSockaddr converts an opaque unix.Sockaddr to *Addr. If sa is nil, it
-// returns nil. It panics if sa is not of type *unix.SockaddrLinklayer.
-func fromSockaddr(sa unix.Sockaddr) *Addr {
- if sa == nil {
- return nil
- }
-
- sall := sa.(*unix.SockaddrLinklayer)
-
- return &Addr{
- // The syscall already allocated sa; just slice into it with the
- // appropriate length and type conversion rather than making a copy.
- HardwareAddr: net.HardwareAddr(sall.Addr[:sall.Halen]),
- }
-}
-
-// toSockaddr converts a net.Addr to an opaque unix.Sockaddr. It returns an
-// error if the fields cannot be packed into a *unix.SockaddrLinklayer.
-func (c *Conn) toSockaddr(
- op string,
- addr net.Addr,
-) (unix.Sockaddr, error) {
- // The typical error convention for net.Conn types is
- // net.OpError(os.SyscallError(syscall.Errno)), so all calls here should
- // return os.SyscallError(syscall.Errno) so the caller can apply the final
- // net.OpError wrapper.
-
- // Ensure the correct Addr type.
- a, ok := addr.(*Addr)
- if !ok || a.HardwareAddr == nil {
- return nil, os.NewSyscallError(op, unix.EINVAL)
- }
-
- // Pack Addr and Conn metadata into the appropriate sockaddr fields. From
- // packet(7):
- //
- // "When you send packets it is enough to specify sll_family, sll_addr,
- // sll_halen, sll_ifindex, and sll_protocol. The other fields should be 0."
- //
- // sll_family is set on the conversion to unix.RawSockaddrLinklayer.
- sa := unix.SockaddrLinklayer{
- Ifindex: c.ifIndex,
- Protocol: c.protocol,
- }
-
- // Ensure the input address does not exceed the amount of space available;
- // for example an IPoIB address is 20 bytes.
- if len(a.HardwareAddr) > len(sa.Addr) {
- return nil, os.NewSyscallError(op, unix.EINVAL)
- }
-
- sa.Halen = uint8(len(a.HardwareAddr))
- copy(sa.Addr[:], a.HardwareAddr)
-
- return &sa, nil
-}
-
-// htons converts a short (uint16) from host-to-network byte order.
-func htons(i int) (uint16, error) {
- if i < 0 || i > math.MaxUint16 {
- return 0, errors.New("packet: protocol value out of range")
- }
-
- // Store as big endian, retrieve as native endian.
- var b [2]byte
- binary.BigEndian.PutUint16(b[:], uint16(i))
-
- return native.Endian.Uint16(b[:]), nil
-}
diff --git a/vendor/github.com/mdlayher/packet/packet_others.go b/vendor/github.com/mdlayher/packet/packet_others.go
deleted file mode 100644
index 54a8cc429f..0000000000
--- a/vendor/github.com/mdlayher/packet/packet_others.go
+++ /dev/null
@@ -1,33 +0,0 @@
-//go:build !linux
-// +build !linux
-
-package packet
-
-import (
- "fmt"
- "net"
- "runtime"
- "syscall"
- "time"
-
- "golang.org/x/net/bpf"
-)
-
-// errUnimplemented is returned by all functions on non-Linux platforms.
-var errUnimplemented = fmt.Errorf("packet: not implemented on %s", runtime.GOOS)
-
-func listen(_ *net.Interface, _ Type, _ int, _ *Config) (*Conn, error) { return nil, errUnimplemented }
-
-func (*Conn) readFrom(_ []byte) (int, net.Addr, error) { return 0, nil, errUnimplemented }
-func (*Conn) writeTo(_ []byte, _ net.Addr) (int, error) { return 0, errUnimplemented }
-func (*Conn) setPromiscuous(_ bool) error { return errUnimplemented }
-func (*Conn) stats() (*Stats, error) { return nil, errUnimplemented }
-
-type conn struct{}
-
-func (*conn) Close() error { return errUnimplemented }
-func (*conn) SetDeadline(_ time.Time) error { return errUnimplemented }
-func (*conn) SetReadDeadline(_ time.Time) error { return errUnimplemented }
-func (*conn) SetWriteDeadline(_ time.Time) error { return errUnimplemented }
-func (*conn) SetBPF(_ []bpf.RawInstruction) error { return errUnimplemented }
-func (*conn) SyscallConn() (syscall.RawConn, error) { return nil, errUnimplemented }
diff --git a/vendor/github.com/mdlayher/socket/CHANGELOG.md b/vendor/github.com/mdlayher/socket/CHANGELOG.md
deleted file mode 100644
index f0d01641a2..0000000000
--- a/vendor/github.com/mdlayher/socket/CHANGELOG.md
+++ /dev/null
@@ -1,80 +0,0 @@
-# CHANGELOG
-
-## v0.4.1
-
-- [Bug Fix] [commit](https://github.com/mdlayher/socket/commit/2a14ceef4da279de1f957c5761fffcc6c87bbd3b):
- ensure `socket.Conn` can be used with non-socket file descriptors by handling
- `ENOTSOCK` in the constructor.
-
-## v0.4.0
-
-**This is the first release of package socket that only supports Go 1.18+.
-Users on older versions of Go must use v0.3.0.**
-
-- [Improvement]: drop support for older versions of Go so we can begin using
- modern versions of `x/sys` and other dependencies.
-
-## v0.3.0
-
-**This is the last release of package socket that supports Go 1.17 and below.**
-
-- [New API/API change] [PR](https://github.com/mdlayher/socket/pull/8):
- numerous `socket.Conn` methods now support context cancelation. Future
- releases will continue adding support as needed.
- - New `ReadContext` and `WriteContext` methods.
- - `Connect`, `Recvfrom`, `Recvmsg`, `Sendmsg`, and `Sendto` methods now accept
- a context.
- - `Sendto` parameter order was also fixed to match the underlying syscall.
-
-## v0.2.3
-
-- [New API] [commit](https://github.com/mdlayher/socket/commit/a425d96e0f772c053164f8ce4c9c825380a98086):
- `socket.Conn` has new `Pidfd*` methods for wrapping the `pidfd_*(2)` family of
- system calls.
-
-## v0.2.2
-
-- [New API] [commit](https://github.com/mdlayher/socket/commit/a2429f1dfe8ec2586df5a09f50ead865276cd027):
- `socket.Conn` has new `IoctlKCM*` methods for wrapping `ioctl(2)` for `AF_KCM`
- operations.
-
-## v0.2.1
-
-- [New API] [commit](https://github.com/mdlayher/socket/commit/b18ddbe9caa0e34552b4409a3aa311cb460d2f99):
- `socket.Conn` has a new `SetsockoptPacketMreq` method for wrapping
- `setsockopt(2)` for `AF_PACKET` socket options.
-
-## v0.2.0
-
-- [New API] [commit](https://github.com/mdlayher/socket/commit/6e912a68523c45e5fd899239f4b46c402dd856da):
- `socket.FileConn` can be used to create a `socket.Conn` from an existing
- `os.File`, which may be provided by systemd socket activation or another
- external mechanism.
-- [API change] [commit](https://github.com/mdlayher/socket/commit/66d61f565188c23fe02b24099ddc856d538bf1a7):
- `socket.Conn.Connect` now returns the `unix.Sockaddr` value provided by
- `getpeername(2)`, since we have to invoke that system call anyway to verify
- that a connection to a remote peer was successfully established.
-- [Bug Fix] [commit](https://github.com/mdlayher/socket/commit/b60b2dbe0ac3caff2338446a150083bde8c5c19c):
- check the correct error from `unix.GetsockoptInt` in the `socket.Conn.Connect`
- method. Thanks @vcabbage!
-
-## v0.1.2
-
-- [Bug Fix]: `socket.Conn.Connect` now properly checks the `SO_ERROR` socket
- option value after calling `connect(2)` to verify whether or not a connection
- could successfully be established. This means that `Connect` should now report
- an error for an `AF_INET` TCP connection refused or `AF_VSOCK` connection
- reset by peer.
-- [New API]: add `socket.Conn.Getpeername` for use in `Connect`, but also for
- use by external callers.
-
-## v0.1.1
-
-- [New API]: `socket.Conn` now has `CloseRead`, `CloseWrite`, and `Shutdown`
- methods.
-- [Improvement]: internal rework to more robustly handle various errors.
-
-## v0.1.0
-
-- Initial unstable release. Most functionality has been developed and ported
-from package [`netlink`](https://github.com/mdlayher/netlink).
diff --git a/vendor/github.com/mdlayher/socket/LICENSE.md b/vendor/github.com/mdlayher/socket/LICENSE.md
deleted file mode 100644
index 3ccdb75b26..0000000000
--- a/vendor/github.com/mdlayher/socket/LICENSE.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# MIT License
-
-Copyright (C) 2021 Matt Layher
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/mdlayher/socket/README.md b/vendor/github.com/mdlayher/socket/README.md
deleted file mode 100644
index 2aa065cbb7..0000000000
--- a/vendor/github.com/mdlayher/socket/README.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# socket [](https://github.com/mdlayher/socket/actions) [](https://pkg.go.dev/github.com/mdlayher/socket) [](https://goreportcard.com/report/github.com/mdlayher/socket)
-
-Package `socket` provides a low-level network connection type which integrates
-with Go's runtime network poller to provide asynchronous I/O and deadline
-support. MIT Licensed.
-
-This package focuses on UNIX-like operating systems which make use of BSD
-sockets system call APIs. It is meant to be used as a foundation for the
-creation of operating system-specific socket packages, for socket families such
-as Linux's `AF_NETLINK`, `AF_PACKET`, or `AF_VSOCK`. This package should not be
-used directly in end user applications.
-
-Any use of package socket should be guarded by build tags, as one would also
-use when importing the `syscall` or `golang.org/x/sys` packages.
-
-## Stability
-
-See the [CHANGELOG](./CHANGELOG.md) file for a description of changes between
-releases.
-
-This package only supports the two most recent major versions of Go, mirroring
-Go's own release policy. Older versions of Go may lack critical features and bug
-fixes which are necessary for this package to function correctly.
diff --git a/vendor/github.com/mdlayher/socket/accept.go b/vendor/github.com/mdlayher/socket/accept.go
deleted file mode 100644
index 47e9d897ef..0000000000
--- a/vendor/github.com/mdlayher/socket/accept.go
+++ /dev/null
@@ -1,23 +0,0 @@
-//go:build !dragonfly && !freebsd && !illumos && !linux
-// +build !dragonfly,!freebsd,!illumos,!linux
-
-package socket
-
-import (
- "fmt"
- "runtime"
-
- "golang.org/x/sys/unix"
-)
-
-const sysAccept = "accept"
-
-// accept wraps accept(2).
-func accept(fd, flags int) (int, unix.Sockaddr, error) {
- if flags != 0 {
- // These operating systems have no support for flags to accept(2).
- return 0, nil, fmt.Errorf("socket: Conn.Accept flags are ineffective on %s", runtime.GOOS)
- }
-
- return unix.Accept(fd)
-}
diff --git a/vendor/github.com/mdlayher/socket/accept4.go b/vendor/github.com/mdlayher/socket/accept4.go
deleted file mode 100644
index e1016b2063..0000000000
--- a/vendor/github.com/mdlayher/socket/accept4.go
+++ /dev/null
@@ -1,15 +0,0 @@
-//go:build dragonfly || freebsd || illumos || linux
-// +build dragonfly freebsd illumos linux
-
-package socket
-
-import (
- "golang.org/x/sys/unix"
-)
-
-const sysAccept = "accept4"
-
-// accept wraps accept4(2).
-func accept(fd, flags int) (int, unix.Sockaddr, error) {
- return unix.Accept4(fd, flags)
-}
diff --git a/vendor/github.com/mdlayher/socket/conn.go b/vendor/github.com/mdlayher/socket/conn.go
deleted file mode 100644
index 7b3cc7a6e7..0000000000
--- a/vendor/github.com/mdlayher/socket/conn.go
+++ /dev/null
@@ -1,880 +0,0 @@
-package socket
-
-import (
- "context"
- "errors"
- "io"
- "os"
- "sync"
- "sync/atomic"
- "syscall"
- "time"
-
- "golang.org/x/sys/unix"
-)
-
-// Lock in an expected public interface for convenience.
-var _ interface {
- io.ReadWriteCloser
- syscall.Conn
- SetDeadline(t time.Time) error
- SetReadDeadline(t time.Time) error
- SetWriteDeadline(t time.Time) error
-} = &Conn{}
-
-// A Conn is a low-level network connection which integrates with Go's runtime
-// network poller to provide asynchronous I/O and deadline support.
-//
-// Many of a Conn's blocking methods support net.Conn deadlines as well as
-// cancelation via context. Note that passing a context with a deadline set will
-// override any of the previous deadlines set by calls to the SetDeadline family
-// of methods.
-type Conn struct {
- // Indicates whether or not Conn.Close has been called. Must be accessed
- // atomically. Atomics definitions must come first in the Conn struct.
- closed uint32
-
- // A unique name for the Conn which is also associated with derived file
- // descriptors such as those created by accept(2).
- name string
-
- // facts contains information we have determined about Conn to trigger
- // alternate behavior in certain functions.
- facts facts
-
- // Provides access to the underlying file registered with the runtime
- // network poller, and arbitrary raw I/O calls.
- fd *os.File
- rc syscall.RawConn
-}
-
-// facts contains facts about a Conn.
-type facts struct {
- // isStream reports whether this is a streaming descriptor, as opposed to a
- // packet-based descriptor like a UDP socket.
- isStream bool
-
- // zeroReadIsEOF reports Whether a zero byte read indicates EOF. This is
- // false for a message based socket connection.
- zeroReadIsEOF bool
-}
-
-// A Config contains options for a Conn.
-type Config struct {
- // NetNS specifies the Linux network namespace the Conn will operate in.
- // This option is unsupported on other operating systems.
- //
- // If set (non-zero), Conn will enter the specified network namespace and an
- // error will occur in Socket if the operation fails.
- //
- // If not set (zero), a best-effort attempt will be made to enter the
- // network namespace of the calling thread: this means that any changes made
- // to the calling thread's network namespace will also be reflected in Conn.
- // If this operation fails (due to lack of permissions or because network
- // namespaces are disabled by kernel configuration), Socket will not return
- // an error, and the Conn will operate in the default network namespace of
- // the process. This enables non-privileged use of Conn in applications
- // which do not require elevated privileges.
- //
- // Entering a network namespace is a privileged operation (root or
- // CAP_SYS_ADMIN are required), and most applications should leave this set
- // to 0.
- NetNS int
-}
-
-// High-level methods which provide convenience over raw system calls.
-
-// Close closes the underlying file descriptor for the Conn, which also causes
-// all in-flight I/O operations to immediately unblock and return errors. Any
-// subsequent uses of Conn will result in EBADF.
-func (c *Conn) Close() error {
- // The caller has expressed an intent to close the socket, so immediately
- // increment s.closed to force further calls to result in EBADF before also
- // closing the file descriptor to unblock any outstanding operations.
- //
- // Because other operations simply check for s.closed != 0, we will permit
- // double Close, which would increment s.closed beyond 1.
- if atomic.AddUint32(&c.closed, 1) != 1 {
- // Multiple Close calls.
- return nil
- }
-
- return os.NewSyscallError("close", c.fd.Close())
-}
-
-// CloseRead shuts down the reading side of the Conn. Most callers should just
-// use Close.
-func (c *Conn) CloseRead() error { return c.Shutdown(unix.SHUT_RD) }
-
-// CloseWrite shuts down the writing side of the Conn. Most callers should just
-// use Close.
-func (c *Conn) CloseWrite() error { return c.Shutdown(unix.SHUT_WR) }
-
-// Read reads directly from the underlying file descriptor.
-func (c *Conn) Read(b []byte) (int, error) { return c.fd.Read(b) }
-
-// ReadContext reads from the underlying file descriptor with added support for
-// context cancelation.
-func (c *Conn) ReadContext(ctx context.Context, b []byte) (int, error) {
- if c.facts.isStream && len(b) > maxRW {
- b = b[:maxRW]
- }
-
- n, err := readT(c, ctx, "read", func(fd int) (int, error) {
- return unix.Read(fd, b)
- })
- if n == 0 && err == nil && c.facts.zeroReadIsEOF {
- return 0, io.EOF
- }
-
- return n, os.NewSyscallError("read", err)
-}
-
-// Write writes directly to the underlying file descriptor.
-func (c *Conn) Write(b []byte) (int, error) { return c.fd.Write(b) }
-
-// WriteContext writes to the underlying file descriptor with added support for
-// context cancelation.
-func (c *Conn) WriteContext(ctx context.Context, b []byte) (int, error) {
- var (
- n, nn int
- err error
- )
-
- doErr := c.write(ctx, "write", func(fd int) error {
- max := len(b)
- if c.facts.isStream && max-nn > maxRW {
- max = nn + maxRW
- }
-
- n, err = unix.Write(fd, b[nn:max])
- if n > 0 {
- nn += n
- }
- if nn == len(b) {
- return err
- }
- if n == 0 && err == nil {
- err = io.ErrUnexpectedEOF
- return nil
- }
-
- return err
- })
- if doErr != nil {
- return 0, doErr
- }
-
- return nn, os.NewSyscallError("write", err)
-}
-
-// SetDeadline sets both the read and write deadlines associated with the Conn.
-func (c *Conn) SetDeadline(t time.Time) error { return c.fd.SetDeadline(t) }
-
-// SetReadDeadline sets the read deadline associated with the Conn.
-func (c *Conn) SetReadDeadline(t time.Time) error { return c.fd.SetReadDeadline(t) }
-
-// SetWriteDeadline sets the write deadline associated with the Conn.
-func (c *Conn) SetWriteDeadline(t time.Time) error { return c.fd.SetWriteDeadline(t) }
-
-// ReadBuffer gets the size of the operating system's receive buffer associated
-// with the Conn.
-func (c *Conn) ReadBuffer() (int, error) {
- return c.GetsockoptInt(unix.SOL_SOCKET, unix.SO_RCVBUF)
-}
-
-// WriteBuffer gets the size of the operating system's transmit buffer
-// associated with the Conn.
-func (c *Conn) WriteBuffer() (int, error) {
- return c.GetsockoptInt(unix.SOL_SOCKET, unix.SO_SNDBUF)
-}
-
-// SetReadBuffer sets the size of the operating system's receive buffer
-// associated with the Conn.
-//
-// When called with elevated privileges on Linux, the SO_RCVBUFFORCE option will
-// be used to override operating system limits. Otherwise SO_RCVBUF is used
-// (which obeys operating system limits).
-func (c *Conn) SetReadBuffer(bytes int) error { return c.setReadBuffer(bytes) }
-
-// SetWriteBuffer sets the size of the operating system's transmit buffer
-// associated with the Conn.
-//
-// When called with elevated privileges on Linux, the SO_SNDBUFFORCE option will
-// be used to override operating system limits. Otherwise SO_SNDBUF is used
-// (which obeys operating system limits).
-func (c *Conn) SetWriteBuffer(bytes int) error { return c.setWriteBuffer(bytes) }
-
-// SyscallConn returns a raw network connection. This implements the
-// syscall.Conn interface.
-//
-// SyscallConn is intended for advanced use cases, such as getting and setting
-// arbitrary socket options using the socket's file descriptor. If possible,
-// those operations should be performed using methods on Conn instead.
-//
-// Once invoked, it is the caller's responsibility to ensure that operations
-// performed using Conn and the syscall.RawConn do not conflict with each other.
-func (c *Conn) SyscallConn() (syscall.RawConn, error) {
- if atomic.LoadUint32(&c.closed) != 0 {
- return nil, os.NewSyscallError("syscallconn", unix.EBADF)
- }
-
- // TODO(mdlayher): mutex or similar to enforce syscall.RawConn contract of
- // FD remaining valid for duration of calls?
- return c.rc, nil
-}
-
-// Socket wraps the socket(2) system call to produce a Conn. domain, typ, and
-// proto are passed directly to socket(2), and name should be a unique name for
-// the socket type such as "netlink" or "vsock".
-//
-// The cfg parameter specifies optional configuration for the Conn. If nil, no
-// additional configuration will be applied.
-//
-// If the operating system supports SOCK_CLOEXEC and SOCK_NONBLOCK, they are
-// automatically applied to typ to mirror the standard library's socket flag
-// behaviors.
-func Socket(domain, typ, proto int, name string, cfg *Config) (*Conn, error) {
- if cfg == nil {
- cfg = &Config{}
- }
-
- if cfg.NetNS == 0 {
- // Non-Linux or no network namespace.
- return socket(domain, typ, proto, name)
- }
-
- // Linux only: create Conn in the specified network namespace.
- return withNetNS(cfg.NetNS, func() (*Conn, error) {
- return socket(domain, typ, proto, name)
- })
-}
-
-// socket is the internal, cross-platform entry point for socket(2).
-func socket(domain, typ, proto int, name string) (*Conn, error) {
- var (
- fd int
- err error
- )
-
- for {
- fd, err = unix.Socket(domain, typ|socketFlags, proto)
- switch {
- case err == nil:
- // Some OSes already set CLOEXEC with typ.
- if !flagCLOEXEC {
- unix.CloseOnExec(fd)
- }
-
- // No error, prepare the Conn.
- return New(fd, name)
- case !ready(err):
- // System call interrupted or not ready, try again.
- continue
- case err == unix.EINVAL, err == unix.EPROTONOSUPPORT:
- // On Linux, SOCK_NONBLOCK and SOCK_CLOEXEC were introduced in
- // 2.6.27. On FreeBSD, both flags were introduced in FreeBSD 10.
- // EINVAL and EPROTONOSUPPORT check for earlier versions of these
- // OSes respectively.
- //
- // Mirror what the standard library does when creating file
- // descriptors: avoid racing a fork/exec with the creation of new
- // file descriptors, so that child processes do not inherit socket
- // file descriptors unexpectedly.
- //
- // For a more thorough explanation, see similar work in the Go tree:
- // func sysSocket in net/sock_cloexec.go, as well as the detailed
- // comment in syscall/exec_unix.go.
- syscall.ForkLock.RLock()
- fd, err = unix.Socket(domain, typ, proto)
- if err != nil {
- syscall.ForkLock.RUnlock()
- return nil, os.NewSyscallError("socket", err)
- }
- unix.CloseOnExec(fd)
- syscall.ForkLock.RUnlock()
-
- return New(fd, name)
- default:
- // Unhandled error.
- return nil, os.NewSyscallError("socket", err)
- }
- }
-}
-
-// FileConn returns a copy of the network connection corresponding to the open
-// file. It is the caller's responsibility to close the file when finished.
-// Closing the Conn does not affect the File, and closing the File does not
-// affect the Conn.
-func FileConn(f *os.File, name string) (*Conn, error) {
- // First we'll try to do fctnl(2) with F_DUPFD_CLOEXEC because we can dup
- // the file descriptor and set the flag in one syscall.
- fd, err := unix.FcntlInt(f.Fd(), unix.F_DUPFD_CLOEXEC, 0)
- switch err {
- case nil:
- // OK, ready to set up non-blocking I/O.
- return New(fd, name)
- case unix.EINVAL:
- // The kernel rejected our fcntl(2), fall back to separate dup(2) and
- // setting close on exec.
- //
- // Mirror what the standard library does when creating file descriptors:
- // avoid racing a fork/exec with the creation of new file descriptors,
- // so that child processes do not inherit socket file descriptors
- // unexpectedly.
- syscall.ForkLock.RLock()
- fd, err := unix.Dup(fd)
- if err != nil {
- syscall.ForkLock.RUnlock()
- return nil, os.NewSyscallError("dup", err)
- }
- unix.CloseOnExec(fd)
- syscall.ForkLock.RUnlock()
-
- return New(fd, name)
- default:
- // Any other errors.
- return nil, os.NewSyscallError("fcntl", err)
- }
-}
-
-// New wraps an existing file descriptor to create a Conn. name should be a
-// unique name for the socket type such as "netlink" or "vsock".
-//
-// Most callers should use Socket or FileConn to construct a Conn. New is
-// intended for integrating with specific system calls which provide a file
-// descriptor that supports asynchronous I/O. The file descriptor is immediately
-// set to nonblocking mode and registered with Go's runtime network poller for
-// future I/O operations.
-//
-// Unlike FileConn, New does not duplicate the existing file descriptor in any
-// way. The returned Conn takes ownership of the underlying file descriptor.
-func New(fd int, name string) (*Conn, error) {
- // All Conn I/O is nonblocking for integration with Go's runtime network
- // poller. Depending on the OS this might already be set but it can't hurt
- // to set it again.
- if err := unix.SetNonblock(fd, true); err != nil {
- return nil, os.NewSyscallError("setnonblock", err)
- }
-
- // os.NewFile registers the non-blocking file descriptor with the runtime
- // poller, which is then used for most subsequent operations except those
- // that require raw I/O via SyscallConn.
- //
- // See also: https://golang.org/pkg/os/#NewFile
- f := os.NewFile(uintptr(fd), name)
- rc, err := f.SyscallConn()
- if err != nil {
- return nil, err
- }
-
- c := &Conn{
- name: name,
- fd: f,
- rc: rc,
- }
-
- // Probe the file descriptor for socket settings.
- sotype, err := c.GetsockoptInt(unix.SOL_SOCKET, unix.SO_TYPE)
- switch {
- case err == nil:
- // File is a socket, check its properties.
- c.facts = facts{
- isStream: sotype == unix.SOCK_STREAM,
- zeroReadIsEOF: sotype != unix.SOCK_DGRAM && sotype != unix.SOCK_RAW,
- }
- case errors.Is(err, unix.ENOTSOCK):
- // File is not a socket, treat it as a regular file.
- c.facts = facts{
- isStream: true,
- zeroReadIsEOF: true,
- }
- default:
- return nil, err
- }
-
- return c, nil
-}
-
-// Low-level methods which provide raw system call access.
-
-// Accept wraps accept(2) or accept4(2) depending on the operating system, but
-// returns a Conn for the accepted connection rather than a raw file descriptor.
-//
-// If the operating system supports accept4(2) (which allows flags),
-// SOCK_CLOEXEC and SOCK_NONBLOCK are automatically applied to flags to mirror
-// the standard library's socket flag behaviors.
-//
-// If the operating system only supports accept(2) (which does not allow flags)
-// and flags is not zero, an error will be returned.
-//
-// Accept obeys context cancelation and uses the deadline set on the context to
-// cancel accepting the next connection. If a deadline is set on ctx, this
-// deadline will override any previous deadlines set using SetDeadline or
-// SetReadDeadline. Upon return, the read deadline is cleared.
-func (c *Conn) Accept(ctx context.Context, flags int) (*Conn, unix.Sockaddr, error) {
- type ret struct {
- nfd int
- sa unix.Sockaddr
- }
-
- r, err := readT(c, ctx, sysAccept, func(fd int) (ret, error) {
- // Either accept(2) or accept4(2) depending on the OS.
- nfd, sa, err := accept(fd, flags|socketFlags)
- return ret{nfd, sa}, err
- })
- if err != nil {
- // internal/poll, context error, or user function error.
- return nil, nil, err
- }
-
- // Successfully accepted a connection, wrap it in a Conn for use by the
- // caller.
- ac, err := New(r.nfd, c.name)
- if err != nil {
- return nil, nil, err
- }
-
- return ac, r.sa, nil
-}
-
-// Bind wraps bind(2).
-func (c *Conn) Bind(sa unix.Sockaddr) error {
- return c.control(context.Background(), "bind", func(fd int) error {
- return unix.Bind(fd, sa)
- })
-}
-
-// Connect wraps connect(2). In order to verify that the underlying socket is
-// connected to a remote peer, Connect calls getpeername(2) and returns the
-// unix.Sockaddr from that call.
-//
-// Connect obeys context cancelation and uses the deadline set on the context to
-// cancel connecting to a remote peer. If a deadline is set on ctx, this
-// deadline will override any previous deadlines set using SetDeadline or
-// SetWriteDeadline. Upon return, the write deadline is cleared.
-func (c *Conn) Connect(ctx context.Context, sa unix.Sockaddr) (unix.Sockaddr, error) {
- const op = "connect"
-
- // TODO(mdlayher): it would seem that trying to connect to unbound vsock
- // listeners by calling Connect multiple times results in ECONNRESET for the
- // first and nil error for subsequent calls. Do we need to memoize the
- // error? Check what the stdlib behavior is.
-
- var (
- // Track progress between invocations of the write closure. We don't
- // have an explicit WaitWrite call like internal/poll does, so we have
- // to wait until the runtime calls the closure again to indicate we can
- // write.
- progress uint32
-
- // Capture closure sockaddr and error.
- rsa unix.Sockaddr
- err error
- )
-
- doErr := c.write(ctx, op, func(fd int) error {
- if atomic.AddUint32(&progress, 1) == 1 {
- // First call: initiate connect.
- return unix.Connect(fd, sa)
- }
-
- // Subsequent calls: the runtime network poller indicates fd is
- // writable. Check for errno.
- errno, gerr := c.GetsockoptInt(unix.SOL_SOCKET, unix.SO_ERROR)
- if gerr != nil {
- return gerr
- }
- if errno != 0 {
- // Connection is still not ready or failed. If errno indicates
- // the socket is not ready, we will wait for the next write
- // event. Otherwise we propagate this errno back to the as a
- // permanent error.
- uerr := unix.Errno(errno)
- err = uerr
- return uerr
- }
-
- // According to internal/poll, it's possible for the runtime network
- // poller to spuriously wake us and return errno 0 for SO_ERROR.
- // Make sure we are actually connected to a peer.
- peer, err := c.Getpeername()
- if err != nil {
- // internal/poll unconditionally goes back to WaitWrite.
- // Synthesize an error that will do the same for us.
- return unix.EAGAIN
- }
-
- // Connection complete.
- rsa = peer
- return nil
- })
- if doErr != nil {
- // internal/poll or context error.
- return nil, doErr
- }
-
- if err == unix.EISCONN {
- // TODO(mdlayher): is this block obsolete with the addition of the
- // getsockopt SO_ERROR check above?
- //
- // EISCONN is reported if the socket is already established and should
- // not be treated as an error.
- // - Darwin reports this for at least TCP sockets
- // - Linux reports this for at least AF_VSOCK sockets
- return rsa, nil
- }
-
- return rsa, os.NewSyscallError(op, err)
-}
-
-// Getsockname wraps getsockname(2).
-func (c *Conn) Getsockname() (unix.Sockaddr, error) {
- return controlT(c, context.Background(), "getsockname", unix.Getsockname)
-}
-
-// Getpeername wraps getpeername(2).
-func (c *Conn) Getpeername() (unix.Sockaddr, error) {
- return controlT(c, context.Background(), "getpeername", unix.Getpeername)
-}
-
-// GetsockoptInt wraps getsockopt(2) for integer values.
-func (c *Conn) GetsockoptInt(level, opt int) (int, error) {
- return controlT(c, context.Background(), "getsockopt", func(fd int) (int, error) {
- return unix.GetsockoptInt(fd, level, opt)
- })
-}
-
-// Listen wraps listen(2).
-func (c *Conn) Listen(n int) error {
- return c.control(context.Background(), "listen", func(fd int) error {
- return unix.Listen(fd, n)
- })
-}
-
-// Recvmsg wraps recvmsg(2).
-func (c *Conn) Recvmsg(ctx context.Context, p, oob []byte, flags int) (int, int, int, unix.Sockaddr, error) {
- type ret struct {
- n, oobn, recvflags int
- from unix.Sockaddr
- }
-
- r, err := readT(c, ctx, "recvmsg", func(fd int) (ret, error) {
- n, oobn, recvflags, from, err := unix.Recvmsg(fd, p, oob, flags)
- return ret{n, oobn, recvflags, from}, err
- })
- if r.n == 0 && err == nil && c.facts.zeroReadIsEOF {
- return 0, 0, 0, nil, io.EOF
- }
-
- return r.n, r.oobn, r.recvflags, r.from, err
-}
-
-// Recvfrom wraps recvfrom(2).
-func (c *Conn) Recvfrom(ctx context.Context, p []byte, flags int) (int, unix.Sockaddr, error) {
- type ret struct {
- n int
- addr unix.Sockaddr
- }
-
- out, err := readT(c, ctx, "recvfrom", func(fd int) (ret, error) {
- n, addr, err := unix.Recvfrom(fd, p, flags)
- return ret{n, addr}, err
- })
- if out.n == 0 && err == nil && c.facts.zeroReadIsEOF {
- return 0, nil, io.EOF
- }
-
- return out.n, out.addr, err
-}
-
-// Sendmsg wraps sendmsg(2).
-func (c *Conn) Sendmsg(ctx context.Context, p, oob []byte, to unix.Sockaddr, flags int) (int, error) {
- return writeT(c, ctx, "sendmsg", func(fd int) (int, error) {
- return unix.SendmsgN(fd, p, oob, to, flags)
- })
-}
-
-// Sendto wraps sendto(2).
-func (c *Conn) Sendto(ctx context.Context, p []byte, flags int, to unix.Sockaddr) error {
- return c.write(ctx, "sendto", func(fd int) error {
- return unix.Sendto(fd, p, flags, to)
- })
-}
-
-// SetsockoptInt wraps setsockopt(2) for integer values.
-func (c *Conn) SetsockoptInt(level, opt, value int) error {
- return c.control(context.Background(), "setsockopt", func(fd int) error {
- return unix.SetsockoptInt(fd, level, opt, value)
- })
-}
-
-// Shutdown wraps shutdown(2).
-func (c *Conn) Shutdown(how int) error {
- return c.control(context.Background(), "shutdown", func(fd int) error {
- return unix.Shutdown(fd, how)
- })
-}
-
-// Conn low-level read/write/control functions. These functions mirror the
-// syscall.RawConn APIs but the input closures return errors rather than
-// booleans.
-
-// read wraps readT to execute a function and capture its error result. This is
-// a convenience wrapper for functions which don't return any extra values.
-func (c *Conn) read(ctx context.Context, op string, f func(fd int) error) error {
- _, err := readT(c, ctx, op, func(fd int) (struct{}, error) {
- return struct{}{}, f(fd)
- })
- return err
-}
-
-// write executes f, a write function, against the associated file descriptor.
-// op is used to create an *os.SyscallError if the file descriptor is closed.
-func (c *Conn) write(ctx context.Context, op string, f func(fd int) error) error {
- _, err := writeT(c, ctx, op, func(fd int) (struct{}, error) {
- return struct{}{}, f(fd)
- })
- return err
-}
-
-// readT executes c.rc.Read for op using the input function, returning a newly
-// allocated result T.
-func readT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, error)) (T, error) {
- return rwT(c, rwContext[T]{
- Context: ctx,
- Type: read,
- Op: op,
- Do: f,
- })
-}
-
-// writeT executes c.rc.Write for op using the input function, returning a newly
-// allocated result T.
-func writeT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, error)) (T, error) {
- return rwT(c, rwContext[T]{
- Context: ctx,
- Type: write,
- Op: op,
- Do: f,
- })
-}
-
-// readWrite indicates if an operation intends to read or write.
-type readWrite bool
-
-// Possible readWrite values.
-const (
- read readWrite = false
- write readWrite = true
-)
-
-// An rwContext provides arguments to rwT.
-type rwContext[T any] struct {
- // The caller's context passed for cancelation.
- Context context.Context
-
- // The type of an operation: read or write.
- Type readWrite
-
- // The name of the operation used in errors.
- Op string
-
- // The actual function to perform.
- Do func(fd int) (T, error)
-}
-
-// rwT executes c.rc.Read or c.rc.Write (depending on the value of rw.Type) for
-// rw.Op using the input function, returning a newly allocated result T.
-//
-// It obeys context cancelation and the rw.Context must not be nil.
-func rwT[T any](c *Conn, rw rwContext[T]) (T, error) {
- if atomic.LoadUint32(&c.closed) != 0 {
- // If the file descriptor is already closed, do nothing.
- return *new(T), os.NewSyscallError(rw.Op, unix.EBADF)
- }
-
- if err := rw.Context.Err(); err != nil {
- // Early exit due to context cancel.
- return *new(T), os.NewSyscallError(rw.Op, err)
- }
-
- var (
- // The read or write function used to access the runtime network poller.
- poll func(func(uintptr) bool) error
-
- // The read or write function used to set the matching deadline.
- deadline func(time.Time) error
- )
-
- if rw.Type == write {
- poll = c.rc.Write
- deadline = c.SetWriteDeadline
- } else {
- poll = c.rc.Read
- deadline = c.SetReadDeadline
- }
-
- var (
- // Whether or not the context carried a deadline we are actively using
- // for cancelation.
- setDeadline bool
-
- // Signals for the cancelation watcher goroutine.
- wg sync.WaitGroup
- doneC = make(chan struct{})
-
- // Atomic: reports whether we have to disarm the deadline.
- //
- // TODO(mdlayher): switch back to atomic.Bool when we drop support for
- // Go 1.18.
- needDisarm int64
- )
-
- // On cancel, clean up the watcher.
- defer func() {
- close(doneC)
- wg.Wait()
- }()
-
- if d, ok := rw.Context.Deadline(); ok {
- // The context has an explicit deadline. We will use it for cancelation
- // but disarm it after poll for the next call.
- if err := deadline(d); err != nil {
- return *new(T), err
- }
- setDeadline = true
- atomic.AddInt64(&needDisarm, 1)
- } else {
- // The context does not have an explicit deadline. We have to watch for
- // cancelation so we can propagate that signal to immediately unblock
- // the runtime network poller.
- //
- // TODO(mdlayher): is it possible to detect a background context vs a
- // context with possible future cancel?
- wg.Add(1)
- go func() {
- defer wg.Done()
-
- select {
- case <-rw.Context.Done():
- // Cancel the operation. Make the caller disarm after poll
- // returns.
- atomic.AddInt64(&needDisarm, 1)
- _ = deadline(time.Unix(0, 1))
- case <-doneC:
- // Nothing to do.
- }
- }()
- }
-
- var (
- t T
- err error
- )
-
- pollErr := poll(func(fd uintptr) bool {
- t, err = rw.Do(int(fd))
- return ready(err)
- })
-
- if atomic.LoadInt64(&needDisarm) > 0 {
- _ = deadline(time.Time{})
- }
-
- if pollErr != nil {
- if rw.Context.Err() != nil || (setDeadline && errors.Is(pollErr, os.ErrDeadlineExceeded)) {
- // The caller canceled the operation or we set a deadline internally
- // and it was reached.
- //
- // Unpack a plain context error. We wait for the context to be done
- // to synchronize state externally. Otherwise we have noticed I/O
- // timeout wakeups when we set a deadline but the context was not
- // yet marked done.
- <-rw.Context.Done()
- return *new(T), os.NewSyscallError(rw.Op, rw.Context.Err())
- }
-
- // Error from syscall.RawConn methods. Conventionally the standard
- // library does not wrap internal/poll errors in os.NewSyscallError.
- return *new(T), pollErr
- }
-
- // Result from user function.
- return t, os.NewSyscallError(rw.Op, err)
-}
-
-// control executes Conn.control for op using the input function.
-func (c *Conn) control(ctx context.Context, op string, f func(fd int) error) error {
- _, err := controlT(c, ctx, op, func(fd int) (struct{}, error) {
- return struct{}{}, f(fd)
- })
- return err
-}
-
-// controlT executes c.rc.Control for op using the input function, returning a
-// newly allocated result T.
-func controlT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, error)) (T, error) {
- if atomic.LoadUint32(&c.closed) != 0 {
- // If the file descriptor is already closed, do nothing.
- return *new(T), os.NewSyscallError(op, unix.EBADF)
- }
-
- var (
- t T
- err error
- )
-
- doErr := c.rc.Control(func(fd uintptr) {
- // Repeatedly attempt the syscall(s) invoked by f until completion is
- // indicated by the return value of ready or the context is canceled.
- //
- // The last values for t and err are captured outside of the closure for
- // use when the loop breaks.
- for {
- if err = ctx.Err(); err != nil {
- // Early exit due to context cancel.
- return
- }
-
- t, err = f(int(fd))
- if ready(err) {
- return
- }
- }
- })
- if doErr != nil {
- // Error from syscall.RawConn methods. Conventionally the standard
- // library does not wrap internal/poll errors in os.NewSyscallError.
- return *new(T), doErr
- }
-
- // Result from user function.
- return t, os.NewSyscallError(op, err)
-}
-
-// ready indicates readiness based on the value of err.
-func ready(err error) bool {
- switch err {
- case unix.EAGAIN, unix.EINPROGRESS, unix.EINTR:
- // When a socket is in non-blocking mode, we might see a variety of errors:
- // - EAGAIN: most common case for a socket read not being ready
- // - EINPROGRESS: reported by some sockets when first calling connect
- // - EINTR: system call interrupted, more frequently occurs in Go 1.14+
- // because goroutines can be asynchronously preempted
- //
- // Return false to let the poller wait for readiness. See the source code
- // for internal/poll.FD.RawRead for more details.
- return false
- default:
- // Ready regardless of whether there was an error or no error.
- return true
- }
-}
-
-// Darwin and FreeBSD can't read or write 2GB+ files at a time,
-// even on 64-bit systems.
-// The same is true of socket implementations on many systems.
-// See golang.org/issue/7812 and golang.org/issue/16266.
-// Use 1GB instead of, say, 2GB-1, to keep subsequent reads aligned.
-const maxRW = 1 << 30
diff --git a/vendor/github.com/mdlayher/socket/conn_linux.go b/vendor/github.com/mdlayher/socket/conn_linux.go
deleted file mode 100644
index 37579d4a0c..0000000000
--- a/vendor/github.com/mdlayher/socket/conn_linux.go
+++ /dev/null
@@ -1,118 +0,0 @@
-//go:build linux
-// +build linux
-
-package socket
-
-import (
- "context"
- "os"
- "unsafe"
-
- "golang.org/x/net/bpf"
- "golang.org/x/sys/unix"
-)
-
-// IoctlKCMClone wraps ioctl(2) for unix.KCMClone values, but returns a Conn
-// rather than a raw file descriptor.
-func (c *Conn) IoctlKCMClone() (*Conn, error) {
- info, err := controlT(c, context.Background(), "ioctl", unix.IoctlKCMClone)
- if err != nil {
- return nil, err
- }
-
- // Successful clone, wrap in a Conn for use by the caller.
- return New(int(info.Fd), c.name)
-}
-
-// IoctlKCMAttach wraps ioctl(2) for unix.KCMAttach values.
-func (c *Conn) IoctlKCMAttach(info unix.KCMAttach) error {
- return c.control(context.Background(), "ioctl", func(fd int) error {
- return unix.IoctlKCMAttach(fd, info)
- })
-}
-
-// IoctlKCMUnattach wraps ioctl(2) for unix.KCMUnattach values.
-func (c *Conn) IoctlKCMUnattach(info unix.KCMUnattach) error {
- return c.control(context.Background(), "ioctl", func(fd int) error {
- return unix.IoctlKCMUnattach(fd, info)
- })
-}
-
-// PidfdGetfd wraps pidfd_getfd(2) for a Conn which wraps a pidfd, but returns a
-// Conn rather than a raw file descriptor.
-func (c *Conn) PidfdGetfd(targetFD, flags int) (*Conn, error) {
- outFD, err := controlT(c, context.Background(), "pidfd_getfd", func(fd int) (int, error) {
- return unix.PidfdGetfd(fd, targetFD, flags)
- })
- if err != nil {
- return nil, err
- }
-
- // Successful getfd, wrap in a Conn for use by the caller.
- return New(outFD, c.name)
-}
-
-// PidfdSendSignal wraps pidfd_send_signal(2) for a Conn which wraps a Linux
-// pidfd.
-func (c *Conn) PidfdSendSignal(sig unix.Signal, info *unix.Siginfo, flags int) error {
- return c.control(context.Background(), "pidfd_send_signal", func(fd int) error {
- return unix.PidfdSendSignal(fd, sig, info, flags)
- })
-}
-
-// SetBPF attaches an assembled BPF program to a Conn.
-func (c *Conn) SetBPF(filter []bpf.RawInstruction) error {
- // We can't point to the first instruction in the array if no instructions
- // are present.
- if len(filter) == 0 {
- return os.NewSyscallError("setsockopt", unix.EINVAL)
- }
-
- prog := unix.SockFprog{
- Len: uint16(len(filter)),
- Filter: (*unix.SockFilter)(unsafe.Pointer(&filter[0])),
- }
-
- return c.SetsockoptSockFprog(unix.SOL_SOCKET, unix.SO_ATTACH_FILTER, &prog)
-}
-
-// RemoveBPF removes a BPF filter from a Conn.
-func (c *Conn) RemoveBPF() error {
- // 0 argument is ignored.
- return c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_DETACH_FILTER, 0)
-}
-
-// SetsockoptPacketMreq wraps setsockopt(2) for unix.PacketMreq values.
-func (c *Conn) SetsockoptPacketMreq(level, opt int, mreq *unix.PacketMreq) error {
- return c.control(context.Background(), "setsockopt", func(fd int) error {
- return unix.SetsockoptPacketMreq(fd, level, opt, mreq)
- })
-}
-
-// SetsockoptSockFprog wraps setsockopt(2) for unix.SockFprog values.
-func (c *Conn) SetsockoptSockFprog(level, opt int, fprog *unix.SockFprog) error {
- return c.control(context.Background(), "setsockopt", func(fd int) error {
- return unix.SetsockoptSockFprog(fd, level, opt, fprog)
- })
-}
-
-// GetsockoptTpacketStats wraps getsockopt(2) for unix.TpacketStats values.
-func (c *Conn) GetsockoptTpacketStats(level, name int) (*unix.TpacketStats, error) {
- return controlT(c, context.Background(), "getsockopt", func(fd int) (*unix.TpacketStats, error) {
- return unix.GetsockoptTpacketStats(fd, level, name)
- })
-}
-
-// GetsockoptTpacketStatsV3 wraps getsockopt(2) for unix.TpacketStatsV3 values.
-func (c *Conn) GetsockoptTpacketStatsV3(level, name int) (*unix.TpacketStatsV3, error) {
- return controlT(c, context.Background(), "getsockopt", func(fd int) (*unix.TpacketStatsV3, error) {
- return unix.GetsockoptTpacketStatsV3(fd, level, name)
- })
-}
-
-// Waitid wraps waitid(2).
-func (c *Conn) Waitid(idType int, info *unix.Siginfo, options int, rusage *unix.Rusage) error {
- return c.read(context.Background(), "waitid", func(fd int) error {
- return unix.Waitid(idType, fd, info, options, rusage)
- })
-}
diff --git a/vendor/github.com/mdlayher/socket/doc.go b/vendor/github.com/mdlayher/socket/doc.go
deleted file mode 100644
index 7d4566c90b..0000000000
--- a/vendor/github.com/mdlayher/socket/doc.go
+++ /dev/null
@@ -1,13 +0,0 @@
-// Package socket provides a low-level network connection type which integrates
-// with Go's runtime network poller to provide asynchronous I/O and deadline
-// support.
-//
-// This package focuses on UNIX-like operating systems which make use of BSD
-// sockets system call APIs. It is meant to be used as a foundation for the
-// creation of operating system-specific socket packages, for socket families
-// such as Linux's AF_NETLINK, AF_PACKET, or AF_VSOCK. This package should not
-// be used directly in end user applications.
-//
-// Any use of package socket should be guarded by build tags, as one would also
-// use when importing the syscall or golang.org/x/sys packages.
-package socket
diff --git a/vendor/github.com/mdlayher/socket/netns_linux.go b/vendor/github.com/mdlayher/socket/netns_linux.go
deleted file mode 100644
index b29115ad1c..0000000000
--- a/vendor/github.com/mdlayher/socket/netns_linux.go
+++ /dev/null
@@ -1,150 +0,0 @@
-//go:build linux
-// +build linux
-
-package socket
-
-import (
- "errors"
- "fmt"
- "os"
- "runtime"
-
- "golang.org/x/sync/errgroup"
- "golang.org/x/sys/unix"
-)
-
-// errNetNSDisabled is returned when network namespaces are unavailable on
-// a given system.
-var errNetNSDisabled = errors.New("socket: Linux network namespaces are not enabled on this system")
-
-// withNetNS invokes fn within the context of the network namespace specified by
-// fd, while also managing the logic required to safely do so by manipulating
-// thread-local state.
-func withNetNS(fd int, fn func() (*Conn, error)) (*Conn, error) {
- var (
- eg errgroup.Group
- conn *Conn
- )
-
- eg.Go(func() error {
- // Retrieve and store the calling OS thread's network namespace so the
- // thread can be reassigned to it after creating a socket in another network
- // namespace.
- runtime.LockOSThread()
-
- ns, err := threadNetNS()
- if err != nil {
- // No thread-local manipulation, unlock.
- runtime.UnlockOSThread()
- return err
- }
- defer ns.Close()
-
- // Beyond this point, the thread's network namespace is poisoned. Do not
- // unlock the OS thread until all network namespace manipulation completes
- // to avoid returning to the caller with altered thread-local state.
-
- // Assign the current OS thread the goroutine is locked to to the given
- // network namespace.
- if err := ns.Set(fd); err != nil {
- return err
- }
-
- // Attempt Conn creation and unconditionally restore the original namespace.
- c, err := fn()
- if nerr := ns.Restore(); nerr != nil {
- // Failed to restore original namespace. Return an error and allow the
- // runtime to terminate the thread.
- if err == nil {
- _ = c.Close()
- }
-
- return nerr
- }
-
- // No more thread-local state manipulation; return the new Conn.
- runtime.UnlockOSThread()
- conn = c
- return nil
- })
-
- if err := eg.Wait(); err != nil {
- return nil, err
- }
-
- return conn, nil
-}
-
-// A netNS is a handle that can manipulate network namespaces.
-//
-// Operations performed on a netNS must use runtime.LockOSThread before
-// manipulating any network namespaces.
-type netNS struct {
- // The handle to a network namespace.
- f *os.File
-
- // Indicates if network namespaces are disabled on this system, and thus
- // operations should become a no-op or return errors.
- disabled bool
-}
-
-// threadNetNS constructs a netNS using the network namespace of the calling
-// thread. If the namespace is not the default namespace, runtime.LockOSThread
-// should be invoked first.
-func threadNetNS() (*netNS, error) {
- return fileNetNS(fmt.Sprintf("/proc/self/task/%d/ns/net", unix.Gettid()))
-}
-
-// fileNetNS opens file and creates a netNS. fileNetNS should only be called
-// directly in tests.
-func fileNetNS(file string) (*netNS, error) {
- f, err := os.Open(file)
- switch {
- case err == nil:
- return &netNS{f: f}, nil
- case os.IsNotExist(err):
- // Network namespaces are not enabled on this system. Use this signal
- // to return errors elsewhere if the caller explicitly asks for a
- // network namespace to be set.
- return &netNS{disabled: true}, nil
- default:
- return nil, err
- }
-}
-
-// Close releases the handle to a network namespace.
-func (n *netNS) Close() error {
- return n.do(func() error { return n.f.Close() })
-}
-
-// FD returns a file descriptor which represents the network namespace.
-func (n *netNS) FD() int {
- if n.disabled {
- // No reasonable file descriptor value in this case, so specify a
- // non-existent one.
- return -1
- }
-
- return int(n.f.Fd())
-}
-
-// Restore restores the original network namespace for the calling thread.
-func (n *netNS) Restore() error {
- return n.do(func() error { return n.Set(n.FD()) })
-}
-
-// Set sets a new network namespace for the current thread using fd.
-func (n *netNS) Set(fd int) error {
- return n.do(func() error {
- return os.NewSyscallError("setns", unix.Setns(fd, unix.CLONE_NEWNET))
- })
-}
-
-// do runs fn if network namespaces are enabled on this system.
-func (n *netNS) do(fn func() error) error {
- if n.disabled {
- return errNetNSDisabled
- }
-
- return fn()
-}
diff --git a/vendor/github.com/mdlayher/socket/netns_others.go b/vendor/github.com/mdlayher/socket/netns_others.go
deleted file mode 100644
index 4cceb3d047..0000000000
--- a/vendor/github.com/mdlayher/socket/netns_others.go
+++ /dev/null
@@ -1,14 +0,0 @@
-//go:build !linux
-// +build !linux
-
-package socket
-
-import (
- "fmt"
- "runtime"
-)
-
-// withNetNS returns an error on non-Linux systems.
-func withNetNS(_ int, _ func() (*Conn, error)) (*Conn, error) {
- return nil, fmt.Errorf("socket: Linux network namespace support is not available on %s", runtime.GOOS)
-}
diff --git a/vendor/github.com/mdlayher/socket/setbuffer_linux.go b/vendor/github.com/mdlayher/socket/setbuffer_linux.go
deleted file mode 100644
index 0d4aa4417c..0000000000
--- a/vendor/github.com/mdlayher/socket/setbuffer_linux.go
+++ /dev/null
@@ -1,24 +0,0 @@
-//go:build linux
-// +build linux
-
-package socket
-
-import "golang.org/x/sys/unix"
-
-// setReadBuffer wraps the SO_RCVBUF{,FORCE} setsockopt(2) options.
-func (c *Conn) setReadBuffer(bytes int) error {
- err := c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_RCVBUFFORCE, bytes)
- if err != nil {
- err = c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_RCVBUF, bytes)
- }
- return err
-}
-
-// setWriteBuffer wraps the SO_SNDBUF{,FORCE} setsockopt(2) options.
-func (c *Conn) setWriteBuffer(bytes int) error {
- err := c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_SNDBUFFORCE, bytes)
- if err != nil {
- err = c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_SNDBUF, bytes)
- }
- return err
-}
diff --git a/vendor/github.com/mdlayher/socket/setbuffer_others.go b/vendor/github.com/mdlayher/socket/setbuffer_others.go
deleted file mode 100644
index 72b36dbe31..0000000000
--- a/vendor/github.com/mdlayher/socket/setbuffer_others.go
+++ /dev/null
@@ -1,16 +0,0 @@
-//go:build !linux
-// +build !linux
-
-package socket
-
-import "golang.org/x/sys/unix"
-
-// setReadBuffer wraps the SO_RCVBUF setsockopt(2) option.
-func (c *Conn) setReadBuffer(bytes int) error {
- return c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_RCVBUF, bytes)
-}
-
-// setWriteBuffer wraps the SO_SNDBUF setsockopt(2) option.
-func (c *Conn) setWriteBuffer(bytes int) error {
- return c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_SNDBUF, bytes)
-}
diff --git a/vendor/github.com/mdlayher/socket/typ_cloexec_nonblock.go b/vendor/github.com/mdlayher/socket/typ_cloexec_nonblock.go
deleted file mode 100644
index 40e834310b..0000000000
--- a/vendor/github.com/mdlayher/socket/typ_cloexec_nonblock.go
+++ /dev/null
@@ -1,12 +0,0 @@
-//go:build !darwin
-// +build !darwin
-
-package socket
-
-import "golang.org/x/sys/unix"
-
-const (
- // These operating systems support CLOEXEC and NONBLOCK socket options.
- flagCLOEXEC = true
- socketFlags = unix.SOCK_CLOEXEC | unix.SOCK_NONBLOCK
-)
diff --git a/vendor/github.com/mdlayher/socket/typ_none.go b/vendor/github.com/mdlayher/socket/typ_none.go
deleted file mode 100644
index 9bbb1aab5f..0000000000
--- a/vendor/github.com/mdlayher/socket/typ_none.go
+++ /dev/null
@@ -1,11 +0,0 @@
-//go:build darwin
-// +build darwin
-
-package socket
-
-const (
- // These operating systems do not support CLOEXEC and NONBLOCK socket
- // options.
- flagCLOEXEC = false
- socketFlags = 0
-)
diff --git a/vendor/golang.org/x/sync/errgroup/errgroup.go b/vendor/golang.org/x/sync/errgroup/errgroup.go
deleted file mode 100644
index 948a3ee63d..0000000000
--- a/vendor/golang.org/x/sync/errgroup/errgroup.go
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 2016 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package errgroup provides synchronization, error propagation, and Context
-// cancelation for groups of goroutines working on subtasks of a common task.
-//
-// [errgroup.Group] is related to [sync.WaitGroup] but adds handling of tasks
-// returning errors.
-package errgroup
-
-import (
- "context"
- "fmt"
- "sync"
-)
-
-type token struct{}
-
-// A Group is a collection of goroutines working on subtasks that are part of
-// the same overall task.
-//
-// A zero Group is valid, has no limit on the number of active goroutines,
-// and does not cancel on error.
-type Group struct {
- cancel func(error)
-
- wg sync.WaitGroup
-
- sem chan token
-
- errOnce sync.Once
- err error
-}
-
-func (g *Group) done() {
- if g.sem != nil {
- <-g.sem
- }
- g.wg.Done()
-}
-
-// WithContext returns a new Group and an associated Context derived from ctx.
-//
-// The derived Context is canceled the first time a function passed to Go
-// returns a non-nil error or the first time Wait returns, whichever occurs
-// first.
-func WithContext(ctx context.Context) (*Group, context.Context) {
- ctx, cancel := withCancelCause(ctx)
- return &Group{cancel: cancel}, ctx
-}
-
-// Wait blocks until all function calls from the Go method have returned, then
-// returns the first non-nil error (if any) from them.
-func (g *Group) Wait() error {
- g.wg.Wait()
- if g.cancel != nil {
- g.cancel(g.err)
- }
- return g.err
-}
-
-// Go calls the given function in a new goroutine.
-// It blocks until the new goroutine can be added without the number of
-// active goroutines in the group exceeding the configured limit.
-//
-// The first call to return a non-nil error cancels the group's context, if the
-// group was created by calling WithContext. The error will be returned by Wait.
-func (g *Group) Go(f func() error) {
- if g.sem != nil {
- g.sem <- token{}
- }
-
- g.wg.Add(1)
- go func() {
- defer g.done()
-
- if err := f(); err != nil {
- g.errOnce.Do(func() {
- g.err = err
- if g.cancel != nil {
- g.cancel(g.err)
- }
- })
- }
- }()
-}
-
-// TryGo calls the given function in a new goroutine only if the number of
-// active goroutines in the group is currently below the configured limit.
-//
-// The return value reports whether the goroutine was started.
-func (g *Group) TryGo(f func() error) bool {
- if g.sem != nil {
- select {
- case g.sem <- token{}:
- // Note: this allows barging iff channels in general allow barging.
- default:
- return false
- }
- }
-
- g.wg.Add(1)
- go func() {
- defer g.done()
-
- if err := f(); err != nil {
- g.errOnce.Do(func() {
- g.err = err
- if g.cancel != nil {
- g.cancel(g.err)
- }
- })
- }
- }()
- return true
-}
-
-// SetLimit limits the number of active goroutines in this group to at most n.
-// A negative value indicates no limit.
-//
-// Any subsequent call to the Go method will block until it can add an active
-// goroutine without exceeding the configured limit.
-//
-// The limit must not be modified while any goroutines in the group are active.
-func (g *Group) SetLimit(n int) {
- if n < 0 {
- g.sem = nil
- return
- }
- if len(g.sem) != 0 {
- panic(fmt.Errorf("errgroup: modify limit while %v goroutines in the group are still active", len(g.sem)))
- }
- g.sem = make(chan token, n)
-}
diff --git a/vendor/golang.org/x/sync/errgroup/go120.go b/vendor/golang.org/x/sync/errgroup/go120.go
deleted file mode 100644
index f93c740b63..0000000000
--- a/vendor/golang.org/x/sync/errgroup/go120.go
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2023 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build go1.20
-
-package errgroup
-
-import "context"
-
-func withCancelCause(parent context.Context) (context.Context, func(error)) {
- return context.WithCancelCause(parent)
-}
diff --git a/vendor/golang.org/x/sync/errgroup/pre_go120.go b/vendor/golang.org/x/sync/errgroup/pre_go120.go
deleted file mode 100644
index 88ce33434e..0000000000
--- a/vendor/golang.org/x/sync/errgroup/pre_go120.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2023 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build !go1.20
-
-package errgroup
-
-import "context"
-
-func withCancelCause(parent context.Context) (context.Context, func(error)) {
- ctx, cancel := context.WithCancel(parent)
- return ctx, func(error) { cancel() }
-}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index c2c7160d24..5a3eb97a80 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -330,9 +330,6 @@ github.com/jessevdk/go-flags
# github.com/josharian/intern v1.0.0
## explicit; go 1.5
github.com/josharian/intern
-# github.com/josharian/native v1.1.0
-## explicit; go 1.13
-github.com/josharian/native
# github.com/json-iterator/go v1.1.12
## explicit; go 1.12
github.com/json-iterator/go
@@ -371,21 +368,9 @@ github.com/mattn/go-isatty
# github.com/matttproud/golang_protobuf_extensions v1.0.4
## explicit; go 1.9
github.com/matttproud/golang_protobuf_extensions/pbutil
-# github.com/mdlayher/arp v0.0.0-20220512170110-6706a2966875
-## explicit; go 1.12
-github.com/mdlayher/arp
-# github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118
-## explicit; go 1.12
-github.com/mdlayher/ethernet
# github.com/mdlayher/ndp v1.0.1
## explicit; go 1.20
github.com/mdlayher/ndp
-# github.com/mdlayher/packet v1.1.2
-## explicit; go 1.20
-github.com/mdlayher/packet
-# github.com/mdlayher/socket v0.4.1
-## explicit; go 1.20
-github.com/mdlayher/socket
# github.com/mitchellh/copystructure v1.2.0
## explicit; go 1.15
github.com/mitchellh/copystructure
@@ -713,7 +698,6 @@ golang.org/x/oauth2
golang.org/x/oauth2/internal
# golang.org/x/sync v0.6.0
## explicit; go 1.18
-golang.org/x/sync/errgroup
golang.org/x/sync/semaphore
# golang.org/x/sys v0.18.0
## explicit; go 1.18