Skip to content

Commit dbab564

Browse files
committed
Fix GARP sending 0.0.0.0 due to incorrect IPv4 byte extraction
net.ParseIP() returns 16-byte IPv4-mapped IPv6 format where IPv4 bytes are at the END, not beginning. [4]byte(garp.IP) took wrong bytes. Fixed by calling To4() before, forcing validated creation via NewGARP(). Interface prevents bypassing extracting the correct IPv4 address. Signed-off-by: Patryk Diak <[email protected]>
1 parent 53c8c29 commit dbab564

File tree

3 files changed

+61
-16
lines changed

3 files changed

+61
-16
lines changed

go-controller/pkg/kubevirt/pod.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -528,11 +528,10 @@ func (r *DefaultGatewayReconciler) ReconcileIPv4AfterLiveMigration(liveMigration
528528

529529
lrpMAC := util.IPAddrToHWAddr(lrpJoinAddress)
530530
for _, subnet := range r.netInfo.Subnets() {
531-
gwIP := r.netInfo.GetNodeGatewayIP(subnet.CIDR).IP.To4()
532-
if gwIP == nil {
533-
continue
531+
garp, err := util.NewGARP(r.netInfo.GetNodeGatewayIP(subnet.CIDR).IP, &lrpMAC)
532+
if err != nil {
533+
return fmt.Errorf("failed to create GARP for gateway IP %s: %w", r.netInfo.GetNodeGatewayIP(subnet.CIDR).IP, err)
534534
}
535-
garp := util.GARP{IP: gwIP, MAC: &lrpMAC}
536535
if err := util.BroadcastGARP(r.interfaceName, garp); err != nil {
537536
return err
538537
}

go-controller/pkg/node/linkmanager/link_network_manager.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,13 @@ func (c *Controller) syncLink(link netlink.Link) error {
196196
// For IPv4, use arping to try to update other hosts ARP caches, in case this IP was
197197
// previously active on another node
198198
if addressWanted.IP.To4() != nil {
199-
if err = util.BroadcastGARP(linkName, util.GARP{IP: addressWanted.IP}); err != nil {
200-
klog.Errorf("Failed to send a GARP for IP %s over interface %s: %v", addressWanted.IP.String(),
199+
garp, err := util.NewGARP(addressWanted.IP, nil)
200+
if err != nil {
201+
klog.Errorf("Link manager: failed to create GARP for IP %s: %v", addressWanted.IP.String(), err)
202+
continue
203+
}
204+
if err = util.BroadcastGARP(linkName, garp); err != nil {
205+
klog.Errorf("Link manager: failed to send GARP for IP %s over interface %s: %v", addressWanted.IP.String(),
201206
linkName, err)
202207
}
203208
}

go-controller/pkg/util/arp.go

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,69 @@ import (
66
"net/netip"
77

88
"github.com/mdlayher/arp"
9+
10+
"k8s.io/klog/v2"
911
)
1012

11-
type GARP struct {
12-
// IP to advertise the MAC address
13-
IP net.IP
14-
// MAC to advertise (optional), default: link mac address
15-
MAC *net.HardwareAddr
13+
// GARP represents a gratuitous ARP request for an IPv4 address.
14+
type GARP interface {
15+
// IP returns the IPv4 address as a net.IP
16+
IP() net.IP
17+
// IPv4 returns the raw 4-byte IPv4 address
18+
IPv4() [net.IPv4len]byte
19+
// MAC returns the MAC address to advertise (nil means use interface MAC)
20+
MAC() *net.HardwareAddr
21+
}
22+
23+
// garp is the private implementation of GARP
24+
type garp struct {
25+
ip [4]byte
26+
mac *net.HardwareAddr
27+
}
28+
29+
// NewGARP creates a new GARP with validation that the IP is IPv4.
30+
// Returns error if the IP is not a valid IPv4 address.
31+
// mac can be nil to use the interface's MAC address.
32+
func NewGARP(ip net.IP, mac *net.HardwareAddr) (GARP, error) {
33+
ip4 := ip.To4()
34+
if ip4 == nil {
35+
return nil, fmt.Errorf("GARP only supports IPv4 addresses, got %s (len=%d bytes)", ip.String(), len(ip))
36+
}
37+
return &garp{
38+
ip: [4]byte(ip4),
39+
mac: mac,
40+
}, nil
41+
}
42+
43+
// IP returns the IPv4 address as a net.IP
44+
func (g *garp) IP() net.IP {
45+
return net.IP(g.ip[:])
46+
}
47+
48+
// IPv4 returns the raw 4-byte IPv4 address
49+
func (g *garp) IPv4() [4]byte {
50+
return g.ip
51+
}
52+
53+
// MAC returns the MAC address to advertise
54+
func (g *garp) MAC() *net.HardwareAddr {
55+
return g.mac
1656
}
1757

1858
// BroadcastGARP send a pair of GARPs with "request" and "reply" operations
1959
// since some system response to request and others to reply.
2060
// If "garp.MAC" is not passed the link form "interfaceName" mac will be
2161
// advertise
2262
func BroadcastGARP(interfaceName string, garp GARP) error {
23-
srcIP := netip.AddrFrom4([4]byte(garp.IP))
24-
2563
iface, err := net.InterfaceByName(interfaceName)
2664
if err != nil {
2765
return fmt.Errorf("failed finding interface %s: %v", interfaceName, err)
2866
}
2967

30-
if garp.MAC == nil {
31-
garp.MAC = &iface.HardwareAddr
68+
srcIP := netip.AddrFrom4(garp.IPv4())
69+
mac := garp.MAC()
70+
if mac == nil {
71+
mac = &iface.HardwareAddr
3272
}
3373

3474
c, err := arp.Dial(iface)
@@ -50,7 +90,7 @@ func BroadcastGARP(interfaceName string, garp GARP) error {
5090
for _, op := range []arp.Operation{arp.OperationRequest, arp.OperationReply} {
5191
// At at GARP the source and target IP should be the same and point to the
5292
// the IP we want to reconcile -> https://wiki.wireshark.org/Gratuitous_ARP
53-
p, err := arp.NewPacket(op, *garp.MAC /* srcHw */, srcIP, net.HardwareAddr{0, 0, 0, 0, 0, 0}, srcIP)
93+
p, err := arp.NewPacket(op, *mac /* srcHw */, srcIP, net.HardwareAddr{0, 0, 0, 0, 0, 0}, srcIP)
5494
if err != nil {
5595
return fmt.Errorf("failed creating %q GARP %+v: %w", op, garp, err)
5696
}
@@ -60,5 +100,6 @@ func BroadcastGARP(interfaceName string, garp GARP) error {
60100
}
61101
}
62102

103+
klog.Infof("BroadcastGARP: completed GARP broadcast for IP %s on interface %s with MAC: %s", garp.IP().String(), interfaceName, mac.String())
63104
return nil
64105
}

0 commit comments

Comments
 (0)