forked from hashicorp/memberlist
/
net.go
714 lines (615 loc) · 17.8 KB
/
net.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
package memberlist
import (
"bufio"
"bytes"
"encoding/binary"
"fmt"
"github.com/ugorji/go/codec"
"io"
"net"
"time"
)
// This is the minimum and maximum protocol version that we can
// _understand_. We're allowed to speak at any version within this
// range. This range is inclusive.
const (
ProtocolVersionMin uint8 = 0
ProtocolVersionMax = 1
)
// messageType is an integer ID of a type of message that can be received
// on network channels from other members.
type messageType uint8
// The list of available message types.
const (
pingMsg messageType = iota
indirectPingMsg
ackRespMsg
suspectMsg
aliveMsg
deadMsg
pushPullMsg
compoundMsg
userMsg // User mesg, not handled by us
compressMsg
encryptMsg
)
// compressionType is used to specify the compression algorithm
type compressionType uint8
const (
deflateAlgo compressionType = iota
)
const (
compoundHeaderOverhead = 2 // Assumed header overhead
compoundOverhead = 2 // Assumed overhead per entry in compoundHeader
metaMaxSize = 128 // Maximum size for nod emeta data
udpBufSize = 65536
udpRecvBuf = 2 * 1024 * 1024
udpSendBuf = 1400
userMsgOverhead = 1
blockingWarning = 10 * time.Millisecond // Warn if a UDP packet takes this long to process
maxPushStateBytes = 10 * 1024 * 1024
encryptOverhead = 44 // Version: 1, IV: 12, Padding: 15, Tag: 16
)
// ping request sent directly to node
type ping struct {
SeqNo uint32
}
// indirect ping sent to an indirect ndoe
type indirectPingReq struct {
SeqNo uint32
Target []byte
}
// ack response is sent for a ping
type ackResp struct {
SeqNo uint32
}
// suspect is broadcast when we suspect a node is dead
type suspect struct {
Incarnation uint32
Node string
}
// alive is broadcast when we know a node is alive.
// Overloaded for nodes joining
type alive struct {
Incarnation uint32
Node string
Addr []byte
Meta []byte
// The versions of the protocol/delegate that are being spoken, order:
// pmin, pmax, pcur, dmin, dmax, dcur
Vsn []uint8
}
// dead is broadcast when we confirm a node is dead
// Overloaded for nodes leaving
type dead struct {
Incarnation uint32
Node string
}
// pushPullHeader is used to inform the
// otherside how many states we are transfering
type pushPullHeader struct {
Nodes int
UserStateLen int // Encodes the byte lengh of user state
}
// pushNodeState is used for pushPullReq when we are
// transfering out node states
type pushNodeState struct {
Name string
Addr []byte
Meta []byte
Incarnation uint32
State nodeStateType
Vsn []uint8 // Protocol versions
}
// compress is used to wrap an underlying payload
// using a specified compression algorithm
type compress struct {
Algo compressionType
Buf []byte
}
// setUDPRecvBuf is used to resize the UDP receive window. The function
// attempts to set the read buffer to `udpRecvBuf` but backs off until
// the read buffer can be set.
func setUDPRecvBuf(c *net.UDPConn) {
size := udpRecvBuf
for {
if err := c.SetReadBuffer(size); err == nil {
break
}
size = size / 2
}
}
// tcpListen listens for and handles incoming connections
func (m *Memberlist) tcpListen() {
for {
conn, err := m.tcpListener.AcceptTCP()
if err != nil {
if m.shutdown {
break
}
m.logger.Printf("[ERR] Error accepting TCP connection: %s", err)
continue
}
go m.handleConn(conn)
}
}
// handleConn handles a single incoming TCP connection
func (m *Memberlist) handleConn(conn *net.TCPConn) {
m.logger.Printf("[INFO] Responding to push/pull sync with: %s", conn.RemoteAddr())
defer conn.Close()
remoteNodes, userState, err := m.readRemoteState(conn)
if err != nil {
m.logger.Printf("[ERR] Failed to receive remote state: %s", err)
return
}
if err := m.sendLocalState(conn); err != nil {
m.logger.Printf("[ERR] Failed to push local state: %s", err)
}
if err := m.verifyProtocol(remoteNodes); err != nil {
m.logger.Printf("[ERR] Push/pull verification failed: %s", err)
return
}
// Merge the membership state
m.mergeState(remoteNodes)
// Invoke the delegate for user state
if m.config.Delegate != nil {
m.config.Delegate.MergeRemoteState(userState)
}
}
// udpListen listens for and handles incoming UDP packets
func (m *Memberlist) udpListen() {
mainBuf := make([]byte, udpBufSize)
var n int
var addr net.Addr
var err error
var lastPacket time.Time
for {
// Do a check for potentially blocking operations
if !lastPacket.IsZero() && time.Now().Sub(lastPacket) > blockingWarning {
diff := time.Now().Sub(lastPacket)
m.logger.Printf(
"[WARN] Potential blocking operation. Last command took %v",
diff)
}
// Reset buffer
buf := mainBuf[0:udpBufSize]
// Read a packet
n, addr, err = m.udpListener.ReadFrom(buf)
if err != nil {
if m.shutdown {
break
}
m.logger.Printf("[ERR] Error reading UDP packet: %s", err)
continue
}
// Check the length
if n < 1 {
m.logger.Printf("[ERR] UDP packet too short (%d bytes). From: %s",
len(buf), addr)
continue
}
// Capture the current time
lastPacket = time.Now()
// Ingest this packet
m.ingestPacket(buf[:n], addr)
}
}
func (m *Memberlist) ingestPacket(buf []byte, from net.Addr) {
// Check if encryption is enabled
if m.config.SecretKey != nil {
// Decrypt the payload
plain, err := decryptPayload(m.config.SecretKey, buf, nil)
if err != nil {
m.logger.Printf("[ERR] Decrypt packet failed: %v", err)
return
}
// Continue processing the plaintext buffer
buf = plain
}
// Handle the command
m.handleCommand(buf, from)
}
func (m *Memberlist) handleCommand(buf []byte, from net.Addr) {
// Decode the message type
msgType := messageType(buf[0])
buf = buf[1:]
// Switch on the msgType
switch msgType {
case compoundMsg:
m.handleCompound(buf, from)
case pingMsg:
m.handlePing(buf, from)
case indirectPingMsg:
m.handleIndirectPing(buf, from)
case ackRespMsg:
m.handleAck(buf, from)
case suspectMsg:
m.handleSuspect(buf, from)
case aliveMsg:
m.handleAlive(buf, from)
case deadMsg:
m.handleDead(buf, from)
case userMsg:
m.handleUser(buf, from)
case compressMsg:
m.handleCompressed(buf, from)
default:
m.logger.Printf("[ERR] UDP msg type (%d) not supported. From: %s", msgType, from)
}
}
func (m *Memberlist) handleCompound(buf []byte, from net.Addr) {
// Decode the parts
trunc, parts, err := decodeCompoundMessage(buf)
if err != nil {
m.logger.Printf("[ERR] Failed to decode compound request: %s", err)
return
}
// Log any truncation
if trunc > 0 {
m.logger.Printf("[WARN] Compound request had %d truncated messages", trunc)
}
// Handle each message
for _, part := range parts {
m.handleCommand(part, from)
}
}
func (m *Memberlist) handlePing(buf []byte, from net.Addr) {
var p ping
if err := decode(buf, &p); err != nil {
m.logger.Printf("[ERR] Failed to decode ping request: %s", err)
return
}
ack := ackResp{p.SeqNo}
if err := m.encodeAndSendMsg(from, ackRespMsg, &ack); err != nil {
m.logger.Printf("[ERR] Failed to send ack: %s", err)
}
}
func (m *Memberlist) handleIndirectPing(buf []byte, from net.Addr) {
var ind indirectPingReq
if err := decode(buf, &ind); err != nil {
m.logger.Printf("[ERR] Failed to decode indirect ping request: %s", err)
return
}
// Send a ping to the correct host
localSeqNo := m.nextSeqNo()
ping := ping{SeqNo: localSeqNo}
destAddr := &net.UDPAddr{IP: ind.Target, Port: m.config.UDPPort}
// Setup a response handler to relay the ack
respHandler := func() {
ack := ackResp{ind.SeqNo}
if err := m.encodeAndSendMsg(from, ackRespMsg, &ack); err != nil {
m.logger.Printf("[ERR] Failed to forward ack: %s", err)
}
}
m.setAckHandler(localSeqNo, respHandler, m.config.ProbeTimeout)
// Send the ping
if err := m.encodeAndSendMsg(destAddr, pingMsg, &ping); err != nil {
m.logger.Printf("[ERR] Failed to send ping: %s", err)
}
}
func (m *Memberlist) handleAck(buf []byte, from net.Addr) {
var ack ackResp
if err := decode(buf, &ack); err != nil {
m.logger.Printf("[ERR] Failed to decode ack response: %s", err)
return
}
m.invokeAckHandler(ack.SeqNo)
}
func (m *Memberlist) handleSuspect(buf []byte, from net.Addr) {
var sus suspect
if err := decode(buf, &sus); err != nil {
m.logger.Printf("[ERR] Failed to decode suspect message: %s", err)
return
}
m.suspectNode(&sus)
}
func (m *Memberlist) handleAlive(buf []byte, from net.Addr) {
var live alive
if err := decode(buf, &live); err != nil {
m.logger.Printf("[ERR] Failed to decode alive message: %s", err)
return
}
m.aliveNode(&live)
}
func (m *Memberlist) handleDead(buf []byte, from net.Addr) {
var d dead
if err := decode(buf, &d); err != nil {
m.logger.Printf("[ERR] Failed to decode dead message: %s", err)
return
}
m.deadNode(&d)
}
// handleUser is used to notify channels of incoming user data
func (m *Memberlist) handleUser(buf []byte, from net.Addr) {
d := m.config.Delegate
if d != nil {
d.NotifyMsg(buf)
}
}
// handleCompressed is used to unpack a compressed message
func (m *Memberlist) handleCompressed(buf []byte, from net.Addr) {
// Try to decode the payload
payload, err := decompressPayload(buf)
if err != nil {
m.logger.Printf("[ERR] Failed to decompress payload: %v", err)
return
}
// Recursively handle the payload
m.handleCommand(payload, from)
}
// encodeAndSendMsg is used to combine the encoding and sending steps
func (m *Memberlist) encodeAndSendMsg(to net.Addr, msgType messageType, msg interface{}) error {
out, err := encode(msgType, msg)
if err != nil {
return err
}
if err := m.sendMsg(to, out.Bytes()); err != nil {
return err
}
return nil
}
// sendMsg is used to send a UDP message to another host. It will opportunistically
// create a compoundMsg and piggy back other broadcasts
func (m *Memberlist) sendMsg(to net.Addr, msg []byte) error {
// Check if we can piggy back any messages
bytesAvail := udpSendBuf - len(msg) - compoundHeaderOverhead
if m.config.SecretKey != nil {
bytesAvail -= encryptOverhead
}
extra := m.getBroadcasts(compoundOverhead, bytesAvail)
// Fast path if nothing to piggypack
if len(extra) == 0 {
return m.rawSendMsg(to, msg)
}
// Join all the messages
msgs := make([][]byte, 0, 1+len(extra))
msgs = append(msgs, msg)
msgs = append(msgs, extra...)
// Create a compound message
compound := makeCompoundMessage(msgs)
// Send the message
return m.rawSendMsg(to, compound.Bytes())
}
// rawSendMsg is used to send a UDP message to another host without modification
func (m *Memberlist) rawSendMsg(to net.Addr, msg []byte) error {
// Check if we have compression enabled
if m.config.EnableCompression && m.ProtocolVersion() >= 1 {
buf, err := compressPayload(msg)
if err != nil {
m.logger.Printf("[WARN] Failed to compress payload: %v", err)
} else {
msg = buf.Bytes()
}
}
// Check if we have encryption enabled
if m.config.SecretKey != nil {
// Encrypt the payload
var buf bytes.Buffer
err := encryptPayload(m.config.SecretKey, msg, nil, &buf)
if err != nil {
m.logger.Printf("[ERR] Encryption of message failed: %v", err)
return err
}
msg = buf.Bytes()
}
_, err := m.udpListener.WriteTo(msg, to)
return err
}
// sendState is used to initiate a push/pull over TCP with a remote node
func (m *Memberlist) sendAndReceiveState(addr []byte) ([]pushNodeState, []byte, error) {
// Attempt to connect
dialer := net.Dialer{Timeout: m.config.TCPTimeout}
dest := net.TCPAddr{IP: addr, Port: m.config.TCPPort}
conn, err := dialer.Dial("tcp", dest.String())
if err != nil {
return nil, nil, err
}
defer conn.Close()
m.logger.Printf("[INFO] Initiating push/pull sync with: %s", conn.RemoteAddr())
// Send our state
if err := m.sendLocalState(conn); err != nil {
return nil, nil, err
}
// Read remote state
remote, userState, err := m.readRemoteState(conn)
if err != nil {
err := fmt.Errorf("Reading remote state failed: %v", err)
return nil, nil, err
}
// Return the remote state
return remote, userState, nil
}
// sendLocalState is invoked to send our local state over a tcp connection
func (m *Memberlist) sendLocalState(conn net.Conn) error {
// Prepare the local node state
m.nodeLock.RLock()
localNodes := make([]pushNodeState, len(m.nodes))
for idx, n := range m.nodes {
localNodes[idx].Name = n.Name
localNodes[idx].Addr = n.Addr
localNodes[idx].Incarnation = n.Incarnation
localNodes[idx].State = n.State
localNodes[idx].Meta = n.Meta
localNodes[idx].Vsn = []uint8{
n.PMin, n.PMax, n.PCur,
n.DMin, n.DMax, n.DCur,
}
}
m.nodeLock.RUnlock()
// Get the delegate state
var userData []byte
if m.config.Delegate != nil {
userData = m.config.Delegate.LocalState()
}
// Create a bytes buffer writer
bufConn := bytes.NewBuffer(nil)
// Send our node state
header := pushPullHeader{Nodes: len(localNodes), UserStateLen: len(userData)}
hd := codec.MsgpackHandle{}
enc := codec.NewEncoder(bufConn, &hd)
// Begin state push
if _, err := bufConn.Write([]byte{byte(pushPullMsg)}); err != nil {
return err
}
if err := enc.Encode(&header); err != nil {
return err
}
for i := 0; i < header.Nodes; i++ {
if err := enc.Encode(&localNodes[i]); err != nil {
return err
}
}
// Write the user state as well
if userData != nil {
if _, err := bufConn.Write(userData); err != nil {
return err
}
}
// Get the send buffer
sendBuf := bufConn.Bytes()
// Check if compresion is enabled
if m.config.EnableCompression && m.ProtocolVersion() >= 1 {
compBuf, err := compressPayload(bufConn.Bytes())
if err != nil {
m.logger.Printf("[ERROR] Failed to compress local state: %v", err)
} else {
sendBuf = compBuf.Bytes()
}
}
// Check if encryption is enabled
if m.config.SecretKey != nil {
crypt, err := m.encryptLocalState(sendBuf)
if err != nil {
m.logger.Printf("[ERROR] Failed to encrypt local state: %v", err)
return err
}
sendBuf = crypt
}
// Write out the entire send buffer
if _, err := conn.Write(sendBuf); err != nil {
return err
}
return nil
}
// encryptLocalState is used to help encrypt local state before sending
func (m *Memberlist) encryptLocalState(sendBuf []byte) ([]byte, error) {
var buf bytes.Buffer
// Write the encryptMsg byte
buf.WriteByte(byte(encryptMsg))
// Write the size of the message
sizeBuf := make([]byte, 4)
binary.BigEndian.PutUint32(sizeBuf, uint32(encryptedLength(len(sendBuf))))
buf.Write(sizeBuf)
// Write the encrypted cipher text to the buffer
err := encryptPayload(m.config.SecretKey, sendBuf, buf.Bytes()[:5], &buf)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// decryptRemoteState is used to help decrypt the remote state
func (m *Memberlist) decryptRemoteState(bufConn io.Reader) ([]byte, error) {
// Read in enough to determine message length
cipherText := bytes.NewBuffer(nil)
cipherText.WriteByte(byte(encryptMsg))
_, err := io.CopyN(cipherText, bufConn, 4)
if err != nil {
return nil, err
}
// Ensure we aren't asked to download too much. This is to guard against
// an attack vector where a huge amount of state is sent
moreBytes := binary.BigEndian.Uint32(cipherText.Bytes()[1:5])
if moreBytes > maxPushStateBytes {
return nil, fmt.Errorf("Remote node state is larger than limit (%d)", moreBytes)
}
// Read in the rest of the payload
_, err = io.CopyN(cipherText, bufConn, int64(moreBytes))
if err != nil {
return nil, err
}
// Decrypt the cipherText
dataBytes := cipherText.Bytes()[:5]
cipherBytes := cipherText.Bytes()[5:]
return decryptPayload(m.config.SecretKey, cipherBytes, dataBytes)
}
// recvRemoteState is used to read the remote state from a connection
func (m *Memberlist) readRemoteState(conn net.Conn) ([]pushNodeState, []byte, error) {
// Created a buffered reader
var bufConn io.Reader = bufio.NewReader(conn)
// Read the message type
buf := [1]byte{0}
if _, err := bufConn.Read(buf[:]); err != nil {
return nil, nil, err
}
msgType := messageType(buf[0])
// Check if the message is encrypted
if msgType == encryptMsg {
if m.config.SecretKey == nil {
return nil, nil,
fmt.Errorf("Remote state is encrypted and SecretKey is not configured")
}
plain, err := m.decryptRemoteState(bufConn)
if err != nil {
return nil, nil, err
}
// Reset message type and bufConn
msgType = messageType(plain[0])
bufConn = bytes.NewReader(plain[1:])
} else if m.config.SecretKey != nil {
return nil, nil,
fmt.Errorf("SecretKey is configured but remote state is not encrypted")
}
// Get the msgPack decoders
hd := codec.MsgpackHandle{}
dec := codec.NewDecoder(bufConn, &hd)
// Check if we have a compressed message
if msgType == compressMsg {
var c compress
if err := dec.Decode(&c); err != nil {
return nil, nil, err
}
decomp, err := decompressBuffer(&c)
if err != nil {
return nil, nil, err
}
// Reset the message type
msgType = messageType(decomp[0])
// Create a new bufConn
bufConn = bytes.NewReader(decomp[1:])
// Create a new decoder
dec = codec.NewDecoder(bufConn, &hd)
}
// Quit if not push/pull
if msgType != pushPullMsg {
err := fmt.Errorf("received invalid msgType (%d)", msgType)
return nil, nil, err
}
// Read the push/pull header
var header pushPullHeader
if err := dec.Decode(&header); err != nil {
return nil, nil, err
}
// Allocate space for the transfer
remoteNodes := make([]pushNodeState, header.Nodes)
// Try to decode all the states
for i := 0; i < header.Nodes; i++ {
if err := dec.Decode(&remoteNodes[i]); err != nil {
return remoteNodes, nil, err
}
}
// Read the remote user state into a buffer
var userBuf []byte
if header.UserStateLen > 0 {
userBuf = make([]byte, header.UserStateLen)
bytes, err := bufConn.Read(userBuf)
if err == nil && bytes != header.UserStateLen {
err = fmt.Errorf(
"Failed to read full user state (%d / %d)",
bytes, header.UserStateLen)
}
if err != nil {
return remoteNodes, nil, err
}
}
return remoteNodes, userBuf, nil
}