初始化版本

This commit is contained in:
冯佳
2026-02-09 10:27:21 +08:00
commit 64d767932f
4467 changed files with 2486822 additions and 0 deletions

View File

@ -0,0 +1,73 @@
#
# Copyright (c) 2001, 2002 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>
#
all compile: lwip_fuzz lwip_fuzz2 lwip_fuzz3
.PHONY: all clean
ifeq ($(origin CC), default)
CC=afl-gcc
endif
LDFLAGS=-lm
# use 'make D=-DUSER_DEFINE' to pass a user define to gcc
CFLAGS=-O2 $(D)
LWIPDIR=../../src
CONTRIBDIR=../../contrib
include $(CONTRIBDIR)/ports/unix/Common.mk
DEPFILES=.depend_fuzz .depend_lwip .depend_app
clean:
rm -f *.o $(LWIPLIBCOMMON) $(APPLIB) lwip_fuzz lwip_fuzz2 lwip_fuzz3 *.s $(DEPFILES) *.core core
depend dep: $(DEPFILES)
@true
ifneq ($(MAKECMDGOALS),clean)
include $(DEPFILES)
endif
.depend_fuzz: fuzz.c fuzz2.c fuzz3.c fuzz_common.c
$(CCDEP) $(CFLAGS) -MM $^ > .depend_fuzz || rm -f .depend_fuzz
.depend_lwip: $(LWIPFILES)
$(CCDEP) $(CFLAGS) -MM $^ > .depend_lwip || rm -f .depend_lwip
.depend_app: $(APPFILES)
$(CCDEP) $(CFLAGS) -MM $^ > .depend_app || rm -f .depend_app
lwip_fuzz: $(DEPFILES) $(LWIPLIBCOMMON) $(APPLIB) fuzz.o fuzz_common.o
$(CC) $(CFLAGS) -o lwip_fuzz fuzz.o fuzz_common.o $(APPLIB) $(LWIPLIBCOMMON) $(LDFLAGS)
lwip_fuzz2: $(DEPFILES) $(LWIPLIBCOMMON) $(APPLIB) fuzz2.o fuzz_common.o
$(CC) $(CFLAGS) -o lwip_fuzz2 fuzz2.o fuzz_common.o $(APPLIB) $(LWIPLIBCOMMON) $(LDFLAGS)
lwip_fuzz3: $(DEPFILES) $(LWIPLIBCOMMON) $(APPLIB) fuzz3.o fuzz_common.o
$(CC) $(CFLAGS) -o lwip_fuzz3 fuzz3.o fuzz_common.o $(APPLIB) $(LWIPLIBCOMMON) $(LDFLAGS)

View File

@ -0,0 +1,34 @@
Fuzzing the lwIP stack (afl-fuzz requires linux/unix or similar)
This directory contains small apps that read Ethernet frames from stdin and
process them. They are used together with the 'american fuzzy lop' tool (found
at https://lcamtuf.coredump.cx/afl/) or its successor AFL++
(https://github.com/AFLplusplus/AFLplusplus) and the sample inputs to test how
unexpected inputs are handled. The afl tool will read the known inputs, and
try to modify them to exercise as many code paths as possible, by instrumenting
the code and keeping track of which code is executed.
Just running make will produce the test programs.
Then run afl with:
afl-fuzz -i inputs/<INPUT> -o output ./lwip_fuzz
and it should start working. It will probably complain about CPU scheduler,
set AFL_SKIP_CPUFREQ=1 to ignore it.
If it complains about invalid "/proc/sys/kernel/core_pattern" setting, try
executing "sudo bash -c 'echo core > /proc/sys/kernel/core_pattern'".
The input is split into different subdirectories since they test different
parts of the code, and since you want to run one instance of afl-fuzz on each
core.
When afl finds a crash or a hang, the input that caused it will be placed in
the output directory. If you have hexdump and text2pcap tools installed,
running output_to_pcap.sh <outputdir> will create pcap files for each input
file to simplify viewing in wireshark.
The lwipopts.h file needs to have checksum checking off, otherwise almost every
packet will be discarded because of that. The other options can be tuned to
expose different parts of the code.

View File

View File

@ -0,0 +1,39 @@
/*
* 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: Erik Ekman <erik@kryo.se>
* Simon Goldschmidt <goldsimon@gmx.de>
*
*/
#include "fuzz_common.h"
int main(int argc, char** argv)
{
return lwip_fuzztest(argc, argv, LWIP_FUZZ_SINGLE, LWIP_FUZZ_DEFAULT);
}

View File

@ -0,0 +1,39 @@
/*
* 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: Erik Ekman <erik@kryo.se>
* Simon Goldschmidt <goldsimon@gmx.de>
*
*/
#include "fuzz_common.h"
int main(int argc, char** argv)
{
return lwip_fuzztest(argc, argv, LWIP_FUZZ_MULTIPACKET, LWIP_FUZZ_DEFAULT);
}

View File

@ -0,0 +1,40 @@
/*
* 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: Erik Ekman <erik@kryo.se>
* Simon Goldschmidt <goldsimon@gmx.de>
*
*/
#include "fuzz_common.h"
int main(int argc, char** argv)
{
return lwip_fuzztest(argc, argv, LWIP_FUZZ_MULTIPACKET_TIME,
LWIP_FUZZ_STATICARP|LWIP_FUZZ_TCP_SERVER|LWIP_FUZZ_TCP_CLIENT|LWIP_FUZZ_UDP_SERVER|LWIP_FUZZ_UDP_CLIENT);
}

View File

@ -0,0 +1,704 @@
/*
* 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: Erik Ekman <erik@kryo.se>
* Simon Goldschmidt <goldsimon@gmx.de>
*
*/
#include "fuzz_common.h"
#include "lwip/altcp_tcp.h"
#include "lwip/dns.h"
#include "lwip/init.h"
#include "lwip/netif.h"
#include "lwip/sys.h"
#include "lwip/timeouts.h"
#include "lwip/udp.h"
#include "netif/etharp.h"
#if LWIP_IPV6
#include "lwip/ethip6.h"
#include "lwip/nd6.h"
#endif
#include "lwip/apps/httpd.h"
#include "lwip/apps/snmp.h"
#include "lwip/apps/lwiperf.h"
#include "lwip/apps/mdns.h"
#include <string.h>
#include <stdio.h>
static u8_t pktbuf[200000];
static const u8_t *remfuzz_ptr; /* remaining fuzz pointer */
static size_t remfuzz_len; /* remaining fuzz length */
#ifndef FUZZ_DEBUG
#define FUZZ_DEBUG LWIP_DBG_OFF
#endif
#ifdef LWIP_FUZZ_SYS_NOW
/* This offset should be added to the time 'sys_now()' returns */
u32_t sys_now_offset;
#endif
/** Set this to 1 and define FUZZ_DUMP_PCAP_FILE to dump tx and rx packets into
* a pcap file. At the same time, packet info is written via LWIP_DEBUGF so
* packets can be matched to other events for debugging them.
*/
#ifndef FUZZ_DUMP_PCAP
#define FUZZ_DUMP_PCAP 0
#endif
#if FUZZ_DUMP_PCAP
const u8_t pcap_file_header[24] = {
0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00
};
static FILE* fpcap;
static u32_t pcap_packet;
static void pcap_dump_init(void)
{
fpcap = fopen(FUZZ_DUMP_PCAP_FILE, "wb");
if (fpcap != NULL) {
/* write header */
fwrite(pcap_file_header, 1, sizeof(pcap_file_header), fpcap);
}
}
/* This function might have to be called from LWIP_PLATFORM_ASSERT()
* in order to produce correct pcap results on crash.
* Define this global so that for a test, we can call this from anywhere...
*/
void pcap_dump_stop(void);
void pcap_dump_stop(void)
{
if (fpcap != NULL) {
fclose(fpcap);
fpcap = NULL;
}
}
static void pcap_dump_packet(struct pbuf *p, int is_tx)
{
if (fpcap != NULL) {
struct pbuf *q;
u32_t data;
pcap_packet++;
if (is_tx) {
LWIP_DEBUGF(FUZZ_DEBUG, ("> %d fuzz: netif: send %u bytes\n", pcap_packet, p->tot_len));
} else {
LWIP_DEBUGF(FUZZ_DEBUG, ("< %d fuzz: RX packet of %u bytes\n", pcap_packet, p->tot_len));
if (pcap_packet == 50 || pcap_packet == 33 || pcap_packet == 29) {
pcap_packet++;
pcap_packet--;
}
}
/* write packet header */
fwrite(&pcap_packet, 1, sizeof(pcap_packet), fpcap);
data = 0;
fwrite(&data, 1, sizeof(data), fpcap);
data = p->tot_len;
fwrite(&data, 1, sizeof(data), fpcap);
fwrite(&data, 1, sizeof(data), fpcap);
/* write packet data */
for(q = p; q != NULL; q = q->next) {
fwrite(q->payload, 1, q->len, fpcap);
}
}
}
static void pcap_dump_rx_packet(struct pbuf *p)
{
pcap_dump_packet(p, 0);
}
static void pcap_dump_tx_packet(struct pbuf *p)
{
pcap_dump_packet(p, 1);
}
#else /* FUZZ_DUMP_PCAP */
#define pcap_dump_rx_packet(p)
#define pcap_dump_tx_packet(p)
#define pcap_dump_init()
#define pcap_dump_stop()
#endif /* FUZZ_DUMP_PCAP */
/* no-op send function */
static err_t lwip_tx_func(struct netif *netif, struct pbuf *p)
{
pcap_dump_tx_packet(p);
LWIP_UNUSED_ARG(netif);
LWIP_UNUSED_ARG(p);
return ERR_OK;
}
static err_t testif_init(struct netif *netif)
{
netif->name[0] = 'f';
netif->name[1] = 'z';
netif->output = etharp_output;
netif->linkoutput = lwip_tx_func;
netif->mtu = 1500;
netif->hwaddr_len = 6;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_IGMP;
netif->hwaddr[0] = 0x00;
netif->hwaddr[1] = 0x23;
netif->hwaddr[2] = 0xC1;
netif->hwaddr[3] = 0xDE;
netif->hwaddr[4] = 0xD0;
netif->hwaddr[5] = 0x0D;
#if LWIP_IPV6
netif->output_ip6 = ethip6_output;
netif_create_ip6_linklocal_address(netif, 1);
netif->flags |= NETIF_FLAG_MLD6;
#endif
return ERR_OK;
}
static void input_pkt(struct netif *netif, const u8_t *data, size_t len)
{
struct pbuf *p, *q;
err_t err;
if (len > 0xFFFF) {
printf("pkt too big (%#zX bytes)\n", len);
return;
}
p = pbuf_alloc(PBUF_RAW, (u16_t)len, PBUF_POOL);
LWIP_ASSERT("alloc failed", p);
for(q = p; q != NULL; q = q->next) {
MEMCPY(q->payload, data, q->len);
data += q->len;
}
remfuzz_ptr += len;
remfuzz_len -= len;
pcap_dump_rx_packet(p);
err = netif->input(p, netif);
if (err != ERR_OK) {
pbuf_free(p);
}
}
static void input_pkts(enum lwip_fuzz_type type, struct netif *netif, const u8_t *data, size_t len)
{
size_t packet_nr = 0;
remfuzz_ptr = data;
remfuzz_len = len;
if (type == LWIP_FUZZ_SINGLE) {
input_pkt(netif, data, len);
} else {
const u16_t max_packet_size = 1514;
const size_t minlen = sizeof(u16_t) + (type == LWIP_FUZZ_MULTIPACKET_TIME ? sizeof(u32_t) : 0);
while (remfuzz_len > minlen) {
u16_t frame_len;
#ifdef LWIP_FUZZ_SYS_NOW
u32_t external_delay = 0;
#endif
packet_nr++;
if (type == LWIP_FUZZ_MULTIPACKET_TIME) {
#ifdef LWIP_FUZZ_SYS_NOW
/* Extract external delay time from fuzz pool */
memcpy(&external_delay, remfuzz_ptr, sizeof(u32_t));
external_delay = ntohl(external_delay);
#endif
remfuzz_ptr += sizeof(u32_t);
remfuzz_len -= sizeof(u32_t);
}
memcpy(&frame_len, remfuzz_ptr, sizeof(u16_t));
remfuzz_ptr += sizeof(u16_t);
remfuzz_len -= sizeof(u16_t);
frame_len = ntohs(frame_len) & 0x7FF;
frame_len = LWIP_MIN(frame_len, max_packet_size);
if (frame_len > remfuzz_len) {
frame_len = (u16_t)remfuzz_len;
}
if (frame_len != 0) {
if (type == LWIP_FUZZ_MULTIPACKET_TIME) {
#ifdef LWIP_FUZZ_SYS_NOW
/* Update total external delay time, and check timeouts */
sys_now_offset += external_delay;
LWIP_DEBUGF(FUZZ_DEBUG, ("fuzz: sys_now_offset += %u -> %u\n", external_delay, sys_now_offset));
#endif
sys_check_timeouts();
}
input_pkt(netif, remfuzz_ptr, frame_len);
/* Check timeouts again */
sys_check_timeouts();
}
}
}
}
#if LWIP_TCP
static struct altcp_pcb *tcp_client_pcb; /* a pcb for the TCP client */
static struct altcp_pcb *tcp_server_pcb; /* a pcb for the TCP server */
static u16_t tcp_remote_port; /* a TCP port number of the destination */
static u16_t tcp_local_port; /* a TCP port number of the local server */
/**
* tcp_app_fuzz_input
* Input fuzz with a write function for TCP.
*/
static void
tcp_app_fuzz_input(struct altcp_pcb *pcb)
{
if (remfuzz_len > sizeof(u16_t)) {
/*
* (max IP packet size) - ((minimum IP header size) + (minimum TCP header size))
* = 65535 - (20 + 20)
* = 65495
*/
const u16_t max_data_size = 65495;
u16_t data_len;
memcpy(&data_len, remfuzz_ptr, sizeof(u16_t));
remfuzz_ptr += sizeof(u16_t);
remfuzz_len -= sizeof(u16_t);
data_len = ntohs(data_len);
data_len = LWIP_MIN(data_len, max_data_size);
if (data_len > remfuzz_len) {
data_len = (u16_t)remfuzz_len;
}
if (data_len != 0) {
LWIP_DEBUGF(FUZZ_DEBUG, ("fuzz: tcp: write %u bytes\n", data_len));
altcp_write(pcb, remfuzz_ptr, data_len, TCP_WRITE_FLAG_COPY);
altcp_output(pcb);
} else {
LWIP_DEBUGF(FUZZ_DEBUG, ("fuzz: tcp: close\n"));
altcp_close(pcb);
}
remfuzz_ptr += data_len;
remfuzz_len -= data_len;
}
}
/**
* tcp_client_connected
* A connected callback function (for the TCP client)
*/
static err_t
tcp_client_connected(void *arg, struct altcp_pcb *pcb, err_t err)
{
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(err);
LWIP_DEBUGF(FUZZ_DEBUG, ("fuzz: tcp: tcp_client_connected\n"));
tcp_app_fuzz_input(pcb);
return ERR_OK;
}
/**
* tcp_client_recv
* A recv callback function (for the TCP client)
*/
static err_t
tcp_client_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err)
{
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(err);
if (p == NULL) {
altcp_close(pcb);
} else {
altcp_recved(pcb, p->tot_len);
LWIP_DEBUGF(FUZZ_DEBUG, ("fuzz: tcp: tcp_client_recv: %d\n", p->tot_len));
tcp_app_fuzz_input(pcb);
pbuf_free(p);
}
return ERR_OK;
}
/**
* tcp_client_sent
* A sent callback function (for the TCP client)
*/
static err_t
tcp_client_sent(void *arg, struct altcp_pcb *pcb, u16_t len)
{
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(pcb);
LWIP_UNUSED_ARG(len);
return ERR_OK;
}
/**
* tcp_client_poll
* A poll callback function (for the TCP client)
*/
static err_t
tcp_client_poll(void *arg, struct altcp_pcb *pcb)
{
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(pcb);
return ERR_OK;
}
/**
* tcp_client_err
* An err callback function (for the TCP client)
*/
static void
tcp_client_err(void *arg, err_t err)
{
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(err);
}
/**
* tcp_server_recv
* A recv callback function (for the TCP server)
*/
static err_t
tcp_server_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err)
{
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(err);
if (p == NULL) {
altcp_close(pcb);
} else {
altcp_recved(pcb, p->tot_len);
LWIP_DEBUGF(FUZZ_DEBUG, ("fuzz: tcp: tcp_server_recv: %d\n", p->tot_len));
tcp_app_fuzz_input(pcb);
pbuf_free(p);
}
return ERR_OK;
}
/**
* tcp_server_sent
* A sent callback function (for the TCP server)
*/
static err_t
tcp_server_sent(void *arg, struct altcp_pcb *pcb, u16_t len)
{
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(pcb);
LWIP_UNUSED_ARG(len);
return ERR_OK;
}
/**
* tcp_server_poll
* A poll callback function (for the TCP server)
*/
static err_t
tcp_server_poll(void *arg, struct altcp_pcb *pcb)
{
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(pcb);
return ERR_OK;
}
/**
* tcp_server_err
* An err callbuck function (for the TCP server)
*/
static void
tcp_server_err(void *arg, err_t err)
{
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(err);
}
/**
* tcp_server_accept
* An accept callbuck function (for the TCP server)
*/
static err_t
tcp_server_accept(void *arg, struct altcp_pcb *pcb, err_t err)
{
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(err);
if ((err != ERR_OK) || (pcb == NULL)) {
return ERR_VAL;
}
LWIP_DEBUGF(FUZZ_DEBUG, ("fuzz: accept from remote\n"));
altcp_setprio(pcb, TCP_PRIO_MIN);
altcp_recv(pcb, tcp_server_recv);
altcp_err(pcb, tcp_server_err);
altcp_poll(pcb, tcp_server_poll, 10);
altcp_sent(pcb, tcp_server_sent);
return ERR_OK;
}
#endif /* LWIP_TCP */
#if LWIP_UDP
static struct udp_pcb *udp_client_pcb; /* a pcb for the UDP client */
static struct udp_pcb *udp_server_pcb; /* a pcb for the UDP server */
static u16_t udp_remote_port; /* a UDP port number of the destination */
static u16_t udp_local_port; /* a UDP port number of the local server*/
/**
* udp_app_fuzz_input
* Input fuzz with write functions for UDP.
*/
static void
udp_app_fuzz_input(struct udp_pcb *pcb, const ip_addr_t *addr, u16_t port)
{
if (remfuzz_len > sizeof(u16_t)) {
/*
* (max IP packet size) - ((minimum IP header size) - (minimum UDP header size))
* = 65535 - (20 + 8)
* = 65507
*/
const u16_t max_data_size = 65507;
u16_t data_len;
memcpy(&data_len, remfuzz_ptr, sizeof(u16_t));
remfuzz_ptr += sizeof(u16_t);
remfuzz_len -= sizeof(u16_t);
data_len = ntohs(data_len);
data_len = LWIP_MIN(data_len, max_data_size);
if (data_len > remfuzz_len) {
data_len = (u16_t)remfuzz_len;
}
LWIP_DEBUGF(FUZZ_DEBUG, ("fuzz: udp: send %u bytes\n", data_len));
if (data_len != 0) {
struct pbuf *p, *q;
p = pbuf_alloc(PBUF_RAW, (u16_t)data_len, PBUF_POOL);
LWIP_ASSERT("alloc failed", p);
for (q = p; q != NULL; q = q->next) {
MEMCPY(q->payload, remfuzz_ptr, q->len);
remfuzz_ptr += q->len;
}
remfuzz_len -= data_len;
/*
* Trying input from ...
*
* client:
* The pcb has information about the destination.
* We use udp_send().
*
* server:
* The pcb does NOT have information about the destination.
* We use udp_sendto().
*/
if (addr == NULL) {
udp_send(pcb, p);
} else {
udp_sendto(pcb, p, addr, port);
}
pbuf_free(p);
}
}
}
/**
* udp_client_recv
* A recv callback function (for the UDP client)
*/
static void
udp_client_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(p);
LWIP_UNUSED_ARG(addr);
LWIP_UNUSED_ARG(port);
if (p == NULL) {
udp_disconnect(pcb);
} else {
/*
* We call the function with 2nd argument set to NULL
* to input fuzz from udp_send.
*/
udp_app_fuzz_input(pcb, NULL, port);
pbuf_free(p);
}
}
/**
* udp_server_recv
* A recv callback functyion (for the UDP server)
*/
static void
udp_server_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(p);
LWIP_UNUSED_ARG(addr);
LWIP_UNUSED_ARG(port);
if (p != NULL) {
udp_app_fuzz_input(pcb, addr, port);
pbuf_free(p);
}
}
#endif /* LWIP_UDP */
int lwip_fuzztest(int argc, char** argv, enum lwip_fuzz_type type, u32_t test_apps)
{
struct netif net_test;
ip4_addr_t addr;
ip4_addr_t netmask;
ip4_addr_t gw;
size_t len;
err_t err;
ip_addr_t remote_addr; /* a IPv4 addr of the destination */
struct eth_addr remote_mac = ETH_ADDR(0x28, 0x00, 0x00, 0x22, 0x2b, 0x38); /* a MAC addr of the destination */
pcap_dump_init();
lwip_init();
IP4_ADDR(&addr, 172, 30, 115, 84);
IP4_ADDR(&netmask, 255, 255, 255, 0);
IP4_ADDR(&gw, 172, 30, 115, 1);
netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input);
netif_set_up(&net_test);
netif_set_link_up(&net_test);
if (test_apps & LWIP_FUZZ_STATICARP) {
/* Add the ARP entry */
IP_ADDR4(&remote_addr, 172, 30, 115, 37);
etharp_add_static_entry(&(remote_addr.u_addr.ip4), &remote_mac);
}
#if LWIP_IPV6
nd6_tmr(); /* tick nd to join multicast groups */
#endif
dns_setserver(0, &net_test.gw);
if (test_apps & LWIP_FUZZ_DEFAULT) {
/* initialize apps */
httpd_init();
lwiperf_start_tcp_server_default(NULL, NULL);
mdns_resp_init();
mdns_resp_add_netif(&net_test, "hostname");
snmp_init();
}
if (test_apps & LWIP_FUZZ_TCP_CLIENT) {
tcp_client_pcb = altcp_tcp_new_ip_type(IPADDR_TYPE_ANY);
LWIP_ASSERT("Error: altcp_new() failed", tcp_client_pcb != NULL);
tcp_remote_port = 80;
err = altcp_connect(tcp_client_pcb, &remote_addr, tcp_remote_port, tcp_client_connected);
LWIP_ASSERT("Error: altcp_connect() failed", err == ERR_OK);
altcp_recv(tcp_client_pcb, tcp_client_recv);
altcp_err(tcp_client_pcb, tcp_client_err);
altcp_poll(tcp_client_pcb, tcp_client_poll, 10);
altcp_sent(tcp_client_pcb, tcp_client_sent);
}
if (test_apps & LWIP_FUZZ_TCP_SERVER) {
tcp_server_pcb = altcp_tcp_new_ip_type(IPADDR_TYPE_ANY);
LWIP_ASSERT("Error: altcp_new() failed", tcp_server_pcb != NULL);
altcp_setprio(tcp_server_pcb, TCP_PRIO_MIN);
tcp_local_port = 80;
err = altcp_bind(tcp_server_pcb, IP_ANY_TYPE, tcp_local_port);
LWIP_ASSERT("Error: altcp_bind() failed", err == ERR_OK);
tcp_server_pcb = altcp_listen(tcp_server_pcb);
LWIP_ASSERT("Error: altcp_listen() failed", err == ERR_OK);
altcp_accept(tcp_server_pcb, tcp_server_accept);
}
if (test_apps & LWIP_FUZZ_UDP_CLIENT) {
udp_client_pcb = udp_new();
udp_new_ip_type(IPADDR_TYPE_ANY);
udp_recv(udp_client_pcb, udp_client_recv, NULL);
udp_remote_port = 161;
udp_connect(udp_client_pcb, &remote_addr, udp_remote_port);
}
if (test_apps & LWIP_FUZZ_UDP_SERVER) {
udp_server_pcb = udp_new();
udp_new_ip_type(IPADDR_TYPE_ANY);
udp_local_port = 161;
udp_bind(udp_server_pcb, IP_ANY_TYPE, udp_local_port);
udp_recv(udp_server_pcb, udp_server_recv, NULL);
}
if(argc > 1) {
FILE* f;
const char* filename;
printf("reading input from file... ");
fflush(stdout);
filename = argv[1];
LWIP_ASSERT("invalid filename", filename != NULL);
f = fopen(filename, "rb");
LWIP_ASSERT("open failed", f != NULL);
len = fread(pktbuf, 1, sizeof(pktbuf), f);
fclose(f);
printf("testing file: \"%s\"...\r\n", filename);
} else {
len = fread(pktbuf, 1, sizeof(pktbuf), stdin);
}
input_pkts(type, &net_test, pktbuf, len);
pcap_dump_stop();
return 0;
}
#ifdef LWIP_RAND_FOR_FUZZ
u32_t lwip_fuzz_rand(void)
{
#ifdef LWIP_RAND_FOR_FUZZ_SIMULATE_GLIBC
/* this is what glibc rand() returns (first 20 numbers) */
static u32_t rand_nrs[] = {0x6b8b4567, 0x327b23c6, 0x643c9869, 0x66334873, 0x74b0dc51,
0x19495cff, 0x2ae8944a, 0x625558ec, 0x238e1f29, 0x46e87ccd,
0x3d1b58ba, 0x507ed7ab, 0x2eb141f2, 0x41b71efb, 0x79e2a9e3,
0x7545e146, 0x515f007c, 0x5bd062c2, 0x12200854, 0x4db127f8};
static unsigned idx = 0;
u32_t ret = rand_nrs[idx];
idx++;
if (idx >= sizeof(rand_nrs)/sizeof((rand_nrs)[0])) {
idx = 0;
}
return ret;
#else
/* a simple LCG, unsafe but should give the same result for every execution (best for fuzzing) */
u32_t result;
static s32_t state[1] = {0xdeadbeef};
uint64_t val = state[0] & 0xffffffff;
val = ((val * 1103515245) + 12345) & 0x7fffffff;
state[0] = (s32_t)val;
result = (u32_t)val;
return result;
#endif
}
#endif

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2001-2004 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: Simon Goldschmidt <goldsimon@gmx.de>
*
*/
#ifndef LWIP_HDR_FUZZ_COMMON_H
#define LWIP_HDR_FUZZ_COMMON_H
#include "lwip/opt.h"
#include "lwip/arch.h"
#ifdef __cplusplus
extern "C" {
#endif
enum lwip_fuzz_type {
LWIP_FUZZ_SINGLE = 0,
LWIP_FUZZ_MULTIPACKET = 1,
LWIP_FUZZ_MULTIPACKET_TIME = 2
};
/* bitmask of what to test: */
#define LWIP_FUZZ_DEFAULT 0x01
#define LWIP_FUZZ_STATICARP 0x02
#define LWIP_FUZZ_TCP_SERVER 0x04
#define LWIP_FUZZ_TCP_CLIENT 0x08
#define LWIP_FUZZ_UDP_SERVER 0x10
#define LWIP_FUZZ_UDP_CLIENT 0x20
int lwip_fuzztest(int argc, char** argv, enum lwip_fuzz_type type, u32_t test_apps);
#ifdef __cplusplus
}
#endif
#endif /* LWIP_HDR_FUZZ_COMMON_H */

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,84 @@
/*
* 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: Simon Goldschmidt
*
*/
#ifndef LWIP_HDR_LWIPOPTS_H__
#define LWIP_HDR_LWIPOPTS_H__
#define MEMP_NUM_SYS_TIMEOUT 17
#define LWIP_FUZZ_SYS_NOW
#define LWIP_RAND_FOR_FUZZ
/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */
#define NO_SYS 1
#define LWIP_NETCONN 0
#define LWIP_SOCKET 0
#define SYS_LIGHTWEIGHT_PROT 0
#define LWIP_IPV6 1
#define IPV6_FRAG_COPYHEADER 1
#define LWIP_IPV6_DUP_DETECT_ATTEMPTS 0
/* Enable some protocols to test them */
#define LWIP_DHCP 1
#define LWIP_AUTOIP 1
#define LWIP_IGMP 1
#define LWIP_DNS 1
#define LWIP_ALTCP 1
/* Turn off checksum verification of fuzzed data */
#define CHECKSUM_CHECK_IP 0
#define CHECKSUM_CHECK_UDP 0
#define CHECKSUM_CHECK_TCP 0
#define CHECKSUM_CHECK_ICMP 0
#define CHECKSUM_CHECK_ICMP6 0
/* Minimal changes to opt.h required for tcp unit tests: */
#define MEM_SIZE 16000
#define TCP_SND_QUEUELEN 40
#define MEMP_NUM_TCP_SEG TCP_SND_QUEUELEN
#define TCP_OVERSIZE 1
#define TCP_SND_BUF (12 * TCP_MSS)
#define TCP_WND (10 * TCP_MSS)
#define LWIP_WND_SCALE 1
#define TCP_RCV_SCALE 2
#define PBUF_POOL_SIZE 400 /* pbuf tests need ~200KByte */
/* Minimal changes to opt.h required for etharp unit tests: */
#define ETHARP_SUPPORT_STATIC_ENTRIES 1
#define LWIP_NUM_NETIF_CLIENT_DATA 1
#define LWIP_SNMP 1
#define MIB2_STATS 1
#define LWIP_MDNS_RESPONDER 1
#endif /* LWIP_HDR_LWIPOPTS_H__ */

View File

