Sync inav to Gitea
Make sure docs are updated / settings_md (push) Canceled after 0s
Build firmware / test (push) Canceled after 0s
Build firmware / build-SITL-Windows (push) Canceled after 0s
Build firmware / build-SITL-Mac (push) Canceled after 0s
Build firmware / build-SITL-Linux (push) Canceled after 0s
Build firmware / build-SITL-Linux-arm64 (push) Canceled after 0s
Build firmware / upload-artifacts (push) Canceled after 0s
Build firmware / build-single-target (push) Canceled after 0s
Build firmware / build (9) (push) Canceled after 0s
Build firmware / build (8) (push) Canceled after 0s
Build firmware / build (7) (push) Canceled after 0s
Build firmware / build (6) (push) Canceled after 0s
Build firmware / build (5) (push) Canceled after 0s
Build firmware / build (4) (push) Canceled after 0s
Build firmware / build (3) (push) Canceled after 0s
Build firmware / build (2) (push) Canceled after 0s
Build firmware / build (14) (push) Canceled after 0s
Build firmware / build (13) (push) Canceled after 0s
Build firmware / build (12) (push) Canceled after 0s
Build firmware / build (11) (push) Canceled after 0s
Build firmware / build (10) (push) Canceled after 0s
Build firmware / build (1) (push) Canceled after 0s
Build firmware / build (0) (push) Canceled after 0s
Build firmware / detect (push) Canceled after 0s
Build pre-release / build (push) Canceled after 0s
Build pre-release / Release (push) Canceled after 0s
Make sure docs are updated / settings_md (push) Canceled after 0s
Build firmware / test (push) Canceled after 0s
Build firmware / build-SITL-Windows (push) Canceled after 0s
Build firmware / build-SITL-Mac (push) Canceled after 0s
Build firmware / build-SITL-Linux (push) Canceled after 0s
Build firmware / build-SITL-Linux-arm64 (push) Canceled after 0s
Build firmware / upload-artifacts (push) Canceled after 0s
Build firmware / build-single-target (push) Canceled after 0s
Build firmware / build (9) (push) Canceled after 0s
Build firmware / build (8) (push) Canceled after 0s
Build firmware / build (7) (push) Canceled after 0s
Build firmware / build (6) (push) Canceled after 0s
Build firmware / build (5) (push) Canceled after 0s
Build firmware / build (4) (push) Canceled after 0s
Build firmware / build (3) (push) Canceled after 0s
Build firmware / build (2) (push) Canceled after 0s
Build firmware / build (14) (push) Canceled after 0s
Build firmware / build (13) (push) Canceled after 0s
Build firmware / build (12) (push) Canceled after 0s
Build firmware / build (11) (push) Canceled after 0s
Build firmware / build (10) (push) Canceled after 0s
Build firmware / build (1) (push) Canceled after 0s
Build firmware / build (0) (push) Canceled after 0s
Build firmware / detect (push) Canceled after 0s
Build pre-release / build (push) Canceled after 0s
Build pre-release / Release (push) Canceled after 0s
This commit is contained in:
@@ -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>
|
||||
#
|
||||
|
||||
all compile: lwip_fuzz
|
||||
.PHONY: all clean
|
||||
|
||||
CC=afl-gcc
|
||||
LDFLAGS=-lm
|
||||
# use 'make D=-DUSER_DEFINE' to pass a user define to gcc
|
||||
CFLAGS=-O0 $(D)
|
||||
|
||||
CONTRIBDIR=../../../lwip-contrib
|
||||
include $(CONTRIBDIR)/ports/unix/Common.mk
|
||||
|
||||
clean:
|
||||
rm -f *.o $(LWIPLIBCOMMON) $(APPLIB) lwip_fuzz *.s .depend* *.core core
|
||||
|
||||
depend dep: .depend
|
||||
|
||||
include .depend
|
||||
|
||||
.depend: fuzz.c $(LWIPFILES) $(APPFILES)
|
||||
$(CCDEP) $(CFLAGS) -MM $^ > .depend || rm -f .depend
|
||||
|
||||
lwip_fuzz: .depend $(LWIPLIBCOMMON) $(APPLIB) fuzz.o
|
||||
$(CC) $(CFLAGS) -o lwip_fuzz fuzz.o $(APPLIB) $(LWIPLIBCOMMON) $(LDFLAGS)
|
||||
@@ -0,0 +1,37 @@
|
||||
|
||||
Fuzzing the lwIP stack (afl-fuzz requires linux/unix or similar)
|
||||
|
||||
This directory contains a small app that reads Ethernet frames from stdin and
|
||||
processes them. It is used together with the 'american fuzzy lop' tool (found
|
||||
at http://lcamtuf.coredump.cx/afl/) 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 program.
|
||||
|
||||
Running make with parameter 'D=-DLWIP_FUZZ_MULTI_PACKET' will produce a binary
|
||||
that parses the input data as multiple packets (experimental!).
|
||||
|
||||
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.
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* 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>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lwip/init.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/dns.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>
|
||||
|
||||
/* This define enables multi packet processing.
|
||||
* For this, the input is interpreted as 2 byte length + data + 2 byte length + data...
|
||||
* #define LWIP_FUZZ_MULTI_PACKET
|
||||
*/
|
||||
#ifdef LWIP_FUZZ_MULTI_PACKET
|
||||
u8_t pktbuf[20000];
|
||||
#else
|
||||
u8_t pktbuf[2000];
|
||||
#endif
|
||||
|
||||
/* no-op send function */
|
||||
static err_t lwip_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] = '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->ip6_autoconfig_enabled = 1;
|
||||
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;
|
||||
|
||||
LWIP_ASSERT("pkt too big", len <= 0xFFFF);
|
||||
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;
|
||||
}
|
||||
err = netif->input(p, netif);
|
||||
if (err != ERR_OK) {
|
||||
pbuf_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
static void input_pkts(struct netif *netif, const u8_t *data, size_t len)
|
||||
{
|
||||
#ifdef LWIP_FUZZ_MULTI_PACKET
|
||||
const u16_t max_packet_size = 1514;
|
||||
const u8_t *ptr = data;
|
||||
size_t rem_len = len;
|
||||
|
||||
while (rem_len > sizeof(u16_t)) {
|
||||
u16_t frame_len;
|
||||
memcpy(&frame_len, ptr, sizeof(u16_t));
|
||||
ptr += sizeof(u16_t);
|
||||
rem_len -= sizeof(u16_t);
|
||||
frame_len = htons(frame_len) & 0x7FF;
|
||||
frame_len = LWIP_MIN(frame_len, max_packet_size);
|
||||
if (frame_len > rem_len) {
|
||||
frame_len = (u16_t)rem_len;
|
||||
}
|
||||
if (frame_len != 0) {
|
||||
input_pkt(netif, ptr, frame_len);
|
||||
}
|
||||
ptr += frame_len;
|
||||
rem_len -= frame_len;
|
||||
}
|
||||
#else /* LWIP_FUZZ_MULTI_PACKET */
|
||||
input_pkt(netif, data, len);
|
||||
#endif /* LWIP_FUZZ_MULTI_PACKET */
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
struct netif net_test;
|
||||
ip4_addr_t addr;
|
||||
ip4_addr_t netmask;
|
||||
ip4_addr_t gw;
|
||||
size_t len;
|
||||
|
||||
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 LWIP_IPV6
|
||||
nd6_tmr(); /* tick nd to join multicast groups */
|
||||
#endif
|
||||
dns_setserver(0, &net_test.gw);
|
||||
|
||||
/* initialize apps */
|
||||
httpd_init();
|
||||
lwiperf_start_tcp_server_default(NULL, NULL);
|
||||
mdns_resp_init();
|
||||
mdns_resp_add_netif(&net_test, "hostname", 255);
|
||||
snmp_init();
|
||||
|
||||
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(&net_test, pktbuf, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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__
|
||||
|
||||
/* 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__ */
|
||||
@@ -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
|
||||
+726
@@ -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 */
|
||||
+40
@@ -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 */
|
||||
@@ -0,0 +1,31 @@
|
||||
# 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.
|
||||
|
||||
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_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.c
|
||||
${LWIP_TESTDIR}/udp/test_udp.c
|
||||
)
|
||||
@@ -0,0 +1,51 @@
|
||||
#
|
||||
# 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_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.c \
|
||||
$(TESTDIR)/udp/test_udp.c
|
||||
|
||||
@@ -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 = 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)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* note: this modifies the underyling iov_base and iov_len for a partial
|
||||
read for an individual vector. This updates the msg->msg_iov pointer
|
||||
to skip fully consumed vecotrs */
|
||||
|
||||
/* 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 == 6);
|
||||
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 */
|
||||
@@ -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
|
||||
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
* 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
|
||||
#error LWIP_NETCONN_SEM_PER_THREAD==1 not supported
|
||||
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
|
||||
|
||||
#endif /* !NO_SYS */
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
#endif /* LWIP_HDR_TEST_SYS_ARCH_H */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,224 @@
|
||||
#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
|
||||
#if LWIP_DNS
|
||||
#error "This test needs DNS turned off (as it mallocs on init)"
|
||||
#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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,227 @@
|
||||
#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
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
callback_ctr = 0;
|
||||
netif_set_addr(&net_test, &addr, &netmask, &gw);
|
||||
fail_unless(callback_ctr == 0);
|
||||
|
||||
/* 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);
|
||||
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
|
||||
|
||||
|
||||
/** Create the suite including all tests for this module */
|
||||
Suite *
|
||||
netif_suite(void)
|
||||
{
|
||||
testfunc tests[] = {
|
||||
TESTFUNC(test_netif_extcallbacks)
|
||||
};
|
||||
return create_suite("NETIF", tests, sizeof(tests)/sizeof(testfunc), netif_setup, netif_teardown);
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,271 @@
|
||||
#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_DNS
|
||||
#error "This test needs DNS turned off (as it mallocs on init)"
|
||||
#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);
|
||||
|
||||
fail_unless(lwip_stats.mem.used == 0);
|
||||
fail_unless(MEMP_STATS_GET(used, MEMP_PBUF_POOL) == 0);
|
||||
|
||||
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);
|
||||
err = pbuf_copy(p3, p1);
|
||||
fail_unless(err == ERR_VAL);
|
||||
|
||||
pbuf_free(p1);
|
||||
pbuf_free(p3);
|
||||
fail_unless(lwip_stats.mem.used == 0);
|
||||
|
||||
fail_unless(lwip_stats.mem.used == 0);
|
||||
fail_unless(MEMP_STATS_GET(used, MEMP_PBUF_POOL) == 0);
|
||||
}
|
||||
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);
|
||||
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 = pbuf_alloc(PBUF_RAW, 1024, PBUF_POOL);
|
||||
struct pbuf *q = p->next;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
/* 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 = pbuf_alloc(PBUF_RAW, 1024, PBUF_POOL);
|
||||
struct pbuf *q = p->next;
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
/* 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_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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
+272
@@ -0,0 +1,272 @@
|
||||
#include "test_etharp.h"
|
||||
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/etharp.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(ðarphdr->sipaddr, adr, sizeof(ip4_addr_t));
|
||||
SMEMCPY(ðarphdr->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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,160 @@
|
||||
#include "test_ip4.h"
|
||||
|
||||
#include "lwip/ip4.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
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 functions */
|
||||
|
||||
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
|
||||
|
||||
|
||||
/** Create the suite including all tests for this module */
|
||||
Suite *
|
||||
ip4_suite(void)
|
||||
{
|
||||
testfunc tests[] = {
|
||||
TESTFUNC(test_ip4_reass),
|
||||
};
|
||||
return create_suite("IPv4", tests, sizeof(tests)/sizeof(testfunc), ip4_setup, ip4_teardown);
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,321 @@
|
||||
#include "test_ip6.h"
|
||||
|
||||
#include "lwip/ethip6.h"
|
||||
#include "lwip/ip6.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 err_t
|
||||
default_netif_linkoutput(struct netif *netif, struct pbuf *p)
|
||||
{
|
||||
fail_unless(netif == &test_netif6);
|
||||
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_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();
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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
|
||||
|
||||
/** 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)
|
||||
};
|
||||
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 */
|
||||
@@ -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
|
||||
@@ -0,0 +1,42 @@
|
||||
#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)
|
||||
|
||||
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)
|
||||
|
||||
/** 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 */
|
||||
@@ -0,0 +1,107 @@
|
||||
#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 "core/test_def.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 "lwip/init.h"
|
||||
#if !NO_SYS
|
||||
#include "lwip/tcpip.h"
|
||||
#endif
|
||||
|
||||
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);
|
||||
}
|
||||
for (i = 0, mask = 1; i < MEMP_MAX; i++, mask <<= 1) {
|
||||
if (!(skip & mask)) {
|
||||
fail_unless(lwip_stats.memp[i]->used == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#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,
|
||||
def_suite,
|
||||
mem_suite,
|
||||
netif_suite,
|
||||
pbuf_suite,
|
||||
timers_suite,
|
||||
etharp_suite,
|
||||
dhcp_suite,
|
||||
mdns_suite,
|
||||
mqtt_suite,
|
||||
sockets_suite
|
||||
};
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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_NETBUF_RECVINFO 1
|
||||
#define LWIP_HAVE_LOOPIF 1
|
||||
#define TCPIP_THREAD_TEST
|
||||
|
||||
/* Enable DHCP to test it, disable UDP checksum to easier inject packets */
|
||||
#define LWIP_DHCP 1
|
||||
|
||||
/* 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_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)
|
||||
|
||||
/* 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 */
|
||||
|
||||
#endif /* LWIP_HDR_LWIPOPTS_H */
|
||||
@@ -0,0 +1,915 @@
|
||||
/*
|
||||
* 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_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);
|
||||
/* Dont 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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
};
|
||||
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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,319 @@
|
||||
#include "tcp_helper.h"
|
||||
|
||||
#include "lwip/priv/tcp_priv.h"
|
||||
#include "lwip/stats.h"
|
||||
#include "lwip/pbuf.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;
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -0,0 +1,347 @@
|
||||
#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
|
||||
|
||||
/** 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)
|
||||
};
|
||||
return create_suite("UDP", tests, sizeof(tests)/sizeof(testfunc), udp_setup, udp_teardown);
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user