Skip to content

Commit

Permalink
Merge pull request smoltcp-rs#904 from chrysn-pull-requests/pktinfo
Browse files Browse the repository at this point in the history
UDP: Store local and use local address in metadata
  • Loading branch information
thvdveld authored Apr 19, 2024
2 parents d3ed3cc + dc60300 commit 125773e
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 26 deletions.
5 changes: 4 additions & 1 deletion src/iface/interface/tests/ipv4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -938,7 +938,10 @@ fn test_raw_socket_with_udp_socket(#[case] medium: Medium) {
socket.recv(),
Ok((
&UDP_PAYLOAD[..],
IpEndpoint::new(src_addr.into(), 67).into()
udp::UdpMetadata {
local_address: Some(dst_addr.into()),
..IpEndpoint::new(src_addr.into(), 67).into()
}
))
);
}
Expand Down
9 changes: 8 additions & 1 deletion src/iface/interface/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ fn test_handle_udp_broadcast(
payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(),
hop_limit: 0x40,
});
let dst_addr = ip_repr.dst_addr();

// Bind the socket to port 68
let socket = sockets.get_mut::<udp::Socket>(socket_handle);
Expand Down Expand Up @@ -143,7 +144,13 @@ fn test_handle_udp_broadcast(
assert!(socket.can_recv());
assert_eq!(
socket.recv(),
Ok((&UDP_PAYLOAD[..], IpEndpoint::new(src_ip.into(), 67).into()))
Ok((
&UDP_PAYLOAD[..],
udp::UdpMetadata {
local_address: Some(dst_addr),
..IpEndpoint::new(src_ip.into(), 67).into()
}
))
);
}

Expand Down
23 changes: 16 additions & 7 deletions src/iface/interface/tests/sixlowpan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,14 +368,23 @@ In at rhoncus tortor. Cras blandit tellus diam, varius vestibulum nibh commodo n
socket.recv(),
Ok((
&udp_data[..],
IpEndpoint {
addr: IpAddress::Ipv6(Ipv6Address([
0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42,
0xb, 0x1a,
])),
port: 54217,
udp::UdpMetadata {
local_address: Some(
Ipv6Address([
0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x92, 0xfc, 0x48, 0xc2, 0xa4,
0x41, 0xfc, 0x76,
])
.into()
),
..IpEndpoint {
addr: IpAddress::Ipv6(Ipv6Address([
0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x42, 0x42, 0x42, 0x42,
0x42, 0xb, 0x1a,
])),
port: 54217,
}
.into()
}
.into()
))
);

Expand Down
73 changes: 56 additions & 17 deletions src/socket/udp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,27 @@ use crate::socket::PollAt;
#[cfg(feature = "async")]
use crate::socket::WakerRegistration;
use crate::storage::Empty;
use crate::wire::{IpEndpoint, IpListenEndpoint, IpProtocol, IpRepr, UdpRepr};
use crate::wire::{IpAddress, IpEndpoint, IpListenEndpoint, IpProtocol, IpRepr, UdpRepr};

/// Metadata for a sent or received UDP packet.
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct UdpMetadata {
pub endpoint: IpEndpoint,
/// The IP address to which an incoming datagram was sent, or to which an outgoing datagram
/// will be sent. Incoming datagrams always have this set. On outgoing datagrams, if it is not
/// set, and the socket is not bound to a single address anyway, a suitable address will be
/// determined using the algorithms of RFC 6724 (candidate source address selection) or some
/// heuristic (for IPv4).
pub local_address: Option<IpAddress>,
pub meta: PacketMeta,
}

impl<T: Into<IpEndpoint>> From<T> for UdpMetadata {
fn from(value: T) -> Self {
Self {
endpoint: value.into(),
local_address: None,
meta: PacketMeta::default(),
}
}
Expand Down Expand Up @@ -493,6 +500,7 @@ impl<'a> Socket<'a> {

let metadata = UdpMetadata {
endpoint: remote_endpoint,
local_address: Some(ip_repr.dst_addr()),
meta,
};

Expand All @@ -517,19 +525,23 @@ impl<'a> Socket<'a> {
let hop_limit = self.hop_limit.unwrap_or(64);

let res = self.tx_buffer.dequeue_with(|packet_meta, payload_buf| {
let src_addr = match endpoint.addr {
Some(addr) => addr,
None => match cx.get_source_address(&packet_meta.endpoint.addr) {
let src_addr = if let Some(s) = packet_meta.local_address {
s
} else {
match endpoint.addr {
Some(addr) => addr,
None => {
net_trace!(
"udp:{}:{}: cannot find suitable source address, dropping.",
endpoint,
packet_meta.endpoint
);
return Ok(());
}
},
None => match cx.get_source_address(&packet_meta.endpoint.addr) {
Some(addr) => addr,
None => {
net_trace!(
"udp:{}:{}: cannot find suitable source address, dropping.",
endpoint,
packet_meta.endpoint
);
return Ok(());
}
},
}
};

net_trace!(
Expand Down Expand Up @@ -635,6 +647,13 @@ mod test {
addr: REMOTE_ADDR.into_address(),
port: REMOTE_PORT,
};
fn remote_metadata_with_local() -> UdpMetadata {
// Would be great as a const once we have const `.into()`.
UdpMetadata {
local_address: Some(LOCAL_ADDR.into()),
..REMOTE_END.into()
}
}

pub const LOCAL_IP_REPR: IpRepr = IpReprIpvX(IpvXRepr {
src_addr: LOCAL_ADDR,
Expand Down Expand Up @@ -724,6 +743,17 @@ mod test {
assert_eq!(socket.send_slice(b"abcdef", REMOTE_END), Ok(()));
}

#[test]
fn test_send_with_source() {
let mut socket = socket(buffer(0), buffer(1));

assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
assert_eq!(
socket.send_slice(b"abcdef", remote_metadata_with_local()),
Ok(())
);
}

#[rstest]
#[case::ip(Medium::Ip)]
#[cfg(feature = "medium-ip")]
Expand Down Expand Up @@ -811,7 +841,10 @@ mod test {
PAYLOAD,
);

assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END.into())));
assert_eq!(
socket.recv(),
Ok((&b"abcdef"[..], remote_metadata_with_local()))
);
assert!(!socket.can_recv());
}

Expand Down Expand Up @@ -839,8 +872,14 @@ mod test {
&REMOTE_UDP_REPR,
PAYLOAD,
);
assert_eq!(socket.peek(), Ok((&b"abcdef"[..], &REMOTE_END.into(),)));
assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END.into(),)));
assert_eq!(
socket.peek(),
Ok((&b"abcdef"[..], &remote_metadata_with_local(),))
);
assert_eq!(
socket.recv(),
Ok((&b"abcdef"[..], remote_metadata_with_local(),))
);
assert_eq!(socket.peek(), Err(RecvError::Exhausted));
}

Expand Down Expand Up @@ -1013,7 +1052,7 @@ mod test {
dst_port: LOCAL_PORT,
};
socket.process(cx, PacketMeta::default(), &REMOTE_IP_REPR, &repr, &[]);
assert_eq!(socket.recv(), Ok((&[][..], REMOTE_END.into())));
assert_eq!(socket.recv(), Ok((&[][..], remote_metadata_with_local())));
}

#[test]
Expand Down

0 comments on commit 125773e

Please sign in to comment.