Browse Source

Merge #579

579: Remove IpRepr::Unspecified r=Dirbaio a=Dirbaio

Implement simplifications unlocked by adding the `Context` parameter to socket methods. 

- tcp: immediately choose src addr on connect. Now that we have access to `Context` from `.connect()`, we don't have to delay setting it anymore.
- Remove IpRepr::Unspecified and lowering. Choosing the right source IP address is now responsibility of the individual sockets.



Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
bors[bot] 3 years ago
parent
commit
feedb3fbc1
13 changed files with 338 additions and 624 deletions
  1. 1 0
      Cargo.toml
  2. 1 1
      benches/bench.rs
  3. 60 34
      src/iface/interface.rs
  4. 6 6
      src/socket/dhcpv4.rs
  5. 15 15
      src/socket/icmp.rs
  6. 6 6
      src/socket/raw.rs
  7. 94 83
      src/socket/tcp.rs
  8. 74 84
      src/socket/udp.rs
  9. 1 1
      src/wire/icmpv4.rs
  10. 53 381
      src/wire/ip.rs
  11. 19 12
      src/wire/ipv4.rs
  12. 7 0
      src/wire/ipv6.rs
  13. 1 1
      src/wire/mod.rs

+ 1 - 0
Cargo.toml

@@ -22,6 +22,7 @@ log = { version = "0.4.4", default-features = false, optional = true }
 libc = { version = "0.2.18", optional = true }
 bitflags = { version = "1.0", default-features = false }
 defmt = { version = "0.3", optional = true }
+cfg-if = "1.0.0"
 
 [dev-dependencies]
 env_logger = "0.9"

+ 1 - 1
benches/bench.rs

@@ -85,7 +85,7 @@ mod wire {
         let repr = Ipv4Repr {
             src_addr: Ipv4Address([192, 168, 1, 1]),
             dst_addr: Ipv4Address([192, 168, 1, 2]),
-            protocol: IpProtocol::Tcp,
+            next_header: IpProtocol::Tcp,
             payload_len: 100,
             hop_limit: 64,
         };

+ 60 - 34
src/iface/interface.rs

@@ -923,7 +923,8 @@ where
                     (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => {
                         respond!(inner, IpPacket::Icmpv6((ipv6_repr, icmpv6_repr)))
                     }
-                    _ => Err(Error::Unaddressable),
+                    #[allow(unreachable_patterns)]
+                    _ => unreachable!(),
                 }),
                 #[cfg(feature = "socket-udp")]
                 Socket::Udp(socket) => socket.dispatch(inner, |inner, response| {
@@ -1055,6 +1056,18 @@ impl<'a> InterfaceInner<'a> {
         &mut self.rand
     }
 
+    #[allow(unused)] // unused depending on which sockets are enabled
+    pub(crate) fn get_source_address(&mut self, dst_addr: IpAddress) -> Option<IpAddress> {
+        let v = dst_addr.version().unwrap();
+        for cidr in self.ip_addrs.iter() {
+            let addr = cidr.address();
+            if addr.version() == Some(v) {
+                return Some(addr);
+            }
+        }
+        None
+    }
+
     #[cfg(test)]
     pub(crate) fn mock() -> Self {
         Self {
@@ -1080,7 +1093,15 @@ impl<'a> InterfaceInner<'a> {
             },
             now: Instant::from_millis_const(0),
 
-            ip_addrs: ManagedSlice::Owned(vec![]),
+            ip_addrs: ManagedSlice::Owned(vec![
+                #[cfg(feature = "proto-ipv4")]
+                IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address::new(192, 168, 1, 1), 24)),
+                #[cfg(feature = "proto-ipv6")]
+                IpCidr::Ipv6(Ipv6Cidr::new(
+                    Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]),
+                    64,
+                )),
+            ]),
             rand: Rand::new(1234),
             routes: Routes::new(&mut [][..]),
 
@@ -1572,7 +1593,7 @@ impl<'a> InterfaceInner<'a> {
 
         #[cfg(feature = "socket-dhcpv4")]
         {
-            if ipv4_repr.protocol == IpProtocol::Udp && self.hardware_addr.is_some() {
+            if ipv4_repr.next_header == IpProtocol::Udp && self.hardware_addr.is_some() {
                 // First check for source and dest ports, then do `UdpRepr::parse` if they match.
                 // This way we avoid validating the UDP checksum twice for all non-DHCP UDP packets (one here, one in `process_udp`)
                 let udp_packet = UdpPacket::new_checked(ip_payload)?;
@@ -1617,7 +1638,7 @@ impl<'a> InterfaceInner<'a> {
             }
         }
 
-        match ipv4_repr.protocol {
+        match ipv4_repr.next_header {
             IpProtocol::Icmp => self.process_icmpv4(sockets, ip_repr, ip_payload),
 
             #[cfg(feature = "proto-igmp")]
@@ -1791,7 +1812,8 @@ impl<'a> InterfaceInner<'a> {
                     };
                     Ok(self.icmpv6_reply(ipv6_repr, icmp_reply_repr))
                 }
-                _ => Err(Error::Unrecognized),
+                #[allow(unreachable_patterns)]
+                _ => unreachable!(),
             },
 
             // Ignore any echo replies.
@@ -1801,7 +1823,8 @@ impl<'a> InterfaceInner<'a> {
             #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
             Icmpv6Repr::Ndisc(repr) if ip_repr.hop_limit() == 0xff => match ip_repr {
                 IpRepr::Ipv6(ipv6_repr) => self.process_ndisc(ipv6_repr, repr),
-                _ => Ok(None),
+                #[allow(unreachable_patterns)]
+                _ => unreachable!(),
             },
 
             // Don't report an error if a packet with unknown type
@@ -1976,7 +1999,8 @@ impl<'a> InterfaceInner<'a> {
                 };
                 match ip_repr {
                     IpRepr::Ipv4(ipv4_repr) => Ok(self.icmpv4_reply(ipv4_repr, icmp_reply_repr)),
-                    _ => Err(Error::Unrecognized),
+                    #[allow(unreachable_patterns)]
+                    _ => unreachable!(),
                 }
             }
 
@@ -2007,7 +2031,7 @@ impl<'a> InterfaceInner<'a> {
             let ipv4_reply_repr = Ipv4Repr {
                 src_addr: ipv4_repr.dst_addr,
                 dst_addr: ipv4_repr.src_addr,
-                protocol: IpProtocol::Icmp,
+                next_header: IpProtocol::Icmp,
                 payload_len: icmp_repr.buffer_len(),
                 hop_limit: 64,
             };
@@ -2020,7 +2044,7 @@ impl<'a> InterfaceInner<'a> {
                         let ipv4_reply_repr = Ipv4Repr {
                             src_addr: src_addr,
                             dst_addr: ipv4_repr.src_addr,
-                            protocol: IpProtocol::Icmp,
+                            next_header: IpProtocol::Icmp,
                             payload_len: icmp_repr.buffer_len(),
                             hop_limit: 64,
                         };
@@ -2113,7 +2137,6 @@ impl<'a> InterfaceInner<'a> {
                 };
                 Ok(self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr))
             }
-            IpRepr::Unspecified { .. } => Err(Error::Unaddressable),
         }
     }
 
@@ -2390,7 +2413,9 @@ impl<'a> InterfaceInner<'a> {
     }
 
     fn dispatch_ip<Tx: TxToken>(&mut self, tx_token: Tx, packet: IpPacket) -> Result<()> {
-        let ip_repr = packet.ip_repr().lower(&self.ip_addrs)?;
+        let ip_repr = packet.ip_repr();
+        assert!(!ip_repr.src_addr().is_unspecified());
+        assert!(!ip_repr.dst_addr().is_unspecified());
 
         match self.caps.medium {
             #[cfg(feature = "medium-ethernet")]
@@ -2413,7 +2438,6 @@ impl<'a> InterfaceInner<'a> {
                         IpRepr::Ipv4(_) => frame.set_ethertype(EthernetProtocol::Ipv4),
                         #[cfg(feature = "proto-ipv6")]
                         IpRepr::Ipv6(_) => frame.set_ethertype(EthernetProtocol::Ipv6),
-                        _ => return,
                     }
 
                     ip_repr.emit(frame.payload_mut(), &caps.checksum);
@@ -2443,7 +2467,9 @@ impl<'a> InterfaceInner<'a> {
 
     #[cfg(feature = "medium-ieee802154")]
     fn dispatch_ieee802154<Tx: TxToken>(&mut self, tx_token: Tx, packet: IpPacket) -> Result<()> {
-        let ip_repr = packet.ip_repr().lower(&self.ip_addrs)?;
+        let ip_repr = packet.ip_repr();
+        assert!(!ip_repr.src_addr().is_unspecified());
+        assert!(!ip_repr.dst_addr().is_unspecified());
 
         match self.caps.medium {
             #[cfg(feature = "medium-ieee802154")]
@@ -2599,7 +2625,7 @@ impl<'a> InterfaceInner<'a> {
                 src_addr: iface_addr,
                 // Send to the group being reported
                 dst_addr: group_addr,
-                protocol: IpProtocol::Igmp,
+                next_header: IpProtocol::Igmp,
                 payload_len: igmp_repr.buffer_len(),
                 hop_limit: 1,
                 // TODO: add Router Alert IPv4 header option. See
@@ -2618,7 +2644,7 @@ impl<'a> InterfaceInner<'a> {
                 Ipv4Repr {
                     src_addr: iface_addr,
                     dst_addr: Ipv4Address::MULTICAST_ALL_ROUTERS,
-                    protocol: IpProtocol::Igmp,
+                    next_header: IpProtocol::Igmp,
                     payload_len: igmp_repr.buffer_len(),
                     hop_limit: 1,
                 },
@@ -2746,7 +2772,7 @@ mod test {
         let repr = IpRepr::Ipv4(Ipv4Repr {
             src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]),
             dst_addr: Ipv4Address::BROADCAST,
-            protocol: IpProtocol::Unknown(0x0c),
+            next_header: IpProtocol::Unknown(0x0c),
             payload_len: 0,
             hop_limit: 0x40,
         });
@@ -2805,7 +2831,7 @@ mod test {
         let repr = IpRepr::Ipv4(Ipv4Repr {
             src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]),
             dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]),
-            protocol: IpProtocol::Unknown(0x0c),
+            next_header: IpProtocol::Unknown(0x0c),
             payload_len: 0,
             hop_limit: 0x40,
         });
@@ -2821,7 +2847,7 @@ mod test {
             header: Ipv4Repr {
                 src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]),
                 dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]),
-                protocol: IpProtocol::Unknown(12),
+                next_header: IpProtocol::Unknown(12),
                 payload_len: 0,
                 hop_limit: 64,
             },
@@ -2832,7 +2858,7 @@ mod test {
             Ipv4Repr {
                 src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]),
                 dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]),
