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