Просмотр исходного кода

Implement set_ttl for Tcp and Udp sockets

 - Add the ttl member to the IpRepr
 - Add the ttl member along with setters and getters to the tcp and udp
   socket types
 - Add unit tests for the new set_ttl parameter
 - Update usage of IpRepr to include the ttl value
Dan Robertson 7 лет назад
Родитель
Сommit
fea462837d
10 измененных файлов с 181 добавлено и 30 удалено
  1. 0 1
      README.md
  2. 1 0
      examples/ping.rs
  3. 6 3
      src/iface/ethernet.rs
  4. 2 1
      src/socket/raw.rs
  5. 67 8
      src/socket/tcp.rs
  6. 58 4
      src/socket/udp.rs
  7. 1 0
      src/wire/icmpv4.rs
  8. 26 8
      src/wire/ip.rs
  9. 18 4
      src/wire/ipv4.rs
  10. 2 1
      src/wire/mod.rs

+ 0 - 1
README.md

@@ -31,7 +31,6 @@ The only supported medium is Ethernet.
 The only supported internetworking protocol is IPv4.
 
   * IPv4 header checksum is generated and validated.
-  * IPv4 time-to-live value is fixed at 64.
   * IPv4 fragmentation is **not** supported.
   * IPv4 options are **not** supported and are silently ignored.
   * IPv4 routes or default gateways are **not** supported.

+ 1 - 0
examples/ping.rs

@@ -94,6 +94,7 @@ fn main() {
                     dst_addr: remote_addr,
                     protocol: IpProtocol::Icmp,
                     payload_len: icmp_repr.buffer_len(),
+                    ttl: 64
                 };
 
                 let raw_payload = socket

+ 6 - 3
src/iface/ethernet.rs

@@ -378,7 +378,8 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
                     src_addr:    ipv4_repr.dst_addr,
                     dst_addr:    ipv4_repr.src_addr,
                     protocol:    IpProtocol::Icmp,
-                    payload_len: icmp_reply_repr.buffer_len()
+                    payload_len: icmp_reply_repr.buffer_len(),
+                    ttl:         64,
                 };
                 Ok(Packet::Icmpv4(ipv4_reply_repr, icmp_reply_repr))
             }
@@ -403,7 +404,8 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
                     src_addr:    ipv4_repr.dst_addr,
                     dst_addr:    ipv4_repr.src_addr,
                     protocol:    IpProtocol::Icmp,
-                    payload_len: icmp_reply_repr.buffer_len()
+                    payload_len: icmp_reply_repr.buffer_len(),
+                    ttl:         64
                 };
                 Ok(Packet::Icmpv4(ipv4_reply_repr, icmp_reply_repr))
             }
@@ -448,7 +450,8 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
                     src_addr:    ipv4_repr.dst_addr,
                     dst_addr:    ipv4_repr.src_addr,
                     protocol:    IpProtocol::Icmp,
-                    payload_len: icmpv4_reply_repr.buffer_len()
+                    payload_len: icmpv4_reply_repr.buffer_len(),
+                    ttl:         64,
                 };
                 Ok(Packet::Icmpv4(ipv4_reply_repr, icmpv4_reply_repr))
             },

+ 2 - 1
src/socket/raw.rs

