Init commit
This commit is contained in:
37
lwip/test/fuzz/README
Normal file
37
lwip/test/fuzz/README
Normal file
@ -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
lwip/test/fuzz/config.h
Normal file
0
lwip/test/fuzz/config.h
Normal file
189
lwip/test/fuzz/fuzz.c
Normal file
189
lwip/test/fuzz/fuzz.c
Normal file
@ -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
lwip/test/fuzz/inputs/arp/arp_req.bin
Normal file
BIN
lwip/test/fuzz/inputs/arp/arp_req.bin
Normal file
Binary file not shown.
BIN
lwip/test/fuzz/inputs/icmp/icmp_ping.bin
Normal file
BIN
lwip/test/fuzz/inputs/icmp/icmp_ping.bin
Normal file
Binary file not shown.
BIN
lwip/test/fuzz/inputs/ipv6/neighbor_solicitation.bin
Normal file
BIN
lwip/test/fuzz/inputs/ipv6/neighbor_solicitation.bin
Normal file
Binary file not shown.
BIN
lwip/test/fuzz/inputs/ipv6/router_adv.bin
Normal file
BIN
lwip/test/fuzz/inputs/ipv6/router_adv.bin
Normal file
Binary file not shown.
BIN
lwip/test/fuzz/inputs/tcp/tcp_syn.bin
Normal file
BIN
lwip/test/fuzz/inputs/tcp/tcp_syn.bin
Normal file
Binary file not shown.
BIN
lwip/test/fuzz/inputs/udp/udp_port_5000.bin
Normal file
BIN
lwip/test/fuzz/inputs/udp/udp_port_5000.bin
Normal file
Binary file not shown.
80
lwip/test/fuzz/lwipopts.h
Normal file
80
lwip/test/fuzz/lwipopts.h
Normal file
@ -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__ */
|
31
lwip/test/fuzz/output_to_pcap.sh
Normal file
31
lwip/test/fuzz/output_to_pcap.sh
Normal file
@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
echo "This script will make pcap files from the afl-fuzz crash/hang files"
|
||||
echo "It needs hexdump and text2pcap"
|
||||
echo "Please give output directory as argument"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
for i in `ls $1/crashes/id*`
|
||||
do
|
||||
PCAPNAME=`echo $i | grep pcap`
|
||||
if [ -z "$PCAPNAME" ]; then
|
||||
hexdump -C $i > $1/$$.tmp
|
||||
text2pcap $1/$$.tmp ${i}.pcap
|
||||
fi
|
||||
done
|
||||
for i in `ls $1/hangs/id*`
|
||||
do
|
||||
PCAPNAME=`echo $i | grep pcap`
|
||||
if [ -z "$PCAPNAME" ]; then
|
||||
hexdump -C $i > $1/$$.tmp
|
||||
text2pcap $1/$$.tmp ${i}.pcap
|
||||
fi
|
||||
done
|
||||
rm -f $1/$$.tmp
|
||||
|
||||
echo
|
||||
echo "Created pcap files:"
|
||||
ls $1/*/*.pcap
|
726
lwip/test/sockets/sockets_stresstest.c
Normal file
726
lwip/test/sockets/sockets_stresstest.c
Normal file
@ -0,0 +1,726 @@
|
||||
/**
|
||||
* @file
|
||||
* Sockets stresstest
|
||||
*
|
||||
* This file uses the lwIP socket API to do stress tests that should test the
|
||||
* stability when used in many different situations, with many concurrent
|
||||
* sockets making concurrent transfers in different manners.
|
||||
*
|
||||
* - test rely on loopback sockets for now, so netif drivers are not tested
|
||||
* - all enabled functions shall be used
|
||||
* - parallelism of the tests depend on enough resources being available
|
||||
* (configure your lwipopts.h settings high enough)
|
||||
* - test should also be able to run in a real target
|
||||
*
|
||||
* TODO:
|
||||
* - full duplex
|
||||
* - add asserts about internal socket/netconn/pcb state?
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2017 Simon Goldschmidt
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Simon Goldschmidt <goldsimon@gmx.de>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
#include "sockets_stresstest.h"
|
||||
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/sys.h"
|
||||
|
||||
#include "lwip/mem.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#if LWIP_SOCKET && LWIP_IPV4 /* this uses IPv4 loopback sockets, currently */
|
||||
|
||||
#ifndef TEST_SOCKETS_STRESS
|
||||
#define TEST_SOCKETS_STRESS LWIP_DBG_OFF
|
||||
#endif
|
||||
|
||||
#define TEST_TIME_SECONDS 10
|
||||
#define TEST_TXRX_BUFSIZE (TCP_MSS * 2)
|
||||
#define TEST_MAX_RXWAIT_MS 50
|
||||
#define TEST_MAX_CONNECTIONS 50
|
||||
|
||||
#define TEST_SOCK_READABLE 0x01
|
||||
#define TEST_SOCK_WRITABLE 0x02
|
||||
#define TEST_SOCK_ERR 0x04
|
||||
|
||||
#define TEST_MODE_SELECT 0x01
|
||||
#define TEST_MODE_POLL 0x02
|
||||
#define TEST_MODE_NONBLOCKING 0x04
|
||||
#define TEST_MODE_WAIT 0x08
|
||||
#define TEST_MODE_RECVTIMEO 0x10
|
||||
#define TEST_MODE_SLEEP 0x20
|
||||
|
||||
static int sockets_stresstest_numthreads;
|
||||
|
||||
struct test_settings {
|
||||
struct sockaddr_storage addr;
|
||||
int start_client;
|
||||
int loop_cnt;
|
||||
};
|
||||
|
||||
struct sockets_stresstest_fullduplex {
|
||||
int s;
|
||||
volatile int closed;
|
||||
};
|
||||
|
||||
static void
|
||||
fill_test_data(void *buf, size_t buf_len_bytes)
|
||||
{
|
||||
u8_t *p = (u8_t*)buf;
|
||||
u16_t i, chk;
|
||||
|
||||
LWIP_ASSERT("buffer too short", buf_len_bytes >= 4);
|
||||
LWIP_ASSERT("buffer too big", buf_len_bytes <= 0xFFFF);
|
||||
/* store the total number of bytes */
|
||||
p[0] = (u8_t)(buf_len_bytes >> 8);
|
||||
p[1] = (u8_t)buf_len_bytes;
|
||||
|
||||
/* fill buffer with random */
|
||||
chk = 0;
|
||||
for (i = 4; i < buf_len_bytes; i++) {
|
||||
u8_t rnd = (u8_t)LWIP_RAND();
|
||||
p[i] = rnd;
|
||||
chk += rnd;
|
||||
}
|
||||
/* store checksum */
|
||||
p[2] = (u8_t)(chk >> 8);
|
||||
p[3] = (u8_t)chk;
|
||||
}
|
||||
|
||||
static size_t
|
||||
check_test_data(const void *buf, size_t buf_len_bytes)
|
||||
{
|
||||
u8_t *p = (u8_t*)buf;
|
||||
u16_t i, chk, chk_rx, len_rx;
|
||||
|
||||
LWIP_ASSERT("buffer too short", buf_len_bytes >= 4);
|
||||
len_rx = (((u16_t)p[0]) << 8) | p[1];
|
||||
LWIP_ASSERT("len too short", len_rx >= 4);
|
||||
if (len_rx > buf_len_bytes) {
|
||||
/* not all data received in this segment */
|
||||
LWIP_DEBUGF(TEST_SOCKETS_STRESS | LWIP_DBG_TRACE, ("check-\n"));
|
||||
return buf_len_bytes;
|
||||
}
|
||||
chk_rx = (((u16_t)p[2]) << 8) | p[3];
|
||||
/* calculate received checksum */
|
||||
chk = 0;
|
||||
for (i = 4; i < len_rx; i++) {
|
||||
chk += p[i];
|
||||
}
|
||||
LWIP_ASSERT("invalid checksum", chk == chk_rx);
|
||||
if (len_rx < buf_len_bytes) {
|
||||
size_t data_left = buf_len_bytes - len_rx;
|
||||
memmove(p, &p[len_rx], data_left);
|
||||
return data_left;
|
||||
}
|
||||
/* if we come here, we received exactly one chunk
|
||||
-> next offset is 0 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t
|
||||
recv_and_check_data_return_offset(int s, char *rxbuf, size_t rxbufsize, size_t rxoff, int *closed, const char *dbg)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
ret = lwip_read(s, &rxbuf[rxoff], rxbufsize - rxoff);
|
||||
if (ret == 0) {
|
||||
*closed = 1;
|
||||
return rxoff;
|
||||
}
|
||||
*closed = 0;
|
||||
LWIP_DEBUGF(TEST_SOCKETS_STRESS | LWIP_DBG_TRACE, ("%s %d rx %d\n", dbg, s, (int)ret));
|
||||
if (ret == -1) {
|
||||
/* TODO: for this to work, 'errno' has to support multithreading... */
|
||||
int err = errno;
|
||||
if (err == ENOTCONN) {
|
||||
*closed = 1;
|
||||
return 0;
|
||||
}
|
||||
LWIP_ASSERT("err == 0", err == 0);
|
||||
}
|
||||
LWIP_ASSERT("ret > 0", ret > 0);
|
||||
return check_test_data(rxbuf, rxoff + ret);
|
||||
}
|
||||
|
||||
#if LWIP_SOCKET_SELECT
|
||||
static int
|
||||
sockets_stresstest_wait_readable_select(int s, int timeout_ms)
|
||||
{
|
||||
int ret;
|
||||
struct timeval tv;
|
||||
fd_set fs_r;
|
||||
fd_set fs_w;
|
||||
fd_set fs_e;
|
||||
|
||||
FD_ZERO(&fs_r);
|
||||
FD_ZERO(&fs_w);
|
||||
FD_ZERO(&fs_e);
|
||||
|
||||
FD_SET(s, &fs_r);
|
||||
FD_SET(s, &fs_e);
|
||||
|
||||
tv.tv_sec = timeout_ms / 1000;
|
||||
tv.tv_usec = (timeout_ms - (tv.tv_sec * 1000)) * 1000;
|
||||
ret = lwip_select(s + 1, &fs_r, &fs_w, &fs_e, &tv);
|
||||
LWIP_ASSERT("select error", ret >= 0);
|
||||
if (ret) {
|
||||
/* convert poll flags to our flags */
|
||||
ret = 0;
|
||||
if (FD_ISSET(s, &fs_r)) {
|
||||
ret |= TEST_SOCK_READABLE;
|
||||
}
|
||||
if (FD_ISSET(s, &fs_w)) {
|
||||
ret |= TEST_SOCK_WRITABLE;
|
||||
}
|
||||
if (FD_ISSET(s, &fs_e)) {
|
||||
ret |= TEST_SOCK_ERR;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LWIP_SOCKET_POLL
|
||||
static int
|
||||
sockets_stresstest_wait_readable_poll(int s, int timeout_ms)
|
||||
{
|
||||
int ret;
|
||||
struct pollfd pfd;
|
||||
|
||||
pfd.fd = s;
|
||||
pfd.revents = 0;
|
||||
pfd.events = POLLIN | POLLERR;
|
||||
|
||||
ret = lwip_poll(&pfd, 1, timeout_ms);
|
||||
if (ret) {
|
||||
/* convert poll flags to our flags */
|
||||
ret = 0;
|
||||
if (pfd.revents & POLLIN) {
|
||||
ret |= TEST_SOCK_READABLE;
|
||||
}
|
||||
if (pfd.revents & POLLOUT) {
|
||||
ret |= TEST_SOCK_WRITABLE;
|
||||
}
|
||||
if (pfd.revents & POLLERR) {
|
||||
ret |= TEST_SOCK_ERR;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LWIP_SO_RCVTIMEO
|
||||
static int
|
||||
sockets_stresstest_wait_readable_recvtimeo(int s, int timeout_ms)
|
||||
{
|
||||
int ret;
|
||||
char buf;
|
||||
#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD
|
||||
int opt_on = timeout_ms;
|
||||
int opt_off = 0;
|
||||
#else
|
||||
struct timeval opt_on, opt_off;
|
||||
opt_on.tv_sec = timeout_ms / 1000;
|
||||
opt_on.tv_usec = (timeout_ms - (opt_on.tv_sec * 1000)) * 1000;
|
||||
opt_off.tv_sec = 0;
|
||||
opt_off.tv_usec = 0;
|
||||
#endif
|
||||
|
||||
/* enable receive timeout */
|
||||
ret = lwip_setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &opt_on, sizeof(opt_on));
|
||||
LWIP_ASSERT("setsockopt error", ret == 0);
|
||||
|
||||
/* peek for one byte with timeout */
|
||||
ret = lwip_recv(s, &buf, 1, MSG_PEEK);
|
||||
|
||||
/* disable receive timeout */
|
||||
ret = lwip_setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &opt_off, sizeof(opt_off));
|
||||
LWIP_ASSERT("setsockopt error", ret == 0);
|
||||
|
||||
if (ret == 1) {
|
||||
return TEST_SOCK_READABLE;
|
||||
}
|
||||
if (ret == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (ret == -1) {
|
||||
return TEST_SOCK_ERR;
|
||||
}
|
||||
LWIP_ASSERT("invalid return value", 0);
|
||||
return TEST_SOCK_ERR;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
sockets_stresstest_wait_readable_wait_peek(int s, int timeout_ms)
|
||||
{
|
||||
int ret;
|
||||
char buf;
|
||||
|
||||
LWIP_UNUSED_ARG(timeout_ms); /* cannot time out here */
|
||||
|
||||
/* peek for one byte */
|
||||
ret = lwip_recv(s, &buf, 1, MSG_PEEK);
|
||||
|
||||
if (ret == 1) {
|
||||
return TEST_SOCK_READABLE;
|
||||
}
|
||||
if (ret == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (ret == -1) {
|
||||
return TEST_SOCK_ERR;
|
||||
}
|
||||
LWIP_ASSERT("invalid return value", 0);
|
||||
return TEST_SOCK_ERR;
|
||||
}
|
||||
|
||||
static int
|
||||
sockets_stresstest_wait_readable_nonblock(int s, int timeout_ms)
|
||||
{
|
||||
int ret;
|
||||
char buf;
|
||||
u32_t wait_until = sys_now() + timeout_ms;
|
||||
|
||||
while(sys_now() < wait_until) {
|
||||
/* peek for one byte */
|
||||
ret = lwip_recv(s, &buf, 1, MSG_PEEK | MSG_DONTWAIT);
|
||||
|
||||
if (ret == 1) {
|
||||
return TEST_SOCK_READABLE;
|
||||
}
|
||||
if (ret == -1) {
|
||||
/* TODO: for this to work, 'errno' has to support multithreading... */
|
||||
int err = errno;
|
||||
if (err != EWOULDBLOCK) {
|
||||
return TEST_SOCK_ERR;
|
||||
}
|
||||
}
|
||||
/* TODO: sleep? */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sockets_stresstest_rand_mode(int allow_wait, int allow_rx)
|
||||
{
|
||||
u32_t random_value = LWIP_RAND();
|
||||
#if LWIP_SOCKET_SELECT
|
||||
if (random_value & TEST_MODE_SELECT) {
|
||||
return TEST_MODE_SELECT;
|
||||
}
|
||||
#endif
|
||||
#if LWIP_SOCKET_POLL
|
||||
if (random_value & TEST_MODE_POLL) {
|
||||
return TEST_MODE_POLL;
|
||||
}
|
||||
#endif
|
||||
if (!allow_rx) {
|
||||
return TEST_MODE_SLEEP;
|
||||
}
|
||||
#if LWIP_SO_RCVTIMEO
|
||||
if (random_value & TEST_MODE_RECVTIMEO) {
|
||||
return TEST_MODE_RECVTIMEO;
|
||||
}
|
||||
#endif
|
||||
if (allow_wait) {
|
||||
if (random_value & TEST_MODE_RECVTIMEO) {
|
||||
return TEST_MODE_RECVTIMEO;
|
||||
}
|
||||
}
|
||||
return TEST_MODE_NONBLOCKING;
|
||||
}
|
||||
|
||||
static int
|
||||
sockets_stresstest_wait_readable(int mode, int s, int timeout_ms)
|
||||
{
|
||||
switch(mode)
|
||||
{
|
||||
#if LWIP_SOCKET_SELECT
|
||||
case TEST_MODE_SELECT:
|
||||
return sockets_stresstest_wait_readable_select(s, timeout_ms);
|
||||
#endif
|
||||
#if LWIP_SOCKET_POLL
|
||||
case TEST_MODE_POLL:
|
||||
return sockets_stresstest_wait_readable_poll(s, timeout_ms);
|
||||
#endif
|
||||
#if LWIP_SO_RCVTIMEO
|
||||
case TEST_MODE_RECVTIMEO:
|
||||
return sockets_stresstest_wait_readable_recvtimeo(s, timeout_ms);
|
||||
#endif
|
||||
case TEST_MODE_WAIT:
|
||||
return sockets_stresstest_wait_readable_wait_peek(s, timeout_ms);
|
||||
case TEST_MODE_NONBLOCKING:
|
||||
return sockets_stresstest_wait_readable_nonblock(s, timeout_ms);
|
||||
case TEST_MODE_SLEEP:
|
||||
{
|
||||
sys_msleep(timeout_ms);
|
||||
return 1;
|
||||
}
|
||||
default:
|
||||
LWIP_ASSERT("invalid mode", 0);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if LWIP_NETCONN_FULLDUPLEX
|
||||
static void
|
||||
sockets_stresstest_conn_client_r(void *arg)
|
||||
{
|
||||
struct sockets_stresstest_fullduplex *fd = (struct sockets_stresstest_fullduplex *)arg;
|
||||
int s = fd->s;
|
||||
size_t rxoff = 0;
|
||||
char rxbuf[TEST_TXRX_BUFSIZE];
|
||||
|
||||
while (1) {
|
||||
int closed;
|
||||
if (fd->closed) {
|
||||
break;
|
||||
}
|
||||
rxoff = recv_and_check_data_return_offset(s, rxbuf, sizeof(rxbuf), rxoff, &closed, "cli");
|
||||
if (fd->closed) {
|
||||
break;
|
||||
}
|
||||
if (closed) {
|
||||
lwip_close(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SYS_ARCH_DEC(sockets_stresstest_numthreads, 1);
|
||||
LWIP_ASSERT("", sockets_stresstest_numthreads >= 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
sockets_stresstest_conn_client(void *arg)
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
struct sockaddr_in *addr_in;
|
||||
int s, ret;
|
||||
char txbuf[TEST_TXRX_BUFSIZE];
|
||||
char rxbuf[TEST_TXRX_BUFSIZE];
|
||||
size_t rxoff = 0;
|
||||
u32_t max_time = sys_now() + (TEST_TIME_SECONDS * 1000);
|
||||
int do_rx = 1;
|
||||
struct sockets_stresstest_fullduplex *data = NULL;
|
||||
|
||||
memcpy(&addr, arg, sizeof(addr));
|
||||
LWIP_ASSERT("", addr.ss_family == AF_INET);
|
||||
addr_in = (struct sockaddr_in *)&addr;
|
||||
addr_in->sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
|
||||
/* sleep a random time between 1 and 2 seconds */
|
||||
sys_msleep(1000 + (LWIP_RAND() % 1000));
|
||||
|
||||
/* connect to the server */
|
||||
s = lwip_socket(addr.ss_family, SOCK_STREAM, 0);
|
||||
LWIP_ASSERT("s >= 0", s >= 0);
|
||||
|
||||
#if LWIP_NETCONN_FULLDUPLEX
|
||||
if (LWIP_RAND() & 1) {
|
||||
sys_thread_t t;
|
||||
data = (struct sockets_stresstest_fullduplex*)mem_malloc(sizeof(struct sockets_stresstest_fullduplex));
|
||||
LWIP_ASSERT("data != NULL", data != 0);
|
||||
SYS_ARCH_INC(sockets_stresstest_numthreads, 1);
|
||||
data->s = s;
|
||||
data->closed = 0;
|
||||
t = sys_thread_new("sockets_stresstest_conn_client_r", sockets_stresstest_conn_client_r, data, 0, 0);
|
||||
LWIP_ASSERT("thread != NULL", t != 0);
|
||||
do_rx = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* @todo: nonblocking connect? */
|
||||
ret = lwip_connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_storage));
|
||||
LWIP_ASSERT("ret == 0", ret == 0);
|
||||
|
||||
while (sys_now() < max_time) {
|
||||
int closed;
|
||||
int mode = sockets_stresstest_rand_mode(0, do_rx);
|
||||
int timeout_ms = LWIP_RAND() % TEST_MAX_RXWAIT_MS;
|
||||
ret = sockets_stresstest_wait_readable(mode, s, timeout_ms);
|
||||
if (ret) {
|
||||
if (do_rx) {
|
||||
/* read some */
|
||||
LWIP_ASSERT("readable", ret == TEST_SOCK_READABLE);
|
||||
rxoff = recv_and_check_data_return_offset(s, rxbuf, sizeof(rxbuf), rxoff, &closed, "cli");
|
||||
LWIP_ASSERT("client got closed", !closed);
|
||||
}
|
||||
} else {
|
||||
/* timeout, send some */
|
||||
size_t send_len = (LWIP_RAND() % (sizeof(txbuf) - 4)) + 4;
|
||||
fill_test_data(txbuf, send_len);
|
||||
LWIP_DEBUGF(TEST_SOCKETS_STRESS | LWIP_DBG_TRACE, ("cli %d tx %d\n", s, (int)send_len));
|
||||
ret = lwip_write(s, txbuf, send_len);
|
||||
if (ret == -1) {
|
||||
/* TODO: for this to work, 'errno' has to support multithreading... */
|
||||
int err = errno;
|
||||
LWIP_ASSERT("err == 0", err == 0);
|
||||
}
|
||||
LWIP_ASSERT("ret == send_len", ret == (int)send_len);
|
||||
}
|
||||
}
|
||||
if (data) {
|
||||
data->closed = 1;
|
||||
}
|
||||
ret = lwip_close(s);
|
||||
LWIP_ASSERT("ret == 0", ret == 0);
|
||||
|
||||
SYS_ARCH_DEC(sockets_stresstest_numthreads, 1);
|
||||
LWIP_ASSERT("", sockets_stresstest_numthreads >= 0);
|
||||
}
|
||||
|
||||
static void
|
||||
sockets_stresstest_conn_server(void *arg)
|
||||
{
|
||||
int s, ret;
|
||||
char txbuf[TEST_TXRX_BUFSIZE];
|
||||
char rxbuf[TEST_TXRX_BUFSIZE];
|
||||
size_t rxoff = 0;
|
||||
|
||||
s = (int)arg;
|
||||
|
||||
while (1) {
|
||||
int closed;
|
||||
int mode = sockets_stresstest_rand_mode(1, 1);
|
||||
int timeout_ms = LWIP_RAND() % TEST_MAX_RXWAIT_MS;
|
||||
ret = sockets_stresstest_wait_readable(mode, s, timeout_ms);
|
||||
if (ret) {
|
||||
if (ret & TEST_SOCK_ERR) {
|
||||
/* closed? */
|
||||
break;
|
||||
}
|
||||
/* read some */
|
||||
LWIP_ASSERT("readable", ret == TEST_SOCK_READABLE);
|
||||
rxoff = recv_and_check_data_return_offset(s, rxbuf, sizeof(rxbuf), rxoff, &closed, "srv");
|
||||
if (closed) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* timeout, send some */
|
||||
size_t send_len = (LWIP_RAND() % (sizeof(txbuf) - 4)) + 4;
|
||||
fill_test_data(txbuf, send_len);
|
||||
LWIP_DEBUGF(TEST_SOCKETS_STRESS | LWIP_DBG_TRACE, ("srv %d tx %d\n", s, (int)send_len));
|
||||
ret = lwip_write(s, txbuf, send_len);
|
||||
if (ret == -1) {
|
||||
/* TODO: for this to work, 'errno' has to support multithreading... */
|
||||
int err = errno;
|
||||
if (err == ECONNRESET) {
|
||||
break;
|
||||
}
|
||||
if (err == ENOTCONN) {
|
||||
break;
|
||||
}
|
||||
LWIP_ASSERT("unknown error", 0);
|
||||
}
|
||||
LWIP_ASSERT("ret == send_len", ret == (int)send_len);
|
||||
}
|
||||
}
|
||||
ret = lwip_close(s);
|
||||
LWIP_ASSERT("ret == 0", ret == 0);
|
||||
|
||||
SYS_ARCH_DEC(sockets_stresstest_numthreads, 1);
|
||||
LWIP_ASSERT("", sockets_stresstest_numthreads >= 0);
|
||||
}
|
||||
|
||||
static int
|
||||
sockets_stresstest_start_clients(const struct sockaddr_storage *remote_addr)
|
||||
{
|
||||
/* limit the number of connections */
|
||||
const int max_connections = LWIP_MIN(TEST_MAX_CONNECTIONS, MEMP_NUM_TCP_PCB/3);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < max_connections; i++) {
|
||||
sys_thread_t t;
|
||||
SYS_ARCH_INC(sockets_stresstest_numthreads, 1);
|
||||
t = sys_thread_new("sockets_stresstest_conn_client", sockets_stresstest_conn_client, (void*)remote_addr, 0, 0);
|
||||
LWIP_ASSERT("thread != NULL", t != 0);
|
||||
}
|
||||
return max_connections;
|
||||
}
|
||||
|
||||
static void
|
||||
sockets_stresstest_listener(void *arg)
|
||||
{
|
||||
int slisten;
|
||||
int ret;
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addr_len;
|
||||
struct test_settings *settings = (struct test_settings *)arg;
|
||||
int num_clients, num_servers = 0;
|
||||
|
||||
slisten = lwip_socket(AF_INET, SOCK_STREAM, 0);
|
||||
LWIP_ASSERT("slisten >= 0", slisten >= 0);
|
||||
|
||||
memcpy(&addr, &settings->addr, sizeof(struct sockaddr_storage));
|
||||
ret = lwip_bind(slisten, (struct sockaddr *)&addr, sizeof(addr));
|
||||
LWIP_ASSERT("ret == 0", ret == 0);
|
||||
|
||||
ret = lwip_listen(slisten, 0);
|
||||
LWIP_ASSERT("ret == 0", ret == 0);
|
||||
|
||||
addr_len = sizeof(addr);
|
||||
ret = lwip_getsockname(slisten, (struct sockaddr *)&addr, &addr_len);
|
||||
LWIP_ASSERT("ret == 0", ret == 0);
|
||||
|
||||
num_clients = sockets_stresstest_start_clients(&addr);
|
||||
|
||||
while (num_servers < num_clients) {
|
||||
struct sockaddr_storage aclient;
|
||||
socklen_t aclient_len = sizeof(aclient);
|
||||
int sclient = lwip_accept(slisten, (struct sockaddr *)&aclient, &aclient_len);
|
||||
#if 1
|
||||
/* using server threads */
|
||||
{
|
||||
sys_thread_t t;
|
||||
SYS_ARCH_INC(sockets_stresstest_numthreads, 1);
|
||||
num_servers++;
|
||||
t = sys_thread_new("sockets_stresstest_conn_server", sockets_stresstest_conn_server, (void*)sclient, 0, 0);
|
||||
LWIP_ASSERT("thread != NULL", t != 0);
|
||||
}
|
||||
#else
|
||||
/* using server select */
|
||||
#endif
|
||||
}
|
||||
LWIP_DEBUGF(TEST_SOCKETS_STRESS | LWIP_DBG_STATE, ("sockets_stresstest_listener: all %d connections established\n", num_clients));
|
||||
|
||||
/* accepted all clients */
|
||||
while (sockets_stresstest_numthreads > 0) {
|
||||
sys_msleep(1);
|
||||
}
|
||||
|
||||
ret = lwip_close(slisten);
|
||||
LWIP_ASSERT("ret == 0", ret == 0);
|
||||
|
||||
LWIP_DEBUGF(TEST_SOCKETS_STRESS |LWIP_DBG_STATE, ("sockets_stresstest_listener: done\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
sockets_stresstest_listener_loop(void *arg)
|
||||
{
|
||||
int i;
|
||||
struct test_settings *settings = (struct test_settings *)arg;
|
||||
|
||||
if (settings->loop_cnt) {
|
||||
for (i = 0; i < settings->loop_cnt; i++) {
|
||||
LWIP_DEBUGF(TEST_SOCKETS_STRESS |LWIP_DBG_STATE, ("sockets_stresstest_listener_loop: iteration %d\n", i));
|
||||
sockets_stresstest_listener(arg);
|
||||
sys_msleep(2);
|
||||
}
|
||||
LWIP_DEBUGF(TEST_SOCKETS_STRESS |LWIP_DBG_STATE, ("sockets_stresstest_listener_loop: done\n"));
|
||||
} else {
|
||||
for (i = 0; ; i++) {
|
||||
LWIP_DEBUGF(TEST_SOCKETS_STRESS |LWIP_DBG_STATE, ("sockets_stresstest_listener_loop: iteration %d\n", i));
|
||||
sockets_stresstest_listener(arg);
|
||||
sys_msleep(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sockets_stresstest_init_loopback(int addr_family)
|
||||
{
|
||||
sys_thread_t t;
|
||||
struct test_settings *settings = (struct test_settings *)mem_malloc(sizeof(struct test_settings));
|
||||
|
||||
LWIP_ASSERT("OOM", settings != NULL);
|
||||
memset(settings, 0, sizeof(struct test_settings));
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
LWIP_ASSERT("invalid addr_family", (addr_family == AF_INET) || (addr_family == AF_INET6));
|
||||
#endif
|
||||
settings->addr.ss_family = (sa_family_t)addr_family;
|
||||
LWIP_UNUSED_ARG(addr_family);
|
||||
settings->start_client = 1;
|
||||
|
||||
t = sys_thread_new("sockets_stresstest_listener_loop", sockets_stresstest_listener_loop, settings, 0, 0);
|
||||
LWIP_ASSERT("thread != NULL", t != 0);
|
||||
}
|
||||
|
||||
void
|
||||
sockets_stresstest_init_server(int addr_family, u16_t server_port)
|
||||
{
|
||||
sys_thread_t t;
|
||||
struct test_settings *settings = (struct test_settings *)mem_malloc(sizeof(struct test_settings));
|
||||
|
||||
LWIP_ASSERT("OOM", settings != NULL);
|
||||
memset(settings, 0, sizeof(struct test_settings));
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
LWIP_ASSERT("invalid addr_family", (addr_family == AF_INET) || (addr_family == AF_INET6));
|
||||
settings->addr.ss_family = (sa_family_t)addr_family;
|
||||
#endif
|
||||
LWIP_UNUSED_ARG(addr_family);
|
||||
((struct sockaddr_in *)(&settings->addr))->sin_port = server_port;
|
||||
|
||||
t = sys_thread_new("sockets_stresstest_listener", sockets_stresstest_listener, settings, 0, 0);
|
||||
LWIP_ASSERT("thread != NULL", t != 0);
|
||||
}
|
||||
|
||||
void
|
||||
sockets_stresstest_init_client(const char *remote_ip, u16_t remote_port)
|
||||
{
|
||||
#if LWIP_IPV4
|
||||
ip4_addr_t ip4;
|
||||
#endif
|
||||
#if LWIP_IPV6
|
||||
ip6_addr_t ip6;
|
||||
#endif
|
||||
struct sockaddr_storage *addr = (struct sockaddr_storage *)mem_malloc(sizeof(struct sockaddr_storage));
|
||||
|
||||
LWIP_ASSERT("OOM", addr != NULL);
|
||||
memset(addr, 0, sizeof(struct test_settings));
|
||||
#if LWIP_IPV4
|
||||
if (ip4addr_aton(remote_ip, &ip4)) {
|
||||
addr->ss_family = AF_INET;
|
||||
((struct sockaddr_in *)addr)->sin_addr.s_addr = ip4_addr_get_u32(&ip4);
|
||||
}
|
||||
#endif
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
else
|
||||
#endif
|
||||
#if LWIP_IPV6
|
||||
if (ip6addr_aton(remote_ip, &ip6)) {
|
||||
addr->ss_family = AF_INET6;
|
||||
/* todo: copy ipv6 address */
|
||||
}
|
||||
#endif
|
||||
((struct sockaddr_in *)addr)->sin_port = remote_port;
|
||||
sockets_stresstest_start_clients(addr);
|
||||
}
|
||||
|
||||
#endif /* LWIP_SOCKET && LWIP_IPV4 */
|
40
lwip/test/sockets/sockets_stresstest.h
Normal file
40
lwip/test/sockets/sockets_stresstest.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Simon Goldschmidt
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Simon Goldschmidt <goldsimon@gmx.de>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LWIP_HDR_TEST_SOCKETS_STRESSTEST
|
||||
#define LWIP_HDR_TEST_SOCKETS_STRESSTEST
|
||||
|
||||
void sockets_stresstest_init_loopback(int addr_family);
|
||||
void sockets_stresstest_init_server(int addr_family, u16_t server_port);
|
||||
void sockets_stresstest_init_client(const char *remote_ip, u16_t remote_port);
|
||||
|
||||
#endif /* LWIP_HDR_TEST_SOCKETS_STRESSTEST */
|
31
lwip/test/unit/Filelists.cmake
Normal file
31
lwip/test/unit/Filelists.cmake
Normal file
@ -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
|
||||
)
|
51
lwip/test/unit/Filelists.mk
Normal file
51
lwip/test/unit/Filelists.mk
Normal file
@ -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
|
||||
|
852
lwip/test/unit/api/test_sockets.c
Normal file
852
lwip/test/unit/api/test_sockets.c
Normal file
@ -0,0 +1,852 @@
|
||||
#include "test_sockets.h"
|
||||
|
||||
#include "lwip/mem.h"
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/priv/sockets_priv.h"
|
||||
#include "lwip/stats.h"
|
||||
|
||||
#include "lwip/tcpip.h"
|
||||
#include "lwip/priv/tcp_priv.h"
|
||||
#include "lwip/api.h"
|
||||
|
||||
|
||||
static int
|
||||
test_sockets_get_used_count(void)
|
||||
{
|
||||
int used = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_SOCKETS; i++) {
|
||||
struct lwip_sock* s = lwip_socket_dbg_get_socket(i);
|
||||
if (s != NULL) {
|
||||
if (s->fd_used) {
|
||||
used++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return used;
|
||||
}
|
||||
|
||||
|
||||
/* Setups/teardown functions */
|
||||
|
||||
static void
|
||||
sockets_setup(void)
|
||||
{
|
||||
/* expect full free heap */
|
||||
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
|
||||
}
|
||||
|
||||
static void
|
||||
sockets_teardown(void)
|
||||
{
|
||||
fail_unless(test_sockets_get_used_count() == 0);
|
||||
/* poll until all memory is released... */
|
||||
tcpip_thread_poll_one();
|
||||
while (tcp_tw_pcbs) {
|
||||
tcp_abort(tcp_tw_pcbs);
|
||||
tcpip_thread_poll_one();
|
||||
}
|
||||
tcpip_thread_poll_one();
|
||||
/* ensure full free heap */
|
||||
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
|
||||
}
|
||||
|
||||
#ifndef NUM_SOCKETS
|
||||
#define NUM_SOCKETS MEMP_NUM_NETCONN
|
||||
#endif
|
||||
|
||||
#if LWIP_SOCKET
|
||||
static int
|
||||
test_sockets_alloc_socket_nonblocking(int domain, int type)
|
||||
{
|
||||
int s = lwip_socket(domain, type, 0);
|
||||
if (s >= 0) {
|
||||
int ret = lwip_fcntl(s, F_SETFL, O_NONBLOCK);
|
||||
fail_unless(ret == 0);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Verify basic sockets functionality
|
||||
*/
|
||||
START_TEST(test_sockets_basics)
|
||||
{
|
||||
int s, i, ret;
|
||||
int s2[NUM_SOCKETS];
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
s = lwip_socket(AF_INET, SOCK_STREAM, 0);
|
||||
fail_unless(s >= 0);
|
||||
lwip_close(s);
|
||||
|
||||
for (i = 0; i < NUM_SOCKETS; i++) {
|
||||
s2[i] = lwip_socket(AF_INET, SOCK_STREAM, 0);
|
||||
fail_unless(s2[i] >= 0);
|
||||
}
|
||||
|
||||
/* all sockets used, now it should fail */
|
||||
s = lwip_socket(AF_INET, SOCK_STREAM, 0);
|
||||
fail_unless(s == -1);
|
||||
/* close one socket */
|
||||
ret = lwip_close(s2[0]);
|
||||
fail_unless(ret == 0);
|
||||
/* now it should succeed */
|
||||
s2[0] = lwip_socket(AF_INET, SOCK_STREAM, 0);
|
||||
fail_unless(s2[0] >= 0);
|
||||
|
||||
/* close all sockets */
|
||||
for (i = 0; i < NUM_SOCKETS; i++) {
|
||||
ret = lwip_close(s2[i]);
|
||||
fail_unless(ret == 0);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void test_sockets_allfunctions_basic_domain(int domain)
|
||||
{
|
||||
int s, s2, s3, ret;
|
||||
struct sockaddr_storage addr, addr2;
|
||||
socklen_t addrlen, addr2len;
|
||||
char buf[4];
|
||||
/* listen socket */
|
||||
s = lwip_socket(domain, SOCK_STREAM, 0);
|
||||
fail_unless(s >= 0);
|
||||
|
||||
ret = lwip_listen(s, 0);
|
||||
fail_unless(ret == 0);
|
||||
|
||||
addrlen = sizeof(addr);
|
||||
ret = lwip_getsockname(s, (struct sockaddr*)&addr, &addrlen);
|
||||
fail_unless(ret == 0);
|
||||
|
||||
s2 = test_sockets_alloc_socket_nonblocking(domain, SOCK_STREAM);
|
||||
fail_unless(s2 >= 0);
|
||||
/* nonblocking connect s2 to s (but use loopback address) */
|
||||
if (domain == AF_INET) {
|
||||
#if LWIP_IPV4
|
||||
struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr;
|
||||
addr4->sin_addr.s_addr = PP_HTONL(INADDR_LOOPBACK);
|
||||
#endif
|
||||
} else {
|
||||
#if LWIP_IPV6
|
||||
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr;
|
||||
struct in6_addr lo6 = IN6ADDR_LOOPBACK_INIT;
|
||||
addr6->sin6_addr = lo6;
|
||||
#endif
|
||||
}
|
||||
ret = lwip_connect(s2, (struct sockaddr*)&addr, addrlen);
|
||||
fail_unless(ret == -1);
|
||||
fail_unless(errno == EINPROGRESS);
|
||||
ret = lwip_connect(s2, (struct sockaddr*)&addr, addrlen);
|
||||
fail_unless(ret == -1);
|
||||
fail_unless(errno == EALREADY);
|
||||
|
||||
while(tcpip_thread_poll_one());
|
||||
|
||||
s3 = lwip_accept(s, (struct sockaddr*)&addr2, &addr2len);
|
||||
fail_unless(s3 >= 0);
|
||||
|
||||
ret = lwip_connect(s2, (struct sockaddr*)&addr, addrlen);
|
||||
fail_unless(ret == -1);
|
||||
fail_unless(errno == EISCONN);
|
||||
|
||||
/* write from server to client */
|
||||
ret = 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 */
|
8
lwip/test/unit/api/test_sockets.h
Normal file
8
lwip/test/unit/api/test_sockets.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef LWIP_HDR_TEST_SOCKETS_H
|
||||
#define LWIP_HDR_TEST_SOCKETS_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite *sockets_suite(void);
|
||||
|
||||
#endif
|
371
lwip/test/unit/arch/sys_arch.c
Normal file
371
lwip/test/unit/arch/sys_arch.c
Normal file
@ -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 */
|
72
lwip/test/unit/arch/sys_arch.h
Normal file
72
lwip/test/unit/arch/sys_arch.h
Normal file
@ -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 */
|
||||
|
84
lwip/test/unit/core/test_def.c
Normal file
84
lwip/test/unit/core/test_def.c
Normal file
@ -0,0 +1,84 @@
|
||||
#include "test_def.h"
|
||||
|
||||
#include "lwip/def.h"
|
||||
|
||||
#define MAGIC_UNTOUCHED_BYTE 0x7a
|
||||
#define TEST_BUFSIZE 32
|
||||
#define GUARD_SIZE 4
|
||||
|
||||
/* Setups/teardown functions */
|
||||
|
||||
static void
|
||||
def_setup(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
def_teardown(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
def_check_range_untouched(const char *buf, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
fail_unless(buf[i] == (char)MAGIC_UNTOUCHED_BYTE);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_def_itoa(int number, const char *expected)
|
||||
{
|
||||
char buf[TEST_BUFSIZE];
|
||||
char *test_buf = &buf[GUARD_SIZE];
|
||||
|
||||
size_t exp_len = strlen(expected);
|
||||
fail_unless(exp_len + 4 < (TEST_BUFSIZE - (2 * GUARD_SIZE)));
|
||||
|
||||
memset(buf, MAGIC_UNTOUCHED_BYTE, sizeof(buf));
|
||||
lwip_itoa(test_buf, exp_len + 1, number);
|
||||
def_check_range_untouched(buf, GUARD_SIZE);
|
||||
fail_unless(test_buf[exp_len] == 0);
|
||||
fail_unless(!memcmp(test_buf, expected, exp_len));
|
||||
def_check_range_untouched(&test_buf[exp_len + 1], TEST_BUFSIZE - GUARD_SIZE - exp_len - 1);
|
||||
|
||||
/* check with too small buffer */
|
||||
memset(buf, MAGIC_UNTOUCHED_BYTE, sizeof(buf));
|
||||
lwip_itoa(test_buf, exp_len, number);
|
||||
def_check_range_untouched(buf, GUARD_SIZE);
|
||||
def_check_range_untouched(&test_buf[exp_len + 1], TEST_BUFSIZE - GUARD_SIZE - exp_len - 1);
|
||||
|
||||
/* check with too large buffer */
|
||||
memset(buf, MAGIC_UNTOUCHED_BYTE, sizeof(buf));
|
||||
lwip_itoa(test_buf, exp_len + 4, number);
|
||||
def_check_range_untouched(buf, GUARD_SIZE);
|
||||
fail_unless(test_buf[exp_len] == 0);
|
||||
fail_unless(!memcmp(test_buf, expected, exp_len));
|
||||
def_check_range_untouched(&test_buf[exp_len + 4], TEST_BUFSIZE - GUARD_SIZE - exp_len - 4);
|
||||
}
|
||||
|
||||
START_TEST(test_def_lwip_itoa)
|
||||
{
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
test_def_itoa(0, "0");
|
||||
test_def_itoa(1, "1");
|
||||
test_def_itoa(-1, "-1");
|
||||
test_def_itoa(15, "15");
|
||||
test_def_itoa(-15, "-15");
|
||||
test_def_itoa(156, "156");
|
||||
test_def_itoa(1192, "1192");
|
||||
test_def_itoa(-156, "-156");
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/** Create the suite including all tests for this module */
|
||||
Suite *
|
||||
def_suite(void)
|
||||
{
|
||||
testfunc tests[] = {
|
||||
TESTFUNC(test_def_lwip_itoa)
|
||||
};
|
||||
return create_suite("DEF", tests, sizeof(tests)/sizeof(testfunc), def_setup, def_teardown);
|
||||
}
|
8
lwip/test/unit/core/test_def.h
Normal file
8
lwip/test/unit/core/test_def.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef LWIP_HDR_TEST_DEF_H
|
||||
#define LWIP_HDR_TEST_DEF_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite *def_suite(void);
|
||||
|
||||
#endif
|
224
lwip/test/unit/core/test_mem.c
Normal file
224
lwip/test/unit/core/test_mem.c
Normal file
@ -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);
|
||||
}
|
8
lwip/test/unit/core/test_mem.h
Normal file
8
lwip/test/unit/core/test_mem.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef LWIP_HDR_TEST_MEM_H
|
||||
#define LWIP_HDR_TEST_MEM_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite *mem_suite(void);
|
||||
|
||||
#endif
|
227
lwip/test/unit/core/test_netif.c
Normal file
227
lwip/test/unit/core/test_netif.c
Normal file
@ -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);
|
||||
}
|
8
lwip/test/unit/core/test_netif.h
Normal file
8
lwip/test/unit/core/test_netif.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef LWIP_HDR_TEST_NETIF_H
|
||||
#define LWIP_HDR_TEST_NETIF_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite *netif_suite(void);
|
||||
|
||||
#endif
|
271
lwip/test/unit/core/test_pbuf.c
Normal file
271
lwip/test/unit/core/test_pbuf.c
Normal file
@ -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);
|
||||
}
|
8
lwip/test/unit/core/test_pbuf.h
Normal file
8
lwip/test/unit/core/test_pbuf.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef LWIP_HDR_TEST_PBUF_H
|
||||
#define LWIP_HDR_TEST_PBUF_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite *pbuf_suite(void);
|
||||
|
||||
#endif
|
233
lwip/test/unit/core/test_timers.c
Normal file
233
lwip/test/unit/core/test_timers.c
Normal file
@ -0,0 +1,233 @@
|
||||
#include "test_timers.h"
|
||||
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/timeouts.h"
|
||||
#include "arch/sys_arch.h"
|
||||
|
||||
/* Setups/teardown functions */
|
||||
|
||||
static struct sys_timeo* old_list_head;
|
||||
|
||||
static void
|
||||
timers_setup(void)
|
||||
{
|
||||
struct sys_timeo** list_head = sys_timeouts_get_next_timeout();
|
||||
old_list_head = *list_head;
|
||||
*list_head = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
timers_teardown(void)
|
||||
{
|
||||
struct sys_timeo** list_head = sys_timeouts_get_next_timeout();
|
||||
*list_head = old_list_head;
|
||||
lwip_sys_now = 0;
|
||||
}
|
||||
|
||||
static int fired[3];
|
||||
static void
|
||||
dummy_handler(void* arg)
|
||||
{
|
||||
int index = LWIP_PTR_NUMERIC_CAST(int, arg);
|
||||
fired[index] = 1;
|
||||
}
|
||||
|
||||
#define HANDLER_EXECUTION_TIME 5
|
||||
static int cyclic_fired;
|
||||
static void
|
||||
dummy_cyclic_handler(void)
|
||||
{
|
||||
cyclic_fired = 1;
|
||||
lwip_sys_now += HANDLER_EXECUTION_TIME;
|
||||
}
|
||||
|
||||
struct lwip_cyclic_timer test_cyclic = {10, dummy_cyclic_handler};
|
||||
|
||||
static void
|
||||
do_test_cyclic_timers(u32_t offset)
|
||||
{
|
||||
struct sys_timeo** list_head = sys_timeouts_get_next_timeout();
|
||||
|
||||
/* verify normal timer expiration */
|
||||
lwip_sys_now = offset + 0;
|
||||
sys_timeout(test_cyclic.interval_ms, lwip_cyclic_timer, &test_cyclic);
|
||||
|
||||
cyclic_fired = 0;
|
||||
sys_check_timeouts();
|
||||
fail_unless(cyclic_fired == 0);
|
||||
|
||||
lwip_sys_now = offset + test_cyclic.interval_ms;
|
||||
sys_check_timeouts();
|
||||
fail_unless(cyclic_fired == 1);
|
||||
|
||||
fail_unless((*list_head)->time == (u32_t)(lwip_sys_now + test_cyclic.interval_ms - HANDLER_EXECUTION_TIME));
|
||||
|
||||
sys_untimeout(lwip_cyclic_timer, &test_cyclic);
|
||||
|
||||
|
||||
/* verify "overload" - next cyclic timer execution is already overdue twice */
|
||||
lwip_sys_now = offset + 0;
|
||||
sys_timeout(test_cyclic.interval_ms, lwip_cyclic_timer, &test_cyclic);
|
||||
|
||||
cyclic_fired = 0;
|
||||
sys_check_timeouts();
|
||||
fail_unless(cyclic_fired == 0);
|
||||
|
||||
lwip_sys_now = offset + 2*test_cyclic.interval_ms;
|
||||
sys_check_timeouts();
|
||||
fail_unless(cyclic_fired == 1);
|
||||
|
||||
fail_unless((*list_head)->time == (u32_t)(lwip_sys_now + test_cyclic.interval_ms));
|
||||
}
|
||||
|
||||
START_TEST(test_cyclic_timers)
|
||||
{
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
/* check without u32_t wraparound */
|
||||
do_test_cyclic_timers(0);
|
||||
|
||||
/* check with u32_t wraparound */
|
||||
do_test_cyclic_timers(0xfffffff0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* reproduce bug #52748: the bug in timeouts.c */
|
||||
START_TEST(test_bug52748)
|
||||
{
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
memset(&fired, 0, sizeof(fired));
|
||||
|
||||
lwip_sys_now = 50;
|
||||
sys_timeout(20, dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 0));
|
||||
sys_timeout( 5, dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 2));
|
||||
|
||||
lwip_sys_now = 55;
|
||||
sys_check_timeouts();
|
||||
fail_unless(fired[0] == 0);
|
||||
fail_unless(fired[1] == 0);
|
||||
fail_unless(fired[2] == 1);
|
||||
|
||||
lwip_sys_now = 60;
|
||||
sys_timeout(10, dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 1));
|
||||
sys_check_timeouts();
|
||||
fail_unless(fired[0] == 0);
|
||||
fail_unless(fired[1] == 0);
|
||||
fail_unless(fired[2] == 1);
|
||||
|
||||
lwip_sys_now = 70;
|
||||
sys_check_timeouts();
|
||||
fail_unless(fired[0] == 1);
|
||||
fail_unless(fired[1] == 1);
|
||||
fail_unless(fired[2] == 1);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void
|
||||
do_test_timers(u32_t offset)
|
||||
{
|
||||
struct sys_timeo** list_head = sys_timeouts_get_next_timeout();
|
||||
|
||||
lwip_sys_now = offset + 0;
|
||||
|
||||
sys_timeout(10, dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 0));
|
||||
fail_unless(sys_timeouts_sleeptime() == 10);
|
||||
sys_timeout(20, dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 1));
|
||||
fail_unless(sys_timeouts_sleeptime() == 10);
|
||||
sys_timeout( 5, dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 2));
|
||||
fail_unless(sys_timeouts_sleeptime() == 5);
|
||||
|
||||
/* linked list correctly sorted? */
|
||||
fail_unless((*list_head)->time == (u32_t)(lwip_sys_now + 5));
|
||||
fail_unless((*list_head)->next->time == (u32_t)(lwip_sys_now + 10));
|
||||
fail_unless((*list_head)->next->next->time == (u32_t)(lwip_sys_now + 20));
|
||||
|
||||
/* check timers expire in correct order */
|
||||
memset(&fired, 0, sizeof(fired));
|
||||
|
||||
lwip_sys_now += 4;
|
||||
sys_check_timeouts();
|
||||
fail_unless(fired[2] == 0);
|
||||
|
||||
lwip_sys_now += 1;
|
||||
sys_check_timeouts();
|
||||
fail_unless(fired[2] == 1);
|
||||
|
||||
lwip_sys_now += 4;
|
||||
sys_check_timeouts();
|
||||
fail_unless(fired[0] == 0);
|
||||
|
||||
lwip_sys_now += 1;
|
||||
sys_check_timeouts();
|
||||
fail_unless(fired[0] == 1);
|
||||
|
||||
lwip_sys_now += 9;
|
||||
sys_check_timeouts();
|
||||
fail_unless(fired[1] == 0);
|
||||
|
||||
lwip_sys_now += 1;
|
||||
sys_check_timeouts();
|
||||
fail_unless(fired[1] == 1);
|
||||
|
||||
sys_untimeout(dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 0));
|
||||
sys_untimeout(dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 1));
|
||||
sys_untimeout(dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 2));
|
||||
}
|
||||
|
||||
START_TEST(test_timers)
|
||||
{
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
/* check without u32_t wraparound */
|
||||
do_test_timers(0);
|
||||
|
||||
/* check with u32_t wraparound */
|
||||
do_test_timers(0xfffffff0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_long_timer)
|
||||
{
|
||||
LWIP_UNUSED_ARG(_i);
|
||||
|
||||
memset(&fired, 0, sizeof(fired));
|
||||
lwip_sys_now = 0;
|
||||
|
||||
sys_timeout(LWIP_UINT32_MAX / 4, dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 0));
|
||||
fail_unless(sys_timeouts_sleeptime() == LWIP_UINT32_MAX / 4);
|
||||
|
||||
sys_check_timeouts();
|
||||
fail_unless(fired[0] == 0);
|
||||
|
||||
lwip_sys_now += LWIP_UINT32_MAX / 8;
|
||||
|
||||
sys_check_timeouts();
|
||||
fail_unless(fired[0] == 0);
|
||||
|
||||
lwip_sys_now += LWIP_UINT32_MAX / 8;
|
||||
|
||||
sys_check_timeouts();
|
||||
fail_unless(fired[0] == 0);
|
||||
|
||||
lwip_sys_now += 1;
|
||||
|
||||
sys_check_timeouts();
|
||||
fail_unless(fired[0] == 1);
|
||||
|
||||
sys_untimeout(dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 0));
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/** Create the suite including all tests for this module */
|
||||
Suite *
|
||||
timers_suite(void)
|
||||
{
|
||||
testfunc tests[] = {
|
||||
TESTFUNC(test_bug52748),
|
||||
TESTFUNC(test_cyclic_timers),
|
||||
TESTFUNC(test_timers),
|
||||
TESTFUNC(test_long_timer),
|
||||
};
|
||||
return create_suite("TIMERS", tests, LWIP_ARRAYSIZE(tests), timers_setup, timers_teardown);
|
||||
}
|
8
lwip/test/unit/core/test_timers.h
Normal file
8
lwip/test/unit/core/test_timers.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef LWIP_HDR_TEST_TIMERS_H
|
||||
#define LWIP_HDR_TEST_TIMERS_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite *timers_suite(void);
|
||||
|
||||
#endif
|
1057
lwip/test/unit/dhcp/test_dhcp.c
Normal file
1057
lwip/test/unit/dhcp/test_dhcp.c
Normal file
File diff suppressed because it is too large
Load Diff
8
lwip/test/unit/dhcp/test_dhcp.h
Normal file
8
lwip/test/unit/dhcp/test_dhcp.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef LWIP_HDR_TEST_DHCP_H
|
||||
#define LWIP_HDR_TEST_DHCP_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite* dhcp_suite(void);
|
||||
|
||||
#endif
|
272
lwip/test/unit/etharp/test_etharp.c
Normal file
272
lwip/test/unit/etharp/test_etharp.c
Normal file
@ -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);
|
||||
}
|
8
lwip/test/unit/etharp/test_etharp.h
Normal file
8
lwip/test/unit/etharp/test_etharp.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef LWIP_HDR_TEST_ETHARP_H
|
||||
#define LWIP_HDR_TEST_ETHARP_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite* etharp_suite(void);
|
||||
|
||||
#endif
|
160
lwip/test/unit/ip4/test_ip4.c
Normal file
160
lwip/test/unit/ip4/test_ip4.c
Normal file
@ -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);
|
||||
}
|
8
lwip/test/unit/ip4/test_ip4.h
Normal file
8
lwip/test/unit/ip4/test_ip4.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef LWIP_HDR_TEST_IP4_H
|
||||
#define LWIP_HDR_TEST_IP4_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite* ip4_suite(void);
|
||||
|
||||
#endif
|
321
lwip/test/unit/ip6/test_ip6.c
Normal file
321
lwip/test/unit/ip6/test_ip6.c
Normal file
@ -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 */
|
8
lwip/test/unit/ip6/test_ip6.h
Normal file
8
lwip/test/unit/ip6/test_ip6.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef LWIP_HDR_TEST_IP6_H
|
||||
#define LWIP_HDR_TEST_IP6_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite* ip6_suite(void);
|
||||
|
||||
#endif
|
42
lwip/test/unit/lwip_check.h
Normal file
42
lwip/test/unit/lwip_check.h
Normal file
@ -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 */
|
107
lwip/test/unit/lwip_unittests.c
Normal file
107
lwip/test/unit/lwip_unittests.c
Normal file
@ -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;
|
||||
}
|
85
lwip/test/unit/lwipopts.h
Normal file
85
lwip/test/unit/lwipopts.h
Normal file
@ -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 */
|
915
lwip/test/unit/mdns/test_mdns.c
Normal file
915
lwip/test/unit/mdns/test_mdns.c
Normal file
@ -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);
|
||||
}
|
8
lwip/test/unit/mdns/test_mdns.h
Normal file
8
lwip/test/unit/mdns/test_mdns.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef LWIP_HDR_TEST_MDNS_H__
|
||||
#define LWIP_HDR_TEST_MDNS_H__
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite* mdns_suite(void);
|
||||
|
||||
#endif
|
115
lwip/test/unit/mqtt/test_mqtt.c
Normal file
115
lwip/test/unit/mqtt/test_mqtt.c
Normal file
@ -0,0 +1,115 @@
|
||||
#include "test_mqtt.h"
|
||||
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/apps/mqtt.h"
|
||||
#include "lwip/apps/mqtt_priv.h"
|
||||
#include "lwip/netif.h"
|
||||
|
||||
const ip_addr_t test_mqtt_local_ip = IPADDR4_INIT_BYTES(192, 168, 1, 1);
|
||||
const ip_addr_t test_mqtt_remote_ip = IPADDR4_INIT_BYTES(192, 168, 1, 2);
|
||||
const ip_addr_t test_mqtt_netmask = IPADDR4_INIT_BYTES(255, 255, 255, 0);
|
||||
|
||||
static err_t test_mqtt_netif_output(struct netif *netif, struct pbuf *p,
|
||||
const ip4_addr_t *ipaddr)
|
||||
{
|
||||
LWIP_UNUSED_ARG(netif);
|
||||
LWIP_UNUSED_ARG(ipaddr);
|
||||
LWIP_UNUSED_ARG(p);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
test_mqtt_init_netif(struct netif *netif, const ip_addr_t *ip_addr, const ip_addr_t *netmask)
|
||||
{
|
||||
struct netif *n;
|
||||
memset(netif, 0, sizeof(struct netif));
|
||||
netif->output = test_mqtt_netif_output;
|
||||
netif->flags |= NETIF_FLAG_UP | NETIF_FLAG_LINK_UP;
|
||||
ip_addr_copy_from_ip4(netif->netmask, *ip_2_ip4(netmask));
|
||||
ip_addr_copy_from_ip4(netif->ip_addr, *ip_2_ip4(ip_addr));
|
||||
for (n = netif_list; n != NULL; n = n->next) {
|
||||
if (n == netif) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
netif->next = NULL;
|
||||
netif_list = netif;
|
||||
}
|
||||
|
||||
/* Setups/teardown functions */
|
||||
static struct netif *old_netif_list;
|
||||
static struct netif *old_netif_default;
|
||||
|
||||
static void
|
||||
mqtt_setup(void)
|
||||
{
|
||||
old_netif_list = netif_list;
|
||||
old_netif_default = netif_default;
|
||||
netif_list = NULL;
|
||||
netif_default = NULL;
|
||||
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
|
||||
}
|
||||
|
||||
static void
|
||||
mqtt_teardown(void)
|
||||
{
|
||||
netif_list = NULL;
|
||||
netif_default = NULL;
|
||||
/* restore netif_list for next tests (e.g. loopif) */
|
||||
netif_list = old_netif_list;
|
||||
netif_default = old_netif_default;
|
||||
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
|
||||
}
|
||||
|
||||
static void test_mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status)
|
||||
{
|
||||
LWIP_UNUSED_ARG(client);
|
||||
LWIP_UNUSED_ARG(arg);
|
||||
LWIP_UNUSED_ARG(status);
|
||||
}
|
||||
|
||||
START_TEST(basic_connect)
|
||||
{
|
||||
mqtt_client_t* client;
|
||||
struct netif netif;
|
||||
err_t err;
|
||||
struct mqtt_connect_client_info_t client_info = {
|
||||
"dumm",
|
||||
NULL, NULL,
|
||||
10,
|
||||
NULL, NULL, 0, 0
|
||||
};
|
||||
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);
|
||||
}
|
8
lwip/test/unit/mqtt/test_mqtt.h
Normal file
8
lwip/test/unit/mqtt/test_mqtt.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef LWIP_HDR_TEST_MQTT_H__
|
||||
#define LWIP_HDR_TEST_MQTT_H__
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite* mqtt_suite(void);
|
||||
|
||||
#endif
|
319
lwip/test/unit/tcp/tcp_helper.c
Normal file
319
lwip/test/unit/tcp/tcp_helper.c
Normal file
@ -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;
|
||||
}
|
58
lwip/test/unit/tcp/tcp_helper.h
Normal file
58
lwip/test/unit/tcp/tcp_helper.h
Normal file
@ -0,0 +1,58 @@
|
||||
#ifndef LWIP_HDR_TCP_HELPER_H
|
||||
#define LWIP_HDR_TCP_HELPER_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
#include "lwip/arch.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/netif.h"
|
||||
|
||||
/* counters used for test_tcp_counters_* callback functions */
|
||||
struct test_tcp_counters {
|
||||
u32_t recv_calls;
|
||||
u32_t recved_bytes;
|
||||
u32_t recv_calls_after_close;
|
||||
u32_t recved_bytes_after_close;
|
||||
u32_t close_calls;
|
||||
u32_t err_calls;
|
||||
err_t last_err;
|
||||
char* expected_data;
|
||||
u32_t expected_data_len;
|
||||
};
|
||||
|
||||
struct test_tcp_txcounters {
|
||||
u32_t num_tx_calls;
|
||||
u32_t num_tx_bytes;
|
||||
u8_t copy_tx_packets;
|
||||
struct pbuf *tx_packets;
|
||||
};
|
||||
|
||||
extern const ip_addr_t test_local_ip;
|
||||
extern const ip_addr_t test_remote_ip;
|
||||
extern const ip_addr_t test_netmask;
|
||||
#define TEST_REMOTE_PORT 0x100
|
||||
#define TEST_LOCAL_PORT 0x101
|
||||
|
||||
/* Helper functions */
|
||||
void tcp_remove_all(void);
|
||||
|
||||
struct pbuf* tcp_create_segment(ip_addr_t* src_ip, ip_addr_t* dst_ip,
|
||||
u16_t src_port, u16_t dst_port, void* data, size_t data_len,
|
||||
u32_t seqno, u32_t ackno, u8_t headerflags);
|
||||
struct pbuf* tcp_create_rx_segment(struct tcp_pcb* pcb, void* data, size_t data_len,
|
||||
u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags);
|
||||
struct pbuf* tcp_create_rx_segment_wnd(struct tcp_pcb* pcb, void* data, size_t data_len,
|
||||
u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags, u16_t wnd);
|
||||
void tcp_set_state(struct tcp_pcb* pcb, enum tcp_state state, const ip_addr_t* local_ip,
|
||||
const ip_addr_t* remote_ip, u16_t local_port, u16_t remote_port);
|
||||
void test_tcp_counters_err(void* arg, err_t err);
|
||||
err_t test_tcp_counters_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err);
|
||||
|
||||
struct tcp_pcb* test_tcp_new_counters_pcb(struct test_tcp_counters* counters);
|
||||
|
||||
void test_tcp_input(struct pbuf *p, struct netif *inp);
|
||||
|
||||
void test_tcp_init_netif(struct netif *netif, struct test_tcp_txcounters *txcounters,
|
||||
const ip_addr_t *ip_addr, const ip_addr_t *netmask);
|
||||
|
||||
|
||||
#endif
|
1695
lwip/test/unit/tcp/test_tcp.c
Normal file
1695
lwip/test/unit/tcp/test_tcp.c
Normal file
File diff suppressed because it is too large
Load Diff
8
lwip/test/unit/tcp/test_tcp.h
Normal file
8
lwip/test/unit/tcp/test_tcp.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef LWIP_HDR_TEST_TCP_H
|
||||
#define LWIP_HDR_TEST_TCP_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite *tcp_suite(void);
|
||||
|
||||
#endif
|
1018
lwip/test/unit/tcp/test_tcp_oos.c
Normal file
1018
lwip/test/unit/tcp/test_tcp_oos.c
Normal file
File diff suppressed because it is too large
Load Diff
8
lwip/test/unit/tcp/test_tcp_oos.h
Normal file
8
lwip/test/unit/tcp/test_tcp_oos.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef LWIP_HDR_TEST_TCP_OOS_H
|
||||
#define LWIP_HDR_TEST_TCP_OOS_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite *tcp_oos_suite(void);
|
||||
|
||||
#endif
|
347
lwip/test/unit/udp/test_udp.c
Normal file
347
lwip/test/unit/udp/test_udp.c
Normal file
@ -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);
|
||||
}
|
8
lwip/test/unit/udp/test_udp.h
Normal file
8
lwip/test/unit/udp/test_udp.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef LWIP_HDR_TEST_UDP_H
|
||||
#define LWIP_HDR_TEST_UDP_H
|
||||
|
||||
#include "../lwip_check.h"
|
||||
|
||||
Suite* udp_suite(void);
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user