diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 2ef31b5..77d437e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -10,7 +10,7 @@ jobs: matrix: os: [ ubuntu-20.04, ubuntu-22.04, ubuntu-24.04 ] compiler: [clang, gcc] - installdeps: ['sudo apt install -y ninja-build cmake'] + installdeps: ['sudo apt install -y ninja-build cmake libpcap-dev'] include: - os: macos-latest installdeps: 'brew install cmake' diff --git a/.gitignore b/.gitignore index 8eee68f..3d5a806 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ cmake-build-* +build diff --git a/CMakeLists.txt b/CMakeLists.txt index 92a932f..74fba8e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,9 +78,13 @@ add_executable(lwipovpn netif/unixaf.h netif/unixaf_host.c netif/unixaf_host.h + netif/unixaf_pcap.c + netif/unixaf_pcap.h ) -# C doesn't seem to have a good easy to use random function. Use arc4random if available otherwise rand +find_library(PCAP_LIBRARY NAMES pcap) + + # 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 @@ -91,6 +95,7 @@ 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}) +target_link_libraries(lwipovpn PRIVATE ${PCAP_LIBRARY}) if (NOT "${HAVE_ARC4RANDOM}") target_compile_definitions(lwipovpn PRIVATE -DARC4RANDOM_MISSING) endif() diff --git a/README.md b/README.md index ac7c0fc..b89d7b8 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ Features - IPv6 Support - automatic configuration of IPv4 and IPv6 addresses - tap or tun emulation + - write pcap file Enabled apps @@ -35,7 +36,16 @@ can be enabled/implemented to allow even more testing. - shell (a simple shell that can be used with telnet to make some network diagnostics) - tcp echo (port 7) - udp echo (port 7) - - + + +PCAP File Support +----------------- +If the environment variable `LWIP_PCAP_FILE` is e.g. by setting it from OpenVPN via + + setenv LWIP_PCAP_FILE /tmp/lwip.pcap + +then lwipovpn will write all packets send/received into this pcap file. If the file +already exists, the new packets will be appended to it. Limitations ----------- diff --git a/netif/unixaf.c b/netif/unixaf.c index 1f20b11..2b9969d 100644 --- a/netif/unixaf.c +++ b/netif/unixaf.c @@ -34,7 +34,6 @@ #include #include - #include "lwip/opt.h" #include "lwip/debug.h" @@ -48,9 +47,9 @@ #include "unixaf.h" #include "unixaf_host.h" +#include "unixaf_pcap.h" #include "lwip/sockets.h" - /* Define those to better describe your network interface. */ #define IFNAME0 'a' #define IFNAME1 'f' @@ -62,6 +61,7 @@ struct unixafif { /* Add whatever per-interface state that is needed here. */ int fd; + struct unixafif_pcap pcap; }; /* Forward declarations. */ @@ -182,6 +182,7 @@ static void set_tun_fd(struct netif *netif) } } + static void low_level_init(struct netif *netif) { const char *dev_type; @@ -242,6 +243,11 @@ low_level_output(struct netif *netif, struct pbuf *p) { return ERR_IF; } + if (unixaf->pcap.pcap) + { + unixaf_pcap_write_packet(&unixaf->pcap, p->tot_len, buf); + } + /* signal that packet should be sent(); */ size_t written = host_send(unixaf->fd, buf, p->tot_len, 0); if (written < p->tot_len) { @@ -277,6 +283,7 @@ low_level_input(struct netif *netif) { /* 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:", @@ -292,6 +299,12 @@ low_level_input(struct netif *netif) { MIB2_STATS_NETIF_ADD(netif, ifinoctets, len); + if (afif->pcap.pcap) + { + unixaf_pcap_write_packet(&afif->pcap, len, buf); + } + + /* We allocate a pbuf chain of pbufs from the pool. */ p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); if (p != NULL) { @@ -365,6 +378,11 @@ unixafif_init(struct netif *netif) { low_level_init(netif); + bool tap = netif->flags & NETIF_FLAG_ETHARP; + + struct unixafif *uafif = (struct unixafif *) netif->state; + unixaf_pcap_init(&uafif->pcap, tap); + return ERR_OK; } diff --git a/netif/unixaf_pcap.c b/netif/unixaf_pcap.c new file mode 100644 index 0000000..4236946 --- /dev/null +++ b/netif/unixaf_pcap.c @@ -0,0 +1,82 @@ +/* + * 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. + * + * + */ + +#include +#include + +#include "lwip/pbuf.h" +#include "unixaf_pcap.h" + +/* This is the same mess as unixaf_host.c. pcap.h pulls in system network + * header files and those conflict with lwip's own functions. + * So this file "lives" in the system network header space */ + +void +unixaf_pcap_init(struct unixafif_pcap *if_pcap, bool tap) +{ + const char *pcap_file = getenv("LWIP_PCAP_FILE"); + if (!pcap_file) { + return; + } + + /* We could use DLT_LINUX_SLL here instead and add the special header that + * would allow preserving the information of the direction in the pcap file */ + int linktype = tap ? DLT_EN10MB : DLT_RAW; + + if_pcap->pcap = pcap_open_dead(linktype, 65000); + if_pcap->pcap_dump = pcap_dump_open_append(if_pcap->pcap, pcap_file); + + if (if_pcap->pcap_dump) + { + printf("lwipovpn: Capturing packets to '%s'\n", pcap_file); + } + else + { + fprintf(stderr, "Error opening pcap file '%s' for writing: %s\n", pcap_file, + pcap_geterr(if_pcap->pcap)); + pcap_close(if_pcap->pcap); + if_pcap->pcap = NULL; + } +} + +void +unixaf_pcap_write_packet(struct unixafif_pcap *if_pcap, int len, const char* buf) +{ + struct pcap_pkthdr pkthdr; + pkthdr.len = len; + pkthdr.caplen = len; + + gettimeofday(&pkthdr.ts, NULL); + + /* The callback pointer is unsigned char * instead of void *, so we need + * to cast the pointer to it */ + pcap_dump((unsigned char*) if_pcap->pcap_dump, &pkthdr, (const unsigned char *) buf); + pcap_dump_flush(if_pcap->pcap_dump); +} diff --git a/netif/unixaf_pcap.h b/netif/unixaf_pcap.h new file mode 100644 index 0000000..8293c2c --- /dev/null +++ b/netif/unixaf_pcap.h @@ -0,0 +1,64 @@ +/* + * 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_PCAP_H +#define LWIP_UNIXAF_PCAP_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* This file cannot include system network header/pcap header, so use + * forward declarations here to define the pointers */ +typedef struct pcap pcap_t; +typedef struct pcap_dumper pcap_dumper_t; + +struct unixafif_pcap { + pcap_t *pcap; + pcap_dumper_t *pcap_dump; +}; + +void +unixaf_pcap_init(struct unixafif_pcap *if_pcap, bool tap); + +void +unixaf_pcap_write_packet(struct unixafif_pcap *if_pcap, int len, const char* buf); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_UNIXAF_PCAP_H */ +