@@ -8,20 +8,27 @@ use crate::socket::PollAt;
8
8
#[ cfg( feature = "async" ) ]
9
9
use crate :: socket:: WakerRegistration ;
10
10
use crate :: storage:: Empty ;
11
- use crate :: wire:: { IpEndpoint , IpListenEndpoint , IpProtocol , IpRepr , UdpRepr } ;
11
+ use crate :: wire:: { IpAddress , IpEndpoint , IpListenEndpoint , IpProtocol , IpRepr , UdpRepr } ;
12
12
13
13
/// Metadata for a sent or received UDP packet.
14
14
#[ cfg_attr( feature = "defmt" , derive( defmt:: Format ) ) ]
15
15
#[ derive( Debug , PartialEq , Eq , Clone , Copy ) ]
16
16
pub struct UdpMetadata {
17
17
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 > ,
18
24
pub meta : PacketMeta ,
19
25
}
20
26
21
27
impl < T : Into < IpEndpoint > > From < T > for UdpMetadata {
22
28
fn from ( value : T ) -> Self {
23
29
Self {
24
30
endpoint : value. into ( ) ,
31
+ local_address : None ,
25
32
meta : PacketMeta :: default ( ) ,
26
33
}
27
34
}
@@ -493,6 +500,7 @@ impl<'a> Socket<'a> {
493
500
494
501
let metadata = UdpMetadata {
495
502
endpoint : remote_endpoint,
503
+ local_address : Some ( ip_repr. dst_addr ( ) ) ,
496
504
meta,
497
505
} ;
498
506
@@ -517,19 +525,23 @@ impl<'a> Socket<'a> {
517
525
let hop_limit = self . hop_limit . unwrap_or ( 64 ) ;
518
526
519
527
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 {
523
532
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
+ }
533
545
} ;
534
546
535
547
net_trace ! (
@@ -635,6 +647,13 @@ mod test {
635
647
addr : REMOTE_ADDR . into_address ( ) ,
636
648
port : REMOTE_PORT ,
637
649
} ;
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
+ }
638
657
639
658
pub const LOCAL_IP_REPR : IpRepr = IpReprIpvX ( IpvXRepr {
640
659
src_addr : LOCAL_ADDR ,
@@ -724,6 +743,17 @@ mod test {
724
743
assert_eq ! ( socket. send_slice( b"abcdef" , REMOTE_END ) , Ok ( ( ) ) ) ;
725
744
}
726
745
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
+
727
757
#[ rstest]
728
758
#[ case:: ip( Medium :: Ip ) ]
729
759
#[ cfg( feature = "medium-ip" ) ]
@@ -811,7 +841,10 @@ mod test {
811
841
PAYLOAD ,
812
842
) ;
813
843
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
+ ) ;
815
848
assert ! ( !socket. can_recv( ) ) ;
816
849
}
817
850
@@ -839,8 +872,14 @@ mod test {
839
872
& REMOTE_UDP_REPR ,
840
873
PAYLOAD ,
841
874
) ;
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
+ ) ;
844
883
assert_eq ! ( socket. peek( ) , Err ( RecvError :: Exhausted ) ) ;
845
884
}
846
885
@@ -1013,7 +1052,7 @@ mod test {
1013
1052
dst_port : LOCAL_PORT ,
1014
1053
} ;
1015
1054
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 ( ) ) ) ) ;
1017
1056
}
1018
1057
1019
1058
#[ test]
0 commit comments