diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 1c152d0..fba6473 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -474,6 +474,37 @@ static void fix_udp_checksums(struct wolfIP_udp_datagram *udp) fix_udp_checksum(udp, udp_len); } +static void fix_ip_checksum_with_hlen(struct wolfIP_ip_packet *ip, uint16_t ip_hlen) +{ + uint32_t sum = 0; + uint32_t i; + const uint8_t *ptr = (const uint8_t *)(&ip->ver_ihl); + + ip->csum = 0; + for (i = 0; i < ip_hlen; i += 2) { + uint16_t word; + memcpy(&word, ptr + i, sizeof(word)); + sum += ee16(word); + } + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + ip->csum = ee16((uint16_t)~sum); +} + +static void fix_udp_checksum_raw(struct wolfIP_ip_packet *ip, void *udp_hdr, uint16_t udp_len) +{ + union transport_pseudo_header ph; + uint16_t *udp_csum = (uint16_t *)((uint8_t *)udp_hdr + 6); + + memset(&ph, 0, sizeof(ph)); + ph.ph.src = ip->src; + ph.ph.dst = ip->dst; + ph.ph.proto = WI_IPPROTO_UDP; + ph.ph.len = ee16(udp_len); + *udp_csum = 0; + *udp_csum = ee16(transport_checksum(&ph, udp_hdr)); +} + static void inject_udp_datagram(struct wolfIP *s, unsigned int if_idx, ip4 src_ip, ip4 dst_ip, uint16_t src_port, uint16_t dst_port, const uint8_t *payload, uint16_t payload_len) { @@ -5743,6 +5774,7 @@ START_TEST(test_icmp_input_echo_reply_queues) icmp.ip.len = ee16(IP_HEADER_LEN + ICMP_HEADER_LEN); icmp.type = ICMP_ECHO_REPLY; icmp_set_echo_id(&icmp, ts->src_port); + icmp.csum = ee16(icmp_checksum(&icmp, ICMP_HEADER_LEN)); frame_len = (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + ICMP_HEADER_LEN); icmp_input(&s, TEST_PRIMARY_IF, (struct wolfIP_ip_packet *)&icmp, frame_len); @@ -5769,6 +5801,7 @@ START_TEST(test_icmp_input_echo_request_reply_sent) icmp.ip.ttl = 64; icmp.ip.len = ee16(IP_HEADER_LEN + ICMP_HEADER_LEN); icmp.type = ICMP_ECHO_REQUEST; + icmp.csum = ee16(icmp_checksum(&icmp, ICMP_HEADER_LEN)); frame_len = (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + ICMP_HEADER_LEN); icmp_input(&s, TEST_PRIMARY_IF, (struct wolfIP_ip_packet *)&icmp, frame_len); @@ -5777,6 +5810,34 @@ START_TEST(test_icmp_input_echo_request_reply_sent) } END_TEST +START_TEST(test_icmp_input_echo_request_bad_checksum_dropped) +{ + struct wolfIP s; + struct wolfIP_icmp_packet icmp; + uint32_t frame_len; + + wolfIP_init(&s); + mock_link_init(&s); + s.dhcp_state = DHCP_OFF; + wolfIP_filter_set_callback(NULL, NULL); + last_frame_sent_size = 0; + + memset(&icmp, 0, sizeof(icmp)); + icmp.ip.src = ee32(0x0A000002U); + icmp.ip.dst = ee32(0x0A000001U); + icmp.ip.ttl = 64; + icmp.ip.len = ee16(IP_HEADER_LEN + ICMP_HEADER_LEN); + icmp.type = ICMP_ECHO_REQUEST; + icmp.csum = ee16(icmp_checksum(&icmp, ICMP_HEADER_LEN)); + icmp.csum ^= ee16(0x0001); + frame_len = (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + ICMP_HEADER_LEN); + + icmp_input(&s, TEST_PRIMARY_IF, (struct wolfIP_ip_packet *)&icmp, frame_len); + + ck_assert_uint_eq(last_frame_sent_size, 0U); +} +END_TEST + START_TEST(test_icmp_input_echo_request_odd_len_reply_checksum) { struct wolfIP s; @@ -5811,6 +5872,7 @@ START_TEST(test_icmp_input_echo_request_odd_len_reply_checksum) icmp->code = 0; icmp->csum = 0; ((uint8_t *)&icmp->type)[ICMP_HEADER_LEN] = 0xAB; + icmp->csum = ee16(icmp_checksum(icmp, icmp_len)); frame_len = (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + icmp_len); icmp_input(&s, TEST_PRIMARY_IF, ip, frame_len); @@ -5852,6 +5914,7 @@ START_TEST(test_icmp_input_echo_request_dhcp_running_no_reply) icmp.ip.dst = ee32(0x0A000001U); icmp.ip.len = ee16(IP_HEADER_LEN + ICMP_HEADER_LEN); icmp.type = ICMP_ECHO_REQUEST; + icmp.csum = ee16(icmp_checksum(&icmp, ICMP_HEADER_LEN)); frame_len = (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + ICMP_HEADER_LEN); icmp_input(&s, TEST_PRIMARY_IF, (struct wolfIP_ip_packet *)&icmp, frame_len); @@ -5878,6 +5941,7 @@ START_TEST(test_icmp_input_echo_request_filter_drop) icmp.ip.dst = ee32(0x0A000001U); icmp.ip.len = ee16(IP_HEADER_LEN + ICMP_HEADER_LEN); icmp.type = ICMP_ECHO_REQUEST; + icmp.csum = ee16(icmp_checksum(&icmp, ICMP_HEADER_LEN)); frame_len = (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + ICMP_HEADER_LEN); icmp_input(&s, TEST_PRIMARY_IF, (struct wolfIP_ip_packet *)&icmp, frame_len); @@ -5907,6 +5971,7 @@ START_TEST(test_icmp_input_echo_request_ip_filter_drop) icmp.ip.dst = ee32(0x0A000001U); icmp.ip.len = ee16(IP_HEADER_LEN + ICMP_HEADER_LEN); icmp.type = ICMP_ECHO_REQUEST; + icmp.csum = ee16(icmp_checksum(&icmp, ICMP_HEADER_LEN)); frame_len = (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + ICMP_HEADER_LEN); icmp_input(&s, TEST_PRIMARY_IF, (struct wolfIP_ip_packet *)&icmp, frame_len); @@ -5936,6 +6001,7 @@ START_TEST(test_icmp_input_echo_request_eth_filter_drop) icmp.ip.dst = ee32(0x0A000001U); icmp.ip.len = ee16(IP_HEADER_LEN + ICMP_HEADER_LEN); icmp.type = ICMP_ECHO_REQUEST; + icmp.csum = ee16(icmp_checksum(&icmp, ICMP_HEADER_LEN)); frame_len = (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + ICMP_HEADER_LEN); icmp_input(&s, TEST_PRIMARY_IF, (struct wolfIP_ip_packet *)&icmp, frame_len); @@ -5964,6 +6030,7 @@ START_TEST(test_icmp_input_filter_drop_receiving) icmp.ip.dst = ee32(0x0A000001U); icmp.ip.len = ee16(IP_HEADER_LEN + ICMP_HEADER_LEN); icmp.type = ICMP_ECHO_REQUEST; + icmp.csum = ee16(icmp_checksum(&icmp, ICMP_HEADER_LEN)); frame_len = (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + ICMP_HEADER_LEN); icmp_input(&s, TEST_PRIMARY_IF, (struct wolfIP_ip_packet *)&icmp, frame_len); @@ -7712,6 +7779,122 @@ START_TEST(test_udp_try_recv_short_expected_len) } END_TEST +START_TEST(test_udp_try_recv_len_below_header_rejected) +{ + struct wolfIP s; + struct tsocket *ts; + struct wolfIP_udp_datagram udp; + uint32_t local_ip = 0x0A000001U; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0); + + ts = udp_new_socket(&s); + ck_assert_ptr_nonnull(ts); + ts->src_port = 1234; + ts->local_ip = local_ip; + + memset(&udp, 0, sizeof(udp)); + udp.ip.dst = ee32(local_ip); + udp.ip.len = ee16(IP_HEADER_LEN + UDP_HEADER_LEN); + udp.dst_port = ee16(1234); + udp.len = ee16(UDP_HEADER_LEN - 1); + + udp_try_recv(&s, TEST_PRIMARY_IF, &udp, + (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + UDP_HEADER_LEN)); + + ck_assert_ptr_eq(fifo_peek(&ts->sock.udp.rxbuf), NULL); + ck_assert_uint_eq(last_frame_sent_size, 0U); +} +END_TEST + +START_TEST(test_udp_try_recv_unmatched_port_sends_icmp_unreachable) +{ + struct wolfIP s; + uint8_t udp_buf[sizeof(struct wolfIP_udp_datagram) + 4]; + struct wolfIP_udp_datagram *udp = (struct wolfIP_udp_datagram *)udp_buf; + struct wolfIP_icmp_dest_unreachable_packet *icmp; + uint32_t local_ip = 0x0A000001U; + uint32_t remote_ip = 0x0A000002U; + uint8_t src_mac[6] = {0x20, 0x21, 0x22, 0x23, 0x24, 0x25}; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0); + + memset(udp_buf, 0, sizeof(udp_buf)); + memcpy(udp->ip.eth.src, src_mac, sizeof(src_mac)); + memcpy(udp->ip.eth.dst, s.ll_dev[TEST_PRIMARY_IF].mac, 6); + udp->ip.eth.type = ee16(ETH_TYPE_IP); + udp->ip.ver_ihl = 0x45; + udp->ip.ttl = 64; + udp->ip.proto = WI_IPPROTO_UDP; + udp->ip.len = ee16(IP_HEADER_LEN + UDP_HEADER_LEN + 4); + udp->ip.src = ee32(remote_ip); + udp->ip.dst = ee32(local_ip); + udp->src_port = ee16(4321); + udp->dst_port = ee16(1234); + udp->len = ee16(UDP_HEADER_LEN + 4); + memcpy(udp->data, "test", 4); + fix_udp_checksums(udp); + + memset(last_frame_sent, 0, sizeof(last_frame_sent)); + last_frame_sent_size = 0; + + udp_try_recv(&s, TEST_PRIMARY_IF, udp, + (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + UDP_HEADER_LEN + 4)); + + ck_assert_uint_eq(last_frame_sent_size, + sizeof(struct wolfIP_icmp_dest_unreachable_packet)); + icmp = (struct wolfIP_icmp_dest_unreachable_packet *)last_frame_sent; + ck_assert_uint_eq(icmp->type, 3U); + ck_assert_uint_eq(icmp->code, 3U); + ck_assert_mem_eq(icmp->unused, "\x00\x00\x00\x00", sizeof(icmp->unused)); + ck_assert_mem_eq(icmp->ip.eth.dst, src_mac, 6); + ck_assert_mem_eq(icmp->ip.eth.src, s.ll_dev[TEST_PRIMARY_IF].mac, 6); + ck_assert_uint_eq(ee32(icmp->ip.src), local_ip); + ck_assert_uint_eq(ee32(icmp->ip.dst), remote_ip); + ck_assert_mem_eq(icmp->orig_packet, ((uint8_t *)udp) + ETH_HEADER_LEN, + TTL_EXCEEDED_ORIG_PACKET_SIZE); +} +END_TEST + +START_TEST(test_udp_try_recv_unmatched_nonlocal_dst_does_not_send_icmp) +{ + struct wolfIP s; + uint8_t udp_buf[sizeof(struct wolfIP_udp_datagram) + 4]; + struct wolfIP_udp_datagram *udp = (struct wolfIP_udp_datagram *)udp_buf; + uint32_t local_ip = 0x0A000001U; + uint32_t remote_ip = 0x0A000002U; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0); + + memset(udp_buf, 0, sizeof(udp_buf)); + udp->ip.ver_ihl = 0x45; + udp->ip.ttl = 64; + udp->ip.proto = WI_IPPROTO_UDP; + udp->ip.len = ee16(IP_HEADER_LEN + UDP_HEADER_LEN + 4); + udp->ip.src = ee32(remote_ip); + udp->ip.dst = ee32(0x0A0000FEU); + udp->src_port = ee16(4321); + udp->dst_port = ee16(1234); + udp->len = ee16(UDP_HEADER_LEN + 4); + memcpy(udp->data, "test", 4); + fix_udp_checksums(udp); + + memset(last_frame_sent, 0, sizeof(last_frame_sent)); + last_frame_sent_size = 0; + + udp_try_recv(&s, TEST_PRIMARY_IF, udp, + (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + UDP_HEADER_LEN + 4)); + + ck_assert_uint_eq(last_frame_sent_size, 0U); +} +END_TEST + START_TEST(test_dns_callback_bad_flags) { struct wolfIP s; @@ -7923,11 +8106,14 @@ START_TEST(test_dns_abort_query_null_noop) } END_TEST -START_TEST(test_tcp_input_ttl_zero_sends_icmp) +START_TEST(test_tcp_input_ttl_zero_local_ack_still_processes) { struct wolfIP s; struct tsocket *ts; - struct wolfIP_tcp_seg seg; + struct tcp_seg_buf segbuf; + struct wolfIP_tcp_seg *queued; + struct wolfIP_tcp_seg ackseg; + struct pkt_desc *desc; wolfIP_init(&s); mock_link_init(&s); @@ -7943,20 +8129,43 @@ START_TEST(test_tcp_input_ttl_zero_sends_icmp) ts->dst_port = 5678; ts->local_ip = 0x0A000001U; ts->remote_ip = 0x0A000002U; + ts->sock.tcp.snd_una = 100; + ts->sock.tcp.seq = 101; + ts->sock.tcp.bytes_in_flight = 1; + fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); - memset(&seg, 0, sizeof(seg)); - seg.ip.ver_ihl = 0x45; - seg.ip.ttl = 0; - seg.ip.proto = WI_IPPROTO_TCP; - seg.ip.len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN); - seg.ip.src = ee32(ts->remote_ip); - seg.ip.dst = ee32(ts->local_ip); - seg.dst_port = ee16(ts->src_port); - seg.src_port = ee16(ts->dst_port); - seg.hlen = TCP_HEADER_LEN << 2; - fix_tcp_checksums(&seg); - tcp_input(&s, TEST_PRIMARY_IF, &seg, (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN)); - ck_assert_uint_gt(last_frame_sent_size, 0); + memset(&segbuf, 0, sizeof(segbuf)); + queued = &segbuf.seg; + queued->ip.len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN + 1); + queued->hlen = TCP_HEADER_LEN << 2; + queued->seq = ee32(100); + ck_assert_int_eq(fifo_push(&ts->sock.tcp.txbuf, &segbuf, sizeof(segbuf)), 0); + desc = fifo_peek(&ts->sock.tcp.txbuf); + ck_assert_ptr_nonnull(desc); + desc->flags |= PKT_FLAG_SENT; + + memset(&ackseg, 0, sizeof(ackseg)); + ackseg.ip.ver_ihl = 0x45; + ackseg.ip.ttl = 0; + ackseg.ip.proto = WI_IPPROTO_TCP; + ackseg.ip.len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN); + ackseg.ip.src = ee32(ts->remote_ip); + ackseg.ip.dst = ee32(ts->local_ip); + ackseg.src_port = ee16(ts->dst_port); + ackseg.dst_port = ee16(ts->src_port); + ackseg.hlen = TCP_HEADER_LEN << 2; + ackseg.flags = TCP_FLAG_ACK; + ackseg.ack = ee32(101); + ackseg.win = ee16(32); + fix_tcp_checksums(&ackseg); + + tcp_input(&s, TEST_PRIMARY_IF, &ackseg, + (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN)); + + ck_assert_uint_eq(last_frame_sent_size, 0U); + ck_assert_uint_eq(ts->sock.tcp.snd_una, 101U); + ck_assert_uint_eq(ts->sock.tcp.bytes_in_flight, 0U); + ck_assert_ptr_eq(fifo_peek(&ts->sock.tcp.txbuf), NULL); } END_TEST @@ -8615,6 +8824,69 @@ START_TEST(test_dhcp_parse_ack_missing_end_rejected) } END_TEST +START_TEST(test_dhcp_parse_offer_rejects_mismatched_xid) +{ + struct wolfIP s; + struct dhcp_msg msg; + struct dhcp_option *opt; + struct ipconf *primary; + + wolfIP_init(&s); + primary = wolfIP_primary_ipconf(&s); + ck_assert_ptr_nonnull(primary); + s.dhcp_xid = 0x12345678U; + + memset(&msg, 0, sizeof(msg)); + msg.magic = ee32(DHCP_MAGIC); + msg.xid = ee32(0x87654321U); + msg.yiaddr = ee32(0x0A000064U); + opt = (struct dhcp_option *)msg.options; + opt->code = DHCP_OPTION_MSG_TYPE; + opt->len = 1; + opt->data[0] = DHCP_OFFER; + opt = (struct dhcp_option *)((uint8_t *)opt + 3); + opt->code = DHCP_OPTION_END; + opt->len = 0; + + ck_assert_int_eq(dhcp_parse_offer(&s, &msg, sizeof(msg)), -1); + ck_assert_uint_eq(s.dhcp_ip, 0U); + ck_assert_uint_eq(primary->ip, 0U); + ck_assert_int_eq(s.dhcp_state, DHCP_OFF); +} +END_TEST + +START_TEST(test_dhcp_parse_ack_rejects_mismatched_xid) +{ + struct wolfIP s; + struct dhcp_msg msg; + struct dhcp_option *opt; + struct ipconf *primary; + + wolfIP_init(&s); + primary = wolfIP_primary_ipconf(&s); + ck_assert_ptr_nonnull(primary); + s.dhcp_xid = 0x12345678U; + s.dhcp_ip = 0x0A000064U; + s.dhcp_server_ip = 0x0A000001U; + s.dhcp_state = DHCP_REQUEST_SENT; + + memset(&msg, 0, sizeof(msg)); + msg.magic = ee32(DHCP_MAGIC); + msg.xid = ee32(0x87654321U); + opt = (struct dhcp_option *)msg.options; + opt->code = DHCP_OPTION_MSG_TYPE; + opt->len = 1; + opt->data[0] = DHCP_ACK; + opt = (struct dhcp_option *)((uint8_t *)opt + 3); + opt->code = DHCP_OPTION_END; + opt->len = 0; + + ck_assert_int_eq(dhcp_parse_ack(&s, &msg, sizeof(msg)), -1); + ck_assert_int_eq(s.dhcp_state, DHCP_REQUEST_SENT); + ck_assert_uint_eq(primary->ip, 0U); +} +END_TEST + START_TEST(test_dhcp_parse_offer_bad_magic_rejected) { struct wolfIP s; @@ -9040,6 +9312,56 @@ START_TEST(test_ip_recv_forward_ttl_exceeded) } END_TEST +START_TEST(test_ip_recv_udp_with_ip_options_delivers_payload) +{ + struct wolfIP s; + struct tsocket *ts; + uint8_t frame[ETH_HEADER_LEN + IP_HEADER_LEN + 4 + UDP_HEADER_LEN + 4]; + struct wolfIP_ip_packet *ip = (struct wolfIP_ip_packet *)frame; + uint8_t *udp_hdr = frame + ETH_HEADER_LEN + IP_HEADER_LEN + 4; + uint16_t udp_len = UDP_HEADER_LEN + 4; + uint32_t local_ip = 0x0A000001U; + uint32_t remote_ip = 0x0A000002U; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0); + + ts = udp_new_socket(&s); + ck_assert_ptr_nonnull(ts); + ts->src_port = 1234; + ts->local_ip = local_ip; + + memset(frame, 0, sizeof(frame)); + memcpy(ip->eth.dst, s.ll_dev[TEST_PRIMARY_IF].mac, 6); + memcpy(ip->eth.src, "\x01\x02\x03\x04\x05\x06", 6); + ip->eth.type = ee16(ETH_TYPE_IP); + ip->ver_ihl = 0x46; + ip->ttl = 64; + ip->proto = WI_IPPROTO_UDP; + ip->len = ee16(IP_HEADER_LEN + 4 + udp_len); + ip->src = ee32(remote_ip); + ip->dst = ee32(local_ip); + ip->data[0] = 1; + ip->data[1] = 1; + ip->data[2] = 1; + ip->data[3] = 0; + + ((uint16_t *)udp_hdr)[0] = ee16(4321); + ((uint16_t *)udp_hdr)[1] = ee16(1234); + ((uint16_t *)udp_hdr)[2] = ee16(udp_len); + memcpy(udp_hdr + UDP_HEADER_LEN, "opt!", 4); + + fix_udp_checksum_raw(ip, udp_hdr, udp_len); + fix_ip_checksum_with_hlen(ip, (uint16_t)(IP_HEADER_LEN + 4)); + + ip_recv(&s, TEST_PRIMARY_IF, ip, (uint32_t)sizeof(frame)); + + ck_assert_ptr_nonnull(fifo_peek(&ts->sock.udp.rxbuf)); + ck_assert_int_ne((ts->events & CB_EVENT_READABLE), 0); +} +END_TEST + START_TEST(test_ip_recv_forward_arp_queue_and_flush) { struct wolfIP s; @@ -14720,6 +15042,96 @@ START_TEST(test_tcp_input_port_mismatch_skips_socket) } END_TEST +START_TEST(test_tcp_input_unmatched_ack_sends_rst) +{ + struct wolfIP s; + struct wolfIP_tcp_seg *rst; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + last_frame_sent_size = 0; + memset(last_frame_sent, 0, sizeof(last_frame_sent)); + + inject_tcp_segment(&s, TEST_PRIMARY_IF, 0x0A000002U, 0x0A000001U, + 4321, 1234, 77, 101, TCP_FLAG_ACK); + + ck_assert_uint_eq(last_frame_sent_size, (uint32_t)sizeof(struct wolfIP_tcp_seg)); + rst = (struct wolfIP_tcp_seg *)last_frame_sent; + ck_assert_uint_eq(ee32(rst->ip.src), 0x0A000001U); + ck_assert_uint_eq(ee32(rst->ip.dst), 0x0A000002U); + ck_assert_uint_eq(ee16(rst->src_port), 1234); + ck_assert_uint_eq(ee16(rst->dst_port), 4321); + ck_assert_uint_eq(rst->flags, TCP_FLAG_RST); + ck_assert_uint_eq(ee32(rst->seq), 101U); + ck_assert_uint_eq(ee32(rst->ack), 0U); +} +END_TEST + +START_TEST(test_tcp_input_unmatched_ack_nonlocal_dst_does_not_send_rst) +{ + struct wolfIP s; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + last_frame_sent_size = 0; + memset(last_frame_sent, 0, sizeof(last_frame_sent)); + + inject_tcp_segment(&s, TEST_PRIMARY_IF, 0x0A000002U, 0x0A0000FEU, + 4321, 1234, 77, 101, TCP_FLAG_ACK); + + ck_assert_uint_eq(last_frame_sent_size, 0U); +} +END_TEST + +START_TEST(test_tcp_input_unmatched_syn_sends_rst_ack) +{ + struct wolfIP s; + struct wolfIP_tcp_seg *rst; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + last_frame_sent_size = 0; + memset(last_frame_sent, 0, sizeof(last_frame_sent)); + + inject_tcp_segment(&s, TEST_PRIMARY_IF, 0x0A000002U, 0x0A000001U, + 4321, 1234, 77, 0, TCP_FLAG_SYN); + + ck_assert_uint_eq(last_frame_sent_size, (uint32_t)sizeof(struct wolfIP_tcp_seg)); + rst = (struct wolfIP_tcp_seg *)last_frame_sent; + ck_assert_uint_eq(ee32(rst->ip.src), 0x0A000001U); + ck_assert_uint_eq(ee32(rst->ip.dst), 0x0A000002U); + ck_assert_uint_eq(ee16(rst->src_port), 1234); + ck_assert_uint_eq(ee16(rst->dst_port), 4321); + ck_assert_uint_eq(rst->flags, (uint8_t)(TCP_FLAG_RST | TCP_FLAG_ACK)); + ck_assert_uint_eq(ee32(rst->seq), 0U); + ck_assert_uint_eq(ee32(rst->ack), 78U); +} +END_TEST + +START_TEST(test_tcp_input_unmatched_rst_is_discarded) +{ + struct wolfIP s; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + last_frame_sent_size = 0; + memset(last_frame_sent, 0, sizeof(last_frame_sent)); + + inject_tcp_segment(&s, TEST_PRIMARY_IF, 0x0A000002U, 0x0A000001U, + 4321, 1234, 77, 0, TCP_FLAG_RST); + + ck_assert_uint_eq(last_frame_sent_size, 0U); +} +END_TEST + START_TEST(test_tcp_input_syn_bound_ip_mismatch) { struct wolfIP s; @@ -15554,6 +15966,83 @@ START_TEST(test_tcp_input_close_wait_processes_ack) } END_TEST +START_TEST(test_tcp_input_closing_processes_ack) +{ + struct wolfIP s; + struct tsocket *ts; + struct tcp_seg_buf segbuf; + struct wolfIP_tcp_seg *queued; + struct wolfIP_tcp_seg ackseg; + struct pkt_desc *desc; + struct wolfIP_timer tmr; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + ts = &s.tcpsockets[0]; + memset(ts, 0, sizeof(*ts)); + ts->proto = WI_IPPROTO_TCP; + ts->S = &s; + ts->sock.tcp.state = TCP_CLOSING; + ts->src_port = 1234; + ts->dst_port = 4321; + ts->local_ip = 0x0A000001U; + ts->remote_ip = 0x0A000002U; + ts->sock.tcp.last = 100; + ts->sock.tcp.snd_una = 100; + ts->sock.tcp.seq = 101; + ts->sock.tcp.bytes_in_flight = 1; + ts->sock.tcp.rto = 100; + ts->sock.tcp.ctrl_rto_active = 1; + ts->sock.tcp.ctrl_rto_retries = 2; + fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); + + memset(&segbuf, 0, sizeof(segbuf)); + queued = &segbuf.seg; + queued->ip.len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN + 1); + queued->hlen = TCP_HEADER_LEN << 2; + queued->seq = ee32(100); + ck_assert_int_eq(fifo_push(&ts->sock.tcp.txbuf, &segbuf, sizeof(segbuf)), 0); + desc = fifo_peek(&ts->sock.tcp.txbuf); + ck_assert_ptr_nonnull(desc); + desc->flags |= PKT_FLAG_SENT; + + memset(&tmr, 0, sizeof(tmr)); + tmr.cb = test_timer_cb; + tmr.expires = 200; + tmr.arg = ts; + ts->sock.tcp.tmr_rto = timers_binheap_insert(&s.timers, tmr); + ck_assert_int_ne(ts->sock.tcp.tmr_rto, NO_TIMER); + + memset(&ackseg, 0, sizeof(ackseg)); + ackseg.ip.ver_ihl = 0x45; + ackseg.ip.ttl = 64; + ackseg.ip.proto = WI_IPPROTO_TCP; + ackseg.ip.len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN); + ackseg.ip.src = ee32(ts->remote_ip); + ackseg.ip.dst = ee32(ts->local_ip); + ackseg.src_port = ee16(ts->dst_port); + ackseg.dst_port = ee16(ts->src_port); + ackseg.hlen = TCP_HEADER_LEN << 2; + ackseg.flags = TCP_FLAG_ACK; + ackseg.ack = ee32(101); + ackseg.win = ee16(32); + fix_tcp_checksums(&ackseg); + + tcp_input(&s, TEST_PRIMARY_IF, &ackseg, + (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN)); + + ck_assert_int_eq(ts->sock.tcp.state, TCP_TIME_WAIT); + ck_assert_uint_eq(ts->sock.tcp.snd_una, 101U); + ck_assert_uint_eq(ts->sock.tcp.bytes_in_flight, 0U); + ck_assert_ptr_eq(fifo_peek(&ts->sock.tcp.txbuf), NULL); + ck_assert_int_eq(ts->sock.tcp.tmr_rto, NO_TIMER); + ck_assert_uint_eq(ts->sock.tcp.ctrl_rto_active, 0U); + ck_assert_uint_eq(ts->sock.tcp.ctrl_rto_retries, 0U); +} +END_TEST + START_TEST(test_tcp_sock_close_state_transitions) { struct wolfIP s; @@ -18920,15 +19409,18 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_udp_try_recv_filter_drop); tcase_add_test(tc_utils, test_udp_try_recv_dhcp_running_local_zero); tcase_add_test(tc_utils, test_udp_try_recv_short_expected_len); + tcase_add_test(tc_utils, test_udp_try_recv_len_below_header_rejected); tcase_add_test(tc_utils, test_udp_try_recv_conf_null); tcase_add_test(tc_utils, test_udp_try_recv_remote_ip_matches_local_ip); + tcase_add_test(tc_utils, test_udp_try_recv_unmatched_port_sends_icmp_unreachable); + tcase_add_test(tc_utils, test_udp_try_recv_unmatched_nonlocal_dst_does_not_send_icmp); tcase_add_test(tc_utils, test_dns_callback_bad_flags); tcase_add_test(tc_utils, test_dns_callback_bad_name); tcase_add_test(tc_utils, test_dns_callback_short_header_ignored); tcase_add_test(tc_utils, test_dns_callback_wrong_id_ignored); tcase_add_test(tc_utils, test_dns_callback_abort_clears_query_state); tcase_add_test(tc_utils, test_dns_abort_query_null_noop); - tcase_add_test(tc_utils, test_tcp_input_ttl_zero_sends_icmp); + tcase_add_test(tc_utils, test_tcp_input_ttl_zero_local_ack_still_processes); tcase_add_test(tc_utils, test_dns_callback_bad_rr_rdlen); tcase_add_test(tc_utils, test_dhcp_parse_offer_no_match); tcase_add_test(tc_utils, test_dhcp_parse_ack_invalid); @@ -19012,9 +19504,14 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_tcp_input_syn_rcvd_ack_invalid_seq_rejected); tcase_add_test(tc_utils, test_tcp_input_filter_drop); tcase_add_test(tc_utils, test_tcp_input_port_mismatch_skips_socket); + tcase_add_test(tc_utils, test_tcp_input_unmatched_ack_sends_rst); + tcase_add_test(tc_utils, test_tcp_input_unmatched_ack_nonlocal_dst_does_not_send_rst); + tcase_add_test(tc_utils, test_tcp_input_unmatched_syn_sends_rst_ack); + tcase_add_test(tc_utils, test_tcp_input_unmatched_rst_is_discarded); tcase_add_test(tc_utils, test_tcp_input_syn_bound_ip_mismatch); tcase_add_test(tc_utils, test_tcp_input_syn_rcvd_ack_wrong_flags); tcase_add_test(tc_utils, test_tcp_input_close_wait_processes_ack); + tcase_add_test(tc_utils, test_tcp_input_closing_processes_ack); tcase_add_test(tc_utils, test_tcp_input_established_ack_only_returns); tcase_add_test(tc_utils, test_tcp_input_syn_dst_not_local); tcase_add_test(tc_utils, test_tcp_input_syn_dst_outside_subnet); @@ -19210,6 +19707,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_proto, test_forward_interface_dest_is_local_ip); tcase_add_test(tc_proto, test_forward_interface_short_circuit_cases); tcase_add_test(tc_proto, test_ip_recv_forward_ttl_exceeded); + tcase_add_test(tc_proto, test_ip_recv_udp_with_ip_options_delivers_payload); tcase_add_test(tc_proto, test_ip_recv_forward_arp_queue_and_flush); tcase_add_test(tc_proto, test_arp_flush_pending_ttl_expired); tcase_add_test(tc_proto, test_wolfip_forwarding_basic); @@ -19224,6 +19722,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_proto, test_icmp_socket_send_recv); tcase_add_test(tc_proto, test_icmp_input_echo_reply_queues); tcase_add_test(tc_proto, test_icmp_input_echo_request_reply_sent); + tcase_add_test(tc_proto, test_icmp_input_echo_request_bad_checksum_dropped); tcase_add_test(tc_proto, test_icmp_input_echo_request_odd_len_reply_checksum); tcase_add_test(tc_proto, test_icmp_input_echo_request_dhcp_running_no_reply); tcase_add_test(tc_proto, test_icmp_input_echo_request_filter_drop); @@ -19239,6 +19738,8 @@ Suite *wolf_suite(void) tcase_add_test(tc_proto, test_dns_query_and_callback_a); tcase_add_test(tc_proto, test_dhcp_parse_offer_and_ack); tcase_add_test(tc_proto, test_dhcp_parse_offer_defaults_mask_when_missing); + tcase_add_test(tc_proto, test_dhcp_parse_offer_rejects_mismatched_xid); + tcase_add_test(tc_proto, test_dhcp_parse_ack_rejects_mismatched_xid); tcase_add_test(tc_proto, test_tcp_handshake_and_fin_close_wait); tcase_add_test(tc_proto, test_regression_snd_una_initialized_on_syn_rcvd); diff --git a/src/wolfip.c b/src/wolfip.c index e59bef8..3a5287e 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -72,6 +72,8 @@ struct wolfIP_icmp_packet; #define ICMP_ECHO_REPLY 0 #define ICMP_ECHO_REQUEST 8 #define ICMP_TTL_EXCEEDED 11 +#define ICMP_DEST_UNREACH 3 +#define ICMP_PORT_UNREACH 3 #define WI_IPPROTO_ICMP 0x01 #define WI_IPPROTO_TCP 0x06 @@ -125,6 +127,7 @@ struct wolfIP_icmp_packet; /* Macros */ #define IS_IP_BCAST(ip) ((ip) == 0xFFFFFFFFU) +#define IS_IP_MCAST(ip) (((ip) & 0xF0000000U) == 0xE0000000U) #define PKT_FLAG_SENT 0x01U #define PKT_FLAG_ACKED 0x02U @@ -701,6 +704,7 @@ union transport_pseudo_header { #define TTL_EXCEEDED_ORIG_PACKET_SIZE (28) #define ICMP_TTL_EXCEEDED_SIZE (36) +#define ICMP_DEST_UNREACH_SIZE (36) struct PACKED wolfIP_icmp_packet { struct wolfIP_ip_packet ip; @@ -717,6 +721,14 @@ struct PACKED wolfIP_icmp_ttl_exceeded_packet { uint8_t orig_packet[TTL_EXCEEDED_ORIG_PACKET_SIZE]; }; +struct PACKED wolfIP_icmp_dest_unreachable_packet { + struct wolfIP_ip_packet ip; + uint8_t type, code; + uint16_t csum; + uint8_t unused[4]; + uint8_t orig_packet[TTL_EXCEEDED_ORIG_PACKET_SIZE]; +}; + static uint16_t icmp_echo_id(const struct wolfIP_icmp_packet *icmp) { uint16_t net = 0; @@ -1457,6 +1469,8 @@ static unsigned int wolfIP_if_for_local_ip(struct wolfIP *s, ip4 local_ip, int * static uint16_t transport_checksum(union transport_pseudo_header *ph, void *_data); static int transport_verify_checksum(union transport_pseudo_header *ph, void *data); +static void wolfIP_send_port_unreachable(struct wolfIP *s, unsigned int if_idx, + struct wolfIP_ip_packet *orig); #ifdef ETHERNET static uint16_t icmp_checksum(struct wolfIP_icmp_packet *icmp, uint16_t len); static void iphdr_set_checksum(struct wolfIP_ip_packet *ip); @@ -1469,7 +1483,7 @@ static void arp_request(struct wolfIP *s, unsigned int if_idx, ip4 tip); static int arp_lookup(struct wolfIP *s, unsigned int if_idx, ip4 ip, uint8_t *mac); #endif -#ifdef ETHERNET +#if WOLFIP_ENABLE_FORWARDING && defined(ETHERNET) static void wolfIP_send_ttl_exceeded(struct wolfIP *s, unsigned int if_idx, struct wolfIP_ip_packet *orig) { @@ -1482,6 +1496,56 @@ static void wolfIP_send_ttl_exceeded(struct wolfIP *s, unsigned int if_idx, if (!ll || !ll->send) return; icmp.type = ICMP_TTL_EXCEEDED; + memcpy(icmp.orig_packet, ((uint8_t *)orig) + ETH_HEADER_LEN, + TTL_EXCEEDED_ORIG_PACKET_SIZE); + icmp.csum = ee16(icmp_checksum((struct wolfIP_icmp_packet *)&icmp, + ICMP_DEST_UNREACH_SIZE)); + icmp.ip.ver_ihl = 0x45; + icmp.ip.ttl = 64; + icmp.ip.proto = WI_IPPROTO_ICMP; + icmp.ip.id = ipcounter_next(s); + icmp.ip.len = ee16(IP_HEADER_LEN + ICMP_DEST_UNREACH_SIZE); + icmp.ip.src = ee32(wolfIP_ipconf_at(s, if_idx)->ip); + icmp.ip.dst = orig->src; + icmp.ip.csum = 0; + iphdr_set_checksum(&icmp.ip); + if (!wolfIP_ll_is_non_ethernet(s, if_idx)) { + eth_output_add_header(s, if_idx, orig->eth.src, &icmp.ip.eth, ETH_TYPE_IP); + } + if (wolfIP_filter_notify_icmp(WOLFIP_FILT_SENDING, s, if_idx, icmp_pkt, sizeof(icmp)) != 0) + return; + if (wolfIP_filter_notify_ip(WOLFIP_FILT_SENDING, s, if_idx, &icmp.ip, sizeof(icmp)) != 0) + return; + if (!wolfIP_ll_is_non_ethernet(s, if_idx)) { + if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, s, if_idx, &icmp.ip.eth, sizeof(icmp)) != 0) + return; + } + wolfIP_ll_send_frame(s, if_idx, &icmp, sizeof(icmp)); +} +#elif WOLFIP_ENABLE_FORWARDING +static void wolfIP_send_ttl_exceeded(struct wolfIP *s, unsigned int if_idx, + struct wolfIP_ip_packet *orig) +{ + (void)s; + (void)if_idx; + (void)orig; +} +#endif + +#ifdef ETHERNET +static void wolfIP_send_port_unreachable(struct wolfIP *s, unsigned int if_idx, + struct wolfIP_ip_packet *orig) +{ + struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, if_idx); + struct wolfIP_icmp_dest_unreachable_packet icmp = {0}; + struct wolfIP_icmp_packet *icmp_pkt = (struct wolfIP_icmp_packet *)&icmp; +#if !CONFIG_IPFILTER + (void)icmp_pkt; +#endif + if (!ll || !ll->send) + return; + icmp.type = ICMP_DEST_UNREACH; + icmp.code = ICMP_PORT_UNREACH; memcpy(icmp.orig_packet, ((uint8_t *)orig) + ETH_HEADER_LEN, TTL_EXCEEDED_ORIG_PACKET_SIZE); icmp.csum = ee16(icmp_checksum((struct wolfIP_icmp_packet *)&icmp, @@ -1509,8 +1573,8 @@ static void wolfIP_send_ttl_exceeded(struct wolfIP *s, unsigned int if_idx, wolfIP_ll_send_frame(s, if_idx, &icmp, sizeof(icmp)); } #else -static void wolfIP_send_ttl_exceeded(struct wolfIP *s, unsigned int if_idx, - struct wolfIP_ip_packet *orig) +static void wolfIP_send_port_unreachable(struct wolfIP *s, unsigned int if_idx, + struct wolfIP_ip_packet *orig) { (void)s; (void)if_idx; @@ -1647,6 +1711,7 @@ static void udp_try_recv(struct wolfIP *s, unsigned int if_idx, struct wolfIP_udp_datagram *udp, uint32_t frame_len) { int i; + int matched = 0; ip4 dst_ip; ip4 src_ip; @@ -1658,6 +1723,10 @@ static void udp_try_recv(struct wolfIP *s, unsigned int if_idx, if (frame_len < (uint32_t)(ETH_HEADER_LEN + ee16(udp->ip.len))) return; + /* validate minimum UDP length per RFC 768 */ + if (ee16(udp->len) < UDP_HEADER_LEN) + return; + /* validate UDP length field fits within the actual received buffer */ if (ee16(udp->len) > frame_len - ETH_HEADER_LEN - IP_HEADER_LEN) return; @@ -1697,6 +1766,18 @@ static void udp_try_recv(struct wolfIP *s, unsigned int if_idx, /* Insert into socket buffer */ fifo_push(&t->sock.udp.rxbuf, udp, frame_len); t->events |= CB_EVENT_READABLE; + matched = 1; + } + } + if (!matched) { + int dst_match = 0; + + if (dst_ip != IPADDR_ANY && src_ip != IPADDR_ANY && + !IS_IP_BCAST(dst_ip) && !IS_IP_BCAST(src_ip) && + !IS_IP_MCAST(dst_ip) && !IS_IP_MCAST(src_ip)) { + (void)wolfIP_if_for_local_ip(s, dst_ip, &dst_match); + if (dst_match) + wolfIP_send_port_unreachable(s, if_idx, &udp->ip); } } } @@ -2172,6 +2253,80 @@ static void tcp_send_ack(struct tsocket *t) return tcp_send_empty(t, TCP_FLAG_ACK); } +static void tcp_send_reset_reply(struct wolfIP *s, unsigned int if_idx, + const struct wolfIP_tcp_seg *in) +{ + struct wolfIP_tcp_seg out; + union transport_pseudo_header ph; + uint16_t ip_len; + uint32_t tcp_hlen; + uint32_t seg_ack; + + if (in->flags & TCP_FLAG_RST) + return; + + ip_len = ee16(in->ip.len); + tcp_hlen = (uint32_t)(in->hlen >> 2); + if (tcp_hlen < TCP_HEADER_LEN) + return; + if (ip_len < (uint16_t)(IP_HEADER_LEN + tcp_hlen)) + return; + + memset(&out, 0, sizeof(out)); + out.src_port = in->dst_port; + out.dst_port = in->src_port; + out.hlen = TCP_HEADER_LEN << 2; + + if (in->flags & TCP_FLAG_ACK) { + out.seq = in->ack; + out.flags = TCP_FLAG_RST; + } else { + seg_ack = ee32(in->seq); + seg_ack = tcp_seq_inc(seg_ack, ip_len - (uint16_t)(IP_HEADER_LEN + tcp_hlen)); + if (in->flags & TCP_FLAG_SYN) + seg_ack = tcp_seq_inc(seg_ack, 1); + if (in->flags & TCP_FLAG_FIN) + seg_ack = tcp_seq_inc(seg_ack, 1); + out.ack = ee32(seg_ack); + out.flags = TCP_FLAG_RST | TCP_FLAG_ACK; + } + + out.ip.src = in->ip.dst; + out.ip.dst = in->ip.src; + out.ip.ver_ihl = 0x45; + out.ip.len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN); + out.ip.ttl = 64; + out.ip.proto = WI_IPPROTO_TCP; + out.ip.id = ee16(s->ipcounter); + s->ipcounter = (uint16_t)(s->ipcounter + 1); + iphdr_set_checksum(&out.ip); + + memset(&ph, 0, sizeof(ph)); + ph.ph.src = out.ip.src; + ph.ph.dst = out.ip.dst; + ph.ph.proto = WI_IPPROTO_TCP; + ph.ph.len = ee16(TCP_HEADER_LEN); + out.csum = ee16(transport_checksum(&ph, &out.src_port)); + +#ifdef ETHERNET + if (!wolfIP_ll_is_non_ethernet(s, if_idx)) { + if (eth_output_add_header(s, if_idx, in->ip.eth.src, &out.ip.eth, ETH_TYPE_IP) != 0) + return; + } +#endif + if (wolfIP_filter_notify_tcp(WOLFIP_FILT_SENDING, s, if_idx, &out, sizeof(out)) != 0) + return; + if (wolfIP_filter_notify_ip(WOLFIP_FILT_SENDING, s, if_idx, &out.ip, sizeof(out)) != 0) + return; +#ifdef ETHERNET + if (!wolfIP_ll_is_non_ethernet(s, if_idx)) { + if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, s, if_idx, &out.ip.eth, sizeof(out)) != 0) + return; + } +#endif + wolfIP_ll_send_frame(s, if_idx, &out.ip, sizeof(out)); +} + static void tcp_send_finack(struct tsocket *t) { tcp_send_empty(t, TCP_FLAG_FIN | TCP_FLAG_ACK); @@ -2587,8 +2742,13 @@ static void iphdr_set_checksum(struct wolfIP_ip_packet *ip) { uint32_t sum = 0; uint32_t i = 0; + uint32_t ip_hlen = (uint32_t)(ip->ver_ihl & 0x0fU) << 2; const uint8_t *ptr = (const uint8_t *)(&ip->ver_ihl); - for (i = 0; i < IP_HEADER_LEN; i += 2) { + + if (ip_hlen < IP_HEADER_LEN) + ip_hlen = IP_HEADER_LEN; + + for (i = 0; i < ip_hlen; i += 2) { uint16_t word; memcpy(&word, ptr + i, sizeof(word)); sum += ee16(word); @@ -2603,8 +2763,16 @@ static int iphdr_verify_checksum(struct wolfIP_ip_packet *ip) { uint32_t sum = 0; uint32_t i; + uint32_t ip_hlen; const uint8_t *ptr = (const uint8_t *)(&ip->ver_ihl); - for (i = 0; i < IP_HEADER_LEN; i += 2) { + + if ((ip->ver_ihl >> 4) != 4) + return -1; + ip_hlen = (uint32_t)(ip->ver_ihl & 0x0fU) << 2; + if (ip_hlen < IP_HEADER_LEN) + return -1; + + for (i = 0; i < ip_hlen; i += 2) { uint16_t word; memcpy(&word, ptr + i, sizeof(word)); sum += ee16(word); @@ -3175,6 +3343,7 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, struct wolfIP_tcp_seg *tcp, uint32_t frame_len) { int i; + int matched = 0; /* validate minimum TCP segment length */ if (frame_len < sizeof(struct wolfIP_tcp_seg)) @@ -3225,11 +3394,7 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, continue; } } - /* Check IP ttl */ - if (tcp->ip.ttl == 0) { - wolfIP_send_ttl_exceeded(S, if_idx, &tcp->ip); - return; - } + matched = 1; /* Validate minimum TCP header length (data offset). */ if ((tcp->hlen >> 2) < TCP_HEADER_LEN) { return; /* malformed: TCP header below minimum length */ @@ -3417,7 +3582,8 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, else if ((t->sock.tcp.state == TCP_ESTABLISHED) || (t->sock.tcp.state == TCP_CLOSE_WAIT) || (t->sock.tcp.state == TCP_FIN_WAIT_1) || - (t->sock.tcp.state == TCP_FIN_WAIT_2)) { + (t->sock.tcp.state == TCP_FIN_WAIT_2) || + (t->sock.tcp.state == TCP_CLOSING)) { if (tcp->flags & TCP_FLAG_ACK) { tcp_ack(t, tcp); @@ -3466,6 +3632,16 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, } } } + if (!matched) { + ip4 dst = ee32(tcp->ip.dst); + int dst_match = 0; + + if (dst != IPADDR_ANY && !IS_IP_BCAST(dst) && !IS_IP_MCAST(dst)) { + (void)wolfIP_if_for_local_ip(S, dst, &dst_match); + if (dst_match) + tcp_send_reset_reply(S, if_idx, tcp); + } + } } static void tcp_rto_cb(void *arg) @@ -4690,6 +4866,9 @@ static void icmp_input(struct wolfIP *s, unsigned int if_idx, struct wolfIP_ip_p /* validate ip->len doesn't exceed actual received data */ if (len < (uint32_t)(ETH_HEADER_LEN + ee16(ip->len))) return; + /* validate ICMP checksum before processing */ + if (icmp_checksum(icmp, (uint16_t)(ee16(ip->len) - IP_HEADER_LEN)) != 0) + return; if (wolfIP_filter_notify_icmp(WOLFIP_FILT_RECEIVING, s, if_idx, icmp, len) != 0) return; @@ -4794,6 +4973,8 @@ static int dhcp_parse_offer(struct wolfIP *s, struct dhcp_msg *msg, uint32_t msg return -1; if (ee32(msg->magic) != DHCP_MAGIC) return -1; + if (ee32(msg->xid) != s->dhcp_xid) + return -1; if (msg_len - DHCP_HEADER_LEN > sizeof(msg->options)) opt_end = (uint8_t *)msg->options + sizeof(msg->options); else @@ -4889,6 +5070,8 @@ static int dhcp_parse_ack(struct wolfIP *s, struct dhcp_msg *msg, uint32_t msg_l return -1; if (ee32(msg->magic) != DHCP_MAGIC) return -1; + if (ee32(msg->xid) != s->dhcp_xid) + return -1; if (msg_len - DHCP_HEADER_LEN > sizeof(msg->options)) opt_end = (uint8_t *)msg->options + sizeof(msg->options); else @@ -5542,6 +5725,8 @@ size_t wolfIP_instance_size(void) static inline void ip_recv(struct wolfIP *s, unsigned int if_idx, struct wolfIP_ip_packet *ip, uint32_t len) { + uint8_t version; + uint32_t ip_hlen; #if WOLFIP_ENABLE_FORWARDING unsigned int i; #endif @@ -5549,6 +5734,14 @@ static inline void ip_recv(struct wolfIP *s, unsigned int if_idx, * (ethernet header + ip header, with no options) */ if (len < sizeof(struct wolfIP_ip_packet)) return; + version = ip->ver_ihl >> 4; + ip_hlen = (uint32_t)(ip->ver_ihl & 0x0fU) << 2; + if (version != 4 || ip_hlen < IP_HEADER_LEN) + return; + if (len < (uint32_t)(ETH_HEADER_LEN + ip_hlen)) + return; + if (ee16(ip->len) < ip_hlen) + return; /* validate IP header checksum per RFC 1122 */ if (iphdr_verify_checksum(ip) != 0) return; @@ -5563,7 +5756,7 @@ static inline void ip_recv(struct wolfIP *s, unsigned int if_idx, if (wolfIP_filter_notify_ip(WOLFIP_FILT_RECEIVING, s, if_idx, ip, len) != 0) return; #if WOLFIP_ENABLE_FORWARDING - if (ip->ver_ihl == 0x45) { + if (version == 4 && ip_hlen >= IP_HEADER_LEN) { ip4 dest = ee32(ip->dst); int is_local = 0; if (dest == IPADDR_ANY || IS_IP_BCAST(dest)) { @@ -5626,25 +5819,49 @@ static inline void ip_recv(struct wolfIP *s, unsigned int if_idx, } #endif /* WOLFIP_ESP */ - if (ip->ver_ihl == 0x45 && ip->proto == 0x06) { - struct wolfIP_tcp_seg *tcp = (struct wolfIP_tcp_seg *)ip; - tcp_input(s, if_idx, tcp, len); - } - else if (ip->ver_ihl == 0x45 && ip->proto == 0x11) { - struct wolfIP_udp_datagram *udp = (struct wolfIP_udp_datagram *)ip; + { + struct wolfIP_ip_packet *dispatch_ip = ip; + uint32_t dispatch_len = len; + uint8_t frame[LINK_MTU]; + + if (ip_hlen > IP_HEADER_LEN) { + uint32_t opt_len = ip_hlen - IP_HEADER_LEN; + uint16_t total_ip_len = ee16(ip->len); + + if (len > LINK_MTU) + return; + memcpy(frame, ip, len); + dispatch_ip = (struct wolfIP_ip_packet *)frame; + memmove(((uint8_t *)dispatch_ip) + ETH_HEADER_LEN + IP_HEADER_LEN, + ((uint8_t *)dispatch_ip) + ETH_HEADER_LEN + ip_hlen, + len - (ETH_HEADER_LEN + ip_hlen)); + dispatch_len -= opt_len; + dispatch_ip->ver_ihl = (uint8_t)((dispatch_ip->ver_ihl & 0xf0U) | 0x05U); + dispatch_ip->len = ee16(total_ip_len - (uint16_t)opt_len); + dispatch_ip->csum = 0; + iphdr_set_checksum(dispatch_ip); + } + + if (dispatch_ip->proto == 0x06) { + struct wolfIP_tcp_seg *tcp = (struct wolfIP_tcp_seg *)dispatch_ip; + tcp_input(s, if_idx, tcp, dispatch_len); + } + else if (dispatch_ip->proto == 0x11) { + struct wolfIP_udp_datagram *udp = (struct wolfIP_udp_datagram *)dispatch_ip; #ifdef DEBUG_UDP - wolfIP_print_udp(udp); + wolfIP_print_udp(udp); #endif /* DEBUG_UDP */ - udp_try_recv(s, if_idx, udp, len); - } - else if (ip->ver_ihl == 0x45 && ip->proto == 0x01) { - icmp_input(s, if_idx, ip, len); - } + udp_try_recv(s, if_idx, udp, dispatch_len); + } + else if (dispatch_ip->proto == 0x01) { + icmp_input(s, if_idx, dispatch_ip, dispatch_len); + } #ifdef DEBUG_IP - else { - LOG("info: dropping ip packet: 0x%02x\n", ip->proto); - } + else { + LOG("info: dropping ip packet: 0x%02x\n", dispatch_ip->proto); + } #endif + } } static void wolfIP_recv_on(struct wolfIP *s, unsigned int if_idx, void *buf, uint32_t len)