-                protocol: IpProtocol::Icmp,
+                next_header: IpProtocol::Icmp,
                 payload_len: icmp_repr.buffer_len(),
                 hop_limit: 64,
             },
@@ -2922,7 +2948,7 @@ mod test {
         let ip_repr = IpRepr::Ipv4(Ipv4Repr {
             src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]),
             dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]),
-            protocol: IpProtocol::Udp,
+            next_header: IpProtocol::Udp,
             payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(),
             hop_limit: 64,
         });
@@ -2946,7 +2972,7 @@ mod test {
             header: Ipv4Repr {
                 src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]),
                 dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]),
-                protocol: IpProtocol::Udp,
+                next_header: IpProtocol::Udp,
                 payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(),
                 hop_limit: 64,
             },
@@ -2956,7 +2982,7 @@ mod test {
             Ipv4Repr {
                 src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]),
                 dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]),
-                protocol: IpProtocol::Icmp,
+                next_header: IpProtocol::Icmp,
                 payload_len: icmp_repr.buffer_len(),
                 hop_limit: 64,
             },
@@ -2975,7 +3001,7 @@ mod test {
         let ip_repr = IpRepr::Ipv4(Ipv4Repr {
             src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]),
             dst_addr: Ipv4Address::BROADCAST,
-            protocol: IpProtocol::Udp,
+            next_header: IpProtocol::Udp,
             payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(),
             hop_limit: 64,
         });
