Init commit

This commit is contained in:
2021-04-27 17:22:34 +02:00
commit 4f6fe6959f
1140 changed files with 1174277 additions and 0 deletions

37
lwip/test/fuzz/README Normal file
View 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
View File

189
lwip/test/fuzz/fuzz.c Normal file
View 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;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

80
lwip/test/fuzz/lwipopts.h Normal file
View 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__ */

View File

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

View File

@ -0,0 +1,726 @@
/**
* @file
* Sockets stresstest
*
* This file uses the lwIP socket API to do stress tests that should test the
* stability when used in many different situations, with many concurrent
* sockets making concurrent transfers in different manners.
*
* - test rely on loopback sockets for now, so netif drivers are not tested
* - all enabled functions shall be used
* - parallelism of the tests depend on enough resources being available
* (configure your lwipopts.h settings high enough)
* - test should also be able to run in a real target
*
* TODO:
* - full duplex
* - add asserts about internal socket/netconn/pcb state?
*/
/*
* Copyright (c) 2017 Simon Goldschmidt
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Simon Goldschmidt <goldsimon@gmx.de>
*
*/
#include "lwip/opt.h"
#include "sockets_stresstest.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/mem.h"
#include <stdio.h>
#include <string.h>
#if LWIP_SOCKET && LWIP_IPV4 /* this uses IPv4 loopback sockets, currently */
#ifndef TEST_SOCKETS_STRESS
#define TEST_SOCKETS_STRESS LWIP_DBG_OFF
#endif
#define TEST_TIME_SECONDS 10
#define TEST_TXRX_BUFSIZE (TCP_MSS * 2)
#define TEST_MAX_RXWAIT_MS 50
#define TEST_MAX_CONNECTIONS 50
#define TEST_SOCK_READABLE 0x01
#define TEST_SOCK_WRITABLE 0x02
#define TEST_SOCK_ERR 0x04
#define TEST_MODE_SELECT 0x01
#define TEST_MODE_POLL 0x02
#define TEST_MODE_NONBLOCKING 0x04
#define TEST_MODE_WAIT 0x08
#define TEST_MODE_RECVTIMEO 0x10
#define TEST_MODE_SLEEP 0x20
static int sockets_stresstest_numthreads;
struct test_settings {
struct sockaddr_storage addr;
int start_client;
int loop_cnt;
};
struct sockets_stresstest_fullduplex {
int s;
volatile int closed;
};
static void
fill_test_data(void *buf, size_t buf_len_bytes)
{
u8_t *p = (u8_t*)buf;
u16_t i, chk;
LWIP_ASSERT("buffer too short", buf_len_bytes >= 4);
LWIP_ASSERT("buffer too big", buf_len_bytes <= 0xFFFF);
/* store the total number of bytes */
p[0] = (u8_t)(buf_len_bytes >> 8);
p[1] = (u8_t)buf_len_bytes;
/* fill buffer with random */
chk = 0;
for (i = 4; i < buf_len_bytes; i++) {
u8_t rnd = (u8_t)LWIP_RAND();
p[i] = rnd;
chk += rnd;
}
/* store checksum */
p[2] = (u8_t)(chk >> 8);
p[3] = (u8_t)chk;
}
static size_t
check_test_data(const void *buf, size_t buf_len_bytes)
{
u8_t *p = (u8_t*)buf;
u16_t i, chk, chk_rx, len_rx;
LWIP_ASSERT("buffer too short", buf_len_bytes >= 4);
len_rx = (((u16_t)p[0]) << 8) | p[1];
LWIP_ASSERT("len too short", len_rx >= 4);
if (len_rx > buf_len_bytes) {
/* not all data received in this segment */
LWIP_DEBUGF(TEST_SOCKETS_STRESS | LWIP_DBG_TRACE, ("check-\n"));
return buf_len_bytes;
}
chk_rx = (((u16_t)p[2]) << 8) | p[3];
/* calculate received checksum */
chk = 0;
for (i = 4; i < len_rx; i++) {
chk += p[i];
}
LWIP_ASSERT("invalid checksum", chk == chk_rx);
if (len_rx < buf_len_bytes) {
size_t data_left = buf_len_bytes - len_rx;
memmove(p, &p[len_rx], data_left);
return data_left;
}
/* if we come here, we received exactly one chunk
-> next offset is 0 */
return 0;
}
static size_t
recv_and_check_data_return_offset(int s, char *rxbuf, size_t rxbufsize, size_t rxoff, int *closed, const char *dbg)
{
ssize_t ret;
ret = lwip_read(s, &rxbuf[rxoff], rxbufsize - rxoff);
if (ret == 0) {
*closed = 1;
return rxoff;
}
*closed = 0;
LWIP_DEBUGF(TEST_SOCKETS_STRESS | LWIP_DBG_TRACE, ("%s %d rx %d\n", dbg, s, (int)ret));
if (ret == -1) {
/* TODO: for this to work, 'errno' has to support multithreading... */
int err = errno;
if (err == ENOTCONN) {
*closed = 1;
return 0;
}
LWIP_ASSERT("err == 0", err == 0);
}
LWIP_ASSERT("ret > 0", ret > 0);
return check_test_data(rxbuf, rxoff + ret);
}
#if LWIP_SOCKET_SELECT
static int
sockets_stresstest_wait_readable_select(int s, int timeout_ms)
{
int ret;
struct timeval tv;
fd_set fs_r;
fd_set fs_w;
fd_set fs_e;
FD_ZERO(&fs_r);
FD_ZERO(&fs_w);
FD_ZERO(&fs_e);
FD_SET(s, &fs_r);
FD_SET(s, &fs_e);
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms - (tv.tv_sec * 1000)) * 1000;
ret = lwip_select(s + 1, &fs_r, &fs_w, &fs_e, &tv);
LWIP_ASSERT("select error", ret >= 0);
if (ret) {
/* convert poll flags to our flags */
ret = 0;
if (FD_ISSET(s, &fs_r)) {
ret |= TEST_SOCK_READABLE;
}
if (FD_ISSET(s, &fs_w)) {
ret |= TEST_SOCK_WRITABLE;
}
if (FD_ISSET(s, &fs_e)) {
ret |= TEST_SOCK_ERR;
}
return ret;
}
return 0;
}
#endif
#if LWIP_SOCKET_POLL
static int
sockets_stresstest_wait_readable_poll(int s, int timeout_ms)
{
int ret;
struct pollfd pfd;
pfd.fd = s;
pfd.revents = 0;
pfd.events = POLLIN | POLLERR;
ret = lwip_poll(&pfd, 1, timeout_ms);
if (ret) {
/* convert poll flags to our flags */
ret = 0;
if (pfd.revents & POLLIN) {
ret |= TEST_SOCK_READABLE;
}
if (pfd.revents & POLLOUT) {
ret |= TEST_SOCK_WRITABLE;
}
if (pfd.revents & POLLERR) {
ret |= TEST_SOCK_ERR;
}
return ret;
}
return 0;
}
#endif
#if LWIP_SO_RCVTIMEO
static int
sockets_stresstest_wait_readable_recvtimeo(int s, int timeout_ms)
{
int ret;
char buf;
#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD
int opt_on = timeout_ms;
int opt_off = 0;
#else
struct timeval opt_on, opt_off;
opt_on.tv_sec = timeout_ms / 1000;
opt_on.tv_usec = (timeout_ms - (opt_on.tv_sec * 1000)) * 1000;
opt_off.tv_sec = 0;
opt_off.tv_usec = 0;
#endif
/* enable receive timeout */
ret = lwip_setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &opt_on, sizeof(opt_on));
LWIP_ASSERT("setsockopt error", ret == 0);
/* peek for one byte with timeout */
ret = lwip_recv(s, &buf, 1, MSG_PEEK);
/* disable receive timeout */
ret = lwip_setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &opt_off, sizeof(opt_off));
LWIP_ASSERT("setsockopt error", ret == 0);
if (ret == 1) {
return TEST_SOCK_READABLE;
}
if (ret == 0) {
return 0;
}
if (ret == -1) {
return TEST_SOCK_ERR;
}
LWIP_ASSERT("invalid return value", 0);
return TEST_SOCK_ERR;
}
#endif
static int
sockets_stresstest_wait_readable_wait_peek(int s, int timeout_ms)
{
int ret;
char buf;
LWIP_UNUSED_ARG(timeout_ms); /* cannot time out here */
/* peek for one byte */
ret = lwip_recv(s, &buf, 1, MSG_PEEK);
if (ret == 1) {
return TEST_SOCK_READABLE;
}
if (ret == 0) {
return 0;
}
if (ret == -1) {
return TEST_SOCK_ERR;
}
LWIP_ASSERT("invalid return value", 0);
return TEST_SOCK_ERR;
}
static int
sockets_stresstest_wait_readable_nonblock(int s, int timeout_ms)
{
int ret;
char buf;
u32_t wait_until = sys_now() + timeout_ms;
while(sys_now() < wait_until) {
/* peek for one byte */
ret = lwip_recv(s, &buf, 1, MSG_PEEK | MSG_DONTWAIT);
if (ret == 1) {
return TEST_SOCK_READABLE;
}
if (ret == -1) {
/* TODO: for this to work, 'errno' has to support multithreading... */
int err = errno;
if (err != EWOULDBLOCK) {
return TEST_SOCK_ERR;
}
}
/* TODO: sleep? */
}
return 0;
}
static int sockets_stresstest_rand_mode(int allow_wait, int allow_rx)
{
u32_t random_value = LWIP_RAND();
#if LWIP_SOCKET_SELECT
if (random_value & TEST_MODE_SELECT) {
return TEST_MODE_SELECT;
}
#endif
#if LWIP_SOCKET_POLL
if (random_value & TEST_MODE_POLL) {
return TEST_MODE_POLL;
}
#endif
if (!allow_rx) {
return TEST_MODE_SLEEP;
}
#if LWIP_SO_RCVTIMEO
if (random_value & TEST_MODE_RECVTIMEO) {
return TEST_MODE_RECVTIMEO;
}
#endif
if (allow_wait) {
if (random_value & TEST_MODE_RECVTIMEO) {
return TEST_MODE_RECVTIMEO;
}
}
return TEST_MODE_NONBLOCKING;
}
static int
sockets_stresstest_wait_readable(int mode, int s, int timeout_ms)
{
switch(mode)
{
#if LWIP_SOCKET_SELECT
case TEST_MODE_SELECT:
return sockets_stresstest_wait_readable_select(s, timeout_ms);
#endif
#if LWIP_SOCKET_POLL
case TEST_MODE_POLL:
return sockets_stresstest_wait_readable_poll(s, timeout_ms);
#endif
#if LWIP_SO_RCVTIMEO
case TEST_MODE_RECVTIMEO:
return sockets_stresstest_wait_readable_recvtimeo(s, timeout_ms);
#endif
case TEST_MODE_WAIT:
return sockets_stresstest_wait_readable_wait_peek(s, timeout_ms);
case TEST_MODE_NONBLOCKING:
return sockets_stresstest_wait_readable_nonblock(s, timeout_ms);
case TEST_MODE_SLEEP:
{
sys_msleep(timeout_ms);
return 1;
}
default:
LWIP_ASSERT("invalid mode", 0);
break;
}
return 0;
}
#if LWIP_NETCONN_FULLDUPLEX
static void
sockets_stresstest_conn_client_r(void *arg)
{
struct sockets_stresstest_fullduplex *fd = (struct sockets_stresstest_fullduplex *)arg;
int s = fd->s;
size_t rxoff = 0;
char rxbuf[TEST_TXRX_BUFSIZE];
while (1) {
int closed;
if (fd->closed) {
break;
}
rxoff = recv_and_check_data_return_offset(s, rxbuf, sizeof(rxbuf), rxoff, &closed, "cli");
if (fd->closed) {
break;
}
if (closed) {
lwip_close(s);
break;
}
}
SYS_ARCH_DEC(sockets_stresstest_numthreads, 1);
LWIP_ASSERT("", sockets_stresstest_numthreads >= 0);
}
#endif
static void
sockets_stresstest_conn_client(void *arg)
{
struct sockaddr_storage addr;
struct sockaddr_in *addr_in;
int s, ret;
char txbuf[TEST_TXRX_BUFSIZE];
char rxbuf[TEST_TXRX_BUFSIZE];
size_t rxoff = 0;
u32_t max_time = sys_now() + (TEST_TIME_SECONDS * 1000);
int do_rx = 1;
struct sockets_stresstest_fullduplex *data = NULL;
memcpy(&addr, arg, sizeof(addr));
LWIP_ASSERT("", addr.ss_family == AF_INET);
addr_in = (struct sockaddr_in *)&addr;
addr_in->sin_addr.s_addr = inet_addr("127.0.0.1");
/* sleep a random time between 1 and 2 seconds */
sys_msleep(1000 + (LWIP_RAND() % 1000));
/* connect to the server */
s = lwip_socket(addr.ss_family, SOCK_STREAM, 0);
LWIP_ASSERT("s >= 0", s >= 0);
#if LWIP_NETCONN_FULLDUPLEX
if (LWIP_RAND() & 1) {
sys_thread_t t;
data = (struct sockets_stresstest_fullduplex*)mem_malloc(sizeof(struct sockets_stresstest_fullduplex));
LWIP_ASSERT("data != NULL", data != 0);
SYS_ARCH_INC(sockets_stresstest_numthreads, 1);
data->s = s;
data->closed = 0;
t = sys_thread_new("sockets_stresstest_conn_client_r", sockets_stresstest_conn_client_r, data, 0, 0);
LWIP_ASSERT("thread != NULL", t != 0);
do_rx = 0;
}
#endif
/* @todo: nonblocking connect? */
ret = lwip_connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_storage));
LWIP_ASSERT("ret == 0", ret == 0);
while (sys_now() < max_time) {
int closed;
int mode = sockets_stresstest_rand_mode(0, do_rx);
int timeout_ms = LWIP_RAND() % TEST_MAX_RXWAIT_MS;
ret = sockets_stresstest_wait_readable(mode, s, timeout_ms);
if (ret) {
if (do_rx) {
/* read some */
LWIP_ASSERT("readable", ret == TEST_SOCK_READABLE);
rxoff = recv_and_check_data_return_offset(s, rxbuf, sizeof(rxbuf), rxoff, &closed, "cli");
LWIP_ASSERT("client got closed", !closed);
}
} else {
/* timeout, send some */
size_t send_len = (LWIP_RAND() % (sizeof(txbuf) - 4)) + 4;
fill_test_data(txbuf, send_len);
LWIP_DEBUGF(TEST_SOCKETS_STRESS | LWIP_DBG_TRACE, ("cli %d tx %d\n", s, (int)send_len));
ret = lwip_write(s, txbuf, send_len);
if (ret == -1) {
/* TODO: for this to work, 'errno' has to support multithreading... */
int err = errno;
LWIP_ASSERT("err == 0", err == 0);
}
LWIP_ASSERT("ret == send_len", ret == (int)send_len);
}
}
if (data) {
data->closed = 1;
}
ret = lwip_close(s);
LWIP_ASSERT("ret == 0", ret == 0);
SYS_ARCH_DEC(sockets_stresstest_numthreads, 1);
LWIP_ASSERT("", sockets_stresstest_numthreads >= 0);
}
static void
sockets_stresstest_conn_server(void *arg)
{
int s, ret;
char txbuf[TEST_TXRX_BUFSIZE];
char rxbuf[TEST_TXRX_BUFSIZE];
size_t rxoff = 0;
s = (int)arg;
while (1) {
int closed;
int mode = sockets_stresstest_rand_mode(1, 1);
int timeout_ms = LWIP_RAND() % TEST_MAX_RXWAIT_MS;
ret = sockets_stresstest_wait_readable(mode, s, timeout_ms);
if (ret) {
if (ret & TEST_SOCK_ERR) {
/* closed? */
break;
}
/* read some */
LWIP_ASSERT("readable", ret == TEST_SOCK_READABLE);
rxoff = recv_and_check_data_return_offset(s, rxbuf, sizeof(rxbuf), rxoff, &closed, "srv");
if (closed) {
break;
}
} else {
/* timeout, send some */
size_t send_len = (LWIP_RAND() % (sizeof(txbuf) - 4)) + 4;
fill_test_data(txbuf, send_len);
LWIP_DEBUGF(TEST_SOCKETS_STRESS | LWIP_DBG_TRACE, ("srv %d tx %d\n", s, (int)send_len));
ret = lwip_write(s, txbuf, send_len);
if (ret == -1) {
/* TODO: for this to work, 'errno' has to support multithreading... */
int err = errno;
if (err == ECONNRESET) {
break;
}
if (err == ENOTCONN) {
break;
}
LWIP_ASSERT("unknown error", 0);
}
LWIP_ASSERT("ret == send_len", ret == (int)send_len);
}
}
ret = lwip_close(s);
LWIP_ASSERT("ret == 0", ret == 0);
SYS_ARCH_DEC(sockets_stresstest_numthreads, 1);
LWIP_ASSERT("", sockets_stresstest_numthreads >= 0);
}
static int
sockets_stresstest_start_clients(const struct sockaddr_storage *remote_addr)
{
/* limit the number of connections */
const int max_connections = LWIP_MIN(TEST_MAX_CONNECTIONS, MEMP_NUM_TCP_PCB/3);
int i;
for (i = 0; i < max_connections; i++) {
sys_thread_t t;
SYS_ARCH_INC(sockets_stresstest_numthreads, 1);
t = sys_thread_new("sockets_stresstest_conn_client", sockets_stresstest_conn_client, (void*)remote_addr, 0, 0);
LWIP_ASSERT("thread != NULL", t != 0);
}
return max_connections;
}
static void
sockets_stresstest_listener(void *arg)
{
int slisten;
int ret;
struct sockaddr_storage addr;
socklen_t addr_len;
struct test_settings *settings = (struct test_settings *)arg;
int num_clients, num_servers = 0;
slisten = lwip_socket(AF_INET, SOCK_STREAM, 0);
LWIP_ASSERT("slisten >= 0", slisten >= 0);
memcpy(&addr, &settings->addr, sizeof(struct sockaddr_storage));
ret = lwip_bind(slisten, (struct sockaddr *)&addr, sizeof(addr));
LWIP_ASSERT("ret == 0", ret == 0);
ret = lwip_listen(slisten, 0);
LWIP_ASSERT("ret == 0", ret == 0);
addr_len = sizeof(addr);
ret = lwip_getsockname(slisten, (struct sockaddr *)&addr, &addr_len);
LWIP_ASSERT("ret == 0", ret == 0);
num_clients = sockets_stresstest_start_clients(&addr);
while (num_servers < num_clients) {
struct sockaddr_storage aclient;
socklen_t aclient_len = sizeof(aclient);
int sclient = lwip_accept(slisten, (struct sockaddr *)&aclient, &aclient_len);
#if 1
/* using server threads */
{
sys_thread_t t;
SYS_ARCH_INC(sockets_stresstest_numthreads, 1);
num_servers++;
t = sys_thread_new("sockets_stresstest_conn_server", sockets_stresstest_conn_server, (void*)sclient, 0, 0);
LWIP_ASSERT("thread != NULL", t != 0);
}
#else
/* using server select */
#endif
}
LWIP_DEBUGF(TEST_SOCKETS_STRESS | LWIP_DBG_STATE, ("sockets_stresstest_listener: all %d connections established\n", num_clients));
/* accepted all clients */
while (sockets_stresstest_numthreads > 0) {
sys_msleep(1);
}
ret = lwip_close(slisten);
LWIP_ASSERT("ret == 0", ret == 0);
LWIP_DEBUGF(TEST_SOCKETS_STRESS |LWIP_DBG_STATE, ("sockets_stresstest_listener: done\n"));
}
static void
sockets_stresstest_listener_loop(void *arg)
{
int i;
struct test_settings *settings = (struct test_settings *)arg;
if (settings->loop_cnt) {
for (i = 0; i < settings->loop_cnt; i++) {
LWIP_DEBUGF(TEST_SOCKETS_STRESS |LWIP_DBG_STATE, ("sockets_stresstest_listener_loop: iteration %d\n", i));
sockets_stresstest_listener(arg);
sys_msleep(2);
}
LWIP_DEBUGF(TEST_SOCKETS_STRESS |LWIP_DBG_STATE, ("sockets_stresstest_listener_loop: done\n"));
} else {
for (i = 0; ; i++) {
LWIP_DEBUGF(TEST_SOCKETS_STRESS |LWIP_DBG_STATE, ("sockets_stresstest_listener_loop: iteration %d\n", i));
sockets_stresstest_listener(arg);
sys_msleep(2);
}
}
}
void
sockets_stresstest_init_loopback(int addr_family)
{
sys_thread_t t;
struct test_settings *settings = (struct test_settings *)mem_malloc(sizeof(struct test_settings));
LWIP_ASSERT("OOM", settings != NULL);
memset(settings, 0, sizeof(struct test_settings));
#if LWIP_IPV4 && LWIP_IPV6
LWIP_ASSERT("invalid addr_family", (addr_family == AF_INET) || (addr_family == AF_INET6));
#endif
settings->addr.ss_family = (sa_family_t)addr_family;
LWIP_UNUSED_ARG(addr_family);
settings->start_client = 1;
t = sys_thread_new("sockets_stresstest_listener_loop", sockets_stresstest_listener_loop, settings, 0, 0);
LWIP_ASSERT("thread != NULL", t != 0);
}
void
sockets_stresstest_init_server(int addr_family, u16_t server_port)
{
sys_thread_t t;
struct test_settings *settings = (struct test_settings *)mem_malloc(sizeof(struct test_settings));
LWIP_ASSERT("OOM", settings != NULL);
memset(settings, 0, sizeof(struct test_settings));
#if LWIP_IPV4 && LWIP_IPV6
LWIP_ASSERT("invalid addr_family", (addr_family == AF_INET) || (addr_family == AF_INET6));
settings->addr.ss_family = (sa_family_t)addr_family;
#endif
LWIP_UNUSED_ARG(addr_family);
((struct sockaddr_in *)(&settings->addr))->sin_port = server_port;
t = sys_thread_new("sockets_stresstest_listener", sockets_stresstest_listener, settings, 0, 0);
LWIP_ASSERT("thread != NULL", t != 0);
}
void
sockets_stresstest_init_client(const char *remote_ip, u16_t remote_port)
{
#if LWIP_IPV4
ip4_addr_t ip4;
#endif
#if LWIP_IPV6
ip6_addr_t ip6;
#endif
struct sockaddr_storage *addr = (struct sockaddr_storage *)mem_malloc(sizeof(struct sockaddr_storage));
LWIP_ASSERT("OOM", addr != NULL);
memset(addr, 0, sizeof(struct test_settings));
#if LWIP_IPV4
if (ip4addr_aton(remote_ip, &ip4)) {
addr->ss_family = AF_INET;
((struct sockaddr_in *)addr)->sin_addr.s_addr = ip4_addr_get_u32(&ip4);
}
#endif
#if LWIP_IPV4 && LWIP_IPV6
else
#endif
#if LWIP_IPV6
if (ip6addr_aton(remote_ip, &ip6)) {
addr->ss_family = AF_INET6;
/* todo: copy ipv6 address */
}
#endif
((struct sockaddr_in *)addr)->sin_port = remote_port;
sockets_stresstest_start_clients(addr);
}
#endif /* LWIP_SOCKET && LWIP_IPV4 */

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2017 Simon Goldschmidt
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Simon Goldschmidt <goldsimon@gmx.de>
*
*/
#ifndef LWIP_HDR_TEST_SOCKETS_STRESSTEST
#define LWIP_HDR_TEST_SOCKETS_STRESSTEST
void sockets_stresstest_init_loopback(int addr_family);
void sockets_stresstest_init_server(int addr_family, u16_t server_port);
void sockets_stresstest_init_client(const char *remote_ip, u16_t remote_port);
#endif /* LWIP_HDR_TEST_SOCKETS_STRESSTEST */

