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