@ -0,0 +1,31 @@
#!/bin/bash
if [ -z "$1" ]
then
echo "This script will make pcap files from the afl-fuzz crash/hang files"
echo "It needs hexdump and text2pcap"
echo "Please give output directory as argument"
exit 2
fi
for i in `ls $1/crashes/id*`
do
PCAPNAME=`echo $i | grep pcap`
if [ -z "$PCAPNAME" ]; then
hexdump -C $i > $1/$$.tmp
text2pcap $1/$$.tmp ${i}.pcap
fi
done
for i in `ls $1/hangs/id*`
do
PCAPNAME=`echo $i | grep pcap`
if [ -z "$PCAPNAME" ]; then
hexdump -C $i > $1/$$.tmp
text2pcap $1/$$.tmp ${i}.pcap
fi
done
rm -f $1/$$.tmp
echo
echo "Created pcap files:"
ls $1/*/*.pcap

View File

@ -0,0 +1,726 @@
/**
* @file
* Sockets stresstest
*
* This file uses the lwIP socket API to do stress tests that should test the
* stability when used in many different situations, with many concurrent
* sockets making concurrent transfers in different manners.
*
* - test rely on loopback sockets for now, so netif drivers are not tested
* - all enabled functions shall be used
* - parallelism of the tests depend on enough resources being available
* (configure your lwipopts.h settings high enough)
* - test should also be able to run in a real target
*
* TODO:
* - full duplex
* - add asserts about internal socket/netconn/pcb state?
*/
/*
* Copyright (c) 2017 Simon Goldschmidt
* 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: Simon Goldschmidt <goldsimon@gmx.de>
*
*/
#include "lwip/opt.h"
#include "sockets_stresstest.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/mem.h"
#include <stdio.h>
#include <string.h>
#if LWIP_SOCKET && LWIP_IPV4 /* this uses IPv4 loopback sockets, currently */
#ifndef TEST_SOCKETS_STRESS
#define TEST_SOCKETS_STRESS LWIP_DBG_OFF
#endif
#define TEST_TIME_SECONDS 10
#define TEST_TXRX_BUFSIZE (TCP_MSS * 2)
#define TEST_MAX_RXWAIT_MS 50
#define TEST_MAX_CONNECTIONS 50
#define TEST_SOCK_READABLE 0x01
#define TEST_SOCK_WRITABLE 0x02
#define TEST_SOCK_ERR 0x04
#define TEST_MODE_SELECT 0x01
#define TEST_MODE_POLL 0x02
#define TEST_MODE_NONBLOCKING 0x04
#define TEST_MODE_WAIT 0x08
#define TEST_MODE_RECVTIMEO 0x10
#define TEST_MODE_SLEEP 0x20
static int sockets_stresstest_numthreads;
struct test_settings {
struct sockaddr_storage addr;
int start_client;
int loop_cnt;
};
struct sockets_stresstest_fullduplex {
int s;
volatile int closed;
};
static void
fill_test_data(void *buf, size_t buf_len_bytes)
{
u8_t *p = (u8_t*)buf;
u16_t i, chk;
LWIP_ASSERT("buffer too short", buf_len_bytes >= 4);
LWIP_ASSERT("buffer too big", buf_len_bytes <= 0xFFFF);
/* store the total number of bytes */
p[0] = (u8_t)(buf_len_bytes >> 8);
p[1] = (u8_t)buf_len_bytes;
/* fill buffer with random */
chk = 0;
for (i = 4; i < buf_len_bytes; i++) {
u8_t rnd = (u8_t)LWIP_RAND();
p[i] = rnd;
chk += rnd;
}
/* store checksum */
p[2] = (u8_t)(chk >> 8);
p[3] = (u8_t)chk;
}
static size_t
check_test_data(const void *buf, size_t buf_len_bytes)
{
u8_t *p = (u8_t*)buf;
u16_t i, chk, chk_rx, len_rx;
LWIP_ASSERT("buffer too short", buf_len_bytes >= 4);
len_rx = (((u16_t)p[0]) << 8) | p[1];
LWIP_ASSERT("len too short", len_rx >= 4);
if (len_rx > buf_len_bytes) {
/* not all data received in this segment */
LWIP_DEBUGF(TEST_SOCKETS_STRESS | LWIP_DBG_TRACE, ("check-\n"));
return buf_len_bytes;
}
chk_rx = (((u16_t)p[2]) << 8) | p[3];
/* calculate received checksum */
chk = 0;
for (i = 4; i < len_rx; i++) {
chk += p[i];
}
LWIP_ASSERT("invalid checksum", chk == chk_rx);
if (len_rx < buf_len_bytes) {
size_t data_left = buf_len_bytes - len_rx;
memmove(p, &p[len_rx], data_left);
return data_left;
}
/* if we come here, we received exactly one chunk
-> next offset is 0 */
return 0;
}
static size_t
recv_and_check_data_return_offset(int s, char *rxbuf, size_t rxbufsize, size_t rxoff, int *closed, const char *dbg)
{
ssize_t ret;
ret = lwip_read(s, &rxbuf[rxoff], rxbufsize - rxoff);
if (ret == 0) {
*closed = 1;
return rxoff;
}
*closed = 0;
LWIP_DEBUGF(TEST_SOCKETS_STRESS | LWIP_DBG_TRACE, ("%s %d rx %d\n", dbg, s, (int)ret));
if (ret == -1) {
/* TODO: for this to work, 'errno' has to support multithreading... */
int err = errno;
if (err == ENOTCONN) {
*closed = 1;
return 0;
}
LWIP_ASSERT("err == 0", err == 0);
}
LWIP_ASSERT("ret > 0", ret > 0);
return check_test_data(rxbuf, rxoff + ret);
}
#if LWIP_SOCKET_SELECT
static int
sockets_stresstest_wait_readable_select(int s, int timeout_ms)
{
int ret;
struct timeval tv;
fd_set fs_r;
fd_set fs_w;
fd_set fs_e;
FD_ZERO(&fs_r);
FD_ZERO(&fs_w);
FD_ZERO(&fs_e);
FD_SET(s, &fs_r);
FD_SET(s, &fs_e);
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms - (tv.tv_sec * 1000)) * 1000;
ret = lwip_select(s + 1, &fs_r, &fs_w, &fs_e, &tv);
LWIP_ASSERT("select error", ret >= 0);
if (ret) {
/* convert poll flags to our flags */
ret = 0;
if (FD_ISSET(s, &fs_r)) {
ret |= TEST_SOCK_READABLE;
}
if (FD_ISSET(s, &fs_w)) {
ret |= TEST_SOCK_WRITABLE;
}
if (FD_ISSET(s, &fs_e)) {
ret |= TEST_SOCK_ERR;
}
return ret;
}
return 0;
}
#endif
#if LWIP_SOCKET_POLL
static int
sockets_stresstest_wait_readable_poll(int s, int timeout_ms)
{
int ret;
struct pollfd pfd;
pfd.fd = s;
pfd.revents = 0;
pfd.events = POLLIN | POLLERR;
ret = lwip_poll(&pfd, 1, timeout_ms);
if (ret) {
/* convert poll flags to our flags */
ret = 0;
if (pfd.revents & POLLIN) {
ret |= TEST_SOCK_READABLE;
}
if (pfd.revents & POLLOUT) {
ret |= TEST_SOCK_WRITABLE;
}
if (pfd.revents & POLLERR) {
ret |= TEST_SOCK_ERR;
}
return ret;
}
return 0;
}
#endif
#if LWIP_SO_RCVTIMEO
static int
sockets_stresstest_wait_readable_recvtimeo(int s, int timeout_ms)
{
int ret;
char buf;
#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD
int opt_on = timeout_ms;
int opt_off = 0;
#else
struct timeval opt_on, opt_off;
opt_on.tv_sec = timeout_ms / 1000;
opt_on.tv_usec = (timeout_ms - (opt_on.tv_sec * 1000)) * 1000;
opt_off.tv_sec = 0;
opt_off.tv_usec = 0;
#endif
/* enable receive timeout */
ret = lwip_setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &opt_on, sizeof(opt_on));
LWIP_ASSERT("setsockopt error", ret == 0);
/* peek for one byte with timeout */
ret = lwip_recv(s, &buf, 1, MSG_PEEK);
/* disable receive timeout */
ret = lwip_setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &opt_off, sizeof(opt_off));
LWIP_ASSERT("setsockopt error", ret == 0);
if (ret == 1) {
return TEST_SOCK_READABLE;
}
if (ret == 0) {
return 0;
}
if (ret == -1) {
return TEST_SOCK_ERR;
}
LWIP_ASSERT("invalid return value", 0);
return TEST_SOCK_ERR;
}
#endif
static int
sockets_stresstest_wait_readable_wait_peek(int s, int timeout_ms)
{
int ret;
char buf;
LWIP_UNUSED_ARG(timeout_ms); /* cannot time out here */
/* peek for one byte */
ret = lwip_recv(s, &buf, 1, MSG_PEEK);
if (ret == 1) {
return TEST_SOCK_READABLE;
}
if (ret == 0) {
return 0;
}
if (ret == -1) {
return TEST_SOCK_ERR;
}
LWIP_ASSERT("invalid return value", 0);
return TEST_SOCK_ERR;
}
static int
sockets_stresstest_wait_readable_nonblock(int s, int timeout_ms)
{
int ret;
char buf;
u32_t wait_until = sys_now() + timeout_ms;
while(sys_now() < wait_until) {
/* peek for one byte */
ret = lwip_recv(s, &buf, 1, MSG_PEEK | MSG_DONTWAIT);
if (ret == 1) {
return TEST_SOCK_READABLE;
}
if (ret == -1) {
/* TODO: for this to work, 'errno' has to support multithreading... */
int err = errno;
if (err != EWOULDBLOCK) {
return TEST_SOCK_ERR;
}
}
/* TODO: sleep? */
}
return 0;
}
static int sockets_stresstest_rand_mode(int allow_wait, int allow_rx)
{
u32_t random_value = LWIP_RAND();
#if LWIP_SOCKET_SELECT
if (random_value & TEST_MODE_SELECT) {
return TEST_MODE_SELECT;
}
#endif
#if LWIP_SOCKET_POLL
if (random_value & TEST_MODE_POLL) {
return TEST_MODE_POLL;
}
#endif
if (!allow_rx) {
return TEST_MODE_SLEEP;
}
#if LWIP_SO_RCVTIMEO
if (random_value & TEST_MODE_RECVTIMEO) {
return TEST_MODE_RECVTIMEO;
}
#endif
if (allow_wait) {
if (random_value & TEST_MODE_RECVTIMEO) {
return TEST_MODE_RECVTIMEO;
}
}
return TEST_MODE_NONBLOCKING;
}
static int
sockets_stresstest_wait_readable(int mode, int s, int timeout_ms)
{
switch(mode)
{
#if LWIP_SOCKET_SELECT
case TEST_MODE_SELECT:
return sockets_stresstest_wait_readable_select(s, timeout_ms);
#endif
#if LWIP_SOCKET_POLL
case TEST_MODE_POLL:
return sockets_stresstest_wait_readable_poll(s, timeout_ms);
#endif
#if LWIP_SO_RCVTIMEO
case TEST_MODE_RECVTIMEO:
return sockets_stresstest_wait_readable_recvtimeo(s, timeout_ms);
#endif
case TEST_MODE_WAIT:
return sockets_stresstest_wait_readable_wait_peek(s, timeout_ms);
case TEST_MODE_NONBLOCKING:
return sockets_stresstest_wait_readable_nonblock(s, timeout_ms);
case TEST_MODE_SLEEP:
{
sys_msleep(timeout_ms);
return 1;
}
default:
LWIP_ASSERT("invalid mode", 0);
break;
}
return 0;
}
#if LWIP_NETCONN_FULLDUPLEX
static void
sockets_stresstest_conn_client_r(void *arg)
{
struct sockets_stresstest_fullduplex *fd = (struct sockets_stresstest_fullduplex *)arg;
int s = fd->s;
size_t rxoff = 0;
char rxbuf[TEST_TXRX_BUFSIZE];
while (1) {
int closed;
if (fd->closed) {
break;
}
rxoff = recv_and_check_data_return_offset(s, rxbuf, sizeof(rxbuf), rxoff, &closed, "cli");
if (fd->closed) {
break;
}
if (closed) {
lwip_close(s);
break;
}
}
SYS_ARCH_DEC(sockets_stresstest_numthreads, 1);
LWIP_ASSERT("", sockets_stresstest_numthreads >= 0);
}
#endif
static void
sockets_stresstest_conn_client(void *arg)
{
struct sockaddr_storage addr;
struct sockaddr_in *addr_in;
int s, ret;
char txbuf[TEST_TXRX_BUFSIZE];
char rxbuf[TEST_TXRX_BUFSIZE];
size_t rxoff = 0;
u32_t max_time = sys_now() + (TEST_TIME_SECONDS * 1000);
int do_rx = 1;
struct sockets_stresstest_fullduplex *data = NULL;
memcpy(&addr, arg, sizeof(addr));
LWIP_ASSERT("", addr.ss_family == AF_INET);
addr_in = (struct sockaddr_in *)&addr;
addr_in->sin_addr.s_addr = inet_addr("127.0.0.1");
/* sleep a random time between 1 and 2 seconds */
sys_msleep(1000 + (LWIP_RAND() % 1000));
/* connect to the server */
s = lwip_socket(addr.ss_family, SOCK_STREAM, 0);
LWIP_ASSERT("s >= 0", s >= 0);
#if LWIP_NETCONN_FULLDUPLEX
if (LWIP_RAND() & 1) {
sys_thread_t t;
data = (struct sockets_stresstest_fullduplex*)mem_malloc(sizeof(struct sockets_stresstest_fullduplex));
LWIP_ASSERT("data != NULL", data != 0);
SYS_ARCH_INC(sockets_stresstest_numthreads, 1);
data->s = s;
data->closed = 0;
t = sys_thread_new("sockets_stresstest_conn_client_r", sockets_stresstest_conn_client_r, data, 0, 0);
LWIP_ASSERT("thread != NULL", t != 0);
do_rx = 0;
}
#endif
/* @todo: nonblocking connect? */
ret = lwip_connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_storage));
LWIP_ASSERT("ret == 0", ret == 0);
while (sys_now() < max_time) {
int closed;
int mode = sockets_stresstest_rand_mode(0, do_rx);
int timeout_ms = LWIP_RAND() % TEST_MAX_RXWAIT_MS;
ret = sockets_stresstest_wait_readable(mode, s, timeout_ms);
if (ret) {
if (do_rx) {
/* read some */
LWIP_ASSERT("readable", ret == TEST_SOCK_READABLE);
rxoff = recv_and_check_data_return_offset(s, rxbuf, sizeof(rxbuf), rxoff, &closed, "cli");
LWIP_ASSERT("client got closed", !closed);
}
} else {
/* timeout, send some */
size_t send_len = (LWIP_RAND() % (sizeof(txbuf) - 4)) + 4;
fill_test_data(txbuf, send_len);
LWIP_DEBUGF(TEST_SOCKETS_STRESS | LWIP_DBG_TRACE, ("cli %d tx %d\n", s, (int)send_len));
ret = lwip_write(s, txbuf, send_len);
if (ret == -1) {
/* TODO: for this to work, 'errno' has to support multithreading... */
int err = errno;
LWIP_ASSERT("err == 0", err == 0);
}
LWIP_ASSERT("ret == send_len", ret == (int)send_len);
}
}
if (data) {
data->closed = 1;
}
ret = lwip_close(s);
LWIP_ASSERT("ret == 0", ret == 0);
SYS_ARCH_DEC(sockets_stresstest_numthreads, 1);
LWIP_ASSERT("", sockets_stresstest_numthreads >= 0);
}
static void
sockets_stresstest_conn_server(void *arg)
{
int s, ret;
char txbuf[TEST_TXRX_BUFSIZE];
char rxbuf[TEST_TXRX_BUFSIZE];
size_t rxoff = 0;
s = (int)arg;
while (1) {
int closed;
int mode = sockets_stresstest_rand_mode(1, 1);
int timeout_ms = LWIP_RAND() % TEST_MAX_RXWAIT_MS;
ret = sockets_stresstest_wait_readable(mode, s, timeout_ms);
if (ret) {
if (ret & TEST_SOCK_ERR) {
/* closed? */
break;
}
/* read some */
LWIP_ASSERT("readable", ret == TEST_SOCK_READABLE);
rxoff = recv_and_check_data_return_offset(s, rxbuf, sizeof(rxbuf), rxoff, &closed, "srv");
if (closed) {
break;
}
} else {
/* timeout, send some */
size_t send_len = (LWIP_RAND() % (sizeof(txbuf) - 4)) + 4;
fill_test_data(txbuf, send_len);
LWIP_DEBUGF(TEST_SOCKETS_STRESS | LWIP_DBG_TRACE, ("srv %d tx %d\n", s, (int)send_len));
ret = lwip_write(s, txbuf, send_len);
if (ret == -1) {
/* TODO: for this to work, 'errno' has to support multithreading... */
int err = errno;
if (err == ECONNRESET) {
break;
}
if (err == ENOTCONN) {
break;
}
LWIP_ASSERT("unknown error", 0);
}
LWIP_ASSERT("ret == send_len", ret == (int)send_len);
}
}
ret = lwip_close(s);
LWIP_ASSERT("ret == 0", ret == 0);
SYS_ARCH_DEC(sockets_stresstest_numthreads, 1);
LWIP_ASSERT("", sockets_stresstest_numthreads >= 0);
}
static int
sockets_stresstest_start_clients(const struct sockaddr_storage *remote_addr)
{
/* limit the number of connections */
const int max_connections = LWIP_MIN(TEST_MAX_CONNECTIONS, MEMP_NUM_TCP_PCB/3);
int i;
for (i = 0; i < max_connections; i++) {
sys_thread_t t;
SYS_ARCH_INC(sockets_stresstest_numthreads, 1);
t = sys_thread_new("sockets_stresstest_conn_client", sockets_stresstest_conn_client, (void*)remote_addr, 0, 0);
LWIP_ASSERT("thread != NULL", t != 0);
}
return max_connections;
}
static void
sockets_stresstest_listener(void *arg)
{
int slisten;
int ret;
struct sockaddr_storage addr;
socklen_t addr_len;
struct test_settings *settings = (struct test_settings *)arg;
int num_clients, num_servers = 0;
slisten = lwip_socket(AF_INET, SOCK_STREAM, 0);
LWIP_ASSERT("slisten >= 0", slisten >= 0);
memcpy(&addr, &settings->addr, sizeof(struct sockaddr_storage));
ret = lwip_bind(slisten, (struct sockaddr *)&addr, sizeof(addr));
LWIP_ASSERT("ret == 0", ret == 0);
ret = lwip_listen(slisten, 0);
LWIP_ASSERT("ret == 0", ret == 0);
addr_len = sizeof(addr);
ret = lwip_getsockname(slisten, (struct sockaddr *)&addr, &addr_len);
LWIP_ASSERT("ret == 0", ret == 0);
num_clients = sockets_stresstest_start_clients(&addr);
while (num_servers < num_clients) {
struct sockaddr_storage aclient;
socklen_t aclient_len = sizeof(aclient);
int sclient = lwip_accept(slisten, (struct sockaddr *)&aclient, &aclient_len);
#if 1
/* using server threads */
{
sys_thread_t t;
SYS_ARCH_INC(sockets_stresstest_numthreads, 1);
num_servers++;
t = sys_thread_new("sockets_stresstest_conn_server", sockets_stresstest_conn_server, (void*)sclient, 0, 0);
LWIP_ASSERT("thread != NULL", t != 0);
}
#else
/* using server select */
#endif
}
LWIP_DEBUGF(TEST_SOCKETS_STRESS | LWIP_DBG_STATE, ("sockets_stresstest_listener: all %d connections established\n", num_clients));
/* accepted all clients */
while (sockets_stresstest_numthreads > 0) {
sys_msleep(1);
}
ret = lwip_close(slisten);
LWIP_ASSERT("ret == 0", ret == 0);
LWIP_DEBUGF(TEST_SOCKETS_STRESS |LWIP_DBG_STATE, ("sockets_stresstest_listener: done\n"));
}
static void
sockets_stresstest_listener_loop(void *arg)
{
int i;
struct test_settings *settings = (struct test_settings *)arg;
if (settings->loop_cnt) {
for (i = 0; i < settings->loop_cnt; i++) {
LWIP_DEBUGF(TEST_SOCKETS_STRESS |LWIP_DBG_STATE, ("sockets_stresstest_listener_loop: iteration %d\n", i));
sockets_stresstest_listener(arg);
sys_msleep(2);
}
LWIP_DEBUGF(TEST_SOCKETS_STRESS |LWIP_DBG_STATE, ("sockets_stresstest_listener_loop: done\n"));
} else {
for (i = 0; ; i++) {
LWIP_DEBUGF(TEST_SOCKETS_STRESS |LWIP_DBG_STATE, ("sockets_stresstest_listener_loop: iteration %d\n", i));
sockets_stresstest_listener(arg);
sys_msleep(2);
}
}
}
void
sockets_stresstest_init_loopback(int addr_family)
{
sys_thread_t t;
struct test_settings *settings = (struct test_settings *)mem_malloc(sizeof(struct test_settings));
LWIP_ASSERT("OOM", settings != NULL);
memset(settings, 0, sizeof(struct test_settings));
#if LWIP_IPV4 && LWIP_IPV6
LWIP_ASSERT("invalid addr_family", (addr_family == AF_INET) || (addr_family == AF_INET6));
#endif
settings->addr.ss_family = (sa_family_t)addr_family;
LWIP_UNUSED_ARG(addr_family);
settings->start_client = 1;
t = sys_thread_new("sockets_stresstest_listener_loop", sockets_stresstest_listener_loop, settings, 0, 0);
LWIP_ASSERT("thread != NULL", t != 0);
}
void
sockets_stresstest_init_server(int addr_family, u16_t server_port)
{
sys_thread_t t;
struct test_settings *settings = (struct test_settings *)mem_malloc(sizeof(struct test_settings));
LWIP_ASSERT("OOM", settings != NULL);
memset(settings, 0, sizeof(struct test_settings));
#if LWIP_IPV4 && LWIP_IPV6
LWIP_ASSERT("invalid addr_family", (addr_family == AF_INET) || (addr_family == AF_INET6));
settings->addr.ss_family = (sa_family_t)addr_family;
#endif
LWIP_UNUSED_ARG(addr_family);
((struct sockaddr_in *)(&settings->addr))->sin_port = server_port;
t = sys_thread_new("sockets_stresstest_listener", sockets_stresstest_listener, settings, 0, 0);
LWIP_ASSERT("thread != NULL", t != 0);
}
void
sockets_stresstest_init_client(const char *remote_ip, u16_t remote_port)
{
#if LWIP_IPV4
ip4_addr_t ip4;
#endif
#if LWIP_IPV6
ip6_addr_t ip6;
#endif
struct sockaddr_storage *addr = (struct sockaddr_storage *)mem_malloc(sizeof(struct sockaddr_storage));
LWIP_ASSERT("OOM", addr != NULL);
memset(addr, 0, sizeof(struct test_settings));
#if LWIP_IPV4
if (ip4addr_aton(remote_ip, &ip4)) {
addr->ss_family = AF_INET;
((struct sockaddr_in *)addr)->sin_addr.s_addr = ip4_addr_get_u32(&ip4);
}
#endif
#if LWIP_IPV4 && LWIP_IPV6
else
#endif
#if LWIP_IPV6
if (ip6addr_aton(remote_ip, &ip6)) {
addr->ss_family = AF_INET6;
/* todo: copy ipv6 address */
}
#endif
((struct sockaddr_in *)addr)->sin_port = remote_port;
sockets_stresstest_start_clients(addr);
}
#endif /* LWIP_SOCKET && LWIP_IPV4 */

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2017 Simon Goldschmidt
* 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: Simon Goldschmidt <goldsimon@gmx.de>
*
*/
#ifndef LWIP_HDR_TEST_SOCKETS_STRESSTEST
#define LWIP_HDR_TEST_SOCKETS_STRESSTEST
void sockets_stresstest_init_loopback(int addr_family);
void sockets_stresstest_init_server(int addr_family, u16_t server_port);
void sockets_stresstest_init_client(const char *remote_ip, u16_t remote_port);
#endif /* LWIP_HDR_TEST_SOCKETS_STRESSTEST */

View File

@ -0,0 +1,38 @@
# This file is indended to be included in end-user CMakeLists.txt
# include(/path/to/Filelists.cmake)
# It assumes the variable LWIP_DIR is defined pointing to the
# root path of lwIP sources.
#
# This file is NOT designed (on purpose) to be used as cmake
# subdir via add_subdirectory()
# The intention is to provide greater flexibility to users to
# create their own targets using the *_SRCS variables.
if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0")
include_guard(GLOBAL)
endif()
set(LWIP_TESTDIR ${LWIP_DIR}/test/unit)
set(LWIP_TESTFILES
${LWIP_TESTDIR}/lwip_unittests.c
${LWIP_TESTDIR}/api/test_sockets.c
${LWIP_TESTDIR}/arch/sys_arch.c
${LWIP_TESTDIR}/core/test_def.c
${LWIP_TESTDIR}/core/test_dns.c
${LWIP_TESTDIR}/core/test_mem.c
${LWIP_TESTDIR}/core/test_netif.c
${LWIP_TESTDIR}/core/test_pbuf.c
${LWIP_TESTDIR}/core/test_timers.c
${LWIP_TESTDIR}/dhcp/test_dhcp.c
${LWIP_TESTDIR}/etharp/test_etharp.c
${LWIP_TESTDIR}/ip4/test_ip4.c
${LWIP_TESTDIR}/ip6/test_ip6.c
${LWIP_TESTDIR}/mdns/test_mdns.c
${LWIP_TESTDIR}/mqtt/test_mqtt.c
${LWIP_TESTDIR}/tcp/tcp_helper.c
${LWIP_TESTDIR}/tcp/test_tcp_oos.c
${LWIP_TESTDIR}/tcp/test_tcp_state.c
${LWIP_TESTDIR}/tcp/test_tcp.c
${LWIP_TESTDIR}/udp/test_udp.c
${LWIP_TESTDIR}/ppp/test_pppos.c
)

View File

@ -0,0 +1,54 @@
#
# Copyright (c) 2001, 2002 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>
#
TESTDIR=$(LWIPDIR)/../test/unit
TESTFILES=$(TESTDIR)/lwip_unittests.c \
$(TESTDIR)/api/test_sockets.c \
$(TESTDIR)/arch/sys_arch.c \
$(TESTDIR)/core/test_def.c \
$(TESTDIR)/core/test_dns.c \
$(TESTDIR)/core/test_mem.c \
$(TESTDIR)/core/test_netif.c \
$(TESTDIR)/core/test_pbuf.c \
$(TESTDIR)/core/test_timers.c \
$(TESTDIR)/dhcp/test_dhcp.c \
$(TESTDIR)/etharp/test_etharp.c \
$(TESTDIR)/ip4/test_ip4.c \
$(TESTDIR)/ip6/test_ip6.c \
$(TESTDIR)/mdns/test_mdns.c \
$(TESTDIR)/mqtt/test_mqtt.c \
$(TESTDIR)/tcp/tcp_helper.c \
$(TESTDIR)/tcp/test_tcp_oos.c \
$(TESTDIR)/tcp/test_tcp_state.c \
$(TESTDIR)/tcp/test_tcp.c \
$(TESTDIR)/udp/test_udp.c \
$(TESTDIR)/ppp/test_pppos.c

View File

@ -0,0 +1,11 @@
# Shortcuts to building and running tests on unix platforms.
# Output files will be written to the directory listed below.
all:
cd ../../contrib/ports/unix/check/ && $(MAKE)
check:
cd ../../contrib/ports/unix/check/ && $(MAKE) check
clean:
cd ../../contrib/ports/unix/check/ && $(MAKE) clean

View File