View File

@ -0,0 +1,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
)

View 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

View File

@ -0,0 +1,852 @@
#include "test_sockets.h"
#include "lwip/mem.h"
#include "lwip/opt.h"
#include "lwip/sockets.h"
#include "lwip/priv/sockets_priv.h"
#include "lwip/stats.h"
#include "lwip/tcpip.h"
#include "lwip/priv/tcp_priv.h"
#include "lwip/api.h"
static int
test_sockets_get_used_count(void)
{
int used = 0;
int i;
for (i = 0; i < NUM_SOCKETS; i++) {
struct lwip_sock* s = lwip_socket_dbg_get_socket(i);
if (s != NULL) {
if (s->fd_used) {
used++;
}
}
}
return used;
}
/* Setups/teardown functions */
static void
sockets_setup(void)
{
/* expect full free heap */
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
static void
sockets_teardown(void)
{
fail_unless(test_sockets_get_used_count() == 0);
/* poll until all memory is released... */
tcpip_thread_poll_one();
while (tcp_tw_pcbs) {
tcp_abort(tcp_tw_pcbs);
tcpip_thread_poll_one();
}
tcpip_thread_poll_one();
/* ensure full free heap */
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
#ifndef NUM_SOCKETS
#define NUM_SOCKETS MEMP_NUM_NETCONN
#endif
#if LWIP_SOCKET
static int
test_sockets_alloc_socket_nonblocking(int domain, int type)
{
int s = lwip_socket(domain, type, 0);
if (s >= 0) {
int ret = lwip_fcntl(s, F_SETFL, O_NONBLOCK);
fail_unless(ret == 0);
}
return s;
}
/* Verify basic sockets functionality
*/
START_TEST(test_sockets_basics)
{
int s, i, ret;
int s2[NUM_SOCKETS];
LWIP_UNUSED_ARG(_i);
s = lwip_socket(AF_INET, SOCK_STREAM, 0);
fail_unless(s >= 0);
lwip_close(s);
for (i = 0; i < NUM_SOCKETS; i++) {
s2[i] = lwip_socket(AF_INET, SOCK_STREAM, 0);
fail_unless(s2[i] >= 0);
}
/* all sockets used, now it should fail */
s = lwip_socket(AF_INET, SOCK_STREAM, 0);
fail_unless(s == -1);
/* close one socket */
ret = lwip_close(s2[0]);
fail_unless(ret == 0);
/* now it should succeed */
s2[0] = lwip_socket(AF_INET, SOCK_STREAM, 0);
fail_unless(s2[0] >= 0);
/* close all sockets */
for (i = 0; i < NUM_SOCKETS; i++) {
ret = lwip_close(s2[i]);
fail_unless(ret == 0);
}
}
END_TEST
static void test_sockets_allfunctions_basic_domain(int domain)
{
int s, s2, s3, ret;
struct sockaddr_storage addr, addr2;
socklen_t addrlen, addr2len;
char buf[4];
/* listen socket */
s = lwip_socket(domain, SOCK_STREAM, 0);
fail_unless(s >= 0);
ret = lwip_listen(s, 0);
fail_unless(ret == 0);
addrlen = sizeof(addr);
ret = lwip_getsockname(s, (struct sockaddr*)&addr, &addrlen);
fail_unless(ret == 0);
s2 = test_sockets_alloc_socket_nonblocking(domain, SOCK_STREAM);
fail_unless(s2 >= 0);
/* nonblocking connect s2 to s (but use loopback address) */
if (domain == AF_INET) {
#if LWIP_IPV4
struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr;
addr4->sin_addr.s_addr = PP_HTONL(INADDR_LOOPBACK);
#endif
} else {
#if LWIP_IPV6
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr;
struct in6_addr lo6 = IN6ADDR_LOOPBACK_INIT;
addr6->sin6_addr = lo6;
#endif
}
ret = lwip_connect(s2, (struct sockaddr*)&addr, addrlen);
fail_unless(ret == -1);
fail_unless(errno == EINPROGRESS);
ret = lwip_connect(s2, (struct sockaddr*)&addr, addrlen);
fail_unless(ret == -1);
fail_unless(errno == EALREADY);
while(tcpip_thread_poll_one());
s3 = lwip_accept(s, (struct sockaddr*)&addr2, &addr2len);
fail_unless(s3 >= 0);
ret = lwip_connect(s2, (struct sockaddr*)&addr, addrlen);
fail_unless(ret == -1);
fail_unless(errno == EISCONN);
/* write from server to client */
ret = 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 */

View File

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

View File

@ -0,0 +1,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 */

View 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 */

View File

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

View File

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

View File

@ -0,0 +1,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);
}

View File

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

View File

@ -0,0 +1,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);
}

