Monitor outbound links periodically and reconnect the failed links.
This commit is contained in:
parent
740cfab8d0
commit
f120452d28
@ -14,8 +14,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// DefaultKeepAlive defines interval we check if the links are alive
|
// KeepAliveTime defines time interval we send keepalive messages to outbound links
|
||||||
DefaultKeepAlive = 30 * time.Second
|
KeepAliveTime = 30 * time.Second
|
||||||
|
// ReconnectTime defines time interval we periodically attempt to reconnect dead links
|
||||||
|
ReconnectTime = 5 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
// tun represents a network tunnel
|
// tun represents a network tunnel
|
||||||
@ -126,9 +128,30 @@ func (t *tun) newSession() string {
|
|||||||
return uuid.New().String()
|
return uuid.New().String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// monitor monitors all links and attempts to dial the failed ones
|
// monitor monitors outbound links and attempts to reconnect to the failed ones
|
||||||
func (t *tun) monitor() {
|
func (t *tun) monitor() {
|
||||||
// TODO: implement this
|
reconnect := time.NewTicker(ReconnectTime)
|
||||||
|
defer reconnect.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-t.closed:
|
||||||
|
return
|
||||||
|
case <-reconnect.C:
|
||||||
|
for _, node := range t.options.Nodes {
|
||||||
|
t.Lock()
|
||||||
|
if _, ok := t.links[node]; !ok {
|
||||||
|
link, err := t.nodeLink(node)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("Tunnel failed to establish node link to %s: %v", node, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.links[node] = link
|
||||||
|
}
|
||||||
|
t.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// process outgoing messages sent by all local sockets
|
// process outgoing messages sent by all local sockets
|
||||||
@ -159,17 +182,17 @@ func (t *tun) process() {
|
|||||||
// send the message via the interface
|
// send the message via the interface
|
||||||
t.Lock()
|
t.Lock()
|
||||||
if len(t.links) == 0 {
|
if len(t.links) == 0 {
|
||||||
log.Debugf("Zero links to send to")
|
log.Debugf("No links to send to")
|
||||||
}
|
}
|
||||||
for _, link := range t.links {
|
for node, link := range t.links {
|
||||||
if link.loopback && msg.outbound {
|
if link.loopback && msg.outbound {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.Debugf("Sending %+v to %s", newMsg, link.Remote())
|
log.Debugf("Sending %+v to %s", newMsg, node)
|
||||||
if err := link.Send(newMsg); err != nil {
|
if err := link.Send(newMsg); err != nil {
|
||||||
log.Debugf("Error sending %+v to %s: %v", newMsg, link.Remote(), err)
|
log.Debugf("Error sending %+v to %s: %v", newMsg, node, err)
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
delete(t.links, link.id)
|
delete(t.links, node)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,6 +212,11 @@ func (t *tun) listen(link *link) {
|
|||||||
err := link.Recv(msg)
|
err := link.Recv(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("Tunnel link %s receive error: %v", link.Remote(), err)
|
log.Debugf("Tunnel link %s receive error: %v", link.Remote(), err)
|
||||||
|
if err == io.EOF {
|
||||||
|
t.Lock()
|
||||||
|
delete(t.links, link.Remote())
|
||||||
|
t.Unlock()
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,7 +239,7 @@ func (t *tun) listen(link *link) {
|
|||||||
// TODO: handle the close message
|
// TODO: handle the close message
|
||||||
// maybe report io.EOF or kill the link
|
// maybe report io.EOF or kill the link
|
||||||
continue
|
continue
|
||||||
case "ping":
|
case "keepalive":
|
||||||
log.Debugf("Tunnel link %s received keepalive", link.Remote())
|
log.Debugf("Tunnel link %s received keepalive", link.Remote())
|
||||||
link.lastKeepAlive = time.Now()
|
link.lastKeepAlive = time.Now()
|
||||||
continue
|
continue
|
||||||
@ -306,27 +334,28 @@ func (t *tun) listen(link *link) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// keepalive periodically sends ping messages to link
|
// keepalive periodically sends keepalive messages to link
|
||||||
func (t *tun) keepalive(link *link) {
|
func (t *tun) keepalive(link *link) {
|
||||||
ping := time.NewTicker(DefaultKeepAlive)
|
keepalive := time.NewTicker(KeepAliveTime)
|
||||||
defer ping.Stop()
|
defer keepalive.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-t.closed:
|
case <-t.closed:
|
||||||
return
|
return
|
||||||
case <-ping.C:
|
case <-keepalive.C:
|
||||||
// send ping message
|
// send keepalive message
|
||||||
log.Debugf("Tunnel sending ping to link %v: %v", link.Remote(), link.id)
|
log.Debugf("Tunnel sending keepalive to link: %v", link.Remote())
|
||||||
if err := link.Send(&transport.Message{
|
if err := link.Send(&transport.Message{
|
||||||
Header: map[string]string{
|
Header: map[string]string{
|
||||||
"Micro-Tunnel": "ping",
|
"Micro-Tunnel": "keepalive",
|
||||||
"Micro-Tunnel-Token": t.token,
|
"Micro-Tunnel-Token": t.token,
|
||||||
},
|
},
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
|
log.Debugf("Error sending keepalive to link %v: %v", link.Remote(), err)
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
t.Lock()
|
t.Lock()
|
||||||
delete(t.links, link.id)
|
delete(t.links, link.Remote())
|
||||||
t.Unlock()
|
t.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -336,6 +365,43 @@ func (t *tun) keepalive(link *link) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nodeLink attempts to connect to node and returns link if successful
|
||||||
|
// it returns error if the link failed to be established
|
||||||
|
func (t *tun) nodeLink(node string) (*link, error) {
|
||||||
|
log.Debugf("Tunnel dialing %s", node)
|
||||||
|
c, err := t.options.Transport.Dial(node)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("Tunnel failed to connect to %s: %v", node, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debugf("Tunnel connected to %s", node)
|
||||||
|
|
||||||
|
if err := c.Send(&transport.Message{
|
||||||
|
Header: map[string]string{
|
||||||
|
"Micro-Tunnel": "connect",
|
||||||
|
"Micro-Tunnel-Token": t.token,
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the link
|
||||||
|
id := uuid.New().String()
|
||||||
|
link := &link{
|
||||||
|
Socket: c,
|
||||||
|
id: id,
|
||||||
|
}
|
||||||
|
t.links[node] = link
|
||||||
|
|
||||||
|
// process incoming messages
|
||||||
|
go t.listen(link)
|
||||||
|
|
||||||
|
// start keepalive monitor
|
||||||
|
go t.keepalive(link)
|
||||||
|
|
||||||
|
return link, nil
|
||||||
|
}
|
||||||
|
|
||||||
// connect the tunnel to all the nodes and listen for incoming tunnel connections
|
// connect the tunnel to all the nodes and listen for incoming tunnel connections
|
||||||
func (t *tun) connect() error {
|
func (t *tun) connect() error {
|
||||||
l, err := t.options.Transport.Listen(t.options.Address)
|
l, err := t.options.Transport.Listen(t.options.Address)
|
||||||
@ -357,14 +423,14 @@ func (t *tun) connect() error {
|
|||||||
Socket: sock,
|
Socket: sock,
|
||||||
id: id,
|
id: id,
|
||||||
}
|
}
|
||||||
t.links[id] = link
|
t.links[sock.Remote()] = link
|
||||||
t.Unlock()
|
t.Unlock()
|
||||||
|
|
||||||
// delete the link
|
// delete the link
|
||||||
defer func() {
|
defer func() {
|
||||||
log.Debugf("Deleting connection from %s", sock.Remote())
|
log.Debugf("Deleting connection from %s", sock.Remote())
|
||||||
t.Lock()
|
t.Lock()
|
||||||
delete(t.links, id)
|
delete(t.links, sock.Remote())
|
||||||
t.Unlock()
|
t.Unlock()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -387,43 +453,23 @@ func (t *tun) connect() error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("Tunnel dialing %s", node)
|
// connect to node and return link
|
||||||
// TODO: reconnection logic is required to keep the tunnel established
|
link, err := t.nodeLink(node)
|
||||||
c, err := t.options.Transport.Dial(node)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("Tunnel failed to connect to %s: %v", node, err)
|
log.Debugf("Tunnel failed to establish node link to %s: %v", node, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.Debugf("Tunnel connected to %s", node)
|
|
||||||
|
|
||||||
if err := c.Send(&transport.Message{
|
|
||||||
Header: map[string]string{
|
|
||||||
"Micro-Tunnel": "connect",
|
|
||||||
"Micro-Tunnel-Token": t.token,
|
|
||||||
},
|
|
||||||
}); err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// save the link
|
// save the link
|
||||||
id := uuid.New().String()
|
t.links[node] = link
|
||||||
link := &link{
|
|
||||||
Socket: c,
|
|
||||||
id: id,
|
|
||||||
}
|
|
||||||
t.links[id] = link
|
|
||||||
|
|
||||||
// process incoming messages
|
|
||||||
go t.listen(link)
|
|
||||||
|
|
||||||
// start keepalive monitor
|
|
||||||
go t.keepalive(link)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// process outbound messages to be sent
|
// process outbound messages to be sent
|
||||||
// process sends to all links
|
// process sends to all links
|
||||||
go t.process()
|
go t.process()
|
||||||
|
|
||||||
|
// monitor links
|
||||||
|
go t.monitor()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -452,7 +498,7 @@ func (t *tun) Connect() error {
|
|||||||
|
|
||||||
func (t *tun) close() error {
|
func (t *tun) close() error {
|
||||||
// close all the links
|
// close all the links
|
||||||
for id, link := range t.links {
|
for node, link := range t.links {
|
||||||
link.Send(&transport.Message{
|
link.Send(&transport.Message{
|
||||||
Header: map[string]string{
|
Header: map[string]string{
|
||||||
"Micro-Tunnel": "close",
|
"Micro-Tunnel": "close",
|
||||||
@ -460,7 +506,7 @@ func (t *tun) close() error {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
link.Close()
|
link.Close()
|
||||||
delete(t.links, id)
|
delete(t.links, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
// close the listener
|
// close the listener
|
||||||
|
Loading…
Reference in New Issue
Block a user