2021-08-12 17:13:11 -04:00
//go:build linux
2017-07-16 01:35:55 -04:00
// +build linux
/ *
Copyright 2017 The Kubernetes Authors .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package ipvs
import (
"fmt"
"net"
"strings"
2018-12-22 05:07:09 -05:00
"sync"
2019-11-21 18:52:48 -05:00
"time"
2017-07-16 01:35:55 -04:00
2021-06-21 12:53:33 -04:00
"errors"
2020-03-26 10:20:01 -04:00
libipvs "github.com/moby/ipvs"
2020-04-06 11:25:24 -04:00
2021-12-13 11:57:35 -05:00
"golang.org/x/sys/unix"
2020-04-17 15:25:06 -04:00
"k8s.io/klog/v2"
2017-07-16 01:35:55 -04:00
)
2018-02-06 11:26:17 -05:00
// runner implements ipvs.Interface.
2017-07-16 01:35:55 -04:00
type runner struct {
2018-02-06 11:26:17 -05:00
ipvsHandle * libipvs . Handle
2018-12-22 05:07:09 -05:00
mu sync . Mutex // Protect Netlink calls
2017-07-16 01:35:55 -04:00
}
2018-02-06 11:26:17 -05:00
// Protocol is the IPVS service protocol type
type Protocol uint16
2017-07-16 01:35:55 -04:00
// New returns a new Interface which will call ipvs APIs.
2021-10-04 09:04:35 -04:00
func New ( ) Interface {
2018-02-06 11:26:17 -05:00
handle , err := libipvs . New ( "" )
2017-07-16 01:35:55 -04:00
if err != nil {
2023-05-27 05:33:39 -04:00
klog . ErrorS ( err , "IPVS interface can't be initialized" )
2017-07-16 01:35:55 -04:00
return nil
}
return & runner {
2018-02-06 11:26:17 -05:00
ipvsHandle : handle ,
2017-07-16 01:35:55 -04:00
}
}
2018-02-06 11:26:17 -05:00
// AddVirtualServer is part of ipvs.Interface.
2017-07-16 01:35:55 -04:00
func ( runner * runner ) AddVirtualServer ( vs * VirtualServer ) error {
2018-02-06 11:26:17 -05:00
svc , err := toIPVSService ( vs )
2017-07-16 01:35:55 -04:00
if err != nil {
2021-06-21 12:53:33 -04:00
return fmt . Errorf ( "could not convert local virtual server to IPVS service: %w" , err )
2017-07-16 01:35:55 -04:00
}
2018-12-22 05:07:09 -05:00
runner . mu . Lock ( )
defer runner . mu . Unlock ( )
2018-02-06 11:26:17 -05:00
return runner . ipvsHandle . NewService ( svc )
2017-07-16 01:35:55 -04:00
}
2018-02-06 11:26:17 -05:00
// UpdateVirtualServer is part of ipvs.Interface.
2017-07-16 01:35:55 -04:00
func ( runner * runner ) UpdateVirtualServer ( vs * VirtualServer ) error {
2018-02-06 11:26:17 -05:00
svc , err := toIPVSService ( vs )
2017-07-16 01:35:55 -04:00
if err != nil {
2021-06-21 12:53:33 -04:00
return fmt . Errorf ( "could not convert local virtual server to IPVS service: %w" , err )
2017-07-16 01:35:55 -04:00
}
2018-12-22 05:07:09 -05:00
runner . mu . Lock ( )
defer runner . mu . Unlock ( )
2018-02-06 11:26:17 -05:00
return runner . ipvsHandle . UpdateService ( svc )
2017-07-16 01:35:55 -04:00
}
2018-02-06 11:26:17 -05:00
// DeleteVirtualServer is part of ipvs.Interface.
2017-07-16 01:35:55 -04:00
func ( runner * runner ) DeleteVirtualServer ( vs * VirtualServer ) error {
2018-02-06 11:26:17 -05:00
svc , err := toIPVSService ( vs )
2017-07-16 01:35:55 -04:00
if err != nil {
2021-06-21 12:53:33 -04:00
return fmt . Errorf ( "could not convert local virtual server to IPVS service: %w" , err )
2017-07-16 01:35:55 -04:00
}
2018-12-22 05:07:09 -05:00
runner . mu . Lock ( )
defer runner . mu . Unlock ( )
2018-02-06 11:26:17 -05:00
return runner . ipvsHandle . DelService ( svc )
2017-07-16 01:35:55 -04:00
}
2018-02-06 11:26:17 -05:00
// GetVirtualServer is part of ipvs.Interface.
2017-07-16 01:35:55 -04:00
func ( runner * runner ) GetVirtualServer ( vs * VirtualServer ) ( * VirtualServer , error ) {
2018-02-06 11:26:17 -05:00
svc , err := toIPVSService ( vs )
2017-07-16 01:35:55 -04:00
if err != nil {
2021-06-21 12:53:33 -04:00
return nil , fmt . Errorf ( "could not convert local virtual server to IPVS service: %w" , err )
2017-07-16 01:35:55 -04:00
}
2018-12-22 05:07:09 -05:00
runner . mu . Lock ( )
2018-02-06 11:26:17 -05:00
ipvsSvc , err := runner . ipvsHandle . GetService ( svc )
2018-12-22 05:07:09 -05:00
runner . mu . Unlock ( )
2017-07-16 01:35:55 -04:00
if err != nil {
2021-06-21 12:53:33 -04:00
return nil , fmt . Errorf ( "could not get IPVS service: %w" , err )
2017-07-16 01:35:55 -04:00
}
2018-02-06 11:26:17 -05:00
vServ , err := toVirtualServer ( ipvsSvc )
2017-07-16 01:35:55 -04:00
if err != nil {
2021-06-21 12:53:33 -04:00
return nil , fmt . Errorf ( "could not convert IPVS service to local virtual server: %w" , err )
2017-07-16 01:35:55 -04:00
}
2018-02-06 11:26:17 -05:00
return vServ , nil
2017-07-16 01:35:55 -04:00
}
2018-02-06 11:26:17 -05:00
// GetVirtualServers is part of ipvs.Interface.
2017-07-16 01:35:55 -04:00
func ( runner * runner ) GetVirtualServers ( ) ( [ ] * VirtualServer , error ) {
2018-12-22 05:07:09 -05:00
runner . mu . Lock ( )
2018-02-06 11:26:17 -05:00
ipvsSvcs , err := runner . ipvsHandle . GetServices ( )
2018-12-22 05:07:09 -05:00
runner . mu . Unlock ( )
2017-07-16 01:35:55 -04:00
if err != nil {
2021-06-21 12:53:33 -04:00
return nil , fmt . Errorf ( "could not get IPVS services: %w" , err )
2017-07-16 01:35:55 -04:00
}
vss := make ( [ ] * VirtualServer , 0 )
2018-02-06 11:26:17 -05:00
for _ , ipvsSvc := range ipvsSvcs {
vs , err := toVirtualServer ( ipvsSvc )
2017-07-16 01:35:55 -04:00
if err != nil {
2021-06-21 12:53:33 -04:00
return nil , fmt . Errorf ( "could not convert IPVS service to local virtual server: %w" , err )
2017-07-16 01:35:55 -04:00
}
vss = append ( vss , vs )
}
return vss , nil
}
2018-02-06 11:26:17 -05:00
// Flush is part of ipvs.Interface. Currently we delete IPVS services one by one
2017-07-16 01:35:55 -04:00
func ( runner * runner ) Flush ( ) error {
2018-12-22 05:07:09 -05:00
runner . mu . Lock ( )
defer runner . mu . Unlock ( )
2017-09-06 07:07:42 -04:00
return runner . ipvsHandle . Flush ( )
2017-07-16 01:35:55 -04:00
}
2018-02-06 11:26:17 -05:00
// AddRealServer is part of ipvs.Interface.
2017-07-16 01:35:55 -04:00
func ( runner * runner ) AddRealServer ( vs * VirtualServer , rs * RealServer ) error {
2018-02-06 11:26:17 -05:00
svc , err := toIPVSService ( vs )
2017-07-16 01:35:55 -04:00
if err != nil {
2021-06-21 12:53:33 -04:00
return fmt . Errorf ( "could not convert local virtual server to IPVS service: %w" , err )
2017-07-16 01:35:55 -04:00
}
2018-02-06 11:26:17 -05:00
dst , err := toIPVSDestination ( rs )
2017-07-16 01:35:55 -04:00
if err != nil {
2021-06-21 12:53:33 -04:00
return fmt . Errorf ( "could not convert local real server to IPVS destination: %w" , err )
2017-07-16 01:35:55 -04:00
}
2018-12-22 05:07:09 -05:00
runner . mu . Lock ( )
defer runner . mu . Unlock ( )
2018-02-06 11:26:17 -05:00
return runner . ipvsHandle . NewDestination ( svc , dst )
2017-07-16 01:35:55 -04:00
}
2018-02-06 11:26:17 -05:00
// DeleteRealServer is part of ipvs.Interface.
2017-07-16 01:35:55 -04:00
func ( runner * runner ) DeleteRealServer ( vs * VirtualServer , rs * RealServer ) error {
2018-02-06 11:26:17 -05:00
svc , err := toIPVSService ( vs )
2017-07-16 01:35:55 -04:00
if err != nil {
2021-06-21 12:53:33 -04:00
return fmt . Errorf ( "could not convert local virtual server to IPVS service: %w" , err )
2017-07-16 01:35:55 -04:00
}
2018-02-06 11:26:17 -05:00
dst , err := toIPVSDestination ( rs )
2017-07-16 01:35:55 -04:00
if err != nil {
2021-06-21 12:53:33 -04:00
return fmt . Errorf ( "could not convert local real server to IPVS destination: %w" , err )
2017-07-16 01:35:55 -04:00
}
2018-12-22 05:07:09 -05:00
runner . mu . Lock ( )
defer runner . mu . Unlock ( )
2018-02-06 11:26:17 -05:00
return runner . ipvsHandle . DelDestination ( svc , dst )
2017-07-16 01:35:55 -04:00
}
2018-07-19 08:18:04 -04:00
func ( runner * runner ) UpdateRealServer ( vs * VirtualServer , rs * RealServer ) error {
svc , err := toIPVSService ( vs )
if err != nil {
2021-06-21 12:53:33 -04:00
return fmt . Errorf ( "could not convert local virtual server to IPVS service: %w" , err )
2018-07-19 08:18:04 -04:00
}
dst , err := toIPVSDestination ( rs )
if err != nil {
2021-06-21 12:53:33 -04:00
return fmt . Errorf ( "could not convert local real server to IPVS destination: %w" , err )
2018-07-19 08:18:04 -04:00
}
2018-12-22 05:07:09 -05:00
runner . mu . Lock ( )
defer runner . mu . Unlock ( )
2018-07-19 08:18:04 -04:00
return runner . ipvsHandle . UpdateDestination ( svc , dst )
}
2018-02-06 11:26:17 -05:00
// GetRealServers is part of ipvs.Interface.
2017-07-16 01:35:55 -04:00
func ( runner * runner ) GetRealServers ( vs * VirtualServer ) ( [ ] * RealServer , error ) {
2018-02-06 11:26:17 -05:00
svc , err := toIPVSService ( vs )
2017-07-16 01:35:55 -04:00
if err != nil {
2021-06-21 12:53:33 -04:00
return nil , fmt . Errorf ( "could not convert local virtual server to IPVS service: %w" , err )
2017-07-16 01:35:55 -04:00
}
2018-12-22 05:07:09 -05:00
runner . mu . Lock ( )
2018-02-06 11:26:17 -05:00
dsts , err := runner . ipvsHandle . GetDestinations ( svc )
2018-12-22 05:07:09 -05:00
runner . mu . Unlock ( )
2017-07-16 01:35:55 -04:00
if err != nil {
2021-06-21 12:53:33 -04:00
return nil , fmt . Errorf ( "could not get IPVS destination for service: %w" , err )
2017-07-16 01:35:55 -04:00
}
2018-02-06 11:26:17 -05:00
rss := make ( [ ] * RealServer , 0 )
for _ , dst := range dsts {
dst , err := toRealServer ( dst )
2017-07-16 01:35:55 -04:00
// TODO: aggregate errors?
if err != nil {
2021-06-21 12:53:33 -04:00
return nil , fmt . Errorf ( "could not convert IPVS destination to local real server: %w" , err )
2017-07-16 01:35:55 -04:00
}
2018-02-06 11:26:17 -05:00
rss = append ( rss , dst )
2017-07-16 01:35:55 -04:00
}
2018-02-06 11:26:17 -05:00
return rss , nil
2017-07-16 01:35:55 -04:00
}
2019-11-21 18:52:48 -05:00
// ConfigureTimeouts is the equivalent to running "ipvsadm --set" to configure tcp, tcpfin and udp timeouts
func ( runner * runner ) ConfigureTimeouts ( tcpTimeout , tcpFinTimeout , udpTimeout time . Duration ) error {
ipvsConfig := & libipvs . Config {
TimeoutTCP : tcpTimeout ,
TimeoutTCPFin : tcpFinTimeout ,
TimeoutUDP : udpTimeout ,
}
return runner . ipvsHandle . SetConfig ( ipvsConfig )
}
2018-02-06 11:26:17 -05:00
// toVirtualServer converts an IPVS Service to the equivalent VirtualServer structure.
func toVirtualServer ( svc * libipvs . Service ) ( * VirtualServer , error ) {
2017-07-16 01:35:55 -04:00
if svc == nil {
return nil , errors . New ( "ipvs svc should not be empty" )
}
vs := & VirtualServer {
Address : svc . Address ,
Port : svc . Port ,
Scheduler : svc . SchedName ,
2018-02-06 11:26:17 -05:00
Protocol : protocolToString ( Protocol ( svc . Protocol ) ) ,
2017-07-16 01:35:55 -04:00
Timeout : svc . Timeout ,
}
2023-01-31 05:55:45 -05:00
// Test FlagHashed (0x2). A valid flag must include FlagHashed
2017-09-12 05:25:30 -04:00
if svc . Flags & FlagHashed == 0 {
2023-01-31 05:55:45 -05:00
return nil , fmt . Errorf ( "Flags of successfully created IPVS service should enable the flag (%x) since every service is hashed into the service table" , FlagHashed )
2017-09-12 05:25:30 -04:00
}
// Sub Flags to 0x2
// 011 -> 001, 010 -> 000
vs . Flags = ServiceFlags ( svc . Flags &^ uint32 ( FlagHashed ) )
2017-07-16 01:35:55 -04:00
if vs . Address == nil {
2021-12-13 11:57:35 -05:00
if svc . AddressFamily == unix . AF_INET {
2017-07-16 01:35:55 -04:00
vs . Address = net . IPv4zero
} else {
vs . Address = net . IPv6zero
}
}
return vs , nil
}
2018-02-06 11:26:17 -05:00
// toRealServer converts an IPVS Destination to the equivalent RealServer structure.
func toRealServer ( dst * libipvs . Destination ) ( * RealServer , error ) {
2017-07-16 01:35:55 -04:00
if dst == nil {
return nil , errors . New ( "ipvs destination should not be empty" )
}
return & RealServer {
2018-09-04 05:33:22 -04:00
Address : dst . Address ,
Port : dst . Port ,
Weight : dst . Weight ,
ActiveConn : dst . ActiveConnections ,
InactiveConn : dst . InactiveConnections ,
2017-07-16 01:35:55 -04:00
} , nil
}
2018-02-06 11:26:17 -05:00
// toIPVSService converts a VirtualServer to the equivalent IPVS Service structure.
func toIPVSService ( vs * VirtualServer ) ( * libipvs . Service , error ) {
2017-07-16 01:35:55 -04:00
if vs == nil {
return nil , errors . New ( "virtual server should not be empty" )
}
2018-02-06 11:26:17 -05:00
ipvsSvc := & libipvs . Service {
2017-07-16 01:35:55 -04:00
Address : vs . Address ,
2018-02-06 11:26:17 -05:00
Protocol : stringToProtocol ( vs . Protocol ) ,
2017-07-16 01:35:55 -04:00
Port : vs . Port ,
SchedName : vs . Scheduler ,
Flags : uint32 ( vs . Flags ) ,
Timeout : vs . Timeout ,
}
if ip4 := vs . Address . To4 ( ) ; ip4 != nil {
2021-12-13 11:57:35 -05:00
ipvsSvc . AddressFamily = unix . AF_INET
2018-02-06 11:26:17 -05:00
ipvsSvc . Netmask = 0xffffffff
2017-07-16 01:35:55 -04:00
} else {
2021-12-13 11:57:35 -05:00
ipvsSvc . AddressFamily = unix . AF_INET6
2018-02-06 11:26:17 -05:00
ipvsSvc . Netmask = 128
2017-07-16 01:35:55 -04:00
}
2018-02-06 11:26:17 -05:00
return ipvsSvc , nil
2017-07-16 01:35:55 -04:00
}
2018-02-06 11:26:17 -05:00
// toIPVSDestination converts a RealServer to the equivalent IPVS Destination structure.
func toIPVSDestination ( rs * RealServer ) ( * libipvs . Destination , error ) {
2017-07-16 01:35:55 -04:00
if rs == nil {
return nil , errors . New ( "real server should not be empty" )
}
2018-02-06 11:26:17 -05:00
return & libipvs . Destination {
2017-07-16 01:35:55 -04:00
Address : rs . Address ,
Port : rs . Port ,
Weight : rs . Weight ,
} , nil
}
2018-02-06 11:26:17 -05:00
// stringToProtocolType returns the protocol type for the given name
func stringToProtocol ( protocol string ) uint16 {
2017-07-16 01:35:55 -04:00
switch strings . ToLower ( protocol ) {
case "tcp" :
2021-12-13 11:57:35 -05:00
return uint16 ( unix . IPPROTO_TCP )
2017-07-16 01:35:55 -04:00
case "udp" :
2021-12-13 11:57:35 -05:00
return uint16 ( unix . IPPROTO_UDP )
2018-08-26 15:37:11 -04:00
case "sctp" :
2021-12-13 11:57:35 -05:00
return uint16 ( unix . IPPROTO_SCTP )
2017-07-16 01:35:55 -04:00
}
return uint16 ( 0 )
}
2018-02-06 11:26:17 -05:00
// protocolTypeToString returns the name for the given protocol.
func protocolToString ( proto Protocol ) string {
2017-07-16 01:35:55 -04:00
switch proto {
2021-12-13 11:57:35 -05:00
case unix . IPPROTO_TCP :
2017-07-16 01:35:55 -04:00
return "TCP"
2021-12-13 11:57:35 -05:00
case unix . IPPROTO_UDP :
2017-07-16 01:35:55 -04:00
return "UDP"
2021-12-13 11:57:35 -05:00
case unix . IPPROTO_SCTP :
2018-06-11 07:25:18 -04:00
return "SCTP"
2017-07-16 01:35:55 -04:00
}
return ""
}