Skip to content

Commit 5e08aaf

Browse files
committed
Merge branch 'main' of https://github.com/smoltcp-rs/smoltcp into fix/mismatching-src-ip
2 parents d07c192 + 74bd1e5 commit 5e08aaf

File tree

19 files changed

+820
-152
lines changed

19 files changed

+820
-152
lines changed

.github/workflows/coverage.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ jobs:
1414
- name: Run Coverage
1515
run: ./ci.sh coverage
1616
- name: Upload coverage to Codecov
17-
uses: codecov/codecov-action@v3
17+
uses: codecov/codecov-action@v5
1818
with:
19-
#token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
19+
token: ${{ secrets.CODECOV_TOKEN }}
2020
files: lcov.info
2121
fail_ci_if_error: false

CHANGELOG.md

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,59 @@
1-
# Changelog
1+
# Changelog
22
All notable changes to this project will be documented in this file.
33

44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77
## [Unreleased]
88

9-
### Changed
10-
- iface: The `poll` function now only performs a single cycle of processing sockets ([#954](https://github.com/smoltcp-rs/smoltcp/pull/954))
9+
No unreleased changes yet. Please send PRs!
10+
11+
## [0.12.0] - 2024-11-28
12+
13+
Almost a year in the making, the highlights of the release are the migration to `core::net` IP types, IPv6 multicast, TCP improvements, and many fixes. Smoltcp now connects your gadgets to the Internet better than ever.
14+
15+
- Minimum Supported Rust Version (MSRV) bumped to 1.80.
16+
- iface
17+
- IPv6 multicast ([#914](https://github.com/smoltcp-rs/smoltcp/pull/914), [#976](https://github.com/smoltcp-rs/smoltcp/pull/976), [#988](https://github.com/smoltcp-rs/smoltcp/pull/988), [#1009](https://github.com/smoltcp-rs/smoltcp/pull/1009), [#1012](https://github.com/smoltcp-rs/smoltcp/pull/1012))
18+
- Add `poll_egress()` and `poll_ingress_single()` methods for finer-grained control of what and how many packets are processed. ([#954](https://github.com/smoltcp-rs/smoltcp/pull/954), [#991](https://github.com/smoltcp-rs/smoltcp/pull/991), [#993](https://github.com/smoltcp-rs/smoltcp/pull/993))
19+
- Multicast join/leave no longer requires access to device+timestamp. ([#985](https://github.com/smoltcp-rs/smoltcp/pull/985))
20+
- Reset expiry of entries in the neighbor cache on packet reception ([#966](https://github.com/smoltcp-rs/smoltcp/pull/966))
21+
- Honor `any_ip` for ARP ([#880](https://github.com/smoltcp-rs/smoltcp/pull/880))
22+
- Honor `any_ip` for IPv6 ([#900](https://github.com/smoltcp-rs/smoltcp/pull/900))
23+
- Use own source address for ARP and NDISC Solicitations ([#984](https://github.com/smoltcp-rs/smoltcp/pull/984))
24+
- fix panic when discarding HBH Option with multicast destination address ([#996](https://github.com/smoltcp-rs/smoltcp/pull/996))
25+
- fix panic with 6lowpan frag datagram_size < 40 ([#997](https://github.com/smoltcp-rs/smoltcp/pull/997))
26+
- fix panic if no suitable IPv6 src_addr is found ([#895](https://github.com/smoltcp-rs/smoltcp/pull/895))
27+
- Fix specific length IP packets not being fragmented ([#1008](https://github.com/smoltcp-rs/smoltcp/pull/1008))
28+
- tcp
29+
- Add support for congestion control ([#907](https://github.com/smoltcp-rs/smoltcp/pull/907))
30+
- Add support for simultaneous open ([#1001](https://github.com/smoltcp-rs/smoltcp/pull/1001))
31+
- Add support for Timestamp option ([#939](https://github.com/smoltcp-rs/smoltcp/pull/939))
32+
- Send immediate ACKs after RMSS bytes of data ([#1002](https://github.com/smoltcp-rs/smoltcp/pull/1002))
33+
- Do not ignore FIN if segment is partially outside the window. ([#977](https://github.com/smoltcp-rs/smoltcp/pull/977))
34+
- Correctly set internal sACK flag for client sockets ([#995](https://github.com/smoltcp-rs/smoltcp/pull/995))
35+
- Only reset remote_last_ts if some data is enqueued ([#917](https://github.com/smoltcp-rs/smoltcp/pull/917))
36+
- Don't delay ACKs for significant window updates ([#935](https://github.com/smoltcp-rs/smoltcp/pull/935))
37+
- Add `listen_endpoint` getter ([#1005](https://github.com/smoltcp-rs/smoltcp/pull/1005))
38+
- socket
39+
- UDP,ICMP,raw: Add `send_queue`/`recv_queue` ([#1003](https://github.com/smoltcp-rs/smoltcp/pull/1003))
40+
- ICMP: split ICMPv4/v6 accept and process ([#887](https://github.com/smoltcp-rs/smoltcp/pull/887))
41+
- UDP: Store local and use local address in metadata ([#904](https://github.com/smoltcp-rs/smoltcp/pull/904))
42+
- DNS: fix panic if server list is too long ([#986](https://github.com/smoltcp-rs/smoltcp/pull/986))
43+
- DNS: fix panic if no valid source address is found ([#987](https://github.com/smoltcp-rs/smoltcp/pull/987))
44+
- phy
45+
- Change mutability of `RxToken`'s `consume` argument. ([#924](https://github.com/smoltcp-rs/smoltcp/pull/924))
46+
- Add support for NetBSD ([#883](https://github.com/smoltcp-rs/smoltcp/pull/883))
47+
- Add minimum support for iOS ([#896](https://github.com/smoltcp-rs/smoltcp/pull/896))
48+
- Add BPF support for FreeBSD ([#906](https://github.com/smoltcp-rs/smoltcp/pull/906))
49+
- disable checksums on loopback ([#919](https://github.com/smoltcp-rs/smoltcp/pull/919))
50+
- wire
51+
- Use core::net types for IP addresses. ([#937](https://github.com/smoltcp-rs/smoltcp/pull/937), [#994](https://github.com/smoltcp-rs/smoltcp/pull/994))
52+
- Add missing exports in wire for DNS ([#891](https://github.com/smoltcp-rs/smoltcp/pull/891))
53+
- rename Scope to MulticastScope ([#898](https://github.com/smoltcp-rs/smoltcp/pull/898))
54+
- Re-export `dhcpv4::Flags` and `dhcpv4::OpCode` ([#901](https://github.com/smoltcp-rs/smoltcp/pull/901))
55+
- Make Address:v6() constructor const ([#975](https://github.com/smoltcp-rs/smoltcp/pull/975))
56+
- Ipv6RoutingHeader::clear_reserved: fix offsets for Type 2 routing headers. ([#882](https://github.com/smoltcp-rs/smoltcp/pull/882))
1157

1258
## [0.11.0] - 2023-12-23
1359

@@ -281,7 +327,8 @@ only processed when directed to the 255.255.255.255 address. ([377](https://gith
281327
- Use #[non_exhaustive] for enums and structs ([409](https://github.com/smoltcp-rs/smoltcp/pull/409), [411](https://github.com/smoltcp-rs/smoltcp/pull/411))
282328
- Simplify lifetime parameters of sockets, SocketSet, EthernetInterface ([410](https://github.com/smoltcp-rs/smoltcp/pull/410), [413](https://github.com/smoltcp-rs/smoltcp/pull/413))
283329

284-
[Unreleased]: https://github.com/smoltcp-rs/smoltcp/compare/v0.11.0...HEAD
330+
[Unreleased]: https://github.com/smoltcp-rs/smoltcp/compare/v0.12.0...HEAD
331+
[0.12.0]: https://github.com/smoltcp-rs/smoltcp/compare/v0.11.0...v0.12.0
285332
[0.11.0]: https://github.com/smoltcp-rs/smoltcp/compare/v0.10.0...v0.11.0
286333
[0.10.0]: https://github.com/smoltcp-rs/smoltcp/compare/v0.9.1...v0.10.0
287334
[0.9.1]: https://github.com/smoltcp-rs/smoltcp/compare/v0.9.0...v0.9.1

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "smoltcp"
3-
version = "0.11.0"
3+
version = "0.12.0"
44
edition = "2021"
55
rust-version = "1.80"
66
authors = ["whitequark <whitequark@whitequark.org>"]

src/iface/interface/ipv6.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,16 @@ impl InterfaceInner {
422422
#[cfg(feature = "medium-ip")]
423423
Medium::Ip => None,
424424
},
425+
#[cfg(feature = "multicast")]
426+
Icmpv6Repr::Mld(repr) => match repr {
427+
// [RFC 3810 § 6.2], reception checks
428+
MldRepr::Query { .. }
429+
if ip_repr.hop_limit == 1 && ip_repr.src_addr.is_link_local() =>
430+
{
431+
self.process_mldv2(ip_repr, repr)
432+
}
433+
_ => None,
434+
},
425435

426436
// Don't report an error if a packet with unknown type
427437
// has been handled by an ICMP socket

src/iface/interface/mod.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,12 @@ impl Interface {
361361
pub fn update_ip_addrs<F: FnOnce(&mut Vec<IpCidr, IFACE_MAX_ADDR_COUNT>)>(&mut self, f: F) {
362362
f(&mut self.inner.ip_addrs);
363363
InterfaceInner::flush_neighbor_cache(&mut self.inner);
364-
InterfaceInner::check_ip_addrs(&self.inner.ip_addrs)
364+
InterfaceInner::check_ip_addrs(&self.inner.ip_addrs);
365+
366+
#[cfg(all(feature = "proto-ipv6", feature = "multicast"))]
367+
if self.inner.caps.medium == Medium::Ethernet {
368+
self.update_solicited_node_groups();
369+
}
365370
}
366371

367372
/// Check whether the interface has the given IP address assigned.
@@ -1216,7 +1221,7 @@ impl InterfaceInner {
12161221
#[cfg(feature = "proto-ipv4")]
12171222
IpRepr::Ipv4(repr) => {
12181223
// If we have an IPv4 packet, then we need to check if we need to fragment it.
1219-
if total_ip_len > self.caps.max_transmission_unit {
1224+
if total_ip_len > self.caps.ip_mtu() {
12201225
#[cfg(feature = "proto-ipv4-fragmentation")]
12211226
{
12221227
net_debug!("start fragmentation");

src/iface/interface/multicast.rs

Lines changed: 133 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use core::result::Result;
2-
use heapless::LinearMap;
2+
use heapless::{LinearMap, Vec};
33

4-
#[cfg(feature = "proto-ipv4")]
4+
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
55
use super::{check, IpPayload, Packet};
66
use super::{Interface, InterfaceInner};
7-
use crate::config::IFACE_MAX_MULTICAST_GROUP_COUNT;
7+
use crate::config::{IFACE_MAX_ADDR_COUNT, IFACE_MAX_MULTICAST_GROUP_COUNT};
88
use crate::phy::{Device, PacketMeta};
99
use crate::wire::*;
1010

@@ -34,6 +34,18 @@ pub(crate) enum IgmpReportState {
3434
},
3535
}
3636

37+
#[cfg(feature = "proto-ipv6")]
38+
pub(crate) enum MldReportState {
39+
Inactive,
40+
ToGeneralQuery {
41+
timeout: crate::time::Instant,
42+
},
43+
ToSpecificQuery {
44+
group: Ipv6Address,
45+
timeout: crate::time::Instant,
46+
},
47+
}
48+
3749
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3850
enum GroupState {
3951
/// Joining group, we have to send the join packet.
@@ -49,6 +61,8 @@ pub(crate) struct State {
4961
/// When to report for (all or) the next multicast group membership via IGMP
5062
#[cfg(feature = "proto-ipv4")]
5163
igmp_report_state: IgmpReportState,
64+
#[cfg(feature = "proto-ipv6")]
65+
mld_report_state: MldReportState,
5266
}
5367

5468
impl State {
@@ -57,6 +71,8 @@ impl State {
5771
groups: LinearMap::new(),
5872
#[cfg(feature = "proto-ipv4")]
5973
igmp_report_state: IgmpReportState::Inactive,
74+
#[cfg(feature = "proto-ipv6")]
75+
mld_report_state: MldReportState::Inactive,
6076
}
6177
}
6278

@@ -140,6 +156,29 @@ impl Interface {
140156
self.inner.has_multicast_group(addr)
141157
}
142158

159+
#[cfg(feature = "proto-ipv6")]
160+
pub(super) fn update_solicited_node_groups(&mut self) {
161+
// Remove old solicited-node multicast addresses
162+
let removals: Vec<_, IFACE_MAX_MULTICAST_GROUP_COUNT> = self
163+
.inner
164+
.multicast
165+
.groups
166+
.keys()
167+
.cloned()
168+
.filter(|a| matches!(a, IpAddress::Ipv6(a) if a.is_solicited_node_multicast() && !self.inner.has_solicited_node(*a)))
169+
.collect();
170+
for removal in removals {
171+
let _ = self.leave_multicast_group(removal);
172+
}
173+
174+
let cidrs: Vec<IpCidr, IFACE_MAX_ADDR_COUNT> = Vec::from_slice(self.ip_addrs()).unwrap();
175+
for cidr in cidrs {
176+
if let IpCidr::Ipv6(cidr) = cidr {
177+
let _ = self.join_multicast_group(cidr.address().solicited_node());
178+
}
179+
}
180+
}
181+
143182
/// Do multicast egress.
144183
///
145184
/// - Send join/leave packets according to the multicast group state.
@@ -306,6 +345,46 @@ impl Interface {
306345
}
307346
_ => {}
308347
}
348+
#[cfg(feature = "proto-ipv6")]
349+
match self.inner.multicast.mld_report_state {
350+
MldReportState::ToGeneralQuery { timeout } if self.inner.now >= timeout => {
351+
let records = self
352+
.inner
353+
.multicast
354+
.groups
355+
.iter()
356+
.filter_map(|(addr, _)| match addr {
357+
IpAddress::Ipv6(addr) => Some(MldAddressRecordRepr::new(
358+
MldRecordType::ModeIsExclude,
359+
*addr,
360+
)),
361+
#[allow(unreachable_patterns)]
362+
_ => None,
363+
})
364+
.collect::<heapless::Vec<_, IFACE_MAX_MULTICAST_GROUP_COUNT>>();
365+
if let Some(pkt) = self.inner.mldv2_report_packet(&records) {
366+
if let Some(tx_token) = device.transmit(self.inner.now) {
367+
self.inner
368+
.dispatch_ip(tx_token, PacketMeta::default(), pkt, &mut self.fragmenter)
369+
.unwrap();
370+
};
371+
};
372+
self.inner.multicast.mld_report_state = MldReportState::Inactive;
373+
}
374+
MldReportState::ToSpecificQuery { group, timeout } if self.inner.now >= timeout => {
375+
let record = MldAddressRecordRepr::new(MldRecordType::ModeIsExclude, group);
376+
if let Some(pkt) = self.inner.mldv2_report_packet(&[record]) {
377+
if let Some(tx_token) = device.transmit(self.inner.now) {
378+
// NOTE(unwrap): packet destination is multicast, which is always routable and doesn't require neighbor discovery.
379+
self.inner
380+
.dispatch_ip(tx_token, PacketMeta::default(), pkt, &mut self.fragmenter)
381+
.unwrap();
382+
}
383+
}
384+
self.inner.multicast.mld_report_state = MldReportState::Inactive;
385+
}
386+
_ => {}
387+
}
309388
}
310389
}
311390

@@ -425,4 +504,55 @@ impl InterfaceInner {
425504
)
426505
})
427506
}
507+
508+
/// Host duties of the **MLDv2** protocol.
509+
///
510+
/// Sets up `mld_report_state` for responding to MLD general/specific membership queries.
511+
/// Membership must not be reported immediately in order to avoid flooding the network
512+
/// after a query is broadcasted by a router; Currently the delay is fixed and not randomized.
513+
#[cfg(feature = "proto-ipv6")]
514+
pub(super) fn process_mldv2<'frame>(
515+
&mut self,
516+
ip_repr: Ipv6Repr,
517+
repr: MldRepr<'frame>,
518+
) -> Option<Packet<'frame>> {
519+
match repr {
520+
MldRepr::Query {
521+
mcast_addr,
522+
max_resp_code,
523+
..
524+
} => {
525+
// Do not respont immediately to the query, but wait a random time
526+
let delay = crate::time::Duration::from_millis(
527+
(self.rand.rand_u16() % max_resp_code).into(),
528+
);
529+
// General query
530+
if mcast_addr.is_unspecified()
531+
&& (ip_repr.dst_addr == IPV6_LINK_LOCAL_ALL_NODES
532+
|| self.has_ip_addr(ip_repr.dst_addr))
533+
{
534+
let ipv6_multicast_group_count = self
535+
.multicast
536+
.groups
537+
.keys()
538+
.filter(|a| matches!(a, IpAddress::Ipv6(_)))
539+
.count();
540+
if ipv6_multicast_group_count != 0 {
541+
self.multicast.mld_report_state = MldReportState::ToGeneralQuery {
542+
timeout: self.now + delay,
543+
};
544+
}
545+
}
546+
if self.has_multicast_group(mcast_addr) && ip_repr.dst_addr == mcast_addr {
547+
self.multicast.mld_report_state = MldReportState::ToSpecificQuery {
548+
group: mcast_addr,
549+
timeout: self.now + delay,
550+
};
551+
}
552+
None
553+
}
554+
MldRepr::Report { .. } => None,
555+
MldRepr::ReportRecordReprs { .. } => None,
556+
}
557+
}
428558
}

src/iface/interface/tests/ipv4.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,82 @@ fn test_handle_igmp(#[case] medium: Medium) {
759759
}
760760
}
761761

762+
#[rstest]
763+
#[case(Medium::Ip)]
764+
#[cfg(all(feature = "proto-ipv4-fragmentation", feature = "medium-ip"))]
765+
#[case(Medium::Ethernet)]
766+
#[cfg(all(feature = "proto-ipv4-fragmentation", feature = "medium-ethernet"))]
767+
fn test_packet_len(#[case] medium: Medium) {
768+
use crate::config::FRAGMENTATION_BUFFER_SIZE;
769+
770+
let (mut iface, _, _) = setup(medium);
771+
772+
struct TestTxToken {
773+
max_transmission_unit: usize,
774+
}
775+
776+
impl TxToken for TestTxToken {
777+
fn consume<R, F>(self, len: usize, f: F) -> R
778+
where
779+
F: FnOnce(&mut [u8]) -> R,
780+
{
781+
net_debug!("TxToken get len: {}", len);
782+
assert!(len <= self.max_transmission_unit);
783+
let mut junk = [0; 1536];
784+
f(&mut junk[..len])
785+
}
786+
}
787+
788+
iface.inner.neighbor_cache.fill(
789+
IpAddress::Ipv4(Ipv4Address::new(127, 0, 0, 1)),
790+
HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[
791+
0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
792+
])),
793+
Instant::ZERO,
794+
);
795+
796+
for ip_packet_len in [
797+
100,
798+
iface.inner.ip_mtu(),
799+
iface.inner.ip_mtu() + 1,
800+
FRAGMENTATION_BUFFER_SIZE,
801+
] {
802+
net_debug!("ip_packet_len: {}", ip_packet_len);
803+
804+
let mut ip_repr = Ipv4Repr {
805+
src_addr: Ipv4Address::new(127, 0, 0, 1),
806+
dst_addr: Ipv4Address::new(127, 0, 0, 1),
807+
next_header: IpProtocol::Udp,
808+
payload_len: 0,
809+
hop_limit: 64,
810+
};
811+
let udp_repr = UdpRepr {
812+
src_port: 12345,
813+
dst_port: 54321,
814+
};
815+
816+
let ip_packet_payload_len = ip_packet_len - ip_repr.buffer_len();
817+
let udp_packet_payload_len = ip_packet_payload_len - udp_repr.header_len();
818+
ip_repr.payload_len = ip_packet_payload_len;
819+
820+
let udp_packet_payload = vec![1; udp_packet_payload_len];
821+
let ip_payload = IpPayload::Udp(udp_repr, &udp_packet_payload);
822+
let ip_packet = Packet::new_ipv4(ip_repr, ip_payload);
823+
824+
assert_eq!(
825+
iface.inner.dispatch_ip(
826+
TestTxToken {
827+
max_transmission_unit: iface.inner.caps.max_transmission_unit
828+
},
829+
PacketMeta::default(),
830+
ip_packet,
831+
&mut iface.fragmenter,
832+
),
833+
Ok(())
834+
);
835+
}
836+
}
837+
762838
#[rstest]
763839
#[case(Medium::Ip)]
764840
#[cfg(all(feature = "socket-raw", feature = "medium-ip"))]

0 commit comments

Comments
 (0)