mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/kube-vip/kube-vip.git
synced 2026-09-20 08:03:47 +08:00
Merge pull request #1554 from lbohdanl/feat/vlan-support
This commit is contained in:
@@ -44,6 +44,10 @@ type Instance struct {
|
||||
DHCPv4Client vip.DHCPClient
|
||||
DHCPv6Client vip.DHCPClient
|
||||
|
||||
// Service use Vlan
|
||||
IsVLAN bool
|
||||
VLANInterface string
|
||||
|
||||
// External Gateway IP the service is forwarded from
|
||||
UPNPGatewayIPs []string
|
||||
|
||||
@@ -72,10 +76,31 @@ func NewInstance(ctx context.Context, svc *v1.Service, config *kubevip.Config,
|
||||
var err error
|
||||
var dnsAddresses []string
|
||||
|
||||
// Create new service
|
||||
instance := &Instance{
|
||||
ServiceSnapshot: svc,
|
||||
dnsAddresses: dnsAddresses,
|
||||
}
|
||||
|
||||
for _, address := range instanceAddresses {
|
||||
// Detect if we're using a specific interface for services
|
||||
var svcInterface string
|
||||
svcInterface = svc.Annotations[kubevip.ServiceInterface] // If the service has a specific interface defined, then use it
|
||||
|
||||
svcInterface = svc.Annotations[kubevip.ServiceVlan]
|
||||
if svcInterface != "" {
|
||||
parent, tag, err := utils.ParseVLANInterface(svcInterface)
|
||||
if err != nil {
|
||||
log.Error("failed to validate VLAN", "err", err)
|
||||
}
|
||||
|
||||
if err := instance.addVLAN(parent, tag); err != nil {
|
||||
log.Error("failed to create VLAN", "err", err)
|
||||
}
|
||||
} else {
|
||||
// If no vlan defined use specific interface from annotation
|
||||
svcInterface = svc.Annotations[kubevip.ServiceInterface]
|
||||
}
|
||||
|
||||
if svcInterface == kubevip.Auto {
|
||||
link, err = autoFindInterface(address)
|
||||
if err != nil {
|
||||
@@ -194,7 +219,21 @@ func NewInstance(ctx context.Context, svc *v1.Service, config *kubevip.Config,
|
||||
log.Info("hostname", "addr", hostname)
|
||||
// Detect if we're using a specific interface for services
|
||||
var svcInterface string
|
||||
svcInterface = svc.Annotations[kubevip.ServiceInterface] // If the service has a specific interface defined, then use it
|
||||
|
||||
svcInterface = svc.Annotations[kubevip.ServiceVlan]
|
||||
if svcInterface != "" {
|
||||
parent, tag, err := utils.ParseVLANInterface(svcInterface)
|
||||
if err != nil {
|
||||
log.Error("failed to validate VLAN", "err", err)
|
||||
}
|
||||
|
||||
if err := instance.addVLAN(parent, tag); err != nil {
|
||||
log.Error("failed to create VLAN", "err", err)
|
||||
}
|
||||
} else {
|
||||
// If no vlan defined use specific interface from annotation
|
||||
svcInterface = svc.Annotations[kubevip.ServiceInterface]
|
||||
}
|
||||
|
||||
// If it is still blank then use the
|
||||
if svcInterface == "" {
|
||||
@@ -239,12 +278,6 @@ func NewInstance(ctx context.Context, svc *v1.Service, config *kubevip.Config,
|
||||
})
|
||||
}
|
||||
|
||||
// Create new service
|
||||
instance := &Instance{
|
||||
ServiceSnapshot: svc,
|
||||
dnsAddresses: dnsAddresses,
|
||||
}
|
||||
|
||||
if svc.Annotations != nil {
|
||||
instance.DHCPInterfaceHwaddr = svc.Annotations[kubevip.HwAddrKey]
|
||||
requestedIP := svc.Annotations[kubevip.RequestedIP]
|
||||
@@ -432,6 +465,57 @@ func getAutoInterfaceName(link netlink.Link, defaultInterface string) string {
|
||||
return link.Attrs().Name
|
||||
}
|
||||
|
||||
func (i *Instance) addVLAN(parentInterface string, tag int) error {
|
||||
var parent netlink.Link
|
||||
|
||||
interfaceName := fmt.Sprintf("%s.%d", parentInterface, tag)
|
||||
iface, err := netlink.LinkByName(interfaceName)
|
||||
if err != nil {
|
||||
// check if parent interface doesnt exist
|
||||
parent, err = netlink.LinkByName(parentInterface)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error finding VLAN parent interface %s: %v", parentInterface, err)
|
||||
}
|
||||
|
||||
log.Info("Creating new VLAN interface", "interface", interfaceName)
|
||||
|
||||
vlan := &netlink.Vlan{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
Name: interfaceName,
|
||||
ParentIndex: parent.Attrs().Index,
|
||||
},
|
||||
VlanId: tag,
|
||||
VlanProtocol: netlink.VLAN_PROTOCOL_8021Q,
|
||||
}
|
||||
|
||||
err = netlink.LinkAdd(vlan)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not add VLAN %s: %v", interfaceName, err)
|
||||
}
|
||||
|
||||
err = netlink.LinkSetUp(vlan)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not bring up VLAN interface [%s] : %v", interfaceName, err)
|
||||
}
|
||||
|
||||
_, err = net.InterfaceByName(interfaceName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error finding new VLAN interface by name [%v]", err)
|
||||
}
|
||||
} else {
|
||||
log.Info("Using existing VLAN interface", "interface", interfaceName)
|
||||
|
||||
if err := utils.ValidateVLANInterface(iface, parent, tag); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
i.VLANInterface = interfaceName
|
||||
i.IsVLAN = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Instance) startDHCP(ctx context.Context, index int, backoffAttempts uint, wg *sync.WaitGroup) error {
|
||||
if len(i.VIPConfigs) > 2 {
|
||||
return fmt.Errorf("DHCP can be used with 2 VIP config maximally, got: %v", len(i.VIPConfigs))
|
||||
|
||||
@@ -52,6 +52,9 @@ const (
|
||||
// Define an interface name to bind the address of the LoadBalancer to
|
||||
ServiceInterface = "kube-vip.io/serviceInterface"
|
||||
|
||||
// Specify VLAN subinterface for service (e.g. eth0.200)
|
||||
ServiceVlan = "kube-vip.io/serviceVLAN"
|
||||
|
||||
ServiceSecurityIgnore = "kube-vip.io/ignore-service-security"
|
||||
|
||||
// Enable UPNP on a Service
|
||||
|
||||
@@ -431,6 +431,19 @@ func (p *Processor) deleteService(ctx context.Context, uid types.UID) error {
|
||||
for x := range serviceInstance.Clusters {
|
||||
serviceInstance.Clusters[x].Stop()
|
||||
}
|
||||
|
||||
if serviceInstance.IsVLAN {
|
||||
vlan, err := netlink.LinkByName(serviceInstance.VLANInterface)
|
||||
if err != nil {
|
||||
return fmt.Errorf("[service] error finding VLAN Interface: %v", err)
|
||||
}
|
||||
|
||||
err = netlink.LinkDel(vlan)
|
||||
if err != nil {
|
||||
return fmt.Errorf("[service] error deleting VLAN interface : %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if serviceInstance.IsDHCPv4 || serviceInstance.IsDHCPv6 {
|
||||
if serviceInstance.IsDHCPv4 {
|
||||
serviceInstance.DHCPv4Client.Stop()
|
||||
|
||||
64
pkg/utils/vlan.go
Normal file
64
pkg/utils/vlan.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
// ParseVLANInterface does vlan name validation
|
||||
// and splits it into the parent interface and tag
|
||||
func ParseVLANInterface(name string) (string, int, error) {
|
||||
vlanParts := strings.Split(name, ".")
|
||||
if len(vlanParts) != 2 {
|
||||
return "", 0, fmt.Errorf("invalid VLAN name %s, expected format: eth1.300", name)
|
||||
}
|
||||
|
||||
parent := vlanParts[0]
|
||||
if parent == "" {
|
||||
return "", 0, fmt.Errorf("parent interface for VLAN is empty")
|
||||
}
|
||||
|
||||
tagStr := vlanParts[1]
|
||||
tag, err := strconv.Atoi(tagStr)
|
||||
if err != nil {
|
||||
return "", 0, fmt.Errorf("invalid VLAN tag %s", tagStr)
|
||||
}
|
||||
|
||||
// according to IEEE 802.1Q
|
||||
if tag < 1 || tag > 4094 {
|
||||
return "", 0, fmt.Errorf("VLAN tag must be between 1 and 4094, got %d", tag)
|
||||
}
|
||||
|
||||
return parent, tag, nil
|
||||
}
|
||||
|
||||
// ValidateVLANInterface validates vlans parent index and expected vlan tag
|
||||
func ValidateVLANInterface(link netlink.Link, parent netlink.Link, expectedID int) error {
|
||||
vlan, ok := link.(*netlink.Vlan)
|
||||
if !ok {
|
||||
return fmt.Errorf("link %s exists but is not a VLAN interface", link.Attrs().Name)
|
||||
}
|
||||
|
||||
if vlan.ParentIndex != parent.Attrs().Index {
|
||||
return fmt.Errorf(
|
||||
"VLAN interface %s has parent index %d, expected %d",
|
||||
link.Attrs().Name,
|
||||
vlan.ParentIndex,
|
||||
parent.Attrs().Index,
|
||||
)
|
||||
}
|
||||
|
||||
if vlan.VlanId != expectedID {
|
||||
return fmt.Errorf(
|
||||
"VLAN interface %s has id %d, expected %d",
|
||||
link.Attrs().Name,
|
||||
vlan.VlanId,
|
||||
expectedID,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user