@ -0,0 +1,852 @@
#include "test_sockets.h"
#include "lwip/mem.h"
#include "lwip/opt.h"
#include "lwip/sockets.h"
#include "lwip/priv/sockets_priv.h"
#include "lwip/stats.h"
#include "lwip/tcpip.h"
#include "lwip/priv/tcp_priv.h"
#include "lwip/api.h"
static int
test_sockets_get_used_count(void)
{
int used = 0;
int i;
for (i = 0; i < NUM_SOCKETS; i++) {
struct lwip_sock* s = lwip_socket_dbg_get_socket(i);
if (s != NULL) {
if (s->fd_used) {
used++;
}
}
}
return used;
}
/* Setups/teardown functions */
static void
sockets_setup(void)
{
/* expect full free heap */
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
static void
sockets_teardown(void)
{
fail_unless(test_sockets_get_used_count() == 0);
/* poll until all memory is released... */
tcpip_thread_poll_one();
while (tcp_tw_pcbs) {
tcp_abort(tcp_tw_pcbs);
tcpip_thread_poll_one();
}
tcpip_thread_poll_one();
/* ensure full free heap */
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
#ifndef NUM_SOCKETS
#define NUM_SOCKETS MEMP_NUM_NETCONN
#endif
#if LWIP_SOCKET
static int
test_sockets_alloc_socket_nonblocking(int domain, int type)
{
int s = lwip_socket(domain, type, 0);
if (s >= 0) {
int ret = lwip_fcntl(s, F_SETFL, O_NONBLOCK);
fail_unless(ret == 0);
}
return s;
}
/* Verify basic sockets functionality
*/
START_TEST(test_sockets_basics)
{
int s, i, ret;
int s2[NUM_SOCKETS];
LWIP_UNUSED_ARG(_i);
s = lwip_socket(AF_INET, SOCK_STREAM, 0);
fail_unless(s >= 0);
lwip_close(s);
for (i = 0; i < NUM_SOCKETS; i++) {
s2[i] = lwip_socket(AF_INET, SOCK_STREAM, 0);
fail_unless(s2[i] >= 0);
}
/* all sockets used, now it should fail */
s = lwip_socket(AF_INET, SOCK_STREAM, 0);
fail_unless(s == -1);
/* close one socket */
ret = lwip_close(s2[0]);
fail_unless(ret == 0);
/* now it should succeed */
s2[0] = lwip_socket(AF_INET, SOCK_STREAM, 0);
fail_unless(s2[0] >= 0);
/* close all sockets */
for (i = 0; i < NUM_SOCKETS; i++) {
ret = lwip_close(s2[i]);
fail_unless(ret == 0);
}
}
END_TEST
static void test_sockets_allfunctions_basic_domain(int domain)
{
int s, s2, s3, ret;
struct sockaddr_storage addr, addr2;
socklen_t addrlen, addr2len;
char buf[4];
/* listen socket */
s = lwip_socket(domain, SOCK_STREAM, 0);
fail_unless(s >= 0);
ret = lwip_listen(s, 0);
fail_unless(ret == 0);
addrlen = sizeof(addr);
ret = lwip_getsockname(s, (struct sockaddr*)&addr, &addrlen);
fail_unless(ret == 0);
s2 = test_sockets_alloc_socket_nonblocking(domain, SOCK_STREAM);
fail_unless(s2 >= 0);
/* nonblocking connect s2 to s (but use loopback address) */
if (domain == AF_INET) {
#if LWIP_IPV4
struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr;
addr4->sin_addr.s_addr = PP_HTONL(INADDR_LOOPBACK);
#endif
} else {
#if LWIP_IPV6
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr;
struct in6_addr lo6 = IN6ADDR_LOOPBACK_INIT;
addr6->sin6_addr = lo6;
#endif
}
ret = lwip_connect(s2, (struct sockaddr*)&addr, addrlen);
fail_unless(ret == -1);
fail_unless(errno == EINPROGRESS);
ret = lwip_connect(s2, (struct sockaddr*)&addr, addrlen);
fail_unless(ret == -1);
fail_unless(errno == EALREADY);
while(tcpip_thread_poll_one());
s3 = lwip_accept(s, (struct sockaddr*)&addr2, &addr2len);
fail_unless(s3 >= 0);
ret = lwip_connect(s2, (struct sockaddr*)&addr, addrlen);
fail_unless(ret == -1);
fail_unless(errno == EISCONN);
/* write from server to client */
ret = lwip_write(s3, "test", 4);
fail_unless(ret == 4);
ret = lwip_shutdown(s3, SHUT_WR);
fail_unless(ret == 0);
while(tcpip_thread_poll_one());
ret = lwip_recv(s2, buf, 3, MSG_PEEK);
fail_unless(ret == 3);
ret = lwip_recv(s2, buf, 3, MSG_PEEK);
fail_unless(ret == 3);
ret = lwip_read(s2, buf, 4);
fail_unless(ret == 4);
ret = lwip_read(s2, buf, 1);
fail_unless(ret == 0);
ret = lwip_read(s2, buf, 1);
fail_unless(ret == -1);
ret = lwip_write(s2, "foo", 3);
fail_unless(ret == 3);
ret = lwip_close(s2);
fail_unless(ret == 0);
while(tcpip_thread_poll_one());
/* read one byte more than available to check handling FIN */
ret = lwip_read(s3, buf, 4);
fail_unless(ret == 3);
ret = lwip_read(s3, buf, 1);
fail_unless(ret == 0);
ret = lwip_read(s3, buf, 1);
fail_unless(ret == -1);
while(tcpip_thread_poll_one());
ret = lwip_close(s);
fail_unless(ret == 0);
ret = lwip_close(s3);
fail_unless(ret == 0);
}
/* Try to step through all sockets functions once...
*/
START_TEST(test_sockets_allfunctions_basic)
{
LWIP_UNUSED_ARG(_i);
#if LWIP_IPV4
test_sockets_allfunctions_basic_domain(AF_INET);
#endif
#if LWIP_IPV6
test_sockets_allfunctions_basic_domain(AF_INET6);
#endif
}
END_TEST
static void test_sockets_init_loopback_addr(int domain, struct sockaddr_storage *addr_st, socklen_t *sz)
{
memset(addr_st, 0, sizeof(*addr_st));
switch(domain) {
#if LWIP_IPV6
case AF_INET6: {
struct sockaddr_in6 *addr = (struct sockaddr_in6*)addr_st;
struct in6_addr lo6 = IN6ADDR_LOOPBACK_INIT;
addr->sin6_family = AF_INET6;
addr->sin6_port = 0; /* use ephemeral port */
addr->sin6_addr = lo6;
*sz = sizeof(*addr);
}
break;
#endif /* LWIP_IPV6 */
#if LWIP_IPV4
case AF_INET: {
struct sockaddr_in *addr = (struct sockaddr_in*)addr_st;
addr->sin_family = AF_INET;
addr->sin_port = 0; /* use ephemeral port */
addr->sin_addr.s_addr = PP_HTONL(INADDR_LOOPBACK);
*sz = sizeof(*addr);
}
break;
#endif /* LWIP_IPV4 */
default:
*sz = 0;
fail();
break;
}
}
static void test_sockets_msgapi_update_iovs(struct msghdr *msg, size_t bytes)
{
msg_iovlen_t i;
/* note: this modifies the underlying iov_base and iov_len for a partial
read for an individual vector. This updates the msg->msg_iov pointer
to skip fully consumed vectors */
/* process fully consumed vectors */
for (i = 0; i < msg->msg_iovlen; i++) {
if (msg->msg_iov[i].iov_len <= bytes) {
/* reduce bytes by amount of this vector */
bytes -= msg->msg_iov[i].iov_len;
} else {
break; /* iov not fully consumed */
}
}
/* slide down over fully consumed vectors */
msg->msg_iov = &msg->msg_iov[i];
msg->msg_iovlen -= i;
/* update new first vector with any remaining amount */
msg->msg_iov[0].iov_base = ((u8_t *)msg->msg_iov[0].iov_base + bytes);
msg->msg_iov[0].iov_len -= bytes;
}
static void test_sockets_msgapi_tcp(int domain)
{
#define BUF_SZ (TCP_SND_BUF/4)
#define TOTAL_DATA_SZ (BUF_SZ*8) /* ~(TCP_SND_BUF*2) that accounts for integer rounding */
#define NEED_TRAILER (BUF_SZ % 4 != 0)
int listnr, s1, s2, i, ret, opt;
int bytes_written, bytes_read;
struct sockaddr_storage addr_storage;
socklen_t addr_size;
struct iovec siovs[8];
struct msghdr smsg;
u8_t * snd_buf;
struct iovec riovs[5];
struct iovec riovs_tmp[5];
struct msghdr rmsg;
u8_t * rcv_buf;
int rcv_off;
int rcv_trailer = 0;
u8_t val;
test_sockets_init_loopback_addr(domain, &addr_storage, &addr_size);
listnr = test_sockets_alloc_socket_nonblocking(domain, SOCK_STREAM);
fail_unless(listnr >= 0);
s1 = test_sockets_alloc_socket_nonblocking(domain, SOCK_STREAM);
fail_unless(s1 >= 0);
/* setup a listener socket on loopback with ephemeral port */
ret = lwip_bind(listnr, (struct sockaddr*)&addr_storage, addr_size);
fail_unless(ret == 0);
ret = lwip_listen(listnr, 0);
fail_unless(ret == 0);
/* update address with ephemeral port */
ret = lwip_getsockname(listnr, (struct sockaddr*)&addr_storage, &addr_size);
fail_unless(ret == 0);
/* connect, won't complete until we accept it */
ret = lwip_connect(s1, (struct sockaddr*)&addr_storage, addr_size);
fail_unless(ret == -1);
fail_unless(errno == EINPROGRESS);
while (tcpip_thread_poll_one());
/* accept, creating the other side of the connection */
s2 = lwip_accept(listnr, NULL, NULL);
fail_unless(s2 >= 0);
/* double check s1 is connected */
ret = lwip_connect(s1, (struct sockaddr*)&addr_storage, addr_size);
fail_unless(ret == -1);
fail_unless(errno == EISCONN);
/* set s2 to non-blocking, not inherited from listener */
opt = lwip_fcntl(s2, F_GETFL, 0);
fail_unless(opt == O_RDWR);
opt = O_NONBLOCK;
ret = lwip_fcntl(s2, F_SETFL, opt);
fail_unless(ret == 0);
/* we are done with listener, close it */
ret = lwip_close(listnr);
fail_unless(ret == 0);
/* allocate a buffer for a stream of incrementing hex (0x00..0xFF) which we will use
to create an input vector set that is larger than the TCP's send buffer. This will
force execution of the partial IO vector send case */
snd_buf = (u8_t*)mem_malloc(BUF_SZ);
val = 0x00;
fail_unless(snd_buf != NULL);
for (i = 0; i < BUF_SZ; i++,val++) {
snd_buf[i] = val;
}
/* send the buffer 8 times in one message, equating to TOTAL_DATA_SZ */
for (i = 0; i < 8; i++) {
siovs[i].iov_base = snd_buf;
siovs[i].iov_len = BUF_SZ;
}
/* allocate a receive buffer, same size as snd_buf for easy verification */
rcv_buf = (u8_t*)mem_calloc(1, BUF_SZ);
fail_unless(rcv_buf != NULL);
/* split across iovs */
for (i = 0; i < 4; i++) {
riovs[i].iov_base = &rcv_buf[i*(BUF_SZ/4)];
riovs[i].iov_len = BUF_SZ/4;
}
/* handling trailing bytes if buffer doesn't evenly divide by 4 */
#if NEED_TRAILER
if ((BUF_SZ % 4) != 0) {
riovs[5].iov_base = &rcv_buf[4*(BUF_SZ/4)];
riovs[5].iov_len = BUF_SZ - (4*(BUF_SZ/4));
rcv_trailer = 1;
}
#endif /* NEED_TRAILER */
/* we use a copy of riovs since we'll be modifying base and len during
receiving. This gives us an easy way to reset the iovs for next recvmsg */
memcpy(riovs_tmp, riovs, sizeof(riovs));
memset(&smsg, 0, sizeof(smsg));
smsg.msg_iov = siovs;
smsg.msg_iovlen = 8;
memset(&rmsg, 0, sizeof(rmsg));
rmsg.msg_iov = riovs_tmp;
rmsg.msg_iovlen = (rcv_trailer ? 5 : 4);
bytes_written = 0;
bytes_read = 0;
rcv_off = 0;
while (bytes_written < TOTAL_DATA_SZ && (bytes_read < TOTAL_DATA_SZ)) {
/* send data */
if (bytes_written < TOTAL_DATA_SZ) {
ret = lwip_sendmsg(s1, &smsg, 0);
/* note: since we always receive after sending, there will be open
space in the send buffer */
fail_unless(ret > 0);
bytes_written += ret;
if (bytes_written < TOTAL_DATA_SZ) {
test_sockets_msgapi_update_iovs(&smsg, (size_t)ret);
}
}
while (tcpip_thread_poll_one());
/* receive and verify data */
do {
if (bytes_read < TOTAL_DATA_SZ) {
ret = lwip_recvmsg(s2, &rmsg, 0);
fail_unless(ret > 0 || (ret == -1 && errno == EWOULDBLOCK));
if (ret > 0) {
rcv_off += ret;
/* we have received a full buffer */
if (rcv_off == BUF_SZ) {
/* note: since iovs are just pointers, compare underlying buf */
fail_unless(!memcmp(snd_buf, rcv_buf, BUF_SZ));
bytes_read += BUF_SZ;
/* reset receive state for next buffer */
rcv_off = 0;
memset(rcv_buf, 0, BUF_SZ);
memcpy(riovs_tmp, riovs, sizeof(riovs));
rmsg.msg_iov = riovs_tmp;
rmsg.msg_iovlen = (rcv_trailer ? 5 : 4);
} else { /* partial read */
test_sockets_msgapi_update_iovs(&rmsg, (size_t)ret);
}
}
} else {
break;
}
} while(ret > 0);
}
ret = lwip_close(s1);
fail_unless(ret == 0);
ret = lwip_close(s2);
fail_unless(ret == 0);
mem_free(snd_buf);
mem_free(rcv_buf);
}
static void test_sockets_msgapi_udp_send_recv_loop(int s, struct msghdr *smsg, struct msghdr *rmsg)
{
int i, ret;
/* send/receive our datagram of IO vectors 10 times */
for (i = 0; i < 10; i++) {
ret = lwip_sendmsg(s, smsg, 0);
fail_unless(ret == 4);
while (tcpip_thread_poll_one());
/* receive the datagram split across 4 buffers */
ret = lwip_recvmsg(s, rmsg, 0);
fail_unless(ret == 4);
/* verify data */
fail_unless(*((u8_t*)rmsg->msg_iov[0].iov_base) == 0xDE);
fail_unless(*((u8_t*)rmsg->msg_iov[1].iov_base) == 0xAD);
fail_unless(*((u8_t*)rmsg->msg_iov[2].iov_base) == 0xBE);
fail_unless(*((u8_t*)rmsg->msg_iov[3].iov_base) == 0xEF);
/* clear rcv_buf to ensure no data is being skipped */
*((u8_t*)rmsg->msg_iov[0].iov_base) = 0x00;
*((u8_t*)rmsg->msg_iov[1].iov_base) = 0x00;
*((u8_t*)rmsg->msg_iov[2].iov_base) = 0x00;
*((u8_t*)rmsg->msg_iov[3].iov_base) = 0x00;
}
}
static void test_sockets_msgapi_udp(int domain)
{
int s, i, ret;
struct sockaddr_storage addr_storage;
socklen_t addr_size;
struct iovec riovs[4];
struct msghdr rmsg;
u8_t rcv_buf[4];
struct iovec siovs[4];
struct msghdr smsg;
u8_t snd_buf[4] = {0xDE, 0xAD, 0xBE, 0xEF};
/* initialize IO vectors with data */
for (i = 0; i < 4; i++) {
siovs[i].iov_base = &snd_buf[i];
siovs[i].iov_len = sizeof(u8_t);
riovs[i].iov_base = &rcv_buf[i];
riovs[i].iov_len = sizeof(u8_t);
}
test_sockets_init_loopback_addr(domain, &addr_storage, &addr_size);
s = test_sockets_alloc_socket_nonblocking(domain, SOCK_DGRAM);
fail_unless(s >= 0);
ret = lwip_bind(s, (struct sockaddr*)&addr_storage, addr_size);
fail_unless(ret == 0);
/* Update addr with epehermal port */
ret = lwip_getsockname(s, (struct sockaddr*)&addr_storage, &addr_size);
fail_unless(ret == 0);
switch(domain) {
#if LWIP_IPV6
case AF_INET6:
fail_unless(addr_size == sizeof(struct sockaddr_in6));
break;
#endif /* LWIP_IPV6 */
#if LWIP_IPV4
case AF_INET:
fail_unless(addr_size == sizeof(struct sockaddr_in));
break;
#endif /* LWIP_IPV6 */
default:
fail();
break;
}
/* send and receive the datagram in 4 pieces */
memset(&smsg, 0, sizeof(smsg));
smsg.msg_iov = siovs;
smsg.msg_iovlen = 4;
memset(&rmsg, 0, sizeof(rmsg));
rmsg.msg_iov = riovs;
rmsg.msg_iovlen = 4;
/* perform a sendmsg with remote host (self) */
smsg.msg_name = &addr_storage;
smsg.msg_namelen = addr_size;
test_sockets_msgapi_udp_send_recv_loop(s, &smsg, &rmsg);
/* Connect to self, allowing us to not pass message name */
ret = lwip_connect(s, (struct sockaddr*)&addr_storage, addr_size);
fail_unless(ret == 0);
smsg.msg_name = NULL;
smsg.msg_namelen = 0;
test_sockets_msgapi_udp_send_recv_loop(s, &smsg, &rmsg);
ret = lwip_close(s);
fail_unless(ret == 0);
}
#if LWIP_IPV4
static void test_sockets_msgapi_cmsg(int domain)
{
int s, ret, enable;
struct sockaddr_storage addr_storage;
socklen_t addr_size;
struct iovec iov;
struct msghdr msg;
struct cmsghdr *cmsg;
struct in_pktinfo *pktinfo;
u8_t rcv_buf[4];
u8_t snd_buf[4] = {0xDE, 0xAD, 0xBE, 0xEF};
u8_t cmsg_buf[CMSG_SPACE(sizeof(struct in_pktinfo))];
test_sockets_init_loopback_addr(domain, &addr_storage, &addr_size);
s = test_sockets_alloc_socket_nonblocking(domain, SOCK_DGRAM);
fail_unless(s >= 0);
ret = lwip_bind(s, (struct sockaddr*)&addr_storage, addr_size);
fail_unless(ret == 0);
/* Update addr with epehermal port */
ret = lwip_getsockname(s, (struct sockaddr*)&addr_storage, &addr_size);
fail_unless(ret == 0);
enable = 1;
ret = lwip_setsockopt(s, IPPROTO_IP, IP_PKTINFO, &enable, sizeof(enable));
fail_unless(ret == 0);
/* Receive full message, including control message */
iov.iov_base = rcv_buf;
iov.iov_len = sizeof(rcv_buf);
msg.msg_control = cmsg_buf;
msg.msg_controllen = sizeof(cmsg_buf);
msg.msg_flags = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
memset(rcv_buf, 0, sizeof(rcv_buf));
ret = lwip_sendto(s, snd_buf, sizeof(snd_buf), 0, (struct sockaddr*)&addr_storage, addr_size);
fail_unless(ret == sizeof(snd_buf));
tcpip_thread_poll_one();
ret = lwip_recvmsg(s, &msg, 0);
fail_unless(ret == sizeof(rcv_buf));
fail_unless(!memcmp(rcv_buf, snd_buf, sizeof(rcv_buf)));
/* Verify message header */
cmsg = CMSG_FIRSTHDR(&msg);
fail_unless(cmsg != NULL);
fail_unless(cmsg->cmsg_len > 0);
fail_unless(cmsg->cmsg_level == IPPROTO_IP);
fail_unless(cmsg->cmsg_type == IP_PKTINFO);
/* Verify message data */
pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg);
/* We only have loopback interface enabled */
fail_unless(pktinfo->ipi_ifindex == 1);
fail_unless(pktinfo->ipi_addr.s_addr == PP_HTONL(INADDR_LOOPBACK));
/* Verify there are no additional messages */
cmsg = CMSG_NXTHDR(&msg, cmsg);
fail_unless(cmsg == NULL);
/* Send datagram again, testing truncation */
memset(rcv_buf, 0, sizeof(rcv_buf));
ret = lwip_sendto(s, snd_buf, sizeof(snd_buf), 0, (struct sockaddr*)&addr_storage, addr_size);
fail_unless(ret == sizeof(snd_buf));
tcpip_thread_poll_one();
msg.msg_controllen = 1;
msg.msg_flags = 0;
ret = lwip_recvmsg(s, &msg, 0);
fail_unless(ret == sizeof(rcv_buf));
fail_unless(!memcmp(rcv_buf, snd_buf, sizeof(rcv_buf)));
/* Ensure truncation was returned */
fail_unless(msg.msg_flags & MSG_CTRUNC);
/* Ensure no control messages were returned */
fail_unless(msg.msg_controllen == 0);
ret = lwip_close(s);
fail_unless(ret == 0);
}
#endif /* LWIP_IPV4 */
START_TEST(test_sockets_msgapis)
{
LWIP_UNUSED_ARG(_i);
#if LWIP_IPV4
test_sockets_msgapi_udp(AF_INET);
test_sockets_msgapi_tcp(AF_INET);
test_sockets_msgapi_cmsg(AF_INET);
#endif
#if LWIP_IPV6
test_sockets_msgapi_udp(AF_INET6);
test_sockets_msgapi_tcp(AF_INET6);
#endif
}
END_TEST
START_TEST(test_sockets_select)
{
#if LWIP_SOCKET_SELECT
int s;
int ret;
fd_set readset;
fd_set writeset;
fd_set errset;
struct timeval tv;
fail_unless(test_sockets_get_used_count() == 0);
s = lwip_socket(AF_INET, SOCK_STREAM, 0);
fail_unless(s >= 0);
fail_unless(test_sockets_get_used_count() == 0);
FD_ZERO(&readset);
FD_SET(s, &readset);
FD_ZERO(&writeset);
FD_SET(s, &writeset);
FD_ZERO(&errset);
FD_SET(s, &errset);
tv.tv_sec = tv.tv_usec = 0;
ret = lwip_select(s + 1, &readset, &writeset, &errset, &tv);
fail_unless(ret == 0);
fail_unless(test_sockets_get_used_count() == 0);
ret = lwip_close(s);
fail_unless(ret == 0);
#endif
LWIP_UNUSED_ARG(_i);
}
END_TEST
START_TEST(test_sockets_recv_after_rst)
{
int sl, sact;
int spass = -1;
int ret;
struct sockaddr_in sa_listen;
const u16_t port = 1234;
int arg;
const char txbuf[] = "something";
char rxbuf[16];
struct lwip_sock *sact_sock;
int err;
LWIP_UNUSED_ARG(_i);
fail_unless(test_sockets_get_used_count() == 0);
memset(&sa_listen, 0, sizeof(sa_listen));
sa_listen.sin_family = AF_INET;
sa_listen.sin_port = PP_HTONS(port);
sa_listen.sin_addr.s_addr = PP_HTONL(INADDR_LOOPBACK);
/* set up the listener */
sl = lwip_socket(AF_INET, SOCK_STREAM, 0);
fail_unless(sl >= 0);
fail_unless(test_sockets_get_used_count() == 0);
ret = lwip_bind(sl, (struct sockaddr *)&sa_listen, sizeof(sa_listen));
fail_unless(ret == 0);
ret = lwip_listen(sl, 0);
fail_unless(ret == 0);
/* set up the client */
sact = lwip_socket(AF_INET, SOCK_STREAM, 0);
fail_unless(sact >= 0);
fail_unless(test_sockets_get_used_count() == 0);
/* set the client to nonblocking to simplify this test */
arg = 1;
ret = lwip_ioctl(sact, FIONBIO, &arg);
fail_unless(ret == 0);
/* connect */
do {
ret = lwip_connect(sact, (struct sockaddr *)&sa_listen, sizeof(sa_listen));
err = errno;
fail_unless((ret == 0) || (ret == -1));
if (ret != 0) {
if (err == EISCONN) {
/* Although this is not valid, use EISCONN as an indicator for successful connection.
This marks us as "connect phase is done". On error, we would either have a different
errno code or "send" fails later... -> good enough for this test. */
ret = 0;
} else {
fail_unless(err == EINPROGRESS);
if (err != EINPROGRESS) {
goto cleanup;
}
/* we're in progress: little side check: test for EALREADY */
ret = lwip_connect(sact, (struct sockaddr *)&sa_listen, sizeof(sa_listen));
err = errno;
fail_unless(ret == -1);
fail_unless(err == EALREADY);
if ((ret != -1) || (err != EALREADY)) {
goto cleanup;
}
}
tcpip_thread_poll_one();
tcpip_thread_poll_one();
tcpip_thread_poll_one();
tcpip_thread_poll_one();
}
} while (ret != 0);
fail_unless(ret == 0);
/* accept the server connection part */
spass = lwip_accept(sl, NULL, NULL);
fail_unless(spass >= 0);
/* write data from client */
ret = lwip_send(sact, txbuf, sizeof(txbuf), 0);
fail_unless(ret == sizeof(txbuf));
tcpip_thread_poll_one();
tcpip_thread_poll_one();
/* issue RST (This is a HACK, don't try this in your own app!) */
sact_sock = lwip_socket_dbg_get_socket(sact);
fail_unless(sact_sock != NULL);
if (sact_sock != NULL) {
struct netconn *sact_conn = sact_sock->conn;
fail_unless(sact_conn != NULL);
if (sact_conn != NULL) {
struct tcp_pcb *pcb = sact_conn->pcb.tcp;
fail_unless(pcb != NULL);
if (pcb != NULL) {
tcp_rst(pcb, pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
pcb->local_port, pcb->remote_port);
}
}
}
tcpip_thread_poll_one();
tcpip_thread_poll_one();
/* expect to receive data first */
ret = lwip_recv(spass, rxbuf, sizeof(rxbuf), 0);
fail_unless(ret > 0);
tcpip_thread_poll_one();
tcpip_thread_poll_one();
/* expect to receive RST indication */
ret = lwip_recv(spass, rxbuf, sizeof(rxbuf), 0);
fail_unless(ret == -1);
err = errno;
fail_unless(err == ECONNRESET);
tcpip_thread_poll_one();
tcpip_thread_poll_one();
/* expect to receive ENOTCONN indication */
ret = lwip_recv(spass, rxbuf, sizeof(rxbuf), 0);
fail_unless(ret == -1);
err = errno;
fail_unless(err == ENOTCONN);
tcpip_thread_poll_one();
tcpip_thread_poll_one();
/* expect to receive ENOTCONN indication */
ret = lwip_recv(spass, rxbuf, sizeof(rxbuf), 0);
fail_unless(ret == -1);
err = errno;
fail_unless(err == ENOTCONN);
tcpip_thread_poll_one();
tcpip_thread_poll_one();
cleanup:
ret = lwip_close(sl);
fail_unless(ret == 0);
ret = lwip_close(sact);
fail_unless(ret == 0);
if (spass >= 0) {
ret = lwip_close(spass);
fail_unless(ret == 0);
}
}
END_TEST
/** Create the suite including all tests for this module */
Suite *
sockets_suite(void)
{
testfunc tests[] = {
TESTFUNC(test_sockets_basics),
TESTFUNC(test_sockets_allfunctions_basic),
TESTFUNC(test_sockets_msgapis),
TESTFUNC(test_sockets_select),
TESTFUNC(test_sockets_recv_after_rst),
};
return create_suite("SOCKETS", tests, sizeof(tests)/sizeof(testfunc), sockets_setup, sockets_teardown);
}
#else /* LWIP_SOCKET */
Suite *
sockets_suite(void)
{
return create_suite("SOCKETS", NULL, 0, NULL, NULL);
}
#endif /* LWIP_SOCKET */

View File

@ -0,0 +1,8 @@
#ifndef LWIP_HDR_TEST_SOCKETS_H
#define LWIP_HDR_TEST_SOCKETS_H
#include "../lwip_check.h"
Suite *sockets_suite(void);
#endif

View File

@ -0,0 +1,387 @@
/*
* Copyright (c) 2017 Simon Goldschmidt
* 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: Simon Goldschmidt
*
*/
#include <lwip/opt.h>
#include <lwip/arch.h>
#if !NO_SYS
#include "sys_arch.h"
#endif
#include <lwip/stats.h>
#include <lwip/debug.h>
#include <lwip/sys.h>
#include <string.h>
u32_t lwip_sys_now;
u32_t
sys_jiffies(void)
{
return lwip_sys_now;
}
u32_t
sys_now(void)
{
return lwip_sys_now;
}
void
sys_init(void)
{
}
#if !NO_SYS
test_sys_arch_waiting_fn the_waiting_fn;
void
test_sys_arch_wait_callback(test_sys_arch_waiting_fn waiting_fn)
{
the_waiting_fn = waiting_fn;
}
err_t
sys_sem_new(sys_sem_t *sem, u8_t count)
{
LWIP_ASSERT("sem != NULL", sem != NULL);
*sem = count + 1;
return ERR_OK;
}
void
sys_sem_free(sys_sem_t *sem)
{
LWIP_ASSERT("sem != NULL", sem != NULL);
*sem = 0;
}
void
sys_sem_set_invalid(sys_sem_t *sem)
{
LWIP_ASSERT("sem != NULL", sem != NULL);
*sem = 0;
}
/* semaphores are 1-based because RAM is initialized as 0, which would be valid */
u32_t
sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
{
u32_t ret = 0;
LWIP_ASSERT("sem != NULL", sem != NULL);
LWIP_ASSERT("*sem > 0", *sem > 0);
if (*sem == 1) {
/* need to wait */
if(!timeout)
{
/* wait infinite */
LWIP_ASSERT("cannot wait without waiting callback", the_waiting_fn != NULL);
do {
int expectSomething = the_waiting_fn(sem, NULL);
LWIP_ASSERT("*sem > 0", *sem > 0);
LWIP_ASSERT("expecting a semaphore count but it's 0", !expectSomething || (*sem > 1));
ret++;
if (ret == SYS_ARCH_TIMEOUT) {
ret--;
}
} while(*sem == 1);
}
else
{
if (the_waiting_fn) {
int expectSomething = the_waiting_fn(sem, NULL);
LWIP_ASSERT("expecting a semaphore count but it's 0", !expectSomething || (*sem > 1));
}
LWIP_ASSERT("*sem > 0", *sem > 0);
if (*sem == 1) {
return SYS_ARCH_TIMEOUT;
}
ret = 1;
}
}
LWIP_ASSERT("*sem > 0", *sem > 0);
(*sem)--;
LWIP_ASSERT("*sem > 0", *sem > 0);
/* return the time we waited for the sem */
return ret;
}
void
sys_sem_signal(sys_sem_t *sem)
{
LWIP_ASSERT("sem != NULL", sem != NULL);
LWIP_ASSERT("*sem > 0", *sem > 0);
(*sem)++;
LWIP_ASSERT("*sem > 0", *sem > 0);
}
err_t
sys_mutex_new(sys_mutex_t *mutex)
{
LWIP_ASSERT("mutex != NULL", mutex != NULL);
*mutex = 1; /* 1 allocated */
return ERR_OK;
}
void
sys_mutex_free(sys_mutex_t *mutex)
{
/* parameter check */
LWIP_ASSERT("mutex != NULL", mutex != NULL);
LWIP_ASSERT("*mutex >= 1", *mutex >= 1);
*mutex = 0;
}
void
sys_mutex_set_invalid(sys_mutex_t *mutex)
{
LWIP_ASSERT("mutex != NULL", mutex != NULL);
*mutex = 0;
}
void
sys_mutex_lock(sys_mutex_t *mutex)
{
/* nothing to do, no multithreading supported */
LWIP_ASSERT("mutex != NULL", mutex != NULL);
/* check that the mutext is valid and unlocked (no nested locking) */
LWIP_ASSERT("*mutex >= 1", *mutex == 1);
/* we count up just to check the correct pairing of lock/unlock */
(*mutex)++;
LWIP_ASSERT("*mutex >= 1", *mutex >= 1);
}
void
sys_mutex_unlock(sys_mutex_t *mutex)
{
/* nothing to do, no multithreading supported */
LWIP_ASSERT("mutex != NULL", mutex != NULL);
LWIP_ASSERT("*mutex >= 1", *mutex >= 1);
/* we count down just to check the correct pairing of lock/unlock */
(*mutex)--;
LWIP_ASSERT("*mutex >= 1", *mutex >= 1);
}
sys_thread_t
sys_thread_new(const char *name, lwip_thread_fn function, void *arg, int stacksize, int prio)
{
LWIP_UNUSED_ARG(name);
LWIP_UNUSED_ARG(function);
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(stacksize);
LWIP_UNUSED_ARG(prio);
/* threads not supported */
return 0;
}
err_t
sys_mbox_new(sys_mbox_t *mbox, int size)
{
int mboxsize = size;
LWIP_ASSERT("mbox != NULL", mbox != NULL);
LWIP_ASSERT("size >= 0", size >= 0);
if (size == 0) {
mboxsize = 1024;
}
mbox->head = mbox->tail = 0;
mbox->sem = mbox; /* just point to something for sys_mbox_valid() */
mbox->q_mem = (void**)malloc(sizeof(void*)*mboxsize);
mbox->size = mboxsize;
mbox->used = 0;
memset(mbox->q_mem, 0, sizeof(void*)*mboxsize);
return ERR_OK;
}
void
sys_mbox_free(sys_mbox_t *mbox)
{
/* parameter check */
LWIP_ASSERT("mbox != NULL", mbox != NULL);
LWIP_ASSERT("mbox->sem != NULL", mbox->sem != NULL);
LWIP_ASSERT("mbox->sem == mbox", mbox->sem == mbox);
LWIP_ASSERT("mbox->q_mem != NULL", mbox->q_mem != NULL);
mbox->sem = NULL;
free(mbox->q_mem);
mbox->q_mem = NULL;
}
void
sys_mbox_set_invalid(sys_mbox_t *mbox)
{
LWIP_ASSERT("mbox != NULL", mbox != NULL);
LWIP_ASSERT("mbox->q_mem == NULL", mbox->q_mem == NULL);
mbox->sem = NULL;
mbox->q_mem = NULL;
}
void
sys_mbox_post(sys_mbox_t *q, void *msg)
{
LWIP_ASSERT("q != SYS_MBOX_NULL", q != SYS_MBOX_NULL);
LWIP_ASSERT("q->sem == q", q->sem == q);
LWIP_ASSERT("q->q_mem != NULL", q->q_mem != NULL);
LWIP_ASSERT("q->used >= 0", q->used >= 0);
LWIP_ASSERT("q->size > 0", q->size > 0);
LWIP_ASSERT("mbox already full", q->used < q->size);
q->q_mem[q->head] = msg;
q->head++;
if (q->head >= (unsigned int)q->size) {
q->head = 0;
}
LWIP_ASSERT("mbox is full!", q->head != q->tail);
q->used++;
}
err_t
sys_mbox_trypost(sys_mbox_t *q, void *msg)
{
LWIP_ASSERT("q != SYS_MBOX_NULL", q != SYS_MBOX_NULL);
LWIP_ASSERT("q->sem == q", q->sem == q);
LWIP_ASSERT("q->q_mem != NULL", q->q_mem != NULL);
LWIP_ASSERT("q->used >= 0", q->used >= 0);
LWIP_ASSERT("q->size > 0", q->size > 0);
LWIP_ASSERT("q->used <= q->size", q->used <= q->size);
if (q->used == q->size) {
return ERR_MEM;
}
sys_mbox_post(q, msg);
return ERR_OK;
}
err_t
sys_mbox_trypost_fromisr(sys_mbox_t *q, void *msg)
{
return sys_mbox_trypost(q, msg);
}
u32_t
sys_arch_mbox_fetch(sys_mbox_t *q, void **msg, u32_t timeout)
{
u32_t ret = 0;
u32_t ret2;
LWIP_ASSERT("q != SYS_MBOX_NULL", q != SYS_MBOX_NULL);
LWIP_ASSERT("q->sem == q", q->sem == q);
LWIP_ASSERT("q->q_mem != NULL", q->q_mem != NULL);
LWIP_ASSERT("q->used >= 0", q->used >= 0);
LWIP_ASSERT("q->size > 0", q->size > 0);
if (q->used == 0) {
/* need to wait */
/* need to wait */
if(!timeout)
{
/* wait infinite */
LWIP_ASSERT("cannot wait without waiting callback", the_waiting_fn != NULL);
do {
int expectSomething = the_waiting_fn(NULL, q);
LWIP_ASSERT("q->used >= 0", q->used >= 0);
LWIP_ASSERT("expecting item available but it's 0", !expectSomething || (q->used > 0));
ret++;
if (ret == SYS_ARCH_TIMEOUT) {
ret--;
}
} while(q->used == 0);
}
else
{
if (the_waiting_fn) {
int expectSomething = the_waiting_fn(NULL, q);
LWIP_ASSERT("expecting item available count but it's 0", !expectSomething || (q->used > 0));
}
LWIP_ASSERT("q->used >= 0", q->used >= 0);
if (q->used == 0) {
if(msg) {
*msg = NULL;
}
return SYS_ARCH_TIMEOUT;
}
ret = 1;
}
}
LWIP_ASSERT("q->used > 0", q->used > 0);
ret2 = sys_arch_mbox_tryfetch(q, msg);
LWIP_ASSERT("got no message", ret2 == 0);
return ret;
}
u32_t
sys_arch_mbox_tryfetch(sys_mbox_t *q, void **msg)
{
LWIP_ASSERT("q != SYS_MBOX_NULL", q != SYS_MBOX_NULL);
LWIP_ASSERT("q->sem == q", q->sem == q);
LWIP_ASSERT("q->q_mem != NULL", q->q_mem != NULL);
LWIP_ASSERT("q->used >= 0", q->used >= 0);
LWIP_ASSERT("q->size > 0", q->size > 0);
if (!q->used) {
return SYS_ARCH_TIMEOUT;
}
if(msg) {
*msg = q->q_mem[q->tail];
}
q->tail++;
if (q->tail >= (unsigned int)q->size) {
q->tail = 0;
}
q->used--;
LWIP_ASSERT("q->used >= 0", q->used >= 0);
return 0;
}
#if LWIP_NETCONN_SEM_PER_THREAD
/* Simple implementation of this: unit tests only support one thread */
static sys_sem_t global_netconn_sem;
sys_sem_t* sys_arch_netconn_sem_get(void)
{
return &global_netconn_sem;
}
void sys_arch_netconn_sem_alloc(void)
{
sys_sem_new(&global_netconn_sem, 0);
}
void sys_arch_netconn_sem_free(void)
{
sys_sem_free(&global_netconn_sem);
}
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
#endif /* !NO_SYS */

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2017 Simon Goldschmidt
* 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: Simon Goldschmidt
*
*/
#ifndef LWIP_HDR_TEST_SYS_ARCH_H
#define LWIP_HDR_TEST_SYS_ARCH_H
typedef int sys_sem_t;
#define sys_sem_valid(sema) ((sema) != NULL)
typedef int sys_mutex_t;
#define sys_mutex_valid(mutex) (((mutex) != NULL)
struct lwip_mbox {
void* sem;
void** q_mem;
unsigned int head, tail;
int size;
int used;
};
typedef struct lwip_mbox sys_mbox_t;
#define SYS_MBOX_NULL NULL
#define sys_mbox_valid(mbox) ((mbox != NULL) && ((mbox)->sem != NULL) && ((mbox)->sem != (void*)-1))
#define sys_mbox_valid_val(mbox) (((mbox).sem != NULL) && ((mbox).sem != (void*)-1))
/* DWORD (thread id) is used for sys_thread_t but we won't include windows.h */
typedef u32_t sys_thread_t;
#define SYS_ARCH_DECL_PROTECT(lev)
#define SYS_ARCH_PROTECT(lev)
#define SYS_ARCH_UNPROTECT(lev)
/* to implement doing something while blocking on an mbox or semaphore:
* pass a function to test_sys_arch_wait_callback() that returns
* '0' if waiting again and
* '1' if now there should be something to do (used for asserting)
*/
typedef int (*test_sys_arch_waiting_fn)(sys_sem_t* wait_sem, sys_mbox_t* wait_mbox);
void test_sys_arch_wait_callback(test_sys_arch_waiting_fn waiting_fn);
/* current time */
extern u32_t lwip_sys_now;
sys_sem_t* sys_arch_netconn_sem_get(void);
void sys_arch_netconn_sem_alloc(void);
void sys_arch_netconn_sem_free(void);
#define LWIP_NETCONN_THREAD_SEM_GET() sys_arch_netconn_sem_get()
#define LWIP_NETCONN_THREAD_SEM_ALLOC() sys_arch_netconn_sem_alloc()
#define LWIP_NETCONN_THREAD_SEM_FREE() sys_arch_netconn_sem_free()
#endif /* LWIP_HDR_TEST_SYS_ARCH_H */

View File

@ -0,0 +1,84 @@
#include "test_def.h"
#include "lwip/def.h"
#define MAGIC_UNTOUCHED_BYTE 0x7a
#define TEST_BUFSIZE 32
#define GUARD_SIZE 4
/* Setups/teardown functions */
static void
def_setup(void)
{
}
static void
def_teardown(void)
{
}
static void
def_check_range_untouched(const char *buf, size_t len)
{
size_t i;
for (i = 0; i < len; i++) {
fail_unless(buf[i] == (char)MAGIC_UNTOUCHED_BYTE);
}
}
static void test_def_itoa(int number, const char *expected)
{
char buf[TEST_BUFSIZE];
char *test_buf = &buf[GUARD_SIZE];
size_t exp_len = strlen(expected);
fail_unless(exp_len + 4 < (TEST_BUFSIZE - (2 * GUARD_SIZE)));
memset(buf, MAGIC_UNTOUCHED_BYTE, sizeof(buf));
lwip_itoa(test_buf, exp_len + 1, number);
def_check_range_untouched(buf, GUARD_SIZE);
fail_unless(test_buf[exp_len] == 0);
fail_unless(!memcmp(test_buf, expected, exp_len));
def_check_range_untouched(&test_buf[exp_len + 1], TEST_BUFSIZE - GUARD_SIZE - exp_len - 1);
/* check with too small buffer */
memset(buf, MAGIC_UNTOUCHED_BYTE, sizeof(buf));
lwip_itoa(test_buf, exp_len, number);
def_check_range_untouched(buf, GUARD_SIZE);
def_check_range_untouched(&test_buf[exp_len + 1], TEST_BUFSIZE - GUARD_SIZE - exp_len - 1);
/* check with too large buffer */
memset(buf, MAGIC_UNTOUCHED_BYTE, sizeof(buf));
lwip_itoa(test_buf, exp_len + 4, number);
def_check_range_untouched(buf, GUARD_SIZE);
fail_unless(test_buf[exp_len] == 0);
fail_unless(!memcmp(test_buf, expected, exp_len));
def_check_range_untouched(&test_buf[exp_len + 4], TEST_BUFSIZE - GUARD_SIZE - exp_len - 4);
}
START_TEST(test_def_lwip_itoa)
{
LWIP_UNUSED_ARG(_i);
test_def_itoa(0, "0");
test_def_itoa(1, "1");
test_def_itoa(-1, "-1");
test_def_itoa(15, "15");
test_def_itoa(-15, "-15");
test_def_itoa(156, "156");
test_def_itoa(1192, "1192");
test_def_itoa(-156, "-156");
}
END_TEST
/** Create the suite including all tests for this module */
Suite *
def_suite(void)
{
testfunc tests[] = {
TESTFUNC(test_def_lwip_itoa)
};
return create_suite("DEF", tests, sizeof(tests)/sizeof(testfunc), def_setup, def_teardown);
}

View File

@ -0,0 +1,8 @@
#ifndef LWIP_HDR_TEST_DEF_H
#define LWIP_HDR_TEST_DEF_H
#include "../lwip_check.h"
Suite *def_suite(void);
#endif

View File

@ -0,0 +1,52 @@
#include "test_dns.h"
#include "lwip/dns.h"
/* Setups/teardown functions */
static void
dns_setup(void)
{
}
static void
dns_teardown(void)
{
}
/* Test functions */
START_TEST(test_dns_set_get_server)
{
int n;
LWIP_UNUSED_ARG(_i);
for (n = 0; n < 256; n++) {
u8_t i = (u8_t)n;
ip_addr_t server;
/* Should return a zeroed address for any index */
fail_unless(dns_getserver(i));
fail_unless(ip_addr_isany(dns_getserver(i)));
/* Should accept setting address for any index, and ignore if out of range */
IP_ADDR4(&server, 10, 0, 0, i);
dns_setserver(i, &server);
fail_unless(dns_getserver(i));
if (i < DNS_MAX_SERVERS) {
fail_unless(ip_addr_eq(dns_getserver(i), &server) == 1);
} else {
fail_unless(ip_addr_isany(dns_getserver(i)));
}
}
}
END_TEST
/** Create the suite including all tests for this module */
Suite *
dns_suite(void)
{
testfunc tests[] = {
TESTFUNC(test_dns_set_get_server)
};
return create_suite("DNS", tests, sizeof(tests)/sizeof(testfunc), dns_setup, dns_teardown);
}

View File

@ -0,0 +1,8 @@
#ifndef LWIP_HDR_TEST_DNS_H
#define LWIP_HDR_TEST_DNS_H
#include "../lwip_check.h"
Suite *dns_suite(void);
#endif

View File

@ -0,0 +1,221 @@
#include "test_mem.h"
#include "lwip/mem.h"
#include "lwip/stats.h"
#if !LWIP_STATS || !MEM_STATS
#error "This tests needs MEM-statistics enabled"
#endif
/* Setups/teardown functions */
static void
mem_setup(void)
{
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
static void
mem_teardown(void)
{
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
/* Test functions */
/** Call mem_malloc, mem_free and mem_trim and check stats */
START_TEST(test_mem_one)
{
#define SIZE1 16
#define SIZE1_2 12
#define SIZE2 16
void *p1, *p2;
mem_size_t s1, s2;
LWIP_UNUSED_ARG(_i);
fail_unless(lwip_stats.mem.used == 0);
p1 = mem_malloc(SIZE1);
fail_unless(p1 != NULL);
fail_unless(lwip_stats.mem.used >= SIZE1);
s1 = lwip_stats.mem.used;
p2 = mem_malloc(SIZE2);
fail_unless(p2 != NULL);
fail_unless(lwip_stats.mem.used >= SIZE2 + s1);
s2 = lwip_stats.mem.used;
mem_trim(p1, SIZE1_2);
mem_free(p2);
fail_unless(lwip_stats.mem.used <= s2 - SIZE2);
mem_free(p1);
fail_unless(lwip_stats.mem.used == 0);
}
END_TEST
static void malloc_keep_x(int x, int num, int size, int freestep)
{
int i;
void* p[16];
LWIP_ASSERT("invalid size", size >= 0 && size < (mem_size_t)-1);
memset(p, 0, sizeof(p));
for(i = 0; i < num && i < 16; i++) {
p[i] = mem_malloc((mem_size_t)size);
fail_unless(p[i] != NULL);
}
for(i = 0; i < num && i < 16; i += freestep) {
if (i == x) {
continue;
}
mem_free(p[i]);
p[i] = NULL;
}
for(i = 0; i < num && i < 16; i++) {
if (i == x) {
continue;
}
if (p[i] != NULL) {
mem_free(p[i]);
p[i] = NULL;
}
}
fail_unless(p[x] != NULL);
mem_free(p[x]);
}
START_TEST(test_mem_random)
{
const int num = 16;
int x;
int size;
int freestep;
LWIP_UNUSED_ARG(_i);
fail_unless(lwip_stats.mem.used == 0);
for (x = 0; x < num; x++) {
for (size = 1; size < 32; size++) {
for (freestep = 1; freestep <= 3; freestep++) {
fail_unless(lwip_stats.mem.used == 0);
malloc_keep_x(x, num, size, freestep);
fail_unless(lwip_stats.mem.used == 0);
}
}
}
}
END_TEST
START_TEST(test_mem_invalid_free)
{
u8_t *ptr, *ptr_low, *ptr_high;
LWIP_UNUSED_ARG(_i);
fail_unless(lwip_stats.mem.used == 0);
fail_unless(lwip_stats.mem.illegal == 0);
ptr = (u8_t *)mem_malloc(1);
fail_unless(ptr != NULL);
fail_unless(lwip_stats.mem.used != 0);
ptr_low = ptr - 0x10;
mem_free(ptr_low);
fail_unless(lwip_stats.mem.illegal == 1);
lwip_stats.mem.illegal = 0;
ptr_high = ptr + (MEM_SIZE * 2);
mem_free(ptr_high);
fail_unless(lwip_stats.mem.illegal == 1);
lwip_stats.mem.illegal = 0;
mem_free(ptr);
fail_unless(lwip_stats.mem.illegal == 0);
fail_unless(lwip_stats.mem.used == 0);
}
END_TEST
START_TEST(test_mem_double_free)
{
u8_t *ptr1b, *ptr1, *ptr2, *ptr3;
LWIP_UNUSED_ARG(_i);
fail_unless(lwip_stats.mem.used == 0);
fail_unless(lwip_stats.mem.illegal == 0);
ptr1 = (u8_t *)mem_malloc(1);
fail_unless(ptr1 != NULL);
fail_unless(lwip_stats.mem.used != 0);
ptr2 = (u8_t *)mem_malloc(1);
fail_unless(ptr2 != NULL);
fail_unless(lwip_stats.mem.used != 0);
ptr3 = (u8_t *)mem_malloc(1);
fail_unless(ptr3 != NULL);
fail_unless(lwip_stats.mem.used != 0);
/* free the middle mem */
mem_free(ptr2);
fail_unless(lwip_stats.mem.illegal == 0);
/* double-free of middle mem: should fail */
mem_free(ptr2);
fail_unless(lwip_stats.mem.illegal == 1);
lwip_stats.mem.illegal = 0;
/* free upper memory and try again */
mem_free(ptr3);
fail_unless(lwip_stats.mem.illegal == 0);
mem_free(ptr2);
fail_unless(lwip_stats.mem.illegal == 1);
lwip_stats.mem.illegal = 0;
/* free lower memory and try again */
mem_free(ptr1);
fail_unless(lwip_stats.mem.illegal == 0);
fail_unless(lwip_stats.mem.used == 0);
mem_free(ptr2);
fail_unless(lwip_stats.mem.illegal == 1);
fail_unless(lwip_stats.mem.used == 0);
lwip_stats.mem.illegal = 0;
/* reallocate lowest memory, now overlapping already freed ptr2 */
#ifndef MIN_SIZE
#define MIN_SIZE 12
#endif
ptr1b = (u8_t *)mem_malloc(MIN_SIZE * 2);
fail_unless(ptr1b != NULL);
fail_unless(lwip_stats.mem.used != 0);
mem_free(ptr2);
fail_unless(lwip_stats.mem.illegal == 1);
lwip_stats.mem.illegal = 0;
memset(ptr1b, 1, MIN_SIZE * 2);
mem_free(ptr2);
fail_unless(lwip_stats.mem.illegal == 1);
lwip_stats.mem.illegal = 0;
mem_free(ptr1b);
fail_unless(lwip_stats.mem.illegal == 0);
fail_unless(lwip_stats.mem.used == 0);
}
END_TEST
/** Create the suite including all tests for this module */
Suite *
mem_suite(void)
{
testfunc tests[] = {
TESTFUNC(test_mem_one),
TESTFUNC(test_mem_random),
TESTFUNC(test_mem_invalid_free),
TESTFUNC(test_mem_double_free)
};
return create_suite("MEM", tests, sizeof(tests)/sizeof(testfunc), mem_setup, mem_teardown);
}

View File

@ -0,0 +1,8 @@
#ifndef LWIP_HDR_TEST_MEM_H
#define LWIP_HDR_TEST_MEM_H
#include "../lwip_check.h"
Suite *mem_suite(void);
#endif

View File

@ -0,0 +1,285 @@
#include "test_netif.h"
#include "lwip/netif.h"
#include "lwip/stats.h"
#include "lwip/etharp.h"
#include "netif/ethernet.h"
#if !LWIP_NETIF_EXT_STATUS_CALLBACK
#error "This tests needs LWIP_NETIF_EXT_STATUS_CALLBACK enabled"
#endif
static struct netif net_test;
/* Setups/teardown functions */
static void
netif_setup(void)
{
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
static void
netif_teardown(void)
{
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
/* test helper functions */
static err_t
testif_tx_func(struct netif *netif, struct pbuf *p)
{
LWIP_UNUSED_ARG(netif);
LWIP_UNUSED_ARG(p);
return ERR_OK;
}
static err_t
testif_init(struct netif *netif)
{
netif->name[0] = 'c';
netif->name[1] = 'h';
netif->output = etharp_output;
netif->linkoutput = testif_tx_func;
netif->mtu = 1500;
netif->hwaddr_len = 6;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_IGMP | NETIF_FLAG_MLD6;
netif->hwaddr[0] = 0x02;
netif->hwaddr[1] = 0x03;
netif->hwaddr[2] = 0x04;
netif->hwaddr[3] = 0x05;
netif->hwaddr[4] = 0x06;
netif->hwaddr[5] = 0x07;
return ERR_OK;
}
#define MAX_NSC_REASON_IDX 10
static netif_nsc_reason_t expected_reasons;
static int callback_ctr;
static int dummy_active;
static void
test_netif_ext_callback_dummy(struct netif* netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t* args)
{
LWIP_UNUSED_ARG(netif);
LWIP_UNUSED_ARG(reason);
LWIP_UNUSED_ARG(args);
fail_unless(dummy_active);
}
static void
test_netif_ext_callback(struct netif* netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t* args)
{
LWIP_UNUSED_ARG(args); /* @todo */
callback_ctr++;
fail_unless(netif == &net_test);
fail_unless(expected_reasons == reason);
}
/* Test functions */
NETIF_DECLARE_EXT_CALLBACK(netif_callback_1)
NETIF_DECLARE_EXT_CALLBACK(netif_callback_2)
NETIF_DECLARE_EXT_CALLBACK(netif_callback_3)
START_TEST(test_netif_extcallbacks)
{
ip4_addr_t addr;
ip4_addr_t netmask;
ip4_addr_t gw;
LWIP_UNUSED_ARG(_i);
IP4_ADDR(&addr, 0, 0, 0, 0);
IP4_ADDR(&netmask, 0, 0, 0, 0);
IP4_ADDR(&gw, 0, 0, 0, 0);
netif_add_ext_callback(&netif_callback_3, test_netif_ext_callback_dummy);
netif_add_ext_callback(&netif_callback_2, test_netif_ext_callback);
netif_add_ext_callback(&netif_callback_1, test_netif_ext_callback_dummy);
dummy_active = 1;
/* positive tests: check that single events come as expected */
expected_reasons = LWIP_NSC_NETIF_ADDED;
callback_ctr = 0;
netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input);
fail_unless(callback_ctr == 1);
expected_reasons = LWIP_NSC_LINK_CHANGED;
callback_ctr = 0;
netif_set_link_up(&net_test);
fail_unless(callback_ctr == 1);
expected_reasons = LWIP_NSC_STATUS_CHANGED;
callback_ctr = 0;
netif_set_up(&net_test);
fail_unless(callback_ctr == 1);
IP4_ADDR(&addr, 1, 2, 3, 4);
expected_reasons = LWIP_NSC_IPV4_ADDRESS_CHANGED;
callback_ctr = 0;
netif_set_ipaddr(&net_test, &addr);
fail_unless(callback_ctr == 1);
IP4_ADDR(&netmask, 255, 255, 255, 0);
expected_reasons = LWIP_NSC_IPV4_NETMASK_CHANGED;
callback_ctr = 0;
netif_set_netmask(&net_test, &netmask);
fail_unless(callback_ctr == 1);
IP4_ADDR(&gw, 1, 2, 3, 254);
expected_reasons = LWIP_NSC_IPV4_GATEWAY_CHANGED;
callback_ctr = 0;
netif_set_gw(&net_test, &gw);
fail_unless(callback_ctr == 1);
IP4_ADDR(&addr, 0, 0, 0, 0);
expected_reasons = LWIP_NSC_IPV4_ADDRESS_CHANGED;
callback_ctr = 0;
netif_set_ipaddr(&net_test, &addr);
fail_unless(callback_ctr == 1);
IP4_ADDR(&netmask, 0, 0, 0, 0);
expected_reasons = LWIP_NSC_IPV4_NETMASK_CHANGED;
callback_ctr = 0;
netif_set_netmask(&net_test, &netmask);
fail_unless(callback_ctr == 1);
IP4_ADDR(&gw, 0, 0, 0, 0);
expected_reasons = LWIP_NSC_IPV4_GATEWAY_CHANGED;
callback_ctr = 0;
netif_set_gw(&net_test, &gw);
fail_unless(callback_ctr == 1);
/* check for multi-events (only one combined callback expected) */
IP4_ADDR(&addr, 1, 2, 3, 4);
IP4_ADDR(&netmask, 255, 255, 255, 0);
IP4_ADDR(&gw, 1, 2, 3, 254);
expected_reasons = (netif_nsc_reason_t)(LWIP_NSC_IPV4_ADDRESS_CHANGED | LWIP_NSC_IPV4_NETMASK_CHANGED |
LWIP_NSC_IPV4_GATEWAY_CHANGED | LWIP_NSC_IPV4_SETTINGS_CHANGED |
LWIP_NSC_IPV4_ADDR_VALID);
callback_ctr = 0;
netif_set_addr(&net_test, &addr, &netmask, &gw);
fail_unless(callback_ctr == 1);
/* check that for no-change, no callback is expected */
expected_reasons = LWIP_NSC_NONE;
callback_ctr = 0;
netif_set_ipaddr(&net_test, &addr);
fail_unless(callback_ctr == 0);
netif_set_netmask(&net_test, &netmask);
callback_ctr = 0;
fail_unless(callback_ctr == 0);
callback_ctr = 0;
netif_set_gw(&net_test, &gw);
fail_unless(callback_ctr == 0);
/* netif_set_addr() always issues at least LWIP_NSC_IPV4_ADDR_VALID */
expected_reasons = LWIP_NSC_IPV4_ADDR_VALID;
callback_ctr = 0;
netif_set_addr(&net_test, &addr, &netmask, &gw);
fail_unless(callback_ctr == 1);
/* check for single-events */
IP4_ADDR(&addr, 1, 2, 3, 5);
expected_reasons = (netif_nsc_reason_t)(LWIP_NSC_IPV4_ADDRESS_CHANGED | LWIP_NSC_IPV4_SETTINGS_CHANGED |
LWIP_NSC_IPV4_ADDR_VALID);
callback_ctr = 0;
netif_set_addr(&net_test, &addr, &netmask, &gw);
fail_unless(callback_ctr == 1);
expected_reasons = LWIP_NSC_STATUS_CHANGED;
callback_ctr = 0;
netif_set_down(&net_test);
fail_unless(callback_ctr == 1);
expected_reasons = LWIP_NSC_NETIF_REMOVED;
callback_ctr = 0;
netif_remove(&net_test);
fail_unless(callback_ctr == 1);
expected_reasons = LWIP_NSC_NONE;
netif_remove_ext_callback(&netif_callback_2);
netif_remove_ext_callback(&netif_callback_3);
netif_remove_ext_callback(&netif_callback_1);
dummy_active = 0;
}
END_TEST
START_TEST(test_netif_flag_set)
{
ip4_addr_t addr;
ip4_addr_t netmask;
ip4_addr_t gw;
LWIP_UNUSED_ARG(_i);
IP4_ADDR(&addr, 0, 0, 0, 0);
IP4_ADDR(&netmask, 0, 0, 0, 0);
IP4_ADDR(&gw, 0, 0, 0, 0);
netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input);
fail_if(netif_is_flag_set(&net_test, NETIF_FLAG_UP));
fail_unless(netif_is_flag_set(&net_test, NETIF_FLAG_BROADCAST));
fail_if(netif_is_flag_set(&net_test, NETIF_FLAG_LINK_UP));
fail_unless(netif_is_flag_set(&net_test, NETIF_FLAG_ETHARP));
fail_unless(netif_is_flag_set(&net_test, NETIF_FLAG_ETHERNET));
fail_unless(netif_is_flag_set(&net_test, NETIF_FLAG_IGMP));
fail_unless(netif_is_flag_set(&net_test, NETIF_FLAG_MLD6));
netif_remove(&net_test);
}
END_TEST
START_TEST(test_netif_find)
{
struct netif net0;
struct netif net1;
LWIP_UNUSED_ARG(_i);
/* No netifs available */
fail_unless(netif_find("ch0") == NULL);
/* Add netifs with known names */
fail_unless(netif_add_noaddr(&net0, NULL, testif_init, ethernet_input) == &net0);
net0.num = 0;
fail_unless(netif_add_noaddr(&net1, NULL, testif_init, ethernet_input) == &net1);
net1.num = 1;
fail_unless(netif_find("ch0") == &net0);
fail_unless(netif_find("CH0") == NULL);
fail_unless(netif_find("ch1") == &net1);
fail_unless(netif_find("ch3") == NULL);
/* atoi failure is not treated as zero */
fail_unless(netif_find("chX") == NULL);
fail_unless(netif_find("ab0") == NULL);
netif_remove(&net0);
netif_remove(&net1);
}
END_TEST
/** Create the suite including all tests for this module */
Suite *
netif_suite(void)
{
testfunc tests[] = {
TESTFUNC(test_netif_extcallbacks),
TESTFUNC(test_netif_flag_set),
TESTFUNC(test_netif_find)
};
return create_suite("NETIF", tests, sizeof(tests)/sizeof(testfunc), netif_setup, netif_teardown);
}

View File

@ -0,0 +1,8 @@
#ifndef LWIP_HDR_TEST_NETIF_H
#define LWIP_HDR_TEST_NETIF_H
#include "../lwip_check.h"
Suite *netif_suite(void);
#endif

View File

@ -0,0 +1,371 @@
#include "test_pbuf.h"
#include "lwip/pbuf.h"
#include "lwip/stats.h"
#if !LWIP_STATS || !MEM_STATS ||!MEMP_STATS
#error "This tests needs MEM- and MEMP-statistics enabled"
#endif
#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || !LWIP_WND_SCALE
#error "This test needs TCP OOSEQ queueing and window scaling enabled"
#endif
/* Setups/teardown functions */
static void
pbuf_setup(void)
{
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
static void
pbuf_teardown(void)
{
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
#define TESTBUFSIZE_1 65535
#define TESTBUFSIZE_2 65530
#define TESTBUFSIZE_3 50050
static u8_t testbuf_1[TESTBUFSIZE_1];
static u8_t testbuf_1a[TESTBUFSIZE_1];
static u8_t testbuf_2[TESTBUFSIZE_2];
static u8_t testbuf_2a[TESTBUFSIZE_2];
static u8_t testbuf_3[TESTBUFSIZE_3];
static u8_t testbuf_3a[TESTBUFSIZE_3];
/* Test functions */
START_TEST(test_pbuf_alloc_zero_pbufs)
{
struct pbuf *p;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, 0, PBUF_ROM);
fail_unless(p != NULL);
if (p != NULL) {
pbuf_free(p);
}
p = pbuf_alloc(PBUF_RAW, 0, PBUF_RAM);
fail_unless(p != NULL);
if (p != NULL) {
pbuf_free(p);
}
p = pbuf_alloc(PBUF_RAW, 0, PBUF_REF);
fail_unless(p != NULL);
if (p != NULL) {
pbuf_free(p);
}
p = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL);
fail_unless(p != NULL);
if (p != NULL) {
pbuf_free(p);
}
}
END_TEST
/** Call pbuf_copy on a pbuf with zero length */
START_TEST(test_pbuf_copy_zero_pbuf)
{
struct pbuf *p1, *p2, *p3;
err_t err;
LWIP_UNUSED_ARG(_i);
p1 = pbuf_alloc(PBUF_RAW, 1024, PBUF_RAM);
fail_unless(p1 != NULL);
fail_unless(p1->ref == 1);
p2 = pbuf_alloc(PBUF_RAW, 2, PBUF_POOL);
fail_unless(p2 != NULL);
fail_unless(p2->ref == 1);
p2->len = p2->tot_len = 0;
pbuf_cat(p1, p2);
fail_unless(p1->ref == 1);
fail_unless(p2->ref == 1);
p3 = pbuf_alloc(PBUF_RAW, p1->tot_len, PBUF_POOL);
fail_unless(p3 != NULL);
err = pbuf_copy(p3, p1);
fail_unless(err == ERR_VAL);
pbuf_free(p1);
pbuf_free(p3);
}
END_TEST
/** Call pbuf_copy on pbufs with chains of different sizes */
START_TEST(test_pbuf_copy_unmatched_chains)
{
uint16_t i, j;
err_t err;
struct pbuf *source, *dest, *p;
LWIP_UNUSED_ARG(_i);
source = NULL;
/* Build source pbuf from linked 16 byte parts,
* with payload bytes containing their offset */
for (i = 0; i < 8; i++) {
p = pbuf_alloc(PBUF_RAW, 16, PBUF_RAM);
fail_unless(p != NULL);
for (j = 0; j < p->len; j++) {
((u8_t*)p->payload)[j] = (u8_t)((i << 4) | j);
}
if (source) {
pbuf_cat(source, p);
} else {
source = p;
}
}
for (i = 0; i < source->tot_len; i++) {
fail_unless(pbuf_get_at(source, i) == i);
}
/* Build dest pbuf from other lengths */
dest = pbuf_alloc(PBUF_RAW, 35, PBUF_RAM);
fail_unless(dest != NULL);
p = pbuf_alloc(PBUF_RAW, 81, PBUF_RAM);
fail_unless(p != NULL);
pbuf_cat(dest, p);
p = pbuf_alloc(PBUF_RAW, 27, PBUF_RAM);
fail_unless(p != NULL);
pbuf_cat(dest, p);
/* Copy contents and verify data */
err = pbuf_copy(dest, source);
fail_unless(err == ERR_OK);
for (i = 0; i < source->tot_len; i++) {
fail_unless(pbuf_get_at(dest, i) == i);
}
pbuf_free(source);
pbuf_free(dest);
}
END_TEST
START_TEST(test_pbuf_copy_partial_pbuf)
{
struct pbuf *a, *b, *dest;
char lwip[] = "lwip ";
char packet[] = "packet";
err_t err;
LWIP_UNUSED_ARG(_i);
a = pbuf_alloc(PBUF_RAW, 5, PBUF_REF);
fail_unless(a != NULL);
a->payload = lwip;
b = pbuf_alloc(PBUF_RAW, 7, PBUF_REF);
fail_unless(b != NULL);
b->payload = packet;
pbuf_cat(a, b);
dest = pbuf_alloc(PBUF_RAW, 14, PBUF_RAM);
fail_unless(dest != NULL);
memset(dest->payload, 0, dest->len);
/* Don't copy if data will not fit */
err = pbuf_copy_partial_pbuf(dest, a, a->tot_len, 4);
fail_unless(err == ERR_ARG);
/* Don't copy if length is longer than source */
err = pbuf_copy_partial_pbuf(dest, a, a->tot_len + 1, 0);
fail_unless(err == ERR_ARG);
/* Normal copy */
err = pbuf_copy_partial_pbuf(dest, a, a->tot_len, 0);
fail_unless(err == ERR_OK);
fail_unless(strcmp("lwip packet", (char*)dest->payload) == 0);
/* Copy at offset */
err = pbuf_copy_partial_pbuf(dest, a, a->tot_len, 1);
fail_unless(err == ERR_OK);
fail_unless(strcmp("llwip packet", (char*)dest->payload) == 0);
/* Copy at offset with shorter length */
err = pbuf_copy_partial_pbuf(dest, a, 6, 6);
fail_unless(err == ERR_OK);
fail_unless(strcmp("llwip lwip p", (char*)dest->payload) == 0);
/* Copy with shorter length */
err = pbuf_copy_partial_pbuf(dest, a, 5, 0);
fail_unless(err == ERR_OK);
fail_unless(strcmp("lwip lwip p", (char*)dest->payload) == 0);
pbuf_free(dest);
pbuf_free(a);
}
END_TEST
START_TEST(test_pbuf_split_64k_on_small_pbufs)
{
struct pbuf *p, *rest=NULL;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, 1, PBUF_POOL);
fail_unless(p != NULL);
pbuf_split_64k(p, &rest);
fail_unless(p->tot_len == 1);
pbuf_free(p);
}
END_TEST
START_TEST(test_pbuf_queueing_bigger_than_64k)
{
int i;
err_t err;
struct pbuf *p1, *p2, *p3, *rest2=NULL, *rest3=NULL;
LWIP_UNUSED_ARG(_i);
for(i = 0; i < TESTBUFSIZE_1; i++) {
testbuf_1[i] = (u8_t)rand();
}
for(i = 0; i < TESTBUFSIZE_2; i++) {
testbuf_2[i] = (u8_t)rand();
}
for(i = 0; i < TESTBUFSIZE_3; i++) {
testbuf_3[i] = (u8_t)rand();
}
p1 = pbuf_alloc(PBUF_RAW, TESTBUFSIZE_1, PBUF_POOL);
fail_unless(p1 != NULL);
p2 = pbuf_alloc(PBUF_RAW, TESTBUFSIZE_2, PBUF_POOL);
fail_unless(p2 != NULL);
p3 = pbuf_alloc(PBUF_RAW, TESTBUFSIZE_3, PBUF_POOL);
fail_unless(p3 != NULL);
err = pbuf_take(p1, testbuf_1, TESTBUFSIZE_1);
fail_unless(err == ERR_OK);
err = pbuf_take(p2, testbuf_2, TESTBUFSIZE_2);
fail_unless(err == ERR_OK);
err = pbuf_take(p3, testbuf_3, TESTBUFSIZE_3);
fail_unless(err == ERR_OK);
pbuf_cat(p1, p2);
pbuf_cat(p1, p3);
pbuf_split_64k(p1, &rest2);
fail_unless(p1->tot_len == TESTBUFSIZE_1);
fail_unless(rest2->tot_len == (u16_t)((TESTBUFSIZE_2+TESTBUFSIZE_3) & 0xFFFF));
pbuf_split_64k(rest2, &rest3);
fail_unless(rest2->tot_len == TESTBUFSIZE_2);
fail_unless(rest3->tot_len == TESTBUFSIZE_3);
pbuf_copy_partial(p1, testbuf_1a, TESTBUFSIZE_1, 0);
pbuf_copy_partial(rest2, testbuf_2a, TESTBUFSIZE_2, 0);
pbuf_copy_partial(rest3, testbuf_3a, TESTBUFSIZE_3, 0);
fail_if(memcmp(testbuf_1, testbuf_1a, TESTBUFSIZE_1));
fail_if(memcmp(testbuf_2, testbuf_2a, TESTBUFSIZE_2));
fail_if(memcmp(testbuf_3, testbuf_3a, TESTBUFSIZE_3));
pbuf_free(p1);
pbuf_free(rest2);
pbuf_free(rest3);
}
END_TEST
/* Test for bug that writing with pbuf_take_at() did nothing
* and returned ERR_OK when writing at beginning of a pbuf
* in the chain.
*/
START_TEST(test_pbuf_take_at_edge)
{
err_t res;
u8_t *out;
int i;
u8_t testdata[] = { 0x01, 0x08, 0x82, 0x02 };
struct pbuf *p;
struct pbuf *q;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, 1024, PBUF_POOL);
fail_unless(p != NULL);
q = p->next;
/* alloc big enough to get a chain of pbufs */
fail_if(p->tot_len == p->len);
memset(p->payload, 0, p->len);
memset(q->payload, 0, q->len);
/* copy data to the beginning of first pbuf */
res = pbuf_take_at(p, &testdata, sizeof(testdata), 0);
fail_unless(res == ERR_OK);
out = (u8_t*)p->payload;
for (i = 0; i < (int)sizeof(testdata); i++) {
fail_unless(out[i] == testdata[i],
"Bad data at pos %d, was %02X, expected %02X", i, out[i], testdata[i]);
}
/* copy data to the just before end of first pbuf */
res = pbuf_take_at(p, &testdata, sizeof(testdata), p->len - 1);
fail_unless(res == ERR_OK);
out = (u8_t*)p->payload;
fail_unless(out[p->len - 1] == testdata[0],
"Bad data at pos %d, was %02X, expected %02X", p->len - 1, out[p->len - 1], testdata[0]);
out = (u8_t*)q->payload;
for (i = 1; i < (int)sizeof(testdata); i++) {
fail_unless(out[i-1] == testdata[i],
"Bad data at pos %d, was %02X, expected %02X", p->len - 1 + i, out[i-1], testdata[i]);
}
/* copy data to the beginning of second pbuf */
res = pbuf_take_at(p, &testdata, sizeof(testdata), p->len);
fail_unless(res == ERR_OK);
out = (u8_t*)p->payload;
for (i = 0; i < (int)sizeof(testdata); i++) {
fail_unless(out[i] == testdata[i],
"Bad data at pos %d, was %02X, expected %02X", p->len+i, out[i], testdata[i]);
}
pbuf_free(p);
}
END_TEST
/* Verify pbuf_put_at()/pbuf_get_at() when using
* offsets equal to beginning of new pbuf in chain
*/
START_TEST(test_pbuf_get_put_at_edge)
{
u8_t *out;
u8_t testdata = 0x01;
u8_t getdata;
struct pbuf *p;
struct pbuf *q;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, 1024, PBUF_POOL);
fail_unless(p != NULL);
q = p->next;
/* alloc big enough to get a chain of pbufs */
fail_if(p->tot_len == p->len);
memset(p->payload, 0, p->len);
memset(q->payload, 0, q->len);
/* put byte at the beginning of second pbuf */
pbuf_put_at(p, p->len, testdata);
out = (u8_t*)q->payload;
fail_unless(*out == testdata,
"Bad data at pos %d, was %02X, expected %02X", p->len, *out, testdata);
getdata = pbuf_get_at(p, p->len);
fail_unless(*out == getdata,
"pbuf_get_at() returned bad data at pos %d, was %02X, expected %02X", p->len, getdata, *out);
pbuf_free(p);
}
END_TEST
/** Create the suite including all tests for this module */
Suite *
pbuf_suite(void)
{
testfunc tests[] = {
TESTFUNC(test_pbuf_alloc_zero_pbufs),
TESTFUNC(test_pbuf_copy_zero_pbuf),
TESTFUNC(test_pbuf_copy_unmatched_chains),
TESTFUNC(test_pbuf_copy_partial_pbuf),
TESTFUNC(test_pbuf_split_64k_on_small_pbufs),
TESTFUNC(test_pbuf_queueing_bigger_than_64k),
TESTFUNC(test_pbuf_take_at_edge),
TESTFUNC(test_pbuf_get_put_at_edge)
};
return create_suite("PBUF", tests, sizeof(tests)/sizeof(testfunc), pbuf_setup, pbuf_teardown);
}

