diff --git a/config.h b/config.h index cb48afb..e37c1f4 100644 --- a/config.h +++ b/config.h @@ -7,6 +7,12 @@ #define ETHERNET #define LINK_MTU 1536 +#ifndef LINK_MTU_MIN +#define LINK_MTU_MIN 64U +#endif +#if LINK_MTU < LINK_MTU_MIN +#error "LINK_MTU must be greater than or equal to LINK_MTU_MIN" +#endif #define MAX_TCPSOCKETS 4 #define MAX_UDPSOCKETS 2 diff --git a/docs/API.md b/docs/API.md index a760971..52f47d3 100644 --- a/docs/API.md +++ b/docs/API.md @@ -27,6 +27,7 @@ struct wolfIP_ll_dev { uint8_t mac[6]; // Device MAC address char ifname[16]; // Interface name uint8_t non_ethernet; // L3-only link (no Ethernet header/ARP when set) + uint32_t mtu; // Optional internal frame budget, defaults to LINK_MTU int (*poll)(struct wolfIP_ll_dev *ll, void *buf, uint32_t len); // Receive function int (*send)(struct wolfIP_ll_dev *ll, void *buf, uint32_t len); // Transmit function }; @@ -34,6 +35,7 @@ struct wolfIP_ll_dev { wolfIP maintains an array of these descriptors sized by `WOLFIP_MAX_INTERFACES` (default `1`). Call `wolfIP_getdev_ex()` to access a specific slot; the legacy `wolfIP_getdev()` helper targets the first hardware slot (index `0` normally, or `1` when the optional loopback interface is enabled). When `non_ethernet` is set, the interface is treated as L3-only point-to-point: the stack skips ARP/neighbor resolution, omits Ethernet headers on transmit, and expects receive buffers to begin at the IP header. +The `mtu` field still describes wolfIP's internal frame budget including Ethernet headroom, so on non-Ethernet links the payload passed to `ll->send()` is effectively capped at `mtu - ETH_HEADER_LEN` on Ethernet-enabled builds. ### IP Configuration ```c @@ -203,8 +205,13 @@ Per-interface versions of the IP configuration helpers. The legacy functions tar ```c struct wolfIP_ll_dev *wolfIP_getdev(struct wolfIP *s); struct wolfIP_ll_dev *wolfIP_getdev_ex(struct wolfIP *s, unsigned int if_idx); +int wolfIP_mtu_set(struct wolfIP *s, unsigned int if_idx, uint32_t mtu); +int wolfIP_mtu_get(struct wolfIP *s, unsigned int if_idx, uint32_t *mtu); ``` Access the link-layer descriptor(s) that should be wired to hardware drivers. `_ex` returns `NULL` if `if_idx` exceeds `WOLFIP_MAX_INTERFACES`. +`wolfIP_mtu_set()` updates the effective per-interface MTU, treating `0` as the default `LINK_MTU` and clamping to `[LINK_MTU_MIN, LINK_MTU]`. `wolfIP_mtu_get()` returns the effective MTU currently used by the stack. +For `non_ethernet` devices this value remains the internal frame budget; the maximum IP bytes handed to the driver are reduced by `ETH_HEADER_LEN` when Ethernet support is compiled in. +- Returns: `wolfIP_getdev()`/`wolfIP_getdev_ex()` return a pointer to the link-layer descriptor or `NULL` on invalid interface index; `wolfIP_mtu_set()` returns `0` on success or a negative error code on failure; `wolfIP_mtu_get()` returns `0` on success or a negative error code on failure and stores the effective MTU in `*mtu`. ## DHCP Client Functions diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 1c152d0..24206a5 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -1565,6 +1565,45 @@ START_TEST(test_udp_sendto_and_recvfrom) } END_TEST +START_TEST(test_udp_sendto_respects_mtu_api) +{ + struct wolfIP s; + int sd; + struct wolfIP_sockaddr_in sin; + uint8_t small_payload[38] = {0}; + uint8_t large_payload[39] = {0}; + struct tsocket *ts; + uint32_t mtu = 0; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + ck_assert_int_eq(wolfIP_mtu_set(&s, TEST_PRIMARY_IF, 80U), 0); + ck_assert_int_eq(wolfIP_mtu_get(&s, TEST_PRIMARY_IF, &mtu), 0); + ck_assert_uint_eq(mtu, 80U); + + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(sd, 0); + ts = &s.udpsockets[SOCKET_UNMARK(sd)]; + fifo_init(&ts->sock.udp.txbuf, ts->txmem, TXBUF_SIZE); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(5000); + sin.sin_addr.s_addr = ee32(0x0A000002U); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, sd, small_payload, sizeof(small_payload), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(small_payload)); + ck_assert_ptr_nonnull(fifo_peek(&ts->sock.udp.txbuf)); + + fifo_init(&ts->sock.udp.txbuf, ts->txmem, TXBUF_SIZE); + ck_assert_int_eq(wolfIP_sock_sendto(&s, sd, large_payload, sizeof(large_payload), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); + ck_assert_ptr_null(fifo_peek(&ts->sock.udp.txbuf)); +} +END_TEST + START_TEST(test_udp_recvfrom_sets_remote_ip) { struct wolfIP s; @@ -9194,7 +9233,7 @@ START_TEST(test_arp_queue_packet_uses_empty_slot) } END_TEST -START_TEST(test_arp_queue_packet_truncates_len) +START_TEST(test_arp_queue_packet_drops_oversize_len) { struct wolfIP s; uint8_t ip_buf[LINK_MTU]; @@ -9206,7 +9245,8 @@ START_TEST(test_arp_queue_packet_truncates_len) memset(ip_buf, 0, sizeof(ip_buf)); arp_queue_packet(&s, TEST_PRIMARY_IF, 0x0A0000A3U, ip, len); - ck_assert_uint_eq(s.arp_pending[0].len, LINK_MTU); + ck_assert_uint_eq(s.arp_pending[0].len, 0U); + ck_assert_uint_eq(s.arp_pending[0].dest, IPADDR_ANY); } END_TEST @@ -10505,6 +10545,24 @@ START_TEST(test_ll_send_frame_non_ethernet_short_len) } END_TEST +START_TEST(test_ll_send_frame_drops_oversize) +{ + struct wolfIP s; + uint8_t buf[ETH_HEADER_LEN + IP_HEADER_LEN + 32]; + + wolfIP_init(&s); + mock_link_init(&s); + ck_assert_int_eq(wolfIP_mtu_set(&s, TEST_PRIMARY_IF, + (uint32_t)(sizeof(buf) - 1U)), 0); + + memset(buf, 0, sizeof(buf)); + last_frame_sent_size = 0; + + wolfIP_ll_send_frame(&s, TEST_PRIMARY_IF, buf, (uint32_t)sizeof(buf)); + ck_assert_uint_eq(last_frame_sent_size, 0U); +} +END_TEST + START_TEST(test_ll_helpers_invalid_inputs) { struct wolfIP s; @@ -13764,6 +13822,89 @@ START_TEST(test_tcp_input_synack_negotiates_peer_mss) } END_TEST +START_TEST(test_tcp_connect_syn_advertises_interface_mss) +{ + struct wolfIP s; + int tcp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + struct pkt_desc *desc; + struct wolfIP_tcp_seg *syn; + struct tcp_parsed_opts po; + uint16_t expected_mss; + + wolfIP_init(&s); + mock_link_init(&s); + ck_assert_int_eq(wolfIP_mtu_set(&s, TEST_PRIMARY_IF, 640U), 0); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(5004); + sin.sin_addr.s_addr = ee32(0x0A000002U); + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EAGAIN); + + desc = fifo_peek(&ts->sock.tcp.txbuf); + ck_assert_ptr_nonnull(desc); + syn = (struct wolfIP_tcp_seg *)(ts->txmem + desc->pos + sizeof(*desc)); + ck_assert_uint_eq((uint32_t)(syn->flags & TCP_FLAG_SYN), TCP_FLAG_SYN); + + memset(&po, 0, sizeof(po)); + tcp_parse_options(syn, desc->len, &po); + expected_mss = (uint16_t)(640U - ETH_HEADER_LEN - IP_HEADER_LEN - TCP_HEADER_LEN); + ck_assert_int_eq(po.mss_found, 1); + ck_assert_uint_eq(po.mss, expected_mss); +} +END_TEST + +START_TEST(test_tcp_connect_syn_limits_options_to_small_mtu) +{ + struct wolfIP s; + int tcp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + struct pkt_desc *desc; + struct wolfIP_tcp_seg *syn; + struct tcp_parsed_opts po; + uint32_t mtu = 0; + + wolfIP_init(&s); + mock_link_init(&s); + ck_assert_int_eq(wolfIP_mtu_set(&s, TEST_PRIMARY_IF, 64U), 0); + ck_assert_int_eq(wolfIP_mtu_get(&s, TEST_PRIMARY_IF, &mtu), 0); + ck_assert_uint_eq(mtu, 64U); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(5006); + sin.sin_addr.s_addr = ee32(0x0A000002U); + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EAGAIN); + + desc = fifo_peek(&ts->sock.tcp.txbuf); + ck_assert_ptr_nonnull(desc); + ck_assert_uint_le(desc->len, mtu); + syn = (struct wolfIP_tcp_seg *)(ts->txmem + desc->pos + sizeof(*desc)); + + memset(&po, 0, sizeof(po)); + tcp_parse_options(syn, desc->len, &po); + ck_assert_int_eq(po.mss_found, 1); + ck_assert_int_eq(po.ws_found, 1); + ck_assert_int_eq(po.sack_permitted, 0); + ck_assert_int_eq(po.ts_found, 0); +} +END_TEST + START_TEST(test_sock_sendto_tcp_respects_negotiated_peer_mss) { struct wolfIP s; @@ -13924,6 +14065,88 @@ START_TEST(test_sock_sendto_tcp_defaults_to_rfc_mss_when_unset_by_peer) } END_TEST +START_TEST(test_sock_sendto_tcp_respects_interface_mtu) +{ + struct wolfIP s; + int tcp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + struct wolfIP_tcp_seg synack; + uint8_t payload[700]; + int ret; + struct pkt_desc *desc; + uint32_t max_payload; + int seg_count = 0; + + wolfIP_init(&s); + mock_link_init(&s); + ck_assert_int_eq(wolfIP_mtu_set(&s, TEST_PRIMARY_IF, 320U), 0); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; + ts->src_port = 23459; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(5005); + sin.sin_addr.s_addr = ee32(0x0A000002U); + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EAGAIN); + ck_assert_int_eq(ts->sock.tcp.state, TCP_SYN_SENT); + + memset(&synack, 0, sizeof(synack)); + synack.ip.ver_ihl = 0x45; + synack.ip.proto = WI_IPPROTO_TCP; + synack.ip.ttl = 64; + synack.ip.src = ee32(0x0A000002U); + synack.ip.dst = ee32(0x0A000001U); + synack.ip.len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN); + synack.src_port = ee16(5005); + synack.dst_port = ee16(ts->src_port); + synack.seq = ee32(100); + synack.ack = ee32(ts->sock.tcp.seq + 1); + synack.hlen = TCP_HEADER_LEN << 2; + synack.flags = (TCP_FLAG_SYN | TCP_FLAG_ACK); + synack.win = ee16(65535); + fix_tcp_checksums(&synack); + + tcp_input(&s, TEST_PRIMARY_IF, &synack, + (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN)); + ck_assert_int_eq(ts->sock.tcp.state, TCP_ESTABLISHED); + + max_payload = wolfIP_socket_tcp_mss(ts); + ck_assert_uint_gt(max_payload, TCP_OPTIONS_LEN); + max_payload -= TCP_OPTIONS_LEN; + + fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); + memset(payload, 0x3C, sizeof(payload)); + ret = wolfIP_sock_sendto(&s, tcp_sd, payload, sizeof(payload), 0, NULL, 0); + ck_assert_int_eq(ret, (int)sizeof(payload)); + + desc = fifo_peek(&ts->sock.tcp.txbuf); + ck_assert_ptr_nonnull(desc); + while (desc != NULL) { + struct wolfIP_tcp_seg *seg; + uint32_t seg_payload; + uint32_t hdr_len; + uint32_t base_len; + + seg = (struct wolfIP_tcp_seg *)(ts->txmem + desc->pos + sizeof(*desc)); + hdr_len = (uint32_t)(seg->hlen >> 2); + base_len = (uint32_t)(sizeof(struct wolfIP_tcp_seg) + (hdr_len - TCP_HEADER_LEN)); + ck_assert_uint_ge(desc->len, base_len); + seg_payload = desc->len - base_len; + ck_assert_uint_le(seg_payload, max_payload); + + seg_count++; + desc = fifo_next(&ts->sock.tcp.txbuf, desc); + } + ck_assert_int_ge(seg_count, 3); +} +END_TEST + START_TEST(test_tcp_input_syn_rcvd_ack_established) { struct wolfIP s; @@ -14346,6 +14569,62 @@ START_TEST(test_tcp_ack_updates_rtt_and_cwnd) } END_TEST +START_TEST(test_tcp_ack_uses_interface_mss_for_cwnd_growth) +{ + struct wolfIP s; + struct tsocket *ts; + uint8_t buf[sizeof(struct wolfIP_tcp_seg) + TCP_OPTION_TS_LEN + 1]; + struct wolfIP_tcp_seg *seg = (struct wolfIP_tcp_seg *)buf; + struct tcp_opt_ts *tsopt; + struct wolfIP_tcp_seg ackseg; + struct pkt_desc *desc; + uint32_t smss; + + wolfIP_init(&s); + mock_link_init(&s); + ck_assert_int_eq(wolfIP_mtu_set(&s, TEST_PRIMARY_IF, 320U), 0); + + ts = &s.tcpsockets[0]; + memset(ts, 0, sizeof(*ts)); + ts->proto = WI_IPPROTO_TCP; + ts->S = &s; + ts->if_idx = TEST_PRIMARY_IF; + ts->sock.tcp.state = TCP_ESTABLISHED; + smss = tcp_cc_mss(ts); + ts->sock.tcp.cwnd = smss; + ts->sock.tcp.ssthresh = smss * 4; + ts->sock.tcp.peer_rwnd = smss * 8; + fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); + s.last_tick = 1000; + + memset(buf, 0, sizeof(buf)); + seg->ip.len = ee16(IP_HEADER_LEN + (TCP_HEADER_LEN + TCP_OPTION_TS_LEN) + 1); + seg->hlen = (TCP_HEADER_LEN + TCP_OPTION_TS_LEN) << 2; + seg->seq = ee32(100); + tsopt = (struct tcp_opt_ts *)seg->data; + tsopt->opt = TCP_OPTION_TS; + tsopt->len = TCP_OPTION_TS_LEN; + tsopt->val = ee32(123); + tsopt->ecr = ee32(990); + + fifo_push(&ts->sock.tcp.txbuf, seg, sizeof(buf)); + desc = fifo_peek(&ts->sock.tcp.txbuf); + ck_assert_ptr_nonnull(desc); + desc->flags |= PKT_FLAG_SENT; + ts->sock.tcp.bytes_in_flight = ts->sock.tcp.cwnd; + ts->sock.tcp.snd_una = 100; + ts->sock.tcp.seq = 100 + smss; + + memset(&ackseg, 0, sizeof(ackseg)); + ackseg.ack = ee32(101); + ackseg.hlen = TCP_HEADER_LEN << 2; + ackseg.flags = TCP_FLAG_ACK; + + tcp_ack(ts, &ackseg); + ck_assert_uint_eq(ts->sock.tcp.cwnd, smss * 2); +} +END_TEST + START_TEST(test_tcp_ack_last_seq_not_last_ack_state) { struct wolfIP s; @@ -17215,9 +17494,11 @@ START_TEST(test_wolfip_getdev_ex_api) wolfIP_init(&s); ll_def = wolfIP_getdev(&s); ck_assert_ptr_nonnull(ll_def); + ck_assert_uint_eq(ll_def->mtu, LINK_MTU); ck_assert_ptr_eq(ll_def, wolfIP_getdev_ex(&s, TEST_PRIMARY_IF)); #if WOLFIP_ENABLE_LOOPBACK ck_assert_ptr_ne(ll_def, wolfIP_getdev_ex(&s, TEST_LOOPBACK_IF)); + ck_assert_uint_eq(wolfIP_getdev_ex(&s, TEST_LOOPBACK_IF)->mtu, LINK_MTU); #endif ck_assert_ptr_null(wolfIP_getdev_ex(&s, WOLFIP_MAX_INTERFACES)); } @@ -17236,6 +17517,53 @@ START_TEST(test_wolfip_ll_at_and_ipconf_at_invalid) } END_TEST +START_TEST(test_wolfip_ll_frame_mtu_enforces_minimum) +{ + struct wolfIP s; + struct wolfIP_ll_dev *ll; + + wolfIP_init(&s); + mock_link_init(&s); + ll = wolfIP_getdev(&s); + ck_assert_ptr_nonnull(ll); + + ck_assert_int_eq(wolfIP_mtu_set(&s, TEST_PRIMARY_IF, LINK_MTU_MIN - 1U), 0); + ck_assert_uint_eq(wolfIP_ll_frame_mtu(ll), LINK_MTU_MIN); +} +END_TEST + +START_TEST(test_wolfip_mtu_set_get_api) +{ + struct wolfIP s; + uint32_t mtu = 0; + + wolfIP_init(&s); + mock_link_init(&s); + + ck_assert_int_eq(wolfIP_mtu_set(NULL, TEST_PRIMARY_IF, 128U), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_mtu_set(&s, WOLFIP_MAX_INTERFACES, 128U), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_mtu_get(NULL, TEST_PRIMARY_IF, &mtu), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_mtu_get(&s, WOLFIP_MAX_INTERFACES, &mtu), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_mtu_get(&s, TEST_PRIMARY_IF, NULL), -WOLFIP_EINVAL); + + ck_assert_int_eq(wolfIP_mtu_set(&s, TEST_PRIMARY_IF, 256U), 0); + ck_assert_int_eq(wolfIP_mtu_get(&s, TEST_PRIMARY_IF, &mtu), 0); + ck_assert_uint_eq(mtu, 256U); + + ck_assert_int_eq(wolfIP_mtu_set(&s, TEST_PRIMARY_IF, LINK_MTU_MIN - 1U), 0); + ck_assert_int_eq(wolfIP_mtu_get(&s, TEST_PRIMARY_IF, &mtu), 0); + ck_assert_uint_eq(mtu, LINK_MTU_MIN); + + ck_assert_int_eq(wolfIP_mtu_set(&s, TEST_PRIMARY_IF, LINK_MTU + 1U), 0); + ck_assert_int_eq(wolfIP_mtu_get(&s, TEST_PRIMARY_IF, &mtu), 0); + ck_assert_uint_eq(mtu, LINK_MTU); + + ck_assert_int_eq(wolfIP_mtu_set(&s, TEST_PRIMARY_IF, 0), 0); + ck_assert_int_eq(wolfIP_mtu_get(&s, TEST_PRIMARY_IF, &mtu), 0); + ck_assert_uint_eq(mtu, LINK_MTU); +} +END_TEST + START_TEST(test_ip_is_local_conf_variants) { struct ipconf conf; @@ -17297,7 +17625,7 @@ START_TEST(test_wolfip_loopback_send_paths) } END_TEST -START_TEST(test_wolfip_loopback_send_truncates) +START_TEST(test_wolfip_loopback_send_drops_oversize) { struct wolfIP s; struct wolfIP_ll_dev *loop; @@ -17308,7 +17636,10 @@ START_TEST(test_wolfip_loopback_send_truncates) ck_assert_ptr_nonnull(loop); memset(frame, 0xAB, sizeof(frame)); - ck_assert_int_eq(wolfIP_loopback_send(loop, frame, (uint32_t)sizeof(frame)), LINK_MTU); + ck_assert_int_eq(wolfIP_loopback_send(loop, frame, (uint32_t)sizeof(frame)), 0); + + ck_assert_int_eq(wolfIP_mtu_set(&s, TEST_LOOPBACK_IF, LINK_MTU_MIN - 1U), 0); + ck_assert_int_eq(wolfIP_loopback_send(loop, frame, (uint32_t)sizeof(frame)), 0); } END_TEST @@ -18297,6 +18628,47 @@ START_TEST(test_icmp_sendto_respects_bound_local_ip_interface) } END_TEST +START_TEST(test_icmp_sendto_respects_mtu_api) +{ + struct wolfIP s; + int sd; + struct wolfIP_sockaddr_in sin; + uint8_t small_payload[30] = {0}; + uint8_t large_payload[31] = {0}; + struct tsocket *ts; + uint32_t mtu = 0; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + ck_assert_int_eq(wolfIP_mtu_set(&s, TEST_PRIMARY_IF, 64U), 0); + ck_assert_int_eq(wolfIP_mtu_get(&s, TEST_PRIMARY_IF, &mtu), 0); + ck_assert_uint_eq(mtu, 64U); + + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); + ck_assert_int_gt(sd, 0); + ts = &s.icmpsockets[SOCKET_UNMARK(sd)]; + fifo_init(&ts->sock.udp.txbuf, ts->txmem, TXBUF_SIZE); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = ee32(0x0A000002U); + + small_payload[0] = ICMP_ECHO_REQUEST; + large_payload[0] = ICMP_ECHO_REQUEST; + + ck_assert_int_eq(wolfIP_sock_sendto(&s, sd, small_payload, sizeof(small_payload), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(small_payload)); + ck_assert_ptr_nonnull(fifo_peek(&ts->sock.udp.txbuf)); + + fifo_init(&ts->sock.udp.txbuf, ts->txmem, TXBUF_SIZE); + ck_assert_int_eq(wolfIP_sock_sendto(&s, sd, large_payload, sizeof(large_payload), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EINVAL); + ck_assert_ptr_null(fifo_peek(&ts->sock.udp.txbuf)); +} +END_TEST + START_TEST(test_regression_snd_una_initialized_on_syn_rcvd) { struct wolfIP s; @@ -18733,12 +19105,14 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_timer_pop_break_when_root_small); tcase_add_test(tc_utils, test_is_timer_expired_skips_zero_head); tcase_add_test(tc_utils, test_wolfip_getdev_ex_api); + tcase_add_test(tc_utils, test_wolfip_ll_frame_mtu_enforces_minimum); + tcase_add_test(tc_utils, test_wolfip_mtu_set_get_api); tcase_add_test(tc_utils, test_wolfip_ll_at_and_ipconf_at_invalid); tcase_add_test(tc_utils, test_ip_is_local_conf_variants); #if WOLFIP_ENABLE_LOOPBACK tcase_add_test(tc_utils, test_wolfip_loopback_defaults); tcase_add_test(tc_utils, test_wolfip_loopback_send_paths); - tcase_add_test(tc_utils, test_wolfip_loopback_send_truncates); + tcase_add_test(tc_utils, test_wolfip_loopback_send_drops_oversize); tcase_add_test(tc_utils, test_wolfip_loopback_send_null_container); #endif tcase_add_test(tc_utils, test_wolfip_ipconfig_ex_per_interface); @@ -18961,6 +19335,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_forward_packet_filter_drop_udp_icmp); tcase_add_test(tc_utils, test_ll_send_frame_non_ethernet_strips); tcase_add_test(tc_utils, test_ll_send_frame_non_ethernet_short_len); + tcase_add_test(tc_utils, test_ll_send_frame_drops_oversize); tcase_add_test(tc_utils, test_ll_helpers_invalid_inputs); tcase_add_test(tc_utils, test_non_ethernet_recv_oversize_dropped); #endif @@ -19042,6 +19417,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_tcp_ack_duplicate_discards_zero_len_segment); tcase_add_test(tc_utils, test_tcp_ack_cwnd_count_wrap); tcase_add_test(tc_utils, test_tcp_ack_updates_rtt_and_cwnd); + tcase_add_test(tc_utils, test_tcp_ack_uses_interface_mss_for_cwnd_growth); tcase_add_test(tc_utils, test_tcp_ack_last_seq_not_last_ack_state); tcase_add_test(tc_utils, test_tcp_ack_no_progress_when_ack_far_ahead); tcase_add_test(tc_utils, test_tcp_ack_coarse_rtt_sets_writable); @@ -19054,8 +19430,11 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_tcp_ack_inflight_deflate_sets_writable_without_acked_desc); tcase_add_test(tc_utils, test_tcp_input_peer_rwnd_growth_sets_writable); tcase_add_test(tc_utils, test_tcp_input_synack_negotiates_peer_mss); + tcase_add_test(tc_utils, test_tcp_connect_syn_advertises_interface_mss); + tcase_add_test(tc_utils, test_tcp_connect_syn_limits_options_to_small_mtu); tcase_add_test(tc_utils, test_sock_sendto_tcp_respects_negotiated_peer_mss); tcase_add_test(tc_utils, test_sock_sendto_tcp_defaults_to_rfc_mss_when_unset_by_peer); + tcase_add_test(tc_utils, test_sock_sendto_tcp_respects_interface_mtu); tcase_add_test(tc_utils, test_tcp_recv_queues_payload_and_advances_ack); tcase_add_test(tc_utils, test_tcp_recv_wrong_state_does_nothing); tcase_add_test(tc_utils, test_tcp_recv_ack_mismatch_does_nothing); @@ -19072,6 +19451,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_tcp_build_ack_options_does_not_write_past_returned_len); tcase_add_test(tc_utils, test_tcp_build_ack_options_omits_ts_when_not_negotiated); tcase_add_test(tc_utils, test_icmp_sendto_respects_bound_local_ip_interface); + tcase_add_test(tc_utils, test_icmp_sendto_respects_mtu_api); tcase_add_test(tc_utils, test_tcp_sort_sack_blocks_swaps_out_of_order); tcase_add_test(tc_utils, test_tcp_sort_sack_blocks_wrap_order); tcase_add_test(tc_utils, test_tcp_merge_sack_blocks_adjacent_and_disjoint); @@ -19155,7 +19535,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_proto, test_arp_queue_packet_reuses_existing_slot); tcase_add_test(tc_proto, test_arp_queue_packet_same_dest_different_if); tcase_add_test(tc_proto, test_arp_queue_packet_uses_empty_slot); - tcase_add_test(tc_proto, test_arp_queue_packet_truncates_len); + tcase_add_test(tc_proto, test_arp_queue_packet_drops_oversize_len); tcase_add_test(tc_proto, test_arp_queue_packet_slot_fallback_zero); tcase_add_test(tc_proto, test_arp_flush_pending_no_neighbor); tcase_add_test(tc_proto, test_arp_flush_pending_len_zero_clears); @@ -19231,6 +19611,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_proto, test_icmp_input_echo_request_eth_filter_drop); tcase_add_test(tc_proto, test_icmp_input_filter_drop_receiving); 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); tcase_add_test(tc_proto, test_udp_recvfrom_null_src_addr_len); tcase_add_test(tc_proto, test_udp_recvfrom_preserves_remote_ip); diff --git a/src/wolfip.c b/src/wolfip.c index e59bef8..482fc79 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -32,6 +32,10 @@ #include "wolfip.h" #include "config.h" +#ifndef LINK_MTU_MIN +#define LINK_MTU_MIN 64U +#endif + #if WOLFIP_ENABLE_LOOPBACK #define WOLFIP_LOOPBACK_IF_IDX 0U #define WOLFIP_PRIMARY_IF_IDX 1U @@ -110,8 +114,13 @@ struct wolfIP_icmp_packet; #define NO_TIMER 0 -#define WI_IP_MTU 1500 -#define TCP_MSS (WI_IP_MTU - (IP_HEADER_LEN + TCP_HEADER_LEN)) +#define IP_MTU_MAX 1500U +#define WI_IP_STD_MTU IP_MTU_MAX +#define WI_IP_MTU WI_IP_STD_MTU +/* Maximum MSS based on standard MTU; prefer per-socket MSS where possible. */ +#define TCP_MSS_MAX (WI_IP_STD_MTU - (IP_HEADER_LEN + TCP_HEADER_LEN)) +/* Compatibility alias for legacy fixed-size uses. */ +#define TCP_MSS TCP_MSS_MAX #define TCP_DEFAULT_MSS 536U #define TCP_CTRL_RTO_MAXRTX 6U #define TCP_RTO_MAX_BACKOFF 15U /* Max retries before closing; also clamps shift */ @@ -677,7 +686,7 @@ struct tcp_sack_block { struct tcp_ooo_seg { uint32_t seq, len; uint8_t used; - uint8_t data[TCP_MSS]; + uint8_t data[TCP_MSS_MAX]; }; /* UDP datagram */ @@ -1090,6 +1099,8 @@ static void tcp_ctrl_rto_start(struct tsocket *t, uint64_t now); static void tcp_ctrl_rto_stop(struct tsocket *t); static int tcp_ctrl_state_needs_rto(const struct tsocket *t); static int tcp_has_pending_unsent_payload(struct tsocket *t); +static inline struct wolfIP_ll_dev *wolfIP_ll_at(struct wolfIP *s, unsigned int if_idx); +static inline unsigned int wolfIP_socket_if_idx(const struct tsocket *t); #ifdef ETHERNET struct PACKED arp_packet { @@ -1217,9 +1228,75 @@ static inline int tx_has_writable_space(const struct tsocket *t) return 0; } +/* Effective link-layer frame budget after applying default/min/max clamping. */ +static inline uint32_t wolfIP_ll_frame_mtu(const struct wolfIP_ll_dev *ll) +{ + uint32_t mtu; + + if (!ll || ll->mtu == 0) + return LINK_MTU; + mtu = ll->mtu; + if (mtu > LINK_MTU) + mtu = LINK_MTU; + if (mtu < LINK_MTU_MIN) + mtu = LINK_MTU_MIN; + return mtu; +} + +/* Per-interface wrapper around the clamped link-layer frame MTU helper. */ +static inline uint32_t wolfIP_frame_mtu(struct wolfIP *s, unsigned int if_idx) +{ + return wolfIP_ll_frame_mtu(wolfIP_ll_at(s, if_idx)); +} + +/* IP payload MTU derived from the frame budget after removing link overhead. */ +static inline uint32_t wolfIP_ip_mtu(struct wolfIP *s, unsigned int if_idx) +{ + uint32_t mtu = wolfIP_frame_mtu(s, if_idx); + + if (mtu <= ETH_HEADER_LEN) + return 0; + mtu -= ETH_HEADER_LEN; + /* Frame MTU may exceed the IPv4 payload maximum (e.g. 1536-byte link + * frames), but IP payload MTU remains capped at the standard 1500 bytes. */ + if (mtu > IP_MTU_MAX) + mtu = IP_MTU_MAX; + return mtu; +} + +static inline uint32_t wolfIP_socket_ip_mtu(const struct tsocket *t) +{ + if (!t || !t->S) + return 0; + return wolfIP_ip_mtu(t->S, wolfIP_socket_if_idx(t)); +} + +static inline uint32_t wolfIP_socket_tcp_mss(const struct tsocket *t) +{ + uint32_t ip_mtu = wolfIP_socket_ip_mtu(t); + + if (ip_mtu <= (IP_HEADER_LEN + TCP_HEADER_LEN)) + return 0; + return ip_mtu - (IP_HEADER_LEN + TCP_HEADER_LEN); +} + +static inline uint32_t tcp_cc_mss(const struct tsocket *t) +{ + uint32_t mss = t ? wolfIP_socket_tcp_mss(t) : TCP_MSS_MAX; + + if (mss == 0) + mss = TCP_MSS_MAX; + return mss; +} + static inline uint32_t tcp_tx_payload_cap(const struct tsocket *t) { - uint32_t cap = (uint32_t)(TCP_MSS - TCP_OPTIONS_LEN); + uint32_t cap = wolfIP_socket_tcp_mss(t); + + if (cap > TCP_OPTIONS_LEN) + cap -= TCP_OPTIONS_LEN; + else + cap = 0; if (t && t->proto == WI_IPPROTO_TCP) { uint32_t peer_mss = (uint32_t)t->sock.tcp.peer_mss; @@ -1241,8 +1318,8 @@ static int wolfIP_loopback_send(struct wolfIP_ll_dev *ll, void *buf, uint32_t le s = WOLFIP_CONTAINER_OF(ll, struct wolfIP, ll_dev); if (!s) return -1; - if (copy > LINK_MTU) - copy = LINK_MTU; + if (copy > wolfIP_ll_frame_mtu(ll)) + return 0; memcpy(frame, buf, copy); wolfIP_recv_on(s, WOLFIP_LOOPBACK_IF_IDX, frame, copy); return (int)copy; @@ -1269,8 +1346,13 @@ static inline void wolfIP_ll_send_frame(struct wolfIP *s, unsigned int if_idx, void *buf, uint32_t len) { struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, if_idx); + uint32_t frame_mtu; + if (!ll || !ll->send) return; + frame_mtu = wolfIP_ll_frame_mtu(ll); + if (len > frame_mtu) + return; if (ll->non_ethernet) { if (len <= ETH_HEADER_LEN) return; @@ -1751,11 +1833,11 @@ static void icmp_try_recv(struct wolfIP *s, unsigned int if_idx, } /* TCP */ -static uint32_t tcp_initial_cwnd(uint32_t peer_rwnd) +static uint32_t tcp_initial_cwnd(uint32_t peer_rwnd, uint32_t smss) { uint32_t cwnd = peer_rwnd / 2U; uint32_t tx_half = TXBUF_SIZE / 2U; - uint32_t min_cwnd = 2U * TCP_MSS; + uint32_t min_cwnd = 2U * smss; if (cwnd > tx_half) cwnd = tx_half; @@ -1797,7 +1879,7 @@ static struct tsocket *tcp_new_socket(struct wolfIP *s) t->sock.tcp.ctrl_rto_active = 0; t->sock.tcp.last_early_rexmit_ack = 0; t->sock.tcp.peer_rwnd = 0xFFFF; - t->sock.tcp.cwnd = tcp_initial_cwnd(t->sock.tcp.peer_rwnd); + t->sock.tcp.cwnd = tcp_initial_cwnd(t->sock.tcp.peer_rwnd, tcp_cc_mss(t)); t->sock.tcp.ssthresh = tcp_initial_ssthresh(t->sock.tcp.peer_rwnd); t->sock.tcp.peer_mss = TCP_DEFAULT_MSS; t->sock.tcp.snd_wscale = 0; @@ -2009,7 +2091,7 @@ static int tcp_store_ooo_segment(struct tsocket *t, const uint8_t *data, * - cache full: reject (caller still ACKs; peer will retransmit) * * SACK block generation is rebuilt from cache state after each update. */ - if (len == 0 || len > TCP_MSS) + if (len == 0 || len > TCP_MSS_MAX) return -1; for (i = 0; i < TCP_OOO_MAX_SEGS; i++) { if (!t->sock.tcp.ooo[i].used) { @@ -2190,6 +2272,8 @@ static void tcp_send_syn(struct tsocket *t, uint8_t flags) uint8_t include_sack = 0; uint8_t include_ts = 0; uint8_t opt_len = 0; + uint8_t max_opt_len = 0; + uint32_t ip_mtu = wolfIP_socket_ip_mtu(t); tcp = (struct wolfIP_tcp_seg *)buffer; memset(tcp, 0, sizeof(buffer)); if (flags & TCP_FLAG_SYN) { @@ -2205,6 +2289,12 @@ static void tcp_send_syn(struct tsocket *t, uint8_t flags) include_ts = t->sock.tcp.ts_offer; } } + if (ip_mtu > (IP_HEADER_LEN + TCP_HEADER_LEN)) { + uint32_t opt_budget = ip_mtu - (IP_HEADER_LEN + TCP_HEADER_LEN); + if (opt_budget > TCP_MAX_OPTIONS_LEN) + opt_budget = TCP_MAX_OPTIONS_LEN; + max_opt_len = (uint8_t)opt_budget; + } tcp->src_port = ee16(t->src_port); tcp->dst_port = ee16(t->dst_port); tcp->seq = ee32(t->sock.tcp.seq); @@ -2214,24 +2304,16 @@ static void tcp_send_syn(struct tsocket *t, uint8_t flags) tcp->csum = 0; tcp->urg = 0; opt = tcp->data; - if (include_ts) { - ts = (struct tcp_opt_ts *)opt; - ts->opt = TCP_OPTION_TS; - ts->len = TCP_OPTION_TS_LEN; - ts->val = ee32(t->S->last_tick & 0xFFFFFFFFU); - ts->ecr = t->sock.tcp.last_ts; - ts->pad = TCP_OPTION_NOP; - ts->eoo = TCP_OPTION_NOP; - opt += sizeof(*ts); - opt_len += sizeof(*ts); - } - mss = (struct tcp_opt_mss *)opt; - mss->opt = TCP_OPTION_MSS; - mss->len = TCP_OPTION_MSS_LEN; - mss->mss = ee16(TCP_MSS); - opt += sizeof(*mss); - opt_len += sizeof(*mss); - if (include_ws) { + if (max_opt_len >= sizeof(*mss)) { + mss = (struct tcp_opt_mss *)opt; + mss->opt = TCP_OPTION_MSS; + mss->len = TCP_OPTION_MSS_LEN; + mss->mss = ee16((uint16_t)wolfIP_socket_tcp_mss(t)); + opt += sizeof(*mss); + opt_len += sizeof(*mss); + } + if (include_ws && + ((uint8_t)((opt_len + sizeof(*ws) + 3U) & ~3U) <= max_opt_len)) { ws = (struct tcp_opt_ws *)opt; ws->opt = TCP_OPTION_WS; ws->len = TCP_OPTION_WS_LEN; @@ -2239,12 +2321,24 @@ static void tcp_send_syn(struct tsocket *t, uint8_t flags) opt += sizeof(*ws); opt_len += sizeof(*ws); } - if (include_sack) { + if (include_sack && + ((uint8_t)((opt_len + TCP_OPTION_SACK_PERMITTED_LEN + 3U) & ~3U) <= max_opt_len)) { *opt++ = TCP_OPTION_SACK_PERMITTED; *opt++ = TCP_OPTION_SACK_PERMITTED_LEN; opt_len += TCP_OPTION_SACK_PERMITTED_LEN; } - while ((opt_len % 4) != 0 && opt_len < TCP_MAX_OPTIONS_LEN) { + if (include_ts && (uint8_t)(opt_len + sizeof(*ts)) <= max_opt_len) { + ts = (struct tcp_opt_ts *)opt; + ts->opt = TCP_OPTION_TS; + ts->len = TCP_OPTION_TS_LEN; + ts->val = ee32(t->S->last_tick & 0xFFFFFFFFU); + ts->ecr = t->sock.tcp.last_ts; + ts->pad = TCP_OPTION_NOP; + ts->eoo = TCP_OPTION_NOP; + opt += sizeof(*ts); + opt_len += sizeof(*ts); + } + while ((opt_len % 4) != 0 && opt_len < max_opt_len) { *opt++ = TCP_OPTION_NOP; opt_len++; } @@ -3122,16 +3216,19 @@ static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) } /* Grow cwnd only on forward ACK progress (never on duplicate ACKs), * and only if we were cwnd-limited. */ - if (ack_advanced && - ((t->sock.tcp.cwnd <= inflight_pre + TCP_MSS) || - (t->sock.tcp.cwnd <= 2 * TCP_MSS))) { - if (t->sock.tcp.cwnd < t->sock.tcp.ssthresh) { - t->sock.tcp.cwnd += TCP_MSS; - } else { - t->sock.tcp.cwnd_count += TCP_MSS; - if (t->sock.tcp.cwnd_count >= t->sock.tcp.cwnd) { - t->sock.tcp.cwnd_count -= t->sock.tcp.cwnd; - t->sock.tcp.cwnd += TCP_MSS; + { + uint32_t smss = tcp_cc_mss(t); + if (ack_advanced && + ((t->sock.tcp.cwnd <= inflight_pre + smss) || + (t->sock.tcp.cwnd <= 2 * smss))) { + if (t->sock.tcp.cwnd < t->sock.tcp.ssthresh) { + t->sock.tcp.cwnd += smss; + } else { + t->sock.tcp.cwnd_count += smss; + if (t->sock.tcp.cwnd_count >= t->sock.tcp.cwnd) { + t->sock.tcp.cwnd_count -= t->sock.tcp.cwnd; + t->sock.tcp.cwnd += smss; + } } } } @@ -3159,11 +3256,14 @@ static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) } if (t->sock.tcp.dup_acks < 3) return; - t->sock.tcp.ssthresh = t->sock.tcp.cwnd / 2; - if (t->sock.tcp.ssthresh < 2 * TCP_MSS) { - t->sock.tcp.ssthresh = 2 * TCP_MSS; + { + uint32_t smss = tcp_cc_mss(t); + t->sock.tcp.ssthresh = t->sock.tcp.cwnd / 2; + if (t->sock.tcp.ssthresh < 2 * smss) { + t->sock.tcp.ssthresh = 2 * smss; + } + t->sock.tcp.cwnd = t->sock.tcp.ssthresh + smss; } - t->sock.tcp.cwnd = t->sock.tcp.ssthresh + TCP_MSS; t->sock.tcp.cwnd_count = 0; (void)tcp_mark_unsacked_for_retransmit(t, ack); } @@ -3373,7 +3473,7 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, t->sock.tcp.ack = tcp_seq_inc(ee32(tcp->seq), 1); t->sock.tcp.seq = ee32(tcp->ack); t->sock.tcp.snd_una = t->sock.tcp.seq; - t->sock.tcp.cwnd = tcp_initial_cwnd(t->sock.tcp.peer_rwnd); + t->sock.tcp.cwnd = tcp_initial_cwnd(t->sock.tcp.peer_rwnd, tcp_cc_mss(t)); t->sock.tcp.ssthresh = tcp_initial_ssthresh(t->sock.tcp.peer_rwnd); if (tx_has_writable_space(t)) t->events |= CB_EVENT_WRITABLE; @@ -3397,7 +3497,7 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, t->sock.tcp.ack = ee32(tcp->seq); t->sock.tcp.seq = ee32(tcp->ack); t->sock.tcp.snd_una = t->sock.tcp.seq; - t->sock.tcp.cwnd = tcp_initial_cwnd(t->sock.tcp.peer_rwnd); + t->sock.tcp.cwnd = tcp_initial_cwnd(t->sock.tcp.peer_rwnd, tcp_cc_mss(t)); t->sock.tcp.ssthresh = tcp_initial_ssthresh(t->sock.tcp.peer_rwnd); if (tx_has_writable_space(t)) t->events |= CB_EVENT_WRITABLE; @@ -3598,10 +3698,13 @@ static void tcp_rto_cb(void *arg) return; } ts->sock.tcp.rto_backoff++; - ts->sock.tcp.cwnd = TCP_MSS; - ts->sock.tcp.ssthresh = prev_in_flight / 2; - if (ts->sock.tcp.ssthresh < (2 * TCP_MSS)) - ts->sock.tcp.ssthresh = 2 * TCP_MSS; + { + uint32_t smss = tcp_cc_mss(ts); + ts->sock.tcp.cwnd = smss; + ts->sock.tcp.ssthresh = prev_in_flight / 2; + if (ts->sock.tcp.ssthresh < (2 * smss)) + ts->sock.tcp.ssthresh = 2 * smss; + } ptmr = &tmr; ptmr->expires = ts->S->last_tick + (ts->sock.tcp.rto << ts->sock.tcp.rto_backoff); @@ -3917,7 +4020,7 @@ int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *add newts->sock.tcp.snd_una = newts->sock.tcp.seq; newts->sock.tcp.last_ts = ts->sock.tcp.last_ts; newts->sock.tcp.peer_rwnd = ts->sock.tcp.peer_rwnd; - newts->sock.tcp.cwnd = tcp_initial_cwnd(newts->sock.tcp.peer_rwnd); + newts->sock.tcp.cwnd = tcp_initial_cwnd(newts->sock.tcp.peer_rwnd, tcp_cc_mss(newts)); newts->sock.tcp.ssthresh = tcp_initial_ssthresh(newts->sock.tcp.peer_rwnd); newts->sock.tcp.peer_mss = ts->sock.tcp.peer_mss; newts->sock.tcp.snd_wscale = ts->sock.tcp.snd_wscale; @@ -4062,6 +4165,7 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len const struct wolfIP_sockaddr_in *sin = (const struct wolfIP_sockaddr_in *)dest_addr; unsigned int if_idx; struct ipconf *conf; + uint32_t ip_mtu; if (SOCKET_UNMARK(sockfd) >= MAX_UDPSOCKETS) return -WOLFIP_EINVAL; @@ -4077,11 +4181,6 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len } if ((ts->dst_port==0) || (ts->remote_ip==0)) return -1; - if (len > WI_IP_MTU - IP_HEADER_LEN - UDP_HEADER_LEN) - return -1; /* Fragmentation not supported */ - if (fifo_space(&ts->sock.udp.txbuf) < len) { - return -WOLFIP_EAGAIN; - } if (ts->src_port == 0) { ts->src_port = (uint16_t)(wolfIP_getrandom() & 0xFFFF); if (ts->src_port < 1024) @@ -4099,6 +4198,13 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len ts->local_ip = primary->ip; } } + ip_mtu = wolfIP_socket_ip_mtu(ts); + if (ip_mtu <= (IP_HEADER_LEN + UDP_HEADER_LEN) || + len > ip_mtu - IP_HEADER_LEN - UDP_HEADER_LEN) + return -1; /* Fragmentation not supported */ + if (fifo_space(&ts->sock.udp.txbuf) < len) { + return -WOLFIP_EAGAIN; + } udp->src_port = ee16(ts->src_port); udp->dst_port = ee16(ts->dst_port); @@ -4113,6 +4219,7 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len unsigned int if_idx; struct ipconf *conf; uint32_t payload_len = (uint32_t)len; + uint32_t ip_mtu; if (SOCKET_UNMARK(sockfd) >= MAX_ICMPSOCKETS) return -WOLFIP_EINVAL; ts = &s->icmpsockets[SOCKET_UNMARK(sockfd)]; @@ -4123,11 +4230,6 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len } if (ts->remote_ip == 0) return -1; - if (payload_len < ICMP_HEADER_LEN || payload_len > (WI_IP_MTU - IP_HEADER_LEN)) - return -WOLFIP_EINVAL; - if (fifo_space(&ts->sock.udp.txbuf) < payload_len) { - return -WOLFIP_EAGAIN; - } if (ts->src_port == 0) { ts->src_port = (uint16_t)(wolfIP_getrandom() & 0xFFFF); if (ts->src_port == 0) @@ -4154,6 +4256,14 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len } } } + ip_mtu = wolfIP_socket_ip_mtu(ts); + if (ip_mtu <= IP_HEADER_LEN || + payload_len < ICMP_HEADER_LEN || + payload_len > (ip_mtu - IP_HEADER_LEN)) + return -WOLFIP_EINVAL; + if (fifo_space(&ts->sock.udp.txbuf) < payload_len) { + return -WOLFIP_EAGAIN; + } if (sizeof(struct wolfIP_ip_packet) + payload_len > sizeof(frame)) return -WOLFIP_EINVAL; memcpy(&icmp->type, buf, payload_len); @@ -5185,8 +5295,8 @@ static void arp_queue_packet(struct wolfIP *s, unsigned int if_idx, ip4 dest, if (!s || len == 0) return; - if (len > LINK_MTU) - len = LINK_MTU; + if (len > wolfIP_frame_mtu(s, if_idx)) + return; for (i = 0; i < WOLFIP_ARP_PENDING_MAX; i++) { if (s->arp_pending[i].dest == dest && s->arp_pending[i].if_idx == if_idx) { @@ -5223,8 +5333,11 @@ static void arp_flush_pending(struct wolfIP *s, unsigned int if_idx, ip4 ip) pending->dest = IPADDR_ANY; continue; } - if (pending->len > LINK_MTU) - pending->len = LINK_MTU; + if (pending->len > wolfIP_frame_mtu(s, if_idx)) { + pending->dest = IPADDR_ANY; + pending->len = 0; + continue; + } { struct wolfIP_ip_packet *pkt = (struct wolfIP_ip_packet *)pending->frame; @@ -5480,6 +5593,7 @@ void wolfIP_init(struct wolfIP *s) memset(s, 0, sizeof(struct wolfIP)); s->if_count = WOLFIP_MAX_INTERFACES; for (i = 0; i < s->if_count; i++) { + s->ll_dev[i].mtu = LINK_MTU; s->ipconf[i].ll = wolfIP_ll_at(s, i); } #if WOLFIP_ENABLE_LOOPBACK @@ -5514,6 +5628,33 @@ struct wolfIP_ll_dev *wolfIP_getdev_ex(struct wolfIP *s, unsigned int if_idx) return wolfIP_ll_at(s, if_idx); } +int wolfIP_mtu_set(struct wolfIP *s, unsigned int if_idx, uint32_t mtu) +{ + struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, if_idx); + + if (!ll) + return -WOLFIP_EINVAL; + if (mtu == 0) + ll->mtu = LINK_MTU; + else if (mtu < LINK_MTU_MIN) + ll->mtu = LINK_MTU_MIN; + else if (mtu > LINK_MTU) + ll->mtu = LINK_MTU; + else + ll->mtu = mtu; + return 0; +} + +int wolfIP_mtu_get(struct wolfIP *s, unsigned int if_idx, uint32_t *mtu) +{ + struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, if_idx); + + if (!ll || !mtu) + return -WOLFIP_EINVAL; + *mtu = wolfIP_ll_frame_mtu(ll); + return 0; +} + #ifndef WOLFIP_NOSTATIC static struct wolfIP wolfIP_static; void wolfIP_init_static(struct wolfIP **s) @@ -5698,7 +5839,8 @@ void wolfIP_recv(struct wolfIP *s, void *buf, uint32_t len) { if (wolfIP_ll_is_non_ethernet(s, WOLFIP_PRIMARY_IF_IDX)) { uint8_t frame[LINK_MTU]; - if (len > LINK_MTU - ETH_HEADER_LEN) + uint32_t ip_mtu = wolfIP_ip_mtu(s, WOLFIP_PRIMARY_IF_IDX); + if (len > ip_mtu) return; #if ETH_HEADER_LEN > 0 memset(frame, 0, ETH_HEADER_LEN); @@ -5714,7 +5856,8 @@ void wolfIP_recv_ex(struct wolfIP *s, unsigned int if_idx, void *buf, uint32_t l { if (wolfIP_ll_is_non_ethernet(s, if_idx)) { uint8_t frame[LINK_MTU]; - if (len > LINK_MTU - ETH_HEADER_LEN) + uint32_t ip_mtu = wolfIP_ip_mtu(s, if_idx); + if (len > ip_mtu) return; #if ETH_HEADER_LEN > 0 memset(frame, 0, ETH_HEADER_LEN); @@ -6079,14 +6222,18 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) continue; do { if (ll->non_ethernet) { + uint32_t frame_mtu = wolfIP_ll_frame_mtu(ll); + if (frame_mtu <= ETH_HEADER_LEN) + break; #if ETH_HEADER_LEN > 0 memset(buf, 0, ETH_HEADER_LEN); #endif - len = ll->poll(ll, buf + ETH_HEADER_LEN, LINK_MTU - ETH_HEADER_LEN); + len = ll->poll(ll, buf + ETH_HEADER_LEN, + frame_mtu - ETH_HEADER_LEN); if (len > 0) len += ETH_HEADER_LEN; } else { - len = ll->poll(ll, buf, LINK_MTU); + len = ll->poll(ll, buf, wolfIP_ll_frame_mtu(ll)); } if (len > 0) { /* Process packet */ diff --git a/wolfip.h b/wolfip.h index ece4442..60215e3 100644 --- a/wolfip.h +++ b/wolfip.h @@ -97,6 +97,7 @@ struct wolfIP_ll_dev { uint8_t mac[6]; char ifname[16]; uint8_t non_ethernet; + uint32_t mtu; /* poll function */ int (*poll)(struct wolfIP_ll_dev *ll, void *buf, uint32_t len); /* send function */ @@ -253,6 +254,8 @@ void wolfIP_ipconfig_get(struct wolfIP *s, ip4 *ip, ip4 *mask, ip4 *gw); struct wolfIP_ll_dev *wolfIP_getdev(struct wolfIP *s); struct wolfIP_ll_dev *wolfIP_getdev_ex(struct wolfIP *s, unsigned int if_idx); +int wolfIP_mtu_set(struct wolfIP *s, unsigned int if_idx, uint32_t mtu); +int wolfIP_mtu_get(struct wolfIP *s, unsigned int if_idx, uint32_t *mtu); void wolfIP_ipconfig_set_ex(struct wolfIP *s, unsigned int if_idx, ip4 ip, ip4 mask, ip4 gw); void wolfIP_ipconfig_get_ex(struct wolfIP *s, unsigned int if_idx, ip4 *ip, ip4 *mask, ip4 *gw);