feat: add VRRP unicast peer support for macvlan and restricted network environments
Release / Build darwin-amd64 (push) Has been cancelled
Release / Build linux-amd64 (push) Has been cancelled
Release / Build darwin-arm64 (push) Has been cancelled
Release / Build linux-arm64 (push) Has been cancelled
Release / Create Release (push) Has been cancelled
Release / Build darwin-amd64 (push) Has been cancelled
Release / Build linux-amd64 (push) Has been cancelled
Release / Build darwin-arm64 (push) Has been cancelled
Release / Build linux-arm64 (push) Has been cancelled
Release / Create Release (push) Has been cancelled
- Add unicast_src_ip and unicast_peers config fields - NewUnicastSocket binds to source IP for proper packet reception - SendTo() sends VRRP advertisements directly to peer IPs - Receive loop filters packets from non-peer sources in unicast mode - Backward compatible: multicast mode used when unicast_peers is empty - Add unicast config example (etc/config.unicast.yaml) - Update README with unicast documentation and examples
This commit is contained in:
@@ -21,6 +21,8 @@ type Instance struct {
|
||||
AuthType uint8
|
||||
AuthPass string
|
||||
TrackScripts []string
|
||||
UnicastSrcIP net.IP
|
||||
UnicastPeers []net.IP
|
||||
|
||||
state *StateMachine
|
||||
priorityCalc *PriorityCalculator
|
||||
@@ -54,6 +56,8 @@ func NewInstance(
|
||||
authType string,
|
||||
authPass string,
|
||||
trackScripts []string,
|
||||
unicastSrcIP string,
|
||||
unicastPeers []string,
|
||||
log *logger.Logger,
|
||||
) (*Instance, error) {
|
||||
if vrID < 1 || vrID > 255 {
|
||||
@@ -90,6 +94,28 @@ func NewInstance(
|
||||
return nil, fmt.Errorf("failed to get interface: %w", err)
|
||||
}
|
||||
|
||||
var parsedSrcIP net.IP
|
||||
var parsedPeers []net.IP
|
||||
|
||||
if len(unicastPeers) > 0 {
|
||||
parsedPeers = make([]net.IP, 0, len(unicastPeers))
|
||||
for _, peer := range unicastPeers {
|
||||
ip := net.ParseIP(peer)
|
||||
if ip == nil {
|
||||
return nil, fmt.Errorf("invalid unicast peer IP: %s", peer)
|
||||
}
|
||||
parsedPeers = append(parsedPeers, ip.To4())
|
||||
}
|
||||
|
||||
if unicastSrcIP != "" {
|
||||
parsedSrcIP = net.ParseIP(unicastSrcIP)
|
||||
if parsedSrcIP == nil {
|
||||
return nil, fmt.Errorf("invalid unicast src IP: %s", unicastSrcIP)
|
||||
}
|
||||
parsedSrcIP = parsedSrcIP.To4()
|
||||
}
|
||||
}
|
||||
|
||||
inst := &Instance{
|
||||
Name: name,
|
||||
VirtualRouterID: vrID,
|
||||
@@ -101,6 +127,8 @@ func NewInstance(
|
||||
AuthType: authTypeNum,
|
||||
AuthPass: authPass,
|
||||
TrackScripts: trackScripts,
|
||||
UnicastSrcIP: parsedSrcIP,
|
||||
UnicastPeers: parsedPeers,
|
||||
state: NewStateMachine(StateInit),
|
||||
priorityCalc: NewPriorityCalculator(priority),
|
||||
history: NewStateHistory(100),
|
||||
@@ -131,7 +159,15 @@ func (inst *Instance) Start() error {
|
||||
inst.mu.Unlock()
|
||||
|
||||
var err error
|
||||
inst.socket, err = NewSocket(inst.Interface)
|
||||
if len(inst.UnicastPeers) > 0 {
|
||||
if inst.UnicastSrcIP != nil {
|
||||
inst.socket, err = NewUnicastSocket(inst.Interface, inst.UnicastSrcIP)
|
||||
} else {
|
||||
inst.socket, err = NewSocket(inst.Interface)
|
||||
}
|
||||
} else {
|
||||
inst.socket, err = NewSocket(inst.Interface)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create socket: %w", err)
|
||||
}
|
||||
@@ -142,8 +178,13 @@ func (inst *Instance) Start() error {
|
||||
return fmt.Errorf("failed to create ARP sender: %w", err)
|
||||
}
|
||||
|
||||
inst.log.Info("[%s] starting VRRP instance (VRID=%d, Priority=%d, Interface=%s)",
|
||||
inst.Name, inst.VirtualRouterID, inst.Priority, inst.Interface)
|
||||
if len(inst.UnicastPeers) > 0 {
|
||||
inst.log.Info("[%s] starting VRRP instance (VRID=%d, Priority=%d, Interface=%s, Mode=unicast, SrcIP=%s, Peers=%v)",
|
||||
inst.Name, inst.VirtualRouterID, inst.Priority, inst.Interface, inst.socket.localIP, inst.UnicastPeers)
|
||||
} else {
|
||||
inst.log.Info("[%s] starting VRRP instance (VRID=%d, Priority=%d, Interface=%s, Mode=multicast)",
|
||||
inst.Name, inst.VirtualRouterID, inst.Priority, inst.Interface)
|
||||
}
|
||||
|
||||
inst.state.SetState(StateBackup)
|
||||
inst.masterDownTimer.Start()
|
||||
@@ -213,6 +254,13 @@ func (inst *Instance) receiveLoop() {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(inst.UnicastPeers) > 0 {
|
||||
if !inst.isUnicastPeer(srcIP) {
|
||||
inst.log.Debug("[%s] ignoring packet from non-peer %s", inst.Name, srcIP)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if err := pkt.Validate(inst.AuthPass); err != nil {
|
||||
inst.log.Warn("[%s] packet validation failed: %v", inst.Name, err)
|
||||
continue
|
||||
@@ -222,6 +270,15 @@ func (inst *Instance) receiveLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
func (inst *Instance) isUnicastPeer(ip net.IP) bool {
|
||||
for _, peer := range inst.UnicastPeers {
|
||||
if peer.Equal(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (inst *Instance) handleAdvertisement(pkt *VRRPPacket, srcIP net.IP) {
|
||||
currentState := inst.state.GetState()
|
||||
localPriority := inst.priorityCalc.GetPriority()
|
||||
@@ -272,12 +329,22 @@ func (inst *Instance) sendAdvertisement() error {
|
||||
inst.AuthPass,
|
||||
)
|
||||
|
||||
if err := inst.socket.Send(pkt); err != nil {
|
||||
inst.log.Error("[%s] failed to send advertisement: %v", inst.Name, err)
|
||||
return err
|
||||
if len(inst.UnicastPeers) > 0 {
|
||||
for _, peer := range inst.UnicastPeers {
|
||||
if err := inst.socket.SendTo(pkt, peer); err != nil {
|
||||
inst.log.Error("[%s] failed to send unicast advertisement to %s: %v", inst.Name, peer, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
inst.log.Debug("[%s] sent unicast advertisement (priority=%d, peers=%d)", inst.Name, priority, len(inst.UnicastPeers))
|
||||
} else {
|
||||
if err := inst.socket.Send(pkt); err != nil {
|
||||
inst.log.Error("[%s] failed to send advertisement: %v", inst.Name, err)
|
||||
return err
|
||||
}
|
||||
inst.log.Debug("[%s] sent multicast advertisement (priority=%d)", inst.Name, priority)
|
||||
}
|
||||
|
||||
inst.log.Debug("[%s] sent advertisement (priority=%d)", inst.Name, priority)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user