@@ -270,7 +270,8 @@ mod test {
         src_addr: Ipv4Address([10, 0, 0, 1]),
         dst_addr: Ipv4Address([10, 0, 0, 2]),
         protocol: IpProtocol::Unknown(IP_PROTO),
-        payload_len: 4
+        payload_len: 4,
+        ttl: 64
     });
     const PACKET_BYTES: [u8; 24] = [
         0x45, 0x00, 0x00, 0x18,

+ 67 - 8
src/socket/tcp.rs

@@ -179,6 +179,8 @@ pub struct TcpSocket<'a> {
     timeout:         Option<u64>,
     /// Interval at which keep-alive packets will be sent.
     keep_alive:      Option<u64>,
+    /// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets.
+    ttl:             Option<u8>,
     /// Address passed to listen(). Listen address is set when listen() is called and
     /// used every time the socket is reset back to the LISTEN state.
     listen_address:  IpAddress,
@@ -236,6 +238,7 @@ impl<'a> TcpSocket<'a> {
             rx_buffer:       rx_buffer,
             timeout:         None,
             keep_alive:      None,
+            ttl:             None,
             listen_address:  IpAddress::default(),
             local_endpoint:  IpEndpoint::default(),
             remote_endpoint: IpEndpoint::default(),
@@ -311,6 +314,33 @@ impl<'a> TcpSocket<'a> {
         }
     }
 
+    /// Return the time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets.
+    ///
+    /// See also the [set_ttl](#method.set_ttl) method
+    pub fn ttl(&self) -> Option<u8> {
+        self.ttl
+    }
+
+    /// Set the time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets.
+    ///
+    /// A socket without an explicitly set TTL value uses the default [IANA recommended]
+    /// value (`64`).
+    ///
+    /// # Panics
+    ///
+    /// This function panics if a TTL value of `0` is given. See [RFC 1122 § 3.2.1.7].
+    ///
+    /// [IANA recommended]: https://www.iana.org/assignments/ip-parameters/ip-parameters.xhtml
+    /// [RFC 1122 § 3.2.1.7]: https://tools.ietf.org/html/rfc1122#section-3.2.1.7
+    pub fn set_ttl(&mut self, ttl: Option<u8>) {
+        // A host MUST NOT send a datagram with a Time-to-Live (TTL)
+        // value of 0
+        match ttl {
+            Some(0)  => { panic!("A TTL value of 0 is invalid for a sent packet"); },
+            catchall => self.ttl = catchall,
+        }
+    }
+
     /// Return the local endpoint.
     #[inline]
     pub fn local_endpoint(&self) -> IpEndpoint {
@@ -337,6 +367,7 @@ impl<'a> TcpSocket<'a> {
         self.rx_buffer.clear();
         self.keep_alive      = None;
         self.timeout         = None;
+        self.ttl             = None;
         self.listen_address  = IpAddress::default();
         self.local_endpoint  = IpEndpoint::default();
         self.remote_endpoint = IpEndpoint::default();
@@ -733,7 +764,8 @@ impl<'a> TcpSocket<'a> {
             src_addr:    ip_repr.dst_addr(),
             dst_addr:    ip_repr.src_addr(),
             protocol:    IpProtocol::Tcp,
-            payload_len: reply_repr.buffer_len()
+            payload_len: reply_repr.buffer_len(),
+            ttl:         64
         };
         (ip_reply_repr, reply_repr)
     }
@@ -1239,6 +1271,7 @@ impl<'a> TcpSocket<'a> {
             src_addr:     self.local_endpoint.addr,
             dst_addr:     self.remote_endpoint.addr,
             protocol:     IpProtocol::Tcp,
+            ttl:          self.ttl.unwrap_or(64),
             payload_len:  0
         }.lower(&[])?;
 
@@ -1447,7 +1480,8 @@ impl<'a> fmt::Write for TcpSocket<'a> {
 
 #[cfg(test)]
 mod test {
-    use wire::{IpAddress, Ipv4Address, IpCidr};
+    use wire::{IpAddress, IpRepr};
+    use wire::{Ipv4Address, IpCidr, Ipv4Repr};
     use super::*;
 
     #[test]
@@ -1479,7 +1513,8 @@ mod test {
 
     const SEND_IP_TEMPL: IpRepr = IpRepr::Unspecified {
         src_addr: LOCAL_IP, dst_addr: REMOTE_IP,
-        protocol: IpProtocol::Tcp, payload_len: 20
+        protocol: IpProtocol::Tcp, payload_len: 20,
+        ttl: 64
     };
     const SEND_TEMPL: TcpRepr<'static> = TcpRepr {
         src_port: REMOTE_PORT, dst_port: LOCAL_PORT,
@@ -1490,7 +1525,8 @@ mod test {
     };
     const _RECV_IP_TEMPL: IpRepr = IpRepr::Unspecified {
         src_addr: REMOTE_IP, dst_addr: LOCAL_IP,
-        protocol: IpProtocol::Tcp, payload_len: 20
+        protocol: IpProtocol::Tcp, payload_len: 20,
+        ttl: 64
     };
     const RECV_TEMPL:  TcpRepr<'static> = TcpRepr {
         src_port: LOCAL_PORT, dst_port: REMOTE_PORT,
@@ -1506,7 +1542,8 @@ mod test {
             src_addr:    REMOTE_IP,
             dst_addr:    LOCAL_IP,
             protocol:    IpProtocol::Tcp,
-            payload_len: repr.buffer_len()
+            payload_len: repr.buffer_len(),
+            ttl:         64
         };
         trace!("send: {}", repr);
 
@@ -3470,7 +3507,8 @@ mod test {
             src_addr:    REMOTE_IP,
             dst_addr:    LOCAL_IP,
             protocol:    IpProtocol::Tcp,
-            payload_len: tcp_repr.buffer_len()
+            payload_len: tcp_repr.buffer_len(),
+            ttl:         64
         };
         assert!(s.accepts(&ip_repr, &tcp_repr));
 
@@ -3478,7 +3516,8 @@ mod test {
             src_addr:    OTHER_IP,
             dst_addr:    LOCAL_IP,
             protocol:    IpProtocol::Tcp,
-            payload_len: tcp_repr.buffer_len()
+            payload_len: tcp_repr.buffer_len(),
+            ttl:         64
         };
         assert!(!s.accepts(&ip_repr_wrong_src, &tcp_repr));
 
@@ -3486,8 +3525,28 @@ mod test {
             src_addr:    REMOTE_IP,
             dst_addr:    OTHER_IP,
             protocol:    IpProtocol::Tcp,
-            payload_len: tcp_repr.buffer_len()
+            payload_len: tcp_repr.buffer_len(),
+            ttl:         64
         };
         assert!(!s.accepts(&ip_repr_wrong_dst, &tcp_repr));
     }
+
+    #[test]
+    fn test_set_ttl() {
+        let mut s = socket_syn_received();
+        let mut caps = DeviceCapabilities::default();
+        caps.max_transmission_unit = 1520;
+
+        s.set_ttl(Some(0x2a));
+        assert_eq!(s.dispatch(0, &caps, |(ip_repr, _)| {
+            assert_eq!(ip_repr, IpRepr::Ipv4(Ipv4Repr {
+                src_addr: Ipv4Address([10, 0, 0, 1]),
+                dst_addr: Ipv4Address([10, 0, 0, 2]),
+                protocol: IpProtocol::Tcp,
+                payload_len: 24,
+                ttl: 0x2a,
+            }));
+            Ok(())
+        }), Ok(()));
+    }
 }

+ 58 - 4
src/socket/udp.rs

@@ -63,6 +63,8 @@ pub struct UdpSocket<'a, 'b: 'a> {
     endpoint:  IpEndpoint,
     rx_buffer: SocketBuffer<'a, 'b>,
     tx_buffer: SocketBuffer<'a, 'b>,
+    /// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets.
+    ttl:       Option<u8>
 }
 
 impl<'a, 'b> UdpSocket<'a, 'b> {
@@ -74,6 +76,7 @@ impl<'a, 'b> UdpSocket<'a, 'b> {
             endpoint:  IpEndpoint::default(),
             rx_buffer: rx_buffer,
             tx_buffer: tx_buffer,
+            ttl:       None
         })
     }
 
@@ -94,6 +97,33 @@ impl<'a, 'b> UdpSocket<'a, 'b> {
         self.endpoint
     }
 
+    /// Return the time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets.
+    ///
+    /// See also the [set_ttl](#method.set_ttl) method
+    pub fn ttl(&self) -> Option<u8> {
+        self.ttl
+    }
+
+    /// Set the time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets.
+    ///
+    /// A socket without an explicitly set TTL value uses the default [IANA recommended]
+    /// value (`64`).
+    ///
+    /// # Panics
+    ///
+    /// This function panics if a TTL value of `0` is given. See [RFC 1122 § 3.2.1.7].
+    ///
+    /// [IANA recommended]: https://www.iana.org/assignments/ip-parameters/ip-parameters.xhtml
+    /// [RFC 1122 § 3.2.1.7]: https://tools.ietf.org/html/rfc1122#section-3.2.1.7
+    pub fn set_ttl(&mut self, ttl: Option<u8>) {
+        // A host MUST NOT send a datagram with a Time-to-Live (TTL)
+        // value of 0
+        match ttl {
+            Some(0)  => { panic!("A TTL value of 0 is invalid for a sent packet"); },
+            catchall => self.ttl = catchall,
+        }
+    }
+
     /// Bind the socket to the given endpoint.
     ///
     /// This function returns `Err(Error::Illegal)` if the socket was open
@@ -200,6 +230,7 @@ impl<'a, 'b> UdpSocket<'a, 'b> {
             where F: FnOnce((IpRepr, UdpRepr)) -> Result<()> {
         let handle   = self.handle;
         let endpoint = self.endpoint;
+        let ttl = self.ttl.unwrap_or(64);
         self.tx_buffer.dequeue_one_with(|packet_buf| {
             net_trace!("{}:{}:{}: sending {} octets",
                        handle, endpoint,
@@ -214,7 +245,8 @@ impl<'a, 'b> UdpSocket<'a, 'b> {
                 src_addr:    endpoint.addr,
                 dst_addr:    packet_buf.endpoint.addr,
                 protocol:    IpProtocol::Udp,
-                payload_len: repr.buffer_len()
+                payload_len: repr.buffer_len(),
+                ttl:         ttl,
             };
             emit((ip_repr, repr))
         })
@@ -275,7 +307,8 @@ mod test {
         src_addr: LOCAL_IP,
         dst_addr: REMOTE_IP,
         protocol: IpProtocol::Udp,
-        payload_len: 8 + 6
+        payload_len: 8 + 6,
+        ttl: 64,
     };
     const LOCAL_UDP_REPR: UdpRepr = UdpRepr {
         src_port: LOCAL_PORT,
@@ -337,7 +370,8 @@ mod test {
         src_addr: Ipv4Address([10, 0, 0, 2]),
         dst_addr: Ipv4Address([10, 0, 0, 1]),
         protocol: IpProtocol::Udp,
-        payload_len: 8 + 6
+        payload_len: 8 + 6,
+        ttl: 64
     });
     const REMOTE_UDP_REPR: UdpRepr = UdpRepr {
         src_port: REMOTE_PORT,
@@ -407,7 +441,8 @@ mod test {
             src_addr: Ipv4Address([10, 0, 0, 2]),
             dst_addr: Ipv4Address([10, 0, 0, 10]),
             protocol: IpProtocol::Udp,
-            payload_len: 8 + 6
+            payload_len: 8 + 6,
+            ttl: 64
         });
 
         let mut port_bound_socket = socket(buffer(1), buffer(0));
@@ -418,4 +453,23 @@ mod test {
         assert_eq!(ip_bound_socket.bind(LOCAL_END), Ok(()));
         assert!(!ip_bound_socket.accepts(&ip_repr, &REMOTE_UDP_REPR));
     }
+
+    #[test]
+    fn test_set_ttl() {
+        let mut s = socket(buffer(0), buffer(1));
+        assert_eq!(s.bind(LOCAL_END), Ok(()));
+
+        s.set_ttl(Some(0x2a));
+        assert_eq!(s.send_slice(b"abcdef", REMOTE_END), Ok(()));
+        assert_eq!(s.dispatch(|(ip_repr, _)| {
+            assert_eq!(ip_repr, IpRepr::Unspecified{
+                src_addr: LOCAL_IP,
+                dst_addr: REMOTE_IP,
+                protocol: IpProtocol::Udp,
+                payload_len: 8 + 6,
+                ttl: 0x2a,
+            });
+            Ok(())
+        }), Ok(()));
+    }
 }

+ 1 - 0
src/wire/icmpv4.rs

@@ -423,6 +423,7 @@ impl<'a> Repr<'a> {
                         dst_addr: ip_packet.dst_addr(),
                         protocol: ip_packet.protocol(),
                         payload_len: payload.len(),
+                        ttl:      ip_packet.ttl()
                     },
                     data: payload
                 })

+ 26 - 8
src/wire/ip.rs

@@ -266,7 +266,8 @@ pub enum IpRepr {
         src_addr:    Address,
         dst_addr:    Address,
         protocol:    Protocol,
-        payload_len: usize
+        payload_len: usize,
+        ttl:         u8
     },
     Ipv4(Ipv4Repr),
     #[doc(hidden)]
@@ -336,6 +337,15 @@ impl IpRepr {
         }
     }
 