@@ -3046,7 +3072,7 @@ mod test {
         let ip_repr = IpRepr::Ipv4(Ipv4Repr {
             src_addr: src_ip,
             dst_addr: Ipv4Address::BROADCAST,
-            protocol: IpProtocol::Udp,
+            next_header: IpProtocol::Udp,
             payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(),
             hop_limit: 0x40,
         });
@@ -3106,7 +3132,7 @@ mod test {
         let ipv4_repr = Ipv4Repr {
             src_addr: src_ipv4_addr,
             dst_addr: Ipv4Address::BROADCAST,
-            protocol: IpProtocol::Icmp,
+            next_header: IpProtocol::Icmp,
             hop_limit: 64,
             payload_len: icmpv4_repr.buffer_len(),
         };
@@ -3134,7 +3160,7 @@ mod test {
         let expected_ipv4_repr = Ipv4Repr {
             src_addr: our_ipv4_addr,
             dst_addr: src_ipv4_addr,
-            protocol: IpProtocol::Icmp,
+            next_header: IpProtocol::Icmp,
             hop_limit: 64,
             payload_len: expected_icmpv4_repr.buffer_len(),
         };
@@ -3192,7 +3218,7 @@ mod test {
         let ip_repr = Ipv4Repr {
             src_addr: src_addr,
             dst_addr: dst_addr,
-            protocol: IpProtocol::Udp,
+            next_header: IpProtocol::Udp,
             hop_limit: 64,
             payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN,
         };
@@ -3231,7 +3257,7 @@ mod test {
         let expected_ip_repr = Ipv4Repr {
             src_addr: dst_addr,
             dst_addr: src_addr,
-            protocol: IpProtocol::Icmp,
+            next_header: IpProtocol::Icmp,
             hop_limit: 64,
             payload_len: expected_icmp_repr.buffer_len(),
         };
@@ -3541,7 +3567,7 @@ mod test {
         let ipv4_repr = Ipv4Repr {
             src_addr: Ipv4Address::new(0x7f, 0x00, 0x00, 0x02),
             dst_addr: Ipv4Address::new(0x7f, 0x00, 0x00, 0x01),
-            protocol: IpProtocol::Icmp,
+            next_header: IpProtocol::Icmp,
             payload_len: 24,
             hop_limit: 64,
         };
@@ -3714,7 +3740,7 @@ mod test {
         let reports = recv_igmp(&mut iface, timestamp);
         assert_eq!(reports.len(), 2);
         for (i, group_addr) in groups.iter().enumerate() {
-            assert_eq!(reports[i].0.protocol, IpProtocol::Igmp);
+            assert_eq!(reports[i].0.next_header, IpProtocol::Igmp);
             assert_eq!(reports[i].0.dst_addr, *group_addr);
             assert_eq!(
                 reports[i].1,
@@ -3758,7 +3784,7 @@ mod test {
         let leaves = recv_igmp(&mut iface, timestamp);
         assert_eq!(leaves.len(), 2);
         for (i, group_addr) in groups.iter().cloned().enumerate() {
-            assert_eq!(leaves[i].0.protocol, IpProtocol::Igmp);
+            assert_eq!(leaves[i].0.next_header, IpProtocol::Igmp);
             assert_eq!(leaves[i].0.dst_addr, Ipv4Address::MULTICAST_ALL_ROUTERS);
             assert_eq!(leaves[i].1, IgmpRepr::LeaveGroup { group_addr });
         }
@@ -3804,7 +3830,7 @@ mod test {
         let ipv4_repr = Ipv4Repr {
             src_addr: src_addr,
             dst_addr: dst_addr,
-            protocol: IpProtocol::Udp,
+            next_header: IpProtocol::Udp,
             hop_limit: 64,
             payload_len: udp_repr.header_len() + PAYLOAD_LEN,
         };
@@ -3874,7 +3900,7 @@ mod test {
         let ipv4_repr = Ipv4Repr {
             src_addr: src_addr,
             dst_addr: dst_addr,
-            protocol: IpProtocol::Udp,
+            next_header: IpProtocol::Udp,
             hop_limit: 64,
             payload_len: udp_repr.header_len() + PAYLOAD_LEN,
         };
@@ -3965,7 +3991,7 @@ mod test {
         let ipv4_repr = Ipv4Repr {
             src_addr: src_addr,
             dst_addr: dst_addr,
-            protocol: IpProtocol::Udp,
+            next_header: IpProtocol::Udp,
             hop_limit: 64,
             payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(),
         };

+ 6 - 6
src/socket/dhcpv4.rs

@@ -427,7 +427,7 @@ impl Dhcpv4Socket {
         let mut ipv4_repr = Ipv4Repr {
             src_addr: Ipv4Address::UNSPECIFIED,
             dst_addr: Ipv4Address::BROADCAST,
-            protocol: IpProtocol::Udp,
+            next_header: IpProtocol::Udp,
             payload_len: 0, // filled right before emit
             hop_limit: 64,
         };
@@ -606,7 +606,7 @@ mod test {
             let _ = s
                 .socket
                 .dispatch(&mut s.cx, |_, (mut ip_repr, udp_repr, dhcp_repr)| {
-                    assert_eq!(ip_repr.protocol, IpProtocol::Udp);
+                    assert_eq!(ip_repr.next_header, IpProtocol::Udp);
                     assert_eq!(
                         ip_repr.payload_len,
                         udp_repr.header_len() + dhcp_repr.buffer_len()
@@ -671,7 +671,7 @@ mod test {
     const IP_BROADCAST: Ipv4Repr = Ipv4Repr {
         src_addr: Ipv4Address::UNSPECIFIED,
         dst_addr: Ipv4Address::BROADCAST,
-        protocol: IpProtocol::Udp,
+        next_header: IpProtocol::Udp,
         payload_len: 0,
         hop_limit: 64,
     };
@@ -679,7 +679,7 @@ mod test {
     const IP_SERVER_BROADCAST: Ipv4Repr = Ipv4Repr {
         src_addr: SERVER_IP,
         dst_addr: Ipv4Address::BROADCAST,
-        protocol: IpProtocol::Udp,
+        next_header: IpProtocol::Udp,
         payload_len: 0,
         hop_limit: 64,
     };
@@ -687,7 +687,7 @@ mod test {
     const IP_RECV: Ipv4Repr = Ipv4Repr {
         src_addr: SERVER_IP,
         dst_addr: MY_IP,
-        protocol: IpProtocol::Udp,
+        next_header: IpProtocol::Udp,
         payload_len: 0,
         hop_limit: 64,
     };
@@ -695,7 +695,7 @@ mod test {
     const IP_SEND: Ipv4Repr = Ipv4Repr {
         src_addr: MY_IP,
         dst_addr: SERVER_IP,
-        protocol: IpProtocol::Udp,
+        next_header: IpProtocol::Udp,
         payload_len: 0,
         hop_limit: 64,
     };

+ 15 - 15
src/socket/icmp.rs

@@ -453,7 +453,7 @@ impl<'a> IcmpSocket<'a> {
                     let ip_repr = IpRepr::Ipv4(Ipv4Repr {
                         src_addr: Ipv4Address::default(),
                         dst_addr: ipv4_addr,
-                        protocol: IpProtocol::Icmp,
+                        next_header: IpProtocol::Icmp,
                         payload_len: repr.buffer_len(),
                         hop_limit: hop_limit,
                     });
@@ -549,7 +549,7 @@ mod test_ipv4 {
     static LOCAL_IPV4_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr {
         src_addr: Ipv4Address::UNSPECIFIED,
         dst_addr: REMOTE_IPV4,
-        protocol: IpProtocol::Icmp,
+        next_header: IpProtocol::Icmp,
         payload_len: 24,
         hop_limit: 0x40,
     });
@@ -557,7 +557,7 @@ mod test_ipv4 {
     static REMOTE_IPV4_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr {
         src_addr: REMOTE_IPV4,
         dst_addr: LOCAL_IPV4,
-        protocol: IpProtocol::Icmp,
+        next_header: IpProtocol::Icmp,
         payload_len: 24,
         hop_limit: 0x40,
     });
@@ -650,7 +650,7 @@ mod test_ipv4 {
                     IpRepr::Ipv4(Ipv4Repr {
                         src_addr: Ipv4Address::UNSPECIFIED,
                         dst_addr: REMOTE_IPV4,
-                        protocol: IpProtocol::Icmp,
+                        next_header: IpProtocol::Icmp,
                         payload_len: ECHOV4_REPR.buffer_len(),
                         hop_limit: 0x2a,
                     })
@@ -741,19 +741,19 @@ mod test_ipv4 {
             header: Ipv4Repr {
                 src_addr: LOCAL_IPV4,
                 dst_addr: REMOTE_IPV4,
-                protocol: IpProtocol::Icmp,
+                next_header: IpProtocol::Icmp,
                 payload_len: 12,
                 hop_limit: 0x40,
             },
             data: data,
         };
-        let ip_repr = IpRepr::Unspecified {
-            src_addr: REMOTE_IPV4.into(),
-            dst_addr: LOCAL_IPV4.into(),
-            protocol: IpProtocol::Icmp,
+        let ip_repr = IpRepr::Ipv4(Ipv4Repr {
+            src_addr: REMOTE_IPV4,
+            dst_addr: LOCAL_IPV4,
+            next_header: IpProtocol::Icmp,
             payload_len: icmp_repr.buffer_len(),
             hop_limit: 0x40,
-        };
+        });
 
         assert!(!socket.can_recv());
 
@@ -1015,13 +1015,13 @@ mod test_ipv6 {
             },
             data: data,
         };
-        let ip_repr = IpRepr::Unspecified {
-            src_addr: REMOTE_IPV6.into(),
-            dst_addr: LOCAL_IPV6.into(),
-            protocol: IpProtocol::Icmpv6,
+        let ip_repr = IpRepr::Ipv6(Ipv6Repr {
+            src_addr: REMOTE_IPV6,
+            dst_addr: LOCAL_IPV6,
+            next_header: IpProtocol::Icmpv6,
             payload_len: icmp_repr.buffer_len(),
             hop_limit: 0x40,
-        };
+        });
 
         assert!(!socket.can_recv());
 

+ 6 - 6
src/socket/raw.rs

@@ -205,7 +205,7 @@ impl<'a> RawSocket<'a> {
         if ip_repr.version() != self.ip_version {
             return false;
         }
-        if ip_repr.protocol() != self.ip_protocol {
+        if ip_repr.next_header() != self.ip_protocol {
             return false;
         }
 
@@ -244,7 +244,7 @@ impl<'a> RawSocket<'a> {
         F: FnOnce(&mut Context, (IpRepr, &[u8])) -> Result<()>,
     {
         fn prepare<'a>(
-            protocol: IpProtocol,
+            next_header: IpProtocol,
             buffer: &'a mut [u8],
             _checksum_caps: &ChecksumCapabilities,
         ) -> Result<(IpRepr, &'a [u8])> {
@@ -252,7 +252,7 @@ impl<'a> RawSocket<'a> {
                 #[cfg(feature = "proto-ipv4")]
                 IpVersion::Ipv4 => {
                     let mut packet = Ipv4Packet::new_checked(buffer)?;
-                    if packet.protocol() != protocol {
+                    if packet.next_header() != next_header {
                         return Err(Error::Unaddressable);
                     }
                     if _checksum_caps.ipv4.tx() {
@@ -270,7 +270,7 @@ impl<'a> RawSocket<'a> {
                 #[cfg(feature = "proto-ipv6")]
                 IpVersion::Ipv6 => {
                     let packet = Ipv6Packet::new_checked(buffer)?;
-                    if packet.next_header() != protocol {
+                    if packet.next_header() != next_header {
                         return Err(Error::Unaddressable);
                     }
                     let packet = Ipv6Packet::new_unchecked(&*packet.into_inner());
@@ -358,7 +358,7 @@ mod test {
         pub const HEADER_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr {
             src_addr: Ipv4Address([10, 0, 0, 1]),
             dst_addr: Ipv4Address([10, 0, 0, 2]),
-            protocol: IpProtocol::Unknown(IP_PROTO),
+            next_header: IpProtocol::Unknown(IP_PROTO),
             payload_len: 4,
             hop_limit: 64,
         });
@@ -521,7 +521,7 @@ mod test {
             assert_eq!(socket.dispatch(&mut cx, |_, _| unreachable!()), Ok(()));
 
             let mut wrong_protocol = ipv4_locals::PACKET_BYTES;
-            Ipv4Packet::new_unchecked(&mut wrong_protocol).set_protocol(IpProtocol::Tcp);
+            Ipv4Packet::new_unchecked(&mut wrong_protocol).set_next_header(IpProtocol::Tcp);
 
             assert_eq!(socket.send_slice(&wrong_protocol[..]), Ok(()));
             assert_eq!(socket.dispatch(&mut cx, |_, _| unreachable!()), Ok(()));

+ 94 - 83
src/socket/tcp.rs

@@ -718,8 +718,8 @@ impl<'a> TcpSocket<'a> {
         T: Into<IpEndpoint>,
         U: Into<IpEndpoint>,
     {
-        let remote_endpoint = remote_endpoint.into();
-        let local_endpoint = local_endpoint.into();
+        let remote_endpoint: IpEndpoint = remote_endpoint.into();
+        let mut local_endpoint: IpEndpoint = local_endpoint.into();
 
         if self.is_open() {
             return Err(Error::Illegal);
@@ -731,17 +731,12 @@ impl<'a> TcpSocket<'a> {
             return Err(Error::Unaddressable);
         }
 
-        // If local address is not provided, use an unspecified address but a specified protocol.
-        // This lets us lower IpRepr later to determine IP header size and calculate MSS,
-        // but without committing to a specific address right away.
-        let local_addr = match local_endpoint.addr {
-            IpAddress::Unspecified => remote_endpoint.addr.as_unspecified(),
-            ip => ip,
-        };
-        let local_endpoint = IpEndpoint {
-            addr: local_addr,
-            ..local_endpoint
-        };
+        // If local address is not provided, choose it automatically.
+        if local_endpoint.addr.is_unspecified() {
+            local_endpoint.addr = cx
+                .get_source_address(remote_endpoint.addr)
+                .ok_or(Error::Unaddressable)?;
+        }
 
         self.reset();
         self.local_endpoint = local_endpoint;
@@ -1142,13 +1137,13 @@ impl<'a> TcpSocket<'a> {
             sack_ranges: [None, None, None],
             payload: &[],
         };
-        let ip_reply_repr = IpRepr::Unspecified {
-            src_addr: ip_repr.dst_addr(),
-            dst_addr: ip_repr.src_addr(),
-            protocol: IpProtocol::Tcp,
-            payload_len: reply_repr.buffer_len(),
-            hop_limit: 64,
-        };
+        let ip_reply_repr = IpRepr::new(
+            ip_repr.dst_addr(),
+            ip_repr.src_addr(),
+            IpProtocol::Tcp,
+            reply_repr.buffer_len(),
+            64,
+        );
         (ip_reply_repr, reply_repr)
     }
 
@@ -1626,7 +1621,6 @@ impl<'a> TcpSocket<'a> {
                     self.remote_mss = max_seg_size as usize;
                 }
 
-                self.local_endpoint = IpEndpoint::new(ip_repr.dst_addr(), repr.dst_port);
                 self.remote_seq_no = repr.seq_number + 1;
                 self.remote_last_seq = self.local_seq_no + 1;
                 self.remote_last_ack = Some(repr.seq_number);
@@ -2142,14 +2136,13 @@ impl<'a> TcpSocket<'a> {
 
         // Construct the lowered IP representation.
         // We might need this to calculate the MSS, so do it early.
-        let mut ip_repr = IpRepr::Unspecified {
-            src_addr: self.local_endpoint.addr,
-            dst_addr: self.remote_endpoint.addr,
-            protocol: IpProtocol::Tcp,
-            hop_limit: self.hop_limit.unwrap_or(64),
-            payload_len: 0,
-        }
-        .lower(&[])?;
+        let mut ip_repr = IpRepr::new(
+            self.local_endpoint.addr,
+            self.remote_endpoint.addr,
+            IpProtocol::Tcp,
+            0,
+            self.hop_limit.unwrap_or(64),
+        );
 
         // Construct the basic TCP representation, an empty ACK packet.
         // We'll adjust this to be more specific as needed.
@@ -2423,8 +2416,7 @@ impl<'a> fmt::Write for TcpSocket<'a> {
 #[cfg(test)]
 mod test {
     use super::*;
-    use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_UNSPECIFIED};
-    use crate::wire::{IpAddress, IpCidr, IpRepr};
+    use crate::wire::{IpAddress, IpRepr};
     use core::i32;
     use std::ops::{Deref, DerefMut};
     use std::vec::Vec;
@@ -2436,23 +2428,53 @@ mod test {
     const LOCAL_PORT: u16 = 80;
     const REMOTE_PORT: u16 = 49500;
     const LOCAL_END: IpEndpoint = IpEndpoint {
-        addr: MOCK_IP_ADDR_1,
+        addr: LOCAL_ADDR.into_address(),
         port: LOCAL_PORT,
     };
     const REMOTE_END: IpEndpoint = IpEndpoint {
-        addr: MOCK_IP_ADDR_2,
+        addr: REMOTE_ADDR.into_address(),
         port: REMOTE_PORT,
     };
     const LOCAL_SEQ: TcpSeqNumber = TcpSeqNumber(10000);
     const REMOTE_SEQ: TcpSeqNumber = TcpSeqNumber(-10001);
 
-    const SEND_IP_TEMPL: IpRepr = IpRepr::Unspecified {
-        src_addr: MOCK_IP_ADDR_1,
-        dst_addr: MOCK_IP_ADDR_2,
-        protocol: IpProtocol::Tcp,
+    cfg_if::cfg_if! {
+        if #[cfg(feature = "proto-ipv4")] {
+            use crate::wire::Ipv4Address as IpvXAddress;
+            use crate::wire::Ipv4Repr as IpvXRepr;
+            use IpRepr::Ipv4 as IpReprIpvX;
+
+            const LOCAL_ADDR: IpvXAddress = IpvXAddress([192, 168, 1, 1]);
+            const REMOTE_ADDR: IpvXAddress = IpvXAddress([192, 168, 1, 2]);
+            const OTHER_ADDR: IpvXAddress = IpvXAddress([192, 168, 1, 3]);
+
+            const BASE_MSS: u16 = 1460;
+        } else {
+            use crate::wire::Ipv6Address as IpvXAddress;
+            use crate::wire::Ipv6Repr as IpvXRepr;
+            use IpRepr::Ipv6 as IpReprIpvX;
+
+            const LOCAL_ADDR: IpvXAddress = IpvXAddress([
+                0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+            ]);
+            const REMOTE_ADDR: IpvXAddress = IpvXAddress([
+                0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
+            ]);
+            const OTHER_ADDR: IpvXAddress = IpvXAddress([
+                0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
+            ]);
+
+            const BASE_MSS: u16 = 1440;
+        }
+    }
+
+    const SEND_IP_TEMPL: IpRepr = IpReprIpvX(IpvXRepr {
+        src_addr: LOCAL_ADDR,
+        dst_addr: REMOTE_ADDR,
+        next_header: IpProtocol::Tcp,
         payload_len: 20,
         hop_limit: 64,
-    };
+    });
     const SEND_TEMPL: TcpRepr<'static> = TcpRepr {
         src_port: REMOTE_PORT,
         dst_port: LOCAL_PORT,
@@ -2466,13 +2488,13 @@ mod test {
         sack_ranges: [None, None, None],
         payload: &[],
     };
-    const _RECV_IP_TEMPL: IpRepr = IpRepr::Unspecified {
-        src_addr: MOCK_IP_ADDR_1,
-        dst_addr: MOCK_IP_ADDR_2,
-        protocol: IpProtocol::Tcp,
+    const _RECV_IP_TEMPL: IpRepr = IpReprIpvX(IpvXRepr {
+        src_addr: LOCAL_ADDR,
+        dst_addr: REMOTE_ADDR,
+        next_header: IpProtocol::Tcp,
         payload_len: 20,
         hop_limit: 64,
-    };
+    });
     const RECV_TEMPL: TcpRepr<'static> = TcpRepr {
         src_port: LOCAL_PORT,
         dst_port: REMOTE_PORT,
@@ -2487,11 +2509,6 @@ mod test {
         payload: &[],
     };
 
-    #[cfg(feature = "proto-ipv6")]
-    const BASE_MSS: u16 = 1440;
-    #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
-    const BASE_MSS: u16 = 1460;
-
     // =========================================================================================//
     // Helper functions
     // =========================================================================================//
@@ -2521,13 +2538,13 @@ mod test {
     ) -> Result<Option<TcpRepr<'static>>> {
         socket.cx.set_now(timestamp);
 
-        let ip_repr = IpRepr::Unspecified {
-            src_addr: MOCK_IP_ADDR_2,
-            dst_addr: MOCK_IP_ADDR_1,
-            protocol: IpProtocol::Tcp,
+        let ip_repr = IpReprIpvX(IpvXRepr {
+            src_addr: REMOTE_ADDR,
+            dst_addr: LOCAL_ADDR,
+            next_header: IpProtocol::Tcp,
             payload_len: repr.buffer_len(),
             hop_limit: 64,
-        };
+        });
         net_trace!("send: {}", repr);
 
         assert!(socket.socket.accepts(&mut socket.cx, &ip_repr, repr));
@@ -2551,11 +2568,9 @@ mod test {
         let result = socket
             .socket
             .dispatch(&mut socket.cx, |_, (ip_repr, tcp_repr)| {
-                let ip_repr = ip_repr.lower(&[IpCidr::new(LOCAL_END.addr, 24)]).unwrap();
-
-                assert_eq!(ip_repr.protocol(), IpProtocol::Tcp);
-                assert_eq!(ip_repr.src_addr(), MOCK_IP_ADDR_1);
-                assert_eq!(ip_repr.dst_addr(), MOCK_IP_ADDR_2);
+                assert_eq!(ip_repr.next_header(), IpProtocol::Tcp);
+                assert_eq!(ip_repr.src_addr(), LOCAL_ADDR.into());
+                assert_eq!(ip_repr.dst_addr(), REMOTE_ADDR.into());
                 assert_eq!(ip_repr.payload_len(), tcp_repr.buffer_len());
 
                 net_trace!("recv: {}", tcp_repr);
@@ -2647,7 +2662,7 @@ mod test {
     fn socket_syn_sent_with_buffer_sizes(tx_len: usize, rx_len: usize) -> TestSocket {
         let mut s = socket_with_buffer_sizes(tx_len, rx_len);
         s.state = State::SynSent;
-        s.local_endpoint = IpEndpoint::new(MOCK_UNSPECIFIED, LOCAL_PORT);
+        s.local_endpoint = LOCAL_END;
         s.remote_endpoint = REMOTE_END;
         s.local_seq_no = LOCAL_SEQ;
         s.remote_last_seq = LOCAL_SEQ;
@@ -3229,12 +3244,12 @@ mod test {
         );
         assert_eq!(
             s.socket
-                .connect(&mut s.cx, REMOTE_END, (MOCK_UNSPECIFIED, 0)),
+                .connect(&mut s.cx, REMOTE_END, (IpvXAddress::UNSPECIFIED, 0)),
             Err(Error::Unaddressable)
         );
         assert_eq!(
             s.socket
-                .connect(&mut s.cx, (MOCK_UNSPECIFIED, 0), LOCAL_END),
+                .connect(&mut s.cx, (IpvXAddress::UNSPECIFIED, 0), LOCAL_END),
             Err(Error::Unaddressable)
         );
         assert_eq!(
@@ -3256,10 +3271,7 @@ mod test {
         s.socket
             .connect(&mut s.cx, REMOTE_END, LOCAL_END.port)
             .unwrap();
-        assert_eq!(
-            s.local_endpoint,
-            IpEndpoint::new(MOCK_UNSPECIFIED, LOCAL_END.port)
-        );
+        assert_eq!(s.local_endpoint, LOCAL_END);
         recv!(
             s,
             [TcpRepr {
@@ -3291,7 +3303,7 @@ mod test {
         let mut s = socket();
         assert_eq!(
             s.socket
-                .connect(&mut s.cx, REMOTE_END, (MOCK_UNSPECIFIED, 80)),
+                .connect(&mut s.cx, REMOTE_END, (IpvXAddress::UNSPECIFIED, 80)),
             Ok(())
         );
         s.abort();
@@ -3307,8 +3319,7 @@ mod test {
     fn test_connect_specified_local() {
         let mut s = socket();
         assert_eq!(
-            s.socket
-                .connect(&mut s.cx, REMOTE_END, (MOCK_IP_ADDR_2, 80)),
+            s.socket.connect(&mut s.cx, REMOTE_END, (REMOTE_ADDR, 80)),
             Ok(())
         );
     }
@@ -6954,31 +6965,31 @@ mod test {
             ..SEND_TEMPL
         };
 
-        let ip_repr = IpRepr::Unspecified {
-            src_addr: MOCK_IP_ADDR_2,
-            dst_addr: MOCK_IP_ADDR_1,
-            protocol: IpProtocol::Tcp,
+        let ip_repr = IpReprIpvX(IpvXRepr {
+            src_addr: REMOTE_ADDR,
+            dst_addr: LOCAL_ADDR,
+            next_header: IpProtocol::Tcp,
             payload_len: tcp_repr.buffer_len(),
             hop_limit: 64,
-        };
+        });
         assert!(s.socket.accepts(&mut s.cx, &ip_repr, &tcp_repr));
 
-        let ip_repr_wrong_src = IpRepr::Unspecified {
-            src_addr: MOCK_IP_ADDR_3,
-            dst_addr: MOCK_IP_ADDR_1,
-            protocol: IpProtocol::Tcp,
+        let ip_repr_wrong_src = IpReprIpvX(IpvXRepr {
+            src_addr: OTHER_ADDR,
+            dst_addr: LOCAL_ADDR,
+            next_header: IpProtocol::Tcp,
             payload_len: tcp_repr.buffer_len(),
             hop_limit: 64,
-        };
+        });
         assert!(!s.socket.accepts(&mut s.cx, &ip_repr_wrong_src, &tcp_repr));
 
-        let ip_repr_wrong_dst = IpRepr::Unspecified {
-            src_addr: MOCK_IP_ADDR_2,
-            dst_addr: MOCK_IP_ADDR_3,
-            protocol: IpProtocol::Tcp,
+        let ip_repr_wrong_dst = IpReprIpvX(IpvXRepr {
+            src_addr: REMOTE_ADDR,
+            dst_addr: OTHER_ADDR,
+            next_header: IpProtocol::Tcp,
             payload_len: tcp_repr.buffer_len(),
             hop_limit: 64,
-        };
+        });
         assert!(!s.socket.accepts(&mut s.cx, &ip_repr_wrong_dst, &tcp_repr));
     }
 

+ 74 - 84
src/socket/udp.rs

@@ -359,13 +359,13 @@ impl<'a> UdpSocket<'a> {
                     src_port: endpoint.port,
                     dst_port: remote_endpoint.port,
                 };
-                let ip_repr = IpRepr::Unspecified {
-                    src_addr: endpoint.addr,
-                    dst_addr: remote_endpoint.addr,
-                    protocol: IpProtocol::Udp,
-                    payload_len: repr.header_len() + payload_buf.len(),
-                    hop_limit: hop_limit,
-                };
+                let ip_repr = IpRepr::new(
+                    endpoint.addr,
+                    remote_endpoint.addr,
+                    IpProtocol::Udp,
+                    repr.header_len() + payload_buf.len(),
+                    hop_limit,
+                );
                 emit(cx, (ip_repr, repr, payload_buf))
             })?;
 
@@ -387,11 +387,6 @@ impl<'a> UdpSocket<'a> {
 #[cfg(test)]
 mod test {
     use super::*;
-    use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3};
-    #[cfg(feature = "proto-ipv4")]
-    use crate::wire::Ipv4Repr;
-    #[cfg(feature = "proto-ipv6")]
-    use crate::wire::Ipv6Repr;
     use crate::wire::{IpAddress, IpRepr, UdpRepr};
 
     fn buffer(packets: usize) -> UdpSocketBuffer<'static> {
@@ -411,22 +406,64 @@ mod test {
     const LOCAL_PORT: u16 = 53;
     const REMOTE_PORT: u16 = 49500;
 
+    cfg_if::cfg_if! {
+        if #[cfg(feature = "proto-ipv4")] {
+            use crate::wire::Ipv4Address as IpvXAddress;
+            use crate::wire::Ipv4Repr as IpvXRepr;
+            use IpRepr::Ipv4 as IpReprIpvX;
+
+            const LOCAL_ADDR: IpvXAddress = IpvXAddress([192, 168, 1, 1]);
+            const REMOTE_ADDR: IpvXAddress = IpvXAddress([192, 168, 1, 2]);
+            const OTHER_ADDR: IpvXAddress = IpvXAddress([192, 168, 1, 3]);
+        } else {
+            use crate::wire::Ipv6Address as IpvXAddress;
+            use crate::wire::Ipv6Repr as IpvXRepr;
+            use IpRepr::Ipv6 as IpReprIpvX;
+
+            const LOCAL_ADDR: IpvXAddress = IpvXAddress([
+                0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+            ]);
+            const REMOTE_ADDR: IpvXAddress = IpvXAddress([
+                0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
+            ]);
+            const OTHER_ADDR: IpvXAddress = IpvXAddress([
+                0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
+            ]);
+        }
+    }
+
     pub const LOCAL_END: IpEndpoint = IpEndpoint {
-        addr: MOCK_IP_ADDR_1,
+        addr: LOCAL_ADDR.into_address(),
         port: LOCAL_PORT,
     };
     pub const REMOTE_END: IpEndpoint = IpEndpoint {
-        addr: MOCK_IP_ADDR_2,
+        addr: REMOTE_ADDR.into_address(),
         port: REMOTE_PORT,
     };
 
-    pub const LOCAL_IP_REPR: IpRepr = IpRepr::Unspecified {
-        src_addr: MOCK_IP_ADDR_1,
-        dst_addr: MOCK_IP_ADDR_2,
-        protocol: IpProtocol::Udp,
+    pub const LOCAL_IP_REPR: IpRepr = IpReprIpvX(IpvXRepr {
+        src_addr: LOCAL_ADDR,
+        dst_addr: REMOTE_ADDR,
+        next_header: IpProtocol::Udp,
         payload_len: 8 + 6,
         hop_limit: 64,
-    };
+    });
+
+    pub const REMOTE_IP_REPR: IpRepr = IpReprIpvX(IpvXRepr {
+        src_addr: REMOTE_ADDR,
+        dst_addr: LOCAL_ADDR,
+        next_header: IpProtocol::Udp,
+        payload_len: 8 + 6,
+        hop_limit: 64,
+    });
+
+    pub const BAD_IP_REPR: IpRepr = IpReprIpvX(IpvXRepr {
+        src_addr: REMOTE_ADDR,
+        dst_addr: OTHER_ADDR,
+        next_header: IpProtocol::Udp,
+        payload_len: 8 + 6,
+        hop_limit: 64,
+    });
 
     const LOCAL_UDP_REPR: UdpRepr = UdpRepr {
         src_port: LOCAL_PORT,
@@ -440,28 +477,6 @@ mod test {
 
     const PAYLOAD: &[u8] = b"abcdef";
 
-    fn remote_ip_repr() -> IpRepr {
-        match (MOCK_IP_ADDR_2, MOCK_IP_ADDR_1) {
-            #[cfg(feature = "proto-ipv4")]
-            (IpAddress::Ipv4(src), IpAddress::Ipv4(dst)) => IpRepr::Ipv4(Ipv4Repr {
-                src_addr: src,
-                dst_addr: dst,
-                protocol: IpProtocol::Udp,
-                payload_len: 8 + 6,
-                hop_limit: 64,
-            }),
-            #[cfg(feature = "proto-ipv6")]
-            (IpAddress::Ipv6(src), IpAddress::Ipv6(dst)) => IpRepr::Ipv6(Ipv6Repr {
-                src_addr: src,
-                dst_addr: dst,
-                next_header: IpProtocol::Udp,
-                payload_len: 8 + 6,
-                hop_limit: 64,
-            }),
-            _ => unreachable!(),
-        }
-    }
-
     #[test]
     fn test_bind_unaddressable() {
         let mut socket = socket(buffer(0), buffer(0));
@@ -567,16 +582,16 @@ mod test {
         assert!(!socket.can_recv());
         assert_eq!(socket.recv(), Err(Error::Exhausted));
 
-        assert!(socket.accepts(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR));
+        assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR));
         assert_eq!(
-            socket.process(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
+            socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD),
             Ok(())
         );
         assert!(socket.can_recv());
 
-        assert!(socket.accepts(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR));
+        assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR));
         assert_eq!(
-            socket.process(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
+            socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD),
             Err(Error::Exhausted)
         );
         assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END)));
@@ -593,7 +608,7 @@ mod test {
         assert_eq!(socket.peek(), Err(Error::Exhausted));
 
         assert_eq!(
-            socket.process(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
+            socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD),
             Ok(())
         );
         assert_eq!(socket.peek(), Ok((&b"abcdef"[..], &REMOTE_END)));
@@ -608,9 +623,9 @@ mod test {
 
         assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
 
-        assert!(socket.accepts(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR));
+        assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR));
         assert_eq!(
-            socket.process(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
+            socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD),
             Ok(())
         );
 
@@ -627,7 +642,7 @@ mod test {
         assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
 
         assert_eq!(
-            socket.process(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
+            socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD),
             Ok(())
         );
 
@@ -652,13 +667,13 @@ mod test {
             s.dispatch(&mut cx, |_, (ip_repr, _, _)| {
                 assert_eq!(
                     ip_repr,
-                    IpRepr::Unspecified {
-                        src_addr: MOCK_IP_ADDR_1,
-                        dst_addr: MOCK_IP_ADDR_2,
-                        protocol: IpProtocol::Udp,
+                    IpReprIpvX(IpvXRepr {
+                        src_addr: LOCAL_ADDR,
+                        dst_addr: REMOTE_ADDR,
+                        next_header: IpProtocol::Udp,
                         payload_len: 8 + 6,
                         hop_limit: 0x2a,
-                    }
+                    })
                 );
                 Ok(())
             }),
@@ -674,44 +689,22 @@ mod test {
         assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
 
         let mut udp_repr = REMOTE_UDP_REPR;
-        assert!(socket.accepts(&mut cx, &remote_ip_repr(), &udp_repr));
+        assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &udp_repr));
         udp_repr.dst_port += 1;
-        assert!(!socket.accepts(&mut cx, &remote_ip_repr(), &udp_repr));
+        assert!(!socket.accepts(&mut cx, &REMOTE_IP_REPR, &udp_repr));
     }
 
     #[test]
     fn test_doesnt_accept_wrong_ip() {
-        fn generate_bad_repr() -> IpRepr {
-            match (MOCK_IP_ADDR_2, MOCK_IP_ADDR_3) {
-                #[cfg(feature = "proto-ipv4")]
-                (IpAddress::Ipv4(src), IpAddress::Ipv4(dst)) => IpRepr::Ipv4(Ipv4Repr {
-                    src_addr: src,
-                    dst_addr: dst,
-                    protocol: IpProtocol::Udp,
-                    payload_len: 8 + 6,
-                    hop_limit: 64,
-                }),
-                #[cfg(feature = "proto-ipv6")]
-                (IpAddress::Ipv6(src), IpAddress::Ipv6(dst)) => IpRepr::Ipv6(Ipv6Repr {
-                    src_addr: src,
-                    dst_addr: dst,
-                    next_header: IpProtocol::Udp,
-                    payload_len: 8 + 6,
-                    hop_limit: 64,
-                }),
-                _ => unreachable!(),
-            }
-        }
-
         let mut cx = Context::mock();
 
         let mut port_bound_socket = socket(buffer(1), buffer(0));
         assert_eq!(port_bound_socket.bind(LOCAL_PORT), Ok(()));
-        assert!(port_bound_socket.accepts(&mut cx, &generate_bad_repr(), &REMOTE_UDP_REPR));
+        assert!(port_bound_socket.accepts(&mut cx, &BAD_IP_REPR, &REMOTE_UDP_REPR));
 
         let mut ip_bound_socket = socket(buffer(1), buffer(0));
         assert_eq!(ip_bound_socket.bind(LOCAL_END), Ok(()));
-        assert!(!ip_bound_socket.accepts(&mut cx, &generate_bad_repr(), &REMOTE_UDP_REPR));
+        assert!(!ip_bound_socket.accepts(&mut cx, &BAD_IP_REPR, &REMOTE_UDP_REPR));
     }
 
     #[test]
@@ -740,10 +733,7 @@ mod test {
             src_port: REMOTE_PORT,
             dst_port: LOCAL_PORT,
         };
-        assert_eq!(
-            socket.process(&mut cx, &remote_ip_repr(), &repr, &[]),
-            Ok(())
-        );
+        assert_eq!(socket.process(&mut cx, &REMOTE_IP_REPR, &repr, &[]), Ok(()));
         assert_eq!(socket.recv(), Ok((&[][..], REMOTE_END)));
     }
 

+ 1 - 1
src/wire/icmpv4.rs

@@ -417,7 +417,7 @@ impl<'a> Repr<'a> {
                     header: Ipv4Repr {
                         src_addr: ip_packet.src_addr(),
                         dst_addr: ip_packet.dst_addr(),
-                        protocol: ip_packet.protocol(),
+                        next_header: ip_packet.next_header(),
                         payload_len: payload.len(),
                         hop_limit: ip_packet.hop_limit(),
                     },

+ 53 - 381
src/wire/ip.rs

@@ -111,6 +111,17 @@ impl Address {
         Address::Ipv6(Ipv6Address::new(a0, a1, a2, a3, a4, a5, a6, a7))
     }
 
+    /// Return the protocol version.
+    pub fn version(&self) -> Option<Version> {
+        match self {
+            Address::Unspecified => None,
+            #[cfg(feature = "proto-ipv4")]
+            Address::Ipv4(_) => Some(Version::Ipv4),
+            #[cfg(feature = "proto-ipv6")]
+            Address::Ipv6(_) => Some(Version::Ipv6),
+        }
+    }
+
     /// Return an address as a sequence of octets, in big-endian.
     pub fn as_bytes(&self) -> &[u8] {
         match *self {
@@ -495,20 +506,12 @@ impl<T: Into<Address>> From<(T, u16)> for Endpoint {
 
 /// An IP packet representation.
 ///
-/// This enum abstracts the various versions of IP packets. It either contains a concrete
-/// high-level representation for some IP protocol version, or an unspecified representation,
-/// which permits the `IpAddress::Unspecified` addresses.
+/// This enum abstracts the various versions of IP packets. It either contains an IPv4
+/// or IPv6 concrete high-level representation.
 #[derive(Debug, Clone, PartialEq, Eq)]
 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
 #[non_exhaustive]
 pub enum Repr {
-    Unspecified {
-        src_addr: Address,
-        dst_addr: Address,
-        protocol: Protocol,
-        payload_len: usize,
-        hop_limit: u8,
-    },
     #[cfg(feature = "proto-ipv4")]
     Ipv4(Ipv4Repr),
     #[cfg(feature = "proto-ipv6")]
@@ -530,10 +533,42 @@ impl From<Ipv6Repr> for Repr {
 }
 
 impl Repr {
+    /// Create a new IpRepr, choosing the right IP version for the src/dst addrs.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `src_addr` and `dst_addr` are different IP version.
+    pub fn new(
+        src_addr: Address,
+        dst_addr: Address,
+        next_header: Protocol,
+        payload_len: usize,
+        hop_limit: u8,
+    ) -> Self {
+        match (src_addr, dst_addr) {
+            #[cfg(feature = "proto-ipv4")]
+            (Address::Ipv4(src_addr), Address::Ipv4(dst_addr)) => Self::Ipv4(Ipv4Repr {
+                src_addr,
+                dst_addr,
+                next_header,
+                payload_len,
+                hop_limit,
+            }),
+            #[cfg(feature = "proto-ipv6")]
+            (Address::Ipv6(src_addr), Address::Ipv6(dst_addr)) => Self::Ipv6(Ipv6Repr {
+                src_addr,
+                dst_addr,
+                next_header,
+                payload_len,
+                hop_limit,
+            }),
+            _ => panic!("IP version mismatch: src={:?} dst={:?}", src_addr, dst_addr),
+        }
+    }
+
     /// Return the protocol version.
     pub fn version(&self) -> Version {
         match *self {
-            Repr::Unspecified { .. } => Version::Unspecified,
             #[cfg(feature = "proto-ipv4")]
             Repr::Ipv4(_) => Version::Ipv4,
             #[cfg(feature = "proto-ipv6")]
@@ -544,7 +579,6 @@ impl Repr {
     /// Return the source address.
     pub fn src_addr(&self) -> Address {
         match *self {
-            Repr::Unspecified { src_addr, .. } => src_addr,
             #[cfg(feature = "proto-ipv4")]
             Repr::Ipv4(repr) => Address::Ipv4(repr.src_addr),
             #[cfg(feature = "proto-ipv6")]
@@ -555,7 +589,6 @@ impl Repr {
     /// Return the destination address.
     pub fn dst_addr(&self) -> Address {
         match *self {
-            Repr::Unspecified { dst_addr, .. } => dst_addr,
             #[cfg(feature = "proto-ipv4")]
             Repr::Ipv4(repr) => Address::Ipv4(repr.dst_addr),
             #[cfg(feature = "proto-ipv6")]
@@ -563,12 +596,11 @@ impl Repr {
         }
     }
 
-    /// Return the protocol.
-    pub fn protocol(&self) -> Protocol {
+    /// Return the next header (protocol).
+    pub fn next_header(&self) -> Protocol {
         match *self {
-            Repr::Unspecified { protocol, .. } => protocol,
             #[cfg(feature = "proto-ipv4")]
-            Repr::Ipv4(repr) => repr.protocol,
+            Repr::Ipv4(repr) => repr.next_header,
             #[cfg(feature = "proto-ipv6")]
             Repr::Ipv6(repr) => repr.next_header,
         }
@@ -577,7 +609,6 @@ impl Repr {
     /// Return the payload length.
     pub fn payload_len(&self) -> usize {
         match *self {
-            Repr::Unspecified { payload_len, .. } => payload_len,
             #[cfg(feature = "proto-ipv4")]
             Repr::Ipv4(repr) => repr.payload_len,
             #[cfg(feature = "proto-ipv6")]
@@ -588,10 +619,6 @@ impl Repr {
     /// Set the payload length.
     pub fn set_payload_len(&mut self, length: usize) {
         match *self {
-            Repr::Unspecified {
-                ref mut payload_len,
-                ..
-            } => *payload_len = length,
             #[cfg(feature = "proto-ipv4")]
             Repr::Ipv4(Ipv4Repr {
                 ref mut payload_len,
@@ -608,7 +635,6 @@ impl Repr {
     /// Return the TTL value.
     pub fn hop_limit(&self) -> u8 {
         match *self {
-            Repr::Unspecified { hop_limit, .. } => hop_limit,
             #[cfg(feature = "proto-ipv4")]
             Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit,
             #[cfg(feature = "proto-ipv6")]
@@ -616,156 +642,9 @@ impl Repr {
         }
     }
 
-    /// Convert an unspecified representation into a concrete one, or return
-    /// `Err(Error::Unaddressable)` if not possible.
-    ///
-    /// # Panics
-    /// This function panics if source and destination addresses belong to different families,
-    /// or the destination address is unspecified, since this indicates a logic error.
-    pub fn lower(&self, fallback_src_addrs: &[Cidr]) -> Result<Repr> {
-        macro_rules! resolve_unspecified {
-            ($reprty:path, $ipty:path, $iprepr:expr, $fallbacks:expr) => {
-                if $iprepr.src_addr.is_unspecified() {
-                    for cidr in $fallbacks {
-                        match cidr.address() {
-                            $ipty(addr) => {
-                                $iprepr.src_addr = addr;
-                                return Ok($reprty($iprepr));
-                            }
-                            _ => (),
-                        }
-                    }
-                    Err(Error::Unaddressable)
-                } else {
-                    Ok($reprty($iprepr))
-                }
-            };
-        }
-
-        match self {
-            #[cfg(feature = "proto-ipv4")]
-            &Repr::Unspecified {
-                src_addr: src_addr @ Address::Unspecified,
-                dst_addr: Address::Ipv4(dst_addr),
-                protocol,
-                payload_len,
-                hop_limit,
-            }
-            | &Repr::Unspecified {
-                src_addr: src_addr @ Address::Ipv4(_),
-                dst_addr: Address::Ipv4(dst_addr),
-                protocol,
-                payload_len,
-                hop_limit,
-            } if src_addr.is_unspecified() => {
-                let mut src_addr = if let Address::Ipv4(src_ipv4_addr) = src_addr {
-                    Some(src_ipv4_addr)
-                } else {
-                    None
-                };
-                for cidr in fallback_src_addrs {
-                    if let Address::Ipv4(addr) = cidr.address() {
-                        src_addr = Some(addr);
-                        break;
-                    }
-                }
-                Ok(Repr::Ipv4(Ipv4Repr {
-                    src_addr: src_addr.ok_or(Error::Unaddressable)?,
-                    dst_addr,
-                    protocol,
-                    payload_len,
-                    hop_limit,
-                }))
-            }
-
-            #[cfg(feature = "proto-ipv6")]
-            &Repr::Unspecified {
-                src_addr: src_addr @ Address::Unspecified,
-                dst_addr: Address::Ipv6(dst_addr),
-                protocol,
-                payload_len,
-                hop_limit,
-            }
-            | &Repr::Unspecified {
-                src_addr: src_addr @ Address::Ipv6(_),
-                dst_addr: Address::Ipv6(dst_addr),
-                protocol,
-                payload_len,
-                hop_limit,
-            } if src_addr.is_unspecified() => {
-                let mut src_addr = if let Address::Ipv6(src_ipv6_addr) = src_addr {
-                    Some(src_ipv6_addr)
-                } else {
-                    None
-                };
-                for cidr in fallback_src_addrs {
-                    if let Address::Ipv6(addr) = cidr.address() {
-                        src_addr = Some(addr);
-                        break;
-                    }
-                }
-                Ok(Repr::Ipv6(Ipv6Repr {
-                    src_addr: src_addr.ok_or(Error::Unaddressable)?,
-                    next_header: protocol,
-                    dst_addr,
-                    payload_len,
-                    hop_limit,
-                }))
-            }
-
-            #[cfg(feature = "proto-ipv4")]
-            &Repr::Unspecified {
-                src_addr: Address::Ipv4(src_addr),
-                dst_addr: Address::Ipv4(dst_addr),
-                protocol,
-                payload_len,
-                hop_limit,
-            } => Ok(Repr::Ipv4(Ipv4Repr {
-                src_addr: src_addr,
-                dst_addr: dst_addr,
-                protocol: protocol,
-                payload_len: payload_len,
-                hop_limit,
-            })),
-
-            #[cfg(feature = "proto-ipv6")]
-            &Repr::Unspecified {
-                src_addr: Address::Ipv6(src_addr),
-                dst_addr: Address::Ipv6(dst_addr),
-                protocol,
-                payload_len,
-                hop_limit,
-            } => Ok(Repr::Ipv6(Ipv6Repr {
-                src_addr: src_addr,
-                dst_addr: dst_addr,
-                next_header: protocol,
-                payload_len: payload_len,
-                hop_limit: hop_limit,
-            })),
-
-            #[cfg(feature = "proto-ipv4")]
-            &Repr::Ipv4(mut repr) => {
-                resolve_unspecified!(Repr::Ipv4, Address::Ipv4, repr, fallback_src_addrs)
-            }
-
-            #[cfg(feature = "proto-ipv6")]
-            &Repr::Ipv6(mut repr) => {
-                resolve_unspecified!(Repr::Ipv6, Address::Ipv6, repr, fallback_src_addrs)
-            }
-
-            &Repr::Unspecified { .. } => {
-                panic!("source and destination IP address families do not match")
-            }
-        }
-    }
-
     /// Return the length of a header that will be emitted from this high-level representation.
-    ///
-    /// # Panics
-    /// This function panics if invoked on an unspecified representation.
     pub fn buffer_len(&self) -> usize {
         match *self {
-            Repr::Unspecified { .. } => panic!("unspecified IP representation"),
             #[cfg(feature = "proto-ipv4")]
             Repr::Ipv4(repr) => repr.buffer_len(),
             #[cfg(feature = "proto-ipv6")]
@@ -774,16 +653,12 @@ impl Repr {
     }
 
     /// Emit this high-level representation into a buffer.
-    ///
-    /// # Panics
-    /// This function panics if invoked on an unspecified representation.
     pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(
         &self,
         buffer: T,
         _checksum_caps: &ChecksumCapabilities,
     ) {
         match *self {
-            Repr::Unspecified { .. } => panic!("unspecified IP representation"),
             #[cfg(feature = "proto-ipv4")]
             Repr::Ipv4(repr) => repr.emit(&mut Ipv4Packet::new_unchecked(buffer), _checksum_caps),
             #[cfg(feature = "proto-ipv6")]
@@ -795,9 +670,6 @@ impl Repr {
     /// high-level representation.
     ///
     /// This is the same as `repr.buffer_len() + repr.payload_len()`.
-    ///
-    /// # Panics
-    /// This function panics if invoked on an unspecified representation.
     pub fn total_len(&self) -> usize {
         self.buffer_len() + self.payload_len()
     }
@@ -858,14 +730,14 @@ pub mod checksum {
     pub fn pseudo_header(
         src_addr: &Address,
         dst_addr: &Address,
-        protocol: Protocol,
+        next_header: Protocol,
         length: u32,
     ) -> u16 {
         match (src_addr, dst_addr) {
             #[cfg(feature = "proto-ipv4")]
             (&Address::Ipv4(src_addr), &Address::Ipv4(dst_addr)) => {
                 let mut proto_len = [0u8; 4];
-                proto_len[1] = protocol.into();
+                proto_len[1] = next_header.into();
                 NetworkEndian::write_u16(&mut proto_len[2..4], length as u16);
 
                 combine(&[
@@ -878,7 +750,7 @@ pub mod checksum {
             #[cfg(feature = "proto-ipv6")]
             (&Address::Ipv6(src_addr), &Address::Ipv6(dst_addr)) => {
                 let mut proto_len = [0u8; 8];
-                proto_len[7] = protocol.into();
+                proto_len[7] = next_header.into();
                 NetworkEndian::write_u32(&mut proto_len[0..4], length);
                 combine(&[
                     data(src_addr.as_bytes()),
@@ -921,7 +793,7 @@ pub fn pretty_print_ip_payload<T: Into<Repr>>(
 
     let checksum_caps = ChecksumCapabilities::ignored();
     let repr = ip_repr.into();
-    match repr.protocol() {
+    match repr.next_header() {
         #[cfg(feature = "proto-ipv4")]
         Protocol::Icmp => {
             indent.increase(f)?;
@@ -1020,206 +892,6 @@ pub(crate) mod test {
     #[cfg(feature = "proto-ipv4")]
     use crate::wire::{Ipv4Address, Ipv4Repr};
 
-    macro_rules! generate_common_tests {
-        ($name:ident, $repr:ident, $ip_repr:path, $ip_addr:path,
-         $addr_from:path, $nxthdr:ident, $bytes_a:expr, $bytes_b:expr,
-         $unspecified:expr) => {
-            mod $name {
-                use super::*;
-
-                #[test]
-                fn test_ip_repr_lower() {
-                    let ip_addr_a = $addr_from(&$bytes_a);
-                    let ip_addr_b = $addr_from(&$bytes_b);
-                    let proto = IpProtocol::Icmp;
-                    let payload_len = 10;
-
-                    assert_eq!(
-                        Repr::Unspecified {
-                            src_addr: $ip_addr(ip_addr_a),
-                            dst_addr: $ip_addr(ip_addr_b),
-                            protocol: proto,
-                            hop_limit: 0x2a,
-                            payload_len,
-                        }
-                        .lower(&[]),
-                        Ok($ip_repr($repr {
-                            src_addr: ip_addr_a,
-                            dst_addr: ip_addr_b,
-                            $nxthdr: proto,
-                            hop_limit: 0x2a,
-                            payload_len
-                        }))
-                    );
-
-                    assert_eq!(
-                        Repr::Unspecified {
-                            src_addr: IpAddress::Unspecified,
-                            dst_addr: $ip_addr(ip_addr_b),
-                            protocol: proto,
-                            hop_limit: 64,
-                            payload_len
-                        }
-                        .lower(&[]),
-                        Err(Error::Unaddressable)
-                    );
-
-                    assert_eq!(
-                        Repr::Unspecified {
-                            src_addr: IpAddress::Unspecified,
-                            dst_addr: $ip_addr(ip_addr_b),
-                            protocol: proto,
-                            hop_limit: 64,
-                            payload_len
-                        }
-                        .lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]),
-                        Ok($ip_repr($repr {
-                            src_addr: ip_addr_a,
-                            dst_addr: ip_addr_b,
-                            $nxthdr: proto,
-                            hop_limit: 64,
-                            payload_len
-                        }))
-                    );
-
-                    assert_eq!(
-                        Repr::Unspecified {
-                            src_addr: $ip_addr($unspecified),
-                            dst_addr: $ip_addr(ip_addr_b),
-                            protocol: proto,
-                            hop_limit: 64,
-                            payload_len
-                        }
-                        .lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]),
-                        Ok($ip_repr($repr {
-                            src_addr: ip_addr_a,
-                            dst_addr: ip_addr_b,
-                            $nxthdr: proto,
-                            hop_limit: 64,
-                            payload_len
-                        }))
-                    );
-
-                    assert_eq!(
-                        Repr::Unspecified {
-                            src_addr: $ip_addr($unspecified),
-                            dst_addr: $ip_addr(ip_addr_b),
-                            protocol: proto,
-                            hop_limit: 64,
-                            payload_len
-                        }
-                        .lower(&[]),
-                        Ok($ip_repr($repr {
-                            src_addr: $unspecified,
-                            dst_addr: ip_addr_b,
-                            $nxthdr: proto,
-                            hop_limit: 64,
-                            payload_len
-                        }))
-                    );
-
-                    assert_eq!(
-                        $ip_repr($repr {
-                            src_addr: ip_addr_a,
-                            dst_addr: ip_addr_b,
-                            $nxthdr: proto,
-                            hop_limit: 255,
-                            payload_len
-                        })
-                        .lower(&[]),
-                        Ok($ip_repr($repr {
-                            src_addr: ip_addr_a,
-                            dst_addr: ip_addr_b,
-                            $nxthdr: proto,
-                            hop_limit: 255,
-                            payload_len
-                        }))
-                    );
-
-                    assert_eq!(
-                        $ip_repr($repr {
-                            src_addr: $unspecified,
-                            dst_addr: ip_addr_b,
-                            $nxthdr: proto,
-                            hop_limit: 255,
-                            payload_len
-                        })
-                        .lower(&[]),
-                        Err(Error::Unaddressable)
-                    );
-
-                    assert_eq!(
-                        $ip_repr($repr {
-                            src_addr: $unspecified,
-                            dst_addr: ip_addr_b,
-                            $nxthdr: proto,
-                            hop_limit: 64,
-                            payload_len
-                        })
-                        .lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]),
-                        Ok($ip_repr($repr {
-                            src_addr: ip_addr_a,
-                            dst_addr: ip_addr_b,
-                            $nxthdr: proto,
-                            hop_limit: 64,
-                            payload_len
-                        }))
-                    );
-                }
-            }
-        };
-        (ipv4 $addr_bytes_a:expr, $addr_bytes_b:expr) => {
-            generate_common_tests!(
-                ipv4,
-                Ipv4Repr,
-                Repr::Ipv4,
-                IpAddress::Ipv4,
-                Ipv4Address::from_bytes,
-                protocol,
-                $addr_bytes_a,
-                $addr_bytes_b,
-                Ipv4Address::UNSPECIFIED
-            );
-        };
-        (ipv6 $addr_bytes_a:expr, $addr_bytes_b:expr) => {
-            generate_common_tests!(
-                ipv6,
-                Ipv6Repr,
-                Repr::Ipv6,
-                IpAddress::Ipv6,
-                Ipv6Address::from_bytes,
-                next_header,
-                $addr_bytes_a,
-                $addr_bytes_b,
-                Ipv6Address::UNSPECIFIED
-            );
-        };
-    }
-
-    #[cfg(feature = "proto-ipv4")]
-    generate_common_tests!(ipv4
-                           [1, 2, 3, 4],
-                           [5, 6, 7, 8]);
-
-    #[cfg(feature = "proto-ipv6")]
-    generate_common_tests!(ipv6
-                           [0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
-                           [0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]);
-
-    #[test]
-    #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
-    #[should_panic(expected = "source and destination IP address families do not match")]
-    fn test_lower_between_families() {
-        Repr::Unspecified {
-            src_addr: Address::Ipv6(Ipv6Address::UNSPECIFIED),
-            dst_addr: Address::Ipv4(Ipv4Address::UNSPECIFIED),
-            protocol: IpProtocol::Icmpv6,
-            hop_limit: 0xff,
-            payload_len: 0,
-        }
-        .lower(&[]);
-    }
-
     #[test]
     fn endpoint_unspecified() {
         assert!(!Endpoint::UNSPECIFIED.is_specified());

+ 19 - 12
src/wire/ipv4.rs

@@ -87,6 +87,13 @@ impl Address {
     pub const fn is_loopback(&self) -> bool {
         self.0[0] == 127
     }
+
+    /// Convert to an `IpAddress`.
+    ///
+    /// Same as `.into()`, but works in `const`.
+    pub const fn into_address(self) -> super::IpAddress {
+        super::IpAddress::Ipv4(self)
+    }
 }
 
 #[cfg(feature = "std")]
@@ -396,9 +403,9 @@ impl<T: AsRef<[u8]>> Packet<T> {
         data[field::TTL]
     }
 
-    /// Return the protocol field.
+    /// Return the next_header (protocol) field.
     #[inline]
-    pub fn protocol(&self) -> Protocol {
+    pub fn next_header(&self) -> Protocol {
         let data = self.buffer.as_ref();
         Protocol::from(data[field::PROTOCOL])
     }
@@ -532,9 +539,9 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
         data[field::TTL] = value
     }
 
-    /// Set the protocol field.
+    /// Set the next header (protocol) field.
     #[inline]
-    pub fn set_protocol(&mut self, value: Protocol) {
+    pub fn set_next_header(&mut self, value: Protocol) {
         let data = self.buffer.as_mut();
         data[field::PROTOCOL] = value.into()
     }
@@ -591,7 +598,7 @@ impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
 pub struct Repr {
     pub src_addr: Address,
     pub dst_addr: Address,
-    pub protocol: Protocol,
+    pub next_header: Protocol,
     pub payload_len: usize,
     pub hop_limit: u8,
 }
@@ -626,7 +633,7 @@ impl Repr {
         Ok(Repr {
             src_addr: packet.src_addr(),
             dst_addr: packet.dst_addr(),
-            protocol: packet.protocol(),
+            next_header: packet.next_header(),
             payload_len: payload_len,
             hop_limit: packet.hop_limit(),
         })
@@ -656,7 +663,7 @@ impl Repr {
         packet.set_dont_frag(true);
         packet.set_frag_offset(0);
         packet.set_hop_limit(self.hop_limit);
-        packet.set_protocol(self.protocol);
+        packet.set_next_header(self.next_header);
         packet.set_src_addr(self.src_addr);
         packet.set_dst_addr(self.dst_addr);
 
@@ -681,7 +688,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
                     " src={} dst={} proto={} hop_limit={}",
                     self.src_addr(),
                     self.dst_addr(),
-                    self.protocol(),
+                    self.next_header(),
                     self.hop_limit()
                 )?;
                 if self.version() != 4 {
@@ -720,7 +727,7 @@ impl fmt::Display for Repr {
         write!(
             f,
             "IPv4 src={} dst={} proto={}",
-            self.src_addr, self.dst_addr, self.protocol
+            self.src_addr, self.dst_addr, self.next_header
         )
     }
 }
@@ -777,7 +784,7 @@ mod test {
         assert!(packet.dont_frag());
         assert_eq!(packet.frag_offset(), 0x203 * 8);
         assert_eq!(packet.hop_limit(), 0x1a);
-        assert_eq!(packet.protocol(), Protocol::Icmp);
+        assert_eq!(packet.next_header(), Protocol::Icmp);
         assert_eq!(packet.checksum(), 0xd56e);
         assert_eq!(packet.src_addr(), Address([0x11, 0x12, 0x13, 0x14]));
         assert_eq!(packet.dst_addr(), Address([0x21, 0x22, 0x23, 0x24]));
@@ -800,7 +807,7 @@ mod test {
         packet.set_dont_frag(true);
         packet.set_frag_offset(0x203 * 8);
         packet.set_hop_limit(0x1a);
-        packet.set_protocol(Protocol::Icmp);
+        packet.set_next_header(Protocol::Icmp);
         packet.set_src_addr(Address([0x11, 0x12, 0x13, 0x14]));
         packet.set_dst_addr(Address([0x21, 0x22, 0x23, 0x24]));
         packet.fill_checksum();
@@ -844,7 +851,7 @@ mod test {
         Repr {
             src_addr: Address([0x11, 0x12, 0x13, 0x14]),
             dst_addr: Address([0x21, 0x22, 0x23, 0x24]),
-            protocol: Protocol::Icmp,
+            next_header: Protocol::Icmp,
             payload_len: 4,
             hop_limit: 64,
         }

+ 7 - 0
src/wire/ipv6.rs

@@ -188,6 +188,13 @@ impl Address {
             self.0[13], self.0[14], self.0[15],
         ])
     }
+
+    /// Convert to an `IpAddress`.
+    ///
+    /// Same as `.into()`, but works in `const`.
+    pub const fn into_address(self) -> super::IpAddress {
+        super::IpAddress::Ipv6(self)
+    }
 }
 
 #[cfg(feature = "std")]

+ 1 - 1
src/wire/mod.rs

@@ -50,7 +50,7 @@ use smoltcp::wire::*;
 let repr = Ipv4Repr {
     src_addr:    Ipv4Address::new(10, 0, 0, 1),
     dst_addr:    Ipv4Address::new(10, 0, 0, 2),
-    protocol:    IpProtocol::Tcp,
+    next_header: IpProtocol::Tcp,
     payload_len: 10,
     hop_limit:   64
 };