View File

@ -0,0 +1,8 @@
#ifndef LWIP_HDR_TEST_PBUF_H
#define LWIP_HDR_TEST_PBUF_H
#include "../lwip_check.h"
Suite *pbuf_suite(void);
#endif

View File

@ -0,0 +1,233 @@
#include "test_timers.h"
#include "lwip/def.h"
#include "lwip/timeouts.h"
#include "arch/sys_arch.h"
/* Setups/teardown functions */
static struct sys_timeo* old_list_head;
static void
timers_setup(void)
{
struct sys_timeo** list_head = sys_timeouts_get_next_timeout();
old_list_head = *list_head;
*list_head = NULL;
}
static void
timers_teardown(void)
{
struct sys_timeo** list_head = sys_timeouts_get_next_timeout();
*list_head = old_list_head;
lwip_sys_now = 0;
}
static int fired[3];
static void
dummy_handler(void* arg)
{
int index = LWIP_PTR_NUMERIC_CAST(int, arg);
fired[index] = 1;
}
#define HANDLER_EXECUTION_TIME 5
static int cyclic_fired;
static void
dummy_cyclic_handler(void)
{
cyclic_fired = 1;
lwip_sys_now += HANDLER_EXECUTION_TIME;
}
struct lwip_cyclic_timer test_cyclic = {10, dummy_cyclic_handler};
static void
do_test_cyclic_timers(u32_t offset)
{
struct sys_timeo** list_head = sys_timeouts_get_next_timeout();
/* verify normal timer expiration */
lwip_sys_now = offset + 0;
sys_timeout(test_cyclic.interval_ms, lwip_cyclic_timer, &test_cyclic);
cyclic_fired = 0;
sys_check_timeouts();
fail_unless(cyclic_fired == 0);
lwip_sys_now = offset + test_cyclic.interval_ms;
sys_check_timeouts();
fail_unless(cyclic_fired == 1);
fail_unless((*list_head)->time == (u32_t)(lwip_sys_now + test_cyclic.interval_ms - HANDLER_EXECUTION_TIME));
sys_untimeout(lwip_cyclic_timer, &test_cyclic);
/* verify "overload" - next cyclic timer execution is already overdue twice */
lwip_sys_now = offset + 0;
sys_timeout(test_cyclic.interval_ms, lwip_cyclic_timer, &test_cyclic);
cyclic_fired = 0;
sys_check_timeouts();
fail_unless(cyclic_fired == 0);
lwip_sys_now = offset + 2*test_cyclic.interval_ms;
sys_check_timeouts();
fail_unless(cyclic_fired == 1);
fail_unless((*list_head)->time == (u32_t)(lwip_sys_now + test_cyclic.interval_ms));
}
START_TEST(test_cyclic_timers)
{
LWIP_UNUSED_ARG(_i);
/* check without u32_t wraparound */
do_test_cyclic_timers(0);
/* check with u32_t wraparound */
do_test_cyclic_timers(0xfffffff0);
}
END_TEST
/* reproduce bug #52748: the bug in timeouts.c */
START_TEST(test_bug52748)
{
LWIP_UNUSED_ARG(_i);
memset(&fired, 0, sizeof(fired));
lwip_sys_now = 50;
sys_timeout(20, dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 0));
sys_timeout( 5, dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 2));
lwip_sys_now = 55;
sys_check_timeouts();
fail_unless(fired[0] == 0);
fail_unless(fired[1] == 0);
fail_unless(fired[2] == 1);
lwip_sys_now = 60;
sys_timeout(10, dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 1));
sys_check_timeouts();
fail_unless(fired[0] == 0);
fail_unless(fired[1] == 0);
fail_unless(fired[2] == 1);
lwip_sys_now = 70;
sys_check_timeouts();
fail_unless(fired[0] == 1);
fail_unless(fired[1] == 1);
fail_unless(fired[2] == 1);
}
END_TEST
static void
do_test_timers(u32_t offset)
{
struct sys_timeo** list_head = sys_timeouts_get_next_timeout();
lwip_sys_now = offset + 0;
sys_timeout(10, dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 0));
fail_unless(sys_timeouts_sleeptime() == 10);
sys_timeout(20, dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 1));
fail_unless(sys_timeouts_sleeptime() == 10);
sys_timeout( 5, dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 2));
fail_unless(sys_timeouts_sleeptime() == 5);
/* linked list correctly sorted? */
fail_unless((*list_head)->time == (u32_t)(lwip_sys_now + 5));
fail_unless((*list_head)->next->time == (u32_t)(lwip_sys_now + 10));
fail_unless((*list_head)->next->next->time == (u32_t)(lwip_sys_now + 20));
/* check timers expire in correct order */
memset(&fired, 0, sizeof(fired));
lwip_sys_now += 4;
sys_check_timeouts();
fail_unless(fired[2] == 0);
lwip_sys_now += 1;
sys_check_timeouts();
fail_unless(fired[2] == 1);
lwip_sys_now += 4;
sys_check_timeouts();
fail_unless(fired[0] == 0);
lwip_sys_now += 1;
sys_check_timeouts();
fail_unless(fired[0] == 1);
lwip_sys_now += 9;
sys_check_timeouts();
fail_unless(fired[1] == 0);
lwip_sys_now += 1;
sys_check_timeouts();
fail_unless(fired[1] == 1);
sys_untimeout(dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 0));
sys_untimeout(dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 1));
sys_untimeout(dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 2));
}
START_TEST(test_timers)
{
LWIP_UNUSED_ARG(_i);
/* check without u32_t wraparound */
do_test_timers(0);
/* check with u32_t wraparound */
do_test_timers(0xfffffff0);
}
END_TEST
START_TEST(test_long_timer)
{
LWIP_UNUSED_ARG(_i);
memset(&fired, 0, sizeof(fired));
lwip_sys_now = 0;
sys_timeout(LWIP_UINT32_MAX / 4, dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 0));
fail_unless(sys_timeouts_sleeptime() == LWIP_UINT32_MAX / 4);
sys_check_timeouts();
fail_unless(fired[0] == 0);
lwip_sys_now += LWIP_UINT32_MAX / 8;
sys_check_timeouts();
fail_unless(fired[0] == 0);
lwip_sys_now += LWIP_UINT32_MAX / 8;
sys_check_timeouts();
fail_unless(fired[0] == 0);
lwip_sys_now += 1;
sys_check_timeouts();
fail_unless(fired[0] == 1);
sys_untimeout(dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 0));
}
END_TEST
/** Create the suite including all tests for this module */
Suite *
timers_suite(void)
{
testfunc tests[] = {
TESTFUNC(test_bug52748),
TESTFUNC(test_cyclic_timers),
TESTFUNC(test_timers),
TESTFUNC(test_long_timer),
};
return create_suite("TIMERS", tests, LWIP_ARRAYSIZE(tests), timers_setup, timers_teardown);
}

