From 39a1fc9dbd37edf02d4787ef26bb8d54894bbf37 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Tue, 9 Jan 2024 07:27:31 +1100 Subject: [PATCH] AP_Networking: added option for PPP<->ethernet bridge when NET_OPTIONS is set to enable PPP bridging both an ethernet and a PPP link will be brought up, with IP forwarding making the PPP remote endpoint available on the ethernet LAN --- libraries/AP_Networking/AP_Networking.cpp | 34 +++++++- libraries/AP_Networking/AP_Networking.h | 16 ++++ .../AP_Networking/AP_Networking_Config.h | 14 +++ libraries/AP_Networking/AP_Networking_PPP.cpp | 87 +++++++++++++++++-- libraries/AP_Networking/config/lwipopts.h | 31 +++---- 5 files changed, 154 insertions(+), 28 deletions(-) diff --git a/libraries/AP_Networking/AP_Networking.cpp b/libraries/AP_Networking/AP_Networking.cpp index 87b5147b75..dd926a31e5 100644 --- a/libraries/AP_Networking/AP_Networking.cpp +++ b/libraries/AP_Networking/AP_Networking.cpp @@ -86,6 +86,20 @@ const AP_Param::GroupInfo AP_Networking::var_info[] = { AP_SUBGROUPINFO(param.test_ipaddr, "TEST_IP", 8, AP_Networking, AP_Networking_IPV4), #endif + // @Param: OPTIONS + // @DisplayName: Networking options + // @Description: Networking options + // @Bitmask: 0:EnablePPP Ethernet gateway + // @RebootRequired: True + // @User: Advanced + AP_GROUPINFO("OPTIONS", 9, AP_Networking, param.options, 0), + +#if AP_NETWORKING_PPP_GATEWAY_ENABLED + // @Group: REMPPP_IP + // @Path: AP_Networking_address.cpp + AP_SUBGROUPINFO(param.remote_ppp_ip, "REMPPP_IP", 10, AP_Networking, AP_Networking_IPV4), +#endif + AP_GROUPEND }; @@ -129,8 +143,19 @@ void AP_Networking::init() } #endif +#if AP_NETWORKING_PPP_GATEWAY_ENABLED + if (option_is_set(OPTION::PPP_ETHERNET_GATEWAY)) { + /* + when we are a PPP/Ethernet gateway we bring up the ethernet first + */ + backend = new AP_Networking_ChibiOS(*this); + backend_PPP = new AP_Networking_PPP(*this); + } +#endif + + #if AP_NETWORKING_BACKEND_PPP - if (AP::serialmanager().have_serial(AP_SerialManager::SerialProtocol_PPP, 0)) { + if (backend == nullptr && AP::serialmanager().have_serial(AP_SerialManager::SerialProtocol_PPP, 0)) { backend = new AP_Networking_PPP(*this); } #endif @@ -158,6 +183,13 @@ void AP_Networking::init() return; } +#if AP_NETWORKING_PPP_GATEWAY_ENABLED + if (backend_PPP != nullptr && !backend_PPP->init()) { + GCS_SEND_TEXT(MAV_SEVERITY_INFO, "NET: backend_PPP init failed"); + backend_PPP = nullptr; + } +#endif + announce_address_changes(); GCS_SEND_TEXT(MAV_SEVERITY_INFO,"NET: Initialized"); diff --git a/libraries/AP_Networking/AP_Networking.h b/libraries/AP_Networking/AP_Networking.h index 63936f025b..c83f0e26da 100644 --- a/libraries/AP_Networking/AP_Networking.h +++ b/libraries/AP_Networking/AP_Networking.h @@ -26,6 +26,7 @@ class AP_Networking public: friend class AP_Networking_Backend; friend class AP_Networking_ChibiOS; + friend class AP_Networking_PPP; friend class AP_Vehicle; friend class Networking_Periph; @@ -149,6 +150,13 @@ public: static const struct AP_Param::GroupInfo var_info[]; + enum class OPTION { + PPP_ETHERNET_GATEWAY=(1U<<0), + }; + bool option_is_set(OPTION option) const { + return (param.options.get() & int32_t(option)) != 0; + } + private: static AP_Networking *singleton; @@ -172,10 +180,18 @@ private: AP_Int32 tests; AP_Networking_IPV4 test_ipaddr{AP_NETWORKING_TEST_IP}; #endif + +#if AP_NETWORKING_PPP_GATEWAY_ENABLED + AP_Networking_IPV4 remote_ppp_ip{AP_NETWORKING_REMOTE_PPP_IP}; +#endif } param; AP_Networking_Backend *backend; +#if AP_NETWORKING_PPP_GATEWAY_ENABLED + AP_Networking_Backend *backend_PPP; +#endif + HAL_Semaphore sem; enum class NetworkPortType { diff --git a/libraries/AP_Networking/AP_Networking_Config.h b/libraries/AP_Networking/AP_Networking_Config.h index e815b87b5a..d9d8532eef 100644 --- a/libraries/AP_Networking/AP_Networking_Config.h +++ b/libraries/AP_Networking/AP_Networking_Config.h @@ -119,3 +119,17 @@ #ifndef AP_NETWORKING_SENDFILE_BUFSIZE #define AP_NETWORKING_SENDFILE_BUFSIZE (64*512) #endif + +#ifndef AP_NETWORKING_PPP_GATEWAY_ENABLED +#define AP_NETWORKING_PPP_GATEWAY_ENABLED (AP_NETWORKING_BACKEND_CHIBIOS && AP_NETWORKING_BACKEND_PPP) +#endif + +/* + the IP address given to the remote end of the PPP link when running + as a PPP<->ethernet gateway. If this is on the same subnet as the + ethernet interface IP then proxyarp will be used + */ +#ifndef AP_NETWORKING_REMOTE_PPP_IP +#define AP_NETWORKING_REMOTE_PPP_IP "0.0.0.0" +#endif + diff --git a/libraries/AP_Networking/AP_Networking_PPP.cpp b/libraries/AP_Networking/AP_Networking_PPP.cpp index 13929634e7..18f16f0bdb 100644 --- a/libraries/AP_Networking/AP_Networking_PPP.cpp +++ b/libraries/AP_Networking/AP_Networking_PPP.cpp @@ -57,10 +57,17 @@ void AP_Networking_PPP::ppp_status_callback(struct ppp_pcb_s *pcb, int code, voi switch (code) { case PPPERR_NONE: // got new addresses for the link - driver.activeSettings.ip = ntohl(netif_ip4_addr(pppif)->addr); - driver.activeSettings.gw = ntohl(netif_ip4_gw(pppif)->addr); - driver.activeSettings.nm = ntohl(netif_ip4_netmask(pppif)->addr); - driver.activeSettings.last_change_ms = AP_HAL::millis(); +#if AP_NETWORKING_PPP_GATEWAY_ENABLED + if (driver.frontend.option_is_set(AP_Networking::OPTION::PPP_ETHERNET_GATEWAY)) { + GCS_SEND_TEXT(MAV_SEVERITY_INFO, "PPP: got addresses"); + } else +#endif + { + driver.activeSettings.ip = ntohl(netif_ip4_addr(pppif)->addr); + driver.activeSettings.gw = ntohl(netif_ip4_gw(pppif)->addr); + driver.activeSettings.nm = ntohl(netif_ip4_netmask(pppif)->addr); + driver.activeSettings.last_change_ms = AP_HAL::millis(); + } break; case PPPERR_OPEN: @@ -94,10 +101,13 @@ bool AP_Networking_PPP::init() return false; } - // initialise TCP/IP thread - LWIP_TCPIP_LOCK(); - tcpip_init(NULL, NULL); - LWIP_TCPIP_UNLOCK(); + const bool ethernet_gateway = frontend.option_is_set(AP_Networking::OPTION::PPP_ETHERNET_GATEWAY); + if (!ethernet_gateway) { + // initialise TCP/IP thread + LWIP_TCPIP_LOCK(); + tcpip_init(NULL, NULL); + LWIP_TCPIP_UNLOCK(); + } hal.scheduler->delay(100); @@ -127,9 +137,14 @@ void AP_Networking_PPP::ppp_loop(void) while (!hal.scheduler->is_system_initialized()) { hal.scheduler->delay_microseconds(1000); } + const bool ppp_gateway = frontend.option_is_set(AP_Networking::OPTION::PPP_ETHERNET_GATEWAY); + if (ppp_gateway) { + // wait for the ethernet interface to be up + AP::network().startup_wait(); + } // ensure this thread owns the uart - uart->begin(0); + uart->begin(AP::serialmanager().find_baudrate(AP_SerialManager::SerialProtocol_PPP, 0)); uart->set_unbuffered_writes(true); while (true) { @@ -137,9 +152,63 @@ void AP_Networking_PPP::ppp_loop(void) // connect and set as default route LWIP_TCPIP_LOCK(); + +#if AP_NETWORKING_PPP_GATEWAY_ENABLED + if (ppp_gateway) { + /* + when bridging setup the ppp interface with the same IP + as the ethernet interface, and set the remote IP address + as the local address + 1 + */ + ip4_addr_t our_ip, his_ip; + const uint32_t ip = frontend.get_ip_active(); + uint32_t rem_ip = frontend.param.remote_ppp_ip.get_uint32(); + if (rem_ip == 0) { + // use ethernet IP +1 by default + rem_ip = ip+1; + } + our_ip.addr = htonl(ip); + his_ip.addr = htonl(rem_ip); + ppp_set_ipcp_ouraddr(ppp, &our_ip); + ppp_set_ipcp_hisaddr(ppp, &his_ip); + if (netif_list != nullptr) { + const uint32_t nmask = frontend.get_netmask_param(); + if ((ip & nmask) == (rem_ip & nmask)) { + // remote PPP IP is on the same subnet as the + // local ethernet IP, so enable proxyarp to avoid + // users having to setup routes in all devices + netif_set_proxyarp(netif_list, &his_ip); + } + } + } + + // connect to the remote end ppp_connect(ppp, 0); + if (ppp_gateway) { + extern struct netif *netif_list; + /* + when we are setup as a PPP gateway we want the pppif to be + first in the list so routing works if it is on the same + subnet + */ + if (netif_list != nullptr && + netif_list->next != nullptr && + netif_list->next->next == pppif) { + netif_list->next->next = nullptr; + pppif->next = netif_list; + netif_list = pppif; + } + } else { + netif_set_default(pppif); + } +#else + // normal PPP link, connect to the remote end and set as the + // default route + ppp_connect(ppp, 0); netif_set_default(pppif); +#endif // AP_NETWORKING_PPP_GATEWAY_ENABLED + LWIP_TCPIP_UNLOCK(); need_restart = false; diff --git a/libraries/AP_Networking/config/lwipopts.h b/libraries/AP_Networking/config/lwipopts.h index 8da2750f27..78ea48e827 100644 --- a/libraries/AP_Networking/config/lwipopts.h +++ b/libraries/AP_Networking/config/lwipopts.h @@ -74,7 +74,6 @@ extern "C" #define LWIP_ICMP LWIP_IPV4 #define LWIP_SNMP LWIP_UDP -#define MIB2_STATS LWIP_SNMP #ifdef LWIP_HAVE_MBEDTLS #define LWIP_SNMP_V3 (LWIP_SNMP) #endif @@ -255,6 +254,7 @@ a lot of data that needs to be copied, this should be set high. */ #define LWIP_ARP 1 #define ARP_TABLE_SIZE 10 #define ARP_QUEUEING 1 +#define ARP_PROXYARP_SUPPORT 1 /* ---------- IP options ---------- */ @@ -263,6 +263,12 @@ a lot of data that needs to be copied, this should be set high. */ on a device with only one network interface, define this to 0. */ #define IP_FORWARD 1 +/* + extra header space when forwarding for adding the ethernet header +*/ +#define PBUF_LINK_HLEN 16 + + /* IP reassembly and segmentation.These are orthogonal even * if they both deal with IP fragments */ #define IP_REASSEMBLY 1 @@ -302,22 +308,8 @@ a lot of data that needs to be copied, this should be set high. */ /* ---------- Statistics options ---------- */ -#define LWIP_STATS 1 -#define LWIP_STATS_DISPLAY 1 - -#if LWIP_STATS -#define LINK_STATS 1 -#define IP_STATS 1 -#define ICMP_STATS 1 -#define IGMP_STATS 1 -#define IPFRAG_STATS 1 -#define UDP_STATS 1 -#define TCP_STATS 1 -#define MEM_STATS 1 -#define MEMP_STATS 1 -#define PBUF_STATS 1 -#define SYS_STATS 1 -#endif /* LWIP_STATS */ +#define LWIP_STATS 0 +#define LWIP_STATS_DISPLAY 0 /* ---------- NETBIOS options ---------- */ #define LWIP_NETBIOS_RESPOND_NAME_QUERY 1 @@ -343,7 +335,10 @@ a lot of data that needs to be copied, this should be set high. */ #define MSCHAP_SUPPORT 0 /* Set > 0 for MSCHAP */ #define CBCP_SUPPORT 0 /* Set > 0 for CBCP (NOT FUNCTIONAL!) */ #define CCP_SUPPORT 0 /* Set > 0 for CCP */ -#define VJ_SUPPORT 1 /* Set > 0 for VJ header compression. */ +/* + VJ support disabled due to bugs with IP forwarding + */ +#define VJ_SUPPORT 0 /* Set > 0 for VJ header compression. */ #define MD5_SUPPORT 0 /* Set > 0 for MD5 (see also CHAP) */ #endif /* PPP_SUPPORT */