/* ; 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 #include #include #include #include #include #include #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;ktype == 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; iRegisterCommand("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, "\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; iSetValue(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; iMongooseTask(); } 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