View File

@ -0,0 +1,8 @@
#ifndef LWIP_HDR_TEST_TIMERS_H
#define LWIP_HDR_TEST_TIMERS_H
#include "../lwip_check.h"
Suite *timers_suite(void);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
#ifndef LWIP_HDR_TEST_DHCP_H
#define LWIP_HDR_TEST_DHCP_H
#include "../lwip_check.h"
Suite* dhcp_suite(void);
#endif

View File

@ -0,0 +1,273 @@
#include "test_etharp.h"
#include "lwip/udp.h"
#include "lwip/etharp.h"
#include "lwip/inet.h"
#include "netif/ethernet.h"
#include "lwip/stats.h"
#include "lwip/prot/iana.h"
#if !LWIP_STATS || !UDP_STATS || !MEMP_STATS || !ETHARP_STATS
#error "This tests needs UDP-, MEMP- and ETHARP-statistics enabled"
#endif
#if !ETHARP_SUPPORT_STATIC_ENTRIES
#error "This test needs ETHARP_SUPPORT_STATIC_ENTRIES enabled"
#endif
static struct netif test_netif;
static ip4_addr_t test_ipaddr, test_netmask, test_gw;
struct eth_addr test_ethaddr = {{1,1,1,1,1,1}};
struct eth_addr test_ethaddr2 = {{1,1,1,1,1,2}};
struct eth_addr test_ethaddr3 = {{1,1,1,1,1,3}};
struct eth_addr test_ethaddr4 = {{1,1,1,1,1,4}};
static int linkoutput_ctr;
/* Helper functions */
static void
etharp_remove_all(void)
{
int i;
/* call etharp_tmr often enough to have all entries cleaned */
for(i = 0; i < 0xff; i++) {
etharp_tmr();
}
}
static err_t
default_netif_linkoutput(struct netif *netif, struct pbuf *p)
{
fail_unless(netif == &test_netif);
fail_unless(p != NULL);
linkoutput_ctr++;
return ERR_OK;
}
static err_t
default_netif_init(struct netif *netif)
{
fail_unless(netif != NULL);
netif->linkoutput = default_netif_linkoutput;
netif->output = etharp_output;
netif->mtu = 1500;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
netif->hwaddr_len = ETHARP_HWADDR_LEN;
return ERR_OK;
}
static void
default_netif_add(void)
{
IP4_ADDR(&test_gw, 192,168,0,1);
IP4_ADDR(&test_ipaddr, 192,168,0,1);
IP4_ADDR(&test_netmask, 255,255,0,0);
fail_unless(netif_default == NULL);
netif_set_default(netif_add(&test_netif, &test_ipaddr, &test_netmask,
&test_gw, NULL, default_netif_init, NULL));
netif_set_up(&test_netif);
}
static void
default_netif_remove(void)
{
fail_unless(netif_default == &test_netif);
netif_remove(&test_netif);
}
static void
create_arp_response(ip4_addr_t *adr)
{
int k;
struct eth_hdr *ethhdr;
struct etharp_hdr *etharphdr;
struct pbuf *p = pbuf_alloc(PBUF_RAW, sizeof(struct eth_hdr) + sizeof(struct etharp_hdr), PBUF_RAM);
if(p == NULL) {
FAIL_RET();
}
ethhdr = (struct eth_hdr*)p->payload;
etharphdr = (struct etharp_hdr*)(ethhdr + 1);
ethhdr->dest = test_ethaddr;
ethhdr->src = test_ethaddr2;
ethhdr->type = htons(ETHTYPE_ARP);
etharphdr->hwtype = htons(LWIP_IANA_HWTYPE_ETHERNET);
etharphdr->proto = htons(ETHTYPE_IP);
etharphdr->hwlen = ETHARP_HWADDR_LEN;
etharphdr->protolen = sizeof(ip4_addr_t);
etharphdr->opcode = htons(ARP_REPLY);
SMEMCPY(&etharphdr->sipaddr, adr, sizeof(ip4_addr_t));
SMEMCPY(&etharphdr->dipaddr, &test_ipaddr, sizeof(ip4_addr_t));
k = 6;
while(k > 0) {
k--;
/* Write the ARP MAC-Addresses */
etharphdr->shwaddr.addr[k] = test_ethaddr2.addr[k];
etharphdr->dhwaddr.addr[k] = test_ethaddr.addr[k];
/* Write the Ethernet MAC-Addresses */
ethhdr->dest.addr[k] = test_ethaddr.addr[k];
ethhdr->src.addr[k] = test_ethaddr2.addr[k];
}
ethernet_input(p, &test_netif);
}
/* Setups/teardown functions */
static void
etharp_setup(void)
{
etharp_remove_all();
default_netif_add();
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
static void
etharp_teardown(void)
{
etharp_remove_all();
default_netif_remove();
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
/* Test functions */
START_TEST(test_etharp_table)
{
#if ETHARP_SUPPORT_STATIC_ENTRIES
err_t err;
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
ssize_t idx;
const ip4_addr_t *unused_ipaddr;
struct eth_addr *unused_ethaddr;
struct udp_pcb* pcb;
LWIP_UNUSED_ARG(_i);
if (netif_default != &test_netif) {
fail("This test needs a default netif");
}
linkoutput_ctr = 0;
pcb = udp_new();
fail_unless(pcb != NULL);
if (pcb != NULL) {
ip4_addr_t adrs[ARP_TABLE_SIZE + 2];
int i;
for(i = 0; i < ARP_TABLE_SIZE + 2; i++) {
IP4_ADDR(&adrs[i], 192,168,0,i+2);
}
/* fill ARP-table with dynamic entries */
for(i = 0; i < ARP_TABLE_SIZE; i++) {
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 10, PBUF_RAM);
fail_unless(p != NULL);
if (p != NULL) {
err_t err2;
ip_addr_t dst;
ip_addr_copy_from_ip4(dst, adrs[i]);
err2 = udp_sendto(pcb, p, &dst, 123);
fail_unless(err2 == ERR_OK);
/* etharp request sent? */
fail_unless(linkoutput_ctr == (2*i) + 1);
pbuf_free(p);
/* create an ARP response */
create_arp_response(&adrs[i]);
/* queued UDP packet sent? */
fail_unless(linkoutput_ctr == (2*i) + 2);
idx = etharp_find_addr(NULL, &adrs[i], &unused_ethaddr, &unused_ipaddr);
fail_unless(idx == i);
etharp_tmr();
}
}
linkoutput_ctr = 0;
#if ETHARP_SUPPORT_STATIC_ENTRIES
/* create one static entry */
err = etharp_add_static_entry(&adrs[ARP_TABLE_SIZE], &test_ethaddr3);
fail_unless(err == ERR_OK);
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr);
fail_unless(idx == 0);
fail_unless(linkoutput_ctr == 0);
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
linkoutput_ctr = 0;
/* fill ARP-table with dynamic entries */
for(i = 0; i < ARP_TABLE_SIZE; i++) {
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 10, PBUF_RAM);
fail_unless(p != NULL);
if (p != NULL) {
err_t err2;
ip_addr_t dst;
ip_addr_copy_from_ip4(dst, adrs[i]);
err2 = udp_sendto(pcb, p, &dst, 123);
fail_unless(err2 == ERR_OK);
/* etharp request sent? */
fail_unless(linkoutput_ctr == (2*i) + 1);
pbuf_free(p);
/* create an ARP response */
create_arp_response(&adrs[i]);
/* queued UDP packet sent? */
fail_unless(linkoutput_ctr == (2*i) + 2);
idx = etharp_find_addr(NULL, &adrs[i], &unused_ethaddr, &unused_ipaddr);
if (i < ARP_TABLE_SIZE - 1) {
fail_unless(idx == i+1);
} else {
/* the last entry must not overwrite the static entry! */
fail_unless(idx == 1);
}
etharp_tmr();
}
}
#if ETHARP_SUPPORT_STATIC_ENTRIES
/* create a second static entry */
err = etharp_add_static_entry(&adrs[ARP_TABLE_SIZE+1], &test_ethaddr4);
fail_unless(err == ERR_OK);
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr);
fail_unless(idx == 0);
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE+1], &unused_ethaddr, &unused_ipaddr);
fail_unless(idx == 2);
/* and remove it again */
err = etharp_remove_static_entry(&adrs[ARP_TABLE_SIZE+1]);
fail_unless(err == ERR_OK);
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr);
fail_unless(idx == 0);
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE+1], &unused_ethaddr, &unused_ipaddr);
fail_unless(idx == -1);
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
/* check that static entries don't time out */
etharp_remove_all();
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr);
fail_unless(idx == 0);
#if ETHARP_SUPPORT_STATIC_ENTRIES
/* remove the first static entry */
err = etharp_remove_static_entry(&adrs[ARP_TABLE_SIZE]);
fail_unless(err == ERR_OK);
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr);
fail_unless(idx == -1);
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE+1], &unused_ethaddr, &unused_ipaddr);
fail_unless(idx == -1);
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
udp_remove(pcb);
}
}
END_TEST
/** Create the suite including all tests for this module */
Suite *
etharp_suite(void)
{
testfunc tests[] = {
TESTFUNC(test_etharp_table)
};
return create_suite("ETHARP", tests, sizeof(tests)/sizeof(testfunc), etharp_setup, etharp_teardown);
}

View File

@ -0,0 +1,8 @@
#ifndef LWIP_HDR_TEST_ETHARP_H
#define LWIP_HDR_TEST_ETHARP_H
#include "../lwip_check.h"
Suite* etharp_suite(void);
#endif

View File

