@@ -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,17 +621,21 @@ 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. Since we don't know the offset of
625
+ // the ICMPv6 header in the L2 frame, we should only check whether the payload's IPv6
626
+ // header is present, the rest is allowed to be truncated.
627
+ let ip_packet = if packet. buffer . as_ref ( ) . len ( ) >= IPV6_HEADER_LEN {
628
+ Ipv6Packet :: new_unchecked ( packet. payload ( ) )
629
+ } else {
630
+ return Err ( Error ) ;
631
+ } ;
621
632
622
633
let payload = & packet. payload ( ) [ ip_packet. header_len ( ) ..] ;
623
- if payload. len ( ) < 8 {
624
- return Err ( Error ) ;
625
- }
626
634
let repr = Ipv6Repr {
627
635
src_addr : ip_packet. src_addr ( ) ,
628
636
dst_addr : ip_packet. dst_addr ( ) ,
629
637
next_header : ip_packet. next_header ( ) ,
630
- payload_len : payload . len ( ) ,
638
+ payload_len : ip_packet . payload_len ( ) . into ( ) ,
631
639
hop_limit : ip_packet. hop_limit ( ) ,
632
640
} ;
633
641
Ok ( ( payload, repr) )
@@ -696,9 +704,10 @@ impl<'a> Repr<'a> {
696
704
& Repr :: DstUnreachable { header, data, .. }
697
705
| & Repr :: PktTooBig { header, data, .. }
698
706
| & Repr :: TimeExceeded { header, data, .. }
699
- | & Repr :: ParamProblem { header, data, .. } => {
700
- field:: UNUSED . end + header. buffer_len ( ) + data. len ( )
701
- }
707
+ | & Repr :: ParamProblem { header, data, .. } => cmp:: min (
708
+ field:: UNUSED . end + header. buffer_len ( ) + data. len ( ) ,
709
+ MAX_ERROR_PACKET_LEN ,
710
+ ) ,
702
711
& Repr :: EchoRequest { data, .. } | & Repr :: EchoReply { data, .. } => {
703
712
field:: ECHO_SEQNO . end + data. len ( )
704
713
}
@@ -721,11 +730,21 @@ impl<'a> Repr<'a> {
721
730
) where
722
731
T : AsRef < [ u8 ] > + AsMut < [ u8 ] > + ?Sized ,
723
732
{
724
- fn emit_contained_packet ( buffer : & mut [ u8 ] , header : Ipv6Repr , data : & [ u8 ] ) {
725
- let mut ip_packet = Ipv6Packet :: new_unchecked ( buffer) ;
733
+ fn emit_contained_packet < T > ( packet : & mut Packet < & mut T > , header : Ipv6Repr , data : & [ u8 ] )
734
+ where
735
+ T : AsRef < [ u8 ] > + AsMut < [ u8 ] > + ?Sized ,
736
+ {
737
+ let icmp_header_len = packet. header_len ( ) ;
738
+ let mut ip_packet = Ipv6Packet :: new_unchecked ( packet. payload_mut ( ) ) ;
726
739
header. emit ( & mut ip_packet) ;
727
740
let payload = & mut ip_packet. into_inner ( ) [ header. buffer_len ( ) ..] ;
728
- payload. copy_from_slice ( data) ;
741
+ // FIXME: this should rather be checked at IPv6 level, as we can't know in advance how
742
+ // much space we have for the packet due to IPv6 options and etc
743
+ let payload_len = cmp:: min (
744
+ data. len ( ) ,
745
+ MAX_ERROR_PACKET_LEN - icmp_header_len - IPV6_HEADER_LEN ,
746
+ ) ;
747
+ payload[ ..payload_len] . copy_from_slice ( & data[ ..payload_len] ) ;
729
748
}
730
749
731
750
match * self {
@@ -737,15 +756,15 @@ impl<'a> Repr<'a> {
737
756
packet. set_msg_type ( Message :: DstUnreachable ) ;
738
757
packet. set_msg_code ( reason. into ( ) ) ;
739
758
740
- emit_contained_packet ( packet. payload_mut ( ) , header, data) ;
759
+ emit_contained_packet ( packet, header, data) ;
741
760
}
742
761
743
762
Repr :: PktTooBig { mtu, header, data } => {
744
763
packet. set_msg_type ( Message :: PktTooBig ) ;
745
764
packet. set_msg_code ( 0 ) ;
746
765
packet. set_pkt_too_big_mtu ( mtu) ;
747
766
748
- emit_contained_packet ( packet. payload_mut ( ) , header, data) ;
767
+ emit_contained_packet ( packet, header, data) ;
749
768
}
750
769
751
770
Repr :: TimeExceeded {
@@ -756,7 +775,7 @@ impl<'a> Repr<'a> {
756
775
packet. set_msg_type ( Message :: TimeExceeded ) ;
757
776
packet. set_msg_code ( reason. into ( ) ) ;
758
777
759
- emit_contained_packet ( packet. payload_mut ( ) , header, data) ;
778
+ emit_contained_packet ( packet, header, data) ;
760
779
}
761
780
762
781
Repr :: ParamProblem {
@@ -769,7 +788,7 @@ impl<'a> Repr<'a> {
769
788
packet. set_msg_code ( reason. into ( ) ) ;
770
789
packet. set_param_problem_ptr ( pointer) ;
771
790
772
- emit_contained_packet ( packet. payload_mut ( ) , header, data) ;
791
+ emit_contained_packet ( packet, header, data) ;
773
792
}
774
793
775
794
Repr :: EchoRequest {
@@ -981,4 +1000,65 @@ mod test {
981
1000
) ;
982
1001
assert_eq ! ( & * packet. into_inner( ) , & PKT_TOO_BIG_BYTES [ ..] ) ;
983
1002
}
1003
+
1004
+ #[ test]
1005
+ fn test_buffer_length_is_truncated_to_mtu ( ) {
1006
+ let repr = Repr :: PktTooBig {
1007
+ mtu : 1280 ,
1008
+ header : Ipv6Repr {
1009
+ src_addr : Default :: default ( ) ,
1010
+ dst_addr : Default :: default ( ) ,
1011
+ next_header : IpProtocol :: Tcp ,
1012
+ hop_limit : 64 ,
1013
+ payload_len : 1280 ,
1014
+ } ,
1015
+ data : & vec ! [ 0 ; 9999 ] ,
1016
+ } ;
1017
+ assert_eq ! ( repr. buffer_len( ) , 1280 - IPV6_HEADER_LEN ) ;
1018
+ }
1019
+
1020
+ #[ test]
1021
+ fn test_mtu_truncated_payload_roundtrip ( ) {
1022
+ let ip_packet_repr = Ipv6Repr {
1023
+ src_addr : Default :: default ( ) ,
1024
+ dst_addr : Default :: default ( ) ,
1025
+ next_header : IpProtocol :: Tcp ,
1026
+ hop_limit : 64 ,
1027
+ payload_len : IPV6_MIN_MTU - IPV6_HEADER_LEN ,
1028
+ } ;
1029
+ let mut ip_packet = Ipv6Packet :: new_unchecked ( vec ! [ 0 ; IPV6_MIN_MTU ] ) ;
1030
+ ip_packet_repr. emit ( & mut ip_packet) ;
1031
+
1032
+ let repr1 = Repr :: PktTooBig {
1033
+ mtu : IPV6_MIN_MTU as u32 ,
1034
+ header : ip_packet_repr,
1035
+ data : & ip_packet. as_ref ( ) [ IPV6_HEADER_LEN ..] ,
1036
+ } ;
1037
+ // this is needed to make sure roundtrip gives the same value
1038
+ // it is not needed for ensuring the correct bytes get emitted
1039
+ let repr1 = Repr :: PktTooBig {
1040
+ mtu : IPV6_MIN_MTU as u32 ,
1041
+ header : ip_packet_repr,
1042
+ data : & ip_packet. as_ref ( ) [ IPV6_HEADER_LEN ..repr1. buffer_len ( ) - field:: UNUSED . end ] ,
1043
+ } ;
1044
+ let mut data = vec ! [ 0 ; MAX_ERROR_PACKET_LEN ] ;
1045
+ let mut packet = Packet :: new_unchecked ( & mut data) ;
1046
+ repr1. emit (
1047
+ & MOCK_IP_ADDR_1 ,
1048
+ & MOCK_IP_ADDR_2 ,
1049
+ & mut packet,
1050
+ & ChecksumCapabilities :: default ( ) ,
1051
+ ) ;
1052
+
1053
+ let packet = Packet :: new_unchecked ( & data) ;
1054
+ let repr2 = Repr :: parse (
1055
+ & MOCK_IP_ADDR_1 ,
1056
+ & MOCK_IP_ADDR_2 ,
1057
+ & packet,
1058
+ & ChecksumCapabilities :: default ( ) ,
1059
+ )
1060
+ . unwrap ( ) ;
1061
+
1062
+ assert_eq ! ( repr1, repr2) ;
1063
+ }
984
1064
}
0 commit comments