Add listener bind interface check to wait for interface to become available in the event that the system has just booted and the bind is to a virtual interface. Also added better service signal loop break handling.

This commit is contained in:
GRMrGecko 2025-01-07 21:11:28 -06:00
parent 1ba1460b67
commit 5dd0717085
3 changed files with 53 additions and 18 deletions

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"net" "net"
"sync" "sync"
"time"
"github.com/google/gopacket" "github.com/google/gopacket"
"github.com/google/gopacket/layers" "github.com/google/gopacket/layers"
@ -68,13 +69,55 @@ func NewListener(name, address string, maxMessageSize int, perm bool) (l *Listen
} }
} }
// On Windows, there is no public way to configure hardware vxlan offloading. llog := log.WithFields(log.Fields{
// This in term filters packets that are destined to non-broadcast MAC addresses "listener": addr.String(),
// in vxlan packets. Which prevents us from receiving the packets, so we set the })
// interface to promiscuous mode to allow us to receive packets. We can only do
// this if the IP address provided is an absolute address. // Check if we're binding to an interface.
var promisc *Promiscuous var promisc *Promiscuous
if !isZeroAddr(addr.IP) { if !isZeroAddr(addr.IP) {
// Check that the interface exists, on Windows it may be a bit before the interface
// becomes available.
tries := 0
tryLoop:
for {
// Get interface list.
ifaces, err := net.Interfaces()
if err != nil {
return nil, fmt.Errorf("unable to get interface list %s", err)
}
// Check the IP list of each interface for a matching IP to bind addr.
for _, iface := range ifaces {
addrs, err := iface.Addrs()
if err != nil {
continue
}
// If this address has the bind address, we can stop here.
for _, address := range addrs {
ipAddr, _, _ := net.ParseCIDR(address.String())
if addr.IP.Equal(ipAddr) {
break tryLoop
}
}
}
// If the bind address wasn't found on an interface, try again for 5 minutes.
tries++
if tries < 5 {
llog.Print("Unable to find interface, will check again in 1 minute...")
time.Sleep(time.Minute)
} else {
// If we passed 5 minutes, we should stop...
break
}
}
// On Windows, there is no public way to configure hardware vxlan offloading.
// This in term filters packets that are destined to non-broadcast MAC addresses
// in vxlan packets. Which prevents us from receiving the packets, so we set the
// interface to promiscuous mode to allow us to receive packets.
promisc, err = SetInterfacePromiscuous(addr.IP) promisc, err = SetInterfacePromiscuous(addr.IP)
if err != nil { if err != nil {
return return
@ -96,9 +139,7 @@ func NewListener(name, address string, maxMessageSize int, perm bool) (l *Listen
l.net.maxMessageSizeC = make(chan int) l.net.maxMessageSizeC = make(chan int)
l.net.conn = conn l.net.conn = conn
l.net.promisc = promisc l.net.promisc = promisc
l.log = log.WithFields(log.Fields{ l.log = llog
"listener": addr.String(),
})
l.closed = make(chan struct{}) l.closed = make(chan struct{})
l.Permanent = perm l.Permanent = perm
app.Net.Listeners = append(app.Net.Listeners, l) app.Net.Listeners = append(app.Net.Listeners, l)

View File

@ -6,7 +6,7 @@ const (
serviceDisplayName = "Virtual VXLAN" serviceDisplayName = "Virtual VXLAN"
serviceVendor = "com.mrgeckosmedia" serviceVendor = "com.mrgeckosmedia"
serviceDescription = "Virtual VXLAN using TUN interfaces" serviceDescription = "Virtual VXLAN using TUN interfaces"
serviceVersion = "0.1.2" serviceVersion = "0.1.3"
defaultConfigFile = "config.yaml" defaultConfigFile = "config.yaml"
) )

View File

@ -79,10 +79,8 @@ func (a *ServerCmd) Run() error {
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
// Run program signal handler. // Run program signal handler.
sigLoop:
for { for {
// If this service is stopped by signal, this flag will be changed.
done := false
// Check for a signal. // Check for a signal.
select { select {
case sig := <-c: case sig := <-c:
@ -104,15 +102,11 @@ func (a *ServerCmd) Run() error {
// The default signal is either termination or interruption, // The default signal is either termination or interruption,
// so we should stop the loop. // so we should stop the loop.
default: default:
done = true break sigLoop
} }
// If the app stops itself, mark as done. // If the app stops itself, mark as done.
case <-app.Stop: case <-app.Stop:
done = true break sigLoop
}
if done {
close(app.Stop)
break
} }
} }