diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 061e985..cc95d33 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -681,6 +681,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_proto, test_send_ttl_exceeded_sets_df); #if WOLFIP_ENABLE_FORWARDING tcase_add_test(tc_proto, test_wolfip_forward_ttl_exceeded_short_len_does_not_send); + tcase_add_test(tc_proto, test_regression_forward_ttl_exceeded_short_len_with_options_no_send); #endif tcase_add_test(tc_proto, test_arp_request_filter_drop); tcase_add_test(tc_proto, test_arp_request_invalid_interface); @@ -791,6 +792,8 @@ Suite *wolf_suite(void) tcase_add_test(tc_proto, test_forward_packet_eth_filter_drop); tcase_add_test(tc_proto, test_loopback_dest_not_forwarded); tcase_add_test(tc_proto, test_regression_forwarding_rpf_drops_spoofed_source); + tcase_add_test(tc_proto, test_regression_loopback_source_dropped_on_non_loopback_iface); + tcase_add_test(tc_proto, test_regression_icmp_echo_request_non_local_dst_no_reply); tcase_add_test(tc_proto, test_tcp_listen_rejects_wrong_interface); tcase_add_test(tc_proto, test_tcp_listen_accepts_bound_interface); tcase_add_test(tc_proto, test_tcp_listen_accepts_any_interface); @@ -814,6 +817,10 @@ Suite *wolf_suite(void) tcase_add_test(tc_proto, test_icmp_input_dest_unreach_frag_needed_below_floor_preserves_peer_mss); tcase_add_test(tc_proto, test_icmp_input_dest_unreach_port_unreachable_closes_syn_sent_tcp_socket); tcase_add_test(tc_proto, test_icmp_input_dest_unreach_port_unreachable_quoted_ip_options_keep_established_tcp_socket); + tcase_add_test(tc_proto, test_icmp_input_dest_unreach_port_unreachable_mismatched_orig_src_ip_ignored); + tcase_add_test(tc_proto, test_icmp_input_dest_unreach_port_unreachable_mismatched_orig_dst_ip_ignored); + tcase_add_test(tc_proto, test_icmp_input_dest_unreach_port_unreachable_mismatched_orig_src_port_ignored); + tcase_add_test(tc_proto, test_icmp_input_dest_unreach_port_unreachable_mismatched_orig_dst_port_ignored); tcase_add_test(tc_proto, test_udp_sendto_and_recvfrom); tcase_add_test(tc_proto, test_udp_sendto_respects_mtu_api); tcase_add_test(tc_proto, test_udp_recvfrom_sets_remote_ip); diff --git a/src/test/unit/unit_tests_dns_dhcp.c b/src/test/unit/unit_tests_dns_dhcp.c index bd6b4da..8ba650b 100644 --- a/src/test/unit/unit_tests_dns_dhcp.c +++ b/src/test/unit/unit_tests_dns_dhcp.c @@ -1738,6 +1738,7 @@ START_TEST(test_icmp_input_echo_request_reply_sent) wolfIP_init(&s); mock_link_init(&s); s.dhcp_state = DHCP_OFF; + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); wolfIP_filter_set_callback(NULL, NULL); last_frame_sent_size = 0; @@ -1768,6 +1769,7 @@ START_TEST(test_icmp_input_echo_reply_sets_df) wolfIP_init(&s); mock_link_init(&s); s.dhcp_state = DHCP_OFF; + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); wolfIP_filter_set_callback(NULL, NULL); last_frame_sent_size = 0; @@ -1834,6 +1836,7 @@ START_TEST(test_icmp_input_echo_request_odd_len_reply_checksum) wolfIP_init(&s); mock_link_init(&s); s.dhcp_state = DHCP_OFF; + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); wolfIP_filter_set_callback(NULL, NULL); last_frame_sent_size = 0; @@ -1985,6 +1988,7 @@ START_TEST(test_icmp_input_echo_request_filter_drop) wolfIP_init(&s); mock_link_init(&s); s.dhcp_state = DHCP_OFF; + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); filter_block_reason = WOLFIP_FILT_SENDING; wolfIP_filter_set_callback(test_filter_cb_block, NULL); wolfIP_filter_set_icmp_mask(WOLFIP_FILT_MASK(WOLFIP_FILT_SENDING)); @@ -2015,6 +2019,7 @@ START_TEST(test_icmp_input_echo_request_ip_filter_drop) wolfIP_init(&s); mock_link_init(&s); s.dhcp_state = DHCP_OFF; + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); filter_block_reason = WOLFIP_FILT_SENDING; wolfIP_filter_set_callback(test_filter_cb_block, NULL); wolfIP_filter_set_ip_mask(WOLFIP_FILT_MASK(WOLFIP_FILT_SENDING)); @@ -2045,6 +2050,7 @@ START_TEST(test_icmp_input_echo_request_eth_filter_drop) wolfIP_init(&s); mock_link_init(&s); s.dhcp_state = DHCP_OFF; + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); filter_block_reason = WOLFIP_FILT_SENDING; wolfIP_filter_set_callback(test_filter_cb_block, NULL); wolfIP_filter_set_eth_mask(WOLFIP_FILT_MASK(WOLFIP_FILT_SENDING)); @@ -2370,6 +2376,210 @@ START_TEST(test_icmp_input_dest_unreach_port_unreachable_quoted_ip_options_keep_ } END_TEST +START_TEST(test_icmp_input_dest_unreach_port_unreachable_mismatched_orig_src_ip_ignored) +{ + struct wolfIP s; + struct tsocket *ts; + struct wolfIP_icmp_dest_unreachable_packet icmp; + struct wolfIP_tcp_wire_prefix *orig; + uint32_t frame_len; + + 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_SYN_SENT; + ts->local_ip = 0x0A000001U; + ts->remote_ip = 0x0A000002U; + ts->src_port = 1234; + ts->dst_port = 4321; + + memset(&icmp, 0, sizeof(icmp)); + icmp.ip.src = ee32(0x0A0000FEU); + icmp.ip.dst = ee32(ts->local_ip); + icmp.ip.ttl = 64; + icmp.ip.proto = WI_IPPROTO_ICMP; + icmp.ip.len = ee16(IP_HEADER_LEN + ICMP_DEST_UNREACH_SIZE); + icmp.type = ICMP_DEST_UNREACH; + icmp.code = ICMP_PORT_UNREACH; + + orig = (struct wolfIP_tcp_wire_prefix *)icmp.orig_packet; + orig->ip.ver_ihl = 0x45; + orig->ip.proto = WI_IPPROTO_TCP; + orig->ip.src = ee32(0x0A000099U); + orig->ip.dst = ee32(ts->remote_ip); + orig->ip.len = ee16(IP_HEADER_LEN + 8U); + orig->src_port = ee16(ts->src_port); + orig->dst_port = ee16(ts->dst_port); + + icmp.csum = ee16(icmp_checksum((struct wolfIP_icmp_packet *)&icmp, + ICMP_DEST_UNREACH_SIZE)); + frame_len = (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + ICMP_DEST_UNREACH_SIZE); + + icmp_input(&s, TEST_PRIMARY_IF, (struct wolfIP_ip_packet *)&icmp, frame_len); + + ck_assert_uint_eq(ts->proto, WI_IPPROTO_TCP); + ck_assert_int_eq(ts->sock.tcp.state, TCP_SYN_SENT); +} +END_TEST + +START_TEST(test_icmp_input_dest_unreach_port_unreachable_mismatched_orig_dst_ip_ignored) +{ + struct wolfIP s; + struct tsocket *ts; + struct wolfIP_icmp_dest_unreachable_packet icmp; + struct wolfIP_tcp_wire_prefix *orig; + uint32_t frame_len; + + 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_SYN_SENT; + ts->local_ip = 0x0A000001U; + ts->remote_ip = 0x0A000002U; + ts->src_port = 1234; + ts->dst_port = 4321; + + memset(&icmp, 0, sizeof(icmp)); + icmp.ip.src = ee32(0x0A0000FEU); + icmp.ip.dst = ee32(ts->local_ip); + icmp.ip.ttl = 64; + icmp.ip.proto = WI_IPPROTO_ICMP; + icmp.ip.len = ee16(IP_HEADER_LEN + ICMP_DEST_UNREACH_SIZE); + icmp.type = ICMP_DEST_UNREACH; + icmp.code = ICMP_PORT_UNREACH; + + orig = (struct wolfIP_tcp_wire_prefix *)icmp.orig_packet; + orig->ip.ver_ihl = 0x45; + orig->ip.proto = WI_IPPROTO_TCP; + orig->ip.src = ee32(ts->local_ip); + orig->ip.dst = ee32(0x0A0000AAU); + orig->ip.len = ee16(IP_HEADER_LEN + 8U); + orig->src_port = ee16(ts->src_port); + orig->dst_port = ee16(ts->dst_port); + + icmp.csum = ee16(icmp_checksum((struct wolfIP_icmp_packet *)&icmp, + ICMP_DEST_UNREACH_SIZE)); + frame_len = (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + ICMP_DEST_UNREACH_SIZE); + + icmp_input(&s, TEST_PRIMARY_IF, (struct wolfIP_ip_packet *)&icmp, frame_len); + + ck_assert_uint_eq(ts->proto, WI_IPPROTO_TCP); + ck_assert_int_eq(ts->sock.tcp.state, TCP_SYN_SENT); +} +END_TEST + +START_TEST(test_icmp_input_dest_unreach_port_unreachable_mismatched_orig_src_port_ignored) +{ + struct wolfIP s; + struct tsocket *ts; + struct wolfIP_icmp_dest_unreachable_packet icmp; + struct wolfIP_tcp_wire_prefix *orig; + uint32_t frame_len; + + 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_SYN_SENT; + ts->local_ip = 0x0A000001U; + ts->remote_ip = 0x0A000002U; + ts->src_port = 1234; + ts->dst_port = 4321; + + memset(&icmp, 0, sizeof(icmp)); + icmp.ip.src = ee32(0x0A0000FEU); + icmp.ip.dst = ee32(ts->local_ip); + icmp.ip.ttl = 64; + icmp.ip.proto = WI_IPPROTO_ICMP; + icmp.ip.len = ee16(IP_HEADER_LEN + ICMP_DEST_UNREACH_SIZE); + icmp.type = ICMP_DEST_UNREACH; + icmp.code = ICMP_PORT_UNREACH; + + orig = (struct wolfIP_tcp_wire_prefix *)icmp.orig_packet; + orig->ip.ver_ihl = 0x45; + orig->ip.proto = WI_IPPROTO_TCP; + orig->ip.src = ee32(ts->local_ip); + orig->ip.dst = ee32(ts->remote_ip); + orig->ip.len = ee16(IP_HEADER_LEN + 8U); + orig->src_port = ee16((uint16_t)(ts->src_port + 1U)); + orig->dst_port = ee16(ts->dst_port); + + icmp.csum = ee16(icmp_checksum((struct wolfIP_icmp_packet *)&icmp, + ICMP_DEST_UNREACH_SIZE)); + frame_len = (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + ICMP_DEST_UNREACH_SIZE); + + icmp_input(&s, TEST_PRIMARY_IF, (struct wolfIP_ip_packet *)&icmp, frame_len); + + ck_assert_uint_eq(ts->proto, WI_IPPROTO_TCP); + ck_assert_int_eq(ts->sock.tcp.state, TCP_SYN_SENT); +} +END_TEST + +START_TEST(test_icmp_input_dest_unreach_port_unreachable_mismatched_orig_dst_port_ignored) +{ + struct wolfIP s; + struct tsocket *ts; + struct wolfIP_icmp_dest_unreachable_packet icmp; + struct wolfIP_tcp_wire_prefix *orig; + uint32_t frame_len; + + 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_SYN_SENT; + ts->local_ip = 0x0A000001U; + ts->remote_ip = 0x0A000002U; + ts->src_port = 1234; + ts->dst_port = 4321; + + memset(&icmp, 0, sizeof(icmp)); + icmp.ip.src = ee32(0x0A0000FEU); + icmp.ip.dst = ee32(ts->local_ip); + icmp.ip.ttl = 64; + icmp.ip.proto = WI_IPPROTO_ICMP; + icmp.ip.len = ee16(IP_HEADER_LEN + ICMP_DEST_UNREACH_SIZE); + icmp.type = ICMP_DEST_UNREACH; + icmp.code = ICMP_PORT_UNREACH; + + orig = (struct wolfIP_tcp_wire_prefix *)icmp.orig_packet; + orig->ip.ver_ihl = 0x45; + orig->ip.proto = WI_IPPROTO_TCP; + orig->ip.src = ee32(ts->local_ip); + orig->ip.dst = ee32(ts->remote_ip); + orig->ip.len = ee16(IP_HEADER_LEN + 8U); + orig->src_port = ee16(ts->src_port); + orig->dst_port = ee16((uint16_t)(ts->dst_port + 1U)); + + icmp.csum = ee16(icmp_checksum((struct wolfIP_icmp_packet *)&icmp, + ICMP_DEST_UNREACH_SIZE)); + frame_len = (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + ICMP_DEST_UNREACH_SIZE); + + icmp_input(&s, TEST_PRIMARY_IF, (struct wolfIP_ip_packet *)&icmp, frame_len); + + ck_assert_uint_eq(ts->proto, WI_IPPROTO_TCP); + ck_assert_int_eq(ts->sock.tcp.state, TCP_SYN_SENT); +} +END_TEST + START_TEST(test_dns_send_query_errors) { struct wolfIP s; diff --git a/src/test/unit/unit_tests_proto.c b/src/test/unit/unit_tests_proto.c index d5608c6..9cb38c5 100644 --- a/src/test/unit/unit_tests_proto.c +++ b/src/test/unit/unit_tests_proto.c @@ -3764,8 +3764,11 @@ END_TEST START_TEST(test_regression_forwarding_rpf_drops_spoofed_source) { + /* 127/8 is intentionally absent here, since ip_recv drops it earlier via the + * martian-source guard pinned by + * test_regression_loopback_source_dropped_on_non_loopback_iface, so it + * never reaches the forwarding-RPF path this test is meant to pin. */ static const ip4 spoofed_sources[] = { - 0x7F000001U, /* 127.0.0.1; loopback */ 0xA9FE0001U, /* 169.254.0.1; link-local */ 0xC0A80132U /* 192.168.1.50; in TEST_SECOND_IF's subnet, wrong ingress */ }; @@ -3813,6 +3816,115 @@ START_TEST(test_regression_forwarding_rpf_drops_spoofed_source) } END_TEST +START_TEST(test_regression_loopback_source_dropped_on_non_loopback_iface) +{ + static const ip4 spoofed_loopback_sources[] = { + 0x7F000001U, /* 127.0.0.1 */ + 0x7F0000FEU, /* 127.0.0.254 */ + 0x7FFFFFFEU /* 127.255.255.254 (high end of 127/8) */ + }; + static const uint8_t src_mac[6] = {0x52, 0x54, 0x00, 0x12, 0x34, 0x56}; + static const uint32_t local_ip = 0x0A000001U; /* 10.0.0.1 */ + static const uint16_t local_port = 1234; + unsigned int i; + + for (i = 0; i < sizeof(spoofed_loopback_sources) / + sizeof(spoofed_loopback_sources[0]); i++) { + struct wolfIP s; + struct tsocket *ts; + uint8_t frame_buf[sizeof(struct wolfIP_udp_datagram)]; + struct wolfIP_udp_datagram *udp = + (struct wolfIP_udp_datagram *)frame_buf; + uint32_t frame_len = + (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + UDP_HEADER_LEN); + + wolfIP_init(&s); + mock_link_init(&s); + s.dhcp_state = DHCP_OFF; + wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0); + + ts = udp_new_socket(&s); + ck_assert_ptr_nonnull(ts); + ts->src_port = local_port; + ts->local_ip = local_ip; + + memset(frame_buf, 0, sizeof(frame_buf)); + memcpy(udp->ip.eth.dst, s.ll_dev[TEST_PRIMARY_IF].mac, 6); + memcpy(udp->ip.eth.src, src_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); + udp->ip.src = ee32(spoofed_loopback_sources[i]); + udp->ip.dst = ee32(local_ip); + udp->ip.csum = 0; + iphdr_set_checksum(&udp->ip); + udp->src_port = ee16(9999); + udp->dst_port = ee16(local_port); + udp->len = ee16(UDP_HEADER_LEN); + udp->csum = 0; /* RFC 768: zero csum skips verification */ + + wolfIP_recv_ex(&s, TEST_PRIMARY_IF, udp, frame_len); + + /* The spoofed-loopback datagram + * must not surface to recvfrom. */ + ck_assert_ptr_eq(fifo_peek(&ts->sock.udp.rxbuf), NULL); + } +} +END_TEST + +START_TEST(test_regression_icmp_echo_request_non_local_dst_no_reply) +{ + /* RFC 1122 section 3.2.2.6: ICMP echo replies are only for requests destined to + * the responding host. Without a destination check in icmp_input, an + * L2-adjacent attacker can frame eth.dst = wolfIP's MAC, ip.src = victim_A, + * ip.dst = victim_B (neither owned by wolfIP, neither broadcast nor + * multicast). wolfIP would swap src/dst and emit an echo reply with + * ip.src = victim_B back at the original eth.src; turning the stack into + * a small reflector with attacker-controllable source IP. tcp_input and + * udp_try_recv already enforce destination-matching; this pins the + * equivalent for the ICMP echo path. */ + struct wolfIP s; + uint8_t frame_buf[sizeof(struct wolfIP_icmp_packet)]; + struct wolfIP_icmp_packet *icmp = (struct wolfIP_icmp_packet *)frame_buf; + static const uint8_t src_mac[6] = {0x52, 0x54, 0x00, 0x12, 0x34, 0x56}; + static const uint32_t local_ip = 0x0A000001U; /* 10.0.0.1 */ + static const uint32_t spoofed_src = 0xC0000201U; /* 192.0.2.1 (TEST-NET-1) */ + static const uint32_t spoofed_dst = 0xC6336401U; /* 198.51.100.1 (TEST-NET-2) */ + uint32_t frame_len = + (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + ICMP_HEADER_LEN); + + wolfIP_init(&s); + mock_link_init(&s); + s.dhcp_state = DHCP_OFF; + wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0); + + memset(frame_buf, 0, sizeof(frame_buf)); + memcpy(icmp->ip.eth.dst, s.ll_dev[TEST_PRIMARY_IF].mac, 6); + memcpy(icmp->ip.eth.src, src_mac, 6); + icmp->ip.eth.type = ee16(ETH_TYPE_IP); + icmp->ip.ver_ihl = 0x45; + icmp->ip.ttl = 64; + icmp->ip.proto = WI_IPPROTO_ICMP; + icmp->ip.len = ee16(IP_HEADER_LEN + ICMP_HEADER_LEN); + icmp->ip.src = ee32(spoofed_src); + icmp->ip.dst = ee32(spoofed_dst); + icmp->ip.csum = 0; + iphdr_set_checksum(&icmp->ip); + icmp->type = ICMP_ECHO_REQUEST; + icmp->code = 0; + icmp->csum = 0; + icmp->csum = ee16(icmp_checksum(icmp, ICMP_HEADER_LEN)); + + last_frame_sent_size = 0; + + wolfIP_recv_ex(&s, TEST_PRIMARY_IF, frame_buf, frame_len); + + ck_assert_uint_eq(last_frame_sent_size, 0U); +} +END_TEST + /* wolfSSL IO glue tests */ START_TEST(test_wolfssl_io_ctx_registers_callbacks) { diff --git a/src/test/unit/unit_tests_tcp_ack.c b/src/test/unit/unit_tests_tcp_ack.c index e2fe4d1..8fb5a8a 100644 --- a/src/test/unit/unit_tests_tcp_ack.c +++ b/src/test/unit/unit_tests_tcp_ack.c @@ -2598,6 +2598,41 @@ START_TEST(test_wolfip_forward_ttl_exceeded_short_len_does_not_send) ck_assert_uint_eq(last_frame_sent_size, 0U); } END_TEST + +START_TEST(test_regression_forward_ttl_exceeded_short_len_with_options_no_send) +{ + struct wolfIP s; + /* IHL=10 (40-byte IP header), no transport bytes after it. */ + uint8_t ip_buf[ETH_HEADER_LEN + 40]; + struct wolfIP_ip_packet *ip = (struct wolfIP_ip_packet *)ip_buf; + ip4 primary_ip = 0x0A000001U; + ip4 secondary_ip = 0xC0A80101U; + + setup_stack_with_two_ifaces(&s, primary_ip, secondary_ip); + wolfIP_filter_set_callback(NULL, NULL); + last_frame_sent_size = 0; + + memset(ip_buf, 0, sizeof(ip_buf)); + ip->eth.type = ee16(ETH_TYPE_IP); + memcpy(ip->eth.dst, s.ll_dev[TEST_PRIMARY_IF].mac, 6); + memcpy(ip->eth.src, "\x01\x02\x03\x04\x05\x06", 6); + ip->ver_ihl = 0x4A; /* IHL=10, 40-byte header */ + ip->ttl = 1; + ip->proto = WI_IPPROTO_UDP; + ip->len = ee16(40); /* IP datagram length: header only */ + ip->src = ee32(0x0A000099U); /* primary's subnet, passes RPF */ + ip->dst = ee32(0xC0A80199U); /* secondary's subnet, triggers forwarding */ + /* Fill the 20 option bytes (IP header offsets 20..39) with NOPs so the + * checksum covers a well-formed options area. */ + memset(((uint8_t *)ip) + ETH_HEADER_LEN + IP_HEADER_LEN, 0x01, 20); + fix_ip_checksum_with_hlen(ip, 40); + + /* sizeof(ip_buf) == ETH_HEADER_LEN + ip_hlen, exactly 8 bytes short of + * what wolfIP_send_ttl_exceeded would read. */ + wolfIP_recv_on(&s, TEST_PRIMARY_IF, ip, (uint32_t)sizeof(ip_buf)); + ck_assert_uint_eq(last_frame_sent_size, 0U); +} +END_TEST #endif START_TEST(test_arp_request_filter_drop) diff --git a/src/wolfip.c b/src/wolfip.c index 0dfdae0..c0b458f 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -7107,8 +7107,17 @@ static void icmp_input(struct wolfIP *s, unsigned int if_idx, struct wolfIP_ip_p } if (!DHCP_IS_RUNNING(s) && (icmp->type == ICMP_ECHO_REQUEST)) { ip4 dst = ee32(ip->dst); + int dst_match = 0; if (wolfIP_ip_is_broadcast(s, dst) || wolfIP_ip_is_multicast(dst)) return; + /* RFC 1122 §3.2.2.6: only reply to echo requests destined to one of + * our configured local IPs. Without this, an L2-adjacent attacker + * can address a frame to our MAC with arbitrary ip.src/ip.dst and + * have us emit an echo reply with attacker-chosen ip.src — the + * destination-matching mirrors what tcp_input and udp_try_recv do. */ + (void)wolfIP_if_for_local_ip(s, dst, &dst_match); + if (!dst_match) + return; icmp->type = ICMP_ECHO_REPLY; /* Recompute full ICMP checksum for portability */ icmp->csum = 0; @@ -8330,14 +8339,23 @@ static inline void ip_recv(struct wolfIP *s, unsigned int if_idx, if (src == IPADDR_ANY && !DHCP_IS_RUNNING(s)) return; } -#if WOLFIP_ENABLE_LOOPBACK + /* RFC 5735 §4 / RFC 6890: 127/8 is host loopback and must not appear + * on the wire. Drop frames arriving on a non-loopback interface whose + * source or destination is in 127/8; the symmetric source check is + * what stops an off-link attacker from forging ip.src=127.0.0.1 to + * impersonate locally-originated traffic to higher-layer code. */ if (!wolfIP_is_loopback_if(if_idx)) { ip4 dest = ee32(ip->dst); - if ((dest & WOLFIP_LOOPBACK_MASK) == (WOLFIP_LOOPBACK_IP & WOLFIP_LOOPBACK_MASK)) { + ip4 src = ee32(ip->src); + if ((dest & WOLFIP_LOOPBACK_MASK) == + (WOLFIP_LOOPBACK_IP & WOLFIP_LOOPBACK_MASK)) { + return; + } + if ((src & WOLFIP_LOOPBACK_MASK) == + (WOLFIP_LOOPBACK_IP & WOLFIP_LOOPBACK_MASK)) { return; } } -#endif if (wolfIP_filter_notify_ip(WOLFIP_FILT_RECEIVING, s, if_idx, ip, len) != 0) return; #if WOLFIP_RAWSOCKETS @@ -8400,8 +8418,12 @@ static inline void ip_recv(struct wolfIP *s, unsigned int if_idx, int broadcast = 0; if (ip->ttl <= 1) { - /* Need at least Ethernet header + 28 bytes of original packet. */ - if (len < (uint32_t)(ETH_HEADER_LEN + TTL_EXCEEDED_ORIG_PACKET_SIZE_DEFAULT)) + /* wolfIP_send_ttl_exceeded copies orig_ihl + 8 bytes from + * offset ETH_HEADER_LEN, so the frame must hold the full + * IP header plus 8 transport bytes; the ip_hlen >= 20 + * floor at line 8313 keeps this >= the historical + * ETH_HEADER_LEN + 28 minimum for IHL=5 frames. */ + if (len < (uint32_t)(ETH_HEADER_LEN + ip_hlen + 8)) return; wolfIP_send_ttl_exceeded(s, if_idx, ip); return;