+    /// Return the TTL value.
+    pub fn ttl(&self) -> u8 {
+        match self {
+            &IpRepr::Unspecified { ttl, .. } => ttl,
+            &IpRepr::Ipv4(Ipv4Repr { ttl, .. }) => ttl,
+            &IpRepr::__Nonexhaustive => unreachable!()
+        }
+    }
+
     /// Convert an unspecified representation into a concrete one, or return
     /// `Err(Error::Unaddressable)` if not possible.
     ///
@@ -347,20 +357,20 @@ impl IpRepr {
             &IpRepr::Unspecified {
                 src_addr: Address::Ipv4(src_addr),
                 dst_addr: Address::Ipv4(dst_addr),
-                protocol, payload_len
+                protocol, payload_len, ttl
             } => {
                 Ok(IpRepr::Ipv4(Ipv4Repr {
                     src_addr:    src_addr,
                     dst_addr:    dst_addr,
                     protocol:    protocol,
-                    payload_len: payload_len
+                    payload_len: payload_len, ttl
                 }))
             }
 
             &IpRepr::Unspecified {
                 src_addr: Address::Unspecified,
                 dst_addr: Address::Ipv4(dst_addr),
-                protocol, payload_len
+                protocol, payload_len, ttl
             } => {
                 let mut src_addr = None;
                 for cidr in fallback_src_addrs {
@@ -374,9 +384,7 @@ impl IpRepr {
                 }
                 Ok(IpRepr::Ipv4(Ipv4Repr {
                     src_addr:    src_addr.ok_or(Error::Unaddressable)?,
-                    dst_addr:    dst_addr,
-                    protocol:    protocol,
-                    payload_len: payload_len
+                    dst_addr, protocol, payload_len, ttl
                 }))
             }
 
@@ -534,12 +542,14 @@ mod test {
                 src_addr: IpAddress::Ipv4(ip_addr_a),
                 dst_addr: IpAddress::Ipv4(ip_addr_b),
                 protocol: proto,
-                payload_len
+                ttl:      0x2a,
+                payload_len,
             }.lower(&[]),
             Ok(IpRepr::Ipv4(Ipv4Repr{
                 src_addr: ip_addr_a,
                 dst_addr: ip_addr_b,
                 protocol: proto,
+                ttl:      0x2a,
                 payload_len
             }))
         );
@@ -549,6 +559,7 @@ mod test {
                 src_addr: IpAddress::Unspecified,
                 dst_addr: IpAddress::Ipv4(ip_addr_b),
                 protocol: proto,
+                ttl:      64,
                 payload_len
             }.lower(&[]),
             Err(Error::Unaddressable)
@@ -559,12 +570,14 @@ mod test {
                 src_addr: IpAddress::Unspecified,
                 dst_addr: IpAddress::Ipv4(ip_addr_b),
                 protocol: proto,
+                ttl:      64,
                 payload_len
             }.lower(&[IpCidr::new(IpAddress::Ipv4(ip_addr_a), 24)]),
             Ok(IpRepr::Ipv4(Ipv4Repr{
                 src_addr: ip_addr_a,
                 dst_addr: ip_addr_b,
                 protocol: proto,
+                ttl:      64,
                 payload_len
             }))
         );