View File

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

View File

@ -0,0 +1,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);
}

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,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(&etharphdr->sipaddr, adr, sizeof(ip4_addr_t));
SMEMCPY(&etharphdr->dipaddr, &test_ipaddr, sizeof(ip4_addr_t));
k = 6;
while(k > 0) {
k--;
/* Write the ARP MAC-Addresses */
etharphdr->shwaddr.addr[k] = test_ethaddr2.addr[k];
etharphdr->dhwaddr.addr[k] = test_ethaddr.addr[k];
/* Write the Ethernet MAC-Addresses */
ethhdr->dest.addr[k] = test_ethaddr.addr[k];
ethhdr->src.addr[k] = test_ethaddr2.addr[k];
}
ethernet_input(p, &test_netif);
}
/* Setups/teardown functions */
static void
etharp_setup(void)
{
etharp_remove_all();
default_netif_add();
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
static void
etharp_teardown(void)
{
etharp_remove_all();
default_netif_remove();
lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
/* Test functions */
START_TEST(test_etharp_table)
{
#if ETHARP_SUPPORT_STATIC_ENTRIES
err_t err;
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
ssize_t idx;
const ip4_addr_t *unused_ipaddr;
struct eth_addr *unused_ethaddr;
struct udp_pcb* pcb;
LWIP_UNUSED_ARG(_i);
if (netif_default != &test_netif) {
fail("This test needs a default netif");
}
linkoutput_ctr = 0;
pcb = udp_new();
fail_unless(pcb != NULL);
if (pcb != NULL) {
ip4_addr_t adrs[ARP_TABLE_SIZE + 2];
int i;
for(i = 0; i < ARP_TABLE_SIZE + 2; i++) {
IP4_ADDR(&adrs[i], 192,168,0,i+2);
}
/* fill ARP-table with dynamic entries */
for(i = 0; i < ARP_TABLE_SIZE; i++) {
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 10, PBUF_RAM);
fail_unless(p != NULL);
if (p != NULL) {
err_t err2;
ip_addr_t dst;
ip_addr_copy_from_ip4(dst, adrs[i]);
err2 = udp_sendto(pcb, p, &dst, 123);
fail_unless(err2 == ERR_OK);
/* etharp request sent? */
fail_unless(linkoutput_ctr == (2*i) + 1);
pbuf_free(p);
/* create an ARP response */
create_arp_response(&adrs[i]);
/* queued UDP packet sent? */
fail_unless(linkoutput_ctr == (2*i) + 2);
idx = etharp_find_addr(NULL, &adrs[i], &unused_ethaddr, &unused_ipaddr);
fail_unless(idx == i);
etharp_tmr();
}
}
linkoutput_ctr = 0;
#if ETHARP_SUPPORT_STATIC_ENTRIES
/* create one static entry */
err = etharp_add_static_entry(&adrs[ARP_TABLE_SIZE], &test_ethaddr3);
fail_unless(err == ERR_OK);
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr);
fail_unless(idx == 0);
fail_unless(linkoutput_ctr == 0);
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
linkoutput_ctr = 0;
/* fill ARP-table with dynamic entries */
for(i = 0; i < ARP_TABLE_SIZE; i++) {
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 10, PBUF_RAM);
fail_unless(p != NULL);
if (p != NULL) {
err_t err2;
ip_addr_t dst;
ip_addr_copy_from_ip4(dst, adrs[i]);
err2 = udp_sendto(pcb, p, &dst, 123);
fail_unless(err2 == ERR_OK);
/* etharp request sent? */
fail_unless(linkoutput_ctr == (2*i) + 1);
pbuf_free(p);
/* create an ARP response */
create_arp_response(&adrs[i]);
/* queued UDP packet sent? */
fail_unless(linkoutput_ctr == (2*i) + 2);
idx = etharp_find_addr(NULL, &adrs[i], &unused_ethaddr, &unused_ipaddr);
if (i < ARP_TABLE_SIZE - 1) {
fail_unless(idx == i+1);
} else {
/* the last entry must not overwrite the static entry! */
fail_unless(idx == 1);
}
etharp_tmr();
}
}
#if ETHARP_SUPPORT_STATIC_ENTRIES
/* create a second static entry */
err = etharp_add_static_entry(&adrs[ARP_TABLE_SIZE+1], &test_ethaddr4);
fail_unless(err == ERR_OK);
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr);
fail_unless(idx == 0);
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE+1], &unused_ethaddr, &unused_ipaddr);
fail_unless(idx == 2);
/* and remove it again */
err = etharp_remove_static_entry(&adrs[ARP_TABLE_SIZE+1]);
fail_unless(err == ERR_OK);
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr);
fail_unless(idx == 0);
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE+1], &unused_ethaddr, &unused_ipaddr);
fail_unless(idx == -1);
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
/* check that static entries don't time out */
etharp_remove_all();
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr);
fail_unless(idx == 0);
#if ETHARP_SUPPORT_STATIC_ENTRIES
/* remove the first static entry */
err = etharp_remove_static_entry(&adrs[ARP_TABLE_SIZE]);
fail_unless(err == ERR_OK);
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr);
fail_unless(idx == -1);
idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE+1], &unused_ethaddr, &unused_ipaddr);
fail_unless(idx == -1);
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
udp_remove(pcb);
}
}
END_TEST
/** Create the suite including all tests for this module */
Suite *
etharp_suite(void)
{
testfunc tests[] = {
TESTFUNC(test_etharp_table)
};
return create_suite("ETHARP", tests, sizeof(tests)/sizeof(testfunc), etharp_setup, etharp_teardown);
}

View File

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

View File

@ -0,0 +1,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);
}

View File

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

View File

@ -0,0 +1,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 */

View File

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

View File

@ -0,0 +1,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 */

View 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
View 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 */

View 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);
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,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;
}

View File

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

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,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);
}

View File

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