diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..2ef31b5 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,37 @@ +name: CI + +on: + push: + pull_request: + +jobs: + build: + strategy: + matrix: + os: [ ubuntu-20.04, ubuntu-22.04, ubuntu-24.04 ] + compiler: [clang, gcc] + installdeps: ['sudo apt install -y ninja-build cmake'] + include: + - os: macos-latest + installdeps: 'brew install cmake' + compiler: clang + runs-on: ${{matrix.os}} + name: "${{matrix.os}} - ${{matrix.compiler}}" + + env: + CC: ${{ matrix.compiler }} + LSAN_OPTIONS: verbosity=1:log_threads=1 + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install deps + run: ${{matrix.installdeps }} + + - name: Run cmake + run: cmake -B lwipovpn-build + + - name: Build with cmake + run: cmake --build lwipovpn-build diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8eee68f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +cmake-build-* diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..57967a3 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,76 @@ +cmake_minimum_required(VERSION 3.14) + +include(CheckSymbolExists) + +set (CMAKE_CONFIGURATION_TYPES "Debug;Release;ASAN") + +# AddressSanitize - use CXX=clang++ CC=clang cmake -DCMAKE_BUILD_TYPE=asan to build with ASAN +set(CMAKE_C_FLAGS_ASAN + "-fsanitize=address,undefined -fno-sanitize-recover=all -fno-optimize-sibling-calls -fsanitize-address-use-after-scope -fno-omit-frame-pointer -g -O1" + CACHE STRING "Flags used by the C compiler during AddressSanitizer builds." + FORCE) +set(CMAKE_CXX_FLAGS_ASAN + "-fsanitize=address,undefined -fno-sanitize-recover=all -fno-optimize-sibling-calls -fsanitize-address-use-after-scope -fno-omit-frame-pointer -g -O1" + CACHE STRING "Flags used by the C++ compiler during AddressSanitizer builds." + FORCE) + +project(lwipovpn) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED TRUE) + +set(LWIP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lwip) +set(LWIP_CONTRIB_DIR ${LWIP_DIR}/contrib/) + +set (LWIP_INCLUDE_DIRS + "${LWIP_DIR}/src/include" + "${LWIP_DIR}/contrib" + "${LWIP_DIR}/contrib/ports/unix/port/include" + "${CMAKE_CURRENT_SOURCE_DIR}/conf" +) + +include(${LWIP_DIR}/src/Filelists.cmake) +include(${LWIP_DIR}/contrib/Filelists.cmake) +include(${LWIP_DIR}/contrib/ports/unix/Filelists.cmake) +include(${LWIP_DIR}/contrib/ports/CMakeCommon.cmake) + +set (LWIP_DEFINITIONS LWIP_DEBUG=1) + +set(LWIP_OVPN_INCLUDE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}/app" + "${CMAKE_CURRENT_SOURCE_DIR}/netif" +) + +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) + +# Have a warning about missing FreeBSD support until the PR get merged and we update our submodule +if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") + message(INFO "Note for FreeBSD support manually apply/checkout https://github.com/lwip-tcpip/lwip/pull/46 to the lwip submodule") +endif() + + +add_executable(lwipovpn + app/unixaf_app.c + netif/unixaf.c + netif/unixaf.h + netif/unixaf_host.c + netif/unixaf_host.h +) + +# C doesn't seem to have a good easy to use random function. Use arc4random if available otherwise rand +check_symbol_exists(arc4random stdlib.h HAVE_ARC4RANDOM) + +# we do not care about C90 compatibility in lwipovpn since OpenVPN itself requires C11 +set(LWIP_COMPILER_FLAGS_OVPN ${LWIP_COMPILER_FLAGS}) +LIST(REMOVE_ITEM LWIP_COMPILER_FLAGS_OVPN $<$:-Wc90-c99-compat>) +LIST(REMOVE_ITEM LWIP_COMPILER_FLAGS_OVPN -Waggregate-return) + +target_include_directories(lwipovpn PRIVATE ${LWIP_INCLUDE_DIRS} ${LWIP_OVPN_INCLUDE_DIRS}) +target_compile_options(lwipovpn PRIVATE ${LWIP_COMPILER_FLAGS_OVPN}) +target_compile_definitions(lwipovpn PRIVATE ${LWIP_DEFINITIONS} ${LWIP_MBEDTLS_DEFINITIONS}) +if (NOT "${HAVE_ARC4RANDOM}") + target_compile_definitions(lwipovpn PRIVATE -DARC4RANDOM_MISSING) +endif() +target_link_libraries(lwipovpn PRIVATE ${LWIP_SANITIZER_LIBS} lwipcontribexamples lwipcontribapps lwipcontribaddons lwipallapps lwipcontribportunix lwipcore lwipmbedtls) +target_link_libraries(lwipovpn PRIVATE Threads::Threads) diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..961f0cc --- /dev/null +++ b/COPYING @@ -0,0 +1,26 @@ +Copyright (c) 2001, 2002 Swedish Institute of Computer Science. +Copyright (c) 2024 Arne Schwabe +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..41e1ee9 --- /dev/null +++ b/README.md @@ -0,0 +1,80 @@ +OpenVPN lwipovpn tun/tap emulator +================================= +Overview +-------- +This is a small helper tool based on [lwIP - A Lightweight TCP/IP stack](https://savannah.nongnu.org/projects/lwip/) +to emulate a tun/tap device in userspace without having an effect on the system that the lwip is running on. + +This allows: + + - testing a VPN connection without root + - pinging the connected client + - running tests against the client and the lwip stack + - using lwip example applications + - lwip http server + - lwip iperf server + - ... + - better automated testing + +Features +-------- + - IPv4 Support + - IPv6 Support + - automatic configuration of IPv4 and IPv6 addresses + - tap or tun emulation + + +Enabled apps +------------ +lwip comes a number of demo/default apps. lwipovpn has enabled most of them to be helpful in testing. Further apps +can be enabled/implemented to allow even more testing. + + - netio (https://www.nwlab.net/art/netio/netio.html) + - iperf 2 (https://iperf.fr/) + - http server + - shell (a simple shell that can be used with telnet to make some network diagnostics) + - tcp echo (port 7) + - udp echo (port 7) + - + +Limitations +----------- + - Data through is limited. Iperf performance is at 20 MBit/s. This is not a problem for the indented purpose of + this tool which is for testing. + - Windows port is currently missing. It should be possible to use pipes instead of a socketpair with + AF_UNIX/SOCK_DGRAM to support Windows. This might also require adding a length header to ensure that always + full packets are read/written. + - LWIP does not have a netmask for IPv6 addresses. Addresses are always assumed to be /64 + - Routes are ignored and lwipovpn assumes everything to be reachable via OpenVPN + +Building +-------- +This project uses the CMake build system. + + cmake -B lwipovpnbuild -S lwipovpn + cmake --build lwipovpnbuild + +This should result in a `lwipovpnbuild/lwipovpn` binary that can be used with OpenVPN master and +OpenVPN 2.7.x like this: + + openvpn --config client.ovpn --dev-node unix:lwipovpnbuild/lwipovpn + +Implementation +-------------- +The OpenVPN process will call `(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds)` for connection between OpenVPN and lwipovpn +and execute ovpnlwip and pass the second fd to the process. The rest of the configuration is passed as environment +variables. + - TUNTAP_SOCKET_FD: the fd number of the AF_UNIX socket + - TUN_UNIXAF_PATH: alternative to TUNTAP_SOCKET_FD containing a unix domain socket file path. + - TUNTAP_DEV_TYPE: the type of device to emulate: tap or tun + - TUNTAP_MTU: MTU of the emulated devices + - ifconfig_gateway: Gateway address. Derived from route-gateway in OpenVPN + - ifconfig_local, ifconfig_netmask, ifconfig_netmask, ifconfig_ipv6_local, ifconfig_ipv6_netbits as described in the + openvpn manual page. + +Both the OpenVPN --dev-node unix: and the lwipovpn can be used for other purposes but especially lwipovpn is currently +very OpenVPN specific as it uses the OpenVPN environment variables names. + +License +------- +This tool is under the same (3-Clause BSD License)[COPYING] as lwIP itself. \ No newline at end of file diff --git a/app/afunix_config.h b/app/afunix_config.h new file mode 100644 index 0000000..c292e68 --- /dev/null +++ b/app/afunix_config.h @@ -0,0 +1,65 @@ +/** + * Additional settings for the afunix app. + * Copy this to lwipcfg.h and make the config changes you need. + */ + +/* configuration for this port, not used */ +#define PPP_USERNAME "Admin" +#define PPP_PASSWORD "pass" + + + +/* remember to change this MAC address to suit your needs! + the last octet will be increased by netif->num for each netif */ +#define LWIP_MAC_ADDR_BASE {0x00,0x01,0x02,0x03,0x04,0x05} + +/* #define USE_SLIPIF 0 */ +/* #define SIO_USE_COMPORT 0 */ +#ifdef USE_SLIPIF +#if USE_SLIPIF +#define LWIP_PORT_INIT_SLIP1_IPADDR(addr) IP4_ADDR((addr), 192, 168, 2, 2) +#define LWIP_PORT_INIT_SLIP1_GW(addr) IP4_ADDR((addr), 192, 168, 2, 1) +#define LWIP_PORT_INIT_SLIP1_NETMASK(addr) IP4_ADDR((addr), 255, 255, 255, 0) +#if USE_SLIPIF > 1 +#define LWIP_PORT_INIT_SLIP2_IPADDR(addr) IP4_ADDR((addr), 192, 168, 2, 1) +#define LWIP_PORT_INIT_SLIP2_GW(addr) IP4_ADDR((addr), 0, 0, 0, 0) +#define LWIP_PORT_INIT_SLIP2_NETMASK(addr) IP4_ADDR((addr), 255, 255, 255, 0)*/ +#endif /* USE_SLIPIF > 1 */ +#endif /* USE_SLIPIF */ +#endif /* USE_SLIPIF */ + +/* configuration for applications */ + +#define LWIP_CHARGEN_APP 0 +#define LWIP_DNS_APP 0 +#define LWIP_HTTPD_APP 1 +/* Set this to 1 to use the netconn http server, + * otherwise the raw api server will be used. */ +#define LWIP_HTTPD_APP_NETCONN 0 +#define LWIP_HTTPD_EXAMPLE_CUSTOMFILES_ROOTDIR 1 +#define LWIP_HTTPD_EXAMPLE_CGI_SIMPLE 0 +#define LWIP_HTTPD_EXAMPLE_SSI_SIMPLE 0 +#define LWIP_NETBIOS_APP 0 +#define LWIP_NETIO_APP 1 +#define LWIP_MDNS_APP 0 +#define LWIP_MQTT_APP 0 +#define LWIP_PING_APP 0 +#define LWIP_RTP_APP 0 +#define LWIP_SHELL_APP 1 +#define LWIP_SNMP_APP 1 +#define LWIP_SNTP_APP 1 +#define LWIP_SOCKET_EXAMPLES_APP 0 +#define LWIP_TCPECHO_APP 1 +/* Set this to 1 to use the netconn tcpecho server, + * otherwise the raw api server will be used. */ +/*#define LWIP_TCPECHO_APP_NETCONN */ +#define LWIP_TFTP_APP 0 +#define LWIP_TFTP_CLIENT_APP 0 +#define LWIP_UDPECHO_APP 1 +#define LWIP_LWIPERF_APP 1 + +#define USE_DHCP 0 +/*#define USE_AUTOIP 1*/ + +/* define this to your custom application-init function */ +/* #define LWIP_APP_INIT my_app_init() */ diff --git a/app/unixaf_app.c b/app/unixaf_app.c new file mode 100644 index 0000000..45a681f --- /dev/null +++ b/app/unixaf_app.c @@ -0,0 +1,533 @@ +/* + * Copyright (c) 2001,2002 Florian Schulze. + * Copyright (c) 2024 Arne Schwabe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the authors nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is has been based on part on the lwIP test.c app + * + */ + +/* C runtime includes */ +#include +#include +#include +#include +#include +#include + +/* lwIP core includes */ +#include "lwip/opt.h" + +#include "lwip/sys.h" +#include "lwip/timeouts.h" +#include "lwip/debug.h" +#include "lwip/stats.h" +#include "lwip/init.h" +#include "lwip/tcpip.h" +#include "lwip/netif.h" +#include "lwip/api.h" + +#include "lwip/tcp.h" +#include "lwip/udp.h" +#include "lwip/dns.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" + +/* lwIP netif includes */ +#include "lwip/etharp.h" +#include "netif/ethernet.h" + +/* applications includes */ +#include "lwip/apps/netbiosns.h" +#include "lwip/apps/httpd.h" +#include "apps/httpserver/httpserver-netconn.h" +#include "apps/netio/netio.h" +#include "apps/ping/ping.h" +#include "apps/rtp/rtp.h" +#include "apps/chargen/chargen.h" +#include "apps/shell/shell.h" +#include "apps/tcpecho/tcpecho.h" +#include "apps/udpecho/udpecho.h" +#include "apps/tcpecho_raw/tcpecho_raw.h" +#include "apps/socket_examples/socket_examples.h" + +#include "examples/lwiperf/lwiperf_example.h" +#include "examples/mdns/mdns_example.h" +#include "examples/snmp/snmp_example.h" +#include "examples/tftp/tftp_example.h" +#include "examples/sntp/sntp_example.h" +#include "examples/mqtt/mqtt_example.h" + +#include "examples/httpd/cgi_example/cgi_example.h" +#include "examples/httpd/fs_example/fs_example.h" +#include "examples/httpd/https_example/https_example.h" +#include "examples/httpd/ssi_example/ssi_example.h" + +#include "lwip/sockets.h" + +#include "unixaf.h" + +#include "netif/ppp/ppp_opts.h" + +/* include the port-dependent configuration */ +#include "afunix_config.h" + +/** Define this to 1 to enable a port-specific ethernet interface as default interface. */ +#ifndef USE_DEFAULT_ETH_NETIF +#define USE_DEFAULT_ETH_NETIF 1 +#endif + +/** Use an ethernet adapter? Default to enabled if port-specific ethernet netif or PPPoE are used. */ +#ifndef USE_ETHERNET +#define USE_ETHERNET (USE_DEFAULT_ETH_NETIF || PPPOE_SUPPORT) +#endif + +/** Use an ethernet adapter for TCP/IP? By default only if port-specific ethernet netif is used. */ +#ifndef USE_ETHERNET_TCPIP +#define USE_ETHERNET_TCPIP (USE_DEFAULT_ETH_NETIF) +#endif + +// #define LWIPOVPN_DEBUG_STATUS 1 + +#ifndef USE_DHCP +#define USE_DHCP LWIP_DHCP +#endif + +/* global variables for netifs */ +#if LWIP_DHCP +/* dhcp struct for the ethernet netif */ +static struct dhcp netif_dhcp; +#endif /* LWIP_DHCP */ + +#ifndef LWIP_IPV4 +#error unixaf_app requires LWIP_IPV4 +#endif + +#ifndef LWIP_IPV6 +#error unixaf_app requires LWIP_IPV6 +#endif + + + +static const char* +status_print_ip_addr(const ip4_addr_t *addr, char *buf, size_t buflen) +{ + if (!addr || !addr->addr) + return "(not set)"; + else + return ip4addr_ntoa_r(addr, buf, (int) buflen); +} + +static const char* +status_print_ip6_addr(const ip6_addr_t *addr, char *buf, size_t buflen) +{ + if (!addr) + return "(not set)"; + else + return ip6addr_ntoa_r(addr, buf, (int) buflen); +} + +static void +status_print(struct netif *netif, const char *prefix) { + char ip_local[32], ip_netmask[32], ip_gw[32]; + char ip6_local[48]; + + + printf("%s: type=%s mtu=%d local_ip=%s netmask=%s gw=%s local_ipv6=%s\n", + prefix, + netif_is_tap(netif) ? "tap" : "tun", + netif->mtu, + status_print_ip_addr(netif_ip4_addr(netif), ip_local, sizeof(ip_local)), + status_print_ip_addr(netif_ip4_netmask(netif), ip_netmask, sizeof(ip_netmask)), + status_print_ip_addr(netif_ip4_gw(netif), ip_gw, sizeof(ip_gw)), + status_print_ip6_addr(netif_ip6_addr(netif, 0), ip6_local, sizeof(ip6_local)) + ); +} + +static void +status_callback(struct netif *state_netif) { +#ifdef LWIPOVPN_DEBUG_STATUS + if (netif_is_up(state_netif)) { + status_print(state_netif, "status_callback==UP"); + + } else { + printf("status_callback==DOWN\n"); + } +#else + // silence unused warning + (void) state_netif; +#endif +} + +static void +link_callback(struct netif *state_netif) { +#ifdef LWIPOVPN_DEBUG_STATUS + if (netif_is_link_up(state_netif)) { + printf("link_callback==UP\n"); + } else { + printf("link_callback==DOWN\n"); + } +#else + // silence unused warning + (void) state_netif; +#endif +} + +/** returns the parsed IPv4 address in network order */ +static ip4_addr_t +get_ipv4_addr_from_env(const char *env_name) { + ip4_addr_t addr; + + const char *ipv4_addr = getenv(env_name); + if (!ipv4_addr) { + ipv4_addr = "(not set)"; + } + + if (!lwip_inet_pton(AF_INET, ipv4_addr, &addr)) { + char errmsg[512]; + snprintf(errmsg, sizeof(errmsg), "Could not convert %s=%s to IPv4 address", + env_name, ipv4_addr); + perror(errmsg); + addr.addr = IPADDR_ANY; + } + return addr; +} + +static ip6_addr_t +get_ipv6_addr_from_env(const char *env_name) { + ip6_addr_t addr = {0}; + + const char *ipv6_addr = getenv(env_name); + if (!ipv6_addr) { + ipv6_addr = "(not set)"; + } + + if (!lwip_inet_pton(AF_INET6, ipv6_addr, &addr)) { + char errmsg[512]; + snprintf(errmsg, sizeof(errmsg), "Could not convert %s=%s to IPv6 address", + env_name, ipv6_addr); + perror(errmsg); + } + return addr; +} + +/** + * Configure IPv4 on the network interface + * @return if dhcp should be started + */ +static bool +set_ipv4_addr_from_env(struct netif *netif) { + if (getenv("ifconfig_local_dhcp")) { + dhcp_set_struct(netif, &netif_dhcp); + printf("Using DHCP to configure IPv4"); + return true; + } else { + /* The variables match OpenVPN's variables */ + ip4_addr_t ip_addr = get_ipv4_addr_from_env("ifconfig_local"); + ip4_addr_t ip_netmask = get_ipv4_addr_from_env("ifconfig_netmask"); + ip4_addr_t ip_remote = get_ipv4_addr_from_env("ifconfig_gateway"); + + netif_set_addr(netif, &ip_addr, &ip_netmask, &ip_remote); + return false; + } +} + +static void +set_ipv6_addr_from_env(struct netif *netif) { + int netbits = 64; + + /* The variables match OpenVPN's variables */ + ip6_addr_t ip_addr = get_ipv6_addr_from_env("ifconfig_ipv6_local"); + + char *ipv6_netbits = getenv("ifconfig_ipv6_netbits"); + if (ipv6_netbits) { + netbits = atoi(ipv6_netbits); + } + + /* This uses addr 0 */ + netif_create_ip6_linklocal_address(netif, 1); + + /* There seem to be no way to specify an IPv6 netmask. Just warn if the + * netmask is different from the default /64 */ + if (netbits != 64) + { + printf("IPv6 prefix (%d) different from the default /64 is " + "not supported (ignored).\n", netbits); + } + + /* TODO: figure out what addr_idx parameter does, looks like just the + * index of the IP to set multiple IPs */ + netif_ip6_addr_set(netif, 0, &ip_addr); + netif_ip6_addr_set_state(netif, 0, IP6_ADDR_PREFERRED); +} + +/* global variable holding the netif configuration */ +struct netif netif = {0}; + +static void +init_unixaf_netif(void) { + netif_add(&netif, NULL, NULL, NULL, NULL, unixafif_init, tcpip_input); + netif_set_default(&netif); +} + +/* This function initializes all network interfaces */ +static void +afunix_netif_init(void) { + bool startdhcp; + + init_unixaf_netif(); + + /* set status callbacks */ + netif_set_status_callback(netif_default, status_callback); + netif_set_link_callback(netif_default, link_callback); + + /* set address families from OpenVPN environment variables */ + startdhcp = set_ipv4_addr_from_env(netif_default); + set_ipv6_addr_from_env(netif_default); + + netif_set_up(netif_default); + + if (startdhcp) { + err_t err = dhcp_start(netif_default); + LWIP_ASSERT("dhcp_start failed", err == ERR_OK); + } +} + +#if LWIP_DNS_APP && LWIP_DNS +static void +dns_found(const char *name, const ip_addr_t *addr, void *arg) +{ + LWIP_UNUSED_ARG(arg); + printf("%s: %s\n", name, addr ? ipaddr_ntoa(addr) : ""); +} + +static void +dns_dorequest(void *arg) +{ + const char* dnsname = "3com.com"; + ip_addr_t dnsresp; + LWIP_UNUSED_ARG(arg); + + if (dns_gethostbyname(dnsname, &dnsresp, dns_found, NULL) == ERR_OK) { + dns_found(dnsname, &dnsresp, NULL); + } +} +#endif /* LWIP_DNS_APP && LWIP_DNS */ + +/* This function initializes applications */ +static void +apps_init(void) { +#if LWIP_DNS_APP && LWIP_DNS + /* wait until the netif is up (for dhcp, autoip or ppp) */ + sys_timeout(5000, dns_dorequest, NULL); +#endif /* LWIP_DNS_APP && LWIP_DNS */ + +#if LWIP_CHARGEN_APP && LWIP_SOCKET + chargen_init(); +#endif /* LWIP_CHARGEN_APP && LWIP_SOCKET */ + +#if LWIP_PING_APP && LWIP_RAW && LWIP_ICMP + ping_init(&netif_default->gw); +#endif /* LWIP_PING_APP && LWIP_RAW && LWIP_ICMP */ + +#if LWIP_NETBIOS_APP && LWIP_UDP + netbiosns_init(); +#ifndef NETBIOS_LWIP_NAME +#if LWIP_NETIF_HOSTNAME + netbiosns_set_name(netif_default->hostname); +#else + netbiosns_set_name("NETBIOSLWIPDEV"); +#endif +#endif +#endif /* LWIP_NETBIOS_APP && LWIP_UDP */ + +#if LWIP_HTTPD_APP && LWIP_TCP +#if defined(LWIP_HTTPD_APP_NETCONN) && LWIP_HTTPD_APP_NETCONN + http_server_netconn_init(); +#else /* LWIP_HTTPD_APP_NETCONN */ +#if defined(LWIP_HTTPD_EXAMPLE_CUSTOMFILES) && LWIP_HTTPD_EXAMPLE_CUSTOMFILES && defined(LWIP_HTTPD_EXAMPLE_CUSTOMFILES_ROOTDIR) + fs_ex_init(LWIP_HTTPD_EXAMPLE_CUSTOMFILES_ROOTDIR); +#endif + httpd_init(); +#if defined(LWIP_HTTPD_EXAMPLE_SSI_SIMPLE) && LWIP_HTTPD_EXAMPLE_SSI_SIMPLE + ssi_ex_init(); +#endif +#if defined(LWIP_HTTPD_EXAMPLE_CGI_SIMPLE) && LWIP_HTTPD_EXAMPLE_CGI_SIMPLE + cgi_ex_init(); +#endif +#if defined(LWIP_HTTPD_EXAMPLE_HTTPS) && LWIP_HTTPD_EXAMPLE_HTTPS + https_ex_init(); +#endif +#endif /* LWIP_HTTPD_APP_NETCONN */ +#endif /* LWIP_HTTPD_APP && LWIP_TCP */ + +#if LWIP_NETIO_APP && LWIP_TCP + netio_init(); +#endif /* LWIP_NETIO_APP && LWIP_TCP */ + +#if LWIP_RTP_APP && LWIP_SOCKET && LWIP_IGMP + rtp_init(); +#endif /* LWIP_RTP_APP && LWIP_SOCKET && LWIP_IGMP */ + +#if LWIP_SHELL_APP && LWIP_NETCONN + shell_init(); +#endif /* LWIP_SHELL_APP && LWIP_NETCONN */ +#if LWIP_TCPECHO_APP +#if LWIP_NETCONN && defined(LWIP_TCPECHO_APP_NETCONN) + tcpecho_init(); +#else /* LWIP_NETCONN && defined(LWIP_TCPECHO_APP_NETCONN) */ + tcpecho_raw_init(); +#endif +#endif /* LWIP_TCPECHO_APP && LWIP_NETCONN */ +#if LWIP_UDPECHO_APP && LWIP_NETCONN + udpecho_init(); +#endif /* LWIP_UDPECHO_APP && LWIP_NETCONN */ +#if LWIP_SOCKET_EXAMPLES_APP && LWIP_SOCKET + socket_examples_init(); +#endif /* LWIP_SOCKET_EXAMPLES_APP && LWIP_SOCKET */ +#if LWIP_MDNS_APP + mdns_example_init(); +#endif +#if LWIP_SNMP_APP + snmp_example_init(); +#endif +#if LWIP_SNTP_APP + sntp_example_init(); +#endif +#if LWIP_TFTP_APP + tftp_example_init_server(); +#endif +#if LWIP_TFTP_CLIENT_APP + tftp_example_init_client(); +#endif +#if LWIP_LWIPERF_APP + lwiperf_example_init(); +#endif +#if LWIP_MQTT_APP + mqtt_example_init(); +#endif + +#ifdef LWIP_APP_INIT + LWIP_APP_INIT(); +#endif +} + +/* This function initializes this lwIP test. When NO_SYS=1, this is done in + * the main_loop context (there is no other one), when NO_SYS=0, this is done + * in the tcpip_thread context */ +static void +unixaf_app_init(void *arg) { /* remove compiler warning */ +#if NO_SYS + LWIP_UNUSED_ARG(arg); +#else /* NO_SYS */ + sys_sem_t *init_sem; + LWIP_ASSERT("arg != NULL", arg != NULL); + init_sem = (sys_sem_t *) arg; +#endif /* NO_SYS */ + + /* init randomizer again (seed per thread) */ + srand((unsigned int) time(NULL)); + + /* init network interfaces */ + afunix_netif_init(); + + /* init apps */ + apps_init(); + + /* print status interface */ + status_print(netif_default, "lwipovpn init complete"); + +#if !NO_SYS + sys_sem_signal(init_sem); +#endif /* !NO_SYS */ +} + +/* This is somewhat different to other ports: we have a main loop here: + * a dedicated task that waits for packets to arrive. This would normally be + * done from interrupt context with embedded hardware, but we don't get an + * interrupt in windows for that :-) */ +static void +main_loop(void) { + err_t err; + sys_sem_t init_sem; + + /* initialize lwIP stack, network interfaces and applications */ + err = sys_sem_new(&init_sem, 0); + LWIP_ASSERT("failed to create init_sem", err == ERR_OK); + LWIP_UNUSED_ARG(err); + tcpip_init(unixaf_app_init, &init_sem); + /* we have to wait for initialization to finish before + * calling update_adapter()! */ + sys_sem_wait(&init_sem); + sys_sem_free(&init_sem); + +#if (LWIP_SOCKET || LWIP_NETCONN) && LWIP_NETCONN_SEM_PER_THREAD + netconn_thread_init(); +#endif + + /* MAIN LOOP for driver update (and timers if NO_SYS) */ + while (!LWIP_EXAMPLE_APP_ABORT()) { + unixafif_poll(netif_default); + +#if ENABLE_LOOPBACK && !LWIP_NETIF_LOOPBACK_MULTITHREADING + /* check for loopback packets on all netifs */ + netif_poll_all(); +#endif /* ENABLE_LOOPBACK && !LWIP_NETIF_LOOPBACK_MULTITHREADING */ + +#if 0 + { + pid_t parent = getppid(); + if (parent == 1) { + printf("Parent become PID 1, parent dead?"); + } + } +#endif + } + +#if (LWIP_SOCKET || LWIP_NETCONN) && LWIP_NETCONN_SEM_PER_THREAD + netconn_thread_cleanup(); +#endif +} + +int main(void) { + /* no stdio-buffering, please! */ + setvbuf(stdout, NULL, _IONBF, 0); + + main_loop(); + + return 0; +} + +/* This function is only required to prevent arch.h including stdio.h + * (which it does if LWIP_PLATFORM_ASSERT is undefined) + */ +void lwip_example_app_platform_assert(const char *msg, int line, const char *file) { + printf("Assertion \"%s\" failed at line %d in %s\n", msg, line, file); + fflush(NULL); + abort(); +} diff --git a/conf/lwipopts.h b/conf/lwipopts.h new file mode 100644 index 0000000..928860e --- /dev/null +++ b/conf/lwipopts.h @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_LWIPOPTS_H +#define LWIP_LWIPOPTS_H + +#ifdef LWIP_OPTTEST_FILE +#include "lwipopts_test.h" +#else /* LWIP_OPTTEST_FILE */ + +#define LWIP_IPV4 1 +#define LWIP_IPV6 1 + +#define NO_SYS 0 +#define LWIP_SOCKET (NO_SYS==0) +#define LWIP_NETCONN (NO_SYS==0) +#define LWIP_NETIF_API (NO_SYS==0) + +#define LWIP_IGMP LWIP_IPV4 +#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 + +#define LWIP_DNS LWIP_UDP +#define LWIP_MDNS_RESPONDER LWIP_UDP + +#define LWIP_NUM_NETIF_CLIENT_DATA (LWIP_MDNS_RESPONDER) + +#define LWIP_HAVE_LOOPIF 1 +#define LWIP_NETIF_LOOPBACK 1 +#define LWIP_LOOPBACK_MAX_PBUFS 20 + +#define TCP_LISTEN_BACKLOG 1 + +#define LWIP_COMPAT_SOCKETS 1 +#define LWIP_SO_RCVTIMEO 1 +#define LWIP_SO_RCVBUF 1 + +#define LWIP_TCPIP_CORE_LOCKING 1 + +#define LWIP_NETIF_LINK_CALLBACK 1 +#define LWIP_NETIF_STATUS_CALLBACK 1 +#define LWIP_NETIF_EXT_STATUS_CALLBACK 1 + +#ifdef LWIP_DEBUG + +#define LWIP_DBG_MIN_LEVEL 0 +#define PPP_DEBUG LWIP_DBG_OFF +#define MEM_DEBUG LWIP_DBG_OFF +#define MEMP_DEBUG LWIP_DBG_OFF +#define PBUF_DEBUG LWIP_DBG_OFF +#define API_LIB_DEBUG LWIP_DBG_OFF +#define API_MSG_DEBUG LWIP_DBG_OFF +#define TCPIP_DEBUG LWIP_DBG_OFF +#define NETIF_DEBUG LWIP_DBG_OFF +#define SOCKETS_DEBUG LWIP_DBG_OFF +#define DNS_DEBUG LWIP_DBG_OFF +#define AUTOIP_DEBUG LWIP_DBG_OFF +#define DHCP_DEBUG LWIP_DBG_OFF +#define IP_DEBUG LWIP_DBG_OFF +#define IP_REASS_DEBUG LWIP_DBG_OFF +#define ICMP_DEBUG LWIP_DBG_OFF +#define IGMP_DEBUG LWIP_DBG_OFF +#define UDP_DEBUG LWIP_DBG_OFF +#define TCP_DEBUG LWIP_DBG_OFF +#define TCP_INPUT_DEBUG LWIP_DBG_OFF +#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF +#define TCP_RTO_DEBUG LWIP_DBG_OFF +#define TCP_CWND_DEBUG LWIP_DBG_OFF +#define TCP_WND_DEBUG LWIP_DBG_OFF +#define TCP_FR_DEBUG LWIP_DBG_OFF +#define TCP_QLEN_DEBUG LWIP_DBG_OFF +#define TCP_RST_DEBUG LWIP_DBG_OFF +#endif + +#define LWIP_DBG_TYPES_ON (LWIP_DBG_ON|LWIP_DBG_TRACE|LWIP_DBG_STATE|LWIP_DBG_FRESH|LWIP_DBG_HALT) + + +/* ---------- Memory options ---------- */ +/* MEM_ALIGNMENT: should be set to the alignment of the CPU for which + lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2 + byte alignment -> define MEM_ALIGNMENT to 2. */ +/* MSVC port: intel processors don't need 4-byte alignment, + but are faster that way! */ +#define MEM_ALIGNMENT 4U + +/* MEM_SIZE: the size of the heap memory. If the application will send +a lot of data that needs to be copied, this should be set high. */ +#define MEM_SIZE 50240 + +/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application + sends a lot of data out of ROM (or other static memory), this + should be set high. */ +#define MEMP_NUM_PBUF 16 +/* MEMP_NUM_RAW_PCB: the number of UDP protocol control blocks. One + per active RAW "connection". */ +#define MEMP_NUM_RAW_PCB 3 +/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One + per active UDP "connection". */ +#define MEMP_NUM_UDP_PCB 8 +/* MEMP_NUM_TCP_PCB: the number of simultaneously active TCP + connections. */ +#define MEMP_NUM_TCP_PCB 5 +/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP + connections. */ +#define MEMP_NUM_TCP_PCB_LISTEN 8 +/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP + segments. */ +#define MEMP_NUM_TCP_SEG 16 +/* MEMP_NUM_SYS_TIMEOUT: the number of simultaneously active + timeouts. */ +#define MEMP_NUM_SYS_TIMEOUT 17 + +/* The following four are used only with the sequential API and can be + set to 0 if the application only will use the raw API. */ +/* MEMP_NUM_NETBUF: the number of struct netbufs. */ +#define MEMP_NUM_NETBUF 2 +/* MEMP_NUM_NETCONN: the number of struct netconns. */ +#define MEMP_NUM_NETCONN 12 +/* MEMP_NUM_TCPIP_MSG_*: the number of struct tcpip_msg, which is used + for sequential API communication and incoming packets. Used in + src/api/tcpip.c. */ +#define MEMP_NUM_TCPIP_MSG_API 16 +#define MEMP_NUM_TCPIP_MSG_INPKT 16 + + +/* ---------- Pbuf options ---------- */ +/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */ +#define PBUF_POOL_SIZE 1200 + +/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */ +#define PBUF_POOL_BUFSIZE 2560 + +/** SYS_LIGHTWEIGHT_PROT + * define SYS_LIGHTWEIGHT_PROT in lwipopts.h if you want inter-task protection + * for certain critical regions during buffer allocation, deallocation and memory + * allocation and deallocation. + */ +#define SYS_LIGHTWEIGHT_PROT (NO_SYS==0) + + +/* ---------- TCP options ---------- */ +#define LWIP_TCP 1 +#define TCP_TTL 255 + +#define LWIP_ALTCP (LWIP_TCP) +#ifdef LWIP_HAVE_MBEDTLS +#define LWIP_ALTCP_TLS (LWIP_TCP) +#define LWIP_ALTCP_TLS_MBEDTLS (LWIP_TCP) +#endif + + +/* Controls if TCP should queue segments that arrive out of + order. Define to 0 if your device is low on memory. */ +#define TCP_QUEUE_OOSEQ 1 + +/* TCP Maximum segment size. */ +#define TCP_MSS 1024 + +/* TCP sender buffer space (bytes). */ +#define TCP_SND_BUF 2048 + +/* TCP sender buffer space (pbufs). This must be at least = 2 * + TCP_SND_BUF/TCP_MSS for things to work. */ +#define TCP_SND_QUEUELEN (4 * TCP_SND_BUF/TCP_MSS) + +/* TCP writable space (bytes). This must be less than or equal + to TCP_SND_BUF. It is the amount of space which must be + available in the tcp snd_buf for select to return writable */ +#define TCP_SNDLOWAT (TCP_SND_BUF/2) + +/* TCP receive window. */ +#define TCP_WND (20 * 1024) + +/* Maximum number of retransmissions of data segments. */ +#define TCP_MAXRTX 12 + +/* Maximum number of retransmissions of SYN segments. */ +#define TCP_SYNMAXRTX 4 + + +/* ---------- ARP options ---------- */ +#define LWIP_ARP 1 +#define ARP_TABLE_SIZE 10 +#define ARP_QUEUEING 1 + + +/* ---------- IP options ---------- */ +/* Define IP_FORWARD to 1 if you wish to have the ability to forward + IP packets across network interfaces. If you are going to run lwIP + on a device with only one network interface, define this to 0. */ +#define IP_FORWARD 1 + +/* IP reassembly and segmentation.These are orthogonal even + * if they both deal with IP fragments */ +#define IP_REASSEMBLY 1 +#define IP_REASS_MAX_PBUFS (10 * ((1500 + PBUF_POOL_BUFSIZE - 1) / PBUF_POOL_BUFSIZE)) +#define MEMP_NUM_REASSDATA IP_REASS_MAX_PBUFS +#define IP_FRAG 1 +#define IPV6_FRAG_COPYHEADER 1 + +/* ---------- ICMP options ---------- */ +#define ICMP_TTL 255 + + +/* ---------- DHCP options ---------- */ +/* Define LWIP_DHCP to 1 if you want DHCP configuration of + interfaces. */ +#define LWIP_DHCP LWIP_UDP + +/* 1 if you want to do an ARP check on the offered address + (recommended). */ +#define DHCP_DOES_ARP_CHECK (LWIP_DHCP) + + +/* ---------- AUTOIP options ------- */ +#define LWIP_AUTOIP (LWIP_DHCP) +#define LWIP_DHCP_AUTOIP_COOP (LWIP_DHCP && LWIP_AUTOIP) + + +/* ---------- UDP options ---------- */ +#define LWIP_UDP 1 +#define LWIP_UDPLITE LWIP_UDP +#define UDP_TTL 255 + + +/* ---------- RAW options ---------- */ +#define LWIP_RAW 1 + + +/* ---------- 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 */ + +/* ---------- NETBIOS options ---------- */ +#define LWIP_NETBIOS_RESPOND_NAME_QUERY 1 + +/* ---------- PPP options ---------- */ + +#define PPP_SUPPORT 1 /* Set > 0 for PPP */ + +#if PPP_SUPPORT + +#define NUM_PPP 1 /* Max PPP sessions. */ + + +/* Select modules to enable. Ideally these would be set in the makefile but + * we're limited by the command line length so you need to modify the settings + * in this file. + */ +#define PPPOE_SUPPORT 1 +#define PPPOS_SUPPORT 1 + +#define PAP_SUPPORT 1 /* Set > 0 for PAP. */ +#define CHAP_SUPPORT 1 /* Set > 0 for CHAP. */ +#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 0 /* Set > 0 for VJ header compression. */ +#define MD5_SUPPORT 1 /* Set > 0 for MD5 (see also CHAP) */ + +#endif /* PPP_SUPPORT */ + +#endif /* LWIP_OPTTEST_FILE */ + +/* The following defines must be done even in OPTTEST mode: */ + +#if !defined(NO_SYS) || !NO_SYS /* default is 0 */ +void sys_check_core_locking(void); +#define LWIP_ASSERT_CORE_LOCKED() sys_check_core_locking() +#endif + +#ifndef LWIP_PLATFORM_ASSERT +/* Define LWIP_PLATFORM_ASSERT to something to catch missing stdio.h includes */ +void lwip_example_app_platform_assert(const char *msg, int line, const char *file); +#define LWIP_PLATFORM_ASSERT(x) lwip_example_app_platform_assert(x, __LINE__, __FILE__) +#endif + +#endif /* LWIP_LWIPOPTS_H */ diff --git a/netif/unixaf.c b/netif/unixaf.c new file mode 100644 index 0000000..b2f6c8e --- /dev/null +++ b/netif/unixaf.c @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * Copyright (c) 2024 Arne Schwabe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "lwip/opt.h" + +#include "lwip/debug.h" +#include "lwip/mem.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "lwip/timeouts.h" +#include "netif/etharp.h" +#include "lwip/ethip6.h" + +#include "unixaf.h" +#include "unixaf_host.h" +#include "lwip/sockets.h" + + +/* Define those to better describe your network interface. */ +#define IFNAME0 'a' +#define IFNAME1 'f' + +#ifndef AFUNIX_DEBUG +#define AFUNIX_DEBUG LWIP_DBG_OFF +#endif + +struct unixafif { + /* Add whatever per-interface state that is needed here. */ + int fd; +}; + +/* Forward declarations. */ +static void unixafif_input(struct netif *netif); +#if !NO_SYS +static void unixafif_thread(void *arg); +#endif /* !NO_SYS */ + +/** + * Send an IPv4 packet on the given connection emulating a tun or tap device. + */ +static err_t afunix_output_ipv4(struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr) { + LWIP_UNUSED_ARG(ipaddr); + + /* like netif_input, use interface flags to determine if this is tun or tap */ + if (netif_is_tap(netif)) { + return etharp_output(netif, p, ipaddr); + } else { + return netif->linkoutput(netif, p); + } +} + +/** + * Send an IPv6 packet on the given connection emulating a tun or tap device. + */ +static err_t afunix_output_ipv6(struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr) { + /* like netif_input, use interface flags to determine if this is tun or tap */ + if (netif_is_tap(netif)) { + return ethip6_output(netif, p, ipaddr); + } else { + return netif->linkoutput(netif, p); + } +} + +static void set_netif_mtu(struct netif *netif) { + long mtu = 1500; + const char *tuntap_mtu_str = getenv("TUNTAP_MTU"); + if (tuntap_mtu_str) { + char *endptr; + mtu = strtol(tuntap_mtu_str, &endptr, 10); + + if (*endptr != '\0' || mtu < 68 || mtu > 65000) { + fprintf(stderr, "Could parse environment variable TUNTAP_MTU=%s", + tuntap_mtu_str); + exit(1); + } + } + netif->mtu = (uint16_t) mtu; +} + +static uint8_t get_random_byte(void) +{ +#ifndef ARC4RANDOM_MISSING + return (uint8_t) (arc4random()); +#else + return (uint8_t) (rand()); +#endif +} +static void set_netif_mac(struct netif *netif) { + const char *lladdr_str = getenv("TUNTAP_LLADDR"); + if (!lladdr_str) { + /* (We just fake an address...). + * This is a random locally administered address with a fixed + * prefix to make easier to identify */ + netif->hwaddr[0] = 0x0e; + netif->hwaddr[1] = 'o'; + netif->hwaddr[2] = get_random_byte(); + netif->hwaddr[3] = get_random_byte(); + netif->hwaddr[4] = get_random_byte(); + netif->hwaddr[5] = get_random_byte(); + netif->hwaddr_len = 6; + return; + } + + if (sscanf(lladdr_str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", + &netif->hwaddr[0], &netif->hwaddr[1], &netif->hwaddr[2], + &netif->hwaddr[3], &netif->hwaddr[4], &netif->hwaddr[5]) + != 6) + { + fprintf(stderr, "Could parse environment variable TUNTAP_MTU=%s", + lladdr_str); + exit(1); + } + netif->hwaddr_len = 6; +} + +static void set_tun_fd(struct netif *netif) +{ + const char *unix_af_path = getenv("TUN_UNIXAF_PATH"); + const char *unix_af_fd_str = getenv("TUNTAP_SOCKET_FD"); + struct unixafif *uafif = (struct unixafif *) netif->state; + + + if (unix_af_path) { + uafif->fd = open(unix_af_path, O_RDWR); + LWIP_DEBUGF(AFUNIX_DEBUG, ("unixafif_init: fd %d\n", uafif->fd)); + if (uafif->fd < 0) { + fprintf(stderr, "tunixafif_init: cannot open %s: %s", unix_af_path, + strerror(errno)); + exit(1); + } + return; + } + + if (!unix_af_fd_str) { + fprintf(stderr, "Could not find environment variable TUNTAP_SOCKET_FD or " + "TUN_UNIXAF_PATH containing the passed socket file " + "descriptor/socket path"); + exit(1); + } + uafif->fd = atoi(unix_af_fd_str); + + LWIP_DEBUGF(AFUNIX_DEBUG, ("unixafif_init: fd %d\n", uafif->fd)); + if (uafif->fd <= 0) { + fprintf(stderr, "tunixafif_init: cannot parse TUNTAP_SOCKET_FD (%s)", + unix_af_fd_str); + exit(1); + } +} + +static void +low_level_init(struct netif *netif) { + const char *dev_type; + + set_netif_mac(netif); + + /* device capabilities */ + dev_type = getenv("TUNTAP_DEV_TYPE"); + if (!dev_type) { + fprintf(stderr, "Environment variable TUNTAP_DEV_TYPE is missing"); + exit(1); + } + + if (!strcmp(dev_type, "tun")) { + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_IGMP; + } else if (!strcmp(dev_type, "tap")) { + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_IGMP; + } else { + fprintf(stderr, "tunixafif_init: cannot parse TUNTAP_DEV_TYPE (%s)", dev_type); + exit(1); + } + + set_tun_fd(netif); + set_netif_mtu(netif); + netif_set_link_up(netif); + +#if !NO_SYS + sys_thread_new("unixafif_thread", unixafif_thread, netif, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO); +#endif /* !NO_SYS */ +} +/*-----------------------------------------------------------------------------------*/ +/* + * low_level_output(): + * + * Should do the actual transmission of the packet. The packet is + * contained in the pbuf that is passed to the function. This pbuf + * might be chained. + * + */ +/*-----------------------------------------------------------------------------------*/ + +static err_t +low_level_output(struct netif *netif, struct pbuf *p) { + struct unixafif *unixaf = (struct unixafif *) netif->state; + char buf[1518]; /* max packet size including VLAN excluding CRC */ + + if (p->tot_len > sizeof(buf)) { + MIB2_STATS_NETIF_INC(netif, ifoutdiscards); + perror("unixafif: packet too large"); + return ERR_IF; + } + + /* initiate transfer(); copy packet contents to buf */ + uint16_t outsize = pbuf_copy_partial(p, buf, p->tot_len, 0); + + if (!outsize || outsize != p->tot_len) { + perror("unixafif: error copying packet contents"); + return ERR_IF; + } + + /* signal that packet should be sent(); */ + size_t written = host_send(unixaf->fd, buf, p->tot_len, 0); + if (written < p->tot_len) { + MIB2_STATS_NETIF_INC(netif, ifoutdiscards); + + fprintf(stderr, "unixafif: fd %d send short (%zd of %d)", + unixaf->fd, written, p->tot_len); + return ERR_IF; + } else { + LWIP_DEBUGF(AFUNIX_DEBUG, ("unixafif: fd %d send bytes to socket (%zd of %d)\n", + unixaf->fd, written, p->tot_len)); + MIB2_STATS_NETIF_ADD(netif, ifoutoctets, (u32_t) written); + return ERR_OK; + } +} +/*-----------------------------------------------------------------------------------*/ +/* + * low_level_input(): + * + * Should allocate a pbuf and transfer the bytes of the incoming + * packet from the interface into the pbuf. + * + */ +/*-----------------------------------------------------------------------------------*/ +static struct pbuf * +low_level_input(struct netif *netif) { + struct pbuf *p; + u16_t len; + ssize_t readlen; + char buf[1518]; /* max packet size including VLAN excluding CRC */ + struct unixafif *afif = (struct unixafif *) netif->state; + + /* Obtain the size of the packet and put it into the "len" + variable. */ + readlen = host_recv(afif->fd, buf, sizeof(buf), 0); + if (readlen < 0) { + char errmsg[512]; + snprintf(errmsg, sizeof(errmsg), "recv of fd %d returned %zd:", + afif->fd, readlen); + perror(errmsg); + MIB2_STATS_NETIF_INC(netif, ifindiscards); + return NULL; + } else { + LWIP_DEBUGF(AFUNIX_DEBUG, ("received %zd bytes from socket\n", readlen)); + } + + len = (u16_t) readlen; + + MIB2_STATS_NETIF_ADD(netif, ifinoctets, len); + + /* We allocate a pbuf chain of pbufs from the pool. */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + if (p != NULL) { + pbuf_take(p, buf, len); + /* acknowledge that packet has been read(); */ + } else { + /* drop packet(); */ + MIB2_STATS_NETIF_INC(netif, ifindiscards); + LWIP_DEBUGF(NETIF_DEBUG, ("unixafif_input: could not allocate pbuf\n")); + } + + return p; +} + +/*-----------------------------------------------------------------------------------*/ +/* + * unixafif_input(): + * + * This function should be called when a packet is ready to be read + * from the interface. It uses the function low_level_input() that + * should handle the actual reception of bytes from the network + * interface. + * + */ +/*-----------------------------------------------------------------------------------*/ +static void +unixafif_input(struct netif *netif) { + struct pbuf *p = low_level_input(netif); + + if (p == NULL) { +#if LINK_STATS + LINK_STATS_INC(link.recv); +#endif /* LINK_STATS */ + LWIP_DEBUGF(AFUNIX_DEBUG, ("unixafif_input: low_level_input returned NULL\n")); + return; + } + + if (netif->input(p, netif) != ERR_OK) { + LWIP_DEBUGF(NETIF_DEBUG, ("unixafif_input: netif input error\n")); + pbuf_free(p); + } +} +/*-----------------------------------------------------------------------------------*/ +/* + * unixif_init(): + * + * Should be called at the beginning of the program to set up the + * network interface. It calls the function low_level_init() to do the + * actual setup of the hardware. + * + */ +/*-----------------------------------------------------------------------------------*/ +err_t +unixafif_init(struct netif *netif) { + struct tapif *unixafif = (struct tapif *) mem_malloc(sizeof(struct unixafif)); + + if (unixafif == NULL) { + LWIP_DEBUGF(NETIF_DEBUG, ("unixafif_init: out of memory for unixafif\n")); + return ERR_MEM; + } + netif->state = unixafif; + MIB2_INIT_NETIF(netif, snmp_ifType_other, 100000000); + + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; + + netif->output = afunix_output_ipv4; + netif->output_ip6 = afunix_output_ipv6; + + netif->linkoutput = low_level_output; + + low_level_init(netif); + + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +void +unixafif_poll(struct netif *netif) { + unixafif_input(netif); +} + +static void +unixafif_thread(void *arg) { + struct netif *netif; + struct unixafif *unixif; + fd_set fdset; + int ret; + + netif = (struct netif *) arg; + unixif = (struct unixafif *) netif->state; + + while (true) { + FD_ZERO(&fdset); + FD_SET(unixif->fd, &fdset); + + /* Wait for a packet to arrive. */ + ret = select(unixif->fd + 1, &fdset, NULL, NULL, NULL); + + if (ret == 1) { + /* Handle incoming packet. */ + unixafif_input(netif); + } else if (ret == -1) { + perror("unixafif_thread: select"); + } + } +} + diff --git a/netif/unixaf.h b/netif/unixaf.h new file mode 100644 index 0000000..b2fdbd7 --- /dev/null +++ b/netif/unixaf.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Author: Arne Schwabe + * + */ +#ifndef LWIP_UNIXAF_H +#define LWIP_UNIXAF_H + +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +err_t unixafif_init(struct netif *netif); +void unixafif_poll(struct netif *netif); +/** + * Return whether we consider a netif a tap (layer 2) interface. Otherwise it + * is considered a layer 3 tun interface + */ +static inline +bool netif_is_tap(const struct netif *netif) { + return netif->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET); +} + + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_UNIXAF_H */ diff --git a/netif/unixaf_host.c b/netif/unixaf_host.c new file mode 100644 index 0000000..8f7bcbe --- /dev/null +++ b/netif/unixaf_host.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * Copyright (c) 2024 Arne Schwabe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * + */ + +/* This is a mess between needing to use recv/send from the system for our + * socket and not accidentally conflicting with lwip's own functions. + * So this file is reserved for function that MUST use the hosts network + * stack instead of lwip's own stack */ + + +#include +#include +#include "unixaf_host.h" + +/** function that calls the \c recv function of the host os network stack */ +ssize_t +host_recv(int socket, void *buffer, size_t length, int flags) +{ + return recv(socket, buffer, length, flags); +} + +/** function that calls the \c send function of the host os network stack */ +ssize_t +host_send(int socket, const void *buffer, size_t length, int flags) +{ + return send(socket, buffer, length, flags); +} diff --git a/netif/unixaf_host.h b/netif/unixaf_host.h new file mode 100644 index 0000000..e336c16 --- /dev/null +++ b/netif/unixaf_host.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Author: Arne Schwabe + * + */ +#ifndef LWIP_UNIXAF_HOST_H +#define LWIP_UNIXAF_HOST_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** function that calls the \c recv function of the host os network stack */ +ssize_t +host_recv(int socket, void *buffer, size_t length, int flags); + +/** function that calls the \c send function of the host os network stack */ +ssize_t +host_send(int socket, const void *buffer, size_t length, int flags); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_UNIXAF_HOST_H */ +