/** 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; }
/** * Process an incoming UDP datagram. * * Given an incoming UDP datagram (as a chain of pbufs) this function * finds a corresponding UDP PCB and hands over the pbuf to the pcbs * recv function. If no pcb is found or the datagram is incorrect, the * pbuf is freed. * * @param p pbuf to be demultiplexed to a UDP PCB (p->payload pointing to the UDP header) * @param inp network interface on which the datagram was received. * */ void udp_input(struct pbuf *p, struct netif *inp) { struct udp_hdr *udphdr; struct udp_pcb *pcb, *prev; struct udp_pcb *uncon_pcb; u16_t src, dest; u8_t broadcast; u8_t for_us = 0; LWIP_UNUSED_ARG(inp); PERF_START; UDP_STATS_INC(udp.recv); /* Check minimum length (UDP header) */ if (p->len < UDP_HLEN) { /* drop short packets */ LWIP_DEBUGF(UDP_DEBUG, ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len)); UDP_STATS_INC(udp.lenerr); UDP_STATS_INC(udp.drop); MIB2_STATS_INC(mib2.udpinerrors); pbuf_free(p); goto end; } udphdr = (struct udp_hdr *)p->payload; /* is broadcast packet ? */ broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif()); LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len)); /* convert src and dest ports to host byte order */ src = lwip_ntohs(udphdr->src); dest = lwip_ntohs(udphdr->dest); udp_debug_print(udphdr); /* print the UDP source and destination */ LWIP_DEBUGF(UDP_DEBUG, ("udp (")); ip_addr_debug_print(UDP_DEBUG, ip_current_dest_addr()); LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", lwip_ntohs(udphdr->dest))); ip_addr_debug_print(UDP_DEBUG, ip_current_src_addr()); LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", lwip_ntohs(udphdr->src))); pcb = NULL; prev = NULL; uncon_pcb = NULL; /* Iterate through the UDP pcb list for a matching pcb. * 'Perfect match' pcbs (connected to the remote port & ip address) are * preferred. If no perfect match is found, the first unconnected pcb that * matches the local port and ip address gets the datagram. */ for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { /* print the PCB local and remote address */ LWIP_DEBUGF(UDP_DEBUG, ("pcb (")); ip_addr_debug_print(UDP_DEBUG, &pcb->local_ip); LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", pcb->local_port)); ip_addr_debug_print(UDP_DEBUG, &pcb->remote_ip); LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", pcb->remote_port)); /* compare PCB local addr+port to UDP destination addr+port */ if ((pcb->local_port == dest) && (udp_input_local_match(pcb, inp, broadcast) != 0)) { if (((pcb->flags & UDP_FLAGS_CONNECTED) == 0) && ((uncon_pcb == NULL) #if SO_REUSE /* prefer specific IPs over cath-all */ || !ip_addr_isany(&pcb->local_ip) #endif /* SO_REUSE */ )) { /* the first unconnected matching PCB */ uncon_pcb = pcb; } /* compare PCB remote addr+port to UDP source addr+port */ if ((pcb->remote_port == src) && (ip_addr_isany_val(pcb->remote_ip) || ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()))) { /* the first fully matching PCB */ if (prev != NULL) { /* move the pcb to the front of udp_pcbs so that is found faster next time */ prev->next = pcb->next; pcb->next = udp_pcbs; udp_pcbs = pcb; } else { UDP_STATS_INC(udp.cachehit); } break; } } prev = pcb; } /* no fully matching pcb found? then look for an unconnected pcb */ if (pcb == NULL) { pcb = uncon_pcb; } /* Check checksum if this is a match or if it was directed at us. */ if (pcb != NULL) { for_us = 1; } else { #if LWIP_IPV6 if (ip_current_is_v6()) { for_us = netif_get_ip6_addr_match(inp, ip6_current_dest_addr()) >= 0; } #endif /* LWIP_IPV6 */ #if LWIP_IPV4 if (!ip_current_is_v6()) { for_us = ip4_addr_cmp(netif_ip4_addr(inp), ip4_current_dest_addr()); } #endif /* LWIP_IPV4 */ } if (for_us) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n")); #if CHECKSUM_CHECK_UDP IF__NETIF_CHECKSUM_ENABLED(inp, CHECKSUM_CHECK_UDP) { #if LWIP_UDPLITE if (ip_current_header_proto() == IP_PROTO_UDPLITE) { /* Do the UDP Lite checksum */ u16_t chklen = lwip_ntohs(udphdr->len); if (chklen < sizeof(struct udp_hdr)) { if (chklen == 0) { /* For UDP-Lite, checksum length of 0 means checksum over the complete packet (See RFC 3828 chap. 3.1) */ chklen = p->tot_len; } else { /* At least the UDP-Lite header must be covered by the checksum! (Again, see RFC 3828 chap. 3.1) */ goto chkerr; } } if (ip_chksum_pseudo_partial(p, IP_PROTO_UDPLITE, p->tot_len, chklen, ip_current_src_addr(), ip_current_dest_addr()) != 0) { goto chkerr; } } else #endif /* LWIP_UDPLITE */ { if (udphdr->chksum != 0) { if (ip_chksum_pseudo(p, IP_PROTO_UDP, p->tot_len, ip_current_src_addr(), ip_current_dest_addr()) != 0) { goto chkerr; } } } } #endif /* CHECKSUM_CHECK_UDP */ if (pbuf_header(p, -UDP_HLEN)) { /* Can we cope with this failing? Just assert for now */ LWIP_ASSERT("pbuf_header failed\n", 0); UDP_STATS_INC(udp.drop); MIB2_STATS_INC(mib2.udpinerrors); pbuf_free(p); goto end; } if (pcb != NULL) { MIB2_STATS_INC(mib2.udpindatagrams); #if SO_REUSE && SO_REUSE_RXTOALL if (ip_get_option(pcb, SOF_REUSEADDR) && (broadcast || ip_addr_ismulticast(ip_current_dest_addr()))) { /* pass broadcast- or multicast packets to all multicast pcbs if SOF_REUSEADDR is set on the first match */ struct udp_pcb *mpcb; u8_t p_header_changed = 0; s16_t hdrs_len = (s16_t)(ip_current_header_tot_len() + UDP_HLEN); for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) { if (mpcb != pcb) { /* compare PCB local addr+port to UDP destination addr+port */ if ((mpcb->local_port == dest) && (udp_input_local_match(mpcb, inp, broadcast) != 0)) { /* pass a copy of the packet to all local matches */ if (mpcb->recv != NULL) { struct pbuf *q; /* for that, move payload to IP header again */ if (p_header_changed == 0) { pbuf_header_force(p, hdrs_len); p_header_changed = 1; } q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); if (q != NULL) { err_t err = pbuf_copy(q, p); if (err == ERR_OK) { /* move payload to UDP data */ pbuf_header(q, -hdrs_len); mpcb->recv(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src); } } } } } } if (p_header_changed) { /* and move payload to UDP data again */ pbuf_header(p, -hdrs_len); } } #endif /* SO_REUSE && SO_REUSE_RXTOALL */ /* callback */ if (pcb->recv != NULL) { /* now the recv function is responsible for freeing p */ pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src); } else { /* no recv function registered? then we have to free the pbuf! */ pbuf_free(p); goto end; } } else { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n")); #if LWIP_ICMP || LWIP_ICMP6 /* No match was found, send ICMP destination port unreachable unless destination address was broadcast/multicast. */ if (!broadcast && !ip_addr_ismulticast(ip_current_dest_addr())) { /* move payload pointer back to ip header */ pbuf_header_force(p, ip_current_header_tot_len() + UDP_HLEN); icmp_port_unreach(ip_current_is_v6(), p); } #endif /* LWIP_ICMP || LWIP_ICMP6 */ UDP_STATS_INC(udp.proterr); UDP_STATS_INC(udp.drop); MIB2_STATS_INC(mib2.udpnoports); pbuf_free(p); } } else {
END_TEST /** Check that we handle malformed tcp headers, and discard the pbuf(s) */ START_TEST(test_tcp_malformed_header) { struct test_tcp_counters counters; struct tcp_pcb* pcb; struct pbuf* p; char data[] = {1, 2, 3, 4}; ip_addr_t remote_ip, local_ip, netmask; u16_t data_len, chksum; u16_t remote_port = 0x100, local_port = 0x101; struct netif netif; struct test_tcp_txcounters txcounters; struct tcp_hdr *hdr; LWIP_UNUSED_ARG(_i); /* initialize local vars */ memset(&netif, 0, sizeof(netif)); IP_ADDR4(&local_ip, 192, 168, 1, 1); IP_ADDR4(&remote_ip, 192, 168, 1, 2); IP_ADDR4(&netmask, 255, 255, 255, 0); test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask); data_len = sizeof(data); /* initialize counter struct */ memset(&counters, 0, sizeof(counters)); counters.expected_data_len = data_len; counters.expected_data = data; /* create and initialize the pcb */ pcb = test_tcp_new_counters_pcb(&counters); EXPECT_RET(pcb != NULL); tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); /* create a segment */ p = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0); pbuf_header(p, -(s16_t)sizeof(struct ip_hdr)); hdr = (struct tcp_hdr *)p->payload; TCPH_HDRLEN_FLAGS_SET(hdr, 15, 0x3d1); hdr->chksum = 0; chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len, &remote_ip, &local_ip); hdr->chksum = chksum; pbuf_header(p, sizeof(struct ip_hdr)); EXPECT(p != NULL); EXPECT(p->next == NULL); if (p != NULL) { /* pass the segment to tcp_input */ test_tcp_input(p, &netif); /* check if counters are as expected */ EXPECT(counters.close_calls == 0); EXPECT(counters.recv_calls == 0); EXPECT(counters.recved_bytes == 0); EXPECT(counters.err_calls == 0); } /* make sure the pcb is freed */ EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); tcp_abort(pcb); EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); }