Files
go-alived/internal/vrrp/socket.go
loveuer 94c1c81ee0 fix: resolve critical bugs and refactor code structure
P0 Fixes:
- Fix potential panic in factory.go due to unsafe type assertion
- Fix VIP CIDR mask being lost during parsing (was hardcoded to /32)

P1 Fixes:
- Fix go.mod incorrect indirect dependency markers
- Fix receiveLoop blocking issue preventing graceful shutdown

Refactoring:
- Split state.go into state.go, timer.go, priority.go, history.go
- Split monitor.go into monitor.go and manager.go
- Add IncreasePriority() method for complete priority adjustment
- Fix go vet format string warning in test.go

🤖 Generated with [Qoder][https://qoder.com]
2026-03-04 00:14:47 -08:00

147 lines
3.3 KiB
Go

package vrrp
import (
"fmt"
"net"
"os"
"syscall"
"time"
"golang.org/x/net/ipv4"
)
const (
VRRPMulticastAddr = "224.0.0.18"
)
type Socket struct {
conn *ipv4.RawConn
packetConn net.PacketConn
iface *net.Interface
localIP net.IP
groupIP net.IP
}
func NewSocket(ifaceName string) (*Socket, error) {
iface, err := net.InterfaceByName(ifaceName)
if err != nil {
return nil, fmt.Errorf("failed to get interface %s: %w", ifaceName, err)
}
addrs, err := iface.Addrs()
if err != nil {
return nil, fmt.Errorf("failed to get addresses for %s: %w", ifaceName, err)
}
var localIP net.IP
for _, addr := range addrs {
if ipNet, ok := addr.(*net.IPNet); ok {
if ipv4 := ipNet.IP.To4(); ipv4 != nil {
localIP = ipv4
break
}
}
}
if localIP == nil {
return nil, fmt.Errorf("no IPv4 address found on interface %s", ifaceName)
}
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, VRRPProtocolNumber)
if err != nil {
return nil, fmt.Errorf("failed to create raw socket: %w", err)
}
if err := syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
syscall.Close(fd)
return nil, fmt.Errorf("failed to set SO_REUSEADDR: %w", err)
}
file := os.NewFile(uintptr(fd), "vrrp-socket")
packetConn, err := net.FilePacketConn(file)
file.Close()
if err != nil {
return nil, fmt.Errorf("failed to create packet connection: %w", err)
}
rawConn, err := ipv4.NewRawConn(packetConn)
if err != nil {
packetConn.Close()
return nil, fmt.Errorf("failed to create raw connection: %w", err)
}
groupIP := net.ParseIP(VRRPMulticastAddr).To4()
if groupIP == nil {
rawConn.Close()
return nil, fmt.Errorf("invalid multicast address: %s", VRRPMulticastAddr)
}
if err := rawConn.JoinGroup(iface, &net.IPAddr{IP: groupIP}); err != nil {
rawConn.Close()
return nil, fmt.Errorf("failed to join multicast group: %w", err)
}
if err := rawConn.SetControlMessage(ipv4.FlagTTL|ipv4.FlagSrc|ipv4.FlagDst|ipv4.FlagInterface, true); err != nil {
rawConn.Close()
return nil, fmt.Errorf("failed to set control message: %w", err)
}
return &Socket{
conn: rawConn,
packetConn: packetConn,
iface: iface,
localIP: localIP,
groupIP: groupIP,
}, nil
}
func (s *Socket) Send(pkt *VRRPPacket) error {
data, err := pkt.Marshal()
if err != nil {
return fmt.Errorf("failed to marshal packet: %w", err)
}
header := &ipv4.Header{
Version: ipv4.Version,
Len: ipv4.HeaderLen,
TOS: 0xC0,
TotalLen: ipv4.HeaderLen + len(data),
TTL: 255,
Protocol: VRRPProtocolNumber,
Dst: s.groupIP,
Src: s.localIP,
}
if err := s.conn.WriteTo(header, data, nil); err != nil {
return fmt.Errorf("failed to send packet: %w", err)
}
return nil
}
func (s *Socket) Receive() (*VRRPPacket, net.IP, error) {
buf := make([]byte, 1500)
header, payload, _, err := s.conn.ReadFrom(buf)
if err != nil {
return nil, nil, fmt.Errorf("failed to receive packet: %w", err)
}
pkt, err := Unmarshal(payload)
if err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal packet: %w", err)
}
return pkt, header.Src, nil
}
func (s *Socket) SetReadDeadline(t time.Time) error {
return s.packetConn.SetReadDeadline(t)
}
func (s *Socket) Close() error {
if err := s.conn.LeaveGroup(s.iface, &net.IPAddr{IP: s.groupIP}); err != nil {
return err
}
return s.conn.Close()
}