Skip to content

Commit d081f1f

Browse files
authored
[!] add cross-platform support for ARP gratuitous requests, fixes #288 (#290)
1 parent cb46207 commit d081f1f

File tree

8 files changed

+158
-199
lines changed

8 files changed

+158
-199
lines changed

README.md

Lines changed: 61 additions & 42 deletions
Large diffs are not rendered by default.

go.mod

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ go 1.23.0
55
toolchain go1.24.1
66

77
require (
8+
github.com/google/gopacket v1.1.19
89
github.com/hashicorp/consul/api v1.31.2
9-
github.com/mdlayher/arp v0.0.0-20220512170110-6706a2966875
1010
github.com/spf13/pflag v1.0.6
1111
github.com/spf13/viper v1.20.0
1212
go.etcd.io/etcd/client/v3 v3.5.19
@@ -31,12 +31,8 @@ require (
3131
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
3232
github.com/hashicorp/golang-lru v1.0.2 // indirect
3333
github.com/hashicorp/serf v0.10.1 // indirect
34-
github.com/josharian/native v1.1.0 // indirect
3534
github.com/mattn/go-colorable v0.1.13 // indirect
3635
github.com/mattn/go-isatty v0.0.20 // indirect
37-
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 // indirect
38-
github.com/mdlayher/packet v1.1.2 // indirect
39-
github.com/mdlayher/socket v0.5.1 // indirect
4036
github.com/mitchellh/go-homedir v1.1.0 // indirect
4137
github.com/mitchellh/mapstructure v1.5.0 // indirect
4238
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
@@ -51,7 +47,6 @@ require (
5147
go.uber.org/multierr v1.11.0 // indirect
5248
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect
5349
golang.org/x/net v0.36.0 // indirect
54-
golang.org/x/sync v0.11.0 // indirect
5550
golang.org/x/text v0.22.0 // indirect
5651
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect
5752
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect

go.sum

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
5151
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
5252
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
5353
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
54-
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
55-
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
5654
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
5755
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
5856
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
57+
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
58+
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
5959
github.com/hashicorp/consul/api v1.31.2 h1:NicObVJHcCmyOIl7Z9iHPvvFrocgTYo9cITSGg0/7pw=
6060
github.com/hashicorp/consul/api v1.31.2/go.mod h1:Z8YgY0eVPukT/17ejW+l+C7zJmKwgPHtjU1q16v/Y40=
6161
github.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg=
@@ -102,9 +102,6 @@ github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR
102102
github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0=
103103
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
104104
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
105-
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
106-
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
107-
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
108105
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
109106
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
110107
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
@@ -135,16 +132,6 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
135132
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
136133
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
137134
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
138-
github.com/mdlayher/arp v0.0.0-20220512170110-6706a2966875 h1:ql8x//rJsHMjS+qqEag8n3i4azw1QneKh5PieH9UEbY=
139-
github.com/mdlayher/arp v0.0.0-20220512170110-6706a2966875/go.mod h1:kfOoFJuHWp76v1RgZCb9/gVUc7XdY877S2uVYbNliGc=
140-
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 h1:2oDp6OOhLxQ9JBoUuysVz9UZ9uI6oLUbvAZu0x8o+vE=
141-
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118/go.mod h1:ZFUnHIVchZ9lJoWoEGUg8Q3M4U8aNNWA3CVSUTkW4og=
142-
github.com/mdlayher/packet v1.0.0/go.mod h1:eE7/ctqDhoiRhQ44ko5JZU2zxB88g+JH/6jmnjzPjOU=
143-
github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY=
144-
github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4=
145-
github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E=
146-
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
147-
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
148135
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
149136
github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY=
150137
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
@@ -239,12 +226,12 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
239226
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
240227
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA=
241228
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
229+
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
230+
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
242231
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
243232
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
244233
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
245234
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
246-
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
247-
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
248235
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
249236
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
250237
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -260,8 +247,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
260247
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
261248
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
262249
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
263-
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
264-
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
265250
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
266251
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
267252
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -281,7 +266,6 @@ golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7w
281266
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
282267
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
283268
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
284-
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
285269
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
286270
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
287271
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -298,6 +282,7 @@ golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
298282
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
299283
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
300284
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
285+
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
301286
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
302287
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
303288
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

ipmanager/basicConfigurer.go

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import (
55
"net"
66
"strings"
77

8-
arp "github.com/mdlayher/arp"
8+
"github.com/google/gopacket"
9+
"github.com/google/gopacket/layers"
910
)
1011

1112
// BasicConfigurer can be used to enable vip-management on nodes
@@ -16,7 +17,6 @@ import (
1617
// nearby routers and other devices.
1718
type BasicConfigurer struct {
1819
*IPConfiguration
19-
arpClient *arp.Client
2020
ntecontext uint32 //used by Windows to delete IP address
2121
}
2222

@@ -48,8 +48,43 @@ func (c *BasicConfigurer) queryAddress() bool {
4848
return false
4949
}
5050

51-
func (c *BasicConfigurer) cleanupArp() {
52-
if c.arpClient != nil {
53-
c.arpClient.Close()
51+
const (
52+
MACAddressSize = 6
53+
IPv4AddressSize = 4
54+
)
55+
56+
// createGratuitousARP prepares a packet with a gratuitous ARP request
57+
func (c *BasicConfigurer) createGratuitousARP() ([]byte, error) {
58+
// Create the Ethernet layer
59+
ethLayer := &layers.Ethernet{
60+
SrcMAC: c.Iface.HardwareAddr,
61+
DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, // Broadcast
62+
EthernetType: layers.EthernetTypeARP,
5463
}
64+
65+
// Create the ARP layer
66+
arpLayer := &layers.ARP{
67+
AddrType: layers.LinkTypeEthernet,
68+
Protocol: layers.EthernetTypeIPv4,
69+
HwAddressSize: MACAddressSize,
70+
ProtAddressSize: IPv4AddressSize,
71+
Operation: layers.ARPReply, // Gratuitous ARP is sent as a reply
72+
SourceHwAddress: c.Iface.HardwareAddr,
73+
SourceProtAddress: c.IPConfiguration.VIP.AsSlice(),
74+
DstHwAddress: c.Iface.HardwareAddr, // Gratuitous ARP targets itself
75+
DstProtAddress: c.IPConfiguration.VIP.AsSlice(),
76+
}
77+
78+
// Create a packet with the layers
79+
buffer := gopacket.NewSerializeBuffer()
80+
opts := gopacket.SerializeOptions{
81+
FixLengths: true,
82+
ComputeChecksums: true,
83+
}
84+
85+
if err := gopacket.SerializeLayers(buffer, opts, ethLayer, arpLayer); err != nil {
86+
return nil, err
87+
}
88+
89+
return buffer.Bytes(), nil
5590
}

ipmanager/basicConfigurer_linux.go

Lines changed: 31 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,46 @@ package ipmanager
33
import (
44
"net"
55
"os/exec"
6-
"time"
7-
8-
arp "github.com/mdlayher/arp"
6+
"syscall"
97
)
108

11-
const (
12-
arpRequestOp = 1
13-
arpReplyOp = 2
14-
)
9+
// htons converts uint16 to network byte order
10+
func htons(i uint16) uint16 {
11+
return (i<<8)&0xff00 | i>>8
12+
}
1513

16-
var (
17-
ethernetBroadcast = net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
18-
)
14+
func sendPacketLinux(iface net.Interface, packetData []byte) error {
15+
fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, int(htons(syscall.ETH_P_ALL)))
16+
if err != nil {
17+
return err
18+
}
19+
defer syscall.Close(fd)
1920

20-
// configureAddress assigns virtual IP address
21-
func (c *BasicConfigurer) configureAddress() bool {
22-
if c.arpClient == nil {
23-
if err := c.createArpClient(); err != nil {
24-
log.Error("Couldn't create an Arp client:", err)
25-
}
21+
var sll syscall.SockaddrLinklayer
22+
sll.Protocol = htons(syscall.ETH_P_ARP)
23+
sll.Ifindex = iface.Index
24+
sll.Hatype = syscall.ARPHRD_ETHER
25+
sll.Pkttype = syscall.PACKET_BROADCAST
26+
27+
if err = syscall.Bind(fd, &sll); err != nil {
28+
return err
2629
}
2730

28-
log.Infof("Configuring address %s on %s", c.getCIDR(), c.Iface.Name)
31+
return syscall.Sendto(fd, packetData, 0, &sll)
32+
}
2933

34+
// configureAddress assigns virtual IP address
35+
func (c *BasicConfigurer) configureAddress() bool {
36+
log.Infof("Configuring address %s on %s", c.getCIDR(), c.Iface.Name)
3037
result := c.runAddressConfiguration("add")
31-
3238
if result {
33-
// For now it is save to say that also working even if a
34-
// gratuitous arp message could not be send but logging an
35-
// errror should be enough.
36-
_ = c.arpSendGratuitous()
39+
if buff, err := c.createGratuitousARP(); err != nil {
40+
log.Warn("Failed to compose gratuitous ARP request: ", err)
41+
} else {
42+
if err := sendPacketLinux(c.Iface, buff); err != nil {
43+
log.Warn("Failed to send gratuitous ARP request: ", err)
44+
}
45+
}
3746
}
3847

3948
return result
@@ -64,96 +73,3 @@ func (c *BasicConfigurer) runAddressConfiguration(action string) bool {
6473
}
6574
return true
6675
}
67-
68-
func (c *BasicConfigurer) createArpClient() (err error) {
69-
for i := 0; i < c.RetryNum; i++ {
70-
if c.arpClient, err = arp.Dial(&c.Iface); err == nil {
71-
return
72-
}
73-
log.Infof("Problems with producing the arp client: %s", err)
74-
time.Sleep(time.Duration(c.RetryAfter) * time.Millisecond)
75-
}
76-
return
77-
}
78-
79-
// sends a gratuitous ARP request and reply
80-
func (c *BasicConfigurer) arpSendGratuitous() error {
81-
/* While RFC 2002 does not say whether a gratuitous ARP request or reply is preferred
82-
* to update ones neighbours' MAC tables, the Wireshark Wiki recommends sending both.
83-
* https://wiki.wireshark.org/Gratuitous_ARP
84-
* This site also recommends sending a reply, as requests might be ignored by some hardware:
85-
* https://support.citrix.com/article/CTX112701
86-
*/
87-
if c.arpClient == nil {
88-
log.Info("No arp client available, skip send gratuitous ARP")
89-
return nil
90-
}
91-
gratuitousReplyPackage, err := arp.NewPacket(
92-
arpReplyOp,
93-
c.Iface.HardwareAddr,
94-
c.VIP,
95-
c.Iface.HardwareAddr,
96-
c.VIP,
97-
)
98-
if err != nil {
99-
log.Infof("Gratuitous arp reply package is malformed: %s", err)
100-
return err
101-
}
102-
103-
/* RFC 2002 specifies (in section 4.6) that a gratuitous ARP request
104-
* should "not set" the target Hardware Address (THA).
105-
* Since the arp package offers no option to leave the THA out, we specify the Zero-MAC.
106-
* If parsing that fails for some reason, we'll just use the local interface's address.
107-
* The field is probably ignored by the receivers' implementation anyway.
108-
*/
109-
arpRequestDestMac, err := net.ParseMAC("00:00:00:00:00:00")
110-
if err != nil {
111-
// not entirely RFC-2002 conform but better then nothing.
112-
arpRequestDestMac = c.Iface.HardwareAddr
113-
}
114-
115-
gratuitousRequestPackage, err := arp.NewPacket(
116-
arpRequestOp,
117-
c.Iface.HardwareAddr,
118-
c.VIP,
119-
arpRequestDestMac,
120-
c.VIP,
121-
)
122-
if err != nil {
123-
log.Infof("Gratuitous arp request package is malformed: %s", err)
124-
return err
125-
}
126-
127-
for i := 0; i < c.RetryNum; i++ {
128-
errReply := c.arpClient.WriteTo(gratuitousReplyPackage, ethernetBroadcast)
129-
if err != nil {
130-
log.Error("Couldn't write to the arpClient:", errReply)
131-
} else {
132-
log.Info("Sent gratuitous ARP reply")
133-
}
134-
135-
errRequest := c.arpClient.WriteTo(gratuitousRequestPackage, ethernetBroadcast)
136-
if err != nil {
137-
log.Error("Couldn't write to the arpClient:", errRequest)
138-
} else {
139-
log.Info("Sent gratuitous ARP request")
140-
}
141-
142-
if errReply != nil || errRequest != nil {
143-
/* If something went wrong while sending the packages, we'll recreate the ARP client for the next try,
144-
* to avoid having a stale client that gives "network is down" error.
145-
*/
146-
err = c.createArpClient()
147-
} else {
148-
//TODO: think about whether to leave this out to achieve simple repeat sending of GARP packages
149-
break
150-
}
151-
time.Sleep(time.Duration(c.RetryAfter) * time.Millisecond)
152-
}
153-
if err != nil {
154-
log.Error("Too many retries", err)
155-
return err
156-
}
157-
158-
return nil
159-
}

ipmanager/basicConfigurer_windows.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@ import (
77
"github.com/cybertec-postgresql/vip-manager/iphlpapi"
88
)
99

10+
func sendPacketWindows(iface net.Interface, packetData []byte) error {
11+
// Open a raw socket using Winsock
12+
conn, err := net.Dial("ip4:ethernet", iface.HardwareAddr.String())
13+
if err != nil {
14+
return err
15+
}
16+
defer conn.Close()
17+
// Send the packet
18+
_, err = conn.Write(packetData)
19+
return err
20+
}
21+
1022
// configureAddress assigns virtual IP address
1123
func (c *BasicConfigurer) configureAddress() bool {
1224
log.Infof("Configuring address %s on %s", c.getCIDR(), c.Iface.Name)
@@ -25,10 +37,14 @@ func (c *BasicConfigurer) configureAddress() bool {
2537
log.Error("Failed to add address: ", err)
2638
return false
2739
}
28-
// For now it is save to say that also working even if a
29-
// gratuitous arp message could not be send but logging an
30-
// errror should be enough.
31-
//_ = c.ARPSendGratuitous()
40+
41+
if buff, err := c.createGratuitousARP(); err != nil {
42+
log.Warn("Failed to compose gratuitous ARP request: ", err)
43+
} else {
44+
if err := sendPacketWindows(c.Iface, buff); err != nil {
45+
log.Warn("Failed to send gratuitous ARP request: ", err)
46+
}
47+
}
3248
return true
3349
}
3450

ipmanager/hetznerConfigurer.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,3 @@ func (c *HetznerConfigurer) runAddressConfiguration() bool {
275275
c.cachedState = unknown
276276
return false
277277
}
278-
279-
func (c *HetznerConfigurer) cleanupArp() {
280-
// dummy function as the usage of interfaces requires us to have this function.
281-
// It is sufficient for the leader to tell Hetzner to switch the IP, no cleanup needed.
282-
}

0 commit comments

Comments
 (0)