mirror of
https://github.com/cloudnativelabs/kube-router.git
synced 2025-10-09 17:01:30 +02:00
209 lines
6.3 KiB
Go
209 lines
6.3 KiB
Go
// +build linux
|
|
|
|
package libipvs
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"fmt"
|
|
"os/exec"
|
|
"strings"
|
|
"syscall"
|
|
|
|
"github.com/hkwi/nlgo"
|
|
)
|
|
|
|
type IPVSHandle interface {
|
|
Flush() error
|
|
GetInfo() (info Info, err error)
|
|
ListServices() (services []*Service, err error)
|
|
NewService(s *Service) error
|
|
UpdateService(s *Service) error
|
|
DelService(s *Service) error
|
|
ListDestinations(s *Service) (dsts []*Destination, err error)
|
|
NewDestination(s *Service, d *Destination) error
|
|
UpdateDestination(s *Service, d *Destination) error
|
|
DelDestination(s *Service, d *Destination) error
|
|
}
|
|
|
|
// Handle provides a ipvs handle to program ipvs rules.
|
|
type Handle struct {
|
|
genlHub *nlgo.GenlHub
|
|
genlFamily nlgo.GenlFamily
|
|
}
|
|
|
|
// ResponseHandler know how to process netlink response
|
|
type ResponseHandler struct {
|
|
Policy nlgo.MapPolicy
|
|
Handle func(attrs nlgo.AttrMap) error
|
|
}
|
|
|
|
// New provides a new ipvs handle. It will return a valid handle or an error in case an
|
|
// error occurred while creating the handle.
|
|
func New() (IPVSHandle, error) {
|
|
h := &Handle{}
|
|
|
|
if out, err := exec.Command("modprobe", "-va", "ip_vs").CombinedOutput(); err != nil {
|
|
return nil, fmt.Errorf("Running modprobe ip_vs failed with message: `%s`, error: %v", strings.TrimSpace(string(out)), err)
|
|
}
|
|
|
|
if genlHub, err := nlgo.NewGenlHub(); err != nil {
|
|
return nil, err
|
|
} else {
|
|
h.genlHub = genlHub
|
|
}
|
|
// lookup family
|
|
if genlFamily := h.genlHub.Family(IPVS_GENL_NAME); genlFamily.Id == 0 {
|
|
return nil, fmt.Errorf("Invalid genl family: %v", IPVS_GENL_NAME)
|
|
} else if genlFamily.Version != IPVS_GENL_VERSION {
|
|
return nil, fmt.Errorf("Unsupported ipvs genl family: %+v", genlFamily)
|
|
} else {
|
|
h.genlFamily = genlFamily
|
|
}
|
|
return h, nil
|
|
}
|
|
|
|
var emptyAttrs = nlgo.AttrSlice{}
|
|
|
|
func (i *Handle) Flush() error {
|
|
return i.doCmd(IPVS_CMD_FLUSH, syscall.NLM_F_ACK, emptyAttrs, nil)
|
|
}
|
|
|
|
func (i *Handle) ListServices() (services []*Service, err error) {
|
|
respHandler := &ResponseHandler{
|
|
Policy: ipvs_cmd_policy,
|
|
Handle: func(attrs nlgo.AttrMap) error {
|
|
if serviceAttrs := attrs.Get(IPVS_CMD_ATTR_SERVICE); serviceAttrs == nil {
|
|
return fmt.Errorf("IPVS_CMD_GET_SERVICE without IPVS_CMD_ATTR_SERVICE")
|
|
} else if service, err := unpackService(serviceAttrs.(nlgo.AttrMap)); err != nil {
|
|
return err
|
|
} else {
|
|
services = append(services, &service)
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
return services, i.doCmd(IPVS_CMD_GET_SERVICE, syscall.NLM_F_DUMP, emptyAttrs, respHandler)
|
|
}
|
|
|
|
func (i *Handle) ListDestinations(s *Service) (dsts []*Destination, err error) {
|
|
respHandler := &ResponseHandler{
|
|
Policy: ipvs_cmd_policy,
|
|
Handle: func(attrs nlgo.AttrMap) error {
|
|
if destAttrs := attrs.Get(IPVS_CMD_ATTR_DEST); destAttrs == nil {
|
|
return fmt.Errorf("IPVS_CMD_GET_DEST without IPVS_CMD_ATTR_DEST")
|
|
} else if dst, err := unpackDest(destAttrs.(nlgo.AttrMap)); err != nil {
|
|
return err
|
|
} else {
|
|
dsts = append(dsts, &dst)
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
attrs := i.fillAttrs(s, nil, false, false)
|
|
return dsts, i.doCmd(IPVS_CMD_GET_DEST, syscall.NLM_F_DUMP, attrs, respHandler)
|
|
}
|
|
|
|
func (i *Handle) GetInfo() (info Info, err error) {
|
|
respHandler := &ResponseHandler{
|
|
Policy: ipvs_info_policy,
|
|
Handle: func(attrs nlgo.AttrMap) error {
|
|
if cmdInfo, err := unpackInfo(attrs); err != nil {
|
|
return err
|
|
} else {
|
|
info = cmdInfo
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
return info, i.doCmd(IPVS_CMD_GET_INFO, syscall.NLM_F_ACK, emptyAttrs, respHandler)
|
|
}
|
|
|
|
// NewService creates a new ipvs service in the passed handle.
|
|
func (i *Handle) NewService(s *Service) error {
|
|
attrs := i.fillAttrs(s, nil, true, false)
|
|
return i.doCmd(IPVS_CMD_NEW_SERVICE, syscall.NLM_F_ACK, attrs, nil)
|
|
}
|
|
|
|
// UpdateService updates an already existing service in the passed
|
|
// handle.
|
|
func (i *Handle) UpdateService(s *Service) error {
|
|
attrs := i.fillAttrs(s, nil, true, false)
|
|
return i.doCmd(IPVS_CMD_SET_SERVICE, syscall.NLM_F_ACK, attrs, nil)
|
|
}
|
|
|
|
// DelService deletes an already existing service in the passed
|
|
// handle.
|
|
func (i *Handle) DelService(s *Service) error {
|
|
attrs := i.fillAttrs(s, nil, false, false)
|
|
return i.doCmd(IPVS_CMD_DEL_SERVICE, syscall.NLM_F_ACK, attrs, nil)
|
|
}
|
|
|
|
// NewDestination creates a new real server in the passed ipvs
|
|
// service which should already be existing in the passed handle.
|
|
func (i *Handle) NewDestination(s *Service, d *Destination) error {
|
|
attrs := i.fillAttrs(s, d, false, true)
|
|
return i.doCmd(IPVS_CMD_NEW_DEST, syscall.NLM_F_ACK, attrs, nil)
|
|
}
|
|
|
|
// UpdateDestination updates an already existing real server in the
|
|
// passed ipvs service in the passed handle.
|
|
func (i *Handle) UpdateDestination(s *Service, d *Destination) error {
|
|
attrs := i.fillAttrs(s, d, false, true)
|
|
return i.doCmd(IPVS_CMD_SET_DEST, syscall.NLM_F_ACK, attrs, nil)
|
|
}
|
|
|
|
// DelDestination deletes an already existing real server in the
|
|
// passed ipvs service in the passed handle.
|
|
func (i *Handle) DelDestination(s *Service, d *Destination) error {
|
|
attrs := i.fillAttrs(s, d, false, false)
|
|
return i.doCmd(IPVS_CMD_DEL_DEST, syscall.NLM_F_ACK, attrs, nil)
|
|
}
|
|
|
|
func (i *Handle) doCmd(cmd uint8, reqType uint16, attrs nlgo.AttrSlice, respHandler *ResponseHandler) error {
|
|
req := i.genlFamily.Request(cmd, reqType, nil, attrs.Bytes())
|
|
resp, err := i.genlHub.Sync(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, msg := range resp {
|
|
if msg.Header.Type == syscall.NLMSG_ERROR {
|
|
if msgErr := nlgo.NlMsgerr(msg.NetlinkMessage); msgErr.Payload().Error != 0 {
|
|
return msgErr
|
|
} else {
|
|
// ack
|
|
}
|
|
} else if msg.Header.Type == syscall.NLMSG_DONE {
|
|
// ack
|
|
|
|
} else if msg.Family == i.genlFamily {
|
|
if respHandler != nil {
|
|
if attrsValue, err := respHandler.Policy.Parse(msg.Body()); err != nil {
|
|
return fmt.Errorf("ipvs:Client.request: Invalid response: %s\n%s", err, hex.Dump(msg.Data))
|
|
} else if attrMap, ok := attrsValue.(nlgo.AttrMap); !ok {
|
|
return fmt.Errorf("ipvs:Client.request: Invalid attrs value: %v", attrsValue)
|
|
} else {
|
|
if err := respHandler.Handle(attrMap); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
fmt.Printf("Client.request: Unknown response: %+v", msg)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (i *Handle) fillAttrs(s *Service, d *Destination, sfull, dfull bool) nlgo.AttrSlice {
|
|
attrs := nlgo.AttrSlice{}
|
|
if s != nil {
|
|
attrs = append(attrs, nlattr(IPVS_CMD_ATTR_SERVICE, s.attrs(sfull)))
|
|
}
|
|
if d != nil {
|
|
attrs = append(attrs, nlattr(IPVS_CMD_ATTR_DEST, d.attrs(dfull)))
|
|
}
|
|
return attrs
|
|
}
|