2017-04-17 10:33:09 +05:30

215 lines
5.9 KiB
Go

package libipvs
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"syscall"
"github.com/hkwi/nlgo"
)
// Helper to build an nlgo.Attr
func nlattr(typ uint16, value nlgo.NlaValue) nlgo.Attr {
return nlgo.Attr{Header: syscall.NlAttr{Type: typ}, Value: value}
}
// Helpers for struct <-> nlgo.Binary
func unpack(value nlgo.Binary, out interface{}) error {
return binary.Read(bytes.NewReader(([]byte)(value)), binary.BigEndian, out)
}
func pack(in interface{}) nlgo.Binary {
var buf bytes.Buffer
if err := binary.Write(&buf, binary.BigEndian, in); err != nil {
panic(err)
}
return nlgo.Binary(buf.Bytes())
}
// Helpers for net.IP <-> nlgo.Binary
func unpackAddr(value nlgo.Binary, af AddressFamily) (net.IP, error) {
buf := ([]byte)(value)
size := 0
switch af {
case syscall.AF_INET:
size = 4
case syscall.AF_INET6:
size = 16
default:
return nil, fmt.Errorf("ipvs: unknown af=%d addr=%v", af, buf)
}
if size > len(buf) {
return nil, fmt.Errorf("ipvs: short af=%d addr=%v", af, buf)
}
return (net.IP)(buf[:size]), nil
}
func packAddr(af AddressFamily, addr net.IP) nlgo.Binary {
var ip net.IP
switch af {
case syscall.AF_INET:
ip = addr.To4()
case syscall.AF_INET6:
ip = addr.To16()
default:
panic(fmt.Errorf("ipvs:packAddr: unknown af=%d addr=%v", af, addr))
}
if ip == nil {
panic(fmt.Errorf("ipvs:packAddr: invalid af=%d addr=%v", af, addr))
}
return (nlgo.Binary)(ip)
}
// Helpers for uint16 port <-> nlgo.U16
func htons(value uint16) uint16 {
return ((value & 0x00ff) << 8) | ((value & 0xff00) >> 8)
}
func ntohs(value uint16) uint16 {
return ((value & 0x00ff) << 8) | ((value & 0xff00) >> 8)
}
func unpackPort(val nlgo.U16) uint16 {
return ntohs((uint16)(val))
}
func packPort(port uint16) nlgo.U16 {
return nlgo.U16(htons(port))
}
func unpackService(attrs nlgo.AttrMap) (Service, error) {
var service Service
var addr nlgo.Binary
var flags nlgo.Binary
for _, attr := range attrs.Slice() {
switch attr.Field() {
case IPVS_SVC_ATTR_AF:
service.AddressFamily = (AddressFamily)(attr.Value.(nlgo.U16))
case IPVS_SVC_ATTR_PROTOCOL:
service.Protocol = (Protocol)(attr.Value.(nlgo.U16))
case IPVS_SVC_ATTR_ADDR:
addr = attr.Value.(nlgo.Binary)
case IPVS_SVC_ATTR_PORT:
service.Port = unpackPort(attr.Value.(nlgo.U16))
case IPVS_SVC_ATTR_FWMARK:
service.FWMark = (uint32)(attr.Value.(nlgo.U32))
case IPVS_SVC_ATTR_SCHED_NAME:
service.SchedName = (string)(attr.Value.(nlgo.NulString))
case IPVS_SVC_ATTR_FLAGS:
flags = attr.Value.(nlgo.Binary)
case IPVS_SVC_ATTR_TIMEOUT:
service.Timeout = (uint32)(attr.Value.(nlgo.U32))
case IPVS_SVC_ATTR_NETMASK:
service.Netmask = (uint32)(attr.Value.(nlgo.U32))
case IPVS_SVC_ATTR_STATS:
service.Stats = unpackStats(attr)
}
}
// TOTE: ipvs Service with Fwmarks has no Address, so we just ignore the error
if addrIP, err := unpackAddr(addr, service.AddressFamily); err != nil && service.FWMark != 0 {
return service, fmt.Errorf("ipvs:Service.unpack: addr: %s", err)
} else {
service.Address = addrIP
}
if err := unpack(flags, &service.Flags); err != nil {
return service, fmt.Errorf("ipvs:Service.unpack: flags: %s", err)
}
return service, nil
}
func unpackDest(attrs nlgo.AttrMap) (Destination, error) {
var dest Destination
var addr []byte
for _, attr := range attrs.Slice() {
switch attr.Field() {
case IPVS_DEST_ATTR_ADDR_FAMILY:
dest.AddressFamily = (AddressFamily)(attr.Value.(nlgo.U16))
case IPVS_DEST_ATTR_ADDR:
addr = ([]byte)(attr.Value.(nlgo.Binary))
case IPVS_DEST_ATTR_PORT:
dest.Port = unpackPort(attr.Value.(nlgo.U16))
case IPVS_DEST_ATTR_FWD_METHOD:
dest.FwdMethod = (FwdMethod)(attr.Value.(nlgo.U32))
case IPVS_DEST_ATTR_WEIGHT:
dest.Weight = (uint32)(attr.Value.(nlgo.U32))
case IPVS_DEST_ATTR_U_THRESH:
dest.UThresh = (uint32)(attr.Value.(nlgo.U32))
case IPVS_DEST_ATTR_L_THRESH:
dest.LThresh = (uint32)(attr.Value.(nlgo.U32))
case IPVS_DEST_ATTR_ACTIVE_CONNS:
dest.ActiveConns = (uint32)(attr.Value.(nlgo.U32))
case IPVS_DEST_ATTR_INACT_CONNS:
dest.InactConns = (uint32)(attr.Value.(nlgo.U32))
case IPVS_DEST_ATTR_PERSIST_CONNS:
dest.PersistConns = (uint32)(attr.Value.(nlgo.U32))
case IPVS_DEST_ATTR_STATS:
dest.Stats = unpackStats(attr)
}
}
if addrIP, err := unpackAddr(addr, dest.AddressFamily); err != nil {
return dest, fmt.Errorf("ipvs:Dest.unpack: addr: %s", err)
} else {
dest.Address = addrIP
}
return dest, nil
}
func unpackInfo(attrs nlgo.AttrMap) (info Info, err error) {
for _, attr := range attrs.Slice() {
switch attr.Field() {
case IPVS_INFO_ATTR_VERSION:
info.Version = (Version)(attr.Value.(nlgo.U32))
case IPVS_INFO_ATTR_CONN_TAB_SIZE:
info.ConnTabSize = (uint32)(attr.Value.(nlgo.U32))
}
}
return
}
func unpackStats(attrs nlgo.Attr) Stats {
var stats Stats
for _, attr := range attrs.Value.(nlgo.AttrMap).Slice() {
switch attr.Field() {
case IPVS_STATS_ATTR_CONNS:
stats.Connections = (uint32)(attr.Value.(nlgo.U32))
case IPVS_STATS_ATTR_INPKTS: /* incoming packets */
stats.PacketsIn = (uint32)(attr.Value.(nlgo.U32))
case IPVS_STATS_ATTR_OUTPKTS: /* outgoing packets */
stats.PacketsOut = (uint32)(attr.Value.(nlgo.U32))
case IPVS_STATS_ATTR_INBYTES: /* incoming bytes */
stats.BytesIn = (uint64)(attr.Value.(nlgo.U64))
case IPVS_STATS_ATTR_OUTBYTES: /* outgoing bytes */
stats.BytesOut = (uint64)(attr.Value.(nlgo.U64))
case IPVS_STATS_ATTR_CPS: /* current connection rate */
stats.CPS = (uint32)(attr.Value.(nlgo.U32))
case IPVS_STATS_ATTR_INPPS: /* current in packet rate */
stats.PPSIn = (uint32)(attr.Value.(nlgo.U32))
case IPVS_STATS_ATTR_OUTPPS: /* current out packet rate */
stats.PPSOut = (uint32)(attr.Value.(nlgo.U32))
case IPVS_STATS_ATTR_INBPS: /* current in byte rate */
stats.BPSIn = (uint32)(attr.Value.(nlgo.U32))
case IPVS_STATS_ATTR_OUTBPS: /* current out byte rate */
stats.BPSOut = (uint32)(attr.Value.(nlgo.U32))
}
}
return stats
}