doukuang1950 2016-11-29 12:55
浏览 224

如何使用netlink for golang实现类似“ ipvsadm --restore”的功能

I want implement a "ipvsadm --restore" like function, leveraging on github.com/vishvananda/netlink/nl. Search on github, I find a helpful golang ipvs lib in libnetwork.

However, it just provide New/Update/Delete functions for Service and Destination.

I want a "ipvsadm --restore" like function. I am not an ipvs/netlink expert, is there anyone can kindly point out how to implement one.

Actually, I have try to implement like this, but it does not work.

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条回答 默认 最新

    报告相同问题?

    悬赏问题

    • ¥15 如何在炒股软件中,爬到我想看的日k线
    • ¥15 51单片机中C语言怎么做到下面类似的功能的函数(相关搜索:c语言)
    • ¥15 seatunnel 怎么配置Elasticsearch
    • ¥15 PSCAD安装问题 ERROR: Visual Studio 2013, 2015, 2017 or 2019 is not found in the system.
    • ¥15 (标签-MATLAB|关键词-多址)
    • ¥15 关于#MATLAB#的问题,如何解决?(相关搜索:信噪比,系统容量)
    • ¥500 52810做蓝牙接受端
    • ¥15 基于PLC的三轴机械手程序
    • ¥15 多址通信方式的抗噪声性能和系统容量对比
    • ¥15 winform的chart曲线生成时有凸起