@@ -8,20 +8,27 @@ use crate::socket::PollAt;
88#[ cfg( feature = "async" ) ]
99use crate :: socket:: WakerRegistration ;
1010use crate :: storage:: Empty ;
11- use crate :: wire:: { IpEndpoint , IpListenEndpoint , IpProtocol , IpRepr , UdpRepr } ;
11+ use crate :: wire:: { IpAddress , IpEndpoint , IpListenEndpoint , IpProtocol , IpRepr , UdpRepr } ;
1212
1313/// Metadata for a sent or received UDP packet.
1414#[ cfg_attr( feature = "defmt" , derive( defmt:: Format ) ) ]
1515#[ derive( Debug , PartialEq , Eq , Clone , Copy ) ]
1616pub struct UdpMetadata {
1717 pub endpoint : IpEndpoint ,
18+ /// The IP address to which an incoming datagram was sent, or to which an outgoing datagram
19+ /// will be sent. Incoming datagrams always have this set. On outgoing datagrams, if it is not
20+ /// set, and the socket is not bound to a single address anyway, a suitable address will be
21+ /// determined using the algorithms of RFC 6724 (candidate source address selection) or some
22+ /// heuristic (for IPv4).
23+ pub local_address : Option < IpAddress > ,
1824 pub meta : PacketMeta ,
1925}
2026
2127impl < T : Into < IpEndpoint > > From < T > for UdpMetadata {
2228 fn from ( value : T ) -> Self {
2329 Self {
2430 endpoint : value. into ( ) ,
31+ local_address : None ,
2532 meta : PacketMeta :: default ( ) ,
2633 }
2734 }
@@ -493,6 +500,7 @@ impl<'a> Socket<'a> {
493500
494501 let metadata = UdpMetadata {
495502 endpoint : remote_endpoint,
503+ local_address : Some ( ip_repr. dst_addr ( ) ) ,
496504 meta,
497505 } ;
498506
@@ -517,19 +525,23 @@ impl<'a> Socket<'a> {
517525 let hop_limit = self . hop_limit . unwrap_or ( 64 ) ;
518526
519527 let res = self . tx_buffer . dequeue_with ( |packet_meta, payload_buf| {
520- let src_addr = match endpoint. addr {
521- Some ( addr) => addr,
522- None => match cx. get_source_address ( & packet_meta. endpoint . addr ) {
528+ let src_addr = if let Some ( s) = packet_meta. local_address {
529+ s
530+ } else {
531+ match endpoint. addr {
523532 Some ( addr) => addr,
524- None => {
525- net_trace ! (
526- "udp:{}:{}: cannot find suitable source address, dropping." ,
527- endpoint,
528- packet_meta. endpoint
529- ) ;
530- return Ok ( ( ) ) ;
531- }
532- } ,
533+ None => match cx. get_source_address ( & packet_meta. endpoint . addr ) {
534+ Some ( addr) => addr,
535+ None => {
536+ net_trace ! (
537+ "udp:{}:{}: cannot find suitable source address, dropping." ,
538+ endpoint,
539+ packet_meta. endpoint
540+ ) ;
541+ return Ok ( ( ) ) ;
542+ }
543+ } ,
544+ }
533545 } ;
534546
535547 net_trace ! (
@@ -635,6 +647,13 @@ mod test {
635647 addr : REMOTE_ADDR . into_address ( ) ,
636648 port : REMOTE_PORT ,
637649 } ;
650+ fn remote_metadata_with_local ( ) -> UdpMetadata {
651+ // Would be great as a const once we have const `.into()`.
652+ UdpMetadata {
653+ local_address : Some ( LOCAL_ADDR . into ( ) ) ,
654+ ..REMOTE_END . into ( )
655+ }
656+ }
638657
639658 pub const LOCAL_IP_REPR : IpRepr = IpReprIpvX ( IpvXRepr {
640659 src_addr : LOCAL_ADDR ,
@@ -724,6 +743,17 @@ mod test {
724743 assert_eq ! ( socket. send_slice( b"abcdef" , REMOTE_END ) , Ok ( ( ) ) ) ;
725744 }
726745
746+ #[ test]
747+ fn test_send_with_source ( ) {
748+ let mut socket = socket ( buffer ( 0 ) , buffer ( 1 ) ) ;
749+
750+ assert_eq ! ( socket. bind( LOCAL_PORT ) , Ok ( ( ) ) ) ;
751+ assert_eq ! (
752+ socket. send_slice( b"abcdef" , remote_metadata_with_local( ) ) ,
753+ Ok ( ( ) )
754+ ) ;
755+ }
756+
727757 #[ rstest]
728758 #[ case:: ip( Medium :: Ip ) ]
729759 #[ cfg( feature = "medium-ip" ) ]
@@ -811,7 +841,10 @@ mod test {
811841 PAYLOAD ,
812842 ) ;
813843
814- assert_eq ! ( socket. recv( ) , Ok ( ( & b"abcdef" [ ..] , REMOTE_END . into( ) ) ) ) ;
844+ assert_eq ! (
845+ socket. recv( ) ,
846+ Ok ( ( & b"abcdef" [ ..] , remote_metadata_with_local( ) ) )
847+ ) ;
815848 assert ! ( !socket. can_recv( ) ) ;
816849 }
817850
@@ -839,8 +872,14 @@ mod test {
839872 & REMOTE_UDP_REPR ,
840873 PAYLOAD ,
841874 ) ;
842- assert_eq ! ( socket. peek( ) , Ok ( ( & b"abcdef" [ ..] , & REMOTE_END . into( ) , ) ) ) ;
843- assert_eq ! ( socket. recv( ) , Ok ( ( & b"abcdef" [ ..] , REMOTE_END . into( ) , ) ) ) ;
875+ assert_eq ! (
876+ socket. peek( ) ,
877+ Ok ( ( & b"abcdef" [ ..] , & remote_metadata_with_local( ) , ) )
878+ ) ;
879+ assert_eq ! (
880+ socket. recv( ) ,
881+ Ok ( ( & b"abcdef" [ ..] , remote_metadata_with_local( ) , ) )
882+ ) ;
844883 assert_eq ! ( socket. peek( ) , Err ( RecvError :: Exhausted ) ) ;
845884 }
846885
@@ -1013,7 +1052,7 @@ mod test {
10131052 dst_port : LOCAL_PORT ,
10141053 } ;
10151054 socket. process ( cx, PacketMeta :: default ( ) , & REMOTE_IP_REPR , & repr, & [ ] ) ;
1016- assert_eq ! ( socket. recv( ) , Ok ( ( & [ ] [ ..] , REMOTE_END . into ( ) ) ) ) ;
1055+ assert_eq ! ( socket. recv( ) , Ok ( ( & [ ] [ ..] , remote_metadata_with_local ( ) ) ) ) ;
10171056 }
10181057
10191058 #[ test]
0 commit comments