@@ -574,12 +587,14 @@ mod test {
                 src_addr: ip_addr_a,
                 dst_addr: ip_addr_b,
                 protocol: proto,
+                ttl:      255,
                 payload_len
             }).lower(&[]),
             Ok(IpRepr::Ipv4(Ipv4Repr{
                 src_addr: ip_addr_a,
                 dst_addr: ip_addr_b,
                 protocol: proto,
+                ttl:      255,
                 payload_len
             }))
         );
@@ -589,6 +604,7 @@ mod test {
                 src_addr: Ipv4Address::UNSPECIFIED,
                 dst_addr: ip_addr_b,
                 protocol: proto,
+                ttl:      255,
                 payload_len
             }).lower(&[]),
             Err(Error::Unaddressable)
@@ -599,12 +615,14 @@ mod test {
                 src_addr: Ipv4Address::UNSPECIFIED,
                 dst_addr: ip_addr_b,
                 protocol: proto,
+                ttl:      64,
                 payload_len
             }).lower(&[IpCidr::new(IpAddress::Ipv4(ip_addr_a), 24)]),
             Ok(IpRepr::Ipv4(Ipv4Repr{
                 src_addr: ip_addr_a,
                 dst_addr: ip_addr_b,
                 protocol: proto,
+                ttl:      64,
                 payload_len
             }))
         );

