mirror of https://github.com/toelke/lwip-ftpd.git
Philipp Toelke
4 years ago
10 changed files with 501 additions and 2 deletions
@ -0,0 +1,31 @@ |
|||||
|
FROM debian:10 AS builder |
||||
|
|
||||
|
RUN apt update && apt install -y unzip libpcap-dev build-essential git cmake && rm -rf /var/lib/dpkg/lists/* |
||||
|
|
||||
|
WORKDIR /src |
||||
|
|
||||
|
RUN git clone https://git.savannah.nongnu.org/git/lwip.git |
||||
|
|
||||
|
RUN git clone https://github.com/toelke/lwip-ftpd.git |
||||
|
|
||||
|
COPY . lwip-ftpd |
||||
|
|
||||
|
ADD http://elm-chan.org/fsw/ff/arc/ff14.zip . |
||||
|
|
||||
|
RUN unzip -q ff14.zip -d ff |
||||
|
|
||||
|
RUN sed -i -e 's/FF_FS_RPATH[ ]*0/FF_FS_RPATH 2/;s/FF_USE_MKFS[ ]*0/FF_USE_MKFS 1/;' ff/source/ffconf.h |
||||
|
|
||||
|
WORKDIR /build |
||||
|
|
||||
|
RUN cmake /src/lwip-ftpd/test-in-docker/src && make -j |
||||
|
|
||||
|
FROM debian:10 AS runner |
||||
|
|
||||
|
RUN apt update && apt install -y libpcap0.8 valgrind && rm -rf /var/lib/dpkg/lists/* |
||||
|
|
||||
|
WORKDIR /app |
||||
|
|
||||
|
COPY --from=builder /build/lwip-runner . |
||||
|
|
||||
|
CMD valgrind /app/lwip-runner |
@ -0,0 +1 @@ |
|||||
|
#pragma pack(push,1) |
@ -0,0 +1,106 @@ |
|||||
|
/*
|
||||
|
* 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 <adam@sics.se> |
||||
|
* |
||||
|
*/ |
||||
|
#ifndef LWIP_ARCH_CC_H |
||||
|
#define LWIP_ARCH_CC_H |
||||
|
|
||||
|
#include <stdio.h> /* printf, fflush, FILE */ |
||||
|
#include <stdlib.h> /* abort */ |
||||
|
#include <limits.h> |
||||
|
#include <stdint.h> |
||||
|
|
||||
|
#ifdef _MSC_VER |
||||
|
#pragma warning (disable: 4127) /* conditional expression is constant */ |
||||
|
#pragma warning (disable: 4996) /* 'strncpy' was declared deprecated */ |
||||
|
#pragma warning (disable: 4103) /* structure packing changed by including file */ |
||||
|
#pragma warning (disable: 4820) /* 'x' bytes padding added after data member 'y' */ |
||||
|
#endif |
||||
|
|
||||
|
//#define LWIP_PROVIDE_ERRNO
|
||||
|
|
||||
|
/* Define platform endianness (might already be defined) */ |
||||
|
#ifndef BYTE_ORDER |
||||
|
#define BYTE_ORDER LITTLE_ENDIAN |
||||
|
#endif /* BYTE_ORDER */ |
||||
|
|
||||
|
/* Define generic types used in lwIP */ |
||||
|
typedef uint8_t u8_t; |
||||
|
typedef int8_t s8_t; |
||||
|
typedef uint16_t u16_t; |
||||
|
typedef int16_t s16_t; |
||||
|
typedef uint32_t u32_t; |
||||
|
typedef int32_t s32_t; |
||||
|
|
||||
|
typedef size_t mem_ptr_t; |
||||
|
typedef u32_t sys_prot_t; |
||||
|
|
||||
|
/* Define (sn)printf formatters for these lwIP types */ |
||||
|
#define X8_F "02x" |
||||
|
#define U16_F "hu" |
||||
|
#define U32_F "lu" |
||||
|
#define S32_F "ld" |
||||
|
#define X32_F "lx" |
||||
|
|
||||
|
#ifdef __GNUC__ |
||||
|
#define S16_F "d" |
||||
|
#define X16_F "uX" |
||||
|
#define SZT_F "u" |
||||
|
#else |
||||
|
#define S16_F "hd" |
||||
|
#define X16_F "hx" |
||||
|
#define SZT_F "lu" |
||||
|
#endif |
||||
|
|
||||
|
/* Compiler hints for packing structures */ |
||||
|
#define PACK_STRUCT_STRUCT |
||||
|
#define PACK_STRUCT_USE_INCLUDES |
||||
|
|
||||
|
/* Plaform specific diagnostic output */ |
||||
|
#define LWIP_PLATFORM_DIAG(x) do { printf x; } while(0) |
||||
|
|
||||
|
#define LWIP_PLATFORM_ASSERT(x) do { printf("Assertion \"%s\" failed at line %d in %s\n", \ |
||||
|
x, __LINE__, __FILE__); fflush(NULL); abort(); } while(0) |
||||
|
|
||||
|
#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \ |
||||
|
printf("Assertion \"%s\" failed at line %d in %s\n", message, __LINE__, __FILE__); \ |
||||
|
fflush(NULL);handler;} } while(0) |
||||
|
|
||||
|
#ifdef _MSC_VER |
||||
|
/* C runtime functions redefined */ |
||||
|
#define snprintf _snprintf |
||||
|
#endif |
||||
|
|
||||
|
//u32_t dns_lookup_external_hosts_file(const char *name);
|
||||
|
|
||||
|
#define LWIP_RAND() ((u32_t)rand()) |
||||
|
|
||||
|
#endif /* LWIP_ARCH_CC_H */ |
||||
|
|
@ -0,0 +1 @@ |
|||||
|
#pragma pack(pop) |
@ -0,0 +1,4 @@ |
|||||
|
#ifndef LWIP_ARCH_SYS_ARCH_H |
||||
|
#define LWIP_ARCH_SYS_ARCH_H |
||||
|
|
||||
|
#endif /* LWIP_ARCH_SYS_ARCH_H */ |
@ -0,0 +1,31 @@ |
|||||
|
#define NO_SYS 1 |
||||
|
#define SYS_LIGHTWEIGHT_PROT 0 |
||||
|
|
||||
|
#define MEM_LIBC_MALLOC 1 |
||||
|
#define MEMP_MEM_MALLOC 1 |
||||
|
#define MEM_USE_POOLS 0 |
||||
|
#define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT 1 |
||||
|
|
||||
|
#define LWIP_ETHERNET 1 |
||||
|
#define LWIP_IPV4 1 |
||||
|
#define LWIP_TCP 1 |
||||
|
#define LWIP_ARP 1 |
||||
|
#define LWIP_ICMP 1 |
||||
|
#define IP_FRAG 1 |
||||
|
|
||||
|
#define LWIP_DEBUG 1 |
||||
|
#define FTPD_DEBUG LWIP_DBG_ON |
||||
|
//#define IP4_DEBUG LWIP_DBG_ON
|
||||
|
//#define NETIF_DEBUG LWIP_DBG_ON
|
||||
|
//#define TCP_DEBUG LWIP_DBG_ON
|
||||
|
//#define ETHARP_DEBUG LWIP_DBG_ON
|
||||
|
|
||||
|
#define PPP_SUPPORT 0 |
||||
|
#define LWIP_SOCKET 0 |
||||
|
#define LWIP_NETCONN 0 |
||||
|
#define LWIP_RAW 0 |
||||
|
#define LWIP_COMPAT_SOCKETS 0 |
||||
|
#define LWIP_STATS 0 |
||||
|
|
||||
|
#define LWIP_CHECKSUM_CTRL_PER_NETIF 1 |
||||
|
|
@ -0,0 +1,100 @@ |
|||||
|
/*
|
||||
|
* 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 <adam@sics.se> |
||||
|
* Simon Goldschmidt |
||||
|
* |
||||
|
*/ |
||||
|
|
||||
|
#include <stdlib.h> |
||||
|
#include <lwip/opt.h> |
||||
|
|
||||
|
#ifdef _WIN32 |
||||
|
|
||||
|
#include <windows.h> |
||||
|
|
||||
|
LARGE_INTEGER freq, sys_start_time; |
||||
|
|
||||
|
static void sys_init_timing(void) |
||||
|
{ |
||||
|
QueryPerformanceFrequency(&freq); |
||||
|
QueryPerformanceCounter(&sys_start_time); |
||||
|
} |
||||
|
|
||||
|
static LONGLONG sys_get_ms_longlong(void) |
||||
|
{ |
||||
|
LONGLONG ret; |
||||
|
LARGE_INTEGER now; |
||||
|
#if NO_SYS |
||||
|
if (freq.QuadPart == 0) { |
||||
|
sys_init_timing(); |
||||
|
} |
||||
|
#endif /* NO_SYS */ |
||||
|
QueryPerformanceCounter(&now); |
||||
|
ret = now.QuadPart-sys_start_time.QuadPart; |
||||
|
return (u32_t)(((ret)*1000)/freq.QuadPart); |
||||
|
} |
||||
|
|
||||
|
u32_t sys_jiffies(void) |
||||
|
{ |
||||
|
return (u32_t)sys_get_ms_longlong(); |
||||
|
} |
||||
|
|
||||
|
u32_t sys_now(void) |
||||
|
{ |
||||
|
return (u32_t)sys_get_ms_longlong(); |
||||
|
} |
||||
|
|
||||
|
#else |
||||
|
|
||||
|
#include <time.h> |
||||
|
|
||||
|
u32_t sys_now(void) |
||||
|
{ |
||||
|
struct timespec ts; |
||||
|
|
||||
|
clock_gettime(CLOCK_MONOTONIC, &ts); |
||||
|
return ts.tv_sec * 1000L + ts.tv_nsec / 1000000L; |
||||
|
} |
||||
|
|
||||
|
u32_t sys_jiffies(void) |
||||
|
{ |
||||
|
struct timespec ts; |
||||
|
|
||||
|
clock_gettime(CLOCK_MONOTONIC, &ts); |
||||
|
return ts.tv_sec * 1000000000L + ts.tv_nsec; |
||||
|
} |
||||
|
|
||||
|
#endif |
||||
|
|
||||
|
void sys_init(void) |
||||
|
{ |
||||
|
#ifdef _WIN32 |
||||
|
sys_init_timing(); |
||||
|
#endif |
||||
|
} |
@ -0,0 +1,28 @@ |
|||||
|
cmake_minimum_required (VERSION 2.8) |
||||
|
|
||||
|
project(lwip-runner) |
||||
|
|
||||
|
find_path( PCAP_INCLUDE_DIR NAMES pcap/pcap.h pcap.h HINTS "${PCAP_HINTS}/include") |
||||
|
|
||||
|
find_library( PCAP_LIBRARY NAMES pcap wpcap HINTS "${PCAP_HINTS}/lib") |
||||
|
|
||||
|
find_path(LWIP_DIR src/include/lwip/init.h ${CMAKE_CURRENT_SOURCE_DIR}/../../../lwip) |
||||
|
|
||||
|
include_directories( |
||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../lwip-include |
||||
|
${PCAP_INCLUDE_DIR} |
||||
|
${LWIP_DIR}/src/include |
||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../../ff |
||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../.. |
||||
|
) |
||||
|
|
||||
|
include(${LWIP_DIR}/src/Filelists.cmake) |
||||
|
|
||||
|
add_executable(lwip-runner |
||||
|
${CMAKE_CURRENT_SOURCE_DIR}/main.c |
||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../lwip-include/sys_arch.c |
||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../ftpd.c |
||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../vfs.c |
||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../../ff/source/ff.c |
||||
|
) |
||||
|
target_link_libraries(lwip-runner ${PCAP_LIBRARY} lwipcore) |
@ -0,0 +1,190 @@ |
|||||
|
#include <stdio.h> |
||||
|
#include <stddef.h> |
||||
|
#include <stdarg.h> |
||||
|
#include <string.h> |
||||
|
#include <fcntl.h> |
||||
|
|
||||
|
#include <pcap/pcap.h> |
||||
|
|
||||
|
#include <lwip/init.h> |
||||
|
#include <lwip/netif.h> |
||||
|
#include <lwip/ethip6.h> |
||||
|
#include <netif/etharp.h> |
||||
|
#include <lwip/udp.h> |
||||
|
#include <lwip/mld6.h> |
||||
|
#include <lwip/timeouts.h> |
||||
|
|
||||
|
#include <ftpd.h> |
||||
|
|
||||
|
#include <source/ff.h> |
||||
|
#include <source/diskio.h> |
||||
|
|
||||
|
// 4MiB should be enough for everyone
|
||||
|
#define DATA_SIZE (4*1024*1024) |
||||
|
|
||||
|
int data; |
||||
|
|
||||
|
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void *buff) { |
||||
|
if (pdrv != 0) return RES_NOTRDY; |
||||
|
|
||||
|
switch(cmd) { |
||||
|
case CTRL_SYNC: |
||||
|
fsync(data); |
||||
|
return RES_OK; |
||||
|
case GET_SECTOR_COUNT: |
||||
|
*((LBA_t*)buff) = DATA_SIZE/512; |
||||
|
return RES_OK; |
||||
|
case GET_BLOCK_SIZE: |
||||
|
*((WORD*)buff) = 512; |
||||
|
return RES_OK; |
||||
|
} |
||||
|
return RES_PARERR; |
||||
|
} |
||||
|
|
||||
|
DWORD get_fattime() { |
||||
|
// I am lazy.
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
DSTATUS disk_initialize(BYTE pdrv) { |
||||
|
if (pdrv != 0) return STA_NOINIT; |
||||
|
|
||||
|
data = open("data.img", O_CREAT | O_RDWR); |
||||
|
printf("disk_initialize: open returns %d\n", data); |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
DSTATUS disk_status (BYTE pdrv) { |
||||
|
if (pdrv != 0) return STA_NOINIT; |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
DRESULT disk_write(BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count) { |
||||
|
if (pdrv != 0) return RES_NOTRDY; |
||||
|
|
||||
|
size_t r = lseek(data, 512*sector, SEEK_SET); |
||||
|
printf("disk_write: lseek returns %d\n", r); |
||||
|
|
||||
|
r = write(data, buff, 512 * count); |
||||
|
printf("disk_write(%d): write returns %d\n", count, r); |
||||
|
|
||||
|
return RES_OK; |
||||
|
} |
||||
|
|
||||
|
DRESULT disk_read(BYTE pdrv, BYTE* buff, LBA_t sector, UINT count) { |
||||
|
if (pdrv != 0) return RES_NOTRDY; |
||||
|
|
||||
|
size_t r = lseek(data, 512*sector, SEEK_SET); |
||||
|
printf("disk_read: lseek returns %d\n", r); |
||||
|
|
||||
|
r = read(data, buff, 512 * count); |
||||
|
printf("disk_read(%d): read returns %d\n", count, r); |
||||
|
|
||||
|
return RES_OK; |
||||
|
} |
||||
|
|
||||
|
int dbg_printf(const char *fmt, ...) { |
||||
|
va_list args; |
||||
|
va_start(args, fmt); |
||||
|
int r = vprintf(fmt, args); |
||||
|
va_end(args); |
||||
|
return r; |
||||
|
} |
||||
|
|
||||
|
static err_t pcap_output(struct netif* netif, struct pbuf* p) { |
||||
|
pcap_t *pcap = netif->state; |
||||
|
printf("Sending packet with length %d\n", p->tot_len); |
||||
|
|
||||
|
int r = pcap_sendpacket(pcap, (uint8_t*)p->payload, p->tot_len); |
||||
|
|
||||
|
if (r != 0) { |
||||
|
printf("Error sending packet\n"); |
||||
|
printf("Error: %s\n", pcap_geterr(pcap)); |
||||
|
return ERR_IF; |
||||
|
} |
||||
|
return ERR_OK; |
||||
|
} |
||||
|
|
||||
|
static err_t init_callback(struct netif* netif) { |
||||
|
netif->name[0] = 't'; |
||||
|
netif->name[1] = 'p'; |
||||
|
netif->linkoutput = pcap_output; |
||||
|
netif->output = etharp_output; |
||||
|
|
||||
|
netif->mtu = 1500; |
||||
|
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET; |
||||
|
|
||||
|
netif_set_link_up(netif); |
||||
|
|
||||
|
return ERR_OK; |
||||
|
} |
||||
|
|
||||
|
int main(size_t argc, char **argv) { |
||||
|
pcap_t *pcap = pcap_open_live("eth0", 65536, 1, 100, NULL); |
||||
|
char errbuf[PCAP_ERRBUF_SIZE]; |
||||
|
int r = pcap_setnonblock(pcap, 1, errbuf); |
||||
|
if (r == PCAP_ERROR) { |
||||
|
printf("pcap_setnonblock returned: %s", errbuf); |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
FATFS fs; |
||||
|
unsigned char *buf[4096]; |
||||
|
|
||||
|
{ |
||||
|
FRESULT r = f_mount(&fs, "", 1); |
||||
|
printf("f_mount returns %d\n", r); |
||||
|
if (r == FR_NO_FILESYSTEM) { |
||||
|
r = f_mkfs("", NULL, buf, sizeof buf); |
||||
|
printf("f_mkfs returns %d\n", r); |
||||
|
r = f_mount(&fs, "", 1); |
||||
|
printf("f_mount returns %d\n", r); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
struct netif netif; |
||||
|
memset(&netif, 0, sizeof netif); |
||||
|
netif.hwaddr_len = 6; |
||||
|
memcpy(netif.hwaddr, "\xaa\x00\x00\x00\x00\x01", 6); |
||||
|
|
||||
|
ip4_addr_t ip, mask, gw; |
||||
|
IP4_ADDR(&ip, 172,17,0,5); |
||||
|
IP4_ADDR(&mask, 255,255,0,0); |
||||
|
IP4_ADDR(&gw, 172,17,0,1); |
||||
|
|
||||
|
netif_add(&netif, &ip, &mask, &gw, pcap, init_callback, ethernet_input); |
||||
|
netif_set_up(&netif); |
||||
|
|
||||
|
NETIF_SET_CHECKSUM_CTRL(&netif, 0x00FF); |
||||
|
|
||||
|
ftpd_init(); |
||||
|
|
||||
|
sys_restart_timeouts(); |
||||
|
|
||||
|
struct pcap_pkthdr *hdr = NULL; |
||||
|
const unsigned char *data = NULL; |
||||
|
while(1) { |
||||
|
sys_check_timeouts(); |
||||
|
int r = pcap_next_ex(pcap, &hdr, &data); |
||||
|
switch (r) { |
||||
|
case 0: |
||||
|
// Timeout
|
||||
|
continue; |
||||
|
case -1: |
||||
|
printf("Error: %s\n", pcap_geterr(pcap)); |
||||
|
continue; |
||||
|
case 1: |
||||
|
break; |
||||
|
default: |
||||
|
printf("Unknown result: %d\n", r); |
||||
|
continue; |
||||
|
} |
||||
|
printf("Packet length: %d / %d\n", hdr->len, hdr->caplen); |
||||
|
struct pbuf *pbuf = pbuf_alloc(PBUF_RAW, hdr->len, PBUF_RAM); |
||||
|
memcpy(pbuf->payload, data, hdr->len); |
||||
|
netif.input(pbuf, &netif); |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
Loading…
Reference in new issue