2016-01-19 13:39:46 +00:00
/ *
Copyright ( c ) 2015 - 2016 Christopher Young
Distributable under the terms of The "BSD New" " License
that can be found in the LICENSE file , herein included
as part of this header .
network . go : Client networking routines , DHCP lease monitoring , queue management , ICMP monitoring .
* /
2015-08-20 16:49:23 +00:00
package main
import (
2016-03-21 20:31:56 +00:00
"errors"
2016-09-19 16:21:39 +00:00
"github.com/tarm/serial"
2015-09-15 15:29:41 +00:00
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
2015-08-20 16:49:23 +00:00
"io/ioutil"
2015-08-20 16:54:46 +00:00
"log"
2015-12-21 04:59:39 +00:00
"math"
2015-09-15 15:29:41 +00:00
"math/rand"
2015-08-20 16:49:23 +00:00
"net"
2015-09-15 15:29:41 +00:00
"os"
2015-08-20 16:54:46 +00:00
"strconv"
"strings"
2015-08-20 17:06:40 +00:00
"sync"
2015-08-20 16:54:46 +00:00
"time"
2015-08-20 16:49:23 +00:00
)
2015-08-20 20:47:05 +00:00
type networkMessage struct {
2015-09-01 20:16:31 +00:00
msg [ ] byte
msgType uint8
queueable bool
ts time . Time
2015-08-20 20:47:05 +00:00
}
type networkConnection struct {
2016-05-03 13:31:57 +00:00
Conn * net . UDPConn
Ip string
Port uint32
Capability uint8
messageQueue [ ] [ ] byte // Device message queue.
2016-05-03 13:39:16 +00:00
MessageQueueLen int // Length of the message queue. For debugging.
2015-09-15 15:29:41 +00:00
/ *
Sleep mode / throttle variables . "sleep mode" is actually now just a very reduced packet rate , since we don ' t know positively
when a client is ready to accept packets - we just assume so if we don ' t receive ICMP Unreachable packets in 5 secs .
* /
2016-01-19 14:50:02 +00:00
LastUnreachable time . Time // Last time the device sent an ICMP Unreachable packet.
2015-09-15 15:29:41 +00:00
nextMessageTime time . Time // The next time that the device is "able" to receive a message.
2015-09-22 12:55:51 +00:00
numOverflows uint32 // Number of times the queue has overflowed - for calculating the amount to chop off from the queue.
2016-01-19 14:50:02 +00:00
SleepFlag bool // Whether or not this client has been marked as sleeping - only used for debugging (relies on messages being sent to update this flag in sendToAllConnectedClients()).
2016-03-21 20:31:56 +00:00
FFCrippled bool
2015-08-20 20:47:05 +00:00
}
2016-09-19 16:21:39 +00:00
type serialConnection struct {
DeviceString string
Baud int
serialPort * serial . Port
}
2015-08-20 20:47:05 +00:00
var messageQueue chan networkMessage
var outSockets map [ string ] networkConnection
2015-08-20 16:49:23 +00:00
var dhcpLeases map [ string ] string
2015-08-20 17:06:40 +00:00
var netMutex * sync . Mutex
2015-08-20 16:49:23 +00:00
2015-09-22 13:52:49 +00:00
var totalNetworkMessagesSent uint32
2015-09-15 15:29:41 +00:00
var pingResponse map [ string ] time . Time // Last time an IP responded to an "echo" response.
2015-08-20 20:47:05 +00:00
const (
2015-08-31 16:24:45 +00:00
NETWORK_GDL90_STANDARD = 1
NETWORK_AHRS_FFSIM = 2
NETWORK_AHRS_GDL90 = 4
dhcp_lease_file = "/var/lib/dhcp/dhcpd.leases"
2016-10-01 15:48:27 +00:00
extra_hosts_file = "/etc/stratux-static-hosts.conf"
2015-08-20 20:47:05 +00:00
)
2015-08-20 16:49:23 +00:00
// Read the "dhcpd.leases" file and parse out IP/hostname.
func getDHCPLeases ( ) ( map [ string ] string , error ) {
2015-08-21 23:05:05 +00:00
dat , err := ioutil . ReadFile ( dhcp_lease_file )
2015-08-20 16:49:23 +00:00
ret := make ( map [ string ] string )
if err != nil {
return ret , err
}
lines := strings . Split ( string ( dat ) , "\n" )
open_block := false
block_ip := ""
for _ , line := range lines {
spaced := strings . Split ( line , " " )
if len ( spaced ) > 2 && spaced [ 0 ] == "lease" {
open_block = true
block_ip = spaced [ 1 ]
} else if open_block && len ( spaced ) >= 4 && spaced [ 2 ] == "client-hostname" {
hostname := strings . TrimRight ( strings . TrimLeft ( strings . Join ( spaced [ 3 : ] , " " ) , "\"" ) , "\";" )
ret [ block_ip ] = hostname
open_block = false
2015-09-02 03:06:19 +00:00
} else if open_block && strings . HasPrefix ( spaced [ 0 ] , "}" ) { // No hostname.
open_block = false
ret [ block_ip ] = ""
2015-08-20 16:49:23 +00:00
}
}
2016-10-01 15:48:27 +00:00
// Added the ability to have static IP hosts stored in /etc/stratux-static-hosts.conf
dat2 , err := ioutil . ReadFile ( extra_hosts_file )
if err != nil {
return ret , nil
}
iplines := strings . Split ( string ( dat2 ) , "\n" )
2016-09-28 04:56:17 +00:00
block_ip2 := ""
for _ , ipline := range iplines {
spacedip := strings . Split ( ipline , " " )
2016-10-01 15:48:27 +00:00
if len ( spacedip ) == 2 {
// The ip is in block_ip2
block_ip2 = spacedip [ 0 ]
// the hostname is here
ret [ block_ip2 ] = spacedip [ 1 ]
}
2016-09-28 04:56:17 +00:00
}
2016-10-01 15:48:27 +00:00
2015-08-20 16:49:23 +00:00
return ret , nil
}
2015-09-15 15:29:41 +00:00
func isSleeping ( k string ) bool {
ipAndPort := strings . Split ( k , ":" )
// No ping response. Assume disconnected/sleeping device.
2016-10-18 03:30:43 +00:00
if lastPing , ok := pingResponse [ ipAndPort [ 0 ] ] ; ! ok || stratuxClock . Since ( lastPing ) > ( 10 * time . Second ) {
2015-09-15 15:29:41 +00:00
return true
}
2016-01-19 14:50:02 +00:00
if stratuxClock . Since ( outSockets [ k ] . LastUnreachable ) < ( 5 * time . Second ) {
2015-09-15 15:29:41 +00:00
return true
}
return false
}
// Throttle mode for testing port open and giving some start-up time to the app.
// Throttling is 0.1% data rate for first 15 seconds.
func isThrottled ( k string ) bool {
2016-01-19 14:50:02 +00:00
return ( rand . Int ( ) % 1000 != 0 ) && stratuxClock . Since ( outSockets [ k ] . LastUnreachable ) < ( 15 * time . Second )
2015-09-15 15:29:41 +00:00
}
2015-08-20 20:47:05 +00:00
func sendToAllConnectedClients ( msg networkMessage ) {
2016-09-19 16:21:39 +00:00
if ( msg . msgType & NETWORK_GDL90_STANDARD ) != 0 {
// It's a GDL90 message. Send to serial output channel (which may or may not cause something to happen).
serialOutputChan <- msg . msg
2016-11-17 07:25:36 +00:00
networkGDL90Chan <- msg . msg
2016-09-19 16:21:39 +00:00
}
2015-08-20 17:06:40 +00:00
netMutex . Lock ( )
defer netMutex . Unlock ( )
2015-09-01 20:16:31 +00:00
for k , netconn := range outSockets {
2016-01-19 14:50:02 +00:00
sleepFlag := isSleeping ( k )
netconn . SleepFlag = sleepFlag
outSockets [ k ] = netconn
2015-09-15 15:29:41 +00:00
// Check if this port is able to accept the type of message we're sending.
if ( netconn . Capability & msg . msgType ) == 0 {
continue
}
// Send non-queueable messages immediately, or discard if the client is in sleep mode.
2016-01-19 14:50:02 +00:00
2016-02-15 01:27:02 +00:00
if ! sleepFlag {
netconn . numOverflows = 0 // Reset the overflow counter whenever the client is not sleeping so that we're not penalizing future sleepmodes.
2016-01-26 06:00:01 +00:00
}
2015-09-15 15:29:41 +00:00
if ! msg . queueable {
2016-02-15 01:27:02 +00:00
if sleepFlag {
continue
}
2016-01-26 06:00:01 +00:00
netconn . Conn . Write ( msg . msg ) // Write immediately.
totalNetworkMessagesSent ++
2016-02-15 01:27:02 +00:00
globalStatus . NetworkDataMessagesSent ++
globalStatus . NetworkDataMessagesSentNonqueueable ++
globalStatus . NetworkDataBytesSent += uint64 ( len ( msg . msg ) )
globalStatus . NetworkDataBytesSentNonqueueable += uint64 ( len ( msg . msg ) )
2015-09-15 15:29:41 +00:00
} else {
// Queue the message if the message is "queueable".
if len ( netconn . messageQueue ) >= maxUserMsgQueueSize { // Too many messages queued? Drop the oldest.
log . Printf ( "%s:%d - message queue overflow.\n" , netconn . Ip , netconn . Port )
2015-09-22 12:55:51 +00:00
netconn . numOverflows ++
s := 2 * netconn . numOverflows // Double the amount we chop off on each overflow.
2015-09-22 13:52:49 +00:00
if int ( s ) >= len ( netconn . messageQueue ) {
2015-09-22 12:55:51 +00:00
netconn . messageQueue = make ( [ ] [ ] byte , 0 )
} else {
netconn . messageQueue = netconn . messageQueue [ s : ]
}
2015-09-15 15:29:41 +00:00
}
2016-02-15 05:04:38 +00:00
netconn . messageQueue = append ( netconn . messageQueue , msg . msg ) // each netconn.messageQueue is therefore an array (well, a slice) of formatted GDL90 messages
2015-09-15 15:29:41 +00:00
outSockets [ k ] = netconn
2015-08-20 20:47:05 +00:00
}
2015-08-20 16:49:23 +00:00
}
}
2016-09-19 16:21:39 +00:00
var serialOutputChan chan [ ] byte
2016-11-17 07:09:18 +00:00
var networkGDL90Chan chan [ ] byte
func networkOutWatcher ( ) {
2016-11-17 07:25:36 +00:00
for {
2016-11-17 17:46:48 +00:00
ch := <- networkGDL90Chan
gdl90Update . SendJSON ( ch )
2016-11-17 07:25:36 +00:00
}
2016-11-17 07:09:18 +00:00
}
2016-09-19 16:21:39 +00:00
// Monitor serial output channel, send to serial port.
func serialOutWatcher ( ) {
// Check every 30 seconds for a serial output device.
serialTicker := time . NewTicker ( 30 * time . Second )
serialDev := "/dev/serialout0" //FIXME: This is temporary. Only one serial output device for now.
for {
select {
case <- serialTicker . C :
if _ , err := os . Stat ( serialDev ) ; ! os . IsNotExist ( err ) { // Check if the device file exists.
var thisSerialConn serialConnection
// Check if we need to start handling a new device.
if val , ok := globalSettings . SerialOutputs [ serialDev ] ; ! ok {
newSerialOut := serialConnection { DeviceString : serialDev , Baud : 38400 }
log . Printf ( "detected new serial output, setting up now: %s. Default baudrate 38400.\n" , serialDev )
if globalSettings . SerialOutputs == nil {
globalSettings . SerialOutputs = make ( map [ string ] serialConnection )
}
globalSettings . SerialOutputs [ serialDev ] = newSerialOut
saveSettings ( )
thisSerialConn = newSerialOut
} else {
thisSerialConn = val
}
// Check if we need to open the connection now.
if thisSerialConn . serialPort == nil {
cfg := & serial . Config { Name : thisSerialConn . DeviceString , Baud : thisSerialConn . Baud }
p , err := serial . OpenPort ( cfg )
if err != nil {
log . Printf ( "serialout port (%s) err: %s\n" , thisSerialConn . DeviceString , err . Error ( ) )
break // We'll attempt again in 30 seconds.
} else {
log . Printf ( "opened serialout: Name: %s, Baud: %d\n" , thisSerialConn . DeviceString , thisSerialConn . Baud )
}
// Save the serial port connection.
thisSerialConn . serialPort = p
globalSettings . SerialOutputs [ serialDev ] = thisSerialConn
}
}
case b := <- serialOutputChan :
if val , ok := globalSettings . SerialOutputs [ serialDev ] ; ok {
if val . serialPort != nil {
_ , err := val . serialPort . Write ( b )
if err != nil { // Encountered an error in writing to the serial port. Close it and set Serial_out_enabled.
log . Printf ( "serialout (%s) port err: %s. Closing port.\n" , val . DeviceString , err . Error ( ) )
val . serialPort . Close ( )
val . serialPort = nil
globalSettings . SerialOutputs [ serialDev ] = val
}
}
}
}
}
}
2016-03-22 00:32:24 +00:00
// Returns the number of DHCP leases and prints queue lengths.
func getNetworkStats ( ) {
var numNonSleepingClients uint
for k , netconn := range outSockets {
2016-02-15 05:04:38 +00:00
queueBytes := 0
for _ , msg := range netconn . messageQueue {
queueBytes += len ( msg )
}
2016-02-26 18:37:45 +00:00
if globalSettings . DEBUG {
log . Printf ( "On %s:%d, Queue length = %d messages / %d bytes\n" , netconn . Ip , netconn . Port , len ( netconn . messageQueue ) , queueBytes )
}
2016-03-22 00:32:24 +00:00
ipAndPort := strings . Split ( k , ":" )
if len ( ipAndPort ) != 2 {
continue
}
ip := ipAndPort [ 0 ]
if pingRespTime , ok := pingResponse [ ip ] ; ok {
// Don't count the ping time if it is the same as stratuxClock epoch.
// If the client has responded to a ping in the last 15 minutes, count it as "connected" or "recent".
if ! pingRespTime . Equal ( time . Time { } ) && stratuxClock . Since ( pingRespTime ) < 15 * time . Minute {
numNonSleepingClients ++
}
}
2016-02-15 05:04:38 +00:00
}
2016-03-22 00:32:24 +00:00
globalStatus . Connected_Users = numNonSleepingClients
2015-08-20 16:49:23 +00:00
}
// See who has a DHCP lease and make a UDP connection to each of them.
func refreshConnectedClients ( ) {
2015-08-20 17:06:40 +00:00
netMutex . Lock ( )
defer netMutex . Unlock ( )
2015-08-20 16:49:23 +00:00
validConnections := make ( map [ string ] bool )
t , err := getDHCPLeases ( )
if err != nil {
log . Printf ( "getDHCPLeases(): %s\n" , err . Error ( ) )
return
}
dhcpLeases = t
// Client connected that wasn't before.
for ip , hostname := range dhcpLeases {
2015-08-20 20:47:05 +00:00
for _ , networkOutput := range globalSettings . NetworkOutputs {
2015-08-21 09:08:34 +00:00
ipAndPort := ip + ":" + strconv . Itoa ( int ( networkOutput . Port ) )
2015-08-20 16:49:23 +00:00
if _ , ok := outSockets [ ipAndPort ] ; ! ok {
2015-08-21 09:08:34 +00:00
log . Printf ( "client connected: %s:%d (%s).\n" , ip , networkOutput . Port , hostname )
2015-08-20 16:49:23 +00:00
addr , err := net . ResolveUDPAddr ( "udp" , ipAndPort )
if err != nil {
log . Printf ( "ResolveUDPAddr(%s): %s\n" , ipAndPort , err . Error ( ) )
continue
}
outConn , err := net . DialUDP ( "udp" , nil , addr )
if err != nil {
log . Printf ( "DialUDP(%s): %s\n" , ipAndPort , err . Error ( ) )
continue
}
2015-09-01 20:16:31 +00:00
newq := make ( [ ] [ ] byte , 0 )
2015-09-15 15:29:41 +00:00
outSockets [ ipAndPort ] = networkConnection { Conn : outConn , Ip : ip , Port : networkOutput . Port , Capability : networkOutput . Capability , messageQueue : newq }
2015-08-20 16:49:23 +00:00
}
validConnections [ ipAndPort ] = true
}
}
// Client that was connected before that isn't.
for ipAndPort , conn := range outSockets {
if _ , ok := validConnections [ ipAndPort ] ; ! ok {
log . Printf ( "removed connection %s.\n" , ipAndPort )
2015-08-21 09:08:34 +00:00
conn . Conn . Close ( )
2015-08-20 16:49:23 +00:00
delete ( outSockets , ipAndPort )
}
}
}
func messageQueueSender ( ) {
2016-02-15 22:11:19 +00:00
secondTimer := time . NewTicker ( 15 * time . Second )
2015-12-21 05:05:47 +00:00
queueTimer := time . NewTicker ( 100 * time . Millisecond )
2015-12-21 04:59:39 +00:00
var lastQueueTimeChange time . Time // Reevaluate send frequency every 5 seconds.
2015-08-20 16:49:23 +00:00
for {
select {
2015-08-20 16:54:46 +00:00
case msg := <- messageQueue :
2015-08-20 16:49:23 +00:00
sendToAllConnectedClients ( msg )
2015-09-15 15:29:41 +00:00
case <- queueTimer . C :
netMutex . Lock ( )
2015-12-21 04:59:39 +00:00
averageSendableQueueSize := float64 ( 0.0 )
2015-09-15 15:29:41 +00:00
for k , netconn := range outSockets {
if len ( netconn . messageQueue ) > 0 && ! isSleeping ( k ) && ! isThrottled ( k ) {
2015-12-21 04:59:39 +00:00
averageSendableQueueSize += float64 ( len ( netconn . messageQueue ) ) // Add num sendable messages.
2016-02-15 05:04:38 +00:00
var queuedMsg [ ] byte
// Combine the first 256 entries in netconn.messageQueue to avoid flooding wlan0 with too many IOPS.
// Need to play nice with non-queued messages, so this limits the number of entries to combine.
// UAT uplink block is 432 bytes, so transmit block size shouldn't be larger than 108 KiB. 10 Mbps per device would therefore be needed to send within a 100 ms window.
mqDepth := len ( netconn . messageQueue )
if mqDepth > 256 {
mqDepth = 256
}
for j := 0 ; j < mqDepth ; j ++ {
queuedMsg = append ( queuedMsg , netconn . messageQueue [ j ] ... )
}
/ *
for j , _ := range netconn . messageQueue {
queuedMsg = append ( queuedMsg , netconn . messageQueue [ j ] ... )
}
* /
netconn . Conn . Write ( queuedMsg )
2015-09-22 13:52:49 +00:00
totalNetworkMessagesSent ++
2016-02-15 01:27:02 +00:00
globalStatus . NetworkDataMessagesSent ++
2016-02-15 05:04:38 +00:00
globalStatus . NetworkDataBytesSent += uint64 ( len ( queuedMsg ) )
//netconn.messageQueue = [][]byte{}
if mqDepth < len ( netconn . messageQueue ) {
netconn . messageQueue = netconn . messageQueue [ mqDepth : ]
} else {
netconn . messageQueue = [ ] [ ] byte { }
}
outSockets [ k ] = netconn
/ *
tmpConn := netconn
tmpConn . Conn . Write ( tmpConn . messageQueue [ 0 ] )
totalNetworkMessagesSent ++
globalStatus . NetworkDataMessagesSent ++
globalStatus . NetworkDataBytesSent += uint64 ( len ( tmpConn . messageQueue [ 0 ] ) )
tmpConn . messageQueue = tmpConn . messageQueue [ 1 : ]
outSockets [ k ] = tmpConn
* /
2015-09-15 15:29:41 +00:00
}
2016-05-03 13:50:59 +00:00
netconn . MessageQueueLen = len ( netconn . messageQueue )
outSockets [ k ] = netconn
2015-09-15 15:29:41 +00:00
}
2015-12-21 04:59:39 +00:00
2016-01-07 16:29:55 +00:00
if stratuxClock . Since ( lastQueueTimeChange ) >= 5 * time . Second {
2015-12-21 04:59:39 +00:00
var pd float64
if averageSendableQueueSize > 0.0 && len ( outSockets ) > 0 {
2016-01-26 06:00:01 +00:00
averageSendableQueueSize = averageSendableQueueSize / float64 ( len ( outSockets ) ) // It's a total, not an average, up until this point.
pd = math . Max ( float64 ( 1.0 / 750.0 ) , float64 ( 1.0 / ( 4.0 * averageSendableQueueSize ) ) ) // Say 250ms is enough to get through the whole queue.
2015-12-21 04:59:39 +00:00
} else {
2016-02-25 05:53:35 +00:00
pd = float64 ( 0.1 ) // 100ms.
2015-12-21 04:59:39 +00:00
}
2016-03-10 15:20:46 +00:00
if globalSettings . DEBUG {
log . Printf ( "Average sendable queue is %v messages. Changing queue timer to %f seconds\n" , averageSendableQueueSize , pd )
}
2016-02-25 05:53:35 +00:00
2015-12-21 04:59:39 +00:00
queueTimer . Stop ( )
queueTimer = time . NewTicker ( time . Duration ( pd * 1000000000.0 ) * time . Nanosecond )
2016-01-07 16:29:55 +00:00
lastQueueTimeChange = stratuxClock . Time
2015-12-21 04:59:39 +00:00
}
2015-09-15 15:29:41 +00:00
netMutex . Unlock ( )
2015-08-20 16:49:23 +00:00
case <- secondTimer . C :
getNetworkStats ( )
}
}
}
2015-09-01 20:16:31 +00:00
func sendMsg ( msg [ ] byte , msgType uint8 , queueable bool ) {
2016-01-07 16:34:37 +00:00
messageQueue <- networkMessage { msg : msg , msgType : msgType , queueable : queueable , ts : stratuxClock . Time }
2015-08-20 20:47:05 +00:00
}
2015-09-01 20:16:31 +00:00
func sendGDL90 ( msg [ ] byte , queueable bool ) {
sendMsg ( msg , NETWORK_GDL90_STANDARD , queueable )
2015-08-20 16:49:23 +00:00
}
2015-08-21 23:05:05 +00:00
func monitorDHCPLeases ( ) {
2015-08-25 06:22:53 +00:00
timer := time . NewTicker ( 30 * time . Second )
2015-08-21 23:05:05 +00:00
for {
select {
2015-08-25 06:22:53 +00:00
case <- timer . C :
2015-08-21 23:05:05 +00:00
refreshConnectedClients ( )
}
}
}
2015-09-15 15:29:41 +00:00
func icmpEchoSender ( c * icmp . PacketConn ) {
timer := time . NewTicker ( 5 * time . Second )
for {
<- timer . C
// Collect IPs.
ips := make ( map [ string ] bool )
for k , _ := range outSockets {
ipAndPort := strings . Split ( k , ":" )
ips [ ipAndPort [ 0 ] ] = true
}
// Send to all IPs.
for ip , _ := range ips {
wm := icmp . Message {
Type : ipv4 . ICMPTypeEcho , Code : 0 ,
Body : & icmp . Echo {
ID : os . Getpid ( ) & 0xffff , Seq : 1 ,
Data : [ ] byte ( "STRATUX" ) ,
} ,
}
wb , err := wm . Marshal ( nil )
if err != nil {
log . Printf ( "couldn't send ICMP Echo: %s\n" , err . Error ( ) )
continue
}
if _ , err := c . WriteTo ( wb , & net . IPAddr { IP : net . ParseIP ( ip ) } ) ; err != nil {
log . Printf ( "couldn't send ICMP Echo: %s\n" , err . Error ( ) )
continue
}
2015-09-22 13:52:49 +00:00
totalNetworkMessagesSent ++
2015-09-15 15:29:41 +00:00
}
}
}
// Monitor clients going in/out of sleep mode via ICMP unreachable packets.
2015-09-01 20:16:31 +00:00
func sleepMonitor ( ) {
2015-09-15 15:29:41 +00:00
c , err := icmp . ListenPacket ( "ip4:icmp" , "0.0.0.0" )
2015-09-01 20:16:31 +00:00
if err != nil {
2015-09-15 15:29:41 +00:00
log . Printf ( "error listening for udp - sending data to all ports for all connected clients. err: %s" , err )
2015-09-01 20:16:31 +00:00
return
}
2015-09-15 15:29:41 +00:00
go icmpEchoSender ( c )
defer c . Close ( )
2015-09-01 20:16:31 +00:00
for {
2015-09-15 15:29:41 +00:00
buf := make ( [ ] byte , 1500 )
n , peer , err := c . ReadFrom ( buf )
if err != nil {
log . Printf ( "%s\n" , err . Error ( ) )
continue
}
2015-09-21 18:01:01 +00:00
msg , err := icmp . ParseMessage ( 1 , buf [ : n ] )
2015-09-15 15:29:41 +00:00
if err != nil {
continue
}
ip := peer . String ( )
// Look for echo replies, mark it as received.
if msg . Type == ipv4 . ICMPTypeEchoReply {
2016-01-07 16:34:37 +00:00
pingResponse [ ip ] = stratuxClock . Time
2015-09-15 15:29:41 +00:00
continue // No further processing needed.
}
// Only deal with ICMP Unreachable packets (since that's what iOS and Android seem to be sending whenever the apps are not available).
if msg . Type != ipv4 . ICMPTypeDestinationUnreachable {
continue
}
// Packet parsing.
2015-09-21 18:01:01 +00:00
mb , err := msg . Body . Marshal ( 1 )
2015-09-01 20:16:31 +00:00
if err != nil {
2015-09-15 15:29:41 +00:00
continue
2015-09-01 20:16:31 +00:00
}
2015-09-15 15:29:41 +00:00
if len ( mb ) < 28 {
2015-09-01 20:16:31 +00:00
continue
}
2015-09-15 15:29:41 +00:00
// The unreachable port.
port := ( uint16 ( mb [ 26 ] ) << 8 ) | uint16 ( mb [ 27 ] )
ipAndPort := ip + ":" + strconv . Itoa ( int ( port ) )
2015-09-01 20:16:31 +00:00
netMutex . Lock ( )
2015-09-15 15:29:41 +00:00
p , ok := outSockets [ ipAndPort ]
2015-09-01 20:16:31 +00:00
if ! ok {
// Can't do anything, the client isn't even technically connected.
netMutex . Unlock ( )
continue
}
2016-01-19 14:50:02 +00:00
p . LastUnreachable = stratuxClock . Time
2015-09-15 15:29:41 +00:00
outSockets [ ipAndPort ] = p
2015-09-01 20:16:31 +00:00
netMutex . Unlock ( )
}
}
2016-02-15 01:27:02 +00:00
func networkStatsCounter ( ) {
timer := time . NewTicker ( 1 * time . Second )
var previousNetworkMessagesSent , previousNetworkBytesSent , previousNetworkMessagesSentNonqueueable , previousNetworkBytesSentNonqueueable uint64
for {
globalStatus . NetworkDataMessagesSentLastSec = globalStatus . NetworkDataMessagesSent - previousNetworkMessagesSent
globalStatus . NetworkDataBytesSentLastSec = globalStatus . NetworkDataBytesSent - previousNetworkBytesSent
globalStatus . NetworkDataMessagesSentNonqueueableLastSec = globalStatus . NetworkDataMessagesSentNonqueueable - previousNetworkMessagesSentNonqueueable
globalStatus . NetworkDataBytesSentNonqueueableLastSec = globalStatus . NetworkDataBytesSentNonqueueable - previousNetworkBytesSentNonqueueable
// debug option. Uncomment to log per-second network statistics. Useful for debugging WiFi instability.
//log.Printf("Network data messages sent: %d total, %d last second. Network data bytes sent: %d total, %d last second.\n", globalStatus.NetworkDataMessagesSent, globalStatus.NetworkDataMessagesSentLastSec, globalStatus.NetworkDataBytesSent, globalStatus.NetworkDataBytesSentLastSec)
previousNetworkMessagesSent = globalStatus . NetworkDataMessagesSent
previousNetworkBytesSent = globalStatus . NetworkDataBytesSent
previousNetworkMessagesSentNonqueueable = globalStatus . NetworkDataMessagesSentNonqueueable
previousNetworkBytesSentNonqueueable = globalStatus . NetworkDataBytesSentNonqueueable
2016-11-23 03:14:45 +00:00
<- timer . C
2016-02-15 05:04:38 +00:00
2016-02-15 01:27:02 +00:00
}
}
2016-03-21 20:31:56 +00:00
/ *
ffMonitor ( ) .
Watches for "i-want-to-play-ffm-udp" , "i-can-play-ffm-udp" , and "i-cannot-play-ffm-udp" UDP messages broadcasted on
port 50113. Tags the client , issues a warning , and disables AHRS .
* /
func ffMonitor ( ) {
ff_warned := false // Has a warning been issued via globalStatus.Errors?
addr := net . UDPAddr { Port : 50113 , IP : net . ParseIP ( "0.0.0.0" ) }
conn , err := net . ListenUDP ( "udp" , & addr )
if err != nil {
log . Printf ( "ffMonitor(): error listening on port 50113: %s\n" , err . Error ( ) )
return
}
2016-05-27 03:48:13 +00:00
defer conn . Close ( )
2016-03-21 20:31:56 +00:00
for {
buf := make ( [ ] byte , 1024 )
n , addr , err := conn . ReadFrom ( buf )
ipAndPort := strings . Split ( addr . String ( ) , ":" )
ip := ipAndPort [ 0 ]
if err != nil {
log . Printf ( "err: %s\n" , err . Error ( ) )
return
}
// Got message, check if it's in the correct format.
if n < 3 || buf [ 0 ] != 0xFF || buf [ 1 ] != 0xFE {
continue
}
s := string ( buf [ 2 : n ] )
s = strings . Replace ( s , "\x00" , "" , - 1 )
ffIpAndPort := ip + ":4000"
netMutex . Lock ( )
p , ok := outSockets [ ffIpAndPort ]
if ! ok {
// Can't do anything, the client isn't even technically connected.
netMutex . Unlock ( )
continue
}
if strings . HasPrefix ( s , "i-want-to-play-ffm-udp" ) || strings . HasPrefix ( s , "i-can-play-ffm-udp" ) || strings . HasPrefix ( s , "i-cannot-play-ffm-udp" ) {
p . FFCrippled = true
//FIXME: AHRS doesn't need to be disabled globally, just messages need to be filtered.
globalSettings . AHRS_Enabled = false
if ! ff_warned {
e := errors . New ( "Stratux is not supported by your EFB app. Your EFB app is known to regularly make changes that cause compatibility issues with Stratux. See the README for a list of apps that officially support Stratux." )
addSystemError ( e )
ff_warned = true
}
}
outSockets [ ffIpAndPort ] = p
netMutex . Unlock ( )
}
}
2015-08-20 16:49:23 +00:00
func initNetwork ( ) {
2015-08-20 20:47:05 +00:00
messageQueue = make ( chan networkMessage , 1024 ) // Buffered channel, 1024 messages.
2016-09-19 16:21:39 +00:00
serialOutputChan = make ( chan [ ] byte , 1024 ) // Buffered channel, 1024 GDL90 messages.
2016-11-17 07:25:36 +00:00
networkGDL90Chan = make ( chan [ ] byte , 1024 )
2015-08-20 20:47:05 +00:00
outSockets = make ( map [ string ] networkConnection )
2015-09-15 15:29:41 +00:00
pingResponse = make ( map [ string ] time . Time )
2015-08-20 17:06:40 +00:00
netMutex = & sync . Mutex { }
2015-08-20 16:49:23 +00:00
refreshConnectedClients ( )
2015-08-21 23:05:05 +00:00
go monitorDHCPLeases ( )
2015-08-20 16:49:23 +00:00
go messageQueueSender ( )
2015-09-01 20:16:31 +00:00
go sleepMonitor ( )
2016-02-15 01:27:02 +00:00
go networkStatsCounter ( )
2016-09-19 16:21:39 +00:00
go serialOutWatcher ( )
2016-11-17 07:09:18 +00:00
go networkOutWatcher ( )
2015-08-20 16:54:46 +00:00
}