@ -0,0 +1,343 @@
#include "test_ip4.h"
#include "lwip/icmp.h"
#include "lwip/ip4.h"
#include "lwip/etharp.h"
#include "lwip/inet_chksum.h"
#include "lwip/stats.h"
#include "lwip/prot/ip.h"
#include "lwip/prot/ip4.h"
#include "lwip/tcpip.h"
#if !LWIP_IPV4 || !IP_REASSEMBLY || !MIB2_STATS || !IPFRAG_STATS
#error "This tests needs LWIP_IPV4, IP_REASSEMBLY; MIB2- and IPFRAG-statistics enabled"
#endif
static struct netif test_netif;
static ip4_addr_t test_ipaddr, test_netmask, test_gw;
static int linkoutput_ctr;
static int linkoutput_byte_ctr;
static u16_t linkoutput_pkt_len;
static u8_t linkoutput_pkt[100];
/* reference internal lwip variable in netif.c */
static err_t
test_netif_linkoutput(struct netif *netif, struct pbuf *p)
{
fail_unless(netif == &test_netif);
fail_unless(p != NULL);
linkoutput_ctr++;
linkoutput_byte_ctr += p->tot_len;
/* Copy start of packet into buffer */
linkoutput_pkt_len = pbuf_copy_partial(p, linkoutput_pkt, sizeof(linkoutput_pkt), 0);
return ERR_OK;
}
static err_t
test_netif_init(struct netif *netif)
{
fail_unless(netif != NULL);
netif->linkoutput = test_netif_linkoutput;
netif->output = etharp_output;
netif->mtu = 1500;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
netif->hwaddr_len = ETHARP_HWADDR_LEN;
return ERR_OK;
}
static void
test_netif_add(void)
{
IP4_ADDR(&test_gw, 192,168,0,1);
IP4_ADDR(&test_ipaddr, 192,168,0,1);
IP4_ADDR(&test_netmask, 255,255,0,0);
fail_unless(netif_default == NULL);
netif_add(&test_netif, &test_ipaddr, &test_netmask, &test_gw,
NULL, test_netif_init, NULL);
netif_set_default(&test_netif);
netif_set_up(&test_netif);
}
static void
test_netif_remove(void)
{
if (netif_default == &test_netif) {
netif_remove(&test_netif);
}
}
/* Helper functions */
static void
create_ip4_input_fragment(u16_t ip_id, u16_t start, u16_t len, int last)
{
struct pbuf *p;
struct netif *input_netif = netif_list; /* just use any netif */
fail_unless((start & 7) == 0);
fail_unless(((len & 7) == 0) || last);
fail_unless(input_netif != NULL);
p = pbuf_alloc(PBUF_RAW, len + sizeof(struct ip_hdr), PBUF_RAM);
fail_unless(p != NULL);
if (p != NULL) {
err_t err;
struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
IPH_VHL_SET(iphdr, 4, sizeof(struct ip_hdr) / 4);
IPH_TOS_SET(iphdr, 0);
IPH_LEN_SET(iphdr, lwip_htons(p->tot_len));
IPH_ID_SET(iphdr, lwip_htons(ip_id));
if (last) {
IPH_OFFSET_SET(iphdr, lwip_htons(start / 8));
} else {
IPH_OFFSET_SET(iphdr, lwip_htons((start / 8) | IP_MF));
}
IPH_TTL_SET(iphdr, 5);
IPH_PROTO_SET(iphdr, IP_PROTO_UDP);
IPH_CHKSUM_SET(iphdr, 0);
ip4_addr_copy(iphdr->src, *netif_ip4_addr(input_netif));
iphdr->src.addr = lwip_htonl(lwip_htonl(iphdr->src.addr) + 1);
ip4_addr_copy(iphdr->dest, *netif_ip4_addr(input_netif));
IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, sizeof(struct ip_hdr)));
err = ip4_input(p, input_netif);
if (err != ERR_OK) {
pbuf_free(p);
}
fail_unless(err == ERR_OK);
}
}
static err_t arpless_output(struct netif *netif, struct pbuf *p,
const ip4_addr_t *ipaddr) {
LWIP_UNUSED_ARG(ipaddr);
return netif->linkoutput(netif, p);
}
/* Setups/teardown functions */
static void
ip4_setup(void)
{
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
static void
ip4_teardown(void)
{
if (netif_list->loop_first != NULL) {
pbuf_free(netif_list->loop_first);
netif_list->loop_first = NULL;
}
netif_list->loop_last = NULL;
/* poll until all memory is released... */
tcpip_thread_poll_one();
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
test_netif_remove();
netif_set_up(netif_get_loopif());
}
/* Test functions */
START_TEST(test_ip4_frag)
{
struct pbuf *data = pbuf_alloc(PBUF_IP, 8000, PBUF_RAM);
ip_addr_t peer_ip = IPADDR4_INIT_BYTES(192,168,0,5);
err_t err;
LWIP_UNUSED_ARG(_i);
linkoutput_ctr = 0;
linkoutput_byte_ctr = 0;
/* Verify that 8000 byte payload is split into six packets */
fail_unless(data != NULL);
test_netif_add();
test_netif.output = arpless_output;
err = ip4_output_if_src(data, &test_ipaddr, ip_2_ip4(&peer_ip),
16, 0, IP_PROTO_UDP, &test_netif);
fail_unless(err == ERR_OK);
fail_unless(linkoutput_ctr == 6);
fail_unless(linkoutput_byte_ctr == (8000 + (6 * IP_HLEN)));
pbuf_free(data);
test_netif_remove();
}
END_TEST
START_TEST(test_ip4_reass)
{
const u16_t ip_id = 128;
LWIP_UNUSED_ARG(_i);
memset(&lwip_stats.mib2, 0, sizeof(lwip_stats.mib2));
create_ip4_input_fragment(ip_id, 8*200, 200, 1);
fail_unless(lwip_stats.ip_frag.recv == 1);
fail_unless(lwip_stats.ip_frag.err == 0);
fail_unless(lwip_stats.ip_frag.memerr == 0);
fail_unless(lwip_stats.ip_frag.drop == 0);
fail_unless(lwip_stats.mib2.ipreasmoks == 0);
create_ip4_input_fragment(ip_id, 0*200, 200, 0);
fail_unless(lwip_stats.ip_frag.recv == 2);
fail_unless(lwip_stats.ip_frag.err == 0);
fail_unless(lwip_stats.ip_frag.memerr == 0);
fail_unless(lwip_stats.ip_frag.drop == 0);
fail_unless(lwip_stats.mib2.ipreasmoks == 0);
create_ip4_input_fragment(ip_id, 1*200, 200, 0);
fail_unless(lwip_stats.ip_frag.recv == 3);
fail_unless(lwip_stats.ip_frag.err == 0);
fail_unless(lwip_stats.ip_frag.memerr == 0);
fail_unless(lwip_stats.ip_frag.drop == 0);
fail_unless(lwip_stats.mib2.ipreasmoks == 0);
create_ip4_input_fragment(ip_id, 2*200, 200, 0);
fail_unless(lwip_stats.ip_frag.recv == 4);
fail_unless(lwip_stats.ip_frag.err == 0);
fail_unless(lwip_stats.ip_frag.memerr == 0);
fail_unless(lwip_stats.ip_frag.drop == 0);
fail_unless(lwip_stats.mib2.ipreasmoks == 0);
create_ip4_input_fragment(ip_id, 3*200, 200, 0);
fail_unless(lwip_stats.ip_frag.recv == 5);
fail_unless(lwip_stats.ip_frag.err == 0);
fail_unless(lwip_stats.ip_frag.memerr == 0);
fail_unless(lwip_stats.ip_frag.drop == 0);
fail_unless(lwip_stats.mib2.ipreasmoks == 0);
create_ip4_input_fragment(ip_id, 4*200, 200, 0);
fail_unless(lwip_stats.ip_frag.recv == 6);
fail_unless(lwip_stats.ip_frag.err == 0);
fail_unless(lwip_stats.ip_frag.memerr == 0);
fail_unless(lwip_stats.ip_frag.drop == 0);
fail_unless(lwip_stats.mib2.ipreasmoks == 0);
create_ip4_input_fragment(ip_id, 7*200, 200, 0);
fail_unless(lwip_stats.ip_frag.recv == 7);
fail_unless(lwip_stats.ip_frag.err == 0);
fail_unless(lwip_stats.ip_frag.memerr == 0);
fail_unless(lwip_stats.ip_frag.drop == 0);
fail_unless(lwip_stats.mib2.ipreasmoks == 0);
create_ip4_input_fragment(ip_id, 6*200, 200, 0);
fail_unless(lwip_stats.ip_frag.recv == 8);
fail_unless(lwip_stats.ip_frag.err == 0);
fail_unless(lwip_stats.ip_frag.memerr == 0);
fail_unless(lwip_stats.ip_frag.drop == 0);
fail_unless(lwip_stats.mib2.ipreasmoks == 0);
create_ip4_input_fragment(ip_id, 5*200, 200, 0);
fail_unless(lwip_stats.ip_frag.recv == 9);
fail_unless(lwip_stats.ip_frag.err == 0);
fail_unless(lwip_stats.ip_frag.memerr == 0);
fail_unless(lwip_stats.ip_frag.drop == 0);
fail_unless(lwip_stats.mib2.ipreasmoks == 1);
}
END_TEST
/* packets to 127.0.0.1 shall not be sent out to netif_default */
START_TEST(test_127_0_0_1)
{
ip4_addr_t localhost;
struct pbuf* p;
LWIP_UNUSED_ARG(_i);
linkoutput_ctr = 0;
test_netif_add();
netif_set_down(netif_get_loopif());
IP4_ADDR(&localhost, 127, 0, 0, 1);
p = pbuf_alloc(PBUF_IP, 10, PBUF_POOL);
if(ip4_output(p, netif_ip4_addr(netif_default), &localhost, 0, 0, IP_PROTO_UDP) != ERR_OK) {
pbuf_free(p);
}
fail_unless(linkoutput_ctr == 0);
}
END_TEST
START_TEST(test_ip4addr_aton)
{
ip4_addr_t ip_addr;
LWIP_UNUSED_ARG(_i);
fail_unless(ip4addr_aton("192.168.0.1", &ip_addr) == 1);
fail_unless(ip4addr_aton("192.168.0.0001", &ip_addr) == 1);
fail_unless(ip4addr_aton("192.168.0.zzz", &ip_addr) == 0);
fail_unless(ip4addr_aton("192.168.1", &ip_addr) == 1);
fail_unless(ip4addr_aton("192.168.0xd3", &ip_addr) == 1);
fail_unless(ip4addr_aton("192.168.0xz5", &ip_addr) == 0);
fail_unless(ip4addr_aton("192.168.095", &ip_addr) == 0);
}
END_TEST
/* Test for bug #59364 */
START_TEST(test_ip4_icmp_replylen_short)
{
/* IP packet to 192.168.0.1 using proto 0x22 and 1 byte payload */
const u8_t unknown_proto[] = {
0x45, 0x00, 0x00, 0x15, 0xd4, 0x31, 0x00, 0x00, 0xff, 0x22,
0x66, 0x41, 0xc0, 0xa8, 0x00, 0x02, 0xc0, 0xa8, 0x00, 0x01,
0xaa };
struct pbuf *p;
const int icmp_len = IP_HLEN + sizeof(struct icmp_hdr);
LWIP_UNUSED_ARG(_i);
linkoutput_ctr = 0;
test_netif_add();
test_netif.output = arpless_output;
p = pbuf_alloc(PBUF_IP, sizeof(unknown_proto), PBUF_RAM);
pbuf_take(p, unknown_proto, sizeof(unknown_proto));
fail_unless(ip4_input(p, &test_netif) == ERR_OK);
fail_unless(linkoutput_ctr == 1);
/* Verify outgoing ICMP packet has no extra data */
fail_unless(linkoutput_pkt_len == icmp_len + sizeof(unknown_proto));
fail_if(memcmp(&linkoutput_pkt[icmp_len], unknown_proto, sizeof(unknown_proto)));
}
END_TEST
START_TEST(test_ip4_icmp_replylen_first_8)
{
/* IP packet to 192.168.0.1 using proto 0x22 and 11 bytes payload */
const u8_t unknown_proto[] = {
0x45, 0x00, 0x00, 0x1f, 0xd4, 0x31, 0x00, 0x00, 0xff, 0x22,
0x66, 0x37, 0xc0, 0xa8, 0x00, 0x02, 0xc0, 0xa8, 0x00, 0x01,
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
0xaa };
struct pbuf *p;
const int icmp_len = IP_HLEN + sizeof(struct icmp_hdr);
const int unreach_len = IP_HLEN + 8;
LWIP_UNUSED_ARG(_i);
linkoutput_ctr = 0;
test_netif_add();
test_netif.output = arpless_output;
p = pbuf_alloc(PBUF_IP, sizeof(unknown_proto), PBUF_RAM);
pbuf_take(p, unknown_proto, sizeof(unknown_proto));
fail_unless(ip4_input(p, &test_netif) == ERR_OK);
fail_unless(linkoutput_ctr == 1);
fail_unless(linkoutput_pkt_len == icmp_len + unreach_len);
fail_if(memcmp(&linkoutput_pkt[icmp_len], unknown_proto, unreach_len));
}
END_TEST
/** Create the suite including all tests for this module */
Suite *
ip4_suite(void)
{
testfunc tests[] = {
TESTFUNC(test_ip4_frag),
TESTFUNC(test_ip4_reass),
TESTFUNC(test_127_0_0_1),
TESTFUNC(test_ip4addr_aton),
TESTFUNC(test_ip4_icmp_replylen_short),
TESTFUNC(test_ip4_icmp_replylen_first_8),
};
return create_suite("IPv4", tests, sizeof(tests)/sizeof(testfunc), ip4_setup, ip4_teardown);
}

View File

@ -0,0 +1,8 @@
#ifndef LWIP_HDR_TEST_IP4_H
#define LWIP_HDR_TEST_IP4_H
#include "../lwip_check.h"
Suite* ip4_suite(void);
#endif

View File

@ -0,0 +1,558 @@
#include "test_ip6.h"
#include "lwip/ethip6.h"
#include "lwip/ip6.h"
#include "lwip/icmp6.h"
#include "lwip/inet_chksum.h"
#include "lwip/nd6.h"
#include "lwip/stats.h"
#include "lwip/prot/ethernet.h"
#include "lwip/prot/ip.h"
#include "lwip/prot/ip6.h"
#include "lwip/tcpip.h"
#if LWIP_IPV6 /* allow to build the unit tests without IPv6 support */
static struct netif test_netif6;
static int linkoutput_ctr;
static int linkoutput_byte_ctr;
static err_t
default_netif_linkoutput(struct netif *netif, struct pbuf *p)
{
fail_unless(netif == &test_netif6);
fail_unless(p != NULL);
linkoutput_ctr++;
linkoutput_byte_ctr += p->tot_len;
return ERR_OK;
}
static err_t
default_netif_init(struct netif *netif)
{
fail_unless(netif != NULL);
netif->linkoutput = default_netif_linkoutput;
netif->output_ip6 = ethip6_output;
netif->mtu = 1500;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHERNET | NETIF_FLAG_MLD6;
netif->hwaddr_len = ETH_HWADDR_LEN;
return ERR_OK;
}
static void
default_netif_add(void)
{
struct netif *n;
fail_unless(netif_default == NULL);
n = netif_add_noaddr(&test_netif6, NULL, default_netif_init, NULL);
fail_unless(n == &test_netif6);
netif_set_default(&test_netif6);
}
static void
default_netif_remove(void)
{
fail_unless(netif_default == &test_netif6);
netif_remove(&test_netif6);
}
static void
ip6_test_handle_timers(int count)
{
int i;
for (i = 0; i < count; i++) {
nd6_tmr();
}
}
/* Helper functions */
static void
create_ip6_input_fragment(u32_t ip_id, u16_t start, u16_t len, int last, u8_t next_hdr)
{
struct pbuf* p;
struct netif* input_netif = netif_list; /* just use any netif */
fail_unless((start & 7) == 0);
fail_unless(((len & 7) == 0) || last);
fail_unless(input_netif != NULL);
p = pbuf_alloc(PBUF_RAW, len + sizeof(struct ip6_frag_hdr) +
sizeof(struct ip6_hdr), PBUF_RAM);
fail_unless(p != NULL);
if (p != NULL) {
err_t err;
struct ip6_frag_hdr* fraghdr;
struct ip6_hdr* ip6hdr = (struct ip6_hdr*)p->payload;
IP6H_VTCFL_SET(ip6hdr, 6, 0, 0);
IP6H_PLEN_SET(ip6hdr, len + sizeof(struct ip6_frag_hdr));
IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_FRAGMENT);
IP6H_HOPLIM_SET(ip6hdr, 64);
ip6_addr_copy_to_packed(ip6hdr->src, *netif_ip6_addr(input_netif, 0));
ip6hdr->src.addr[3]++;
ip6_addr_copy_to_packed(ip6hdr->dest, *netif_ip6_addr(input_netif, 0));
fraghdr = (struct ip6_frag_hdr*)(ip6hdr + 1);
fraghdr->_nexth = next_hdr;
fraghdr->reserved = 0;
if (last) {
fraghdr->_fragment_offset = htons(start & ~7);
} else {
fraghdr->_fragment_offset = htons((start & ~7) | 1);
}
fraghdr->_identification = htonl(ip_id);
err = ip6_input(p, input_netif);
if (err != ERR_OK) {
pbuf_free(p);
}
fail_unless(err == ERR_OK);
}
}
/* Setups/teardown functions */
static void
ip6_setup(void)
{
default_netif_add();
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
static void
ip6_teardown(void)
{
if (netif_list->loop_first != NULL) {
pbuf_free(netif_list->loop_first);
netif_list->loop_first = NULL;
}
netif_list->loop_last = NULL;
/* poll until all memory is released... */
tcpip_thread_poll_one();
default_netif_remove();
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
/* Test functions */
static void
test_ip6_ll_addr_iter(int expected_ctr1, int expected_ctr2)
{
fail_unless(linkoutput_ctr == 0);
/* test that nothing is sent with link uo but netif down */
netif_set_link_up(&test_netif6);
ip6_test_handle_timers(500);
fail_unless(linkoutput_ctr == 0);
netif_set_link_down(&test_netif6);
fail_unless(linkoutput_ctr == 0);
/* test that nothing is sent with link down but netif up */
netif_set_up(&test_netif6);
ip6_test_handle_timers(500);
fail_unless(linkoutput_ctr == 0);
netif_set_down(&test_netif6);
fail_unless(linkoutput_ctr == 0);
/* test what is sent with link up + netif up */
netif_set_link_up(&test_netif6);
netif_set_up(&test_netif6);
ip6_test_handle_timers(500);
fail_unless(linkoutput_ctr == expected_ctr1);
netif_set_down(&test_netif6);
netif_set_link_down(&test_netif6);
fail_unless(linkoutput_ctr == expected_ctr1);
linkoutput_ctr = 0;
netif_set_up(&test_netif6);
netif_set_link_up(&test_netif6);
ip6_test_handle_timers(500);
fail_unless(linkoutput_ctr == expected_ctr2);
netif_set_link_down(&test_netif6);
netif_set_down(&test_netif6);
fail_unless(linkoutput_ctr == expected_ctr2);
linkoutput_ctr = 0;
}
START_TEST(test_ip6_ll_addr)
{
LWIP_UNUSED_ARG(_i);
linkoutput_ctr = 0;
/* test without link-local address */
test_ip6_ll_addr_iter(0, 0);
/* test with link-local address */
netif_create_ip6_linklocal_address(&test_netif6, 1);
test_ip6_ll_addr_iter(3 + LWIP_IPV6_DUP_DETECT_ATTEMPTS + LWIP_IPV6_MLD, 3);
}
END_TEST
START_TEST(test_ip6_aton_ipv4mapped)
{
int ret;
ip_addr_t addr;
ip6_addr_t addr6;
const ip_addr_t addr_expected = IPADDR6_INIT_HOST(0, 0, 0xFFFF, 0xD4CC65D2);
const char *full_ipv6_addr = "0:0:0:0:0:FFFF:D4CC:65D2";
const char *shortened_ipv6_addr = "::FFFF:D4CC:65D2";
const char *full_ipv4_mapped_addr = "0:0:0:0:0:FFFF:212.204.101.210";
const char *shortened_ipv4_mapped_addr = "::FFFF:212.204.101.210";
const char *bogus_ipv4_mapped_addr = "::FFFF:212.204.101.2101";
LWIP_UNUSED_ARG(_i);
/* check IPv6 representation */
memset(&addr6, 0, sizeof(addr6));
ret = ip6addr_aton(full_ipv6_addr, &addr6);
fail_unless(ret == 1);
fail_unless(memcmp(&addr6, &addr_expected, 16) == 0);
memset(&addr, 0, sizeof(addr));
ret = ipaddr_aton(full_ipv6_addr, &addr);
fail_unless(ret == 1);
fail_unless(memcmp(&addr, &addr_expected, 16) == 0);
/* check shortened IPv6 representation */
memset(&addr6, 0, sizeof(addr6));
ret = ip6addr_aton(shortened_ipv6_addr, &addr6);
fail_unless(ret == 1);
fail_unless(memcmp(&addr6, &addr_expected, 16) == 0);
memset(&addr, 0, sizeof(addr));
ret = ipaddr_aton(shortened_ipv6_addr, &addr);
fail_unless(ret == 1);
fail_unless(memcmp(&addr, &addr_expected, 16) == 0);
/* checked shortened mixed representation */
memset(&addr6, 0, sizeof(addr6));
ret = ip6addr_aton(shortened_ipv4_mapped_addr, &addr6);
fail_unless(ret == 1);
fail_unless(memcmp(&addr6, &addr_expected, 16) == 0);
memset(&addr, 0, sizeof(addr));
ret = ipaddr_aton(shortened_ipv4_mapped_addr, &addr);
fail_unless(ret == 1);
fail_unless(memcmp(&addr, &addr_expected, 16) == 0);
/* checked mixed representation */
memset(&addr6, 0, sizeof(addr6));
ret = ip6addr_aton(full_ipv4_mapped_addr, &addr6);
fail_unless(ret == 1);
fail_unless(memcmp(&addr6, &addr_expected, 16) == 0);
memset(&addr, 0, sizeof(addr));
ret = ipaddr_aton(full_ipv4_mapped_addr, &addr);
fail_unless(ret == 1);
fail_unless(memcmp(&addr, &addr_expected, 16) == 0);
/* checked bogus mixed representation */
memset(&addr6, 0, sizeof(addr6));
ret = ip6addr_aton(bogus_ipv4_mapped_addr, &addr6);
fail_unless(ret == 0);
memset(&addr, 0, sizeof(addr));
ret = ipaddr_aton(bogus_ipv4_mapped_addr, &addr);
fail_unless(ret == 0);
}
END_TEST
START_TEST(test_ip6_ntoa_ipv4mapped)
{
const ip_addr_t addr = IPADDR6_INIT_HOST(0, 0, 0xFFFF, 0xD4CC65D2);
char buf[128];
char *str;
LWIP_UNUSED_ARG(_i);
str = ip6addr_ntoa_r(ip_2_ip6(&addr), buf, sizeof(buf));
fail_unless(str == buf);
fail_unless(!strcmp(str, "::FFFF:212.204.101.210"));
}
END_TEST
struct test_addr_and_str {
ip_addr_t addr;
const char *str;
};
START_TEST(test_ip6_ntoa)
{
struct test_addr_and_str tests[] = {
{IPADDR6_INIT_HOST(0xfe800000, 0x00000000, 0xb2a1a2ff, 0xfea3a4a5), "FE80::B2A1:A2FF:FEA3:A4A5"}, /* test shortened zeros */
{IPADDR6_INIT_HOST(0xfe800000, 0xff000000, 0xb2a1a2ff, 0xfea3a4a5), "FE80:0:FF00:0:B2A1:A2FF:FEA3:A4A5"}, /* don't omit single zero blocks */
{IPADDR6_INIT_HOST(0xfe800000, 0xff000000, 0xb2000000, 0x0000a4a5), "FE80:0:FF00:0:B200::A4A5"}, /* omit longest zero block */
};
char buf[128];
char *str;
size_t i;
LWIP_UNUSED_ARG(_i);
for (i = 0; i < LWIP_ARRAYSIZE(tests); i++) {
str = ip6addr_ntoa_r(ip_2_ip6(&tests[i].addr), buf, sizeof(buf));
fail_unless(str == buf);
fail_unless(!strcmp(str, tests[i].str));
}
}
END_TEST
START_TEST(test_ip6_lladdr)
{
u8_t zeros[128];
const u8_t test_mac_addr[6] = {0xb0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5};
const u32_t expected_ip6_addr_1[4] = {PP_HTONL(0xfe800000), 0, PP_HTONL(0xb2a1a2ff), PP_HTONL(0xfea3a4a5)};
const u32_t expected_ip6_addr_2[4] = {PP_HTONL(0xfe800000), 0, PP_HTONL(0x0000b0a1), PP_HTONL(0xa2a3a4a5)};
LWIP_UNUSED_ARG(_i);
memset(zeros, 0, sizeof(zeros));
fail_unless(test_netif6.hwaddr_len == 6);
fail_unless(!memcmp(test_netif6.hwaddr, zeros, 6));
fail_unless(test_netif6.ip6_addr_state[0] == 0);
fail_unless(!memcmp(netif_ip6_addr(&test_netif6, 0), zeros, sizeof(ip6_addr_t)));
/* set specific mac addr */
memcpy(test_netif6.hwaddr, test_mac_addr, 6);
/* create link-local addr based on mac (EUI-48) */
netif_create_ip6_linklocal_address(&test_netif6, 1);
fail_unless(IP_IS_V6(&test_netif6.ip6_addr[0]));
fail_unless(!memcmp(&netif_ip6_addr(&test_netif6, 0)->addr, expected_ip6_addr_1, 16));
#if LWIP_IPV6_SCOPES
fail_unless(netif_ip6_addr(&test_netif6, 0)->zone == (test_netif6.num + 1));
#endif
/* reset address */
memset(&test_netif6.ip6_addr[0], 0, sizeof(ip6_addr_t));
test_netif6.ip6_addr_state[0] = 0;
/* create link-local addr based interface ID */
netif_create_ip6_linklocal_address(&test_netif6, 0);
fail_unless(IP_IS_V6(&test_netif6.ip6_addr[0]));
fail_unless(!memcmp(&netif_ip6_addr(&test_netif6, 0)->addr, expected_ip6_addr_2, 16));
#if LWIP_IPV6_SCOPES
fail_unless(netif_ip6_addr(&test_netif6, 0)->zone == (test_netif6.num + 1));
#endif
/* reset address */
memset(&test_netif6.ip6_addr[0], 0, sizeof(ip6_addr_t));
test_netif6.ip6_addr_state[0] = 0;
/* reset mac address */
memset(&test_netif6.hwaddr, 0, sizeof(test_netif6.hwaddr));
}
END_TEST
static struct pbuf *cloned_pbuf = NULL;
static err_t clone_output(struct netif *netif, struct pbuf *p, const ip6_addr_t *addr) {
LWIP_UNUSED_ARG(netif);
LWIP_UNUSED_ARG(addr);
cloned_pbuf = pbuf_clone(PBUF_RAW, PBUF_RAM, p);
return ERR_OK;
}
/* Reproduces bug #58553 */
START_TEST(test_ip6_dest_unreachable_chained_pbuf)
{
ip_addr_t my_addr = IPADDR6_INIT_HOST(0x20010db8, 0x0, 0x0, 0x1);
ip_addr_t peer_addr = IPADDR6_INIT_HOST(0x20010db8, 0x0, 0x0, 0x4);
/* Create chained pbuf with UDP data that will get destination unreachable */
u8_t udp_hdr[] = {
0x60, 0x00, 0x27, 0x03, 0x00, 0x2d, 0x11, 0x40,
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x01, 0xff, 0x03, 0xff, 0x00, 0x2d, 0x00, 0x00,
};
struct pbuf *header = pbuf_alloc(PBUF_RAW, sizeof(udp_hdr), PBUF_ROM);
u8_t udp_payload[] = "abcdefghijklmnopqrstuvwxyz0123456789";
struct pbuf *data = pbuf_alloc(PBUF_RAW, sizeof(udp_payload), PBUF_ROM);
u8_t *icmpptr;
struct ip6_hdr *outhdr;
struct icmp6_hdr *icmp6hdr;
LWIP_UNUSED_ARG(_i);
fail_unless(header);
header->payload = udp_hdr;
fail_unless(data);
data->payload = udp_payload;
pbuf_cat(header, data);
data = NULL;
/* Configure and enable local address */
netif_set_up(&test_netif6);
netif_ip6_addr_set(&test_netif6, 0, ip_2_ip6(&my_addr));
netif_ip6_addr_set_state(&test_netif6, 0, IP6_ADDR_VALID);
test_netif6.output_ip6 = clone_output;
/* Process packet and send ICMPv6 reply for unreachable UDP port */
ip6_input(header, &test_netif6);
header = NULL;
/* Verify ICMP reply packet contents */
fail_unless(cloned_pbuf);
fail_unless(cloned_pbuf->len == IP6_HLEN + ICMP6_HLEN + sizeof(udp_hdr) + sizeof(udp_payload));
outhdr = (struct ip6_hdr*) cloned_pbuf->payload;
fail_unless(ip6_addr_packed_eq(ip_2_ip6(&my_addr), &outhdr->src, IP6_NO_ZONE));
fail_unless(ip6_addr_packed_eq(ip_2_ip6(&peer_addr), &outhdr->dest, IP6_NO_ZONE));
icmpptr = &((u8_t*)cloned_pbuf->payload)[IP6_HLEN];
icmp6hdr = (struct icmp6_hdr*) icmpptr;
fail_unless(icmp6hdr->type == ICMP6_TYPE_DUR);
fail_unless(icmp6hdr->code == ICMP6_DUR_PORT);
fail_unless(icmp6hdr->data == lwip_htonl(0));
icmpptr += ICMP6_HLEN;
fail_unless(memcmp(icmpptr, udp_hdr, sizeof(udp_hdr)) == 0, "mismatch in copied ip6/udp header");
icmpptr += sizeof(udp_hdr);
fail_unless(memcmp(icmpptr, udp_payload, sizeof(udp_payload)) == 0, "mismatch in copied udp payload");
pbuf_free(cloned_pbuf);
}
END_TEST
/* Reproduces bug #57374 */
START_TEST(test_ip6_frag_pbuf_len_assert)
{
ip_addr_t my_addr = IPADDR6_INIT_HOST(0x20010db8, 0x0, 0x0, 0x1);
ip_addr_t peer_addr = IPADDR6_INIT_HOST(0x20010db8, 0x0, 0x0, 0x4);
struct pbuf *payload, *hdr;
err_t err;
int i;
LWIP_UNUSED_ARG(_i);
/* Configure and enable local address */
test_netif6.mtu = 1500;
netif_set_up(&test_netif6);
netif_ip6_addr_set(&test_netif6, 0, ip_2_ip6(&my_addr));
netif_ip6_addr_set_state(&test_netif6, 0, IP6_ADDR_VALID);
/* Create packet with lots of small pbufs around mtu limit */
payload = pbuf_alloc(PBUF_RAW, 1400, PBUF_POOL);
fail_unless(payload != NULL);
for (i = 0; i < 16; i++) {
struct pbuf *p = pbuf_alloc(PBUF_RAW, 32, PBUF_RAM);
fail_unless(p != NULL);
pbuf_cat(payload, p);
}
/* Prefix with header like UDP would */
hdr = pbuf_alloc(PBUF_IP, 8, PBUF_RAM);
fail_unless(hdr != NULL);
pbuf_chain(hdr, payload);
/* Send it and don't crash while fragmenting */
err = ip6_output_if_src(hdr, ip_2_ip6(&my_addr), ip_2_ip6(&peer_addr), 15, 0, IP_PROTO_UDP, &test_netif6);
fail_unless(err == ERR_OK);
pbuf_free(hdr);
pbuf_free(payload);
}
END_TEST
static err_t direct_output(struct netif *netif, struct pbuf *p, const ip6_addr_t *addr) {
LWIP_UNUSED_ARG(addr);
return netif->linkoutput(netif, p);
}
START_TEST(test_ip6_frag)
{
ip_addr_t my_addr = IPADDR6_INIT_HOST(0x20010db8, 0x0, 0x0, 0x1);
ip_addr_t peer_addr = IPADDR6_INIT_HOST(0x20010db8, 0x0, 0x0, 0x4);
struct pbuf *data;
err_t err;
LWIP_UNUSED_ARG(_i);
/* Configure and enable local address */
test_netif6.mtu = 1500;
netif_set_up(&test_netif6);
netif_ip6_addr_set(&test_netif6, 0, ip_2_ip6(&my_addr));
netif_ip6_addr_set_state(&test_netif6, 0, IP6_ADDR_VALID);
test_netif6.output_ip6 = direct_output;
/* Reset counters after multicast traffic */
linkoutput_ctr = 0;
linkoutput_byte_ctr = 0;
/* Verify that 8000 byte payload is split into six packets */
data = pbuf_alloc(PBUF_IP, 8000, PBUF_RAM);
fail_unless(data != NULL);
err = ip6_output_if_src(data, ip_2_ip6(&my_addr), ip_2_ip6(&peer_addr),
15, 0, IP_PROTO_UDP, &test_netif6);
fail_unless(err == ERR_OK);
fail_unless(linkoutput_ctr == 6);
fail_unless(linkoutput_byte_ctr == (8000 + (6 * (IP6_HLEN + IP6_FRAG_HLEN))));
pbuf_free(data);
}
END_TEST
static void test_ip6_reass_helper(u32_t ip_id, const u16_t *segments, size_t num_segs, u16_t seglen)
{
ip_addr_t my_addr = IPADDR6_INIT_HOST(0x20010db8, 0x0, 0x0, 0x1);
size_t i;
memset(&lwip_stats.mib2, 0, sizeof(lwip_stats.mib2));
memset(&lwip_stats.ip6_frag, 0, sizeof(lwip_stats.ip6_frag));
netif_set_up(&test_netif6);
netif_ip6_addr_set(&test_netif6, 0, ip_2_ip6(&my_addr));
netif_ip6_addr_set_state(&test_netif6, 0, IP6_ADDR_VALID);
for (i = 0; i < num_segs; i++) {
u16_t seg = segments[i];
int last = seg + 1U == num_segs;
create_ip6_input_fragment(ip_id, seg * seglen, seglen, last, IP6_NEXTH_UDP);
fail_unless(lwip_stats.ip6_frag.recv == i + 1);
fail_unless(lwip_stats.ip6_frag.err == 0);
fail_unless(lwip_stats.ip6_frag.memerr == 0);
fail_unless(lwip_stats.ip6_frag.drop == 0);
if (i + 1 == num_segs) {
fail_unless(lwip_stats.mib2.ip6reasmoks == 1);
}
else {
fail_unless(lwip_stats.mib2.ip6reasmoks == 0);
}
}
}
START_TEST(test_ip6_reass)
{
#define NUM_SEGS 9
const u16_t t1[NUM_SEGS] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
const u16_t t2[NUM_SEGS] = { 8, 0, 1, 2, 3, 4, 7, 6, 5 };
const u16_t t3[NUM_SEGS] = { 1, 2, 3, 4, 5, 6, 7, 8, 0 };
const u16_t t4[NUM_SEGS] = { 8, 2, 4, 6, 7, 5, 3, 1, 0 };
LWIP_UNUSED_ARG(_i);
test_ip6_reass_helper(128, t1, NUM_SEGS, 200);
test_ip6_reass_helper(129, t2, NUM_SEGS, 208);
test_ip6_reass_helper(130, t3, NUM_SEGS, 8);
test_ip6_reass_helper(130, t4, NUM_SEGS, 1448);
}
/** Create the suite including all tests for this module */
Suite *
ip6_suite(void)
{
testfunc tests[] = {
TESTFUNC(test_ip6_ll_addr),
TESTFUNC(test_ip6_aton_ipv4mapped),
TESTFUNC(test_ip6_ntoa_ipv4mapped),
TESTFUNC(test_ip6_ntoa),
TESTFUNC(test_ip6_lladdr),
TESTFUNC(test_ip6_dest_unreachable_chained_pbuf),
TESTFUNC(test_ip6_frag_pbuf_len_assert),
TESTFUNC(test_ip6_frag),
TESTFUNC(test_ip6_reass)
};
return create_suite("IPv6", tests, sizeof(tests)/sizeof(testfunc), ip6_setup, ip6_teardown);
}
#else /* LWIP_IPV6 */
/* allow to build the unit tests without IPv6 support */
START_TEST(test_ip6_dummy)
{
LWIP_UNUSED_ARG(_i);
}
END_TEST
Suite *
ip6_suite(void)
{
testfunc tests[] = {
TESTFUNC(test_ip6_dummy),
};
return create_suite("IPv6", tests, sizeof(tests)/sizeof(testfunc), NULL, NULL);
}
#endif /* LWIP_IPV6 */

View File

@ -0,0 +1,8 @@
#ifndef LWIP_HDR_TEST_IP6_H
#define LWIP_HDR_TEST_IP6_H
#include "../lwip_check.h"
Suite* ip6_suite(void);
#endif

View File

@ -0,0 +1,52 @@
#ifndef LWIP_HDR_LWIP_CHECK_H
#define LWIP_HDR_LWIP_CHECK_H
/* Common header file for lwIP unit tests using the check framework */
#include <config.h>
#include <check.h>
#include <stdlib.h>
#define FAIL_RET() do { fail(); return; } while(0)
#define EXPECT(x) fail_unless(x)
#define EXPECT_RET(x) do { fail_unless(x); if(!(x)) { return; }} while(0)
#define EXPECT_RETX(x, y) do { fail_unless(x); if(!(x)) { return y; }} while(0)
#define EXPECT_RETNULL(x) EXPECT_RETX(x, NULL)
#if (CHECK_MAJOR_VERSION == 0 && CHECK_MINOR_VERSION < 13)
typedef struct {
TFun func;
const char *name;
} testfunc;
#define TESTFUNC(x) {(x), "" # x "" }
/* Modified function from check.h, supplying function name */
#define tcase_add_named_test(tc,tf) \
_tcase_add_test((tc),(tf).func,(tf).name,0, 0, 0, 1)
#else
/* From 0.13.0 check keeps track of the method name internally */
typedef const TTest * testfunc;
#define TESTFUNC(x) x
#define tcase_add_named_test(tc,tf) tcase_add_test(tc,tf)
#endif
/** typedef for a function returning a test suite */
typedef Suite* (suite_getter_fn)(void);
/** Create a test suite */
Suite* create_suite(const char* name, testfunc *tests, size_t num_tests, SFun setup, SFun teardown);
#ifdef LWIP_UNITTESTS_LIB
int lwip_unittests_run(void)
#endif
/* helper functions */
#define SKIP_POOL(x) (1 << x)
#define SKIP_HEAP (1 << MEMP_MAX)
void lwip_check_ensure_no_alloc(unsigned int skip);
#endif /* LWIP_HDR_LWIP_CHECK_H */

View File

@ -0,0 +1,131 @@
#include "lwip_check.h"
#include "ip4/test_ip4.h"
#include "ip6/test_ip6.h"
#include "udp/test_udp.h"
#include "tcp/test_tcp.h"
#include "tcp/test_tcp_oos.h"
#include "tcp/test_tcp_state.h"
#include "core/test_def.h"
#include "core/test_dns.h"
#include "core/test_mem.h"
#include "core/test_netif.h"
#include "core/test_pbuf.h"
#include "core/test_timers.h"
#include "etharp/test_etharp.h"
#include "dhcp/test_dhcp.h"
#include "mdns/test_mdns.h"
#include "mqtt/test_mqtt.h"
#include "api/test_sockets.h"
#include "ppp/test_pppos.h"
#include "lwip/init.h"
#if !NO_SYS
#include "lwip/tcpip.h"
#endif
/* This function is used for LWIP_RAND by some ports... */
unsigned int
lwip_port_rand(void)
{
return (unsigned int)rand();
}
Suite* create_suite(const char* name, testfunc *tests, size_t num_tests, SFun setup, SFun teardown)
{
size_t i;
Suite *s = suite_create(name);
for(i = 0; i < num_tests; i++) {
TCase *tc_core = tcase_create(name);
if ((setup != NULL) || (teardown != NULL)) {
tcase_add_checked_fixture(tc_core, setup, teardown);
}
tcase_add_named_test(tc_core, tests[i]);
suite_add_tcase(s, tc_core);
}
return s;
}
void lwip_check_ensure_no_alloc(unsigned int skip)
{
int i;
unsigned int mask;
if (!(skip & SKIP_HEAP)) {
fail_unless(lwip_stats.mem.used == 0,
"mem heap still has %d bytes allocated", lwip_stats.mem.used);
}
for (i = 0, mask = 1; i < MEMP_MAX; i++, mask <<= 1) {
if (!(skip & mask)) {
#if defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY
fail_unless(lwip_stats.memp[i]->used == 0,
"memp pool '%s' still has %d entries allocated",
lwip_stats.memp[i]->name, lwip_stats.memp[i]->used);
#else
fail_unless(lwip_stats.memp[i]->used == 0,
"memp pool %d still has %d entries allocated",
i, lwip_stats.memp[i]->used);
#endif
}
}
}
#ifdef LWIP_UNITTESTS_LIB
int lwip_unittests_run(void)
#else
int main(void)
#endif
{
int number_failed;
SRunner *sr;
size_t i;
suite_getter_fn* suites[] = {
ip4_suite,
ip6_suite,
udp_suite,
tcp_suite,
tcp_oos_suite,
tcp_state_suite,
def_suite,
dns_suite,
mem_suite,
netif_suite,
pbuf_suite,
timers_suite,
etharp_suite,
dhcp_suite,
mdns_suite,
mqtt_suite,
sockets_suite
#if PPP_SUPPORT && PPPOS_SUPPORT
, pppos_suite
#endif /* PPP_SUPPORT && PPPOS_SUPPORT */
};
size_t num = sizeof(suites)/sizeof(void*);
LWIP_ASSERT("No suites defined", num > 0);
#if NO_SYS
lwip_init();
#else
tcpip_init(NULL, NULL);
#endif
sr = srunner_create((suites[0])());
srunner_set_xml(sr, "lwip_unittests.xml");
for(i = 1; i < num; i++) {
srunner_add_suite(sr, ((suite_getter_fn*)suites[i])());
}
#ifdef LWIP_UNITTESTS_NOFORK
srunner_set_fork_status(sr, CK_NOFORK);
#endif
#ifdef LWIP_UNITTESTS_FORK
srunner_set_fork_status(sr, CK_FORK);
#endif
srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -0,0 +1,117 @@
/*
* 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: Simon Goldschmidt
*
*/
#ifndef LWIP_HDR_LWIPOPTS_H
#define LWIP_HDR_LWIPOPTS_H
#define LWIP_TESTMODE 1
#define LWIP_IPV6 1
#define LWIP_CHECKSUM_ON_COPY 1
#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK 1
#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL(printfmsg) LWIP_ASSERT("TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL", 0)
/* We link to special sys_arch.c (for basic non-waiting API layers unit tests) */
#define NO_SYS 0
#define SYS_LIGHTWEIGHT_PROT 0
#define LWIP_NETCONN !NO_SYS
#define LWIP_SOCKET !NO_SYS
#define LWIP_NETCONN_FULLDUPLEX LWIP_SOCKET
#define LWIP_NETCONN_SEM_PER_THREAD 1
#define LWIP_NETBUF_RECVINFO 1
#define LWIP_HAVE_LOOPIF 1
#define TCPIP_THREAD_TEST
/* Enable DHCP to test it */
#define LWIP_DHCP 1
/* Enable DNS, with random source port to avoid alloc in dns_init */
#define LWIP_DNS 1
#define LWIP_DNS_SECURE (LWIP_DNS_SECURE_RAND_XID | LWIP_DNS_SECURE_RAND_SRC_PORT)
/* Minimal changes to opt.h required for tcp unit tests: */
#define MEM_SIZE 17000
#define TCP_SND_QUEUELEN 40
#define MEMP_NUM_TCP_SEG TCP_SND_QUEUELEN
#define TCP_SND_BUF (12 * TCP_MSS)
#define TCP_WND (10 * TCP_MSS)
#define LWIP_WND_SCALE 1
#define TCP_RCV_SCALE 0
#define PBUF_POOL_SIZE 400 /* pbuf tests need ~200KByte */
/* Enable IGMP and MDNS for MDNS tests */
#define LWIP_IGMP 1
#define LWIP_MDNS_RESPONDER 1
#define LWIP_NUM_NETIF_CLIENT_DATA (LWIP_MDNS_RESPONDER)
/* Enable PPP and PPPOS support for PPPOS test suites */
#define PPP_SUPPORT 1
#define PPPOS_SUPPORT 1
/* Minimal changes to opt.h required for etharp unit tests: */
#define ETHARP_SUPPORT_STATIC_ENTRIES 1
#define MEMP_NUM_SYS_TIMEOUT (LWIP_NUM_SYS_TIMEOUT_INTERNAL + 8)
/* MIB2 stats are required to check IPv4 reassembly results */
#define MIB2_STATS 1
/* netif tests want to test this, so enable: */
#define LWIP_NETIF_EXT_STATUS_CALLBACK 1
/* Check lwip_stats.mem.illegal instead of asserting */
#define LWIP_MEM_ILLEGAL_FREE(msg) /* to nothing */
/* autodetect if we are running the tests on 32-bit or 64-bit */
#if defined(_WIN32) || defined(_WIN64)
#if defined(_WIN64)
#define PTR64
#else
#define PTR32
#endif
#elif defined(__GNUC__)
#if defined(__x86_64__) || defined(__ppc64__)
#define PTR64
#else
#define PTR32
#endif
#elif UINTPTR_MAX > UINT_MAX
#define PTR64
#else
#define PTR32
#endif
#ifdef PTR64
#define IPV6_FRAG_COPYHEADER 1
#endif
#endif /* LWIP_HDR_LWIPOPTS_H */

View File

@ -0,0 +1,916 @@
/*
* Copyright (c) 2015 Verisure Innovation AB
* 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: Erik Ekman <erik@kryo.se>
*
*/
#include "test_mdns.h"
#include "lwip/pbuf.h"
#include "lwip/apps/mdns.h"
#include "lwip/apps/mdns_domain.h"
#include "lwip/apps/mdns_priv.h"
START_TEST(readname_basic)
{
static const u8_t data[] = { 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00 };
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
fail_if(p == NULL);
p->payload = (void *)(size_t)data;
offset = mdns_readname(p, 0, &domain);
pbuf_free(p);
fail_unless(offset == sizeof(data));
fail_unless(domain.length == sizeof(data));
fail_if(memcmp(&domain.name, data, sizeof(data)));
}
END_TEST
START_TEST(readname_anydata)
{
static const u8_t data[] = { 0x05, 0x00, 0xFF, 0x08, 0xc0, 0x0f, 0x04, 0x7f, 0x80, 0x82, 0x88, 0x00 };
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
fail_if(p == NULL);
p->payload = (void *)(size_t)data;
offset = mdns_readname(p, 0, &domain);
pbuf_free(p);
fail_unless(offset == sizeof(data));
fail_unless(domain.length == sizeof(data));
fail_if(memcmp(&domain.name, data, sizeof(data)));
}
END_TEST
START_TEST(readname_short_buf)
{
static const u8_t data[] = { 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a' };
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
fail_if(p == NULL);
p->payload = (void *)(size_t)data;
offset = mdns_readname(p, 0, &domain);
pbuf_free(p);
fail_unless(offset == MDNS_READNAME_ERROR);
}
END_TEST
START_TEST(readname_long_label)
{
static const u8_t data[] = {
0x05, 'm', 'u', 'l', 't', 'i',
0x52, 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 0x00
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
fail_if(p == NULL);
p->payload = (void *)(size_t)data;
offset = mdns_readname(p, 0, &domain);
pbuf_free(p);
fail_unless(offset == MDNS_READNAME_ERROR);
}
END_TEST
START_TEST(readname_overflow)
{
static const u8_t data[] = {
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x00
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
fail_if(p == NULL);
p->payload = (void *)(size_t)data;
offset = mdns_readname(p, 0, &domain);
pbuf_free(p);
fail_unless(offset == MDNS_READNAME_ERROR);
}
END_TEST
START_TEST(readname_jump_earlier)
{
static const u8_t data[] = {
/* Some padding needed, not supported to jump to bytes containing dns header */
/* 0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 10 */ 0x0f, 0x0e, 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xab,
/* 20 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x0c
};
static const u8_t fullname[] = {
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
fail_if(p == NULL);
p->payload = (void *)(size_t)data;
offset = mdns_readname(p, 20, &domain);
pbuf_free(p);
fail_unless(offset == sizeof(data));
fail_unless(domain.length == sizeof(fullname));
fail_if(memcmp(&domain.name, fullname, sizeof(fullname)));
}
END_TEST
START_TEST(readname_jump_earlier_jump)
{
static const u8_t data[] = {
/* Some padding needed, not supported to jump to bytes containing dns header */
/* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x0a, 0xf2,
/* 0x10 */ 0x04, 'c', 'a', 's', 't', 0x00, 0xc0, 0x10,
/* 0x18 */ 0x05, 'm', 'u', 'l', 't', 'i', 0xc0, 0x16
};
static const u8_t fullname[] = {
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
fail_if(p == NULL);
p->payload = (void *)(size_t)data;
offset = mdns_readname(p, 0x18, &domain);
pbuf_free(p);
fail_unless(offset == sizeof(data));
fail_unless(domain.length == sizeof(fullname));
fail_if(memcmp(&domain.name, fullname, sizeof(fullname)));
}
END_TEST
START_TEST(readname_jump_maxdepth)
{
static const u8_t data[] = {
/* Some padding needed, not supported to jump to bytes containing dns header */
/* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x0a, 0xf2,
/* 0x10 */ 0x04, 'n', 'a', 'm', 'e', 0xc0, 0x27, 0x03,
/* 0x18 */ 0x03, 'd', 'n', 's', 0xc0, 0x10, 0xc0, 0x10,
/* 0x20 */ 0x04, 'd', 'e', 'e', 'p', 0xc0, 0x18, 0x00,
/* 0x28 */ 0x04, 'c', 'a', 's', 't', 0xc0, 0x20, 0xb0,
/* 0x30 */ 0x05, 'm', 'u', 'l', 't', 'i', 0xc0, 0x28
};
static const u8_t fullname[] = {
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
0x04, 'd', 'e', 'e', 'p', 0x03, 'd', 'n', 's',
0x04, 'n', 'a', 'm', 'e', 0x00
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
fail_if(p == NULL);
p->payload = (void *)(size_t)data;
offset = mdns_readname(p, 0x30, &domain);
pbuf_free(p);
fail_unless(offset == sizeof(data));
fail_unless(domain.length == sizeof(fullname));
fail_if(memcmp(&domain.name, fullname, sizeof(fullname)));
}
END_TEST
START_TEST(readname_jump_later)
{
static const u8_t data[] = {
/* 0x00 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x10, 0x00, 0x01, 0x40,
/* 0x10 */ 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xab
};
static const u8_t fullname[] = {
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
fail_if(p == NULL);
p->payload = (void *)(size_t)data;
offset = mdns_readname(p, 0, &domain);
pbuf_free(p);
fail_unless(offset == 13);
fail_unless(domain.length == sizeof(fullname));
fail_if(memcmp(&domain.name, fullname, sizeof(fullname)));
}
END_TEST
START_TEST(readname_half_jump)
{
static const u8_t data[] = {
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
fail_if(p == NULL);
p->payload = (void *)(size_t)data;
offset = mdns_readname(p, 0, &domain);
pbuf_free(p);
fail_unless(offset == MDNS_READNAME_ERROR);
}
END_TEST
START_TEST(readname_jump_toolong)
{
static const u8_t data[] = {
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc2, 0x10, 0x00, 0x01, 0x40
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
fail_if(p == NULL);
p->payload = (void *)(size_t)data;
offset = mdns_readname(p, 0, &domain);
pbuf_free(p);
fail_unless(offset == MDNS_READNAME_ERROR);
}
END_TEST
START_TEST(readname_jump_loop_label)
{
static const u8_t data[] = {
/* 0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 10 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x10
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
fail_if(p == NULL);
p->payload = (void *)(size_t)data;
offset = mdns_readname(p, 10, &domain);
pbuf_free(p);
fail_unless(offset == MDNS_READNAME_ERROR);
}
END_TEST
START_TEST(readname_jump_loop_jump)
{
static const u8_t data[] = {
/* 0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 10 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x15
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
fail_if(p == NULL);
p->payload = (void *)(size_t)data;
offset = mdns_readname(p, 10, &domain);
pbuf_free(p);
fail_unless(offset == MDNS_READNAME_ERROR);
}
END_TEST
START_TEST(add_label_basic)
{
static const u8_t data[] = { 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00 };
struct mdns_domain domain;
err_t res;
LWIP_UNUSED_ARG(_i);
memset(&domain, 0, sizeof(domain));
res = mdns_domain_add_label(&domain, "multi", 5);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain, "cast", 4);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain, NULL, 0);
fail_unless(res == ERR_OK);
fail_unless(domain.length == sizeof(data));
fail_if(memcmp(&domain.name, data, sizeof(data)));
}
END_TEST
START_TEST(add_label_long_label)
{
static const char *toolong = "abcdefghijklmnopqrstuvwxyz0123456789-abcdefghijklmnopqrstuvwxyz0123456789-abcdefghijklmnopqrstuvwxyz0123456789-";
struct mdns_domain domain;
err_t res;
LWIP_UNUSED_ARG(_i);
memset(&domain, 0, sizeof(domain));
res = mdns_domain_add_label(&domain, "multi", 5);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain, toolong, (u8_t)strlen(toolong));
fail_unless(res == ERR_VAL);
}
END_TEST
START_TEST(add_label_full)
{
static const char *label = "0123456789abcdef0123456789abcdef";
struct mdns_domain domain;
err_t res;
LWIP_UNUSED_ARG(_i);
memset(&domain, 0, sizeof(domain));
res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
fail_unless(res == ERR_OK);
fail_unless(domain.length == 33);
res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
fail_unless(res == ERR_OK);
fail_unless(domain.length == 66);
res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
fail_unless(res == ERR_OK);
fail_unless(domain.length == 99);
res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
fail_unless(res == ERR_OK);
fail_unless(domain.length == 132);
res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
fail_unless(res == ERR_OK);
fail_unless(domain.length == 165);
res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
fail_unless(res == ERR_OK);
fail_unless(domain.length == 198);
res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
fail_unless(res == ERR_OK);
fail_unless(domain.length == 231);
res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
fail_unless(res == ERR_VAL);
fail_unless(domain.length == 231);
res = mdns_domain_add_label(&domain, label, 25);
fail_unless(res == ERR_VAL);
fail_unless(domain.length == 231);
res = mdns_domain_add_label(&domain, label, 24);
fail_unless(res == ERR_VAL);
fail_unless(domain.length == 231);
res = mdns_domain_add_label(&domain, label, 23);
fail_unless(res == ERR_OK);
fail_unless(domain.length == 255);
res = mdns_domain_add_label(&domain, NULL, 0);
fail_unless(res == ERR_OK);
fail_unless(domain.length == 256);
res = mdns_domain_add_label(&domain, NULL, 0);
fail_unless(res == ERR_VAL);
fail_unless(domain.length == 256);
}
END_TEST
START_TEST(domain_eq_basic)
{
static const u8_t data[] = {
0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00
};
struct mdns_domain domain1, domain2;
err_t res;
LWIP_UNUSED_ARG(_i);
memset(&domain1, 0, sizeof(domain1));
res = mdns_domain_add_label(&domain1, "multi", 5);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain1, "cast", 4);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain1, NULL, 0);
fail_unless(res == ERR_OK);
fail_unless(domain1.length == sizeof(data));
memset(&domain2, 0, sizeof(domain2));
res = mdns_domain_add_label(&domain2, "multi", 5);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain2, "cast", 4);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain2, NULL, 0);
fail_unless(res == ERR_OK);
fail_unless(mdns_domain_eq(&domain1, &domain2));
}
END_TEST
START_TEST(domain_eq_diff)
{
struct mdns_domain domain1, domain2;
err_t res;
LWIP_UNUSED_ARG(_i);
memset(&domain1, 0, sizeof(domain1));
res = mdns_domain_add_label(&domain1, "multi", 5);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain1, "base", 4);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain1, NULL, 0);
fail_unless(res == ERR_OK);
memset(&domain2, 0, sizeof(domain2));
res = mdns_domain_add_label(&domain2, "multi", 5);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain2, "cast", 4);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain2, NULL, 0);
fail_unless(res == ERR_OK);
fail_if(mdns_domain_eq(&domain1, &domain2));
}
END_TEST
START_TEST(domain_eq_case)
{
struct mdns_domain domain1, domain2;
err_t res;
LWIP_UNUSED_ARG(_i);
memset(&domain1, 0, sizeof(domain1));
res = mdns_domain_add_label(&domain1, "multi", 5);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain1, "cast", 4);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain1, NULL, 0);
fail_unless(res == ERR_OK);
memset(&domain2, 0, sizeof(domain2));
res = mdns_domain_add_label(&domain2, "MulTI", 5);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain2, "casT", 4);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain2, NULL, 0);
fail_unless(res == ERR_OK);
fail_unless(mdns_domain_eq(&domain1, &domain2));
}
END_TEST
START_TEST(domain_eq_anydata)
{
static const u8_t data1[] = { 0x05, 0xcc, 0xdc, 0x00, 0xa0 };
static const u8_t data2[] = { 0x7f, 0x8c, 0x01, 0xff, 0xcf };
struct mdns_domain domain1, domain2;
err_t res;
LWIP_UNUSED_ARG(_i);
memset(&domain1, 0, sizeof(domain1));
res = mdns_domain_add_label(&domain1, (const char*)data1, sizeof(data1));
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain1, "cast", 4);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain1, (const char*)data2, sizeof(data2));
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain1, NULL, 0);
fail_unless(res == ERR_OK);
memset(&domain2, 0, sizeof(domain2));
res = mdns_domain_add_label(&domain2, (const char*)data1, sizeof(data1));
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain2, "casT", 4);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain2, (const char*)data2, sizeof(data2));
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain2, NULL, 0);
fail_unless(res == ERR_OK);
fail_unless(mdns_domain_eq(&domain1, &domain2));
}
END_TEST
START_TEST(domain_eq_length)
{
struct mdns_domain domain1, domain2;
err_t res;
LWIP_UNUSED_ARG(_i);
memset(&domain1, 0, sizeof(domain1));
memset(domain1.name, 0xAA, sizeof(MDNS_DOMAIN_MAXLEN));
res = mdns_domain_add_label(&domain1, "multi", 5);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain1, "cast", 4);
fail_unless(res == ERR_OK);
memset(&domain2, 0, sizeof(domain2));
memset(domain2.name, 0xBB, sizeof(MDNS_DOMAIN_MAXLEN));
res = mdns_domain_add_label(&domain2, "multi", 5);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain2, "cast", 4);
fail_unless(res == ERR_OK);
fail_unless(mdns_domain_eq(&domain1, &domain2));
}
END_TEST
START_TEST(compress_full_match)
{
static const u8_t data[] = {
0x00, 0x00,
0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
u16_t length;
err_t res;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
fail_if(p == NULL);
p->payload = (void *)(size_t)data;
memset(&domain, 0, sizeof(domain));
res = mdns_domain_add_label(&domain, "foobar", 6);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain, "local", 5);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain, NULL, 0);
fail_unless(res == ERR_OK);
offset = 2;
length = mdns_compress_domain(p, &offset, &domain);
/* Write 0 bytes, then a jump to addr 2 */
fail_unless(length == 0);
fail_unless(offset == 2);
pbuf_free(p);
}
END_TEST
START_TEST(compress_full_match_subset)
{
static const u8_t data[] = {
0x00, 0x00,
0x02, 'g', 'o', 0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
u16_t length;
err_t res;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
fail_if(p == NULL);
p->payload = (void *)(size_t)data;
memset(&domain, 0, sizeof(domain));
res = mdns_domain_add_label(&domain, "foobar", 6);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain, "local", 5);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain, NULL, 0);
fail_unless(res == ERR_OK);
offset = 2;
length = mdns_compress_domain(p, &offset, &domain);
/* Write 0 bytes, then a jump to addr 5 */
fail_unless(length == 0);
fail_unless(offset == 5);
pbuf_free(p);
}
END_TEST
START_TEST(compress_full_match_jump)
{
static const u8_t data[] = {
/* 0x00 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
/* 0x10 */ 0x04, 'l', 'w', 'i', 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xc0, 0x00, 0x02, 0x00,
/* 0x20 */ 0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0xc0, 0x15
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
u16_t length;
err_t res;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
fail_if(p == NULL);
p->payload = (void *)(size_t)data;
memset(&domain, 0, sizeof(domain));
res = mdns_domain_add_label(&domain, "foobar", 6);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain, "local", 5);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain, NULL, 0);
fail_unless(res == ERR_OK);
offset = 0x20;
length = mdns_compress_domain(p, &offset, &domain);
/* Write 0 bytes, then a jump to addr 0x20 */
fail_unless(length == 0);
fail_unless(offset == 0x20);
pbuf_free(p);
}
END_TEST
START_TEST(compress_no_match)
{
static const u8_t data[] = {
0x00, 0x00,
0x04, 'l', 'w', 'i', 'p', 0x05, 'w', 'i', 'k', 'i', 'a', 0x03, 'c', 'o', 'm', 0x00
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
u16_t length;
err_t res;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
fail_if(p == NULL);
p->payload = (void *)(size_t)data;
memset(&domain, 0, sizeof(domain));
res = mdns_domain_add_label(&domain, "foobar", 6);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain, "local", 5);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain, NULL, 0);
fail_unless(res == ERR_OK);
offset = 2;
length = mdns_compress_domain(p, &offset, &domain);
/* Write all bytes, no jump */
fail_unless(length == domain.length);
pbuf_free(p);
}
END_TEST
START_TEST(compress_2nd_label)
{
static const u8_t data[] = {
0x00, 0x00,
0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
u16_t length;
err_t res;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
fail_if(p == NULL);
p->payload = (void *)(size_t)data;
memset(&domain, 0, sizeof(domain));
res = mdns_domain_add_label(&domain, "lwip", 4);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain, "local", 5);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain, NULL, 0);
fail_unless(res == ERR_OK);
offset = 2;
length = mdns_compress_domain(p, &offset, &domain);
/* Write 5 bytes, then a jump to addr 9 */
fail_unless(length == 5);
fail_unless(offset == 9);
pbuf_free(p);
}
END_TEST
START_TEST(compress_2nd_label_short)
{
static const u8_t data[] = {
0x00, 0x00,
0x04, 'l', 'w', 'i', 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
u16_t length;
err_t res;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
fail_if(p == NULL);
p->payload = (void *)(size_t)data;
memset(&domain, 0, sizeof(domain));
res = mdns_domain_add_label(&domain, "foobar", 6);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain, "local", 5);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain, NULL, 0);
fail_unless(res == ERR_OK);
offset = 2;
length = mdns_compress_domain(p, &offset, &domain);
/* Write 5 bytes, then a jump to addr 7 */
fail_unless(length == 7);
fail_unless(offset == 7);
pbuf_free(p);
}
END_TEST
START_TEST(compress_jump_to_jump)
{
static const u8_t data[] = {
/* 0x00 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
/* 0x10 */ 0x04, 'l', 'w', 'i', 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xc0, 0x00, 0x02, 0x00,
/* 0x20 */ 0x07, 'b', 'a', 'n', 'a', 'n', 'a', 's', 0xc0, 0x15
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
u16_t length;
err_t res;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
fail_if(p == NULL);
p->payload = (void *)(size_t)data;
memset(&domain, 0, sizeof(domain));
res = mdns_domain_add_label(&domain, "foobar", 6);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain, "local", 5);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain, NULL, 0);
fail_unless(res == ERR_OK);
offset = 0x20;
length = mdns_compress_domain(p, &offset, &domain);
/* Don't compress if jump would be to a jump */
fail_unless(length == domain.length);
offset = 0x10;
length = mdns_compress_domain(p, &offset, &domain);
/* Write 7 bytes, then a jump to addr 0x15 */
fail_unless(length == 7);
fail_unless(offset == 0x15);
pbuf_free(p);
}
END_TEST
START_TEST(compress_long_match)
{
static const u8_t data[] = {
0x00, 0x00,
0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0x05, 'l', 'o', 'c', 'a', 'l', 0x03, 'c', 'o', 'm', 0x00
};
struct pbuf *p;
struct mdns_domain domain;
u16_t offset;
u16_t length;
err_t res;
LWIP_UNUSED_ARG(_i);
p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
fail_if(p == NULL);
p->payload = (void *)(size_t)data;
memset(&domain, 0, sizeof(domain));
res = mdns_domain_add_label(&domain, "foobar", 6);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain, "local", 5);
fail_unless(res == ERR_OK);
res = mdns_domain_add_label(&domain, NULL, 0);
fail_unless(res == ERR_OK);
offset = 2;
length = mdns_compress_domain(p, &offset, &domain);
fail_unless(length == domain.length);
pbuf_free(p);
}
END_TEST
Suite* mdns_suite(void)
{
testfunc tests[] = {
TESTFUNC(readname_basic),
TESTFUNC(readname_anydata),
TESTFUNC(readname_short_buf),
TESTFUNC(readname_long_label),
TESTFUNC(readname_overflow),
TESTFUNC(readname_jump_earlier),
TESTFUNC(readname_jump_earlier_jump),
TESTFUNC(readname_jump_maxdepth),
TESTFUNC(readname_jump_later),
TESTFUNC(readname_half_jump),
TESTFUNC(readname_jump_toolong),
TESTFUNC(readname_jump_loop_label),
TESTFUNC(readname_jump_loop_jump),
TESTFUNC(add_label_basic),
TESTFUNC(add_label_long_label),
TESTFUNC(add_label_full),
TESTFUNC(domain_eq_basic),
TESTFUNC(domain_eq_diff),
TESTFUNC(domain_eq_case),
TESTFUNC(domain_eq_anydata),
TESTFUNC(domain_eq_length),
TESTFUNC(compress_full_match),
TESTFUNC(compress_full_match_subset),
TESTFUNC(compress_full_match_jump),
TESTFUNC(compress_no_match),
TESTFUNC(compress_2nd_label),
TESTFUNC(compress_2nd_label_short),
TESTFUNC(compress_jump_to_jump),
TESTFUNC(compress_long_match),
};
return create_suite("MDNS", tests, sizeof(tests)/sizeof(testfunc), NULL, NULL);
}

View File

@ -0,0 +1,8 @@
#ifndef LWIP_HDR_TEST_MDNS_H__
#define LWIP_HDR_TEST_MDNS_H__
#include "../lwip_check.h"
Suite* mdns_suite(void);
#endif

View File

@ -0,0 +1,115 @@
#include "test_mqtt.h"
#include "lwip/pbuf.h"
#include "lwip/apps/mqtt.h"
#include "lwip/apps/mqtt_priv.h"
#include "lwip/netif.h"
const ip_addr_t test_mqtt_local_ip = IPADDR4_INIT_BYTES(192, 168, 1, 1);
const ip_addr_t test_mqtt_remote_ip = IPADDR4_INIT_BYTES(192, 168, 1, 2);
const ip_addr_t test_mqtt_netmask = IPADDR4_INIT_BYTES(255, 255, 255, 0);
static err_t test_mqtt_netif_output(struct netif *netif, struct pbuf *p,
const ip4_addr_t *ipaddr)
{
LWIP_UNUSED_ARG(netif);
LWIP_UNUSED_ARG(ipaddr);
LWIP_UNUSED_ARG(p);
return ERR_OK;
}
static void
test_mqtt_init_netif(struct netif *netif, const ip_addr_t *ip_addr, const ip_addr_t *netmask)
{
struct netif *n;
memset(netif, 0, sizeof(struct netif));
netif->output = test_mqtt_netif_output;
netif->flags |= NETIF_FLAG_UP | NETIF_FLAG_LINK_UP;
ip_addr_copy_from_ip4(netif->netmask, *ip_2_ip4(netmask));
ip_addr_copy_from_ip4(netif->ip_addr, *ip_2_ip4(ip_addr));
for (n = netif_list; n != NULL; n = n->next) {
if (n == netif) {
return;
}
}
netif->next = NULL;
netif_list = netif;
}
/* Setups/teardown functions */
static struct netif *old_netif_list;
static struct netif *old_netif_default;
static void
mqtt_setup(void)
{
old_netif_list = netif_list;
old_netif_default = netif_default;
netif_list = NULL;
netif_default = NULL;
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
static void
mqtt_teardown(void)
{
netif_list = NULL;
netif_default = NULL;
/* restore netif_list for next tests (e.g. loopif) */
netif_list = old_netif_list;
netif_default = old_netif_default;
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
static void test_mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status)
{
LWIP_UNUSED_ARG(client);
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(status);
}
START_TEST(basic_connect)
{
mqtt_client_t* client;
struct netif netif;
err_t err;
struct mqtt_connect_client_info_t client_info = {
"dumm",
NULL, NULL,
10,
NULL, NULL, 0, 0, 0
};
struct pbuf *p;
unsigned char rxbuf[] = {0x20, 0x02, 0x00, 0x00};
LWIP_UNUSED_ARG(_i);
test_mqtt_init_netif(&netif, &test_mqtt_local_ip, &test_mqtt_netmask);
client = mqtt_client_new();
fail_unless(client != NULL);
err = mqtt_client_connect(client, &test_mqtt_remote_ip, 1234, test_mqtt_connection_cb, NULL, &client_info);
fail_unless(err == ERR_OK);
client->conn->connected(client->conn->callback_arg, client->conn, ERR_OK);
p = pbuf_alloc(PBUF_RAW, sizeof(rxbuf), PBUF_REF);
fail_unless(p != NULL);
p->payload = rxbuf;
/* since we hack the rx path, we have to hack the rx window, too: */
client->conn->rcv_wnd -= p->tot_len;
if (client->conn->recv(client->conn->callback_arg, client->conn, p, ERR_OK) != ERR_OK) {
pbuf_free(p);
}
mqtt_disconnect(client);
/* fixme: mqtt_client_fre() is missing... */
mem_free(client);
}
END_TEST
Suite* mqtt_suite(void)
{
testfunc tests[] = {
TESTFUNC(basic_connect),
};
return create_suite("MQTT", tests, sizeof(tests)/sizeof(testfunc), mqtt_setup, mqtt_teardown);
}

View File

@ -0,0 +1,8 @@
#ifndef LWIP_HDR_TEST_MQTT_H__
#define LWIP_HDR_TEST_MQTT_H__
#include "../lwip_check.h"
Suite* mqtt_suite(void);
#endif

View File

@ -0,0 +1,67 @@
#include "test_pppos.h"
#include "lwip/netif.h"
#include "netif/ppp/pppos.h"
#include "netif/ppp/ppp.h"
#if PPP_SUPPORT && PPPOS_SUPPORT
static struct netif pppos_netif;
static ppp_pcb *ppp;
static u32_t ppp_output_cb(ppp_pcb *pcb, const void *data, u32_t len, void *ctx)
{
LWIP_UNUSED_ARG(pcb);
LWIP_UNUSED_ARG(data);
LWIP_UNUSED_ARG(len);
LWIP_UNUSED_ARG(ctx);
return 0;
}
static void ppp_link_status_cb(ppp_pcb *pcb, int err_code, void *ctx)
{
LWIP_UNUSED_ARG(pcb);
LWIP_UNUSED_ARG(err_code);
LWIP_UNUSED_ARG(ctx);
}
static void pppos_setup(void)
{
ppp = pppos_create(&pppos_netif, ppp_output_cb, ppp_link_status_cb, NULL);
fail_if(ppp == NULL);
ppp_connect(ppp, 0);
}
static void pppos_teardown(void)
{
}
START_TEST(test_pppos_empty_packet_with_valid_fcs)
{
u8_t two_breaks[] = { 0x7e, 0, 0, 0x7e };
u8_t other_packet[] = { 0x7e, 0x7d, 0x20, 0x00, 0x7e };
/* Set internal states of the underlying pcb */
pppos_pcb *pppos = (pppos_pcb *)ppp->link_ctx_cb;
LWIP_UNUSED_ARG(_i);
pppos->open = 1; /* Pretend the connection is open already */
pppos->in_accm[0] = 0xf0; /* Make sure 0x0's are not escaped chars */
pppos_input(ppp, two_breaks, sizeof(two_breaks));
pppos_input(ppp, other_packet, sizeof(other_packet));
}
END_TEST
/** Create the suite including all tests for this module */
Suite *
pppos_suite(void)
{
testfunc tests[] = {
TESTFUNC(test_pppos_empty_packet_with_valid_fcs)
};
return create_suite("PPPOS", tests, sizeof(tests)/sizeof(testfunc), pppos_setup, pppos_teardown);
}
#endif /* PPP_SUPPORT && PPPOS_SUPPORT */

View File

@ -0,0 +1,13 @@
#ifndef LWIP_HDR_TEST_PPPOS_H
#define LWIP_HDR_TEST_PPPOS_H
#include "../lwip_check.h"
#include "netif/ppp/ppp.h"
#if PPP_SUPPORT && PPPOS_SUPPORT
Suite* pppos_suite(void);
#endif /* PPP_SUPPORT && PPPOS_SUPPORT */
#endif /* LWIP_HDR_TEST_PPPOS_H */

View File

@ -0,0 +1,325 @@
#include "tcp_helper.h"
#include "lwip/priv/tcp_priv.h"
#include "lwip/stats.h"
#include "lwip/pbuf.h"
#include "lwip/inet.h"
#include "lwip/inet_chksum.h"
#include "lwip/ip_addr.h"
#if !LWIP_STATS || !TCP_STATS || !MEMP_STATS
#error "This tests needs TCP- and MEMP-statistics enabled"
#endif
const ip_addr_t test_local_ip = IPADDR4_INIT_BYTES(192, 168, 1, 1);
const ip_addr_t test_remote_ip = IPADDR4_INIT_BYTES(192, 168, 1, 2);
const ip_addr_t test_netmask = IPADDR4_INIT_BYTES(255, 255, 255, 0);
/** Remove all pcbs on the given list. */
static void
tcp_remove(struct tcp_pcb* pcb_list)
{
struct tcp_pcb *pcb = pcb_list;
struct tcp_pcb *pcb2;
while(pcb != NULL) {
pcb2 = pcb;
pcb = pcb->next;
if (pcb2->state == LISTEN) {
tcp_close(pcb2);
} else {
tcp_abort(pcb2);
}
}
}
/** Remove all pcbs on listen-, active- and time-wait-list (bound- isn't exported). */
void
tcp_remove_all(void)
{
tcp_remove(tcp_listen_pcbs.pcbs);
tcp_remove(tcp_bound_pcbs);
tcp_remove(tcp_active_pcbs);
tcp_remove(tcp_tw_pcbs);
fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB_LISTEN) == 0);
fail_unless(MEMP_STATS_GET(used, MEMP_TCP_SEG) == 0);
fail_unless(MEMP_STATS_GET(used, MEMP_PBUF_POOL) == 0);
}
/** Create a TCP segment usable for passing to tcp_input */
static struct pbuf*
tcp_create_segment_wnd(ip_addr_t* src_ip, ip_addr_t* dst_ip,
u16_t src_port, u16_t dst_port, void* data, size_t data_len,
u32_t seqno, u32_t ackno, u8_t headerflags, u16_t wnd)
{
struct pbuf *p, *q;
struct ip_hdr* iphdr;
struct tcp_hdr* tcphdr;
u16_t pbuf_len = (u16_t)(sizeof(struct ip_hdr) + sizeof(struct tcp_hdr) + data_len);
LWIP_ASSERT("data_len too big", data_len <= 0xFFFF);
p = pbuf_alloc(PBUF_RAW, pbuf_len, PBUF_POOL);
EXPECT_RETNULL(p != NULL);
/* first pbuf must be big enough to hold the headers */
EXPECT_RETNULL(p->len >= (sizeof(struct ip_hdr) + sizeof(struct tcp_hdr)));
if (data_len > 0) {
/* first pbuf must be big enough to hold at least 1 data byte, too */
EXPECT_RETNULL(p->len > (sizeof(struct ip_hdr) + sizeof(struct tcp_hdr)));
}
for(q = p; q != NULL; q = q->next) {
memset(q->payload, 0, q->len);
}
iphdr = (struct ip_hdr*)p->payload;
/* fill IP header */
iphdr->dest.addr = ip_2_ip4(dst_ip)->addr;
iphdr->src.addr = ip_2_ip4(src_ip)->addr;
IPH_VHL_SET(iphdr, 4, IP_HLEN / 4);
IPH_TOS_SET(iphdr, 0);
IPH_LEN_SET(iphdr, htons(p->tot_len));
IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
/* let p point to TCP header */
pbuf_header(p, -(s16_t)sizeof(struct ip_hdr));
tcphdr = (struct tcp_hdr*)p->payload;
tcphdr->src = htons(src_port);
tcphdr->dest = htons(dst_port);
tcphdr->seqno = htonl(seqno);
tcphdr->ackno = htonl(ackno);
TCPH_HDRLEN_SET(tcphdr, sizeof(struct tcp_hdr)/4);
TCPH_FLAGS_SET(tcphdr, headerflags);
tcphdr->wnd = htons(wnd);
if (data_len > 0) {
/* let p point to TCP data */
pbuf_header(p, -(s16_t)sizeof(struct tcp_hdr));
/* copy data */
pbuf_take(p, data, (u16_t)data_len);
/* let p point to TCP header again */
pbuf_header(p, sizeof(struct tcp_hdr));
}
/* calculate checksum */
tcphdr->chksum = ip_chksum_pseudo(p,
IP_PROTO_TCP, p->tot_len, src_ip, dst_ip);
pbuf_header(p, sizeof(struct ip_hdr));
return p;
}
/** Create a TCP segment usable for passing to tcp_input */
struct pbuf*
tcp_create_segment(ip_addr_t* src_ip, ip_addr_t* dst_ip,
u16_t src_port, u16_t dst_port, void* data, size_t data_len,
u32_t seqno, u32_t ackno, u8_t headerflags)
{
return tcp_create_segment_wnd(src_ip, dst_ip, src_port, dst_port, data,
data_len, seqno, ackno, headerflags, TCP_WND);
}
/** Create a TCP segment usable for passing to tcp_input
* - IP-addresses, ports, seqno and ackno are taken from pcb
* - seqno and ackno can be altered with an offset
*/
struct pbuf*
tcp_create_rx_segment(struct tcp_pcb* pcb, void* data, size_t data_len, u32_t seqno_offset,
u32_t ackno_offset, u8_t headerflags)
{
return tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, pcb->remote_port, pcb->local_port,
data, data_len, pcb->rcv_nxt + seqno_offset, pcb->lastack + ackno_offset, headerflags);
}
/** Create a TCP segment usable for passing to tcp_input
* - IP-addresses, ports, seqno and ackno are taken from pcb
* - seqno and ackno can be altered with an offset
* - TCP window can be adjusted
*/
struct pbuf* tcp_create_rx_segment_wnd(struct tcp_pcb* pcb, void* data, size_t data_len,
u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags, u16_t wnd)
{
return tcp_create_segment_wnd(&pcb->remote_ip, &pcb->local_ip, pcb->remote_port, pcb->local_port,
data, data_len, pcb->rcv_nxt + seqno_offset, pcb->lastack + ackno_offset, headerflags, wnd);
}
/** Safely bring a tcp_pcb into the requested state */
void
tcp_set_state(struct tcp_pcb* pcb, enum tcp_state state, const ip_addr_t* local_ip,
const ip_addr_t* remote_ip, u16_t local_port, u16_t remote_port)
{
u32_t iss;
/* @todo: are these all states? */
/* @todo: remove from previous list */
pcb->state = state;
iss = tcp_next_iss(pcb);
pcb->snd_wl2 = iss;
pcb->snd_nxt = iss;
pcb->lastack = iss;
pcb->snd_lbb = iss;
if (state == ESTABLISHED) {
TCP_REG(&tcp_active_pcbs, pcb);
ip_addr_copy(pcb->local_ip, *local_ip);
pcb->local_port = local_port;
ip_addr_copy(pcb->remote_ip, *remote_ip);
pcb->remote_port = remote_port;
} else if(state == LISTEN) {
TCP_REG(&tcp_listen_pcbs.pcbs, pcb);
ip_addr_copy(pcb->local_ip, *local_ip);
pcb->local_port = local_port;
} else if(state == TIME_WAIT) {
TCP_REG(&tcp_tw_pcbs, pcb);
ip_addr_copy(pcb->local_ip, *local_ip);
pcb->local_port = local_port;
ip_addr_copy(pcb->remote_ip, *remote_ip);
pcb->remote_port = remote_port;
} else {
fail();
}
}
void
test_tcp_counters_err(void* arg, err_t err)
{
struct test_tcp_counters* counters = (struct test_tcp_counters*)arg;
EXPECT_RET(arg != NULL);
counters->err_calls++;
counters->last_err = err;
}
static void
test_tcp_counters_check_rxdata(struct test_tcp_counters* counters, struct pbuf* p)
{
struct pbuf* q;
u32_t i, received;
if(counters->expected_data == NULL) {
/* no data to compare */
return;
}
EXPECT_RET(counters->recved_bytes + p->tot_len <= counters->expected_data_len);
received = counters->recved_bytes;
for(q = p; q != NULL; q = q->next) {
char *data = (char*)q->payload;
for(i = 0; i < q->len; i++) {
EXPECT_RET(data[i] == counters->expected_data[received]);
received++;
}
}
EXPECT(received == counters->recved_bytes + p->tot_len);
}
err_t
test_tcp_counters_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err)
{
struct test_tcp_counters* counters = (struct test_tcp_counters*)arg;
EXPECT_RETX(arg != NULL, ERR_OK);
EXPECT_RETX(pcb != NULL, ERR_OK);
EXPECT_RETX(err == ERR_OK, ERR_OK);
if (p != NULL) {
if (counters->close_calls == 0) {
counters->recv_calls++;
test_tcp_counters_check_rxdata(counters, p);
counters->recved_bytes += p->tot_len;
} else {
counters->recv_calls_after_close++;
counters->recved_bytes_after_close += p->tot_len;
}
pbuf_free(p);
} else {
counters->close_calls++;
}
EXPECT(counters->recv_calls_after_close == 0 && counters->recved_bytes_after_close == 0);
return ERR_OK;
}
/** Allocate a pcb and set up the test_tcp_counters_* callbacks */
struct tcp_pcb*
test_tcp_new_counters_pcb(struct test_tcp_counters* counters)
{
struct tcp_pcb* pcb = tcp_new();
if (pcb != NULL) {
/* set up args and callbacks */
tcp_arg(pcb, counters);
tcp_recv(pcb, test_tcp_counters_recv);
tcp_err(pcb, test_tcp_counters_err);
pcb->snd_wnd = TCP_WND;
pcb->snd_wnd_max = TCP_WND;
}
return pcb;
}
/** Calls tcp_input() after adjusting current_iphdr_dest */
void test_tcp_input(struct pbuf *p, struct netif *inp)
{
struct ip_hdr *iphdr = (struct ip_hdr*)p->payload;
/* these lines are a hack, don't use them as an example :-) */
ip_addr_copy_from_ip4(*ip_current_dest_addr(), iphdr->dest);
ip_addr_copy_from_ip4(*ip_current_src_addr(), iphdr->src);
ip_current_netif() = inp;
ip_data.current_ip4_header = iphdr;
ip_data.current_input_netif = inp;
/* since adding IPv6, p->payload must point to tcp header, not ip header */
pbuf_header(p, -(s16_t)sizeof(struct ip_hdr));
tcp_input(p, inp);
ip_addr_set_zero(ip_current_dest_addr());
ip_addr_set_zero(ip_current_src_addr());
ip_current_netif() = NULL;
ip_data.current_ip4_header = NULL;
}
static err_t test_tcp_netif_output(struct netif *netif, struct pbuf *p,
const ip4_addr_t *ipaddr)
{
struct test_tcp_txcounters *txcounters = (struct test_tcp_txcounters*)netif->state;
LWIP_UNUSED_ARG(ipaddr);
if (txcounters != NULL)
{
txcounters->num_tx_calls++;
txcounters->num_tx_bytes += p->tot_len;
if (txcounters->copy_tx_packets) {
struct pbuf *p_copy = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
err_t err;
EXPECT(p_copy != NULL);
err = pbuf_copy(p_copy, p);
EXPECT(err == ERR_OK);
if (txcounters->tx_packets == NULL) {
txcounters->tx_packets = p_copy;
} else {
pbuf_cat(txcounters->tx_packets, p_copy);
}
}
}
return ERR_OK;
}
void test_tcp_init_netif(struct netif *netif, struct test_tcp_txcounters *txcounters,
const ip_addr_t *ip_addr, const ip_addr_t *netmask)
{
struct netif *n;
memset(netif, 0, sizeof(struct netif));
if (txcounters != NULL) {
memset(txcounters, 0, sizeof(struct test_tcp_txcounters));
netif->state = txcounters;
}
netif->output = test_tcp_netif_output;
netif->flags |= NETIF_FLAG_UP | NETIF_FLAG_LINK_UP;
ip_addr_copy_from_ip4(netif->netmask, *ip_2_ip4(netmask));
ip_addr_copy_from_ip4(netif->ip_addr, *ip_2_ip4(ip_addr));
for (n = netif_list; n != NULL; n = n->next) {
if (n == netif) {
return;
}
}
netif->next = NULL;
netif_list = netif;
}

View File

@ -0,0 +1,58 @@
#ifndef LWIP_HDR_TCP_HELPER_H
#define LWIP_HDR_TCP_HELPER_H
#include "../lwip_check.h"
#include "lwip/arch.h"
#include "lwip/tcp.h"
#include "lwip/netif.h"
/* counters used for test_tcp_counters_* callback functions */
struct test_tcp_counters {
u32_t recv_calls;
u32_t recved_bytes;
u32_t recv_calls_after_close;
u32_t recved_bytes_after_close;
u32_t close_calls;
u32_t err_calls;
err_t last_err;
char* expected_data;
u32_t expected_data_len;
};
struct test_tcp_txcounters {
u32_t num_tx_calls;
u32_t num_tx_bytes;
u8_t copy_tx_packets;
struct pbuf *tx_packets;
};
extern const ip_addr_t test_local_ip;
extern const ip_addr_t test_remote_ip;
extern const ip_addr_t test_netmask;
#define TEST_REMOTE_PORT 0x100
#define TEST_LOCAL_PORT 0x101
/* Helper functions */
void tcp_remove_all(void);
struct pbuf* tcp_create_segment(ip_addr_t* src_ip, ip_addr_t* dst_ip,
u16_t src_port, u16_t dst_port, void* data, size_t data_len,
u32_t seqno, u32_t ackno, u8_t headerflags);
struct pbuf* tcp_create_rx_segment(struct tcp_pcb* pcb, void* data, size_t data_len,
u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags);
struct pbuf* tcp_create_rx_segment_wnd(struct tcp_pcb* pcb, void* data, size_t data_len,
u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags, u16_t wnd);
void tcp_set_state(struct tcp_pcb* pcb, enum tcp_state state, const ip_addr_t* local_ip,
const ip_addr_t* remote_ip, u16_t local_port, u16_t remote_port);
void test_tcp_counters_err(void* arg, err_t err);
err_t test_tcp_counters_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err);
struct tcp_pcb* test_tcp_new_counters_pcb(struct test_tcp_counters* counters);
void test_tcp_input(struct pbuf *p, struct netif *inp);
void test_tcp_init_netif(struct netif *netif, struct test_tcp_txcounters *txcounters,
const ip_addr_t *ip_addr, const ip_addr_t *netmask);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
#ifndef LWIP_HDR_TEST_TCP_H
#define LWIP_HDR_TEST_TCP_H
#include "../lwip_check.h"
Suite *tcp_suite(void);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
#ifndef LWIP_HDR_TEST_TCP_OOS_H
#define LWIP_HDR_TEST_TCP_OOS_H
#include "../lwip_check.h"
Suite *tcp_oos_suite(void);
#endif

View File

@ -0,0 +1,665 @@
#include "test_tcp_state.h"
#include "lwip/priv/tcp_priv.h"
#include "lwip/stats.h"
#include "tcp_helper.h"
#include "lwip/inet_chksum.h"
#ifdef _MSC_VER
#pragma warning(disable: 4307) /* we explicitly wrap around TCP seqnos */
#endif
#if !LWIP_STATS || !TCP_STATS || !MEMP_STATS
#error "This tests needs TCP- and MEMP-statistics enabled"
#endif
static struct netif test_netif = {0};
static struct test_tcp_txcounters test_txcounters = {0};
#define SEQNO1 (0xFFFFFF00 - TCP_MSS)
#define ISS 6510
static u8_t test_tcp_timer;
/* our own version of tcp_tmr so we can reset fast/slow timer state */
static void
test_tcp_tmr(void)
{
tcp_fasttmr();
if (++test_tcp_timer & 1) {
tcp_slowtmr();
}
}
/* Get TCP flags from packets */
static u8_t
get_tcp_flags_from_packet(struct pbuf *p, u16_t tcp_hdr_offset)
{
struct tcp_hdr tcphdr;
u16_t ret;
EXPECT_RETX(p != NULL, 0);
EXPECT_RETX(p->len >= tcp_hdr_offset + sizeof(struct tcp_hdr), 0);
ret = pbuf_copy_partial(p, &tcphdr, sizeof(struct tcp_hdr), tcp_hdr_offset);
EXPECT(ret == sizeof(struct tcp_hdr));
return TCPH_FLAGS(&tcphdr);
}
/* Create listening tcp_pcb */
static struct tcp_pcb_listen *
create_listening_pcb(u16_t local_port, struct test_tcp_counters *counters)
{
struct tcp_pcb *pcb;
struct tcp_pcb_listen *lpcb=NULL;
err_t err;
u16_t port = local_port?local_port:1234;
if (counters) {
pcb = test_tcp_new_counters_pcb(counters);
} else {
pcb = tcp_new();
}
EXPECT(pcb != NULL);
if (pcb) {
err = tcp_bind(pcb, &test_netif.ip_addr, port);
EXPECT(err == ERR_OK);
lpcb = (struct tcp_pcb_listen *)tcp_listen(pcb);
}
return lpcb;
}
/* Setup/teardown functions */
static struct netif* old_netif_list;
static struct netif* old_netif_default;
static void
tcp_state_setup(void)
{
struct tcp_pcb dummy_pcb; /* we need this for tcp_next_iss() only */
/* reset iss to default (6510) */
tcp_ticks = 0;
tcp_ticks = 0 - (tcp_next_iss(&dummy_pcb) - 6510);
tcp_next_iss(&dummy_pcb);
tcp_ticks = 0;
test_tcp_timer = 0;
old_netif_list = netif_list;
old_netif_default = netif_default;
netif_list = NULL;
netif_default = NULL;
tcp_remove_all();
test_tcp_init_netif(&test_netif, &test_txcounters, &test_local_ip, &test_netmask);
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
static void
tcp_state_teardown(void)
{
netif_list = NULL;
netif_default = NULL;
tcp_remove_all();
/* restore netif_list for next tests (e.g. loopif) */
netif_list = old_netif_list;
netif_default = old_netif_default;
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
/* helper functions */
static void
test_rst_generation_with_incoming_packet(struct pbuf *p,
struct netif *netif, struct test_tcp_txcounters *tx_counters)
{
u16_t tcp_flags;
EXPECT_RET(p != NULL);
memset(tx_counters, 0, sizeof(struct test_tcp_txcounters));
/* pass the segment to tcp_input */
tx_counters->copy_tx_packets = 1;
test_tcp_input(p, netif);
tx_counters->copy_tx_packets = 0;
/* check if packets are as expected */
EXPECT(tx_counters->tx_packets != NULL);
if (tx_counters->tx_packets) {
tcp_flags = get_tcp_flags_from_packet(tx_counters->tx_packets, 20);
EXPECT(tcp_flags & TCP_RST);
pbuf_free(tx_counters->tx_packets);
tx_counters->tx_packets = NULL;
}
}
/* Test functions */
/* Call tcp_new() and test memp stats (max number) */
START_TEST(test_tcp_new_max_num)
{
struct tcp_pcb* pcb[MEMP_NUM_TCP_PCB + 1];
int i;
LWIP_UNUSED_ARG(_i);
fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
for(i = 0;i < MEMP_NUM_TCP_PCB; i++) {
pcb[i] = tcp_new();
fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == (i + 1));
}
fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == MEMP_NUM_TCP_PCB);
/* Trying to remove the oldest pcb in TIME_WAIT,LAST_ACK,CLOSING state when pcb full */
pcb[MEMP_NUM_TCP_PCB] = tcp_new();
fail_unless(pcb[MEMP_NUM_TCP_PCB] == NULL);
tcp_set_state(pcb[0], TIME_WAIT, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
pcb[MEMP_NUM_TCP_PCB] = tcp_new();
fail_unless(pcb[MEMP_NUM_TCP_PCB] != NULL);
fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == MEMP_NUM_TCP_PCB);
for (i = 1; i <= MEMP_NUM_TCP_PCB; i++)
{
tcp_abort(pcb[i]);
}
fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
}
END_TEST
/* pcbs in TIME_WAIT state will be deleted when creating new pcb reach the max number */
START_TEST(test_tcp_new_max_num_remove_TIME_WAIT)
{
struct tcp_pcb* pcb;
struct tcp_pcb* pcb_list[MEMP_NUM_TCP_PCB + 1];
int i;
LWIP_UNUSED_ARG(_i);
/* create a pcb in TIME_WAIT state */
pcb = tcp_new();
EXPECT_RET(pcb != NULL);
tcp_set_state(pcb, TIME_WAIT, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
EXPECT_RET(pcb->state == TIME_WAIT);
/* Create max number pcbs */
for(i = 0;i < MEMP_NUM_TCP_PCB-1; i++) {
pcb_list[i] = tcp_new();
EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == (i + 2));
}
EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == MEMP_NUM_TCP_PCB);
/* Create one more pcb, and expect that the pcb in the TIME_WAIT state is deleted */
pcb_list[MEMP_NUM_TCP_PCB-1] = tcp_new();
EXPECT_RET(pcb_list[MEMP_NUM_TCP_PCB-1] != NULL);
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == MEMP_NUM_TCP_PCB);
for (i = 0; i <= MEMP_NUM_TCP_PCB-1; i++)
{
tcp_abort(pcb_list[i]);
}
EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
}
END_TEST
/* Call tcp_connect to check active open */
START_TEST(test_tcp_connect_active_open)
{
struct test_tcp_counters counters;
struct tcp_pcb *pcb;
struct pbuf *p;
err_t err;
u16_t test_port = 1234;
u32_t seqno = 0;
LWIP_UNUSED_ARG(_i);
/* create and initialize the pcb */
tcp_ticks = SEQNO1 - ISS;
pcb = test_tcp_new_counters_pcb(&counters);
EXPECT_RET(pcb != NULL);
/* Get seqno from SYN packet */
test_txcounters.copy_tx_packets = 1;
err = tcp_connect(pcb, &test_remote_ip, test_port, NULL);
test_txcounters.copy_tx_packets = 0;
EXPECT(err == ERR_OK);
EXPECT(pcb->state == SYN_SENT);
EXPECT(test_txcounters.num_tx_calls == 1);
EXPECT_RET(test_txcounters.tx_packets != NULL);
if (test_txcounters.tx_packets != NULL) {
struct tcp_hdr tcphdr;
u16_t ret;
ret = pbuf_copy_partial(test_txcounters.tx_packets, &tcphdr, 20, 20);
EXPECT(ret == 20);
EXPECT(TCPH_FLAGS(&tcphdr) & TCP_SYN);
pbuf_free(test_txcounters.tx_packets);
test_txcounters.tx_packets = NULL;
seqno = lwip_htonl(tcphdr.seqno);
EXPECT(seqno == pcb->lastack);
}
/* check correct syn packet */
p = tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, test_port,
pcb->local_port, NULL, 0, 12345, seqno + 1, TCP_SYN|TCP_ACK);
EXPECT_RET(p != NULL);
test_tcp_input(p, &test_netif);
EXPECT_RET(pcb->state == ESTABLISHED);
EXPECT_RET(test_txcounters.num_tx_calls == 2);
/* make sure the pcb is freed */
EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
tcp_abort(pcb);
EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
}
END_TEST
START_TEST(test_tcp_active_close)
{
struct tcp_pcb *pcb, *pcbl;
struct test_tcp_counters counters;
struct pbuf *p;
err_t err;
u32_t i;
LWIP_UNUSED_ARG(_i);
/* create TCP in LISTEN state */
memset(&counters, 0, sizeof(counters));
pcb = test_tcp_new_counters_pcb(&counters);
EXPECT_RET(pcb != NULL);
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
err = tcp_bind(pcb, &test_netif.ip_addr, 1234);
EXPECT_RET(err == ERR_OK);
pcbl = tcp_listen(pcb);
EXPECT_RET(pcbl != NULL);
EXPECT_RET(pcbl->state == LISTEN);
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB_LISTEN) == 1);
memset(&test_txcounters, 0, sizeof(test_txcounters));
err = tcp_close(pcbl);
EXPECT_RET(err == ERR_OK);
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB_LISTEN) == 0);
EXPECT(test_txcounters.num_tx_calls == 0);
/* close TCP in SYN_SENT state */
memset(&counters, 0, sizeof(counters));
pcb = test_tcp_new_counters_pcb(&counters);
EXPECT_RET(pcb != NULL);
err = tcp_connect(pcb, &test_netif.gw, 1234, NULL);
EXPECT_RET(err == ERR_OK);
EXPECT_RET(pcb->state == SYN_SENT);
EXPECT(test_txcounters.num_tx_calls == 1);
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
memset(&test_txcounters, 0, sizeof(test_txcounters));
err = tcp_close(pcb);
EXPECT_RET(err == ERR_OK);
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
EXPECT(test_txcounters.num_tx_calls == 0);
/* close TCP in ESTABLISHED state */
memset(&counters, 0, sizeof(counters));
pcb = test_tcp_new_counters_pcb(&counters);
EXPECT_RET(pcb != NULL);
tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
memset(&test_txcounters, 0, sizeof(test_txcounters));
err = tcp_close(pcb);
EXPECT_RET(err == ERR_OK);
EXPECT_RET(pcb->state == FIN_WAIT_1);
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
/* test_tcp_tmr(); */
EXPECT(test_txcounters.num_tx_calls == 1);
/* create a segment ACK and pass it to tcp_input */
p = tcp_create_rx_segment(pcb, NULL, 0, 0, 1, TCP_ACK);
EXPECT_RET(p != NULL);
test_tcp_input(p, &test_netif);
EXPECT_RET(pcb->state == FIN_WAIT_2);
/* create a segment FIN and pass it to tcp_input */
p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_FIN);
EXPECT_RET(p != NULL);
test_tcp_input(p, &test_netif);
EXPECT_RET(pcb->state == TIME_WAIT);
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
for (i = 0; i < 2 * TCP_MSL / TCP_TMR_INTERVAL + 1; i++) {
test_tcp_tmr();
}
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
}
END_TEST
START_TEST(test_tcp_imultaneous_close)
{
struct test_tcp_counters counters;
struct tcp_pcb* pcb;
struct pbuf* p;
char data = 0x0f;
err_t err;
u32_t i;
LWIP_UNUSED_ARG(_i);
/* initialize counter struct */
memset(&counters, 0, sizeof(counters));
counters.expected_data_len = 1;
counters.expected_data = &data;
/* create and initialize the pcb */
pcb = test_tcp_new_counters_pcb(&counters);
EXPECT_RET(pcb != NULL);
tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
err = tcp_close(pcb);
EXPECT_RET(err == ERR_OK);
EXPECT_RET(pcb->state == FIN_WAIT_1);
/* create a FIN segment */
p = tcp_create_rx_segment(pcb, &data, 0, 0, 0, TCP_FIN);
EXPECT(p != NULL);
if (p != NULL) {
test_tcp_input(p, &test_netif);
}
EXPECT_RET(pcb->state == CLOSING);
/* create an ACK segment */
p = tcp_create_rx_segment(pcb, &data, 0, 0, 1, TCP_ACK);
EXPECT(p != NULL);
if (p != NULL) {
test_tcp_input(p, &test_netif);
}
EXPECT_RET(pcb->state == TIME_WAIT);
for (i = 0; i < 2 * TCP_MSL / TCP_TMR_INTERVAL + 1; i++) {
test_tcp_tmr();
}
EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
}
END_TEST
/* RST was generated when receive any incoming segment in CLOSED state */
START_TEST(test_tcp_gen_rst_in_CLOSED)
{
struct pbuf *p;
ip_addr_t src_addr = test_remote_ip;
ip_addr_t dst_addr = test_local_ip;
LWIP_UNUSED_ARG(_i);
/* Do not create any pcb */
/* create a segment */
p = tcp_create_segment(&src_addr, &dst_addr, TEST_REMOTE_PORT,
TEST_LOCAL_PORT, NULL, 0, 12345, 54321, TCP_ACK);
EXPECT(p != NULL);
test_rst_generation_with_incoming_packet(p, &test_netif, &test_txcounters);
EXPECT(test_txcounters.num_tx_calls == 1);
}
END_TEST
/* RST was generated when receive ACK in LISTEN state */
START_TEST(test_tcp_gen_rst_in_LISTEN)
{
struct tcp_pcb_listen *lpcb;
struct pbuf *p;
ip_addr_t src_addr = test_remote_ip;
LWIP_UNUSED_ARG(_i);
/* create a pcb in LISTEN state */
lpcb = create_listening_pcb(TEST_LOCAL_PORT, NULL);
EXPECT_RET(lpcb != NULL);
/* create a segment */
p = tcp_create_segment(&src_addr,&lpcb->local_ip, TEST_REMOTE_PORT,
lpcb->local_port, NULL, 0, 12345, 54321, TCP_ACK);
EXPECT(p != NULL);
test_rst_generation_with_incoming_packet(p, &test_netif, &test_txcounters);
EXPECT(test_txcounters.num_tx_calls == 1);
/* the PCB still in LISTEN state */
EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB_LISTEN) == 1);
if (MEMP_STATS_GET(used, MEMP_TCP_PCB_LISTEN) != 0) {
/* can not use tcp_abort() */
tcp_close((struct tcp_pcb *)lpcb);
}
}
END_TEST
/* RST was generated when receive an SYN in TIME_WAIT state */
START_TEST(test_tcp_gen_rst_in_TIME_WAIT)
{
struct tcp_pcb *pcb;
struct pbuf *p;
LWIP_UNUSED_ARG(_i);
/* create a pcb in LISTEN state */
pcb = tcp_new();
EXPECT_RET(pcb != NULL);
tcp_set_state(pcb, TIME_WAIT, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
/* create a segment */
p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_SYN);
EXPECT(p != NULL);
test_rst_generation_with_incoming_packet(p, &test_netif, &test_txcounters);
EXPECT(test_txcounters.num_tx_calls == 1);
EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
EXPECT(pcb->state == TIME_WAIT);
}
END_TEST
/* receive TCP_RST with different seqno */
START_TEST(test_tcp_process_rst_seqno)
{
struct test_tcp_counters counters;
struct tcp_pcb *pcb;
struct pbuf *p;
err_t err;
LWIP_UNUSED_ARG(_i);
/* create and initialize a pcb in SYN_SENT state */
memset(&counters, 0, sizeof(counters));
pcb = test_tcp_new_counters_pcb(&counters);
EXPECT_RET(pcb != NULL);
err = tcp_connect(pcb, &test_remote_ip, TEST_REMOTE_PORT, NULL);
EXPECT_RET(err == ERR_OK);
/* a RST segment with incorrect seqno will not be accepted */
p = tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, TEST_REMOTE_PORT,
pcb->local_port, NULL, 0, 12345, pcb->snd_nxt-10, TCP_RST);
EXPECT(p != NULL);
test_tcp_input(p, &test_netif);
EXPECT(counters.err_calls == 0);
/* a RST segment with correct seqno will be accepted */
p = tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, TEST_REMOTE_PORT,
pcb->local_port, NULL, 0, 12345, pcb->snd_nxt, TCP_RST);
EXPECT(p != NULL);
test_tcp_input(p, &test_netif);
EXPECT(counters.err_calls == 1);
counters.err_calls = 0;
EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
/* create another pcb in ESTABLISHED state */
memset(&counters, 0, sizeof(counters));
pcb = test_tcp_new_counters_pcb(&counters);
EXPECT_RET(pcb != NULL);
tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
/* a RST segment with incorrect seqno will not be accepted */
p = tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, pcb->remote_port,
pcb->local_port, NULL, 0, pcb->rcv_nxt-10, 54321, TCP_RST);
EXPECT(p != NULL);
test_tcp_input(p, &test_netif);
EXPECT(counters.err_calls == 0);
/* a RST segment with correct seqno will be accepted */
p = tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, TEST_REMOTE_PORT,
pcb->local_port, NULL, 0, pcb->rcv_nxt, 54321, TCP_RST);
EXPECT(p != NULL);
test_tcp_input(p, &test_netif);
EXPECT(counters.err_calls == 1);
counters.err_calls = 0;
EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
}
END_TEST
/* RST was generated when receive an SYN+ACK with incorrect ACK number in SYN_SENT state */
START_TEST(test_tcp_gen_rst_in_SYN_SENT_ackseq)
{
struct tcp_pcb *pcb;
struct pbuf *p;
u16_t test_port = 1234;
err_t err;
LWIP_UNUSED_ARG(_i);
/* create and initialize a pcb in listen state */
pcb = tcp_new();
EXPECT_RET(pcb != NULL);
err = tcp_connect(pcb, &test_remote_ip, test_port, NULL);
EXPECT_RET(err == ERR_OK);
/* create a SYN+ACK segment with incorrect seqno */
p = tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, pcb->remote_port,
pcb->local_port, NULL, 0, 12345, pcb->lastack-10, TCP_SYN|TCP_ACK);
EXPECT(p != NULL);
test_rst_generation_with_incoming_packet(p, &test_netif, &test_txcounters);
/* LWIP: send RST then re-send SYN immediately */
EXPECT(test_txcounters.num_tx_calls == 2);
}
END_TEST
/* RST was generated when receive an ACK without SYN in SYN_SENT state */
START_TEST(test_tcp_gen_rst_in_SYN_SENT_non_syn_ack)
{
struct tcp_pcb *pcb;
struct pbuf *p;
u16_t test_port = 1234;
err_t err;
LWIP_UNUSED_ARG(_i);
/* create and initialize a pcb in listen state */
pcb = tcp_new();
EXPECT_RET(pcb != NULL);
err = tcp_connect(pcb, &test_remote_ip, test_port, NULL);
EXPECT_RET(err == ERR_OK);
/* create a SYN+ACK segment with incorrect seqno */
p = tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, pcb->remote_port,
pcb->local_port, NULL, 0, 12345, pcb->lastack, TCP_ACK);
EXPECT(p != NULL);
test_rst_generation_with_incoming_packet(p, &test_netif, &test_txcounters);
/* LWIP: send RST then re-send SYN immediately */
EXPECT(test_txcounters.num_tx_calls == 2);
}
END_TEST
/* RST was generated when receive an ACK with incorrect seqno in SYN_RCVD state */
START_TEST(test_tcp_gen_rst_in_SYN_RCVD)
{
struct tcp_pcb_listen *lpcb;
struct pbuf *p;
u32_t ack_seqno = 0;
ip_addr_t src_addr = test_remote_ip;
LWIP_UNUSED_ARG(_i);
/* create and initialize a pcb in listen state */
lpcb = create_listening_pcb(TEST_LOCAL_PORT, NULL);
EXPECT_RET(lpcb != NULL);
/* LISTEN -> SYN_RCVD */
p = tcp_create_segment(&src_addr, &lpcb->local_ip, TEST_REMOTE_PORT,
lpcb->local_port, NULL, 0, 1000, 54321, TCP_SYN);
EXPECT(p != NULL);
memset(&test_txcounters, 0, sizeof(struct test_tcp_txcounters));
test_tcp_input(p, &test_netif);
EXPECT(test_txcounters.num_tx_calls == 1);
EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
if (MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1) {
ack_seqno = tcp_active_pcbs[0].lastack;
}
/* create a ACK segment with incorrect seqno */
p = tcp_create_segment(&src_addr, &lpcb->local_ip, TEST_REMOTE_PORT,
lpcb->local_port, NULL, 0, 1001, ack_seqno+1111, TCP_ACK);
EXPECT(p != NULL);
test_rst_generation_with_incoming_packet(p, &test_netif, &test_txcounters);
EXPECT(test_txcounters.num_tx_calls == 1);
/* the active pcb still exists */
EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB_LISTEN) == 1);
if (MEMP_STATS_GET(used, MEMP_TCP_PCB_LISTEN) != 0) {
/* can not use tcp_abort() */
tcp_close((struct tcp_pcb *)lpcb);
}
}
END_TEST
/* a listen pcb returns to LISTEN from SYN_RCVD when RST received */
START_TEST(test_tcp_receive_rst_SYN_RCVD_to_LISTEN)
{
struct tcp_pcb_listen *lpcb;
struct pbuf *p;
u16_t tcp_flags;
ip_addr_t src_addr = test_remote_ip;
LWIP_UNUSED_ARG(_i);
/* create and initialize a pcb in listen state */
lpcb = create_listening_pcb(TEST_LOCAL_PORT, NULL);
EXPECT_RET(lpcb != NULL);
/* create a SYN segment */
p = tcp_create_segment(&src_addr, &lpcb->local_ip, TEST_REMOTE_PORT,
lpcb->local_port, NULL, 0, 1000, 54321, TCP_SYN);
EXPECT(p != NULL);
/* pass the segment to tcp_input */
memset(&test_txcounters, 0, sizeof(struct test_tcp_txcounters));
test_txcounters.copy_tx_packets = 1;
test_tcp_input(p, &test_netif);
test_txcounters.copy_tx_packets = 0;
/* check if packets are as expected */
EXPECT(test_txcounters.num_tx_calls == 1);
tcp_flags = get_tcp_flags_from_packet(test_txcounters.tx_packets, 20);
pbuf_free(test_txcounters.tx_packets);
test_txcounters.tx_packets = NULL;
EXPECT((tcp_flags & TCP_SYN) && (tcp_flags & TCP_ACK));
EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
/* create a RST segment */
p = tcp_create_segment(&src_addr, &lpcb->local_ip, TEST_REMOTE_PORT,
lpcb->local_port, NULL, 0, 1001, 54321, TCP_RST);
EXPECT(p != NULL);
test_tcp_input(p, &test_netif);
EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB_LISTEN) == 1);
if (MEMP_STATS_GET(used, MEMP_TCP_PCB_LISTEN) != 0) {
/* can not use tcp_abort() */
tcp_close((struct tcp_pcb *)lpcb);
}
}
END_TEST
/** Create the suite including all tests for this module */
Suite *
tcp_state_suite(void)
{
testfunc tests[] = {
TESTFUNC(test_tcp_new_max_num),
TESTFUNC(test_tcp_new_max_num_remove_TIME_WAIT),
TESTFUNC(test_tcp_connect_active_open),
TESTFUNC(test_tcp_active_close),
TESTFUNC(test_tcp_imultaneous_close),
TESTFUNC(test_tcp_gen_rst_in_CLOSED),
TESTFUNC(test_tcp_gen_rst_in_LISTEN),
TESTFUNC(test_tcp_gen_rst_in_TIME_WAIT),
TESTFUNC(test_tcp_process_rst_seqno),
TESTFUNC(test_tcp_gen_rst_in_SYN_SENT_ackseq),
TESTFUNC(test_tcp_gen_rst_in_SYN_SENT_non_syn_ack),
TESTFUNC(test_tcp_gen_rst_in_SYN_RCVD),
TESTFUNC(test_tcp_receive_rst_SYN_RCVD_to_LISTEN),
};
return create_suite("TCP_STATE", tests, sizeof(tests) / sizeof(testfunc), tcp_state_setup, tcp_state_teardown);
}

