如何使用netlink为golang实现类似函数的“ipvsadm --restore”

时间:2016-11-29 12:55:23

标签: go netlink

我希望在github.com/vishvananda/netlink/nl上实现一个类似函数的“ipvsadm --restore”。在github上搜索,我在libnetwork找到了一个有用的golang ipvs lib。

但是,它只为服务和目标提供新的/更新/删除功能。

我想要一个像“ipvsadm --restore”这样的函数。我不是ipvs / netlink专家,有没有人可以指出如何实现一个。

实际上,我试图像这样实现,但它不起作用。

package ipvs

import (
    "net"
    "syscall"
    "fmt"
    "os/exec"
    "strings"
    "github.com/vishvananda/netlink/nl"
    "bytes"
    "encoding/binary"
)

// Service defines an IPVS service in its entirety.
type Service struct {
    // Virtual service address.
    Address  net.IP
    Protocol uint16
    Port     uint16
    FWMark   uint32 // Firewall mark of the service.

    // Virtual service options.
    SchedName     string
    Flags         uint32
    Timeout       uint32
    Netmask       uint32
    AddressFamily uint16
    PEName        string
}

// Destination defines an IPVS destination (real server) in its
// entirety.
type Destination struct {
    Address         net.IP
    Port            uint16
    Weight          int
    ConnectionFlags uint32
    AddressFamily   uint16
    UpperThreshold  uint32
    LowerThreshold  uint32
}


type ServiceDestination struct {
    Service *Service
    Destinations []*Destination
}

// Handle provides an ipvs handle to program ipvs rules.
type IPVSHandle struct {
    ipvsFamily int
}

// NewIPVSHandler provides a new ipvs handler
func NewIPVSHandle() (*IPVSHandle, error) {
    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)
    }

    ipvsFamily, err := getIPVSFamily()
    if err != nil {
        return nil, fmt.Errorf("Could not get ipvs family information from the kernel. It is possible that ipvs is not enabled in your kernel.")
    }

    return &IPVSHandle{ipvsFamily: ipvsFamily}, nil
}

func ( i *IPVSHandle) Restore(items []ServiceDestination)([][]byte, error) {
    req := nl.NewNetlinkRequest(i.ipvsFamily, syscall.NLM_F_REPLACE)
    req.AddData(&genlMsgHdr{cmd: ipvsCmdSetConfig, version: 1})


    listAttr := nl.NewRtAttr(ipvsCmdAttrUnspec, nl.Uint16Attr(uint16(i.ipvsFamily)))

    for _, item := range  items {
        itemAttr := nl.NewRtAttr(ipvsCmdAttrUnspec, nl.Uint16Attr(uint16(i.ipvsFamily)))

        srvAttr := toServiceAttr(item.Service)
        nl.NewRtAttrChild(itemAttr, ipvsCmdAttrService, srvAttr.Serialize())

        for _, d := range item.Destinations {
            nl.NewRtAttrChild(itemAttr, ipvsCmdAttrDest, fillDestinaton(d).Serialize())
        }

        nl.NewRtAttrChild(listAttr, ipvsCmdAttrUnspec, itemAttr.Serialize())
    }

    req.AddData(listAttr)
    return req.Execute(syscall.NETLINK_GENERIC, 0)
}

func toServiceAttr(s *Service) *nl.RtAttr {
    cmdAttr := nl.NewRtAttr(ipvsCmdAttrService, nil)
    nl.NewRtAttrChild(cmdAttr, ipvsSvcAttrAddressFamily, nl.Uint16Attr(s.AddressFamily))
    if s.FWMark != 0 {
        nl.NewRtAttrChild(cmdAttr, ipvsSvcAttrFWMark, nl.Uint32Attr(s.FWMark))
    } else {
        nl.NewRtAttrChild(cmdAttr, ipvsSvcAttrProtocol, nl.Uint16Attr(s.Protocol))
        nl.NewRtAttrChild(cmdAttr, ipvsSvcAttrAddress, rawIPData(s.Address))

        // Port needs to be in network byte order.
        portBuf := new(bytes.Buffer)
        binary.Write(portBuf, binary.BigEndian, s.Port)
        nl.NewRtAttrChild(cmdAttr, ipvsSvcAttrPort, portBuf.Bytes())
    }

    nl.NewRtAttrChild(cmdAttr, ipvsSvcAttrSchedName, nl.ZeroTerminated(s.SchedName))
    if s.PEName != "" {
        nl.NewRtAttrChild(cmdAttr, ipvsSvcAttrPEName, nl.ZeroTerminated(s.PEName))
    }

    f := &ipvsFlags{
        flags: s.Flags,
        mask:  0xFFFFFFFF,
    }
    nl.NewRtAttrChild(cmdAttr, ipvsSvcAttrFlags, f.Serialize())
    nl.NewRtAttrChild(cmdAttr, ipvsSvcAttrTimeout, nl.Uint32Attr(s.Timeout))
    nl.NewRtAttrChild(cmdAttr, ipvsSvcAttrNetmask, nl.Uint32Attr(s.Netmask))
    return cmdAttr
}

func fillDestinaton(d *Destination) nl.NetlinkRequestData {
    cmdAttr := nl.NewRtAttr(ipvsCmdAttrDest, nil)

    nl.NewRtAttrChild(cmdAttr, ipvsDestAttrAddress, rawIPData(d.Address))
    // Port needs to be in network byte order.
    portBuf := new(bytes.Buffer)
    binary.Write(portBuf, binary.BigEndian, d.Port)
    nl.NewRtAttrChild(cmdAttr, ipvsDestAttrPort, portBuf.Bytes())

    nl.NewRtAttrChild(cmdAttr, ipvsDestAttrForwardingMethod, nl.Uint32Attr(d.ConnectionFlags&ConnectionFlagFwdMask))
    nl.NewRtAttrChild(cmdAttr, ipvsDestAttrWeight, nl.Uint32Attr(uint32(d.Weight)))
    nl.NewRtAttrChild(cmdAttr, ipvsDestAttrUpperThreshold, nl.Uint32Attr(d.UpperThreshold))
    nl.NewRtAttrChild(cmdAttr, ipvsDestAttrLowerThreshold, nl.Uint32Attr(d.LowerThreshold))

    return cmdAttr
}

0 个答案:

没有答案