Files
vpp-maglev/internal/vpp/lbconf.go

104 lines
2.7 KiB
Go

// SPDX-License-Identifier: Apache-2.0
package vpp
import (
"bytes"
"fmt"
"log/slog"
"net"
"git.ipng.ch/ipng/vpp-maglev/internal/config"
ip_types "git.ipng.ch/ipng/vpp-maglev/internal/vpp/binapi/ip_types"
lb "git.ipng.ch/ipng/vpp-maglev/internal/vpp/binapi/lb"
)
// SetLBConf sends lb_conf to VPP with the global load-balancer settings from
// cfg. Called on VPP connect (startup and reconnect) and after every
// successful config reload. Returns nil if VPP is not connected (silently
// skipped — the next connect will push the conf).
//
// The values sent are cached on the Client; if SetLBConf is called twice in
// a row with unchanged values, no API call is made and no log is emitted.
func (c *Client) SetLBConf(cfg *config.Config) error {
if !c.IsConnected() {
return nil
}
req := &lb.LbConf{
IP4SrcAddress: ip_types.IP4Address(ip4Bytes(cfg.VPP.LB.IPv4SrcAddress)),
IP6SrcAddress: ip_types.IP6Address(ip6Bytes(cfg.VPP.LB.IPv6SrcAddress)),
StickyBucketsPerCore: cfg.VPP.LB.StickyBucketsPerCore,
FlowTimeout: uint32(cfg.VPP.LB.FlowTimeout.Seconds()),
}
// Skip if nothing changed since the last successful push.
c.mu.Lock()
prev := c.lastLBConf
c.mu.Unlock()
if prev != nil &&
bytes.Equal(prev.IP4SrcAddress[:], req.IP4SrcAddress[:]) &&
bytes.Equal(prev.IP6SrcAddress[:], req.IP6SrcAddress[:]) &&
prev.StickyBucketsPerCore == req.StickyBucketsPerCore &&
prev.FlowTimeout == req.FlowTimeout {
return nil
}
ch, err := c.apiChannel()
if err != nil {
return err
}
defer ch.Close()
reply := &lb.LbConfReply{}
if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
return fmt.Errorf("lb_conf: %w", err)
}
if reply.Retval != 0 {
return fmt.Errorf("lb_conf: retval=%d", reply.Retval)
}
c.mu.Lock()
c.lastLBConf = req
c.mu.Unlock()
slog.Info("vpp-lb-conf-set",
"ipv4-src", ipStringFromCfg(cfg.VPP.LB.IPv4SrcAddress),
"ipv6-src", ipStringFromCfg(cfg.VPP.LB.IPv6SrcAddress),
"sticky-buckets-per-core", req.StickyBucketsPerCore,
"flow-timeout", cfg.VPP.LB.FlowTimeout.String())
return nil
}
// ip4Bytes returns the 4-byte representation of an IPv4 address, or all-zero
// if ip is nil/unset.
func ip4Bytes(ip net.IP) [4]byte {
var out [4]byte
if ip == nil {
return out
}
if b := ip.To4(); b != nil {
copy(out[:], b)
}
return out
}
// ip6Bytes returns the 16-byte representation of an IPv6 address, or all-zero
// if ip is nil/unset.
func ip6Bytes(ip net.IP) [16]byte {
var out [16]byte
if ip == nil {
return out
}
copy(out[:], ip.To16())
return out
}
// ipStringFromCfg renders an IP for logging; returns "unset" if nil.
func ipStringFromCfg(ip net.IP) string {
if ip == nil {
return "unset"
}
return ip.String()
}