From d9e8a40f0b83ec31573d34bb20f5a85f859ad636 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 9 Oct 2026 00:07:14 +0200 Subject: [PATCH 1/6] document IPVS netlink constants and use explicit values Replace iota-based constant definitions with explicit values matching the Linux kernel's IPVS generic netlink interface. Add comments identifying the corresponding kernel constants and links to their definitions in the Linux UAPI headers. This makes it easier to verify the values against the kernel and prevents accidental changes to protocol values when constants are added or reordered. Signed-off-by: Sebastiaan van Stijn --- constants_linux.go | 162 +++++++++++++++++++++++---------------------- 1 file changed, 84 insertions(+), 78 deletions(-) diff --git a/constants_linux.go b/constants_linux.go index 592a0ee..4fec775 100644 --- a/constants_linux.go +++ b/constants_linux.go @@ -1,105 +1,111 @@ package ipvs +import "golang.org/x/sys/unix" + const ( - genlCtrlID = 0x10 + genlCtrlID = unix.GENL_ID_CTRL ) -// GENL control commands +// Generic netlink controller constants. +// See https://github.com/torvalds/linux/blob/v7.2/include/uapi/linux/genetlink.h#L36-L53 const ( - genlCtrlCmdUnspec uint8 = iota - genlCtrlCmdNewFamily - genlCtrlCmdDelFamily - genlCtrlCmdGetFamily + genlCtrlCmdUnspec = unix.CTRL_CMD_UNSPEC + genlCtrlCmdNewFamily = unix.CTRL_CMD_NEWFAMILY + genlCtrlCmdDelFamily = unix.CTRL_CMD_DELFAMILY + genlCtrlCmdGetFamily = unix.CTRL_CMD_GETFAMILY ) -// GENL family attributes +// Generic netlink family attributes. +// See https://github.com/torvalds/linux/blob/v7.2/include/uapi/linux/genetlink.h#L57-L70 const ( - genlCtrlAttrUnspec int = iota - genlCtrlAttrFamilyID - genlCtrlAttrFamilyName + genlCtrlAttrUnspec = unix.CTRL_ATTR_UNSPEC + genlCtrlAttrFamilyID = unix.CTRL_ATTR_FAMILY_ID + genlCtrlAttrFamilyName = unix.CTRL_ATTR_FAMILY_NAME ) -// IPVS genl commands +// IPVS generic netlink commands. +// See https://github.com/torvalds/linux/blob/v7.2/include/uapi/linux/ip_vs.h#L301-L329 const ( - ipvsCmdUnspec uint8 = iota - ipvsCmdNewService - ipvsCmdSetService - ipvsCmdDelService - ipvsCmdGetService - ipvsCmdNewDest - ipvsCmdSetDest - ipvsCmdDelDest - ipvsCmdGetDest - ipvsCmdNewDaemon - ipvsCmdDelDaemon - ipvsCmdGetDaemon - ipvsCmdSetConfig - ipvsCmdGetConfig - ipvsCmdSetInfo - ipvsCmdGetInfo - ipvsCmdZero - ipvsCmdFlush + ipvsCmdUnspec = 0 // IPVS_CMD_UNSPEC + ipvsCmdNewService = 1 // IPVS_CMD_NEW_SERVICE + ipvsCmdSetService = 2 // IPVS_CMD_SET_SERVICE + ipvsCmdDelService = 3 // IPVS_CMD_DEL_SERVICE + ipvsCmdGetService = 4 // IPVS_CMD_GET_SERVICE + ipvsCmdNewDest = 5 // IPVS_CMD_NEW_DEST + ipvsCmdSetDest = 6 // IPVS_CMD_SET_DEST + ipvsCmdDelDest = 7 // IPVS_CMD_DEL_DEST + ipvsCmdGetDest = 8 // IPVS_CMD_GET_DEST + ipvsCmdNewDaemon = 9 // IPVS_CMD_NEW_DAEMON + ipvsCmdDelDaemon = 10 // IPVS_CMD_DEL_DAEMON + ipvsCmdGetDaemon = 11 // IPVS_CMD_GET_DAEMON + ipvsCmdSetConfig = 12 // IPVS_CMD_SET_CONFIG + ipvsCmdGetConfig = 13 // IPVS_CMD_GET_CONFIG + ipvsCmdSetInfo = 14 // IPVS_CMD_SET_INFO + ipvsCmdGetInfo = 15 // IPVS_CMD_GET_INFO + ipvsCmdZero = 16 // IPVS_CMD_ZERO + ipvsCmdFlush = 17 // IPVS_CMD_FLUSH ) -// Attributes used in the first level of commands +// IPVS generic netlink command attributes. +// See https://github.com/torvalds/linux/blob/v7.2/include/uapi/linux/ip_vs.h#L333-L343 const ( - ipvsCmdAttrUnspec int = iota - ipvsCmdAttrService - ipvsCmdAttrDest - ipvsCmdAttrDaemon - ipvsCmdAttrTimeoutTCP - ipvsCmdAttrTimeoutTCPFin - ipvsCmdAttrTimeoutUDP + ipvsCmdAttrUnspec = 0 // IPVS_CMD_ATTR_UNSPEC + ipvsCmdAttrService = 1 // IPVS_CMD_ATTR_SERVICE + ipvsCmdAttrDest = 2 // IPVS_CMD_ATTR_DEST + ipvsCmdAttrDaemon = 3 // IPVS_CMD_ATTR_DAEMON + ipvsCmdAttrTimeoutTCP = 4 // IPVS_CMD_ATTR_TIMEOUT_TCP + ipvsCmdAttrTimeoutTCPFin = 5 // IPVS_CMD_ATTR_TIMEOUT_TCP_FIN + ipvsCmdAttrTimeoutUDP = 6 // IPVS_CMD_ATTR_TIMEOUT_UDP ) -// Attributes used to describe a service. Used inside nested attribute -// ipvsCmdAttrService +// IPVS service attributes, nested in IPVS_CMD_ATTR_SERVICE. +// See https://github.com/torvalds/linux/blob/v7.2/include/uapi/linux/ip_vs.h#L347-L372 const ( - ipvsSvcAttrUnspec int = iota - ipvsSvcAttrAddressFamily - ipvsSvcAttrProtocol - ipvsSvcAttrAddress - ipvsSvcAttrPort - ipvsSvcAttrFWMark - ipvsSvcAttrSchedName - ipvsSvcAttrFlags - ipvsSvcAttrTimeout - ipvsSvcAttrNetmask - ipvsSvcAttrStats - ipvsSvcAttrPEName + ipvsSvcAttrUnspec = 0 // IPVS_SVC_ATTR_UNSPEC + ipvsSvcAttrAddressFamily = 1 // IPVS_SVC_ATTR_AF + ipvsSvcAttrProtocol = 2 // IPVS_SVC_ATTR_PROTOCOL + ipvsSvcAttrAddress = 3 // IPVS_SVC_ATTR_ADDR + ipvsSvcAttrPort = 4 // IPVS_SVC_ATTR_PORT + ipvsSvcAttrFWMark = 5 // IPVS_SVC_ATTR_FWMARK + ipvsSvcAttrSchedName = 6 // IPVS_SVC_ATTR_SCHED_NAME + ipvsSvcAttrFlags = 7 // IPVS_SVC_ATTR_FLAGS + ipvsSvcAttrTimeout = 8 // IPVS_SVC_ATTR_TIMEOUT + ipvsSvcAttrNetmask = 9 // IPVS_SVC_ATTR_NETMASK + ipvsSvcAttrStats = 10 // IPVS_SVC_ATTR_STATS + ipvsSvcAttrPEName = 11 // IPVS_SVC_ATTR_PE_NAME ) -// Attributes used to describe a destination (real server). Used -// inside nested attribute ipvsCmdAttrDest. +// IPVS destination attributes, nested in IPVS_CMD_ATTR_DEST. +// See https://github.com/torvalds/linux/blob/v7.2/include/uapi/linux/ip_vs.h#L376-L409 const ( - ipvsDestAttrUnspec int = iota - ipvsDestAttrAddress - ipvsDestAttrPort - ipvsDestAttrForwardingMethod - ipvsDestAttrWeight - ipvsDestAttrUpperThreshold - ipvsDestAttrLowerThreshold - ipvsDestAttrActiveConnections - ipvsDestAttrInactiveConnections - ipvsDestAttrPersistentConnections - ipvsDestAttrStats - ipvsDestAttrAddressFamily + ipvsDestAttrUnspec = 0 // IPVS_DEST_ATTR_UNSPEC + ipvsDestAttrAddress = 1 // IPVS_DEST_ATTR_ADDR + ipvsDestAttrPort = 2 // IPVS_DEST_ATTR_PORT + ipvsDestAttrForwardingMethod = 3 // IPVS_DEST_ATTR_FWD_METHOD + ipvsDestAttrWeight = 4 // IPVS_DEST_ATTR_WEIGHT + ipvsDestAttrUpperThreshold = 5 // IPVS_DEST_ATTR_U_THRESH + ipvsDestAttrLowerThreshold = 6 // IPVS_DEST_ATTR_L_THRESH + ipvsDestAttrActiveConnections = 7 // IPVS_DEST_ATTR_ACTIVE_CONNS + ipvsDestAttrInactiveConnections = 8 // IPVS_DEST_ATTR_INACT_CONNS + ipvsDestAttrPersistentConnections = 9 // IPVS_DEST_ATTR_PERSIST_CONNS + ipvsDestAttrStats = 10 // IPVS_DEST_ATTR_STATS + ipvsDestAttrAddressFamily = 11 // IPVS_DEST_ATTR_ADDR_FAMILY ) -// IPVS Statistics constants - +// IPVS statistics attributes. +// See https://github.com/torvalds/linux/blob/v7.2/include/uapi/linux/ip_vs.h#L433-L454 const ( - ipvsStatsUnspec int = iota - ipvsStatsConns - ipvsStatsPktsIn - ipvsStatsPktsOut - ipvsStatsBytesIn - ipvsStatsBytesOut - ipvsStatsCPS - ipvsStatsPPSIn - ipvsStatsPPSOut - ipvsStatsBPSIn - ipvsStatsBPSOut + ipvsStatsUnspec = 0 // IPVS_STATS_ATTR_UNSPEC + ipvsStatsConns = 1 // IPVS_STATS_ATTR_CONNS + ipvsStatsPktsIn = 2 // IPVS_STATS_ATTR_INPKTS + ipvsStatsPktsOut = 3 // IPVS_STATS_ATTR_OUTPKTS + ipvsStatsBytesIn = 4 // IPVS_STATS_ATTR_INBYTES + ipvsStatsBytesOut = 5 // IPVS_STATS_ATTR_OUTBYTES + ipvsStatsCPS = 6 // IPVS_STATS_ATTR_CPS + ipvsStatsPPSIn = 7 // IPVS_STATS_ATTR_INPPS + ipvsStatsPPSOut = 8 // IPVS_STATS_ATTR_OUTPPS + ipvsStatsBPSIn = 9 // IPVS_STATS_ATTR_INBPS + ipvsStatsBPSOut = 10 // IPVS_STATS_ATTR_OUTBPS ) // Destination forwarding methods From 13f5f711e8cde1df3cf1346b5625cfbf9c424da0 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 9 Oct 2026 00:28:11 +0200 Subject: [PATCH 2/6] define a distinct type for IPVS netlink commands Introduce an ipvsCmd type for IPVS generic netlink commands and update the command helpers to use it. This groups the command constants under a distinct type, allowing linters such as exhaustive to detect missing cases in switches and providing stronger type checking. Keep the generic netlink request helper accepting uint8, as it is also used for commands outside the IPVS family. Signed-off-by: Sebastiaan van Stijn --- constants_linux.go | 38 ++++++++++++++++++++------------------ netlink_linux.go | 10 +++++----- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/constants_linux.go b/constants_linux.go index 4fec775..6953823 100644 --- a/constants_linux.go +++ b/constants_linux.go @@ -25,25 +25,27 @@ const ( // IPVS generic netlink commands. // See https://github.com/torvalds/linux/blob/v7.2/include/uapi/linux/ip_vs.h#L301-L329 +type ipvsCmd uint8 + const ( - ipvsCmdUnspec = 0 // IPVS_CMD_UNSPEC - ipvsCmdNewService = 1 // IPVS_CMD_NEW_SERVICE - ipvsCmdSetService = 2 // IPVS_CMD_SET_SERVICE - ipvsCmdDelService = 3 // IPVS_CMD_DEL_SERVICE - ipvsCmdGetService = 4 // IPVS_CMD_GET_SERVICE - ipvsCmdNewDest = 5 // IPVS_CMD_NEW_DEST - ipvsCmdSetDest = 6 // IPVS_CMD_SET_DEST - ipvsCmdDelDest = 7 // IPVS_CMD_DEL_DEST - ipvsCmdGetDest = 8 // IPVS_CMD_GET_DEST - ipvsCmdNewDaemon = 9 // IPVS_CMD_NEW_DAEMON - ipvsCmdDelDaemon = 10 // IPVS_CMD_DEL_DAEMON - ipvsCmdGetDaemon = 11 // IPVS_CMD_GET_DAEMON - ipvsCmdSetConfig = 12 // IPVS_CMD_SET_CONFIG - ipvsCmdGetConfig = 13 // IPVS_CMD_GET_CONFIG - ipvsCmdSetInfo = 14 // IPVS_CMD_SET_INFO - ipvsCmdGetInfo = 15 // IPVS_CMD_GET_INFO - ipvsCmdZero = 16 // IPVS_CMD_ZERO - ipvsCmdFlush = 17 // IPVS_CMD_FLUSH + ipvsCmdUnspec ipvsCmd = 0 // IPVS_CMD_UNSPEC + ipvsCmdNewService ipvsCmd = 1 // IPVS_CMD_NEW_SERVICE + ipvsCmdSetService ipvsCmd = 2 // IPVS_CMD_SET_SERVICE + ipvsCmdDelService ipvsCmd = 3 // IPVS_CMD_DEL_SERVICE + ipvsCmdGetService ipvsCmd = 4 // IPVS_CMD_GET_SERVICE + ipvsCmdNewDest ipvsCmd = 5 // IPVS_CMD_NEW_DEST + ipvsCmdSetDest ipvsCmd = 6 // IPVS_CMD_SET_DEST + ipvsCmdDelDest ipvsCmd = 7 // IPVS_CMD_DEL_DEST + ipvsCmdGetDest ipvsCmd = 8 // IPVS_CMD_GET_DEST + ipvsCmdNewDaemon ipvsCmd = 9 // IPVS_CMD_NEW_DAEMON + ipvsCmdDelDaemon ipvsCmd = 10 // IPVS_CMD_DEL_DAEMON + ipvsCmdGetDaemon ipvsCmd = 11 // IPVS_CMD_GET_DAEMON + ipvsCmdSetConfig ipvsCmd = 12 // IPVS_CMD_SET_CONFIG + ipvsCmdGetConfig ipvsCmd = 13 // IPVS_CMD_GET_CONFIG + ipvsCmdSetInfo ipvsCmd = 14 // IPVS_CMD_SET_INFO + ipvsCmdGetInfo ipvsCmd = 15 // IPVS_CMD_GET_INFO + ipvsCmdZero ipvsCmd = 16 // IPVS_CMD_ZERO + ipvsCmdFlush ipvsCmd = 17 // IPVS_CMD_FLUSH ) // IPVS generic netlink command attributes. diff --git a/netlink_linux.go b/netlink_linux.go index c811141..da7c707 100644 --- a/netlink_linux.go +++ b/netlink_linux.go @@ -132,7 +132,7 @@ func fillDestination(d *Destination) (nl.NetlinkRequestData, error) { return cmdAttr, nil } -func (i *Handle) doCmdwithResponse(s *Service, d *Destination, cmd uint8) ([][]byte, error) { +func (i *Handle) doCmdwithResponse(s *Service, d *Destination, cmd ipvsCmd) ([][]byte, error) { req := newIPVSRequest(cmd) req.Seq = atomic.AddUint32(&i.seq, 1) @@ -163,7 +163,7 @@ func (i *Handle) doCmdwithResponse(s *Service, d *Destination, cmd uint8) ([][]b return res, nil } -func (i *Handle) doCmd(s *Service, d *Destination, cmd uint8) error { +func (i *Handle) doCmd(s *Service, d *Destination, cmd ipvsCmd) error { _, err := i.doCmdwithResponse(s, d, cmd) return err @@ -216,8 +216,8 @@ func rawIPData(ip net.IP) []byte { return ip } -func newIPVSRequest(cmd uint8) *nl.NetlinkRequest { - return newGenlRequest(ipvsFamily, cmd) +func newIPVSRequest(cmd ipvsCmd) *nl.NetlinkRequest { + return newGenlRequest(ipvsFamily, uint8(cmd)) } func newGenlRequest(familyID int, cmd uint8) *nl.NetlinkRequest { @@ -437,7 +437,7 @@ func (i *Handle) doGetServicesCmd(svc *Service) ([]*Service, error) { } // doCmdWithoutAttr a simple wrapper of netlink socket execute command -func (i *Handle) doCmdWithoutAttr(cmd uint8) ([][]byte, error) { +func (i *Handle) doCmdWithoutAttr(cmd ipvsCmd) ([][]byte, error) { req := newIPVSRequest(cmd) req.Seq = atomic.AddUint32(&i.seq, 1) return execute(i.sock, req) From 41ce5bd5d2f520a21ef3136ac5ca98586d324b66 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 9 Oct 2026 01:39:29 +0200 Subject: [PATCH 3/6] define distinct types for IPVS netlink attributes Introduce dedicated types for IPVS command, service, destination, and statistics attributes, using int as the underlying type to match the netlink package's API. Update the encoding and decoding paths to use these types, with explicit conversions at the netlink API boundaries. This improves type safety and allows linters such as exhaustive to detect missing attribute cases in switch statements. The attribute values and wire format remain unchanged. Signed-off-by: Sebastiaan van Stijn --- constants_linux.go | 92 +++++++++++++++++++++++++--------------------- netlink_linux.go | 54 +++++++++++++-------------- 2 files changed, 77 insertions(+), 69 deletions(-) diff --git a/constants_linux.go b/constants_linux.go index 6953823..301dd36 100644 --- a/constants_linux.go +++ b/constants_linux.go @@ -50,64 +50,72 @@ const ( // IPVS generic netlink command attributes. // See https://github.com/torvalds/linux/blob/v7.2/include/uapi/linux/ip_vs.h#L333-L343 +type ipvsCmdAttr int + const ( - ipvsCmdAttrUnspec = 0 // IPVS_CMD_ATTR_UNSPEC - ipvsCmdAttrService = 1 // IPVS_CMD_ATTR_SERVICE - ipvsCmdAttrDest = 2 // IPVS_CMD_ATTR_DEST - ipvsCmdAttrDaemon = 3 // IPVS_CMD_ATTR_DAEMON - ipvsCmdAttrTimeoutTCP = 4 // IPVS_CMD_ATTR_TIMEOUT_TCP - ipvsCmdAttrTimeoutTCPFin = 5 // IPVS_CMD_ATTR_TIMEOUT_TCP_FIN - ipvsCmdAttrTimeoutUDP = 6 // IPVS_CMD_ATTR_TIMEOUT_UDP + ipvsCmdAttrUnspec ipvsCmdAttr = 0 // IPVS_CMD_ATTR_UNSPEC + ipvsCmdAttrService ipvsCmdAttr = 1 // IPVS_CMD_ATTR_SERVICE + ipvsCmdAttrDest ipvsCmdAttr = 2 // IPVS_CMD_ATTR_DEST + ipvsCmdAttrDaemon ipvsCmdAttr = 3 // IPVS_CMD_ATTR_DAEMON + ipvsCmdAttrTimeoutTCP ipvsCmdAttr = 4 // IPVS_CMD_ATTR_TIMEOUT_TCP + ipvsCmdAttrTimeoutTCPFin ipvsCmdAttr = 5 // IPVS_CMD_ATTR_TIMEOUT_TCP_FIN + ipvsCmdAttrTimeoutUDP ipvsCmdAttr = 6 // IPVS_CMD_ATTR_TIMEOUT_UDP ) // IPVS service attributes, nested in IPVS_CMD_ATTR_SERVICE. // See https://github.com/torvalds/linux/blob/v7.2/include/uapi/linux/ip_vs.h#L347-L372 +type ipvsSvcAttr int + const ( - ipvsSvcAttrUnspec = 0 // IPVS_SVC_ATTR_UNSPEC - ipvsSvcAttrAddressFamily = 1 // IPVS_SVC_ATTR_AF - ipvsSvcAttrProtocol = 2 // IPVS_SVC_ATTR_PROTOCOL - ipvsSvcAttrAddress = 3 // IPVS_SVC_ATTR_ADDR - ipvsSvcAttrPort = 4 // IPVS_SVC_ATTR_PORT - ipvsSvcAttrFWMark = 5 // IPVS_SVC_ATTR_FWMARK - ipvsSvcAttrSchedName = 6 // IPVS_SVC_ATTR_SCHED_NAME - ipvsSvcAttrFlags = 7 // IPVS_SVC_ATTR_FLAGS - ipvsSvcAttrTimeout = 8 // IPVS_SVC_ATTR_TIMEOUT - ipvsSvcAttrNetmask = 9 // IPVS_SVC_ATTR_NETMASK - ipvsSvcAttrStats = 10 // IPVS_SVC_ATTR_STATS - ipvsSvcAttrPEName = 11 // IPVS_SVC_ATTR_PE_NAME + ipvsSvcAttrUnspec ipvsSvcAttr = 0 // IPVS_SVC_ATTR_UNSPEC + ipvsSvcAttrAddressFamily ipvsSvcAttr = 1 // IPVS_SVC_ATTR_AF + ipvsSvcAttrProtocol ipvsSvcAttr = 2 // IPVS_SVC_ATTR_PROTOCOL + ipvsSvcAttrAddress ipvsSvcAttr = 3 // IPVS_SVC_ATTR_ADDR + ipvsSvcAttrPort ipvsSvcAttr = 4 // IPVS_SVC_ATTR_PORT + ipvsSvcAttrFWMark ipvsSvcAttr = 5 // IPVS_SVC_ATTR_FWMARK + ipvsSvcAttrSchedName ipvsSvcAttr = 6 // IPVS_SVC_ATTR_SCHED_NAME + ipvsSvcAttrFlags ipvsSvcAttr = 7 // IPVS_SVC_ATTR_FLAGS + ipvsSvcAttrTimeout ipvsSvcAttr = 8 // IPVS_SVC_ATTR_TIMEOUT + ipvsSvcAttrNetmask ipvsSvcAttr = 9 // IPVS_SVC_ATTR_NETMASK + ipvsSvcAttrStats ipvsSvcAttr = 10 // IPVS_SVC_ATTR_STATS + ipvsSvcAttrPEName ipvsSvcAttr = 11 // IPVS_SVC_ATTR_PE_NAME ) // IPVS destination attributes, nested in IPVS_CMD_ATTR_DEST. // See https://github.com/torvalds/linux/blob/v7.2/include/uapi/linux/ip_vs.h#L376-L409 +type ipvsDestAttr int + const ( - ipvsDestAttrUnspec = 0 // IPVS_DEST_ATTR_UNSPEC - ipvsDestAttrAddress = 1 // IPVS_DEST_ATTR_ADDR - ipvsDestAttrPort = 2 // IPVS_DEST_ATTR_PORT - ipvsDestAttrForwardingMethod = 3 // IPVS_DEST_ATTR_FWD_METHOD - ipvsDestAttrWeight = 4 // IPVS_DEST_ATTR_WEIGHT - ipvsDestAttrUpperThreshold = 5 // IPVS_DEST_ATTR_U_THRESH - ipvsDestAttrLowerThreshold = 6 // IPVS_DEST_ATTR_L_THRESH - ipvsDestAttrActiveConnections = 7 // IPVS_DEST_ATTR_ACTIVE_CONNS - ipvsDestAttrInactiveConnections = 8 // IPVS_DEST_ATTR_INACT_CONNS - ipvsDestAttrPersistentConnections = 9 // IPVS_DEST_ATTR_PERSIST_CONNS - ipvsDestAttrStats = 10 // IPVS_DEST_ATTR_STATS - ipvsDestAttrAddressFamily = 11 // IPVS_DEST_ATTR_ADDR_FAMILY + ipvsDestAttrUnspec ipvsDestAttr = 0 // IPVS_DEST_ATTR_UNSPEC + ipvsDestAttrAddress ipvsDestAttr = 1 // IPVS_DEST_ATTR_ADDR + ipvsDestAttrPort ipvsDestAttr = 2 // IPVS_DEST_ATTR_PORT + ipvsDestAttrForwardingMethod ipvsDestAttr = 3 // IPVS_DEST_ATTR_FWD_METHOD + ipvsDestAttrWeight ipvsDestAttr = 4 // IPVS_DEST_ATTR_WEIGHT + ipvsDestAttrUpperThreshold ipvsDestAttr = 5 // IPVS_DEST_ATTR_U_THRESH + ipvsDestAttrLowerThreshold ipvsDestAttr = 6 // IPVS_DEST_ATTR_L_THRESH + ipvsDestAttrActiveConnections ipvsDestAttr = 7 // IPVS_DEST_ATTR_ACTIVE_CONNS + ipvsDestAttrInactiveConnections ipvsDestAttr = 8 // IPVS_DEST_ATTR_INACT_CONNS + ipvsDestAttrPersistentConnections ipvsDestAttr = 9 // IPVS_DEST_ATTR_PERSIST_CONNS + ipvsDestAttrStats ipvsDestAttr = 10 // IPVS_DEST_ATTR_STATS + ipvsDestAttrAddressFamily ipvsDestAttr = 11 // IPVS_DEST_ATTR_ADDR_FAMILY ) // IPVS statistics attributes. // See https://github.com/torvalds/linux/blob/v7.2/include/uapi/linux/ip_vs.h#L433-L454 +type ipvsStats int + const ( - ipvsStatsUnspec = 0 // IPVS_STATS_ATTR_UNSPEC - ipvsStatsConns = 1 // IPVS_STATS_ATTR_CONNS - ipvsStatsPktsIn = 2 // IPVS_STATS_ATTR_INPKTS - ipvsStatsPktsOut = 3 // IPVS_STATS_ATTR_OUTPKTS - ipvsStatsBytesIn = 4 // IPVS_STATS_ATTR_INBYTES - ipvsStatsBytesOut = 5 // IPVS_STATS_ATTR_OUTBYTES - ipvsStatsCPS = 6 // IPVS_STATS_ATTR_CPS - ipvsStatsPPSIn = 7 // IPVS_STATS_ATTR_INPPS - ipvsStatsPPSOut = 8 // IPVS_STATS_ATTR_OUTPPS - ipvsStatsBPSIn = 9 // IPVS_STATS_ATTR_INBPS - ipvsStatsBPSOut = 10 // IPVS_STATS_ATTR_OUTBPS + ipvsStatsUnspec ipvsStats = 0 // IPVS_STATS_ATTR_UNSPEC + ipvsStatsConns ipvsStats = 1 // IPVS_STATS_ATTR_CONNS + ipvsStatsPktsIn ipvsStats = 2 // IPVS_STATS_ATTR_INPKTS + ipvsStatsPktsOut ipvsStats = 3 // IPVS_STATS_ATTR_OUTPKTS + ipvsStatsBytesIn ipvsStats = 4 // IPVS_STATS_ATTR_INBYTES + ipvsStatsBytesOut ipvsStats = 5 // IPVS_STATS_ATTR_OUTBYTES + ipvsStatsCPS ipvsStats = 6 // IPVS_STATS_ATTR_CPS + ipvsStatsPPSIn ipvsStats = 7 // IPVS_STATS_ATTR_INPPS + ipvsStatsPPSOut ipvsStats = 8 // IPVS_STATS_ATTR_OUTPPS + ipvsStatsBPSIn ipvsStats = 9 // IPVS_STATS_ATTR_INBPS + ipvsStatsBPSOut ipvsStats = 10 // IPVS_STATS_ATTR_OUTBPS ) // Destination forwarding methods diff --git a/netlink_linux.go b/netlink_linux.go index da7c707..91d4d4e 100644 --- a/netlink_linux.go +++ b/netlink_linux.go @@ -85,31 +85,31 @@ func setup() { } func fillService(s *Service) nl.NetlinkRequestData { - cmdAttr := nl.NewRtAttr(ipvsCmdAttrService, nil) - cmdAttr.AddRtAttr(ipvsSvcAttrAddressFamily, nl.Uint16Attr(s.AddressFamily)) + cmdAttr := nl.NewRtAttr(int(ipvsCmdAttrService), nil) + cmdAttr.AddRtAttr(int(ipvsSvcAttrAddressFamily), nl.Uint16Attr(s.AddressFamily)) if s.FWMark != 0 { - cmdAttr.AddRtAttr(ipvsSvcAttrFWMark, nl.Uint32Attr(s.FWMark)) + cmdAttr.AddRtAttr(int(ipvsSvcAttrFWMark), nl.Uint32Attr(s.FWMark)) } else { - cmdAttr.AddRtAttr(ipvsSvcAttrProtocol, nl.Uint16Attr(s.Protocol)) - cmdAttr.AddRtAttr(ipvsSvcAttrAddress, rawIPData(s.Address)) + cmdAttr.AddRtAttr(int(ipvsSvcAttrProtocol), nl.Uint16Attr(s.Protocol)) + cmdAttr.AddRtAttr(int(ipvsSvcAttrAddress), rawIPData(s.Address)) // Port needs to be in network byte order. var portBuf bytes.Buffer _ = binary.Write(&portBuf, binary.BigEndian, s.Port) - cmdAttr.AddRtAttr(ipvsSvcAttrPort, portBuf.Bytes()) + cmdAttr.AddRtAttr(int(ipvsSvcAttrPort), portBuf.Bytes()) } - cmdAttr.AddRtAttr(ipvsSvcAttrSchedName, nl.ZeroTerminated(s.SchedName)) + cmdAttr.AddRtAttr(int(ipvsSvcAttrSchedName), nl.ZeroTerminated(s.SchedName)) if s.PEName != "" { - cmdAttr.AddRtAttr(ipvsSvcAttrPEName, nl.ZeroTerminated(s.PEName)) + cmdAttr.AddRtAttr(int(ipvsSvcAttrPEName), nl.ZeroTerminated(s.PEName)) } f := &ipvsFlags{ flags: s.Flags, mask: 0xFFFFFFFF, } - cmdAttr.AddRtAttr(ipvsSvcAttrFlags, f.Serialize()) - cmdAttr.AddRtAttr(ipvsSvcAttrTimeout, nl.Uint32Attr(s.Timeout)) - cmdAttr.AddRtAttr(ipvsSvcAttrNetmask, nl.Uint32Attr(s.Netmask)) + cmdAttr.AddRtAttr(int(ipvsSvcAttrFlags), f.Serialize()) + cmdAttr.AddRtAttr(int(ipvsSvcAttrTimeout), nl.Uint32Attr(s.Timeout)) + cmdAttr.AddRtAttr(int(ipvsSvcAttrNetmask), nl.Uint32Attr(s.Netmask)) return cmdAttr } @@ -117,17 +117,17 @@ func fillDestination(d *Destination) (nl.NetlinkRequestData, error) { if d.Weight < 0 || d.Weight > math.MaxInt32 { return nil, fmt.Errorf("destination weight out of range: %d", d.Weight) } - cmdAttr := nl.NewRtAttr(ipvsCmdAttrDest, nil) + cmdAttr := nl.NewRtAttr(int(ipvsCmdAttrDest), nil) - cmdAttr.AddRtAttr(ipvsDestAttrAddress, rawIPData(d.Address)) + cmdAttr.AddRtAttr(int(ipvsDestAttrAddress), rawIPData(d.Address)) // Port needs to be in network byte order. var portBuf bytes.Buffer _ = binary.Write(&portBuf, binary.BigEndian, d.Port) - cmdAttr.AddRtAttr(ipvsDestAttrPort, portBuf.Bytes()) - cmdAttr.AddRtAttr(ipvsDestAttrForwardingMethod, nl.Uint32Attr(d.ConnectionFlags&ConnectionFlagFwdMask)) - cmdAttr.AddRtAttr(ipvsDestAttrWeight, nl.Uint32Attr(uint32(d.Weight))) - cmdAttr.AddRtAttr(ipvsDestAttrUpperThreshold, nl.Uint32Attr(d.UpperThreshold)) - cmdAttr.AddRtAttr(ipvsDestAttrLowerThreshold, nl.Uint32Attr(d.LowerThreshold)) + cmdAttr.AddRtAttr(int(ipvsDestAttrPort), portBuf.Bytes()) + cmdAttr.AddRtAttr(int(ipvsDestAttrForwardingMethod), nl.Uint32Attr(d.ConnectionFlags&ConnectionFlagFwdMask)) + cmdAttr.AddRtAttr(int(ipvsDestAttrWeight), nl.Uint32Attr(uint32(d.Weight))) + cmdAttr.AddRtAttr(int(ipvsDestAttrUpperThreshold), nl.Uint32Attr(d.UpperThreshold)) + cmdAttr.AddRtAttr(int(ipvsDestAttrLowerThreshold), nl.Uint32Attr(d.LowerThreshold)) return cmdAttr, nil } @@ -137,8 +137,8 @@ func (i *Handle) doCmdwithResponse(s *Service, d *Destination, cmd ipvsCmd) ([][ req.Seq = atomic.AddUint32(&i.seq, 1) if s == nil { - req.Flags |= syscall.NLM_F_DUMP // Flag to dump all messages - req.AddData(nl.NewRtAttr(ipvsCmdAttrService, nil)) // Add a dummy attribute + req.Flags |= syscall.NLM_F_DUMP // Flag to dump all messages + req.AddData(nl.NewRtAttr(int(ipvsCmdAttrService), nil)) // Add a dummy attribute } else { req.AddData(fillService(s)) } @@ -307,7 +307,7 @@ func assembleStats(msg []byte) (SvcStats, error) { } for _, attr := range attrs { - switch int(attr.Attr.Type) { + switch ipvsStats(attr.Attr.Type) { case ipvsStatsUnspec: // unspecified case ipvsStatsConns: @@ -341,7 +341,7 @@ func assembleService(attrs []syscall.NetlinkRouteAttr) (*Service, error) { var addressBytes []byte for _, attr := range attrs { - switch int(attr.Attr.Type) { + switch ipvsSvcAttr(attr.Attr.Type) { case ipvsSvcAttrUnspec: // unspecified case ipvsSvcAttrAddressFamily: @@ -448,7 +448,7 @@ func assembleDestination(attrs []syscall.NetlinkRouteAttr) (*Destination, error) var addressBytes []byte for _, attr := range attrs { - switch int(attr.Attr.Type) { + switch ipvsDestAttr(attr.Attr.Type) { case ipvsDestAttrUnspec: // unspecified case ipvsDestAttrAddressFamily: @@ -603,7 +603,7 @@ func (i *Handle) parseConfig(msg []byte) (*Config, error) { } for _, attr := range attrs { - switch int(attr.Attr.Type) { + switch ipvsCmdAttr(attr.Attr.Type) { case ipvsCmdAttrUnspec: // unspecified case ipvsCmdAttrTimeoutTCP: @@ -639,9 +639,9 @@ func (i *Handle) doSetConfigCmd(c *Config) error { req := newIPVSRequest(ipvsCmdSetConfig) req.Seq = atomic.AddUint32(&i.seq, 1) - req.AddData(nl.NewRtAttr(ipvsCmdAttrTimeoutTCP, nl.Uint32Attr(uint32(c.TimeoutTCP.Seconds())))) - req.AddData(nl.NewRtAttr(ipvsCmdAttrTimeoutTCPFin, nl.Uint32Attr(uint32(c.TimeoutTCPFin.Seconds())))) - req.AddData(nl.NewRtAttr(ipvsCmdAttrTimeoutUDP, nl.Uint32Attr(uint32(c.TimeoutUDP.Seconds())))) + req.AddData(nl.NewRtAttr(int(ipvsCmdAttrTimeoutTCP), nl.Uint32Attr(uint32(c.TimeoutTCP.Seconds())))) + req.AddData(nl.NewRtAttr(int(ipvsCmdAttrTimeoutTCPFin), nl.Uint32Attr(uint32(c.TimeoutTCPFin.Seconds())))) + req.AddData(nl.NewRtAttr(int(ipvsCmdAttrTimeoutUDP), nl.Uint32Attr(uint32(c.TimeoutUDP.Seconds())))) _, err := execute(i.sock, req) From 5a0414fa2b6369ba9dbf02faa363398ee15ed34e Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 9 Oct 2026 14:36:03 +0200 Subject: [PATCH 4/6] deduplicate IPVS forwarding method constants Use the ConnFwd* constants as the canonical names for IPVS forwarding methods, and deprecate the duplicate ConnectionFlag* constants while retaining them as aliases for compatibility. Update internal references to use ConnFwd* and document their corresponding Linux kernel definitions. Simplify lookupFwMethod by returning string literals directly instead of indexing into a separate slice, and handle the previously missing LocalNode and Bypass methods. Signed-off-by: Sebastiaan van Stijn --- constants_linux.go | 51 +++++++++++++++++++++++++++------------------- ipvs_linux_test.go | 34 +++++++++++++++---------------- netlink_linux.go | 2 +- 3 files changed, 48 insertions(+), 39 deletions(-) diff --git a/constants_linux.go b/constants_linux.go index 301dd36..bddb653 100644 --- a/constants_linux.go +++ b/constants_linux.go @@ -118,26 +118,27 @@ const ( ipvsStatsBPSOut ipvsStats = 10 // IPVS_STATS_ATTR_OUTBPS ) -// Destination forwarding methods +// Deprecated forwarding method names retained for compatibility. const ( - // ConnectionFlagFwdMask indicates the mask in the connection - // flags which is used by forwarding method bits. - ConnectionFlagFwdMask = 0x0007 + // ConnectionFlagFwdMask is an alias for ConnFwdMask. + // Deprecated: Use ConnFwdMask instead. + ConnectionFlagFwdMask = ConnFwdMask - // ConnectionFlagMasq is used for masquerade forwarding method. - ConnectionFlagMasq = 0x0000 + // ConnectionFlagMasq is an alias for ConnFwdMasq. + // Deprecated: Use ConnFwdMasq instead. + ConnectionFlagMasq = ConnFwdMasq - // ConnectionFlagLocalNode is used for local node forwarding - // method. - ConnectionFlagLocalNode = 0x0001 + // ConnectionFlagLocalNode is an alias for ConnFwdLocalNode. + // Deprecated: Use ConnFwdLocalNode instead. + ConnectionFlagLocalNode = ConnFwdLocalNode - // ConnectionFlagTunnel is used for tunnel mode forwarding - // method. - ConnectionFlagTunnel = 0x0002 + // ConnectionFlagTunnel is an alias for ConnFwdTunnel. + // Deprecated: Use ConnFwdTunnel instead. + ConnectionFlagTunnel = ConnFwdTunnel - // ConnectionFlagDirectRoute is used for direct routing - // forwarding method. - ConnectionFlagDirectRoute = 0x0003 + // ConnectionFlagDirectRoute is an alias for ConnFwdDirectRoute. + // Deprecated: Use ConnFwdDirectRoute instead. + ConnectionFlagDirectRoute = ConnFwdDirectRoute ) const ( @@ -171,22 +172,30 @@ const ( WeightedLeastConnection = "wlc" ) +// Destination forwarding methods. +// See https://github.com/torvalds/linux/blob/v7.2/include/uapi/linux/ip_vs.h#L72-L132 const ( - // ConnFwdMask is a mask for the fwd methods + // ConnFwdMask is the mask for the forwarding method bits. + // Corresponds to IP_VS_CONN_F_FWD_MASK. ConnFwdMask = 0x0007 - // ConnFwdMasq denotes forwarding via masquerading/NAT + // ConnFwdMasq denotes forwarding via masquerading/NAT. + // Corresponds to IP_VS_CONN_F_MASQ. ConnFwdMasq = 0x0000 - // ConnFwdLocalNode denotes forwarding to a local node + // ConnFwdLocalNode denotes forwarding to a local node. + // Corresponds to IP_VS_CONN_F_LOCALNODE. ConnFwdLocalNode = 0x0001 - // ConnFwdTunnel denotes forwarding via a tunnel + // ConnFwdTunnel denotes forwarding via a tunnel. + // Corresponds to IP_VS_CONN_F_TUNNEL. ConnFwdTunnel = 0x0002 - // ConnFwdDirectRoute denotes forwarding via direct routing + // ConnFwdDirectRoute denotes forwarding via direct routing. + // Corresponds to IP_VS_CONN_F_DROUTE. ConnFwdDirectRoute = 0x0003 - // ConnFwdBypass denotes forwarding while bypassing the cache + // ConnFwdBypass denotes forwarding while bypassing the cache. + // Corresponds to IP_VS_CONN_F_BYPASS. ConnFwdBypass = 0x0004 ) diff --git a/ipvs_linux_test.go b/ipvs_linux_test.go index 3f9ef99..6ac9cdb 100644 --- a/ipvs_linux_test.go +++ b/ipvs_linux_test.go @@ -31,28 +31,28 @@ var ( } fwdMethods = []uint32{ - ConnectionFlagMasq, - ConnectionFlagTunnel, - ConnectionFlagDirectRoute, - } - - fwdMethodStrings = []string{ - "Masq", - "Tunnel", - "Route", + ConnFwdMasq, + ConnFwdTunnel, + ConnFwdDirectRoute, + ConnFwdBypass, } ) func lookupFwMethod(fwMethod uint32) string { switch fwMethod { - case ConnectionFlagMasq: - return fwdMethodStrings[0] - case ConnectionFlagTunnel: - return fwdMethodStrings[1] - case ConnectionFlagDirectRoute: - return fwdMethodStrings[2] - } - return "" + case ConnFwdMasq: + return "Masq" + case ConnFwdLocalNode: + return "Local" + case ConnFwdTunnel: + return "Tunnel" + case ConnFwdDirectRoute: + return "Route" + case ConnFwdBypass: + return "Bypass" + default: + return "" + } } func checkDestination(t *testing.T, i *Handle, s *Service, d *Destination, checkPresent bool) { diff --git a/netlink_linux.go b/netlink_linux.go index 91d4d4e..04c7849 100644 --- a/netlink_linux.go +++ b/netlink_linux.go @@ -124,7 +124,7 @@ func fillDestination(d *Destination) (nl.NetlinkRequestData, error) { var portBuf bytes.Buffer _ = binary.Write(&portBuf, binary.BigEndian, d.Port) cmdAttr.AddRtAttr(int(ipvsDestAttrPort), portBuf.Bytes()) - cmdAttr.AddRtAttr(int(ipvsDestAttrForwardingMethod), nl.Uint32Attr(d.ConnectionFlags&ConnectionFlagFwdMask)) + cmdAttr.AddRtAttr(int(ipvsDestAttrForwardingMethod), nl.Uint32Attr(d.ConnectionFlags&ConnFwdMask)) cmdAttr.AddRtAttr(int(ipvsDestAttrWeight), nl.Uint32Attr(uint32(d.Weight))) cmdAttr.AddRtAttr(int(ipvsDestAttrUpperThreshold), nl.Uint32Attr(d.UpperThreshold)) cmdAttr.AddRtAttr(int(ipvsDestAttrLowerThreshold), nl.Uint32Attr(d.LowerThreshold)) From cbcb945b4a96ac3b06fd584646e9b6af4fbb6439 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 9 Oct 2026 14:41:41 +0200 Subject: [PATCH 5/6] document IPVS scheduler name constants Add a reference to the Linux kernel's IPVS scheduler implementations to clarify where the scheduler names originate. Unlike the netlink attributes and forwarding flags, these names are registered as strings by the individual kernel schedulers, rather than defined as UAPI constants. Signed-off-by: Sebastiaan van Stijn --- constants_linux.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/constants_linux.go b/constants_linux.go index bddb653..e5a4abb 100644 --- a/constants_linux.go +++ b/constants_linux.go @@ -141,6 +141,9 @@ const ( ConnectionFlagDirectRoute = ConnFwdDirectRoute ) +// IPVS scheduler names. +// These correspond to the names registered by the Linux IPVS schedulers. +// See https://github.com/torvalds/linux/tree/v7.2/net/netfilter/ipvs const ( // RoundRobin distributes jobs equally amongst the available // real servers. From e4b9843bcc8aa9e485e9db6bc7efe83b24070f63 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 9 Oct 2026 14:54:13 +0200 Subject: [PATCH 6/6] use unix address family constants directly Replace nl.FAMILY_* constants with their corresponding unix.AF_* constants. The netlink package aliases these constants to the Linux address family values, so using golang.org/x/sys/unix directly avoids an unnecessary layer of indirection and makes their origin explicit. Signed-off-by: Sebastiaan van Stijn --- ipvs_linux_test.go | 13 ++++++------- netlink_linux.go | 3 ++- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ipvs_linux_test.go b/ipvs_linux_test.go index 6ac9cdb..f54333a 100644 --- a/ipvs_linux_test.go +++ b/ipvs_linux_test.go @@ -10,7 +10,6 @@ import ( "github.com/moby/ipvs/ns" "github.com/vishvananda/netlink" - "github.com/vishvananda/netlink/nl" "golang.org/x/sys/unix" ) @@ -137,11 +136,11 @@ func TestService(t *testing.T) { Netmask uint32 }{ { - AddressFamily: nl.FAMILY_V4, + AddressFamily: unix.AF_INET, IP: "1.2.3.4", Netmask: 0xFFFFFFFF, }, { - AddressFamily: nl.FAMILY_V6, + AddressFamily: unix.AF_INET6, IP: "2001:db8:3c4d:15::1a00", Netmask: 128, }, @@ -211,7 +210,7 @@ func TestService(t *testing.T) { svcs := []Service{ { - AddressFamily: nl.FAMILY_V4, + AddressFamily: unix.AF_INET, SchedName: RoundRobin, Protocol: unix.IPPROTO_TCP, Port: 80, @@ -219,7 +218,7 @@ func TestService(t *testing.T) { Netmask: 0xFFFFFFFF, }, { - AddressFamily: nl.FAMILY_V4, + AddressFamily: unix.AF_INET, SchedName: LeastConnection, Protocol: unix.IPPROTO_UDP, Port: 8080, @@ -300,12 +299,12 @@ func TestDestination(t *testing.T) { Destinations []string }{ { - AddressFamily: nl.FAMILY_V4, + AddressFamily: unix.AF_INET, IP: "1.2.3.4", Netmask: 0xFFFFFFFF, Destinations: []string{"10.1.1.2", "10.1.1.3", "10.1.1.4"}, }, { - AddressFamily: nl.FAMILY_V6, + AddressFamily: unix.AF_INET6, IP: "2001:db8:3c4d:15::1a00", Netmask: 128, Destinations: []string{"2001:db8:3c4d:15::1a2b", "2001:db8:3c4d:15::1a2c", "2001:db8:3c4d:15::1a2d"}, diff --git a/netlink_linux.go b/netlink_linux.go index 04c7849..86ffe00 100644 --- a/netlink_linux.go +++ b/netlink_linux.go @@ -17,6 +17,7 @@ import ( "github.com/sirupsen/logrus" "github.com/vishvananda/netlink/nl" "github.com/vishvananda/netns" + "golang.org/x/sys/unix" ) // For Quick Reference IPVS related netlink message is described at the end of this file. @@ -210,7 +211,7 @@ func getIPVSFamily() (int, error) { func rawIPData(ip net.IP) []byte { family := nl.GetIPFamily(ip) - if family == nl.FAMILY_V4 { + if family == unix.AF_INET { return ip.To4() } return ip