OVMS3/OVMS.V3/main/ovms_netmanager.cpp

1039 lines
31 KiB
C++

/*
; Project: Open Vehicle Monitor System
; Date: 14th March 2017
;
; Changes:
; 1.0 Initial release
;
; (C) 2011 Michael Stegen / Stegen Electronics
; (C) 2011-2017 Mark Webb-Johnson
; (C) 2011 Sonny Chen @ EPRO/DX
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in
; all copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
; THE SOFTWARE.
*/
#include "ovms_log.h"
static const char *TAG = "netmanager";
#include <string.h>
#include <stdio.h>
#include <lwip/tcpip.h>
#include <lwip/ip_addr.h>
#include <lwip/netif.h>
#include <lwip/dns.h>
#include <netinet/in.h>
#include "metrics_standard.h"
#include "ovms_peripherals.h"
#include "ovms_netmanager.h"
#include "ovms_command.h"
#include "ovms_config.h"
#include "ovms_module.h"
#ifndef CONFIG_OVMS_NETMAN_TASK_PRIORITY
#define CONFIG_OVMS_NETMAN_TASK_PRIORITY 5
#endif
OvmsNetManager MyNetManager __attribute__ ((init_priority (8999)));
void network_status(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
struct netif *ni;
for (ni = netif_list; ni; ni = ni->next)
{
if (ni->name[0]=='l' && ni->name[1]=='o')
continue;
writer->printf("Interface#%d: %c%c%d (ifup=%d linkup=%d)\n",
ni->num,ni->name[0],ni->name[1],ni->num,
((ni->flags & NETIF_FLAG_UP) != 0),
((ni->flags & NETIF_FLAG_LINK_UP) != 0));
writer->printf(" IPv4: " IPSTR "/" IPSTR " gateway " IPSTR "\n",
IP2STR(&ni->ip_addr.u_addr.ip4), IP2STR(&ni->netmask.u_addr.ip4), IP2STR(&ni->gw.u_addr.ip4));
writer->puts("");
}
writer->printf("DNS:");
int dnsservers = 0;
for (int k=0;k<DNS_MAX_SERVERS;k++)
{
const ip_addr_t* srv = dns_getserver(k);
if (! ip_addr_isany(srv))
{
dnsservers++;
if (srv->type == IPADDR_TYPE_V4)
writer->printf(" " IPSTR, IP2STR(&srv->u_addr.ip4));
else if (srv->type == IPADDR_TYPE_V6)
writer->printf(" " IPSTR, IP2STR(&srv->u_addr.ip6));
}
}
if (dnsservers == 0)
writer->puts(" None");
else
writer->puts("");
if (netif_default)
{
writer->printf("\nDefault Interface: %c%c%d (" IPSTR "/" IPSTR " gateway " IPSTR ")\n",
netif_default->name[0], netif_default->name[1], netif_default->num,
IP2STR(&netif_default->ip_addr.u_addr.ip4),
IP2STR(&netif_default->netmask.u_addr.ip4),
IP2STR(&netif_default->gw.u_addr.ip4));
}
else
{
writer->printf("\nDefault Interface: None\n");
}
}
void network_restart(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
writer->puts("Restarting network...");
vTaskDelay(pdMS_TO_TICKS(100));
MyNetManager.RestartNetwork();
}
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
void network_connections(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
if (!MyNetManager.MongooseRunning())
{
writer->puts("ERROR: Mongoose task not running");
return;
}
if (xTaskGetCurrentTaskHandle() == MyNetManager.GetMongooseTaskHandle())
{
// inline execution in mongoose context:
if (strcmp(cmd->GetName(), "close") == 0)
{
uint32_t id = strtol(argv[0], NULL, 16);
int cnt = MyNetManager.CloseConnection(id);
if (cnt == 0)
writer->printf("ERROR: connection ID %08x not found\n", id);
else
writer->printf("Close signal sent to %d connection(s)\n", cnt);
}
else if (strcmp(cmd->GetName(), "cleanup") == 0)
{
int cnt = MyNetManager.CleanupConnections();
if (cnt == 0)
writer->puts("All connections OK");
else
writer->printf("Close signal sent to %d connection(s)\n", cnt);
}
else
{
MyNetManager.ListConnections(verbosity, writer);
}
}
else
{
// remote execution from non-mongoose context:
static netman_job_t job;
memset(&job, 0, sizeof(job));
if (strcmp(cmd->GetName(), "close") == 0)
{
job.cmd = nmc_close;
job.close.id = strtol(argv[0], NULL, 16);
if (!MyNetManager.ExecuteJob(&job, pdMS_TO_TICKS(5000)))
writer->puts("ERROR: job failed");
else if (job.close.cnt == 0)
writer->printf("ERROR: connection ID %08x not found\n", job.close.id);
else
writer->printf("Close signal sent to %d connection(s)\n", job.close.cnt);
}
else if (strcmp(cmd->GetName(), "cleanup") == 0)
{
job.cmd = nmc_cleanup;
if (!MyNetManager.ExecuteJob(&job, pdMS_TO_TICKS(5000)))
writer->puts("ERROR: job failed");
else if (job.cleanup.cnt == 0)
writer->puts("All connections OK");
else
writer->printf("Close signal sent to %d connection(s)\n", job.cleanup.cnt);
}
else
{
// Note: a timeout occurring after the job has begun processing will result in a crash
// here due to the StringWriter being deleted. This only applies to the list command.
// If this becomes a problem, we need to upgrade the job processing from send/notify
// to a full send/receive scheme.
job.cmd = nmc_list;
job.list.verbosity = verbosity;
job.list.buf = new StringWriter();
if (!MyNetManager.ExecuteJob(&job, pdMS_TO_TICKS(10000)))
writer->puts("ERROR: job failed");
else
writer->write(job.list.buf->data(), job.list.buf->size());
delete job.list.buf;
job.list.buf = NULL;
}
}
}
#endif // CONFIG_OVMS_SC_GPL_MONGOOSE
OvmsNetManager::OvmsNetManager()
{
ESP_LOGI(TAG, "Initialising NETMANAGER (8999)");
m_connected_wifi = false;
m_connected_modem = false;
m_connected_any = false;
m_wifi_sta = false;
m_wifi_good = false;
m_wifi_ap = false;
m_network_any = false;
m_cfg_wifi_sq_good = -87;
m_cfg_wifi_sq_bad = -89;
for (int i=0; i<DNS_MAX_SERVERS; i++)
{
m_dns_modem[i] = (ip_addr_t)ip_addr_any;
m_dns_wifi[i] = (ip_addr_t)ip_addr_any;
m_previous_dns[i] = (ip_addr_t)ip_addr_any;
}
m_previous_name[0] = m_previous_name[1] = 0;
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
m_mongoose_task = 0;
m_mongoose_running = false;
m_jobqueue = xQueueCreate(CONFIG_OVMS_HW_NETMANAGER_QUEUE_SIZE, sizeof(netman_job_t*));
#endif //#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
// Register our commands
OvmsCommand* cmd_network = MyCommandApp.RegisterCommand("network","NETWORK framework",network_status, "", 0, 0, false);
cmd_network->RegisterCommand("status","Show network status",network_status, "", 0, 0, false);
cmd_network->RegisterCommand("restart","Restart network",network_restart, "", 0, 0, false);
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
cmd_network->RegisterCommand("list", "List network connections", network_connections);
cmd_network->RegisterCommand("close", "Close network connection(s)", network_connections, "<id>\nUse ID from connection list / 0 to close all", 1, 1);
cmd_network->RegisterCommand("cleanup", "Close orphaned network connections", network_connections);
#endif // CONFIG_OVMS_SC_GPL_MONGOOSE
// Register our events
#undef bind // Kludgy, but works
using std::placeholders::_1;
using std::placeholders::_2;
#ifdef CONFIG_OVMS_COMP_WIFI
MyEvents.RegisterEvent(TAG,"system.wifi.sta.gotip", std::bind(&OvmsNetManager::WifiStaGotIP, this, _1, _2));
MyEvents.RegisterEvent(TAG,"system.wifi.sta.lostip", std::bind(&OvmsNetManager::WifiStaLostIP, this, _1, _2));
MyEvents.RegisterEvent(TAG,"network.wifi.sta.good", std::bind(&OvmsNetManager::WifiStaGood, this, _1, _2));
MyEvents.RegisterEvent(TAG,"network.wifi.sta.bad", std::bind(&OvmsNetManager::WifiStaBad, this, _1, _2));
MyEvents.RegisterEvent(TAG,"system.wifi.sta.stop", std::bind(&OvmsNetManager::WifiStaStop, this, _1, _2));
MyEvents.RegisterEvent(TAG,"system.wifi.sta.connected", std::bind(&OvmsNetManager::WifiStaConnected, this, _1, _2));
MyEvents.RegisterEvent(TAG,"system.wifi.sta.disconnected", std::bind(&OvmsNetManager::WifiStaStop, this, _1, _2));
MyEvents.RegisterEvent(TAG,"system.wifi.down", std::bind(&OvmsNetManager::WifiStaStop, this, _1, _2));
MyEvents.RegisterEvent(TAG,"system.wifi.ap.start", std::bind(&OvmsNetManager::WifiUpAP, this, _1, _2));
MyEvents.RegisterEvent(TAG,"system.wifi.ap.stop", std::bind(&OvmsNetManager::WifiDownAP, this, _1, _2));
MyEvents.RegisterEvent(TAG,"system.wifi.ap.sta.disconnected", std::bind(&OvmsNetManager::WifiApStaDisconnect, this, _1, _2));
#endif // #ifdef CONFIG_OVMS_COMP_WIFI
MyEvents.RegisterEvent(TAG,"system.modem.gotip", std::bind(&OvmsNetManager::ModemUp, this, _1, _2));
MyEvents.RegisterEvent(TAG,"system.modem.stop", std::bind(&OvmsNetManager::ModemDown, this, _1, _2));
MyEvents.RegisterEvent(TAG,"system.modem.down", std::bind(&OvmsNetManager::ModemDown, this, _1, _2));
MyEvents.RegisterEvent(TAG,"config.mounted", std::bind(&OvmsNetManager::ConfigChanged, this, _1, _2));
MyEvents.RegisterEvent(TAG,"config.changed", std::bind(&OvmsNetManager::ConfigChanged, this, _1, _2));
MyEvents.RegisterEvent(TAG,"system.shuttingdown", std::bind(&OvmsNetManager::EventSystemShuttingDown, this, _1, _2));
MyConfig.RegisterParam("network", "Network Configuration", true, true);
// Our instances:
// dns Space-separated list of DNS servers
// wifi.sq.good Threshold for usable wifi signal [dBm], default -87
// wifi.sq.bad Threshold for unusable wifi signal [dBm], default -89
#ifdef CONFIG_OVMS_COMP_WIFI
MyMetrics.RegisterListener(TAG, MS_N_WIFI_SQ, std::bind(&OvmsNetManager::WifiStaCheckSQ, this, _1));
#endif
}
OvmsNetManager::~OvmsNetManager()
{
}
void OvmsNetManager::RestartNetwork()
{
#ifdef CONFIG_OVMS_COMP_WIFI
if (MyPeripherals && MyPeripherals->m_esp32wifi)
MyPeripherals->m_esp32wifi->Restart();
#endif // #ifdef CONFIG_OVMS_COMP_WIFI
#ifdef CONFIG_OVMS_COMP_CELLULAR
if (MyPeripherals && MyPeripherals->m_cellular_modem)
MyPeripherals->m_cellular_modem->Restart();
#endif // CONFIG_OVMS_COMP_CELLULAR
}
#ifdef CONFIG_OVMS_COMP_WIFI
void OvmsNetManager::WifiConnect()
{
if (m_wifi_sta && !m_connected_wifi)
{
m_connected_wifi = true;
PrioritiseAndIndicate();
MyEvents.SignalEvent("network.wifi.up",NULL);
if (m_connected_modem)
{
ESP_LOGI(TAG, "WIFI client up (with MODEM up): reconfigured for WIFI client priority");
MyEvents.SignalEvent("network.reconfigured",NULL);
}
else
{
ESP_LOGI(TAG, "WIFI client up (with MODEM down): starting network with WIFI client");
MyEvents.SignalEvent("network.up",NULL);
}
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
StartMongooseTask();
#endif
MyEvents.SignalEvent("network.interface.change",NULL);
}
}
void OvmsNetManager::WifiDisconnect()
{
if (m_connected_wifi)
{
m_connected_wifi = false;
PrioritiseAndIndicate();
MyEvents.SignalEvent("network.wifi.down",NULL);
if (m_connected_any)
{
ESP_LOGI(TAG, "WIFI client down (with MODEM up): reconfigured for MODEM priority");
MyEvents.SignalEvent("network.reconfigured",NULL);
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
ScheduleCleanup();
#endif
}
else
{
ESP_LOGI(TAG, "WIFI client down (with MODEM down): network connectivity has been lost");
MyEvents.SignalEvent("network.down",NULL);
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
StopMongooseTask();
#endif
}
MyEvents.SignalEvent("network.interface.change",NULL);
}
}
void OvmsNetManager::WifiStaGotIP(std::string event, void* data)
{
m_wifi_sta = true;
ESP_LOGI(TAG, "WIFI client got IP");
SaveDNSServer(m_dns_wifi);
// Re-prioritise, just in case, as Wifi stack seems to mess with this
// (in particular if an AP interface is up, and STA goes down, Wifi
// stack seems to switch default interface to AP)
PrioritiseAndIndicate();
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
ScheduleCleanup();
#endif
WifiStaCheckSQ(StdMetrics.ms_m_net_wifi_sq);
}
void OvmsNetManager::WifiStaLostIP(std::string event, void* data)
{
// Re-prioritise, just in case, as Wifi stack seems to mess with this
// (in particular if an AP interface is up, and STA goes down, Wifi
// stack seems to switch default interface to AP)
PrioritiseAndIndicate();
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
ScheduleCleanup();
#endif
}
void OvmsNetManager::WifiStaConnected(std::string event, void* data)
{
// Re-prioritise, just in case, as Wifi stack seems to mess with this
// (in particular if an AP interface is up, and STA goes down, Wifi
// stack seems to switch default interface to AP)
PrioritiseAndIndicate();
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
ScheduleCleanup();
#endif
}
void OvmsNetManager::WifiStaStop(std::string event, void* data)
{
WifiStaSetSQ(false);
if (m_wifi_sta)
{
m_wifi_sta = false;
ESP_LOGI(TAG, "WIFI client stop");
if (m_connected_wifi)
{
WifiDisconnect();
return;
}
}
// Re-prioritise, just in case, as Wifi stack seems to mess with this
// (in particular if an AP interface is up, and STA goes down, Wifi
// stack seems to switch default interface to AP)
PrioritiseAndIndicate();
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
ScheduleCleanup();
#endif
}
void OvmsNetManager::WifiStaGood(std::string event, void* data)
{
if (m_wifi_sta && !m_connected_wifi)
{
ESP_LOGI(TAG, "WIFI client has good signal quality (%.1f dBm); connect",
StdMetrics.ms_m_net_wifi_sq->AsFloat());
WifiConnect();
}
}
void OvmsNetManager::WifiStaBad(std::string event, void* data)
{
if (m_wifi_sta && m_connected_wifi)
{
ESP_LOGI(TAG, "WIFI client has bad signal quality (%.1f dBm); disconnect",
StdMetrics.ms_m_net_wifi_sq->AsFloat());
// Stop using the Wifi network:
WifiDisconnect();
// If enabled, start reconnecting immediately (for fast transition to next best
// AP instead of trying to reassociate to current AP):
if (MyPeripherals && MyPeripherals->m_esp32wifi &&
MyConfig.GetParamValueBool("network", "wifi.bad.reconnect") == true)
{
MyPeripherals->m_esp32wifi->Reconnect(NULL);
}
}
}
void OvmsNetManager::WifiStaCheckSQ(OvmsMetric* metric)
{
if (m_wifi_sta)
{
float sq = StdMetrics.ms_m_net_wifi_sq->AsFloat();
if ((!metric || !m_wifi_good) && sq >= m_cfg_wifi_sq_good)
{
m_wifi_good = true;
MyEvents.SignalEvent("network.wifi.sta.good", NULL);
}
else if ((!metric || m_wifi_good) && sq <= m_cfg_wifi_sq_bad)
{
m_wifi_good = false;
MyEvents.SignalEvent("network.wifi.sta.bad", NULL);
}
}
}
void OvmsNetManager::WifiStaSetSQ(bool good)
{
if (m_wifi_sta && m_wifi_good != good)
{
m_wifi_good = good;
MyEvents.SignalEvent(good ? "network.wifi.sta.good" : "network.wifi.sta.bad", NULL);
}
}
void OvmsNetManager::WifiUpAP(std::string event, void* data)
{
m_wifi_ap = true;
PrioritiseAndIndicate();
ESP_LOGI(TAG, "WIFI access point is up");
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
StartMongooseTask();
#endif //#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
MyEvents.SignalEvent("network.interface.change",NULL);
}
void OvmsNetManager::WifiDownAP(std::string event, void* data)
{
m_wifi_ap = false;
PrioritiseAndIndicate();
ESP_LOGI(TAG, "WIFI access point is down");
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
StopMongooseTask();
#endif //#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
MyEvents.SignalEvent("network.interface.change",NULL);
}
void OvmsNetManager::WifiApStaDisconnect(std::string event, void* data)
{
ESP_LOGI(TAG, "WIFI access point station disconnected");
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
ScheduleCleanup();
#endif
}
#endif // #ifdef CONFIG_OVMS_COMP_WIFI
void OvmsNetManager::ModemUp(std::string event, void* data)
{
m_connected_modem = true;
SaveDNSServer(m_dns_modem);
PrioritiseAndIndicate();
MyEvents.SignalEvent("network.modem.up",NULL);
if (!m_connected_wifi)
{
ESP_LOGI(TAG, "MODEM up (with WIFI client down): starting network with MODEM");
MyEvents.SignalEvent("network.up",NULL);
}
else
{
ESP_LOGI(TAG, "MODEM up (with WIFI client up): staying with WIFI client priority");
}
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
StartMongooseTask();
#endif //#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
MyEvents.SignalEvent("network.interface.change",NULL);
}
void OvmsNetManager::ModemDown(std::string event, void* data)
{
if (m_connected_modem)
{
m_connected_modem = false;
PrioritiseAndIndicate();
MyEvents.SignalEvent("network.modem.down",NULL);
if (m_connected_any)
{
ESP_LOGI(TAG, "MODEM down (with WIFI client up): staying with WIFI client priority");
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
ScheduleCleanup();
#endif
}
else
{
ESP_LOGI(TAG, "MODEM down (with WIFI client down): network connectivity has been lost");
MyEvents.SignalEvent("network.down",NULL);
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
StopMongooseTask();
#endif //#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
}
MyEvents.SignalEvent("network.interface.change",NULL);
}
else
{
// Re-prioritise, just in case, as Wifi stack seems to mess with this
// (in particular if an AP interface is up, and STA goes down, Wifi
// stack seems to switch default interface to AP)
PrioritiseAndIndicate();
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
ScheduleCleanup();
#endif
}
}
void OvmsNetManager::ConfigChanged(std::string event, void* data)
{
OvmsConfigParam* param = (OvmsConfigParam*)data;
if (!param || param->GetName() == "network")
{
// Network config has been changed, apply:
m_cfg_wifi_sq_good = MyConfig.GetParamValueFloat("network", "wifi.sq.good", -87);
m_cfg_wifi_sq_bad = MyConfig.GetParamValueFloat("network", "wifi.sq.bad", -89);
if (m_cfg_wifi_sq_good < m_cfg_wifi_sq_bad)
{
float x = m_cfg_wifi_sq_good;
m_cfg_wifi_sq_good = m_cfg_wifi_sq_bad;
m_cfg_wifi_sq_bad = x;
}
#ifdef CONFIG_OVMS_COMP_WIFI
WifiStaCheckSQ(NULL);
#endif
if (param && m_network_any)
PrioritiseAndIndicate();
}
}
void OvmsNetManager::EventSystemShuttingDown(std::string event, void* data)
{
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
StopMongooseTask();
#endif //#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
}
void OvmsNetManager::SaveDNSServer(ip_addr_t* dnsstore)
{
for (int i=0; i<DNS_MAX_SERVERS; i++)
{
dnsstore[i] = *dns_getserver(i);
ESP_LOGD(TAG, "Saved DNS#%d %s", i, inet_ntoa(dnsstore[i]));
}
}
void OvmsNetManager::SetDNSServer(ip_addr_t* dnsstore)
{
// Read DNS configuration:
std::string servers = MyConfig.GetParamValue("network", "dns");
if (!servers.empty())
{
int spos = 0;
size_t sep = 0;
while ((spos<DNS_MAX_SERVERS)&&(sep != string::npos))
{
// Set the specified DNS servers
size_t next = servers.find(' ',sep);
std::string cserver;
ip_addr_t serverip;
if (next == std::string::npos)
{
// The last one
cserver = std::string(servers,sep,string::npos);
sep = string::npos;
}
else
{
// One of many
cserver = std::string(servers,sep,next-sep);
sep = next+1;
}
ip4_addr_set_u32(ip_2_ip4(&serverip), ipaddr_addr(cserver.c_str()));
serverip.type = IPADDR_TYPE_V4;
if (!ip_addr_cmp(&serverip, &m_previous_dns[spos]))
{
m_previous_dns[spos] = serverip;
ESP_LOGI(TAG, "Set DNS#%d %s",spos,cserver.c_str());
}
dns_setserver(spos++, &serverip);
}
for (;spos<DNS_MAX_SERVERS;spos++)
{
dns_setserver(spos, IP_ADDR_ANY);
}
}
else if (dnsstore)
{
// Set stored DNS servers:
for (int i=0; i<DNS_MAX_SERVERS; i++)
{
if (ip_addr_cmp(&dnsstore[i], &m_previous_dns[i]))
{
m_previous_dns[i] = dnsstore[i];
ESP_LOGI(TAG, "Set DNS#%d %s", i, inet_ntoa(dnsstore[i]));
}
dns_setserver(i, &dnsstore[i]);
}
}
}
void OvmsNetManager::SetNetType(std::string type)
{
if (type == "wifi")
{
StdMetrics.ms_m_net_type->SetValue(type);
#ifdef CONFIG_OVMS_COMP_WIFI
if (MyPeripherals && MyPeripherals->m_esp32wifi)
MyPeripherals->m_esp32wifi->UpdateNetMetrics();
#endif // CONFIG_OVMS_COMP_WIFI
}
else if (type == "modem")
{
StdMetrics.ms_m_net_type->SetValue(type);
#ifdef CONFIG_OVMS_COMP_CELLULAR
if (MyPeripherals && MyPeripherals->m_cellular_modem)
MyPeripherals->m_cellular_modem->UpdateNetMetrics();
#endif // CONFIG_OVMS_COMP_CELLULAR
}
else
{
StdMetrics.ms_m_net_type->SetValue("none");
StdMetrics.ms_m_net_provider->SetValue("");
StdMetrics.ms_m_net_sq->SetValue(0, dbm);
}
}
void SafePrioritiseAndIndicate(void* ctx)
{
MyNetManager.DoSafePrioritiseAndIndicate();
}
void OvmsNetManager::PrioritiseAndIndicate()
{
tcpip_callback_with_block(SafePrioritiseAndIndicate, NULL, 1);
}
void OvmsNetManager::DoSafePrioritiseAndIndicate()
{
const char *search = NULL;
ip_addr_t* dns = NULL;
// A convenient place to keep track of connectivity in general
m_connected_any = m_connected_wifi || m_connected_modem;
m_network_any = m_connected_wifi || m_connected_modem || m_wifi_ap;
// Priority order...
if (m_connected_wifi)
{
// Wifi is up
SetNetType("wifi");
search = "st";
dns = m_dns_wifi;
}
else if (m_connected_modem)
{
// Modem is up
SetNetType("modem");
search = "pp";
dns = m_dns_modem;
}
if (search == NULL)
{
SetNetType("none");
return;
}
for (struct netif *pri = netif_list; pri != NULL; pri=pri->next)
{
if ((pri->name[0]==search[0])&&
(pri->name[1]==search[1]))
{
if (search[0] != m_previous_name[0] || search[1] != m_previous_name[1])
{
ESP_LOGI(TAG, "Interface priority is %c%c%d (" IPSTR "/" IPSTR " gateway " IPSTR ")",
pri->name[0], pri->name[1], pri->num,
IP2STR(&pri->ip_addr.u_addr.ip4), IP2STR(&pri->netmask.u_addr.ip4), IP2STR(&pri->gw.u_addr.ip4));
m_previous_name[0] = search[0];
m_previous_name[1] = search[1];
for (int i=0; i<DNS_MAX_SERVERS; i++)
m_previous_dns[i] = (ip_addr_t)ip_addr_any;
}
netif_set_default(pri);
SetDNSServer(dns);
return;
}
}
ESP_LOGE(TAG, "Inconsistent state: no interface of type '%s' found", search);
}
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
static void MongooseRawTask(void *pvParameters)
{
OvmsNetManager* me = (OvmsNetManager*)pvParameters;
me->MongooseTask();
}
void OvmsNetManager::MongooseTask()
{
int pri, lastpri = CONFIG_OVMS_NETMAN_TASK_PRIORITY;
// Initialise the mongoose manager
ESP_LOGD(TAG, "MongooseTask starting");
mg_mgr_init(&m_mongoose_mgr, NULL);
MyEvents.SignalEvent("network.mgr.init",NULL);
m_mongoose_running = true;
// Main event loop
while (m_mongoose_running)
{
// poll interfaces:
if (mg_mgr_poll(&m_mongoose_mgr, 250) == 0)
{
ESP_LOGD(TAG, "MongooseTask: no interfaces available => exit");
break;
}
// check for netmanager control jobs:
ProcessJobs();
// Detect broken mutex priority inheritance:
// (may be removed if solved by esp-idf commit 22d636b7b0d6006e06b5b3cfddfbf6e2cf69b4b8)
if ((pri = uxTaskPriorityGet(NULL)) != lastpri)
{
ESP_LOGD(TAG, "MongooseTask: priority changed from %d to %d", lastpri, pri);
lastpri = pri;
}
}
m_mongoose_running = false;
// Shutdown cleanly
ESP_LOGD(TAG, "MongooseTask stopping");
MyEvents.SignalEvent("network.mgr.stop",NULL);
mg_mgr_free(&m_mongoose_mgr);
m_mongoose_task = NULL;
vTaskDelete(NULL);
}
struct mg_mgr* OvmsNetManager::GetMongooseMgr()
{
return &m_mongoose_mgr;
}
bool OvmsNetManager::MongooseRunning()
{
return m_mongoose_running;
}
void OvmsNetManager::StartMongooseTask()
{
if (!m_mongoose_running)
{
if (m_network_any)
{
// wait for previous task to finish shutting down:
while (m_mongoose_task)
vTaskDelay(pdMS_TO_TICKS(50));
// start new task:
xTaskCreatePinnedToCore(MongooseRawTask, "OVMS NetMan",10*1024, (void*)this,
CONFIG_OVMS_NETMAN_TASK_PRIORITY, &m_mongoose_task, CORE(1));
AddTaskToMap(m_mongoose_task);
}
}
}
void OvmsNetManager::StopMongooseTask()
{
if (!m_network_any)
m_mongoose_running = false;
}
void OvmsNetManager::ProcessJobs()
{
netman_job_t* job;
while (xQueueReceive(m_jobqueue, &job, 0) == pdTRUE)
{
ESP_LOGD(TAG, "MongooseTask: got cmd %d from %p", job->cmd, job->caller);
switch (job->cmd)
{
case nmc_none:
break;
case nmc_list:
job->list.cnt = ListConnections(job->list.verbosity, job->list.buf);
break;
case nmc_close:
job->close.cnt = CloseConnection(job->close.id);
break;
case nmc_cleanup:
job->cleanup.cnt = CleanupConnections();
break;
default:
ESP_LOGW(TAG, "MongooseTask: got unknown cmd %d from %p", job->cmd, job->caller);
}
if (job->caller)
xTaskNotifyGive(job->caller);
ESP_LOGD(TAG, "MongooseTask: done cmd %d from %p", job->cmd, job->caller);
}
}
bool OvmsNetManager::ExecuteJob(netman_job_t* job, TickType_t timeout /*=portMAX_DELAY*/)
{
if (!m_mongoose_running)
return false;
if (timeout)
{
job->caller = xTaskGetCurrentTaskHandle();
xTaskNotifyWait(ULONG_MAX, ULONG_MAX, NULL, 0); // clear state
}
else
job->caller = 0;
ESP_LOGD(TAG, "send cmd %d from %p", job->cmd, job->caller);
if (xQueueSend(m_jobqueue, &job, timeout) != pdTRUE)
{
ESP_LOGW(TAG, "ExecuteJob: cmd %d: queue overflow", job->cmd);
return false;
}
if (timeout && ulTaskNotifyTake(pdTRUE, timeout) == 0)
{
// try to prevent delayed processing (cannot stop if already started):
netman_cmd_t cmd = job->cmd;
job->cmd = nmc_none;
job->caller = 0;
ESP_LOGW(TAG, "ExecuteJob: cmd %d: timeout", cmd);
return false;
}
ESP_LOGD(TAG, "done cmd %d from %p", job->cmd, job->caller);
return true;
}
void OvmsNetManager::ScheduleCleanup()
{
static netman_job_t job;
memset(&job, 0, sizeof(job));
job.cmd = nmc_cleanup;
ExecuteJob(&job, 0);
}
int OvmsNetManager::ListConnections(int verbosity, OvmsWriter* writer)
{
if (!m_mongoose_running)
return 0;
mg_connection *c;
int cnt = 0;
char local[48], remote[48];
writer->printf("ID Flags Handler Local Remote\n");
for (c = mg_next(&m_mongoose_mgr, NULL); c; c = mg_next(&m_mongoose_mgr, c))
{
if (c->flags & MG_F_LISTENING)
continue;
mg_conn_addr_to_str(c, local, sizeof(local), MG_SOCK_STRINGIFY_IP|MG_SOCK_STRINGIFY_PORT);
mg_conn_addr_to_str(c, remote, sizeof(remote), MG_SOCK_STRINGIFY_IP|MG_SOCK_STRINGIFY_PORT|MG_SOCK_STRINGIFY_REMOTE);
writer->printf("%08x %08x %08x %-21s %s\n", (uint32_t)c, (uint32_t)c->flags, (uint32_t)c->user_data, local, remote);
cnt++;
}
return cnt;
}
int OvmsNetManager::CloseConnection(uint32_t id)
{
if (!m_mongoose_running)
return 0;
mg_connection *c;
int cnt = 0;
for (c = mg_next(&m_mongoose_mgr, NULL); c; c = mg_next(&m_mongoose_mgr, c))
{
if (c->flags & MG_F_LISTENING)
continue;
if (id == 0 || c == (mg_connection*)id)
{
c->flags |= MG_F_CLOSE_IMMEDIATELY;
cnt++;
}
}
return cnt;
}
int OvmsNetManager::CleanupConnections()
{
if (!m_mongoose_running)
return 0;
struct netif *ni;
mg_connection *c;
tcpip_adapter_sta_list_t ap_ip_list;
union socket_address sa;
socklen_t slen = sizeof(sa);
int cnt = 0;
// get IP addresses of stations connected to our wifi AP:
ap_ip_list.num = 0;
#ifdef CONFIG_OVMS_COMP_WIFI
if (m_wifi_ap)
{
wifi_sta_list_t sta_list;
if (esp_wifi_ap_get_sta_list(&sta_list) != ESP_OK)
ESP_LOGW(TAG, "CleanupConnections: can't get AP station list");
else if (tcpip_adapter_get_sta_list(&sta_list, &ap_ip_list) != ESP_OK)
ESP_LOGW(TAG, "CleanupConnections: can't get AP station IP list");
}
#endif // #ifdef CONFIG_OVMS_COMP_WIFI
for (c = mg_next(&m_mongoose_mgr, NULL); c; c = mg_next(&m_mongoose_mgr, c))
{
if (c->flags & MG_F_LISTENING)
continue;
// get local address:
memset(&sa, 0, sizeof(sa));
if (getsockname(c->sock, &sa.sa, &slen) != 0)
{
ESP_LOGW(TAG, "CleanupConnections: conn %08x: getsockname failed", (uint32_t)c);
continue;
}
if (sa.sa.sa_family != AF_INET || sa.sin.sin_addr.s_addr == IPADDR_ANY || sa.sin.sin_addr.s_addr == IPADDR_NONE)
continue;
// find interface:
for (ni = netif_list; ni; ni = ni->next)
{
if (sa.sin.sin_addr.s_addr == ip4_addr_get_u32(netif_ip4_addr(ni)))
break;
}
if (ni)
ESP_LOGD(TAG, "CleanupConnections: conn %08x -> iface %c%c%d", (uint32_t)c, ni->name[0], ni->name[1], ni->num);
else
ESP_LOGD(TAG, "CleanupConnections: conn %08x -> no iface", (uint32_t)c);
if (!ni || !(ni->flags & NETIF_FLAG_UP) || !(ni->flags & NETIF_FLAG_LINK_UP))
{
ESP_LOGI(TAG, "CleanupConnections: closing conn %08x: interface/link down", (uint32_t)c);
c->flags |= MG_F_CLOSE_IMMEDIATELY;
cnt++;
continue;
}
// check remote address on wifi AP:
if (ni->name[0] == 'a' && ni->name[1] == 'p')
{
memset(&sa, 0, sizeof(sa));
if (getpeername(c->sock, &sa.sa, &slen) != 0)
{
ESP_LOGW(TAG, "CleanupConnections: conn %08x: getpeername failed", (uint32_t)c);
continue;
}
int i;
for (i = 0; i < ap_ip_list.num; i++)
{
if (sa.sin.sin_addr.s_addr == ap_ip_list.sta[i].ip.addr)
break;
}
if (i < ap_ip_list.num)
{
ESP_LOGD(TAG, "CleanupConnections: conn %08x -> AP IP " IPSTR, (uint32_t)c, IP2STR(&ap_ip_list.sta[i].ip));
}
else
{
ESP_LOGI(TAG, "CleanupConnections: closing conn %08x: AP peer disconnected", (uint32_t)c);
c->flags |= MG_F_CLOSE_IMMEDIATELY;
cnt++;
}
} // AP remotes
} // connection loop
return cnt;
}
bool OvmsNetManager::IsNetManagerTask()
{
// Return TRUE if the currently running task is the Net Manager task
extern void *pxCurrentTCB[portNUM_PROCESSORS];
return (m_mongoose_task == pxCurrentTCB[xPortGetCoreID()]);
}
#endif //#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE