diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7c9383a --- /dev/null +++ b/.gitignore @@ -0,0 +1,193 @@ +# Created by https://www.toptal.com/developers/gitignore/api/c,cmake,clion +# Edit at https://www.toptal.com/developers/gitignore?templates=c,cmake,clion + +### C ### +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +### CLion ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### CLion Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml + +### CMake ### +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +### CMake Patch ### +# External projects +*-prefix/ + +# End of https://www.toptal.com/developers/gitignore/api/c,cmake,clion + +# CMake Doxygen +CMakeDoxyfile.in +CMakeDoxygenDefaults.cmake +/tcpproxy \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5e84c46 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lwip"] + path = lwip + url = https://git.savannah.nongnu.org/git/lwip.git diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..79b3c94 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..bbfd58c --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/tcpproxy.iml b/.idea/tcpproxy.iml new file mode 100644 index 0000000..f08604b --- /dev/null +++ b/.idea/tcpproxy.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..ed6ec8e --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4ee89e8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.25) + +project(tcpproxy C) + +set(LWIP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lwip) +set(LWIP_CONTRIB_DIR ${LWIP_DIR}/contrib) +set (LWIP_INCLUDE_DIRS + "${LWIP_DIR}/src/include" + "${LWIP_CONTRIB_DIR}" + "${LWIP_CONTRIB_DIR}/ports/unix/port/include" + "${CMAKE_CURRENT_SOURCE_DIR}/src") + + +include(${LWIP_DIR}/src/Filelists.cmake) +include(${LWIP_DIR}/contrib/Filelists.cmake) +include(${LWIP_DIR}/contrib/ports/unix/Filelists.cmake) + +set(CMAKE_C_STANDARD 11) + +find_library(LIBEVENT event_core) + + +add_executable(tcpproxy src/main.c ${lwipcontribportunix_SRCS}) +target_include_directories(tcpproxy PRIVATE ${LWIP_INCLUDE_DIRS}) +target_compile_options(tcpproxy PRIVATE ${LWIP_COMPILER_FLAGS}) +target_compile_definitions(tcpproxy PRIVATE ${LWIP_DEFINITIONS} ${LWIP_MBEDTLS_DEFINITIONS}) +target_link_libraries(tcpproxy ${LWIP_SANITIZER_LIBS} lwipcontribexamples lwipcontribapps lwipcontribaddons lwipallapps lwipcontribportunix lwipcore lwipmbedtls ${LIBEVENT}) + diff --git a/Dockerfile b/Dockerfile index fde198f..da3f18e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,18 @@ -FROM haproxy:lts -ENV PORT=3890 -EXPOSE $PORT -ADD haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg +FROM debian:bookworm-slim AS builder +RUN apt-get update && apt-get install -y build-essential cmake 'libevent-dev=2.1.*' +COPY CMakeLists.txt /app/src/ +COPY NOTICE /app/src/ +COPY lwip /app/src/lwip/ +COPY src /app/src/src/ +RUN mkdir /app/build && cd /app/build && cmake /app/src && make && mv tcpproxy .. + +FROM debian:bookworm-slim +RUN apt-get update && apt-get install -y --no-install-recommends openconnect libevent-core-2.1-7 +COPY --from=builder /app/tcpproxy /app/ +COPY --from=builder /app/src/NOTICE /app/ +COPY --from=builder /app/src/lwip/COPYING /app/ +COPY docker-entrypoint.sh /app/ +ARG HOST +EXPOSE 1234 +USER nobody +ENTRYPOINT ["/app/docker-entrypoint.sh"] diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..e0b2444 --- /dev/null +++ b/NOTICE @@ -0,0 +1,28 @@ +Copyright (c) 2023 Penn Mackintosh +Adapted from [ocproxy](https://github.com/cernekee/ocproxy) to use upgrade LwIP, remove LDAP functionality +Copyright (c) 2016 Google Inc. +Copyright (c) 2012-2014 David Edmondson +Copyright (c) 2012-2014 Kevin Cernekee +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/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 0000000..5de93ca --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +echo openconnect --script-tun --script "/app/tcpproxy -L 1234:$HOST" "$@" +exec openconnect --script-tun --script "/app/tcpproxy -L 1234:$HOST" "$@" diff --git a/haproxy.cfg b/haproxy.cfg deleted file mode 100644 index cb38dca..0000000 --- a/haproxy.cfg +++ /dev/null @@ -1,11 +0,0 @@ -global - -defaults - mode tcp - timeout connect 5s - timeout client 50s - timeout server 50s - -listen tcp-in - bind "0.0.0.0:${PORT}" - server server1 "${TARGET}" diff --git a/lwip b/lwip new file mode 160000 index 0000000..e29870c --- /dev/null +++ b/lwip @@ -0,0 +1 @@ +Subproject commit e29870c15e8bf28eac9c811dd236c474f3f2008f diff --git a/src/lwipopts.h b/src/lwipopts.h new file mode 100644 index 0000000..83d39a4 --- /dev/null +++ b/src/lwipopts.h @@ -0,0 +1,330 @@ +/* + * 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 0 +#define LWIP_NETCONN 0 +#define LWIP_NETIF_API (NO_SYS==0) + +#define LWIP_IGMP 0 +#define LWIP_ICMP LWIP_IPV4 + +#define LWIP_SNMP LWIP_UDP +#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_SINGLE_NETIF 1 +#define LWIP_LOOPBACK_MAX_PBUFS 10 + +#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 1024000 + +/* 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 1600 +/* 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 simulatenously active TCP + connections. */ +#define MEMP_NUM_TCP_PCB 1000 +/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP + connections. */ +#define MEMP_NUM_TCP_PCB_LISTEN 80 +/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP + segments. */ +#define MEMP_NUM_TCP_SEG 1600 +/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active + timeouts. */ +#define MEMP_NUM_SYS_TIMEOUT 300 + +/* 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 200 +/* MEMP_NUM_NETCONN: the number of struct netconns. */ +#define MEMP_NUM_NETCONN 1000 +/* 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 1600 +#define MEMP_NUM_TCPIP_MSG_INPKT 1600 + + +/* ---------- Pbuf options ---------- */ +/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */ +#define PBUF_POOL_SIZE 10000 + +/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */ +#define PBUF_POOL_BUFSIZE 2048 + +/** 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 65534 /* Match TCP_WND. */ + +/* 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/8) + +/* TCP receive window. */ +#define TCP_WND 65534 /* Avoid wrap. */ + +/* 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 0 +#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 0 + +/* 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 0 +#define LWIP_UDPLITE LWIP_UDP +#define UDP_TTL 255 + + +/* ---------- RAW options ---------- */ +#define LWIP_RAW 0 + + +/* ---------- Statistics options ---------- */ + +#define LWIP_STATS 0 +#define LWIP_STATS_DISPLAY 0 + +#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 0 + +/* ---------- PPP options ---------- */ + +#define PPP_SUPPORT 0 /* 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 */ +#if 0 +void sys_check_core_locking(void); +#define LWIP_ASSERT_CORE_LOCKED() sys_check_core_locking() +#endif +#endif + +#ifndef LWIP_PLATFORM_ASSERT +#if 0 +/* 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 + +#endif /* LWIP_LWIPOPTS_H */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..bd12813 --- /dev/null +++ b/src/main.c @@ -0,0 +1,808 @@ +/* + * Copyright (c) 2023 Penn Mackintosh + * Adapted from [ocproxy](https://github.com/cernekee/ocproxy) to use upgrade LwIP, remove LDAP functionality + * Copyright (c) 2016 Google Inc. + * Copyright (c) 2012-2014 David Edmondson + * Copyright (c) 2012-2014 Kevin Cernekee + * 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. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +//#include "lwip/opt.h" +//#include "lwip/debug.h" +//#include "lwip/err.h" +//#include "lwip/init.h" +#include "lwip/ip.h" +#include "lwip/tcp.h" +#include "lwip/init.h" +//#include "lwip/dns.h" +//#include "lwip/netif.h" +//#include "lwip/stats.h" +//#include "lwip/sys.h" +//#include "lwip/tcp.h" + +enum { + STATE_NEW = 0, + STATE_DNS, + STATE_CONNECTING, + STATE_DATA, + STATE_DEAD, + STATE_MAX +}; + +#define CONN_TYPE_REDIR 0 + +#define SOCKBUF_LEN 2048 + +#define FL_ACTIVATE 1 +#define FL_DIE_ON_ERROR 2 + +#define MAX_IOVEC 128 +#define MAX_CONN 1024 + +struct ocp_sock { + /* general */ + int fd; + struct evconnlistener *listener; + struct event *ev; + struct tcp_pcb *tpcb; + int state; + int conn_type; + struct ocp_sock *next; + + /* for TCP send/receive */ + int done_len; + int lwip_blocked; + int sock_pos; + int sock_total; + char sockbuf[SOCKBUF_LEN]; + + /* for all listeners */ + int lport; + char *bind_addr; + evconnlistener_cb listen_cb; + + /* for port forwarding */ + ip_addr_t rhost; + int rport; + + /* for lwip_data_cb() */ + struct netif *netif; +}; + +struct socks_auth { + u8_t ver; + u8_t n_methods; + u8_t methods; +} PACK_STRUCT_STRUCT; + +struct socks_req { + u8_t ver; + u8_t cmd; + u8_t rsv; + u8_t atyp; + union { + struct { + u32_t dst_addr; + u16_t dst_port; + u8_t end; + } ipv4; + struct { + u8_t fqdn_len; + u8_t fqdn_name[255]; /* variable length */ + u16_t port; + } fqdn; + } u; +} PACK_STRUCT_STRUCT; + +struct socks_reply { + u8_t ver; + u8_t rep; + u8_t rsv; + u8_t atyp; + u32_t bnd_addr; + u16_t bnd_port; +} PACK_STRUCT_STRUCT; + +static struct event_base *event_base; + +static struct ocp_sock ocp_sock_pool[MAX_CONN]; +static struct ocp_sock *ocp_sock_free_list; +static struct ocp_sock *ocp_sock_bind_list; +static int ocp_sock_used; +static int ocp_sock_max; + +/* nonstatic debug cmd option, exported in lwipopts.h */ +unsigned char debug_flags = 0; + +static int allow_remote; +static int tcpdump_enabled; +static int got_sighup; +static int got_sigusr1; +static char *dns_domain; + +static void start_connection(struct ocp_sock *s); + +/********************************************************************** + * Utility functions / libevent wrappers + **********************************************************************/ + +static void die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fflush(stdout); + vfprintf(stderr, fmt, ap); + va_end(ap); + exit(1); +} + +static void warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fflush(stdout); + fprintf(stderr, "warning: "); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +static char *xstrdup(const char *s) +{ + char *ret = strdup(s); + if (!ret) + die("out of memory\n"); + return ret; +} + +static int ocp_atoi(const char *s) +{ + char *p; + long val = strtol(s, &p, 0); + if (!*s || *p) + die("invalid integer: '%s'\n", s); + return val; +} + +static struct ocp_sock *ocp_sock_new(int fd, event_callback_fn cb, int flags) +{ + struct ocp_sock *s; + + s = ocp_sock_free_list; + if (!s) { + if (flags & FL_DIE_ON_ERROR) + die("%s: ran out of ocp_socks\n", __func__); + return NULL; + } + ocp_sock_free_list = s->next; + memset(s, 0, sizeof(*s)); + + ocp_sock_used++; + if (ocp_sock_used > ocp_sock_max) + ocp_sock_max = ocp_sock_used; + + s->next = ocp_sock_bind_list; + ocp_sock_bind_list = s; + + if (fd < 0) + return s; + + s->fd = fd; + s->ev = event_new(event_base, fd, EV_READ, cb, s); + if (flags & FL_ACTIVATE) + event_add(s->ev, NULL); + return s; +} + +static void ocp_sock_del(struct ocp_sock *s) +{ + if (s->state == STATE_DNS) { + s->state = STATE_DEAD; + return; + } + close(s->fd); + if (s->tpcb) { + tcp_arg(s->tpcb, NULL); + tcp_close(s->tpcb); + } + event_free(s->ev); + memset(s, 0xdd, sizeof(*s)); + s->next = ocp_sock_free_list; + ocp_sock_free_list = s; + ocp_sock_used--; +} + +/********************************************************************** + * lwIP TCP<->socket TCP traffic + **********************************************************************/ + +/* Called when the local TCP socket has data available (or hung up) */ +static void local_data_cb(evutil_socket_t fd, short what, void *ctx) +{ + struct ocp_sock *s = ctx; + ssize_t len; + int try_len; + err_t err; + + try_len = tcp_sndbuf(s->tpcb); + if (try_len > SOCKBUF_LEN) + try_len = SOCKBUF_LEN; + if (!try_len || tcp_sndqueuelen(s->tpcb) > (TCP_SND_QUEUELEN/2)) { + s->lwip_blocked = 1; + return; + } + + len = read(s->fd, s->sockbuf, try_len); + if (len <= 0) { + ocp_sock_del(s); + return; + } + err = tcp_write(s->tpcb, s->sockbuf, len, TCP_WRITE_FLAG_COPY); + if (err == ERR_MEM) + die("%s: out of memory\n", __func__); + else if (err != ERR_OK) + warn("tcp_write returned %d\n", (int)err); + + tcp_output(s->tpcb); + event_add(s->ev, NULL); +} + +/* Called when lwIP has sent data to the VPN */ +static err_t sent_cb(void *ctx, struct tcp_pcb *tpcb, u16_t len) +{ + struct ocp_sock *s = ctx; + + if (!s) + return ERR_OK; + + if (s->lwip_blocked) { + s->lwip_blocked = 0; + event_add(s->ev, NULL); + } + + return ERR_OK; +} + +/* Called when lwIP has new TCP data from the VPN */ +static err_t recv_cb(void *ctx, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) +{ + struct ocp_sock *s = ctx; + struct pbuf *first = p; + int offset; + ssize_t wlen; + + if (!s) + return ERR_ABRT; + + if (!p) { + ocp_sock_del(s); + return ERR_OK; + } + + /* + * tcp_tmr() will call this function every 250ms with the same pbuf, + * if we refused data during the previous attempt. s->done_len + * will reflect the number of bytes we were able to send to the socket + * so far (if any). + */ + + for (offset = s->done_len; p && offset >= p->len; offset -= p->len) + p = p->next; + + for (; p; p = p->next) { + int try_len = p->len - offset; + + wlen = write(s->fd, (char *)p->payload + offset, try_len); + offset = 0; + + if (wlen < 0) { + ocp_sock_del(s); + return ERR_ABRT; + } + s->done_len += wlen; + tcp_recved(tpcb, wlen); + if (wlen < try_len) + return ERR_WOULDBLOCK; + } + + /* if we got here, then the whole pbuf is done */ + s->done_len = 0; + pbuf_free(first); + + return ERR_OK; +} + +/********************************************************************** + * SOCKS protocol + **********************************************************************/ + +/********************************************************************** + * Connection setup + **********************************************************************/ + +/* Called on lwIP TCP errors; used to detect connection failure */ +static void tcp_err_cb(void *arg, err_t err) +{ + struct ocp_sock *s = arg; + warn("%s: called with %d\n", __func__, (int) err); + + if (s) { + s->tpcb = NULL; + ocp_sock_del(s); + } +} + +/* Called when lwIP tcp_connect() is successful */ +static err_t connect_cb(void *arg, struct tcp_pcb *tpcb, err_t err) +{ + if (err != ERR_OK) + warn("%s: called with %d\n", __func__, (int) err); + + struct ocp_sock *s = arg; + + s->state = STATE_DATA; + event_add(s->ev, NULL); + tcp_recv(tpcb, recv_cb); + tcp_sent(tpcb, sent_cb); + + return ERR_OK; +} + +static void start_connection(struct ocp_sock *s) +{ + struct tcp_pcb *tpcb; + err_t err; + + s->state = STATE_CONNECTING; + + tpcb = tcp_new(); + if (!tpcb) + die("%s: out of memory\n", __func__); + tcp_nagle_disable(tpcb); + tcp_arg(tpcb, s); + tcp_recv(tpcb, NULL); + tcp_err(tpcb, tcp_err_cb); + s->tpcb = tpcb; + + + err = tcp_connect(tpcb, &s->rhost, s->rport, connect_cb); + if (err != ERR_OK) { + warn("%s: tcp_connect(%s:%d) returned %d\n", __func__, ipaddr_ntoa(&s->rhost), s->rport, (int) err); + } +} + +/* Called upon connection to a local TCP socket */ +static void new_conn_cb(struct evconnlistener *listener, evutil_socket_t fd, + struct sockaddr *address, int socklen, void *ctx) +{ + struct ocp_sock *lsock = ctx, *s; + + s = ocp_sock_new(fd, local_data_cb, 0); + if (!s) { + warn("too many connections\n"); + return; + } + + s->conn_type = lsock->conn_type; + s->rhost = lsock->rhost; + s->rport = lsock->rport; + + start_connection(s); + +} + +/********************************************************************** + * lwIP<->VPN traffic + **********************************************************************/ + +static void vpn_conn_down(void) +{ + printf("VPN connection has terminated\n"); + event_base_loopbreak(event_base); +} + +/* Called when the VPN sends us a raw IP packet destined for lwIP */ +static void lwip_data_cb(evutil_socket_t fd, short what, void *ctx) +{ + struct ocp_sock *s = ctx; + ssize_t len; + struct pbuf *p; + + len = read(s->fd, s->sockbuf, SOCKBUF_LEN); + if (len <= 0) { + /* This might never happen, because s->fd is a DGRAM socket */ + vpn_conn_down(); + } + if ((p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL)) != NULL) { + char *bufptr; + struct pbuf *q; + + bufptr = s->sockbuf; + q = p; + while (len > 0) { + int copy = (len > q->len) ? q->len : len; + + memcpy(q->payload, bufptr, copy); + len -= copy; + bufptr += copy; + q = q->next; + } + LINK_STATS_INC(link.recv); + s->netif->input(p, s->netif); + } else + warn("%s: could not allocate pbuf\n", __func__); + event_add(s->ev, NULL); +} + +/* Called when lwIP has data to send up to the VPN */ +static err_t lwip_data_out(struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr) +{ + struct ocp_sock *s = netif->state; + int i = 0, total = 0; + ssize_t ret; + struct iovec iov[MAX_IOVEC]; + + + for (; p; p = p->next) { + if (i >= MAX_IOVEC) { + warn("%s: too many chunks, dropping packet\n", __func__); + return ERR_OK; + } + iov[i].iov_base = p->payload; + iov[i++].iov_len = p->len; + total += p->len; + } + + ret = writev(s->fd, iov, i); + if (ret < 0) { + if (errno == ECONNREFUSED || errno == ENOTCONN) + vpn_conn_down(); + else + LINK_STATS_INC(link.drop); + } else if (ret != total) + LINK_STATS_INC(link.lenerr); + else + LINK_STATS_INC(link.xmit); + + return ERR_OK; +} + +/********************************************************************** + * Periodic tasks + **********************************************************************/ + +static void handle_sig(int sig) +{ + if (sig == SIGHUP) + got_sighup = 1; + else if (sig == SIGUSR1) + got_sigusr1 = 1; +} + +static void new_periodic_event(event_callback_fn cb, void *arg, int timeout_ms) +{ + struct timeval tv; + struct event *ev; + + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = 1000 * (timeout_ms % 1000); + ev = event_new(event_base, -1, EV_PERSIST, cb, arg); + if (!ev) + die("can't create new periodic event\n"); + evtimer_add(ev, &tv); +} + +static void cb_housekeeping(evutil_socket_t fd, short what, void *ctx) +{ + int *vpnfd = ctx; + + /* + * OpenConnect will ignore 0-byte datagrams if it's alive, but + * we'll get ECONNREFUSED if the peer has died. + */ + if (write(*vpnfd, vpnfd, 0) < 0 && + (errno == ECONNREFUSED || errno == ENOTCONN)) + vpn_conn_down(); + else if (got_sighup) + vpn_conn_down(); + + if (got_sigusr1) { + LINK_STATS_DISPLAY(); + MEM_STATS_DISPLAY(); + printf("open connections: %d / %d, max %d\n", + ocp_sock_used, MAX_CONN, ocp_sock_max); + got_sigusr1 = 0; + } +} + +/********************************************************************** + * Program initialization + **********************************************************************/ + +static err_t init_oc_netif(struct netif *netif) +{ + netif->name[0] = 'u'; + netif->name[1] = 'n'; + netif->output = lwip_data_out; + return ERR_OK; +} + +static void bind_all_listeners(void) +{ + struct ocp_sock *s; + struct sockaddr_in sock; + + for (s = ocp_sock_bind_list; s; s = s->next) { + if (!s->listen_cb) + continue; + if (s->lport < 1 || s->lport > 65535) + die("invalid port number: %d\n", s->lport); + + memset(&sock, 0, sizeof(sock)); + sock.sin_port = htons(s->lport); + sock.sin_family = AF_INET; + + if (s->bind_addr) { + /* + * TODO: support IPv6 and multiple listening sockets + * per hostname + */ + if (!inet_aton(s->bind_addr, &sock.sin_addr)) + die("can't parse IP: '%s'\n", s->bind_addr); + + } else { + sock.sin_addr.s_addr = htonl(allow_remote ? + INADDR_ANY : INADDR_LOOPBACK); + } + + s->listener = evconnlistener_new_bind(event_base, s->listen_cb, + s, LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1, + (struct sockaddr *)&sock, sizeof(sock)); + if (!s->listener) + die("can't set up listener on port %d/tcp\n", s->lport); + } +} + +static struct ocp_sock *new_listener(int port, evconnlistener_cb cb) +{ + struct ocp_sock *s; + + s = ocp_sock_new(-1, NULL, FL_DIE_ON_ERROR); + s->lport = port; + s->listen_cb = cb; + + return s; +} + +static void fwd_add(const char *opt) +{ + char *str = xstrdup(opt), *tmp = str, *p; + int lport; + struct ocp_sock *s; + + p = strsep(&str, ":"); + if (!str) + goto bad; + lport = ocp_atoi(p); + + p = strsep(&str, ":"); + if (!str) + goto bad; + + s = new_listener(lport, new_conn_cb); + struct addrinfo *addrinfo; + + int ret = getaddrinfo(p, NULL, NULL, &addrinfo); + if (ret != 0) { + die("getaddrinfo: %s\n", gai_strerror(ret)); + } + ip_addr_t addr; + if (addrinfo->ai_addr->sa_family == AF_INET6) { + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) addrinfo->ai_addr; + //addr.u_addr.ip6.zone = addr6->sin6_scope_id; + addr.u_addr.ip6.addr[0] = + addr6->sin6_addr.s6_addr[0] << 24 | + addr6->sin6_addr.s6_addr[1] << 16 | + addr6->sin6_addr.s6_addr[2] << 8 | + addr6->sin6_addr.s6_addr[3] << 0; + addr.u_addr.ip6.addr[1] = + addr6->sin6_addr.s6_addr[4] << 24 | + addr6->sin6_addr.s6_addr[5] << 16 | + addr6->sin6_addr.s6_addr[6] << 8 | + addr6->sin6_addr.s6_addr[7] << 0; + addr.u_addr.ip6.addr[2] = + addr6->sin6_addr.s6_addr[8] << 24 | + addr6->sin6_addr.s6_addr[9] << 16 | + addr6->sin6_addr.s6_addr[10] << 8 | + addr6->sin6_addr.s6_addr[11] << 0; + addr.u_addr.ip6.addr[3] = + addr6->sin6_addr.s6_addr[12] << 24 | + addr6->sin6_addr.s6_addr[13] << 16 | + addr6->sin6_addr.s6_addr[14] << 8 | + addr6->sin6_addr.s6_addr[15] << 0; + } else { + struct sockaddr_in *addr4 = (struct sockaddr_in *) addrinfo->ai_addr; + addr.u_addr.ip4.addr = addr4->sin_addr.s_addr; + } + s->rhost = addr; + s->rport = ocp_atoi(str); + s->conn_type = CONN_TYPE_REDIR; + + if (s->rport <= 0) + die("Remote port must be a positive integer\n"); + + free(tmp); + + return; + bad: + die("Invalid port forward specifier: '%s'\n", opt); +} + +static struct option longopts[] = { + { "ip", 1, NULL, 'I' }, + { "mtu", 1, NULL, 'M' }, + { "dns", 1, NULL, 'd' }, + { "domain", 1, NULL, 'o' }, + { "localfw", 1, NULL, 'L' }, + { "dynfw", 1, NULL, 'D' }, + { "allow-remote", 0, NULL, 'g' }, + { "verbose", 0, NULL, 'v' }, + { "tcpdump", 0, NULL, 'T' }, + { NULL } +}; + +int main(int argc, char **argv) +{ + int opt, i, vpnfd; + char *str; + char *ip_str, *mtu_str, *dns_str; + ip_addr_t ip, netmask, gw, dns; + struct ocp_sock *s; + struct netif netif; + + ip_str = mtu_str = dns_str = NULL; + + ocp_sock_free_list = &ocp_sock_pool[0]; + for (i = 1; i < MAX_CONN; i++) + ocp_sock_pool[i - 1].next = &ocp_sock_pool[i]; + + event_base = event_base_new(); + if (!event_base) + die("can't initialize libevent\n"); + + str = getenv("VPNFD"); + if (!str) + die("VPNFD is not set, aborting\n"); + vpnfd = ocp_atoi(str); + + /* try to set the IP configuration from the environment, first */ + ip_str = getenv("INTERNAL_IP4_ADDRESS"); + mtu_str = getenv("INTERNAL_IP4_MTU"); + + dns_domain = getenv("CISCO_DEF_DOMAIN"); + str = getenv("INTERNAL_IP4_DNS"); + if (str) { + char *p; + + /* this could contain many addresses; just use the first one */ + dns_str = xstrdup(str); + p = strchr(dns_str, ' '); + if (p) + *p = 0; + } + + /* override with command line options */ + while ((opt = getopt_long(argc, argv, + "I:M:d:o:D:k:gL:vT", longopts, NULL)) != -1) { + switch (opt) { + case 'I': + ip_str = optarg; + break; + case 'M': + mtu_str = optarg; + break; + case 'd': + dns_str = optarg; + break; + case 'o': + dns_domain = optarg; + break; + case 'g': + allow_remote = 1; + break; + case 'L': + fwd_add(optarg); + break; + case 'v': + debug_flags = LWIP_DBG_ON | LWIP_DBG_TRACE | + LWIP_DBG_STATE | LWIP_DBG_FRESH | + LWIP_DBG_HALT; + break; + case 'T': + tcpdump_enabled = 1; + break; + default: + die("unknown option: %c\n", opt); + } + } + + if (!ip_str || !mtu_str) + die("missing -I or -M\n"); + + if (!ipaddr_aton(ip_str, &ip)) + die("Invalid IP address: '%s'\n", ip_str); + + /* Debugging help. */ + signal(SIGHUP, handle_sig); + signal(SIGUSR1, handle_sig); + signal(SIGPIPE, SIG_IGN); + setlinebuf(stdout); + setlinebuf(stderr); + + /* Set up lwIP interface */ + s = ocp_sock_new(vpnfd, lwip_data_cb, FL_ACTIVATE | FL_DIE_ON_ERROR); + memset(&netif, 0, sizeof(netif)); + s->netif = &netif; + + lwip_init(); + ip_addr_set_zero(&netmask); + ip_addr_set_zero(&gw); + netif_add(&netif, &ip.u_addr.ip4, &netmask.u_addr.ip4, &gw.u_addr.ip4, s, init_oc_netif, ip_input); + netif.mtu = ocp_atoi(mtu_str); + + netif_set_default(&netif); + netif_set_up(&netif); + netif_set_link_up(&netif); + + /* bind after all options have been parsed (especially -g) */ + bind_all_listeners(); + + + new_periodic_event(cb_housekeeping, &vpnfd, 1000); + + event_base_dispatch(event_base); + + return 0; +}