@@ -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
2262func 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