diff --git a/src/core/udp.c b/src/core/udp.c index 1a91238d..ac0e4d6e 100644 --- a/src/core/udp.c +++ b/src/core/udp.c @@ -265,6 +265,17 @@ udp_input(struct pbuf *p, struct netif *inp) if (uncon_pcb == NULL) { /* the first unconnected matching PCB */ uncon_pcb = pcb; +#if LWIP_IPV4 + } else if (broadcast && ip4_current_dest_addr()->addr == IPADDR_BROADCAST) { + /* global broadcast address (only valid for IPv4; match was checked before) */ + if (!IP_IS_V4_VAL(uncon_pcb->local_ip) || !ip4_addr_cmp(ip_2_ip4(&uncon_pcb->local_ip), netif_ip4_addr(inp))) { + /* uncon_pcb does not match the input netif, check this pcb */ + if (IP_IS_V4_VAL(pcb->local_ip) && ip4_addr_cmp(ip_2_ip4(&pcb->local_ip), netif_ip4_addr(inp))) { + /* better match */ + uncon_pcb = pcb; + } + } +#endif /* LWIP_IPV4 */ } #if SO_REUSE else if (!ip_addr_isany(&pcb->local_ip)) { diff --git a/test/unit/udp/test_udp.c b/test/unit/udp/test_udp.c index 4ad693e9..83ce2e31 100644 --- a/test/unit/udp/test_udp.c +++ b/test/unit/udp/test_udp.c @@ -2,11 +2,23 @@ #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) @@ -22,12 +34,90 @@ udp_remove_all(void) 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)); } @@ -35,6 +125,7 @@ static void udp_teardown(void) { udp_remove_all(); + default_netif_remove(); lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT)); } @@ -58,6 +149,191 @@ START_TEST(test_udp_new_remove) } END_TEST +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 * @@ -65,6 +341,7 @@ 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); }