View File

@ -0,0 +1,8 @@
#ifndef LWIP_HDR_TEST_TCP_STATE_H
#define LWIP_HDR_TEST_TCP_STATE_H
#include "../lwip_check.h"
Suite *tcp_state_suite(void);
#endif

View File

@ -0,0 +1,472 @@
#include "test_udp.h"
#include "lwip/udp.h"
#include "lwip/stats.h"
#include "lwip/inet_chksum.h"
#if !LWIP_STATS || !UDP_STATS || !MEMP_STATS
#error "This tests needs UDP- and MEMP-statistics enabled"
#endif
struct test_udp_rxdata {
u32_t rx_cnt;
u32_t rx_bytes;
struct udp_pcb *pcb;
};
static struct netif test_netif1, test_netif2;
static ip4_addr_t test_gw1, test_ipaddr1, test_netmask1;
static ip4_addr_t test_gw2, test_ipaddr2, test_netmask2;
static int output_ctr, linkoutput_ctr;
/* Helper functions */
static void
udp_remove_all(void)
{
struct udp_pcb *pcb = udp_pcbs;
struct udp_pcb *pcb2;
while(pcb != NULL) {
pcb2 = pcb;
pcb = pcb->next;
udp_remove(pcb2);
}
fail_unless(MEMP_STATS_GET(used, MEMP_UDP_PCB) == 0);
}
static err_t
default_netif_output(struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr)
{
fail_unless((netif == &test_netif1) || (netif == &test_netif2));
fail_unless(p != NULL);
fail_unless(ipaddr != NULL);
output_ctr++;
return ERR_OK;
}
static err_t
default_netif_linkoutput(struct netif *netif, struct pbuf *p)
{
fail_unless((netif == &test_netif1) || (netif == &test_netif2));
fail_unless(p != NULL);
linkoutput_ctr++;
return ERR_OK;
}
static err_t
default_netif_init(struct netif *netif)
{
fail_unless(netif != NULL);
netif->output = default_netif_output;
netif->linkoutput = default_netif_linkoutput;
netif->mtu = 1500;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
netif->hwaddr_len = 6;
return ERR_OK;
}
static void
default_netif_add(void)
{
struct netif *n;
#if LWIP_HAVE_LOOPIF
fail_unless(netif_list != NULL); /* the loopif */
fail_unless(netif_list->next == NULL);
#else
fail_unless(netif_list == NULL);
#endif
fail_unless(netif_default == NULL);
IP4_ADDR(&test_ipaddr1, 192,168,0,1);
IP4_ADDR(&test_netmask1, 255,255,255,0);
IP4_ADDR(&test_gw1, 192,168,0,254);
n = netif_add(&test_netif1, &test_ipaddr1, &test_netmask1,
&test_gw1, NULL, default_netif_init, NULL);
fail_unless(n == &test_netif1);
IP4_ADDR(&test_ipaddr2, 192,168,1,1);
IP4_ADDR(&test_netmask2, 255,255,255,0);
IP4_ADDR(&test_gw2, 192,168,1,254);
n = netif_add(&test_netif2, &test_ipaddr2, &test_netmask2,
&test_gw2, NULL, default_netif_init, NULL);
fail_unless(n == &test_netif2);
netif_set_default(&test_netif1);
netif_set_up(&test_netif1);
netif_set_up(&test_netif2);
}
static void
default_netif_remove(void)
{
fail_unless(netif_default == &test_netif1);
netif_remove(&test_netif1);
netif_remove(&test_netif2);
fail_unless(netif_default == NULL);
#if LWIP_HAVE_LOOPIF
fail_unless(netif_list != NULL); /* the loopif */
fail_unless(netif_list->next == NULL);
#else
fail_unless(netif_list == NULL);
#endif
}
/* Setups/teardown functions */
static void
udp_setup(void)
{
udp_remove_all();
default_netif_add();
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
static void
udp_teardown(void)
{
udp_remove_all();
default_netif_remove();
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
/* Test functions */
START_TEST(test_udp_new_remove)
{
struct udp_pcb* pcb;
LWIP_UNUSED_ARG(_i);
fail_unless(MEMP_STATS_GET(used, MEMP_UDP_PCB) == 0);
pcb = udp_new();
fail_unless(pcb != NULL);
if (pcb != NULL) {
fail_unless(MEMP_STATS_GET(used, MEMP_UDP_PCB) == 1);
udp_remove(pcb);
fail_unless(MEMP_STATS_GET(used, MEMP_UDP_PCB) == 0);
}
}
END_TEST
static void test_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p,
const ip_addr_t *addr, u16_t port)
{
struct test_udp_rxdata *ctr = (struct test_udp_rxdata *)arg;
LWIP_UNUSED_ARG(addr);
LWIP_UNUSED_ARG(port);
fail_unless(arg != NULL);
fail_unless(ctr->pcb == pcb);
ctr->rx_cnt++;
ctr->rx_bytes += p->tot_len;
if (p != NULL) {
pbuf_free(p);
}
}
static struct pbuf *
test_udp_create_test_packet(u16_t length, u16_t port, u32_t dst_addr)
{
err_t err;
u8_t ret;
struct udp_hdr *uh;
struct ip_hdr *ih;
struct pbuf *p;
const u8_t test_data[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
p = pbuf_alloc(PBUF_TRANSPORT, length, PBUF_POOL);
fail_unless(p != NULL);
if (p == NULL) {
return NULL;
}
fail_unless(p->next == NULL);
err = pbuf_take(p, test_data, length);
fail_unless(err == ERR_OK);
/* add UDP header */
ret = pbuf_add_header(p, sizeof(struct udp_hdr));
fail_unless(!ret);
uh = (struct udp_hdr *)p->payload;
uh->chksum = 0;
uh->dest = uh->src = lwip_htons(port);
uh->len = lwip_htons(p->tot_len);
/* add IPv4 header */
ret = pbuf_add_header(p, sizeof(struct ip_hdr));
fail_unless(!ret);
ih = (struct ip_hdr *)p->payload;
memset(ih, 0, sizeof(*ih));
ih->dest.addr = dst_addr;
ih->_len = lwip_htons(p->tot_len);
ih->_ttl = 32;
ih->_proto = IP_PROTO_UDP;
IPH_VHL_SET(ih, 4, sizeof(struct ip_hdr) / 4);
IPH_CHKSUM_SET(ih, inet_chksum(ih, sizeof(struct ip_hdr)));
return p;
}
/* bind 2 pcbs to specific netif IP and test which one gets broadcasts */
START_TEST(test_udp_broadcast_rx_with_2_netifs)
{
err_t err;
struct udp_pcb *pcb1, *pcb2;
const u16_t port = 12345;
struct test_udp_rxdata ctr1, ctr2;
struct pbuf *p;
#if SO_REUSE
struct udp_pcb *pcb_any;
struct test_udp_rxdata ctr_any;
#endif
LWIP_UNUSED_ARG(_i);
pcb1 = udp_new();
fail_unless(pcb1 != NULL);
pcb2 = udp_new();
fail_unless(pcb2 != NULL);
#if SO_REUSE
pcb_any = udp_new();
fail_unless(pcb_any != NULL);
ip_set_option(pcb1, SOF_REUSEADDR);
ip_set_option(pcb2, SOF_REUSEADDR);
ip_set_option(pcb_any, SOF_REUSEADDR);
err = udp_bind(pcb_any, NULL, port);
fail_unless(err == ERR_OK);
memset(&ctr_any, 0, sizeof(ctr_any));
ctr_any.pcb = pcb_any;
udp_recv(pcb_any, test_recv, &ctr_any);
#endif
err = udp_bind(pcb1, &test_netif1.ip_addr, port);
fail_unless(err == ERR_OK);
err = udp_bind(pcb2, &test_netif2.ip_addr, port);
fail_unless(err == ERR_OK);
memset(&ctr1, 0, sizeof(ctr1));
ctr1.pcb = pcb1;
memset(&ctr2, 0, sizeof(ctr2));
ctr2.pcb = pcb2;
udp_recv(pcb1, test_recv, &ctr1);
udp_recv(pcb2, test_recv, &ctr2);
/* unicast to netif1 */
p = test_udp_create_test_packet(16, port, test_ipaddr1.addr);
EXPECT_RET(p != NULL);
err = ip4_input(p, &test_netif1);
fail_unless(err == ERR_OK);
fail_unless(ctr1.rx_cnt == 1);
fail_unless(ctr1.rx_bytes == 16);
fail_unless(ctr2.rx_cnt == 0);
#if SO_REUSE
fail_unless(ctr_any.rx_cnt == 0);
#endif
ctr1.rx_cnt = ctr1.rx_bytes = 0;
/* unicast to netif2 */
p = test_udp_create_test_packet(16, port, test_ipaddr2.addr);
EXPECT_RET(p != NULL);
err = ip4_input(p, &test_netif2);
fail_unless(err == ERR_OK);
fail_unless(ctr2.rx_cnt == 1);
fail_unless(ctr2.rx_bytes == 16);
fail_unless(ctr1.rx_cnt == 0);
#if SO_REUSE
fail_unless(ctr_any.rx_cnt == 0);
#endif
ctr2.rx_cnt = ctr2.rx_bytes = 0;
/* broadcast to netif1-broadcast, input to netif2 */
p = test_udp_create_test_packet(16, port, test_ipaddr1.addr | ~test_netmask1.addr);
EXPECT_RET(p != NULL);
err = ip4_input(p, &test_netif2);
fail_unless(err == ERR_OK);
fail_unless(ctr1.rx_cnt == 1);
fail_unless(ctr1.rx_bytes == 16);
fail_unless(ctr2.rx_cnt == 0);
#if SO_REUSE
fail_unless(ctr_any.rx_cnt == 0);
#endif
ctr1.rx_cnt = ctr1.rx_bytes = 0;
/* broadcast to netif2-broadcast, input to netif1 */
p = test_udp_create_test_packet(16, port, test_ipaddr2.addr | ~test_netmask2.addr);
EXPECT_RET(p != NULL);
err = ip4_input(p, &test_netif1);
fail_unless(err == ERR_OK);
fail_unless(ctr2.rx_cnt == 1);
fail_unless(ctr2.rx_bytes == 16);
fail_unless(ctr1.rx_cnt == 0);
#if SO_REUSE
fail_unless(ctr_any.rx_cnt == 0);
#endif
ctr2.rx_cnt = ctr2.rx_bytes = 0;
/* broadcast to global-broadcast, input to netif1 */
p = test_udp_create_test_packet(16, port, 0xffffffff);
EXPECT_RET(p != NULL);
err = ip4_input(p, &test_netif1);
fail_unless(err == ERR_OK);
fail_unless(ctr1.rx_cnt == 1);
fail_unless(ctr1.rx_bytes == 16);
fail_unless(ctr2.rx_cnt == 0);
#if SO_REUSE
fail_unless(ctr_any.rx_cnt == 0);
#endif
ctr1.rx_cnt = ctr1.rx_bytes = 0;
/* broadcast to global-broadcast, input to netif2 */
p = test_udp_create_test_packet(16, port, 0xffffffff);
EXPECT_RET(p != NULL);
err = ip4_input(p, &test_netif2);
fail_unless(err == ERR_OK);
fail_unless(ctr2.rx_cnt == 1);
fail_unless(ctr2.rx_bytes == 16);
fail_unless(ctr1.rx_cnt == 0);
#if SO_REUSE
fail_unless(ctr_any.rx_cnt == 0);
#endif
ctr2.rx_cnt = ctr2.rx_bytes = 0;
}
END_TEST
START_TEST(test_udp_bind)
{
struct udp_pcb* pcb1;
struct udp_pcb* pcb2;
ip_addr_t ip1;
ip_addr_t ip2;
err_t err1;
err_t err2;
LWIP_UNUSED_ARG(_i);
/* bind on same port using different IP address types */
ip_addr_set_any_val(0, ip1);
ip_addr_set_any_val(1, ip2);
pcb1 = udp_new_ip_type(IPADDR_TYPE_V4);
pcb2 = udp_new_ip_type(IPADDR_TYPE_V6);
err1 = udp_bind(pcb1, &ip1, 2105);
err2 = udp_bind(pcb2, &ip2, 2105);
fail_unless(err1 == ERR_OK);
fail_unless(err2 == ERR_OK);
udp_remove(pcb1);
udp_remove(pcb2);
/* bind on same port using SAME IPv4 address type */
ip_addr_set_any_val(0, ip1);
ip_addr_set_any_val(0, ip2);
pcb1 = udp_new_ip_type(IPADDR_TYPE_V4);
pcb2 = udp_new_ip_type(IPADDR_TYPE_V4);
err1 = udp_bind(pcb1, &ip1, 2105);
err2 = udp_bind(pcb2, &ip2, 2105);
fail_unless(err1 == ERR_OK);
fail_unless(err2 == ERR_USE);
udp_remove(pcb1);
udp_remove(pcb2);
/* bind on same port using SAME IPv6 address type */
ip_addr_set_any_val(1, ip1);
ip_addr_set_any_val(1, ip2);
pcb1 = udp_new_ip_type(IPADDR_TYPE_V6);
pcb2 = udp_new_ip_type(IPADDR_TYPE_V6);
err1 = udp_bind(pcb1, &ip1, 2105);
err2 = udp_bind(pcb2, &ip2, 2105);
fail_unless(err1 == ERR_OK);
fail_unless(err2 == ERR_USE);
udp_remove(pcb1);
udp_remove(pcb2);
/* Bind with different IP address type */
ip_addr_set_any_val(0, ip1);
ip_addr_set_any_val(1, ip2);
pcb1 = udp_new_ip_type(IPADDR_TYPE_V6);
pcb2 = udp_new_ip_type(IPADDR_TYPE_V4);
err1 = udp_bind(pcb1, &ip1, 2105);
err2 = udp_bind(pcb2, &ip2, 2105);
fail_unless(err1 == ERR_OK);
fail_unless(err2 == ERR_OK);
udp_remove(pcb1);
udp_remove(pcb2);
/* Bind with different IP numbers */
IP_ADDR4(&ip1, 1, 2, 3, 4);
IP_ADDR4(&ip2, 4, 3, 2, 1);
pcb1 = udp_new_ip_type(IPADDR_TYPE_V6);
pcb2 = udp_new_ip_type(IPADDR_TYPE_V4);
err1 = udp_bind(pcb1, &ip1, 2105);
err2 = udp_bind(pcb2, &ip2, 2105);
fail_unless(err1 == ERR_OK);
fail_unless(err2 == ERR_OK);
udp_remove(pcb1);
udp_remove(pcb2);
/* Bind with same IP numbers */
IP_ADDR4(&ip1, 1, 2, 3, 4);
IP_ADDR4(&ip2, 1, 2, 3, 4);
pcb1 = udp_new_ip_type(IPADDR_TYPE_V6);
pcb2 = udp_new_ip_type(IPADDR_TYPE_V4);
err1 = udp_bind(pcb1, &ip1, 2105);
err2 = udp_bind(pcb2, &ip2, 2105);
fail_unless(err1 == ERR_OK);
fail_unless(err2 == ERR_USE);
udp_remove(pcb1);
udp_remove(pcb2);
/* bind on same port using ANY + IPv4 */
ip1 = *IP_ANY_TYPE;
IP_ADDR4(&ip2, 1, 2, 3, 4);
pcb1 = udp_new_ip_type(IPADDR_TYPE_ANY);
pcb2 = udp_new_ip_type(IPADDR_TYPE_V4);
err1 = udp_bind(pcb1, &ip1, 2105);
err2 = udp_bind(pcb2, &ip2, 2105);
fail_unless(err1 == ERR_OK);
fail_unless(err2 == ERR_USE);
udp_remove(pcb1);
udp_remove(pcb2);
}
END_TEST
/** Create the suite including all tests for this module */
Suite *
udp_suite(void)
{
testfunc tests[] = {
TESTFUNC(test_udp_new_remove),
TESTFUNC(test_udp_broadcast_rx_with_2_netifs),
TESTFUNC(test_udp_bind)
};
return create_suite("UDP", tests, sizeof(tests)/sizeof(testfunc), udp_setup, udp_teardown);
}

View File

@ -0,0 +1,8 @@
#ifndef LWIP_HDR_TEST_UDP_H
#define LWIP_HDR_TEST_UDP_H
#include "../lwip_check.h"
Suite* udp_suite(void);
#endif