@@ -10,6 +10,10 @@ use crate::wire::NdiscRepr;
10
10
#[ cfg( feature = "proto-rpl" ) ]
11
11
use crate :: wire:: RplRepr ;
12
12
use crate :: wire:: { IpAddress , IpProtocol , Ipv6Packet , Ipv6Repr } ;
13
+ use crate :: wire:: { IPV6_HEADER_LEN , IPV6_MIN_MTU } ;
14
+
15
+ /// Error packets must not exceed min MTU
16
+ const MAX_ERROR_PACKET_LEN : usize = IPV6_MIN_MTU - IPV6_HEADER_LEN ;
13
17
14
18
enum_with_unknown ! {
15
19
/// Internet protocol control message type.
@@ -617,7 +621,14 @@ impl<'a> Repr<'a> {
617
621
where
618
622
T : AsRef < [ u8 ] > + ?Sized ,
619
623
{
620
- let ip_packet = Ipv6Packet :: new_checked ( packet. payload ( ) ) ?;
624
+ // The packet must be truncated to fit the min MTU. In case the packet is potentially
625
+ // truncated, don't do length validation. It's fine because at this length we're
626
+ // guaranteed to have the original IPv6 header.
627
+ let ip_packet = if packet. buffer . as_ref ( ) . len ( ) >= MAX_ERROR_PACKET_LEN {
628
+ Ipv6Packet :: new_unchecked ( packet. payload ( ) )
629
+ } else {
630
+ Ipv6Packet :: new_checked ( packet. payload ( ) ) ?
631
+ } ;
621
632
622
633
let payload = & packet. payload ( ) [ ip_packet. header_len ( ) ..] ;
623
634
if payload. len ( ) < 8 {
@@ -627,7 +638,7 @@ impl<'a> Repr<'a> {
627
638
src_addr : ip_packet. src_addr ( ) ,
628
639
dst_addr : ip_packet. dst_addr ( ) ,
629
640
next_header : ip_packet. next_header ( ) ,
630
- payload_len : payload . len ( ) ,
641
+ payload_len : ip_packet . payload_len ( ) . into ( ) ,
631
642
hop_limit : ip_packet. hop_limit ( ) ,
632
643
} ;
633
644
Ok ( ( payload, repr) )
@@ -696,9 +707,10 @@ impl<'a> Repr<'a> {
696
707
& Repr :: DstUnreachable { header, data, .. }
697
708
| & Repr :: PktTooBig { header, data, .. }
698
709
| & Repr :: TimeExceeded { header, data, .. }
699
- | & Repr :: ParamProblem { header, data, .. } => {
700
- field:: UNUSED . end + header. buffer_len ( ) + data. len ( )
701
- }
710
+ | & Repr :: ParamProblem { header, data, .. } => cmp:: min (
711
+ field:: UNUSED . end + header. buffer_len ( ) + data. len ( ) ,
712
+ MAX_ERROR_PACKET_LEN ,
713
+ ) ,
702
714
& Repr :: EchoRequest { data, .. } | & Repr :: EchoReply { data, .. } => {
703
715
field:: ECHO_SEQNO . end + data. len ( )
704
716
}
@@ -721,11 +733,21 @@ impl<'a> Repr<'a> {
721
733
) where
722
734
T : AsRef < [ u8 ] > + AsMut < [ u8 ] > + ?Sized ,
723
735
{
724
- fn emit_contained_packet ( buffer : & mut [ u8 ] , header : Ipv6Repr , data : & [ u8 ] ) {
725
- let mut ip_packet = Ipv6Packet :: new_unchecked ( buffer) ;
736
+ fn emit_contained_packet < T > ( packet : & mut Packet < & mut T > , header : Ipv6Repr , data : & [ u8 ] )
737
+ where
738
+ T : AsRef < [ u8 ] > + AsMut < [ u8 ] > + ?Sized ,
739
+ {
740
+ let icmp_header_len = packet. header_len ( ) ;
741
+ let mut ip_packet = Ipv6Packet :: new_unchecked ( packet. payload_mut ( ) ) ;
726
742
header. emit ( & mut ip_packet) ;
727
743
let payload = & mut ip_packet. into_inner ( ) [ header. buffer_len ( ) ..] ;
728
- payload. copy_from_slice ( data) ;
744
+ // FIXME: this should rather be checked at IPv6 level, as we can't know in advance how
745
+ // much space we have for the packet due to IPv6 options and etc
746
+ let payload_len = cmp:: min (
747
+ data. len ( ) ,
748
+ MAX_ERROR_PACKET_LEN - icmp_header_len - IPV6_HEADER_LEN ,
749
+ ) ;
750
+ payload[ ..payload_len] . copy_from_slice ( & data[ ..payload_len] ) ;
729
751
}
730
752
731
753
match * self {
@@ -737,15 +759,15 @@ impl<'a> Repr<'a> {
737
759
packet. set_msg_type ( Message :: DstUnreachable ) ;
738
760
packet. set_msg_code ( reason. into ( ) ) ;
739
761
740
- emit_contained_packet ( packet. payload_mut ( ) , header, data) ;
762
+ emit_contained_packet ( packet, header, data) ;
741
763
}
742
764
743
765
Repr :: PktTooBig { mtu, header, data } => {
744
766
packet. set_msg_type ( Message :: PktTooBig ) ;
745
767
packet. set_msg_code ( 0 ) ;
746
768
packet. set_pkt_too_big_mtu ( mtu) ;
747
769
748
- emit_contained_packet ( packet. payload_mut ( ) , header, data) ;
770
+ emit_contained_packet ( packet, header, data) ;
749
771
}
750
772
751
773
Repr :: TimeExceeded {
@@ -756,7 +778,7 @@ impl<'a> Repr<'a> {
756
778
packet. set_msg_type ( Message :: TimeExceeded ) ;
757
779
packet. set_msg_code ( reason. into ( ) ) ;
758
780
759
- emit_contained_packet ( packet. payload_mut ( ) , header, data) ;
781
+ emit_contained_packet ( packet, header, data) ;
760
782
}
761
783
762
784
Repr :: ParamProblem {
@@ -769,7 +791,7 @@ impl<'a> Repr<'a> {
769
791
packet. set_msg_code ( reason. into ( ) ) ;
770
792
packet. set_param_problem_ptr ( pointer) ;
771
793
772
- emit_contained_packet ( packet. payload_mut ( ) , header, data) ;
794
+ emit_contained_packet ( packet, header, data) ;
773
795
}
774
796
775
797
Repr :: EchoRequest {
@@ -981,4 +1003,65 @@ mod test {
981
1003
) ;
982
1004
assert_eq ! ( & * packet. into_inner( ) , & PKT_TOO_BIG_BYTES [ ..] ) ;
983
1005
}
1006
+
1007
+ #[ test]
1008
+ fn test_buffer_length_is_truncated_to_mtu ( ) {
1009
+ let repr = Repr :: PktTooBig {
1010
+ mtu : 1280 ,
1011
+ header : Ipv6Repr {
1012
+ src_addr : Default :: default ( ) ,
1013
+ dst_addr : Default :: default ( ) ,
1014
+ next_header : IpProtocol :: Tcp ,
1015
+ hop_limit : 64 ,
1016
+ payload_len : 1280 ,
1017
+ } ,
1018
+ data : & vec ! [ 0 ; 9999 ] ,
1019
+ } ;
1020
+ assert_eq ! ( repr. buffer_len( ) , 1280 - IPV6_HEADER_LEN ) ;
1021
+ }
1022
+
1023
+ #[ test]
1024
+ fn test_mtu_truncated_payload_roundtrip ( ) {
1025
+ let ip_packet_repr = Ipv6Repr {
1026
+ src_addr : Default :: default ( ) ,
1027
+ dst_addr : Default :: default ( ) ,
1028
+ next_header : IpProtocol :: Tcp ,
1029
+ hop_limit : 64 ,
1030
+ payload_len : IPV6_MIN_MTU - IPV6_HEADER_LEN ,
1031
+ } ;
1032
+ let mut ip_packet = Ipv6Packet :: new_unchecked ( vec ! [ 0 ; IPV6_MIN_MTU ] ) ;
1033
+ ip_packet_repr. emit ( & mut ip_packet) ;
1034
+
1035
+ let repr1 = Repr :: PktTooBig {
1036
+ mtu : IPV6_MIN_MTU as u32 ,
1037
+ header : ip_packet_repr,
1038
+ data : & ip_packet. as_ref ( ) [ IPV6_HEADER_LEN ..] ,
1039
+ } ;
1040
+ // this is needed to make sure roundtrip gives the same value
1041
+ // it is not needed for ensuring the correct bytes get emitted
1042
+ let repr1 = Repr :: PktTooBig {
1043
+ mtu : IPV6_MIN_MTU as u32 ,
1044
+ header : ip_packet_repr,
1045
+ data : & ip_packet. as_ref ( ) [ IPV6_HEADER_LEN ..repr1. buffer_len ( ) - field:: UNUSED . end ] ,
1046
+ } ;
1047
+ let mut data = vec ! [ 0 ; MAX_ERROR_PACKET_LEN ] ;
1048
+ let mut packet = Packet :: new_unchecked ( & mut data) ;
1049
+ repr1. emit (
1050
+ & MOCK_IP_ADDR_1 ,
1051
+ & MOCK_IP_ADDR_2 ,
1052
+ & mut packet,
1053
+ & ChecksumCapabilities :: default ( ) ,
1054
+ ) ;
1055
+
1056
+ let packet = Packet :: new_unchecked ( & data) ;
1057
+ let repr2 = Repr :: parse (
1058
+ & MOCK_IP_ADDR_1 ,
1059
+ & MOCK_IP_ADDR_2 ,
1060
+ & packet,
1061
+ & ChecksumCapabilities :: default ( ) ,
1062
+ )
1063
+ . unwrap ( ) ;
1064
+
1065
+ assert_eq ! ( repr1, repr2) ;
1066
+ }
984
1067
}
0 commit comments