+ 18 - 4
src/wire/ipv4.rs

@@ -448,7 +448,8 @@ pub struct Repr {
     pub src_addr:    Address,
     pub dst_addr:    Address,
     pub protocol:    Protocol,
-    pub payload_len: usize
+    pub payload_len: usize,
+    pub ttl:         u8
 }
 
 impl Repr {
@@ -474,7 +475,8 @@ impl Repr {
             src_addr:    packet.src_addr(),
             dst_addr:    packet.dst_addr(),
             protocol:    packet.protocol(),
-            payload_len: payload_len
+            payload_len: payload_len,
+            ttl:         packet.ttl()
         })
     }
 
@@ -497,7 +499,7 @@ impl Repr {
         packet.set_more_frags(false);
         packet.set_dont_frag(true);
         packet.set_frag_offset(0);
-        packet.set_ttl(64);
+        packet.set_ttl(self.ttl);
         packet.set_protocol(self.protocol);
         packet.set_src_addr(self.src_addr);
         packet.set_dst_addr(self.dst_addr);
@@ -722,7 +724,8 @@ mod test {
             src_addr:    Address([0x11, 0x12, 0x13, 0x14]),
             dst_addr:    Address([0x21, 0x22, 0x23, 0x24]),
             protocol:    Protocol::Icmp,
-            payload_len: 4
+            payload_len: 4,
+            ttl:         64
         }
     }
 
@@ -733,6 +736,17 @@ mod test {
         assert_eq!(repr, packet_repr());
     }
 
+    #[test]
+    fn test_parse_bad_version() {
+        let mut bytes = vec![0; 24];
+        bytes.copy_from_slice(&REPR_PACKET_BYTES[..]);
+        let mut packet = Packet::new(&mut bytes);
+        packet.set_version(6);
+        packet.fill_checksum();
+        let packet = Packet::new(&*packet.into_inner());
+        assert_eq!(Repr::parse(&packet, &ChecksumCapabilities::default()), Err(Error::Malformed));
+    }
+
     #[test]
     fn test_parse_total_len_underflow() {
         let mut bytes = vec![0; 24];

+ 2 - 1
src/wire/mod.rs

@@ -52,7 +52,8 @@ let repr = Ipv4Repr {
     src_addr:    Ipv4Address::new(10, 0, 0, 1),
     dst_addr:    Ipv4Address::new(10, 0, 0, 2),
     protocol:    IpProtocol::Tcp,
-    payload_len: 10
+    payload_len: 10,
+    ttl:         64
 };
 let mut buffer = vec![0; repr.buffer_len() + repr.payload_len];
 { // emission