mirror of
https://github.com/octoleo/syncthing.git
synced 2024-12-23 03:18:59 +00:00
Enable discovery gossiping
This commit is contained in:
parent
b783169c72
commit
1d602b9efa
@ -36,12 +36,28 @@ The Announcement packet has the following structure:
|
|||||||
0 1 2 3
|
0 1 2 3
|
||||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| Magic Number (0x029E4C77) |
|
| Magic (0x029E4C77) |
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| Length of Node ID |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
/ /
|
/ /
|
||||||
\ Node ID (variable length) \
|
\ Node Structure \
|
||||||
|
/ /
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Number of Extra Nodes |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
/ /
|
||||||
|
\ Zero or more Node Structures \
|
||||||
|
/ /
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
|
||||||
|
Node Structure:
|
||||||
|
|
||||||
|
0 1 2 3
|
||||||
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Length of ID |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
/ /
|
||||||
|
\ ID (variable length) \
|
||||||
/ /
|
/ /
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| Number of Addresses |
|
| Number of Addresses |
|
||||||
@ -62,29 +78,37 @@ The Announcement packet has the following structure:
|
|||||||
\ IP (variable length) \
|
\ IP (variable length) \
|
||||||
/ /
|
/ /
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| Port Number | 0x0000 |
|
| Port | 0x0000 |
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
|
||||||
This is the XDR encoding of:
|
This is the XDR encoding of:
|
||||||
|
|
||||||
struct Announcement {
|
struct Announcement {
|
||||||
unsigned int MagicNumber;
|
unsigned int Magic;
|
||||||
string NodeID<>;
|
Node This;
|
||||||
|
Node Extra<>;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Node {
|
||||||
|
string ID<>;
|
||||||
Address Addresses<>;
|
Address Addresses<>;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Address {
|
struct Address {
|
||||||
opaque IP<>;
|
opaque IP<>;
|
||||||
unsigned short PortNumber;
|
unsigned short Port;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeID is padded to a multiple of 32 bits and all fields are in sent in
|
The first Node structure contains information about the sending node.
|
||||||
network (big endian) byte order. In the Address structure, the IP field
|
The following zero or more Extra nodes contain information about other
|
||||||
can be of three differnt kinds;
|
nodes known to the sending node.
|
||||||
|
|
||||||
|
In the Address structure, the IP field can be of three differnt kinds;
|
||||||
|
|
||||||
- A zero length indicates that the IP address should be taken from the
|
- A zero length indicates that the IP address should be taken from the
|
||||||
source address of the announcement packet, be it IPv4 or IPv6. The
|
source address of the announcement packet, be it IPv4 or IPv6. The
|
||||||
source address must be a valid unicast address.
|
source address must be a valid unicast address. This is only valid
|
||||||
|
in the first node structure, not in the list of extras.
|
||||||
|
|
||||||
- A four byte length indicates that the address is an IPv4 unicast
|
- A four byte length indicates that the address is an IPv4 unicast
|
||||||
address.
|
address.
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
@ -109,17 +110,33 @@ func (d *Discoverer) announcementPkt() []byte {
|
|||||||
}
|
}
|
||||||
var pkt = AnnounceV2{
|
var pkt = AnnounceV2{
|
||||||
Magic: AnnouncementMagicV2,
|
Magic: AnnouncementMagicV2,
|
||||||
NodeID: d.myID,
|
This: Node{d.myID, addrs},
|
||||||
Addresses: addrs,
|
|
||||||
}
|
}
|
||||||
return pkt.MarshalXDR()
|
return pkt.MarshalXDR()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Discoverer) sendLocalAnnouncements() {
|
func (d *Discoverer) sendLocalAnnouncements() {
|
||||||
var buf = d.announcementPkt()
|
var addrs = resolveAddrs(d.listenAddrs)
|
||||||
|
|
||||||
|
var pkt = AnnounceV2{
|
||||||
|
Magic: AnnouncementMagicV2,
|
||||||
|
This: Node{d.myID, addrs},
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
d.beacon.Send(buf)
|
pkt.Extra = nil
|
||||||
|
d.registryLock.RLock()
|
||||||
|
for node, addrs := range d.registry {
|
||||||
|
if len(pkt.Extra) == 16 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
anode := Node{node, resolveAddrs(addrs)}
|
||||||
|
pkt.Extra = append(pkt.Extra, anode)
|
||||||
|
}
|
||||||
|
d.registryLock.RUnlock()
|
||||||
|
|
||||||
|
d.beacon.Send(pkt.MarshalXDR())
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-d.localBcastTick:
|
case <-d.localBcastTick:
|
||||||
@ -145,8 +162,7 @@ func (d *Discoverer) sendExternalAnnouncements() {
|
|||||||
if d.extPort != 0 {
|
if d.extPort != 0 {
|
||||||
var pkt = AnnounceV2{
|
var pkt = AnnounceV2{
|
||||||
Magic: AnnouncementMagicV2,
|
Magic: AnnouncementMagicV2,
|
||||||
NodeID: d.myID,
|
This: Node{d.myID, []Address{{Port: d.extPort}}},
|
||||||
Addresses: []Address{{Port: d.extPort}},
|
|
||||||
}
|
}
|
||||||
buf = pkt.MarshalXDR()
|
buf = pkt.MarshalXDR()
|
||||||
} else {
|
} else {
|
||||||
@ -203,7 +219,7 @@ func (d *Discoverer) recvAnnouncements() {
|
|||||||
|
|
||||||
var pkt AnnounceV2
|
var pkt AnnounceV2
|
||||||
err := pkt.UnmarshalXDR(buf)
|
err := pkt.UnmarshalXDR(buf)
|
||||||
if err != nil {
|
if err != nil && err != io.EOF {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,33 +227,53 @@ func (d *Discoverer) recvAnnouncements() {
|
|||||||
dlog.Printf("parsed announcement: %#v", pkt)
|
dlog.Printf("parsed announcement: %#v", pkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
if pkt.NodeID != d.myID {
|
var newNode bool
|
||||||
var addrs []string
|
if pkt.This.ID != d.myID {
|
||||||
for _, a := range pkt.Addresses {
|
n := d.registerNode(addr, pkt.This)
|
||||||
var nodeAddr string
|
newNode = newNode || n
|
||||||
if len(a.IP) > 0 {
|
|
||||||
nodeAddr = fmt.Sprintf("%s:%d", net.IP(a.IP), a.Port)
|
|
||||||
} else {
|
|
||||||
ua := addr.(*net.UDPAddr)
|
|
||||||
ua.Port = int(a.Port)
|
|
||||||
nodeAddr = ua.String()
|
|
||||||
}
|
}
|
||||||
addrs = append(addrs, nodeAddr)
|
for _, node := range pkt.Extra {
|
||||||
|
if node.ID != d.myID {
|
||||||
|
n := d.registerNode(nil, node)
|
||||||
|
newNode = newNode || n
|
||||||
}
|
}
|
||||||
if debug {
|
|
||||||
dlog.Printf("register: %#v", addrs)
|
|
||||||
}
|
}
|
||||||
d.registryLock.Lock()
|
|
||||||
_, seen := d.registry[pkt.NodeID]
|
if newNode {
|
||||||
if !seen {
|
|
||||||
select {
|
select {
|
||||||
case d.forcedBcastTick <- time.Now():
|
case d.forcedBcastTick <- time.Now():
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
d.registry[pkt.NodeID] = addrs
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Discoverer) registerNode(addr net.Addr, node Node) bool {
|
||||||
|
var addrs []string
|
||||||
|
for _, a := range node.Addresses {
|
||||||
|
var nodeAddr string
|
||||||
|
if len(a.IP) > 0 {
|
||||||
|
nodeAddr = fmt.Sprintf("%s:%d", net.IP(a.IP), a.Port)
|
||||||
|
addrs = append(addrs, nodeAddr)
|
||||||
|
} else if addr != nil {
|
||||||
|
ua := addr.(*net.UDPAddr)
|
||||||
|
ua.Port = int(a.Port)
|
||||||
|
nodeAddr = ua.String()
|
||||||
|
addrs = append(addrs, nodeAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(addrs) == 0 {
|
||||||
|
if debug {
|
||||||
|
dlog.Println("no valid address for", node.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if debug {
|
||||||
|
dlog.Printf("register: %s -> %#v", node.ID, addrs)
|
||||||
|
}
|
||||||
|
d.registryLock.Lock()
|
||||||
|
_, seen := d.registry[node.ID]
|
||||||
|
d.registry[node.ID] = addrs
|
||||||
d.registryLock.Unlock()
|
d.registryLock.Unlock()
|
||||||
}
|
return !seen
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Discoverer) externalLookup(node string) []string {
|
func (d *Discoverer) externalLookup(node string) []string {
|
||||||
@ -268,7 +304,7 @@ func (d *Discoverer) externalLookup(node string) []string {
|
|||||||
}
|
}
|
||||||
buffers.Put(buf)
|
buffers.Put(buf)
|
||||||
|
|
||||||
buf = buffers.Get(256)
|
buf = buffers.Get(2048)
|
||||||
defer buffers.Put(buf)
|
defer buffers.Put(buf)
|
||||||
|
|
||||||
n, err := conn.Read(buf)
|
n, err := conn.Read(buf)
|
||||||
@ -287,7 +323,7 @@ func (d *Discoverer) externalLookup(node string) []string {
|
|||||||
|
|
||||||
var pkt AnnounceV2
|
var pkt AnnounceV2
|
||||||
err = pkt.UnmarshalXDR(buf[:n])
|
err = pkt.UnmarshalXDR(buf[:n])
|
||||||
if err != nil {
|
if err != nil && err != io.EOF {
|
||||||
log.Println("discover/external/decode:", err)
|
log.Println("discover/external/decode:", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -297,9 +333,35 @@ func (d *Discoverer) externalLookup(node string) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var addrs []string
|
var addrs []string
|
||||||
for _, a := range pkt.Addresses {
|
for _, a := range pkt.This.Addresses {
|
||||||
nodeAddr := fmt.Sprintf("%s:%d", net.IP(a.IP), a.Port)
|
nodeAddr := fmt.Sprintf("%s:%d", net.IP(a.IP), a.Port)
|
||||||
addrs = append(addrs, nodeAddr)
|
addrs = append(addrs, nodeAddr)
|
||||||
}
|
}
|
||||||
return addrs
|
return addrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addrToAddr(addr *net.TCPAddr) Address {
|
||||||
|
if len(addr.IP) == 0 || addr.IP.IsUnspecified() {
|
||||||
|
return Address{Port: uint16(addr.Port)}
|
||||||
|
} else if bs := addr.IP.To4(); bs != nil {
|
||||||
|
return Address{IP: bs, Port: uint16(addr.Port)}
|
||||||
|
} else if bs := addr.IP.To16(); bs != nil {
|
||||||
|
return Address{IP: bs, Port: uint16(addr.Port)}
|
||||||
|
}
|
||||||
|
return Address{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveAddrs(addrs []string) []Address {
|
||||||
|
var raddrs []Address
|
||||||
|
for _, addrStr := range addrs {
|
||||||
|
addrRes, err := net.ResolveTCPAddr("tcp", addrStr)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
addr := addrToAddr(addrRes)
|
||||||
|
if len(addr.IP) > 0 {
|
||||||
|
raddrs = append(raddrs, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return raddrs
|
||||||
|
}
|
||||||
|
@ -12,7 +12,12 @@ type QueryV2 struct {
|
|||||||
|
|
||||||
type AnnounceV2 struct {
|
type AnnounceV2 struct {
|
||||||
Magic uint32
|
Magic uint32
|
||||||
NodeID string // max:64
|
This Node
|
||||||
|
Extra []Node // max:16
|
||||||
|
}
|
||||||
|
|
||||||
|
type Node struct {
|
||||||
|
ID string // max:64
|
||||||
Addresses []Address // max:16
|
Addresses []Address // max:16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,16 +59,13 @@ func (o AnnounceV2) MarshalXDR() []byte {
|
|||||||
|
|
||||||
func (o AnnounceV2) encodeXDR(xw *xdr.Writer) (int, error) {
|
func (o AnnounceV2) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||||
xw.WriteUint32(o.Magic)
|
xw.WriteUint32(o.Magic)
|
||||||
if len(o.NodeID) > 64 {
|
o.This.encodeXDR(xw)
|
||||||
|
if len(o.Extra) > 16 {
|
||||||
return xw.Tot(), xdr.ErrElementSizeExceeded
|
return xw.Tot(), xdr.ErrElementSizeExceeded
|
||||||
}
|
}
|
||||||
xw.WriteString(o.NodeID)
|
xw.WriteUint32(uint32(len(o.Extra)))
|
||||||
if len(o.Addresses) > 16 {
|
for i := range o.Extra {
|
||||||
return xw.Tot(), xdr.ErrElementSizeExceeded
|
o.Extra[i].encodeXDR(xw)
|
||||||
}
|
|
||||||
xw.WriteUint32(uint32(len(o.Addresses)))
|
|
||||||
for i := range o.Addresses {
|
|
||||||
o.Addresses[i].encodeXDR(xw)
|
|
||||||
}
|
}
|
||||||
return xw.Tot(), xw.Error()
|
return xw.Tot(), xw.Error()
|
||||||
}
|
}
|
||||||
@ -86,7 +83,58 @@ func (o *AnnounceV2) UnmarshalXDR(bs []byte) error {
|
|||||||
|
|
||||||
func (o *AnnounceV2) decodeXDR(xr *xdr.Reader) error {
|
func (o *AnnounceV2) decodeXDR(xr *xdr.Reader) error {
|
||||||
o.Magic = xr.ReadUint32()
|
o.Magic = xr.ReadUint32()
|
||||||
o.NodeID = xr.ReadStringMax(64)
|
(&o.This).decodeXDR(xr)
|
||||||
|
_ExtraSize := int(xr.ReadUint32())
|
||||||
|
if _ExtraSize > 16 {
|
||||||
|
return xdr.ErrElementSizeExceeded
|
||||||
|
}
|
||||||
|
o.Extra = make([]Node, _ExtraSize)
|
||||||
|
for i := range o.Extra {
|
||||||
|
(&o.Extra[i]).decodeXDR(xr)
|
||||||
|
}
|
||||||
|
return xr.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o Node) EncodeXDR(w io.Writer) (int, error) {
|
||||||
|
var xw = xdr.NewWriter(w)
|
||||||
|
return o.encodeXDR(xw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o Node) MarshalXDR() []byte {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
var xw = xdr.NewWriter(&buf)
|
||||||
|
o.encodeXDR(xw)
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o Node) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||||
|
if len(o.ID) > 64 {
|
||||||
|
return xw.Tot(), xdr.ErrElementSizeExceeded
|
||||||
|
}
|
||||||
|
xw.WriteString(o.ID)
|
||||||
|
if len(o.Addresses) > 16 {
|
||||||
|
return xw.Tot(), xdr.ErrElementSizeExceeded
|
||||||
|
}
|
||||||
|
xw.WriteUint32(uint32(len(o.Addresses)))
|
||||||
|
for i := range o.Addresses {
|
||||||
|
o.Addresses[i].encodeXDR(xw)
|
||||||
|
}
|
||||||
|
return xw.Tot(), xw.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Node) DecodeXDR(r io.Reader) error {
|
||||||
|
xr := xdr.NewReader(r)
|
||||||
|
return o.decodeXDR(xr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Node) UnmarshalXDR(bs []byte) error {
|
||||||
|
var buf = bytes.NewBuffer(bs)
|
||||||
|
var xr = xdr.NewReader(buf)
|
||||||
|
return o.decodeXDR(xr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Node) decodeXDR(xr *xdr.Reader) error {
|
||||||
|
o.ID = xr.ReadStringMax(64)
|
||||||
_AddressesSize := int(xr.ReadUint32())
|
_AddressesSize := int(xr.ReadUint32())
|
||||||
if _AddressesSize > 16 {
|
if _AddressesSize > 16 {
|
||||||
return xdr.ErrElementSizeExceeded
|
return xdr.ErrElementSizeExceeded
|
||||||
|
@ -72,20 +72,25 @@ func (r *Reader) ReadUint16() uint16 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reader) ReadUint32() uint32 {
|
func (r *Reader) ReadUint32() uint32 {
|
||||||
|
var n int
|
||||||
if r.err != nil {
|
if r.err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
_, r.err = io.ReadFull(r.r, r.b[:4])
|
n, r.err = io.ReadFull(r.r, r.b[:4])
|
||||||
r.tot += 4
|
if n < 4 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
r.tot += n
|
||||||
return uint32(r.b[3]) | uint32(r.b[2])<<8 | uint32(r.b[1])<<16 | uint32(r.b[0])<<24
|
return uint32(r.b[3]) | uint32(r.b[2])<<8 | uint32(r.b[1])<<16 | uint32(r.b[0])<<24
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reader) ReadUint64() uint64 {
|
func (r *Reader) ReadUint64() uint64 {
|
||||||
|
var n int
|
||||||
if r.err != nil {
|
if r.err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
_, r.err = io.ReadFull(r.r, r.b[:8])
|
n, r.err = io.ReadFull(r.r, r.b[:8])
|
||||||
r.tot += 8
|
r.tot += n
|
||||||
return uint64(r.b[7]) | uint64(r.b[6])<<8 | uint64(r.b[5])<<16 | uint64(r.b[4])<<24 |
|
return uint64(r.b[7]) | uint64(r.b[6])<<8 | uint64(r.b[5])<<16 | uint64(r.b[4])<<24 |
|
||||||
uint64(r.b[3])<<32 | uint64(r.b[2])<<40 | uint64(r.b[1])<<48 | uint64(r.b[0])<<56
|
uint64(r.b[3])<<32 | uint64(r.b[2])<<40 | uint64(r.b[1])<<48 | uint64(r.b[0])<<56
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user